Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FBXLoader: Fix rotation discontinuities. #27057

Merged
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 104 additions & 49 deletions examples/jsm/loaders/FBXLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2666,7 +2666,7 @@

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 );

}
Expand Down Expand Up @@ -2698,32 +2698,20 @@

}

generateRotationTrack( modelName, curves, initialValue, preRotation, postRotation, eulerOrder ) {
generateRotationTrack( modelName, curves, preRotation, postRotation, eulerOrder ) {

if ( curves.x !== undefined ) {
let times;
Mugen87 marked this conversation as resolved.
Show resolved Hide resolved
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 );
Expand All @@ -2749,15 +2737,32 @@

const quaternionValues = [];

if ( ! values || ! times ) return new QuaternionKeyframeTrack( modelName + '.quaternion', [], [] );
Fixed Show fixed Hide fixed

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 );

}
Expand Down Expand Up @@ -2888,47 +2893,103 @@
// 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 ];

}

}
Expand Down Expand Up @@ -4134,11 +4195,5 @@

}

// 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 };