design: rest-pose centre pivot supersedes neck pivot; weaker live perspective (Tim t1008u)
@@ -520,3 +520,12 @@ Decision (superseded and reverted per Tim t991u, see below): automatic per-body
520
520
## Decision 2026-11-07 (Tim t1007u): every material per primitive, live preview equals production
521
521
522
522
A glTF body keeps one material per primitive. The live 3D preview draws each primitive range with its own base color factor × base color texture and its own alpha cutoff, exactly like production (which already did); it never merges the materials into one color or drops textures. External image URIs (next to the `.gltf`) are resolved like embedded and `data:` images. Factors are applied as loaded (no correction); production multiplies in linear space and stores sRGB diffuse, the live view stays a simplified lit view. Regression: `tests/fixtures/bodies/multi-material/` (external `.bin`, two external PNGs, three materials: factor only, texture, texture × factor), a unit test on the live GPU frame, a GPU probe case and the `textured_body_fixture` example, which checks all three material colors in the production diffuse.
523
524
## Decision 2026-11-07 (Tim t1008u): orbit pivot at the rest-pose vertical centre; weaker live perspective
525
526
Supersedes the neck pivot of t952u (the fixed framing itself is unchanged).
527
528
- **Pivot.** `pivot_y_milli = None` now resolves to the vertical centre of the model's rest-pose bounds (`(min Y + max Y) / 2` of all rest-pose vertices), not the neck joint. Reason: around the neck the feet swing far along the view axis at pitch ±60 and leave the 8-bit depth range. An explicit `pivot_y_milli` stays a manual override; a model without finite geometry falls back to `center_y_milli` with a diagnostic. The neck-name detection is removed. `pivot_source.kind` in `render.json`/`.ppda` identity becomes `bounds_center` (with `min_y_milli`/`max_y_milli`); `manual` and `fallback` are unchanged, and the readers still accept the legacy `neck` kind of older exports. The UI button **Auto centre** clears a manual pivot.
529
- **Depth range.** Unchanged rule (T-pose span plus one arm length per side, or a manual total), centred on the orbit pivot at camera distance 10, so it now follows the new pivot depth.
530
- **Perspective.** Production renders and the fixed render frame in the live view are orthographic, i.e. already the limit of "infinitely far and zoomed in": there is no perspective to weaken, the camera distance does not change the image, and no `.ppd` setting is added. Only the free live view (fixed frame unchecked) is perspective; it now sits 2.5× further away with a narrowed field of view (`2·atan(tan(19°)/2.5)` ≈ 15.4° instead of 38°), so the on-screen size stays the same with weaker perspective.
531
- **Check.** A GPU production render of Tim's `maleb.glb`/`femaleb.glb` with his framing must report 0 clamped depth pixels at 8 bit for all pitches including ±60; `depth_clamp_probe` cross-checks per pitch on the CPU.
@@ -520,3 +520,12 @@ Decision (superseded and reverted per Tim t991u, see below): automatic per-body520
## Decision 2026-11-07 (Tim t1007u): every material per primitive, live preview equals production520
## Decision 2026-11-07 (Tim t1007u): every material per primitive, live preview equals production521
521
522
A glTF body keeps one material per primitive. The live 3D preview draws each primitive range with its own base color factor × base color texture and its own alpha cutoff, exactly like production (which already did); it never merges the materials into one color or drops textures. External image URIs (next to the `.gltf`) are resolved like embedded and `data:` images. Factors are applied as loaded (no correction); production multiplies in linear space and stores sRGB diffuse, the live view stays a simplified lit view. Regression: `tests/fixtures/bodies/multi-material/` (external `.bin`, two external PNGs, three materials: factor only, texture, texture × factor), a unit test on the live GPU frame, a GPU probe case and the `textured_body_fixture` example, which checks all three material colors in the production diffuse.522
A glTF body keeps one material per primitive. The live 3D preview draws each primitive range with its own base color factor × base color texture and its own alpha cutoff, exactly like production (which already did); it never merges the materials into one color or drops textures. External image URIs (next to the `.gltf`) are resolved like embedded and `data:` images. Factors are applied as loaded (no correction); production multiplies in linear space and stores sRGB diffuse, the live view stays a simplified lit view. Regression: `tests/fixtures/bodies/multi-material/` (external `.bin`, two external PNGs, three materials: factor only, texture, texture × factor), a unit test on the live GPU frame, a GPU probe case and the `textured_body_fixture` example, which checks all three material colors in the production diffuse.523
524
## Decision 2026-11-07 (Tim t1008u): orbit pivot at the rest-pose vertical centre; weaker live perspective525
526
Supersedes the neck pivot of t952u (the fixed framing itself is unchanged).527
528
- **Pivot.** `pivot_y_milli = None` now resolves to the vertical centre of the model's rest-pose bounds (`(min Y + max Y) / 2` of all rest-pose vertices), not the neck joint. Reason: around the neck the feet swing far along the view axis at pitch ±60 and leave the 8-bit depth range. An explicit `pivot_y_milli` stays a manual override; a model without finite geometry falls back to `center_y_milli` with a diagnostic. The neck-name detection is removed. `pivot_source.kind` in `render.json`/`.ppda` identity becomes `bounds_center` (with `min_y_milli`/`max_y_milli`); `manual` and `fallback` are unchanged, and the readers still accept the legacy `neck` kind of older exports. The UI button **Auto centre** clears a manual pivot.529
- **Depth range.** Unchanged rule (T-pose span plus one arm length per side, or a manual total), centred on the orbit pivot at camera distance 10, so it now follows the new pivot depth.530
- **Perspective.** Production renders and the fixed render frame in the live view are orthographic, i.e. already the limit of "infinitely far and zoomed in": there is no perspective to weaken, the camera distance does not change the image, and no `.ppd` setting is added. Only the free live view (fixed frame unchecked) is perspective; it now sits 2.5× further away with a narrowed field of view (`2·atan(tan(19°)/2.5)` ≈ 15.4° instead of 38°), so the on-screen size stays the same with weaker perspective.531
- **Check.** A GPU production render of Tim's `maleb.glb`/`femaleb.glb` with his framing must report 0 clamped depth pixels at 8 bit for all pitches including ±60; `depth_clamp_probe` cross-checks per pitch on the CPU.