ogl
Version:
WebGL Library
100 lines (81 loc) • 3.55 kB
JavaScript
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;
}
}