text: import { Vec3 } from '../math/Vec3.js';
  import { Quat } from '../math/Quat.js';
  
  const tmpVec3A = new Vec3();
  const tmpVec3B = new Vec3();
  const tmpVec3C = new Vec3();
  const tmpVec3D = new Vec3();
  
  const tmpQuatA = new Quat();
  const tmpQuatB = new Quat();
  const tmpQuatC = new Quat();
  const tmpQuatD = new Quat();
  
  export class GLTFAnimation {
      constructor(data, weight = 1) {
          this.data = data;
          this.elapsed = 0;
          this.weight = weight;
  
          // Set to false to not apply modulo to elapsed against duration
          this.loop = true;
  
          // Get duration from largest final time in all channels
          this.duration = data.reduce((a, { times }) => Math.max(a, times[times.length - 1]), 0);
      }
  
      update(totalWeight = 1, isSet) {
          const weight = isSet ? 1 : this.weight / totalWeight;
          const elapsed = this.loop ? this.elapsed % this.duration : Math.min(this.elapsed, this.duration);
  
          this.data.forEach(({ node, transform, interpolation, times, values }) => {
              // Get index of two time values elapsed is between
              const prevIndex =
                  Math.max(
                      1,
                      times.findIndex((t) => t > elapsed)
                  ) - 1;
              const nextIndex = prevIndex + 1;
  
              // Get linear blend/alpha between the two
              let alpha = (elapsed - times[prevIndex]) / (times[nextIndex] - times[prevIndex]);
              if (interpolation === 'STEP') alpha = 0;
  
              let prevVal = tmpVec3A;
              let prevTan = tmpVec3B;
              let nextTan = tmpVec3C;
              let nextVal = tmpVec3D;
              let size = 3;
  
              if (transform === 'quaternion') {
                  prevVal = tmpQuatA;
                  prevTan = tmpQuatB;
                  nextTan = tmpQuatC;
                  nextVal = tmpQuatD;
                  size = 4;
              }
  
              if (interpolation === 'CUBICSPLINE') {
                  // Get the prev and next values from the indices
                  prevVal.fromArray(values, prevIndex * size * 3 + size * 1);
                  prevTan.fromArray(values, prevIndex * size * 3 + size * 2);
                  nextTan.fromArray(values, nextIndex * size * 3 + size * 0);
                  nextVal.fromArray(values, nextIndex * size * 3 + size * 1);
  
                  // interpolate for final value
                  prevVal = this.cubicSplineInterpolate(alpha, prevVal, prevTan, nextTan, nextVal);
                  if (size === 4) prevVal.normalize();
              } else {
                  // Get the prev and next values from the indices
                  prevVal.fromArray(values, prevIndex * size);
                  nextVal.fromArray(values, nextIndex * size);
  
                  // interpolate for final value
                  if (size === 4) prevVal.slerp(nextVal, alpha);
                  else prevVal.lerp(nextVal, alpha);
              }
  
              // interpolate between multiple possible animations
              if (size === 4) node[transform].slerp(prevVal, weight);
              else node[transform].lerp(prevVal, weight);
          });
      }
  
      cubicSplineInterpolate(t, prevVal, prevTan, nextTan, nextVal) {
          const t2 = t * t;
          const t3 = t2 * t;
  
          const s2 = 3 * t2 - 2 * t3;
          const s3 = t3 - t2;
          const s0 = 1 - s2;
          const s1 = s3 - t2 + t;
  
          for (let i = 0; i < prevVal.length; i++) {
              prevVal[i] = s0 * prevVal[i] + s1 * (1 - t) * prevTan[i] + s2 * nextVal[i] + s3 * t * nextTan[i];
          }
  
          return prevVal;
      }
  }
  