jmonkeyengine icon indicating copy to clipboard operation
jmonkeyengine copied to clipboard

Issue when the armature is scaled

Open codex128 opened this issue 2 years ago • 3 comments

I experienced an issue when the Armature object is scaled to something other than 100% in blender. The model looks and animates fine in blender, but appears bigger or smaller than it should in JME before an animation action is applied. After an animation action is applied to the model, the model assumes the correct size.

It also has a strange impact on DynamicAnimControl (in jbullet and Minie), where the DAC "freezes" into the completely wrong pose when in ragdoll mode.

I was using a Mixamo model with this hierarchy setup:

v Model Root
|   v Armature
|   |   Skinned & Animated Model

I have not tested this problem with other setups.

For more details, view this forum post.

I bypassed this issue by setting Armature scale to 100%, then scaling the model root in JME to make the model appear the same size. Looks and feels the same, but does not experience this issue.

codex128 avatar Jul 29 '23 18:07 codex128

I figured out why starting an animation enlarges the model by 100x. The armature's root joint (id=0, name="Hips") has a bind transform of

Transform[ -6.7569296E-8, 0.99791944, 5.280944E-7]
[ 2.1916875E-8, -1.3934987E-6, 4.390481E-5, 1.0]
[ 0.01 , 0.01, 0.01]

To me, this seems a reasonable way for JME's glTF loader to cope with a scaled armature object.

Track data (from animation clips) get combined with the inverse bind transform to compute the skinning matrices. Each joint inherits 100x scaling from the root joint's inverse bind transform. Consequently, each skinning matrix includes 100x scaling.

To me, it seems odd that 0.01x scaling doesn't appear in the armature's initial transforms. They include very little scaling, though the root joint's initial transform does include a 90-degree X-axis rotation (presumably to convert from Z-up to Y-up coordinates). I glanced at the code in GltfLoader that sets the initial transforms, but didn't see anything suspicious. It's possible that when the model was exported (from Blender to glTF) it was rotated but not scaled.

stephengold avatar Aug 30 '23 21:08 stephengold

The issue with DynamicAnimControl results from the control's reliance on the BindPosePosition vertex buffer(s) to position the pivots of each physics joint. Once a physics joint has been created in Bullet, there's unfortunately no good mechanism to alter its pivot offsets. If an animation enlarges the model, the collision shapes in the ragdoll expand accordingly, but the pivot offsets remain unchanged, causing the ragdoll to "freeze" as reported.

DynamicAnimControl includes a rebuild() method to address changes to model scaling. It re-creates all the physics objects in the ragdoll, including the physics joints. However, rebuild() still relies on the BindPosePosition vertex buffer(s) to position the pivots. Because rebuild() is unaware of bind-transform scaling, it doesn't solve the "freeze" issue.

Maintaining a Bullet ragdoll in the face of scale changes is a tough problem to solve in general. However, the current case (of bind-transform scaling in the root joint) might be one I can address in Minie.

stephengold avatar Aug 31 '23 05:08 stephengold

I tried compensating for armature scaling in DynamicAnimControl, but it turned out to be more complicated than I expected. For one thing, DAC has multiple mechanisms for converting coordinates between mesh space and physics space; all of them are impacted. Furthermore (like every feature of DAC) the complexity is compounded by supporting 2 animation systems plus cloning and serialization.

For now, I've modified Minie to log a warning when a DAC is added to a model with armature scaling exceeding 1%.

Seeing no evidence of a bug in the glTF loader, I'm going to mark this as a physics issue.

stephengold avatar Aug 31 '23 17:08 stephengold