@bitmagic/cli 0.1.65 → 0.1.66-dev.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +37 -1
  2. package/assets/blender-level/find_blender.py +3 -1
  3. package/assets/character-rigging/SKILL.md +80 -0
  4. package/assets/character-rigging/references/articulation.md +55 -0
  5. package/assets/character-rigging/references/new-rig.md +51 -0
  6. package/assets/character-rigging/references/repairs.md +44 -0
  7. package/assets/character-rigging/references/verification.md +49 -0
  8. package/assets/character-rigging/scripts/rig_tool.py +197 -0
  9. package/assets/character-skin-authoring/SKILL.md +43 -0
  10. package/assets/character-skin-authoring/references/exporters.md +134 -0
  11. package/assets/character-skin-authoring/references/eyes.md +215 -0
  12. package/assets/character-skin-authoring/references/materials.md +115 -0
  13. package/assets/character-skin-authoring/references/playground.md +78 -0
  14. package/assets/character-skin-authoring/references/profiles.md +97 -0
  15. package/assets/character-skin-authoring/references/projection.md +190 -0
  16. package/assets/character-skin-authoring/references/regional-detail.md +72 -0
  17. package/assets/character-skin-authoring/scripts/assemble_character.py +205 -0
  18. package/assets/character-skin-authoring/scripts/authored_detail.py +47 -0
  19. package/assets/character-skin-authoring/scripts/authored_surface_patch.py +83 -0
  20. package/assets/character-skin-authoring/scripts/brow_groom.py +62 -0
  21. package/assets/character-skin-authoring/scripts/build_face_detail.py +107 -0
  22. package/assets/character-skin-authoring/scripts/build_surface_candidate.py +28 -0
  23. package/assets/character-skin-authoring/scripts/canthus_wall.py +57 -0
  24. package/assets/character-skin-authoring/scripts/character_build.py +54 -0
  25. package/assets/character-skin-authoring/scripts/eye_assets.py +333 -0
  26. package/assets/character-skin-authoring/scripts/eye_opening.py +114 -0
  27. package/assets/character-skin-authoring/scripts/eye_texture.py +35 -0
  28. package/assets/character-skin-authoring/scripts/eyelid_detail.py +34 -0
  29. package/assets/character-skin-authoring/scripts/eyelid_morph.py +171 -0
  30. package/assets/character-skin-authoring/scripts/eyelid_patch.py +173 -0
  31. package/assets/character-skin-authoring/scripts/face_atlas.py +51 -0
  32. package/assets/character-skin-authoring/scripts/geometry_repair.py +112 -0
  33. package/assets/character-skin-authoring/scripts/glb_arrays.py +13 -0
  34. package/assets/character-skin-authoring/scripts/glb_output.py +21 -0
  35. package/assets/character-skin-authoring/scripts/inspect_skin_point.py +66 -0
  36. package/assets/character-skin-authoring/scripts/normal_patch_height.py +38 -0
  37. package/assets/character-skin-authoring/scripts/prepare_face_reference.py +23 -0
  38. package/assets/character-skin-authoring/scripts/regional_atlas.py +51 -0
  39. package/assets/character-skin-authoring/scripts/select_preview.py +90 -0
  40. package/assets/character-skin-authoring/scripts/skin_views.py +267 -0
  41. package/assets/character-skin-authoring/scripts/surface_pbr_maps.py +46 -0
  42. package/assets/character-skin-authoring/scripts/texture_repair.py +98 -0
  43. package/assets/character-skin-authoring/scripts/vertex_detail_support.py +80 -0
  44. package/assets/character-skin-authoring/scripts/vision_blend_materials.py +74 -0
  45. package/assets/character-skin-authoring/scripts/vision_materials.py +276 -0
  46. package/assets/character-skin-authoring/scripts/workspace_paths.py +18 -0
  47. package/assets/metahuman/src/work/metahuman/MetaHumanActor.ts +580 -0
  48. package/assets/metahuman/src/work/metahuman/MetaHumanLabHooks.ts +122 -0
  49. package/assets/metahuman/src/work/metahuman/MetaHumanMaterials.ts +500 -0
  50. package/assets/metahuman/src/work/metahuman/MetaHumanSpeech.ts +242 -0
  51. package/assets/metahuman/src/work/metahuman/StudioEnvironment.ts +142 -0
  52. package/assets/metahuman/tools/lipsync/make_line.py +181 -0
  53. package/assets/metahuman/tools/metahuman/README.md +21 -0
  54. package/assets/metahuman/tools/metahuman/e2e/eval.mjs +34 -0
  55. package/assets/metahuman/tools/metahuman/e2e/probes/arm-pose-check.js +32 -0
  56. package/assets/metahuman/tools/metahuman/e2e/probes/bone-displacement.js +26 -0
  57. package/assets/metahuman/tools/metahuman/e2e/probes/clip-shoulder.js +41 -0
  58. package/assets/metahuman/tools/metahuman/e2e/probes/grafted-bones.js +11 -0
  59. package/assets/metahuman/tools/metahuman/e2e/probes/helper-follow-descent.js +38 -0
  60. package/assets/metahuman/tools/metahuman/e2e/probes/helper-follow-sweep.js +31 -0
  61. package/assets/metahuman/tools/metahuman/e2e/probes/helper-joints-inspect.js +10 -0
  62. package/assets/metahuman/tools/metahuman/e2e/probes/outfit-bad-vertices.js +26 -0
  63. package/assets/metahuman/tools/metahuman/e2e/probes/outfit-body-material.js +7 -0
  64. package/assets/metahuman/tools/metahuman/e2e/probes/outfit-own-skeleton.js +8 -0
  65. package/assets/metahuman/tools/metahuman/e2e/probes/shirt-bone-ownership.js +25 -0
  66. package/assets/metahuman/tools/metahuman/e2e/probes/shirt-edge-stretch.js +26 -0
  67. package/assets/metahuman/tools/metahuman/e2e/probes/shirt-long-edges.js +23 -0
  68. package/assets/metahuman/tools/metahuman/e2e/probes/walk-sampler.js +105 -0
  69. package/assets/metahuman/tools/metahuman/e2e/shot.mjs +125 -0
  70. package/assets/metahuman/tools/metahuman/e2e/walk-probe.mjs +43 -0
  71. package/assets/metahuman/tools/metahuman/merge-skeletons.mjs +69 -0
  72. package/assets/metahuman/tools/metahuman/package.json +13 -0
  73. package/assets/metahuman/tools/metahuman/prepare.sh +25 -0
  74. package/assets/metahuman/tools/metahuman/reorder-body-first.mjs +20 -0
  75. package/assets/metahuman/tools/metahuman/smooth-outfit-weights.mjs +48 -0
  76. package/assets/metahuman/tools/metahuman/strip-draco.mjs +32 -0
  77. package/assets/metahuman/tools/metahuman/transfer-skin-weights.mjs +90 -0
  78. package/dist/assets/voxelize-glb.d.ts +5 -1
  79. package/dist/assets/voxelize-glb.js +1 -0
  80. package/dist/assets/voxelize-glb.js.map +1 -1
  81. package/dist/assets/vxl-summary.js +2 -2
  82. package/dist/commands/forge.js +3 -1
  83. package/dist/commands/forge.js.map +1 -1
  84. package/dist/commands/generate.d.ts +62 -0
  85. package/dist/commands/generate.js +241 -2
  86. package/dist/commands/generate.js.map +1 -1
  87. package/dist/commands/init.js +2 -1
  88. package/dist/commands/init.js.map +1 -1
  89. package/dist/commands/judge.js +5 -0
  90. package/dist/commands/judge.js.map +1 -1
  91. package/dist/commands/verify.js +7 -0
  92. package/dist/commands/verify.js.map +1 -1
  93. package/dist/editor/asset-viewer.js +10 -5
  94. package/dist/editor/asset-viewer.js.map +1 -1
  95. package/dist/generate/audio-file.d.ts +6 -0
  96. package/dist/generate/audio-file.js +42 -0
  97. package/dist/generate/audio-file.js.map +1 -0
  98. package/dist/generate/model.js +3 -3
  99. package/dist/generate/model.js.map +1 -1
  100. package/dist/generate/music.d.ts +4 -3
  101. package/dist/generate/music.js +6 -23
  102. package/dist/generate/music.js.map +1 -1
  103. package/dist/generate/prop.d.ts +19 -1
  104. package/dist/generate/prop.js +34 -6
  105. package/dist/generate/prop.js.map +1 -1
  106. package/dist/generate/stream.d.ts +20 -0
  107. package/dist/generate/stream.js +15 -1
  108. package/dist/generate/stream.js.map +1 -1
  109. package/dist/scaffold/asset-pipeline-skill-files.js +7 -2
  110. package/dist/scaffold/asset-pipeline-skill-files.js.map +1 -1
  111. package/dist/scaffold/character-rigging-skill-files.d.ts +2 -0
  112. package/dist/scaffold/character-rigging-skill-files.js +17 -0
  113. package/dist/scaffold/character-rigging-skill-files.js.map +1 -0
  114. package/dist/scaffold/character-skin-skill-files.d.ts +2 -0
  115. package/dist/scaffold/character-skin-skill-files.js +49 -0
  116. package/dist/scaffold/character-skin-skill-files.js.map +1 -0
  117. package/dist/scaffold/metahuman-skill-files.d.ts +21 -0
  118. package/dist/scaffold/metahuman-skill-files.js +306 -0
  119. package/dist/scaffold/metahuman-skill-files.js.map +1 -0
  120. package/dist/scaffold/project-files.js +28 -19
  121. package/dist/scaffold/project-files.js.map +1 -1
  122. package/dist/scaffold/project.js +6 -0
  123. package/dist/scaffold/project.js.map +1 -1
  124. package/dist/scaffold/skill-files.js +5 -2
  125. package/dist/scaffold/skill-files.js.map +1 -1
  126. package/dist/telemetry/command-context.d.ts +23 -0
  127. package/dist/telemetry/command-context.js +11 -0
  128. package/dist/telemetry/command-context.js.map +1 -1
  129. package/dist/telemetry/report.d.ts +1 -0
  130. package/dist/telemetry/report.js +4 -1
  131. package/dist/telemetry/report.js.map +1 -1
  132. package/dist/tools/bundles.js +8 -0
  133. package/dist/tools/bundles.js.map +1 -1
  134. package/dist/verify/browser.js +25 -0
  135. package/dist/verify/browser.js.map +1 -1
  136. package/dist/verify/categories.d.ts +16 -0
  137. package/dist/verify/categories.js +51 -0
  138. package/dist/verify/categories.js.map +1 -0
  139. package/package.json +10 -7
