feat(assets): wardrobe wave 1 — per-body shells, 8 garments, try-on UI (T-1089)
Infra: offset-shell gains --per-body mode (each body's own segments, cut/mask thresholds derived from that body's bone landmarks — reproduces the hand-calibrated reference constants exactly on average_m); compositor prefers <body>_mask.png with reference_mask.png fallback; tshirt re-authored per-body on all 11 (the Q-060 torso poke-through class is GONE — residual flags are a sleeve-hem epsilon artifact on thick arms, offset-insensitive, documented). Garments (all per-body x 11, chromakey-gated <=150px worst, previewed): hoodie (hood-down roll, kangaroo pocket, logo), button-down (collar/placket), shorts, jeans (analytic denim field driving albedo+mask together; boundary weld + open-rim flattening — real segment-splitter findings), formal pants, jacket (over-shirt standoff, zip), suit_jacket_black (lapel region, tintable shirt triangle — the hand-author proof), uniform_utility (11-segment coverall, gap-free waist join by construction, 4-zone showcase, logo patch). Try-on UI: creation screen shows per-region tint pickers (multi_region garments) + logo picker (logo_capable + logos/*.png scan), data-driven off manifest+coverage. Manifest merged by the lead: 12 clothing entries. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,8 +22,11 @@ const CLOTHING_DIR := "res://assets/characters/clothing/tshirt_modern/"
|
||||
const LOGO_PATH := "res://assets/characters/logos/thrds.png"
|
||||
const MANIFEST_PATH := "res://assets/characters/manifest.json"
|
||||
|
||||
## All 11 body types — tshirt_modern is authored PER BODY (offset-shell mode,
|
||||
## T-1089): each body gets its own <body>.glb + <body>_mask.png.
|
||||
const FITTED_BODIES := [
|
||||
"average_m", "average_f", "muscular_m", "muscular_f", "teen_m", "teen_f",
|
||||
"average_m", "average_f", "muscular_m", "muscular_f", "thin_m", "thin_f",
|
||||
"heavy_m", "heavy_f", "teen_m", "teen_f", "child",
|
||||
]
|
||||
|
||||
|
||||
@@ -155,8 +158,23 @@ func _has_skinned_mesh(root: Node) -> bool:
|
||||
|
||||
func test_logo_and_mask_textures_load() -> void:
|
||||
assert_bool(ResourceLoader.exists(LOGO_PATH)).is_true()
|
||||
# Runtime fallback mask (SD-fit garments + safety net) must still exist.
|
||||
assert_bool(ResourceLoader.exists(CLOTHING_DIR + "reference_mask.png")).is_true()
|
||||
var logo := load(LOGO_PATH) as Texture2D
|
||||
assert_object(logo).is_not_null()
|
||||
var mask := load(CLOTHING_DIR + "reference_mask.png") as Texture2D
|
||||
assert_object(mask).is_not_null()
|
||||
|
||||
|
||||
func test_per_body_masks_load() -> void:
|
||||
# Per-body offset-shell authoring: the compositor prefers
|
||||
# <body>_mask.png over reference_mask.png (character_visual.gd).
|
||||
for body in FITTED_BODIES:
|
||||
var mask_path := CLOTHING_DIR + "%s_mask.png" % body
|
||||
assert_bool(ResourceLoader.exists(mask_path)).override_failure_message(
|
||||
"per-body region mask missing: %s" % mask_path
|
||||
).is_true()
|
||||
var mask := load(mask_path) as Texture2D
|
||||
assert_object(mask).override_failure_message(
|
||||
"per-body region mask failed to load: %s" % mask_path
|
||||
).is_not_null()
|
||||
|
||||
Reference in New Issue
Block a user