|
@@ -858,6 +858,61 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /*********************************/
|
|
|
+ /********** INTERPOLATION ********/
|
|
|
+ /*********************************/
|
|
|
+
|
|
|
+ // Spline Interpolation
|
|
|
+ // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation
|
|
|
+ function GLTFCubicSplineInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
|
|
|
+
|
|
|
+ THREE.Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ GLTFCubicSplineInterpolant.prototype = Object.create( THREE.Interpolant.prototype );
|
|
|
+ GLTFCubicSplineInterpolant.prototype.constructor = GLTFCubicSplineInterpolant;
|
|
|
+
|
|
|
+ GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) {
|
|
|
+
|
|
|
+ var result = this.resultBuffer;
|
|
|
+ var values = this.sampleValues;
|
|
|
+ var stride = this.valueSize;
|
|
|
+
|
|
|
+ var stride2 = stride * 2;
|
|
|
+ var stride3 = stride * 3;
|
|
|
+
|
|
|
+ var td = t1 - t0;
|
|
|
+
|
|
|
+ var p = ( t - t0 ) / td;
|
|
|
+ var pp = p * p;
|
|
|
+ var ppp = pp * p;
|
|
|
+
|
|
|
+ var offset1 = i1 * stride3;
|
|
|
+ var offset0 = offset1 - stride3;
|
|
|
+
|
|
|
+ var s0 = 2 * ppp - 3 * pp + 1;
|
|
|
+ var s1 = ppp - 2 * pp + p;
|
|
|
+ var s2 = - 2 * ppp + 3 * pp;
|
|
|
+ var s3 = ppp - pp;
|
|
|
+
|
|
|
+ // Layout of keyframe output values for CUBICSPLINE animations:
|
|
|
+ // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ]
|
|
|
+ for ( var i = 0; i !== stride; i ++ ) {
|
|
|
+
|
|
|
+ var p0 = values[ offset0 + i + stride ]; // splineVertex_k
|
|
|
+ var m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k)
|
|
|
+ var p1 = values[ offset1 + i + stride ]; // splineVertex_k+1
|
|
|
+ var m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k)
|
|
|
+
|
|
|
+ result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
/*********************************/
|
|
|
/********** INTERNALS ************/
|
|
|
/*********************************/
|
|
@@ -996,7 +1051,10 @@ THREE.GLTFLoader = ( function () {
|
|
|
};
|
|
|
|
|
|
var INTERPOLATION = {
|
|
|
- CUBICSPLINE: THREE.InterpolateSmooth,
|
|
|
+ CUBICSPLINE: THREE.InterpolateSmooth, // We use custom interpolation GLTFCubicSplineInterpolation for CUBICSPLINE.
|
|
|
+ // KeyframeTrack.optimize() can't handle glTF Cubic Spline output values layout,
|
|
|
+ // using THREE.InterpolateSmooth for KeyframeTrack instantiation to prevent optimization.
|
|
|
+ // See KeyframeTrack.optimize() for the detail.
|
|
|
LINEAR: THREE.InterpolateLinear,
|
|
|
STEP: THREE.InterpolateDiscrete
|
|
|
};
|
|
@@ -2325,36 +2383,6 @@ THREE.GLTFLoader = ( function () {
|
|
|
|
|
|
var targetName = node.name ? node.name : node.uuid;
|
|
|
|
|
|
- if ( sampler.interpolation === 'CUBICSPLINE' ) {
|
|
|
-
|
|
|
- var itemSize = outputAccessor.itemSize;
|
|
|
- var TypedArray = outputAccessor.array.constructor;
|
|
|
- var outputAccessorValues = new TypedArray( outputAccessor.count * itemSize / 3 );
|
|
|
-
|
|
|
- // Layout of keyframe output values for CUBICSPLINE animations:
|
|
|
- //
|
|
|
- // [ inTangent1, splineVertex1, outTangent1, inTangent2, splineVertex2, ... ]
|
|
|
- //
|
|
|
- // THREE.KeyframeTrack infers tangents from the spline vertices when interpolating:
|
|
|
- // those values are extracted below. This still guarantees smooth curves, but does
|
|
|
- // throw away more precise information in the tangents. In the future, consider
|
|
|
- // re-sampling at a higher framerate using the tangents provided.
|
|
|
- //
|
|
|
- // See: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation
|
|
|
-
|
|
|
- for ( var j = 0, jl = outputAccessor.count; j < jl; j += 3 ) {
|
|
|
-
|
|
|
- outputAccessorValues[ j * itemSize / 3 ] = outputAccessor.getX( j + 1 );
|
|
|
- if ( itemSize > 1 ) outputAccessorValues[ j * itemSize / 3 + 1 ] = outputAccessor.getY( j + 1 );
|
|
|
- if ( itemSize > 2 ) outputAccessorValues[ j * itemSize / 3 + 2 ] = outputAccessor.getZ( j + 1 );
|
|
|
- if ( itemSize > 3 ) outputAccessorValues[ j * itemSize / 3 + 3 ] = outputAccessor.getW( j + 1 );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- outputAccessor = new THREE.BufferAttribute( outputAccessorValues, itemSize / 3, outputAccessor.normalized );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear;
|
|
|
|
|
|
var targetNames = [];
|
|
@@ -2387,12 +2415,30 @@ THREE.GLTFLoader = ( function () {
|
|
|
// be reused by other tracks, make copies here.
|
|
|
for ( var j = 0, jl = targetNames.length; j < jl; j ++ ) {
|
|
|
|
|
|
- tracks.push( new TypedKeyframeTrack(
|
|
|
+ var track = new TypedKeyframeTrack(
|
|
|
targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ],
|
|
|
THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ),
|
|
|
THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ),
|
|
|
interpolation
|
|
|
- ) );
|
|
|
+ );
|
|
|
+
|
|
|
+ // Here is the trick to enable custom interpolation.
|
|
|
+ // Overrides .createInterpolant in a factory method which creates custom interpolation.
|
|
|
+ if ( sampler.interpolation === 'CUBICSPLINE' ) {
|
|
|
+
|
|
|
+ track.createInterpolant = function ( result ) {
|
|
|
+
|
|
|
+ // A CUBICSPLINE keyframe in glTF has three output values for each input value,
|
|
|
+ // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
|
|
|
+ // must be divided by three to get the interpolant's sampleSize argument.
|
|
|
+
|
|
|
+ return new GLTFCubicSplineInterpolant( this.times, this.values, this.getValueSize() / 3, result );
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ tracks.push( track );
|
|
|
|
|
|
}
|
|
|
|