render: extract WMO runtime Mesh finalizer
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
class_name WmoRuntimeMeshFinalizer
|
||||
extends RefCounted
|
||||
|
||||
## Refreshes cached WMO runtime Mesh materials through the configured builder.
|
||||
## Scene traversal, placement and Node lifetime remain caller-owned.
|
||||
|
||||
const MATERIAL_REFRESH_VERSION := 10
|
||||
const MATERIAL_REFRESH_VERSION_METADATA := "wow_wmo_material_refresh_version"
|
||||
|
||||
var _wmo_material_builder: Object
|
||||
|
||||
|
||||
func _init(wmo_material_builder: Object) -> void:
|
||||
_wmo_material_builder = wmo_material_builder
|
||||
|
||||
|
||||
## Finalizes a stale Mesh in place and returns the exact input Resource identity.
|
||||
## Null remains null; already-current meshes do not cross the builder boundary.
|
||||
func finalize_mesh(mesh: Mesh, extracted_directory: String) -> Mesh:
|
||||
if mesh == null:
|
||||
return null
|
||||
if int(mesh.get_meta(MATERIAL_REFRESH_VERSION_METADATA, 0)) >= MATERIAL_REFRESH_VERSION:
|
||||
return mesh
|
||||
mesh.set_meta(MATERIAL_REFRESH_VERSION_METADATA, MATERIAL_REFRESH_VERSION)
|
||||
if not (mesh is ArrayMesh):
|
||||
return mesh
|
||||
|
||||
var array_mesh := mesh as ArrayMesh
|
||||
for surface_index in range(array_mesh.get_surface_count()):
|
||||
var rebuilt_material := rebuild_cached_material(
|
||||
array_mesh.surface_get_material(surface_index),
|
||||
extracted_directory
|
||||
)
|
||||
if rebuilt_material != null:
|
||||
array_mesh.surface_set_material(surface_index, rebuilt_material)
|
||||
return mesh
|
||||
|
||||
|
||||
## Rebuilds a material carrying the WMO cache metadata contract. Materials
|
||||
## without texture0 metadata and failed builder results remain unchanged by the caller.
|
||||
func rebuild_cached_material(
|
||||
material: Material,
|
||||
extracted_directory: String
|
||||
) -> Material:
|
||||
if (
|
||||
material == null
|
||||
or not material.has_meta("texture0_path")
|
||||
or _wmo_material_builder == null
|
||||
):
|
||||
return null
|
||||
|
||||
var texture_paths := PackedStringArray()
|
||||
var texture0_index := _append_texture_path(
|
||||
texture_paths,
|
||||
String(material.get_meta("texture0_path", ""))
|
||||
)
|
||||
var texture1_index := _append_texture_path(
|
||||
texture_paths,
|
||||
String(material.get_meta("texture1_path", ""))
|
||||
)
|
||||
var texture2_index := _append_texture_path(
|
||||
texture_paths,
|
||||
String(material.get_meta("texture2_path", ""))
|
||||
)
|
||||
|
||||
var diffuse_color := Color.WHITE
|
||||
var emissive_color := Color.BLACK
|
||||
var secondary_color := Color.WHITE
|
||||
if material is ShaderMaterial:
|
||||
var shader_material := material as ShaderMaterial
|
||||
var diffuse_value: Variant = shader_material.get_shader_parameter("diffuse_color")
|
||||
var emissive_value: Variant = shader_material.get_shader_parameter("emissive_color")
|
||||
var secondary_value: Variant = shader_material.get_shader_parameter("secondary_color")
|
||||
if diffuse_value is Color:
|
||||
diffuse_color = diffuse_value
|
||||
if emissive_value is Color:
|
||||
emissive_color = emissive_value
|
||||
if secondary_value is Color:
|
||||
secondary_color = secondary_value
|
||||
|
||||
var material_definition := {
|
||||
"texture0": texture0_index,
|
||||
"texture1": texture1_index,
|
||||
"texture2": texture2_index,
|
||||
"flags": int(material.get_meta("wow_flags", 0)),
|
||||
"shader": int(material.get_meta("wow_shader", 0)),
|
||||
"blend_mode": int(material.get_meta("wow_blend_mode", 0)),
|
||||
"diffuse_color": diffuse_color,
|
||||
"emissive_color": emissive_color,
|
||||
"color2": secondary_color,
|
||||
}
|
||||
return _wmo_material_builder.call(
|
||||
"_build_material",
|
||||
material_definition,
|
||||
texture_paths,
|
||||
extracted_directory
|
||||
) as Material
|
||||
|
||||
|
||||
func _append_texture_path(texture_paths: PackedStringArray, texture_path: String) -> int:
|
||||
if texture_path.is_empty():
|
||||
return -1
|
||||
var texture_index := texture_paths.size()
|
||||
texture_paths.append(texture_path)
|
||||
return texture_index
|
||||
@@ -0,0 +1 @@
|
||||
uid://bsxqkjuk77cgf
|
||||
@@ -33,6 +33,9 @@ const WMO_SCENE_RESOURCE_CACHE_STATE_SCRIPT := preload(
|
||||
const WMO_SCENE_RESOURCE_FINALIZER_SCRIPT := preload(
|
||||
"res://src/render/wmo/wmo_scene_resource_finalizer.gd"
|
||||
)
|
||||
const WMO_RUNTIME_MESH_FINALIZER_SCRIPT := preload(
|
||||
"res://src/render/wmo/wmo_runtime_mesh_finalizer.gd"
|
||||
)
|
||||
const M2_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/m2_builder.gd")
|
||||
const M2_NATIVE_ANIMATOR_SCRIPT := preload("res://src/scenes/streaming/m2_native_animator.gd")
|
||||
const STREAMING_FOCUS_SCRIPT := preload("res://src/domain/streaming/streaming_focus.gd")
|
||||
@@ -123,7 +126,6 @@ const RENDER_BUDGET_SCHEDULER_SCRIPT := preload("res://src/render/streaming/rend
|
||||
const REQUIRED_BAKED_TILE_FORMAT_VERSION := 5
|
||||
const REQUIRED_SPLAT_TILE_FORMAT_VERSION := 1
|
||||
const REQUIRED_CONTROL_SPLAT_TILE_FORMAT_VERSION := 3
|
||||
const WMO_MATERIAL_REFRESH_VERSION := 10
|
||||
const RENDER_GROUND_QUERY_RAY_HEIGHT_UNITS := 5000.0
|
||||
|
||||
const TILE_SIZE := COORDINATE_MAPPER_SCRIPT.ADT_TILE_SIZE_YARDS
|
||||
@@ -376,6 +378,9 @@ var _wmo_scene_resource_cache_state := WMO_SCENE_RESOURCE_CACHE_STATE_SCRIPT.new
|
||||
var _wmo_scene_resource_finalizer := WMO_SCENE_RESOURCE_FINALIZER_SCRIPT.new(
|
||||
WMO_BUILDER_SCRIPT
|
||||
)
|
||||
var _wmo_runtime_mesh_finalizer := WMO_RUNTIME_MESH_FINALIZER_SCRIPT.new(
|
||||
WMO_BUILDER_SCRIPT
|
||||
)
|
||||
var _wmo_missing_cache: Dictionary = {}
|
||||
var _wmo_placement_resolver := WMO_PLACEMENT_RESOLVER_SCRIPT.new()
|
||||
var _world_wmo_root: Node3D
|
||||
@@ -3840,7 +3845,7 @@ func _process_wmo_render_build_jobs() -> void:
|
||||
if build_step["operation"] == WMO_RENDER_BUILD_STEP_PLANNER_SCRIPT.OPERATION_MESH:
|
||||
var mesh := meshes[selected_index] as Mesh
|
||||
if mesh != null:
|
||||
_refresh_cached_wmo_mesh_materials(mesh)
|
||||
_wmo_runtime_mesh_finalizer.finalize_mesh(mesh, extracted_dir)
|
||||
var mesh_instance := MeshInstance3D.new()
|
||||
mesh_instance.name = (
|
||||
mesh_names[selected_index]
|
||||
@@ -3871,7 +3876,7 @@ func _process_wmo_render_build_jobs() -> void:
|
||||
if build_step["operation"] == WMO_RENDER_BUILD_STEP_PLANNER_SCRIPT.OPERATION_MULTIMESH:
|
||||
var multimesh := multimeshes[selected_index] as MultiMesh
|
||||
if multimesh != null:
|
||||
_refresh_cached_wmo_mesh_materials(multimesh.mesh)
|
||||
_wmo_runtime_mesh_finalizer.finalize_mesh(multimesh.mesh, extracted_dir)
|
||||
var multimesh_instance := MultiMeshInstance3D.new()
|
||||
multimesh_instance.name = (
|
||||
multimesh_names[selected_index]
|
||||
@@ -3950,81 +3955,18 @@ func _prepare_runtime_wmo_instance(instance: Node3D) -> void:
|
||||
|
||||
func _refresh_cached_wmo_materials_recursive(node: Node) -> void:
|
||||
if node is MeshInstance3D:
|
||||
_refresh_cached_wmo_mesh_materials((node as MeshInstance3D).mesh)
|
||||
_wmo_runtime_mesh_finalizer.finalize_mesh(
|
||||
(node as MeshInstance3D).mesh,
|
||||
extracted_dir
|
||||
)
|
||||
elif node is MultiMeshInstance3D:
|
||||
var multimesh := (node as MultiMeshInstance3D).multimesh
|
||||
if multimesh != null:
|
||||
_refresh_cached_wmo_mesh_materials(multimesh.mesh)
|
||||
_wmo_runtime_mesh_finalizer.finalize_mesh(multimesh.mesh, extracted_dir)
|
||||
for child in node.get_children():
|
||||
_refresh_cached_wmo_materials_recursive(child)
|
||||
|
||||
|
||||
func _refresh_cached_wmo_mesh_materials(mesh: Mesh) -> void:
|
||||
if mesh == null:
|
||||
return
|
||||
if int(mesh.get_meta("wow_wmo_material_refresh_version", 0)) >= WMO_MATERIAL_REFRESH_VERSION:
|
||||
return
|
||||
mesh.set_meta("wow_wmo_material_refresh_version", WMO_MATERIAL_REFRESH_VERSION)
|
||||
if not (mesh is ArrayMesh):
|
||||
return
|
||||
|
||||
var array_mesh := mesh as ArrayMesh
|
||||
for surface_idx in array_mesh.get_surface_count():
|
||||
var rebuilt := _rebuild_cached_wmo_material(array_mesh.surface_get_material(surface_idx))
|
||||
if rebuilt != null:
|
||||
array_mesh.surface_set_material(surface_idx, rebuilt)
|
||||
|
||||
|
||||
func _rebuild_cached_wmo_material(material: Material) -> Material:
|
||||
if material == null or not material.has_meta("texture0_path"):
|
||||
return null
|
||||
|
||||
var tex0_path := String(material.get_meta("texture0_path", ""))
|
||||
var tex1_path := String(material.get_meta("texture1_path", ""))
|
||||
var tex2_path := String(material.get_meta("texture2_path", ""))
|
||||
var textures := PackedStringArray()
|
||||
var tex0_index := -1
|
||||
var tex1_index := -1
|
||||
var tex2_index := -1
|
||||
if not tex0_path.is_empty():
|
||||
tex0_index = textures.size()
|
||||
textures.append(tex0_path)
|
||||
if not tex1_path.is_empty():
|
||||
tex1_index = textures.size()
|
||||
textures.append(tex1_path)
|
||||
if not tex2_path.is_empty():
|
||||
tex2_index = textures.size()
|
||||
textures.append(tex2_path)
|
||||
|
||||
var diffuse := Color.WHITE
|
||||
var emissive := Color.BLACK
|
||||
var secondary := Color.WHITE
|
||||
if material is ShaderMaterial:
|
||||
var shader_material := material as ShaderMaterial
|
||||
var diffuse_value: Variant = shader_material.get_shader_parameter("diffuse_color")
|
||||
var emissive_value: Variant = shader_material.get_shader_parameter("emissive_color")
|
||||
var secondary_value: Variant = shader_material.get_shader_parameter("secondary_color")
|
||||
if diffuse_value is Color:
|
||||
diffuse = diffuse_value
|
||||
if emissive_value is Color:
|
||||
emissive = emissive_value
|
||||
if secondary_value is Color:
|
||||
secondary = secondary_value
|
||||
|
||||
var mat_def := {
|
||||
"texture0": tex0_index,
|
||||
"texture1": tex1_index,
|
||||
"texture2": tex2_index,
|
||||
"flags": int(material.get_meta("wow_flags", 0)),
|
||||
"shader": int(material.get_meta("wow_shader", 0)),
|
||||
"blend_mode": int(material.get_meta("wow_blend_mode", 0)),
|
||||
"diffuse_color": diffuse,
|
||||
"emissive_color": emissive,
|
||||
"color2": secondary,
|
||||
}
|
||||
return WMO_BUILDER_SCRIPT._build_material(mat_def, textures, extracted_dir)
|
||||
|
||||
|
||||
func _cancel_wmo_build_job(tile_key: String) -> void:
|
||||
if _wmo_build_jobs.has(tile_key):
|
||||
_wmo_build_jobs.erase(tile_key)
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
extends SceneTree
|
||||
|
||||
## Asset-free identity, refresh-version, material-definition, ownership and
|
||||
## timing regression for cached WMO runtime Mesh finalization.
|
||||
|
||||
const FINALIZER_SCRIPT := preload(
|
||||
"res://src/render/wmo/wmo_runtime_mesh_finalizer.gd"
|
||||
)
|
||||
const FINALIZER_PATH := "res://src/render/wmo/wmo_runtime_mesh_finalizer.gd"
|
||||
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
|
||||
|
||||
|
||||
class FakeWmoMaterialBuilder extends RefCounted:
|
||||
var definitions: Array[Dictionary] = []
|
||||
var texture_path_sets: Array[PackedStringArray] = []
|
||||
var extracted_directories: Array[String] = []
|
||||
var rebuilt_material: Material = StandardMaterial3D.new()
|
||||
|
||||
func _build_material(
|
||||
material_definition: Dictionary,
|
||||
texture_paths: PackedStringArray,
|
||||
extracted_directory: String
|
||||
) -> Material:
|
||||
definitions.append(material_definition.duplicate(true))
|
||||
texture_path_sets.append(texture_paths.duplicate())
|
||||
extracted_directories.append(extracted_directory)
|
||||
return rebuilt_material
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
var failures: Array[String] = []
|
||||
_verify_null_current_and_non_array_contract(failures)
|
||||
_verify_unmarked_surface_contract(failures)
|
||||
_verify_standard_material_definition(failures)
|
||||
_verify_shader_material_colors(failures)
|
||||
_verify_missing_builder_contract(failures)
|
||||
_verify_source_boundaries(failures)
|
||||
var elapsed_milliseconds := _verify_bounded_timing(failures)
|
||||
if not failures.is_empty():
|
||||
for failure in failures:
|
||||
push_error("WMO_RUNTIME_MESH_FINALIZER: %s" % failure)
|
||||
quit(1)
|
||||
return
|
||||
print(
|
||||
"WMO_RUNTIME_MESH_FINALIZER PASS cases=27 iterations=1000 elapsed_ms=%.3f"
|
||||
% elapsed_milliseconds
|
||||
)
|
||||
quit(0)
|
||||
|
||||
|
||||
func _verify_null_current_and_non_array_contract(failures: Array[String]) -> void:
|
||||
var builder := FakeWmoMaterialBuilder.new()
|
||||
var finalizer: RefCounted = FINALIZER_SCRIPT.new(builder)
|
||||
_expect_true(
|
||||
finalizer.call("finalize_mesh", null, "res://data/extracted") == null,
|
||||
"null Mesh remains null",
|
||||
failures
|
||||
)
|
||||
var current_mesh := ArrayMesh.new()
|
||||
current_mesh.set_meta("wow_wmo_material_refresh_version", 10)
|
||||
_expect_true(
|
||||
finalizer.call("finalize_mesh", current_mesh, "res://data/extracted") == current_mesh,
|
||||
"current Mesh exact identity retained",
|
||||
failures
|
||||
)
|
||||
_expect_equal(builder.definitions.size(), 0, "current Mesh skips builder", failures)
|
||||
var primitive_mesh := BoxMesh.new()
|
||||
_expect_true(
|
||||
finalizer.call("finalize_mesh", primitive_mesh, "res://data/extracted") == primitive_mesh,
|
||||
"non-ArrayMesh exact identity retained",
|
||||
failures
|
||||
)
|
||||
_expect_equal(
|
||||
int(primitive_mesh.get_meta("wow_wmo_material_refresh_version", 0)),
|
||||
10,
|
||||
"non-ArrayMesh stamped current",
|
||||
failures
|
||||
)
|
||||
|
||||
|
||||
func _verify_unmarked_surface_contract(failures: Array[String]) -> void:
|
||||
var builder := FakeWmoMaterialBuilder.new()
|
||||
var finalizer: RefCounted = FINALIZER_SCRIPT.new(builder)
|
||||
var mesh := _mesh_with_surfaces(2)
|
||||
var ordinary_material := StandardMaterial3D.new()
|
||||
mesh.surface_set_material(1, ordinary_material)
|
||||
var finalized_mesh: Mesh = finalizer.call(
|
||||
"finalize_mesh",
|
||||
mesh,
|
||||
"res://data/extracted"
|
||||
) as Mesh
|
||||
_expect_true(finalized_mesh == mesh, "stale ArrayMesh exact identity retained", failures)
|
||||
_expect_true(mesh.surface_get_material(0) == null, "null surface remains null", failures)
|
||||
_expect_true(
|
||||
mesh.surface_get_material(1) == ordinary_material,
|
||||
"unmarked material exact identity retained",
|
||||
failures
|
||||
)
|
||||
_expect_equal(builder.definitions.size(), 0, "unmarked surfaces skip builder", failures)
|
||||
_expect_equal(
|
||||
int(mesh.get_meta("wow_wmo_material_refresh_version", 0)),
|
||||
10,
|
||||
"stale ArrayMesh stamped current",
|
||||
failures
|
||||
)
|
||||
|
||||
|
||||
func _verify_standard_material_definition(failures: Array[String]) -> void:
|
||||
var builder := FakeWmoMaterialBuilder.new()
|
||||
var finalizer: RefCounted = FINALIZER_SCRIPT.new(builder)
|
||||
var cached_material := StandardMaterial3D.new()
|
||||
cached_material.set_meta("texture0_path", "World\\A.blp")
|
||||
cached_material.set_meta("texture1_path", "")
|
||||
cached_material.set_meta("texture2_path", "World\\C.blp")
|
||||
cached_material.set_meta("wow_flags", 17)
|
||||
cached_material.set_meta("wow_shader", 4)
|
||||
cached_material.set_meta("wow_blend_mode", 6)
|
||||
var mesh := _mesh_with_surfaces(1)
|
||||
mesh.surface_set_material(0, cached_material)
|
||||
finalizer.call("finalize_mesh", mesh, "res://fixture/extracted")
|
||||
_expect_equal(builder.definitions.size(), 1, "eligible surface rebuilt once", failures)
|
||||
_expect_true(
|
||||
mesh.surface_get_material(0) == builder.rebuilt_material,
|
||||
"exact builder Material adopted",
|
||||
failures
|
||||
)
|
||||
_expect_true(
|
||||
builder.texture_path_sets[0] == PackedStringArray(["World\\A.blp", "World\\C.blp"]),
|
||||
"texture paths compacted in slot order",
|
||||
failures
|
||||
)
|
||||
_expect_true(
|
||||
builder.extracted_directories == ["res://fixture/extracted"],
|
||||
"extracted directory forwarded exactly",
|
||||
failures
|
||||
)
|
||||
var definition := builder.definitions[0]
|
||||
_expect_equal(int(definition["texture0"]), 0, "texture0 compact index", failures)
|
||||
_expect_equal(int(definition["texture1"]), -1, "empty texture1 index", failures)
|
||||
_expect_equal(int(definition["texture2"]), 1, "texture2 compact index", failures)
|
||||
_expect_equal(int(definition["flags"]), 17, "WMO flags forwarded", failures)
|
||||
_expect_equal(int(definition["shader"]), 4, "WMO shader forwarded", failures)
|
||||
_expect_equal(int(definition["blend_mode"]), 6, "WMO blend mode forwarded", failures)
|
||||
_expect_true(definition["diffuse_color"] == Color.WHITE, "default diffuse color", failures)
|
||||
_expect_true(definition["emissive_color"] == Color.BLACK, "default emissive color", failures)
|
||||
_expect_true(definition["color2"] == Color.WHITE, "default secondary color", failures)
|
||||
|
||||
|
||||
func _verify_shader_material_colors(failures: Array[String]) -> void:
|
||||
var builder := FakeWmoMaterialBuilder.new()
|
||||
var finalizer: RefCounted = FINALIZER_SCRIPT.new(builder)
|
||||
var shader := Shader.new()
|
||||
shader.code = (
|
||||
"shader_type spatial; uniform vec4 diffuse_color; "
|
||||
+ "uniform vec4 emissive_color; uniform vec4 secondary_color;"
|
||||
)
|
||||
var cached_material := ShaderMaterial.new()
|
||||
cached_material.shader = shader
|
||||
cached_material.set_shader_parameter("diffuse_color", Color(0.1, 0.2, 0.3, 0.4))
|
||||
cached_material.set_shader_parameter("emissive_color", Color(0.5, 0.6, 0.7, 0.8))
|
||||
cached_material.set_shader_parameter("secondary_color", Color(0.9, 0.8, 0.7, 0.6))
|
||||
cached_material.set_meta("texture0_path", "World\\Only.blp")
|
||||
var mesh := _mesh_with_surfaces(1)
|
||||
mesh.surface_set_material(0, cached_material)
|
||||
finalizer.call("finalize_mesh", mesh, "res://data/extracted")
|
||||
var definition := builder.definitions[0]
|
||||
_expect_true(
|
||||
definition["diffuse_color"] == Color(0.1, 0.2, 0.3, 0.4),
|
||||
"shader diffuse color forwarded",
|
||||
failures
|
||||
)
|
||||
_expect_true(
|
||||
definition["emissive_color"] == Color(0.5, 0.6, 0.7, 0.8),
|
||||
"shader emissive color forwarded",
|
||||
failures
|
||||
)
|
||||
_expect_true(
|
||||
definition["color2"] == Color(0.9, 0.8, 0.7, 0.6),
|
||||
"shader secondary color forwarded",
|
||||
failures
|
||||
)
|
||||
|
||||
|
||||
func _verify_missing_builder_contract(failures: Array[String]) -> void:
|
||||
var finalizer: RefCounted = FINALIZER_SCRIPT.new(null)
|
||||
var cached_material := StandardMaterial3D.new()
|
||||
cached_material.set_meta("texture0_path", "World\\A.blp")
|
||||
var mesh := _mesh_with_surfaces(1)
|
||||
mesh.surface_set_material(0, cached_material)
|
||||
finalizer.call("finalize_mesh", mesh, "res://data/extracted")
|
||||
_expect_true(
|
||||
mesh.surface_get_material(0) == cached_material,
|
||||
"missing builder retains cached Material identity",
|
||||
failures
|
||||
)
|
||||
|
||||
|
||||
func _verify_source_boundaries(failures: Array[String]) -> void:
|
||||
var loader_source := _read_text(LOADER_PATH, failures)
|
||||
var finalizer_source := _read_text(FINALIZER_PATH, failures)
|
||||
_expect_true(
|
||||
loader_source.contains("WMO_RUNTIME_MESH_FINALIZER_SCRIPT.new("),
|
||||
"loader composes runtime Mesh finalizer",
|
||||
failures
|
||||
)
|
||||
_expect_true(
|
||||
loader_source.contains("_wmo_runtime_mesh_finalizer.finalize_mesh("),
|
||||
"loader delegates runtime Mesh finalization",
|
||||
failures
|
||||
)
|
||||
for released_token in [
|
||||
"func _refresh_cached_wmo_mesh_materials(",
|
||||
"func _rebuild_cached_wmo_material(",
|
||||
"const WMO_MATERIAL_REFRESH_VERSION",
|
||||
]:
|
||||
_expect_false(
|
||||
loader_source.contains(released_token),
|
||||
"loader releases %s" % released_token,
|
||||
failures
|
||||
)
|
||||
for owned_token in [
|
||||
"const MATERIAL_REFRESH_VERSION := 10",
|
||||
"wow_wmo_material_refresh_version",
|
||||
"array_mesh.surface_get_material(surface_index)",
|
||||
"_wmo_material_builder.call(",
|
||||
]:
|
||||
_expect_true(
|
||||
finalizer_source.contains(owned_token),
|
||||
"finalizer owns %s" % owned_token,
|
||||
failures
|
||||
)
|
||||
_expect_true(
|
||||
loader_source.contains("func _refresh_cached_wmo_materials_recursive(node: Node)"),
|
||||
"loader retains scene traversal",
|
||||
failures
|
||||
)
|
||||
|
||||
|
||||
func _verify_bounded_timing(failures: Array[String]) -> float:
|
||||
var builder := FakeWmoMaterialBuilder.new()
|
||||
var finalizer: RefCounted = FINALIZER_SCRIPT.new(builder)
|
||||
var mesh := ArrayMesh.new()
|
||||
mesh.set_meta("wow_wmo_material_refresh_version", 10)
|
||||
var started_microseconds := Time.get_ticks_usec()
|
||||
for iteration in range(1000):
|
||||
finalizer.call("finalize_mesh", mesh, "res://data/extracted")
|
||||
var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0
|
||||
if elapsed_milliseconds >= 1000.0:
|
||||
failures.append(
|
||||
"1000 current Mesh finalizations took %.3fms (budget < 1000ms)"
|
||||
% elapsed_milliseconds
|
||||
)
|
||||
return elapsed_milliseconds
|
||||
|
||||
|
||||
func _mesh_with_surfaces(surface_count: int) -> ArrayMesh:
|
||||
var mesh := ArrayMesh.new()
|
||||
var arrays: Array = []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array([
|
||||
Vector3.ZERO,
|
||||
Vector3.RIGHT,
|
||||
Vector3.UP,
|
||||
])
|
||||
for surface_index in range(surface_count):
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
||||
return mesh
|
||||
|
||||
|
||||
func _read_text(path: String, failures: Array[String]) -> String:
|
||||
var file := FileAccess.open(path, FileAccess.READ)
|
||||
if file == null:
|
||||
failures.append("cannot read %s" % path)
|
||||
return ""
|
||||
var source := file.get_as_text()
|
||||
file.close()
|
||||
return source
|
||||
|
||||
|
||||
func _expect_true(value: bool, label: String, failures: Array[String]) -> void:
|
||||
if not value:
|
||||
failures.append(label)
|
||||
|
||||
|
||||
func _expect_false(value: bool, label: String, failures: Array[String]) -> void:
|
||||
if value:
|
||||
failures.append(label)
|
||||
|
||||
|
||||
func _expect_equal(actual: int, expected: int, label: String, failures: Array[String]) -> void:
|
||||
if actual != expected:
|
||||
failures.append("%s: expected %d, got %d" % [label, expected, actual])
|
||||
Reference in New Issue
Block a user