big
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
extends SceneTree
|
||||
|
||||
## Geometry/material regression probe for Akiba Crossing.
|
||||
##
|
||||
## This deliberately verifies the mechanisms that prove the requested fixes:
|
||||
## imported art has one transformed triangle collider per rendered mesh (not
|
||||
## an enclosing AABB), every supported prop sits on its declared plane, every
|
||||
## utility curve terminates at a pole/building terminal, and cable interiors
|
||||
## do not intersect static geometry.
|
||||
|
||||
var _frames := 0
|
||||
var _result_ready := false
|
||||
var _exit_code := 0
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
change_scene_to_file("res://scenes/maps/neon_alley/neon_alley.tscn")
|
||||
|
||||
|
||||
func _process(_delta: float) -> bool:
|
||||
_frames += 1
|
||||
if _result_ready:
|
||||
if _frames >= 45:
|
||||
quit(_exit_code)
|
||||
return false
|
||||
if _frames < 90:
|
||||
return false
|
||||
if current_scene == null:
|
||||
printerr("AKIBA_INTEGRITY no current scene")
|
||||
quit(1)
|
||||
return false
|
||||
|
||||
var exact_bodies := 0
|
||||
var exact_meshes := 0
|
||||
var exact_shapes := 0
|
||||
var collision_mismatches := 0
|
||||
var fallbacks := 0
|
||||
var untextured_cars := 0
|
||||
var out_of_district := 0
|
||||
for candidate in root.find_children("*", "StaticBody3D", true, false):
|
||||
var body := candidate as StaticBody3D
|
||||
if body.has_meta("exact_collision_fallback"):
|
||||
fallbacks += 1
|
||||
if not body.has_meta("mesh_exact_collision"):
|
||||
continue
|
||||
exact_bodies += 1
|
||||
var meshes := body.find_children("*", "MeshInstance3D", true, false)
|
||||
var shapes := body.find_children(
|
||||
"ExactMeshCollision_*", "CollisionShape3D", false, false)
|
||||
exact_meshes += meshes.size()
|
||||
exact_shapes += shapes.size()
|
||||
if meshes.size() != shapes.size():
|
||||
collision_mismatches += 1
|
||||
_report_collision_issue(body,
|
||||
"mesh_count=%d shape_count=%d" % [meshes.size(), shapes.size()])
|
||||
for shape_candidate in shapes:
|
||||
var shape := shape_candidate as CollisionShape3D
|
||||
if not shape.shape is ConcavePolygonShape3D \
|
||||
or (shape.shape as ConcavePolygonShape3D).get_faces().is_empty():
|
||||
collision_mismatches += 1
|
||||
_report_collision_issue(body, "%s is not triangle collision" % shape.name)
|
||||
continue
|
||||
var source_path: String = str(shape.get_meta("source_mesh_path", ""))
|
||||
var source := root.get_node_or_null(NodePath(source_path)) as MeshInstance3D
|
||||
if source == null:
|
||||
collision_mismatches += 1
|
||||
_report_collision_issue(body, "%s has no source mesh" % shape.name)
|
||||
continue
|
||||
var expected := body.global_transform.affine_inverse() * source.global_transform
|
||||
if not shape.transform.is_equal_approx(expected):
|
||||
collision_mismatches += 1
|
||||
_report_collision_issue(body, "%s transform differs from %s" % [
|
||||
shape.name, source.name])
|
||||
if body.name.begins_with("Car_") and not _has_textured_surface(body):
|
||||
untextured_cars += 1
|
||||
|
||||
for child in root.find_children("*", "MeshInstance3D", true, false):
|
||||
var mesh_instance := child as MeshInstance3D
|
||||
if mesh_instance.mesh == null:
|
||||
continue
|
||||
var global_bounds := _transform_aabb(
|
||||
mesh_instance.get_aabb(), mesh_instance.global_transform)
|
||||
var center := global_bounds.get_center()
|
||||
if absf(center.x) > 336.0 or absf(center.z) > 336.0:
|
||||
out_of_district += 1
|
||||
if out_of_district <= 8:
|
||||
print("AKIBA_OUT_OF_DISTRICT %s center=%s" % [
|
||||
mesh_instance.get_path(), center])
|
||||
|
||||
var support_checked := 0
|
||||
var unsupported := 0
|
||||
var road_lamps := 0
|
||||
var road_intrusions := 0
|
||||
for candidate in root.find_children("*", "Node3D", true, false):
|
||||
var node := candidate as Node3D
|
||||
if node.has_meta("support_fitted"):
|
||||
support_checked += 1
|
||||
var plane := float(node.get_meta("support_plane_y"))
|
||||
var bounds := _global_visual_bounds(node)
|
||||
if bounds.size == Vector3.ZERO or absf(bounds.position.y - plane) > 0.025:
|
||||
unsupported += 1
|
||||
if unsupported <= 8:
|
||||
print("AKIBA_UNSUPPORTED_PROP %s bottom=%.4f plane=%.4f" % [
|
||||
node.get_path(), bounds.position.y, plane])
|
||||
var source_path: String = str(node.get_meta("source_asset_path", ""))
|
||||
if source_path.contains("light-curved"):
|
||||
road_lamps += 1
|
||||
var p := node.global_position
|
||||
var central_ns := absf(p.x) < 20.0
|
||||
var central_ew := absf(p.z) < 20.0
|
||||
if (central_ns and absf(p.x) < 7.0) \
|
||||
or (central_ew and absf(p.z) < 7.0):
|
||||
road_intrusions += 1
|
||||
print("AKIBA_ROAD_LAMP %s position=%s" % [node.get_path(), p])
|
||||
|
||||
var terminals: Array[Vector3] = []
|
||||
for marker in root.find_children("UtilityTerminal_*", "Node3D", true, false):
|
||||
terminals.append((marker as Node3D).global_position)
|
||||
var curves: Dictionary = {}
|
||||
var cable_intersections := 0
|
||||
var space: PhysicsDirectSpaceState3D = \
|
||||
current_scene.get_world_3d().direct_space_state
|
||||
for candidate in root.find_children("PowerCable_*", "MeshInstance3D", true, false):
|
||||
var cable := candidate as MeshInstance3D
|
||||
var curve_id := int(cable.get_meta("curve_id", -1))
|
||||
var segment_index := int(cable.get_meta("segment_index", -1))
|
||||
var segment_count := int(cable.get_meta("segment_count", 0))
|
||||
if not curves.has(curve_id):
|
||||
curves[curve_id] = {
|
||||
"from": cable.get_meta("cable_from"),
|
||||
"to": cable.get_meta("cable_to"),
|
||||
"first": segment_index,
|
||||
"last": segment_index,
|
||||
}
|
||||
else:
|
||||
var record: Dictionary = curves[curve_id]
|
||||
if segment_index < int(record["first"]):
|
||||
record["first"] = segment_index
|
||||
record["from"] = cable.get_meta("cable_from")
|
||||
if segment_index > int(record["last"]):
|
||||
record["last"] = segment_index
|
||||
record["to"] = cable.get_meta("cable_to")
|
||||
curves[curve_id] = record
|
||||
var from: Vector3 = cable.get_meta("cable_from")
|
||||
var to: Vector3 = cable.get_meta("cable_to")
|
||||
var query := PhysicsRayQueryParameters3D.create(
|
||||
from.lerp(to, 0.16), from.lerp(to, 0.84))
|
||||
query.collide_with_areas = false
|
||||
var hit: Dictionary = space.intersect_ray(query)
|
||||
if not hit.is_empty():
|
||||
cable_intersections += 1
|
||||
if cable_intersections <= 8:
|
||||
print("AKIBA_CABLE_INTERSECTION curve=%d segment=%d/%d collider=%s" % [
|
||||
curve_id, segment_index, segment_count,
|
||||
(hit["collider"] as Node).get_path()])
|
||||
|
||||
var unterminated_curves := 0
|
||||
for curve_id in curves:
|
||||
var record: Dictionary = curves[curve_id]
|
||||
if not _near_terminal(record["from"], terminals) \
|
||||
or not _near_terminal(record["to"], terminals):
|
||||
unterminated_curves += 1
|
||||
if unterminated_curves <= 8:
|
||||
print("AKIBA_UNTERMINATED_CURVE id=%s from=%s to=%s" % [
|
||||
curve_id, record["from"], record["to"]])
|
||||
|
||||
var power_cables := root.find_children(
|
||||
"PowerCable_*", "MeshInstance3D", true, false).size()
|
||||
var utility_poles := root.find_children(
|
||||
"UtilityPole_*", "StaticBody3D", true, false).size()
|
||||
var failures := collision_mismatches + fallbacks + untextured_cars \
|
||||
+ out_of_district + unsupported + road_intrusions \
|
||||
+ cable_intersections + unterminated_curves
|
||||
print("AKIBA_INTEGRITY exact_bodies=%d exact_meshes=%d exact_shapes=%d collision_mismatches=%d fallbacks=%d untextured_cars=%d out_of_district=%d support_checked=%d unsupported=%d road_lamps=%d road_intrusions=%d cables=%d curves=%d poles=%d terminals=%d cable_intersections=%d unterminated_curves=%d failures=%d" % [
|
||||
exact_bodies, exact_meshes, exact_shapes, collision_mismatches,
|
||||
fallbacks, untextured_cars, out_of_district, support_checked,
|
||||
unsupported, road_lamps, road_intrusions, power_cables,
|
||||
curves.size(), utility_poles, terminals.size(), cable_intersections,
|
||||
unterminated_curves, failures])
|
||||
if failures > 0:
|
||||
printerr("AKIBA_INTEGRITY FAILED")
|
||||
_exit_code = 1
|
||||
else:
|
||||
print("AKIBA_INTEGRITY PASSED")
|
||||
_exit_code = 0
|
||||
unload_current_scene()
|
||||
_result_ready = true
|
||||
_frames = 0
|
||||
return false
|
||||
|
||||
|
||||
func _report_collision_issue(body: StaticBody3D, detail: String) -> void:
|
||||
print("AKIBA_COLLISION_MISMATCH %s %s" % [body.get_path(), detail])
|
||||
|
||||
|
||||
func _near_terminal(point: Vector3, terminals: Array[Vector3]) -> bool:
|
||||
for terminal in terminals:
|
||||
if point.distance_to(terminal) <= 0.035:
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _global_visual_bounds(node: Node) -> AABB:
|
||||
var result := AABB()
|
||||
var has_bounds := false
|
||||
for child in node.find_children("*", "MeshInstance3D", true, false):
|
||||
var mesh_instance := child as MeshInstance3D
|
||||
if mesh_instance.mesh == null:
|
||||
continue
|
||||
var transformed := _transform_aabb(
|
||||
mesh_instance.get_aabb(), mesh_instance.global_transform)
|
||||
result = result.merge(transformed) if has_bounds else transformed
|
||||
has_bounds = true
|
||||
return result if has_bounds else AABB()
|
||||
|
||||
|
||||
func _transform_aabb(bounds: AABB, xform: Transform3D) -> AABB:
|
||||
var result := AABB(xform * bounds.position, Vector3.ZERO)
|
||||
for x in 2:
|
||||
for y in 2:
|
||||
for z in 2:
|
||||
var corner := bounds.position + Vector3(
|
||||
bounds.size.x * float(x),
|
||||
bounds.size.y * float(y),
|
||||
bounds.size.z * float(z))
|
||||
result = result.expand(xform * corner)
|
||||
return result
|
||||
|
||||
|
||||
func _has_textured_surface(body: Node) -> bool:
|
||||
for child in body.find_children("*", "MeshInstance3D", true, false):
|
||||
var mesh_instance := child as MeshInstance3D
|
||||
if mesh_instance.mesh == null:
|
||||
continue
|
||||
for surface in mesh_instance.mesh.get_surface_count():
|
||||
var material := mesh_instance.get_active_material(surface)
|
||||
if material is ShaderMaterial \
|
||||
and (material.get_shader_parameter("has_texture") == true \
|
||||
or material.get_shader_parameter("use_vertex_color") == true):
|
||||
return true
|
||||
if material is BaseMaterial3D and material.albedo_texture != null:
|
||||
return true
|
||||
return false
|
||||
Reference in New Issue
Block a user