BlueGit

golden-age paperdoll-renderer

design: trimmed atlas frames (Tim t1030u)

timfa Grok Bot · pushed by timfa

Authored 2026-11-08 · 6 days ago

Hash
6ce46586861a84af7edfe4a68b956e0de961d016
Parent
f7e4357d
Pipeline
Passed
1 file changed +10 −0
design.md +10 −0
@@ -583,3 +583,13 @@ Per sex, every equipment bone can be linked to one character bone (at least one
583 583 Automatic filling links equipment bones to character bones by name: (1) exact name, case-insensitive; (2) equal after normalising: lowercase, strip namespace/armature prefixes (`mixamorig:`, `Armature|`, everything up to the last `:` or `|`, `bip01`, `def-`), strip numeric suffixes (`.001`, `_01`, `1`), and fold side markers (`L`/`R`/`left`/`right` as prefix or suffix, `LeftArm`) into one side tag; (3) partial: same side, and one normalised name contains the other (at least 3 characters). The best score wins; ties go to the first character bone. Auto-fill only fills unlinked bones; manual links are kept.
584 584
585 585 Storage: equipment bindings already live in the `.ppe` preset, which the `.ppd` lists; the `.ppd` only stores the preset paths, so the `.ppd` schema is unchanged. The `.ppe` container goes from version 1 to 2 (new optional fields: the part skeleton, the bone of each mesh, and per-sex bone links). Version 1 files load as rigid parts (migration) and are written as version 2 on the next save. Renders and the live preview use the same layer function, so both show the mapping.
586
587 ## Decision 2026-11-08 (Tim t1030u): trimmed atlas frames
588
589 When the atlases are built, each view (one frame at one pitch/yaw) can be cropped to the bounding box of its content plus 2 px padding, clamped to the frame. The crop is the same for all four maps, so diffuse, normal, depth and specular stay aligned. "Content" means any pixel that is not all-zero bytes in any of the four maps (the renderer's empty background is all zeros in every map). Outside the crop every map is therefore exactly zero, and rebuilding the full frame by pasting the crop into a zero frame is lossless. A view with no content is *empty*: it takes no atlas space and is marked `empty: true`.
590
591 Packing is deterministic and keeps row streaming: each atlas row is one (clip, frame) row as before. Within it the 60 views are placed left to right in column order (pitch −60→+60, yaw 0→330), with their trimmed widths and no gaps. The row height is the tallest crop in that row; the atlas width is the widest row (at least 1 × 1). Untrimmed atlases keep the fixed grid.
592
593 Two project settings in the `.ppd` `render` block, both on by default: `trim_body` (body sets) and `trim_equipment` (equipment sets). The `.ppd` goes from version 3 to 4; older files load with both settings on. Loose raw PNGs stay full size; trimming changes only the atlases. It is not part of the render contract (`render.json`), so resuming a run with a changed setting just builds the atlases with the current setting.
594
595 `.ppda` version 5 adds `trimmed` (bool) and, per view, `offset` `[x, y]` within the full frame, `frame` `[w, h]` (= the cell/target size) and `empty`. `x, y, w, h` are the rect in the atlas (`0, 0, 0, 0` for empty views). Untrimmed v5 files use the grid with offset `[0, 0]` and the full frame. The reader checks v5 rects by recomputing the packing from the stored sizes, so overlapping or out-of-bounds rects are refused. Versions 2–4 still load. The reader (`LoadedAtlas::cell`, used by the 2D preview, compositor and checks) always returns the full frame: the stored rect pasted at its offset into a zero frame. Composition at character position + offset is therefore pixel-identical to an untrimmed export.
@@ -583,3 +583,13 @@ Per sex, every equipment bone can be linked to one character bone (at least one
583Automatic filling links equipment bones to character bones by name: (1) exact name, case-insensitive; (2) equal after normalising: lowercase, strip namespace/armature prefixes (`mixamorig:`, `Armature|`, everything up to the last `:` or `|`, `bip01`, `def-`), strip numeric suffixes (`.001`, `_01`, `1`), and fold side markers (`L`/`R`/`left`/`right` as prefix or suffix, `LeftArm`) into one side tag; (3) partial: same side, and one normalised name contains the other (at least 3 characters). The best score wins; ties go to the first character bone. Auto-fill only fills unlinked bones; manual links are kept.
583Automatic filling links equipment bones to character bones by name: (1) exact name, case-insensitive; (2) equal after normalising: lowercase, strip namespace/armature prefixes (`mixamorig:`, `Armature|`, everything up to the last `:` or `|`, `bip01`, `def-`), strip numeric suffixes (`.001`, `_01`, `1`), and fold side markers (`L`/`R`/`left`/`right` as prefix or suffix, `LeftArm`) into one side tag; (3) partial: same side, and one normalised name contains the other (at least 3 characters). The best score wins; ties go to the first character bone. Auto-fill only fills unlinked bones; manual links are kept.
584
584
585Storage: equipment bindings already live in the `.ppe` preset, which the `.ppd` lists; the `.ppd` only stores the preset paths, so the `.ppd` schema is unchanged. The `.ppe` container goes from version 1 to 2 (new optional fields: the part skeleton, the bone of each mesh, and per-sex bone links). Version 1 files load as rigid parts (migration) and are written as version 2 on the next save. Renders and the live preview use the same layer function, so both show the mapping.
585Storage: equipment bindings already live in the `.ppe` preset, which the `.ppd` lists; the `.ppd` only stores the preset paths, so the `.ppd` schema is unchanged. The `.ppe` container goes from version 1 to 2 (new optional fields: the part skeleton, the bone of each mesh, and per-sex bone links). Version 1 files load as rigid parts (migration) and are written as version 2 on the next save. Renders and the live preview use the same layer function, so both show the mapping.
586
587## Decision 2026-11-08 (Tim t1030u): trimmed atlas frames
588
589When the atlases are built, each view (one frame at one pitch/yaw) can be cropped to the bounding box of its content plus 2 px padding, clamped to the frame. The crop is the same for all four maps, so diffuse, normal, depth and specular stay aligned. "Content" means any pixel that is not all-zero bytes in any of the four maps (the renderer's empty background is all zeros in every map). Outside the crop every map is therefore exactly zero, and rebuilding the full frame by pasting the crop into a zero frame is lossless. A view with no content is *empty*: it takes no atlas space and is marked `empty: true`.
590
591Packing is deterministic and keeps row streaming: each atlas row is one (clip, frame) row as before. Within it the 60 views are placed left to right in column order (pitch −60→+60, yaw 0→330), with their trimmed widths and no gaps. The row height is the tallest crop in that row; the atlas width is the widest row (at least 1 × 1). Untrimmed atlases keep the fixed grid.
592
593Two project settings in the `.ppd` `render` block, both on by default: `trim_body` (body sets) and `trim_equipment` (equipment sets). The `.ppd` goes from version 3 to 4; older files load with both settings on. Loose raw PNGs stay full size; trimming changes only the atlases. It is not part of the render contract (`render.json`), so resuming a run with a changed setting just builds the atlases with the current setting.
594
595`.ppda` version 5 adds `trimmed` (bool) and, per view, `offset` `[x, y]` within the full frame, `frame` `[w, h]` (= the cell/target size) and `empty`. `x, y, w, h` are the rect in the atlas (`0, 0, 0, 0` for empty views). Untrimmed v5 files use the grid with offset `[0, 0]` and the full frame. The reader checks v5 rects by recomputing the packing from the stored sizes, so overlapping or out-of-bounds rects are refused. Versions 2–4 still load. The reader (`LoadedAtlas::cell`, used by the 2D preview, compositor and checks) always returns the full frame: the stored rect pasted at its offset into a zero frame. Composition at character position + offset is therefore pixel-identical to an untrimmed export.