Start the prop and gear fit pass: hand-gear measurement, top-edge clipboard, carried role props, 15-piece gear set
This commit is contained in:
1 parent
cba00facf5
commit
219588bf8a
17 files changed
+127
-73
No files matched your search
+53
-8
@@ -100,7 +100,7 @@ CHARACTERS = {
|
||||
"npc_coat": {"layer": "coat", "hair": "long", "base": "hoodie",
|
||||
"gear": ["sunglasses", "shoulder_bag", "phone"],
|
||||
"roles": ["influencer", "trespasser", "violent_threat"]},
|
||||
"npc_vest": {"layer": "vest", "hair": "buzz", "gear": ["cap", "clipboard"],
|
||||
"npc_vest": {"layer": "vest", "hair": "buzz", "gear": ["clipboard"],
|
||||
"base": "worker",
|
||||
# Delivery keeps the hi-vis stripe bands as authored; every
|
||||
# other worker role tints them with the shirt.
|
||||
@@ -118,7 +118,7 @@ CHARACTERS = {
|
||||
"roles": ["responder"]},
|
||||
"npc_guard": {"layer": "guard", "hair": "buzz", "build": "broad",
|
||||
"base": "worker",
|
||||
"gear": ["cap", "wand"],
|
||||
"gear": ["wand"],
|
||||
"roles": ["player_guard", "venue_security"]},
|
||||
# Crowd-variety prototypes (character development): the three Quaternius
|
||||
# sources are spread across the crowd pools so a crowd never shows one mesh.
|
||||
@@ -147,6 +147,20 @@ ATTACH_BLENDER = {"head": (0.0, 0.0, 1.62), "hand": (0.27, -0.02, 0.78),
|
||||
# Attach points in the authored GLB space (Godot coordinates), used by `fit`.
|
||||
ATTACH_GODOT = {"head": (0.0, 1.62, 0.0), "hand": (0.27, 0.78, 0.02),
|
||||
"back": (0.0, 1.10, 0.0), "chest": (0.0, 1.30, 0.0)}
|
||||
# The runtime hangs hand gear off the Quaternius wrist proxy, not off the authored
|
||||
# `hand` attach point: the wrist rests at Godot y 1.017 with the hip line at 0.95.
|
||||
# `fit` uses these to say whether a hand item clears the thigh when the arm hangs.
|
||||
HAND_REST_Y = 1.017
|
||||
HIP_LINE = 0.95
|
||||
# Real-world size of each hand item in metres (longest edge), so `fit` can flag a
|
||||
# prop that drifted off-scale instead of only "it is big".
|
||||
# Longest edge as authored today; `fit` flags drift past 10 %, so a re-authored
|
||||
# item has to update its row instead of silently growing.
|
||||
HAND_ITEM_AUTHORED_SIZE = {
|
||||
"phone": 0.150, "cup": 0.142, "clipboard": 0.305, "umbrella": 0.900,
|
||||
"camera": 0.138, "protest_sign": 1.230, "flashlight": 0.243,
|
||||
"wand": 0.400, "radio": 0.203,
|
||||
}
|
||||
HEAD_BAND = (1.55, 1.81) # face/headgear height band in Godot metres
|
||||
# Head gear is authored at the procedural library's head size; the shipped
|
||||
# Quaternius roster has a 0.20 m head whose centre sits higher (see
|
||||
@@ -164,8 +178,11 @@ FIT_HIP_BAND = (0.55, 0.95) # fallback body radius for trousers/vests
|
||||
# Side bags include a strap that crosses the torso, so the full gear bbox cannot
|
||||
# tell where the bag body starts. Measure the body-only y band for this fit.
|
||||
FIT_BODY_BANDS = {"shoulder_bag": (-0.34, -0.16)}
|
||||
# Head gear is the firefighter helmet only (owner, 2026-11-06). `cap` and `hood`
|
||||
# were retired with the hat removal; `build_gear()` keeps their branch code so a
|
||||
# future design can restore them, but they are not assets any more.
|
||||
GEAR = {
|
||||
"cap": "head", "hood": "head", "sunglasses": "head",
|
||||
"sunglasses": "head",
|
||||
"firefighter_helmet": "head",
|
||||
"backpack": "back", "shoulder_bag": "back", "scba_tank": "back",
|
||||
"lanyard": "chest",
|
||||
@@ -2225,10 +2242,14 @@ def build_gear(name):
|
||||
p.append(lathe("Sleeve", [(0.039, -0.01), (0.044, 0.035)], M("cardboard"), seg=16,
|
||||
smooth=True))
|
||||
elif name == "clipboard":
|
||||
p.append(box("Board", (0.23, 0.008, 0.31), (0, -0.04, -0.06), M("dark_wood"),
|
||||
# Grip the *top* edge, as a hand actually carries a clipboard. The old
|
||||
# geometry put the grip in the middle of the board, so it hung 26 cm below
|
||||
# the wrist and swung through the thigh line; from the top edge it reads
|
||||
# as held, and the board keeps its A4 size (0.22 x 0.30 m).
|
||||
p.append(box("Board", (0.22, 0.008, 0.30), (0, -0.04, -0.175), M("dark_wood"),
|
||||
bevel=0.004))
|
||||
p.append(box("Paper", (0.20, 0.002, 0.26), (0, -0.045, -0.08), M("paper")))
|
||||
p.append(box("Clip", (0.08, 0.014, 0.03), (0, -0.046, 0.085), M("steel"), bevel=0.004))
|
||||
p.append(box("Paper", (0.19, 0.002, 0.25), (0, -0.045, -0.195), M("paper")))
|
||||
p.append(box("Clip", (0.08, 0.014, 0.03), (0, -0.046, -0.035), M("steel"), bevel=0.004))
|
||||
elif name == "umbrella":
|
||||
p.append(tube("Handle", [(0.0, 0.0, -0.06), (0.0, 0.0, -0.10), (0.0, -0.04, -0.14),
|
||||
(0.0, -0.08, -0.10)], 0.011, M("dark_wood"), seg=8))
|
||||
@@ -3466,7 +3487,7 @@ def _gear_local_positions(name):
|
||||
|
||||
def _gear_fit(name, reference):
|
||||
attach = GEAR.get(name)
|
||||
if attach not in ("head", "back", "chest"):
|
||||
if attach not in ("head", "back", "chest", "hand"):
|
||||
return None
|
||||
audit = audit_asset("npc_gear", name)
|
||||
bounds = audit.get("bounds")
|
||||
@@ -3481,7 +3502,31 @@ def _gear_fit(name, reference):
|
||||
"min": [round(v, 3) for v in lo],
|
||||
"max": [round(v, 3) for v in hi],
|
||||
}
|
||||
if attach == "head":
|
||||
if attach == "hand":
|
||||
# The GLB origin is the grip, so the audit bounds are grip-relative and the
|
||||
# item hangs along Godot -Y. `lo` already has the authored attach offset
|
||||
# added, so subtract it before converting to a rest position.
|
||||
grip = bounds["min"]
|
||||
drop = round(-float(grip[1]), 3)
|
||||
real = HAND_ITEM_AUTHORED_SIZE.get(name, 0.0)
|
||||
longest = round(max(hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]), 3)
|
||||
result.update({
|
||||
"width": round(hi[0] - lo[0], 3),
|
||||
"height": round(hi[1] - lo[1], 3),
|
||||
"depth": round(hi[2] - lo[2], 3),
|
||||
"grip_drop": drop,
|
||||
"rest_lowest_y": round(HAND_REST_Y + float(grip[1]), 3),
|
||||
"clears_hip": (HAND_REST_Y + float(grip[1])) >= HIP_LINE,
|
||||
"longest": longest,
|
||||
"authored_size": real,
|
||||
"size_ratio": round(longest / real, 3) if real else 0.0,
|
||||
# Same object, bigger head: on the 0.20 m Quaternius head a 0.315 m
|
||||
# clipboard is 1.57 head-widths where it used to be 1.0. Report the
|
||||
# proportion so "it reads oversized" is a number, not a feeling.
|
||||
"size_vs_head": round(longest / reference["head_width"], 2)
|
||||
if reference.get("head_width") else 0.0,
|
||||
})
|
||||
elif attach == "head":
|
||||
result.update({
|
||||
"width": round(hi[0] - lo[0], 3),
|
||||
"width_ratio": round((hi[0] - lo[0]) / reference["head_width"], 3)
|
||||
|
||||
Reference in new issue
Block a user