Skip to content

Commit

Permalink
introduce GLTFLoader extension methods for animations
Browse files Browse the repository at this point in the history
  • Loading branch information
hybridherbst committed Sep 6, 2022
1 parent 98824a8 commit 41b2a78
Showing 1 changed file with 158 additions and 87 deletions.
245 changes: 158 additions & 87 deletions examples/jsm/loaders/GLTFLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
BufferGeometry,
ClampToEdgeWrapping,
Color,
ColorKeyframeTrack,
DirectionalLight,
DoubleSide,
FileLoader,
Expand Down Expand Up @@ -2381,6 +2382,48 @@ function getNormalizedComponentScale( constructor ) {

}

function getArrayFromAccessor( accessor ) {

let outputArray = accessor.array;

if ( accessor.normalized ) {

const scale = getNormalizedComponentScale( outputArray.constructor );
const scaled = new Float32Array( outputArray.length );

for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) {

scaled[ j ] = outputArray[ j ] * scale;

}

outputArray = scaled;

}

return outputArray;

}

function createCubicSplineTrackInterpolant( track ) {

track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( 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.

const interpolantType = ( this instanceof QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant;

return new interpolantType( this.times, this.values, this.getValueSize() / 3, result );

};

// Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants.
track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true;

}

function getImageURIMimeType( uri ) {

if ( uri.search( /\.jpe?g($|\?)/i ) > 0 || uri.search( /^data\:image\/jpeg/ ) === 0 ) return 'image/jpeg';
Expand Down Expand Up @@ -3779,6 +3822,7 @@ class GLTFParser {
loadAnimation( animationIndex ) {

const json = this.json;
const parser = this;

const animationDef = json.animations[ animationIndex ];

Expand All @@ -3793,11 +3837,16 @@ class GLTFParser {
const channel = animationDef.channels[ i ];
const sampler = animationDef.samplers[ channel.sampler ];
const target = channel.target;
const name = target.node;
const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input;
const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output;

pendingNodes.push( this.getDependency( 'node', name ) );
const nodeDependency = parser._invokeOne( function ( ext ) {

return ext.loadAnimationTargetFromChannel && ext.loadAnimationTargetFromChannel( channel );

} );

pendingNodes.push( nodeDependency );
pendingInputAccessors.push( this.getDependency( 'accessor', input ) );
pendingOutputAccessors.push( this.getDependency( 'accessor', output ) );
pendingSamplers.push( sampler );
Expand Down Expand Up @@ -3833,103 +3882,27 @@ class GLTFParser {

if ( node === undefined ) continue;

node.updateMatrix();

let TypedKeyframeTrack;

switch ( PATH_PROPERTIES[ target.path ] ) {

case PATH_PROPERTIES.weights:

TypedKeyframeTrack = NumberKeyframeTrack;
break;

case PATH_PROPERTIES.rotation:

TypedKeyframeTrack = QuaternionKeyframeTrack;
break;
if ( node.updateMatrix ) {

case PATH_PROPERTIES.position:
case PATH_PROPERTIES.scale:
default:

TypedKeyframeTrack = VectorKeyframeTrack;
break;
node.updateMatrix();
node.matrixAutoUpdate = true;

}

const targetName = node.name ? node.name : node.uuid;

const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : InterpolateLinear;

const targetNames = [];

if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) {

node.traverse( function ( object ) {

if ( object.morphTargetInfluences ) {

targetNames.push( object.name ? object.name : object.uuid );

}
const createdTracks = parser._invokeOne( function ( ext ) {

} );

} else {

targetNames.push( targetName );

}

let outputArray = outputAccessor.array;

if ( outputAccessor.normalized ) {

const scale = getNormalizedComponentScale( outputArray.constructor );
const scaled = new Float32Array( outputArray.length );

for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) {

scaled[ j ] = outputArray[ j ] * scale;

}

outputArray = scaled;

}
return ext.createAnimationTracks && ext.createAnimationTracks( node, inputAccessor, outputAccessor, sampler, target );

for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) {

const track = new TypedKeyframeTrack(
targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ],
inputAccessor.array,
outputArray,
interpolation
);

// Override interpolation with custom factory method.
if ( sampler.interpolation === 'CUBICSPLINE' ) {

track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( 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.

const interpolantType = ( this instanceof QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant;
} );

return new interpolantType( this.times, this.values, this.getValueSize() / 3, result );
if ( createdTracks ) {

};
for ( let k = 0; k < createdTracks.length; k ++ ) {

// Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants.
track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true;
tracks.push( createdTracks[ k ] );

}

tracks.push( track );

}

}
Expand Down Expand Up @@ -4190,6 +4163,104 @@ class GLTFParser {

}

loadAnimationTargetFromChannel( animationChannel ) {

const target = animationChannel.target;
const name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated.
return this.getDependency( 'node', name );

}

createAnimationTracks( node, inputAccessor, outputAccessor, sampler, target ) {

const tracks = [];

const targetName = node.name ? node.name : node.uuid;

const targetNames = [];

if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) {

node.traverse( function ( object ) {

if ( object.morphTargetInfluences ) {

targetNames.push( object.name ? object.name : object.uuid );

}

} );

} else {

targetNames.push( targetName );

}

let TypedKeyframeTrack;

switch ( PATH_PROPERTIES[ target.path ] ) {

case PATH_PROPERTIES.weights:

TypedKeyframeTrack = NumberKeyframeTrack;
break;

case PATH_PROPERTIES.rotation:

TypedKeyframeTrack = QuaternionKeyframeTrack;
break;

case PATH_PROPERTIES.position:
case PATH_PROPERTIES.scale:
default:
switch ( outputAccessor.itemSize ) {

case 1:
TypedKeyframeTrack = NumberKeyframeTrack;
break;
case 2:
case 3:
TypedKeyframeTrack = VectorKeyframeTrack;
break;
case 4:
TypedKeyframeTrack = ColorKeyframeTrack;
break;

}

break;

}

const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : InterpolateLinear;

const outputArray = getArrayFromAccessor( outputAccessor );

for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) {

const track = new TypedKeyframeTrack(
targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ],
inputAccessor.array,
outputArray,
interpolation
);

// Override interpolation with custom factory method.
if ( interpolation === 'CUBICSPLINE' ) {

createCubicSplineTrackInterpolant( track );

}

tracks.push( track );

}

return tracks;

}

}

function buildNodeHierarchy( nodeId, parentObject, json, parser ) {
Expand Down

0 comments on commit 41b2a78

Please sign in to comment.