Formalize imported mesh materials
This commit is contained in:
523
docs/plan/Renderer阶段收口_旧兼容路径清理与正式化计划_2026-04-08.md
Normal file
523
docs/plan/Renderer阶段收口_旧兼容路径清理与正式化计划_2026-04-08.md
Normal file
@@ -0,0 +1,523 @@
|
||||
# Renderer阶段收口:旧兼容路径清理与正式化计划
|
||||
|
||||
日期:`2026-04-08`
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前 `Rendering` 模块的主执行架构已经基本成型:
|
||||
|
||||
- `RenderSceneExtractor`
|
||||
- `SceneRenderRequestPlanner`
|
||||
- `SceneRenderer / CameraRenderer`
|
||||
- built-in forward / shadow / object-id / outline / final-color / skybox
|
||||
|
||||
这些主链路已经能稳定支撑:
|
||||
|
||||
- runtime 场景渲染
|
||||
- editor scene/game viewport
|
||||
- 多光源、阴影、object-id、outline、skybox 等现有能力
|
||||
|
||||
因此,当前 Rendering 的主要问题已经不再是“能不能画出来”,而是:
|
||||
|
||||
- 还残留一些旧路线兼容代码
|
||||
- 一些 built-in 运行契约仍然依赖隐式推断
|
||||
- 少量路径仍然带有明显的过渡期实现痕迹
|
||||
|
||||
如果这些问题不在当前阶段彻底收口,后续继续推进:
|
||||
|
||||
- Renderer 模块扩展
|
||||
- Material / Shader editor
|
||||
- Unity 风格 SRP 底层承接
|
||||
|
||||
就会持续建立在一层“虽然能跑,但不是正式规则”的兼容逻辑之上。
|
||||
|
||||
这不符合当前阶段的目标。
|
||||
|
||||
当前阶段的正确方向不是新增更多渲染功能,而是:
|
||||
|
||||
- 清理旧兼容路径
|
||||
- 去掉运行时语义猜测
|
||||
- 把 built-in shader / material / pass contract 进一步正式化
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前已确认的问题
|
||||
|
||||
基于本轮对 `engine/include/XCEngine/Rendering`、`engine/src/Rendering`、`engine/src/Resources/Shader`、`engine/src/Resources/Mesh` 的代码审查,当前确认存在以下问题。
|
||||
|
||||
### 2.1 Mesh 导入仍可生成“无 shader / 无 schema”的旧材质路线
|
||||
|
||||
当前 `MeshLoader` 导入子材质时,仍然直接写入:
|
||||
|
||||
- `baseColor`
|
||||
- `baseColorTexture`
|
||||
- `opacity`
|
||||
- `twoSided`
|
||||
|
||||
而不是直接落到正式 shader schema 对应的属性名与纹理槽位。
|
||||
|
||||
这导致 runtime 渲染阶段仍然需要兜底兼容这些旧名字。
|
||||
|
||||
典型位置:
|
||||
|
||||
- `engine/src/Resources/Mesh/MeshLoader.cpp`
|
||||
- `engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h`
|
||||
|
||||
### 2.2 Rendering 仍通过属性别名表推断 built-in 材质语义
|
||||
|
||||
当前 `RenderMaterialResolve.h` 中,仍然保留了大量 builtin 属性/纹理别名表,例如:
|
||||
|
||||
- `baseColor`
|
||||
- `_BaseColor`
|
||||
- `color`
|
||||
- `_Color`
|
||||
- `baseColorTexture`
|
||||
- `_BaseColorTexture`
|
||||
- `_MainTex`
|
||||
- `texture`
|
||||
|
||||
这意味着 runtime 当前并不是“按 shader schema 正式解析”,而是:
|
||||
|
||||
- 优先找 semantic
|
||||
- 找不到就继续按一批旧属性名字猜
|
||||
|
||||
这属于典型过渡兼容逻辑,不应成为正式长期实现。
|
||||
|
||||
### 2.3 BuiltinForward / Depth / Shadow 仍存在 per-material fallback constant 路线
|
||||
|
||||
当前如果材质没有正式 schema constant layout,管线仍会临时构造:
|
||||
|
||||
- `FallbackPerMaterialConstants`
|
||||
|
||||
并继续提交 draw。
|
||||
|
||||
这说明 runtime 仍允许“非正式材质实例”继续进入正式绘制链路。
|
||||
|
||||
这条路径虽然提高了兼容性,但本质上绕开了已经建立的 shader/material 正式模型。
|
||||
|
||||
### 2.4 Built-in pass resource binding 仍依赖隐式硬编码
|
||||
|
||||
当前 builtin shader pass 如果未显式声明 `resources`,运行时仍会通过:
|
||||
|
||||
- `TryBuildImplicitBuiltinPassResourceBindings`
|
||||
|
||||
自动补一套绑定布局。
|
||||
|
||||
这意味着资源绑定契约并不完全存在于 shader 资产中,而是仍有一部分硬编码在 C++ 中。
|
||||
|
||||
这会带来两个问题:
|
||||
|
||||
1. shader 资产与 runtime 存在双份真相
|
||||
2. 后续继续演进 shader/material/editor 时,容易再次产生隐式规则
|
||||
|
||||
### 2.5 HLSL register 重写仍保留 legacy alias
|
||||
|
||||
当前 `ShaderVariantUtils.h` 仍保留:
|
||||
|
||||
- `ResolveLegacyHlslBindingDeclarationAlias`
|
||||
|
||||
以及基于 `gBaseColorTexture` / `gLinearSampler` 一类旧命名的重写逻辑。
|
||||
|
||||
这说明 shader runtime 编译阶段仍在兼容旧命名风格。
|
||||
|
||||
这属于典型“过渡兼容层”,应在 built-in shader 显式资源契约完成后清掉。
|
||||
|
||||
### 2.6 Built-in pass 选择仍存在隐式默认规则
|
||||
|
||||
当前如果 shader 没有显式 builtin metadata,`MatchesBuiltinPass(...)` 仍会把它默认当成:
|
||||
|
||||
- `ForwardLit`
|
||||
|
||||
这意味着 shader 即使没有明确声明自己属于哪个 built-in pass,也有可能继续进入主几何管线。
|
||||
|
||||
这不利于长期正式化。
|
||||
|
||||
### 2.7 Shader artifact 仍兼容多代旧 schema
|
||||
|
||||
当前 shader artifact loader 仍兼容:
|
||||
|
||||
- `XCSHD01`
|
||||
- `XCSHD02`
|
||||
- `XCSHD03`
|
||||
- `XCSHD04`
|
||||
- 当前 schema
|
||||
|
||||
但 shader artifact 本质上是 `Library` 中的可重建中间产物,不属于必须长期 runtime 兼容的用户资产格式。
|
||||
|
||||
如果继续保留多代 schema 分支,会让 shader 资源链路长期背着历史包袱。
|
||||
|
||||
---
|
||||
|
||||
## 3. 本阶段设计原则
|
||||
|
||||
本计划执行时,必须严格遵守以下原则。
|
||||
|
||||
### 3.1 正式路径只能有一条
|
||||
|
||||
对 built-in shader / material / pass 来说,正式路径必须是:
|
||||
|
||||
`导入/authoring -> shader schema -> material instance -> explicit pass contract -> render pipeline`
|
||||
|
||||
不能继续允许 runtime 依赖旧命名、旧别名、旧格式去自动猜测。
|
||||
|
||||
### 3.2 兼容应尽量前移到导入/重建阶段,而不是留在 runtime
|
||||
|
||||
如果确实存在历史资产问题,应优先采用:
|
||||
|
||||
- 重新导入
|
||||
- 重新生成 artifact
|
||||
- 一次性迁移
|
||||
|
||||
而不是继续在 runtime loader / renderer 中保留长期兼容分支。
|
||||
|
||||
### 3.3 Built-in shader 契约必须显式写进 shader 资产
|
||||
|
||||
以下内容必须属于 shader/pass 资产本身,而不是 runtime 猜出来:
|
||||
|
||||
- pass 类型
|
||||
- pass metadata
|
||||
- resource binding
|
||||
- property semantic
|
||||
|
||||
### 3.4 Rendering 不再为“无正式 shader/schema 的材质”兜底渲染
|
||||
|
||||
当前阶段的目标是“收口”,不是“继续最大化兼容”。
|
||||
|
||||
因此:
|
||||
|
||||
- 非正式材质应尽快在导入层修正
|
||||
- runtime 应逐步拒绝无 schema 的正式绘制路径
|
||||
|
||||
### 3.5 每一步都必须可验证
|
||||
|
||||
每个阶段完成后必须配套:
|
||||
|
||||
- unit test
|
||||
- 必要的 integration test
|
||||
- editor 编译/回归
|
||||
|
||||
不能只凭画面“看起来没问题”判断完成。
|
||||
|
||||
---
|
||||
|
||||
## 4. 本阶段目标
|
||||
|
||||
本阶段完成后,Rendering 模块应达到以下状态:
|
||||
|
||||
1. Mesh 导入出来的材质直接走正式 shader/material 体系
|
||||
2. runtime 不再依赖 `baseColor` / `_MainTex` 等别名表去维持 built-in 主链
|
||||
3. built-in pass resource binding 由 shader 资产显式声明,不再依赖隐式硬编码补全
|
||||
4. built-in pass 分类必须显式声明,不再存在“默认 ForwardLit”
|
||||
5. shader artifact runtime loader 不再长期兼容多代旧 schema
|
||||
6. 对应测试体系同步升级,保证收口后功能不回退
|
||||
|
||||
---
|
||||
|
||||
## 5. 明确不在本阶段处理的内容
|
||||
|
||||
以下内容不属于本阶段目标:
|
||||
|
||||
- render graph
|
||||
- deferred renderer
|
||||
- 新一轮后处理功能扩展
|
||||
- C# SRP 脚本侧 API
|
||||
- ShaderGraph
|
||||
- 高级材质编辑器功能扩展
|
||||
|
||||
这些方向都依赖本阶段先把底层 contract 收紧。
|
||||
|
||||
---
|
||||
|
||||
## 6. 分阶段执行计划
|
||||
|
||||
## Phase 1:建立基线与目标测试
|
||||
|
||||
### 目标
|
||||
|
||||
先把当前遗留兼容路径的行为边界用测试钉住,并同步写出“目标行为”的新测试。
|
||||
|
||||
### 任务
|
||||
|
||||
- 审查并整理当前覆盖以下行为的测试:
|
||||
- `RenderMaterialResolve`
|
||||
- builtin forward pipeline resource binding
|
||||
- mesh material import
|
||||
- shader artifact load
|
||||
- 新增/调整测试,使其明确区分:
|
||||
- 当前历史兼容行为
|
||||
- 本阶段目标正式行为
|
||||
- 对以下目标先写失败测试或待切换测试:
|
||||
- imported mesh material 必须绑定正式 builtin shader
|
||||
- imported material property 必须落到正式 schema 名称
|
||||
- builtin pass 若无显式 metadata,不得进入主 pipeline
|
||||
- builtin shader 若无显式 resources,不得依赖隐式 binding 补全
|
||||
|
||||
### 验收标准
|
||||
|
||||
- 能清楚列出哪些测试在保护旧行为,哪些测试在保护目标行为
|
||||
- 后续每个阶段都能基于这些测试判断是否真正收口
|
||||
|
||||
---
|
||||
|
||||
## Phase 2:收口 Mesh 导入材质到正式 shader/material 路径
|
||||
|
||||
### 目标
|
||||
|
||||
彻底去掉 imported mesh material 的“无 shader / 裸属性名”旧路线。
|
||||
|
||||
### 任务
|
||||
|
||||
- 调整 `MeshLoader` 导入逻辑:
|
||||
- imported material 直接绑定正式 builtin shader
|
||||
- 默认按现有主线落到 builtin lit/forward 合同
|
||||
- 导入属性与纹理时,直接写正式 property name / texture slot:
|
||||
- 例如 `_BaseColor`
|
||||
- `_MainTex`
|
||||
- `_Cutoff`
|
||||
- 其他已正式声明的 builtin 属性
|
||||
- 不再向 imported material 写入仅靠 runtime 别名识别的裸字段:
|
||||
- `baseColor`
|
||||
- `baseColorTexture`
|
||||
- `color`
|
||||
- `texture`
|
||||
- 更新 mesh import 相关测试、render extractor 测试、相关 integration 资源测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
- mesh import 结果中的材质都带有正式 shader 引用
|
||||
- mesh import 结果中的属性/纹理绑定名称与 shader schema 对齐
|
||||
- 不再需要 runtime 靠旧别名才能让导入材质正常渲染
|
||||
|
||||
---
|
||||
|
||||
## Phase 3:移除 runtime builtin 材质语义别名与 fallback 常量路径
|
||||
|
||||
### 目标
|
||||
|
||||
让 built-in pipeline 只吃正式 schema 材质,不再继续兼容旧材质命名。
|
||||
|
||||
### 任务
|
||||
|
||||
- 清理 `RenderMaterialResolve.h` 中的旧别名解析表:
|
||||
- base color property alias
|
||||
- base texture alias
|
||||
- skybox texture alias
|
||||
- alpha cutoff alias
|
||||
- 保留并强化基于 `shader property semantic` 的正式解析路径
|
||||
- 移除 `FallbackPerMaterialConstants` 路线
|
||||
- 当材质未携带正式 schema constant layout 时:
|
||||
- 显式报错 / 记录诊断
|
||||
- 拒绝进入需要正式材质常量的绘制路径
|
||||
- 调整 forward / depth / shadow / skybox 相关单测
|
||||
|
||||
### 验收标准
|
||||
|
||||
- builtin pipeline 不再依赖属性别名表维持主链
|
||||
- builtin pipeline 不再手工构造 per-material fallback constant 继续绘制
|
||||
- runtime 只接受正式 shader/material 契约
|
||||
|
||||
---
|
||||
|
||||
## Phase 4:显式化 builtin pass resource binding contract
|
||||
|
||||
### 目标
|
||||
|
||||
让 builtin shader pass 的资源绑定契约完全存在于 shader 资产中,而不是藏在 runtime 硬编码里。
|
||||
|
||||
### 任务
|
||||
|
||||
- 为所有 builtin shader pass 补齐显式 `resources` 描述
|
||||
- 覆盖至少以下 shader:
|
||||
- `forward-lit.shader`
|
||||
- `depth-only.shader`
|
||||
- `shadow-caster.shader`
|
||||
- `object-id.shader`
|
||||
- `skybox.shader`
|
||||
- `final-color.shader`
|
||||
- 其他当前仍在主链中的 builtin shader
|
||||
- 清理 `TryBuildImplicitBuiltinPassResourceBindings`
|
||||
- 清理 `ShaderVariantUtils.h` 中围绕 implicit/legacy binding 的兼容逻辑:
|
||||
- legacy alias register rewrite
|
||||
- 依赖 `gXxx` 名称重写的分支
|
||||
- 调整 shader loader / rendering pipeline / builtin pass 单测
|
||||
|
||||
### 验收标准
|
||||
|
||||
- builtin shader pass 缺少显式资源绑定时,构建或运行应明确失败
|
||||
- runtime 不再替 shader 资产自动补 binding layout
|
||||
- HLSL runtime 编译不再依赖 legacy alias register 重写
|
||||
|
||||
---
|
||||
|
||||
## Phase 5:显式化 builtin pass metadata 与 pass 选择规则
|
||||
|
||||
### 目标
|
||||
|
||||
去掉“默认 ForwardLit”一类隐式 pass 归类规则。
|
||||
|
||||
### 任务
|
||||
|
||||
- 收紧 `BuiltinPassMetadataUtils`:
|
||||
- built-in pass 匹配必须依赖显式 pass name / tag
|
||||
- 删除“无 metadata 默认归 ForwardLit”的逻辑
|
||||
- 审查并统一 builtin shader 的 pass metadata:
|
||||
- `Name`
|
||||
- `LightMode`
|
||||
- 其它当前正式要求的 tag
|
||||
- 对进入 builtin 主线的 shader 建立硬约束:
|
||||
- 没有显式 builtin metadata 的 shader,不得继续被当作主几何 shader 使用
|
||||
- 更新 pass 匹配测试和 shader authoring 测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
- builtin pass 选择全部基于显式 metadata
|
||||
- 不存在 runtime 默认猜一个 pass 类型的行为
|
||||
|
||||
---
|
||||
|
||||
## Phase 6:清理旧 shader artifact schema 兼容
|
||||
|
||||
### 目标
|
||||
|
||||
让 shader artifact runtime loader 与 material artifact 一样,收口到 current schema。
|
||||
|
||||
### 任务
|
||||
|
||||
- 清理 `ShaderArtifactLoader.cpp` 中对旧 schema 的分支兼容:
|
||||
- `XCSHD01`
|
||||
- `XCSHD02`
|
||||
- `XCSHD03`
|
||||
- `XCSHD04`
|
||||
- 将旧 `Library` artifact 的处理方式改为:
|
||||
- 识别为过期
|
||||
- 触发重新导入 / 重新生成
|
||||
- 或直接报错要求重建 `Library`
|
||||
- 更新 asset database / shader load 相关测试
|
||||
- 明确记录此阶段会带来的影响:
|
||||
- 旧 `Library` 无法直接沿用
|
||||
- 需要一次性刷新或重建
|
||||
|
||||
### 验收标准
|
||||
|
||||
- shader artifact loader 只接受 current schema
|
||||
- 对旧 artifact 的处理边界清晰且可测试
|
||||
|
||||
---
|
||||
|
||||
## Phase 7:全量验证与阶段收口
|
||||
|
||||
### 目标
|
||||
|
||||
确认 Rendering 在去掉旧兼容层之后没有破坏现有功能。
|
||||
|
||||
### 任务
|
||||
|
||||
- 编译并运行:
|
||||
- `material_tests`
|
||||
- `rendering_unit_tests`
|
||||
- `asset_tests`
|
||||
- `editor_tests`
|
||||
- 受影响的 mesh/shader 资源测试
|
||||
- 重新编译 `XCEditor`
|
||||
- 重点回归:
|
||||
- scene viewport
|
||||
- game viewport
|
||||
- object-id picking
|
||||
- selection outline
|
||||
- skybox
|
||||
- 阴影
|
||||
- 多光源
|
||||
- backpack / sphere / quad 等 integration scene
|
||||
- 形成阶段收口报告
|
||||
|
||||
### 验收标准
|
||||
|
||||
- 所有直接相关测试通过
|
||||
- editor 编译通过
|
||||
- 关键 integration scene 渲染行为不回退
|
||||
- 能明确宣告 runtime 旧兼容路径已移除
|
||||
|
||||
---
|
||||
|
||||
## 7. 风险与注意事项
|
||||
|
||||
### 7.1 这是一次“切正式路径”的收口,不是小修小补
|
||||
|
||||
本计划一旦执行,就会主动删除一部分兼容逻辑。
|
||||
|
||||
因此不能以“尽量少改代码”为目标,而应以:
|
||||
|
||||
- 正式路径唯一
|
||||
- contract 清晰
|
||||
- 后续 SRP 可承接
|
||||
|
||||
为目标。
|
||||
|
||||
### 7.2 `Library` 重建属于预期影响
|
||||
|
||||
一旦收掉旧 shader artifact schema 兼容,旧 `Library` 里的 shader artifact 失效是正常现象。
|
||||
|
||||
这不应被视为回归,而应被视为阶段性收口的合理代价。
|
||||
|
||||
### 7.3 必须避免引入新的“临时兼容层”
|
||||
|
||||
执行过程中需要特别警惕以下错误做法:
|
||||
|
||||
- 新加一层 alias 表,试图“先兼容一下”
|
||||
- 把 runtime fallback 换个名字继续保留
|
||||
- 在 editor 或 import 层再次引入一套过渡数据模型
|
||||
|
||||
如果遇到结构性问题,正确做法是:
|
||||
|
||||
- 直接改到正式模型
|
||||
- 同步补测试
|
||||
|
||||
而不是再加一层短期兜底。
|
||||
|
||||
---
|
||||
|
||||
## 8. 建议执行顺序
|
||||
|
||||
建议严格按以下顺序推进:
|
||||
|
||||
1. `Phase 1` 测试基线整理
|
||||
2. `Phase 2` mesh 导入材质正式化
|
||||
3. `Phase 3` runtime 材质别名与 fallback 常量清理
|
||||
4. `Phase 4` builtin pass 显式资源绑定
|
||||
5. `Phase 5` builtin pass metadata 显式化
|
||||
6. `Phase 6` shader artifact schema 收口
|
||||
7. `Phase 7` 全量验证
|
||||
|
||||
原因是:
|
||||
|
||||
- 如果不先把 imported material 拉回正式路径
|
||||
- 后面的 runtime alias / fallback 清理就一定会打断现有资源链路
|
||||
|
||||
---
|
||||
|
||||
## 9. 本阶段完成后的预期状态
|
||||
|
||||
本计划完成后,Rendering 模块应达到以下状态:
|
||||
|
||||
1. built-in shader/material/pass contract 全部走正式显式路径
|
||||
2. runtime 不再依赖旧命名猜测材质语义
|
||||
3. runtime 不再替非正式材质拼接 fallback 常量布局
|
||||
4. builtin shader 资源绑定契约完全由 shader 资产声明
|
||||
5. builtin pass 类型选择完全依赖显式 metadata
|
||||
6. shader artifact runtime loader 不再背负旧 schema 包袱
|
||||
7. 整个 Rendering 模块更适合作为后续 Unity 风格 SRP 的底层承接
|
||||
|
||||
---
|
||||
|
||||
## 10. 一句话总结
|
||||
|
||||
当前 Rendering 真正需要的不是继续加功能,而是把残留的旧兼容路径彻底拔干净。
|
||||
|
||||
这一阶段的本质,是把:
|
||||
|
||||
- imported material
|
||||
- built-in shader binding
|
||||
- pass metadata
|
||||
- shader artifact
|
||||
|
||||
全部拉回到同一套正式 contract 上,为后续 Renderer / Material / Shader / SRP 的继续推进打地基。
|
||||
@@ -409,11 +409,11 @@ MeshBuffers CreateUvSphereMeshBuffers() {
|
||||
const Core::uint32 i3 = i2 + 1;
|
||||
|
||||
buffers.indices.push_back(i0);
|
||||
buffers.indices.push_back(i2);
|
||||
buffers.indices.push_back(i1);
|
||||
buffers.indices.push_back(i1);
|
||||
buffers.indices.push_back(i2);
|
||||
buffers.indices.push_back(i1);
|
||||
buffers.indices.push_back(i3);
|
||||
buffers.indices.push_back(i2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -731,7 +731,7 @@ Material* BuildDefaultPrimitiveMaterial(const Containers::String& path) {
|
||||
material->SetRenderQueue(MaterialRenderQueue::Geometry);
|
||||
material->SetShader(ResourceManager::Get().Load<Shader>(GetBuiltinForwardLitShaderPath()));
|
||||
material->SetTexture(
|
||||
Containers::String("baseColorTexture"),
|
||||
Containers::String("_MainTex"),
|
||||
ResourceManager::Get().Load<Texture>(GetBuiltinDefaultPrimitiveTexturePath()));
|
||||
material->RecalculateMemorySize();
|
||||
return material;
|
||||
|
||||
@@ -402,59 +402,35 @@ bool HasMaterialTexture(
|
||||
return false;
|
||||
}
|
||||
|
||||
void ImportMaterialProperties(const aiMaterial& assimpMaterial, Material& material) {
|
||||
Math::Vector4 ResolveImportedBaseColor(const aiMaterial& assimpMaterial, bool hasBaseColorTexture) {
|
||||
float opacity = 1.0f;
|
||||
if (assimpMaterial.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) {
|
||||
material.SetFloat("opacity", opacity);
|
||||
}
|
||||
|
||||
const bool hasBaseColorTexture =
|
||||
HasMaterialTexture(assimpMaterial, { aiTextureType_BASE_COLOR, aiTextureType_DIFFUSE });
|
||||
assimpMaterial.Get(AI_MATKEY_OPACITY, opacity);
|
||||
|
||||
aiColor4D baseColor;
|
||||
if (assimpMaterial.Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, baseColor) == AI_SUCCESS) {
|
||||
material.SetFloat4("baseColor",
|
||||
Math::Vector4(baseColor.r, baseColor.g, baseColor.b, baseColor.a));
|
||||
} else {
|
||||
aiColor3D diffuseColor;
|
||||
if (assimpMaterial.Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor) == AI_SUCCESS) {
|
||||
material.SetFloat4("baseColor",
|
||||
hasBaseColorTexture
|
||||
? Math::Vector4(1.0f, 1.0f, 1.0f, opacity)
|
||||
: Math::Vector4(diffuseColor.r, diffuseColor.g, diffuseColor.b, opacity));
|
||||
}
|
||||
return Math::Vector4(baseColor.r, baseColor.g, baseColor.b, baseColor.a);
|
||||
}
|
||||
|
||||
aiColor3D emissiveColor;
|
||||
if (assimpMaterial.Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColor) == AI_SUCCESS) {
|
||||
material.SetFloat3("emissiveColor",
|
||||
Math::Vector3(emissiveColor.r, emissiveColor.g, emissiveColor.b));
|
||||
aiColor3D diffuseColor;
|
||||
if (assimpMaterial.Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor) == AI_SUCCESS) {
|
||||
return hasBaseColorTexture
|
||||
? Math::Vector4(1.0f, 1.0f, 1.0f, opacity)
|
||||
: Math::Vector4(diffuseColor.r, diffuseColor.g, diffuseColor.b, opacity);
|
||||
}
|
||||
|
||||
aiColor3D specularColor;
|
||||
if (assimpMaterial.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS) {
|
||||
material.SetFloat3("specularColor",
|
||||
Math::Vector3(specularColor.r, specularColor.g, specularColor.b));
|
||||
}
|
||||
return Math::Vector4(1.0f, 1.0f, 1.0f, opacity);
|
||||
}
|
||||
|
||||
float metallic = 0.0f;
|
||||
if (assimpMaterial.Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, metallic) == AI_SUCCESS) {
|
||||
material.SetFloat("metallic", metallic);
|
||||
}
|
||||
|
||||
float roughness = 0.0f;
|
||||
if (assimpMaterial.Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, roughness) == AI_SUCCESS) {
|
||||
material.SetFloat("roughness", roughness);
|
||||
}
|
||||
|
||||
float shininess = 0.0f;
|
||||
if (assimpMaterial.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) {
|
||||
material.SetFloat("shininess", shininess);
|
||||
}
|
||||
void ImportMaterialProperties(const aiMaterial& assimpMaterial, Material& material) {
|
||||
const bool hasBaseColorTexture =
|
||||
HasMaterialTexture(assimpMaterial, { aiTextureType_BASE_COLOR, aiTextureType_DIFFUSE });
|
||||
material.SetFloat4("_BaseColor", ResolveImportedBaseColor(assimpMaterial, hasBaseColorTexture));
|
||||
|
||||
int twoSided = 0;
|
||||
if (assimpMaterial.Get(AI_MATKEY_TWOSIDED, twoSided) == AI_SUCCESS) {
|
||||
material.SetBool("twoSided", twoSided != 0);
|
||||
if (assimpMaterial.Get(AI_MATKEY_TWOSIDED, twoSided) == AI_SUCCESS && twoSided != 0) {
|
||||
MaterialRenderState renderState = material.GetRenderState();
|
||||
renderState.cullMode = MaterialCullMode::None;
|
||||
material.SetRenderState(renderState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,14 +446,7 @@ void ImportMaterialTextures(const aiMaterial& assimpMaterial,
|
||||
}
|
||||
};
|
||||
|
||||
assignTexture("baseColorTexture", { aiTextureType_BASE_COLOR, aiTextureType_DIFFUSE });
|
||||
assignTexture("normalTexture", { aiTextureType_NORMAL_CAMERA, aiTextureType_NORMALS, aiTextureType_HEIGHT });
|
||||
assignTexture("specularTexture", { aiTextureType_SPECULAR });
|
||||
assignTexture("emissiveTexture", { aiTextureType_EMISSION_COLOR, aiTextureType_EMISSIVE });
|
||||
assignTexture("metallicTexture", { aiTextureType_METALNESS });
|
||||
assignTexture("roughnessTexture", { aiTextureType_DIFFUSE_ROUGHNESS });
|
||||
assignTexture("occlusionTexture", { aiTextureType_AMBIENT_OCCLUSION, aiTextureType_LIGHTMAP });
|
||||
assignTexture("opacityTexture", { aiTextureType_OPACITY });
|
||||
assignTexture("_MainTex", { aiTextureType_BASE_COLOR, aiTextureType_DIFFUSE });
|
||||
}
|
||||
|
||||
Material* ImportSingleMaterial(const aiMaterial& assimpMaterial,
|
||||
@@ -500,8 +469,10 @@ Material* ImportSingleMaterial(const aiMaterial& assimpMaterial,
|
||||
params.memorySize = materialName.length() + params.path.Length();
|
||||
material->Initialize(params);
|
||||
|
||||
material->SetShader(ResourceManager::Get().Load<Shader>(GetBuiltinForwardLitShaderPath()));
|
||||
ImportMaterialProperties(assimpMaterial, *material);
|
||||
ImportMaterialTextures(assimpMaterial, *material, context);
|
||||
material->RecalculateMemorySize();
|
||||
return material;
|
||||
}
|
||||
|
||||
|
||||
@@ -620,7 +620,7 @@ void BackpackTest::InitializeMaterialResources() {
|
||||
|
||||
MaterialResources& resources = mMaterialResources[materialIndex];
|
||||
|
||||
Texture* baseColorTexture = GetMaterialTexture(material, "baseColorTexture");
|
||||
Texture* baseColorTexture = GetMaterialTexture(material, "_MainTex");
|
||||
if (baseColorTexture != nullptr) {
|
||||
const bool created =
|
||||
CreateTextureViewFromResourceTexture(
|
||||
|
||||
File diff suppressed because one or more lines are too long
2961
tests/Rendering/integration/backpack_scene/GT.ppm
Normal file
2961
tests/Rendering/integration/backpack_scene/GT.ppm
Normal file
File diff suppressed because one or more lines are too long
@@ -1,7 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <XCEngine/Core/Asset/AssetDatabase.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
||||
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
||||
@@ -25,16 +24,6 @@ std::string GetMeshFixturePath(const char* fileName) {
|
||||
return (std::filesystem::path(XCENGINE_TEST_FIXTURES_DIR) / "Resources" / "Mesh" / fileName).string();
|
||||
}
|
||||
|
||||
XCEngine::Core::uint32 ReadMeshIndex(const Mesh& mesh, XCEngine::Core::uint32 index) {
|
||||
if (mesh.IsUse32BitIndex()) {
|
||||
const auto* indices = static_cast<const XCEngine::Core::uint32*>(mesh.GetIndexData());
|
||||
return indices[index];
|
||||
}
|
||||
|
||||
const auto* indices = static_cast<const XCEngine::Core::uint16*>(mesh.GetIndexData());
|
||||
return static_cast<XCEngine::Core::uint32>(indices[index]);
|
||||
}
|
||||
|
||||
XCEngine::Core::uint32 GetFirstSectionMaterialIndex(const Mesh& mesh) {
|
||||
if (mesh.GetSections().Empty()) {
|
||||
return 0;
|
||||
@@ -154,41 +143,6 @@ TEST(MeshLoader, LoadValidObjMesh) {
|
||||
delete mesh;
|
||||
}
|
||||
|
||||
TEST(MeshLoader, BuiltinSphereUsesFrontFacingWindingForOutwardNormals) {
|
||||
LoadResult result = CreateBuiltinMeshResource(GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Sphere));
|
||||
ASSERT_TRUE(result);
|
||||
ASSERT_NE(result.resource, nullptr);
|
||||
|
||||
auto* mesh = static_cast<Mesh*>(result.resource);
|
||||
const auto* vertices = static_cast<const StaticMeshVertex*>(mesh->GetVertexData());
|
||||
ASSERT_NE(vertices, nullptr);
|
||||
ASSERT_GE(mesh->GetIndexCount(), 3u);
|
||||
|
||||
bool foundNonDegenerateTriangle = false;
|
||||
for (XCEngine::Core::uint32 index = 0; index + 2 < mesh->GetIndexCount(); index += 3) {
|
||||
const XCEngine::Core::uint32 i0 = ReadMeshIndex(*mesh, index + 0);
|
||||
const XCEngine::Core::uint32 i1 = ReadMeshIndex(*mesh, index + 1);
|
||||
const XCEngine::Core::uint32 i2 = ReadMeshIndex(*mesh, index + 2);
|
||||
|
||||
const XCEngine::Math::Vector3 edge01 = vertices[i1].position - vertices[i0].position;
|
||||
const XCEngine::Math::Vector3 edge02 = vertices[i2].position - vertices[i0].position;
|
||||
const XCEngine::Math::Vector3 geometricNormal =
|
||||
XCEngine::Math::Vector3::Cross(edge01, edge02);
|
||||
if (geometricNormal.SqrMagnitude() <= XCEngine::Math::EPSILON) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const XCEngine::Math::Vector3 averagedVertexNormal =
|
||||
(vertices[i0].normal + vertices[i1].normal + vertices[i2].normal).Normalized();
|
||||
EXPECT_LT(XCEngine::Math::Vector3::Dot(geometricNormal, averagedVertexNormal), 0.0f);
|
||||
foundNonDegenerateTriangle = true;
|
||||
break;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(foundNonDegenerateTriangle);
|
||||
delete mesh;
|
||||
}
|
||||
|
||||
TEST(MeshLoader, GeneratesNormalsAndTangentsWhenRequested) {
|
||||
MeshLoader loader;
|
||||
MeshImportSettings settings;
|
||||
@@ -229,10 +183,15 @@ TEST(MeshLoader, ImportsMaterialTexturesFromObj) {
|
||||
|
||||
Material* material = mesh->GetMaterial(mesh->GetSections()[0].materialID);
|
||||
ASSERT_NE(material, nullptr);
|
||||
EXPECT_TRUE(material->HasProperty("baseColorTexture"));
|
||||
ASSERT_NE(material->GetShader(), nullptr);
|
||||
EXPECT_EQ(material->GetShader()->GetPath(), GetBuiltinForwardLitShaderPath());
|
||||
EXPECT_TRUE(material->HasProperty("_BaseColor"));
|
||||
EXPECT_TRUE(material->HasProperty("_MainTex"));
|
||||
EXPECT_FALSE(material->HasProperty("baseColorTexture"));
|
||||
EXPECT_EQ(material->GetTextureBindingCount(), 1u);
|
||||
EXPECT_EQ(material->GetTextureBindingName(0), "_MainTex");
|
||||
|
||||
ResourceHandle<Texture> diffuseTexture = material->GetTexture("baseColorTexture");
|
||||
ResourceHandle<Texture> diffuseTexture = material->GetTexture("_MainTex");
|
||||
ASSERT_TRUE(diffuseTexture.IsValid());
|
||||
EXPECT_EQ(diffuseTexture->GetWidth(), 2u);
|
||||
EXPECT_EQ(diffuseTexture->GetHeight(), 2u);
|
||||
@@ -276,8 +235,10 @@ TEST(MeshLoader, AssetDatabaseCreatesModelArtifactAndReusesItWithoutReimport) {
|
||||
Material* sourceSectionMaterial = GetFirstSectionMaterial(*sourceMesh);
|
||||
ASSERT_NE(sourceSectionMaterial, nullptr);
|
||||
const XCEngine::Core::uint32 sourceMaterialIndex = GetFirstSectionMaterialIndex(*sourceMesh);
|
||||
ASSERT_NE(sourceSectionMaterial->GetShader(), nullptr);
|
||||
EXPECT_EQ(sourceSectionMaterial->GetShader()->GetPath(), GetBuiltinForwardLitShaderPath());
|
||||
EXPECT_EQ(sourceSectionMaterial->GetTextureBindingCount(), 1u);
|
||||
EXPECT_EQ(sourceSectionMaterial->GetTextureBindingName(0), "baseColorTexture");
|
||||
EXPECT_EQ(sourceSectionMaterial->GetTextureBindingName(0), "_MainTex");
|
||||
EXPECT_FALSE(sourceSectionMaterial->GetTextureBindingPath(0).Empty());
|
||||
delete sourceMesh;
|
||||
|
||||
@@ -304,14 +265,16 @@ TEST(MeshLoader, AssetDatabaseCreatesModelArtifactAndReusesItWithoutReimport) {
|
||||
ASSERT_NE(materialArtifactResult.resource, nullptr);
|
||||
auto* artifactMaterial = static_cast<Material*>(materialArtifactResult.resource);
|
||||
ASSERT_NE(artifactMaterial, nullptr);
|
||||
ASSERT_NE(artifactMaterial->GetShader(), nullptr);
|
||||
EXPECT_EQ(artifactMaterial->GetShader()->GetPath(), GetBuiltinForwardLitShaderPath());
|
||||
EXPECT_EQ(artifactMaterial->GetTextureBindingCount(), 1u);
|
||||
EXPECT_EQ(artifactMaterial->GetTextureBindingName(0), "baseColorTexture");
|
||||
EXPECT_EQ(artifactMaterial->GetTextureBindingName(0), "_MainTex");
|
||||
EXPECT_FALSE(artifactMaterial->GetTextureBindingPath(0).Empty());
|
||||
const ResourceHandle<Texture> artifactLazyTexture = artifactMaterial->GetTexture("baseColorTexture");
|
||||
const ResourceHandle<Texture> artifactLazyTexture = artifactMaterial->GetTexture("_MainTex");
|
||||
EXPECT_FALSE(artifactLazyTexture.IsValid());
|
||||
EXPECT_GT(manager.GetAsyncPendingCount(), 0u);
|
||||
ASSERT_TRUE(PumpAsyncLoadsUntilIdle(manager));
|
||||
const ResourceHandle<Texture> artifactResolvedTexture = artifactMaterial->GetTexture("baseColorTexture");
|
||||
const ResourceHandle<Texture> artifactResolvedTexture = artifactMaterial->GetTexture("_MainTex");
|
||||
ASSERT_TRUE(artifactResolvedTexture.IsValid());
|
||||
EXPECT_EQ(artifactResolvedTexture->GetWidth(), 2u);
|
||||
EXPECT_EQ(artifactResolvedTexture->GetHeight(), 2u);
|
||||
@@ -326,15 +289,17 @@ TEST(MeshLoader, AssetDatabaseCreatesModelArtifactAndReusesItWithoutReimport) {
|
||||
ASSERT_GE(artifactMesh->GetMaterials().Size(), 1u);
|
||||
Material* artifactSectionMaterial = GetFirstSectionMaterial(*artifactMesh);
|
||||
ASSERT_NE(artifactSectionMaterial, nullptr);
|
||||
ASSERT_NE(artifactSectionMaterial->GetShader(), nullptr);
|
||||
EXPECT_EQ(artifactSectionMaterial->GetShader()->GetPath(), GetBuiltinForwardLitShaderPath());
|
||||
EXPECT_EQ(artifactSectionMaterial->GetTextureBindingCount(), 1u);
|
||||
EXPECT_EQ(artifactSectionMaterial->GetTextureBindingName(0), "baseColorTexture");
|
||||
EXPECT_EQ(artifactSectionMaterial->GetTextureBindingName(0), "_MainTex");
|
||||
EXPECT_FALSE(artifactSectionMaterial->GetTextureBindingPath(0).Empty());
|
||||
const ResourceHandle<Texture> artifactMeshLazyTexture = artifactSectionMaterial->GetTexture("baseColorTexture");
|
||||
const ResourceHandle<Texture> artifactMeshLazyTexture = artifactSectionMaterial->GetTexture("_MainTex");
|
||||
EXPECT_FALSE(artifactMeshLazyTexture.IsValid());
|
||||
EXPECT_GT(manager.GetAsyncPendingCount(), 0u);
|
||||
ASSERT_TRUE(PumpAsyncLoadsUntilIdle(manager));
|
||||
const ResourceHandle<Texture> artifactMeshResolvedTexture =
|
||||
artifactSectionMaterial->GetTexture("baseColorTexture");
|
||||
artifactSectionMaterial->GetTexture("_MainTex");
|
||||
ASSERT_TRUE(artifactMeshResolvedTexture.IsValid());
|
||||
EXPECT_EQ(artifactMeshResolvedTexture->GetWidth(), 2u);
|
||||
EXPECT_EQ(artifactMeshResolvedTexture->GetHeight(), 2u);
|
||||
@@ -449,14 +414,16 @@ TEST(MeshLoader, ResourceManagerLoadsModelByAssetRefFromProjectAssets) {
|
||||
EXPECT_LT(firstSectionMaterialIndex, initialMaterialCount);
|
||||
Material* firstMaterial = GetFirstSectionMaterial(*firstHandle.Get());
|
||||
ASSERT_NE(firstMaterial, nullptr);
|
||||
ASSERT_NE(firstMaterial->GetShader(), nullptr);
|
||||
EXPECT_EQ(firstMaterial->GetShader()->GetPath(), GetBuiltinForwardLitShaderPath());
|
||||
EXPECT_EQ(firstMaterial->GetTextureBindingCount(), 1u);
|
||||
EXPECT_EQ(firstMaterial->GetTextureBindingName(0), "baseColorTexture");
|
||||
EXPECT_EQ(firstMaterial->GetTextureBindingName(0), "_MainTex");
|
||||
EXPECT_FALSE(firstMaterial->GetTextureBindingPath(0).Empty());
|
||||
const ResourceHandle<Texture> firstLazyTexture = firstMaterial->GetTexture("baseColorTexture");
|
||||
const ResourceHandle<Texture> firstLazyTexture = firstMaterial->GetTexture("_MainTex");
|
||||
EXPECT_FALSE(firstLazyTexture.IsValid());
|
||||
EXPECT_GT(manager.GetAsyncPendingCount(), 0u);
|
||||
ASSERT_TRUE(PumpAsyncLoadsUntilIdle(manager));
|
||||
const ResourceHandle<Texture> firstResolvedTexture = firstMaterial->GetTexture("baseColorTexture");
|
||||
const ResourceHandle<Texture> firstResolvedTexture = firstMaterial->GetTexture("_MainTex");
|
||||
ASSERT_TRUE(firstResolvedTexture.IsValid());
|
||||
EXPECT_EQ(firstResolvedTexture->GetWidth(), 2u);
|
||||
EXPECT_EQ(firstResolvedTexture->GetHeight(), 2u);
|
||||
@@ -477,14 +444,16 @@ TEST(MeshLoader, ResourceManagerLoadsModelByAssetRefFromProjectAssets) {
|
||||
EXPECT_EQ(GetFirstSectionMaterialIndex(*secondHandle.Get()), firstSectionMaterialIndex);
|
||||
Material* secondMaterial = GetFirstSectionMaterial(*secondHandle.Get());
|
||||
ASSERT_NE(secondMaterial, nullptr);
|
||||
ASSERT_NE(secondMaterial->GetShader(), nullptr);
|
||||
EXPECT_EQ(secondMaterial->GetShader()->GetPath(), GetBuiltinForwardLitShaderPath());
|
||||
EXPECT_EQ(secondMaterial->GetTextureBindingCount(), 1u);
|
||||
EXPECT_EQ(secondMaterial->GetTextureBindingName(0), "baseColorTexture");
|
||||
EXPECT_EQ(secondMaterial->GetTextureBindingName(0), "_MainTex");
|
||||
EXPECT_FALSE(secondMaterial->GetTextureBindingPath(0).Empty());
|
||||
const ResourceHandle<Texture> secondLazyTexture = secondMaterial->GetTexture("baseColorTexture");
|
||||
const ResourceHandle<Texture> secondLazyTexture = secondMaterial->GetTexture("_MainTex");
|
||||
EXPECT_FALSE(secondLazyTexture.IsValid());
|
||||
EXPECT_GT(manager.GetAsyncPendingCount(), 0u);
|
||||
ASSERT_TRUE(PumpAsyncLoadsUntilIdle(manager));
|
||||
const ResourceHandle<Texture> secondResolvedTexture = secondMaterial->GetTexture("baseColorTexture");
|
||||
const ResourceHandle<Texture> secondResolvedTexture = secondMaterial->GetTexture("_MainTex");
|
||||
ASSERT_TRUE(secondResolvedTexture.IsValid());
|
||||
EXPECT_EQ(secondResolvedTexture->GetWidth(), 2u);
|
||||
EXPECT_EQ(secondResolvedTexture->GetHeight(), 2u);
|
||||
|
||||
Reference in New Issue
Block a user