docs: sync mesh renderer component docs

This commit is contained in:
2026-04-03 11:50:13 +08:00
parent 6636834b35
commit 2138195b69
13 changed files with 324 additions and 84 deletions

View File

@@ -12,6 +12,14 @@ void ClearMaterials();
- `m_materials` - `m_materials`
- `m_materialPaths` - `m_materialPaths`
- `m_materialRefs`
- `m_pendingMaterialLoads`
- `m_asyncMaterialLoadRequested`
## 注意事项
- 它不会重置 `castShadows``receiveShadows``renderLayer`
- 调用后 `GetMaterialCount()` 会回到 `0`
## 相关文档 ## 相关文档

View File

@@ -11,15 +11,44 @@ void Deserialize(std::istream& is) override;
当前实现会: 当前实现会:
1. 先清空材质并重置默认标志。 1. 先清空材质并重置默认标志。
2. 解析 `materials``castShadows``receiveShadows``renderLayer` 2. 解析 `materialPaths``materialRefs``castShadows``receiveShadows``renderLayer`
3. 对每个非空材质路径调用 `ResourceManager::Get().Load<Resources::Material>()` 尝试重新加载 3. 兼容读取历史 `materials=` 键,并把它当作 `materialPaths=` 处理
4.`m_materials``m_materialPaths``m_materialRefs`、pending 数组和 async 标记数组在槽位数量上重新对齐。
随后每个槽位按下面的优先级恢复:
1. 如果 `materialRef` 有效,先尝试按 `AssetRef` 恢复。
2. 如果当前处于 deferred scene load优先尝试把 `AssetRef` 重新解析回路径,但不立即同步加载材质。
3. 如果没有有效 `AssetRef`,或者按 `AssetRef` 恢复失败,再退回到路径恢复。
4. 只有在非 deferred 路径下,才会直接调用 [SetMaterialPath](SetMaterialPath.md) 做同步加载。
这意味着“反序列化完成”并不等于“所有材质句柄都已经可用”。
## 当前实现说明 ## 当前实现说明
- 如果某个材质加载失败,对应路径仍会保留,但句柄可能为空。
- 默认值是:`castShadows = true``receiveShadows = true``renderLayer = 0` - 默认值是:`castShadows = true``receiveShadows = true``renderLayer = 0`
- 如果某个槽位最终只恢复出了路径或 `AssetRef`,对应 `m_materials[index]` 仍可能为空。
- 在 deferred scene load 模式下,真正的兑现通常要等首次 [GetMaterial](GetMaterial.md) 或 [GetMaterialHandle](GetMaterialHandle.md) 调用时才会触发。
## 项目资产恢复语义
当前实现特别强调项目资产的稳定恢复:
- 当场景文本里有有效 `materialRef=` 时,组件会尽量依赖它,而不是单纯依赖字符串路径。
- 如果 `AssetRef` 能解析到当前项目中的材质,路径会被重新规范化回 `Assets/...`
- 如果解析失败,仍会回退到文本路径。
这和商业引擎常见的“文本可读路径 + 稳定内部引用”双轨设计一致,优点是既便于调试,也能提高资源重命名后的恢复成功率。
## 兼容性说明
- 仍兼容历史 `materials=` 文本。
- 新版 [Serialize](Serialize.md) 不再输出 `materials=`
## 相关文档 ## 相关文档
- [返回类型总览](MeshRendererComponent.md) - [返回类型总览](MeshRendererComponent.md)
- [Serialize](Serialize.md) - [Serialize](Serialize.md)
- [GetMaterial](GetMaterial.md)
- [GetMaterialHandle](GetMaterialHandle.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)

View File

