From 71e6e18c1c15659f28fa53bd2ef54b171a830c9a Mon Sep 17 00:00:00 2001 From: JohnM <234386938+treuenten@users.noreply.github.com> Date: Sat, 10 Oct 2026 00:11:53 +0800 Subject: [PATCH] Draw props whose materials a map borrows from common_mp A map zone streams the materials it shares with common_mp as ",name" references: stubs with no textures and no technique set. Since capture_material refuses a material without a technique set, the slot of such a reference was never bound and every model surface on it was dropped: 97 of the 14551 map model surfaces on mp_terminal and 28 of the 14125 on mp_favela, among them a surface of the wall fan cs_wallfan1 and one of the air conditioner com_ex_airconditioner. When the capture refuses a reference, the slot is now bound to the real material of the same name and namespace, which the common pool already holds. A reference with no real counterpart stays unbound as before. ",mc/shadowcaster" references bind now too, so scene_lit_pass_material skips shadow-only materials for script models and dyn ents, as the static model path already does. Co-Authored-By: Claude Opus 5.5 --- crates/asset_material/src/material_catalog.rs | 20 ++++++++++++++++++- .../render_anim/src/anim/model_materials.rs | 3 +++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/asset_material/src/material_catalog.rs b/crates/asset_material/src/material_catalog.rs index 580b27b..1eee3bd 100644 --- a/crates/asset_material/src/material_catalog.rs +++ b/crates/asset_material/src/material_catalog.rs @@ -937,6 +937,21 @@ impl MaterialCatalog { ) } + fn reference_material_index(&self, s: &ZoneStream<'_>) -> Option { + let name = s + .latest_material()? + .name + .and_then(|name| s.cstr(name).ok()) + .map(AssetRef::decode)?; + if !name.is_reference() { + return None; + } + let namespace = self.capture_ns?; + self.materials + .iter() + .position(|m| m.namespace == namespace && m.name.is_real() && m.name.same_name(&name)) + } + fn capture_material(&mut self, s: &ZoneStream<'_>) -> Option { let geometry = s.latest_material()?; let name = geometry @@ -1299,7 +1314,10 @@ impl AssetLinkSink for MaterialCatalog { } AssetType::Material => { let header = s.latest_material().and_then(|g| g.header); - let Some(index) = self.capture_material(s) else { + let Some(index) = self + .capture_material(s) + .or_else(|| self.reference_material_index(s)) + else { self.capture_gaps += 1; return Ok(()); }; diff --git a/crates/render_anim/src/anim/model_materials.rs b/crates/render_anim/src/anim/model_materials.rs index 1fe5629..cdeaae2 100644 --- a/crates/render_anim/src/anim/model_materials.rs +++ b/crates/render_anim/src/anim/model_materials.rs @@ -111,6 +111,9 @@ pub fn scene_lit_pass_material( authored: assets::MaterialIndex, ) -> Option { let world_material = tess.catalog().derived(authored)?; + if world_material.shadow_only { + return None; + } let ordinal = tess .catalog() .parts()