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:
parent
548b099204
commit
3720492bc2
8 changed files with 0 additions and 228 deletions
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://ddg6weqfjv8i0
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://dvlhhmfxt7pf0
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://cdm6syy0x6t8b
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
uid://bumqqgmnl1lyb
|
||||
Loading…
Add table
Reference in a new issue