refactor(M03): extract M2 runtime mesh finalizer

Work-Package: M03-RND-M2-RUNTIME-MESH-FINALIZER-001
Agent: sindo-main-codex
Tests: 37 headless renderer/coordinate contracts pass; checkpoint dry-run 7/7; documentation and coordination gates pass
Fidelity: preserves refresh version 2, classifier, rebuild and fallback transitions; no visual parity claim
This commit is contained in:
2026-07-17 15:01:52 +04:00
parent 951bd54fff
commit ece7724a28
15 changed files with 626 additions and 85 deletions
@@ -0,0 +1,78 @@
class_name M2RuntimeMeshFinalizer
extends RefCounted
## Finalizes stale cached M2 Meshes using caller-supplied raw data. The caller
## owns raw-file I/O, cache adoption, missing state and materialization.
const M2_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/m2_builder.gd")
const M2_RUNTIME_MESH_REBUILD_CLASSIFIER_SCRIPT := preload(
"res://src/render/m2/m2_runtime_mesh_rebuild_classifier.gd"
)
const M2_MESH_RESOURCE_EXTRACTOR_SCRIPT := preload(
"res://src/render/m2/m2_mesh_resource_extractor.gd"
)
const MATERIAL_REFRESH_VERSION := 2
const MATERIAL_REFRESH_VERSION_META := "wow_m2_material_refresh_version"
var _runtime_mesh_rebuild_classifier := M2_RUNTIME_MESH_REBUILD_CLASSIFIER_SCRIPT.new()
var _mesh_resource_extractor := M2_MESH_RESOURCE_EXTRACTOR_SCRIPT.new()
## Returns whether one Mesh is stale and the caller should load raw M2 data.
func requires_raw_data_for_refresh(mesh: Mesh) -> bool:
return (
mesh != null
and int(mesh.get_meta(MATERIAL_REFRESH_VERSION_META, 0)) < MATERIAL_REFRESH_VERSION
)
## Marks, rebuilds or falls back one stale Mesh using already-loaded raw M2
## data. Current Meshes return unchanged and never inspect raw data.
func finalize_mesh(
normalized_relative_path: String,
mesh: Mesh,
raw_m2_data: Dictionary,
extracted_directory: String) -> Mesh:
if mesh == null:
return null
if not requires_raw_data_for_refresh(mesh):
return mesh
if raw_m2_data.is_empty() or not _runtime_mesh_rebuild_classifier.needs_runtime_mesh_rebuild(
normalized_relative_path,
raw_m2_data):
_mark_mesh_current(mesh)
return mesh
var rebuilt_mesh := _rebuild_mesh(raw_m2_data, extracted_directory)
if rebuilt_mesh != null:
return rebuilt_mesh
_mark_mesh_current(mesh)
return mesh
## Clears memoized per-path rebuild decisions at the existing map/shutdown sites.
func clear() -> void:
_runtime_mesh_rebuild_classifier.clear()
## Returns the memoized rebuild-decision count for diagnostics and verification.
func cached_rebuild_decision_count() -> int:
return _runtime_mesh_rebuild_classifier.cached_path_count()
func _mark_mesh_current(mesh: Mesh) -> void:
mesh.set_meta(MATERIAL_REFRESH_VERSION_META, MATERIAL_REFRESH_VERSION)
func _rebuild_mesh(raw_m2_data: Dictionary, extracted_directory: String) -> Mesh:
var temporary_prototype: Node3D = M2_BUILDER_SCRIPT.build(
raw_m2_data,
extracted_directory
)
if temporary_prototype == null:
return null
var rebuilt_mesh := _mesh_resource_extractor.find_first_mesh_in_subtree(
temporary_prototype
)
temporary_prototype.free()
return rebuilt_mesh
@@ -0,0 +1 @@
uid://bn6vvtmqo0don