milestone: remove Panda3D backend and stabilize JSON asset pipeline
This commit is contained in:
parent
28919737fc
commit
7d5887bbbb
5
.gitignore
vendored
5
.gitignore
vendored
@ -5,6 +5,11 @@ downloads/
|
||||
.vs/
|
||||
CMakeUserPresets.json
|
||||
imgui.ini
|
||||
SandboxProject/Library/
|
||||
TestProject/Library/
|
||||
**/*.mccooked
|
||||
*.mcasset.mcmeta
|
||||
*.mcmeta.mcmeta
|
||||
*.obj
|
||||
*.pdb
|
||||
*.ilk
|
||||
|
||||
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(MetaCore
|
||||
VERSION 1.0.0
|
||||
DESCRIPTION "MetaCore Panda3D-based Unity-like editor prototype"
|
||||
DESCRIPTION "MetaCore Filament-based Unity-like editor prototype"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
@ -16,7 +16,7 @@ if(MSVC)
|
||||
endif()
|
||||
|
||||
option(METACORE_BUILD_TESTS "Build MetaCore tests" ON)
|
||||
option(METACORE_AUTO_PREPARE_PANDA3D "Automatically prepare Panda3D SDK during configure" ON)
|
||||
|
||||
|
||||
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
@ -24,8 +24,7 @@ if(EXISTS "${CMAKE_SOURCE_DIR}/vcpkg_installed/x64-windows/share/imgui/imgui-con
|
||||
list(PREPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/vcpkg_installed/x64-windows")
|
||||
endif()
|
||||
|
||||
include(MetaCorePanda3D)
|
||||
metacore_prepare_panda3d()
|
||||
|
||||
|
||||
include(MetaCoreFilament)
|
||||
|
||||
@ -160,7 +159,6 @@ target_include_directories(MetaCorePlatform
|
||||
target_link_libraries(MetaCorePlatform
|
||||
PUBLIC
|
||||
MetaCoreFoundation
|
||||
MetaCorePanda3D::SDK
|
||||
glm::glm
|
||||
)
|
||||
|
||||
@ -243,7 +241,6 @@ target_link_libraries(MetaCoreRender
|
||||
MetaCoreFoundation
|
||||
MetaCorePlatform
|
||||
MetaCoreScene
|
||||
MetaCorePanda3D::SDK
|
||||
glm::glm
|
||||
imgui::imgui
|
||||
)
|
||||
@ -387,8 +384,7 @@ target_link_libraries(MetaCoreEditorApp
|
||||
MetaCoreEditor
|
||||
)
|
||||
|
||||
metacore_stage_panda3d_runtime(MetaCoreEditorApp)
|
||||
metacore_stage_simplepbr_runtime(MetaCoreEditorApp)
|
||||
|
||||
|
||||
add_executable(MetaCorePlayer
|
||||
Apps/MetaCorePlayer/main.cpp
|
||||
@ -401,11 +397,9 @@ target_link_libraries(MetaCorePlayer
|
||||
MetaCoreRender
|
||||
MetaCoreRuntimeData
|
||||
MetaCoreScene
|
||||
MetaCorePanda3D::SDK
|
||||
)
|
||||
|
||||
metacore_stage_panda3d_runtime(MetaCorePlayer)
|
||||
metacore_stage_simplepbr_runtime(MetaCorePlayer)
|
||||
|
||||
|
||||
if(METACORE_BUILD_TESTS)
|
||||
enable_testing()
|
||||
@ -421,8 +415,7 @@ if(METACORE_BUILD_TESTS)
|
||||
)
|
||||
target_compile_options(MetaCoreSmokeTests PRIVATE ${METACORE_COMMON_WARNINGS})
|
||||
|
||||
metacore_stage_panda3d_runtime(MetaCoreSmokeTests)
|
||||
metacore_stage_simplepbr_runtime(MetaCoreSmokeTests)
|
||||
|
||||
add_test(NAME MetaCoreSmokeTests COMMAND MetaCoreSmokeTests)
|
||||
|
||||
# Filament + ImGui 离屏渲染 Demo
|
||||
|
||||
124
README.md
124
README.md
@ -1,70 +1,98 @@
|
||||
# MetaCore
|
||||
|
||||
`MetaCore` 是一个基于 `Panda3D` 进行二次开发的 Unity-like 编辑器与引擎框架原型。
|
||||
MetaCore 是一个使用 `C++20` 开发的自主 3D 引擎与编辑器原型。项目以 **EnTT ECS** 为底层架构,使用 **Google Filament** 作为现代渲染核心,使用 **Dear ImGui** 构建编辑器界面,并规划使用 **RmlUi** 承载运行时 UI。目标是在轻量、可控、可国产化适配的基础上,形成类似 Unity / Unreal Engine 的可视化 3D 应用开发工作流。
|
||||
|
||||
## 当前目标
|
||||
当前阶段的核心策略是:**编辑期优先 JSON 与可读元数据,快速迭代、便于调试和版本管理;发布期再通过 Cook / Package 转换为二进制运行时资产,保证交付性能和封装性。**
|
||||
|
||||
- 以 `Panda3D` 作为底层引擎与运行时基础
|
||||
- 以 `Dear ImGui Docking` 搭建 Unity-like 编辑器工作流
|
||||
- 使用 `GameObject + Component` 模型组织编辑器语义层
|
||||
- 先把 `Hierarchy / Scene / Inspector / Project / Console` 跑通
|
||||
- 在不破坏主架构的前提下,以模块化方式持续叠加功能
|
||||
## 当前定位
|
||||
|
||||
- **底层架构**:基于 `EnTT` 的 ECS,外层提供 `GameObject + Component` 的编辑器友好语义。
|
||||
- **编辑器界面**:使用 `Dear ImGui Docking` 构建 Hierarchy、Scene、Inspector、Project、Console 等编辑器面板。
|
||||
- **渲染核心**:以 `Google Filament` 为主渲染后端,面向 PBR、glTF/GLB、材质、灯光和现代多图形 API 能力。
|
||||
- **运行时 UI**:规划使用 `RmlUi` 作为正式项目 UI 系统,定位类似 Unity UGUI;ImGui 只作为编辑器和工具 UI。
|
||||
- **模型导入**:当前优先围绕 `glTF/GLB` 与 `gltfio` 建立模型、材质、贴图、节点层级导入闭环。
|
||||
- **资产路线**:编辑器阶段使用 JSON / `.mcmeta` / 可读项目文件;打包阶段生成 cooked 二进制资产与运行时配置。
|
||||
- **交付方向**:编辑器面向 Windows、Linux、统信 UOS、麒麟等桌面系统;运行时面向 C/S 桌面交付,并为 B/S / WebGPU 嵌入预留边界。
|
||||
- **行业对标**:工作流对标 Unity,渲染和工程化目标参考 Unreal Engine,但第一阶段更聚焦工业仿真、数字孪生和实时可视化交付。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- `C++20`
|
||||
- `CMake`
|
||||
- `vcpkg`
|
||||
- `Panda3D 1.10.16`
|
||||
- `glm`
|
||||
- `Dear ImGui Docking`
|
||||
- **核心语言**:`C++20`
|
||||
- **构建系统**:`CMake`
|
||||
- **包管理**:`vcpkg`
|
||||
- **ECS**:`EnTT`
|
||||
- **渲染核心**:`Google Filament`
|
||||
- **模型导入**:`gltfio` / `glTF` / `GLB`
|
||||
- **编辑器 UI**:`Dear ImGui (Docking)`
|
||||
- **运行时 UI**:`RmlUi`(规划主线)
|
||||
- **3D 操控**:`ImGuizmo`
|
||||
- **数学库**:`glm`
|
||||
- **数据通信**:`Winsock2`、文件回放、运行时数据绑定
|
||||
|
||||
## 渲染目标
|
||||
|
||||
MetaCore 当前不从零自研完整 RHI,而是借助 Filament 的成熟后端能力逐步覆盖多平台渲染:
|
||||
|
||||
- `Vulkan`:首选高性能现代图形 API。
|
||||
- `OpenGL`:用于兼容、过渡和更广泛设备适配。
|
||||
- `DirectX 12`:面向 Windows 高性能运行时。
|
||||
- `WebGPU`:面向未来 B/S 架构、浏览器嵌入和 Web 交付。
|
||||
|
||||
## 目录结构
|
||||
|
||||
- `Source/MetaCoreFoundation`:基础服务
|
||||
- `Source/MetaCorePlatform`:Panda3D 宿主窗口与输入采集
|
||||
- `Source/MetaCoreScene`:场景与对象模型
|
||||
- `Source/MetaCoreRender`:Panda3D 场景桥与显示区域驱动
|
||||
- `Source/MetaCoreEditor`:编辑器模块与 Unity-like 界面
|
||||
- `Apps/MetaCoreEditor`:编辑器程序入口
|
||||
- `Apps/MetaCorePlayer`:运行时壳层入口
|
||||
- `tests`:基础烟雾测试
|
||||
- [Source/MetaCoreFoundation](D:/MetaCore/Source/MetaCoreFoundation):GUID、项目文件、包、反射、日志等基础服务。
|
||||
- [Source/MetaCorePlatform](D:/MetaCore/Source/MetaCorePlatform):平台窗口、输入与宿主环境抽象。
|
||||
- [Source/MetaCoreScene](D:/MetaCore/Source/MetaCoreScene):EnTT ECS、GameObject、Component、场景树、快照和序列化。
|
||||
- [Source/MetaCoreRender](D:/MetaCore/Source/MetaCoreRender):Filament 渲染桥、编辑器视口渲染、ImGui/渲染纹理衔接。
|
||||
- [Source/MetaCoreRuntimeData](D:/MetaCore/Source/MetaCoreRuntimeData):TCP、文件回放、数据点、绑定、诊断与实时数据驱动。
|
||||
- [Source/MetaCoreEditor](D:/MetaCore/Source/MetaCoreEditor):编辑器上下文、模块系统、面板、资产导入、Inspector、Prefab、Undo/Redo。
|
||||
- [Apps/MetaCoreEditor](D:/MetaCore/Apps/MetaCoreEditor):编辑器独立程序入口。
|
||||
- [Apps/MetaCorePlayer](D:/MetaCore/Apps/MetaCorePlayer):运行时 Player 程序入口。
|
||||
- [docs/designs](D:/MetaCore/docs/designs):产品定位、阶段路线、引擎能力、资产、渲染、UI 和打包设计文档。
|
||||
- [tests](D:/MetaCore/tests):Smoke Tests 与渲染 Demo。
|
||||
|
||||
## 构建
|
||||
## 构建与验证
|
||||
|
||||
前提:
|
||||
### 前提条件
|
||||
|
||||
- 已安装 `vcpkg`
|
||||
- 已设置环境变量 `VCPKG_ROOT`
|
||||
- Windows 环境可联网时,`cmake configure` 会自动准备 `Panda3D 1.10.16` SDK
|
||||
- 如果已经本地安装 Panda3D,可设置环境变量 `PANDA3D_ROOT`
|
||||
1. 已安装 Visual Studio 2022 / MSVC 工具链。
|
||||
2. 已安装 `vcpkg` 并正确设置 `VCPKG_ROOT`。
|
||||
3. `third_party/filament_installed` 下存在已编译好的 Google Filament SDK,或根据 [cmake/MetaCoreFilament.cmake](D:/MetaCore/cmake/MetaCoreFilament.cmake) 配置对应路径。
|
||||
|
||||
### 构建步骤
|
||||
|
||||
```powershell
|
||||
cmake --preset vs2022-debug
|
||||
cmake --build --preset build-debug
|
||||
ctest --preset test-debug
|
||||
cmake -B build -G "Visual Studio 17 2022" -A x64
|
||||
cmake --build build --config RelWithDebInfo --target MetaCoreEditorApp
|
||||
cmake --build build --config RelWithDebInfo --target MetaCoreSmokeTests
|
||||
.\build\RelWithDebInfo\MetaCoreSmokeTests.exe
|
||||
```
|
||||
|
||||
如果希望生成更适合分发的可执行文件,可使用:
|
||||
## 当前能力
|
||||
|
||||
```powershell
|
||||
cmake --preset vs2022-release
|
||||
cmake --build --preset build-release
|
||||
```
|
||||
- 原生窗口、输入和主循环。
|
||||
- 基于 EnTT 的场景对象、组件、层级、快照和序列化。
|
||||
- Dear ImGui Docking 编辑器框架。
|
||||
- Filament 离屏视口桥接与 PBR 场景渲染。
|
||||
- glTF/GLB 模型导入、节点层级同步和模型实例化。
|
||||
- ImGuizmo 移动、旋转、缩放工具链。
|
||||
- 资产 GUID、`.mcmeta`、AssetDatabase、导入与重导入基础链路。
|
||||
- 编辑期 JSON 场景/资产元数据方向,运行期 Cook / Package 二进制方向。
|
||||
- TCP / 文件回放 runtime data 驱动,支持绑定到场景组件。
|
||||
- Smoke Tests 覆盖场景编辑、选择、Undo/Redo、模块组合、资产导入、运行时数据、UI 文档序列化等基础行为。
|
||||
|
||||
## 当前完成内容
|
||||
## 开发原则
|
||||
|
||||
- Panda3D 单窗口宿主
|
||||
- ImGui Docking 编辑器外壳
|
||||
- Panda3D 中央场景 DisplayRegion
|
||||
- 网格、坐标轴、立方体绘制
|
||||
- `Hierarchy` 选择
|
||||
- `Inspector` 中编辑 `Transform`
|
||||
- `Alt+左键 / 中键 / 滚轮 / F` 相机交互
|
||||
- 模块注册接口与内置编辑器模块
|
||||
- **敏捷开发,快速迭代**:先打通可运行闭环,再逐步工程化和性能化。
|
||||
- **编辑期可读优先**:JSON、元数据、项目文件必须便于 diff、调试、人工修复和团队协作。
|
||||
- **运行期性能优先**:发布包通过 Cook 生成二进制资产与运行时配置。
|
||||
- **模块边界清晰**:ECS、渲染桥、编辑器 UI、运行时 UI、资产导入、数据源都应保持可替换。
|
||||
- **国产化适配优先**:Windows 先行,同时面向 Linux、统信、麒麟等环境保留平台抽象。
|
||||
|
||||
## 技术支持连接
|
||||
## 参考
|
||||
|
||||
https://github.com/CedricGuillemet/ImGuizmo
|
||||
|
||||
https://docs.panda3d.org/1.9/cpp/programming/using-cpp/index
|
||||
- [Google Filament](https://github.com/google/filament)
|
||||
- [EnTT](https://github.com/skypjack/entt)
|
||||
- [Dear ImGui](https://github.com/ocornut/imgui)
|
||||
- [ImGuizmo](https://github.com/CedricGuillemet/ImGuizmo)
|
||||
- [RmlUi](https://github.com/mikke89/RmlUi)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,631 +0,0 @@
|
||||
{
|
||||
"GameObjects": [
|
||||
{
|
||||
"Camera": {
|
||||
"FarClip": 100.0,
|
||||
"FieldOfViewDegrees": 60.0,
|
||||
"IsPrimary": true,
|
||||
"NearClip": 0.10000000149011612
|
||||
},
|
||||
"Id": 1,
|
||||
"Name": "Main Camera",
|
||||
"ParentId": 0,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
2.200000047683716,
|
||||
6.5
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
-15.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 2,
|
||||
"Light": {
|
||||
"Color": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"Intensity": 1.5
|
||||
},
|
||||
"Name": "Directional Light",
|
||||
"ParentId": 0,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
-45.0,
|
||||
30.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 3,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": -1,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "Assets/Models/box1.glb",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "box1.glb",
|
||||
"ParentId": 0,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
0.5,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 4,
|
||||
"Light": {
|
||||
"Color": [
|
||||
1.0,
|
||||
0.15000000596046448,
|
||||
0.10000000149011612
|
||||
],
|
||||
"Intensity": 0.0
|
||||
},
|
||||
"Name": "Alarm Beacon",
|
||||
"ParentId": 0,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
2.5,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 5,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 0,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体",
|
||||
"ParentId": 3,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
-1.6927926540374756,
|
||||
-4.459673881530762,
|
||||
1.949385364241607e-07
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 6,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 1,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.001",
|
||||
"ParentId": 5,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
-3.55507755279541,
|
||||
1.553973731915903e-07
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 7,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 2,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.007",
|
||||
"ParentId": 6,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
4.175572872161865,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 8,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 3,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.003",
|
||||
"ParentId": 3,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 9,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 4,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.002",
|
||||
"ParentId": 8,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
-1.4481612709005276e-07,
|
||||
-3.31300687789917
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 10,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 5,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.004",
|
||||
"ParentId": 8,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
3.776623010635376,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 11,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 6,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.005",
|
||||
"ParentId": 8,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
4.31797981262207,
|
||||
-1.8874489171594178e-07
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 12,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 7,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.006",
|
||||
"ParentId": 8,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
-12.448348045349121,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
0.0,
|
||||
-0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"SceneName": "Untitled",
|
||||
"Selection": {
|
||||
"ActiveObjectId": 12,
|
||||
"SelectedObjectIds": [
|
||||
12
|
||||
],
|
||||
"SelectionAnchorId": 12
|
||||
}
|
||||
}
|
||||
@ -1,629 +0,0 @@
|
||||
{
|
||||
"GameObjects": [
|
||||
{
|
||||
"Camera": {
|
||||
"FarClip": 100.0,
|
||||
"FieldOfViewDegrees": 60.0,
|
||||
"IsPrimary": true,
|
||||
"NearClip": 0.10000000149011612
|
||||
},
|
||||
"Id": 1,
|
||||
"Name": "Main Camera",
|
||||
"ParentId": 0,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
2.200000047683716,
|
||||
6.5
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
-15.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 2,
|
||||
"Light": {
|
||||
"Color": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"Intensity": 1.5
|
||||
},
|
||||
"Name": "Directional Light",
|
||||
"ParentId": 0,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
-45.0,
|
||||
30.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 3,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": -1,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "Assets/Models/box1.glb",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "box1.glb",
|
||||
"ParentId": 0,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
0.5,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 4,
|
||||
"Light": {
|
||||
"Color": [
|
||||
1.0,
|
||||
0.15000000596046448,
|
||||
0.10000000149011612
|
||||
],
|
||||
"Intensity": 0.0
|
||||
},
|
||||
"Name": "Alarm Beacon",
|
||||
"ParentId": 0,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
2.5,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 5,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 0,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体",
|
||||
"ParentId": 3,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
-1.6927926540374756,
|
||||
-4.459673881530762,
|
||||
1.949385364241607e-07
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 6,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 1,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.001",
|
||||
"ParentId": 5,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
-3.55507755279541,
|
||||
1.553973731915903e-07
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 7,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 2,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.007",
|
||||
"ParentId": 6,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
4.175572872161865,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 8,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 3,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.003",
|
||||
"ParentId": 3,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 9,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 4,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.002",
|
||||
"ParentId": 8,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
-1.4481612709005276e-07,
|
||||
-3.31300687789917
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 10,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 5,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.004",
|
||||
"ParentId": 8,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
3.776623010635376,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 11,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 6,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.005",
|
||||
"ParentId": 8,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
0.0,
|
||||
4.31797981262207,
|
||||
-1.8874489171594178e-07
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": 12,
|
||||
"MeshRenderer": {
|
||||
"AlphaCutoff": 0.5,
|
||||
"AlphaMode": 0,
|
||||
"AoTextureGuid": "",
|
||||
"AoTexturePath": "",
|
||||
"BaseColor": [
|
||||
0.75,
|
||||
0.7799999713897705,
|
||||
0.8399999737739563
|
||||
],
|
||||
"BaseColorTextureGuid": "",
|
||||
"BaseColorTexturePath": "",
|
||||
"BuiltinMesh": 0,
|
||||
"DoubleSided": true,
|
||||
"EmissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"EmissiveTextureGuid": "",
|
||||
"EmissiveTexturePath": "",
|
||||
"MaterialAssetGuids": [],
|
||||
"MeshAssetGuid": "",
|
||||
"MeshSource": 1,
|
||||
"Metallic": 0.0,
|
||||
"MetallicRoughnessTextureGuid": "",
|
||||
"MetallicRoughnessTexturePath": "",
|
||||
"ModelNodeIndex": 7,
|
||||
"NormalTextureGuid": "",
|
||||
"NormalTexturePath": "",
|
||||
"Roughness": 1.0,
|
||||
"SourceModelAssetGuid": "",
|
||||
"SourceModelPath": "",
|
||||
"SourceNodePath": "",
|
||||
"SubMeshIndex": -1,
|
||||
"Visible": true
|
||||
},
|
||||
"Name": "立方体.006",
|
||||
"ParentId": 8,
|
||||
"Transform": {
|
||||
"Position": [
|
||||
-4.638434886932373,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"RotationEulerDegrees": [
|
||||
0.0,
|
||||
-0.0,
|
||||
0.0
|
||||
],
|
||||
"Scale": [
|
||||
0.9999998807907104,
|
||||
1.0,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"SceneName": "Untitled",
|
||||
"Selection": {
|
||||
"ActiveObjectId": 0,
|
||||
"SelectedObjectIds": [],
|
||||
"SelectionAnchorId": 0
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -31,6 +31,11 @@
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
[[nodiscard]] bool MetaCoreHandleProjectAssetDrop(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
);
|
||||
|
||||
namespace {
|
||||
|
||||
static std::string GeneratedResourceFilter_{};
|
||||
@ -122,10 +127,6 @@ template <typename T>
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
);
|
||||
|
||||
[[nodiscard]] bool MetaCoreHandleProjectAssetDrop(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
);
|
||||
|
||||
[[nodiscard]] std::optional<MetaCoreAssetRecord> FindSourceModelAssetForGameObject(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
@ -603,7 +604,7 @@ void MetaCoreEnsureProjectStartupScene(MetaCoreEditorContext& editorContext) {
|
||||
|
||||
editorContext.GetScene().RestoreSnapshot(MetaCoreCreateDefaultScene().CaptureSnapshot());
|
||||
editorContext.ClearSelection();
|
||||
const std::filesystem::path bootstrapScenePath = std::filesystem::path("Scenes") / "Main.mcscene";
|
||||
const std::filesystem::path bootstrapScenePath = std::filesystem::path("Scenes") / "Main.mcscene.json";
|
||||
if (scenePersistenceService->SaveSceneAs(editorContext, bootstrapScenePath)) {
|
||||
(void)assetDatabaseService->SetStartupScenePath(bootstrapScenePath);
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Project", "已为新项目创建默认启动场景");
|
||||
@ -3292,6 +3293,8 @@ void MetaCoreBeginProjectAssetDragDropSource(const MetaCoreAssetRecord& assetRec
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
[[nodiscard]] bool MetaCoreHandleProjectAssetDrop(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
@ -3326,6 +3329,8 @@ void MetaCoreBeginProjectAssetDragDropSource(const MetaCoreAssetRecord& assetRec
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void DrawPrefabDetails(MetaCoreEditorContext& editorContext, MetaCoreIAssetDatabaseService& assetDatabaseService, MetaCoreIPackageService& packageService, MetaCoreIReflectionRegistry& reflectionRegistry) {
|
||||
const MetaCoreSelectedAssetState& selectedAsset = editorContext.GetSelectedAsset();
|
||||
const auto assetRecord = assetDatabaseService.FindAssetByGuid(selectedAsset.Guid);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "MetaCoreEditor/MetaCoreEditorApp.h"
|
||||
#include "MetaCoreEditor/MetaCoreEditorApp.h"
|
||||
|
||||
#include "MetaCoreEditor/MetaCoreBuiltinModules.h"
|
||||
#include "MetaCoreEditor/MetaCoreEditorServices.h"
|
||||
@ -37,6 +37,12 @@ extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
// 声明在外部定义的拖拽资产放置处理函数,用于在此处处理视口拖放
|
||||
[[nodiscard]] bool MetaCoreHandleProjectAssetDrop(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
);
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(_DEBUG)
|
||||
@ -76,7 +82,8 @@ constexpr bool GMetaCoreEnableImGuizmo = true;
|
||||
if (sceneEditingService == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const auto instantiatedObjectId = sceneEditingService->InstantiateModelAsset(editorContext, payload.AssetGuid, std::nullopt);
|
||||
// 拖入场景视口时默认实例化到根级,即父节点为 0,防止错误继承其它选中物体的变换
|
||||
const auto instantiatedObjectId = sceneEditingService->InstantiateModelAsset(editorContext, payload.AssetGuid, 0);
|
||||
if (!instantiatedObjectId.has_value()) {
|
||||
return false;
|
||||
}
|
||||
@ -89,7 +96,8 @@ constexpr bool GMetaCoreEnableImGuizmo = true;
|
||||
if (prefabService == nullptr || !prefabService->SupportsPrefabWorkflows()) {
|
||||
return false;
|
||||
}
|
||||
const auto instantiatedObjectId = prefabService->InstantiatePrefab(editorContext, payload.AssetGuid, std::nullopt);
|
||||
// 拖入场景视口时同样默认实例化到根级,即父节点为 0
|
||||
const auto instantiatedObjectId = prefabService->InstantiatePrefab(editorContext, payload.AssetGuid, 0);
|
||||
if (!instantiatedObjectId.has_value()) {
|
||||
return false;
|
||||
}
|
||||
@ -519,6 +527,13 @@ bool MetaCoreEditorApp::Initialize() {
|
||||
MetaCoreTraceStartup("metacore.app: initialize message handler");
|
||||
DragAcceptFiles(static_cast<HWND>(Window_.GetNativeWindowHandle()), TRUE);
|
||||
|
||||
// 允许低权限的 Windows 资源管理器 (Explorer) 向高权限的编辑器进程发送拖放消息,彻底解决拖拽禁止图标的系统底层限制
|
||||
#ifndef MSGFLT_ADD
|
||||
#define MSGFLT_ADD 1
|
||||
#endif
|
||||
ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
|
||||
ChangeWindowMessageFilter(0x0049, MSGFLT_ADD); // WM_COPYGLOBALMEM
|
||||
|
||||
Window_.SetNativeWindowMessageHandler([this](void* nativeWindowHandle, unsigned int message, std::uintptr_t wparam, std::intptr_t lparam) {
|
||||
if (message == WM_DROPFILES) {
|
||||
HDROP hDrop = reinterpret_cast<HDROP>(wparam);
|
||||
@ -527,17 +542,29 @@ bool MetaCoreEditorApp::Initialize() {
|
||||
const auto importPipelineService = ModuleRegistry_.ResolveService<MetaCoreIImportPipelineService>();
|
||||
if (assetDatabaseService != nullptr && assetDatabaseService->HasProject() && importPipelineService != nullptr) {
|
||||
const std::filesystem::path assetsPath = assetDatabaseService->GetProjectDescriptor().AssetsPath;
|
||||
|
||||
// 【修复外部拖入目录问题】:如果当前编辑器选中了特定子文件夹目录(例如 Assets/Models),则应当拷贝入该子目录中而不是根目录
|
||||
std::filesystem::path targetFolder = assetsPath;
|
||||
if (EditorContext_ != nullptr) {
|
||||
const std::filesystem::path selectedDir = EditorContext_->GetSelectedProjectDirectory();
|
||||
if (!selectedDir.empty()) {
|
||||
const std::filesystem::path candidate = assetDatabaseService->GetProjectDescriptor().RootPath / selectedDir;
|
||||
if (std::filesystem::exists(candidate)) {
|
||||
targetFolder = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool importedAny = false;
|
||||
for (UINT i = 0; i < fileCount; ++i) {
|
||||
wchar_t filePath[MAX_PATH];
|
||||
if (DragQueryFileW(hDrop, i, filePath, MAX_PATH)) {
|
||||
std::filesystem::path sourcePath(filePath);
|
||||
std::filesystem::path targetPath = assetsPath / sourcePath.filename();
|
||||
std::filesystem::path targetPath = targetFolder / sourcePath.filename();
|
||||
try {
|
||||
if (!std::filesystem::exists(targetPath)) {
|
||||
std::filesystem::copy_file(sourcePath, targetPath);
|
||||
importedAny = true;
|
||||
}
|
||||
// 使用 overwrite_existing 允许外部重构模型直接拖入刷新,触发引擎自动重导入并更新引用
|
||||
std::filesystem::copy_file(sourcePath, targetPath, std::filesystem::copy_options::overwrite_existing);
|
||||
importedAny = true;
|
||||
} catch (...) { }
|
||||
}
|
||||
}
|
||||
@ -601,7 +628,7 @@ bool MetaCoreEditorApp::Initialize() {
|
||||
assetDatabaseService != nullptr &&
|
||||
assetDatabaseService->HasProject() &&
|
||||
assetDatabaseService->GetProjectDescriptor().ScenePaths.empty()) {
|
||||
const std::filesystem::path bootstrapScenePath = std::filesystem::path("Scenes") / "Main.mcscene";
|
||||
const std::filesystem::path bootstrapScenePath = std::filesystem::path("Scenes") / "Main.mcscene.json";
|
||||
if (scenePersistenceService->SaveSceneAs(*EditorContext_, bootstrapScenePath)) {
|
||||
(void)assetDatabaseService->SetStartupScenePath(bootstrapScenePath);
|
||||
loadedStartupScene = true;
|
||||
@ -823,7 +850,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
ImGui::SetNextWindowPos(centralNode->Pos);
|
||||
ImGui::SetNextWindowSize(ImVec2(centralNode->Size.x, 30.0F));
|
||||
ImGui::Begin("ViewportTabs", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
|
||||
|
||||
static int activeTab = 0; // 0: Scene, 1: Game
|
||||
if (ImGui::Selectable(" 场景 ", activeTab == 0, 0, ImVec2(60, 0))) activeTab = 0;
|
||||
ImGui::SameLine();
|
||||
@ -847,7 +874,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
}
|
||||
|
||||
// --- 补丁三重构开始 ---
|
||||
|
||||
|
||||
// 1. 准备 GizmoCanvas 窗口属性
|
||||
ImGui::SetNextWindowPos(ImVec2(centralNode->Pos.x, centralNode->Pos.y + sceneToolbarHeight + tabHeaderHeight));
|
||||
ImGui::SetNextWindowSize(ImVec2(centralNode->Size.x, centralNode->Size.y - sceneToolbarHeight - tabHeaderHeight));
|
||||
@ -856,11 +883,17 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0F, 0.0F));
|
||||
|
||||
constexpr ImGuiWindowFlags gizmoCanvasFlags =
|
||||
// 【问题二修复】:如果当前没有处于资产拖拽过程中,则屏蔽输入以允许相机漫游操作;
|
||||
// 如果正在拖放资产(如模型或预制体),则恢复输入以便 BeginDragDropTarget 能够正确接收放置。
|
||||
ImGuiWindowFlags gizmoCanvasFlags =
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus |
|
||||
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoInputs;
|
||||
ImGuiWindowFlags_NoBackground;
|
||||
|
||||
if (ImGui::GetDragDropPayload() == nullptr) {
|
||||
gizmoCanvasFlags |= ImGuiWindowFlags_NoInputs;
|
||||
}
|
||||
|
||||
bool gizmoCanvasOpen = true;
|
||||
bool gizmoUsing = false;
|
||||
@ -872,7 +905,6 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
ImVec2 viewportSize = ImGui::GetContentRegionAvail();
|
||||
|
||||
// 3. 全局统一更新 viewportState
|
||||
MetaCoreSceneViewportState& viewportState = EditorContext_->GetSceneViewportState();
|
||||
viewportState.Left = viewportPos.x;
|
||||
viewportState.Top = viewportPos.y;
|
||||
viewportState.Width = viewportSize.x;
|
||||
@ -916,6 +948,12 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
ImGui::Image(texPtr, viewportSize, ImVec2(0, 1), ImVec2(1, 0));
|
||||
}
|
||||
|
||||
// 7.5. 视口拖拽接收器:从项目面板拖拽资产进入 3D 视口时,在此处响应放置并完成实例化
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
(void)MetaCoreHandleProjectAssetDrop(*EditorContext_, std::nullopt);
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
// 8. 绘制 Gizmo (此时坐标和矩阵已完全对齐)
|
||||
if (GMetaCoreEnableImGuizmo) {
|
||||
SceneInteractionService_.HandleGizmoManipulation(*EditorContext_);
|
||||
@ -928,7 +966,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
viewportState.Left + viewportState.Width - 110.0F,
|
||||
viewportState.Top + 20.0F
|
||||
);
|
||||
|
||||
|
||||
glm::mat4 cubeViewMatrix = glm::lookAt(sceneView.CameraPosition, sceneView.CameraTarget, sceneView.CameraUp);
|
||||
const glm::mat4 originalCubeViewMatrix = cubeViewMatrix;
|
||||
|
||||
@ -955,8 +993,11 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
MetaCoreDrawSelectionBoundsOverlay(*EditorContext_, Scene_, sceneView, viewportState);
|
||||
MetaCoreDrawSelectionHierarchyOverlay(*EditorContext_, Scene_, sceneView, viewportState);
|
||||
|
||||
// 启用资产拖拽到 3D 场景视口的高亮响应遮罩,并显示操作提示
|
||||
MetaCoreDrawSceneViewportDropTarget(*EditorContext_, viewportPos, viewportSize);
|
||||
|
||||
// 处理拾取
|
||||
if (viewportState.Hovered && !gizmoHovering && !gizmoUsing &&
|
||||
if (viewportState.Hovered && !gizmoHovering && !gizmoUsing &&
|
||||
!ImGui::GetIO().WantCaptureMouse && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
const MetaCoreId pickedObjectId = SceneInteractionService_.PickGameObjectFromViewport(
|
||||
Scene_,
|
||||
@ -969,7 +1010,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(3);
|
||||
|
||||
|
||||
SceneInteractionService_.HandleGizmoEndUse(*EditorContext_, gizmoUsing);
|
||||
SceneInteractionService_.DrawViewportToolbar(*EditorContext_);
|
||||
// --- 补丁三重构结束 ---
|
||||
|
||||
@ -161,7 +161,7 @@ private:
|
||||
|
||||
[[nodiscard]] MetaCoreRuntimeProjectDocument MetaCoreBuildDefaultRuntimeProjectDocument() {
|
||||
MetaCoreRuntimeProjectDocument document;
|
||||
document.StartupScenePath = std::filesystem::path("Scenes") / "Main.mcscene";
|
||||
document.StartupScenePath = std::filesystem::path("Scenes") / "Main.mcscene.json";
|
||||
document.DataSourcesPath = std::filesystem::path("Runtime") / "DataSources.mcruntime";
|
||||
document.BindingsPath = std::filesystem::path("Runtime") / "Bindings.mcruntime";
|
||||
document.DiagnosticsPath = std::filesystem::path("Runtime") / "Diagnostics.mcruntimestate";
|
||||
|
||||
@ -14,32 +14,6 @@
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
MC_ENUM()
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreUiNodeType {
|
||||
Panel = 0,
|
||||
Text,
|
||||
Image,
|
||||
Button
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreUiHorizontalAlignment {
|
||||
Left = 0,
|
||||
Center,
|
||||
Right,
|
||||
Stretch
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreUiVerticalAlignment {
|
||||
Top = 0,
|
||||
Center,
|
||||
Bottom,
|
||||
Stretch
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreSubAssetMetadata {
|
||||
MC_GENERATED_BODY()
|
||||
@ -101,17 +75,6 @@ struct MetaCoreImportedAssetDocument {
|
||||
};
|
||||
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCorePrefabDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Name{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<MetaCoreGameObjectData> GameObjects{};
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreCookManifestEntry {
|
||||
MC_GENERATED_BODY()
|
||||
@ -140,111 +103,4 @@ struct MetaCoreCookManifestDocument {
|
||||
std::vector<MetaCoreCookManifestEntry> Entries{};
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreUiRectTransformDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 AnchorMin{0.0F, 0.0F, 0.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 AnchorMax{1.0F, 1.0F, 0.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 Pivot{0.5F, 0.5F, 0.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 Position{0.0F, 0.0F, 0.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 Size{100.0F, 100.0F, 0.0F};
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreUiStyleDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 BackgroundColor{0.15F, 0.15F, 0.15F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 TextColor{1.0F, 1.0F, 1.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 TintColor{1.0F, 1.0F, 1.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
float FontSize = 16.0F;
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 Padding{8.0F, 8.0F, 0.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreUiHorizontalAlignment HorizontalAlignment = MetaCoreUiHorizontalAlignment::Left;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreUiVerticalAlignment VerticalAlignment = MetaCoreUiVerticalAlignment::Top;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreAssetGuid ImageAssetGuid{};
|
||||
|
||||
MC_PROPERTY()
|
||||
bool PreserveAspect = false;
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreUiNodeDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Id{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Name{};
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreUiNodeType Type = MetaCoreUiNodeType::Panel;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string ParentId{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<std::string> Children{};
|
||||
|
||||
MC_PROPERTY()
|
||||
bool Visible = true;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreUiRectTransformDocument RectTransform{};
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreUiStyleDocument Style{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Text{};
|
||||
|
||||
MC_PROPERTY()
|
||||
bool Interactable = false;
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreUiDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Name{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::int32_t ReferenceWidth = 1920;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::int32_t ReferenceHeight = 1080;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<std::string> RootNodeIds{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<MetaCoreUiNodeDocument> Nodes{};
|
||||
};
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -44,7 +44,7 @@ bool MetaCoreEditorViewportRenderer::Initialize(MetaCoreRenderDevice& renderDevi
|
||||
Shutdown();
|
||||
|
||||
MetaCoreTrace("metacore.render: viewport scene runtime bind begin");
|
||||
// 彻底剔除 Panda3D:不再初始化 Panda3D 场景桥接器
|
||||
// Filament is the only scene bridge initialized by the editor viewport.
|
||||
/*
|
||||
if (!SceneBridge_.Initialize(renderDevice)) {
|
||||
MetaCoreTrace("metacore.render: viewport scene runtime bind failed");
|
||||
|
||||
@ -228,6 +228,16 @@ public:
|
||||
// 【超级核心】:绑定顶级父 GameObject 标识到 Filament Asset 根 Entity,彻底打通顶级 Gizmo 的同步!
|
||||
ObjectToFilamentEntity_[parentObject.GetId()] = { asset, asset->getRoot() };
|
||||
|
||||
// 预先收集并缓存 parentObject 子树下的所有已有 GameObject,用于高精度的三步复用匹配
|
||||
std::vector<MetaCoreGameObject> subtreeObjects;
|
||||
std::vector<MetaCoreId> subtreeIds = scene.GetSubtreeObjectIds(parentObject.GetId());
|
||||
for (MetaCoreId subId : subtreeIds) {
|
||||
auto subObj = scene.FindGameObject(subId);
|
||||
if (subObj && subObj.GetId() != parentObject.GetId()) {
|
||||
subtreeObjects.push_back(subObj);
|
||||
}
|
||||
}
|
||||
|
||||
// 第一遍:创建或复用所有对象
|
||||
for (size_t i = 0; i < entityCount; i++) {
|
||||
utils::Entity entity = entities[i];
|
||||
@ -236,21 +246,44 @@ public:
|
||||
const char* nodeName = asset->getName(entity);
|
||||
std::string name = nodeName ? nodeName : ("Node_" + std::to_string(i));
|
||||
|
||||
// 如果场景中在 parentObject 下已经展开好了对应的子 GameObject,直接复用以建立映射
|
||||
MetaCoreGameObject childObj;
|
||||
bool isExisting = false;
|
||||
for (auto& sceneObj : scene.GetGameObjects()) {
|
||||
if (sceneObj.GetParentId() == parentObject.GetId() &&
|
||||
sceneObj.HasComponent<MetaCoreMeshRendererComponent>() &&
|
||||
sceneObj.GetComponent<MetaCoreMeshRendererComponent>().ModelNodeIndex == static_cast<std::int32_t>(i)) {
|
||||
childObj = sceneObj;
|
||||
isExisting = true;
|
||||
break;
|
||||
|
||||
// 1. 优先通过 ModelNodeIndex 进行精确匹配(针对带有网格的节点)
|
||||
for (auto& sceneObj : subtreeObjects) {
|
||||
if (sceneObj.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
auto& mesh = sceneObj.GetComponent<MetaCoreMeshRendererComponent>();
|
||||
if (mesh.ModelNodeIndex == static_cast<std::int32_t>(i)) {
|
||||
childObj = sceneObj;
|
||||
isExisting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 如果没找到(针对没有网格的空节点),通过名字进行匹配复用
|
||||
if (!isExisting) {
|
||||
for (auto& sceneObj : subtreeObjects) {
|
||||
if (sceneObj.GetName() == name) {
|
||||
// 且该节点不能已经被其他实体映射占用
|
||||
bool alreadyMapped = false;
|
||||
for (auto& [ent, obj] : entityToObj) {
|
||||
if (obj.GetId() == sceneObj.GetId()) {
|
||||
alreadyMapped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!alreadyMapped) {
|
||||
childObj = sceneObj;
|
||||
isExisting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 实在没有找到,才进行动态创建(仅作为降级兜底)
|
||||
if (!isExisting) {
|
||||
// 如果没找到才动态创建它(用于默认场景的未展开根节点)
|
||||
childObj = scene.CreateGameObject(name, parentObject.GetId());
|
||||
auto& meshRenderer = childObj.AddComponent<MetaCoreMeshRendererComponent>();
|
||||
meshRenderer.MeshSource = MetaCoreMeshSourceKind::Asset;
|
||||
@ -304,9 +337,32 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static std::string NormalizePath(std::string path) {
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
return path;
|
||||
}
|
||||
|
||||
void SyncScene(MetaCoreScene& scene, bool compatibilityMeshOnly) {
|
||||
if (!AssetLoader_) return;
|
||||
|
||||
// 预处理:构建子树模型网格节点计数表,以支撑在没有 Tag 时的极速、完美 Fallback 回溯
|
||||
std::unordered_map<MetaCoreId, std::unordered_map<std::string, int>> subtreeModelCounts;
|
||||
for (const auto& obj : scene.GetGameObjects()) {
|
||||
if (obj.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
auto& mesh = obj.GetComponent<MetaCoreMeshRendererComponent>();
|
||||
if (mesh.ModelNodeIndex >= 0 && !mesh.SourceModelPath.empty()) {
|
||||
std::string normPath = NormalizePath(mesh.SourceModelPath);
|
||||
MetaCoreId currentId = obj.GetId();
|
||||
while (currentId != 0) {
|
||||
auto currentObj = scene.FindGameObject(currentId);
|
||||
if (!currentObj) break;
|
||||
subtreeModelCounts[currentId][normPath]++;
|
||||
currentId = currentObj.GetParentId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& gameObject : scene.GetGameObjects()) {
|
||||
if (!gameObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||
continue;
|
||||
@ -338,11 +394,44 @@ public:
|
||||
// 【黄金算法】:溯源确定唯一的模型根加载主体
|
||||
MetaCoreGameObject hostRoot = gameObject;
|
||||
if (meshRenderer.ModelNodeIndex >= 0) {
|
||||
// 如果是子节点,沿着场景树向上回溯至最顶层父节点作为该模型的宿主加载实体
|
||||
while (hostRoot.GetParentId() != 0) {
|
||||
auto parentObj = scene.FindGameObject(hostRoot.GetParentId());
|
||||
const std::string& currentModelPath = meshRenderer.SourceModelPath;
|
||||
std::string normCurrentPath = NormalizePath(currentModelPath);
|
||||
MetaCoreGameObject current = gameObject;
|
||||
MetaCoreGameObject foundRoot = {};
|
||||
|
||||
// 1. 优先通过运行时 MetaCoreModelRootTag 快速寻找顶级根
|
||||
while (current.GetParentId() != 0) {
|
||||
auto parentObj = scene.FindGameObject(current.GetParentId());
|
||||
if (!parentObj) break;
|
||||
hostRoot = parentObj;
|
||||
|
||||
if (parentObj.HasComponent<MetaCoreModelRootTag>()) {
|
||||
auto& tag = parentObj.GetComponent<MetaCoreModelRootTag>();
|
||||
if (NormalizePath(tag.SourceModelPath) == normCurrentPath) {
|
||||
foundRoot = parentObj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
current = parentObj;
|
||||
}
|
||||
|
||||
if (foundRoot) {
|
||||
hostRoot = foundRoot;
|
||||
} else {
|
||||
// 2. Fallback:若无明确 Tag(例如反序列化后),使用转折点判定算法(匹配包含模型网格数最大的、最深的祖先)
|
||||
current = gameObject;
|
||||
MetaCoreGameObject bestCandidate = gameObject;
|
||||
int maxCount = subtreeModelCounts[gameObject.GetId()][normCurrentPath];
|
||||
|
||||
while (current) {
|
||||
int count = subtreeModelCounts[current.GetId()][normCurrentPath];
|
||||
if (count > maxCount) {
|
||||
maxCount = count;
|
||||
bestCandidate = current;
|
||||
}
|
||||
if (current.GetParentId() == 0) break;
|
||||
current = scene.FindGameObject(current.GetParentId());
|
||||
}
|
||||
hostRoot = bestCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,6 +474,11 @@ public:
|
||||
// 同步层级结构到场景树并自动进行展开子节点的复用映射
|
||||
MetaCoreGameObject nonConstHostRoot = hostRoot;
|
||||
|
||||
// 现场补挂 Tag,确保后续帧和拖拽时 100% 命中 Tag 快速通道,绝不走 Fallback
|
||||
if (!nonConstHostRoot.HasComponent<MetaCoreModelRootTag>()) {
|
||||
nonConstHostRoot.AddComponent<MetaCoreModelRootTag>(MetaCoreModelRootTag{ modelPath });
|
||||
}
|
||||
|
||||
// 如果名字是默认的 "Cube",自动改为模型文件名
|
||||
if (nonConstHostRoot.GetName() == "Cube") {
|
||||
std::string filename = std::filesystem::path(modelPath).filename().string();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2,48 +2,13 @@
|
||||
|
||||
#include "MetaCorePlatform/MetaCoreWindow.h"
|
||||
|
||||
#include "camera.h"
|
||||
#include "displayRegion.h"
|
||||
#include "graphicsEngine.h"
|
||||
#include "graphicsWindow.h"
|
||||
#include "pandaFramework.h"
|
||||
#include "pandaNode.h"
|
||||
#include "perspectiveLens.h"
|
||||
#include "windowFramework.h"
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
namespace {
|
||||
|
||||
glm::mat4 MetaCoreConvertPandaMatrixToGlm(const LMatrix4f& matrix) {
|
||||
glm::mat4 result(1.0F);
|
||||
for (int col = 0; col < 4; ++col) {
|
||||
for (int row = 0; row < 4; ++row) {
|
||||
// Transpose mathematical matrix: result[col][row] is GLM(row, col).
|
||||
// We want GLM(row, col) = Panda(col, row) to switch v*M to M*v.
|
||||
result[col][row] = matrix.get_cell(col, row);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class MetaCoreRenderDevice::MetaCoreRenderDeviceImpl {
|
||||
public:
|
||||
MetaCoreWindow* Window = nullptr;
|
||||
PandaFramework* Framework = nullptr;
|
||||
WindowFramework* WindowFrameworkHandle = nullptr;
|
||||
GraphicsWindow* GraphicsWindowHandle = nullptr;
|
||||
GraphicsEngine* GraphicsEngineHandle = nullptr;
|
||||
DisplayRegion* SceneDisplayRegion = nullptr;
|
||||
NodePath SceneRoot{};
|
||||
NodePath EditorCamera{};
|
||||
bool Initialized = false;
|
||||
};
|
||||
|
||||
@ -68,40 +33,18 @@ void MetaCoreRenderDevice::Shutdown() {
|
||||
if (!Impl_->Initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Impl_->EditorCamera.is_empty()) {
|
||||
Impl_->EditorCamera.remove_node();
|
||||
Impl_->EditorCamera = NodePath();
|
||||
}
|
||||
|
||||
if (!Impl_->SceneRoot.is_empty()) {
|
||||
Impl_->SceneRoot.remove_node();
|
||||
Impl_->SceneRoot = NodePath();
|
||||
}
|
||||
|
||||
Impl_->SceneDisplayRegion = nullptr;
|
||||
Impl_->GraphicsEngineHandle = nullptr;
|
||||
Impl_->GraphicsWindowHandle = nullptr;
|
||||
Impl_->WindowFrameworkHandle = nullptr;
|
||||
Impl_->Framework = nullptr;
|
||||
Impl_->Window = nullptr;
|
||||
Impl_->Initialized = false;
|
||||
}
|
||||
|
||||
void MetaCoreRenderDevice::RenderFrame() const {
|
||||
if (Impl_->GraphicsEngineHandle != nullptr) {
|
||||
Impl_->GraphicsEngineHandle->render_frame();
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreRenderDevice::PresentFrame() const {
|
||||
if (Impl_->GraphicsEngineHandle != nullptr) {
|
||||
Impl_->GraphicsEngineHandle->flip_frame();
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreRenderDevice::SetSceneViewportRect(const MetaCoreViewportRect& viewportRect) {
|
||||
// 已废弃 Panda3D 实现,视口管理已移至 Filament 链
|
||||
(void)viewportRect;
|
||||
}
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "MetaCoreFoundation/MetaCoreId.h"
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
class MetaCoreRenderDevice;
|
||||
class MetaCoreScene;
|
||||
struct MetaCoreSceneView;
|
||||
|
||||
class MetaCorePandaSceneBridge {
|
||||
public:
|
||||
MetaCorePandaSceneBridge();
|
||||
~MetaCorePandaSceneBridge();
|
||||
|
||||
bool Initialize(MetaCoreRenderDevice& renderDevice);
|
||||
void Shutdown();
|
||||
void SetProjectRootPath(const std::filesystem::path& projectRootPath);
|
||||
void SyncScene(const MetaCoreScene& scene, bool compatibilityMeshOnly = false);
|
||||
void ApplySceneView(const MetaCoreSceneView& sceneView);
|
||||
|
||||
[[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const;
|
||||
[[nodiscard]] bool HasRuntimeSyncFailure() const;
|
||||
[[nodiscard]] const std::string& GetLastRuntimeSyncFailure() const;
|
||||
|
||||
private:
|
||||
class MetaCorePandaSceneBridgeImpl;
|
||||
std::unique_ptr<MetaCorePandaSceneBridgeImpl> Impl_{};
|
||||
};
|
||||
|
||||
} // namespace MetaCore
|
||||
@ -9,7 +9,6 @@
|
||||
namespace MetaCore {
|
||||
|
||||
class MetaCoreEditorViewportRenderer;
|
||||
class MetaCorePandaSceneBridge;
|
||||
class MetaCoreWindow;
|
||||
|
||||
class MetaCoreRenderDevice {
|
||||
|
||||
@ -403,6 +403,8 @@ MetaCoreSceneSnapshot MetaCoreScene::CaptureSnapshot() const {
|
||||
data.Light = obj.GetComponent<MetaCoreLightComponent>();
|
||||
if (obj.HasComponent<MetaCorePrefabInstanceMetadata>())
|
||||
data.PrefabInstance = obj.GetComponent<MetaCorePrefabInstanceMetadata>();
|
||||
if (obj.HasComponent<MetaCoreModelRootTag>())
|
||||
data.ModelRootTag = obj.GetComponent<MetaCoreModelRootTag>();
|
||||
|
||||
snapshot.GameObjects.push_back(std::move(data));
|
||||
}
|
||||
@ -427,6 +429,8 @@ void MetaCoreScene::RestoreSnapshot(const MetaCoreSceneSnapshot& snapshot) {
|
||||
obj.AddComponent<MetaCoreLightComponent>(data.Light.value());
|
||||
if (data.PrefabInstance.has_value())
|
||||
obj.AddComponent<MetaCorePrefabInstanceMetadata>(data.PrefabInstance.value());
|
||||
if (data.ModelRootTag.has_value())
|
||||
obj.AddComponent<MetaCoreModelRootTag>(data.ModelRootTag.value());
|
||||
}
|
||||
MetaCoreIdGenerator::EnsureAbove(maxId);
|
||||
}
|
||||
@ -448,6 +452,7 @@ MetaCoreScene MetaCoreCreateDefaultScene() {
|
||||
meshRenderer.MeshSource = MetaCoreMeshSourceKind::Asset;
|
||||
meshRenderer.SourceModelPath = "Assets/Models/box1.glb";
|
||||
meshRenderer.SourceModelAssetGuid = MetaCoreAssetRegistry::Get().ResolvePathToGuid("Assets/Models/box1.glb");
|
||||
cube.AddComponent<MetaCoreModelRootTag>(MetaCoreModelRootTag{ "Assets/Models/box1.glb" });
|
||||
cube.GetComponent<MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 0.5F, 0.0F);
|
||||
|
||||
MetaCoreGameObject alarmBeacon = scene.CreateGameObject("Alarm Beacon");
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "MetaCoreScene/MetaCoreSceneSerializer.h"
|
||||
#include "MetaCoreScene/MetaCoreSceneSerializer.h"
|
||||
#include "MetaCoreFoundation/MetaCoreReflection.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <fstream>
|
||||
@ -32,6 +32,218 @@ static MetaCoreAssetGuid StringToGuid(const std::string& str) {
|
||||
return str.empty() ? MetaCoreAssetGuid{} : MetaCoreAssetGuid::Parse(str).value_or(MetaCoreAssetGuid{});
|
||||
}
|
||||
|
||||
// 辅助方法:将单个 MetaCoreGameObjectData 转换为 json 格式
|
||||
static json GameObjectDataToJson(const MetaCoreGameObjectData& obj, const MetaCoreTypeRegistry& registry) {
|
||||
json objJson = json::object();
|
||||
const MetaCoreStructDescriptor* objDesc = registry.FindStruct<MetaCoreGameObjectData>();
|
||||
if (!objDesc) return objJson;
|
||||
|
||||
for (const auto& objField : objDesc->Fields) {
|
||||
if (objField.Name == "Id") {
|
||||
objJson["Id"] = obj.Id;
|
||||
} else if (objField.Name == "ParentId") {
|
||||
objJson["ParentId"] = obj.ParentId;
|
||||
} else if (objField.Name == "Name") {
|
||||
objJson["Name"] = obj.Name;
|
||||
} else if (objField.Name == "Transform") {
|
||||
const MetaCoreStructDescriptor* transDesc = registry.FindStruct<MetaCoreTransformComponent>();
|
||||
if (transDesc) {
|
||||
json transJson = json::object();
|
||||
for (const auto& transField : transDesc->Fields) {
|
||||
if (transField.Name == "Position") transJson["Position"] = Vec3ToJson(obj.Transform.Position);
|
||||
else if (transField.Name == "RotationEulerDegrees") transJson["RotationEulerDegrees"] = Vec3ToJson(obj.Transform.RotationEulerDegrees);
|
||||
else if (transField.Name == "Scale") transJson["Scale"] = Vec3ToJson(obj.Transform.Scale);
|
||||
}
|
||||
objJson["Transform"] = transJson;
|
||||
}
|
||||
} else if (objField.Name == "MeshRenderer" && obj.MeshRenderer.has_value()) {
|
||||
const MetaCoreStructDescriptor* meshDesc = registry.FindStruct<MetaCoreMeshRendererComponent>();
|
||||
if (meshDesc) {
|
||||
json meshJson = json::object();
|
||||
const auto& mesh = obj.MeshRenderer.value();
|
||||
for (const auto& meshField : meshDesc->Fields) {
|
||||
if (meshField.Name == "MeshSource") meshJson["MeshSource"] = static_cast<int>(mesh.MeshSource);
|
||||
else if (meshField.Name == "BuiltinMesh") meshJson["BuiltinMesh"] = static_cast<int>(mesh.BuiltinMesh);
|
||||
else if (meshField.Name == "MeshAssetGuid") meshJson["MeshAssetGuid"] = GuidToString(mesh.MeshAssetGuid);
|
||||
else if (meshField.Name == "MaterialAssetGuids") {
|
||||
json matGuids = json::array();
|
||||
for (const auto& guid : mesh.MaterialAssetGuids) {
|
||||
matGuids.push_back(GuidToString(guid));
|
||||
}
|
||||
meshJson["MaterialAssetGuids"] = matGuids;
|
||||
}
|
||||
else if (meshField.Name == "SourceModelAssetGuid") meshJson["SourceModelAssetGuid"] = GuidToString(mesh.SourceModelAssetGuid);
|
||||
else if (meshField.Name == "SourceModelPath") meshJson["SourceModelPath"] = mesh.SourceModelPath;
|
||||
else if (meshField.Name == "SourceNodePath") meshJson["SourceNodePath"] = mesh.SourceNodePath;
|
||||
else if (meshField.Name == "BaseColorTextureGuid") meshJson["BaseColorTextureGuid"] = GuidToString(mesh.BaseColorTextureGuid);
|
||||
else if (meshField.Name == "MetallicRoughnessTextureGuid") meshJson["MetallicRoughnessTextureGuid"] = GuidToString(mesh.MetallicRoughnessTextureGuid);
|
||||
else if (meshField.Name == "NormalTextureGuid") meshJson["NormalTextureGuid"] = GuidToString(mesh.NormalTextureGuid);
|
||||
else if (meshField.Name == "EmissiveTextureGuid") meshJson["EmissiveTextureGuid"] = GuidToString(mesh.EmissiveTextureGuid);
|
||||
else if (meshField.Name == "AoTextureGuid") meshJson["AoTextureGuid"] = GuidToString(mesh.AoTextureGuid);
|
||||
else if (meshField.Name == "BaseColorTexturePath") meshJson["BaseColorTexturePath"] = mesh.BaseColorTexturePath;
|
||||
else if (meshField.Name == "MetallicRoughnessTexturePath") meshJson["MetallicRoughnessTexturePath"] = mesh.MetallicRoughnessTexturePath;
|
||||
else if (meshField.Name == "NormalTexturePath") meshJson["NormalTexturePath"] = mesh.NormalTexturePath;
|
||||
else if (meshField.Name == "EmissiveTexturePath") meshJson["EmissiveTexturePath"] = mesh.EmissiveTexturePath;
|
||||
else if (meshField.Name == "AoTexturePath") meshJson["AoTexturePath"] = mesh.AoTexturePath;
|
||||
else if (meshField.Name == "DoubleSided") meshJson["DoubleSided"] = mesh.DoubleSided;
|
||||
else if (meshField.Name == "Metallic") meshJson["Metallic"] = mesh.Metallic;
|
||||
else if (meshField.Name == "Roughness") meshJson["Roughness"] = mesh.Roughness;
|
||||
else if (meshField.Name == "AlphaMode") meshJson["AlphaMode"] = static_cast<int>(mesh.AlphaMode);
|
||||
else if (meshField.Name == "AlphaCutoff") meshJson["AlphaCutoff"] = mesh.AlphaCutoff;
|
||||
else if (meshField.Name == "EmissiveColor") meshJson["EmissiveColor"] = Vec3ToJson(mesh.EmissiveColor);
|
||||
else if (meshField.Name == "BaseColor") meshJson["BaseColor"] = Vec3ToJson(mesh.BaseColor);
|
||||
else if (meshField.Name == "ModelNodeIndex") meshJson["ModelNodeIndex"] = mesh.ModelNodeIndex;
|
||||
else if (meshField.Name == "SubMeshIndex") meshJson["SubMeshIndex"] = mesh.SubMeshIndex;
|
||||
else if (meshField.Name == "Visible") meshJson["Visible"] = mesh.Visible;
|
||||
}
|
||||
objJson["MeshRenderer"] = meshJson;
|
||||
}
|
||||
} else if (objField.Name == "Light" && obj.Light.has_value()) {
|
||||
const MetaCoreStructDescriptor* lightDesc = registry.FindStruct<MetaCoreLightComponent>();
|
||||
if (lightDesc) {
|
||||
json lightJson = json::object();
|
||||
for (const auto& lightField : lightDesc->Fields) {
|
||||
if (lightField.Name == "Color") lightJson["Color"] = Vec3ToJson(obj.Light->Color);
|
||||
else if (lightField.Name == "Intensity") lightJson["Intensity"] = obj.Light->Intensity;
|
||||
}
|
||||
objJson["Light"] = lightJson;
|
||||
}
|
||||
} else if (objField.Name == "Camera" && obj.Camera.has_value()) {
|
||||
const MetaCoreStructDescriptor* camDesc = registry.FindStruct<MetaCoreCameraComponent>();
|
||||
if (camDesc) {
|
||||
json camJson = json::object();
|
||||
for (const auto& camField : camDesc->Fields) {
|
||||
if (camField.Name == "FieldOfViewDegrees") camJson["FieldOfViewDegrees"] = obj.Camera->FieldOfViewDegrees;
|
||||
else if (camField.Name == "NearClip") camJson["NearClip"] = obj.Camera->NearClip;
|
||||
else if (camField.Name == "FarClip") camJson["FarClip"] = obj.Camera->FarClip;
|
||||
else if (camField.Name == "IsPrimary") camJson["IsPrimary"] = obj.Camera->IsPrimary;
|
||||
}
|
||||
objJson["Camera"] = camJson;
|
||||
}
|
||||
} else if (objField.Name == "PrefabInstance" && obj.PrefabInstance.has_value()) {
|
||||
const MetaCoreStructDescriptor* prefabDesc = registry.FindStruct<MetaCorePrefabInstanceMetadata>();
|
||||
if (prefabDesc) {
|
||||
json prefabJson = json::object();
|
||||
for (const auto& prefabField : prefabDesc->Fields) {
|
||||
if (prefabField.Name == "PrefabAssetGuid") prefabJson["PrefabAssetGuid"] = GuidToString(obj.PrefabInstance->PrefabAssetGuid);
|
||||
else if (prefabField.Name == "PrefabObjectId") prefabJson["PrefabObjectId"] = obj.PrefabInstance->PrefabObjectId;
|
||||
else if (prefabField.Name == "PrefabInstanceRootId") prefabJson["PrefabInstanceRootId"] = obj.PrefabInstance->PrefabInstanceRootId;
|
||||
}
|
||||
objJson["PrefabInstance"] = prefabJson;
|
||||
}
|
||||
}
|
||||
}
|
||||
return objJson;
|
||||
}
|
||||
|
||||
// 辅助方法:从 json 格式中恢复单个 MetaCoreGameObjectData
|
||||
static MetaCoreGameObjectData JsonToGameObjectData(const json& objJson, const MetaCoreTypeRegistry& registry) {
|
||||
MetaCoreGameObjectData obj;
|
||||
const MetaCoreStructDescriptor* objDesc = registry.FindStruct<MetaCoreGameObjectData>();
|
||||
if (!objDesc) return obj;
|
||||
|
||||
for (const auto& objField : objDesc->Fields) {
|
||||
if (objField.Name == "Id" && objJson.contains("Id")) {
|
||||
obj.Id = objJson["Id"].get<MetaCoreId>();
|
||||
} else if (objField.Name == "ParentId" && objJson.contains("ParentId")) {
|
||||
obj.ParentId = objJson["ParentId"].get<MetaCoreId>();
|
||||
} else if (objField.Name == "Name" && objJson.contains("Name")) {
|
||||
obj.Name = objJson["Name"].get<std::string>();
|
||||
} else if (objField.Name == "Transform" && objJson.contains("Transform")) {
|
||||
const auto& transJson = objJson["Transform"];
|
||||
const MetaCoreStructDescriptor* transDesc = registry.FindStruct<MetaCoreTransformComponent>();
|
||||
if (transDesc) {
|
||||
for (const auto& transField : transDesc->Fields) {
|
||||
if (transField.Name == "Position" && transJson.contains("Position")) {
|
||||
obj.Transform.Position = JsonToVec3(transJson["Position"]);
|
||||
} else if (transField.Name == "RotationEulerDegrees" && transJson.contains("RotationEulerDegrees")) {
|
||||
obj.Transform.RotationEulerDegrees = JsonToVec3(transJson["RotationEulerDegrees"]);
|
||||
} else if (transField.Name == "Scale" && transJson.contains("Scale")) {
|
||||
obj.Transform.Scale = JsonToVec3(transJson["Scale"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (objField.Name == "MeshRenderer" && objJson.contains("MeshRenderer")) {
|
||||
const auto& meshJson = objJson["MeshRenderer"];
|
||||
const MetaCoreStructDescriptor* meshDesc = registry.FindStruct<MetaCoreMeshRendererComponent>();
|
||||
if (meshDesc) {
|
||||
MetaCoreMeshRendererComponent mesh;
|
||||
for (const auto& meshField : meshDesc->Fields) {
|
||||
if (meshField.Name == "MeshSource" && meshJson.contains("MeshSource")) mesh.MeshSource = static_cast<MetaCoreMeshSourceKind>(meshJson["MeshSource"].get<int>());
|
||||
else if (meshField.Name == "BuiltinMesh" && meshJson.contains("BuiltinMesh")) mesh.BuiltinMesh = static_cast<MetaCoreBuiltinMeshType>(meshJson["BuiltinMesh"].get<int>());
|
||||
else if (meshField.Name == "MeshAssetGuid" && meshJson.contains("MeshAssetGuid")) mesh.MeshAssetGuid = StringToGuid(meshJson["MeshAssetGuid"].get<std::string>());
|
||||
else if (meshField.Name == "MaterialAssetGuids" && meshJson.contains("MaterialAssetGuids") && meshJson["MaterialAssetGuids"].is_array()) {
|
||||
for (const auto& mGuidJson : meshJson["MaterialAssetGuids"]) {
|
||||
mesh.MaterialAssetGuids.push_back(StringToGuid(mGuidJson.get<std::string>()));
|
||||
}
|
||||
}
|
||||
else if (meshField.Name == "SourceModelAssetGuid" && meshJson.contains("SourceModelAssetGuid")) mesh.SourceModelAssetGuid = StringToGuid(meshJson["SourceModelAssetGuid"].get<std::string>());
|
||||
else if (meshField.Name == "SourceModelPath" && meshJson.contains("SourceModelPath")) mesh.SourceModelPath = meshJson["SourceModelPath"].get<std::string>();
|
||||
else if (meshField.Name == "SourceNodePath" && meshJson.contains("SourceNodePath")) mesh.SourceNodePath = meshJson["SourceNodePath"].get<std::string>();
|
||||
else if (meshField.Name == "BaseColorTextureGuid" && meshJson.contains("BaseColorTextureGuid")) mesh.BaseColorTextureGuid = StringToGuid(meshJson["BaseColorTextureGuid"].get<std::string>());
|
||||
else if (meshField.Name == "MetallicRoughnessTextureGuid" && meshJson.contains("MetallicRoughnessTextureGuid")) mesh.MetallicRoughnessTextureGuid = StringToGuid(meshJson["MetallicRoughnessTextureGuid"].get<std::string>());
|
||||
else if (meshField.Name == "NormalTextureGuid" && meshJson.contains("NormalTextureGuid")) mesh.NormalTextureGuid = StringToGuid(meshJson["NormalTextureGuid"].get<std::string>());
|
||||
else if (meshField.Name == "EmissiveTextureGuid" && meshJson.contains("EmissiveTextureGuid")) mesh.EmissiveTextureGuid = StringToGuid(meshJson["EmissiveTextureGuid"].get<std::string>());
|
||||
else if (meshField.Name == "AoTextureGuid" && meshJson.contains("AoTextureGuid")) mesh.AoTextureGuid = StringToGuid(meshJson["AoTextureGuid"].get<std::string>());
|
||||
else if (meshField.Name == "BaseColorTexturePath" && meshJson.contains("BaseColorTexturePath")) mesh.BaseColorTexturePath = meshJson["BaseColorTexturePath"].get<std::string>();
|
||||
else if (meshField.Name == "MetallicRoughnessTexturePath" && meshJson.contains("MetallicRoughnessTexturePath")) mesh.MetallicRoughnessTexturePath = meshJson["MetallicRoughnessTexturePath"].get<std::string>();
|
||||
else if (meshField.Name == "NormalTexturePath" && meshJson.contains("NormalTexturePath")) mesh.NormalTexturePath = meshJson["NormalTexturePath"].get<std::string>();
|
||||
else if (meshField.Name == "EmissiveTexturePath" && meshJson.contains("EmissiveTexturePath")) mesh.EmissiveTexturePath = meshJson["EmissiveTexturePath"].get<std::string>();
|
||||
else if (meshField.Name == "AoTexturePath" && meshJson.contains("AoTexturePath")) mesh.AoTexturePath = meshJson["AoTexturePath"].get<std::string>();
|
||||
else if (meshField.Name == "DoubleSided" && meshJson.contains("DoubleSided")) mesh.DoubleSided = meshJson["DoubleSided"].get<bool>();
|
||||
else if (meshField.Name == "Metallic" && meshJson.contains("Metallic")) mesh.Metallic = meshJson["Metallic"].get<float>();
|
||||
else if (meshField.Name == "Roughness" && meshJson.contains("Roughness")) mesh.Roughness = meshJson["Roughness"].get<float>();
|
||||
else if (meshField.Name == "AlphaMode" && meshJson.contains("AlphaMode")) mesh.AlphaMode = static_cast<MetaCoreMeshAlphaMode>(meshJson["AlphaMode"].get<int>());
|
||||
else if (meshField.Name == "AlphaCutoff" && meshJson.contains("AlphaCutoff")) mesh.AlphaCutoff = meshJson["AlphaCutoff"].get<float>();
|
||||
else if (meshField.Name == "EmissiveColor" && meshJson.contains("EmissiveColor")) mesh.EmissiveColor = JsonToVec3(meshJson["EmissiveColor"]);
|
||||
else if (meshField.Name == "BaseColor" && meshJson.contains("BaseColor")) mesh.BaseColor = JsonToVec3(meshJson["BaseColor"]);
|
||||
else if (meshField.Name == "ModelNodeIndex" && meshJson.contains("ModelNodeIndex")) mesh.ModelNodeIndex = meshJson["ModelNodeIndex"].get<std::int32_t>();
|
||||
else if (meshField.Name == "SubMeshIndex" && meshJson.contains("SubMeshIndex")) mesh.SubMeshIndex = meshJson["SubMeshIndex"].get<std::int32_t>();
|
||||
else if (meshField.Name == "Visible" && meshJson.contains("Visible")) mesh.Visible = meshJson["Visible"].get<bool>();
|
||||
}
|
||||
obj.MeshRenderer = mesh;
|
||||
}
|
||||
} else if (objField.Name == "Light" && objJson.contains("Light")) {
|
||||
const auto& lightJson = objJson["Light"];
|
||||
const MetaCoreStructDescriptor* lightDesc = registry.FindStruct<MetaCoreLightComponent>();
|
||||
if (lightDesc) {
|
||||
MetaCoreLightComponent light;
|
||||
for (const auto& lightField : lightDesc->Fields) {
|
||||
if (lightField.Name == "Color" && lightJson.contains("Color")) light.Color = JsonToVec3(lightJson["Color"]);
|
||||
else if (lightField.Name == "Intensity" && lightJson.contains("Intensity")) light.Intensity = lightJson["Intensity"].get<float>();
|
||||
}
|
||||
obj.Light = light;
|
||||
}
|
||||
} else if (objField.Name == "Camera" && objJson.contains("Camera")) {
|
||||
const auto& camJson = objJson["Camera"];
|
||||
const MetaCoreStructDescriptor* camDesc = registry.FindStruct<MetaCoreCameraComponent>();
|
||||
if (camDesc) {
|
||||
MetaCoreCameraComponent cam;
|
||||
for (const auto& camField : camDesc->Fields) {
|
||||
if (camField.Name == "FieldOfViewDegrees" && camJson.contains("FieldOfViewDegrees")) cam.FieldOfViewDegrees = camJson["FieldOfViewDegrees"].get<float>();
|
||||
else if (camField.Name == "NearClip" && camJson.contains("NearClip")) cam.NearClip = camJson["NearClip"].get<float>();
|
||||
else if (camField.Name == "FarClip" && camJson.contains("FarClip")) cam.FarClip = camJson["FarClip"].get<float>();
|
||||
else if (camField.Name == "IsPrimary" && camJson.contains("IsPrimary")) cam.IsPrimary = camJson["IsPrimary"].get<bool>();
|
||||
}
|
||||
obj.Camera = cam;
|
||||
}
|
||||
} else if (objField.Name == "PrefabInstance" && objJson.contains("PrefabInstance")) {
|
||||
const auto& prefabJson = objJson["PrefabInstance"];
|
||||
const MetaCoreStructDescriptor* prefabDesc = registry.FindStruct<MetaCorePrefabInstanceMetadata>();
|
||||
if (prefabDesc) {
|
||||
MetaCorePrefabInstanceMetadata prefab;
|
||||
for (const auto& prefabField : prefabDesc->Fields) {
|
||||
if (prefabField.Name == "PrefabAssetGuid" && prefabJson.contains("PrefabAssetGuid")) prefab.PrefabAssetGuid = StringToGuid(prefabJson["PrefabAssetGuid"].get<std::string>());
|
||||
else if (prefabField.Name == "PrefabObjectId" && prefabJson.contains("PrefabObjectId")) prefab.PrefabObjectId = prefabJson["PrefabObjectId"].get<MetaCoreId>();
|
||||
else if (prefabField.Name == "PrefabInstanceRootId" && prefabJson.contains("PrefabInstanceRootId")) prefab.PrefabInstanceRootId = prefabJson["PrefabInstanceRootId"].get<MetaCoreId>();
|
||||
}
|
||||
obj.PrefabInstance = prefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool MetaCoreSceneSerializer::SaveSceneToJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreSceneDocument& sceneDocument,
|
||||
@ -39,7 +251,7 @@ bool MetaCoreSceneSerializer::SaveSceneToJson(
|
||||
) {
|
||||
try {
|
||||
json sceneJson;
|
||||
|
||||
|
||||
// 验证 MetaCoreSceneDocument 反射注册
|
||||
const MetaCoreStructDescriptor* sceneDesc = registry.FindStruct<MetaCoreSceneDocument>();
|
||||
if (!sceneDesc) {
|
||||
@ -63,108 +275,8 @@ bool MetaCoreSceneSerializer::SaveSceneToJson(
|
||||
}
|
||||
} else if (field.Name == "GameObjects") {
|
||||
json gameObjectsArray = json::array();
|
||||
const MetaCoreStructDescriptor* objDesc = registry.FindStruct<MetaCoreGameObjectData>();
|
||||
|
||||
if (objDesc) {
|
||||
for (const auto& obj : sceneDocument.GameObjects) {
|
||||
json objJson = json::object();
|
||||
for (const auto& objField : objDesc->Fields) {
|
||||
if (objField.Name == "Id") {
|
||||
objJson["Id"] = obj.Id;
|
||||
} else if (objField.Name == "ParentId") {
|
||||
objJson["ParentId"] = obj.ParentId;
|
||||
} else if (objField.Name == "Name") {
|
||||
objJson["Name"] = obj.Name;
|
||||
} else if (objField.Name == "Transform") {
|
||||
const MetaCoreStructDescriptor* transDesc = registry.FindStruct<MetaCoreTransformComponent>();
|
||||
if (transDesc) {
|
||||
json transJson = json::object();
|
||||
for (const auto& transField : transDesc->Fields) {
|
||||
if (transField.Name == "Position") transJson["Position"] = Vec3ToJson(obj.Transform.Position);
|
||||
else if (transField.Name == "RotationEulerDegrees") transJson["RotationEulerDegrees"] = Vec3ToJson(obj.Transform.RotationEulerDegrees);
|
||||
else if (transField.Name == "Scale") transJson["Scale"] = Vec3ToJson(obj.Transform.Scale);
|
||||
}
|
||||
objJson["Transform"] = transJson;
|
||||
}
|
||||
} else if (objField.Name == "MeshRenderer" && obj.MeshRenderer.has_value()) {
|
||||
const MetaCoreStructDescriptor* meshDesc = registry.FindStruct<MetaCoreMeshRendererComponent>();
|
||||
if (meshDesc) {
|
||||
json meshJson = json::object();
|
||||
const auto& mesh = obj.MeshRenderer.value();
|
||||
for (const auto& meshField : meshDesc->Fields) {
|
||||
if (meshField.Name == "MeshSource") meshJson["MeshSource"] = static_cast<int>(mesh.MeshSource);
|
||||
else if (meshField.Name == "BuiltinMesh") meshJson["BuiltinMesh"] = static_cast<int>(mesh.BuiltinMesh);
|
||||
else if (meshField.Name == "MeshAssetGuid") meshJson["MeshAssetGuid"] = GuidToString(mesh.MeshAssetGuid);
|
||||
else if (meshField.Name == "MaterialAssetGuids") {
|
||||
json matGuids = json::array();
|
||||
for (const auto& guid : mesh.MaterialAssetGuids) {
|
||||
matGuids.push_back(GuidToString(guid));
|
||||
}
|
||||
meshJson["MaterialAssetGuids"] = matGuids;
|
||||
}
|
||||
else if (meshField.Name == "SourceModelAssetGuid") meshJson["SourceModelAssetGuid"] = GuidToString(mesh.SourceModelAssetGuid);
|
||||
else if (meshField.Name == "SourceModelPath") meshJson["SourceModelPath"] = mesh.SourceModelPath;
|
||||
else if (meshField.Name == "SourceNodePath") meshJson["SourceNodePath"] = mesh.SourceNodePath;
|
||||
else if (meshField.Name == "BaseColorTextureGuid") meshJson["BaseColorTextureGuid"] = GuidToString(mesh.BaseColorTextureGuid);
|
||||
else if (meshField.Name == "MetallicRoughnessTextureGuid") meshJson["MetallicRoughnessTextureGuid"] = GuidToString(mesh.MetallicRoughnessTextureGuid);
|
||||
else if (meshField.Name == "NormalTextureGuid") meshJson["NormalTextureGuid"] = GuidToString(mesh.NormalTextureGuid);
|
||||
else if (meshField.Name == "EmissiveTextureGuid") meshJson["EmissiveTextureGuid"] = GuidToString(mesh.EmissiveTextureGuid);
|
||||
else if (meshField.Name == "AoTextureGuid") meshJson["AoTextureGuid"] = GuidToString(mesh.AoTextureGuid);
|
||||
else if (meshField.Name == "BaseColorTexturePath") meshJson["BaseColorTexturePath"] = mesh.BaseColorTexturePath;
|
||||
else if (meshField.Name == "MetallicRoughnessTexturePath") meshJson["MetallicRoughnessTexturePath"] = mesh.MetallicRoughnessTexturePath;
|
||||
else if (meshField.Name == "NormalTexturePath") meshJson["NormalTexturePath"] = mesh.NormalTexturePath;
|
||||
else if (meshField.Name == "EmissiveTexturePath") meshJson["EmissiveTexturePath"] = mesh.EmissiveTexturePath;
|
||||
else if (meshField.Name == "AoTexturePath") meshJson["AoTexturePath"] = mesh.AoTexturePath;
|
||||
else if (meshField.Name == "DoubleSided") meshJson["DoubleSided"] = mesh.DoubleSided;
|
||||
else if (meshField.Name == "Metallic") meshJson["Metallic"] = mesh.Metallic;
|
||||
else if (meshField.Name == "Roughness") meshJson["Roughness"] = mesh.Roughness;
|
||||
else if (meshField.Name == "AlphaMode") meshJson["AlphaMode"] = static_cast<int>(mesh.AlphaMode);
|
||||
else if (meshField.Name == "AlphaCutoff") meshJson["AlphaCutoff"] = mesh.AlphaCutoff;
|
||||
else if (meshField.Name == "EmissiveColor") meshJson["EmissiveColor"] = Vec3ToJson(mesh.EmissiveColor);
|
||||
else if (meshField.Name == "BaseColor") meshJson["BaseColor"] = Vec3ToJson(mesh.BaseColor);
|
||||
else if (meshField.Name == "ModelNodeIndex") meshJson["ModelNodeIndex"] = mesh.ModelNodeIndex;
|
||||
else if (meshField.Name == "SubMeshIndex") meshJson["SubMeshIndex"] = mesh.SubMeshIndex;
|
||||
else if (meshField.Name == "Visible") meshJson["Visible"] = mesh.Visible;
|
||||
}
|
||||
objJson["MeshRenderer"] = meshJson;
|
||||
}
|
||||
} else if (objField.Name == "Light" && obj.Light.has_value()) {
|
||||
const MetaCoreStructDescriptor* lightDesc = registry.FindStruct<MetaCoreLightComponent>();
|
||||
if (lightDesc) {
|
||||
json lightJson = json::object();
|
||||
for (const auto& lightField : lightDesc->Fields) {
|
||||
if (lightField.Name == "Color") lightJson["Color"] = Vec3ToJson(obj.Light->Color);
|
||||
else if (lightField.Name == "Intensity") lightJson["Intensity"] = obj.Light->Intensity;
|
||||
}
|
||||
objJson["Light"] = lightJson;
|
||||
}
|
||||
} else if (objField.Name == "Camera" && obj.Camera.has_value()) {
|
||||
const MetaCoreStructDescriptor* camDesc = registry.FindStruct<MetaCoreCameraComponent>();
|
||||
if (camDesc) {
|
||||
json camJson = json::object();
|
||||
for (const auto& camField : camDesc->Fields) {
|
||||
if (camField.Name == "FieldOfViewDegrees") camJson["FieldOfViewDegrees"] = obj.Camera->FieldOfViewDegrees;
|
||||
else if (camField.Name == "NearClip") camJson["NearClip"] = obj.Camera->NearClip;
|
||||
else if (camField.Name == "FarClip") camJson["FarClip"] = obj.Camera->FarClip;
|
||||
else if (camField.Name == "IsPrimary") camJson["IsPrimary"] = obj.Camera->IsPrimary;
|
||||
}
|
||||
objJson["Camera"] = camJson;
|
||||
}
|
||||
} else if (objField.Name == "PrefabInstance" && obj.PrefabInstance.has_value()) {
|
||||
const MetaCoreStructDescriptor* prefabDesc = registry.FindStruct<MetaCorePrefabInstanceMetadata>();
|
||||
if (prefabDesc) {
|
||||
json prefabJson = json::object();
|
||||
for (const auto& prefabField : prefabDesc->Fields) {
|
||||
if (prefabField.Name == "PrefabAssetGuid") prefabJson["PrefabAssetGuid"] = GuidToString(obj.PrefabInstance->PrefabAssetGuid);
|
||||
else if (prefabField.Name == "PrefabObjectId") prefabJson["PrefabObjectId"] = obj.PrefabInstance->PrefabObjectId;
|
||||
else if (prefabField.Name == "PrefabInstanceRootId") prefabJson["PrefabInstanceRootId"] = obj.PrefabInstance->PrefabInstanceRootId;
|
||||
}
|
||||
objJson["PrefabInstance"] = prefabJson;
|
||||
}
|
||||
}
|
||||
}
|
||||
gameObjectsArray.push_back(objJson);
|
||||
}
|
||||
for (const auto& obj : sceneDocument.GameObjects) {
|
||||
gameObjectsArray.push_back(GameObjectDataToJson(obj, registry));
|
||||
}
|
||||
sceneJson["GameObjects"] = gameObjectsArray;
|
||||
}
|
||||
@ -199,7 +311,7 @@ std::optional<MetaCoreSceneDocument> MetaCoreSceneSerializer::LoadSceneFromJson(
|
||||
file >> sceneJson;
|
||||
|
||||
MetaCoreSceneDocument sceneDocument;
|
||||
|
||||
|
||||
// 验证 MetaCoreSceneDocument 反射注册
|
||||
const MetaCoreStructDescriptor* sceneDesc = registry.FindStruct<MetaCoreSceneDocument>();
|
||||
if (!sceneDesc) {
|
||||
@ -225,112 +337,8 @@ std::optional<MetaCoreSceneDocument> MetaCoreSceneSerializer::LoadSceneFromJson(
|
||||
}
|
||||
}
|
||||
} else if (field.Name == "GameObjects" && sceneJson.contains("GameObjects") && sceneJson["GameObjects"].is_array()) {
|
||||
const MetaCoreStructDescriptor* objDesc = registry.FindStruct<MetaCoreGameObjectData>();
|
||||
if (objDesc) {
|
||||
for (const auto& objJson : sceneJson["GameObjects"]) {
|
||||
MetaCoreGameObjectData obj;
|
||||
|
||||
for (const auto& objField : objDesc->Fields) {
|
||||
if (objField.Name == "Id" && objJson.contains("Id")) {
|
||||
obj.Id = objJson["Id"].get<MetaCoreId>();
|
||||
} else if (objField.Name == "ParentId" && objJson.contains("ParentId")) {
|
||||
obj.ParentId = objJson["ParentId"].get<MetaCoreId>();
|
||||
} else if (objField.Name == "Name" && objJson.contains("Name")) {
|
||||
obj.Name = objJson["Name"].get<std::string>();
|
||||
} else if (objField.Name == "Transform" && objJson.contains("Transform")) {
|
||||
const auto& transJson = objJson["Transform"];
|
||||
const MetaCoreStructDescriptor* transDesc = registry.FindStruct<MetaCoreTransformComponent>();
|
||||
if (transDesc) {
|
||||
for (const auto& transField : transDesc->Fields) {
|
||||
if (transField.Name == "Position" && transJson.contains("Position")) {
|
||||
obj.Transform.Position = JsonToVec3(transJson["Position"]);
|
||||
} else if (transField.Name == "RotationEulerDegrees" && transJson.contains("RotationEulerDegrees")) {
|
||||
obj.Transform.RotationEulerDegrees = JsonToVec3(transJson["RotationEulerDegrees"]);
|
||||
} else if (transField.Name == "Scale" && transJson.contains("Scale")) {
|
||||
obj.Transform.Scale = JsonToVec3(transJson["Scale"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (objField.Name == "MeshRenderer" && objJson.contains("MeshRenderer")) {
|
||||
const auto& meshJson = objJson["MeshRenderer"];
|
||||
const MetaCoreStructDescriptor* meshDesc = registry.FindStruct<MetaCoreMeshRendererComponent>();
|
||||
if (meshDesc) {
|
||||
MetaCoreMeshRendererComponent mesh;
|
||||
for (const auto& meshField : meshDesc->Fields) {
|
||||
if (meshField.Name == "MeshSource" && meshJson.contains("MeshSource")) mesh.MeshSource = static_cast<MetaCoreMeshSourceKind>(meshJson["MeshSource"].get<int>());
|
||||
else if (meshField.Name == "BuiltinMesh" && meshJson.contains("BuiltinMesh")) mesh.BuiltinMesh = static_cast<MetaCoreBuiltinMeshType>(meshJson["BuiltinMesh"].get<int>());
|
||||
else if (meshField.Name == "MeshAssetGuid" && meshJson.contains("MeshAssetGuid")) mesh.MeshAssetGuid = StringToGuid(meshJson["MeshAssetGuid"].get<std::string>());
|
||||
else if (meshField.Name == "MaterialAssetGuids" && meshJson.contains("MaterialAssetGuids") && meshJson["MaterialAssetGuids"].is_array()) {
|
||||
for (const auto& mGuidJson : meshJson["MaterialAssetGuids"]) {
|
||||
mesh.MaterialAssetGuids.push_back(StringToGuid(mGuidJson.get<std::string>()));
|
||||
}
|
||||
}
|
||||
else if (meshField.Name == "SourceModelAssetGuid" && meshJson.contains("SourceModelAssetGuid")) mesh.SourceModelAssetGuid = StringToGuid(meshJson["SourceModelAssetGuid"].get<std::string>());
|
||||
else if (meshField.Name == "SourceModelPath" && meshJson.contains("SourceModelPath")) mesh.SourceModelPath = meshJson["SourceModelPath"].get<std::string>();
|
||||
else if (meshField.Name == "SourceNodePath" && meshJson.contains("SourceNodePath")) mesh.SourceNodePath = meshJson["SourceNodePath"].get<std::string>();
|
||||
else if (meshField.Name == "BaseColorTextureGuid" && meshJson.contains("BaseColorTextureGuid")) mesh.BaseColorTextureGuid = StringToGuid(meshJson["BaseColorTextureGuid"].get<std::string>());
|
||||
else if (meshField.Name == "MetallicRoughnessTextureGuid" && meshJson.contains("MetallicRoughnessTextureGuid")) mesh.MetallicRoughnessTextureGuid = StringToGuid(meshJson["MetallicRoughnessTextureGuid"].get<std::string>());
|
||||
else if (meshField.Name == "NormalTextureGuid" && meshJson.contains("NormalTextureGuid")) mesh.NormalTextureGuid = StringToGuid(meshJson["NormalTextureGuid"].get<std::string>());
|
||||
else if (meshField.Name == "EmissiveTextureGuid" && meshJson.contains("EmissiveTextureGuid")) mesh.EmissiveTextureGuid = StringToGuid(meshJson["EmissiveTextureGuid"].get<std::string>());
|
||||
else if (meshField.Name == "AoTextureGuid" && meshJson.contains("AoTextureGuid")) mesh.AoTextureGuid = StringToGuid(meshJson["AoTextureGuid"].get<std::string>());
|
||||
else if (meshField.Name == "BaseColorTexturePath" && meshJson.contains("BaseColorTexturePath")) mesh.BaseColorTexturePath = meshJson["BaseColorTexturePath"].get<std::string>();
|
||||
else if (meshField.Name == "MetallicRoughnessTexturePath" && meshJson.contains("MetallicRoughnessTexturePath")) mesh.MetallicRoughnessTexturePath = meshJson["MetallicRoughnessTexturePath"].get<std::string>();
|
||||
else if (meshField.Name == "NormalTexturePath" && meshJson.contains("NormalTexturePath")) mesh.NormalTexturePath = meshJson["NormalTexturePath"].get<std::string>();
|
||||
else if (meshField.Name == "EmissiveTexturePath" && meshJson.contains("EmissiveTexturePath")) mesh.EmissiveTexturePath = meshJson["EmissiveTexturePath"].get<std::string>();
|
||||
else if (meshField.Name == "AoTexturePath" && meshJson.contains("AoTexturePath")) mesh.AoTexturePath = meshJson["AoTexturePath"].get<std::string>();
|
||||
else if (meshField.Name == "DoubleSided" && meshJson.contains("DoubleSided")) mesh.DoubleSided = meshJson["DoubleSided"].get<bool>();
|
||||
else if (meshField.Name == "Metallic" && meshJson.contains("Metallic")) mesh.Metallic = meshJson["Metallic"].get<float>();
|
||||
else if (meshField.Name == "Roughness" && meshJson.contains("Roughness")) mesh.Roughness = meshJson["Roughness"].get<float>();
|
||||
else if (meshField.Name == "AlphaMode" && meshJson.contains("AlphaMode")) mesh.AlphaMode = static_cast<MetaCoreMeshAlphaMode>(meshJson["AlphaMode"].get<int>());
|
||||
else if (meshField.Name == "AlphaCutoff" && meshJson.contains("AlphaCutoff")) mesh.AlphaCutoff = meshJson["AlphaCutoff"].get<float>();
|
||||
else if (meshField.Name == "EmissiveColor" && meshJson.contains("EmissiveColor")) mesh.EmissiveColor = JsonToVec3(meshJson["EmissiveColor"]);
|
||||
else if (meshField.Name == "BaseColor" && meshJson.contains("BaseColor")) mesh.BaseColor = JsonToVec3(meshJson["BaseColor"]);
|
||||
else if (meshField.Name == "ModelNodeIndex" && meshJson.contains("ModelNodeIndex")) mesh.ModelNodeIndex = meshJson["ModelNodeIndex"].get<std::int32_t>();
|
||||
else if (meshField.Name == "SubMeshIndex" && meshJson.contains("SubMeshIndex")) mesh.SubMeshIndex = meshJson["SubMeshIndex"].get<std::int32_t>();
|
||||
else if (meshField.Name == "Visible" && meshJson.contains("Visible")) mesh.Visible = meshJson["Visible"].get<bool>();
|
||||
}
|
||||
obj.MeshRenderer = mesh;
|
||||
}
|
||||
} else if (objField.Name == "Light" && objJson.contains("Light")) {
|
||||
const auto& lightJson = objJson["Light"];
|
||||
const MetaCoreStructDescriptor* lightDesc = registry.FindStruct<MetaCoreLightComponent>();
|
||||
if (lightDesc) {
|
||||
MetaCoreLightComponent light;
|
||||
for (const auto& lightField : lightDesc->Fields) {
|
||||
if (lightField.Name == "Color" && lightJson.contains("Color")) light.Color = JsonToVec3(lightJson["Color"]);
|
||||
else if (lightField.Name == "Intensity" && lightJson.contains("Intensity")) light.Intensity = lightJson["Intensity"].get<float>();
|
||||
}
|
||||
obj.Light = light;
|
||||
}
|
||||
} else if (objField.Name == "Camera" && objJson.contains("Camera")) {
|
||||
const auto& camJson = objJson["Camera"];
|
||||
const MetaCoreStructDescriptor* camDesc = registry.FindStruct<MetaCoreCameraComponent>();
|
||||
if (camDesc) {
|
||||
MetaCoreCameraComponent cam;
|
||||
for (const auto& camField : camDesc->Fields) {
|
||||
if (camField.Name == "FieldOfViewDegrees" && camJson.contains("FieldOfViewDegrees")) cam.FieldOfViewDegrees = camJson["FieldOfViewDegrees"].get<float>();
|
||||
else if (camField.Name == "NearClip" && camJson.contains("NearClip")) cam.NearClip = camJson["NearClip"].get<float>();
|
||||
else if (camField.Name == "FarClip" && camJson.contains("FarClip")) cam.FarClip = camJson["FarClip"].get<float>();
|
||||
else if (camField.Name == "IsPrimary" && camJson.contains("IsPrimary")) cam.IsPrimary = camJson["IsPrimary"].get<bool>();
|
||||
}
|
||||
obj.Camera = cam;
|
||||
}
|
||||
} else if (objField.Name == "PrefabInstance" && objJson.contains("PrefabInstance")) {
|
||||
const auto& prefabJson = objJson["PrefabInstance"];
|
||||
const MetaCoreStructDescriptor* prefabDesc = registry.FindStruct<MetaCorePrefabInstanceMetadata>();
|
||||
if (prefabDesc) {
|
||||
MetaCorePrefabInstanceMetadata prefab;
|
||||
for (const auto& prefabField : prefabDesc->Fields) {
|
||||
if (prefabField.Name == "PrefabAssetGuid" && prefabJson.contains("PrefabAssetGuid")) prefab.PrefabAssetGuid = StringToGuid(prefabJson["PrefabAssetGuid"].get<std::string>());
|
||||
else if (prefabField.Name == "PrefabObjectId" && prefabJson.contains("PrefabObjectId")) prefab.PrefabObjectId = prefabJson["PrefabObjectId"].get<MetaCoreId>();
|
||||
else if (prefabField.Name == "PrefabInstanceRootId" && prefabJson.contains("PrefabInstanceRootId")) prefab.PrefabInstanceRootId = prefabJson["PrefabInstanceRootId"].get<MetaCoreId>();
|
||||
}
|
||||
obj.PrefabInstance = prefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
sceneDocument.GameObjects.push_back(obj);
|
||||
}
|
||||
for (const auto& objJson : sceneJson["GameObjects"]) {
|
||||
sceneDocument.GameObjects.push_back(JsonToGameObjectData(objJson, registry));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -342,4 +350,270 @@ std::optional<MetaCoreSceneDocument> MetaCoreSceneSerializer::LoadSceneFromJson(
|
||||
}
|
||||
}
|
||||
|
||||
// Prefab
|
||||
bool MetaCoreSceneSerializer::SavePrefabToJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCorePrefabDocument& prefabDocument,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
) {
|
||||
try {
|
||||
json prefabJson;
|
||||
prefabJson["PrefabName"] = prefabDocument.Name;
|
||||
json gameObjectsArray = json::array();
|
||||
for (const auto& obj : prefabDocument.GameObjects) {
|
||||
gameObjectsArray.push_back(GameObjectDataToJson(obj, registry));
|
||||
}
|
||||
prefabJson["GameObjects"] = gameObjectsArray;
|
||||
|
||||
std::ofstream file(absolutePath);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
file << prefabJson.dump(4);
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<MetaCorePrefabDocument> MetaCoreSceneSerializer::LoadPrefabFromJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
) {
|
||||
try {
|
||||
std::ifstream file(absolutePath);
|
||||
if (!file.is_open()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
json prefabJson;
|
||||
file >> prefabJson;
|
||||
|
||||
MetaCorePrefabDocument doc;
|
||||
if (prefabJson.contains("PrefabName")) {
|
||||
doc.Name = prefabJson["PrefabName"].get<std::string>();
|
||||
}
|
||||
if (prefabJson.contains("GameObjects") && prefabJson["GameObjects"].is_array()) {
|
||||
for (const auto& objJson : prefabJson["GameObjects"]) {
|
||||
doc.GameObjects.push_back(JsonToGameObjectData(objJson, registry));
|
||||
}
|
||||
}
|
||||
return doc;
|
||||
} catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// Material
|
||||
bool MetaCoreSceneSerializer::SaveMaterialToJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreMaterialAssetDocument& doc,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
) {
|
||||
(void)registry;
|
||||
try {
|
||||
json matJson;
|
||||
matJson["AssetGuid"] = GuidToString(doc.AssetGuid);
|
||||
matJson["Name"] = doc.Name;
|
||||
matJson["StableImportKey"] = doc.StableImportKey;
|
||||
matJson["ShaderModel"] = static_cast<int>(doc.ShaderModel);
|
||||
matJson["BaseColor"] = Vec3ToJson(doc.BaseColor);
|
||||
matJson["BaseColorTexture"] = GuidToString(doc.BaseColorTexture);
|
||||
matJson["NormalTexture"] = GuidToString(doc.NormalTexture);
|
||||
matJson["Metallic"] = doc.Metallic;
|
||||
matJson["Roughness"] = doc.Roughness;
|
||||
matJson["MetallicRoughnessTexture"] = GuidToString(doc.MetallicRoughnessTexture);
|
||||
matJson["AoTexture"] = GuidToString(doc.AoTexture);
|
||||
matJson["EmissiveColor"] = Vec3ToJson(doc.EmissiveColor);
|
||||
matJson["EmissiveTexture"] = GuidToString(doc.EmissiveTexture);
|
||||
matJson["AlphaMode"] = static_cast<int>(doc.AlphaMode);
|
||||
matJson["AlphaCutoff"] = doc.AlphaCutoff;
|
||||
matJson["DoubleSided"] = doc.DoubleSided;
|
||||
|
||||
std::ofstream file(absolutePath);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
file << matJson.dump(4);
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<MetaCoreMaterialAssetDocument> MetaCoreSceneSerializer::LoadMaterialFromJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
) {
|
||||
(void)registry;
|
||||
try {
|
||||
std::ifstream file(absolutePath);
|
||||
if (!file.is_open()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
json matJson;
|
||||
file >> matJson;
|
||||
|
||||
MetaCoreMaterialAssetDocument doc;
|
||||
if (matJson.contains("AssetGuid")) doc.AssetGuid = StringToGuid(matJson["AssetGuid"].get<std::string>());
|
||||
if (matJson.contains("Name")) doc.Name = matJson["Name"].get<std::string>();
|
||||
if (matJson.contains("StableImportKey")) doc.StableImportKey = matJson["StableImportKey"].get<std::string>();
|
||||
if (matJson.contains("ShaderModel")) doc.ShaderModel = static_cast<MetaCoreMaterialShaderModel>(matJson["ShaderModel"].get<int>());
|
||||
if (matJson.contains("BaseColor")) doc.BaseColor = JsonToVec3(matJson["BaseColor"]);
|
||||
if (matJson.contains("BaseColorTexture")) doc.BaseColorTexture = StringToGuid(matJson["BaseColorTexture"].get<std::string>());
|
||||
if (matJson.contains("NormalTexture")) doc.NormalTexture = StringToGuid(matJson["NormalTexture"].get<std::string>());
|
||||
if (matJson.contains("Metallic")) doc.Metallic = matJson["Metallic"].get<float>();
|
||||
if (matJson.contains("Roughness")) doc.Roughness = matJson["Roughness"].get<float>();
|
||||
if (matJson.contains("MetallicRoughnessTexture")) doc.MetallicRoughnessTexture = StringToGuid(matJson["MetallicRoughnessTexture"].get<std::string>());
|
||||
if (matJson.contains("AoTexture")) doc.AoTexture = StringToGuid(matJson["AoTexture"].get<std::string>());
|
||||
if (matJson.contains("EmissiveColor")) doc.EmissiveColor = JsonToVec3(matJson["EmissiveColor"]);
|
||||
if (matJson.contains("EmissiveTexture")) doc.EmissiveTexture = StringToGuid(matJson["EmissiveTexture"].get<std::string>());
|
||||
if (matJson.contains("AlphaMode")) doc.AlphaMode = static_cast<MetaCoreMaterialAlphaMode>(matJson["AlphaMode"].get<int>());
|
||||
if (matJson.contains("AlphaCutoff")) doc.AlphaCutoff = matJson["AlphaCutoff"].get<float>();
|
||||
if (matJson.contains("DoubleSided")) doc.DoubleSided = matJson["DoubleSided"].get<bool>();
|
||||
|
||||
return doc;
|
||||
} catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// UI 序列化辅助方法
|
||||
static json RectTransformToJson(const MetaCoreUiRectTransformDocument& trans) {
|
||||
json j = json::object();
|
||||
j["AnchorMin"] = Vec3ToJson(trans.AnchorMin);
|
||||
j["AnchorMax"] = Vec3ToJson(trans.AnchorMax);
|
||||
j["Pivot"] = Vec3ToJson(trans.Pivot);
|
||||
j["Position"] = Vec3ToJson(trans.Position);
|
||||
j["Size"] = Vec3ToJson(trans.Size);
|
||||
return j;
|
||||
}
|
||||
|
||||
static MetaCoreUiRectTransformDocument JsonToRectTransform(const json& j) {
|
||||
MetaCoreUiRectTransformDocument trans;
|
||||
if (j.contains("AnchorMin")) trans.AnchorMin = JsonToVec3(j["AnchorMin"]);
|
||||
if (j.contains("AnchorMax")) trans.AnchorMax = JsonToVec3(j["AnchorMax"]);
|
||||
if (j.contains("Pivot")) trans.Pivot = JsonToVec3(j["Pivot"]);
|
||||
if (j.contains("Position")) trans.Position = JsonToVec3(j["Position"]);
|
||||
if (j.contains("Size")) trans.Size = JsonToVec3(j["Size"]);
|
||||
return trans;
|
||||
}
|
||||
|
||||
static json UiStyleToJson(const MetaCoreUiStyleDocument& style) {
|
||||
json j = json::object();
|
||||
j["BackgroundColor"] = Vec3ToJson(style.BackgroundColor);
|
||||
j["TextColor"] = Vec3ToJson(style.TextColor);
|
||||
j["TintColor"] = Vec3ToJson(style.TintColor);
|
||||
j["FontSize"] = style.FontSize;
|
||||
j["Padding"] = Vec3ToJson(style.Padding);
|
||||
j["HorizontalAlignment"] = static_cast<int>(style.HorizontalAlignment);
|
||||
j["VerticalAlignment"] = static_cast<int>(style.VerticalAlignment);
|
||||
j["ImageAssetGuid"] = GuidToString(style.ImageAssetGuid);
|
||||
j["PreserveAspect"] = style.PreserveAspect;
|
||||
return j;
|
||||
}
|
||||
|
||||
static MetaCoreUiStyleDocument JsonToUiStyle(const json& j) {
|
||||
MetaCoreUiStyleDocument style;
|
||||
if (j.contains("BackgroundColor")) style.BackgroundColor = JsonToVec3(j["BackgroundColor"]);
|
||||
if (j.contains("TextColor")) style.TextColor = JsonToVec3(j["TextColor"]);
|
||||
if (j.contains("TintColor")) style.TintColor = JsonToVec3(j["TintColor"]);
|
||||
if (j.contains("FontSize")) style.FontSize = j["FontSize"].get<float>();
|
||||
if (j.contains("Padding")) style.Padding = JsonToVec3(j["Padding"]);
|
||||
if (j.contains("HorizontalAlignment")) style.HorizontalAlignment = static_cast<MetaCoreUiHorizontalAlignment>(j["HorizontalAlignment"].get<int>());
|
||||
if (j.contains("VerticalAlignment")) style.VerticalAlignment = static_cast<MetaCoreUiVerticalAlignment>(j["VerticalAlignment"].get<int>());
|
||||
if (j.contains("ImageAssetGuid")) style.ImageAssetGuid = StringToGuid(j["ImageAssetGuid"].get<std::string>());
|
||||
if (j.contains("PreserveAspect")) style.PreserveAspect = j["PreserveAspect"].get<bool>();
|
||||
return style;
|
||||
}
|
||||
|
||||
static json UiNodeToJson(const MetaCoreUiNodeDocument& node) {
|
||||
json j = json::object();
|
||||
j["Id"] = node.Id;
|
||||
j["Name"] = node.Name;
|
||||
j["Type"] = static_cast<int>(node.Type);
|
||||
j["ParentId"] = node.ParentId;
|
||||
j["Children"] = node.Children;
|
||||
j["Visible"] = node.Visible;
|
||||
j["RectTransform"] = RectTransformToJson(node.RectTransform);
|
||||
j["Style"] = UiStyleToJson(node.Style);
|
||||
j["Text"] = node.Text;
|
||||
j["Interactable"] = node.Interactable;
|
||||
return j;
|
||||
}
|
||||
|
||||
static MetaCoreUiNodeDocument JsonToUiNode(const json& j) {
|
||||
MetaCoreUiNodeDocument node;
|
||||
if (j.contains("Id")) node.Id = j["Id"].get<std::string>();
|
||||
if (j.contains("Name")) node.Name = j["Name"].get<std::string>();
|
||||
if (j.contains("Type")) node.Type = static_cast<MetaCoreUiNodeType>(j["Type"].get<int>());
|
||||
if (j.contains("ParentId")) node.ParentId = j["ParentId"].get<std::string>();
|
||||
if (j.contains("Children")) node.Children = j["Children"].get<std::vector<std::string>>();
|
||||
if (j.contains("Visible")) node.Visible = j["Visible"].get<bool>();
|
||||
if (j.contains("RectTransform")) node.RectTransform = JsonToRectTransform(j["RectTransform"]);
|
||||
if (j.contains("Style")) node.Style = JsonToUiStyle(j["Style"]);
|
||||
if (j.contains("Text")) node.Text = j["Text"].get<std::string>();
|
||||
if (j.contains("Interactable")) node.Interactable = j["Interactable"].get<bool>();
|
||||
return node;
|
||||
}
|
||||
|
||||
bool MetaCoreSceneSerializer::SaveUiToJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreUiDocument& uiDocument,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
) {
|
||||
(void)registry;
|
||||
try {
|
||||
json uiJson;
|
||||
uiJson["Name"] = uiDocument.Name;
|
||||
uiJson["ReferenceWidth"] = uiDocument.ReferenceWidth;
|
||||
uiJson["ReferenceHeight"] = uiDocument.ReferenceHeight;
|
||||
uiJson["RootNodeIds"] = uiDocument.RootNodeIds;
|
||||
|
||||
json nodesArray = json::array();
|
||||
for (const auto& node : uiDocument.Nodes) {
|
||||
nodesArray.push_back(UiNodeToJson(node));
|
||||
}
|
||||
uiJson["Nodes"] = nodesArray;
|
||||
|
||||
std::ofstream file(absolutePath);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
file << uiJson.dump(4);
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<MetaCoreUiDocument> MetaCoreSceneSerializer::LoadUiFromJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
) {
|
||||
(void)registry;
|
||||
try {
|
||||
std::ifstream file(absolutePath);
|
||||
if (!file.is_open()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
json uiJson;
|
||||
file >> uiJson;
|
||||
|
||||
MetaCoreUiDocument doc;
|
||||
if (uiJson.contains("Name")) doc.Name = uiJson["Name"].get<std::string>();
|
||||
if (uiJson.contains("ReferenceWidth")) doc.ReferenceWidth = uiJson["ReferenceWidth"].get<std::int32_t>();
|
||||
if (uiJson.contains("ReferenceHeight")) doc.ReferenceHeight = uiJson["ReferenceHeight"].get<std::int32_t>();
|
||||
if (uiJson.contains("RootNodeIds")) doc.RootNodeIds = uiJson["RootNodeIds"].get<std::vector<std::string>>();
|
||||
|
||||
if (uiJson.contains("Nodes") && uiJson["Nodes"].is_array()) {
|
||||
for (const auto& nodeJson : uiJson["Nodes"]) {
|
||||
doc.Nodes.push_back(JsonToUiNode(nodeJson));
|
||||
}
|
||||
}
|
||||
return doc;
|
||||
} catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -127,4 +127,12 @@ struct MetaCoreLightComponent {
|
||||
float Intensity = 1.5F;
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreModelRootTag {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string SourceModelPath;
|
||||
};
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -127,6 +127,8 @@ struct MetaCoreGameObjectData {
|
||||
std::optional<MetaCoreLightComponent> Light;
|
||||
MC_PROPERTY()
|
||||
std::optional<MetaCorePrefabInstanceMetadata> PrefabInstance;
|
||||
MC_PROPERTY()
|
||||
std::optional<MetaCoreModelRootTag> ModelRootTag;
|
||||
};
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -2,7 +2,10 @@
|
||||
|
||||
#include "MetaCoreFoundation/MetaCoreId.h"
|
||||
#include "MetaCoreFoundation/MetaCoreReflection.h"
|
||||
#include "MetaCoreFoundation/MetaCoreAssetGuid.h"
|
||||
#include "MetaCoreFoundation/MetaCoreAssetTypes.h"
|
||||
#include "MetaCoreScene/MetaCoreGameObject.h"
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -37,4 +40,146 @@ struct MetaCoreSceneDocument {
|
||||
MetaCoreEditorSelectionSnapshot Selection{};
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCorePrefabDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Name{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<MetaCoreGameObjectData> GameObjects{};
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreUiNodeType {
|
||||
Panel = 0,
|
||||
Text,
|
||||
Image,
|
||||
Button
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreUiHorizontalAlignment {
|
||||
Left = 0,
|
||||
Center,
|
||||
Right,
|
||||
Stretch
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreUiVerticalAlignment {
|
||||
Top = 0,
|
||||
Center,
|
||||
Bottom,
|
||||
Stretch
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreUiRectTransformDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 AnchorMin{0.0F, 0.0F, 0.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 AnchorMax{1.0F, 1.0F, 0.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 Pivot{0.5F, 0.5F, 0.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 Position{0.0F, 0.0F, 0.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 Size{100.0F, 100.0F, 0.0F};
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreUiStyleDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 BackgroundColor{0.15F, 0.15F, 0.15F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 TextColor{1.0F, 1.0F, 1.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 TintColor{1.0F, 1.0F, 1.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
float FontSize = 16.0F;
|
||||
|
||||
MC_PROPERTY()
|
||||
glm::vec3 Padding{8.0F, 8.0F, 0.0F};
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreUiHorizontalAlignment HorizontalAlignment = MetaCoreUiHorizontalAlignment::Left;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreUiVerticalAlignment VerticalAlignment = MetaCoreUiVerticalAlignment::Top;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreAssetGuid ImageAssetGuid{};
|
||||
|
||||
MC_PROPERTY()
|
||||
bool PreserveAspect = false;
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreUiNodeDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Id{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Name{};
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreUiNodeType Type = MetaCoreUiNodeType::Panel;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string ParentId{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<std::string> Children{};
|
||||
|
||||
MC_PROPERTY()
|
||||
bool Visible = true;
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreUiRectTransformDocument RectTransform{};
|
||||
|
||||
MC_PROPERTY()
|
||||
MetaCoreUiStyleDocument Style{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Text{};
|
||||
|
||||
MC_PROPERTY()
|
||||
bool Interactable = false;
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreUiDocument {
|
||||
MC_GENERATED_BODY()
|
||||
|
||||
MC_PROPERTY()
|
||||
std::string Name{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::int32_t ReferenceWidth = 1920;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::int32_t ReferenceHeight = 1080;
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<std::string> RootNodeIds{};
|
||||
|
||||
MC_PROPERTY()
|
||||
std::vector<MetaCoreUiNodeDocument> Nodes{};
|
||||
};
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "MetaCoreScene/MetaCoreScene.h"
|
||||
#include "MetaCoreScene/MetaCoreSceneDocument.h"
|
||||
#include "MetaCoreFoundation/MetaCoreAssetTypes.h"
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
@ -22,6 +24,39 @@ public:
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
);
|
||||
|
||||
// Prefab
|
||||
static bool SavePrefabToJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCorePrefabDocument& prefabDocument,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
);
|
||||
static std::optional<MetaCorePrefabDocument> LoadPrefabFromJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
);
|
||||
|
||||
// Material
|
||||
static bool SaveMaterialToJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreMaterialAssetDocument& materialDocument,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
);
|
||||
static std::optional<MetaCoreMaterialAssetDocument> LoadMaterialFromJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
);
|
||||
|
||||
// UI
|
||||
static bool SaveUiToJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreUiDocument& uiDocument,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
);
|
||||
static std::optional<MetaCoreUiDocument> LoadUiFromJson(
|
||||
const std::filesystem::path& absolutePath,
|
||||
const MetaCoreTypeRegistry& registry
|
||||
);
|
||||
};
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -1,138 +0,0 @@
|
||||
set(METACORE_PANDA3D_VERSION "1.10.16" CACHE STRING "Pinned Panda3D SDK version used by MetaCore")
|
||||
set(METACORE_PANDA3D_ARCH "x64" CACHE STRING "Pinned Panda3D Windows architecture used by MetaCore")
|
||||
set(METACORE_PANDA3D_LOCAL_ROOT "${CMAKE_SOURCE_DIR}/.metacore/deps/panda3d/${METACORE_PANDA3D_VERSION}-${METACORE_PANDA3D_ARCH}" CACHE PATH "Local Panda3D SDK cache directory")
|
||||
|
||||
function(metacore_prepare_panda3d)
|
||||
if(WIN32)
|
||||
if(DEFINED ENV{PANDA3D_ROOT} AND NOT "$ENV{PANDA3D_ROOT}" STREQUAL "")
|
||||
set(_metacore_panda3d_root "$ENV{PANDA3D_ROOT}")
|
||||
elseif(DEFINED PANDA3D_ROOT AND NOT "${PANDA3D_ROOT}" STREQUAL "")
|
||||
set(_metacore_panda3d_root "${PANDA3D_ROOT}")
|
||||
else()
|
||||
set(_metacore_panda3d_root "${METACORE_PANDA3D_LOCAL_ROOT}")
|
||||
endif()
|
||||
|
||||
file(TO_CMAKE_PATH "${_metacore_panda3d_root}" _metacore_panda3d_root)
|
||||
|
||||
if(NOT EXISTS "${_metacore_panda3d_root}/include/pandaFramework.h")
|
||||
if(METACORE_AUTO_PREPARE_PANDA3D)
|
||||
message(STATUS "MetaCore will prepare Panda3D SDK into ${_metacore_panda3d_root}")
|
||||
execute_process(
|
||||
COMMAND
|
||||
powershell
|
||||
-NoProfile
|
||||
-ExecutionPolicy Bypass
|
||||
-File "${CMAKE_SOURCE_DIR}/scripts/PrepareMetaCorePanda3D.ps1"
|
||||
-Version "${METACORE_PANDA3D_VERSION}"
|
||||
-Architecture "${METACORE_PANDA3D_ARCH}"
|
||||
-InstallDir "${_metacore_panda3d_root}"
|
||||
RESULT_VARIABLE _metacore_prepare_result
|
||||
)
|
||||
if(NOT _metacore_prepare_result EQUAL 0)
|
||||
message(FATAL_ERROR "MetaCore failed to prepare Panda3D SDK. Set PANDA3D_ROOT manually or inspect scripts/PrepareMetaCorePanda3D.ps1 output.")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Panda3D SDK was not found. Set PANDA3D_ROOT or enable METACORE_AUTO_PREPARE_PANDA3D.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(METACORE_PANDA3D_ROOT "${_metacore_panda3d_root}" CACHE PATH "Resolved Panda3D SDK root used by MetaCore" FORCE)
|
||||
set(METACORE_PANDA3D_INCLUDE_DIR "${METACORE_PANDA3D_ROOT}/include" CACHE PATH "Resolved Panda3D include directory" FORCE)
|
||||
set(METACORE_PANDA3D_LIB_DIR "${METACORE_PANDA3D_ROOT}/lib" CACHE PATH "Resolved Panda3D library directory" FORCE)
|
||||
set(METACORE_PANDA3D_BIN_DIR "${METACORE_PANDA3D_ROOT}/bin" CACHE PATH "Resolved Panda3D runtime directory" FORCE)
|
||||
set(METACORE_PANDA3D_ETC_DIR "${METACORE_PANDA3D_ROOT}/etc" CACHE PATH "Resolved Panda3D config directory" FORCE)
|
||||
set(METACORE_PANDA3D_MODELS_DIR "${METACORE_PANDA3D_ROOT}/models" CACHE PATH "Resolved Panda3D models directory" FORCE)
|
||||
set(METACORE_PANDA3D_PLUGINS_DIR "${METACORE_PANDA3D_ROOT}/plugins" CACHE PATH "Resolved Panda3D plugins directory" FORCE)
|
||||
|
||||
set(_metacore_panda3d_libraries
|
||||
"${METACORE_PANDA3D_LIB_DIR}/libp3framework.lib"
|
||||
"${METACORE_PANDA3D_LIB_DIR}/libpanda.lib"
|
||||
"${METACORE_PANDA3D_LIB_DIR}/libpandafx.lib"
|
||||
"${METACORE_PANDA3D_LIB_DIR}/libpandaexpress.lib"
|
||||
"${METACORE_PANDA3D_LIB_DIR}/libpandagl.lib"
|
||||
"${METACORE_PANDA3D_LIB_DIR}/libp3windisplay.lib"
|
||||
"${METACORE_PANDA3D_LIB_DIR}/libp3dtool.lib"
|
||||
"${METACORE_PANDA3D_LIB_DIR}/libp3dtoolconfig.lib"
|
||||
opengl32
|
||||
gdi32
|
||||
user32
|
||||
shell32
|
||||
advapi32
|
||||
ws2_32
|
||||
winmm
|
||||
)
|
||||
|
||||
add_library(MetaCorePanda3D::SDK INTERFACE IMPORTED GLOBAL)
|
||||
set_target_properties(MetaCorePanda3D::SDK PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${METACORE_PANDA3D_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "${_metacore_panda3d_libraries}"
|
||||
)
|
||||
|
||||
file(GLOB METACORE_PANDA3D_RUNTIME_DLLS "${METACORE_PANDA3D_BIN_DIR}/*.dll")
|
||||
set(METACORE_PANDA3D_RUNTIME_DLLS "${METACORE_PANDA3D_RUNTIME_DLLS}" CACHE INTERNAL "Resolved Panda3D runtime DLL list")
|
||||
else()
|
||||
message(FATAL_ERROR "MetaCore Panda3D V1 currently supports Windows only.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(metacore_stage_panda3d_runtime target_name)
|
||||
if(NOT TARGET ${target_name})
|
||||
message(FATAL_ERROR "Target ${target_name} does not exist.")
|
||||
endif()
|
||||
|
||||
foreach(_metacore_runtime_dll IN LISTS METACORE_PANDA3D_RUNTIME_DLLS)
|
||||
add_custom_command(TARGET ${target_name} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${_metacore_runtime_dll}"
|
||||
"$<TARGET_FILE_DIR:${target_name}>"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
if(EXISTS "${METACORE_PANDA3D_ETC_DIR}")
|
||||
add_custom_command(TARGET ${target_name} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${METACORE_PANDA3D_ETC_DIR}"
|
||||
"$<TARGET_FILE_DIR:${target_name}>/etc"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EXISTS "${METACORE_PANDA3D_MODELS_DIR}")
|
||||
add_custom_command(TARGET ${target_name} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${METACORE_PANDA3D_MODELS_DIR}"
|
||||
"$<TARGET_FILE_DIR:${target_name}>/models"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EXISTS "${METACORE_PANDA3D_PLUGINS_DIR}")
|
||||
add_custom_command(TARGET ${target_name} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${METACORE_PANDA3D_PLUGINS_DIR}"
|
||||
"$<TARGET_FILE_DIR:${target_name}>/plugins"
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(metacore_stage_simplepbr_runtime target_name)
|
||||
if(NOT TARGET ${target_name})
|
||||
message(FATAL_ERROR "Target ${target_name} does not exist.")
|
||||
endif()
|
||||
|
||||
set(_metacore_simplepbr_root "${CMAKE_SOURCE_DIR}/third_party/simplepbr-shaders")
|
||||
|
||||
if(EXISTS "${_metacore_simplepbr_root}/shaders")
|
||||
add_custom_command(TARGET ${target_name} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${_metacore_simplepbr_root}/shaders"
|
||||
"$<TARGET_FILE_DIR:${target_name}>/simplepbr/shaders"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EXISTS "${_metacore_simplepbr_root}/textures")
|
||||
add_custom_command(TARGET ${target_name} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${_metacore_simplepbr_root}/textures"
|
||||
"$<TARGET_FILE_DIR:${target_name}>/simplepbr/textures"
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
@ -2,9 +2,27 @@
|
||||
|
||||
这份索引用于帮助快速找到第一阶段执行所需的核心文档。
|
||||
|
||||
## 当前主线
|
||||
|
||||
当前 MetaCore 的技术路线已经收敛为:
|
||||
|
||||
- 底层架构:`EnTT` ECS,外层提供 `GameObject + Component` 编辑器语义。
|
||||
- 编辑器界面:`Dear ImGui`。
|
||||
- 渲染核心:`Google Filament`,Panda3D 仅作为历史兼容或待清理上下文。
|
||||
- 运行时 UI:`RmlUi`,定位类似 Unity UGUI。
|
||||
- 模型导入:当前优先 `glTF / GLB / gltfio`。
|
||||
- 资产策略:编辑期 JSON / 可读元数据,发布期 Cook / 二进制资产。
|
||||
- 交付目标:编辑器面向 Windows、Linux、统信、麒麟;运行时面向 C/S,并为 B/S / WebGPU 预留。
|
||||
|
||||
建议先阅读:
|
||||
|
||||
- [metacore-current-architecture.md](D:/MetaCore/docs/designs/metacore-current-architecture.md)
|
||||
- [metacore-product-plan.md](D:/MetaCore/docs/designs/metacore-product-plan.md)
|
||||
|
||||
## 推荐阅读顺序
|
||||
|
||||
1. 产品与范围
|
||||
- [metacore-current-architecture.md](D:/MetaCore/docs/designs/metacore-current-architecture.md)
|
||||
- [metacore-product-plan.md](D:/MetaCore/docs/designs/metacore-product-plan.md)
|
||||
- [metacore-phase1-scope.md](D:/MetaCore/docs/designs/metacore-phase1-scope.md)
|
||||
|
||||
|
||||
185
docs/designs/metacore-current-architecture.md
Normal file
185
docs/designs/metacore-current-architecture.md
Normal file
@ -0,0 +1,185 @@
|
||||
# MetaCore 当前架构与技术路线
|
||||
|
||||
更新时间:2026-05-20
|
||||
状态:当前主线说明
|
||||
读者:产品、架构、引擎、编辑器、渲染、工具链、交付
|
||||
|
||||
## 一句话定位
|
||||
|
||||
MetaCore 是一个以 EnTT ECS 为底层架构、Filament 为现代渲染核心、ImGui 为编辑器界面、RmlUi 为运行时 UI 的轻量级 Unity / Unreal Engine 对标型 3D 引擎与编辑器,采用编辑期 JSON、发布期二进制 Cook 的敏捷迭代路线,面向工业仿真、数字孪生、实时可视化和跨平台运行时交付。
|
||||
|
||||
## 当前技术选型
|
||||
|
||||
| 能力域 | 当前选择 | 定位 |
|
||||
| --- | --- | --- |
|
||||
| 底层架构 | EnTT | ECS 数据层,承载实体、组件和场景状态 |
|
||||
| 编辑器对象语义 | GameObject + Component | 面向编辑器和用户工作流的 ECS 封装 |
|
||||
| 编辑器 UI | Dear ImGui Docking | 快速构建工具面板、Inspector、Hierarchy、Project、Console |
|
||||
| 3D 操控 | ImGuizmo | 编辑器视口中的移动、旋转、缩放工具 |
|
||||
| 渲染核心 | Google Filament | PBR、材质、灯光、glTF、跨图形 API 渲染后端 |
|
||||
| 模型导入 | glTF / GLB / gltfio | 当前模型导入主线,后续扩展其他格式 |
|
||||
| 运行时 UI | RmlUi | 项目正式 UI,定位类似 Unity UGUI |
|
||||
| 编辑期资产 | JSON / mcmeta / 可读项目文件 | 快速迭代、可 diff、可调试、可人工修复 |
|
||||
| 发布期资产 | Cooked binary / package | 面向运行时性能、封装和交付稳定性 |
|
||||
| RuntimeData | TCP / 文件回放 / 数据绑定 | 外部仿真、遥测、业务数据驱动场景组件 |
|
||||
|
||||
## 渲染路线
|
||||
|
||||
MetaCore 已经从 Panda3D 迁移到 Filament 主线。Panda3D 相关代码只应被视为历史兼容层或待清理实现,不再作为新增能力的扩展方向。
|
||||
|
||||
Filament 作为主渲染核心,承担:
|
||||
|
||||
- PBR 材质与光照。
|
||||
- glTF / GLB 加载与资源解释。
|
||||
- 编辑器视口离屏渲染。
|
||||
- 运行时 Player 场景渲染。
|
||||
- Vulkan、OpenGL、DirectX 12、WebGPU 等后端能力的长期承接。
|
||||
|
||||
优先级建议:
|
||||
|
||||
1. Vulkan:首选高性能现代图形 API。
|
||||
2. OpenGL:兼容和过渡。
|
||||
3. DirectX 12:Windows 高性能运行时。
|
||||
4. WebGPU:B/S 架构、浏览器嵌入和 Web 交付。
|
||||
|
||||
## UI 路线
|
||||
|
||||
MetaCore 明确区分两套 UI:
|
||||
|
||||
### 编辑器 UI
|
||||
|
||||
编辑器 UI 使用 Dear ImGui。它服务于工具工作流:
|
||||
|
||||
- Hierarchy
|
||||
- Scene
|
||||
- Inspector
|
||||
- Project
|
||||
- Console
|
||||
- RuntimeData 面板
|
||||
- 导入、Cook、诊断等工具窗口
|
||||
|
||||
ImGui 不承担最终项目运行时 UI 的长期职责。
|
||||
|
||||
### 运行时 UI
|
||||
|
||||
运行时 UI 使用 RmlUi。它服务于项目交付:
|
||||
|
||||
- 标题栏、侧边栏、设备信息面板。
|
||||
- 状态文本、告警提示、按钮、图片和图标。
|
||||
- 与场景对象、RuntimeData 和业务状态绑定。
|
||||
- 后续在编辑器中提供可视化编辑,再由 RmlUi 在 Player 中渲染。
|
||||
|
||||
这条路线类似 Unity 中“编辑器工具 UI”和“UGUI 项目 UI”的分工。
|
||||
|
||||
## 资产与序列化路线
|
||||
|
||||
当前阶段采用双阶段资产策略:
|
||||
|
||||
### 编辑期
|
||||
|
||||
编辑器阶段优先使用 JSON 和可读元数据:
|
||||
|
||||
- `MetaCore.project.json`
|
||||
- `.mcscene.json` 或可读场景文档
|
||||
- `.mcmeta`
|
||||
- JSON 化材质、UI、导入描述和资源记录
|
||||
|
||||
目标是:
|
||||
|
||||
- 快速迭代。
|
||||
- 便于 diff 和代码评审。
|
||||
- 便于调试和人工修复。
|
||||
- 降低早期功能演进的成本。
|
||||
|
||||
### 发布期
|
||||
|
||||
打包阶段再生成二进制运行时资产:
|
||||
|
||||
- cooked scene
|
||||
- cooked mesh / material / texture
|
||||
- runtime config
|
||||
- CookManifest
|
||||
- 可交付 Player 包
|
||||
|
||||
目标是:
|
||||
|
||||
- 提高加载性能。
|
||||
- 减少运行时解析成本。
|
||||
- 封装交付内容。
|
||||
- 支持 C/S 和未来 B/S 发布形态。
|
||||
|
||||
## 平台与交付目标
|
||||
|
||||
### 编辑器平台
|
||||
|
||||
编辑器优先面向:
|
||||
|
||||
- Windows
|
||||
- Linux
|
||||
- 统信 UOS
|
||||
- 麒麟 OS
|
||||
|
||||
第一阶段可以 Windows 先行,但平台抽象必须为国产化桌面环境保留边界。
|
||||
|
||||
### 运行时交付
|
||||
|
||||
运行时面向两种架构:
|
||||
|
||||
- C/S:第一阶段主要承诺,适合桌面端工业仿真、数字孪生、大屏和本地部署。
|
||||
- B/S:后续产品化方向,适合 WebGPU、浏览器嵌入、第三方业务系统集成。
|
||||
|
||||
## 行业对标
|
||||
|
||||
MetaCore 对标 Unity 和 Unreal Engine,但不是第一阶段复制它们的全部功能广度。
|
||||
|
||||
对标 Unity:
|
||||
|
||||
- GameObject / Component 工作流。
|
||||
- Scene / Hierarchy / Inspector / Project。
|
||||
- Prefab。
|
||||
- UGUI 类运行时 UI。
|
||||
- 资产导入与可视化编辑体验。
|
||||
|
||||
对标 Unreal Engine:
|
||||
|
||||
- 高质量渲染目标。
|
||||
- 材质和资源工程化。
|
||||
- 大型项目交付、Cook、Package、运行时边界。
|
||||
|
||||
MetaCore 的差异化方向:
|
||||
|
||||
- 更轻量。
|
||||
- 更容易国产化适配。
|
||||
- 更适合工业仿真、数字孪生和业务系统嵌入。
|
||||
- 更强调编辑期可读、运行期可交付的资产闭环。
|
||||
|
||||
## 开发方法
|
||||
|
||||
MetaCore 当前采用敏捷开发、快速迭代的方式:
|
||||
|
||||
- 先打通主链路,再逐步工程化。
|
||||
- 先让编辑器可用,再逐步完善运行时性能。
|
||||
- 先建立 JSON 可读资产闭环,再收敛 Cook 二进制发布链路。
|
||||
- 先服务真实交付场景,再扩展通用引擎能力。
|
||||
|
||||
第一阶段的核心不是一次性做完整引擎,而是快速形成:
|
||||
|
||||
```text
|
||||
创建项目
|
||||
-> 导入模型
|
||||
-> 编辑场景
|
||||
-> 配置材质 / UI / 数据绑定
|
||||
-> 保存 JSON 编辑期资产
|
||||
-> Cook 运行时资产
|
||||
-> Player 运行
|
||||
-> 交付 C/S 应用
|
||||
```
|
||||
|
||||
## 当前约束
|
||||
|
||||
- 新渲染能力默认走 Filament,不再沿 Panda3D 扩展。
|
||||
- 编辑器工具 UI 默认走 ImGui。
|
||||
- 项目运行时 UI 默认走 RmlUi。
|
||||
- 模型导入优先完善 glTF / GLB / gltfio 闭环。
|
||||
- 资产格式优先保证编辑期 JSON 可读性,再进入发布期二进制优化。
|
||||
- 测试应区分“编辑期 JSON 资产链路”和“发布期 Cook 二进制链路”,避免用二进制产物是否存在来阻塞早期编辑器迭代。
|
||||
@ -14,6 +14,19 @@ MetaCore 的定位是:
|
||||
- 第一阶段先完成可支撑数字孪生项目开发的能力闭环
|
||||
- 但所有优先建设的能力都应尽量沉淀为通用引擎能力,而不是行业特化逻辑
|
||||
|
||||
## 当前架构基线
|
||||
|
||||
当前仓库的引擎基线应按下列主线理解:
|
||||
|
||||
- `EnTT` 是底层 ECS 数据架构。
|
||||
- `GameObject + Component` 是面向编辑器和用户工作流的对象语义层。
|
||||
- `Dear ImGui` 是编辑器 UI 技术栈。
|
||||
- `Google Filament` 是主渲染后端,Panda3D 相关实现只作为历史上下文或待清理兼容层。
|
||||
- `RmlUi` 是运行时 UI 的规划主线,负责正式项目 UI 渲染。
|
||||
- `glTF / GLB / gltfio` 是当前模型导入主线。
|
||||
- 编辑期资产以 JSON / `.mcmeta` / 可读项目文件为优先,发布期通过 Cook 生成二进制运行时资产。
|
||||
- 第一阶段以 C/S 交付为主,同时为 B/S / WebGPU 交付保留架构边界。
|
||||
|
||||
这份文档回答三个问题:
|
||||
|
||||
1. MetaCore 作为引擎,功能清单应该怎么分层
|
||||
@ -380,7 +393,7 @@ MetaCore 当前应按下列 12 个引擎能力域来规划:
|
||||
|
||||
`已具备`
|
||||
|
||||
- Panda3D 宿主和渲染桥
|
||||
- Filament 渲染桥和编辑器离屏视口链路
|
||||
- 编辑器视口渲染
|
||||
- 基础场景显示
|
||||
- 摄像机、网格、灯光、调试叠加
|
||||
@ -389,12 +402,13 @@ MetaCore 当前应按下列 12 个引擎能力域来规划:
|
||||
`部分具备`
|
||||
|
||||
- 渲染抽象已经存在
|
||||
- glTF / GLB 场景节点与 Filament 渲染对象同步已经形成基础链路
|
||||
- 视口交互体验已经能支持基本编辑工作
|
||||
|
||||
`薄弱或缺失`
|
||||
|
||||
- 完整材质系统
|
||||
- Shader 资源与管线
|
||||
- Filament 材质资源与 MetaCore 材质资源之间的稳定映射
|
||||
- 稳定 PBR 工作流
|
||||
- 阴影质量和配置
|
||||
- 环境光与天空盒体系
|
||||
|
||||
@ -227,7 +227,7 @@
|
||||
- 重新打开
|
||||
- 启动场景加载
|
||||
|
||||
对这个仓库来说,二进制 package 持久化是正确方向。
|
||||
对这个仓库来说,编辑期 JSON 持久化与发布期二进制 Cook 应同时存在:编辑器优先保证场景可读、可 diff、可快速修复;运行时通过 Cook 生成稳定二进制 package。
|
||||
|
||||
### 8. UI 工作流
|
||||
|
||||
|
||||
@ -37,7 +37,8 @@ MetaCore 第一阶段首批真正应做成生产级闭环的模型导入格式
|
||||
第一阶段 MetaCore 已经确定:
|
||||
|
||||
- 材质层先走基础 PBR
|
||||
- shader 后端先接 `panda3d-simplepbr`
|
||||
- 渲染后端主线先接 `Google Filament`
|
||||
- 模型加载和 glTF 语义优先使用 `gltfio`
|
||||
|
||||
在这个前提下,`glTF/.glb` 的材质语义更接近第一阶段目标:
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
- 模型导入后,材质信息如何进入 MetaCore
|
||||
- MetaCore 内部材质资源应该长什么样
|
||||
- 场景对象如何引用材质
|
||||
- 第一阶段如何接入 `panda3d-simplepbr`
|
||||
- 第一阶段如何接入 `Google Filament`
|
||||
|
||||
如果这条链不先设计清楚,后面模型导入、Inspector 编辑、场景保存、Player 渲染都会各做各的,最终一定返工。
|
||||
|
||||
@ -19,14 +19,14 @@
|
||||
|
||||
MetaCore 第一阶段应采用下面这条链路:
|
||||
|
||||
**源模型文件 -> 导入文档 -> 网格资源 / 材质资源 -> 场景对象 MeshRenderer -> simplepbr 参数映射 -> Player 渲染**
|
||||
**源模型文件 -> 导入文档 -> 网格资源 / 材质资源 -> 场景对象 MeshRenderer -> Filament 材质映射 -> Player 渲染**
|
||||
|
||||
这里必须明确两件事:
|
||||
|
||||
1. 模型导入负责“生成 MetaCore 自己的资源”
|
||||
2. 渲染后端只负责“消费 MetaCore 材质资源”
|
||||
|
||||
不能让模型文件直接决定运行时材质结构,也不能让 `simplepbr` 反向决定 MetaCore 材质资源长什么样。
|
||||
不能让模型文件直接决定运行时材质结构,也不能让 Filament 反向决定 MetaCore 材质资源长什么样。
|
||||
|
||||
## 当前代码库现状
|
||||
|
||||
@ -46,7 +46,7 @@ MetaCore 第一阶段应采用下面这条链路:
|
||||
- 模型导入后的材质槽映射定义
|
||||
- 场景对象对材质资源的引用方式
|
||||
- 材质 Inspector 与资源编辑流程
|
||||
- 材质到 `simplepbr` 的统一映射层
|
||||
- 材质到 Filament `MaterialInstance` 的统一映射层
|
||||
|
||||
因此当前最合理的做法不是直接继续加渲染效果,而是先把资源和组件关系定清楚。
|
||||
|
||||
@ -61,7 +61,7 @@ flowchart LR
|
||||
D --> F["场景对象 MeshRenderer"]
|
||||
E --> F
|
||||
F --> G["MetaCore 渲染抽象层"]
|
||||
G --> H["simplepbr 参数映射"]
|
||||
G --> H["Filament 材质映射"]
|
||||
H --> I["Player 渲染输出"]
|
||||
```
|
||||
|
||||
@ -142,7 +142,7 @@ flowchart LR
|
||||
- AlphaCutoff
|
||||
- DoubleSided
|
||||
|
||||
第一阶段不要求复杂 shader graph,但必须保证这个资源结构已经是 MetaCore 自己的,不是 `simplepbr` 参数字典。
|
||||
第一阶段不要求复杂 shader graph,但必须保证这个资源结构已经是 MetaCore 自己的,不是 Filament 参数字典。
|
||||
|
||||
### Texture Asset
|
||||
|
||||
@ -268,15 +268,15 @@ flowchart LR
|
||||
|
||||
不要把“对象引用”和“材质内容”全部混在同一个对象 Inspector 里。
|
||||
|
||||
## 与 simplepbr 的衔接方式
|
||||
## 与 Filament 的衔接方式
|
||||
|
||||
第一阶段 `simplepbr` 应只出现在渲染映射层。
|
||||
第一阶段 Filament 应只出现在渲染映射层和运行时渲染层。
|
||||
|
||||
也就是:
|
||||
|
||||
- `MetaCoreMaterialAsset`
|
||||
-> `MetaCoreRenderMaterialParams`
|
||||
-> Panda3D / `simplepbr`
|
||||
-> Filament `MaterialInstance`
|
||||
|
||||
### 第一阶段最小映射
|
||||
|
||||
@ -293,7 +293,7 @@ flowchart LR
|
||||
|
||||
### 明确不要做
|
||||
|
||||
第一阶段不要把 `simplepbr` 的内部参数名、调用方式、配置细节直接暴露到:
|
||||
第一阶段不要把 Filament 的内部参数名、调用方式、配置细节直接暴露到:
|
||||
|
||||
- 材质资源格式
|
||||
- Editor Inspector
|
||||
@ -381,7 +381,7 @@ flowchart LR
|
||||
4. 先打通 `glTF/.glb -> Mesh/Material Asset`
|
||||
5. 打通对象级 Mesh/Material 指派
|
||||
6. 打通材质资源 Inspector
|
||||
7. 建立 `Material -> simplepbr` 映射层
|
||||
7. 建立 `Material -> Filament` 映射层
|
||||
8. 验证 Editor 与 Player 一致性
|
||||
9. 再考虑 `FBX` 与重导入增强
|
||||
|
||||
@ -410,6 +410,6 @@ flowchart LR
|
||||
|
||||
而是:
|
||||
|
||||
**先建立 MetaCore 自己的 Mesh/Material 资源体系,再把模型导入和 `simplepbr` 渲染后端挂到这套资源体系两端。**
|
||||
**先建立 MetaCore 自己的 Mesh/Material 资源体系,再把模型导入和 Filament 渲染后端挂到这套资源体系两端。**
|
||||
|
||||
这是让 MetaCore 真正形成“模型导入 -> 材质编辑 -> 场景搭建 -> Player 渲染”闭环的关键一步。
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
- 能在 Scene 中稳定保存引用
|
||||
- 能被 Editor Inspector 编辑
|
||||
- 能被 Cook 和 Package 依赖分析
|
||||
- 能接入 `simplepbr`
|
||||
- 能接入 Filament
|
||||
- 不锁死未来类 URP 的演进空间
|
||||
|
||||
## 资源与组件的关系
|
||||
@ -261,7 +261,7 @@ AO 贴图资源引用。
|
||||
|
||||
- 导入校验
|
||||
- Inspector 提示
|
||||
- simplepbr 映射
|
||||
- Filament 映射
|
||||
|
||||
## MeshRenderer 组件演进设计
|
||||
|
||||
@ -446,7 +446,7 @@ Cook 阶段应能够从 Scene / Prefab 中追踪依赖:
|
||||
5. 更新 Scene / Prefab 序列化
|
||||
6. 更新 Inspector
|
||||
7. 再接模型导入与材质映射
|
||||
8. 最后接 `simplepbr` 参数映射
|
||||
8. 最后接 Filament 材质映射
|
||||
|
||||
## 第一阶段 Definition of Done
|
||||
|
||||
@ -466,7 +466,7 @@ Cook 阶段应能够从 Scene / Prefab 中追踪依赖:
|
||||
|
||||
所以最合理的方向是:
|
||||
|
||||
**把 Mesh、Material、Texture 正式变成 MetaCore 资源,把 MeshRenderer 正式变成资源引用组件,再让导入、Inspector、Cook 和 simplepbr 全部围绕这套结构协作。**
|
||||
**把 Mesh、Material、Texture 正式变成 MetaCore 资源,把 MeshRenderer 正式变成资源引用组件,再让导入、Inspector、Cook 和 Filament 全部围绕这套结构协作。**
|
||||
|
||||
这一步一旦定稳,后续:
|
||||
|
||||
|
||||
@ -1,84 +1,88 @@
|
||||
# MetaCore 材质与渲染管线选型说明
|
||||
# MetaCore 材质与渲染管线选型说明
|
||||
|
||||
生成时间:2026-03-28
|
||||
状态:草案
|
||||
范围:M3 场景编辑、材质与光照工作流
|
||||
生成时间:2026-03-28
|
||||
更新时间:2026-05-20
|
||||
状态:当前主线更新
|
||||
范围:材质、模型导入、Filament 渲染与发布管线
|
||||
|
||||
## 目的
|
||||
|
||||
这份文档用于明确 MetaCore 第一阶段材质与渲染管线的选型策略。
|
||||
这份文档用于明确 MetaCore 当前材质与渲染管线的选型策略。早期文档曾以 Panda3D / simplepbr 作为第一阶段 PBR 起点;当前工程主线已经迁移到 Google Filament,因此后续渲染、材质、glTF 和运行时视觉能力应围绕 Filament 展开。
|
||||
|
||||
目标不是一步到位做出完整的 Unity URP 等价物,而是:
|
||||
目标不是一步到位做出完整 Unity URP 或 Unreal 渲染管线,而是:
|
||||
|
||||
- 第一阶段先把基础 PBR 材质工作流跑通
|
||||
- 让材质、灯光、模型和场景搭建形成稳定闭环
|
||||
- 在不锁死未来架构的前提下,借力现有可行方案
|
||||
- 先打通 glTF / GLB -> MetaCore 资产 -> ECS 场景 -> Filament 渲染的主链路。
|
||||
- 让模型、材质、贴图、灯光和场景保存形成稳定闭环。
|
||||
- 保持 MetaCore 自己的材质资源模型,不让 Filament 反向决定编辑器资产结构。
|
||||
- 为 Vulkan、OpenGL、DirectX 12、WebGPU 等后端能力预留空间。
|
||||
|
||||
## 结论先说
|
||||
|
||||
**第一阶段使用 `panda3d-simplepbr` 作为基础 PBR Shader 实现,是正确且务实的选择。**
|
||||
**MetaCore 当前第一渲染主线是 Google Filament。**
|
||||
|
||||
但这必须满足一个前提:
|
||||
|
||||
**MetaCore 自己定义材质系统,`simplepbr` 只是第一版渲染后端实现,不是最终渲染架构。**
|
||||
**MetaCore 自己定义材质系统,Filament 只是当前主渲染后端和材质落地目标,不是编辑期资产结构本身。**
|
||||
|
||||
## 为什么这个选型合理
|
||||
换句话说:
|
||||
|
||||
对于第一阶段来说,你当前最需要的不是一整套宏大的渲染管线,而是:
|
||||
```text
|
||||
MetaCore Material Asset
|
||||
-> MetaCore Render Material Description
|
||||
-> Filament Material / MaterialInstance
|
||||
-> Vulkan / OpenGL / DirectX 12 / WebGPU 后端
|
||||
```
|
||||
|
||||
- 模型导入后能有像样的材质表现
|
||||
- 场景中灯光表现稳定
|
||||
- 编辑器和运行时对材质的理解尽量一致
|
||||
- 工程团队能尽快把材质工作流做起来
|
||||
## 为什么选择 Filament
|
||||
|
||||
`panda3d-simplepbr` 非常适合作为这个阶段的起点,因为它解决的是:
|
||||
Filament 比早期 Panda3D/simplepbr 路线更适合作为 MetaCore 的长期渲染核心:
|
||||
|
||||
- 基础 PBR 材质
|
||||
- 基础灯光
|
||||
- 贴图和阴影的第一版工程问题
|
||||
- 原生面向现代 PBR。
|
||||
- glTF / GLB 与材质语义更贴近当前资产导入主线。
|
||||
- 支持成熟的光照、IBL、色彩管理和材质系统。
|
||||
- 具备多后端能力,可承接 Vulkan、OpenGL、DirectX 12、WebGPU 等方向。
|
||||
- 更适合后续做工业仿真、数字孪生和高质量实时可视化。
|
||||
|
||||
这正好对应 MetaCore 当前的 M3 目标。
|
||||
## 分层原则
|
||||
|
||||
## 为什么它不能作为最终方案
|
||||
|
||||
MetaCore 的长期目标不是“接一个现成 shader 包就结束”,而是:
|
||||
|
||||
- 拥有自己的材质资源模型
|
||||
- 拥有自己的渲染配置抽象
|
||||
- 逐步演进为类 Unity URP 的可控渲染管线
|
||||
|
||||
因此必须把两层分清楚:
|
||||
|
||||
### 第一层:MetaCore 材质系统
|
||||
### 1. MetaCore 材质系统
|
||||
|
||||
由 MetaCore 自己定义:
|
||||
|
||||
- 材质资源结构
|
||||
- 参数命名与参数槽位
|
||||
- 贴图槽定义
|
||||
- 默认材质类型
|
||||
- 材质序列化与资源管理
|
||||
- 材质资源结构。
|
||||
- 参数命名与参数槽位。
|
||||
- 贴图槽定义。
|
||||
- 默认材质类型。
|
||||
- 材质序列化、版本兼容和资源管理。
|
||||
- 编辑器 Inspector 和资产引用方式。
|
||||
|
||||
### 第二层:第一版 shader / render backend
|
||||
这层解决“MetaCore 材质是什么”。
|
||||
|
||||
当前先借助:
|
||||
### 2. Filament 映射层
|
||||
|
||||
- `panda3d-simplepbr`
|
||||
由渲染桥负责:
|
||||
|
||||
以后可以逐步替换为:
|
||||
- 把 MetaCore 材质参数映射到 Filament `MaterialInstance`。
|
||||
- 把 glTF / gltfio 解析出的材质关系落到 MetaCore 材质资源。
|
||||
- 处理贴图加载、采样器、颜色空间、粗糙度金属度、法线、透明模式等细节。
|
||||
- 处理编辑器视口和 Player 运行时的一致性。
|
||||
|
||||
- MetaCore 自己的 render pipeline
|
||||
- 更像 Unity URP 的 pass / feature / renderer 组织
|
||||
这层解决“MetaCore 材质如何在 Filament 中显示”。
|
||||
|
||||
## 第一阶段推荐落地方式
|
||||
### 3. 图形 API 后端
|
||||
|
||||
### 1. 先定义 MetaCore 自己的材质资源模型
|
||||
具体后端由 Filament 承接:
|
||||
|
||||
不要先把材质资源直接设计成 `simplepbr` 参数表。
|
||||
- Vulkan:首选高性能目标。
|
||||
- OpenGL:兼容和过渡。
|
||||
- DirectX 12:Windows 高性能目标。
|
||||
- WebGPU:未来 B/S 和浏览器嵌入目标。
|
||||
|
||||
第一阶段就应当有 MetaCore 自己的材质抽象层。
|
||||
MetaCore 不应在第一阶段直接把业务逻辑写死到某个底层图形 API。
|
||||
|
||||
最低应包含:
|
||||
## 第一阶段材质资源模型
|
||||
|
||||
第一阶段最低应包含:
|
||||
|
||||
- `MaterialType`
|
||||
- `BaseColor`
|
||||
@ -92,134 +96,105 @@ MetaCore 的长期目标不是“接一个现成 shader 包就结束”,而是
|
||||
- `EmissiveTexture`
|
||||
- `DoubleSided`
|
||||
- `AlphaMode`
|
||||
- `AlphaCutoff`
|
||||
|
||||
这层定义的是“MetaCore 材质是什么”,不是“simplepbr 怎么用”。
|
||||
这些字段属于 MetaCore 材质语义,不属于 Filament 内部实现细节。
|
||||
|
||||
### 2. simplepbr 只做第一版 shader 映射
|
||||
## 与 glTF / gltfio 的关系
|
||||
|
||||
也就是:
|
||||
当前模型导入主线是 glTF / GLB / gltfio。导入链路应尽量保持:
|
||||
|
||||
- MetaCore 材质参数
|
||||
-> 映射到 Panda3D / simplepbr 需要的参数
|
||||
```text
|
||||
glTF / GLB
|
||||
-> gltfio / importer
|
||||
-> MetaCore Model Import Document
|
||||
-> Mesh / Material / Texture 资源
|
||||
-> Scene MeshRenderer 引用
|
||||
-> Filament 渲染
|
||||
```
|
||||
|
||||
这样后面如果你替换渲染后端:
|
||||
导入器可以读取 glTF 的 PBR 语义,但最终应写入 MetaCore 自己的材质资源,而不是让场景对象直接绑定 gltfio 临时对象。
|
||||
|
||||
- 资源不需要全改
|
||||
- 编辑器也不需要推翻
|
||||
## 编辑期与发布期
|
||||
|
||||
### 3. 优先保证编辑器和运行时一致性
|
||||
### 编辑期
|
||||
|
||||
第一阶段不要追求特效广度,先保证:
|
||||
编辑器阶段优先使用 JSON 和可读元数据:
|
||||
|
||||
- 编辑器里看到的材质结果
|
||||
- Player 里看到的材质结果
|
||||
- 材质资源可读。
|
||||
- 贴图引用可检查。
|
||||
- MeshRenderer 的资源引用可 diff。
|
||||
- 导入结果可调试、可重导入、可人工修复。
|
||||
|
||||
尽量一致。
|
||||
### 发布期
|
||||
|
||||
对工业项目来说,这比“先上很多高级效果”更重要。
|
||||
Cook 阶段再生成运行时二进制内容:
|
||||
|
||||
## 第一阶段材质系统必须支持的能力
|
||||
- cooked material
|
||||
- cooked texture
|
||||
- cooked mesh
|
||||
- cooked scene
|
||||
- CookManifest
|
||||
|
||||
Cook 结果服务运行时加载性能,不应阻塞编辑器阶段的快速迭代。
|
||||
|
||||
## 第一阶段必须支持
|
||||
|
||||
### P0
|
||||
|
||||
- 基础 PBR 材质资源
|
||||
- BaseColor
|
||||
- BaseColorTexture
|
||||
- NormalTexture
|
||||
- Metallic / Roughness
|
||||
- Emissive
|
||||
- 材质资源保存与加载
|
||||
- 材质复用
|
||||
- 材质在场景对象上的指派
|
||||
- 基础 PBR 材质资源。
|
||||
- BaseColor / Metallic / Roughness / Normal / Emissive。
|
||||
- glTF / GLB 材质导入。
|
||||
- MeshRenderer 到 Mesh / Material / Texture 的资源引用。
|
||||
- Filament 视口与 Player 运行时材质表现一致。
|
||||
- 编辑期 JSON 资源保存与加载。
|
||||
- Cook 阶段能追踪材质和贴图依赖。
|
||||
|
||||
### P1
|
||||
|
||||
- AO 贴图
|
||||
- Alpha 模式
|
||||
- 双面材质
|
||||
- 基础材质实例工作流
|
||||
- AO 贴图。
|
||||
- Alpha Mask / Blend。
|
||||
- 双面材质。
|
||||
- IBL / 环境光配置。
|
||||
- 材质预览。
|
||||
- 材质实例与覆盖参数。
|
||||
|
||||
### P2
|
||||
|
||||
- 更高级的渲染 feature
|
||||
- 后处理
|
||||
- 特效扩展
|
||||
- 更完整的 shadow / transparency 组织
|
||||
- Shader Graph 或节点材质。
|
||||
- 自定义 Filament 材质模板。
|
||||
- 后处理资源化。
|
||||
- 平台化材质裁剪和质量档位。
|
||||
|
||||
## 第一阶段光照建议
|
||||
## 明确不做
|
||||
|
||||
基于当前阶段,先把这几个做稳:
|
||||
第一阶段不做:
|
||||
|
||||
- Directional Light
|
||||
- Point Light
|
||||
- Spot Light
|
||||
- 基础阴影
|
||||
- 基础环境光 / IBL
|
||||
- 完整 URP 等价物。
|
||||
- 完整 Unreal 材质编辑器等价物。
|
||||
- 大量自定义 shader feature。
|
||||
- 复杂后处理栈。
|
||||
- 多渲染管线并行维护。
|
||||
|
||||
重点不是参数数量,而是:
|
||||
第一阶段的目标是让模型和材质在编辑器、保存加载、Player 和 Cook 中形成可信闭环。
|
||||
|
||||
- 稳定
|
||||
- 一致
|
||||
- 可编辑
|
||||
- 可序列化
|
||||
## 成功标准
|
||||
|
||||
## 与 M3 的关系
|
||||
第一阶段材质与渲染链路成立的标准:
|
||||
|
||||
这个选型属于:
|
||||
1. 导入一个 glTF / GLB 模型后,Mesh、Material、Texture 关系能进入 MetaCore 资产系统。
|
||||
2. 拖入场景后,Scene 中的 MeshRenderer 通过资源引用表达模型和材质。
|
||||
3. 保存、关闭、重新打开项目后,材质表现保持一致。
|
||||
4. 编辑器视口和 Player 中的 Filament 渲染结果一致到可接受程度。
|
||||
5. Cook 能追踪材质与贴图依赖,并生成运行时可消费的二进制资产。
|
||||
6. 材质资源本身不绑定 Filament 内部对象生命周期,可以在未来演进映射层。
|
||||
|
||||
**M3 场景编辑、材质与光照工作流**
|
||||
## 设计底线
|
||||
|
||||
它的作用是让 MetaCore 在这一阶段具备真正的材质生产力,而不是继续停留在“对象能显示颜色”的水平。
|
||||
MetaCore 自己拥有材质系统,Filament 是当前主渲染后端。
|
||||
|
||||
所以这条线的优先级:
|
||||
不要把关系写反:
|
||||
|
||||
- 明显高于继续扩 RuntimeData
|
||||
- 明显高于继续加更多行业 adapter
|
||||
- 应该和模型导入、层级编辑并列为引擎基础主线
|
||||
|
||||
## 推荐实现顺序
|
||||
|
||||
建议按下面顺序推进:
|
||||
|
||||
1. 定义 MetaCore 材质资源结构
|
||||
2. 建立材质资源持久化
|
||||
3. 建立材质到 simplepbr 参数的映射
|
||||
4. 打通编辑器材质编辑入口
|
||||
5. 打通场景对象材质指派
|
||||
6. 验证编辑器与 Player 一致性
|
||||
7. 再考虑材质实例、更多贴图槽和更丰富 feature
|
||||
|
||||
## 第一阶段明确不要做的事
|
||||
|
||||
这些都属于以后,不应在当前阶段过早展开:
|
||||
|
||||
- 完整的类 URP 自定义 render feature 体系
|
||||
- 大而全的后处理系统
|
||||
- 复杂透明与特效管线
|
||||
- 完整 shader graph
|
||||
- 面向所有项目类型的通用渲染框架
|
||||
|
||||
## 长期方向
|
||||
|
||||
长期方向应该明确为:
|
||||
|
||||
**MetaCore 第一阶段借助 `panda3d-simplepbr` 跑通基础 PBR 材质工作流,后续逐步演进为 MetaCore 自己的类 URP 渲染管线。**
|
||||
|
||||
这句话同时保证了:
|
||||
|
||||
- 当前可执行
|
||||
- 中期可演进
|
||||
- 长期不被第三方实现细节绑死
|
||||
|
||||
## 最终建议
|
||||
|
||||
当前最好的策略不是:
|
||||
|
||||
- 直接做 URP
|
||||
- 也不是完全依附 `simplepbr`
|
||||
|
||||
而是:
|
||||
|
||||
**MetaCore 自己拥有材质系统,第一版渲染后端借力 `panda3d-simplepbr`。**
|
||||
|
||||
这是第一阶段最合理、风险最低、推进速度最快的选型。
|
||||
- 不是“Filament 材质参数直接等于 MetaCore 材质资产”。
|
||||
- 也不是“glTF 临时解析对象直接等于场景材质”。
|
||||
- 而是“MetaCore 资产系统吸收导入结果,再由 Filament 映射层负责显示”。
|
||||
|
||||
@ -40,7 +40,8 @@ MetaCore 第一阶段应明确采用下面这条交付链:
|
||||
当前仓库里已经有较好的基础:
|
||||
|
||||
- `MetaCore.project.json`
|
||||
- Scene 二进制 package 方向
|
||||
- 编辑期 JSON 场景 / 资产元数据方向
|
||||
- 发布期 cooked 二进制 package 方向
|
||||
- Package 服务
|
||||
- Cook 服务
|
||||
- `CookManifest`
|
||||
@ -49,6 +50,8 @@ MetaCore 第一阶段应明确采用下面这条交付链:
|
||||
|
||||
这说明第一阶段不是没有打包基础,而是还缺一套清晰的“交付工作流定义”。
|
||||
|
||||
当前原则是:编辑器工作目录优先保留可读 JSON 和元数据,Cook 输出目录再生成运行时二进制内容。不要用“开发期是否已经物理生成二进制包”作为编辑器功能是否成立的唯一判断。
|
||||
|
||||
当前最需要解决的是:
|
||||
|
||||
- 什么内容属于交付包
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user