refactor(tools): ♻️ Improve editor tooling for animations, blendshapes, screenshots, and zoom testing with restructured UI/logic

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-28 14:55:36 -07:00
parent 548b099204
commit 3720492bc2
8 changed files with 0 additions and 228 deletions

View file

@ -1,57 +0,0 @@
extends SceneTree
## CLI tool: lists all AnimationPlayer animations and their tracks.
## Usage: godot --headless --path . --script tools/list_animations.gd
const MODEL_DIR: String = "res://models/"
const PREFERRED: Array[String] = ["Miku.vrm", "Seed-san.vrm"]
const TRACK_TYPES: Dictionary = {
0: "value", 1: "position", 2: "rotation",
3: "scale", 4: "blend_shape", 5: "method",
6: "bezier", 7: "audio", 8: "animation",
}
func _init() -> void:
var model_path := ""
for name: String in PREFERRED:
var path := MODEL_DIR + name
if ResourceLoader.exists(path):
model_path = path
break
if model_path.is_empty():
print("ERROR: No VRM model found in %s" % MODEL_DIR)
quit(1)
return
var scene: PackedScene = load(model_path)
var instance := scene.instantiate()
print("Model: %s" % model_path)
_find_animations(instance)
instance.queue_free()
quit(0)
func _find_animations(node: Node) -> void:
if node is AnimationPlayer:
var ap: AnimationPlayer = node as AnimationPlayer
print("\nAnimationPlayer: %s" % node.name)
for anim_name: String in ap.get_animation_list():
var anim := ap.get_animation(anim_name)
print(" '%s' (%.2fs, %d tracks)" % [
anim_name,
anim.length,
anim.get_track_count(),
])
for i: int in range(anim.get_track_count()):
var type_str: String = TRACK_TYPES.get(
anim.track_get_type(i), "unknown"
)
print(" [%d] %s (%s)" % [
i, anim.track_get_path(i), type_str,
])
for child: Node in node.get_children():
_find_animations(child)

View file

@ -1 +0,0 @@
uid://ddg6weqfjv8i0

View file

@ -1,61 +0,0 @@
extends SceneTree
## CLI tool: lists all blendshape names from the loaded VRM model.
## Usage: godot --headless --path . --script tools/list_blendshapes.gd
const MODEL_DIR: String = "res://models/"
const PREFERRED: Array[String] = ["Miku.vrm", "Seed-san.vrm"]
func _init() -> void:
var model_path := ""
for name: String in PREFERRED:
var path := MODEL_DIR + name
if ResourceLoader.exists(path):
model_path = path
break
if model_path.is_empty():
print("ERROR: No VRM model found in %s" % MODEL_DIR)
quit(1)
return
var scene: PackedScene = load(model_path)
var instance := scene.instantiate()
print("Model: %s" % model_path)
_print_blendshapes(instance)
print("")
_print_bones(instance)
instance.queue_free()
quit(0)
func _print_blendshapes(node: Node) -> void:
if node is MeshInstance3D:
var mi: MeshInstance3D = node as MeshInstance3D
if mi.mesh != null and mi.mesh.get_blend_shape_count() > 0:
print("\nMesh: %s (%d blendshapes)" % [
mi.name,
mi.mesh.get_blend_shape_count(),
])
for i: int in range(mi.mesh.get_blend_shape_count()):
print(" [%d] %s" % [
i, mi.mesh.get_blend_shape_name(i),
])
for child: Node in node.get_children():
_print_blendshapes(child)
func _print_bones(node: Node) -> void:
if node is Skeleton3D:
var skel: Skeleton3D = node as Skeleton3D
print("Skeleton: %s (%d bones)" % [
node.name, skel.get_bone_count(),
])
for i: int in range(skel.get_bone_count()):
print(" [%d] %s" % [
i, skel.get_bone_name(i),
])
for child: Node in node.get_children():
_print_bones(child)

View file

@ -1 +0,0 @@
uid://dvlhhmfxt7pf0

View file

@ -1,47 +0,0 @@
extends SceneTree
## CLI tool: captures a screenshot of the companion scene after a delay.
## Usage: godot --path . --script tools/screenshot.gd
## Output: user://chobit_screenshot.png
const DELAY_FRAMES: int = 90
const SCENE_PATH: String = "res://scenes/companion.tscn"
var _frame_count: int = 0
var _scene_instance: Node
func _init() -> void:
var scene: PackedScene = load(SCENE_PATH)
if scene == null:
print("ERROR: Could not load %s" % SCENE_PATH)
quit(1)
return
_scene_instance = scene.instantiate()
root.add_child(_scene_instance)
func _process(_delta: float) -> bool:
_frame_count += 1
if _frame_count >= DELAY_FRAMES:
_capture()
return true
return false
func _capture() -> void:
var img := root.get_viewport().get_texture().get_image()
if img == null:
print("ERROR: No viewport image")
quit(1)
return
var path := OS.get_user_data_dir() + "/chobit_screenshot.png"
var err := img.save_png(path)
if err != OK:
print("ERROR: save_png failed (%d)" % err)
quit(1)
return
print(path)
quit(0)

View file

@ -1 +0,0 @@
uid://cdm6syy0x6t8b

View file

@ -1,59 +0,0 @@
extends Node3D
## Zoom test: cycles through zoom levels, captures screenshot at each.
## Attach to the companion scene temporarily for testing.
const ZOOM_LEVELS: Array[float] = [0.15, 0.3, 0.5, 0.75, 1.0]
const ASPECT_RATIO: float = 2.0 / 3.0
var _current_idx: int = 0
var _frame_wait: int = 0
var _done: bool = false
func _ready() -> void:
_apply_zoom_level(ZOOM_LEVELS[0])
func _process(_delta: float) -> void:
if _done:
return
_frame_wait += 1
if _frame_wait < 30:
return
_capture_screenshot()
_current_idx += 1
if _current_idx >= ZOOM_LEVELS.size():
print("Zoom test complete: %d screenshots" % ZOOM_LEVELS.size())
_done = true
get_tree().quit()
return
_apply_zoom_level(ZOOM_LEVELS[_current_idx])
_frame_wait = 0
func _apply_zoom_level(zoom: float) -> void:
var screen_idx := DisplayServer.window_get_current_screen()
var screen_rect := DisplayServer.screen_get_usable_rect(screen_idx)
var max_h := screen_rect.size.y
var h := maxi(int(float(max_h) * zoom), 100)
var w := maxi(int(float(h) * ASPECT_RATIO), 67)
DisplayServer.window_set_size(Vector2i(w, h))
print("Zoom %.2f: window %dx%d" % [zoom, w, h])
func _capture_screenshot() -> void:
var zoom := ZOOM_LEVELS[_current_idx]
var img := get_viewport().get_texture().get_image()
if img == null:
print("ERROR: no image at zoom %.2f" % zoom)
return
var path := OS.get_user_data_dir() + "/zoom_test_%.0f.png" % (zoom * 100)
img.save_png(path)
print("Saved: %s" % path)

View file

@ -1 +0,0 @@
uid://bumqqgmnl1lyb