|
@@ -2731,13 +2731,11 @@ class AnimationParser {
|
|
|
const tracks = [];
|
|
|
|
|
|
let initialPosition = new Vector3();
|
|
|
- let initialRotation = new Quaternion();
|
|
|
let initialScale = new Vector3();
|
|
|
|
|
|
- if ( rawTracks.transform ) rawTracks.transform.decompose( initialPosition, initialRotation, initialScale );
|
|
|
+ if ( rawTracks.transform ) rawTracks.transform.decompose( initialPosition, new Quaternion(), initialScale );
|
|
|
|
|
|
initialPosition = initialPosition.toArray();
|
|
|
- initialRotation = new Euler().setFromQuaternion( initialRotation, rawTracks.eulerOrder ).toArray();
|
|
|
initialScale = initialScale.toArray();
|
|
|
|
|
|
if ( rawTracks.T !== undefined && Object.keys( rawTracks.T.curves ).length > 0 ) {
|
|
@@ -2749,7 +2747,7 @@ class AnimationParser {
|
|
|
|
|
|
if ( rawTracks.R !== undefined && Object.keys( rawTracks.R.curves ).length > 0 ) {
|
|
|
|
|
|
- const rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, initialRotation, rawTracks.preRotation, rawTracks.postRotation, rawTracks.eulerOrder );
|
|
|
+ const rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, rawTracks.preRotation, rawTracks.postRotation, rawTracks.eulerOrder );
|
|
|
if ( rotationTrack !== undefined ) tracks.push( rotationTrack );
|
|
|
|
|
|
}
|
|
@@ -2781,32 +2779,20 @@ class AnimationParser {
|
|
|
|
|
|
}
|
|
|
|
|
|
- generateRotationTrack( modelName, curves, initialValue, preRotation, postRotation, eulerOrder ) {
|
|
|
+ generateRotationTrack( modelName, curves, preRotation, postRotation, eulerOrder ) {
|
|
|
|
|
|
- if ( curves.x !== undefined ) {
|
|
|
+ let times;
|
|
|
+ let values;
|
|
|
|
|
|
- this.interpolateRotations( curves.x );
|
|
|
- curves.x.values = curves.x.values.map( MathUtils.degToRad );
|
|
|
+ if ( curves.x !== undefined && curves.y !== undefined && curves.z !== undefined ) {
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- if ( curves.y !== undefined ) {
|
|
|
-
|
|
|
- this.interpolateRotations( curves.y );
|
|
|
- curves.y.values = curves.y.values.map( MathUtils.degToRad );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( curves.z !== undefined ) {
|
|
|
+ const result = this.interpolateRotations( curves.x, curves.y, curves.z, eulerOrder );
|
|
|
|
|
|
- this.interpolateRotations( curves.z );
|
|
|
- curves.z.values = curves.z.values.map( MathUtils.degToRad );
|
|
|
+ times = result[ 0 ];
|
|
|
+ values = result[ 1 ];
|
|
|
|
|
|
}
|
|
|
|
|
|
- const times = this.getTimesForAllAxes( curves );
|
|
|
- const values = this.getKeyframeTrackValues( times, curves, initialValue );
|
|
|
-
|
|
|
if ( preRotation !== undefined ) {
|
|
|
|
|
|
preRotation = preRotation.map( MathUtils.degToRad );
|
|
@@ -2832,15 +2818,32 @@ class AnimationParser {
|
|
|
|
|
|
const quaternionValues = [];
|
|
|
|
|
|
+ if ( ! values || ! times ) return new QuaternionKeyframeTrack( modelName + '.quaternion', [], [] );
|
|
|
+
|
|
|
for ( let i = 0; i < values.length; i += 3 ) {
|
|
|
|
|
|
euler.set( values[ i ], values[ i + 1 ], values[ i + 2 ], eulerOrder );
|
|
|
-
|
|
|
quaternion.setFromEuler( euler );
|
|
|
|
|
|
if ( preRotation !== undefined ) quaternion.premultiply( preRotation );
|
|
|
if ( postRotation !== undefined ) quaternion.multiply( postRotation );
|
|
|
|
|
|
+ // Check unroll
|
|
|
+ if ( i > 2 ) {
|
|
|
+
|
|
|
+ const prevQuat = new Quaternion().fromArray(
|
|
|
+ quaternionValues,
|
|
|
+ ( ( i - 3 ) / 3 ) * 4
|
|
|
+ );
|
|
|
+
|
|
|
+ if ( prevQuat.dot( quaternion ) < 0 ) {
|
|
|
+
|
|
|
+ quaternion.set( - quaternion.x, - quaternion.y, - quaternion.z, - quaternion.w );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
quaternion.toArray( quaternionValues, ( i / 3 ) * 4 );
|
|
|
|
|
|
}
|
|
@@ -2971,47 +2974,103 @@ class AnimationParser {
|
|
|
// Rotations are defined as Euler angles which can have values of any size
|
|
|
// These will be converted to quaternions which don't support values greater than
|
|
|
// PI, so we'll interpolate large rotations
|
|
|
- interpolateRotations( curve ) {
|
|
|
+ interpolateRotations( curvex, curvey, curvez, eulerOrder ) {
|
|
|
+
|
|
|
+ const times = [];
|
|
|
+ const values = [];
|
|
|
+ for ( let i = 1; i < curvex.values.length; i ++ ) {
|
|
|
+
|
|
|
+ const initialValue = [
|
|
|
+ curvex.values[ i - 1 ],
|
|
|
+ curvey.values[ i - 1 ],
|
|
|
+ curvez.values[ i - 1 ],
|
|
|
+ ];
|
|
|
|
|
|
- for ( let i = 1; i < curve.values.length; i ++ ) {
|
|
|
+ if ( isNaN( initialValue[ 0 ] ) || isNaN( initialValue[ 1 ] ) || isNaN( initialValue[ 2 ] ) ) {
|
|
|
|
|
|
- const initialValue = curve.values[ i - 1 ];
|
|
|
- const valuesSpan = curve.values[ i ] - initialValue;
|
|
|
+ continue;
|
|
|
|
|
|
- const absoluteSpan = Math.abs( valuesSpan );
|
|
|
+ }
|
|
|
+
|
|
|
+ const initialValueRad = initialValue.map( MathUtils.degToRad );
|
|
|
+
|
|
|
+ const currentValue = [
|
|
|
+ curvex.values[ i ],
|
|
|
+ curvey.values[ i ],
|
|
|
+ curvez.values[ i ],
|
|
|
+ ];
|
|
|
+
|
|
|
+ if ( isNaN( currentValue[ 0 ] ) || isNaN( currentValue[ 1 ] ) || isNaN( currentValue[ 2 ] ) ) {
|
|
|
+
|
|
|
+ continue;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentValueRad = currentValue.map( MathUtils.degToRad );
|
|
|
+
|
|
|
+ const valuesSpan = [
|
|
|
+ currentValue[ 0 ] - initialValue[ 0 ],
|
|
|
+ currentValue[ 1 ] - initialValue[ 1 ],
|
|
|
+ currentValue[ 2 ] - initialValue[ 2 ],
|
|
|
+ ];
|
|
|
+
|
|
|
+ const absoluteSpan = [
|
|
|
+ Math.abs( valuesSpan[ 0 ] ),
|
|
|
+ Math.abs( valuesSpan[ 1 ] ),
|
|
|
+ Math.abs( valuesSpan[ 2 ] ),
|
|
|
+ ];
|
|
|
+
|
|
|
+ if ( absoluteSpan[ 0 ] >= 180 || absoluteSpan[ 1 ] >= 180 || absoluteSpan[ 2 ] >= 180 ) {
|
|
|
|
|
|
- if ( absoluteSpan >= 180 ) {
|
|
|
+ const maxAbsSpan = Math.max( ...absoluteSpan );
|
|
|
|
|
|
- const numSubIntervals = absoluteSpan / 180;
|
|
|
+ const numSubIntervals = maxAbsSpan / 180;
|
|
|
|
|
|
- const step = valuesSpan / numSubIntervals;
|
|
|
- let nextValue = initialValue + step;
|
|
|
+ const E1 = new Euler( ...initialValueRad, eulerOrder );
|
|
|
+ const E2 = new Euler( ...currentValueRad, eulerOrder );
|
|
|
|
|
|
- const initialTime = curve.times[ i - 1 ];
|
|
|
- const timeSpan = curve.times[ i ] - initialTime;
|
|
|
- const interval = timeSpan / numSubIntervals;
|
|
|
- let nextTime = initialTime + interval;
|
|
|
+ const Q1 = new Quaternion().setFromEuler( E1 );
|
|
|
+ const Q2 = new Quaternion().setFromEuler( E2 );
|
|
|
|
|
|
- const interpolatedTimes = [];
|
|
|
- const interpolatedValues = [];
|
|
|
+ // Check unroll
|
|
|
+ if ( Q1.dot( Q2 ) ) {
|
|
|
|
|
|
- while ( nextTime < curve.times[ i ] ) {
|
|
|
+ Q2.set( - Q2.x, - Q2.y, - Q2.z, - Q2.w );
|
|
|
|
|
|
- interpolatedTimes.push( nextTime );
|
|
|
- nextTime += interval;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Interpolate
|
|
|
+ const initialTime = curvex.times[ i - 1 ];
|
|
|
+ const timeSpan = curvex.times[ i ] - initialTime;
|
|
|
+
|
|
|
+ const Q = new Quaternion();
|
|
|
+ const E = new Euler();
|
|
|
+ for ( let t = 0; t < 1; t += 1 / numSubIntervals ) {
|
|
|
|
|
|
- interpolatedValues.push( nextValue );
|
|
|
- nextValue += step;
|
|
|
+ Q.copy( Q1.clone().slerp( Q2.clone(), t ) );
|
|
|
+
|
|
|
+ times.push( initialTime + t * timeSpan );
|
|
|
+ E.setFromQuaternion( Q, eulerOrder );
|
|
|
+
|
|
|
+ values.push( E.x );
|
|
|
+ values.push( E.y );
|
|
|
+ values.push( E.z );
|
|
|
|
|
|
}
|
|
|
|
|
|
- curve.times = inject( curve.times, i, interpolatedTimes );
|
|
|
- curve.values = inject( curve.values, i, interpolatedValues );
|
|
|
+ } else {
|
|
|
+
|
|
|
+ times.push( curvex.times[ i ] );
|
|
|
+ values.push( MathUtils.degToRad( curvex.values[ i ] ) );
|
|
|
+ values.push( MathUtils.degToRad( curvey.values[ i ] ) );
|
|
|
+ values.push( MathUtils.degToRad( curvez.values[ i ] ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
+ return [ times, values ];
|
|
|
+
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -4217,11 +4276,5 @@ function slice( a, b, from, to ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
-// inject array a2 into array a1 at index
|
|
|
-function inject( a1, index, a2 ) {
|
|
|
-
|
|
|
- return a1.slice( 0, index ).concat( a2 ).concat( a1.slice( index ) );
|
|
|
-
|
|
|
-}
|
|
|
|
|
|
export { FBXLoader };
|