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

Optimize morph target animation creation from glb data #5140

Merged
merged 1 commit into from
Mar 10, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 19 additions & 14 deletions src/framework/parsers/glb-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1448,17 +1448,6 @@ const createAnimation = function (gltfAnimation, animationIndex, gltfAccessors,
return path;
};

const retrieveWeightName = (gltfNode, weightIndex) => {
if (meshes && meshes[gltfNode.mesh]) {
const mesh = meshes[gltfNode.mesh];
if (mesh.hasOwnProperty('extras') && mesh.extras.hasOwnProperty('targetNames') && mesh.extras.targetNames[weightIndex]) {
return `name.${mesh.extras.targetNames[weightIndex]}`;
}
}

return weightIndex;
};

// All morph targets are included in a single channel of the animation, with all targets output data interleaved with each other.
// This function splits each morph target out into it a curve with its own output data, allowing us to animate each morph target independently by name.
const createMorphTargetCurves = (curve, gltfNode, entityPath) => {
Expand All @@ -1467,24 +1456,41 @@ const createAnimation = function (gltfAnimation, animationIndex, gltfAccessors,
Debug.warn(`glb-parser: No output data is available for the morph target curve (${entityPath}/graph/weights). Skipping.`);
return;
}

// names of morph targets
let targetNames;
if (meshes && meshes[gltfNode.mesh]) {
const mesh = meshes[gltfNode.mesh];
if (mesh.hasOwnProperty('extras') && mesh.extras.hasOwnProperty('targetNames')) {
targetNames = mesh.extras.targetNames;
}
}

const outData = out.data;
const morphTargetCount = outData.length / inputMap[curve.input].data.length;
const keyframeCount = outData.length / morphTargetCount;

// single array buffer for all keys, 4 bytes per entry
const singleBufferSize = keyframeCount * 4;
const buffer = new ArrayBuffer(singleBufferSize * morphTargetCount);

for (let j = 0; j < morphTargetCount; j++) {
const morphTargetOutput = new Float32Array(keyframeCount);
const morphTargetOutput = new Float32Array(buffer, singleBufferSize * j, keyframeCount);

// the output data for all morph targets in a single curve is interleaved. We need to retrieve the keyframe output data for a single morph target
for (let k = 0; k < keyframeCount; k++) {
morphTargetOutput[k] = outData[k * morphTargetCount + j];
}
const output = new AnimData(1, morphTargetOutput);
const weightName = targetNames?.[j] ? `name.${targetNames[j]}` : j;

// add the individual morph target output data to the outputMap using a negative value key (so as not to clash with sampler.output values)
outputMap[-outputCounter] = output;
const morphCurve = {
paths: [{
entityPath: entityPath,
component: 'graph',
propertyPath: [`weight.${retrieveWeightName(gltfNode, j)}`]
propertyPath: [`weight.${weightName}`]
}],
// each morph target curve input can use the same sampler.input from the channel they were all in
input: curve.input,
Expand Down Expand Up @@ -1520,7 +1526,6 @@ const createAnimation = function (gltfAnimation, animationIndex, gltfAccessors,
propertyPath: [transformSchema[target.path]]
});
}

}

const inputs = [];
Expand Down