Drop hand items into the palm, raise the lanyard, and capture every gear piece worn on the character
Full build / Import + headless tests (push) Successful in 43s
Full build / Full desktop build + optional release (push) Skipped

This commit is contained in:
zwitschi committed 2026-11-06 18:30:52 +01:00
1 parent 6b58350464
commit a36068aae7
6 files changed
+235 -20

No files matched your search

+29 -6
View File
@@ -180,12 +180,32 @@ const BLENDER_GEAR_DIR := "res://assets/models/blender/npc_gear/"
const GEAR_OFFSETS := {
"lanyard": Vector3(0, 0, 0.04),
"firefighter_helmet": Vector3(0, 0.03, 0),
# The Quaternius hair comes down over the forehead, so the lens plane has to sit
# in front of it or the glasses disappear inside the fringe.
"sunglasses": Vector3(0, -0.01, 0.035),
}
## Hand gear is authored around the wrist proxy. The palm sits ~4 cm in front
## of that origin, so without this offset the first phone/flashlight versions
## were partly buried inside the hand. Keep the grip axis and move the whole
## item forward into the open palm.
## Hand gear is authored around the wrist proxy. The palm sits ~4 cm in front of
## that origin, so without this offset the first phone/flashlight versions were
## partly buried inside the hand. Keep the grip axis and move the whole item
## forward into the open palm.
const HAND_ITEM_FORWARD := Vector3(0, 0, 0.04)
## The grip is the wrist *joint*, but a carried object rests in the palm, which is
## below it. The local -Y of the hand proxy follows the hanging arm, so these drops
## put the grip in the palm and follow the swing instead of floating at the wrist.
## Owner report (2026-11-06): the camera, clipboard, board, flashlight and radio
## all sat too high. Items not listed use `HAND_DROP_DEFAULT`.
const HAND_ITEM_DROP := {
"camera": 0.085,
"flashlight": 0.065,
"cup": 0.050,
"umbrella": 0.045,
"clipboard": 0.035,
"protest_sign": 0.035,
"phone": 0.030,
"radio": 0.030,
"wand": 0.030,
}
const HAND_DROP_DEFAULT := 0.040
const GEAR_ATTACH := {
"sunglasses": "head",
"firefighter_helmet": "head",
@@ -801,7 +821,9 @@ func _attach_gear(gear_name: String) -> bool:
if _rig == null:
node.free()
return false
node.position = Vector3(0, 1.30, 0)
# Sternum on the Quaternius rig; 1.30 was the procedural chest and put
# the lanyard badge on the belly (owner report).
node.position = Vector3(0, 1.38, 0)
_rig.add_child(node)
_:
node.free()
@@ -810,6 +832,7 @@ func _attach_gear(gear_name: String) -> bool:
node.position += GEAR_OFFSETS[gear_name]
if str(GEAR_ATTACH[gear_name]) == "hand":
node.position += HAND_ITEM_FORWARD
node.position.y -= float(HAND_ITEM_DROP.get(gear_name, HAND_DROP_DEFAULT))
_detail_nodes.append(node)
var ao := Materials.load_ao(BLENDER_GEAR_DIR + gear_name + "_ao.png")
for child in node.find_children("*", "MeshInstance3D", true, false):
@@ -988,7 +1011,7 @@ func _add_props(npc_type: int) -> void:
## Carried role props ride the hand proxy so they follow the arm. The offset is
## in the hand's local space: the grip sits ~4 cm in front of the wrist origin,
## the same convention the Blender hand gear uses (`HAND_ITEM_FORWARD`).
const HAND_PROP_OFFSET := Vector3(0, -0.02, 0.05)
const HAND_PROP_OFFSET := Vector3(0, -0.06, 0.05)
## Hand proxy for a side (0 = the avatar's left, 1 = its right), falling back to
## the rig when the fallback body has no proxies.