Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,6 @@ __pycache__/
/multiview_bake_*.png
/multiview_bake_*i.png
!docs/SKINNING_QUALITY.md
!docs/img
!docs/img/twist_bar_rest_lbs.png
!docs/img/twist_bar_90_lbs.png
Comment on lines +152 to +154

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Do not use negation rules for generated images.

The ! prefix unignores these paths, so docs/img and both PNGs remain eligible for tracking. If these generated comparison images should be ignored, use explicit ignore patterns without !, such as docs/img/.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.gitignore around lines 152 - 154, Replace the negated patterns for docs/img
and its generated PNG files with explicit ignore patterns, removing the !
prefixes so the generated comparison images remain ignored.

60 changes: 56 additions & 4 deletions docs/SKINNING_QUALITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,32 @@ runs the comparison automatically when `QTMESH_SKIN_REF_FBX` (the
Mixamo file) and `QTMESH_SKIN_OURS_FBX` are set; it is skipped
otherwise, so CI stays hermetic.

### Recorded run

_Pending a manual Mixamo export (requires an Adobe account). Record
the `--compare` and `--evaluate` outputs here when first run._
### Recorded run (2026-07-10, bandit.fbx — artist-skinned reference)

Reference: a production character ("bandit", UE-style rig — `pelvis` /
`spine_03` / `thigh_twist_01_l` naming, 119 bones, 90,573 verts,
artist-painted weights) used in place of a Mixamo export — same
`--compare`/`--evaluate` pipeline; a Mixamo-exported reference slots
into the identical commands. Both candidates were skinned by the
app on a same-pipeline copy of the mesh (skeleton preserved,
weights recomputed), then compared against the artist original.

| metric | SkinTokens ML (+ geodesic localisation) | GeodesicVoxel | artist reference |
|---|---|---|---|
| Mean weight L1 vs artist | **1.2241** | **1.0929** | — |
| Avg influences / vertex | 2.52 | 3.44 | 2.22 |
| Max influences | 4 | 4 | 4 |
| Smoothness energy (lower = smoother) | 0.0209 | 0.0124 | 0.0099 |
| Bleed fraction (non-geodesically-local weight) | 0.0764 | 0.0283 | **0.1426** |

Top differing bones in both cases are the head/spine/pelvis mass
distribution plus accessory bones (`hat`, `holster`, `ponytail_*`) —
places where artists paint deliberate stylistic weights. Notably the
artist reference itself scores the *highest* bleed: artists
intentionally assign non-local weights (twist bones, accessories),
which is also why weight-L1 vs the artist is a proxy, not a verdict —
the visual pick in production use was the ML skinner (see the default
choice in `SkinWeights`).

## Dual-quaternion display (Slice D)

Expand All @@ -105,3 +127,33 @@ candy-wrapper collapse on twists that no weight map can fix. Display
only: exporters consume the unchanged vertex weights — engines
re-skin with their own blend. Entities above the RTSS bone cap (96)
stay on the default path.

### Forearm-twist comparison (recorded 2026-07-10)

Reproducible fixture — a dense 0.6×0.18 bar (486 verts, 121 rings),
auto-rigged with the `generic` 3-joint template, GVB-skinned, with a
"Twist" animation (Top bone, 0° → 180° about the bone axis) injected
into the exported glTF. Headless LBS renders via the pose pipeline:

```
qtmesh pose twist_bar_rig.gltf2 --animation Twist --time 0.5 -o bar_90.stl
qtmesh turntable bar_90.stl -o bar_90_%02d.png --frames 4
```
Comment on lines +138 to +141

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Specify the fenced block language.

Change the opening fence to ```bash to satisfy MD040.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 138-138: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/SKINNING_QUALITY.md` around lines 138 - 141, Specify the fenced code
block language in the command example by changing its opening fence to ```bash,
while leaving the contained commands unchanged.

Source: Linters/SAST tools


| rest (LBS) | 90° twist (LBS) |
|---|---|
| ![rest](img/twist_bar_rest_lbs.png) | ![90° LBS](img/twist_bar_90_lbs.png) |
Comment on lines +138 to +145

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the forearm comparison commands reproduce the referenced images.

The documented commands only render the 90° pose to bar_90_%02d.png; they do not render the rest pose or produce the referenced img/twist_bar_rest_lbs.png and img/twist_bar_90_lbs.png files. Add the rest-pose command and document the exact output/rename step.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 138-138: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/SKINNING_QUALITY.md` around lines 138 - 145, The forearm comparison
documentation omits the rest-pose render and image naming steps. Update the
command block in the skinning quality guide to render both the rest pose and 90°
Twist pose, then explicitly document how the turntable outputs are renamed or
placed as img/twist_bar_rest_lbs.png and img/twist_bar_90_lbs.png to match the
referenced images.


The 90° frame shows linear blending's volume loss: through the
Spine→Top blend zone the silhouette necks inward (the blended matrix
at 50% of a 90° rotation scales the cross-section by cos 45° ≈ 0.71),
the classic precursor of the 180° candy-wrapper collapse. Under DQS
the cross-section keeps its width through the same zone.

**Known issue:** on macOS (legacy `RenderSystem_GL`, max glsl120) the
DQS technique currently renders nothing — the entity disappears until
toggled back to Linear, so the DQS side of this comparison cannot be
captured there yet. Tracked in
[#833](https://github.com/fernandotonon/QtMeshEditor/issues/833);
the imprint/technique path is covered by `SkinningDisplayTest` on
Linux CI, and a pixel-level LBS/DQS diff should land with that fix.
Binary file added docs/img/twist_bar_90_lbs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/twist_bar_rest_lbs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading