Tinkering with my custom glTF loader - using the Michelle dance model - basically just a single skinned mesh that is animated using bones/skin mesh. However, I suddenly notice a glitch!
The full code with all the bits and pieces is included at the bottom - web based example - using a custom loader/renderer for debugging/analysing glTF files with animations (renderer is a function on the end using WebGPU/WGSL).
I'll skip through all the pain of where in the code and what - it eventually comes down to the slerp function I wrote! I thought it was correct, and I spent ages checking the data, frame corruption, data values, ... painful hours... only to narrow it down to this one place!
Take a look and see if you can see the problem?
let interpolatedOutput = startOutput.map((start, i) => { const end = endOutput[i]; if (isNaN(end) || isNaN(start)) { console.warn('Encountered NaN in animation output data.'); return start; // Fallback to start if NaN encountered } //return start + (end - start) * interpolationFactor;
// Determine if the data represents quaternions if (Array.isArray(start) && start.length === 4 && Array.isArray(end) && end.length === 4) { return slerp(start, end, interpolationFactor); } else { return start + (end - start) * interpolationFactor; // Linear interpolation for non-quaternion data } });
Essentially,
startOutput
and
endOutput
are the interpolation values for the various components (scalars, translations and rotations). Rotation works better if you use
slerp
instead of just interpolating the values - otherwise you get strange issues for large angle - causes distortion etc.
Anyhow, less about quaterions - the solution is to use a
slerp
which I did. But I modified the code to use the
slerp
inside the iterator - which goes over each value and interpolates it constructing a new final array.
Fixed interpolation function - so the slerp works! Pass the start and end values directly to the slerp function - outside the iterator for going over each value - as shown below.
return startOutput.map((start, i) => { const end = endOutput[i]; if (isNaN(end) || isNaN(start)) { console.warn('Encountered NaN in animation output data.'); return start; // Fallback to start if NaN encountered } return start + (end - start) * interpolationFactor; }); }// end interpolateValues(..)
let interpolatedOutput = interpolateValues(startOutput, endOutput, interpolationFactor);
You can see the working animation as the bottom - the biggest pain was debugging it in JavaScript - as you know it is something to do with rotation - but because it's a complex model with 100+ bones and the glitch only happening very occasionally - it took longer than it should have!
Just goes to show - even though the code works - and it seems okay in initial testig with some simple skinned models - it doesn't mean it's bug free!!!