123 lines
5.0 KiB
GDScript
123 lines
5.0 KiB
GDScript
extends SceneTree
|
|
|
|
const STAGE_SCENE := preload("res://scenes/stage/sandbox_stage.tscn")
|
|
const BONE_PATHS: Array[String] = [
|
|
"Skeleton2D/Torso/Head",
|
|
"Skeleton2D/Torso/LeftUpperArm",
|
|
"Skeleton2D/Torso/LeftUpperArm/LeftLowerArm",
|
|
"Skeleton2D/Torso/RightUpperArm",
|
|
"Skeleton2D/Torso/RightUpperArm/RightLowerArm",
|
|
]
|
|
const MARKER_NAMES: Array[String] = ["Head", "Left_Hand", "Right_Hand", "Torso"]
|
|
const ROTATION_TOLERANCE := 0.0001
|
|
const POSITION_TOLERANCE := 0.01
|
|
|
|
var _checks := 0
|
|
var _failures := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_run")
|
|
|
|
|
|
func _run() -> void:
|
|
var stage := STAGE_SCENE.instantiate() as Node2D
|
|
root.add_child(stage)
|
|
stage._snap_enabled = false
|
|
var rig: StickmanRig = stage._spawner.spawn("stickman", Vector2.ZERO)
|
|
stage._finalize_placed_node(rig)
|
|
for frame in 3:
|
|
await process_frame
|
|
var pose_before := _capture_pose(rig)
|
|
var markers_before := _capture_markers(rig)
|
|
stage.set_mode(stage.StageMode.DIRECT)
|
|
for frame in 3:
|
|
await process_frame
|
|
var pose_after := _capture_pose(rig)
|
|
var markers_after := _capture_markers(rig)
|
|
_check(stage.current_mode == stage.StageMode.DIRECT, "stage enters DIRECT")
|
|
for path: String in BONE_PATHS:
|
|
var before: Dictionary = pose_before[path]
|
|
var after: Dictionary = pose_after[path]
|
|
_check(absf(angle_difference(float(before["rotation"]), float(after["rotation"]))) <= ROTATION_TOLERANCE,
|
|
"EDIT to DIRECT preserves %s rotation" % path.get_file())
|
|
_check((before["position"] as Vector2).distance_to(after["position"] as Vector2) <= POSITION_TOLERANCE,
|
|
"EDIT to DIRECT preserves %s position" % path.get_file())
|
|
for marker_name: String in MARKER_NAMES:
|
|
var before: Dictionary = markers_before[marker_name]
|
|
var after: Dictionary = markers_after[marker_name]
|
|
_check((before["position"] as Vector2).distance_to(after["position"] as Vector2) <= POSITION_TOLERANCE,
|
|
"EDIT to DIRECT preserves %s IK marker" % marker_name)
|
|
stage.set_mode(stage.StageMode.EDIT)
|
|
for frame in 3:
|
|
await process_frame
|
|
var pose_round_trip := _capture_pose(rig)
|
|
var markers_round_trip := _capture_markers(rig)
|
|
_check(stage.current_mode == stage.StageMode.EDIT, "stage returns to EDIT")
|
|
for path: String in BONE_PATHS:
|
|
var before: Dictionary = pose_before[path]
|
|
var round_trip: Dictionary = pose_round_trip[path]
|
|
_check(absf(angle_difference(float(before["rotation"]), float(round_trip["rotation"]))) <= ROTATION_TOLERANCE,
|
|
"EDIT to DIRECT to EDIT preserves %s rotation" % path.get_file())
|
|
_check((before["position"] as Vector2).distance_to(round_trip["position"] as Vector2) <= POSITION_TOLERANCE,
|
|
"EDIT to DIRECT to EDIT preserves %s position" % path.get_file())
|
|
for marker_name: String in MARKER_NAMES:
|
|
var before: Dictionary = markers_before[marker_name]
|
|
var round_trip: Dictionary = markers_round_trip[marker_name]
|
|
_check((before["position"] as Vector2).distance_to(round_trip["position"] as Vector2) <= POSITION_TOLERANCE,
|
|
"EDIT to DIRECT to EDIT preserves %s IK marker" % marker_name)
|
|
stage.set_mode(stage.StageMode.PLAY)
|
|
for frame in 3:
|
|
await physics_frame
|
|
stage.set_mode(stage.StageMode.DIRECT)
|
|
for frame in 4:
|
|
await physics_frame
|
|
var pose_after_play := _capture_pose(rig)
|
|
var markers_after_play := _capture_markers(rig)
|
|
_check(stage.current_mode == stage.StageMode.DIRECT, "stage enters DIRECT after PLAY")
|
|
for path: String in BONE_PATHS:
|
|
var before: Dictionary = pose_before[path]
|
|
var after_play: Dictionary = pose_after_play[path]
|
|
_check(absf(angle_difference(float(before["rotation"]), float(after_play["rotation"]))) <= ROTATION_TOLERANCE,
|
|
"EDIT to PLAY to DIRECT restores %s rotation" % path.get_file())
|
|
_check((before["position"] as Vector2).distance_to(after_play["position"] as Vector2) <= POSITION_TOLERANCE,
|
|
"EDIT to PLAY to DIRECT restores %s position" % path.get_file())
|
|
for marker_name: String in MARKER_NAMES:
|
|
var before: Dictionary = markers_before[marker_name]
|
|
var after_play: Dictionary = markers_after_play[marker_name]
|
|
_check((before["position"] as Vector2).distance_to(after_play["position"] as Vector2) <= POSITION_TOLERANCE,
|
|
"EDIT to PLAY to DIRECT restores %s IK marker" % marker_name)
|
|
stage.queue_free()
|
|
await process_frame
|
|
if _failures == 0:
|
|
print("RESULT: ALL PASSED (%d assertions)" % _checks)
|
|
quit(0)
|
|
else:
|
|
print("RESULT: %d FAILURE(S) out of %d assertions" % [_failures, _checks])
|
|
quit(1)
|
|
|
|
|
|
func _capture_pose(rig: StickmanRig) -> Dictionary:
|
|
var result := {}
|
|
for path: String in BONE_PATHS:
|
|
var bone := rig.get_node(path) as Bone2D
|
|
result[path] = {"rotation": bone.global_rotation, "position": bone.global_position}
|
|
return result
|
|
|
|
|
|
func _capture_markers(rig: StickmanRig) -> Dictionary:
|
|
var result := {}
|
|
for marker_name: String in MARKER_NAMES:
|
|
var marker := rig.get_node("IK_Targets/" + marker_name) as Marker2D
|
|
result[marker_name] = {"position": marker.position, "rotation": marker.rotation}
|
|
return result
|
|
|
|
|
|
func _check(condition: bool, label: String) -> void:
|
|
_checks += 1
|
|
if condition:
|
|
print("PASS: %s" % label)
|
|
else:
|
|
_failures += 1
|
|
push_error("FAIL: %s" % label)
|