package/README.md CHANGED
@@ -217,7 +217,7 @@ browser reloads itself on every change and the assets panel shows each generatio
217
217
  | `bitmagic trailer publish <file.mp4> [--no-attach]` | Upload a trailer (`.mp4`/`.webm`, ≤ 200 MiB, header-checked so a renamed image is refused before upload) to the CDN under `game-media/<gameId>/` and put it at the front of the game page gallery. Re-running replaces the previous trailer (remembered in `.bitmagic/trailer/published.json`); the page is capped at 12 media items. Works before the first publish — the page goes live on the first `bitmagic publish --visibility public`. `--no-attach` uploads only. |
218
218
  | `bitmagic forge --prompt "..." [--city\|--dungeon\|--platformer\|--freeform] [--name <name>] [--resume <jobId>]` | Design and bake a whole playable level into `world.json`. Costs sparks, and takes many minutes — the bake alone can take twenty. `--resume <jobId>` picks a failed run back up from the last step that finished, rather than redoing the whole thing. |
219
219
  | `bitmagic forge --edit "..." [--level <id or name>]` | Change a detail of a level this project forged — a door style, the light fixtures, a prop — without re-forging it. The stored design is patched and only what the change touches is re-made; the level is replaced in place. See [Editing a forged level](#editing-a-forged-level). |
220
- | `bitmagic tools <list\|path\|install> [bundle]` | The script bundles the CLI ships for skills that drive Blender or Python (`blender-level`, `unity-import`): what they are, where this install keeps them, and `install` to copy one into the project's `tools/`. Local, no network, no sparks. |
220
+ | `bitmagic tools <list\|path\|install> [bundle]` | The script bundles the CLI ships for skills that drive Blender, Python or Node (`blender-level`, `unity-import`, `metahuman`): what they are, where this install keeps them, and `install` to copy one into the project's `tools/`. Local, no network, no sparks. |
221
221
  | `bitmagic levels <list\|add\|rename\|set-start\|remove> ...` | Manage the levels `forge` creates: which one boots, their names, registering a bake you already have, deleting one. Local, no network, no sparks. See **Managing levels** below. |
222
222
 
223
223
  Every command exits non-zero on failure with a single-line message (no stack trace) and, where a
@@ -531,6 +531,32 @@ before. Without a prior user gesture the browser blocks autoplay, and the engine
531
531
  immediately — so the cutscene silently does not run. `engine/agent-docs/video-cutscenes.md` covers
532
532
  that and what to freeze while a cutscene plays.
533
533
 
534
+ ## Spoken lines
535
+
536
+ `bitmagic generate speech --text "Halt, traveler. You shall not pass."` speaks a line, writes it
537
+ into `world.json` as an audio asset, and prints the `engine.playSound("<id>")` call that plays it.
538
+ Unlike a music bed it is registered as a **one-shot, not a loop** — dialogue plays when something
539
+ triggers it.
540
+
541
+ `--voice` picks who says it: `narrator` (the default), `male`, `female`, or a raw Eleven Labs
542
+ `voice_id` if you have cloned one of your own. `--model` switches model, and `--stability`,
543
+ `--similarity-boost`, `--style` and `--speed` are passed through to Eleven Labs when you want them;
544
+ leave them alone and Eleven Labs' own defaults apply.
545
+
546
+ It is **billed per second of audio produced**, not per character of text. A sentence costs about a
547
+ spark. The estimate charged up front comes from the length of the text; what you actually pay comes
548
+ from the finished audio.
549
+
550
+ Like `generate music`, it can write a file instead of a game asset:
551
+
552
+ ```bash
553
+ bitmagic generate speech --text "Halt, traveler." --out line.mp3 --no-world
554
+ ```
555
+
556
+ `--out` saves the MP3, `--no-world` leaves `world.json` untouched. That pair is the input to the
557
+ MetaHuman lip-sync tool (`tools/lipsync/make_line.py --audio line.mp3`), where a spoken line is
558
+ cut into mouth cues rather than played as-is. Use `--out` on its own to keep both.
559
+
534
560
  ## Reference image
535
561
 
536
562
  When a game has a strong setting — a tropical island, a neon city — one accepted concept image can
@@ -1198,6 +1224,7 @@ parametric builder produces the model; the engine voxelizes it and derives its p
1198
1224
 
1199
1225
  ```bash
1200
1226
  bitmagic generate vehicle --prompt "a rusty orange pickup with flame decals"
1227
+ bitmagic generate vehicle --preset ? # list the free presets
1201
1228
  bitmagic generate vehicle --preset VAN # a standard vehicle: skips the designer, free
1202
1229
  ```
1203
1230
 
@@ -1306,6 +1333,15 @@ by the engine's `MeshLevel`. The scaffolded `building-levels-in-blender` skill w
1306
1333
  its first section says when that path applies (a frontier model, and Blender installed) and sends
1307
1334
  everything else to `bitmagic forge`.
1308
1335
 
1336
+ ## Importing a MetaHuman
1337
+
1338
+ An Epic MetaHuman (a UE 5.7 glTF export from the smorchj/metahuman-to-glb pipeline) can be the
1339
+ game's playable high-res character without voxelization. The scaffolded `importing-metahumans` skill
1340
+ carries the whole path — `bitmagic tools install metahuman` lands the GLB preparation scripts, the
1341
+ runtime module (`src/work/metahuman/`), the lip-sync tool and a headless verification rig — and names
1342
+ each failure a raw export produces (arms crossed into the chest, a torn shirt, hair left behind by a
1343
+ head turn, tinted skin, black eyes) with the fix that resolved it.
1344
+
1309
1345
  ## Art style
1310
1346
 
1311
1347
  A 3D game is voxel-built unless `src/work/game.json` says otherwise:
@@ -21,7 +21,9 @@ CANDIDATES = [
21
21
  INSTALL_HINT = (
22
22
  'Blender was not found. `building-levels-in-blender` builds the level with it — install it '
23
23
  '(macOS: brew install --cask blender · Debian/Ubuntu: sudo apt install blender · '
24
- 'Windows: winget install BlenderFoundation.Blender) and set BLENDER=<path> if it lives somewhere else.'
24
+ 'Windows: winget install BlenderFoundation.Blender) and set BLENDER=<path> if it lives somewhere else. '
25
+ 'If installing it is not an option here, take the forge route instead: `bitmagic forge` builds the level '
26
+ 'without Blender (see the level rows in choosing-asset-pipelines).'
25
27
  )
26
28
 
27
29
 
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: rigging-characters
3
+ description: Rig an unrigged character or diagnose and repair a deforming character in local Blender, including skeletal fit, skin weights, hands and optional appendages. Use for GLB character animation problems, not level construction or paid mesh generation.
4
+ ---
5
+
6
+ # Rigging and repairing characters
7
+
8
+ Produce a reusable animated character whose exported file survives real motion. This is an
9
+ agent-guided workflow, not a universal automatic rigger. It requires local Blender and an agent
10
+ that can inspect rendered images. It uses no paid service, private asset catalog or hosted tool.
11
+ With no visual inspection capability, produce a technical diagnostic and leave visual QA pending.
12
+
13
+ ## Start with a small, explicit workspace
14
+
15
+ Read the project's AGENTS.md and identify the input GLB or blend file, desired animation clips,
16
+ runtime skeleton constraints and output location. Infer these from the project before asking.
17
+ Copy the source to a per-character work directory; never overwrite the only original. Record its
18
+ SHA-256. Keep the repair profile, scripts, candidate, review images and verdict together there.
19
+ Do not put per-character work inside this shipped skill: GDK upgrade refreshes its files.
20
+
21
+ Find Blender with `blender --version`, BLENDER_BIN, or the installed application executable
22
+ (macOS commonly `/Applications/Blender.app/Contents/MacOS/Blender`; Windows installations commonly
23
+ live under Program Files/Blender Foundation). Quote paths. The helpers run in Blender's Python;
24
+ they need no pip packages. Check operator availability in the installed version instead of
25
+ assuming all exporters have identical flags. Do not install or switch an engine as a side effect.
26
+
27
+ Run the bundled helper using its actual location in this skill:
28
+
29
+ ```sh
30
+ blender --background --factory-startup --python-exit-code 1 --python /path/to/rigging-characters/scripts/rig_tool.py -- inspect --input /path/to/source.glb --output /path/to/work/inspect.json
31
+ ```
32
+
33
+ The inspection reports Blender-space bounds, rest bones, actions, materials and skin-weight
34
+ problems. Its `technical_only` result is not a visual approval. Render a baseline with the same
35
+ helper's `render` command; use `--help` for view, frame and named-action options. Inspect front,
36
+ side and back views with both mesh and skeleton visible in Blender when diagnosing placement.
37
+ The helper's default front is Blender -Y; verify the imported character actually faces it.
38
+
39
+ ## Pick the smallest repair that addresses the observed defect
40
+
41
+ - **No usable rig:** read [new rigs and animation compatibility](references/new-rig.md).
42
+ - **Existing rig, bad motion:** read [diagnosis and targeted repairs](references/repairs.md).
43
+ - **Hands, eyes, tails or wings:** use the relevant section of
44
+ [secondary articulation](references/articulation.md), only if the task needs it.
45
+
46
+ Separate source-anatomy errors from skeleton placement, rest-pose mismatch, weight leakage and
47
+ animation/retargeting errors. A good T-pose alone proves none of these. Do not regenerate an
48
+ expensive mesh to solve a joint or skinning defect. Do not replace a custom skeleton with a
49
+ standard one merely to make a tool accept it. Preserve the game's bone-name and clip contract.
50
+
51
+ Change one causal variable at a time. Keep bind data and mesh rest pose consistent when changing
52
+ bones. For weight-only repairs, preserve positions, topology, UVs, materials and animations;
53
+ normalize after editing, and measure displacement of regions that should stay still.
54
+
55
+ ## Review the exported result, then deliver
56
+
57
+ Read [verification and runtime handoff](references/verification.md). Reimport the candidate GLB
58
+ into a fresh Blender process and inspect it under the requested clips. Test both left and right
59
+ strides, intermediate phases, idle, and an extreme required motion such as punching or kicking.
60
+ Inspect hands close up during open/fist/grip, not only in a full-body render. Check the actual
61
+ runtime as well when available: bind matrices, coordinate conversion and clip application can
62
+ differ from the authoring scene. Keep numerical and visual verdicts distinct.
63
+
64
+ Record accepted limitations explicitly. A failed repair remains a candidate; do not relabel a
65
+ passing body stage as a fully approved character while eyes or fingers still fail. If meaningful
66
+ iterations stop improving the result, explain the remaining defect and recommend reconstruction
67
+ or rejection according to the user's tolerance. Do not silently remove defining anatomy.
68
+
69
+ Report the source and final hashes, skeleton/animation compatibility, changed regions, verified
70
+ motions and remaining limitations. Deliver the candidate GLB and reproducible repair files.
71
+ Publishing or replacing a catalog entry is a separate action governed by the user's instructions.
72
+
73
+ ## Keep work bounded and context small
74
+
75
+ Load only the relevant reference. Execute inspection/rendering/validation in scripts and read
76
+ compact reports; do not repeatedly paste whole meshes, logs or full project histories. Save the
77
+ current finding and next experiment per character so another agent can resume without the chat.
78
+ Reuse unchanged hash-bound evidence; rerun affected checks and the final integration check after
79
+ a change. Parallel workers need separate outputs and exclusive ownership of each character.
80
+ Separate headless Blender processes can coexist; bound them by available memory and CPU.
@@ -0,0 +1,55 @@
1
+ # Secondary articulation
2
+
3
+ ## Fingers
4
+
5
+ Count the actual digits on each hand, including the thumb. Four-digit characters are valid;
6
+ never add an invisible fifth finger just to match a human naming convention. Fit a chain inside
7
+ each separated digit (usually up to three joints); use fewer for short or rigid digits. Parent
8
+ chains to the hand, derive curl axes from the palm plane and each digit's rest direction, and
9
+ calibrate left/right signs separately. The thumb needs opposition as well as curl.
10
+
11
+ Assign weights by connected anatomy/geodesic neighborhood, not nearest bone alone: adjacent
12
+ fingers are close in space. Keep palm influence at the knuckles and smooth between phalanges.
13
+ Inspect both hands in open, half curl, fist and grip; test punch and walk with fists. Look for
14
+ flipped triangles, stretched strips, palm collapse and thumb penetration. Limit range where
15
+ appropriate and record it. Fused digits may support a grouped paddle, but label that capability
16
+ honestly; independent fingers require geometry that can deform independently.
17
+
18
+ ## Tails and wings
19
+
20
+ Keep the original appendage as a separate skinned mesh, partitioned at its anatomical root.
21
+ Preserve source materials, UVs and rest alignment. Cap the body opening; when hidden there must
22
+ not be a long stump, hole or stretched strip. Fit enough segments to represent the appendage's
23
+ curvature and intended bend. Parent a tail root to pelvis/hips; wing roots commonly attach to
24
+ chest/upper spine. A four-wing character needs four identifiable chains, not two accidental
25
+ groups. Do not infer movement behavior from bone count; runtime decides cat-tail versus
26
+ crocodile-tail motion. Keep main-body animation from accidentally animating added chains.
27
+
28
+ Test visible/hidden, root alignment in walking, maximum intended bend/flap and folds. Partition
29
+ fused geometry conservatively: if anatomy cannot be separated without damaging body silhouette,
30
+ leave it unresolved and explain rather than claiming a finished optional attachment.
31
+
32
+ ## Eyes
33
+
34
+ Preserve separate original-eye objects for a static alternative. Cut at the actual convex eye
35
+ surface/socket boundary, using shape, depth, normals and painted features together. White-only
36
+ selection misses pupils that meet the boundary; a bounding ellipse can cut eyelids and cheeks.
37
+ Do not include the upper socket wall in the pupil surface. Avoid a visibly protruding spherical
38
+ replacement when the original eye is shallow or oval.
39
+
40
+ An oval fixed eye can move its iris/pupil in surface coordinates or a procedural material while
41
+ keeping geometry stationary. Rotating a stretched sphere distorts the fit. Wet reflections need
42
+ a suitable glossy/clearcoat material and actual lighting/environment in runtime, not a painted
43
+ white dot. Preserve the original pupil style unless asked to change it. Blinking needs eyelid
44
+ geometry/morphs or a carefully fitted mask; gaze support does not imply blink support.
45
+
46
+ ## Metadata handoff
47
+
48
+ Inspect any existing asset metadata and preserve its schema. Do not invent a Bitmagic engine
49
+ API or assume support merely because GLB extras can store data. For a new contract, provide a
50
+ versioned sidecar manifest and optional mirrored GLB extras with exact object/bone names:
51
+ body nodes, static/dynamic eye nodes, gaze mechanism, material parameters, blink availability,
52
+ hand digit chains/curl axes/ranges, appendage nodes/chains/root parents and default visibility.
53
+ State the coordinate space and angle units. Include a capability as false when unsupported.
54
+ Runtime may need code to hide alternatives, drive eyes or animate added bones; a manifest alone
55
+ does not implement those controls. Keep a usable neutral appearance without custom shaders.
@@ -0,0 +1,51 @@
1
+ # New rigs and animation compatibility
2
+
3
+ ## Establish coordinates and anatomy first
4
+
5
+ glTF uses Y-up; Blender imports into Z-up. Measure in one named space and transform explicitly.
6
+ Report units and the asset's front direction. Do not infer hips from half of the total bounding
7
+ height: hats, antennae, capes and tails distort it. Locate pelvis, leg sockets, knees, ankles,
8
+ shoulder sockets, elbows and wrists from front AND side images. Put joints inside the limb volume.
9
+ Use asymmetric coordinates if the source is asymmetric; do not mirror a bad fit blindly.
10
+
11
+ Choose an existing compatible project rig/animation as the reference when available. There is
12
+ no skeleton bundled here and no assumed private Mixamo file. Without a reference, construct a
13
+ simple anatomical rig using Blender edit bones: root/hips, spine/chest, neck/head, bilateral
14
+ upper/lower arms/hands and upper/lower legs/feet. Add toes or fingers only when needed. Record
15
+ each bone's parent, head, tail and roll in a JSON profile, in armature-local Blender coordinates.
16
+ Create bones with `armature.edit_bones.new`, assign head/tail, then parents in a second pass.
17
+ Use connected bones only where heads really coincide with parent tails.
18
+
19
+ For a fresh rig, identity object transforms in a common coordinate space simplify binding. If
20
+ transforms are applied, apply them before binding and verify the visible shape is unchanged.
21
+ Never blindly apply transforms to an already animated skeleton or copy its rest matrices into a
22
+ different coordinate space. Preserve the input and build a new candidate.
23
+
24
+ Automatic weights (`bpy.ops.object.parent_set(type='ARMATURE_AUTO')`, mesh selected and armature
25
+ active in Object mode) are a starting point. They can fail or leak across touching garments,
26
+ fingers and appendages. Check the armature modifier, actual deform groups, unweighted vertices,
27
+ weight sums and influence count. Diagnose failures rather than exporting an unbound mesh.
28
+ Split semantic accessories before weighting when their topology confuses the body solution.
29
+
30
+ ## Shared clips with per-character skeletons
31
+
32
+ Custom bone lengths and joint positions can share animation semantics, but identical names are
33
+ not sufficient. Retarget rotations relative to each source and target rest orientation, mapping
34
+ parents in hierarchy order. Keep target rest translations for limb lengths. Treat root motion
35
+ and scale separately; scaling all local bone translations is not a safe retargeting method.
36
+ Validate mapping, missing bones, handedness and rotation order with one diagnostic pose before
37
+ baking a clip. If using world-space rotation deltas, solve each target local rotation from the
38
+ already evaluated target parent; do not apply the source world rotation as a local quaternion.
39
+
40
+ Do not copy raw quaternion channels between different rest rolls. Bake an evaluated compatible
41
+ action when the target engine cannot reproduce constraints or retargeting. The GLB must carry
42
+ the correct inverse bind matrices for the final rest skeleton. Reimport it and check the same
43
+ poses; Blender constraints and drivers are not an engine contract.
44
+
45
+ ## Rest-pose correction
46
+
47
+ Wide stance often needs narrower hip-to-ankle placement and rebinding, or a mesh rest correction
48
+ baked with an Armature modifier followed by a new matching rest skeleton. Keep the original
49
+ surface intact as far as possible and inspect the crotch and knees after the change. Moving
50
+ only pose bones does not fix the underlying bind pose. Likewise, an A-pose mesh cannot be bound
51
+ as if it were a T-pose without reconciling the two.
@@ -0,0 +1,44 @@
1
+ # Diagnose before editing
2
+
3
+ Use the same camera, clip and phases before/after. First inspect the skeleton overlay; then
4
+ isolate bone rotations and paint weights to distinguish bad pivots from influence leakage.
5
+
6
+ | Symptom | Check | Typical bounded repair |
7
+ |---|---|---|
8
+ | Feet stay wide during walk | Rest ankle spacing, hip socket positions, imported clip rest convention | Narrow/rest-correct and rebind, or retarget to the real target stance; inspect crotch/knee folds |
9
+ | Palms face forward at idle | Forearm roll, wrist axes, mesh palm normal, source animation convention | Distribute twist along forearm and hand while preserving their alignment; derive sign/angle from geometry |
10
+ | Waist bends as part of thigh | Upper-leg heads too high, pelvis placement, abdomen thigh weights | Lower anatomical leg sockets and move pelvis/garment weights out of the thighs |
11
+ | Arms disappear into torso | Shoulder span, elbow positions, chest/back accessories affecting fit | Fit actual arm sockets; separate cape/feathers from anatomy when calculating weights |
12
+ | Upper back swings sideways | Opposite-arm weights on chest/back, shoulder hierarchy, cape influence | Remove cross-arm influence from the affected region; retain appropriate spine/shoulder motion |
13
+ | Long triangular fans | Sparse topology spanning different moving regions, fused anatomy, sharp weight seam | Smooth a bounded weight transition or separate semantic geometry; do not mask a topological bridge with more weights |
14
+ | Bent wrist while presenting fist | Shoulder/elbow path versus hand orientation | Move the upper arm outward for clearance, rotate forearm around its long axis, keep wrist aligned; overlap transitions |
15
+
16
+ These are diagnoses to test, not fixed prescriptions. A 90-degree palm correction that worked
17
+ on one mesh can break another with different bone roll. A shoulder pose that clears a jacket
18
+ in one animation is not a structural repair for incorrect jacket weights in every animation.
19
+
20
+ ## Weight edits
21
+
22
+ Select regions using anatomy plus geometry/material evidence. World-space boxes alone can
23
+ accidentally include a touching hand or back accessory. Inspect front/side/back masks. Work on
24
+ the actual deforming mesh, not a duplicate displayed only in one scene. Keep unmapped vertices
25
+ explicitly flagged. Normalize weights after each change; limit influences to the target engine's
26
+ budget (commonly four), without discarding a necessary influence merely to pass a check.
27
+
28
+ Weight-only edits should leave vertex positions, faces, UVs and material assignments unchanged.
29
+ Use spatial correspondence with documented tolerance when export duplicates seam vertices;
30
+ vertex indices need not survive GLB re-export. Compare bone influence by name, not numeric joint
31
+ index, because exporters can reorder the joint array. Measure the maximum drift, not only means.
32
+
33
+ If moving a joint in Edit mode, reconcile the rest pose and bind matrices. Existing animation
34
+ channels may need retargeting/rebaking. First prove bind-pose equivalence, then verify the clips.
35
+ Keep an independent numeric test of regions outside the edit mask. Do not repeatedly widen the
36
+ mask until a local symptom disappears at the expense of the rest of the body.
37
+
38
+ ## Choosing when to stop
39
+
40
+ Retain failed trials and write why each failed. Set a small initial experiment budget, expand it
41
+ only when the latest trial demonstrates progress. Fused cape/abdomen-to-thigh bridges, missing
42
+ fingers and self-overlapping eye surfaces may require reconstruction beyond a skinning repair.
43
+ Explain that distinction. Minor creases may meet the user's tolerance; collapsed anatomy does
44
+ not become acceptable because technical checks pass.
@@ -0,0 +1,49 @@
1
+ # Verification and runtime handoff
2
+
3
+ ## Technical checks
4
+
5
+ Verify finite vertices and transforms; valid indices; material/texture availability; a usable
6
+ armature modifier and deform weights; no unweighted skin vertices; normalized nonnegative
7
+ weights; engine influence budget; acyclic bone hierarchy; rest alignment and inverse binds.
8
+ The bundled inspection helper reports common Blender-side issues, not an exhaustive glTF
9
+ validator. Run a glTF validator when available, particularly after editing GLB bytes directly.
10
+ An invalid buffer length must not be ignored just because one viewer tolerates it.
11
+
12
+ Compare unchanged regions and data against the source. For weight-only work, compare positions,
13
+ UVs, triangles and materials; after topology changes, use bidirectional surface distance and
14
+ texture correspondence with a stated tolerance. Check every intended clip survives export and
15
+ that constraints have been baked when necessary. Never sum cached approvals for different file
16
+ hashes as evidence for the current candidate.
17
+
18
+ ## Visual pass
19
+
20
+ Use a fresh import of the exported file. Render the same source/candidate poses under neutral
21
+ lighting with front, side and back views, fit the complete animated bounds, and inspect enlarged
22
+ problem areas. Select stages that expose the failure, not only flattering camera angles.
23
+ For ordinary locomotion, sample both strides and intermediate frames; 12 evenly spaced walk
24
+ phases and 12 run phases are a useful baseline, not a universal success threshold. Add idle,
25
+ punch, kick and jump when those clips are actually available/required. Missing clips must be
26
+ reported; procedural joint probes are diagnostics, not equivalent animation coverage.
27
+
28
+ `rig_tool.py render` can select an imported action and frame. Its actions are Blender import
29
+ names: inspect first. Multi-armature scenes require explicit armature selection for action
30
+ assignment. glTF import may also create NLA tracks; the helper disables these when selecting an
31
+ action so two clips do not blend accidentally. If layered-action slot assignment is ambiguous,
32
+ it stops; select the slot explicitly rather than rendering the wrong pose.
33
+
34
+ Load the final file in the target runtime if accessible. Test existing shared animations,
35
+ root motion, materials, static/dynamic alternatives, finger controls and hidden appendages.
36
+ Record the loaded hash/URL so a stale cached asset cannot pass the new repair. For KTX2 or other
37
+ texture conversion, compare decoded appearance and confirm the geometry, skin, clips and extras
38
+ remain intact. Texture compression is optional packaging, not a reason to rebuild a passing rig.
39
+
40
+ ## Durable review record
41
+
42
+ Keep a small JSON or Markdown report alongside the deliverable: input/candidate SHA-256, Blender
43
+ version, units/front direction, diagnosed cause, changes, numeric results and tolerances,
44
+ reviewed image paths and clip phases, runtime checks, limitations, and verdict. Distinguish
45
+ technical_pass, visual_pending, needs_changes and accepted_with_limits. Explicit user rejection
46
+ overrides older approval. Do not replace the production asset until the intended scope passes.
47
+
48
+ Prefer a compact per-character profile and executable repair over a transcript of experiments.
49
+ This lets the next agent reproduce the result without loading the entire batch history.
@@ -0,0 +1,197 @@
1
+ """Local Blender inspection/render helper. No edits to input files, no network."""
2
+ import argparse
3
+ import hashlib
4
+ import json
5
+ import math
6
+ from pathlib import Path
7
+ import sys
8
+
9
+ import bpy
10
+ from mathutils import Vector
11
+
12
+
13
+ def load_asset(path):
14
+ if path.suffix.lower() == '.blend':
15
+ # Never load startup UI/scripts from an asset file.
16
+ bpy.ops.wm.open_mainfile(filepath=str(path), load_ui=False, use_scripts=False)
17
+ elif path.suffix.lower() in ('.glb', '.gltf'):
18
+ bpy.ops.wm.read_factory_settings(use_empty=True)
19
+ bpy.ops.import_scene.gltf(filepath=str(path))
20
+ else:
21
+ raise ValueError('Input must be .blend, .glb or .gltf')
22
+
23
+
24
+ def bounds(objects):
25
+ depsgraph = bpy.context.evaluated_depsgraph_get()
26
+ points = []
27
+ for obj in objects:
28
+ evaluated = obj.evaluated_get(depsgraph)
29
+ mesh = evaluated.to_mesh()
30
+ try:
31
+ points.extend(evaluated.matrix_world @ v.co for v in mesh.vertices)
32
+ finally:
33
+ evaluated.to_mesh_clear()
34
+ if not points:
35
+ raise ValueError('No mesh vertices')
36
+ if any(not math.isfinite(x) for p in points for x in p):
37
+ raise ValueError('Nonfinite evaluated vertices')
38
+ return [min(p[i] for p in points) for i in range(3)], [max(p[i] for p in points) for i in range(3)]
39
+
40
+
41
+ def inspect(meshes, armatures, limit):
42
+ result = {'technical_only': True, 'coordinate_space': 'Blender world; Z up',
43
+ 'unit_scale': bpy.context.scene.unit_settings.scale_length,
44
+ 'bounds': bounds(meshes), 'meshes': [], 'armatures': [],
45
+ 'actions': [{'name': a.name, 'frames': list(a.frame_range),
46
+ 'slots': [s.identifier for s in getattr(a, 'slots', [])]}
47
+ for a in bpy.data.actions]}
48
+ for obj in meshes:
49
+ mesh = obj.data
50
+ mesh.calc_loop_triangles()
51
+ rigs = [m.object for m in obj.modifiers if m.type == 'ARMATURE' and m.object]
52
+ deform = {b.name for rig in rigs for b in rig.data.bones if b.use_deform}
53
+ groups = {g.index for g in obj.vertex_groups if g.name in deform}
54
+ unweighted = nonunit = excessive = invalid = 0
55
+ for v in mesh.vertices:
56
+ weights = [g.weight for g in v.groups if g.group in groups and g.weight > 0]
57
+ unweighted += not weights
58
+ nonunit += bool(weights) and abs(sum(weights) - 1) > 1e-4
59
+ excessive += len(weights) > limit
60
+ invalid += any(not math.isfinite(g.weight) or g.weight < 0 for g in v.groups)
61
+ result['meshes'].append({'name': obj.name, 'vertices': len(mesh.vertices),
62
+ 'triangles': len(mesh.loop_triangles),
63
+ 'armatures': [r.name for r in rigs],
64
+ 'unweighted_vertices': unweighted,
65
+ 'nonunit_weight_vertices': nonunit,
66
+ 'excess_influences_vertices': excessive,
67
+ 'invalid_weight_vertices': invalid,
68
+ 'materials': [m.name if m else None for m in mesh.materials],
69
+ 'uv_layers': [u.name for u in mesh.uv_layers]})
70
+ for rig in armatures:
71
+ result['armatures'].append({'name': rig.name, 'matrix_world': [list(r) for r in rig.matrix_world],
72
+ 'bones': [{'name': b.name, 'parent': b.parent.name if b.parent else None,
73
+ 'head_local': list(b.head_local), 'tail_local': list(b.tail_local),
74
+ 'matrix_local': [list(r) for r in b.matrix_local], 'deform': b.use_deform}
75
+ for b in rig.data.bones]})
76
+ return result
77
+
78
+
79
+ def select_action(args, armatures):
80
+ if not args.action:
81
+ return
82
+ rigs = [a for a in armatures if not args.armature or a.name == args.armature]
83
+ if len(rigs) != 1:
84
+ raise ValueError('Action selection requires exactly one armature; use --armature')
85
+ action = bpy.data.actions.get(args.action)
86
+ if action is None:
87
+ raise ValueError('Unknown action: ' + args.action)
88
+ rig = rigs[0]
89
+ animation = rig.animation_data_create()
90
+ for track in animation.nla_tracks:
91
+ track.mute = True
92
+ animation.action = action
93
+ slots = list(getattr(action, 'slots', []))
94
+ if slots:
95
+ choices = [s for s in slots if s.identifier == args.slot] if args.slot else slots
96
+ if len(choices) != 1:
97
+ raise ValueError('Action has ambiguous slots; use --slot from inspection output')
98
+ animation.action_slot = choices[0]
99
+
100
+
101
+ def render(args, meshes, armatures, output):
102
+ select_action(args, armatures)
103
+ bpy.context.scene.frame_set(args.frame)
104
+ # Remove source cameras/lights from rendering only; never save back to source.
105
+ for obj in list(bpy.context.scene.objects):
106
+ if obj.type in ('LIGHT', 'CAMERA'):
107
+ bpy.data.objects.remove(obj, do_unlink=True)
108
+ lo, hi = bounds(meshes)
109
+ center = (Vector(lo) + Vector(hi)) * .5
110
+ extent = max(hi[i] - lo[i] for i in range(3))
111
+ if extent <= 0:
112
+ raise ValueError('Degenerate bounds')
113
+ front = Vector({'-Y': (0, -1, 0), '+Y': (0, 1, 0), '+X': (1, 0, 0), '-X': (-1, 0, 0)}[args.front])
114
+ direction = {'front': front, 'back': -front, 'side': Vector((-front.y, front.x, 0)),
115
+ 'three-quarter': (front + Vector((-front.y, front.x, .2))).normalized()}[args.view]
116
+ camera_data = bpy.data.cameras.new('ReviewCamera')
117
+ camera = bpy.data.objects.new('ReviewCamera', camera_data)
118
+ bpy.context.scene.collection.objects.link(camera)
119
+ camera.location = center + direction * extent * 3
120
+ camera.rotation_euler = (center - camera.location).to_track_quat('-Z', 'Y').to_euler()
121
+ camera_data.type = 'ORTHO'
122
+ camera_data.ortho_scale = extent * 1.35
123
+ camera_data.clip_start = max(extent / 10000, .00001)
124
+ camera_data.clip_end = extent * 20
125
+ scene = bpy.context.scene
126
+ scene.camera = camera
127
+ try:
128
+ scene.render.engine = 'BLENDER_EEVEE_NEXT' # Blender 4.x
129
+ except TypeError:
130
+ scene.render.engine = 'BLENDER_EEVEE' # Blender 5.x (and 3.x)
131
+ scene.render.resolution_x = scene.render.resolution_y = args.size
132
+ scene.render.resolution_percentage = 100
133
+ scene.render.image_settings.file_format = 'PNG'
134
+ scene.render.filepath = str(output)
135
+ scene.render.film_transparent = False
136
+ world = bpy.data.worlds.new('ReviewWorld')
137
+ world.use_nodes = True
138
+ world.node_tree.nodes['Background'].inputs[0].default_value = (.12, .12, .12, 1)
139
+ world.node_tree.nodes['Background'].inputs[1].default_value = .5
140
+ scene.world = world
141
+ for i, offset in enumerate([front * 2 + Vector((0, 0, 3)), -front + Vector((2, 0, 1))]):
142
+ light_data = bpy.data.lights.new('ReviewLight', 'AREA')
143
+ light_data.energy = extent * extent * (180 if i == 0 else 90)
144
+ light_data.shape = 'DISK'
145
+ light_data.size = extent * 2
146
+ light = bpy.data.objects.new('ReviewLight', light_data)
147
+ scene.collection.objects.link(light)
148
+ light.location = center + offset * extent
149
+ light.rotation_euler = (center - light.location).to_track_quat('-Z', 'Y').to_euler()
150
+ bpy.ops.render.render(write_still=True)
151
+ return {'visual_review': 'pending', 'action': args.action, 'armature': args.armature,
152
+ 'frame': args.frame, 'view': args.view, 'front': args.front,
153
+ 'bounds': [lo, hi], 'image': str(output)}
154
+
155
+
156
+ def main():
157
+ parser = argparse.ArgumentParser(description=__doc__)
158
+ parser.add_argument('command', choices=['inspect', 'render'])
159
+ parser.add_argument('--input', required=True, type=Path)
160
+ parser.add_argument('--output', required=True, type=Path)
161
+ parser.add_argument('--max-influences', type=int, default=4)
162
+ parser.add_argument('--action')
163
+ parser.add_argument('--armature')
164
+ parser.add_argument('--slot')
165
+ parser.add_argument('--frame', type=int, default=1)
166
+ parser.add_argument('--front', choices=['-Y', '+Y', '-X', '+X'], default='-Y')
167
+ parser.add_argument('--view', choices=['front', 'back', 'side', 'three-quarter'], default='front')
168
+ parser.add_argument('--size', type=int, default=768)
169
+ args = parser.parse_args(sys.argv[sys.argv.index('--') + 1:] if '--' in sys.argv else [])
170
+ source, output = args.input.resolve(), args.output.resolve()
171
+ sidecar = output.with_suffix(output.suffix + '.json')
172
+ if source in (output, sidecar) or output.exists() or (args.command == 'render' and sidecar.exists()):
173
+ raise ValueError('Use a fresh output path; input and existing results are never overwritten')
174
+ if args.size < 64 or args.max_influences < 1:
175
+ raise ValueError('Invalid render size or influence budget')
176
+ digest = hashlib.sha256(source.read_bytes()).hexdigest()
177
+ load_asset(source)
178
+ armatures = [o for o in bpy.context.scene.objects if o.type == 'ARMATURE']
179
+ custom_shapes = {bone.custom_shape for rig in armatures for bone in rig.pose.bones if bone.custom_shape}
180
+ meshes = [o for o in bpy.context.scene.objects if o.type == 'MESH' and o not in custom_shapes]
181
+ for shape in custom_shapes:
182
+ shape.hide_render = True
183
+ output.parent.mkdir(parents=True, exist_ok=True)
184
+ result = inspect(meshes, armatures, args.max_influences) if args.command == 'inspect' else render(args, meshes, armatures, output)
185
+ result.update(source=str(source), source_sha256=digest, blender=bpy.app.version_string)
186
+ result['excluded_bone_display_shapes'] = sorted(o.name for o in custom_shapes)
187
+ if hashlib.sha256(source.read_bytes()).hexdigest() != digest:
188
+ raise ValueError('Input changed during review')
189
+ target = output if args.command == 'inspect' else sidecar
190
+ with target.open('x') as stream:
191
+ json.dump(result, stream, indent=2)
192
+ stream.write('\n')
193
+ print(json.dumps({'report': str(target), 'source_sha256': digest}))
194
+
195
+
196
+ if __name__ == '__main__':
197
+ main()
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: character-skin-authoring
3
+ description: Use vision to author and review exposed-skin maps on textured 3D characters, including faces, hands and bodies. Prepare exact model views, annotate visible anatomy, project evidence into UV space, and review skin detail placement. Eye construction is a separate workflow.
4
+ ---
5
+
6
+ # Vision-led character skin authoring
7
+
8
+ The local agent makes anatomical/material decisions from images. Do not replace those decisions with RGB thresholds, world-height cutoffs, race/name rules, or another automated classifier. Deterministic tools handle projection and bookkeeping. The creator's existing agent subscription supplies vision; this workflow requires no hosted segmentation service.
9
+
10
+ ## Establish evidence
11
+
12
+ Locate the model, its physical height, materials, texture resolution and rig. Keep the original immutable and write a separate material revision. Inspect the original generated image when available, but make the **actual mesh and its textures** authoritative: the generated mesh may differ from that image.
13
+
14
+ Use the local `scripts/skin_views.py` helper to create unlit, occlusion-correct views with their UV correspondences. Read [the projection contract](references/projection.md) for commands and formats. It requires local Blender and a Python environment with NumPy/Pillow. The helper performs no semantic recognition. Verify orientation from the resulting images; its axes are not anatomical labels.
15
+
16
+ Start with a full-body survey from opposite directions. Then frame each exposed region closely enough to see its boundaries. Inspect the face from front and both sides; inspect both hands from palm and dorsal sides and oblique angles showing finger webs. Inspect ears, neck, chest, arms, legs, feet or any other exposed region actually present. Add cameras when existing views cannot show a surface. Do not infer a hidden palm from a dorsal hand image or project a front silhouette through to the back.
17
+
18
+ ## Author the maps using vision
19
+
20
+ For each viewed surface, annotate pixel polygons as `skin`, `dorsal_hand`, `palm`, `sole`, `lips`, `eyes`, `hair`, `nails`, or `non_skin`. `unknown` is explicit unresolved evidence. Use separate polygons for exclusions, ordered after enclosing polygons. Do not give an indistinct nail facial pores merely because the rest of the hand is clear. Record why a boundary is ambiguous; obtain a closer/different view before resolving it. Scars, freckles, tattoos and makeup usually remain skin with appearance annotations, not separate non-skin regions. Tight cloth is still cloth; green skin is still skin. If garment colour spills onto anatomically visible skin in the source albedo, record a texture defect separately from the semantic mask. A correct skin label cannot repair that colour.
21
+
22
+ Review the colored annotation overlay on the **same source image**, then review the **baked atlas reprojected onto every prepared view**, including unannotated side views. Use the dense visibility bake at a resolution appropriate to the source atlas; sparse splatting is a diagnostic, not a finished mask. Include the filename, image dimensions and source hash with every annotation set. If a tool displays a resized image, convert coordinates back to the recorded dimensions. Never claim to have visually reviewed an image you have not opened.
23
+
24
+ The helper deliberately leaves unobserved UV texels unknown and reports contradictory labels. Do not grow a skin mask across unknown areas to make a coverage statistic look good. Get additional views, increase sampling resolution, or use a reviewed Blender texture-paint correction. Mirrored/overlapping UVs can make conflicting anatomical labels impossible in one atlas: document that and create separate material/UV regions rather than average the labels.
25
+
26
+ ## Apply region-appropriate detail
27
+
28
+ For material application and engine integration, read [material application](references/materials.md). The [installed exporters](references/exporters.md) assemble reviewed inputs in an explicit external workspace. After coverage passes review, register landmarks and surface scale for each detail patch. Use licensed exemplars and retain attribution and modification provenance. The b3662 face pilot in this repository demonstrates registered scan detail; its coordinates are **not** a universal face template.
29
+
30
+ - Face: region-specific pores, fine relief, oiliness and pigment. Exclude eye surfaces, lashes, brows, lips and facial hair from the ordinary pore layer; preserve actual folds and identity.
31
+ - Dorsal hands/fingers: knuckle folds, tendons and finer hand texture. Nails receive their own response.
32
+ - Palms/soles: glabrous skin with appropriate creases/ridges and roughness; do not stamp facial follicles onto it.
33
+ - Neck, torso, limbs, ears: use suitable regional detail and scale. Preserve the character's natural variation; do not transplant a donor's beard, identity marks or complexion.
34
+
35
+ Blend patch boundaries in surface space and check physical scale. A silhouette or clothing boundary needs a coverage edge; two adjacent skin patches need a continuous transition. Keep skin nonmetallic and preserve clothing/armor materials. Use the existing registered detail/SSS renderer where available; do not invent new renderer hooks or claim the projection helper installs materials by itself.
36
+
37
+ ## Review and handoff
38
+
39
+ Inspect colored material regions and final shaded results separately. Use fixed cameras under diffuse, specular, side, studio and back lighting. Check full-body, face-sized and close detail views, plus a representative pose when the model is rigged. Confirm both hands, finger webs, nails, ears and every other exposed area; inspect clothing edges without revisiting unrelated deferred edits.
40
+
41
+ Record results per character and region: `reviewed`, `needs_view`, `conflict`, or `needs_detail_asset`. Archive source hashes, views, annotations, projected labels, atlas registration, licenses and review images together. Only call a character complete when its exposed regions are reviewed and the **applied** maps have been inspected. A parseable annotation file or a successful bake is not semantic validation.
42
+
43
+ Review the face and ocular exclusion before eye geometry/shading work; use the separate [optical-eye workflow](references/eyes.md) when that work is requested. Eye progress does not mark unresolved body skin as complete. This workflow is maintained in the GDK CLI assets and installed into supported agent skill directories from the same source. Keep per-character evidence and candidates outside those refreshed directories.