Files
XCEngine/docs/api/XCEngine/Components/MeshRendererComponent/MeshRendererComponent.md

188 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MeshRendererComponent
**命名空间**: `XCEngine::Components`
**类型**: `class`
**头文件**: `XCEngine/Components/MeshRendererComponent.h`
**描述**: 保存材质槽、项目资产 `AssetRef`、运行时材质句柄以及少量渲染附加状态,把对象的“怎么画”这一侧数据收口到一个组件里。
## 角色概述
`MeshRendererComponent` 回答的问题是:
- 这个对象的 mesh 该用哪些材质、带哪些附加渲染状态去画?
当前它的职责可以分成四层:
- 保存运行时 `Material` 句柄数组
- 保存项目材质的稳定 `AssetRef`
- 仅对 `builtin://` 这类 virtual path 保留 `materialPaths`
- 保存 `castShadows / receiveShadows / renderLayer`
和 [MeshFilterComponent](../MeshFilterComponent/MeshFilterComponent.md) 一样,当前最需要纠正的文档心智是:
- 对项目资产材质,正式序列化协议已经是 `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](SetMaterialPath.md) 当前仍然是同步接口:
1. 自动扩容到目标槽位
2. 清掉旧 pending async load 状态
3. 写入 `m_materialPaths[index]`
4. 若路径为空,则清掉 handle 和 `AssetRef`
5. 若路径非空,则同步 `Load<Material>(path)`
6. 再尝试按路径回填 `m_materialRefs[index]`
因此对项目路径 `Assets/runtime.material` 来说,运行时是允许直接按路径设置的;但场景文本正式协议已经不是“只存路径”。
### 2. 运行时按句柄设置
[SetMaterial](SetMaterial.md) / [SetMaterials](SetMaterials.md) 会从句柄反推出路径,再按路径尝试回填 `AssetRef`
这让“运行时直接塞句柄”和“后续还能稳定序列化回项目身份”保持一致。
### 3. 反序列化恢复
[Deserialize](Deserialize.md) 当前只识别:
- `materialPaths=<path0|path1|...>`
- `materialRefs=<guid,localId,resourceType|...>`
- `castShadows`
- `receiveShadows`
- `renderLayer`
恢复顺序是:
1. 某槽位若有有效 `materialRef`,优先按 `AssetRef` 恢复
2. 若处于 deferred scene load先尝试 `TryResolveAssetPath(materialRef, path)` 恢复 `Assets/...`
3. 若不在 deferred 模式,则直接按 `AssetRef` 加载材质
4. 若没有有效 `AssetRef`,只接受带 virtual scheme 的 `materialPaths`
5. 没有 `AssetRef` 的普通项目路径会被清空
也就是说,下面这种旧式数据:
```text
materialPaths=Assets/runtime.material;materialRefs=;
```
当前不会再被当成正式项目材质绑定恢复。
## 序列化语义
当前 [Serialize](Serialize.md) 会写出:
```text
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](GetMaterial.md) 和 [GetMaterialHandle](GetMaterialHandle.md) 当前都带副作用。
它们会内部触发:
1. `EnsureDeferredAsyncMaterialLoadStarted(index)`
2. `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](GetMaterialPath.md)、[GetMaterialPaths](GetMaterialPaths.md) 和 [GetMaterialAssetRefs](GetMaterialAssetRefs.md)
## 相关方法
- [GetMaterialCount](GetMaterialCount.md)
- [GetMaterial](GetMaterial.md)
- [GetMaterialHandle](GetMaterialHandle.md)
- [GetMaterialPath](GetMaterialPath.md)
- [GetMaterialPaths](GetMaterialPaths.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)
- [SetMaterial](SetMaterial.md)
- [SetMaterialPath](SetMaterialPath.md)
- [SetMaterials](SetMaterials.md)
- [ClearMaterials](ClearMaterials.md)
- [GetCastShadows](GetCastShadows.md)
- [SetCastShadows](SetCastShadows.md)
- [GetReceiveShadows](GetReceiveShadows.md)
- [SetReceiveShadows](SetReceiveShadows.md)
- [GetRenderLayer](GetRenderLayer.md)
- [SetRenderLayer](SetRenderLayer.md)
- [Serialize](Serialize.md)
- [Deserialize](Deserialize.md)
## 相关文档
- [当前模块](../Components.md)
- [MeshFilterComponent](../MeshFilterComponent/MeshFilterComponent.md)
- [RenderSceneExtractor](../../Rendering/Extraction/RenderSceneExtractor/RenderSceneExtractor.md)
- [BuiltinForwardPipeline](../../Rendering/Pipelines/BuiltinForwardPipeline/BuiltinForwardPipeline.md)
- [API 总索引](../../../main.md)