159 lines
6.3 KiB
GDScript
159 lines
6.3 KiB
GDScript
extends SceneTree
|
|
## Headless smoke test verifying Studio floor collision, selection,
|
|
## gizmo ray picking, inspector property sync, and transformation manipulation.
|
|
|
|
var _failures: int = 0
|
|
var _checks: int = 0
|
|
|
|
const StudioMainClass = preload("res://scripts/studio/studio_main.gd")
|
|
const TransformGizmo3DClass = preload("res://scripts/studio/gizmos/transform_gizmo_3d.gd")
|
|
|
|
func _init() -> void:
|
|
print("--- Running Smoke Test: Studio Viewport Interaction & Floor Collision ---")
|
|
test_floor_collision_body()
|
|
test_inspector_sync_and_visual_transform()
|
|
test_gizmo_picking_and_dragging()
|
|
test_nudge_shortcuts_and_delete()
|
|
|
|
if _failures == 0:
|
|
print("--- PASS: All %d studio interaction checks passed! ---" % _checks)
|
|
quit(0)
|
|
else:
|
|
printerr("--- FAIL: %d failures out of %d checks ---" % [_failures, _checks])
|
|
quit(1)
|
|
|
|
|
|
func _check(desc: String, cond: bool) -> void:
|
|
_checks += 1
|
|
if cond:
|
|
print(" ✓ %s" % desc)
|
|
else:
|
|
_failures += 1
|
|
printerr(" ✗ FAIL: %s" % desc)
|
|
|
|
|
|
func test_floor_collision_body() -> void:
|
|
print("Testing Studio floor collision setup...")
|
|
var studio := StudioMainClass.new()
|
|
root.add_child(studio)
|
|
|
|
var floor_body = studio._sub_viewport.find_child("StudioFloorBody", true, false)
|
|
_check("StudioFloorBody exists in 3D viewport", floor_body != null)
|
|
if floor_body is StaticBody3D:
|
|
var sb := floor_body as StaticBody3D
|
|
_check("Floor body has collision layer 1 enabled", sb.get_collision_layer_value(1))
|
|
_check("Floor body has collision mask 1 enabled", sb.get_collision_mask_value(1))
|
|
var col = sb.find_child("*", true, false) as CollisionShape3D
|
|
_check("Floor body has CollisionShape3D", col != null and col.shape is BoxShape3D)
|
|
if col != null and col.shape is BoxShape3D:
|
|
var box := col.shape as BoxShape3D
|
|
_check("Floor collider spans at least 100x100m", box.size.x >= 100.0 and box.size.z >= 100.0)
|
|
|
|
studio.queue_free()
|
|
|
|
|
|
func test_inspector_sync_and_visual_transform() -> void:
|
|
print("Testing Inspector property sync to 3D visual nodes...")
|
|
var studio := StudioMainClass.new()
|
|
root.add_child(studio)
|
|
|
|
# Place an object
|
|
var obj_id := studio.core.place_object("crate_small", Vector3(10, 20, 0))
|
|
studio._spawn_visual_node(obj_id)
|
|
studio._on_selection_changed(obj_id)
|
|
|
|
var visual_node := studio._world_container.get_node_or_null(obj_id) as Node3D
|
|
_check("Visual node exists in world container", visual_node != null)
|
|
|
|
# Simulate inspector position change (moving object via Inspector)
|
|
studio._inspector_dock._pos_x.value = 15.0
|
|
studio._inspector_dock._pos_y.value = 3.0
|
|
studio._inspector_dock._pos_z.value = 25.0
|
|
studio._inspector_dock._on_pos_changed(0.0)
|
|
|
|
_check("Visual node position updated to match Inspector X", is_equal_approx(visual_node.position.x, 15.0))
|
|
_check("Visual node position updated to match Inspector Y (height)", is_equal_approx(visual_node.position.y, 3.0))
|
|
_check("Visual node position updated to match Inspector Z (depth)", is_equal_approx(visual_node.position.z, 25.0))
|
|
|
|
# Simulate inspector yaw rotation change
|
|
studio._inspector_dock._rot_y.value = 90.0
|
|
studio._inspector_dock._on_rot_changed(90.0)
|
|
_check("Visual node rotated to match Inspector Yaw", absf(visual_node.rotation_degrees.y - 90.0) < 0.1)
|
|
|
|
# Simulate inspector scale change
|
|
studio._inspector_dock._scale_x.value = 2.0
|
|
studio._inspector_dock._scale_y.value = 1.5
|
|
studio._inspector_dock._scale_z.value = 2.0
|
|
studio._inspector_dock._on_scale_changed(0.0)
|
|
_check("Visual node scale updated to match Inspector", is_equal_approx(visual_node.scale.x, 2.0) and is_equal_approx(visual_node.scale.y, 1.5))
|
|
|
|
studio.queue_free()
|
|
|
|
|
|
func test_gizmo_picking_and_dragging() -> void:
|
|
print("Testing Gizmo geometric picking and axis detection...")
|
|
var studio := StudioMainClass.new()
|
|
root.add_child(studio)
|
|
|
|
var obj_id := studio.core.place_object("bison", Vector3(0, 0, 0))
|
|
studio._spawn_visual_node(obj_id)
|
|
studio._on_selection_changed(obj_id)
|
|
|
|
var gizmo = studio._gizmo_3d
|
|
_check("Gizmo is attached and visible on selection", gizmo != null and gizmo.visible)
|
|
|
|
# Test X axis picking (ray aimed near +X arrow at (1.0, 0, 0) relative to gizmo)
|
|
var ray_origin: Vector3 = gizmo.position + Vector3(1.0, 5.0, 0.0)
|
|
var ray_dir: Vector3 = Vector3(0.0, -1.0, 0.0)
|
|
var picked_axis = gizmo.pick_axis(ray_origin, ray_dir)
|
|
_check("Gizmo ray picking correctly identified X axis", picked_axis == TransformGizmo3DClass.Axis.X)
|
|
|
|
# Test Y axis picking (ray aimed near +Y arrow at (0, 1.0, 0) relative to gizmo)
|
|
ray_origin = gizmo.position + Vector3(0.0, 1.0, 5.0)
|
|
ray_dir = Vector3(0.0, 0.0, -1.0)
|
|
picked_axis = gizmo.pick_axis(ray_origin, ray_dir)
|
|
_check("Gizmo ray picking correctly identified Y axis", picked_axis == TransformGizmo3DClass.Axis.Y)
|
|
|
|
# Test Z axis picking (ray aimed near +Z arrow at (0, 0, 1.0) relative to gizmo)
|
|
ray_origin = gizmo.position + Vector3(0.0, 5.0, 1.0)
|
|
ray_dir = Vector3(0.0, -1.0, 0.0)
|
|
picked_axis = gizmo.pick_axis(ray_origin, ray_dir)
|
|
_check("Gizmo ray picking correctly identified Z axis", picked_axis == TransformGizmo3DClass.Axis.Z)
|
|
|
|
# Test Center picking (ray aimed at gizmo origin)
|
|
ray_origin = gizmo.position + Vector3(0.0, 5.0, 0.0)
|
|
ray_dir = Vector3(0.0, -1.0, 0.0)
|
|
picked_axis = gizmo.pick_axis(ray_origin, ray_dir)
|
|
_check("Gizmo ray picking correctly identified Center/Plane handle", picked_axis == TransformGizmo3DClass.Axis.XY)
|
|
|
|
studio.queue_free()
|
|
|
|
|
|
func test_nudge_shortcuts_and_delete() -> void:
|
|
print("Testing keyboard nudge shortcuts and object deletion...")
|
|
var studio := StudioMainClass.new()
|
|
root.add_child(studio)
|
|
|
|
var obj_id := studio.core.place_object("crate_small", Vector3(0, 0, 0))
|
|
studio._spawn_visual_node(obj_id)
|
|
studio.core.select_object(obj_id)
|
|
studio._on_selection_changed(obj_id)
|
|
|
|
var visual_node := studio._world_container.get_node_or_null(obj_id) as Node3D
|
|
|
|
# Nudge position right (+X)
|
|
var old_x = visual_node.position.x
|
|
studio._nudge_selected_position(Vector3(1, 0, 0))
|
|
_check("Nudge right moved visual node +X by 1.0", is_equal_approx(visual_node.position.x, old_x + 1.0))
|
|
|
|
# Nudge rotation
|
|
studio._nudge_selected_rotation(45.0)
|
|
_check("Nudge rotation rotated visual node by 45 degrees", is_equal_approx(visual_node.rotation_degrees.y, 45.0))
|
|
|
|
# Delete object
|
|
studio._delete_selected_object(obj_id)
|
|
_check("Object deleted from Core model", studio.core.model.find_object(obj_id) == null)
|
|
_check("Selection cleared after delete", studio.core.selection.get_selected() == "")
|
|
|
|
studio.queue_free()
|