glTF-Transform icon indicating copy to clipboard operation
glTF-Transform copied to clipboard

Broken animation after resampling

Open donmccurdy opened this issue 2 years ago • 3 comments

Describe the bug

After resampling, the animation sequence in this example is broken.

To Reproduce

Archive.zip

Steps to reproduce the behavior:

  1. gltf-transform resample Arm.glb Arm-resampled.glb

Decreasing tolerance will fix the issue, but also prevents resampling from reducing the size very much. My hunch is that the current implementation of `resample() does not handle quaternion keyframe tracks properly.

Expected behavior

Animation should appear visually identical, within some small tolerance.

Related

  • https://github.com/donmccurdy/glTF-Transform/pull/492
  • https://github.com/donmccurdy/glTF-Transform/issues/489

Sub-issues:

  • [ ] resample() should remove channels that have no effect
  • [ ] resample() must slerp, not lerp, quaternion tracks
  • [ ] resample() must not collapse keyframes A-B-C if A and C are >180º apart

donmccurdy avatar Feb 16 '22 15:02 donmccurdy

Digging into the file a bit more, there are lots of junk keyframe tracks that aren't contributing anything to the final animation. Some are small (2 keyframes, both identical) and others are large (50,627 keyframes, all containing scale=1,1,1). The resample command deals with the duplicate keyframes but doesn't currently prune no-op tracks. That — along with an automatic dedup step — would probably be reasonable additions to the function.

Screen Shot 2022-02-16 at 11 03 06 AM

donmccurdy avatar Feb 16 '22 16:02 donmccurdy

To chime in here regarding the file - everything with two keyframes is there because the "animation pose" is different to the "non-animated pose" for this model. The rest pose could be anything (e.g. stretched out) but if during the entire animation clip a part of the model would be rotated differently, then there's a start and end keyframe for that but no actual animation inbetween. So, these tracks aren't always "no-op tracks", they can also be "pose tracks" without any animation/movement happening.

There might of course be some cases where the keyframes can be stripped (I thought I do that on export already though) when those two poses match.

hybridherbst avatar Feb 16 '22 16:02 hybridherbst

Somehow gltfpack manages to reduce this file to just 6 animation channels – I guess that must (at least in part) mean it's baking the animated pose to the rest pose? Does gltfpack's output work for your use cases, or is that something you'd want to be opt-in if it happens at all?

In either case, the track containing only scale=1,1,1 could probably be removed by resample()... 😅

donmccurdy avatar Feb 16 '22 16:02 donmccurdy

Manually set the tolerance to 1.1920928955078125e-7 (the epsilon for float32) would help.

await document.transform(
	// Losslessly resample animation frames.
	resample({tolerance: 1.1920928955078125e-7}),

	// Remove duplicate vertex or texture data, if any.
	dedup(),

	// Remove unused nodes, textures, or other data.
	prune(),

);

resampled.zip

kzhsw avatar Dec 24 '22 02:12 kzhsw

This issue will be fixed in https://github.com/donmccurdy/glTF-Transform/pull/760. The main thing was using slerp() to compare quaternion keyframes. I've left the two-keyframe "animation pose" channels alone, as I think removing those would probably be unexpected behavior for the resample() function here.

donmccurdy avatar Dec 28 '22 04:12 donmccurdy