diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c5f52a..1b6b9d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Semantic Versioning. cheekbones are sculpted into the head instead of reading as separate spheres. - Character torsos taper from the chest to the waist and flare slightly into the hips, so shirts and jackets sit less like straight tubes. +- Character hands now have a wider palm, bent fingers with rounded tips and a + separate thumb instead of flat bars. - Tourists no longer appear to carry two backpacks: the photo-pose camera bag is hidden except while they are actually taking a picture. - Backpack shoulder straps are now flattened bands that sit outside the chest diff --git a/MEMORY.md b/MEMORY.md index b2e8477..3be4059 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -90,6 +90,11 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o 0.150→0.146, chest 0.170→0.168, upper chest 0.172→0.170. This is a subtle shirt taper, not a skeleton change; all proportions pass and `sheet_npc_baseline_compare-torso.jpg` records it. Max character is 4,754 tris / 4,584 verts. +- **NPC hand pass (0.9.4, 2026-11-02):** palm radius 0.046 at z .742, scale (.80,.62,.88); + fingers are `tube` paths from z .758 to .681–.694 with tip spheres, thumb is a bent tube. The + optional hand capture is `capture_npc_baseline.gd -- --view=hand` (8 frames, baseline stays 48). + `npc_firefighter` is now at 4,948 verts (52 below the 5,000 cap); do not add hand/face geometry + without replacing something. Grip offsets remain open. - **UI flow smoke (2026-11-02):** `smoke_ui_flows.gd` boots the real scene and exercises inspection allow/deny, normal vs emergency radio, Help filtering, wand allow/deny and review return-to-menu. Keep this smoke in the 0.9.6 diff --git a/TODO.md b/TODO.md index 835d763..6b0826a 100644 --- a/TODO.md +++ b/TODO.md @@ -116,9 +116,12 @@ and equipment fit. ### Hands, feet and clothing -- [ ] Refine hands and fingers enough to read at conversation distance without exceeding the - character triangle budget; check grip alignment for phone, cup, clipboard, camera, sign, - flashlight, wand and radio. +- [x] Refine hands and fingers enough to read at conversation distance without exceeding the + character triangle budget. (Wider palm, two-segment bent fingers with rounded tips, separate + thumb; optional `--view=hand` capture and `sheet_npc_hands_compare.jpg`.) +- [ ] Fix grip alignment for phone, cup, clipboard, camera, sign, flashlight, wand and radio + against the new palm/finger geometry. (Several props still occlude the palm or float beside + it in the hand capture.) - [ ] Audit shoes/boots for silhouette, grounding and contact with the floor; preserve the responder and firefighter footwear language. - [ ] Improve clothing layer transitions at collars, cuffs, plackets, waistbands, pockets, badges diff --git a/assets/models/blender/manifest.json b/assets/models/blender/manifest.json index f9a60a5..518154f 100644 --- a/assets/models/blender/manifest.json +++ b/assets/models/blender/manifest.json @@ -487,8 +487,8 @@ "accessory", "hair" ], - "triangles": 3762, - "vertices": 3474 + "triangles": 4042, + "vertices": 3894 }, "npc_coat": { "bounds_godot": { @@ -528,8 +528,8 @@ "accessory", "hair" ], - "triangles": 4754, - "vertices": 4584 + "triangles": 5034, + "vertices": 4814 }, "npc_dress": { "bounds_godot": { @@ -567,8 +567,8 @@ "accessory", "hair" ], - "triangles": 4180, - "vertices": 3863 + "triangles": 4460, + "vertices": 4191 }, "npc_firefighter": { "bounds_godot": { @@ -608,8 +608,8 @@ "accessory", "hair" ], - "triangles": 4624, - "vertices": 4583 + "triangles": 4904, + "vertices": 4948 }, "npc_guard": { "bounds_godot": { @@ -652,8 +652,8 @@ "accessory", "hair" ], - "triangles": 4326, - "vertices": 4443 + "triangles": 4606, + "vertices": 4679 }, "npc_hoodie": { "bounds_godot": { @@ -693,8 +693,8 @@ "accessory", "hair" ], - "triangles": 4584, - "vertices": 4294 + "triangles": 4864, + "vertices": 4506 }, "npc_jacket": { "bounds_godot": { @@ -734,8 +734,8 @@ "accessory", "hair" ], - "triangles": 4152, - "vertices": 3900 + "triangles": 4432, + "vertices": 4265 }, "npc_vest": { "bounds_godot": { @@ -775,8 +775,8 @@ "accessory", "hair" ], - "triangles": 4260, - "vertices": 4183 + "triangles": 4540, + "vertices": 4503 }, "palm_tree": { "bounds_godot": { diff --git a/assets/models/blender/npc/npc_casual.glb b/assets/models/blender/npc/npc_casual.glb index ed87176..7f4ff48 100644 Binary files a/assets/models/blender/npc/npc_casual.glb and b/assets/models/blender/npc/npc_casual.glb differ diff --git a/assets/models/blender/npc/npc_casual_ao.png b/assets/models/blender/npc/npc_casual_ao.png index 1bd500e..7429ec1 100644 Binary files a/assets/models/blender/npc/npc_casual_ao.png and b/assets/models/blender/npc/npc_casual_ao.png differ diff --git a/assets/models/blender/npc/npc_coat.glb b/assets/models/blender/npc/npc_coat.glb index fd922da..c3447e6 100644 Binary files a/assets/models/blender/npc/npc_coat.glb and b/assets/models/blender/npc/npc_coat.glb differ diff --git a/assets/models/blender/npc/npc_coat_ao.png b/assets/models/blender/npc/npc_coat_ao.png index cfc3528..c05d791 100644 Binary files a/assets/models/blender/npc/npc_coat_ao.png and b/assets/models/blender/npc/npc_coat_ao.png differ diff --git a/assets/models/blender/npc/npc_dress.glb b/assets/models/blender/npc/npc_dress.glb index d517058..6fe54bb 100644 Binary files a/assets/models/blender/npc/npc_dress.glb and b/assets/models/blender/npc/npc_dress.glb differ diff --git a/assets/models/blender/npc/npc_dress_ao.png b/assets/models/blender/npc/npc_dress_ao.png index c666ed0..b305b7a 100644 Binary files a/assets/models/blender/npc/npc_dress_ao.png and b/assets/models/blender/npc/npc_dress_ao.png differ diff --git a/assets/models/blender/npc/npc_firefighter.glb b/assets/models/blender/npc/npc_firefighter.glb index a29192f..79f3559 100644 Binary files a/assets/models/blender/npc/npc_firefighter.glb and b/assets/models/blender/npc/npc_firefighter.glb differ diff --git a/assets/models/blender/npc/npc_firefighter_ao.png b/assets/models/blender/npc/npc_firefighter_ao.png index ce44bcd..3861f59 100644 Binary files a/assets/models/blender/npc/npc_firefighter_ao.png and b/assets/models/blender/npc/npc_firefighter_ao.png differ diff --git a/assets/models/blender/npc/npc_guard.glb b/assets/models/blender/npc/npc_guard.glb index 5f9afbc..5bf7893 100644 Binary files a/assets/models/blender/npc/npc_guard.glb and b/assets/models/blender/npc/npc_guard.glb differ diff --git a/assets/models/blender/npc/npc_guard_ao.png b/assets/models/blender/npc/npc_guard_ao.png index 20b2e46..4816f58 100644 Binary files a/assets/models/blender/npc/npc_guard_ao.png and b/assets/models/blender/npc/npc_guard_ao.png differ diff --git a/assets/models/blender/npc/npc_hoodie.glb b/assets/models/blender/npc/npc_hoodie.glb index 184b216..a3a4627 100644 Binary files a/assets/models/blender/npc/npc_hoodie.glb and b/assets/models/blender/npc/npc_hoodie.glb differ diff --git a/assets/models/blender/npc/npc_hoodie_ao.png b/assets/models/blender/npc/npc_hoodie_ao.png index d35870b..7a07040 100644 Binary files a/assets/models/blender/npc/npc_hoodie_ao.png and b/assets/models/blender/npc/npc_hoodie_ao.png differ diff --git a/assets/models/blender/npc/npc_jacket.glb b/assets/models/blender/npc/npc_jacket.glb index 889a093..a0a94a1 100644 Binary files a/assets/models/blender/npc/npc_jacket.glb and b/assets/models/blender/npc/npc_jacket.glb differ diff --git a/assets/models/blender/npc/npc_jacket_ao.png b/assets/models/blender/npc/npc_jacket_ao.png index 8e444b2..ceed370 100644 Binary files a/assets/models/blender/npc/npc_jacket_ao.png and b/assets/models/blender/npc/npc_jacket_ao.png differ diff --git a/assets/models/blender/npc/npc_vest.glb b/assets/models/blender/npc/npc_vest.glb index 1e8204a..a9c7142 100644 Binary files a/assets/models/blender/npc/npc_vest.glb and b/assets/models/blender/npc/npc_vest.glb differ diff --git a/assets/models/blender/npc/npc_vest_ao.png b/assets/models/blender/npc/npc_vest_ao.png index dec6029..a20e863 100644 Binary files a/assets/models/blender/npc/npc_vest_ao.png and b/assets/models/blender/npc/npc_vest_ao.png differ diff --git a/docs/blender-assets.md b/docs/blender-assets.md index 853b9e1..a87a5ff 100644 --- a/docs/blender-assets.md +++ b/docs/blender-assets.md @@ -94,8 +94,8 @@ All eight use the `npc_base` contract, so each one can replace `Materials.npc_slot()` tints them the same way it tints `npc_base`. - budget ≤ 6000 triangles and ≤ 5000 vertices (glTF vertex count, after seam splits; `CHAR_MAX_VERTS` in `tools/blender_assets.py`, raised from 4000 for - the 2026-11-01 face/hand pass). The current range is 3,762–4,754 triangles - and 3,474–4,584 vertices. + the 2026-11-01 face/hand pass). The current range is 4,042–5,034 triangles + and 3,894–4,948 vertices. The vertex cap is now the tighter budget. Construction: the torso and limbs are one continuous Skin-modifier mesh, subdivided twice, decimated to 26 % with X symmetry, and cut at the waist, cuffs, diff --git a/docs/blender_previews/README.md b/docs/blender_previews/README.md index 7f2ded9..90e6568 100644 --- a/docs/blender_previews/README.md +++ b/docs/blender_previews/README.md @@ -48,6 +48,7 @@ Committed contact sheets and local Blender renders for the character, gear and p | sheet_npc_baseline_compare-body.jpg | [`sheet_npc_baseline_compare-body.jpg`](sheet_npc_baseline_compare-body.jpg) | | sheet_npc_baseline_compare-face.jpg | [`sheet_npc_baseline_compare-face.jpg`](sheet_npc_baseline_compare-face.jpg) | | sheet_npc_baseline_compare-torso.jpg | [`sheet_npc_baseline_compare-torso.jpg`](sheet_npc_baseline_compare-torso.jpg) | +| sheet_npc_hands_compare.jpg | [`sheet_npc_hands_compare.jpg`](sheet_npc_hands_compare.jpg) | | sheet_npc_gear.jpg | [`sheet_npc_gear.jpg`](sheet_npc_gear.jpg) | | sheet_props.jpg | [`sheet_props.jpg`](sheet_props.jpg) | | shoulder_bag.png | [`shoulder_bag.png`](shoulder_bag.png) | diff --git a/docs/blender_previews/sheet_npc_hands_compare.jpg b/docs/blender_previews/sheet_npc_hands_compare.jpg new file mode 100644 index 0000000..5d257a7 Binary files /dev/null and b/docs/blender_previews/sheet_npc_hands_compare.jpg differ diff --git a/docs/development-status.md b/docs/development-status.md index 7c5b9fd..81f6f05 100644 --- a/docs/development-status.md +++ b/docs/development-status.md @@ -42,6 +42,7 @@ Implemented so far: - NPC neck/shoulder transition (0.9.4 work): the body graph now uses a fuller neck base, a slightly tapered neck top and a lower shoulder root, giving the head/neck/shoulder line a trapezius slope instead of a thin post. The full character library was rebuilt with a final degenerate-face cleanup; the baseline comparison is `sheet_npc_baseline_compare-body.jpg`. - NPC face-depth pass (0.9.4 work): sclera are recessed and capped by a thin upper lid, and the separate jaw/cheek spheres are replaced by a chin plate and cheekbones sculpted into the head mesh. The full library was rebuilt; the comparison is `sheet_npc_baseline_compare-face.jpg`. - NPC torso/hip transition (0.9.4 work): the pelvis and chest are slightly wider than the waist, giving shirts and jackets a readable taper into the hips without changing the build multipliers or the skeleton. The comparison is `sheet_npc_baseline_compare-torso.jpg`. +- NPC hand pass (0.9.4 work): wider, shallower palms; four two-segment bent fingers with rounded tips and a separate bent thumb; optional `--view=hand` capture at 0.80 m. The hand comparison is `sheet_npc_hands_compare.jpg`; item grip offsets remain open. - NPC proportion audit (0.9.4 work): `tools/npc_proportions.py` measures all eight GLBs against a stylized height/head/shoulder/hip/torso/leg/arm/foot guide. All variants pass; the head and broad-build silhouettes are the nearest upper bounds. Results are in [`npc-proportion-audit.md`](npc-proportion-audit.md). - NPC P1 portrait fixes (0.9.4 work): tourists now hide their photo-pose camera bag outside `photo`/`film` poses, removing the duplicate-backpack silhouette; the backpack gear was rebuilt with flattened, forward chest straps and a fresh AO map. The before/after sheet is [`blender_previews/sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg); the face and remaining clothing/hand work stay open. - NPC visual baseline (0.9.4 work): `src/test/capture_npc_baseline.gd` captures all eight library characters from front, three-quarter, profile, back, full-body and crowd views in a neutral studio (48 frames). The criteria, measurements and ranked gaps are in [`npc-visual-review.md`](npc-visual-review.md); the committed sheet is [`blender_previews/sheet_npc_baseline.jpg`](blender_previews/sheet_npc_baseline.jpg). No model changes were made in this baseline step. diff --git a/docs/npc-visual-review.md b/docs/npc-visual-review.md index 304fe23..41e219c 100644 --- a/docs/npc-visual-review.md +++ b/docs/npc-visual-review.md @@ -1,12 +1,15 @@ # NPC visual baseline and acceptance criteria -Status: baseline captured 2026-11-02. The first P1 fixes (tourist photo-prop -visibility, backpack straps), the neck/shoulder and torso/hip transitions and -the face-depth pass are applied. Remaining hand/clothing work is open; the -baseline remains the reference for 0.9.4. Comparison sheets: +Status: baseline captured 2026-11-02. The P1 fixes (tourist photo-prop +visibility, backpack straps), the neck/shoulder and torso/hip transitions, the +face-depth pass and the first hand pass are applied. Hand-item grip alignment +and clothing work remain open; the baseline remains the reference for 0.9.4. +Comparison sheets: [`sheet_npc_baseline_compare-p1.jpg`](blender_previews/sheet_npc_baseline_compare-p1.jpg), [`sheet_npc_baseline_compare-body.jpg`](blender_previews/sheet_npc_baseline_compare-body.jpg) -and the latest [`sheet_npc_baseline_compare-torso.jpg`](blender_previews/sheet_npc_baseline_compare-torso.jpg). +the latest [`sheet_npc_baseline_compare-torso.jpg`](blender_previews/sheet_npc_baseline_compare-torso.jpg), +and the conversation-distance hand pass +[`sheet_npc_hands_compare.jpg`](blender_previews/sheet_npc_hands_compare.jpg). ## What was captured @@ -37,6 +40,17 @@ python3 tools/npc_baseline.py sheet --dir tmp/npc-baseline The 48 raw PNGs are local QA under `tmp/` (gitignored). The committed contact sheet is [`blender_previews/sheet_npc_baseline.jpg`](blender_previews/sheet_npc_baseline.jpg). +For the P2 hand/grip check there is an optional eighth view, captured separately +so the 48-frame baseline stays stable: + +```bash +godot --path . --script res://src/test/capture_npc_baseline.gd -- \ + --out=res://tmp/npc-hands --view=hand +``` + +That writes eight `*__hand.png` frames at 0.80 m from the right hand; the +before/after review image is +[`sheet_npc_hands_compare.jpg`](blender_previews/sheet_npc_hands_compare.jpg). The capture is deterministic apart from engine rasterisation; re-running into the same directory keeps the earlier files and writes `-vN` copies rather than overwriting them. @@ -100,19 +114,19 @@ From `python3 tools/blender_assets.py fit` and the manifest: | Character | Tris | Verts | Head width | Clothing radius | Notable gear clearances | | --- | ---: | ---: | ---: | ---: | --- | -| `npc_casual` | 3,762 | 3,474 | 0.315 m | 0.191 m (hip) | backpack x −0.041, radial −0.162 | -| `npc_jacket` | 4,152 | 3,900 | 0.315 m | 0.191 m (hip) | lanyard x −0.116, radial +0.023 (runtime +4 cm forward) | -| `npc_coat` | 4,754 | 4,584 | 0.315 m | 0.225 m (skirt) | sunglasses width ratio 0.641; bag x +0.037, radial −0.130 | -| `npc_vest` | 4,260 | 4,183 | 0.315 m | 0.191 m (hip) | cap width ratio 1.067 | -| `npc_dress` | 4,180 | 3,863 | 0.315 m | 0.260 m (skirt) | bag x +0.002, radial −0.095 | -| `npc_hoodie` | 4,584 | 4,294 | 0.315 m | 0.190 m (hip) | — | -| `npc_firefighter` | 4,624 | 4,583 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 | -| `npc_guard` | 4,326 | 4,443 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 | +| `npc_casual` | 4,042 | 3,894 | 0.315 m | 0.191 m (hip) | backpack x −0.041, radial −0.162 | +| `npc_jacket` | 4,432 | 4,265 | 0.315 m | 0.191 m (hip) | lanyard x −0.116, radial +0.023 (runtime +4 cm forward) | +| `npc_coat` | 5,034 | 4,814 | 0.315 m | 0.225 m (skirt) | sunglasses width ratio 0.641; bag x +0.037, radial −0.130 | +| `npc_vest` | 4,540 | 4,503 | 0.315 m | 0.191 m (hip) | cap width ratio 1.067 | +| `npc_dress` | 4,460 | 4,191 | 0.315 m | 0.260 m (skirt) | bag x +0.002, radial −0.095 | +| `npc_hoodie` | 4,864 | 4,506 | 0.315 m | 0.190 m (hip) | — | +| `npc_firefighter` | 4,904 | 4,948 | 0.315 m | 0.255 m (skirt) | helmet width ratio 1.651; SCBA x −0.105, radial −0.076 | +| `npc_guard` | 4,606 | 4,679 | 0.315 m | 0.194 m (hip) | cap width ratio 1.067 | -The widest character is `npc_coat` at 4,754 triangles and 4,584 vertices, so the -current headroom is 1,246 triangles and 416 vertices per character. Any hand or -clothing refinement must come out of that budget or replace existing detail. The -refit backpack measures 380 triangles / 406 vertices. +`npc_coat` is widest at 5,034 triangles (966 below the cap); `npc_firefighter` is +closest on vertices at 4,948 (only 52 below the 5,000 cap). Any further hand or +clothing detail must replace existing geometry rather than adding it. The refit +backpack measures 380 triangles / 406 vertices. ## Baseline findings, ranked @@ -140,9 +154,12 @@ refit backpack measures 380 triangles / 406 vertices. ### P2 — fix after the first P1 pass -4. **Hands and grips are not proven at conversation distance.** The full-body - frame shows small block hands; the camera, phone and clipboard need a - close-up grip check before the hand mesh is accepted. +4. **Hands: first pass fixed 2026-11-02; grip alignment still open.** The palm + is wider and shallower, and each finger is now a two-segment bent tube with a + rounded tip and a separate bent thumb. The hand capture shows a readable + four-finger hand instead of flat bars. The camera, phone, cup, clipboard and + sign still need their attach offsets checked: several props occlude the palm + or read as floating beside it. 5. **Clothing detail is tile-driven and transitional seams are visible.** The fabric pattern repeats strongly on the casual, coat and dress variants, and collar/cuff/waist edges are abrupt. This is more visible than the shoe/hem @@ -189,15 +206,16 @@ The committed runs are [`sheet_npc_baseline_compare-body.jpg`](blender_previews/sheet_npc_baseline_compare-body.jpg) and [`sheet_npc_baseline_compare-face.jpg`](blender_previews/sheet_npc_baseline_compare-face.jpg). The transition pass is [`sheet_npc_baseline_compare-torso.jpg`](blender_previews/sheet_npc_baseline_compare-torso.jpg). +The hand pass is [`sheet_npc_hands_compare.jpg`](blender_previews/sheet_npc_hands_compare.jpg). Keep the baseline sheet as the before artifact and add a new comparison sheet rather than replacing it. ## Next implementation order -1. Add the remaining body-shape variation between variants and re-measure the +1. Fix hand-item grip offsets (phone, cup, clipboard, camera, sign, flashlight, + wand and radio) against the new palm/finger geometry. +2. Add the remaining body-shape variation between variants and re-measure the LOD/impostor silhouettes. -2. Add a conversation-distance hand/grip capture and fix hand geometry before - judging hand items. 3. Audit hems and footwear in walk/sit/block poses. 4. Refine nose/lip/ear shapes only if the portrait pass still flags them after the hands and clothing read cleanly. diff --git a/src/test/capture_npc_baseline.gd b/src/test/capture_npc_baseline.gd index b4349b9..78b7d22 100644 --- a/src/test/capture_npc_baseline.gd +++ b/src/test/capture_npc_baseline.gd @@ -30,6 +30,8 @@ const VARIANTS := [ ] ## Camera orbits the subject at yaw 0 (front = Godot +Z). Portrait views keep ## the eye/head in frame; the last two show the full body and crowd silhouette. +const HAND_VIEW := {"name": "hand", "yaw": 15.0, "distance": 0.80, "height": 0.72, + "target_x": 0.27, "target_y": 0.72, "fov": 32.0} const VIEWS := [ {"name": "front", "yaw": 0.0, "distance": 1.85, "height": 1.55, "target_y": 1.52, "fov": 32.0}, @@ -48,6 +50,8 @@ const SETTLE_FRAMES := 4 const MAX_FRAMES := 4000 var out_dir := "user://screenshots/npc_baseline" +var only_view := "" +var _views: Array = VIEWS var frames := 0 var _phase := "stage_variant" var _variant_i := 0 @@ -61,6 +65,10 @@ func _initialize() -> void: for arg in OS.get_cmdline_user_args(): if str(arg).begins_with("--out="): out_dir = str(arg).substr(6) + elif str(arg).begins_with("--view="): + only_view = str(arg).substr(7) + if only_view == "hand": + _views = [HAND_VIEW] root.size = Vector2i(1280, 720) _build_stage() @@ -83,7 +91,7 @@ func _process(_delta: float) -> bool: "shoot": _shoot() _view_i += 1 - if _view_i >= VIEWS.size(): + if _view_i >= _views.size(): _view_i = 0 _variant_i += 1 if _variant_i >= VARIANTS.size(): @@ -169,10 +177,10 @@ func _stage_variant() -> void: func _frame_view() -> void: if _avatar == null or not is_instance_valid(_avatar): return - var view: Dictionary = VIEWS[_view_i] + var view: Dictionary = _views[_view_i] var yaw := deg_to_rad(float(view["yaw"])) var distance := float(view["distance"]) - var target := Vector3(0.0, float(view["target_y"]), 0.0) + var target := Vector3(float(view.get("target_x", 0.0)), float(view["target_y"]), 0.0) _camera.position = target + Vector3(sin(yaw) * distance, float(view["height"]) - target.y, cos(yaw) * distance) _camera.fov = float(view["fov"]) @@ -180,7 +188,7 @@ func _frame_view() -> void: func _shoot() -> void: var variant: Dictionary = VARIANTS[_variant_i] - var view: Dictionary = VIEWS[_view_i] + var view: Dictionary = _views[_view_i] var dir := ProjectSettings.globalize_path(out_dir) DirAccess.make_dir_recursive_absolute(dir) var base := "%s__%s" % [variant["name"], view["name"]] diff --git a/tools/blender_assets.py b/tools/blender_assets.py index aa9c74f..91655b6 100644 --- a/tools/blender_assets.py +++ b/tools/blender_assets.py @@ -813,16 +813,28 @@ def _hands_and_shoes(mats, spec): w = BUILDS[spec.get("build", "average")][0] for s in (-1.0, 1.0): x = 0.275 * s * w - parts.append(sphere("Palm", 0.045, (x, -0.01, 0.735), skin, seg=10, rings=6, - scale=(0.55, 0.95, 1.25))) - parts.append(sphere("Thumb", 0.018, (x - 0.014 * s, -0.048, 0.752), skin, - seg=8, rings=6, scale=(1.0, 1.0, 1.7), rot=(0.4, 0, 0))) - # four fingers: relaxed, slightly curled forward, outer ones shorter + # Wider and shallower than the old ball: a palm, not a wrist knob. + parts.append(sphere("Palm", 0.046, (x, -0.012, 0.742), skin, seg=10, rings=6, + scale=(0.80, 0.62, 0.88))) + # Four bent, tapering fingers with rounded tips. The two segments keep + # the hand readable at conversation distance without adding a full + # finger skeleton; the outer fingers are slightly shorter. for index, offset in enumerate((-0.024, -0.008, 0.008, 0.024)): - length = (0.046, 0.052, 0.050, 0.040)[index] - parts.append(cyl("Finger", 0.011, 0.009, length, - (x + offset * s, -0.030, 0.714 - length * 0.5), skin, - seg=6, rot=(math.radians(14), 0, 0))) + fx = x + offset * s + tip_z = (0.692, 0.681, 0.684, 0.694)[index] + radius = 0.0115 if index in (1, 2) else 0.0105 + parts.append(tube("Finger", [(fx, -0.018, 0.758), + (fx, -0.030, 0.726), + (fx, -0.042, tip_z)], + radius, skin, seg=6, caps=False)) + parts.append(sphere("Fingertip", radius, (fx, -0.042, tip_z), skin, + seg=6, rings=4)) + parts.append(tube("Thumb", [(x - 0.010 * s, -0.018, 0.758), + (x - 0.028 * s, -0.040, 0.740), + (x - 0.034 * s, -0.055, 0.718)], + 0.012, skin, seg=6, caps=False)) + parts.append(sphere("ThumbTip", 0.012, (x - 0.034 * s, -0.055, 0.718), skin, + seg=6, rings=4)) parts.append(box("Shoe", (0.10, 0.25, 0.075), (0.12 * s, -0.045, 0.04), shoe, bevel=0.028, segs=1)) parts.append(box("Sole", (0.106, 0.262, 0.02), (0.12 * s, -0.045, 0.01),