@@ -12,10 +12,37 @@ Resources::Material* GetMaterial(size_t index) const;
## 返回值 ## 返回值
-索引有效则返回对应材质 -当前槽位已经有可用材质,返回对应 `Material*`
- 否则返回 `nullptr` - 否则返回 `nullptr`
## 行为说明
按当前实现,这个访问器会先做两件事:
1. `EnsureDeferredAsyncMaterialLoadStarted(index)`
2. `ResolvePendingMaterials()`
因此它不是纯只读 getter而是一个“读访问 + 尝试推进材质兑现”的接口。
## `nullptr` 的含义
返回 `nullptr` 并不只代表“索引越界”,还可能表示:
- 槽位存在,但路径为空。
- 槽位存在,材质还没完成异步加载。
- 同步或异步加载失败。
- 槽位越界。
如果调用方需要区分这些情况,应结合 [GetMaterialPath](GetMaterialPath.md) 和 [GetMaterialAssetRefs](GetMaterialAssetRefs.md) 一起判断。
## 使用建议
- 渲染提取或真正需要访问 `Material` 对象时使用它。
- 如果只是想读取序列化主数据、调试路径或避免副作用,不要把它当作元数据 getter 使用。
## 相关文档 ## 相关文档
- [返回类型总览](MeshRendererComponent.md) - [返回类型总览](MeshRendererComponent.md)
- [GetMaterialHandle](GetMaterialHandle.md) - [GetMaterialHandle](GetMaterialHandle.md)
- [GetMaterialPath](GetMaterialPath.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)

View File

@@ -0,0 +1,34 @@
# MeshRendererComponent::GetMaterialAssetRefs
获取当前所有材质槽的资产引用数组。
```cpp
const std::vector<Resources::AssetRef>& GetMaterialAssetRefs() const;
```
## 返回值
- 返回内部 `m_materialRefs` 的常量引用。
## 行为说明
每个槽位的 `AssetRef` 可能在以下路径里被更新:
- [SetMaterialPath](SetMaterialPath.md) 成功把路径映射到项目资产时
- [SetMaterial](SetMaterial.md) / [SetMaterials](SetMaterials.md) 成功从材质路径回填时
- [Deserialize](Deserialize.md) 直接读到 `materialRefs=` 字段时
- 异步材质兑现后,再按最终路径回填时
## 注意事项
- 这是只读元数据访问器,不会触发加载。
- 数组长度和材质槽数量保持对齐,但某些槽位的 `AssetRef` 可能是无效的。
- `builtin://` 这类虚拟路径、普通运行时路径或当前无法解析到项目资产的路径,通常都不会得到有效 `AssetRef`
## 相关文档
- [返回类型总览](MeshRendererComponent.md)
- [GetMaterialPaths](GetMaterialPaths.md)
- [GetMaterialPath](GetMaterialPath.md)
- [Serialize](Serialize.md)
- [Deserialize](Deserialize.md)

View File

@@ -10,6 +10,11 @@ size_t GetMaterialCount() const;
- 当前 `m_materials.size()` - 当前 `m_materials.size()`
## 当前实现说明
- 它表示“材质槽数量”,不是“成功加载的材质数量”。
- 反序列化保留下来的空槽位和尾部空槽位也会计入这个数量。
## 相关文档 ## 相关文档
- [返回类型总览](MeshRendererComponent.md) - [返回类型总览](MeshRendererComponent.md)

View File

