183 lines
4.9 KiB
GDScript
183 lines
4.9 KiB
GDScript
class_name M2MeshResourceFinalizer
|
|
extends RefCounted
|
|
|
|
## Polls static M2 ResourceLoader requests and publishes prepared Mesh outcomes.
|
|
## The caller owns permits, pipeline/cache lifetime and MultiMesh materialization.
|
|
|
|
var _mesh_resource_extractor: RefCounted
|
|
var _runtime_mesh_finalizer: RefCounted
|
|
var _raw_model_repository: RefCounted
|
|
var _resource_loader_adapter: Object
|
|
|
|
|
|
func _init(
|
|
mesh_resource_extractor: RefCounted,
|
|
runtime_mesh_finalizer: RefCounted,
|
|
raw_model_repository: RefCounted,
|
|
resource_loader_adapter: Object = null
|
|
) -> void:
|
|
_mesh_resource_extractor = mesh_resource_extractor
|
|
_runtime_mesh_finalizer = runtime_mesh_finalizer
|
|
_raw_model_repository = raw_model_repository
|
|
_resource_loader_adapter = resource_loader_adapter
|
|
|
|
|
|
## Moves terminal threaded requests into the pipeline finalize FIFO.
|
|
## Empty resource paths retain the historical immediate missing-model outcome.
|
|
func poll_terminal_requests(
|
|
load_pipeline_state: RefCounted,
|
|
prototype_cache_state: RefCounted
|
|
) -> int:
|
|
if load_pipeline_state == null or prototype_cache_state == null:
|
|
return 0
|
|
var completed_request_count := 0
|
|
var request_records: Array = load_pipeline_state.call(
|
|
"request_records_snapshot"
|
|
)
|
|
for request_variant in request_records:
|
|
if not (request_variant is Dictionary):
|
|
continue
|
|
var request: Dictionary = request_variant
|
|
var normalized_relative_path := String(request.get("normalized", ""))
|
|
var resource_path := String(request.get("path", ""))
|
|
if resource_path.is_empty():
|
|
load_pipeline_state.call(
|
|
"discard_request",
|
|
normalized_relative_path
|
|
)
|
|
prototype_cache_state.call(
|
|
"mark_model_missing",
|
|
normalized_relative_path
|
|
)
|
|
completed_request_count += 1
|
|
continue
|
|
var load_status := load_threaded_get_status(resource_path)
|
|
if (
|
|
load_status != ResourceLoader.THREAD_LOAD_LOADED
|
|
and load_status != ResourceLoader.THREAD_LOAD_FAILED
|
|
):
|
|
continue
|
|
load_pipeline_state.call(
|
|
"complete_request",
|
|
normalized_relative_path,
|
|
load_status
|
|
)
|
|
completed_request_count += 1
|
|
return completed_request_count
|
|
|
|
|
|
## Pops and finalizes one terminal record. A true result means a record was
|
|
## consumed, including cached, failed and invalid outcomes.
|
|
func finalize_next_resource(
|
|
load_pipeline_state: RefCounted,
|
|
mesh_resource_cache_state: RefCounted,
|
|
prototype_cache_state: RefCounted,
|
|
extracted_directory: String
|
|
) -> bool:
|
|
if (
|
|
load_pipeline_state == null
|
|
or mesh_resource_cache_state == null
|
|
or prototype_cache_state == null
|
|
):
|
|
return false
|
|
var terminal_record: Dictionary = load_pipeline_state.call(
|
|
"pop_finalize_record"
|
|
)
|
|
if terminal_record.is_empty():
|
|
return false
|
|
var normalized_relative_path := String(
|
|
terminal_record.get("normalized", "")
|
|
)
|
|
if (
|
|
normalized_relative_path.is_empty()
|
|
or bool(mesh_resource_cache_state.call(
|
|
"has_mesh",
|
|
normalized_relative_path
|
|
))
|
|
):
|
|
return true
|
|
if int(terminal_record.get(
|
|
"status",
|
|
ResourceLoader.THREAD_LOAD_FAILED
|
|
)) != ResourceLoader.THREAD_LOAD_LOADED:
|
|
prototype_cache_state.call(
|
|
"mark_model_missing",
|
|
normalized_relative_path
|
|
)
|
|
return true
|
|
|
|
var resource_path := String(terminal_record.get("path", ""))
|
|
var loaded_resource := load_threaded_get(resource_path)
|
|
var mesh := _mesh_resource_extractor.call(
|
|
"extract_first_mesh",
|
|
loaded_resource
|
|
) as Mesh
|
|
if mesh == null:
|
|
prototype_cache_state.call(
|
|
"mark_model_missing",
|
|
normalized_relative_path
|
|
)
|
|
return true
|
|
mesh_resource_cache_state.call(
|
|
"store_mesh",
|
|
normalized_relative_path,
|
|
prepare_mesh_for_runtime(
|
|
normalized_relative_path,
|
|
mesh,
|
|
extracted_directory
|
|
)
|
|
)
|
|
return true
|
|
|
|
|
|
## Applies the existing refresh-version/raw-data/rebuild path to one Mesh.
|
|
func prepare_mesh_for_runtime(
|
|
normalized_relative_path: String,
|
|
mesh: Mesh,
|
|
extracted_directory: String
|
|
) -> Mesh:
|
|
if mesh == null:
|
|
return null
|
|
if (
|
|
_runtime_mesh_finalizer == null
|
|
or not bool(_runtime_mesh_finalizer.call(
|
|
"requires_raw_data_for_refresh",
|
|
mesh
|
|
))
|
|
):
|
|
return mesh
|
|
var raw_model_data: Dictionary = {}
|
|
if _raw_model_repository != null:
|
|
raw_model_data = _raw_model_repository.call(
|
|
"load_static_model_data",
|
|
extracted_directory,
|
|
normalized_relative_path
|
|
)
|
|
return _runtime_mesh_finalizer.call(
|
|
"finalize_mesh",
|
|
normalized_relative_path,
|
|
mesh,
|
|
raw_model_data,
|
|
extracted_directory
|
|
) as Mesh
|
|
|
|
|
|
## Production ResourceLoader status adapter; injectable in synthetic tests.
|
|
func load_threaded_get_status(resource_path: String) -> int:
|
|
if _resource_loader_adapter != null:
|
|
return int(_resource_loader_adapter.call(
|
|
"load_threaded_get_status",
|
|
resource_path
|
|
))
|
|
return ResourceLoader.load_threaded_get_status(resource_path)
|
|
|
|
|
|
## Production ResourceLoader terminal-result adapter; injectable in tests.
|
|
func load_threaded_get(resource_path: String) -> Resource:
|
|
if _resource_loader_adapter != null:
|
|
return _resource_loader_adapter.call(
|
|
"load_threaded_get",
|
|
resource_path
|
|
) as Resource
|
|
return ResourceLoader.load_threaded_get(resource_path)
|