|
@@ -31474,168 +31474,69 @@ RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), {
|
|
|
} );
|
|
|
|
|
|
/**
|
|
|
- * @author tschw
|
|
|
+ *
|
|
|
+ * A Track that interpolates Strings
|
|
|
+ *
|
|
|
+ *
|
|
|
* @author Ben Houston / http://clara.io/
|
|
|
* @author David Sarno / http://lighthaus.us/
|
|
|
+ * @author tschw
|
|
|
*/
|
|
|
|
|
|
-var AnimationUtils = {
|
|
|
-
|
|
|
- // same as Array.prototype.slice, but also works on typed arrays
|
|
|
- arraySlice: function ( array, from, to ) {
|
|
|
-
|
|
|
- if ( AnimationUtils.isTypedArray( array ) ) {
|
|
|
-
|
|
|
- // in ios9 array.subarray(from, undefined) will return empty array
|
|
|
- // but array.subarray(from) or array.subarray(from, len) is correct
|
|
|
- return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return array.slice( from, to );
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- // converts an array to a specific type
|
|
|
- convertArray: function ( array, type, forceClone ) {
|
|
|
-
|
|
|
- if ( ! array || // let 'undefined' and 'null' pass
|
|
|
- ! forceClone && array.constructor === type ) return array;
|
|
|
-
|
|
|
- if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {
|
|
|
-
|
|
|
- return new type( array ); // create typed array
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return Array.prototype.slice.call( array ); // create Array
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- isTypedArray: function ( object ) {
|
|
|
-
|
|
|
- return ArrayBuffer.isView( object ) &&
|
|
|
- ! ( object instanceof DataView );
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- // returns an array by which times and values can be sorted
|
|
|
- getKeyframeOrder: function ( times ) {
|
|
|
-
|
|
|
- function compareTime( i, j ) {
|
|
|
-
|
|
|
- return times[ i ] - times[ j ];
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- var n = times.length;
|
|
|
- var result = new Array( n );
|
|
|
- for ( var i = 0; i !== n; ++ i ) result[ i ] = i;
|
|
|
-
|
|
|
- result.sort( compareTime );
|
|
|
-
|
|
|
- return result;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- // uses the array previously returned by 'getKeyframeOrder' to sort data
|
|
|
- sortedArray: function ( values, stride, order ) {
|
|
|
-
|
|
|
- var nValues = values.length;
|
|
|
- var result = new values.constructor( nValues );
|
|
|
-
|
|
|
- for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {
|
|
|
-
|
|
|
- var srcOffset = order[ i ] * stride;
|
|
|
-
|
|
|
- for ( var j = 0; j !== stride; ++ j ) {
|
|
|
-
|
|
|
- result[ dstOffset ++ ] = values[ srcOffset + j ];
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- // function for parsing AOS keyframe formats
|
|
|
- flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {
|
|
|
-
|
|
|
- var i = 1, key = jsonKeys[ 0 ];
|
|
|
-
|
|
|
- while ( key !== undefined && key[ valuePropertyName ] === undefined ) {
|
|
|
-
|
|
|
- key = jsonKeys[ i ++ ];
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( key === undefined ) return; // no data
|
|
|
-
|
|
|
- var value = key[ valuePropertyName ];
|
|
|
- if ( value === undefined ) return; // no data
|
|
|
-
|
|
|
- if ( Array.isArray( value ) ) {
|
|
|
-
|
|
|
- do {
|
|
|
-
|
|
|
- value = key[ valuePropertyName ];
|
|
|
-
|
|
|
- if ( value !== undefined ) {
|
|
|
-
|
|
|
- times.push( key.time );
|
|
|
- values.push.apply( values, value ); // push all elements
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- key = jsonKeys[ i ++ ];
|
|
|
-
|
|
|
- } while ( key !== undefined );
|
|
|
-
|
|
|
- } else if ( value.toArray !== undefined ) {
|
|
|
+function StringKeyframeTrack( name, times, values, interpolation ) {
|
|
|
|
|
|
- // ...assume THREE.Math-ish
|
|
|
+ KeyframeTrack.call( this, name, times, values, interpolation );
|
|
|
|
|
|
- do {
|
|
|
+}
|
|
|
|
|
|
- value = key[ valuePropertyName ];
|
|
|
+StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
|
|
|
|
|
|
- if ( value !== undefined ) {
|
|
|
+ constructor: StringKeyframeTrack,
|
|
|
|
|
|
- times.push( key.time );
|
|
|
- value.toArray( values, values.length );
|
|
|
+ ValueTypeName: 'string',
|
|
|
+ ValueBufferType: Array,
|
|
|
|
|
|
- }
|
|
|
+ DefaultInterpolation: InterpolateDiscrete,
|
|
|
|
|
|
- key = jsonKeys[ i ++ ];
|
|
|
+ InterpolantFactoryMethodLinear: undefined,
|
|
|
|
|
|
- } while ( key !== undefined );
|
|
|
+ InterpolantFactoryMethodSmooth: undefined
|
|
|
|
|
|
- } else {
|
|
|
+} );
|
|
|
|
|
|
- // otherwise push as-is
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * A Track of Boolean keyframe values.
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * @author Ben Houston / http://clara.io/
|
|
|
+ * @author David Sarno / http://lighthaus.us/
|
|
|
+ * @author tschw
|
|
|
+ */
|
|
|
|
|
|
- do {
|
|
|
+function BooleanKeyframeTrack( name, times, values ) {
|
|
|
|
|
|
- value = key[ valuePropertyName ];
|
|
|
+ KeyframeTrack.call( this, name, times, values );
|
|
|
|
|
|
- if ( value !== undefined ) {
|
|
|
+}
|
|
|
|
|
|
- times.push( key.time );
|
|
|
- values.push( value );
|
|
|
+BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
|
|
|
|
|
|
- }
|
|
|
+ constructor: BooleanKeyframeTrack,
|
|
|
|
|
|
- key = jsonKeys[ i ++ ];
|
|
|
+ ValueTypeName: 'bool',
|
|
|
+ ValueBufferType: Array,
|
|
|
|
|
|
- } while ( key !== undefined );
|
|
|
+ DefaultInterpolation: InterpolateDiscrete,
|
|
|
|
|
|
- }
|
|
|
+ InterpolantFactoryMethodLinear: undefined,
|
|
|
+ InterpolantFactoryMethodSmooth: undefined
|
|
|
|
|
|
- }
|
|
|
+ // Note: Actually this track could have a optimized / compressed
|
|
|
+ // representation of a single value and a custom interpolant that
|
|
|
+ // computes "firstValue ^ isOdd( index )".
|
|
|
|
|
|
-};
|
|
|
+} );
|
|
|
|
|
|
/**
|
|
|
* Abstract base class of interpolants over parametric samples.
|
|
@@ -31895,89 +31796,220 @@ Object.assign( Interpolant.prototype, {
|
|
|
} );
|
|
|
|
|
|
/**
|
|
|
- * Fast and simple cubic spline interpolant.
|
|
|
- *
|
|
|
- * It was derived from a Hermitian construction setting the first derivative
|
|
|
- * at each sample position to the linear slope between neighboring positions
|
|
|
- * over their parameter interval.
|
|
|
+ * Spherical linear unit quaternion interpolant.
|
|
|
*
|
|
|
* @author tschw
|
|
|
*/
|
|
|
|
|
|
-function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
|
|
|
+function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
|
|
|
|
|
|
Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
|
|
|
|
|
|
- this._weightPrev = - 0;
|
|
|
- this._offsetPrev = - 0;
|
|
|
- this._weightNext = - 0;
|
|
|
- this._offsetNext = - 0;
|
|
|
-
|
|
|
}
|
|
|
|
|
|
-CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
|
|
|
+QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
|
|
|
|
|
|
- constructor: CubicInterpolant,
|
|
|
+ constructor: QuaternionLinearInterpolant,
|
|
|
|
|
|
- DefaultSettings_: {
|
|
|
+ interpolate_: function ( i1, t0, t, t1 ) {
|
|
|
|
|
|
- endingStart: ZeroCurvatureEnding,
|
|
|
- endingEnd: ZeroCurvatureEnding
|
|
|
+ var result = this.resultBuffer,
|
|
|
+ values = this.sampleValues,
|
|
|
+ stride = this.valueSize,
|
|
|
|
|
|
- },
|
|
|
+ offset = i1 * stride,
|
|
|
|
|
|
- intervalChanged_: function ( i1, t0, t1 ) {
|
|
|
+ alpha = ( t - t0 ) / ( t1 - t0 );
|
|
|
|
|
|
- var pp = this.parameterPositions,
|
|
|
- iPrev = i1 - 2,
|
|
|
- iNext = i1 + 1,
|
|
|
+ for ( var end = offset + stride; offset !== end; offset += 4 ) {
|
|
|
|
|
|
- tPrev = pp[ iPrev ],
|
|
|
- tNext = pp[ iNext ];
|
|
|
+ Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );
|
|
|
|
|
|
- if ( tPrev === undefined ) {
|
|
|
+ }
|
|
|
|
|
|
- switch ( this.getSettings_().endingStart ) {
|
|
|
+ return result;
|
|
|
|
|
|
- case ZeroSlopeEnding:
|
|
|
+ }
|
|
|
|
|
|
- // f'(t0) = 0
|
|
|
- iPrev = i1;
|
|
|
- tPrev = 2 * t0 - t1;
|
|
|
+} );
|
|
|
|
|
|
- break;
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * A Track of quaternion keyframe values.
|
|
|
+ *
|
|
|
+ * @author Ben Houston / http://clara.io/
|
|
|
+ * @author David Sarno / http://lighthaus.us/
|
|
|
+ * @author tschw
|
|
|
+ */
|
|
|
|
|
|
- case WrapAroundEnding:
|
|
|
+function QuaternionKeyframeTrack( name, times, values, interpolation ) {
|
|
|
|
|
|
- // use the other end of the curve
|
|
|
- iPrev = pp.length - 2;
|
|
|
- tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];
|
|
|
+ KeyframeTrack.call( this, name, times, values, interpolation );
|
|
|
|
|
|
- break;
|
|
|
+}
|
|
|
|
|
|
- default: // ZeroCurvatureEnding
|
|
|
+QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
|
|
|
|
|
|
- // f''(t0) = 0 a.k.a. Natural Spline
|
|
|
- iPrev = i1;
|
|
|
- tPrev = t1;
|
|
|
+ constructor: QuaternionKeyframeTrack,
|
|
|
|
|
|
- }
|
|
|
+ ValueTypeName: 'quaternion',
|
|
|
|
|
|
- }
|
|
|
+ // ValueBufferType is inherited
|
|
|
|
|
|
- if ( tNext === undefined ) {
|
|
|
+ DefaultInterpolation: InterpolateLinear,
|
|
|
|
|
|
- switch ( this.getSettings_().endingEnd ) {
|
|
|
+ InterpolantFactoryMethodLinear: function ( result ) {
|
|
|
|
|
|
- case ZeroSlopeEnding:
|
|
|
+ return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );
|
|
|
|
|
|
- // f'(tN) = 0
|
|
|
- iNext = i1;
|
|
|
- tNext = 2 * t1 - t0;
|
|
|
+ },
|
|
|
|
|
|
- break;
|
|
|
+ InterpolantFactoryMethodSmooth: undefined // not yet implemented
|
|
|
|
|
|
- case WrapAroundEnding:
|
|
|
+} );
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * A Track of keyframe values that represent color.
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * @author Ben Houston / http://clara.io/
|
|
|
+ * @author David Sarno / http://lighthaus.us/
|
|
|
+ * @author tschw
|
|
|
+ */
|
|
|
+
|
|
|
+function ColorKeyframeTrack( name, times, values, interpolation ) {
|
|
|
+
|
|
|
+ KeyframeTrack.call( this, name, times, values, interpolation );
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
|
|
|
+
|
|
|
+ constructor: ColorKeyframeTrack,
|
|
|
+
|
|
|
+ ValueTypeName: 'color'
|
|
|
+
|
|
|
+ // ValueBufferType is inherited
|
|
|
+
|
|
|
+ // DefaultInterpolation is inherited
|
|
|
+
|
|
|
+ // Note: Very basic implementation and nothing special yet.
|
|
|
+ // However, this is the place for color space parameterization.
|
|
|
+
|
|
|
+} );
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * A Track of numeric keyframe values.
|
|
|
+ *
|
|
|
+ * @author Ben Houston / http://clara.io/
|
|
|
+ * @author David Sarno / http://lighthaus.us/
|
|
|
+ * @author tschw
|
|
|
+ */
|
|
|
+
|
|
|
+function NumberKeyframeTrack( name, times, values, interpolation ) {
|
|
|
+
|
|
|
+ KeyframeTrack.call( this, name, times, values, interpolation );
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
|
|
|
+
|
|
|
+ constructor: NumberKeyframeTrack,
|
|
|
+
|
|
|
+ ValueTypeName: 'number'
|
|
|
+
|
|
|
+ // ValueBufferType is inherited
|
|
|
+
|
|
|
+ // DefaultInterpolation is inherited
|
|
|
+
|
|
|
+} );
|
|
|
+
|
|
|
+/**
|
|
|
+ * Fast and simple cubic spline interpolant.
|
|
|
+ *
|
|
|
+ * It was derived from a Hermitian construction setting the first derivative
|
|
|
+ * at each sample position to the linear slope between neighboring positions
|
|
|
+ * over their parameter interval.
|
|
|
+ *
|
|
|
+ * @author tschw
|
|
|
+ */
|
|
|
+
|
|
|
+function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
|
|
|
+
|
|
|
+ Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
|
|
|
+
|
|
|
+ this._weightPrev = - 0;
|
|
|
+ this._offsetPrev = - 0;
|
|
|
+ this._weightNext = - 0;
|
|
|
+ this._offsetNext = - 0;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
|
|
|
+
|
|
|
+ constructor: CubicInterpolant,
|
|
|
+
|
|
|
+ DefaultSettings_: {
|
|
|
+
|
|
|
+ endingStart: ZeroCurvatureEnding,
|
|
|
+ endingEnd: ZeroCurvatureEnding
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ intervalChanged_: function ( i1, t0, t1 ) {
|
|
|
+
|
|
|
+ var pp = this.parameterPositions,
|
|
|
+ iPrev = i1 - 2,
|
|
|
+ iNext = i1 + 1,
|
|
|
+
|
|
|
+ tPrev = pp[ iPrev ],
|
|
|
+ tNext = pp[ iNext ];
|
|
|
+
|
|
|
+ if ( tPrev === undefined ) {
|
|
|
+
|
|
|
+ switch ( this.getSettings_().endingStart ) {
|
|
|
+
|
|
|
+ case ZeroSlopeEnding:
|
|
|
+
|
|
|
+ // f'(t0) = 0
|
|
|
+ iPrev = i1;
|
|
|
+ tPrev = 2 * t0 - t1;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case WrapAroundEnding:
|
|
|
+
|
|
|
+ // use the other end of the curve
|
|
|
+ iPrev = pp.length - 2;
|
|
|
+ tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ default: // ZeroCurvatureEnding
|
|
|
+
|
|
|
+ // f''(t0) = 0 a.k.a. Natural Spline
|
|
|
+ iPrev = i1;
|
|
|
+ tPrev = t1;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( tNext === undefined ) {
|
|
|
+
|
|
|
+ switch ( this.getSettings_().endingEnd ) {
|
|
|
+
|
|
|
+ case ZeroSlopeEnding:
|
|
|
+
|
|
|
+ // f'(tN) = 0
|
|
|
+ iNext = i1;
|
|
|
+ tNext = 2 * t1 - t0;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case WrapAroundEnding:
|
|
|
|
|
|
// use the other end of the curve
|
|
|
iNext = 1;
|
|
@@ -32110,745 +32142,716 @@ DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.protot
|
|
|
|
|
|
} );
|
|
|
|
|
|
-var KeyframeTrackPrototype;
|
|
|
-
|
|
|
-KeyframeTrackPrototype = {
|
|
|
-
|
|
|
- TimeBufferType: Float32Array,
|
|
|
- ValueBufferType: Float32Array,
|
|
|
+/**
|
|
|
+ * @author tschw
|
|
|
+ * @author Ben Houston / http://clara.io/
|
|
|
+ * @author David Sarno / http://lighthaus.us/
|
|
|
+ */
|
|
|
|
|
|
- DefaultInterpolation: InterpolateLinear,
|
|
|
+var AnimationUtils = {
|
|
|
|
|
|
- InterpolantFactoryMethodDiscrete: function ( result ) {
|
|
|
+ // same as Array.prototype.slice, but also works on typed arrays
|
|
|
+ arraySlice: function ( array, from, to ) {
|
|
|
|
|
|
- return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
|
|
|
+ if ( AnimationUtils.isTypedArray( array ) ) {
|
|
|
|
|
|
- },
|
|
|
+ // in ios9 array.subarray(from, undefined) will return empty array
|
|
|
+ // but array.subarray(from) or array.subarray(from, len) is correct
|
|
|
+ return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );
|
|
|
|
|
|
- InterpolantFactoryMethodLinear: function ( result ) {
|
|
|
+ }
|
|
|
|
|
|
- return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
|
|
|
+ return array.slice( from, to );
|
|
|
|
|
|
},
|
|
|
|
|
|
- InterpolantFactoryMethodSmooth: function ( result ) {
|
|
|
+ // converts an array to a specific type
|
|
|
+ convertArray: function ( array, type, forceClone ) {
|
|
|
|
|
|
- return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
|
|
|
+ if ( ! array || // let 'undefined' and 'null' pass
|
|
|
+ ! forceClone && array.constructor === type ) return array;
|
|
|
|
|
|
- },
|
|
|
+ if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {
|
|
|
|
|
|
- setInterpolation: function ( interpolation ) {
|
|
|
+ return new type( array ); // create typed array
|
|
|
|
|
|
- var factoryMethod;
|
|
|
+ }
|
|
|
|
|
|
- switch ( interpolation ) {
|
|
|
+ return Array.prototype.slice.call( array ); // create Array
|
|
|
|
|
|
- case InterpolateDiscrete:
|
|
|
+ },
|
|
|
|
|
|
- factoryMethod = this.InterpolantFactoryMethodDiscrete;
|
|
|
+ isTypedArray: function ( object ) {
|
|
|
|
|
|
- break;
|
|
|
+ return ArrayBuffer.isView( object ) &&
|
|
|
+ ! ( object instanceof DataView );
|
|
|
|
|
|
- case InterpolateLinear:
|
|
|
+ },
|
|
|
|
|
|
- factoryMethod = this.InterpolantFactoryMethodLinear;
|
|
|
+ // returns an array by which times and values can be sorted
|
|
|
+ getKeyframeOrder: function ( times ) {
|
|
|
|
|
|
- break;
|
|
|
+ function compareTime( i, j ) {
|
|
|
|
|
|
- case InterpolateSmooth:
|
|
|
+ return times[ i ] - times[ j ];
|
|
|
|
|
|
- factoryMethod = this.InterpolantFactoryMethodSmooth;
|
|
|
+ }
|
|
|
|
|
|
- break;
|
|
|
+ var n = times.length;
|
|
|
+ var result = new Array( n );
|
|
|
+ for ( var i = 0; i !== n; ++ i ) result[ i ] = i;
|
|
|
|
|
|
- }
|
|
|
+ result.sort( compareTime );
|
|
|
|
|
|
- if ( factoryMethod === undefined ) {
|
|
|
+ return result;
|
|
|
|
|
|
- var message = "unsupported interpolation for " +
|
|
|
- this.ValueTypeName + " keyframe track named " + this.name;
|
|
|
+ },
|
|
|
|
|
|
- if ( this.createInterpolant === undefined ) {
|
|
|
+ // uses the array previously returned by 'getKeyframeOrder' to sort data
|
|
|
+ sortedArray: function ( values, stride, order ) {
|
|
|
|
|
|
- // fall back to default, unless the default itself is messed up
|
|
|
- if ( interpolation !== this.DefaultInterpolation ) {
|
|
|
+ var nValues = values.length;
|
|
|
+ var result = new values.constructor( nValues );
|
|
|
|
|
|
- this.setInterpolation( this.DefaultInterpolation );
|
|
|
+ for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {
|
|
|
|
|
|
- } else {
|
|
|
+ var srcOffset = order[ i ] * stride;
|
|
|
|
|
|
- throw new Error( message ); // fatal, in this case
|
|
|
+ for ( var j = 0; j !== stride; ++ j ) {
|
|
|
|
|
|
- }
|
|
|
+ result[ dstOffset ++ ] = values[ srcOffset + j ];
|
|
|
|
|
|
}
|
|
|
|
|
|
- console.warn( 'THREE.KeyframeTrackPrototype:', message );
|
|
|
- return;
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- this.createInterpolant = factoryMethod;
|
|
|
+ return result;
|
|
|
|
|
|
},
|
|
|
|
|
|
- getInterpolation: function () {
|
|
|
+ // function for parsing AOS keyframe formats
|
|
|
+ flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {
|
|
|
|
|
|
- switch ( this.createInterpolant ) {
|
|
|
+ var i = 1, key = jsonKeys[ 0 ];
|
|
|
|
|
|
- case this.InterpolantFactoryMethodDiscrete:
|
|
|
+ while ( key !== undefined && key[ valuePropertyName ] === undefined ) {
|
|
|
|
|
|
- return InterpolateDiscrete;
|
|
|
+ key = jsonKeys[ i ++ ];
|
|
|
|
|
|
- case this.InterpolantFactoryMethodLinear:
|
|
|
+ }
|
|
|
|
|
|
- return InterpolateLinear;
|
|
|
+ if ( key === undefined ) return; // no data
|
|
|
|
|
|
- case this.InterpolantFactoryMethodSmooth:
|
|
|
+ var value = key[ valuePropertyName ];
|
|
|
+ if ( value === undefined ) return; // no data
|
|
|
|
|
|
- return InterpolateSmooth;
|
|
|
+ if ( Array.isArray( value ) ) {
|
|
|
|
|
|
- }
|
|
|
+ do {
|
|
|
|
|
|
- },
|
|
|
+ value = key[ valuePropertyName ];
|
|
|
|
|
|
- getValueSize: function () {
|
|
|
+ if ( value !== undefined ) {
|
|
|
|
|
|
- return this.values.length / this.times.length;
|
|
|
+ times.push( key.time );
|
|
|
+ values.push.apply( values, value ); // push all elements
|
|
|
|
|
|
- },
|
|
|
+ }
|
|
|
|
|
|
- // move all keyframes either forwards or backwards in time
|
|
|
- shift: function ( timeOffset ) {
|
|
|
+ key = jsonKeys[ i ++ ];
|
|
|
|
|
|
- if ( timeOffset !== 0.0 ) {
|
|
|
+ } while ( key !== undefined );
|
|
|
|
|
|
- var times = this.times;
|
|
|
+ } else if ( value.toArray !== undefined ) {
|
|
|
|
|
|
- for ( var i = 0, n = times.length; i !== n; ++ i ) {
|
|
|
+ // ...assume THREE.Math-ish
|
|
|
|
|
|
- times[ i ] += timeOffset;
|
|
|
+ do {
|
|
|
|
|
|
- }
|
|
|
+ value = key[ valuePropertyName ];
|
|
|
|
|
|
- }
|
|
|
+ if ( value !== undefined ) {
|
|
|
|
|
|
- return this;
|
|
|
+ times.push( key.time );
|
|
|
+ value.toArray( values, values.length );
|
|
|
|
|
|
- },
|
|
|
+ }
|
|
|
|
|
|
- // scale all keyframe times by a factor (useful for frame <-> seconds conversions)
|
|
|
- scale: function ( timeScale ) {
|
|
|
+ key = jsonKeys[ i ++ ];
|
|
|
|
|
|
- if ( timeScale !== 1.0 ) {
|
|
|
+ } while ( key !== undefined );
|
|
|
|
|
|
- var times = this.times;
|
|
|
+ } else {
|
|
|
|
|
|
- for ( var i = 0, n = times.length; i !== n; ++ i ) {
|
|
|
+ // otherwise push as-is
|
|
|
|
|
|
- times[ i ] *= timeScale;
|
|
|
+ do {
|
|
|
|
|
|
- }
|
|
|
+ value = key[ valuePropertyName ];
|
|
|
|
|
|
- }
|
|
|
+ if ( value !== undefined ) {
|
|
|
|
|
|
- return this;
|
|
|
+ times.push( key.time );
|
|
|
+ values.push( value );
|
|
|
|
|
|
- },
|
|
|
+ }
|
|
|
|
|
|
- // removes keyframes before and after animation without changing any values within the range [startTime, endTime].
|
|
|
- // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
|
|
|
- trim: function ( startTime, endTime ) {
|
|
|
+ key = jsonKeys[ i ++ ];
|
|
|
|
|
|
- var times = this.times,
|
|
|
- nKeys = times.length,
|
|
|
- from = 0,
|
|
|
- to = nKeys - 1;
|
|
|
+ } while ( key !== undefined );
|
|
|
|
|
|
- while ( from !== nKeys && times[ from ] < startTime ) ++ from;
|
|
|
- while ( to !== - 1 && times[ to ] > endTime ) -- to;
|
|
|
+ }
|
|
|
|
|
|
- ++ to; // inclusive -> exclusive bound
|
|
|
+ }
|
|
|
|
|
|
- if ( from !== 0 || to !== nKeys ) {
|
|
|
+};
|
|
|
|
|
|
- // empty tracks are forbidden, so keep at least one keyframe
|
|
|
- if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * A timed sequence of keyframes for a specific property.
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * @author Ben Houston / http://clara.io/
|
|
|
+ * @author David Sarno / http://lighthaus.us/
|
|
|
+ * @author tschw
|
|
|
+ */
|
|
|
|
|
|
- var stride = this.getValueSize();
|
|
|
- this.times = AnimationUtils.arraySlice( times, from, to );
|
|
|
- this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
|
|
|
+function KeyframeTrack( name, times, values, interpolation ) {
|
|
|
|
|
|
- }
|
|
|
+ if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' );
|
|
|
+ if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name );
|
|
|
|
|
|
- return this;
|
|
|
+ this.name = name;
|
|
|
|
|
|
- },
|
|
|
+ this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
|
|
|
+ this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
|
|
|
|
|
|
- // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
|
|
|
- validate: function () {
|
|
|
+ this.setInterpolation( interpolation || this.DefaultInterpolation );
|
|
|
|
|
|
- var valid = true;
|
|
|
+ this.validate();
|
|
|
+ this.optimize();
|
|
|
|
|
|
- var valueSize = this.getValueSize();
|
|
|
- if ( valueSize - Math.floor( valueSize ) !== 0 ) {
|
|
|
+}
|
|
|
|
|
|
- console.error( 'THREE.KeyframeTrackPrototype: Invalid value size in track.', this );
|
|
|
- valid = false;
|
|
|
+// Static methods:
|
|
|
|
|
|
- }
|
|
|
+Object.assign( KeyframeTrack, {
|
|
|
|
|
|
- var times = this.times,
|
|
|
- values = this.values,
|
|
|
+ // Serialization (in static context, because of constructor invocation
|
|
|
+ // and automatic invocation of .toJSON):
|
|
|
|
|
|
- nKeys = times.length;
|
|
|
+ parse: function ( json ) {
|
|
|
|
|
|
- if ( nKeys === 0 ) {
|
|
|
+ if ( json.type === undefined ) {
|
|
|
|
|
|
- console.error( 'THREE.KeyframeTrackPrototype: Track is empty.', this );
|
|
|
- valid = false;
|
|
|
+ throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' );
|
|
|
|
|
|
}
|
|
|
|
|
|
- var prevTime = null;
|
|
|
+ var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type );
|
|
|
|
|
|
- for ( var i = 0; i !== nKeys; i ++ ) {
|
|
|
+ if ( json.times === undefined ) {
|
|
|
|
|
|
- var currTime = times[ i ];
|
|
|
+ var times = [], values = [];
|
|
|
|
|
|
- if ( typeof currTime === 'number' && isNaN( currTime ) ) {
|
|
|
+ AnimationUtils.flattenJSON( json.keys, times, values, 'value' );
|
|
|
|
|
|
- console.error( 'THREE.KeyframeTrackPrototype: Time is not a valid number.', this, i, currTime );
|
|
|
- valid = false;
|
|
|
- break;
|
|
|
+ json.times = times;
|
|
|
+ json.values = values;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- if ( prevTime !== null && prevTime > currTime ) {
|
|
|
+ // derived classes can define a static parse method
|
|
|
+ if ( trackType.parse !== undefined ) {
|
|
|
|
|
|
- console.error( 'THREE.KeyframeTrackPrototype: Out of order keys.', this, i, currTime, prevTime );
|
|
|
- valid = false;
|
|
|
- break;
|
|
|
+ return trackType.parse( json );
|
|
|
|
|
|
- }
|
|
|
+ } else {
|
|
|
|
|
|
- prevTime = currTime;
|
|
|
+ // by default, we assume a constructor compatible with the base
|
|
|
+ return new trackType( json.name, json.times, json.values, json.interpolation );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( values !== undefined ) {
|
|
|
+ },
|
|
|
|
|
|
- if ( AnimationUtils.isTypedArray( values ) ) {
|
|
|
+ toJSON: function ( track ) {
|
|
|
|
|
|
- for ( var i = 0, n = values.length; i !== n; ++ i ) {
|
|
|
+ var trackType = track.constructor;
|
|
|
|
|
|
- var value = values[ i ];
|
|
|
+ var json;
|
|
|
|
|
|
- if ( isNaN( value ) ) {
|
|
|
+ // derived classes can define a static toJSON method
|
|
|
+ if ( trackType.toJSON !== undefined ) {
|
|
|
|
|
|
- console.error( 'THREE.KeyframeTrackPrototype: Value is not a valid number.', this, i, value );
|
|
|
- valid = false;
|
|
|
- break;
|
|
|
+ json = trackType.toJSON( track );
|
|
|
|
|
|
- }
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ // by default, we assume the data can be serialized as-is
|
|
|
+ json = {
|
|
|
|
|
|
- }
|
|
|
+ 'name': track.name,
|
|
|
+ 'times': AnimationUtils.convertArray( track.times, Array ),
|
|
|
+ 'values': AnimationUtils.convertArray( track.values, Array )
|
|
|
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
- return valid;
|
|
|
+ var interpolation = track.getInterpolation();
|
|
|
|
|
|
- },
|
|
|
+ if ( interpolation !== track.DefaultInterpolation ) {
|
|
|
|
|
|
- // removes equivalent sequential keys as common in morph target sequences
|
|
|
- // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
|
|
|
- optimize: function () {
|
|
|
+ json.interpolation = interpolation;
|
|
|
|
|
|
- var times = this.times,
|
|
|
- values = this.values,
|
|
|
- stride = this.getValueSize(),
|
|
|
+ }
|
|
|
|
|
|
- smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
|
|
|
+ }
|
|
|
|
|
|
- writeIndex = 1,
|
|
|
- lastIndex = times.length - 1;
|
|
|
+ json.type = track.ValueTypeName; // mandatory
|
|
|
|
|
|
- for ( var i = 1; i < lastIndex; ++ i ) {
|
|
|
+ return json;
|
|
|
|
|
|
- var keep = false;
|
|
|
+ },
|
|
|
|
|
|
- var time = times[ i ];
|
|
|
- var timeNext = times[ i + 1 ];
|
|
|
+ _getTrackTypeForValueTypeName: function ( typeName ) {
|
|
|
|
|
|
- // remove adjacent keyframes scheduled at the same time
|
|
|
+ switch ( typeName.toLowerCase() ) {
|
|
|
|
|
|
- if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
|
|
|
+ case 'scalar':
|
|
|
+ case 'double':
|
|
|
+ case 'float':
|
|
|
+ case 'number':
|
|
|
+ case 'integer':
|
|
|
|
|
|
- if ( ! smoothInterpolation ) {
|
|
|
+ return NumberKeyframeTrack;
|
|
|
|
|
|
- // remove unnecessary keyframes same as their neighbors
|
|
|
+ case 'vector':
|
|
|
+ case 'vector2':
|
|
|
+ case 'vector3':
|
|
|
+ case 'vector4':
|
|
|
|
|
|
- var offset = i * stride,
|
|
|
- offsetP = offset - stride,
|
|
|
- offsetN = offset + stride;
|
|
|
+ return VectorKeyframeTrack;
|
|
|
|
|
|
- for ( var j = 0; j !== stride; ++ j ) {
|
|
|
+ case 'color':
|
|
|
|
|
|
- var value = values[ offset + j ];
|
|
|
+ return ColorKeyframeTrack;
|
|
|
|
|
|
- if ( value !== values[ offsetP + j ] ||
|
|
|
- value !== values[ offsetN + j ] ) {
|
|
|
+ case 'quaternion':
|
|
|
|
|
|
- keep = true;
|
|
|
- break;
|
|
|
+ return QuaternionKeyframeTrack;
|
|
|
|
|
|
- }
|
|
|
+ case 'bool':
|
|
|
+ case 'boolean':
|
|
|
|
|
|
- }
|
|
|
+ return BooleanKeyframeTrack;
|
|
|
|
|
|
- } else keep = true;
|
|
|
+ case 'string':
|
|
|
|
|
|
- }
|
|
|
+ return StringKeyframeTrack;
|
|
|
|
|
|
- // in-place compaction
|
|
|
+ }
|
|
|
|
|
|
- if ( keep ) {
|
|
|
+ throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName );
|
|
|
|
|
|
- if ( i !== writeIndex ) {
|
|
|
+ }
|
|
|
|
|
|
- times[ writeIndex ] = times[ i ];
|
|
|
+} );
|
|
|
|
|
|
- var readOffset = i * stride,
|
|
|
- writeOffset = writeIndex * stride;
|
|
|
+Object.assign( KeyframeTrack.prototype, {
|
|
|
|
|
|
- for ( var j = 0; j !== stride; ++ j )
|
|
|
+ constructor: KeyframeTrack,
|
|
|
|
|
|
- values[ writeOffset + j ] = values[ readOffset + j ];
|
|
|
+ TimeBufferType: Float32Array,
|
|
|
|
|
|
- }
|
|
|
+ ValueBufferType: Float32Array,
|
|
|
|
|
|
- ++ writeIndex;
|
|
|
+ DefaultInterpolation: InterpolateLinear,
|
|
|
|
|
|
- }
|
|
|
+ InterpolantFactoryMethodDiscrete: function ( result ) {
|
|
|
|
|
|
- }
|
|
|
+ return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
|
|
|
|
|
|
- // flush last keyframe (compaction looks ahead)
|
|
|
+ },
|
|
|
|
|
|
- if ( lastIndex > 0 ) {
|
|
|
+ InterpolantFactoryMethodLinear: function ( result ) {
|
|
|
|
|
|
- times[ writeIndex ] = times[ lastIndex ];
|
|
|
+ return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
|
|
|
|
|
|
- for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j )
|
|
|
+ },
|
|
|
|
|
|
- values[ writeOffset + j ] = values[ readOffset + j ];
|
|
|
+ InterpolantFactoryMethodSmooth: function ( result ) {
|
|
|
|
|
|
- ++ writeIndex;
|
|
|
+ return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- if ( writeIndex !== times.length ) {
|
|
|
+ setInterpolation: function ( interpolation ) {
|
|
|
|
|
|
- this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
|
|
|
- this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
|
|
|
+ var factoryMethod;
|
|
|
|
|
|
- }
|
|
|
+ switch ( interpolation ) {
|
|
|
|
|
|
- return this;
|
|
|
+ case InterpolateDiscrete:
|
|
|
|
|
|
- }
|
|
|
+ factoryMethod = this.InterpolantFactoryMethodDiscrete;
|
|
|
|
|
|
-};
|
|
|
+ break;
|
|
|
|
|
|
-function KeyframeTrackConstructor( name, times, values, interpolation ) {
|
|
|
+ case InterpolateLinear:
|
|
|
|
|
|
- if ( name === undefined ) throw new Error( 'track name is undefined' );
|
|
|
+ factoryMethod = this.InterpolantFactoryMethodLinear;
|
|
|
|
|
|
- if ( times === undefined || times.length === 0 ) {
|
|
|
+ break;
|
|
|
|
|
|
- throw new Error( 'no keyframes in track named ' + name );
|
|
|
+ case InterpolateSmooth:
|
|
|
|
|
|
- }
|
|
|
+ factoryMethod = this.InterpolantFactoryMethodSmooth;
|
|
|
|
|
|
- this.name = name;
|
|
|
+ break;
|
|
|
|
|
|
- this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
|
|
|
- this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
|
|
|
+ }
|
|
|
|
|
|
- this.setInterpolation( interpolation || this.DefaultInterpolation );
|
|
|
+ if ( factoryMethod === undefined ) {
|
|
|
|
|
|
- this.validate();
|
|
|
- this.optimize();
|
|
|
+ var message = "unsupported interpolation for " +
|
|
|
+ this.ValueTypeName + " keyframe track named " + this.name;
|
|
|
|
|
|
-}
|
|
|
+ if ( this.createInterpolant === undefined ) {
|
|
|
|
|
|
-/**
|
|
|
- *
|
|
|
- * A Track of vectored keyframe values.
|
|
|
- *
|
|
|
- *
|
|
|
- * @author Ben Houston / http://clara.io/
|
|
|
- * @author David Sarno / http://lighthaus.us/
|
|
|
- * @author tschw
|
|
|
- */
|
|
|
+ // fall back to default, unless the default itself is messed up
|
|
|
+ if ( interpolation !== this.DefaultInterpolation ) {
|
|
|
|
|
|
-function VectorKeyframeTrack( name, times, values, interpolation ) {
|
|
|
+ this.setInterpolation( this.DefaultInterpolation );
|
|
|
|
|
|
- KeyframeTrackConstructor.call( this, name, times, values, interpolation );
|
|
|
+ } else {
|
|
|
|
|
|
-}
|
|
|
+ throw new Error( message ); // fatal, in this case
|
|
|
|
|
|
-VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
|
|
|
+ }
|
|
|
|
|
|
- constructor: VectorKeyframeTrack,
|
|
|
+ }
|
|
|
|
|
|
- ValueTypeName: 'vector'
|
|
|
+ console.warn( 'THREE.KeyframeTrack:', message );
|
|
|
+ return;
|
|
|
|
|
|
- // ValueBufferType is inherited
|
|
|
+ }
|
|
|
|
|
|
- // DefaultInterpolation is inherited
|
|
|
+ this.createInterpolant = factoryMethod;
|
|
|
|
|
|
-} );
|
|
|
+ },
|
|
|
|
|
|
-/**
|
|
|
- * Spherical linear unit quaternion interpolant.
|
|
|
- *
|
|
|
- * @author tschw
|
|
|
- */
|
|
|
+ getInterpolation: function () {
|
|
|
|
|
|
-function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
|
|
|
+ switch ( this.createInterpolant ) {
|
|
|
|
|
|
- Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
|
|
|
+ case this.InterpolantFactoryMethodDiscrete:
|
|
|
|
|
|
-}
|
|
|
+ return InterpolateDiscrete;
|
|
|
|
|
|
-QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
|
|
|
+ case this.InterpolantFactoryMethodLinear:
|
|
|
|
|
|
- constructor: QuaternionLinearInterpolant,
|
|
|
+ return InterpolateLinear;
|
|
|
|
|
|
- interpolate_: function ( i1, t0, t, t1 ) {
|
|
|
+ case this.InterpolantFactoryMethodSmooth:
|
|
|
|
|
|
- var result = this.resultBuffer,
|
|
|
- values = this.sampleValues,
|
|
|
- stride = this.valueSize,
|
|
|
+ return InterpolateSmooth;
|
|
|
|
|
|
- offset = i1 * stride,
|
|
|
+ }
|
|
|
|
|
|
- alpha = ( t - t0 ) / ( t1 - t0 );
|
|
|
+ },
|
|
|
|
|
|
- for ( var end = offset + stride; offset !== end; offset += 4 ) {
|
|
|
+ getValueSize: function () {
|
|
|
|
|
|
- Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );
|
|
|
+ return this.values.length / this.times.length;
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- return result;
|
|
|
+ // move all keyframes either forwards or backwards in time
|
|
|
+ shift: function ( timeOffset ) {
|
|
|
|
|
|
- }
|
|
|
+ if ( timeOffset !== 0.0 ) {
|
|
|
|
|
|
-} );
|
|
|
+ var times = this.times;
|
|
|
|
|
|
-/**
|
|
|
- *
|
|
|
- * A Track of quaternion keyframe values.
|
|
|
- *
|
|
|
- * @author Ben Houston / http://clara.io/
|
|
|
- * @author David Sarno / http://lighthaus.us/
|
|
|
- * @author tschw
|
|
|
- */
|
|
|
+ for ( var i = 0, n = times.length; i !== n; ++ i ) {
|
|
|
|
|
|
-function QuaternionKeyframeTrack( name, times, values, interpolation ) {
|
|
|
+ times[ i ] += timeOffset;
|
|
|
|
|
|
- KeyframeTrackConstructor.call( this, name, times, values, interpolation );
|
|
|
+ }
|
|
|
|
|
|
-}
|
|
|
+ }
|
|
|
|
|
|
-QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
|
|
|
+ return this;
|
|
|
|
|
|
- constructor: QuaternionKeyframeTrack,
|
|
|
+ },
|
|
|
|
|
|
- ValueTypeName: 'quaternion',
|
|
|
+ // scale all keyframe times by a factor (useful for frame <-> seconds conversions)
|
|
|
+ scale: function ( timeScale ) {
|
|
|
|
|
|
- // ValueBufferType is inherited
|
|
|
+ if ( timeScale !== 1.0 ) {
|
|
|
|
|
|
- DefaultInterpolation: InterpolateLinear,
|
|
|
+ var times = this.times;
|
|
|
|
|
|
- InterpolantFactoryMethodLinear: function ( result ) {
|
|
|
+ for ( var i = 0, n = times.length; i !== n; ++ i ) {
|
|
|
|
|
|
- return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );
|
|
|
+ times[ i ] *= timeScale;
|
|
|
|
|
|
- },
|
|
|
+ }
|
|
|
|
|
|
- InterpolantFactoryMethodSmooth: undefined // not yet implemented
|
|
|
+ }
|
|
|
|
|
|
-} );
|
|
|
+ return this;
|
|
|
|
|
|
-/**
|
|
|
- *
|
|
|
- * A Track of numeric keyframe values.
|
|
|
- *
|
|
|
- * @author Ben Houston / http://clara.io/
|
|
|
- * @author David Sarno / http://lighthaus.us/
|
|
|
- * @author tschw
|
|
|
- */
|
|
|
+ },
|
|
|
|
|
|
-function NumberKeyframeTrack( name, times, values, interpolation ) {
|
|
|
+ // removes keyframes before and after animation without changing any values within the range [startTime, endTime].
|
|
|
+ // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
|
|
|
+ trim: function ( startTime, endTime ) {
|
|
|
|
|
|
- KeyframeTrackConstructor.call( this, name, times, values, interpolation );
|
|
|
+ var times = this.times,
|
|
|
+ nKeys = times.length,
|
|
|
+ from = 0,
|
|
|
+ to = nKeys - 1;
|
|
|
|
|
|
-}
|
|
|
+ while ( from !== nKeys && times[ from ] < startTime ) {
|
|
|
|
|
|
-NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
|
|
|
+ ++ from;
|
|
|
|
|
|
- constructor: NumberKeyframeTrack,
|
|
|
+ }
|
|
|
|
|
|
- ValueTypeName: 'number'
|
|
|
+ while ( to !== - 1 && times[ to ] > endTime ) {
|
|
|
|
|
|
- // ValueBufferType is inherited
|
|
|
+ -- to;
|
|
|
|
|
|
- // DefaultInterpolation is inherited
|
|
|
+ }
|
|
|
|
|
|
-} );
|
|
|
+ ++ to; // inclusive -> exclusive bound
|
|
|
|
|
|
-/**
|
|
|
- *
|
|
|
- * A Track that interpolates Strings
|
|
|
- *
|
|
|
- *
|
|
|
- * @author Ben Houston / http://clara.io/
|
|
|
- * @author David Sarno / http://lighthaus.us/
|
|
|
- * @author tschw
|
|
|
- */
|
|
|
+ if ( from !== 0 || to !== nKeys ) {
|
|
|
|
|
|
-function StringKeyframeTrack( name, times, values, interpolation ) {
|
|
|
+ // empty tracks are forbidden, so keep at least one keyframe
|
|
|
+ if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;
|
|
|
|
|
|
- KeyframeTrackConstructor.call( this, name, times, values, interpolation );
|
|
|
+ var stride = this.getValueSize();
|
|
|
+ this.times = AnimationUtils.arraySlice( times, from, to );
|
|
|
+ this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
|
|
|
|
|
|
-}
|
|
|
+ }
|
|
|
|
|
|
-StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
|
|
|
+ return this;
|
|
|
|
|
|
- constructor: StringKeyframeTrack,
|
|
|
+ },
|
|
|
|
|
|
- ValueTypeName: 'string',
|
|
|
- ValueBufferType: Array,
|
|
|
+ // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
|
|
|
+ validate: function () {
|
|
|
|
|
|
- DefaultInterpolation: InterpolateDiscrete,
|
|
|
+ var valid = true;
|
|
|
|
|
|
- InterpolantFactoryMethodLinear: undefined,
|
|
|
+ var valueSize = this.getValueSize();
|
|
|
+ if ( valueSize - Math.floor( valueSize ) !== 0 ) {
|
|
|
|
|
|
- InterpolantFactoryMethodSmooth: undefined
|
|
|
+ console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this );
|
|
|
+ valid = false;
|
|
|
|
|
|
-} );
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- *
|
|
|
- * A Track of Boolean keyframe values.
|
|
|
- *
|
|
|
- *
|
|
|
- * @author Ben Houston / http://clara.io/
|
|
|
- * @author David Sarno / http://lighthaus.us/
|
|
|
- * @author tschw
|
|
|
- */
|
|
|
+ var times = this.times,
|
|
|
+ values = this.values,
|
|
|
|
|
|
-function BooleanKeyframeTrack( name, times, values ) {
|
|
|
+ nKeys = times.length;
|
|
|
|
|
|
- KeyframeTrackConstructor.call( this, name, times, values );
|
|
|
+ if ( nKeys === 0 ) {
|
|
|
|
|
|
-}
|
|
|
+ console.error( 'THREE.KeyframeTrack: Track is empty.', this );
|
|
|
+ valid = false;
|
|
|
|
|
|
-BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
|
|
|
+ }
|
|
|
|
|
|
- constructor: BooleanKeyframeTrack,
|
|
|
+ var prevTime = null;
|
|
|
|
|
|
- ValueTypeName: 'bool',
|
|
|
- ValueBufferType: Array,
|
|
|
+ for ( var i = 0; i !== nKeys; i ++ ) {
|
|
|
|
|
|
- DefaultInterpolation: InterpolateDiscrete,
|
|
|
+ var currTime = times[ i ];
|
|
|
|
|
|
- InterpolantFactoryMethodLinear: undefined,
|
|
|
- InterpolantFactoryMethodSmooth: undefined
|
|
|
+ if ( typeof currTime === 'number' && isNaN( currTime ) ) {
|
|
|
|
|
|
- // Note: Actually this track could have a optimized / compressed
|
|
|
- // representation of a single value and a custom interpolant that
|
|
|
- // computes "firstValue ^ isOdd( index )".
|
|
|
+ console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime );
|
|
|
+ valid = false;
|
|
|
+ break;
|
|
|
|
|
|
-} );
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- *
|
|
|
- * A Track of keyframe values that represent color.
|
|
|
- *
|
|
|
- *
|
|
|
- * @author Ben Houston / http://clara.io/
|
|
|
- * @author David Sarno / http://lighthaus.us/
|
|
|
- * @author tschw
|
|
|
- */
|
|
|
+ if ( prevTime !== null && prevTime > currTime ) {
|
|
|
|
|
|
-function ColorKeyframeTrack( name, times, values, interpolation ) {
|
|
|
+ console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime );
|
|
|
+ valid = false;
|
|
|
+ break;
|
|
|
|
|
|
- KeyframeTrackConstructor.call( this, name, times, values, interpolation );
|
|
|
+ }
|
|
|
|
|
|
-}
|
|
|
+ prevTime = currTime;
|
|
|
|
|
|
-ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
|
|
|
+ }
|
|
|
|
|
|
- constructor: ColorKeyframeTrack,
|
|
|
+ if ( values !== undefined ) {
|
|
|
|
|
|
- ValueTypeName: 'color'
|
|
|
+ if ( AnimationUtils.isTypedArray( values ) ) {
|
|
|
|
|
|
- // ValueBufferType is inherited
|
|
|
+ for ( var i = 0, n = values.length; i !== n; ++ i ) {
|
|
|
|
|
|
- // DefaultInterpolation is inherited
|
|
|
+ var value = values[ i ];
|
|
|
|
|
|
+ if ( isNaN( value ) ) {
|
|
|
|
|
|
- // Note: Very basic implementation and nothing special yet.
|
|
|
- // However, this is the place for color space parameterization.
|
|
|
+ console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value );
|
|
|
+ valid = false;
|
|
|
+ break;
|
|
|
|
|
|
-} );
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- *
|
|
|
- * A timed sequence of keyframes for a specific property.
|
|
|
- *
|
|
|
- *
|
|
|
- * @author Ben Houston / http://clara.io/
|
|
|
- * @author David Sarno / http://lighthaus.us/
|
|
|
- * @author tschw
|
|
|
- */
|
|
|
+ }
|
|
|
|
|
|
-function KeyframeTrack( name, times, values, interpolation ) {
|
|
|
+ }
|
|
|
|
|
|
- KeyframeTrackConstructor.apply( this, name, times, values, interpolation );
|
|
|
+ }
|
|
|
|
|
|
-}
|
|
|
+ return valid;
|
|
|
|
|
|
-KeyframeTrack.prototype = KeyframeTrackPrototype;
|
|
|
-KeyframeTrackPrototype.constructor = KeyframeTrack;
|
|
|
+ },
|
|
|
|
|
|
-// Static methods:
|
|
|
+ // removes equivalent sequential keys as common in morph target sequences
|
|
|
+ // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
|
|
|
+ optimize: function () {
|
|
|
|
|
|
-Object.assign( KeyframeTrack, {
|
|
|
+ var times = this.times,
|
|
|
+ values = this.values,
|
|
|
+ stride = this.getValueSize(),
|
|
|
|
|
|
- // Serialization (in static context, because of constructor invocation
|
|
|
- // and automatic invocation of .toJSON):
|
|
|
+ smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
|
|
|
|
|
|
- parse: function ( json ) {
|
|
|
+ writeIndex = 1,
|
|
|
+ lastIndex = times.length - 1;
|
|
|
|
|
|
- if ( json.type === undefined ) {
|
|
|
+ for ( var i = 1; i < lastIndex; ++ i ) {
|
|
|
|
|
|
- throw new Error( 'track type undefined, can not parse' );
|
|
|
+ var keep = false;
|
|
|
|
|
|
- }
|
|
|
+ var time = times[ i ];
|
|
|
+ var timeNext = times[ i + 1 ];
|
|
|
|
|
|
- var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type );
|
|
|
+ // remove adjacent keyframes scheduled at the same time
|
|
|
|
|
|
- if ( json.times === undefined ) {
|
|
|
+ if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
|
|
|
|
|
|
- var times = [], values = [];
|
|
|
+ if ( ! smoothInterpolation ) {
|
|
|
|
|
|
- AnimationUtils.flattenJSON( json.keys, times, values, 'value' );
|
|
|
+ // remove unnecessary keyframes same as their neighbors
|
|
|
|
|
|
- json.times = times;
|
|
|
- json.values = values;
|
|
|
+ var offset = i * stride,
|
|
|
+ offsetP = offset - stride,
|
|
|
+ offsetN = offset + stride;
|
|
|
|
|
|
- }
|
|
|
+ for ( var j = 0; j !== stride; ++ j ) {
|
|
|
|
|
|
- // derived classes can define a static parse method
|
|
|
- if ( trackType.parse !== undefined ) {
|
|
|
+ var value = values[ offset + j ];
|
|
|
|
|
|
- return trackType.parse( json );
|
|
|
+ if ( value !== values[ offsetP + j ] ||
|
|
|
+ value !== values[ offsetN + j ] ) {
|
|
|
|
|
|
- } else {
|
|
|
+ keep = true;
|
|
|
+ break;
|
|
|
|
|
|
- // by default, we assume a constructor compatible with the base
|
|
|
- return new trackType( json.name, json.times, json.values, json.interpolation );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- },
|
|
|
+ } else {
|
|
|
|
|
|
- toJSON: function ( track ) {
|
|
|
+ keep = true;
|
|
|
|
|
|
- var trackType = track.constructor;
|
|
|
+ }
|
|
|
|
|
|
- var json;
|
|
|
+ }
|
|
|
|
|
|
- // derived classes can define a static toJSON method
|
|
|
- if ( trackType.toJSON !== undefined ) {
|
|
|
+ // in-place compaction
|
|
|
|
|
|
- json = trackType.toJSON( track );
|
|
|
+ if ( keep ) {
|
|
|
|
|
|
- } else {
|
|
|
+ if ( i !== writeIndex ) {
|
|
|
|
|
|
- // by default, we assume the data can be serialized as-is
|
|
|
- json = {
|
|
|
+ times[ writeIndex ] = times[ i ];
|
|
|
|
|
|
- 'name': track.name,
|
|
|
- 'times': AnimationUtils.convertArray( track.times, Array ),
|
|
|
- 'values': AnimationUtils.convertArray( track.values, Array )
|
|
|
+ var readOffset = i * stride,
|
|
|
+ writeOffset = writeIndex * stride;
|
|
|
|
|
|
- };
|
|
|
+ for ( var j = 0; j !== stride; ++ j ) {
|
|
|
|
|
|
- var interpolation = track.getInterpolation();
|
|
|
+ values[ writeOffset + j ] = values[ readOffset + j ];
|
|
|
|
|
|
- if ( interpolation !== track.DefaultInterpolation ) {
|
|
|
+ }
|
|
|
|
|
|
- json.interpolation = interpolation;
|
|
|
+ }
|
|
|
+
|
|
|
+ ++ writeIndex;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- json.type = track.ValueTypeName; // mandatory
|
|
|
+ // flush last keyframe (compaction looks ahead)
|
|
|
|
|
|
- return json;
|
|
|
+ if ( lastIndex > 0 ) {
|
|
|
|
|
|
- },
|
|
|
+ times[ writeIndex ] = times[ lastIndex ];
|
|
|
|
|
|
- _getTrackTypeForValueTypeName: function ( typeName ) {
|
|
|
+ for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) {
|
|
|
|
|
|
- switch ( typeName.toLowerCase() ) {
|
|
|
+ values[ writeOffset + j ] = values[ readOffset + j ];
|
|
|
|
|
|
- case 'scalar':
|
|
|
- case 'double':
|
|
|
- case 'float':
|
|
|
- case 'number':
|
|
|
- case 'integer':
|
|
|
+ }
|
|
|
|
|
|
- return NumberKeyframeTrack;
|
|
|
+ ++ writeIndex;
|
|
|
|
|
|
- case 'vector':
|
|
|
- case 'vector2':
|
|
|
- case 'vector3':
|
|
|
- case 'vector4':
|
|
|
+ }
|
|
|
|
|
|
- return VectorKeyframeTrack;
|
|
|
+ if ( writeIndex !== times.length ) {
|
|
|
|
|
|
- case 'color':
|
|
|
+ this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
|
|
|
+ this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
|
|
|
|
|
|
- return ColorKeyframeTrack;
|
|
|
+ }
|
|
|
|
|
|
- case 'quaternion':
|
|
|
+ return this;
|
|
|
|
|
|
- return QuaternionKeyframeTrack;
|
|
|
+ }
|
|
|
|
|
|
- case 'bool':
|
|
|
- case 'boolean':
|
|
|
+} );
|
|
|
|
|
|
- return BooleanKeyframeTrack;
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * A Track of vectored keyframe values.
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * @author Ben Houston / http://clara.io/
|
|
|
+ * @author David Sarno / http://lighthaus.us/
|
|
|
+ * @author tschw
|
|
|
+ */
|
|
|
|
|
|
- case 'string':
|
|
|
+function VectorKeyframeTrack( name, times, values, interpolation ) {
|
|
|
|
|
|
- return StringKeyframeTrack;
|
|
|
+ KeyframeTrack.call( this, name, times, values, interpolation );
|
|
|
|
|
|
- }
|
|
|
+}
|
|
|
|
|
|
- throw new Error( 'Unsupported typeName: ' + typeName );
|
|
|
+VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
|
|
|
|
|
|
- }
|
|
|
+ constructor: VectorKeyframeTrack,
|
|
|
+
|
|
|
+ ValueTypeName: 'vector'
|
|
|
+
|
|
|
+ // ValueBufferType is inherited
|
|
|
+
|
|
|
+ // DefaultInterpolation is inherited
|
|
|
|
|
|
} );
|
|
|
|