extends SceneTree ## Asset-free formula, dependency and timing regression for ADT M2 transforms. const RESOLVER_SCRIPT := preload("res://src/render/m2/m2_placement_transform_resolver.gd") const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd" const GROUPER_PATH := "res://src/render/m2/m2_placement_grouper.gd" const WATERFALL_WORLD_YAW_OFFSET := PI * 0.5 const CLIFFROCK_WORLD_YAW_OFFSET := PI func _initialize() -> void: var failures: Array[String] = [] _verify_regular_basis(failures) _verify_cliffrock_basis(failures) _verify_new_waterfall_basis(failures) _verify_tall_waterfall_basis_and_offset(failures) _verify_path_normalization_and_scale_clamp(failures) _verify_loader_boundary(failures) var elapsed_milliseconds := _verify_bounded_timing(failures) if not failures.is_empty(): for failure in failures: push_error("M2_PLACEMENT_TRANSFORM_RESOLVER: %s" % failure) quit(1) return print( "M2_PLACEMENT_TRANSFORM_RESOLVER PASS cases=9 iterations=10000 elapsed_ms=%.3f" % elapsed_milliseconds ) quit(0) func _verify_regular_basis(failures: Array[String]) -> void: var resolver: RefCounted = RESOLVER_SCRIPT.new() var rotation := Vector3(0.2, -0.7, 1.1) _expect_basis_near( resolver.call("resolve_basis", rotation, "World/Generic/Tree.m2"), Basis.from_euler(rotation), "regular basis", failures ) _expect_vector_near( resolver.call("resolve_origin_offset", rotation, "World/Generic/Tree.m2", 3.0), Vector3.ZERO, "regular offset", failures ) func _verify_cliffrock_basis(failures: Array[String]) -> void: var resolver: RefCounted = RESOLVER_SCRIPT.new() var rotation := Vector3(0.3, 0.4, -0.2) var expected_basis := ( Basis(Vector3.UP, rotation.y + CLIFFROCK_WORLD_YAW_OFFSET) * Basis(Vector3.BACK, rotation.x) * Basis(Vector3.RIGHT, -rotation.z) ) _expect_basis_near( resolver.call("resolve_basis", rotation, "WORLD\\AZEROTH\\ElwynnCliffRock01.MDX"), expected_basis, "cliffrock basis", failures ) func _verify_new_waterfall_basis(failures: Array[String]) -> void: var resolver: RefCounted = RESOLVER_SCRIPT.new() var rotation := Vector3(-0.1, 0.8, 0.2) var fall_axis := Vector3(-0.138742, 0.990329, 0.0).normalized() var expected_basis := ( Basis(Vector3.UP, WATERFALL_WORLD_YAW_OFFSET) * Basis.from_euler(rotation) * Basis(fall_axis, PI * 0.5) ) _expect_basis_near( resolver.call("resolve_basis", rotation, "world/x/NewWaterfall.m2"), expected_basis, "new waterfall basis", failures ) _expect_vector_near( resolver.call("resolve_origin_offset", rotation, "world/x/NewWaterfall.m2", 1.0), Vector3.ZERO, "new waterfall offset", failures ) func _verify_tall_waterfall_basis_and_offset(failures: Array[String]) -> void: var resolver: RefCounted = RESOLVER_SCRIPT.new() var rotation := Vector3(0.05, -0.2, 0.15) var path := "world/x/ElwynnTallWaterfall01.m2" var fall_axis := Vector3(-0.068962, 0.997619, 0.0).normalized() var placement_basis := ( Basis(Vector3.UP, WATERFALL_WORLD_YAW_OFFSET) * Basis.from_euler(rotation) ) var expected_basis := placement_basis * Basis(fall_axis, -PI * 0.5) var anchor := Vector3(-2.667799, 89.62273, 0.00129) var expected_offset := (placement_basis * anchor - expected_basis * anchor) * 1.5 _expect_basis_near( resolver.call("resolve_basis", rotation, path), expected_basis, "tall waterfall basis", failures ) _expect_vector_near( resolver.call("resolve_origin_offset", rotation, path, 1.5), expected_offset, "tall waterfall offset", failures ) func _verify_path_normalization_and_scale_clamp(failures: Array[String]) -> void: var resolver: RefCounted = RESOLVER_SCRIPT.new() var path := "WORLD\\X\\ELWYNNTALLWATERFALL01.MDX" var minimum_offset: Vector3 = resolver.call("resolve_origin_offset", Vector3.ZERO, path, 0.0001) _expect_vector_near( resolver.call("resolve_origin_offset", Vector3.ZERO, path, -9.0), minimum_offset, "negative scale clamp", failures ) _expect_basis_near( resolver.call("resolve_basis", Vector3.ZERO, "ElwynnCliffRock02.m2"), Basis(Vector3.UP, PI), "second cliffrock", failures ) func _verify_loader_boundary(failures: Array[String]) -> void: var source := _read_text(LOADER_PATH, failures) var grouper_source := _read_text(GROUPER_PATH, failures) _expect_true( source.contains("M2_PLACEMENT_TRANSFORM_RESOLVER_SCRIPT.new()"), "loader composes resolver", failures ) _expect_equal( source.count("_m2_placement_transform_resolver.resolve_basis("), 2, "two direct loader basis adapters", failures ) _expect_equal( source.count("_m2_placement_transform_resolver.resolve_origin_offset("), 2, "two direct loader offset adapters", failures ) _expect_equal( grouper_source.count("placement_transform_resolver.resolve_basis("), 1, "one grouped basis adapter", failures ) _expect_equal( grouper_source.count("placement_transform_resolver.resolve_origin_offset("), 1, "one grouped offset adapter", failures ) _expect_true( not source.contains("func _adt_m2_placement_basis("), "loader omits basis formula", failures ) _expect_true( not source.contains("func _waterfall_sheet_twist("), "loader omits waterfall formula", failures ) func _verify_bounded_timing(failures: Array[String]) -> float: var resolver: RefCounted = RESOLVER_SCRIPT.new() var rotation := Vector3(0.1, 0.2, 0.3) var started_microseconds := Time.get_ticks_usec() for iteration in range(10000): var path := ( "world/x/ElwynnTallWaterfall01.m2" if iteration % 4 == 0 else "world/generic/Tree.m2" ) resolver.call("resolve_basis", rotation, path) resolver.call("resolve_origin_offset", rotation, path, 1.0) var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0 _expect_true(elapsed_milliseconds < 1000.0, "10000 transform pairs bounded", failures) return elapsed_milliseconds func _read_text(path: String, failures: Array[String]) -> String: var file := FileAccess.open(path, FileAccess.READ) if file == null: failures.append("cannot open %s" % path) return "" return file.get_as_text() func _expect_basis_near( actual_basis: Basis, expected_basis: Basis, label: String, failures: Array[String] ) -> void: if not actual_basis.is_equal_approx(expected_basis): failures.append("%s basis mismatch" % label) func _expect_vector_near( actual_value: Vector3, expected_value: Vector3, label: String, failures: Array[String] ) -> void: if not actual_value.is_equal_approx(expected_value): failures.append("%s expected %s, got %s" % [label, expected_value, actual_value]) func _expect_equal( actual_value: int, expected_value: int, label: String, failures: Array[String] ) -> void: if actual_value != expected_value: failures.append("%s expected %d, got %d" % [label, expected_value, actual_value]) func _expect_true(actual_value: bool, label: String, failures: Array[String]) -> void: if not actual_value: failures.append("%s expected true" % label)