Page MenuHome

Automatic Weights fail when object is small and far from world origin
Open, Confirmed, LowPublic


System Information
Operating system: Windows-10-10.0.16299 64 Bits
Graphics card: GeForce GTX 1080/PCIe/SSE2 NVIDIA Corporation 4.5.0 NVIDIA 431.86

Blender Version
Broken: version: 2.81 (sub 15), branch: master, commit date: 2019-10-14 23:04, hash: rB695cbf5eef79

Short description of error
There's a small cube far from world origin. When you try to parent it to equally tiny bone with automatic weights, the result is an empty vertex group.

Exact steps for others to reproduce the error

  • Open file:

  • press ctrl+p (set parent to) > With Automatic Weights
  • You can see in weight paint mode that weights are blue

If you for example only change 'y' location of the armature from -0.01126 m to -0.01125 m it suddenly works. Similarly if you change cube's size from 0.02384 m to 0.02385 m it's going to work. Same if you move it closer to world origin. I know it might be a corner case scenario, but I'm reporting anyway.



Event Timeline

Germano Cavalcante (mano-wii) lowered the priority of this task from Needs Triage by Developer to Confirmed, Low.

I can confirm, but I put low priority because you can work around the problem.
@Sybren A. Stüvel (sybren), I'm not sure who to assign to, (this code isn't maintained for a long time).
From my investigation, it seems that the problem is because of the 32-bit precision in the heat_ray_source_visible function.

This hacking change solves the problem:

diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
index 6bf507df421..b5cc2cd07a3 100644
--- a/source/blender/editors/armature/meshlaplacian.c
+++ b/source/blender/editors/armature/meshlaplacian.c
@@ -478,9 +478,17 @@ static int heat_ray_source_visible(LaplacianSystem *sys, int vertex, int source)
   closest_to_line_segment_v3(end, data.start, sys->heat.root[source], sys->heat.tip[source]);
-  sub_v3_v3v3(data.vec, end, data.start);
-  madd_v3_v3v3fl(data.start, data.start, data.vec, 1e-5);
-  mul_v3_fl(data.vec, 1.0f - 2e-5f);
+  for (int i = 0; i < 3; i++) {
+    data.vec[i] = end[i] - data.start[i];
+    if (data.vec[i] != 0.0f) {
+      float offset = data.vec[i] * 1e-5;
+      if (fabs(offset) < 2 * FLT_EPSILON) {
+        offset = data.vec[i] < 0.0f ? -2 * FLT_EPSILON : 2 * FLT_EPSILON;
+      }
+      data.start[i] += offset;
+      data.vec[i] -= 2 * offset;
+    }
+  }
   /* pass normalized vec + distance to bvh */
   hit.index = -1;

Let me add that if you move objects farther away from the center the problem gets worse. Here's an example where parenting with automatic weights fails completely with a message:

Bone Heat Weighting: failed to find solution for one or more bones

despite the fact that all numbers are nice and round (-3 m, 0.02 m).

Worth adding that changing "Unit Scale" to let's say 100 doesn't help.

There are other issues with Blender's precision when objects are moved far from the origin; animation also suffers, among other things. There is a workaround for this, which is to have characters/objects that are at the origin when skinning (either in their own collection or even their own blend file). You can then use instancing to place the characters at the desired location in the scene.

@Germano Cavalcante (mano-wii) Interesting workaround. Can you elaborate a bit on what your code does differently than the existing code? I feel that both the old & the new code are under-documented when it comes to code comments, and I generally aim to avoid magic numbers like 1e-5 or -2 * FLT_EPSILON without explanation.

@Sybren A. Stüvel (sybren), basically that patch creates a vec of FLT_EPSILONs when the precision is too small to create a vec.
The initial problem is that data.vec * 1e-5 gets so small that it becomes a vec of zeros.

Another solution would be to remove these magic numbers and make the callback work with zeros and FLT_EPSILONs.
(But I don't know how safe this would be)