Description

I refer to this function: source/blender/blenkernel/intern/armature.c :: vec_roll_to_mat3_normalized()

The conversion of roll to matrix breaks when a bone has very small values in x and z. In that case we get a division by very small numbers which can cause a broken bone roll matrix.

details:

`due to float precision errors, we can have nor = (0.0, 0.99999994, 0.0)...`

I have found other situations where nor == (x, 0.99999994, z) with x and/or z very close to 0, but not exactly 0. However the test (nor[0] || nor[2]) can be true for tiny numbers. So further down in the function we see:

```/* If nor is too close to -Y, apply the special case. */
theta = nor[0] * nor[0] + nor[2] * nor[2];```

So when x and/or z are tiny numbers, then theta becomes tiny as well. And since we only check for (x or z != 0):

`if (theta > THETA_SAFE || ((nor[0] || nor[2]) && theta > THETA_CRITICAL)) ...`

You can see that theta can potentially drop below the THETA_THRESHOLD_NEGY_CLOSE if both nor[0] and nor[2] are very close to 0. And this finally leads to potentially very huge numbers for example here (division by almost zero):

```theta = nor[0] * nor[0] + nor[2] * nor[2];
bMatrix[0][0] = (nor[0] + nor[2]) * (nor[0] - nor[2]) / -theta;```

Because of this i believe it is better to not only check (nor[0] or nor[2] != 0) but also to make sure that (nor[0] * nor[0] + nor[2] * nor[2]) is above THETA_THRESHOLD_NEGY_CLOSE.

Below is a python script that generates a rig that suffers from this bug. Run the script, then switch from edit mode to pose mode and see how "bad bone" disappears. Because its pose bone matrix is totally broken. The problem originates in the super small bone roll values which finally lead to the bug:

```import bpy

bone_data = [
["good bone", (0.0, 0.0, 1.2038891315460205), (0.0, 0.01536799967288971, 1.3546786308288574), -4.1202467175263267e-16],
["bad bone", (0.0, 0.01536799967288971, 1.3546786308288574), (0.0, 0.0, 1.2038891315460205), -1.570796257510665e-07],
]

def make_armature(ctx, name):
data = bpy.data.armatures.new(name)
obj  = bpy.data.objects.new(name, data)
ctx.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT')
parent = None
for bname, head, tail, roll in bone_data:
bone = obj.data.edit_bones.new(bname)
if parent:
bone.parent = parent
parent=bone
bone.tail=tail
bone.roll = roll
return obj

armobj=make_armature(bpy.context, "armature")```

This is a follow up task of the refactoring handled in https://developer.blender.org/D9410

Revisions and Commits

 rB Blender D9551 rB16eafdadf604 Fix precision issues and a bug in vec_roll_to_mat3_normalized. rBdf445cc571bd Fix T82455: vec_roll_to_mat3_normalized returns NaN when nor close to -Y.

Event Timeline

Gaia Clary (gaiaclary) changed the task status from Needs Triage to Confirmed.Nov 6 2020, 10:36 AM
Gaia Clary (gaiaclary) created this task.

This may be set for Blender developers!

This would be due to the source code!

@champions (schampions) Welcome to developer.blender.org -- here, everything is for Blender developers. Useful feedback is appreciated, but do know that this is not just a forum. It's the central hub for Blender development.

Sybren A. Stüvel (sybren) changed the subtype of this task from "Report" to "Bug".Nov 17 2020, 3:21 PM
Sybren A. Stüvel (sybren) moved this task from Backlog to Bugs on the Animation & Rigging board.