# GIF capture: plays a showcase of UAL1 clips on the tattooed model while the # camera orbits a full 360°, then quits. Run with movie-maker mode: # tinqs...console.exe --path res://gif_capture.tscn \ # --write-movie /capture.png --fixed-fps 15 extends Node3D const GLTF_PATH := "C:/Users/CAN/tinqs-ltd/ariki-game/assets/quaternius/source-models/Regular_Male_FullBody.gltf" const BAKED_PATH := "C:/Users/CAN/tinqs-ltd/tattoo-test/baked_body.png" const NORMAL_PATH := "C:/Users/CAN/tinqs-ltd/ariki-game/assets/quaternius/source-models/T_Regular_Male_Normal_png.png" const EYE_PATH := "C:/Users/CAN/tinqs-ltd/ariki-game/assets/quaternius/source-models/T_Eye_Brown.png" const HAIR_PATH := "C:/Users/CAN/tinqs-ltd/ariki-game/assets/quaternius/source-models/T_Hair_1_BaseColor.png" const UAL1 := "C:/Users/CAN/tinqs-ltd/ariki-game/assets/quaternius/anim-lib-1/Universal Animation Library[Standard]/Unreal-Godot/UAL1_Standard.glb" # [clip, seconds] — one full camera orbit over the whole timeline const SCHEDULE := [ ["Idle_Loop", 2.5], ["Walk_Loop", 2.5], ["Jog_Fwd_Loop", 2.5], ["Dance_Loop", 3.0], ["Punch_Jab", 1.2], ["Punch_Cross", 1.3], ] var skel: Skeleton3D var cam: Camera3D var player: AnimationPlayer var hud: Label var elapsed := 0.0 var total := 0.0 var seg_idx := -1 var orbit_pitch := 0.12 var orbit_dist := 2.5 var orbit_target := Vector3(0, 1.0, 0) func _ready() -> void: var env := Environment.new() env.background_mode = Environment.BG_COLOR env.background_color = Color(0.12, 0.13, 0.16) env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR env.ambient_light_color = Color(0.75, 0.78, 0.85) env.ambient_light_energy = 0.7 env.tonemap_mode = Environment.TONE_MAPPER_FILMIC var we := WorldEnvironment.new() we.environment = env add_child(we) var sun := DirectionalLight3D.new() sun.rotation_degrees = Vector3(-40, -35, 0) sun.light_energy = 1.5 add_child(sun) var fill := DirectionalLight3D.new() fill.rotation_degrees = Vector3(-15, 140, 0) fill.light_energy = 0.6 add_child(fill) var under := DirectionalLight3D.new() under.rotation_degrees = Vector3(35, 0, 0) under.light_energy = 0.35 add_child(under) var ground := MeshInstance3D.new() var plane := PlaneMesh.new() plane.size = Vector2(12, 12) ground.mesh = plane var gmat := StandardMaterial3D.new() gmat.albedo_color = Color(0.16, 0.17, 0.21) gmat.roughness = 1.0 ground.set_surface_override_material(0, gmat) add_child(ground) var doc := GLTFDocument.new() var state := GLTFState.new() if doc.append_from_file(GLTF_PATH, state) != OK: push_error("GLTF load failed") get_tree().quit(1) return var model := doc.generate_scene(state) add_child(model) var baked := ImageTexture.create_from_image(Image.load_from_file(BAKED_PATH)) var body_normal := ImageTexture.create_from_image(Image.load_from_file(NORMAL_PATH)) var eye_tex := ImageTexture.create_from_image(Image.load_from_file(EYE_PATH)) var hair_tex := ImageTexture.create_from_image(Image.load_from_file(HAIR_PATH)) var stack: Array = [model] while not stack.is_empty(): var n: Node = stack.pop_back() if n is Skeleton3D and skel == null: skel = n if n is MeshInstance3D: var lname := String(n.name).to_lower() var mat := StandardMaterial3D.new() mat.roughness = 0.85 if lname.contains("regularmale"): mat.albedo_texture = baked mat.normal_enabled = true mat.normal_texture = body_normal elif lname.contains("eye") and not lname.contains("brow"): mat.albedo_texture = eye_tex else: mat.albedo_texture = hair_tex for s in n.mesh.get_surface_count(): n.set_surface_override_material(s, mat) for c in n.get_children(): stack.push_back(c) _load_ual_clips() cam = Camera3D.new() cam.fov = 50 add_child(cam) cam.current = true hud = Label.new() hud.position = Vector2(16, 12) hud.add_theme_font_size_override("font_size", 26) hud.add_theme_color_override("font_color", Color(0.95, 0.95, 0.9)) add_child(hud) for spec in SCHEDULE: total += spec[1] func _load_ual_clips() -> void: var doc := GLTFDocument.new() var state := GLTFState.new() if doc.append_from_file(UAL1, state) != OK: push_error("UAL1 load failed") get_tree().quit(1) return var ual := doc.generate_scene(state) var src: AnimationPlayer = null var stack: Array = [ual] while not stack.is_empty(): var n: Node = stack.pop_back() if n is AnimationPlayer: src = n break for c in n.get_children(): stack.push_back(c) if src == null: push_error("no AnimationPlayer in UAL1") get_tree().quit(1) return player = AnimationPlayer.new() add_child(player) var skel_path := String(get_path_to(skel)) var lib := AnimationLibrary.new() for spec in SCHEDULE: var clip_name: String = spec[0] if lib.has_animation(clip_name): continue if not src.has_animation(clip_name): push_warning("UAL1 missing clip: " + clip_name) continue var anim := src.get_animation(clip_name).duplicate() as Animation anim.loop_mode = Animation.LOOP_LINEAR for i in anim.get_track_count(): var path := String(anim.track_get_path(i)) if path.contains("Armature/Skeleton3D"): anim.track_set_path(i, path.replace("Armature/Skeleton3D", skel_path)) lib.add_animation(clip_name, anim) player.add_animation_library("", lib) ual.queue_free() func _process(delta: float) -> void: if player == null: return elapsed += delta if elapsed >= total: get_tree().quit() return # which schedule segment are we in? var t := elapsed var idx := 0 for spec in SCHEDULE: if t < spec[1]: break t -= spec[1] idx += 1 idx = mini(idx, SCHEDULE.size() - 1) if idx != seg_idx: seg_idx = idx var clip: String = SCHEDULE[idx][0] if player.has_animation(clip): player.play(clip) hud.text = clip.replace("_Loop", "").replace("_", " ") # one full orbit over the whole timeline, starting from the front var yaw := TAU * elapsed / total var dir := Vector3( sin(yaw) * cos(orbit_pitch), sin(orbit_pitch), cos(yaw) * cos(orbit_pitch)) cam.look_at_from_position(orbit_target + dir * orbit_dist, orbit_target)