## Tests for the T-1089 wardrobe engine: the toon_garment multi-region tint + ## logo pipeline and the proof garment (tshirt_modern). ## ## Covers what is verifiable headless: ## 1. The new toon_garment.gdshader loads/compiles. ## 2. The region-mask CHANNEL MATH — pure GDScript replication of the shader's ## RGBA→4-tint channel blend, asserting channel-blended (not indexed) routing. ## 3. The manifest schema extension (style tag + logo_capable flag). ## 4. coverage.json shape. ## 5. Every fitted body variant loads as a skinned garment mesh. ## 6. The logo + region-mask textures load. ## ## The shader itself runs on the GPU, so its arithmetic is re-implemented here in ## GDScript and asserted against the same convention the .gdshader uses: ## region_tint = m.r*t0 + m.g*t1 + m.b*t2 + m.a*t3 ## Ref: T-1089 gaps G3 (multi-region tint) + G4 (logo decal), D-251. class_name TestGarmentEngineT1089 extends GdUnitTestSuite const SHADER_PATH := "res://assets/characters/shaders/toon_garment.gdshader" 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 .glb + _mask.png. const FITTED_BODIES := [ "average_m", "average_f", "muscular_m", "muscular_f", "thin_m", "thin_f", "heavy_m", "heavy_f", "teen_m", "teen_f", "child", ] # --- 1. Shader loads ------------------------------------------------------- func test_toon_garment_shader_loads() -> void: assert_bool(ResourceLoader.exists(SHADER_PATH)).override_failure_message( "toon_garment.gdshader missing at %s" % SHADER_PATH ).is_true() var shader := load(SHADER_PATH) as Shader assert_object(shader).is_not_null() assert_str(shader.code).contains("region_mask") assert_str(shader.code).contains("logo_tex") assert_str(shader.code).contains("UV2") func test_toon_garment_binds_to_material() -> void: # A ShaderMaterial with all four tints + logo params must accept them. var shader := load(SHADER_PATH) as Shader var mat := ShaderMaterial.new() mat.shader = shader mat.set_shader_parameter("tint_0", Color.RED) mat.set_shader_parameter("tint_1", Color.GREEN) mat.set_shader_parameter("tint_2", Color.BLUE) mat.set_shader_parameter("tint_3", Color.WHITE) mat.set_shader_parameter("logo_enabled", 1.0) assert_object(mat.get_shader_parameter("tint_2")).is_equal(Color.BLUE) # --- 2. Region-mask channel math (the testable shader logic) --------------- ## Pure-GDScript replication of the shader's per-texel region tint selection. func _region_tint(mask: Color, t0: Color, t1: Color, t2: Color, t3: Color) -> Color: var r := t0 * mask.r + t1 * mask.g + t2 * mask.b + t3 * mask.a return Color(r.r, r.g, r.b, 1.0) func test_pure_channel_routes_to_its_tint() -> void: var t0 := Color(0.1, 0.2, 0.3) var t1 := Color(0.4, 0.5, 0.6) var t2 := Color(0.7, 0.8, 0.9) var t3 := Color(0.0, 0.0, 0.0) # Pure collar (R) -> tint_0, body (G) -> tint_1, sleeve (B) -> tint_2 assert_bool(_region_tint(Color(1, 0, 0, 0), t0, t1, t2, t3).is_equal_approx(t0)).is_true() assert_bool(_region_tint(Color(0, 1, 0, 0), t0, t1, t2, t3).is_equal_approx(t1)).is_true() assert_bool(_region_tint(Color(0, 0, 1, 0), t0, t1, t2, t3).is_equal_approx(t2)).is_true() func test_boundary_channel_blends_not_indexes() -> void: # The whole point of G3: a bilinear boundary texel (R=0.5,G=0.5) must produce # a MIDPOINT of the two tints — a crossfade — not snap to one index. var blended := _region_tint( Color(0.5, 0.5, 0.0, 0.0), Color(1, 0, 0), Color(0, 0, 1), Color.BLACK, Color.BLACK ) assert_bool(blended.is_equal_approx(Color(0.5, 0.0, 0.5))).override_failure_message( "boundary texel must crossfade tints, got %s" % blended ).is_true() func test_zero_mask_contributes_no_tint() -> void: var out := _region_tint(Color(0, 0, 0, 0), Color.RED, Color.GREEN, Color.BLUE, Color.WHITE) assert_bool(out.is_equal_approx(Color.BLACK)).is_true() # --- 3. Manifest schema extension ------------------------------------------ func test_manifest_has_tshirt_with_style_and_logo_flag() -> void: var f := FileAccess.open(MANIFEST_PATH, FileAccess.READ) assert_object(f).is_not_null() var data: Variant = JSON.parse_string(f.get_as_text()) f.close() assert_bool(data is Dictionary).is_true() var clothing: Dictionary = data["clothing"] assert_bool(clothing.has("tshirt_modern")).override_failure_message( "manifest.clothing missing tshirt_modern entry" ).is_true() var entry: Dictionary = clothing["tshirt_modern"] assert_str(entry.get("slot", "")).is_equal("torso") assert_str(entry.get("style", "")).is_equal("modern") assert_bool(entry.get("logo_capable", false)).is_true() # --- 4. coverage.json ------------------------------------------------------ func test_coverage_json_shape() -> void: var path := CLOTHING_DIR + "coverage.json" var f := FileAccess.open(path, FileAccess.READ) assert_object(f).is_not_null() var cov: Variant = JSON.parse_string(f.get_as_text()) f.close() assert_bool(cov is Dictionary).is_true() assert_bool((cov as Dictionary).has("hides")).is_true() assert_array((cov as Dictionary)["hides"]).contains(["torso"]) assert_str((cov as Dictionary).get("torso_variant", "")).is_equal("full") # --- 5. Fitted variants load as skinned garment meshes --------------------- func test_all_fitted_bodies_load_skinned() -> void: for body in FITTED_BODIES: var glb := CLOTHING_DIR + "%s.glb" % body assert_bool(ResourceLoader.exists(glb)).override_failure_message( "fitted variant missing: %s" % glb ).is_true() var scene := load(glb) as PackedScene assert_object(scene).override_failure_message( "variant %s failed to load as PackedScene" % body ).is_not_null() var inst := scene.instantiate() var skinned := _has_skinned_mesh(inst) inst.queue_free() assert_bool(skinned).override_failure_message( "variant %s has no skinned MeshInstance3D (cannot animate)" % body ).is_true() func _has_skinned_mesh(root: Node) -> bool: var stack: Array[Node] = [root] while not stack.is_empty(): var n: Node = stack.pop_back() for c in n.get_children(): stack.append(c) if n is MeshInstance3D and (n as MeshInstance3D).skin != null: return true return false # --- 6. Decal + mask textures load ----------------------------------------- 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 # _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()