Files
settled-reach/spikes/synty-intake/scripts/qa_scene.gd
T
jpmschweitzerandClaude Fable 5 4bcd778e16 chore(assets): Synty intake spike — scripts, evidence logs, verdict data (T-1089)
Bridge-C evidence spike: unitypackage extraction, rest-pose recon, garment
transplant onto the Quaternius rig (Data Transfer POLYINTERP_NEAREST), 11-body
batch fit, Godot QA scenes. Technical PASS; route nonetheless REJECTED by
product call (cost, baked body-segment modularity, style) — full trail on
T-1089. Salvage: source-agnostic transplant/batch-fit scripts (the G1-family
pipeline for any donor mesh) and the T-1090 discovery (5 body types misrender
bare). Extracted vendor payload (spikes/**/raw/) now gitignored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:01:24 +02:00

168 lines
4.8 KiB
GDScript

extends Node3D
## Spike T-1089 Synty intake — Godot QA scene (THROWAWAY, lives in
## client/spike_synty_qa/ only for the res:// path; source of truth is
## spikes/synty-intake/scripts/qa_scene.gd).
##
## Builds a CharacterVisual (average_m + peasant pants/shoes), reparents the
## transplanted Synty torso garment onto its skeleton (mirrors
## character_visual.gd:517-535), plays Walk, saves screenshots to
## spikes/synty-intake/out/qa/, then quits.
const GARMENT_GLB := "res://spike_synty_qa/SK_SCFI_CIVL_09_10TORS_HU01_quaternius.glb"
const SHOT_DIR := "/var/mnt/data/projects/settled-reach/spikes/synty-intake/out/qa/"
var _cv: CharacterVisual = null
var _cam: Camera3D = null
var _garment_meshes: Array[MeshInstance3D] = []
func _ready() -> void:
_setup_stage()
_cv = CharacterVisual.new()
add_child(_cv)
var desc := CharacterVisualDescriptor.new()
desc.body_type = CharacterVisualDescriptor.BodyType.AVERAGE_M
desc.head_id = "head_001"
desc.hair_id = "buzzed"
desc.eyebrow_id = "regular"
desc.skin_tone = 3
desc.clothing_slots = {"legs": "peasant_pants", "feet": "peasant_shoes"}
_cv.load_descriptor(desc)
var skel := _cv.get_skeleton()
print("QA: skeleton bones = ", skel.get_bone_count() if skel else -1)
_attach_garment(skel)
_run_qa.call_deferred()
func _setup_stage() -> void:
var light := DirectionalLight3D.new()
light.rotation_degrees = Vector3(-45, 30, 0)
light.light_energy = 1.2
add_child(light)
var env := Environment.new()
env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
env.ambient_light_color = Color(0.8, 0.8, 0.85)
env.ambient_light_energy = 0.9
env.background_mode = Environment.BG_COLOR
env.background_color = Color(0.18, 0.19, 0.22)
var we := WorldEnvironment.new()
we.environment = env
add_child(we)
_cam = Camera3D.new()
add_child(_cam)
_set_cam_front()
func _set_cam_front() -> void:
_cam.position = Vector3(0, 1.25, 1.4)
_cam.look_at_from_position(_cam.position, Vector3(0, 1.15, 0), Vector3.UP)
func _set_cam_side() -> void:
_cam.position = Vector3(1.4, 1.25, 0)
_cam.look_at_from_position(_cam.position, Vector3(0, 1.15, 0), Vector3.UP)
func _set_cam_back() -> void:
_cam.position = Vector3(0, 1.25, -1.4)
_cam.look_at_from_position(_cam.position, Vector3(0, 1.15, 0), Vector3.UP)
func _attach_garment(skel: Skeleton3D) -> void:
# Mirror character_visual.gd:517-535 — reparent skinned meshes onto OUR
# skeleton; matching bone names in the Skin resource drive them.
var scene := load(GARMENT_GLB) as PackedScene
if scene == null:
push_error("QA: garment GLB failed to load: " + GARMENT_GLB)
return
var inst := scene.instantiate()
var stack: Array[Node] = [inst]
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:
var mi := n as MeshInstance3D
mi.get_parent().remove_child(mi)
mi.owner = null
skel.add_child(mi)
_garment_meshes.append(mi)
inst.queue_free()
print("QA: garment skinned meshes attached = ", _garment_meshes.size())
for mi in _garment_meshes:
print("QA: garment mesh '", mi.name, "' skin binds = ", mi.skin.get_bind_count())
func _shot(fname: String) -> void:
await RenderingServer.frame_post_draw
var img := get_viewport().get_texture().get_image()
img.save_png(SHOT_DIR + fname)
print("QA: saved ", fname)
func _wait_frames(n: int) -> void:
for i in n:
await get_tree().process_frame
func _run_qa() -> void:
await _wait_frames(5)
# report garment AABB sanity (explosion check)
for mi in _garment_meshes:
print("QA: garment local AABB = ", mi.get_aabb())
# --- idle ---
await _shot("01_idle_front.png")
_set_cam_side()
await _shot("02_idle_side.png")
_set_cam_back()
await _shot("02b_idle_back.png")
# --- walk ---
var ap := _cv.get_animation_player()
var names: Array[String] = []
if ap:
for lib_name in ap.get_animation_library_list():
var lib := ap.get_animation_library(lib_name)
for a in lib.get_animation_list():
names.append(str(lib_name) + "/" + str(a))
print("QA: animations = ", names)
var walk := ""
for n in names:
if n.to_lower().contains("walk"):
walk = n.get_slice("/", 1)
if n.to_lower().ends_with("/walk"):
break
if walk == "":
push_warning("QA: no walk animation found — staying on idle")
else:
print("QA: playing '", walk, "'")
_cv.play_animation(walk)
# mid-stride frames at two phases, front + side
await _wait_frames(24)
_set_cam_front()
await _shot("03_walk_front_a.png")
_set_cam_side()
await _shot("04_walk_side_a.png")
await _wait_frames(14)
await _shot("05_walk_side_b.png")
_set_cam_front()
await _shot("06_walk_front_b.png")
_set_cam_back()
await _wait_frames(10)
await _shot("07_walk_back.png")
for mi in _garment_meshes:
print("QA: post-walk garment local AABB = ", mi.get_aabb())
print("QA: DONE")
get_tree().quit(0)