diff --git a/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.DirectionalShadows.cs b/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.DirectionalShadows.cs index 44a497e2..f8cb8dd5 100644 --- a/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.DirectionalShadows.cs +++ b/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.DirectionalShadows.cs @@ -1270,6 +1270,9 @@ public sealed partial class WbDrawDispatcher // directly", correct for the non-Setup flat-MeshRef caller below. bool? entityHasCutoutSubsetOverride = null) { + if (_meshAdapter.IsRuntimeHiddenMarker(meshRef.GfxObjId)) + return; + bool entityHasCutoutSubset = entityHasCutoutSubsetOverride ?? renderData.HasCutoutSubset; for (int batchIndex = 0; batchIndex < renderData.Batches.Count; diff --git a/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.WalkClassify.cs b/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.WalkClassify.cs index 80e3674f..0b3e244a 100644 --- a/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.WalkClassify.cs +++ b/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.WalkClassify.cs @@ -223,6 +223,9 @@ public sealed partial class WbDrawDispatcher for (int partIndex = 0; partIndex < meshRefs.Count; partIndex++) { MeshRef meshRef = meshRefs[partIndex]; + if (_meshAdapter.IsRuntimeHiddenMarker(meshRef.GfxObjId)) + continue; + ObjectRenderData? renderData = _meshAdapter.TryGetRenderData(meshRef.GfxObjId); if (renderData is null) { @@ -246,6 +249,9 @@ public sealed partial class WbDrawDispatcher for (int setupPartIndex = 0; setupPartIndex < renderData.SetupParts.Count; setupPartIndex++) { (ulong gfxObjId, Matrix4x4 partTransform) = renderData.SetupParts[setupPartIndex]; + if (_meshAdapter.IsRuntimeHiddenMarker((uint)gfxObjId)) + continue; + ObjectRenderData? partData = _meshAdapter.TryGetRenderData(gfxObjId); if (partData is null) { diff --git a/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs b/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs index 32cbf82f..4002d188 100644 --- a/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs +++ b/src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs @@ -3314,6 +3314,9 @@ public sealed partial class WbDrawDispatcher : IDisposable // a non-Setup single-mesh entity (see the flat MeshRef call site). bool? entityHasCutoutSubsetOverride = null) { + if (_meshAdapter.IsRuntimeHiddenMarker(meshRef.GfxObjId)) + return true; + bool entityHasCutoutSubset = entityHasCutoutSubsetOverride ?? renderData.HasCutoutSubset; bool allTexturesReady = true; for (int batchIdx = 0; batchIdx < renderData.Batches.Count; batchIdx++) diff --git a/src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs b/src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs index 0a866362..39cc1336 100644 --- a/src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs +++ b/src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs @@ -49,6 +49,7 @@ public sealed class WbMeshAdapter private readonly IMeshPipelineDevice? _graphicsDevice; private readonly ObjectMeshManager? _meshManager; private readonly AcDream.App.Rendering.IGpuResourceRetirementQueue? _resourceRetirement; + private readonly Func _runtimeHiddenMarker; private readonly IPreparedAssetSource? _ownedPreparedAssets; private readonly MeshUploadFrameBudget _ordinaryUploadBudget = CreateUploadBudget(MaximumUploadsPerFrame); @@ -83,6 +84,15 @@ public sealed class WbMeshAdapter internal (int Count, long Bytes) CpuMeshCacheDiagnostics => _meshManager?.CpuCacheDiagnostics ?? default; + /// + /// Retail's statically decidable degrade-to-nothing admission. Marker + /// meshes whose only real level ends at distance zero never reach a draw + /// at runtime; the walk classifier and shadow caster share this exact + /// memoized predicate. + /// + internal bool IsRuntimeHiddenMarker(uint gfxObjId) => + _runtimeHiddenMarker(gfxObjId); + internal void SetDestinationRevealUploadPriority(bool enabled) => _destinationRevealUploadPriority = enabled; @@ -182,6 +192,15 @@ public sealed class WbMeshAdapter ArgumentNullException.ThrowIfNull(budgets); _resourceRetirement = resourceRetirement; + var hiddenMarkerMemo = + new System.Collections.Concurrent.ConcurrentDictionary(); + _runtimeHiddenMarker = gfxObjId => + hiddenMarkerMemo.GetOrAdd( + gfxObjId, + static (id, source) => + AcDream.Core.Meshing.GfxObjDegradeResolver + .IsRuntimeHiddenMarker(source, id), + dats); var resources = new AcDream.App.Rendering.ResourceCleanupGroup(); IMeshPipelineDevice? graphicsDevice = null; IPreparedAssetSource? resolvedPreparedAssets = preparedAssets; @@ -295,6 +314,7 @@ public sealed class WbMeshAdapter // Uninitialized constructor — only for tests / flag-off cases where // the caller wants a Dispose-safe no-op instance. _isUninitialized = true; + _runtimeHiddenMarker = static _ => false; } /// Test/init helper — produces a Dispose-safe instance with no