Implement playable Mac client and rendering validation
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
extends SceneTree
|
||||
var failures: Array[String] = []
|
||||
var report := {"species": [], "maps": [], "scope": "native static tree geometry/materials; not SpeedTree wind/LOD parity"}
|
||||
func check(ok: bool, label: String) -> void:
|
||||
if not ok:
|
||||
failures.append(label)
|
||||
printerr("FAIL: " + label)
|
||||
func _init() -> void: call_deferred("run")
|
||||
func run() -> void:
|
||||
var assets := AssetRoot.path()
|
||||
var directory := assets.path_join("TreeGeometry")
|
||||
var manifest: Dictionary = JSON.parse_string(FileAccess.get_file_as_string(directory.path_join("manifest.json")))
|
||||
check(manifest.trees.size() == 87, "all 87 extracted species staged")
|
||||
var sample_mesh: ArrayMesh
|
||||
for hash in manifest.trees:
|
||||
var record: Dictionary = manifest.trees[hash]
|
||||
var path := directory.path_join(record.glb)
|
||||
check(FileAccess.get_sha256(assets.path_join(record.source)) == hash, "SPT provenance " + record.source)
|
||||
check(FileAccess.get_sha256(path) == record.glb_sha256, "GLB provenance " + record.source)
|
||||
var state := GLTFState.new()
|
||||
var doc := GLTFDocument.new()
|
||||
check(doc.append_from_file(path, state) == OK, "GLB loads " + record.source)
|
||||
if state.get_meshes().size() != 1:
|
||||
check(false, "one native combined mesh")
|
||||
continue
|
||||
var mesh: ArrayMesh = state.get_meshes()[0].get_mesh().get_mesh()
|
||||
check(mesh.get_surface_count() == int(record.primitives), "all branch/frond/leaf surfaces retained")
|
||||
var masked := 0
|
||||
var vertices := 0
|
||||
for surface in mesh.get_surface_count():
|
||||
var arrays := mesh.surface_get_arrays(surface)
|
||||
vertices += arrays[Mesh.ARRAY_VERTEX].size()
|
||||
var material: StandardMaterial3D = mesh.surface_get_material(surface)
|
||||
check(material != null and material.albedo_texture != null, "real texture retained")
|
||||
if material and material.transparency == BaseMaterial3D.TRANSPARENCY_ALPHA_SCISSOR:
|
||||
masked += 1
|
||||
check(material.cull_mode == BaseMaterial3D.CULL_DISABLED, "leaf cards double-sided")
|
||||
check(is_equal_approx(material.alpha_scissor_threshold, 0.3), "native alpha cutoff retained")
|
||||
check(vertices > 0 and mesh.get_aabb().size.is_finite(), "finite native geometry")
|
||||
report.species.append({"source": record.source, "vertices": vertices, "surfaces": mesh.get_surface_count(), "masked": masked, "height_m": mesh.get_aabb().size.y})
|
||||
if String(record.source).ends_with("b1_beech_rt2.spt"): sample_mesh = mesh
|
||||
for map in ["OutdoorA1/metin2_map_a1", "OutdoorA3/metin2_map_a3", "OutdoorB1/metin2_map_b1",
|
||||
"OutdoorB3/metin2_map_b3", "OutdoorC1/metin2_map_c1", "OutdoorC3/metin2_map_c3"]:
|
||||
var world := Metin2World.new()
|
||||
world.auto_load = false
|
||||
world.env_enabled = false
|
||||
world.assets_root = assets
|
||||
world.map_path = map
|
||||
root.add_child(world)
|
||||
check(world.load_map(), "map loads " + map)
|
||||
var stats: Dictionary = world.get_load_report()
|
||||
check(stats.native_trees_placed > 0 and stats.proxy_trees_placed == 0, "all placed trees native " + map)
|
||||
check(stats.native_trees_placed == stats.trees_placed, "tree placement count preserved")
|
||||
for node in world.find_children("*", "MultiMeshInstance3D", true, false):
|
||||
if node.has_meta("tree_geometry"):
|
||||
check(node.get_meta("tree_geometry") == "native_spt", "renderer uses native path")
|
||||
check(node.multimesh.mesh.get_meta("source_sha256", "") != "", "mesh provenance retained")
|
||||
report.maps.append({"map": map, "native": stats.native_trees_placed, "proxy": stats.proxy_trees_placed})
|
||||
world.free()
|
||||
await process_frame
|
||||
var output := OS.get_environment("MT_TREE_OUTPUT")
|
||||
if output == "": output = ProjectSettings.globalize_path("res://../build/rendering/native-trees")
|
||||
DirAccess.make_dir_recursive_absolute(output)
|
||||
if sample_mesh and DisplayServer.get_name() != "headless": await render_sample(sample_mesh, output)
|
||||
report["failures"] = failures
|
||||
FileAccess.open(output.path_join("report.json"), FileAccess.WRITE).store_string(JSON.stringify(report, "\t"))
|
||||
print("native_tree_test: failures=%d report=%s" % [failures.size(), output])
|
||||
quit(1 if not failures.is_empty() else 0)
|
||||
func render_sample(mesh: ArrayMesh, output: String) -> void:
|
||||
var viewport := SubViewport.new()
|
||||
viewport.size = Vector2i(640, 640)
|
||||
viewport.own_world_3d = true
|
||||
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
|
||||
root.add_child(viewport)
|
||||
var environment := WorldEnvironment.new()
|
||||
environment.environment = Environment.new()
|
||||
environment.environment.background_mode = Environment.BG_COLOR
|
||||
environment.environment.background_color = Color(0.12, 0.16, 0.23)
|
||||
environment.environment.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
|
||||
environment.environment.ambient_light_color = Color.WHITE
|
||||
environment.environment.ambient_light_energy = 0.8
|
||||
viewport.add_child(environment)
|
||||
var instance := MeshInstance3D.new()
|
||||
instance.mesh = mesh
|
||||
viewport.add_child(instance)
|
||||
var light := DirectionalLight3D.new()
|
||||
light.rotation_degrees = Vector3(-40, -30, 0)
|
||||
viewport.add_child(light)
|
||||
var camera := Camera3D.new()
|
||||
camera.projection = Camera3D.PROJECTION_ORTHOGONAL
|
||||
camera.size = 28
|
||||
viewport.add_child(camera)
|
||||
for angle in [0.0, PI / 2]:
|
||||
camera.position = Vector3(sin(angle) * 35, 12, cos(angle) * 35)
|
||||
camera.look_at(Vector3(0, 10, 0))
|
||||
await process_frame
|
||||
await RenderingServer.frame_post_draw
|
||||
var image := viewport.get_texture().get_image()
|
||||
var background := image.get_pixel(0, 0)
|
||||
var visible := 0
|
||||
for y in 640:
|
||||
for x in 640:
|
||||
if image.get_pixel(x, y) != background: visible += 1
|
||||
check(visible > 5000 and visible < 200000, "native tree silhouette is visible without opaque atlas rectangle")
|
||||
check(image.save_png(output.path_join("beech-%d.png" % int(rad_to_deg(angle)))) == OK, "native tree screenshot")
|
||||
viewport.free()
|
||||
await process_frame
|
||||
Reference in New Issue
Block a user