@@ -15,11 +15,23 @@ const Resources::ResourceHandle<Resources::Material>& GetMaterialHandle(size_t i
- 若索引有效则返回对应句柄引用。 - 若索引有效则返回对应句柄引用。
- 否则返回一个静态空句柄引用。 - 否则返回一个静态空句柄引用。
## 行为说明
和 [GetMaterial](GetMaterial.md) 一样,当前实现会在返回前先执行:
1. `EnsureDeferredAsyncMaterialLoadStarted(index)`
2. `ResolvePendingMaterials()`
因此它也可能在首次访问时启动一次异步加载,而不是单纯返回缓存引用。
## 注意事项 ## 注意事项
- 越界时返回的是共享的静态空句柄,而不是临时对象。 - 越界时返回的是共享的静态空句柄,而不是临时对象。
- 即使索引有效,返回的句柄也可能仍为空,因为异步结果尚未完成或加载失败。
- 如果调用方只想读取路径 / `AssetRef` 元数据,应优先使用 [GetMaterialPath](GetMaterialPath.md) 或 [GetMaterialAssetRefs](GetMaterialAssetRefs.md)。
## 相关文档 ## 相关文档
- [返回类型总览](MeshRendererComponent.md) - [返回类型总览](MeshRendererComponent.md)
- [GetMaterial](GetMaterial.md) - [GetMaterial](GetMaterial.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)

View File

@@ -1,28 +1,35 @@
# GetMaterialPath # MeshRendererComponent::GetMaterialPath
**所属类型**: [MeshRendererComponent](MeshRendererComponent.md) 获取指定材质槽当前缓存的路径字符串。
## 签名
```cpp ```cpp
const std::string& GetMaterialPath(size_t index) const; const std::string& GetMaterialPath(size_t index) const;
``` ```
## 作用
返回指定材质槽当前记录的资源路径字符串。
## 当前实现行为 ## 当前实现行为
- 如果 `index` 在范围内,返回 `m_materialPaths[index]` - 如果 `index` 在范围内,返回 `m_materialPaths[index]`
- 如果越界,返回一个静态空字符串引用,而不是抛异常。 - 如果越界,返回一个静态空字符串引用,而不是抛异常。
## 语义说明
- 这是纯元数据访问器,不会触发异步加载。
- 返回的是组件当前内存中的路径缓存,不等于最终序列化文本里的 `materialPaths=` 输出。
- 对于有效项目资产槽位,序列化时路径可能被省略到 `materialRefs=` 中,但运行时这里仍然可能保留 `Assets/...` 路径。
## 使用建议 ## 使用建议
这个接口更适合做编辑器显示、序列化检查和路径级调试;真正的运行时材质对象访问仍应优先看 [GetMaterial](GetMaterial.md) 或 [GetMaterialHandle](GetMaterialHandle.md)。 适合用于:
- 编辑器 Inspector 或调试 UI
- 路径级日志输出
- 需要避免触发加载副作用的代码路径
真正需要访问 `Material` 对象时,仍应看 [GetMaterial](GetMaterial.md) 或 [GetMaterialHandle](GetMaterialHandle.md)。
## 相关文档 ## 相关文档
- [MeshRendererComponent](MeshRendererComponent.md) - [返回类型总览](MeshRendererComponent.md)
- [GetMaterial](GetMaterial.md) - [GetMaterial](GetMaterial.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)
- [SetMaterialPath](SetMaterialPath.md) - [SetMaterialPath](SetMaterialPath.md)

View File

@@ -10,8 +10,20 @@ const std::vector<std::string>& GetMaterialPaths() const;
- 当前缓存的材质路径数组引用。 - 当前缓存的材质路径数组引用。
## 行为说明
- 数组槽位与 `m_materials``m_materialRefs` 的槽位语义保持对齐。
- 这是只读元数据访问器,不会触发加载。
- 这里返回的是内存里的完整路径缓存,不是最终文本序列化时的 fallback 输出。
## 何时使用
- 需要批量显示或检查所有材质槽路径时。
- 需要避免对每个槽位逐个访问 [GetMaterial](GetMaterial.md) 产生副作用时。
## 相关文档 ## 相关文档
- [返回类型总览](MeshRendererComponent.md) - [返回类型总览](MeshRendererComponent.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)
- [Serialize](Serialize.md) - [Serialize](Serialize.md)
- [Deserialize](Deserialize.md) - [Deserialize](Deserialize.md)

View File

@@ -6,64 +6,127 @@
**头文件**: `XCEngine/Components/MeshRendererComponent.h` **头文件**: `XCEngine/Components/MeshRendererComponent.h`
**描述**: 保存材质槽、阴影开关和渲染层等绘制配置,声明“这个对象上的 Mesh 应该如何被渲染” **描述**: 保存材质槽、资产引用、阴影开关和渲染层等绘制配置,把场景对象与具体 `Material` 资源绑定起来
## 角色概述 ## 角色概述
`MeshRendererComponent` 负责的是“绘制配置”,不是“几何来源”。 `MeshRendererComponent` 负责回答“这个对象上的 mesh 应该用什么材质、以什么附加状态被渲染”。它不提供几何来源,也不直接发起 draw call当前链路里它主要扮演四个角色
- [MeshFilterComponent](../MeshFilterComponent/MeshFilterComponent.md) 负责网格 - 为运行时保存可直接使用的 `Material` 句柄。
- `MeshRendererComponent` 负责材质槽和渲染附加参数 - 为场景文本保存稳定的材质路径数组。
- 为项目资产保存 `AssetRef`,让场景在资源移动或重命名后仍有机会恢复材质绑定。
- 为脚本层、编辑器和渲染提取层提供统一的材质槽视图。
两者配合后,`RenderSceneExtractor` 才能把场景对象整理成可提交到渲染管线的可见项。 和它配套工作的主要对象是:
## 当前实现行为 - [MeshFilterComponent](../MeshFilterComponent/MeshFilterComponent.md):负责 mesh 资源本身。
- `ResourceManager`:负责按路径或 `AssetRef` 加载 `Material`,也负责延迟异步加载。
- [RenderSceneExtractor](../../Rendering/RenderSceneExtractor/RenderSceneExtractor.md):只有 mesh 和材质都可用时,才会把对象整理成可见渲染项。
### 1. 同时维护材质 handle 和路径数组 这种拆分很接近商业引擎里常见的 `MeshFilter + MeshRenderer` 思路。好处是“几何来源”和“绘制配置”可以分别序列化、分别编辑,也更适合后续做资源热更新、材质复用和编辑器 Inspector。
内部维护两套并行数组: ## 当前状态模型
- `m_materials` 当前实现维护了五份与材质槽相关的状态:
- `m_materialPaths`
这和 `MeshFilterComponent` 的思路一致,目的是同时满足: | 状态 | 类型 | 作用 |
|------|------|------|
| `m_materials` | `std::vector<ResourceHandle<Material>>` | 当前已兑现的运行时材质句柄。 |
| `m_materialPaths` | `std::vector<std::string>` | 可序列化、可调试的路径数组。 |
| `m_materialRefs` | `std::vector<Resources::AssetRef>` | 项目资产数据库可解析的稳定引用。 |
| `m_pendingMaterialLoads` | `std::vector<std::shared_ptr<...>>` | 异步加载尚未收口时的挂起结果。 |
| `m_asyncMaterialLoadRequested` | `std::vector<bool>` | 防止同一槽位重复发起异步加载。 |
- 运行时快速拿资源 这几组数组会尽量保持同一槽位语义对齐。换句话说,`slot 3` 的 handle、path、`AssetRef`、pending state 都是在描述“第 3 个材质槽”。
- 序列化时稳定写路径
### 2. 材质槽会按需自动扩容 ## 绑定与解析流程
`SetMaterialPath()``SetMaterial()` 在写入指定槽位前,都会先调用内部 `EnsureMaterialSlot(index)` ### 1. 路径驱动的绑定
因此 [SetMaterialPath](SetMaterialPath.md) 会
- 可以直接写入一个较大的槽位索引 - 先确保槽位存在。
- 中间缺失槽位会被自动补成空材质 - 清掉该槽位旧的异步挂起状态。
- 写入新的 `m_materialPaths[index]`
- 立即调用 `ResourceManager::Load<Material>(path)` 做一次同步加载尝试。
- 再调用 `TryGetAssetRef(path, ResourceType::Material, ...)` 尝试回填 `AssetRef`
`tests/Components/test_mesh_render_components.cpp` 已覆盖这一行为 即使同步加载失败,路径仍会保留;如果这条路径能映射到项目资产,`m_materialRefs[index]` 也可能仍然有效
### 3. 越界读取是“安全空值”语义 要注意一个很容易误解的点:按当前源码,`SetMaterialPath()` 本身仍是“立即同步加载”的接口,它不会因为 deferred scene load 模式而自动改成纯记录路径。
按当前实现: ### 2. 句柄驱动的绑定
- `GetMaterial(index)` 越界时返回 `nullptr` [SetMaterial](SetMaterial.md) 会:
- `GetMaterialHandle(index)` 越界时返回静态空 handle
- `GetMaterialPath(index)` 越界时返回静态空字符串
这让上层调用少了很多显式边界判断,但也意味着调用者不能把空返回值误读成“这个槽位一定存在但内容为空” - 保存句柄
- 通过 `material->GetPath()` 反推路径。
- 再按路径尝试回填 `AssetRef`
### 4. 反序列化按路径重建槽位 这让“运行时直接塞一个已经加载好的材质”与“场景序列化仍能恢复路径/资产引用”两件事可以同时成立。
`Deserialize()` 会: ### 3. 反序列化后的恢复策略
- 先清空旧材质与标记 [Deserialize](Deserialize.md) 现在会同时处理:
- 解析 `materials=` 字段
-`|` 分隔多材质路径
- 对每个槽位调用 `SetMaterialPath()` 尝试重新加载
即使资源此刻没有加载成功,路径数组也会被保留下来。这一点同样有测试覆盖。 - `materialPaths=<path0|path1|...>`
- `materialRefs=<guid,localId,resourceType|...>`
- 历史兼容键 `materials=<path0|path1|...>`
## 阴影和渲染层的现实状态 恢复优先级大致是:
1. 如果 `materialRef` 有效,优先尝试按 `AssetRef` 恢复。
2. 如果当前是 deferred scene load尽量先把 `AssetRef` 解析回路径,但不立即同步兑现材质。
3. 如果没有可用 `AssetRef`,再按路径恢复。
### 4. 首次访问时的异步兑现
[GetMaterial](GetMaterial.md) 和 [GetMaterialHandle](GetMaterialHandle.md) 虽然是 `const`,但当前实现会通过 `const_cast` 内部触发:
1. `EnsureDeferredAsyncMaterialLoadStarted(index)`
2. `ResolvePendingMaterials()`
也就是说,这两个访问器不只是“读缓存”,还会推动路径恢复后的材质兑现流程向前走。
从行为上看,它们的主用途是支持 deferred scene load但按当前源码只要某个槽位“有路径、还没有 material、也还没发起过请求”首次访问就可能触发一次异步加载尝试。
## 序列化语义
当前序列化会输出五段键值:
```text
materialPaths=<fallbackPath0|fallbackPath1|...>;
materialRefs=<guid,localId,resourceType|...>;
castShadows=1;
receiveShadows=1;
renderLayer=0;
```
其中最关键的设计点是:
- `materialRefs` 是主身份信息,面向项目资产恢复。
- `materialPaths` 更像回退字段,只在该槽位没有有效 `AssetRef` 时才真正写出路径。
例如一个项目资产材质槽,当前文本很可能是:
```text
materialPaths=;
materialRefs=<有效 guid,localId,type>;
```
这不是数据丢失,而是说明当前序列化更信任 `AssetRef` 作为稳定身份。
## 与渲染链路的关系
当前 `RenderSceneUtility` 会把 `MeshRendererComponent*` 直接挂进可见渲染对象,再由后续材质提取逻辑读取材质槽。
这意味着:
- `MeshRendererComponent` 是否“有槽位”不重要。
- 重要的是在真正提取材质时,对应槽位能否拿到有效 `Material`
- 在 deferred / async 场景里,对象可能已经有 `MeshFilterComponent + MeshRendererComponent`,但某个时刻材质仍为空,直到异步结果被 `ResolvePendingMaterials()` 收口。
## 阴影与渲染层的现实状态
当前组件公开了: 当前组件公开了:
@@ -71,35 +134,36 @@
- `GetReceiveShadows()` / `SetReceiveShadows()` - `GetReceiveShadows()` / `SetReceiveShadows()`
- `GetRenderLayer()` / `SetRenderLayer()` - `GetRenderLayer()` / `SetRenderLayer()`
但按当前 [RenderSceneExtractor](../../Rendering/RenderSceneExtractor/RenderSceneExtractor.md) 实现,场景提取时还没有看到这些字段被真正消费。也就是说 但按当前源码检索,这几个字段目前主要被
- 这些字段当前可以保存 - 组件测试、场景序列化测试
- 也可以被序列化 - Mono scripting internal call
- 但它们还没有完整接入当前已取证的渲染提取路径
文档必须把这一点说清楚,避免用户把“字段存在”误解成“功能已完整生效”。 消费;还没有看到它们被当前已取证的渲染提取路径真正读取。也就是说:
## 序列化语义 - 它们当前可以保存。
- 可以被序列化。
- 也可以被脚本层读写。
- 但不能简单等同于“阴影层级和 render layer 已完整进入 renderer 主路径”。
当前写出: ## 测试与真实使用点
- `materials`,多个路径用 `|` 分隔 - `tests/Components/test_mesh_render_components.cpp` 覆盖了槽位扩容、双轨序列化、历史键兼容、项目材质 `AssetRef` 恢复,以及 deferred async material load。
- `castShadows` - `tests/Scene/test_scene.cpp` 覆盖了场景序列化后对 `castShadows` / `receiveShadows` / `renderLayer` 的恢复。
- `receiveShadows` - `engine/src/Scripting/Mono/MonoScriptRuntime.cpp` 暴露了阴影和 render layer 的脚本读写入口。
- `renderLayer`
测试还覆盖了“末尾空材质槽”会被保留的情况,这对编辑器材质槽 UI 很重要。 ## 线程与访问语义
## 线程语义
- 当前实现没有内部加锁。 - 当前实现没有内部加锁。
- 资源路径解析和材质切换默认按主线程配置路径使用。 - 路径写入、序列化和槽位编辑默认按主线程使用。
- `GetMaterial()` / `GetMaterialHandle()` 是带副作用的读访问器;如果调用方只想查看元数据而不触发兑现,应优先使用 [GetMaterialPath](GetMaterialPath.md)、[GetMaterialPaths](GetMaterialPaths.md) 或 [GetMaterialAssetRefs](GetMaterialAssetRefs.md)。
## 当前实现限制 ## 当前实现限制
- 当前文档目录原先缺少 `GetMaterialPath()``SetMaterialPath()` 独立方法页,本轮已补齐 - 当前只维护“平铺材质槽数组”,不处理 submesh 级独立绑定策略、材质实例化策略或 streaming 策略
- `castShadows``receiveShadows``renderLayer` 还没有完整接入当前已取证的场景提取逻辑 - `GetMaterial()` / `GetMaterialHandle()` 可能在首次访问时触发异步加载,因此它们不是纯只读 getter
- 它只负责声明绘制配置,不等于完整 renderer feature 集合 - `materialPaths` 在序列化文本里只作为 fallback 字段输出,不应把“文本里 path 为空”误解成“组件内存里没有路径缓存”
- `castShadows``receiveShadows``renderLayer` 还没有完整接入当前已取证的 renderer 主路径。
## 相关方法 ## 相关方法
@@ -108,6 +172,7 @@
- [GetMaterialHandle](GetMaterialHandle.md) - [GetMaterialHandle](GetMaterialHandle.md)
- [GetMaterialPath](GetMaterialPath.md) - [GetMaterialPath](GetMaterialPath.md)
- [GetMaterialPaths](GetMaterialPaths.md) - [GetMaterialPaths](GetMaterialPaths.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)
- [SetMaterial](SetMaterial.md) - [SetMaterial](SetMaterial.md)
- [SetMaterialPath](SetMaterialPath.md) - [SetMaterialPath](SetMaterialPath.md)
- [SetMaterials](SetMaterials.md) - [SetMaterials](SetMaterials.md)

View File

@@ -8,18 +8,31 @@ void Serialize(std::ostream& os) const override;
## 行为说明 ## 行为说明
当前实现会输出类似 当前实现不再输出旧的 `materials=` 键,而是并行写出材质路径 fallback 和稳定资产引用
```text ```text
materials=Mat0|Mat1; materialPaths=|builtin://materials/default;
materialRefs=<guid,localId,resourceType>|;
castShadows=1; castShadows=1;
receiveShadows=0; receiveShadows=0;
renderLayer=3; renderLayer=3;
``` ```
其中材质路径使用 `|` 分隔。 其中
- `materialPaths``materialRefs` 都按材质槽顺序用 `|` 分隔。
- 对于能稳定映射到项目资产的槽位,当前会优先写 `materialRefs`,并把同槽位 `materialPaths` 留空。
- 对于没有有效 `AssetRef` 的槽位,例如 `builtin://` 这类虚拟路径,才会把路径写进 `materialPaths`
- 会保留尾部空槽位,避免编辑器材质槽数量在反序列化后缩水。
## 当前实现含义
- 场景文本现在同时保存“人类可读路径 fallback”和“项目资产稳定身份”。
- 项目资产改名或移动后,更应依赖 `materialRefs` 恢复绑定,而不是依赖旧路径字符串。
- 旧版依赖 `materials=` 的读取逻辑仍由 [Deserialize](Deserialize.md) 保留兼容。
## 相关文档 ## 相关文档
- [返回类型总览](MeshRendererComponent.md) - [返回类型总览](MeshRendererComponent.md)
- [Deserialize](Deserialize.md) - [Deserialize](Deserialize.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)

View File

@@ -1,6 +1,6 @@
# MeshRendererComponent::SetMaterial # MeshRendererComponent::SetMaterial
设置单个材质槽。 按句柄设置单个材质槽。
```cpp ```cpp
void SetMaterial(size_t index, const Resources::ResourceHandle<Resources::Material>& material); void SetMaterial(size_t index, const Resources::ResourceHandle<Resources::Material>& material);
@@ -12,16 +12,26 @@ void SetMaterial(size_t index, Resources::Material* material);
当前实现会: 当前实现会:
1. 通过 `EnsureMaterialSlot()` 保证数组至少扩容到 `index + 1` 1. 通过 `EnsureMaterialSlot()` 保证数组至少扩容到 `index + 1`
2. 写入 `m_materials[index]` 2. 清掉该槽位旧的 pending async load并复位 async-request 标记
3. 根据句柄中的资源路径同步更新 `m_materialPaths[index]` 3. 写入 `m_materials[index]`
4. 根据句柄中的资源路径同步更新 `m_materialPaths[index]`
5. 再按该路径尝试解析 `m_materialRefs[index]`;失败则重置为无效引用。
## 参数 ## 参数
- `index` - 材质槽索引。 - `index` - 材质槽索引。
- `material` - 要设置的材质句柄或裸指针。 - `material` - 要设置的材质句柄或裸指针。
## 语义说明
- 这个接口以“运行时材质对象已经存在”为前提。
- 如果材质句柄没有可解析路径,或者路径不能映射到项目资产,`m_materialRefs[index]` 会变成无效引用。
- 和 [SetMaterialPath](SetMaterialPath.md) 相比,它更偏运行时写入,而不是路径驱动的资源恢复。
## 相关文档 ## 相关文档
- [返回类型总览](MeshRendererComponent.md) - [返回类型总览](MeshRendererComponent.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)
- [SetMaterialPath](SetMaterialPath.md)
- [SetMaterials](SetMaterials.md) - [SetMaterials](SetMaterials.md)
- [ClearMaterials](ClearMaterials.md) - [ClearMaterials](ClearMaterials.md)

View File

@@ -1,32 +1,42 @@
# SetMaterialPath # MeshRendererComponent::SetMaterialPath
**所属类型**: [MeshRendererComponent](MeshRendererComponent.md) 按路径设置单个材质槽。
## 签名
```cpp ```cpp
void SetMaterialPath(size_t index, const std::string& materialPath); void SetMaterialPath(size_t index, const std::string& materialPath);
``` ```
## 作用
为指定材质槽设置资源路径,并尝试通过 `ResourceManager` 加载对应材质。
## 当前实现行为 ## 当前实现行为
- 调用内部 `EnsureMaterialSlot(index)` 自动扩容槽位数组。 - 调用内部 `EnsureMaterialSlot(index)` 自动扩容槽位数组。
- 先记录 `m_materialPaths[index] = materialPath` - 清空该槽位旧的 pending async load并把 `m_asyncMaterialLoadRequested[index]` 复位为 `false`
- 若路径为空,则重置该槽位的材质 handle - 记录 `m_materialPaths[index] = materialPath`
- 若路径为空,则同时重置 `m_materials[index]``m_materialRefs[index]`
- 若路径非空,则调用 `ResourceManager::Get().Load<Resources::Material>(...)` 尝试加载。 - 若路径非空,则调用 `ResourceManager::Get().Load<Resources::Material>(...)` 尝试加载。
- 然后调用 `TryGetAssetRef(...)` 试着把路径映射成项目资产引用。
- 即使加载失败,路径也会保留。 - 即使加载失败,路径也会保留。
## 重要语义
- 这是一个同步设置接口,不是“只记录路径、等以后再加载”的接口。
- 虚拟路径或内置路径也允许写入,但通常拿不到有效 `AssetRef`
- 如果后续重新设置同一槽位,旧的异步状态会被显式丢弃。
## 使用建议 ## 使用建议
这比直接设置 handle 更适合编辑器材质面板和序列化恢复路径,因为路径才是当前场景文件的主持久化数据。 这比直接设置 handle 更适合
- 编辑器材质面板
- 场景文本反序列化后的路径恢复
- 希望保留稳定文本路径的工具链
如果调用方已经手里拿到了一个确定可用的材质句柄,并且希望以运行时对象为主写入,则可以考虑 [SetMaterial](SetMaterial.md)。
## 相关文档 ## 相关文档
- [MeshRendererComponent](MeshRendererComponent.md) - [返回类型总览](MeshRendererComponent.md)
- [GetMaterialPath](GetMaterialPath.md) - [GetMaterialPath](GetMaterialPath.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)
- [SetMaterial](SetMaterial.md)
- [Serialize](Serialize.md) - [Serialize](Serialize.md)
- [Deserialize](Deserialize.md) - [Deserialize](Deserialize.md)

View File

@@ -8,7 +8,14 @@ void SetMaterials(const std::vector<Resources::ResourceHandle<Resources::Materia
## 行为说明 ## 行为说明
当前实现会整体替换 `m_materials`,并把 `m_materialPaths` 调整为相同大小,然后逐项同步路径。 当前实现会整体替换所有材质槽状态:
1. 用传入数组直接替换 `m_materials`
2.`m_materialPaths``m_materialRefs`、pending 数组和 async 标记数组与新长度对齐
3. 对每个槽位从句柄反推路径
4. 再按路径尝试回填 `AssetRef`
这不是增量更新,而是整组重建。旧数组里超出新长度的槽位会被直接丢弃。
## 参数 ## 参数
@@ -18,4 +25,5 @@ void SetMaterials(const std::vector<Resources::ResourceHandle<Resources::Materia
- [返回类型总览](MeshRendererComponent.md) - [返回类型总览](MeshRendererComponent.md)
- [SetMaterial](SetMaterial.md) - [SetMaterial](SetMaterial.md)
- [GetMaterialAssetRefs](GetMaterialAssetRefs.md)
- [GetMaterialCount](GetMaterialCount.md) - [GetMaterialCount](GetMaterialCount.md)