Skip to content

Commit 750b576

Browse files
authored
fix: sync vertex-weight-limit README numbers to the remodeled check output (#84)
The remodels drifted the measured values the README quoted: the drop-without-renormalize failure reads 1.616e-02 (sums at 0.984), lbs_err 2.7e-07, pose_dev 2.8e-03. Also finishes the boots-to-cuffs terminology in the script comments. Comments and docs only; check re-verified on Blender 5.1.2 (exit 0). Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
1 parent 643c7eb commit 750b576

3 files changed

Lines changed: 14 additions & 14 deletions

File tree

docs/gallery/vertex-weight-limit/index.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ <h1>vertex-weight-limit</h1>
186186
<p>A runnable example that rigs a mech arm — flanged bolted pedestal and shoulder fairing, a panel-seamed upper arm ending in clevis cheeks, the elbow hinge pin and knuckle barrel capped with hex bolts through a ribbed flex cuff, a long seam-grooved forearm, wrist cuff and collar, and a three-finger gripper — with deliberately rich five-bone weight bumps in the cuffs, then enforces the game-engine <strong>maximum of four bone influences per vertex</strong> through the data API, following <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/skills/mesh-editing-and-bmesh/SKILL.md"><code>mesh-editing-and-bmesh</code></a> and building on the linear-blend-skinning precedent of <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/armature-bend"><code>armature-bend</code></a>.</p>
187187
<p><strong>Pipeline arc:</strong> modeling/LOD in <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/lod-decimate-chain"><code>lod-decimate-chain</code></a>, weighting here, export in <a href="https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main/examples/gltf-export-roundtrip"><code>gltf-export-roundtrip</code></a>.</p>
188188
<p><strong>What it witnesses:</strong> the skinning constraint every game engine enforces and AI-generated rigging code most often violates silently.</p>
189-
<p>1. <strong>The limit is a data-API operation, not a context operator.</strong> Instead of <code>bpy.ops.object.vertex_group_limit_total</code>, the example reads each vertex&#x27;s groups, keeps the top four by weight, <code>VertexGroup.remove</code>s the rest, and renormalizes the survivors. Dropping without renormalizing leaves sums at 0.986 — a mesh that shrinks toward the origin under load (the check&#x27;s measured failure, 1.438e-02 off unit sum). 2. <strong>The armature modifier is still exactly linear blend skinning</strong> after the limit: every depsgraph-evaluated vertex equals <code>Σ wᵢ · (pose.matrix @ bone.matrix_local.inverted()) @ rest</code>, with the weights <strong>read back from the mesh&#x27;s own deform layer</strong> (<code>v.groups</code>) — the weights on the mesh are the contract, not the weights you meant to write. Measured <code>lbs_err = 3.0e-07</code>. 3. <strong>Pruning must not damage the pose.</strong> Evaluated positions before and after the limit are held within 0.05 (measured 3.0e-03), the pedestal mount stays exactly pinned (Root is unposed), and the pre-limit authoring really carries five influences in the boots — otherwise the witness would be vacuous.</p>
189+
<p>1. <strong>The limit is a data-API operation, not a context operator.</strong> Instead of <code>bpy.ops.object.vertex_group_limit_total</code>, the example reads each vertex&#x27;s groups, keeps the top four by weight, <code>VertexGroup.remove</code>s the rest, and renormalizes the survivors. Dropping without renormalizing leaves sums at 0.984 — a mesh that shrinks toward the origin under load (the check&#x27;s measured failure, 1.616e-02 off unit sum). 2. <strong>The armature modifier is still exactly linear blend skinning</strong> after the limit: every depsgraph-evaluated vertex equals <code>Σ wᵢ · (pose.matrix @ bone.matrix_local.inverted()) @ rest</code>, with the weights <strong>read back from the mesh&#x27;s own deform layer</strong> (<code>v.groups</code>) — the weights on the mesh are the contract, not the weights you meant to write. Measured <code>lbs_err = 2.7e-07</code>. 3. <strong>Pruning must not damage the pose.</strong> Evaluated positions before and after the limit are held within 0.05 (measured 2.8e-03), the pedestal mount stays exactly pinned (Root is unposed), and the pre-limit authoring really carries five influences in the cuffs — otherwise the witness would be vacuous.</p>
190190
<p>The vertex-group API (<code>v.groups</code>, <code>VertexGroup.add</code>/<code>remove</code>) is stable between Blender 4.5 LTS and 5.1 — the example runs identically on both, which is itself the version witness (measured values match to the digit).</p>
191191
<p>The render shows the pruned arm mid-pose: the flex cuffs carry the teal accent — the five-influence zones the limit prunes glow at the elbow hinge and wrist, sealed by the bright hoop on the elbow cuff — proof that the limited weights still deform as authored.</p>
192192
<h2>Run</h2>
@@ -240,7 +240,7 @@ <h2>Source</h2>
240240

241241
SIDES = <span class="n">32</span>
242242
MAX_INFLUENCES = <span class="n">4</span> <span class="c"># the engine constraint being witnessed</span>
243-
BUMP_R = <span class="n">1.8</span> <span class="c"># flex-boot blend support; guarantees 5 pre-limit</span>
243+
BUMP_R = <span class="n">1.8</span> <span class="c"># flex-cuff blend support; guarantees 5 pre-limit</span>
244244
LBS_TOL = <span class="n">5e-4</span> <span class="c"># float32 mesh coords vs double pose matrices</span>
245245
SUM_TOL = <span class="n">1e-5</span> <span class="c"># per-vertex weight sum after renormalize</span>
246246
POSE_TOL = <span class="n">0.05</span> <span class="c"># pruning must not move the pose beyond this</span>
@@ -261,7 +261,7 @@ <h2>Source</h2>
261261

262262

263263
<span class="k">def</span> flex_weights(z):
264-
<span class="s">&quot;&quot;&quot;Rich pre-limit weights over all five bones — boots blend broadly.&quot;&quot;&quot;</span>
264+
<span class="s">&quot;&quot;&quot;Rich pre-limit weights over all five bones — cuffs blend broadly.&quot;&quot;&quot;</span>
265265
w = [bump(z, BONE_CENTERS[b]) <span class="k">for</span> b <span class="k">in</span> BONES]
266266
total = sum(w)
267267
<span class="k">return</span> [x / total <span class="k">for</span> x <span class="k">in</span> w]
@@ -408,7 +408,7 @@ <h2>Source</h2>
408408

409409
<span class="k">def</span> assign_weights(obj, part_of):
410410
<span class="s">&quot;&quot;&quot;Author the rich pre-limit weights: hard single-bone on armor, broad
411-
five-bone bumps in the flex boots.&quot;&quot;&quot;</span>
411+
five-bone bumps in the flex cuffs.&quot;&quot;&quot;</span>
412412
groups = {b: obj.vertex_groups.new(name=b) <span class="k">for</span> b <span class="k">in</span> BONES}
413413
<span class="k">if</span> len(part_of) != len(obj.data.vertices):
414414
<span class="k">raise</span> RuntimeError(<span class="s">f&quot;</span><span class="s">part tag count </span>{len(part_of)}<span class="s"> != vert count </span><span class="s">&quot;</span>
@@ -466,7 +466,7 @@ <h2>Source</h2>
466466
<span class="k">def</span> check(obj, arm, groups, pose_before):
467467
me = obj.data
468468

469-
<span class="c"># pre-limit witness: the flex boots really carry five influences</span>
469+
<span class="c"># pre-limit witness: the flex cuffs really carry five influences</span>
470470
pre_max = max(len(v.groups) <span class="k">for</span> v <span class="k">in</span> me.vertices)
471471
<span class="k">if</span> pre_max != <span class="n">5</span>:
472472
print(<span class="s">f&quot;</span><span class="s">ERROR: pre-limit max influences </span>{pre_max}<span class="s"> != 5 — the rich </span><span class="s">&quot;</span>

examples/vertex-weight-limit/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,18 @@ AI-generated rigging code most often violates silently.
2121
`bpy.ops.object.vertex_group_limit_total`, the example reads each vertex's
2222
groups, keeps the top four by weight, `VertexGroup.remove`s the rest, and
2323
renormalizes the survivors. Dropping without renormalizing leaves sums at
24-
0.986 — a mesh that shrinks toward the origin under load (the check's
25-
measured failure, 1.438e-02 off unit sum).
24+
0.984 — a mesh that shrinks toward the origin under load (the check's
25+
measured failure, 1.616e-02 off unit sum).
2626
2. **The armature modifier is still exactly linear blend skinning** after the
2727
limit: every depsgraph-evaluated vertex equals
2828
`Σ wᵢ · (pose.matrix @ bone.matrix_local.inverted()) @ rest`, with the
2929
weights **read back from the mesh's own deform layer** (`v.groups`) — the
3030
weights on the mesh are the contract, not the weights you meant to write.
31-
Measured `lbs_err = 3.0e-07`.
31+
Measured `lbs_err = 2.7e-07`.
3232
3. **Pruning must not damage the pose.** Evaluated positions before and after
33-
the limit are held within 0.05 (measured 3.0e-03), the pedestal mount stays
33+
the limit are held within 0.05 (measured 2.8e-03), the pedestal mount stays
3434
exactly pinned (Root is unposed), and the pre-limit authoring really carries
35-
five influences in the boots — otherwise the witness would be vacuous.
35+
five influences in the cuffs — otherwise the witness would be vacuous.
3636

3737
The vertex-group API (`v.groups`, `VertexGroup.add`/`remove`) is stable between
3838
Blender 4.5 LTS and 5.1 — the example runs identically on both, which is itself

examples/vertex-weight-limit/vertex_weight_limit.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
SIDES = 32
3636
MAX_INFLUENCES = 4 # the engine constraint being witnessed
37-
BUMP_R = 1.8 # flex-boot blend support; guarantees 5 pre-limit
37+
BUMP_R = 1.8 # flex-cuff blend support; guarantees 5 pre-limit
3838
LBS_TOL = 5e-4 # float32 mesh coords vs double pose matrices
3939
SUM_TOL = 1e-5 # per-vertex weight sum after renormalize
4040
POSE_TOL = 0.05 # pruning must not move the pose beyond this
@@ -55,7 +55,7 @@ def bump(z, center, radius=BUMP_R):
5555

5656

5757
def flex_weights(z):
58-
"""Rich pre-limit weights over all five bones — boots blend broadly."""
58+
"""Rich pre-limit weights over all five bones — cuffs blend broadly."""
5959
w = [bump(z, BONE_CENTERS[b]) for b in BONES]
6060
total = sum(w)
6161
return [x / total for x in w]
@@ -202,7 +202,7 @@ def tag(rings_verts, bone):
202202

203203
def assign_weights(obj, part_of):
204204
"""Author the rich pre-limit weights: hard single-bone on armor, broad
205-
five-bone bumps in the flex boots."""
205+
five-bone bumps in the flex cuffs."""
206206
groups = {b: obj.vertex_groups.new(name=b) for b in BONES}
207207
if len(part_of) != len(obj.data.vertices):
208208
raise RuntimeError(f"part tag count {len(part_of)} != vert count "
@@ -260,7 +260,7 @@ def eval_positions(obj):
260260
def check(obj, arm, groups, pose_before):
261261
me = obj.data
262262

263-
# pre-limit witness: the flex boots really carry five influences
263+
# pre-limit witness: the flex cuffs really carry five influences
264264
pre_max = max(len(v.groups) for v in me.vertices)
265265
if pre_max != 5:
266266
print(f"ERROR: pre-limit max influences {pre_max} != 5 — the rich "

0 commit comments

Comments
 (0)