@tool
extends EditorScript
## Run this from the Godot editor: Script Editor -> File -> Run (Ctrl+Shift+X)
## It opens the tumble.glb, lists all animations, and extracts poses.
##
## BEFORE RUNNING: Edit the EXTRACTION_MAP below to map pose names to
## animation names + seek times. Run once with empty map to see available
## animations and their durations.
const PoseLibraryClass := preload("res://scenes/player/procedural/pose_library.gd")
## Edit this map! Format: "pose_name": { "anim": "animation_name", "time": seconds }
## Run with empty map first to see available animations.
const EXTRACTION_MAP: Dictionary = {
"idle": { "anim": "default", "time": 0.042 },
"walk_a": { "anim": "walk-forward", "time": 0.042 }, # contact: left foot forward
"walk_b": { "anim": "walk-forward", "time": 0.458 }, # contact: right foot forward
"run_a": { "anim": "run", "time": 0.042 },
"run_b": { "anim": "run", "time": 0.458 },
"jump_launch": { "anim": "jump", "time": 0.042 },
"jump_air": { "anim": "jump", "time": 0.5 },
"fall": { "anim": "falling", "time": 0.042 },
"land": { "anim": "landing", "time": 0.042 },
}
const OUTPUT_PATH: String = "res://scenes/player/procedural/tumble_poses.tres"
func _run() -> void:
# Load the tumble model
var tumble_scene: PackedScene = load("res://assets/models/tumble/tumble.glb")
if not tumble_scene:
print("ERROR: Could not load tumble.glb")
return
var tumble: Node3D = tumble_scene.instantiate()
# Find AnimationPlayer and Skeleton3D
var anim_player: AnimationPlayer = _find_child(tumble, "AnimationPlayer") as AnimationPlayer
var skeleton: Skeleton3D = _find_child(tumble, "Skeleton3D") as Skeleton3D
if not anim_player:
print("ERROR: No AnimationPlayer found in tumble.glb")
tumble.queue_free()
return
if not skeleton:
print("ERROR: No Skeleton3D found in tumble.glb")
tumble.queue_free()
return
# List all animations
print("=== Available Animations ===")
var anims: PackedStringArray = anim_player.get_animation_list()
for anim_name in anims:
var anim: Animation = anim_player.get_animation(anim_name)
print(" '", anim_name, "': length=", snapped(anim.length, 0.001), "s, tracks=", anim.get_track_count(), ", loop=", anim.loop_mode)
print("")
# List all bones
print("=== Skeleton Bones (", skeleton.get_bone_count(), ") ===")
for i in skeleton.get_bone_count():
var parent_idx: int = skeleton.get_bone_parent(i)
var parent_name: String = skeleton.get_bone_name(parent_idx) if parent_idx >= 0 else "(root)"
print(" [", i, "] ", skeleton.get_bone_name(i), " <- ", parent_name)
print("")
if EXTRACTION_MAP.is_empty():
print("EXTRACTION_MAP is empty. Edit this script, fill in the map above, and run again.")
tumble.queue_free()
return
# We need to add to scene tree for AnimationPlayer to work
get_editor_interface().get_edited_scene_root().add_child(tumble)
# Extract poses
var lib: Resource = PoseLibraryClass.new()
for pose_name: String in EXTRACTION_MAP:
var entry: Dictionary = EXTRACTION_MAP[pose_name]
var anim_name: String = entry.get("anim", "")
var seek_time: float = entry.get("time", 0.0)
if not anim_player.has_animation(anim_name):
print("WARNING: Animation '", anim_name, "' not found, skipping '", pose_name, "'")
continue
anim_player.play(anim_name)
anim_player.seek(seek_time, true)
anim_player.advance(0.0)
var pose: Dictionary = {}
for bone_idx in skeleton.get_bone_count():
var bone_name: String = skeleton.get_bone_name(bone_idx)
var pos: Vector3 = skeleton.get_bone_pose_position(bone_idx)
var rot: Quaternion = skeleton.get_bone_pose_rotation(bone_idx)
var scl: Vector3 = skeleton.get_bone_pose_scale(bone_idx)
pose[bone_name] = Transform3D(Basis(rot).scaled(scl), pos)
lib.poses[pose_name] = pose
print("Captured '", pose_name, "' from '", anim_name, "' @ t=", seek_time, " (", pose.size(), " bones)")
anim_player.stop()
tumble.get_parent().remove_child(tumble)
tumble.queue_free()
# Save
var err: int = ResourceSaver.save(lib, OUTPUT_PATH)
if err == OK:
print("\nSaved PoseLibrary to ", OUTPUT_PATH, " with ", lib.poses.size(), " poses")
print("Poses: ", lib.poses.keys())
else:
print("ERROR: Failed to save, error code ", err)
func _find_child(node: Node, type_name: String) -> Node:
for child in node.get_children():
if child.get_class() == type_name:
return child
var found: Node = _find_child(child, type_name)
if found:
return found
return null