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!
Glitchy dance - the following shows a short snippet of the dance with the problem - if you watch it - you'll notice that the Michelle model has some secret powers - she can bend her leg in all sorts of ways and twist her body - which isn't correct
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?
<?php
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.
const interpolateValues = (startOutput, endOutput, interpolationFactor) =>
{
if ( startOutput.length == 4 )
{
return slerp(startOutput, endOutput, interpolationFactor);
}
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!
Working dance - that's smooth as jello on a slippy sloppy surface!
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!!!