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-face.jpg`](sheet_npc_baseline_compare-face.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_gear.jpg`](sheet_npc_gear.jpg) |
|
| [`sheet_props.jpg`](sheet_props.jpg) |
|
| [`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),