6.9 KiB
MeshRendererComponent
命名空间: XCEngine::Components
类型: class
头文件: XCEngine/Components/MeshRendererComponent.h
描述: 保存材质槽、项目资产 AssetRef、运行时材质句柄以及少量渲染附加状态,把对象的“怎么画”这一侧数据收口到一个组件里。
角色概述
MeshRendererComponent 回答的问题是:
- 这个对象的 mesh 该用哪些材质、带哪些附加渲染状态去画?
当前它的职责可以分成四层:
- 保存运行时
Material句柄数组 - 保存项目材质的稳定
AssetRef - 仅对
builtin://这类 virtual path 保留materialPaths - 保存
castShadows / receiveShadows / renderLayer
和 MeshFilterComponent 一样,当前最需要纠正的文档心智是:
- 对项目资产材质,正式序列化协议已经是
materialRefs materialPaths主要保留给 virtual path,而不是给普通项目路径长期兜底
当前状态模型
当前实现维护 5 组与材质槽相关的状态:
| 状态 | 类型 | 作用 |
|---|---|---|
m_materials |
std::vector<ResourceHandle<Material>> |
当前已兑现的运行时材质句柄。 |
m_materialPaths |
std::vector<std::string> |
槽位路径缓存;项目资产通常会是 Assets/...,virtual 资源可能是 builtin://...。 |
m_materialRefs |
std::vector<Resources::AssetRef> |
项目资产稳定身份。 |
m_pendingMaterialLoads |
std::vector<std::shared_ptr<...>> |
deferred async material load 的挂起结果。 |
m_asyncMaterialLoadRequested |
std::vector<bool> |
防止重复发起异步加载。 |
所有数组都按槽位对齐;slot 2 的 path、ref、handle 和 pending state 都在描述同一材质槽。
绑定与恢复流程
1. 运行时按路径设置
SetMaterialPath 当前仍然是同步接口:
- 自动扩容到目标槽位
- 清掉旧 pending async load 状态
- 写入
m_materialPaths[index] - 若路径为空,则清掉 handle 和
AssetRef - 若路径非空,则同步
Load<Material>(path) - 再尝试按路径回填
m_materialRefs[index]
因此对项目路径 Assets/runtime.material 来说,运行时是允许直接按路径设置的;但场景文本正式协议已经不是“只存路径”。
2. 运行时按句柄设置
SetMaterial / SetMaterials 会从句柄反推出路径,再按路径尝试回填 AssetRef。
这让“运行时直接塞句柄”和“后续还能稳定序列化回项目身份”保持一致。
3. 反序列化恢复
Deserialize 当前只识别:
materialPaths=<path0|path1|...>materialRefs=<guid,localId,resourceType|...>castShadowsreceiveShadowsrenderLayer
恢复顺序是:
- 某槽位若有有效
materialRef,优先按AssetRef恢复 - 若处于 deferred scene load,先尝试
TryResolveAssetPath(materialRef, path)恢复Assets/... - 若不在 deferred 模式,则直接按
AssetRef加载材质 - 若没有有效
AssetRef,只接受带 virtual scheme 的materialPaths - 没有
AssetRef的普通项目路径会被清空
也就是说,下面这种旧式数据:
materialPaths=Assets/runtime.material;materialRefs=;
当前不会再被当成正式项目材质绑定恢复。
序列化语义
当前 Serialize 会写出:
materialPaths=<slot0|slot1|...>;
materialRefs=<slot0|slot1|...>;
castShadows=1;
receiveShadows=1;
renderLayer=0;
但其中最关键的规则是:
- 对有有效
AssetRef的项目材质槽,materialPaths会被清空 - 对没有
AssetRef且是 virtual scheme 的槽位,materialPaths才保留真实路径
因此:
- 项目材质的主协议是
materialRefs builtin://materials/default-primitive这类虚拟路径才长期留在materialPaths
与 deferred scene load 的关系
GetMaterial 和 GetMaterialHandle 当前都带副作用。
它们会内部触发:
EnsureDeferredAsyncMaterialLoadStarted(index)ResolvePendingMaterials()
所以在 deferred 场景里,反序列化后常见状态是:
m_materialRefs[index]已恢复m_materialPaths[index]可能已恢复m_materials[index]仍然为空
直到后续第一次访问材质句柄时,异步兑现才真正开始或收口。
与渲染链路的关系
当前 RenderSceneUtility 会把 MeshRendererComponent* 挂进可见项,再由后续材质提取逻辑读取具体槽位材质。
因此当前对 renderer 的真实影响是:
- 组件是否存在并不等于材质已经可用
- deferred load 下,材质槽元数据可能先恢复,材质句柄后到
另外要继续明确一个实现边界:
castShadows、receiveShadows和renderLayer当前已可保存、序列化、脚本读写- 但还不能简单等同于“这些标志已经完整进入当前 renderer 主路径”
测试与锚点
tests/Components/test_mesh_render_components.cpp- 覆盖 builtin virtual path 的序列化 / 反序列化
- 覆盖“没有
AssetRef的普通项目路径不会作为正式协议保留” - 覆盖项目材质按
AssetRef序列化与恢复 - 覆盖 deferred async material load
tests/Scene/test_scene.cpp- 覆盖 builtin material path 的场景保存 / 加载
当前实现边界
- 当前只维护平铺材质槽数组,不处理更复杂的实例化或 streaming 策略
- 项目材质的正式序列化协议已经收口到
materialRefs - 没有
AssetRef的普通项目路径在反序列化时当前会被清掉;只有 virtual scheme 仍可单独依赖路径 GetMaterial()/GetMaterialHandle()是带副作用的读访问器;只读元数据时应优先使用 GetMaterialPath、GetMaterialPaths 和 GetMaterialAssetRefs
相关方法
- GetMaterialCount
- GetMaterial
- GetMaterialHandle
- GetMaterialPath
- GetMaterialPaths
- GetMaterialAssetRefs
- SetMaterial
- SetMaterialPath
- SetMaterials
- ClearMaterials
- GetCastShadows
- SetCastShadows
- GetReceiveShadows
- SetReceiveShadows
- GetRenderLayer
- SetRenderLayer
- Serialize
- Deserialize