Files
tattoo-test/rotate_glb.gd
T
Can Narin 8fa53c8e78 male(v15): Tripo mesh + Meshy rig, UAL retarget test beds
- male/: raw + z-facing + quat-named bodies, Meshy rigged GLB/FBX,
  stock walk/run clips, male-ual-clips.glb (16 UAL1 clips baked via
  ariki-game mixamo_retarget.py --map ual)
- anim_male.tscn: Meshy stock clips; anim_male_ual.tscn: UAL clips
  (scales position keys x100 — Meshy skeleton is cm, clips are m)
- view_tripo.tscn: env-var GLB turntable/anim shots + interactive orbit
- rotate_glb.gd: headless face-+Z/height fixup (Meshy pose estimation
  rejects Tripo's +X-facing normalized output)
- Meshy spine chain is reverse-named: Spine02 = lowest vertebra

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 21:39:30 +03:00

68 lines
2.0 KiB
GDScript

# Headless GLB fixup: rotate a model to face +Z and scale to a target height,
# baking the transform into vertices via the root node transform on export.
# Env: ROT_IN (glb path), ROT_OUT (glb path), ROT_YAW_DEG, ROT_HEIGHT_M
extends SceneTree
func _init() -> void:
var in_path := OS.get_environment("ROT_IN")
var out_path := OS.get_environment("ROT_OUT")
var yaw_deg := float(OS.get_environment("ROT_YAW_DEG"))
var target_h := float(OS.get_environment("ROT_HEIGHT_M"))
var doc := GLTFDocument.new()
var state := GLTFState.new()
if doc.append_from_file(in_path, state) != OK:
push_error("load failed: " + in_path)
quit(1)
return
var scene := doc.generate_scene(state)
# measure current height from merged mesh AABB
var merged := AABB()
var first := true
var stack: Array = [scene]
while not stack.is_empty():
var n: Node = stack.pop_back()
if n is MeshInstance3D:
var box: AABB = (n as MeshInstance3D).get_aabb()
if first:
merged = box
first = false
else:
merged = merged.merge(box)
for c in n.get_children():
stack.push_back(c)
var h := merged.size.y
var s := target_h / h if h > 0.001 else 1.0
print("height=", h, " scale=", s)
var wrapper := Node3D.new()
wrapper.name = "Root"
wrapper.rotate_y(deg_to_rad(yaw_deg))
wrapper.scale = Vector3(s, s, s)
# move model so feet sit at y=0 after scaling
scene.position.y = -merged.position.y
var parent := scene.get_parent()
if parent != null:
parent.remove_child(scene)
wrapper.add_child(scene)
_own_recursive(scene, wrapper)
var out_doc := GLTFDocument.new()
var out_state := GLTFState.new()
if out_doc.append_from_scene(wrapper, out_state) != OK:
push_error("export append failed")
quit(1)
return
if out_doc.write_to_filesystem(out_state, out_path) != OK:
push_error("write failed: " + out_path)
quit(1)
return
print("WROTE ", out_path)
quit(0)
func _own_recursive(n: Node, owner: Node) -> void:
n.owner = owner
for c in n.get_children():
_own_recursive(c, owner)