design: #12 glTF multi-mesh/skin + texture transform decisions
@@ -418,3 +418,8 @@ Two project settings in `.ppd` (`render`): `depth_bits` = **8 (default)** or 16,
418
418
- `rgb_bands`: RGBA PNG at that bit depth, `v = round(t·3M)`, `R = min(v, M)`, `G = clamp(v − M, 0, M)`, `B = clamp(v − 2M, 0, M)`; decode `t = (R+G+B)/(3M)` (3M+1 levels; 766 at 8-bit).
419
419
- `rgb_packed`: RGBA PNG at that bit depth, positional base B over three channels: `v = round(t·(B³ − 1))`, `R = v / B²`, `G = (v / B) mod B`, `B = v mod B` (8-bit: 24-bit value, `R = v>>16`, `G = (v>>8)&255`, `B = v&255`; 16-bit: 48-bit value); decode `t = (R·B² + G·B + Bc)/(B³ − 1)`. **rgb_packed requires nearest sampling, no mipmaps/filtering and lossless storage** (no texture compression); any interpolation between texels or lossy compression corrupts the value.
420
420
Alpha is always kept (scaled to the bit depth). Distance = near + t·(far − near). `render.json` and the `.ppda` record `depth_bits` and `depth_encoding`, so the game renderer and the 2D previewer decode all six combinations; the previewer/compositor support all of them. Older `.ppda`/`render.json` without `depth_bits` are read as 16-bit gray (their actual format). Both settings are part of the render contract (changing them is a contract difference for resume).
421
422
## Decision 2026-11-07 (#12 glTF fidelity, implementation choice)
423
- A skinned glTF body loads every mesh node and every skin; joints of all skins are merged into one skeleton (shared joints once, max 128).
424
- A mesh node without a skin moves rigidly with its nearest joint ancestor (else the first joint); its rest-pose scene placement is baked once so it is exact at rest.
425
- KHR_texture_transform on the base-color texture is baked into the UVs at load. Sampler wrap modes stay unread (nearest + REPEAT everywhere); metallic/roughness, normal/occlusion/emissive textures and morph targets stay unused, body specular stays zero. These limits are documented in the README rather than approximated.
@@ -418,3 +418,8 @@ Two project settings in `.ppd` (`render`): `depth_bits` = **8 (default)** or 16,418
- `rgb_bands`: RGBA PNG at that bit depth, `v = round(t·3M)`, `R = min(v, M)`, `G = clamp(v − M, 0, M)`, `B = clamp(v − 2M, 0, M)`; decode `t = (R+G+B)/(3M)` (3M+1 levels; 766 at 8-bit).418
- `rgb_bands`: RGBA PNG at that bit depth, `v = round(t·3M)`, `R = min(v, M)`, `G = clamp(v − M, 0, M)`, `B = clamp(v − 2M, 0, M)`; decode `t = (R+G+B)/(3M)` (3M+1 levels; 766 at 8-bit).419
- `rgb_packed`: RGBA PNG at that bit depth, positional base B over three channels: `v = round(t·(B³ − 1))`, `R = v / B²`, `G = (v / B) mod B`, `B = v mod B` (8-bit: 24-bit value, `R = v>>16`, `G = (v>>8)&255`, `B = v&255`; 16-bit: 48-bit value); decode `t = (R·B² + G·B + Bc)/(B³ − 1)`. **rgb_packed requires nearest sampling, no mipmaps/filtering and lossless storage** (no texture compression); any interpolation between texels or lossy compression corrupts the value.419
- `rgb_packed`: RGBA PNG at that bit depth, positional base B over three channels: `v = round(t·(B³ − 1))`, `R = v / B²`, `G = (v / B) mod B`, `B = v mod B` (8-bit: 24-bit value, `R = v>>16`, `G = (v>>8)&255`, `B = v&255`; 16-bit: 48-bit value); decode `t = (R·B² + G·B + Bc)/(B³ − 1)`. **rgb_packed requires nearest sampling, no mipmaps/filtering and lossless storage** (no texture compression); any interpolation between texels or lossy compression corrupts the value.420
Alpha is always kept (scaled to the bit depth). Distance = near + t·(far − near). `render.json` and the `.ppda` record `depth_bits` and `depth_encoding`, so the game renderer and the 2D previewer decode all six combinations; the previewer/compositor support all of them. Older `.ppda`/`render.json` without `depth_bits` are read as 16-bit gray (their actual format). Both settings are part of the render contract (changing them is a contract difference for resume).420
Alpha is always kept (scaled to the bit depth). Distance = near + t·(far − near). `render.json` and the `.ppda` record `depth_bits` and `depth_encoding`, so the game renderer and the 2D previewer decode all six combinations; the previewer/compositor support all of them. Older `.ppda`/`render.json` without `depth_bits` are read as 16-bit gray (their actual format). Both settings are part of the render contract (changing them is a contract difference for resume).421
422
## Decision 2026-11-07 (#12 glTF fidelity, implementation choice)423
- A skinned glTF body loads every mesh node and every skin; joints of all skins are merged into one skeleton (shared joints once, max 128).424
- A mesh node without a skin moves rigidly with its nearest joint ancestor (else the first joint); its rest-pose scene placement is baked once so it is exact at rest.425
- KHR_texture_transform on the base-color texture is baked into the UVs at load. Sampler wrap modes stay unread (nearest + REPEAT everywhere); metallic/roughness, normal/occlusion/emissive textures and morph targets stay unused, body specular stays zero. These limits are documented in the README rather than approximated.