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

6.9 KiB
Raw Blame History

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 当前仍然是同步接口:

  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 / SetMaterials 会从句柄反推出路径,再按路径尝试回填 AssetRef

这让“运行时直接塞句柄”和“后续还能稳定序列化回项目身份”保持一致。

3. 反序列化恢复

Deserialize 当前只识别:

  • 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 的普通项目路径会被清空

也就是说,下面这种旧式数据:

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 的关系

GetMaterialGetMaterialHandle 当前都带副作用。

它们会内部触发:

  1. EnsureDeferredAsyncMaterialLoadStarted(index)
  2. ResolvePendingMaterials()

所以在 deferred 场景里,反序列化后常见状态是:

  • m_materialRefs[index] 已恢复
  • m_materialPaths[index] 可能已恢复
  • m_materials[index] 仍然为空

直到后续第一次访问材质句柄时,异步兑现才真正开始或收口。

与渲染链路的关系

当前 RenderSceneUtility 会把 MeshRendererComponent* 挂进可见项,再由后续材质提取逻辑读取具体槽位材质。

因此当前对 renderer 的真实影响是:

  • 组件是否存在并不等于材质已经可用
  • deferred load 下,材质槽元数据可能先恢复,材质句柄后到

另外要继续明确一个实现边界:

  • castShadowsreceiveShadowsrenderLayer 当前已可保存、序列化、脚本读写
  • 但还不能简单等同于“这些标志已经完整进入当前 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() 是带副作用的读访问器;只读元数据时应优先使用 GetMaterialPathGetMaterialPathsGetMaterialAssetRefs

相关方法

相关文档