File size: 5,509 Bytes
84e8204 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | extends Node
##
## SelfTest - headless verification of every core subsystem.
## Run: godot --headless res://tests/self_test.tscn
##
## Exits with code 0 if all checks pass, 1 otherwise. This is the regression
## gate for the modules; it does not need a display or GPU.
##
var _failures := 0
var _host: Node3D
func _ready() -> void:
_host = Node3D.new()
_host.name = "TestHost"
var geom := Node3D.new()
geom.name = "Geometry"
_host.add_child(geom)
var rig := Node3D.new()
rig.name = "Rig"
_host.add_child(rig)
add_child(_host)
_test_memory_manager()
_test_mesh_generators()
_test_sculpt()
_test_rigging()
_test_simulation()
_test_timeline()
print("")
if _failures == 0:
print("SELFTEST: ALL PASS")
get_tree().quit(0)
else:
print("SELFTEST: %d FAILURE(S)" % _failures)
get_tree().quit(1)
func _check(name: String, cond: bool) -> void:
if cond:
print(" [PASS] ", name)
else:
_failures += 1
print(" [FAIL] ", name)
func _test_memory_manager() -> void:
print("[Test] MemoryManager")
_check("budget set", MemoryManager.asset_budget_mb > 0.0)
_check("feature count >= 20", FeatureRegistry.feature_count() >= 20)
var snap := MemoryManager.debug_snapshot()
_check("snapshot has budget", snap.has("budget_mb"))
func _load_module(path: String) -> Node:
var script: GDScript = load(path)
var n: Node = script.new()
_host.add_child(n)
if n.has_method("_activate"):
n.call("_activate", _host)
return n
func _test_mesh_generators() -> void:
print("[Test] Mesh generators")
var mg := _load_module("res://scripts/modules/mesh_gen.gd")
for kind in ["cube", "sphere", "cylinder", "plane", "torus", "ico", "suzanne"]:
var data: Dictionary = mg.call("generate", kind, {})
var vcount: int = (data["verts"] as PackedVector3Array).size()
var icount: int = (data["indices"] as PackedInt32Array).size()
_check("%s has verts & tris (v=%d,i=%d)" % [kind, vcount, icount], vcount > 0 and icount > 0)
var mi: MeshInstance3D = mg.call("spawn", "sphere", {"radius": 1.0})
_check("spawn returns MeshInstance3D", mi != null and mi.mesh != null and mi.mesh.get_surface_count() == 1)
var cost := VertexBufferManager.estimate_cost_mb(1000)
_check("cost estimate > 0", cost > 0.0)
mg.queue_free()
func _test_sculpt() -> void:
print("[Test] Sculpting")
var mg := _load_module("res://scripts/modules/mesh_gen.gd")
var sc := _load_module("res://scripts/modules/sculpt.gd")
var mi: MeshInstance3D = mg.call("spawn", "sphere", {"radius": 1.0})
sc.call("set_target", mi)
var before: PackedVector3Array = (mi.mesh.surface_get_arrays(0)[Mesh.ARRAY_VERTEX] as PackedVector3Array).duplicate()
for brush in ["draw", "grab", "pinch", "smooth", "flatten"]:
sc.set("_brush_id", brush)
sc.call("begin", Vector3(0, 1, 0))
sc.call("apply", Vector3(0, 1, 0), Vector3(0, 0.1, 0))
sc.call("apply", Vector3(0, 1, 0), Vector3(0.05, 0.1, 0))
sc.call("end")
var after: PackedVector3Array = mi.mesh.surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
var changed := false
for i in range(mini(before.size(), after.size())):
if before[i].distance_to(after[i]) > 0.0001:
changed = true
break
_check("sculpt modified vertices", changed)
_check("mesh still valid after sculpt", mi.mesh.get_surface_count() == 1)
mg.queue_free()
sc.queue_free()
func _test_rigging() -> void:
print("[Test] Rigging")
var mg := _load_module("res://scripts/modules/mesh_gen.gd")
var rg := _load_module("res://scripts/modules/rigging.gd")
var mi: MeshInstance3D = mg.call("spawn", "cube", {"size": 1.0})
rg.call("generate_default_armature", mi)
_check("bones created", int(rg.call("bone_count")) >= 9)
_check("mesh has skin", mi.skin != null)
_check("mesh has skeleton path", mi.skeleton != NodePath())
if mi.mesh.get_surface_count() > 0:
var arr: Array = mi.mesh.surface_get_arrays(0)
_check("bones channel present", arr[Mesh.ARRAY_BONES] != null)
_check("weights channel present", arr[Mesh.ARRAY_WEIGHTS] != null)
mg.queue_free()
rg.queue_free()
func _test_simulation() -> void:
print("[Test] Simulation")
var sim := _load_module("res://scripts/modules/simulation.gd")
sim.call("create_cloth", "sim.cloth", 6, 6, 1.0)
var before: PackedVector3Array = (sim.get("_sims")["sim.cloth"]["particles"] as PackedVector3Array).duplicate()
for i in range(30):
sim.call("_step", sim.get("_sims")["sim.cloth"])
sim.call("_upload", sim.get("_sims")["sim.cloth"])
var after: PackedVector3Array = sim.get("_sims")["sim.cloth"]["particles"]
var moved := false
for i in range(after.size()):
if before[i].distance_to(after[i]) > 0.0001:
moved = true
break
_check("cloth fell under gravity", moved)
var mesh: ArrayMesh = sim.get("_sims")["sim.cloth"]["mesh"]
_check("cloth mesh uploaded", mesh.get_surface_count() == 1)
sim.call("create_hair", "sim.hair", 4, 5)
_check("hair sim created", int(sim.call("active_sim_count")) >= 2)
sim.queue_free()
func _test_timeline() -> void:
print("[Test] Timeline")
var mg := _load_module("res://scripts/modules/mesh_gen.gd")
var tl := _load_module("res://scripts/modules/timeline.gd")
var mi: MeshInstance3D = mg.call("spawn", "cube", {"size": 1.0})
tl.call("add_keyframe", mi, "position", 0.0, Vector3.ZERO)
tl.call("add_keyframe", mi, "position", 1.0, Vector3(0, 2, 0))
_check("track created", int(tl.call("track_count")) >= 1)
_check("key count == 2", int(tl.call("key_count")) == 2)
tl.call("scrub", 0.5)
_check("scrub clamps/works", absf(float(tl.call("playhead")) - 0.5) < 0.001)
mg.queue_free()
tl.queue_free()
|