Rebuild MetaCore editor on Panda3D with single-window viewport

This commit is contained in:
ayuan9957 2026-03-20 16:02:35 +08:00
parent a637100607
commit 633156e3be
56 changed files with 1892 additions and 1086 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
build/
vcpkg_installed/
downloads/
.metacore/
.vs/
CMakeUserPresets.json
imgui.ini

View File

@ -1,8 +1,16 @@
#include "MetaCoreEditor/MetaCoreEditorApp.h"
int main() {
#include <filesystem>
#include <iostream>
int main(int argc, char* argv[]) {
if (argc > 0) {
std::filesystem::current_path(std::filesystem::path(argv[0]).parent_path());
}
MetaCore::MetaCoreEditorApp editorApp;
if (!editorApp.Initialize()) {
std::cerr << "MetaCoreEditorApp initialize failed\n";
return 1;
}

View File

@ -1,6 +1,67 @@
#include "MetaCorePlatform/MetaCoreWindow.h"
#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h"
#include "MetaCoreRender/MetaCoreRenderDevice.h"
#include "MetaCoreRender/MetaCoreRenderTypes.h"
#include "MetaCoreScene/MetaCoreScene.h"
#include <filesystem>
#include <iostream>
int main() {
std::cout << "MetaCorePlayer 目前保留为后续运行时模块接入入口。\n";
namespace {
MetaCore::MetaCoreSceneView MetaCoreBuildPlayerSceneView() {
MetaCore::MetaCoreSceneView sceneView;
sceneView.CameraPosition = {0.0F, 2.5F, 6.5F};
sceneView.CameraTarget = {0.0F, 0.7F, 0.0F};
sceneView.CameraUp = {0.0F, 1.0F, 0.0F};
sceneView.VerticalFieldOfViewDegrees = 60.0F;
return sceneView;
}
} // namespace
int main(int argc, char* argv[]) {
if (argc > 0) {
std::filesystem::current_path(std::filesystem::path(argv[0]).parent_path());
}
MetaCore::MetaCoreWindow window;
if (!window.Initialize(1280, 720, "MetaCore Player")) {
std::cerr << "MetaCorePlayer: window initialize failed\n";
return 1;
}
MetaCore::MetaCoreRenderDevice renderDevice;
if (!renderDevice.Initialize(window)) {
std::cerr << "MetaCorePlayer: render device initialize failed\n";
return 1;
}
MetaCore::MetaCoreEditorViewportRenderer viewportRenderer;
if (!viewportRenderer.Initialize(renderDevice)) {
std::cerr << "MetaCorePlayer: viewport renderer initialize failed\n";
return 1;
}
MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene();
while (!window.ShouldClose()) {
window.BeginFrame();
const auto [windowWidth, windowHeight] = window.GetWindowSize();
viewportRenderer.SetViewportRect(MetaCore::MetaCoreViewportRect{
0.0F,
0.0F,
static_cast<float>(windowWidth),
static_cast<float>(windowHeight)
});
viewportRenderer.RenderSceneToViewport(scene, MetaCoreBuildPlayerSceneView());
renderDevice.RenderFrame();
renderDevice.PresentFrame();
window.EndFrame();
}
viewportRenderer.Shutdown();
renderDevice.Shutdown();
window.Shutdown();
return 0;
}

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.26)
project(MetaCore
VERSION 1.0.0
DESCRIPTION "MetaCore Unity-like editor prototype"
DESCRIPTION "MetaCore Panda3D-based Unity-like editor prototype"
LANGUAGES CXX
)
@ -10,27 +10,23 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
option(METACORE_BUILD_TESTS "Build MetaCore tests" ON)
if(MSVC)
# Debug 使Release 使 CRT
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()
if(EXISTS "${CMAKE_SOURCE_DIR}/vcpkg_installed/x64-windows/share/glad/gladConfig.cmake")
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")
if(EXISTS "${CMAKE_SOURCE_DIR}/vcpkg_installed/x64-windows/share/imgui/imgui-config.cmake")
list(PREPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/vcpkg_installed/x64-windows")
endif()
set(METACORE_RUNTIME_BIN_DIR "${CMAKE_SOURCE_DIR}/vcpkg_installed/x64-windows/bin")
include(MetaCorePanda3D)
metacore_prepare_panda3d()
function(metacore_copy_runtime_dlls target_name)
if(EXISTS "${METACORE_RUNTIME_BIN_DIR}/glfw3.dll")
add_custom_command(TARGET ${target_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${METACORE_RUNTIME_BIN_DIR}/glfw3.dll"
"$<TARGET_FILE_DIR:${target_name}>"
)
endif()
endfunction()
find_package(OpenGL REQUIRED)
find_package(glad CONFIG REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
find_package(imgui CONFIG REQUIRED)
@ -54,12 +50,15 @@ target_include_directories(MetaCoreFoundation
Source/MetaCoreFoundation/Public
)
set(METACORE_COMMON_WARNINGS)
if(MSVC)
target_compile_options(MetaCoreFoundation PRIVATE /W4 /permissive- /EHsc)
set(METACORE_COMMON_WARNINGS /W4 /permissive- /EHsc)
else()
target_compile_options(MetaCoreFoundation PRIVATE -Wall -Wextra -Wpedantic)
set(METACORE_COMMON_WARNINGS -Wall -Wextra -Wpedantic)
endif()
target_compile_options(MetaCoreFoundation PRIVATE ${METACORE_COMMON_WARNINGS})
set(METACORE_PLATFORM_HEADERS
Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreInput.h
Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreWindow.h
@ -83,14 +82,12 @@ target_include_directories(MetaCorePlatform
target_link_libraries(MetaCorePlatform
PUBLIC
MetaCoreFoundation
glfw
MetaCorePanda3D::SDK
glm::glm
)
if(MSVC)
target_compile_options(MetaCorePlatform PRIVATE /W4 /permissive- /EHsc)
else()
target_compile_options(MetaCorePlatform PRIVATE -Wall -Wextra -Wpedantic)
endif()
target_compile_options(MetaCorePlatform PRIVATE ${METACORE_COMMON_WARNINGS})
target_compile_definitions(MetaCorePlatform PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
set(METACORE_SCENE_HEADERS
Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreComponents.h
@ -118,29 +115,19 @@ target_link_libraries(MetaCoreScene
glm::glm
)
if(MSVC)
target_compile_options(MetaCoreScene PRIVATE /W4 /permissive- /EHsc)
else()
target_compile_options(MetaCoreScene PRIVATE -Wall -Wextra -Wpedantic)
endif()
target_compile_options(MetaCoreScene PRIVATE ${METACORE_COMMON_WARNINGS})
set(METACORE_RENDER_HEADERS
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderDevice.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreShaderProgram.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreMesh.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreDebugGeometry.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderTypes.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreSceneRenderer.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreEditorViewportRenderer.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCorePandaSceneBridge.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderDevice.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderTypes.h
)
set(METACORE_RENDER_SOURCES
Source/MetaCoreRender/Private/MetaCoreRenderDevice.cpp
Source/MetaCoreRender/Private/MetaCoreShaderProgram.cpp
Source/MetaCoreRender/Private/MetaCoreMesh.cpp
Source/MetaCoreRender/Private/MetaCoreDebugGeometry.cpp
Source/MetaCoreRender/Private/MetaCoreSceneRenderer.cpp
Source/MetaCoreRender/Private/MetaCoreEditorViewportRenderer.cpp
Source/MetaCoreRender/Private/MetaCorePandaSceneBridge.cpp
Source/MetaCoreRender/Private/MetaCoreRenderDevice.cpp
)
add_library(MetaCoreRender STATIC
@ -156,35 +143,31 @@ target_include_directories(MetaCoreRender
target_link_libraries(MetaCoreRender
PUBLIC
MetaCoreFoundation
MetaCoreScene
MetaCorePlatform
OpenGL::GL
glad::glad
MetaCoreScene
MetaCorePanda3D::SDK
glm::glm
)
if(MSVC)
target_compile_options(MetaCoreRender PRIVATE /W4 /permissive- /EHsc)
else()
target_compile_options(MetaCoreRender PRIVATE -Wall -Wextra -Wpedantic)
endif()
target_compile_options(MetaCoreRender PRIVATE ${METACORE_COMMON_WARNINGS})
set(METACORE_EDITOR_HEADERS
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorModule.h
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorContext.h
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorApp.h
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorContext.h
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorModule.h
)
set(METACORE_EDITOR_PRIVATE_HEADERS
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h
)
set(METACORE_EDITOR_SOURCES
Source/MetaCoreEditor/Private/MetaCoreEditorModule.cpp
Source/MetaCoreEditor/Private/MetaCoreEditorContext.cpp
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp
Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp
Source/MetaCoreEditor/Private/MetaCoreEditorContext.cpp
Source/MetaCoreEditor/Private/MetaCoreEditorModule.cpp
)
add_library(MetaCoreEditor STATIC
@ -204,17 +187,14 @@ target_link_libraries(MetaCoreEditor
PUBLIC
MetaCoreFoundation
MetaCorePlatform
MetaCoreScene
MetaCoreRender
MetaCoreScene
glm::glm
imgui::imgui
)
if(MSVC)
target_compile_options(MetaCoreEditor PRIVATE /W4 /permissive- /EHsc)
else()
target_compile_options(MetaCoreEditor PRIVATE -Wall -Wextra -Wpedantic)
endif()
target_compile_options(MetaCoreEditor PRIVATE ${METACORE_COMMON_WARNINGS})
target_compile_definitions(MetaCoreEditor PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
add_executable(MetaCoreEditorApp
Apps/MetaCoreEditor/main.cpp
@ -224,7 +204,8 @@ target_link_libraries(MetaCoreEditorApp
PRIVATE
MetaCoreEditor
)
metacore_copy_runtime_dlls(MetaCoreEditorApp)
metacore_stage_panda3d_runtime(MetaCoreEditorApp)
add_executable(MetaCorePlayer
Apps/MetaCorePlayer/main.cpp
@ -233,21 +214,26 @@ add_executable(MetaCorePlayer
target_link_libraries(MetaCorePlayer
PRIVATE
MetaCoreFoundation
MetaCorePlatform
MetaCoreRender
MetaCoreScene
MetaCorePanda3D::SDK
)
metacore_copy_runtime_dlls(MetaCorePlayer)
metacore_stage_panda3d_runtime(MetaCorePlayer)
if(METACORE_BUILD_TESTS)
enable_testing()
add_executable(MetaCoreSmokeTests
Tests/MetaCoreSmokeTests.cpp
tests/MetaCoreSmokeTests.cpp
)
target_link_libraries(MetaCoreSmokeTests
PRIVATE
MetaCoreEditor
)
metacore_copy_runtime_dlls(MetaCoreSmokeTests)
metacore_stage_panda3d_runtime(MetaCoreSmokeTests)
add_test(NAME MetaCoreSmokeTests COMMAND MetaCoreSmokeTests)
endif()

View File

@ -11,7 +11,7 @@
"hidden": true,
"generator": "Visual Studio 17 2022",
"binaryDir": "${sourceDir}/build/${presetName}",
"toolchainFile": "D:/vcpkg/scripts/buildsystems/vcpkg.cmake",
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "20",
"METACORE_BUILD_TESTS": "ON"

View File

@ -1,50 +1,64 @@
# MetaCore
MetaCore 是一个以 Unity 工作流为参考的纯 C++ 编辑器原型工程
`MetaCore` 是一个基于 `Panda3D` 进行二次开发的 Unity-like 编辑器与引擎框架原型
## 当前目标
- 使用 `GLFW + OpenGL + Dear ImGui Docking` 搭建最小可运行编辑器
- 使用 `GameObject + Component` 模型组织场景
- 以 `Panda3D` 作为底层引擎与运行时基础
- 以 `Dear ImGui Docking` 搭建 Unity-like 编辑器工作流
- 使用 `GameObject + Component` 模型组织编辑器语义层
- 先把 `Hierarchy / Scene / Inspector / Project / Console` 跑通
- 先体现清晰的渲染架构,再以模块化方式持续增加功能
- 在不破坏主架构的前提下,以模块化方式持续叠加功能
## 技术栈
- `C++20`
- `CMake`
- `vcpkg`
- `GLFW`
- `glad`
- `OpenGL 3.3`
- `Panda3D 1.10.16`
- `glm`
- `Dear ImGui Docking`
## 目录结构
- `Source/MetaCoreFoundation`:基础服务
- `Source/MetaCorePlatform`:窗口与输入
- `Source/MetaCorePlatform`Panda3D 宿主窗口与输入采集
- `Source/MetaCoreScene`:场景与对象模型
- `Source/MetaCoreRender`OpenGL 渲染架构
- `Source/MetaCoreRender`Panda3D 场景桥与显示区域驱动
- `Source/MetaCoreEditor`:编辑器模块与 Unity-like 界面
- `Apps/MetaCoreEditor`:编辑器程序入口
- `Apps/MetaCorePlayer`:运行时占位入口
- `Tests`:基础烟雾测试
- `Apps/MetaCorePlayer`:运行时壳层入口
- `tests`:基础烟雾测试
## 构建
前提:
- 已安装 `vcpkg`
- 已设置环境变量 `VCPKG_ROOT`
- Windows 环境可联网时,`cmake configure` 会自动准备 `Panda3D 1.10.16` SDK
- 如果已经本地安装 Panda3D可设置环境变量 `PANDA3D_ROOT`
```powershell
cmake --preset vs2022-debug
cmake --build --preset build-debug
ctest --preset test-debug
```
如果希望生成更适合分发的可执行文件,可使用:
```powershell
cmake --preset vs2022-release
cmake --build --preset build-release
```
## 当前完成内容
- 单窗口 MetaCore 编辑器主循环
- 场景视口离屏渲染
- Panda3D 单窗口宿主
- ImGui Docking 编辑器外壳
- Panda3D 中央场景 DisplayRegion
- 网格、坐标轴、立方体绘制
- `Hierarchy` 选择
- `Inspector` 中编辑 `Transform`
- 轨道/平移/缩放相机控制
- `Alt+左键 / 中键 / 滚轮 / F` 相机交互
- 模块注册接口与内置编辑器模块

View File

@ -0,0 +1,7 @@
{
"name": "DefaultPbr",
"shader_family": "simplepbr",
"base_color": [1.0, 1.0, 1.0, 1.0],
"roughness": 1.0,
"metallic": 0.0
}

View File

@ -0,0 +1,5 @@
{
"id": "00005031af6c28bc73f3772350037bcf00000005",
"relative_path": "Assets/Materials/default_pbr.material.json",
"type": "material"
}

View File

@ -0,0 +1,10 @@
body {
font-family: sans-serif;
color: #f2f4f8;
}
.root-panel {
width: 420px;
margin: 40px auto;
padding: 24px;
background: rgba(16, 18, 24, 0.85);
}

View File

@ -0,0 +1,5 @@
{
"id": "00005031af6c1e30843b82e4691e8cba00000004",
"relative_path": "Assets/UI/main_menu.rcss",
"type": "ui_stylesheet"
}

View File

@ -0,0 +1,11 @@
<rml>
<head>
<link type="text/rcss" href="main_menu.rcss" />
</head>
<body>
<div class="root-panel">
<h1>MetaCore</h1>
<p>Runtime UI scaffold</p>
</div>
</body>
</rml>

View File

@ -0,0 +1,5 @@
{
"id": "00005031af6bf9783f1fb78dcaa5ed8900000003",
"relative_path": "Assets/UI/main_menu.rml",
"type": "ui_document"
}

View File

@ -0,0 +1,19 @@
{
"records": [
{
"id": "00005031af6bf9783f1fb78dcaa5ed8900000003",
"relative_path": "Assets/UI/main_menu.rml",
"type": "ui_document"
},
{
"id": "00005031af6c1e30843b82e4691e8cba00000004",
"relative_path": "Assets/UI/main_menu.rcss",
"type": "ui_stylesheet"
},
{
"id": "00005031af6c28bc73f3772350037bcf00000005",
"relative_path": "Assets/Materials/default_pbr.material.json",
"type": "material"
}
]
}

View File

@ -0,0 +1,68 @@
Shell=MetaCoreOverlayShell, Menus=3, ToolbarGroups=3, Sections=7, Hierarchy=3, Assets=3, Inspector=Demo Cube, DrawCommands=11, DrawVertices=2698, DrawIndices=6627, DrawLists=9
MetaCoreOverlayShell composition
[Menu] 文件 actions=2
- 新建场景 [Ctrl+N]
- 保存场景 [Ctrl+S]
[Menu] 编辑 actions=4
- 创建空对象 [Ctrl+Shift+N]
- 删除所选 [Delete]
- 重命名所选 [F2]
- 聚焦所选 [F]
[Menu] 视图 actions=5
- 切换层级
- 切换检查器
- 切换项目
- 切换控制台
- 切换运行时预览
[Toolbar] 场景 actions=3
- 创建空对象
- 删除所选
- 重命名所选
[Toolbar] 选择 actions=1
- 聚焦所选
[Toolbar] 视图 actions=5
- 切换层级
- 切换检查器
- 切换项目
- 切换控制台
- 切换运行时预览
[Section] panels=1
- 主菜单 (main_menu)
[Section] panels=1
- 工具栏 (toolbar)
[Section] panels=2
- 层级 (hierarchy)
- 项目 (project)
[Section] panels=1
- 视口 (viewport)
[Section] panels=1
- 检查器 (inspector)
[Section] panels=1
- 控制台 (console)
[Section] panels=1
- 运行时预览 (runtime_preview)
[Inspector] Demo Cube
- 标识: 00005031af7e8fac310b0d408494fcfb00000008
- 位置: 0.000000, 0.000000, 0.000000
- 旋转: 0.000000, 0.000000, 0.000000
- 缩放: 1.000000, 1.000000, 1.000000
- 网格: builtin://box
[Console]
- [System] MetaEditor initialized
[Viewport]
- Selection: Demo Cube
- SelectionId: 00005031af7e8fac310b0d408494fcfb00000008
- Operation: Translate
- Space: Local
- SnapEnabled: false
- SnapValues: 1, 1, 1
- Camera: 0.5, -2.5, 2
- Position: 0, 0, 0
- Rotation: 0, 0, 0
- Scale: 1, 1, 1
- FocusTarget: 00005031af7e8fac310b0d408494fcfb00000008
[DrawData]
- CommandCount: 11
- VertexCount: 2698
- IndexCount: 6627
- DrawLists: 9

View File

@ -0,0 +1,9 @@
{
"console_visible": true,
"hierarchy_visible": true,
"inspector_visible": true,
"layout_name": "Default",
"project_visible": true,
"runtime_preview_visible": true,
"toolbar_visible": true
}

View File

@ -0,0 +1,8 @@
{
"name": "MetaCoreSample",
"scenes": [
"Scenes/Main.mcscene.json"
],
"startup_scene": "Scenes/Main.mcscene.json",
"version": "0.1.0"
}

View File

@ -0,0 +1,108 @@
{
"name": "Main",
"objects": [
{
"camera": {
"far_clip": 1000,
"fov_degrees": 60,
"near_clip": 0.100000001490116,
"primary": true
},
"editor_metadata": {
"editor_only": false,
"locked": false,
"selected": false
},
"id": "00005031af666d280adc4216764a07c100000001",
"name": "Main Camera",
"parent_id": "",
"transform": {
"position": [
0,
-10,
3
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
]
}
},
{
"editor_metadata": {
"editor_only": false,
"locked": false,
"selected": false
},
"id": "00005031af669a14494b5bcf7b0288f500000002",
"light": {
"color": [
1,
1,
1
],
"intensity": 1,
"range": 10,
"spot_angle_degrees": 45,
"type": "directional"
},
"name": "Directional Light",
"parent_id": "",
"transform": {
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
]
}
},
{
"editor_metadata": {
"editor_only": false,
"locked": false,
"selected": false
},
"id": "00005031af7e8fac310b0d408494fcfb00000008",
"mesh_renderer": {
"material_asset_id": "builtin://editor-default",
"mesh_asset_id": "builtin://box",
"visible": true
},
"name": "Demo Cube",
"parent_id": "",
"transform": {
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
]
}
}
]
}

View File

@ -2,6 +2,7 @@
#include "MetaCoreEditor/MetaCoreEditorContext.h"
#include "MetaCoreEditorCameraController.h"
#include "MetaCorePlatform/MetaCoreInput.h"
#include "MetaCorePlatform/MetaCoreWindow.h"
#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h"
#include "MetaCoreRender/MetaCoreRenderTypes.h"
@ -9,7 +10,6 @@
#include <imgui.h>
#include <GLFW/glfw3.h>
#include <glm/gtc/type_ptr.hpp>
#include <array>
@ -62,7 +62,7 @@ public:
editorContext.SetDockLayoutBuilt(false);
}
if (ImGui::MenuItem("退出")) {
glfwSetWindowShouldClose(editorContext.GetWindow().GetNativeHandle(), GLFW_TRUE);
editorContext.GetWindow().RequestClose();
}
ImGui::EndMenu();
}
@ -108,7 +108,7 @@ public:
}
if (ImGui::BeginMenu("帮助")) {
ImGui::MenuItem("MetaCore Unity-like Prototype", nullptr, false, false);
ImGui::MenuItem("MetaCore Panda3D Editor Prototype", nullptr, false, false);
ImGui::EndMenu();
}
}
@ -138,27 +138,6 @@ public:
void DrawPanel(MetaCoreEditorContext& editorContext) override {
MetaCoreSceneViewportState& viewportState = editorContext.GetSceneViewportState();
viewportState.Hovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
viewportState.Focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
if (viewportState.Focused && editorContext.GetInput().WasKeyPressed(GLFW_KEY_F)) {
if (MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject(); selectedObject != nullptr) {
editorContext.GetCameraController().FocusGameObject(*selectedObject);
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "相机已聚焦到当前对象");
}
}
editorContext.GetCameraController().Update(viewportState, editorContext.GetInput());
ImGui::Text("对象数: %d", static_cast<int>(editorContext.GetScene().GetGameObjects().size()));
ImGui::SameLine();
if (const MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject(); selectedObject != nullptr) {
ImGui::Text("当前对象: %s", selectedObject->Name.c_str());
} else {
ImGui::TextUnformatted("当前对象: 无");
}
ImGui::Separator();
ImVec2 viewportSize = ImGui::GetContentRegionAvail();
if (viewportSize.x < 1.0F) {
viewportSize.x = 1.0F;
@ -167,30 +146,52 @@ public:
viewportSize.y = 1.0F;
}
const ImVec2 viewportScreenPosition = ImGui::GetCursorScreenPos();
ImGui::InvisibleButton("MetaCoreSceneViewport", viewportSize, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonMiddle);
const ImVec2 mainViewportPosition = ImGui::GetMainViewport()->Pos;
viewportState.Left = viewportScreenPosition.x - mainViewportPosition.x;
viewportState.Top = viewportScreenPosition.y - mainViewportPosition.y;
viewportState.Width = viewportSize.x;
viewportState.Height = viewportSize.y;
viewportState.Hovered = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
viewportState.Focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
editorContext.GetViewportRenderer().Resize(static_cast<int>(viewportSize.x), static_cast<int>(viewportSize.y));
if (viewportState.Focused && !ImGui::GetIO().WantCaptureKeyboard && editorContext.GetInput().WasKeyPressed(MetaCoreInputKey::Focus)) {
if (MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject(); selectedObject != nullptr) {
editorContext.GetCameraController().FocusGameObject(*selectedObject);
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "相机已聚焦到当前对象");
}
}
MetaCoreSceneView sceneView;
sceneView.ViewMatrix = editorContext.GetCameraController().BuildViewMatrix();
sceneView.ProjectionMatrix = editorContext.GetCameraController().BuildProjectionMatrix(viewportSize.x / viewportSize.y);
sceneView.CameraPosition = editorContext.GetCameraController().GetCameraPosition();
editorContext.GetCameraController().Update(viewportState, editorContext.GetInput());
MetaCoreSceneView sceneView = editorContext.GetCameraController().BuildSceneView();
sceneView.SelectedObjectId = editorContext.GetSelectedObjectId();
editorContext.GetViewportRenderer().SetViewportRect(MetaCoreViewportRect{
viewportState.Left,
viewportState.Top,
viewportState.Width,
viewportState.Height
});
editorContext.GetViewportRenderer().RenderSceneToViewport(editorContext.GetScene(), sceneView);
ImGui::Image(
reinterpret_cast<void*>(static_cast<intptr_t>(editorContext.GetViewportRenderer().GetColorTextureHandle())),
viewportSize,
ImVec2(0.0F, 1.0F),
ImVec2(1.0F, 0.0F)
ImDrawList* drawList = ImGui::GetWindowDrawList();
drawList->AddRect(
viewportScreenPosition,
ImVec2(viewportScreenPosition.x + viewportSize.x, viewportScreenPosition.y + viewportSize.y),
IM_COL32(96, 108, 122, 220),
0.0F,
0,
1.0F
);
viewportState.Hovered = ImGui::IsItemHovered();
viewportState.Focused = viewportState.Focused || ImGui::IsItemFocused();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 28.0F);
ImGui::TextDisabled("Alt+左键 环绕 | 中键 平移 | 滚轮 缩放 | F 聚焦");
const char* helpText = "Alt+左键 环绕 | 中键 平移 | 滚轮 缩放 | F 聚焦";
drawList->AddText(
ImVec2(viewportScreenPosition.x + 12.0F, viewportScreenPosition.y + 10.0F),
IM_COL32(220, 224, 230, 220),
helpText
);
}
};

View File

@ -5,8 +5,13 @@
#include <imgui.h>
#include <imgui_internal.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <imgui_impl_win32.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <cstdlib>
#include <filesystem>
@ -14,6 +19,8 @@
#include <memory>
#include <vector>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
namespace MetaCore {
namespace {
@ -27,7 +34,7 @@ void MetaCoreApplyEditorStyle() {
style.WindowBorderSize = 1.0F;
style.FramePadding = ImVec2(8.0F, 4.0F);
style.ItemSpacing = ImVec2(8.0F, 5.0F);
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.11F, 0.12F, 0.14F, 1.0F);
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.11F, 0.12F, 0.14F, 0.97F);
style.Colors[ImGuiCol_TitleBg] = ImVec4(0.10F, 0.11F, 0.13F, 1.0F);
style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.14F, 0.15F, 0.17F, 1.0F);
style.Colors[ImGuiCol_Header] = ImVec4(0.17F, 0.20F, 0.24F, 1.0F);
@ -37,6 +44,7 @@ void MetaCoreApplyEditorStyle() {
style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.27F, 0.33F, 0.39F, 1.0F);
style.Colors[ImGuiCol_Tab] = ImVec4(0.12F, 0.14F, 0.16F, 1.0F);
style.Colors[ImGuiCol_TabSelected] = ImVec4(0.20F, 0.26F, 0.34F, 1.0F);
style.Colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.0F, 0.0F, 0.0F, 0.0F);
}
void MetaCoreConfigureChineseFont() {
@ -87,7 +95,16 @@ bool MetaCoreEditorApp::Initialize() {
return false;
}
if (!RenderDevice_.Initialize(Window_.GetNativeHandle())) {
Window_.SetNativeWindowMessageHandler([](void* nativeWindowHandle, unsigned int message, std::uintptr_t wparam, std::intptr_t lparam) {
ImGui_ImplWin32_WndProcHandler(
static_cast<HWND>(nativeWindowHandle),
message,
static_cast<WPARAM>(wparam),
static_cast<LPARAM>(lparam)
);
});
if (!RenderDevice_.Initialize(Window_)) {
return false;
}
@ -95,7 +112,7 @@ bool MetaCoreEditorApp::Initialize() {
return false;
}
if (!ViewportRenderer_.Initialize()) {
if (!ViewportRenderer_.Initialize(RenderDevice_)) {
return false;
}
@ -123,7 +140,7 @@ bool MetaCoreEditorApp::Initialize() {
}
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "System", "MetaCore 编辑器已初始化");
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Render", "OpenGL 场景视口已准备完成");
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Render", "Panda3D 场景视口已准备完成");
Initialized_ = true;
return true;
@ -134,15 +151,15 @@ int MetaCoreEditorApp::Run() {
Window_.BeginFrame();
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
DrawEditorFrame();
ImGui::Render();
const auto [framebufferWidth, framebufferHeight] = Window_.GetFramebufferSize();
RenderDevice_.BeginFrame(framebufferWidth, framebufferHeight, glm::vec4(0.08F, 0.09F, 0.11F, 1.0F));
RenderDevice_.RenderFrame();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
RenderDevice_.PresentFrame();
Window_.EndFrame();
}
@ -175,11 +192,11 @@ bool MetaCoreEditorApp::InitializeImGui() {
MetaCoreConfigureChineseFont();
MetaCoreApplyEditorStyle();
if (!ImGui_ImplGlfw_InitForOpenGL(Window_.GetNativeHandle(), true)) {
if (!ImGui_ImplWin32_InitForOpenGL(Window_.GetNativeWindowHandle())) {
return false;
}
if (!ImGui_ImplOpenGL3_Init("#version 330")) {
if (!ImGui_ImplOpenGL3_Init("#version 130")) {
return false;
}
@ -188,7 +205,7 @@ bool MetaCoreEditorApp::InitializeImGui() {
void MetaCoreEditorApp::ShutdownImGui() {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
@ -203,6 +220,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoNavFocus |
ImGuiWindowFlags_MenuBar;
@ -225,15 +243,80 @@ void MetaCoreEditorApp::DrawEditorFrame() {
EnsureDefaultDockLayout(dockSpaceId);
ImGui::End();
MetaCoreIEditorPanelProvider* scenePanelProvider = nullptr;
for (const auto& panelProvider : ModuleRegistry_.GetPanelProviders()) {
if (panelProvider->GetPanelId() == "Scene") {
scenePanelProvider = panelProvider.get();
continue;
}
bool& panelOpen = ModuleRegistry_.AccessPanelOpenState(panelProvider->GetPanelId());
if (!panelOpen) {
continue;
}
ImGui::Begin(panelProvider->GetPanelTitle().c_str(), &panelOpen);
bool pushedTransparentColors = false;
if (panelProvider->WantsTransparentBackground()) {
ImGui::SetNextWindowBgAlpha(0.0F);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
pushedTransparentColors = true;
}
bool pushedPadding = false;
if (panelProvider->WantsZeroPadding()) {
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0F, 0.0F));
pushedPadding = true;
}
const ImGuiWindowFlags panelWindowFlags = static_cast<ImGuiWindowFlags>(panelProvider->GetWindowFlags());
ImGui::Begin(panelProvider->GetPanelTitle().c_str(), &panelOpen, panelWindowFlags);
panelProvider->DrawPanel(*EditorContext_);
ImGui::End();
if (pushedPadding) {
ImGui::PopStyleVar();
}
if (pushedTransparentColors) {
ImGui::PopStyleColor(3);
}
}
if (scenePanelProvider != nullptr && ModuleRegistry_.IsPanelOpen("Scene")) {
ImGuiDockNode* centralNode = ImGui::DockBuilderGetCentralNode(dockSpaceId);
if (centralNode != nullptr) {
const ImVec2 overlayPosition = centralNode->Pos;
const ImVec2 overlaySize = centralNode->Size;
ImGui::SetNextWindowPos(overlayPosition);
ImGui::SetNextWindowSize(overlaySize);
ImGui::SetNextWindowBgAlpha(0.0F);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0F, 0.0F));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
constexpr ImGuiWindowFlags sceneOverlayFlags =
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoDocking |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoNavFocus |
ImGuiWindowFlags_NoBackground;
bool sceneOpen = true;
ImGui::Begin("MetaCoreSceneOverlay", &sceneOpen, sceneOverlayFlags);
scenePanelProvider->DrawPanel(*EditorContext_);
ImGui::End();
ImGui::PopStyleColor(3);
ImGui::PopStyleVar(3);
}
}
}
@ -252,7 +335,6 @@ void MetaCoreEditorApp::EnsureDefaultDockLayout(unsigned int dockSpaceId) {
const ImGuiID bottomNodeId = ImGui::DockBuilderSplitNode(mainNodeId, ImGuiDir_Down, 0.28F, nullptr, &mainNodeId);
ImGui::DockBuilderDockWindow("层级", leftNodeId);
ImGui::DockBuilderDockWindow("场景", mainNodeId);
ImGui::DockBuilderDockWindow("检查器", rightNodeId);
ImGui::DockBuilderDockWindow("项目", bottomNodeId);
ImGui::DockBuilderDockWindow("控制台", bottomNodeId);

View File

@ -5,9 +5,8 @@
#include <imgui.h>
#include <GLFW/glfw3.h>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/geometric.hpp>
#include <glm/trigonometric.hpp>
#include <algorithm>
#include <cmath>
@ -20,13 +19,16 @@ void MetaCoreEditorCameraController::Update(const MetaCoreSceneViewportState& vi
}
ImGuiIO& io = ImGui::GetIO();
if (io.WantCaptureMouse) {
return;
}
if (viewportState.Hovered && std::abs(io.MouseWheel) > 0.001F) {
Distance_ *= (io.MouseWheel > 0.0F) ? 0.88F : 1.12F;
Distance_ = std::clamp(Distance_, 1.5F, 40.0F);
}
const bool altDown = input.IsKeyDown(GLFW_KEY_LEFT_ALT) || input.IsKeyDown(GLFW_KEY_RIGHT_ALT);
const bool altDown = input.IsKeyDown(MetaCoreInputKey::LeftAlt) || input.IsKeyDown(MetaCoreInputKey::RightAlt);
if (viewportState.Hovered && altDown && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
YawDegrees_ += io.MouseDelta.x * 0.35F;
PitchDegrees_ -= io.MouseDelta.y * 0.35F;
@ -48,13 +50,13 @@ void MetaCoreEditorCameraController::FocusGameObject(const MetaCoreGameObject& g
Distance_ = std::max(3.5F, scaleRadius * 4.0F + 2.5F);
}
glm::mat4 MetaCoreEditorCameraController::BuildViewMatrix() const {
return glm::lookAt(GetCameraPosition(), Target_, glm::vec3(0.0F, 1.0F, 0.0F));
}
glm::mat4 MetaCoreEditorCameraController::BuildProjectionMatrix(float aspectRatio) const {
aspectRatio = (aspectRatio > 0.01F) ? aspectRatio : 1.0F;
return glm::perspective(glm::radians(FieldOfViewDegrees_), aspectRatio, 0.1F, 200.0F);
MetaCoreSceneView MetaCoreEditorCameraController::BuildSceneView() const {
MetaCoreSceneView sceneView;
sceneView.CameraPosition = GetCameraPosition();
sceneView.CameraTarget = Target_;
sceneView.CameraUp = glm::vec3(0.0F, 1.0F, 0.0F);
sceneView.VerticalFieldOfViewDegrees = FieldOfViewDegrees_;
return sceneView;
}
glm::vec3 MetaCoreEditorCameraController::GetCameraPosition() const {

View File

@ -1,8 +1,8 @@
#pragma once
#include "MetaCoreEditor/MetaCoreEditorContext.h"
#include "MetaCoreRender/MetaCoreRenderTypes.h"
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
namespace MetaCore {
@ -10,13 +10,11 @@ namespace MetaCore {
class MetaCoreInput;
struct MetaCoreGameObject;
// 负责场景视口相机的轨道、平移、缩放与聚焦控制。
class MetaCoreEditorCameraController {
public:
void Update(const MetaCoreSceneViewportState& viewportState, const MetaCoreInput& input);
void FocusGameObject(const MetaCoreGameObject& gameObject);
[[nodiscard]] glm::mat4 BuildViewMatrix() const;
[[nodiscard]] glm::mat4 BuildProjectionMatrix(float aspectRatio) const;
[[nodiscard]] MetaCoreSceneView BuildSceneView() const;
[[nodiscard]] glm::vec3 GetCameraPosition() const;
private:

View File

@ -17,15 +17,15 @@ struct MetaCoreGameObject;
class MetaCoreEditorModuleRegistry;
class MetaCoreEditorCameraController;
// 描述场景视口当前的尺寸与焦点状态。
struct MetaCoreSceneViewportState {
float Left = 0.0F;
float Top = 0.0F;
float Width = 1.0F;
float Height = 1.0F;
bool Hovered = false;
bool Focused = false;
};
// 在编辑器各模块之间共享运行时状态。
class MetaCoreEditorContext {
public:
MetaCoreEditorContext(

View File

@ -10,7 +10,6 @@ namespace MetaCore {
class MetaCoreEditorContext;
struct MetaCoreGameObject;
// 定义可向编辑器注册菜单的扩展接口。
class MetaCoreIMenuProvider {
public:
virtual ~MetaCoreIMenuProvider() = default;
@ -18,17 +17,18 @@ public:
virtual void DrawMenuBar(MetaCoreEditorContext& editorContext) = 0;
};
// 定义可向编辑器注册面板的扩展接口。
class MetaCoreIEditorPanelProvider {
public:
virtual ~MetaCoreIEditorPanelProvider() = default;
[[nodiscard]] virtual std::string GetPanelId() const = 0;
[[nodiscard]] virtual std::string GetPanelTitle() const = 0;
[[nodiscard]] virtual bool IsOpenByDefault() const = 0;
[[nodiscard]] virtual int GetWindowFlags() const { return 0; }
[[nodiscard]] virtual bool WantsTransparentBackground() const { return false; }
[[nodiscard]] virtual bool WantsZeroPadding() const { return false; }
virtual void DrawPanel(MetaCoreEditorContext& editorContext) = 0;
};
// 定义可向 Inspector 注册组件绘制器的扩展接口。
class MetaCoreIInspectorDrawer {
public:
virtual ~MetaCoreIInspectorDrawer() = default;
@ -37,7 +37,6 @@ public:
virtual void DrawInspector(MetaCoreEditorContext& editorContext, MetaCoreGameObject& gameObject) = 0;
};
// 管理编辑器中全部可扩展菜单、面板和 Inspector 绘制器。
class MetaCoreEditorModuleRegistry {
public:
void RegisterMenuProvider(std::unique_ptr<MetaCoreIMenuProvider> provider);
@ -58,7 +57,6 @@ private:
std::unordered_map<std::string, bool> PanelOpenStates_{};
};
// 定义一个可注册到编辑器中的模块生命周期接口。
class MetaCoreIModule {
public:
virtual ~MetaCoreIModule() = default;

View File

@ -1,53 +1,49 @@
#include "MetaCorePlatform/MetaCoreInput.h"
#include <GLFW/glfw3.h>
namespace MetaCore {
void MetaCoreInput::BeginFrame(GLFWwindow* nativeWindow) {
void MetaCoreInput::BeginFrame() {
PreviousKeyStates_ = CurrentKeyStates_;
PreviousMouseStates_ = CurrentMouseStates_;
for (int key = 0; key < static_cast<int>(CurrentKeyStates_.size()); ++key) {
CurrentKeyStates_[static_cast<std::size_t>(key)] = glfwGetKey(nativeWindow, key) == GLFW_PRESS;
}
for (int button = 0; button < static_cast<int>(CurrentMouseStates_.size()); ++button) {
CurrentMouseStates_[static_cast<std::size_t>(button)] = glfwGetMouseButton(nativeWindow, button) == GLFW_PRESS;
}
double cursorX = 0.0;
double cursorY = 0.0;
glfwGetCursorPos(nativeWindow, &cursorX, &cursorY);
const glm::vec2 previousPosition = CursorPosition_;
CursorPosition_ = glm::vec2(static_cast<float>(cursorX), static_cast<float>(cursorY));
CursorDelta_ = CursorPosition_ - previousPosition;
PreviousCursorPosition_ = CursorPosition_;
CursorDelta_ = glm::vec2(0.0F, 0.0F);
MouseWheelDelta_ = 0.0F;
}
bool MetaCoreInput::IsKeyDown(int key) const {
if (key < 0 || key >= static_cast<int>(CurrentKeyStates_.size())) {
return false;
}
void MetaCoreInput::SetKeyState(MetaCoreInputKey key, bool isDown) {
CurrentKeyStates_[static_cast<std::size_t>(key)] = isDown;
}
void MetaCoreInput::SetMouseButtonState(MetaCoreMouseButton button, bool isDown) {
CurrentMouseStates_[static_cast<std::size_t>(button)] = isDown;
}
void MetaCoreInput::SetCursorPosition(const glm::vec2& cursorPosition) {
CursorPosition_ = cursorPosition;
CursorDelta_ = CursorPosition_ - PreviousCursorPosition_;
}
void MetaCoreInput::AddMouseWheelDelta(float delta) {
MouseWheelDelta_ += delta;
}
bool MetaCoreInput::IsKeyDown(MetaCoreInputKey key) const {
return CurrentKeyStates_[static_cast<std::size_t>(key)];
}
bool MetaCoreInput::WasKeyPressed(int key) const {
if (key < 0 || key >= static_cast<int>(CurrentKeyStates_.size())) {
return false;
}
const std::size_t index = static_cast<std::size_t>(key);
return CurrentKeyStates_[index] && !PreviousKeyStates_[index];
bool MetaCoreInput::WasKeyPressed(MetaCoreInputKey key) const {
const std::size_t keyIndex = static_cast<std::size_t>(key);
return CurrentKeyStates_[keyIndex] && !PreviousKeyStates_[keyIndex];
}
bool MetaCoreInput::IsMouseButtonDown(int button) const {
if (button < 0 || button >= static_cast<int>(CurrentMouseStates_.size())) {
return false;
}
bool MetaCoreInput::IsMouseButtonDown(MetaCoreMouseButton button) const {
return CurrentMouseStates_[static_cast<std::size_t>(button)];
}
float MetaCoreInput::GetMouseWheelDelta() const {
return MouseWheelDelta_;
}
const glm::vec2& MetaCoreInput::GetCursorPosition() const {
return CursorPosition_;
}

View File

@ -1,99 +1,259 @@
#include "MetaCorePlatform/MetaCoreWindow.h"
#include <GLFW/glfw3.h>
#include "graphicsWindow.h"
#include "graphicsWindowProc.h"
#include "load_prc_file.h"
#include "nativeWindowHandle.h"
#include "pandaFramework.h"
#include "windowFramework.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <chrono>
#include <memory>
#include <utility>
namespace MetaCore {
namespace {
void MetaCoreConfigurePanda3D(const std::string& title) {
static bool configured = false;
if (configured) {
return;
}
load_prc_file_data("", "load-display pandagl\n");
load_prc_file_data("", "aux-display p3windisplay\n");
load_prc_file_data("", "window-type onscreen\n");
load_prc_file_data("", "model-path ./models\n");
load_prc_file_data("", "sync-video false\n");
load_prc_file_data("", "notify-level warning\n");
load_prc_file_data("", "audio-library-name null\n");
load_prc_file_data("", "textures-power-2 none\n");
load_prc_file_data("", "editor-window-title " + title + "\n");
configured = true;
}
HWND MetaCoreResolveNativeWindowHandle(GraphicsWindow* graphicsWindow) {
if (graphicsWindow == nullptr) {
return nullptr;
}
WindowHandle* windowHandle = graphicsWindow->get_window_handle();
if (windowHandle == nullptr) {
return nullptr;
}
return reinterpret_cast<HWND>(windowHandle->get_int_handle());
}
bool MetaCoreIsVirtualKeyDown(int virtualKey) {
return (GetAsyncKeyState(virtualKey) & 0x8000) != 0;
}
} // namespace
class MetaCoreWindow::MetaCoreWindowImpl {
public:
PandaFramework Framework{};
WindowFramework* WindowFrameworkHandle = nullptr;
GraphicsWindow* GraphicsWindowHandle = nullptr;
HWND NativeHandle = nullptr;
MetaCoreInput Input{};
MetaCoreNativeWindowMessageHandler MessageHandler{};
std::unique_ptr<class MetaCoreWindowMessageProc> MessageProc{};
std::chrono::steady_clock::time_point LastFrameTime = std::chrono::steady_clock::now();
float DeltaSeconds = 1.0F / 60.0F;
bool Initialized = false;
bool CloseRequested = false;
};
class MetaCoreWindowMessageProc final : public GraphicsWindowProc {
public:
explicit MetaCoreWindowMessageProc(MetaCoreWindow::MetaCoreWindowImpl& owner)
: Owner_(owner) {
}
LONG wnd_proc(GraphicsWindow*, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) override;
private:
MetaCoreWindow::MetaCoreWindowImpl& Owner_;
};
LONG MetaCoreWindowMessageProc::wnd_proc(GraphicsWindow*, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if (Owner_.MessageHandler) {
Owner_.MessageHandler(hwnd, msg, static_cast<std::uintptr_t>(wparam), static_cast<std::intptr_t>(lparam));
}
if (msg == WM_MOUSEWHEEL) {
Owner_.Input.AddMouseWheelDelta(static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam)) / static_cast<float>(WHEEL_DELTA));
} else if (msg == WM_CLOSE) {
Owner_.CloseRequested = true;
}
return 0;
}
MetaCoreWindow::MetaCoreWindow()
: Impl_(std::make_unique<MetaCoreWindowImpl>()) {
}
MetaCoreWindow::~MetaCoreWindow() {
Shutdown();
}
bool MetaCoreWindow::Initialize(int width, int height, const std::string& title) {
if (NativeHandle_ != nullptr) {
if (Impl_->Initialized) {
return true;
}
if (glfwInit() != GLFW_TRUE) {
MetaCoreConfigurePanda3D(title);
Impl_->Framework.open_framework();
Impl_->Framework.set_window_title(title);
WindowProperties windowProperties;
windowProperties.set_size(width, height);
windowProperties.set_title(title);
windowProperties.set_foreground(true);
Impl_->WindowFrameworkHandle = Impl_->Framework.open_window(windowProperties, 0);
if (Impl_->WindowFrameworkHandle == nullptr) {
Shutdown();
return false;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#if defined(__APPLE__)
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
#endif
NativeHandle_ = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
if (NativeHandle_ == nullptr) {
glfwTerminate();
Impl_->GraphicsWindowHandle = Impl_->WindowFrameworkHandle->get_graphics_window();
Impl_->NativeHandle = MetaCoreResolveNativeWindowHandle(Impl_->GraphicsWindowHandle);
if (Impl_->GraphicsWindowHandle == nullptr || Impl_->NativeHandle == nullptr) {
Shutdown();
return false;
}
glfwMakeContextCurrent(NativeHandle_);
glfwSwapInterval(1);
LastFrameTime_ = glfwGetTime();
Impl_->MessageProc = std::make_unique<MetaCoreWindowMessageProc>(*Impl_);
if (Impl_->GraphicsWindowHandle->supports_window_procs()) {
Impl_->GraphicsWindowHandle->add_window_proc(Impl_->MessageProc.get());
}
Impl_->CloseRequested = false;
Impl_->LastFrameTime = std::chrono::steady_clock::now();
Impl_->Initialized = true;
return true;
}
void MetaCoreWindow::Shutdown() {
if (NativeHandle_ != nullptr) {
glfwDestroyWindow(NativeHandle_);
NativeHandle_ = nullptr;
glfwTerminate();
if (!Impl_->Initialized) {
return;
}
if (Impl_->GraphicsWindowHandle != nullptr && Impl_->MessageProc != nullptr && Impl_->GraphicsWindowHandle->supports_window_procs()) {
Impl_->GraphicsWindowHandle->remove_window_proc(Impl_->MessageProc.get());
}
Impl_->MessageProc.reset();
if (Impl_->WindowFrameworkHandle != nullptr) {
Impl_->Framework.close_window(Impl_->WindowFrameworkHandle);
Impl_->WindowFrameworkHandle = nullptr;
}
Impl_->GraphicsWindowHandle = nullptr;
Impl_->NativeHandle = nullptr;
Impl_->Framework.close_framework();
Impl_->Initialized = false;
}
void MetaCoreWindow::BeginFrame() {
glfwPollEvents();
const double currentTime = glfwGetTime();
DeltaSeconds_ = static_cast<float>(currentTime - LastFrameTime_);
LastFrameTime_ = currentTime;
if (DeltaSeconds_ <= 0.0F) {
DeltaSeconds_ = 1.0F / 60.0F;
if (!Impl_->Initialized) {
return;
}
Input_.BeginFrame(NativeHandle_);
const auto now = std::chrono::steady_clock::now();
Impl_->DeltaSeconds = std::chrono::duration<float>(now - Impl_->LastFrameTime).count();
if (Impl_->DeltaSeconds <= 0.0F) {
Impl_->DeltaSeconds = 1.0F / 60.0F;
}
Impl_->LastFrameTime = now;
Impl_->Input.BeginFrame();
if (Impl_->GraphicsWindowHandle != nullptr) {
Impl_->GraphicsWindowHandle->process_events();
}
Impl_->Input.SetKeyState(MetaCoreInputKey::LeftAlt, MetaCoreIsVirtualKeyDown(VK_LMENU));
Impl_->Input.SetKeyState(MetaCoreInputKey::RightAlt, MetaCoreIsVirtualKeyDown(VK_RMENU));
Impl_->Input.SetKeyState(MetaCoreInputKey::Focus, MetaCoreIsVirtualKeyDown('F'));
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Left, MetaCoreIsVirtualKeyDown(VK_LBUTTON));
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Right, MetaCoreIsVirtualKeyDown(VK_RBUTTON));
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Middle, MetaCoreIsVirtualKeyDown(VK_MBUTTON));
POINT cursorPosition{};
if (GetCursorPos(&cursorPosition) != FALSE && ScreenToClient(Impl_->NativeHandle, &cursorPosition) != FALSE) {
Impl_->Input.SetCursorPosition(glm::vec2(static_cast<float>(cursorPosition.x), static_cast<float>(cursorPosition.y)));
}
}
void MetaCoreWindow::EndFrame() const {
glfwSwapBuffers(NativeHandle_);
void MetaCoreWindow::EndFrame() {
}
bool MetaCoreWindow::ShouldClose() const {
return NativeHandle_ == nullptr || glfwWindowShouldClose(NativeHandle_) == GLFW_TRUE;
return !Impl_->Initialized || Impl_->CloseRequested;
}
GLFWwindow* MetaCoreWindow::GetNativeHandle() const {
return NativeHandle_;
void MetaCoreWindow::RequestClose() {
Impl_->CloseRequested = true;
if (Impl_->GraphicsWindowHandle != nullptr) {
Impl_->GraphicsWindowHandle->request_close();
}
}
void* MetaCoreWindow::GetNativeWindowHandle() const {
return Impl_->NativeHandle;
}
void* MetaCoreWindow::GetNativeFrameworkHandle() const {
return &Impl_->Framework;
}
void* MetaCoreWindow::GetNativeWindowFrameworkHandle() const {
return Impl_->WindowFrameworkHandle;
}
void* MetaCoreWindow::GetNativeGraphicsWindowHandle() const {
return Impl_->GraphicsWindowHandle;
}
MetaCoreInput& MetaCoreWindow::GetInput() {
return Input_;
return Impl_->Input;
}
const MetaCoreInput& MetaCoreWindow::GetInput() const {
return Input_;
return Impl_->Input;
}
float MetaCoreWindow::GetDeltaSeconds() const {
return DeltaSeconds_;
return Impl_->DeltaSeconds;
}
std::pair<int, int> MetaCoreWindow::GetWindowSize() const {
int width = 0;
int height = 0;
glfwGetWindowSize(NativeHandle_, &width, &height);
return {width, height};
if (Impl_->GraphicsWindowHandle == nullptr) {
return {0, 0};
}
const WindowProperties windowProperties = Impl_->GraphicsWindowHandle->get_properties();
return {windowProperties.get_x_size(), windowProperties.get_y_size()};
}
std::pair<int, int> MetaCoreWindow::GetFramebufferSize() const {
int width = 0;
int height = 0;
glfwGetFramebufferSize(NativeHandle_, &width, &height);
return {width, height};
return GetWindowSize();
}
void MetaCoreWindow::SetNativeWindowMessageHandler(MetaCoreNativeWindowMessageHandler handler) {
Impl_->MessageHandler = std::move(handler);
}
} // namespace MetaCore

View File

@ -4,38 +4,72 @@
#include <array>
struct GLFWwindow;
namespace MetaCore {
// 统一封装窗口轮询后的键鼠状态,避免上层直接依赖 GLFW 轮询细节。
// 定义编辑器当前阶段需要识别的按键。
enum class MetaCoreInputKey : unsigned int {
LeftAlt = 0,
RightAlt,
Focus,
Count
};
// 定义编辑器当前阶段需要识别的鼠标按键。
enum class MetaCoreMouseButton : unsigned int {
Left = 0,
Right,
Middle,
Count
};
// 统一封装窗口层采集后的输入状态,避免上层直接依赖 Win32 或 Panda3D 细节。
class MetaCoreInput {
public:
// 由窗口层在每帧轮询事件后刷新一次输入状态。
void BeginFrame(GLFWwindow* nativeWindow);
// 在每帧开始时重置瞬时状态并保存上一帧快照。
void BeginFrame();
// 由窗口层写入按键状态。
void SetKeyState(MetaCoreInputKey key, bool isDown);
// 由窗口层写入鼠标按键状态。
void SetMouseButtonState(MetaCoreMouseButton button, bool isDown);
// 由窗口层写入当前鼠标位置。
void SetCursorPosition(const glm::vec2& cursorPosition);
// 由窗口消息累加滚轮值,正值表示向前滚动。
void AddMouseWheelDelta(float delta);
// 判断按键当前是否处于按下状态。
[[nodiscard]] bool IsKeyDown(int key) const;
[[nodiscard]] bool IsKeyDown(MetaCoreInputKey key) const;
// 判断按键是否在本帧刚刚按下。
[[nodiscard]] bool WasKeyPressed(int key) const;
[[nodiscard]] bool WasKeyPressed(MetaCoreInputKey key) const;
// 判断鼠标按键当前是否按下。
[[nodiscard]] bool IsMouseButtonDown(int button) const;
[[nodiscard]] bool IsMouseButtonDown(MetaCoreMouseButton button) const;
// 返回当前帧累计的滚轮值。
[[nodiscard]] float GetMouseWheelDelta() const;
// 返回当前鼠标位置。
[[nodiscard]] const glm::vec2& GetCursorPosition() const;
// 返回本帧鼠标相对上一帧的位移。
// 返回相对上一帧的鼠标位移。
[[nodiscard]] const glm::vec2& GetCursorDelta() const;
private:
std::array<bool, 512> CurrentKeyStates_{};
std::array<bool, 512> PreviousKeyStates_{};
std::array<bool, 8> CurrentMouseStates_{};
std::array<bool, 8> PreviousMouseStates_{};
static constexpr std::size_t KeyCount_ = static_cast<std::size_t>(MetaCoreInputKey::Count);
static constexpr std::size_t MouseButtonCount_ = static_cast<std::size_t>(MetaCoreMouseButton::Count);
std::array<bool, KeyCount_> CurrentKeyStates_{};
std::array<bool, KeyCount_> PreviousKeyStates_{};
std::array<bool, MouseButtonCount_> CurrentMouseStates_{};
std::array<bool, MouseButtonCount_> PreviousMouseStates_{};
glm::vec2 CursorPosition_{0.0F, 0.0F};
glm::vec2 PreviousCursorPosition_{0.0F, 0.0F};
glm::vec2 CursorDelta_{0.0F, 0.0F};
float MouseWheelDelta_ = 0.0F;
};
} // namespace MetaCore

View File

@ -2,55 +2,75 @@
#include "MetaCorePlatform/MetaCoreInput.h"
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <utility>
struct GLFWwindow;
namespace MetaCore {
// 负责管理主窗口、时间步进和最基础的事件轮询。
class MetaCoreRenderDevice;
// 定义宿主窗口层向上暴露的原生消息回调,用于把 Win32 消息转交给 ImGui。
using MetaCoreNativeWindowMessageHandler = std::function<void(void*, unsigned int, std::uintptr_t, std::intptr_t)>;
// 封装 Panda3D 窗口、时间步进与基础输入采集。
class MetaCoreWindow {
public:
MetaCoreWindow() = default;
class MetaCoreWindowImpl;
MetaCoreWindow();
~MetaCoreWindow();
// 创建主窗口并初始化 OpenGL 上下文
// 创建 Panda3D 主窗口并准备输入采集
bool Initialize(int width, int height, const std::string& title);
// 释放窗口及 GLFW 资源。
// 释放窗口与 Panda3D 宿主资源。
void Shutdown();
// 轮询事件并刷新输入状态。
// 处理窗口事件、更新时间步进并刷新输入状态。
void BeginFrame();
// 交换前后缓冲区
void EndFrame() const;
// 为未来扩展保留的帧结束入口
void EndFrame();
// 返回窗口是否请求关闭
// 返回窗口是否请求退出
[[nodiscard]] bool ShouldClose() const;
// 返回原生 GLFW 窗口指针。
[[nodiscard]] GLFWwindow* GetNativeHandle() const;
// 主动请求窗口退出。
void RequestClose();
// 返回原生窗口句柄,供 ImGui Win32 backend 初始化使用。
[[nodiscard]] void* GetNativeWindowHandle() const;
// 返回 PandaFramework 原生对象指针,仅供底层桥接层使用。
[[nodiscard]] void* GetNativeFrameworkHandle() const;
// 返回 WindowFramework 原生对象指针,仅供底层桥接层使用。
[[nodiscard]] void* GetNativeWindowFrameworkHandle() const;
// 返回 GraphicsWindow 原生对象指针,仅供底层桥接层使用。
[[nodiscard]] void* GetNativeGraphicsWindowHandle() const;
// 返回当前输入对象。
[[nodiscard]] MetaCoreInput& GetInput();
[[nodiscard]] const MetaCoreInput& GetInput() const;
// 返回当前帧的时间间隔,单位为秒。
// 返回当前帧耗时,单位为秒。
[[nodiscard]] float GetDeltaSeconds() const;
// 返回窗口尺寸。
// 返回当前窗口尺寸。
[[nodiscard]] std::pair<int, int> GetWindowSize() const;
// 返回帧缓冲尺寸。
// 返回当前帧缓冲尺寸。
[[nodiscard]] std::pair<int, int> GetFramebufferSize() const;
// 注册原生消息回调。
void SetNativeWindowMessageHandler(MetaCoreNativeWindowMessageHandler handler);
private:
GLFWwindow* NativeHandle_ = nullptr;
MetaCoreInput Input_{};
float DeltaSeconds_ = 1.0F / 60.0F;
double LastFrameTime_ = 0.0;
std::unique_ptr<MetaCoreWindowImpl> Impl_{};
};
} // namespace MetaCore

View File

@ -1,110 +0,0 @@
#include "MetaCoreRender/MetaCoreDebugGeometry.h"
#include <glm/vec3.hpp>
#include <vector>
namespace MetaCore {
namespace {
MetaCoreVertex MetaCoreMakeVertex(const glm::vec3& position, const glm::vec3& normal, const glm::vec4& color) {
return MetaCoreVertex{position, normal, color};
}
} // namespace
bool MetaCoreDebugGeometry::Initialize() {
std::vector<MetaCoreVertex> gridVertices;
std::vector<std::uint32_t> gridIndices;
std::uint32_t gridIndex = 0;
constexpr int gridHalfExtent = 10;
for (int step = -gridHalfExtent; step <= gridHalfExtent; ++step) {
const bool isCenterLine = step == 0;
const glm::vec4 lineColor = isCenterLine
? glm::vec4(0.34F, 0.38F, 0.42F, 1.0F)
: glm::vec4(0.22F, 0.24F, 0.28F, 1.0F);
gridVertices.push_back(MetaCoreMakeVertex(glm::vec3(static_cast<float>(step), 0.0F, -static_cast<float>(gridHalfExtent)), glm::vec3(0.0F), lineColor));
gridVertices.push_back(MetaCoreMakeVertex(glm::vec3(static_cast<float>(step), 0.0F, static_cast<float>(gridHalfExtent)), glm::vec3(0.0F), lineColor));
gridIndices.push_back(gridIndex++);
gridIndices.push_back(gridIndex++);
gridVertices.push_back(MetaCoreMakeVertex(glm::vec3(-static_cast<float>(gridHalfExtent), 0.0F, static_cast<float>(step)), glm::vec3(0.0F), lineColor));
gridVertices.push_back(MetaCoreMakeVertex(glm::vec3(static_cast<float>(gridHalfExtent), 0.0F, static_cast<float>(step)), glm::vec3(0.0F), lineColor));
gridIndices.push_back(gridIndex++);
gridIndices.push_back(gridIndex++);
}
std::vector<MetaCoreVertex> axesVertices{
MetaCoreMakeVertex(glm::vec3(0.0F), glm::vec3(0.0F), glm::vec4(0.95F, 0.28F, 0.24F, 1.0F)),
MetaCoreMakeVertex(glm::vec3(1.5F, 0.0F, 0.0F), glm::vec3(0.0F), glm::vec4(0.95F, 0.28F, 0.24F, 1.0F)),
MetaCoreMakeVertex(glm::vec3(0.0F), glm::vec3(0.0F), glm::vec4(0.30F, 0.78F, 0.32F, 1.0F)),
MetaCoreMakeVertex(glm::vec3(0.0F, 1.5F, 0.0F), glm::vec3(0.0F), glm::vec4(0.30F, 0.78F, 0.32F, 1.0F)),
MetaCoreMakeVertex(glm::vec3(0.0F), glm::vec3(0.0F), glm::vec4(0.28F, 0.53F, 0.95F, 1.0F)),
MetaCoreMakeVertex(glm::vec3(0.0F, 0.0F, 1.5F), glm::vec3(0.0F), glm::vec4(0.28F, 0.53F, 0.95F, 1.0F))
};
std::vector<std::uint32_t> axesIndices{0, 1, 2, 3, 4, 5};
const glm::vec4 cubeColor(1.0F, 1.0F, 1.0F, 1.0F);
std::vector<MetaCoreVertex> cubeVertices{
MetaCoreMakeVertex(glm::vec3(-0.5F, -0.5F, 0.5F), glm::vec3(0.0F, 0.0F, 1.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, -0.5F, 0.5F), glm::vec3(0.0F, 0.0F, 1.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, 0.5F, 0.5F), glm::vec3(0.0F, 0.0F, 1.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, 0.5F, 0.5F), glm::vec3(0.0F, 0.0F, 1.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, -0.5F, -0.5F), glm::vec3(0.0F, 0.0F, -1.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, -0.5F, -0.5F), glm::vec3(0.0F, 0.0F, -1.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, 0.5F, -0.5F), glm::vec3(0.0F, 0.0F, -1.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, 0.5F, -0.5F), glm::vec3(0.0F, 0.0F, -1.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, 0.5F, 0.5F), glm::vec3(-1.0F, 0.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, 0.5F, -0.5F), glm::vec3(-1.0F, 0.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, -0.5F, -0.5F), glm::vec3(-1.0F, 0.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, -0.5F, 0.5F), glm::vec3(-1.0F, 0.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, 0.5F, 0.5F), glm::vec3(1.0F, 0.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, 0.5F, -0.5F), glm::vec3(1.0F, 0.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, -0.5F, -0.5F), glm::vec3(1.0F, 0.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, -0.5F, 0.5F), glm::vec3(1.0F, 0.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, 0.5F, -0.5F), glm::vec3(0.0F, 1.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, 0.5F, -0.5F), glm::vec3(0.0F, 1.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, 0.5F, 0.5F), glm::vec3(0.0F, 1.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, 0.5F, 0.5F), glm::vec3(0.0F, 1.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, -0.5F, -0.5F), glm::vec3(0.0F, -1.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, -0.5F, -0.5F), glm::vec3(0.0F, -1.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3( 0.5F, -0.5F, 0.5F), glm::vec3(0.0F, -1.0F, 0.0F), cubeColor),
MetaCoreMakeVertex(glm::vec3(-0.5F, -0.5F, 0.5F), glm::vec3(0.0F, -1.0F, 0.0F), cubeColor)
};
std::vector<std::uint32_t> cubeIndices{
0, 1, 2, 0, 2, 3,
4, 6, 5, 4, 7, 6,
8, 9, 10, 8, 10, 11,
12, 14, 13, 12, 15, 14,
16, 17, 18, 16, 18, 19,
20, 22, 21, 20, 23, 22
};
return GridMesh_.Build(gridVertices, gridIndices, MetaCorePrimitiveType::Lines)
&& AxesMesh_.Build(axesVertices, axesIndices, MetaCorePrimitiveType::Lines)
&& CubeMesh_.Build(cubeVertices, cubeIndices, MetaCorePrimitiveType::Triangles);
}
void MetaCoreDebugGeometry::Shutdown() {
GridMesh_.Shutdown();
AxesMesh_.Shutdown();
CubeMesh_.Shutdown();
}
const MetaCoreMesh& MetaCoreDebugGeometry::GetGridMesh() const {
return GridMesh_;
}
const MetaCoreMesh& MetaCoreDebugGeometry::GetAxesMesh() const {
return AxesMesh_;
}
const MetaCoreMesh& MetaCoreDebugGeometry::GetCubeMesh() const {
return CubeMesh_;
}
} // namespace MetaCore

View File

@ -1,116 +1,43 @@
#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h"
#include "MetaCoreRender/MetaCoreSceneRenderer.h"
#include "MetaCoreRender/MetaCoreRenderDevice.h"
#include "MetaCoreScene/MetaCoreScene.h"
#include <glad/glad.h>
namespace MetaCore {
bool MetaCoreEditorViewportRenderer::Initialize() {
bool MetaCoreEditorViewportRenderer::Initialize(MetaCoreRenderDevice& renderDevice) {
Shutdown();
SceneRenderer_ = new MetaCoreSceneRenderer();
if (!SceneRenderer_->Initialize()) {
Shutdown();
if (!SceneBridge_.Initialize(renderDevice)) {
return false;
}
RebuildFramebuffer();
return FramebufferHandle_ != 0;
RenderDevice_ = &renderDevice;
ViewportRect_ = MetaCoreViewportRect{};
return true;
}
void MetaCoreEditorViewportRenderer::Shutdown() {
DestroyFramebuffer();
if (SceneRenderer_ != nullptr) {
SceneRenderer_->Shutdown();
delete SceneRenderer_;
SceneRenderer_ = nullptr;
}
SceneBridge_.Shutdown();
RenderDevice_ = nullptr;
ViewportRect_ = MetaCoreViewportRect{};
}
void MetaCoreEditorViewportRenderer::Resize(int width, int height) {
width = (width > 1) ? width : 1;
height = (height > 1) ? height : 1;
if (width == Width_ && height == Height_) {
return;
void MetaCoreEditorViewportRenderer::SetViewportRect(const MetaCoreViewportRect& viewportRect) {
ViewportRect_ = viewportRect;
if (RenderDevice_ != nullptr) {
RenderDevice_->SetSceneViewportRect(ViewportRect_);
}
Width_ = width;
Height_ = height;
RebuildFramebuffer();
}
void MetaCoreEditorViewportRenderer::RenderSceneToViewport(const MetaCoreScene& scene, const MetaCoreSceneView& sceneView) {
if (FramebufferHandle_ == 0 || SceneRenderer_ == nullptr) {
if (RenderDevice_ == nullptr) {
return;
}
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferHandle_);
glViewport(0, 0, Width_, Height_);
glEnable(GL_DEPTH_TEST);
glClearColor(0.09F, 0.10F, 0.12F, 1.0F);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
SceneRenderer_->RenderScene(scene, sceneView);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
unsigned int MetaCoreEditorViewportRenderer::GetColorTextureHandle() const {
return ColorTextureHandle_;
}
int MetaCoreEditorViewportRenderer::GetWidth() const {
return Width_;
}
int MetaCoreEditorViewportRenderer::GetHeight() const {
return Height_;
}
void MetaCoreEditorViewportRenderer::RebuildFramebuffer() {
DestroyFramebuffer();
glGenFramebuffers(1, &FramebufferHandle_);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferHandle_);
glGenTextures(1, &ColorTextureHandle_);
glBindTexture(GL_TEXTURE_2D, ColorTextureHandle_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width_, Height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ColorTextureHandle_, 0);
glGenRenderbuffers(1, &DepthStencilHandle_);
glBindRenderbuffer(GL_RENDERBUFFER, DepthStencilHandle_);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, Width_, Height_);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, DepthStencilHandle_);
glBindTexture(GL_TEXTURE_2D, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void MetaCoreEditorViewportRenderer::DestroyFramebuffer() {
if (DepthStencilHandle_ != 0) {
glDeleteRenderbuffers(1, &DepthStencilHandle_);
DepthStencilHandle_ = 0;
}
if (ColorTextureHandle_ != 0) {
glDeleteTextures(1, &ColorTextureHandle_);
ColorTextureHandle_ = 0;
}
if (FramebufferHandle_ != 0) {
glDeleteFramebuffers(1, &FramebufferHandle_);
FramebufferHandle_ = 0;
}
RenderDevice_->SetSceneViewportRect(ViewportRect_);
SceneBridge_.ApplySceneView(sceneView);
SceneBridge_.SyncScene(scene);
}
} // namespace MetaCore

View File

@ -1,82 +0,0 @@
#include "MetaCoreRender/MetaCoreMesh.h"
#include <glad/glad.h>
namespace MetaCore {
MetaCoreMesh::~MetaCoreMesh() {
Shutdown();
}
bool MetaCoreMesh::Build(const std::vector<MetaCoreVertex>& vertices, const std::vector<std::uint32_t>& indices, MetaCorePrimitiveType primitiveType) {
Shutdown();
PrimitiveType_ = primitiveType;
IndexCount_ = static_cast<std::uint32_t>(indices.size());
glGenVertexArrays(1, &VertexArrayHandle_);
glGenBuffers(1, &VertexBufferHandle_);
glGenBuffers(1, &IndexBufferHandle_);
glBindVertexArray(VertexArrayHandle_);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferHandle_);
glBufferData(
GL_ARRAY_BUFFER,
static_cast<GLsizeiptr>(vertices.size() * sizeof(MetaCoreVertex)),
vertices.data(),
GL_STATIC_DRAW
);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferHandle_);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
static_cast<GLsizeiptr>(indices.size() * sizeof(std::uint32_t)),
indices.data(),
GL_STATIC_DRAW
);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(MetaCoreVertex), reinterpret_cast<const void*>(offsetof(MetaCoreVertex, Position)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(MetaCoreVertex), reinterpret_cast<const void*>(offsetof(MetaCoreVertex, Normal)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(MetaCoreVertex), reinterpret_cast<const void*>(offsetof(MetaCoreVertex, Color)));
glBindVertexArray(0);
return true;
}
void MetaCoreMesh::Shutdown() {
if (IndexBufferHandle_ != 0) {
glDeleteBuffers(1, &IndexBufferHandle_);
IndexBufferHandle_ = 0;
}
if (VertexBufferHandle_ != 0) {
glDeleteBuffers(1, &VertexBufferHandle_);
VertexBufferHandle_ = 0;
}
if (VertexArrayHandle_ != 0) {
glDeleteVertexArrays(1, &VertexArrayHandle_);
VertexArrayHandle_ = 0;
}
IndexCount_ = 0;
}
void MetaCoreMesh::Draw() const {
glBindVertexArray(VertexArrayHandle_);
glDrawElements(
PrimitiveType_ == MetaCorePrimitiveType::Triangles ? GL_TRIANGLES : GL_LINES,
static_cast<GLsizei>(IndexCount_),
GL_UNSIGNED_INT,
nullptr
);
glBindVertexArray(0);
}
} // namespace MetaCore

View File

@ -0,0 +1,317 @@
#include "MetaCoreRender/MetaCorePandaSceneBridge.h"
#include "MetaCoreRender/MetaCoreRenderDevice.h"
#include "MetaCoreRender/MetaCoreRenderTypes.h"
#include "MetaCoreScene/MetaCoreComponents.h"
#include "MetaCoreScene/MetaCoreScene.h"
#include "ambientLight.h"
#include "camera.h"
#include "directionalLight.h"
#include "lineSegs.h"
#include "loader.h"
#include "nodePath.h"
#include "perspectiveLens.h"
#include "pandaNode.h"
#include "windowFramework.h"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/ext/matrix_transform.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <memory>
#include <unordered_map>
#include <unordered_set>
namespace MetaCore {
namespace {
glm::mat4 MetaCoreBuildBasisSwapMatrix() {
glm::mat4 basis(1.0F);
basis[0] = glm::vec4(1.0F, 0.0F, 0.0F, 0.0F);
basis[1] = glm::vec4(0.0F, 0.0F, 1.0F, 0.0F);
basis[2] = glm::vec4(0.0F, 1.0F, 0.0F, 0.0F);
basis[3] = glm::vec4(0.0F, 0.0F, 0.0F, 1.0F);
return basis;
}
LMatrix4f MetaCoreConvertTransformToPanda(const MetaCoreTransformComponent& transform) {
const glm::mat4 translation = glm::translate(glm::mat4(1.0F), transform.Position);
const glm::mat4 rotation = glm::yawPitchRoll(
glm::radians(transform.RotationEulerDegrees.y),
glm::radians(transform.RotationEulerDegrees.x),
glm::radians(transform.RotationEulerDegrees.z)
);
const glm::mat4 scale = glm::scale(glm::mat4(1.0F), transform.Scale);
const glm::mat4 metaCoreMatrix = translation * rotation * scale;
const glm::mat4 basis = MetaCoreBuildBasisSwapMatrix();
const glm::mat4 pandaMatrix = basis * metaCoreMatrix * basis;
LMatrix4f result;
for (int row = 0; row < 4; ++row) {
for (int column = 0; column < 4; ++column) {
result.set_cell(row, column, pandaMatrix[column][row]);
}
}
return result;
}
LPoint3f MetaCoreToPandaPoint(const glm::vec3& value) {
return LPoint3f(value.x, value.z, value.y);
}
LVector3f MetaCoreToPandaVector(const glm::vec3& value) {
return LVector3f(value.x, value.z, value.y);
}
NodePath MetaCoreCreateGridNode(const NodePath& parentNode) {
LineSegs gridBuilder("MetaCoreGrid");
gridBuilder.set_thickness(1.0F);
constexpr int gridHalfExtent = 10;
for (int lineIndex = -gridHalfExtent; lineIndex <= gridHalfExtent; ++lineIndex) {
const bool isAxisLine = (lineIndex == 0);
gridBuilder.set_color(isAxisLine ? 0.42F : 0.30F, isAxisLine ? 0.45F : 0.33F, isAxisLine ? 0.50F : 0.36F, 1.0F);
gridBuilder.move_to(MetaCoreToPandaPoint(glm::vec3(static_cast<float>(lineIndex), 0.0F, static_cast<float>(-gridHalfExtent))));
gridBuilder.draw_to(MetaCoreToPandaPoint(glm::vec3(static_cast<float>(lineIndex), 0.0F, static_cast<float>(gridHalfExtent))));
gridBuilder.move_to(MetaCoreToPandaPoint(glm::vec3(static_cast<float>(-gridHalfExtent), 0.0F, static_cast<float>(lineIndex))));
gridBuilder.draw_to(MetaCoreToPandaPoint(glm::vec3(static_cast<float>(gridHalfExtent), 0.0F, static_cast<float>(lineIndex))));
}
return parentNode.attach_new_node(gridBuilder.create());
}
NodePath MetaCoreCreateAxisNode(const NodePath& parentNode) {
LineSegs axisBuilder("MetaCoreAxis");
axisBuilder.set_thickness(3.0F);
axisBuilder.set_color(0.92F, 0.26F, 0.26F, 1.0F);
axisBuilder.move_to(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 0.0F)));
axisBuilder.draw_to(MetaCoreToPandaPoint(glm::vec3(2.0F, 0.0F, 0.0F)));
axisBuilder.set_color(0.28F, 0.86F, 0.36F, 1.0F);
axisBuilder.move_to(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 0.0F)));
axisBuilder.draw_to(MetaCoreToPandaPoint(glm::vec3(0.0F, 2.0F, 0.0F)));
axisBuilder.set_color(0.32F, 0.56F, 0.96F, 1.0F);
axisBuilder.move_to(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 0.0F)));
axisBuilder.draw_to(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 2.0F)));
return parentNode.attach_new_node(axisBuilder.create());
}
} // namespace
class MetaCorePandaSceneBridge::MetaCorePandaSceneBridgeImpl {
public:
struct MetaCorePandaObjectState {
NodePath RootNode{};
NodePath MeshNode{};
NodePath LightNode{};
};
MetaCoreRenderDevice* RenderDevice = nullptr;
NodePath GridNode{};
NodePath AxisNode{};
NodePath AmbientLightNode{};
MetaCoreId SelectedObjectId = 0;
std::unordered_map<MetaCoreId, MetaCorePandaObjectState> ObjectStates{};
};
MetaCorePandaSceneBridge::MetaCorePandaSceneBridge()
: Impl_(std::make_unique<MetaCorePandaSceneBridgeImpl>()) {
}
MetaCorePandaSceneBridge::~MetaCorePandaSceneBridge() {
Shutdown();
}
bool MetaCorePandaSceneBridge::Initialize(MetaCoreRenderDevice& renderDevice) {
Shutdown();
auto* windowFrameworkHandle = static_cast<WindowFramework*>(renderDevice.GetNativeWindowFrameworkHandle());
auto* sceneRootHandle = static_cast<NodePath*>(renderDevice.GetNativeSceneRootHandle());
if (windowFrameworkHandle == nullptr || sceneRootHandle == nullptr || sceneRootHandle->is_empty()) {
return false;
}
Impl_->RenderDevice = &renderDevice;
Impl_->GridNode = MetaCoreCreateGridNode(*sceneRootHandle);
Impl_->AxisNode = MetaCoreCreateAxisNode(*sceneRootHandle);
Impl_->GridNode.set_light_off(1);
Impl_->AxisNode.set_light_off(1);
PT(AmbientLight) ambientLight = new AmbientLight("MetaCoreAmbientLight");
ambientLight->set_color(LColor(0.85F, 0.85F, 0.85F, 1.0F));
Impl_->AmbientLightNode = sceneRootHandle->attach_new_node(ambientLight);
sceneRootHandle->set_light(Impl_->AmbientLightNode);
return true;
}
void MetaCorePandaSceneBridge::Shutdown() {
if (Impl_ == nullptr) {
return;
}
for (auto& [objectId, objectState] : Impl_->ObjectStates) {
(void)objectId;
if (!objectState.RootNode.is_empty()) {
objectState.RootNode.remove_node();
}
}
Impl_->ObjectStates.clear();
if (!Impl_->AmbientLightNode.is_empty()) {
Impl_->AmbientLightNode.remove_node();
Impl_->AmbientLightNode = NodePath();
}
if (!Impl_->AxisNode.is_empty()) {
Impl_->AxisNode.remove_node();
Impl_->AxisNode = NodePath();
}
if (!Impl_->GridNode.is_empty()) {
Impl_->GridNode.remove_node();
Impl_->GridNode = NodePath();
}
Impl_->RenderDevice = nullptr;
Impl_->SelectedObjectId = 0;
}
void MetaCorePandaSceneBridge::SyncScene(const MetaCoreScene& scene) {
if (Impl_->RenderDevice == nullptr) {
return;
}
auto* windowFrameworkHandle = static_cast<WindowFramework*>(Impl_->RenderDevice->GetNativeWindowFrameworkHandle());
auto* sceneRootHandle = static_cast<NodePath*>(Impl_->RenderDevice->GetNativeSceneRootHandle());
if (windowFrameworkHandle == nullptr || sceneRootHandle == nullptr || sceneRootHandle->is_empty()) {
return;
}
std::unordered_set<MetaCoreId> aliveObjectIds;
for (const MetaCoreGameObject& gameObject : scene.GetGameObjects()) {
aliveObjectIds.insert(gameObject.Id);
auto [iterator, inserted] = Impl_->ObjectStates.try_emplace(gameObject.Id);
MetaCorePandaSceneBridgeImpl::MetaCorePandaObjectState& objectState = iterator->second;
if (inserted || objectState.RootNode.is_empty()) {
PT(PandaNode) rootNode = new PandaNode(gameObject.Name);
objectState.RootNode = sceneRootHandle->attach_new_node(rootNode);
}
objectState.RootNode.set_name(gameObject.Name);
objectState.RootNode.set_mat(MetaCoreConvertTransformToPanda(gameObject.Transform));
if (gameObject.ParentId != 0) {
const auto parentIterator = Impl_->ObjectStates.find(gameObject.ParentId);
if (parentIterator != Impl_->ObjectStates.end() && !parentIterator->second.RootNode.is_empty()) {
objectState.RootNode.reparent_to(parentIterator->second.RootNode);
} else {
objectState.RootNode.reparent_to(*sceneRootHandle);
}
} else {
objectState.RootNode.reparent_to(*sceneRootHandle);
}
if (gameObject.MeshRenderer.has_value()) {
if (objectState.MeshNode.is_empty()) {
objectState.MeshNode = windowFrameworkHandle->load_model(objectState.RootNode, Filename("models/box"));
}
if (!objectState.MeshNode.is_empty()) {
objectState.MeshNode.set_texture_off(1);
objectState.MeshNode.set_material_off(1);
objectState.MeshNode.set_shader_off(1);
objectState.MeshNode.set_light_off(1);
objectState.MeshNode.set_two_sided(true);
objectState.MeshNode.set_color(
gameObject.MeshRenderer->BaseColor.r,
gameObject.MeshRenderer->BaseColor.g,
gameObject.MeshRenderer->BaseColor.b,
1.0F
);
if (gameObject.MeshRenderer->Visible) {
objectState.MeshNode.show();
} else {
objectState.MeshNode.hide();
}
if (gameObject.Id == Impl_->SelectedObjectId) {
objectState.MeshNode.set_color_scale(1.18F, 1.02F, 0.72F, 1.0F);
} else {
objectState.MeshNode.clear_color_scale();
}
}
} else if (!objectState.MeshNode.is_empty()) {
objectState.MeshNode.remove_node();
objectState.MeshNode = NodePath();
}
if (gameObject.Light.has_value()) {
if (objectState.LightNode.is_empty()) {
PT(DirectionalLight) directionalLight = new DirectionalLight(gameObject.Name);
objectState.LightNode = objectState.RootNode.attach_new_node(directionalLight);
sceneRootHandle->set_light(objectState.LightNode);
}
auto* directionalLight = DCAST(DirectionalLight, objectState.LightNode.node());
if (directionalLight != nullptr) {
directionalLight->set_color(LColor(
gameObject.Light->Color.r * gameObject.Light->Intensity,
gameObject.Light->Color.g * gameObject.Light->Intensity,
gameObject.Light->Color.b * gameObject.Light->Intensity,
1.0F
));
}
} else if (!objectState.LightNode.is_empty()) {
sceneRootHandle->clear_light(objectState.LightNode);
objectState.LightNode.remove_node();
objectState.LightNode = NodePath();
}
}
for (auto iterator = Impl_->ObjectStates.begin(); iterator != Impl_->ObjectStates.end();) {
if (!aliveObjectIds.contains(iterator->first)) {
if (!iterator->second.RootNode.is_empty()) {
iterator->second.RootNode.remove_node();
}
iterator = Impl_->ObjectStates.erase(iterator);
} else {
++iterator;
}
}
}
void MetaCorePandaSceneBridge::ApplySceneView(const MetaCoreSceneView& sceneView) {
if (Impl_->RenderDevice == nullptr) {
return;
}
auto* editorCameraHandle = static_cast<NodePath*>(Impl_->RenderDevice->GetNativeEditorCameraHandle());
if (editorCameraHandle == nullptr || editorCameraHandle->is_empty()) {
return;
}
Impl_->SelectedObjectId = sceneView.SelectedObjectId;
editorCameraHandle->set_pos(MetaCoreToPandaPoint(sceneView.CameraPosition));
editorCameraHandle->look_at(MetaCoreToPandaPoint(sceneView.CameraTarget), MetaCoreToPandaVector(sceneView.CameraUp));
auto* cameraNode = DCAST(Camera, editorCameraHandle->node());
if (cameraNode != nullptr) {
auto* perspectiveLens = DCAST(PerspectiveLens, cameraNode->get_lens());
if (perspectiveLens != nullptr) {
perspectiveLens->set_fov(sceneView.VerticalFieldOfViewDegrees);
}
}
}
} // namespace MetaCore

View File

@ -1,45 +1,170 @@
#include "MetaCoreRender/MetaCoreRenderDevice.h"
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#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 <algorithm>
#include <memory>
namespace MetaCore {
bool MetaCoreRenderDevice::Initialize(GLFWwindow* nativeWindow) {
if (Initialized_) {
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;
};
MetaCoreRenderDevice::MetaCoreRenderDevice()
: Impl_(std::make_unique<MetaCoreRenderDeviceImpl>()) {
}
MetaCoreRenderDevice::~MetaCoreRenderDevice() {
Shutdown();
}
bool MetaCoreRenderDevice::Initialize(MetaCoreWindow& window) {
if (Impl_->Initialized) {
return true;
}
if (gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)) == 0) {
auto* frameworkHandle = static_cast<PandaFramework*>(window.GetNativeFrameworkHandle());
auto* windowFrameworkHandle = static_cast<WindowFramework*>(window.GetNativeWindowFrameworkHandle());
auto* graphicsWindowHandle = static_cast<GraphicsWindow*>(window.GetNativeGraphicsWindowHandle());
if (frameworkHandle == nullptr || windowFrameworkHandle == nullptr || graphicsWindowHandle == nullptr) {
return false;
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glDisable(GL_BLEND);
Initialized_ = nativeWindow != nullptr;
return Initialized_;
Impl_->Window = &window;
Impl_->Framework = frameworkHandle;
Impl_->WindowFrameworkHandle = windowFrameworkHandle;
Impl_->GraphicsWindowHandle = graphicsWindowHandle;
Impl_->GraphicsEngineHandle = frameworkHandle->get_graphics_engine();
if (Impl_->GraphicsEngineHandle == nullptr) {
return false;
}
Impl_->GraphicsEngineHandle->set_auto_flip(false);
Impl_->GraphicsEngineHandle->open_windows();
if (Impl_->WindowFrameworkHandle->get_display_region_2d() != nullptr) {
Impl_->WindowFrameworkHandle->get_display_region_2d()->set_active(false);
}
Impl_->SceneDisplayRegion = Impl_->WindowFrameworkHandle->get_display_region_3d();
if (Impl_->SceneDisplayRegion == nullptr) {
return false;
}
Impl_->GraphicsWindowHandle->set_clear_color_active(true);
Impl_->GraphicsWindowHandle->set_clear_color(LColor(0.38F, 0.48F, 0.62F, 1.0F));
PT(PandaNode) sceneRootNode = new PandaNode("MetaCoreSceneRoot");
Impl_->SceneRoot = Impl_->WindowFrameworkHandle->get_render().attach_new_node(sceneRootNode);
Impl_->SceneRoot.set_shader_auto();
Impl_->EditorCamera = Impl_->WindowFrameworkHandle->make_camera();
Impl_->SceneDisplayRegion->set_camera(Impl_->EditorCamera);
Impl_->SceneDisplayRegion->set_sort(0);
Impl_->SceneDisplayRegion->set_scissor_enabled(true);
Impl_->SceneDisplayRegion->set_clear_color_active(false);
if (auto* cameraNode = DCAST(Camera, Impl_->EditorCamera.node()); cameraNode != nullptr) {
PT(PerspectiveLens) lens = new PerspectiveLens();
lens->set_fov(60.0F);
lens->set_near_far(0.05F, 500.0F);
cameraNode->set_lens(lens);
}
Impl_->Initialized = true;
return true;
}
void MetaCoreRenderDevice::Shutdown() {
Initialized_ = false;
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::BeginFrame(int width, int height, const glm::vec4& clearColor) const {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, width, height);
Clear(clearColor);
void MetaCoreRenderDevice::RenderFrame() const {
if (Impl_->GraphicsEngineHandle != nullptr) {
Impl_->GraphicsEngineHandle->render_frame();
}
}
void MetaCoreRenderDevice::Clear(const glm::vec4& clearColor) const {
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
void MetaCoreRenderDevice::PresentFrame() const {
if (Impl_->GraphicsEngineHandle != nullptr) {
Impl_->GraphicsEngineHandle->flip_frame();
}
}
bool MetaCoreRenderDevice::IsInitialized() const {
return Initialized_;
void MetaCoreRenderDevice::SetSceneViewportRect(const MetaCoreViewportRect& viewportRect) {
if (Impl_->SceneDisplayRegion == nullptr || Impl_->Window == nullptr) {
return;
}
const auto [framebufferWidth, framebufferHeight] = Impl_->Window->GetFramebufferSize();
if (framebufferWidth <= 0 || framebufferHeight <= 0 || viewportRect.Width <= 1.0F || viewportRect.Height <= 1.0F) {
Impl_->SceneDisplayRegion->set_active(false);
return;
}
const float clampedLeft = std::clamp(viewportRect.Left / static_cast<float>(framebufferWidth), 0.0F, 1.0F);
const float clampedRight = std::clamp((viewportRect.Left + viewportRect.Width) / static_cast<float>(framebufferWidth), 0.0F, 1.0F);
const float clampedTop = std::clamp(viewportRect.Top / static_cast<float>(framebufferHeight), 0.0F, 1.0F);
const float clampedBottom = std::clamp((viewportRect.Top + viewportRect.Height) / static_cast<float>(framebufferHeight), 0.0F, 1.0F);
Impl_->SceneDisplayRegion->set_active(true);
Impl_->SceneDisplayRegion->set_dimensions(
clampedLeft,
clampedRight,
1.0F - clampedBottom,
1.0F - clampedTop
);
}
void* MetaCoreRenderDevice::GetNativeWindowFrameworkHandle() const {
return Impl_->WindowFrameworkHandle;
}
void* MetaCoreRenderDevice::GetNativeSceneRootHandle() const {
return Impl_->SceneRoot.is_empty() ? nullptr : static_cast<void*>(&Impl_->SceneRoot);
}
void* MetaCoreRenderDevice::GetNativeEditorCameraHandle() const {
return Impl_->EditorCamera.is_empty() ? nullptr : static_cast<void*>(&Impl_->EditorCamera);
}
} // namespace MetaCore

View File

@ -1,209 +0,0 @@
#include "MetaCoreRender/MetaCoreSceneRenderer.h"
#include "MetaCoreRender/MetaCoreDebugGeometry.h"
#include "MetaCoreRender/MetaCoreShaderProgram.h"
#include "MetaCoreScene/MetaCoreScene.h"
#include <glm/ext/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <memory>
#include <string>
namespace MetaCore {
class MetaCoreSceneRenderer::MetaCoreSceneRendererImpl {
public:
MetaCoreDebugGeometry DebugGeometry{};
MetaCoreShaderProgram LineShader{};
MetaCoreShaderProgram LitShader{};
};
namespace {
glm::mat4 MetaCoreBuildTransformMatrix(const MetaCoreTransformComponent& transform) {
glm::mat4 model(1.0F);
model = glm::translate(model, transform.Position);
model = glm::rotate(model, glm::radians(transform.RotationEulerDegrees.x), glm::vec3(1.0F, 0.0F, 0.0F));
model = glm::rotate(model, glm::radians(transform.RotationEulerDegrees.y), glm::vec3(0.0F, 1.0F, 0.0F));
model = glm::rotate(model, glm::radians(transform.RotationEulerDegrees.z), glm::vec3(0.0F, 0.0F, 1.0F));
model = glm::scale(model, transform.Scale);
return model;
}
glm::vec3 MetaCoreResolveLightDirection(const MetaCoreScene& scene) {
for (const MetaCoreGameObject& object : scene.GetGameObjects()) {
if (object.Light.has_value()) {
glm::mat4 rotation(1.0F);
rotation = glm::rotate(rotation, glm::radians(object.Transform.RotationEulerDegrees.x), glm::vec3(1.0F, 0.0F, 0.0F));
rotation = glm::rotate(rotation, glm::radians(object.Transform.RotationEulerDegrees.y), glm::vec3(0.0F, 1.0F, 0.0F));
rotation = glm::rotate(rotation, glm::radians(object.Transform.RotationEulerDegrees.z), glm::vec3(0.0F, 0.0F, 1.0F));
return glm::normalize(glm::vec3(rotation * glm::vec4(0.15F, -1.0F, -0.35F, 0.0F)));
}
}
return glm::normalize(glm::vec3(0.15F, -1.0F, -0.35F));
}
float MetaCoreResolveLightIntensity(const MetaCoreScene& scene) {
for (const MetaCoreGameObject& object : scene.GetGameObjects()) {
if (object.Light.has_value()) {
return object.Light->Intensity;
}
}
return 1.5F;
}
glm::vec3 MetaCoreResolveLightColor(const MetaCoreScene& scene) {
for (const MetaCoreGameObject& object : scene.GetGameObjects()) {
if (object.Light.has_value()) {
return object.Light->Color;
}
}
return glm::vec3(1.0F, 1.0F, 1.0F);
}
} // namespace
bool MetaCoreSceneRenderer::Initialize() {
Shutdown();
Impl_ = new MetaCoreSceneRendererImpl();
if (!Impl_->DebugGeometry.Initialize()) {
Shutdown();
return false;
}
std::string errorMessage;
const std::string lineVertexShader = R"(
#version 330 core
layout (location = 0) in vec3 aPosition;
layout (location = 2) in vec4 aColor;
uniform mat4 uView;
uniform mat4 uProjection;
uniform mat4 uModel;
out vec4 vColor;
void main() {
vColor = aColor;
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
}
)";
const std::string lineFragmentShader = R"(
#version 330 core
in vec4 vColor;
out vec4 FragColor;
void main() {
FragColor = vColor;
}
)";
const std::string litVertexShader = R"(
#version 330 core
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec3 aNormal;
uniform mat4 uView;
uniform mat4 uProjection;
uniform mat4 uModel;
out vec3 vWorldPosition;
out vec3 vWorldNormal;
void main() {
vec4 worldPosition = uModel * vec4(aPosition, 1.0);
vWorldPosition = worldPosition.xyz;
vWorldNormal = normalize(mat3(transpose(inverse(uModel))) * aNormal);
gl_Position = uProjection * uView * worldPosition;
}
)";
const std::string litFragmentShader = R"(
#version 330 core
in vec3 vWorldPosition;
in vec3 vWorldNormal;
uniform vec3 uBaseColor;
uniform vec3 uLightDirection;
uniform vec3 uLightColor;
uniform float uLightIntensity;
out vec4 FragColor;
void main() {
vec3 normal = normalize(vWorldNormal);
vec3 lightDirection = normalize(-uLightDirection);
float diffuse = max(dot(normal, lightDirection), 0.0);
vec3 ambient = uBaseColor * 0.28;
vec3 lighting = ambient + (uBaseColor * uLightColor * diffuse * uLightIntensity);
FragColor = vec4(lighting, 1.0);
}
)";
if (!Impl_->LineShader.BuildFromSource(lineVertexShader, lineFragmentShader, errorMessage)) {
Shutdown();
return false;
}
if (!Impl_->LitShader.BuildFromSource(litVertexShader, litFragmentShader, errorMessage)) {
Shutdown();
return false;
}
return true;
}
void MetaCoreSceneRenderer::Shutdown() {
if (Impl_ != nullptr) {
Impl_->DebugGeometry.Shutdown();
Impl_->LineShader.Shutdown();
Impl_->LitShader.Shutdown();
delete Impl_;
Impl_ = nullptr;
}
}
void MetaCoreSceneRenderer::RenderScene(const MetaCoreScene& scene, const MetaCoreSceneView& sceneView) const {
if (Impl_ == nullptr) {
return;
}
Impl_->LineShader.Bind();
Impl_->LineShader.SetMatrix4("uView", sceneView.ViewMatrix);
Impl_->LineShader.SetMatrix4("uProjection", sceneView.ProjectionMatrix);
Impl_->LineShader.SetMatrix4("uModel", glm::mat4(1.0F));
Impl_->DebugGeometry.GetGridMesh().Draw();
Impl_->DebugGeometry.GetAxesMesh().Draw();
const glm::vec3 lightDirection = MetaCoreResolveLightDirection(scene);
const glm::vec3 lightColor = MetaCoreResolveLightColor(scene);
const float lightIntensity = MetaCoreResolveLightIntensity(scene);
Impl_->LitShader.Bind();
Impl_->LitShader.SetMatrix4("uView", sceneView.ViewMatrix);
Impl_->LitShader.SetMatrix4("uProjection", sceneView.ProjectionMatrix);
Impl_->LitShader.SetVector3("uLightDirection", lightDirection);
Impl_->LitShader.SetVector3("uLightColor", lightColor);
Impl_->LitShader.SetFloat("uLightIntensity", lightIntensity);
for (const MetaCoreGameObject& object : scene.GetGameObjects()) {
if (!object.MeshRenderer.has_value() || !object.MeshRenderer->Visible) {
continue;
}
glm::vec3 baseColor = object.MeshRenderer->BaseColor;
if (object.Id == sceneView.SelectedObjectId) {
baseColor = glm::mix(baseColor, glm::vec3(1.0F, 0.62F, 0.18F), 0.55F);
}
Impl_->LitShader.SetMatrix4("uModel", MetaCoreBuildTransformMatrix(object.Transform));
Impl_->LitShader.SetVector3("uBaseColor", baseColor);
Impl_->DebugGeometry.GetCubeMesh().Draw();
}
}
} // namespace MetaCore

View File

@ -1,110 +0,0 @@
#include "MetaCoreRender/MetaCoreShaderProgram.h"
#include <glad/glad.h>
#include <vector>
namespace MetaCore {
namespace {
bool MetaCoreCompileShader(unsigned int shaderHandle, const std::string& source, std::string& outError) {
const char* rawSource = source.c_str();
glShaderSource(shaderHandle, 1, &rawSource, nullptr);
glCompileShader(shaderHandle);
int compileStatus = 0;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileStatus);
if (compileStatus == GL_TRUE) {
return true;
}
int logLength = 0;
glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &logLength);
std::vector<char> logBuffer(static_cast<std::size_t>((logLength > 1) ? logLength : 1), '\0');
glGetShaderInfoLog(shaderHandle, logLength, nullptr, logBuffer.data());
outError.assign(logBuffer.data());
return false;
}
} // namespace
MetaCoreShaderProgram::~MetaCoreShaderProgram() {
Shutdown();
}
bool MetaCoreShaderProgram::BuildFromSource(const std::string& vertexSource, const std::string& fragmentSource, std::string& outError) {
Shutdown();
const unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
const unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
if (!MetaCoreCompileShader(vertexShader, vertexSource, outError)) {
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return false;
}
if (!MetaCoreCompileShader(fragmentShader, fragmentSource, outError)) {
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return false;
}
ProgramHandle_ = glCreateProgram();
glAttachShader(ProgramHandle_, vertexShader);
glAttachShader(ProgramHandle_, fragmentShader);
glLinkProgram(ProgramHandle_);
int linkStatus = 0;
glGetProgramiv(ProgramHandle_, GL_LINK_STATUS, &linkStatus);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
if (linkStatus == GL_TRUE) {
return true;
}
int logLength = 0;
glGetProgramiv(ProgramHandle_, GL_INFO_LOG_LENGTH, &logLength);
std::vector<char> logBuffer(static_cast<std::size_t>((logLength > 1) ? logLength : 1), '\0');
glGetProgramInfoLog(ProgramHandle_, logLength, nullptr, logBuffer.data());
outError.assign(logBuffer.data());
glDeleteProgram(ProgramHandle_);
ProgramHandle_ = 0;
return false;
}
void MetaCoreShaderProgram::Shutdown() {
if (ProgramHandle_ != 0) {
glDeleteProgram(ProgramHandle_);
ProgramHandle_ = 0;
}
}
void MetaCoreShaderProgram::Bind() const {
glUseProgram(ProgramHandle_);
}
unsigned int MetaCoreShaderProgram::GetNativeHandle() const {
return ProgramHandle_;
}
void MetaCoreShaderProgram::SetMatrix4(const char* name, const glm::mat4& value) const {
const int location = glGetUniformLocation(ProgramHandle_, name);
glUniformMatrix4fv(location, 1, GL_FALSE, &value[0][0]);
}
void MetaCoreShaderProgram::SetVector3(const char* name, const glm::vec3& value) const {
const int location = glGetUniformLocation(ProgramHandle_, name);
glUniform3fv(location, 1, &value[0]);
}
void MetaCoreShaderProgram::SetFloat(const char* name, float value) const {
const int location = glGetUniformLocation(ProgramHandle_, name);
glUniform1f(location, value);
}
} // namespace MetaCore

View File

@ -1,31 +0,0 @@
#pragma once
#include "MetaCoreRender/MetaCoreMesh.h"
namespace MetaCore {
// 负责构建第一阶段需要的内置几何体。
class MetaCoreDebugGeometry {
public:
// 创建网格、坐标轴和立方体三类内置几何。
bool Initialize();
// 释放全部几何资源。
void Shutdown();
// 返回地面网格。
[[nodiscard]] const MetaCoreMesh& GetGridMesh() const;
// 返回坐标轴。
[[nodiscard]] const MetaCoreMesh& GetAxesMesh() const;
// 返回立方体。
[[nodiscard]] const MetaCoreMesh& GetCubeMesh() const;
private:
MetaCoreMesh GridMesh_{};
MetaCoreMesh AxesMesh_{};
MetaCoreMesh CubeMesh_{};
};
} // namespace MetaCore

View File

@ -1,45 +1,32 @@
#pragma once
#include "MetaCoreRender/MetaCorePandaSceneBridge.h"
#include "MetaCoreRender/MetaCoreRenderTypes.h"
namespace MetaCore {
class MetaCoreRenderDevice;
class MetaCoreScene;
// 管理编辑器场景视口所需的离屏帧缓冲
// 管理编辑器中央场景面板与 Panda3D DisplayRegion 之间的同步
class MetaCoreEditorViewportRenderer {
public:
// 初始化离屏渲染器和场景渲染器
bool Initialize();
// 初始化 Panda3D 场景桥接与显示区域驱动
bool Initialize(MetaCoreRenderDevice& renderDevice);
// 释放离屏缓冲与场景渲染器资源
// 释放桥接层与内部缓存
void Shutdown();
// 根据新的视口尺寸重建帧缓冲
void Resize(int width, int height);
// 写入当前 Scene 面板像素矩形
void SetViewportRect(const MetaCoreViewportRect& viewportRect);
// 将场景渲染到当前离屏纹理中
// 将场景状态同步到 Panda3D并更新编辑器相机
void RenderSceneToViewport(const MetaCoreScene& scene, const MetaCoreSceneView& sceneView);
// 返回颜色纹理句柄。
[[nodiscard]] unsigned int GetColorTextureHandle() const;
// 返回视口宽度。
[[nodiscard]] int GetWidth() const;
// 返回视口高度。
[[nodiscard]] int GetHeight() const;
private:
void RebuildFramebuffer();
void DestroyFramebuffer();
unsigned int FramebufferHandle_ = 0;
unsigned int ColorTextureHandle_ = 0;
unsigned int DepthStencilHandle_ = 0;
int Width_ = 1;
int Height_ = 1;
class MetaCoreSceneRenderer* SceneRenderer_ = nullptr;
MetaCoreRenderDevice* RenderDevice_ = nullptr;
MetaCorePandaSceneBridge SceneBridge_{};
MetaCoreViewportRect ViewportRect_{};
};
} // namespace MetaCore

View File

@ -1,47 +0,0 @@
#pragma once
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <cstdint>
#include <vector>
namespace MetaCore {
// 表示渲染层使用的统一顶点格式。
struct MetaCoreVertex {
glm::vec3 Position{0.0F, 0.0F, 0.0F};
glm::vec3 Normal{0.0F, 0.0F, 0.0F};
glm::vec4 Color{1.0F, 1.0F, 1.0F, 1.0F};
};
// 标识网格的基础绘制模式。
enum class MetaCorePrimitiveType {
Triangles,
Lines
};
// 封装 VAO/VBO/EBO并对外暴露简单的 Draw 接口。
class MetaCoreMesh {
public:
MetaCoreMesh() = default;
~MetaCoreMesh();
// 根据给定顶点和索引创建一个 GPU 网格。
bool Build(const std::vector<MetaCoreVertex>& vertices, const std::vector<std::uint32_t>& indices, MetaCorePrimitiveType primitiveType);
// 销毁 GPU 资源。
void Shutdown();
// 提交一次绘制调用。
void Draw() const;
private:
unsigned int VertexArrayHandle_ = 0;
unsigned int VertexBufferHandle_ = 0;
unsigned int IndexBufferHandle_ = 0;
std::uint32_t IndexCount_ = 0;
MetaCorePrimitiveType PrimitiveType_ = MetaCorePrimitiveType::Triangles;
};
} // namespace MetaCore

View File

@ -0,0 +1,34 @@
#pragma once
#include <memory>
namespace MetaCore {
class MetaCoreRenderDevice;
class MetaCoreScene;
struct MetaCoreSceneView;
// 负责在 MetaCoreScene 与 Panda3D NodePath 场景之间建立同步关系。
class MetaCorePandaSceneBridge {
public:
MetaCorePandaSceneBridge();
~MetaCorePandaSceneBridge();
// 准备 Panda3D 场景根节点、调试网格与编辑器相机。
bool Initialize(MetaCoreRenderDevice& renderDevice);
// 释放桥接层创建的 Panda3D 节点。
void Shutdown();
// 将 MetaCoreScene 当前状态同步到 Panda3D 场景图。
void SyncScene(const MetaCoreScene& scene);
// 更新编辑器相机与当前选中对象高亮状态。
void ApplySceneView(const MetaCoreSceneView& sceneView);
private:
class MetaCorePandaSceneBridgeImpl;
std::unique_ptr<MetaCorePandaSceneBridgeImpl> Impl_{};
};
} // namespace MetaCore

View File

@ -1,31 +1,49 @@
#pragma once
#include <glm/vec4.hpp>
#include "MetaCoreRender/MetaCoreRenderTypes.h"
struct GLFWwindow;
#include <memory>
namespace MetaCore {
// 负责初始化 glad并管理当前 OpenGL 上下文的基础状态。
class MetaCoreEditorViewportRenderer;
class MetaCorePandaSceneBridge;
class MetaCoreWindow;
// 封装 Panda3D 图形引擎、主窗口和中央场景显示区域。
class MetaCoreRenderDevice {
public:
// 初始化 OpenGL 函数加载与默认渲染状态。
bool Initialize(GLFWwindow* nativeWindow);
MetaCoreRenderDevice();
~MetaCoreRenderDevice();
// 关闭设备并重置初始化状态。
// 绑定 Panda3D 窗口与图形引擎,准备后续场景渲染。
bool Initialize(MetaCoreWindow& window);
// 释放 Panda3D 渲染相关资源。
void Shutdown();
// 开始绘制默认帧缓冲。
void BeginFrame(int width, int height, const glm::vec4& clearColor) const;
// 提交 Panda3D 场景绘制
void RenderFrame() const;
// 清理默认帧缓冲
void Clear(const glm::vec4& clearColor) const;
// 提交当前帧翻转,确保 ImGui 叠加结果显示出来
void PresentFrame() const;
// 返回设备是否已初始化。
[[nodiscard]] bool IsInitialized() const;
// 根据 ImGui 场景面板像素矩形更新 Panda3D DisplayRegion。
void SetSceneViewportRect(const MetaCoreViewportRect& viewportRect);
// 返回 WindowFramework 原生对象指针,仅供 Panda3D 桥接层使用。
[[nodiscard]] void* GetNativeWindowFrameworkHandle() const;
// 返回场景根节点原生对象指针,仅供 Panda3D 桥接层使用。
[[nodiscard]] void* GetNativeSceneRootHandle() const;
// 返回编辑器相机节点原生对象指针,仅供 Panda3D 桥接层使用。
[[nodiscard]] void* GetNativeEditorCameraHandle() const;
private:
bool Initialized_ = false;
class MetaCoreRenderDeviceImpl;
std::unique_ptr<MetaCoreRenderDeviceImpl> Impl_{};
};
} // namespace MetaCore

View File

@ -2,16 +2,24 @@
#include "MetaCoreFoundation/MetaCoreId.h"
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
namespace MetaCore {
// 描述一次场景渲染所需的视图参数。
// 描述场景在宿主窗口中的像素矩形区域。
struct MetaCoreViewportRect {
float Left = 0.0F;
float Top = 0.0F;
float Width = 1.0F;
float Height = 1.0F;
};
// 描述一次 Panda3D 场景同步所需的相机与选择参数。
struct MetaCoreSceneView {
glm::mat4 ViewMatrix{1.0F};
glm::mat4 ProjectionMatrix{1.0F};
glm::vec3 CameraPosition{0.0F, 0.0F, 0.0F};
glm::vec3 CameraPosition{0.0F, 2.2F, 6.5F};
glm::vec3 CameraTarget{0.0F, 0.7F, 0.0F};
glm::vec3 CameraUp{0.0F, 1.0F, 0.0F};
float VerticalFieldOfViewDegrees = 60.0F;
MetaCoreId SelectedObjectId = 0;
};

View File

@ -1,26 +0,0 @@
#pragma once
#include "MetaCoreRender/MetaCoreRenderTypes.h"
namespace MetaCore {
class MetaCoreScene;
// 负责把 MetaCoreScene 中的对象绘制到当前激活的 OpenGL 目标上。
class MetaCoreSceneRenderer {
public:
// 初始化 shader 和内置几何资源。
bool Initialize();
// 释放渲染器创建的 GPU 资源。
void Shutdown();
// 根据给定场景和视图参数提交绘制。
void RenderScene(const MetaCoreScene& scene, const MetaCoreSceneView& sceneView) const;
private:
class MetaCoreSceneRendererImpl;
MetaCoreSceneRendererImpl* Impl_ = nullptr;
};
} // namespace MetaCore

View File

@ -1,41 +0,0 @@
#pragma once
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include <string>
namespace MetaCore {
// 封装最基础的 OpenGL Shader Program 生命周期和常用 uniform 设置。
class MetaCoreShaderProgram {
public:
MetaCoreShaderProgram() = default;
~MetaCoreShaderProgram();
// 根据传入的顶点和片元源码创建程序对象。
bool BuildFromSource(const std::string& vertexSource, const std::string& fragmentSource, std::string& outError);
// 销毁当前程序对象。
void Shutdown();
// 绑定当前 shader program。
void Bind() const;
// 返回当前程序句柄。
[[nodiscard]] unsigned int GetNativeHandle() const;
// 设置 mat4 uniform。
void SetMatrix4(const char* name, const glm::mat4& value) const;
// 设置 vec3 uniform。
void SetVector3(const char* name, const glm::vec3& value) const;
// 设置 float uniform。
void SetFloat(const char* name, float value) const;
private:
unsigned int ProgramHandle_ = 0;
};
} // namespace MetaCore

View File

@ -0,0 +1,7 @@
{
"name": "DefaultPbr",
"shader_family": "simplepbr",
"base_color": [1.0, 1.0, 1.0, 1.0],
"roughness": 1.0,
"metallic": 0.0
}

View File

@ -0,0 +1,5 @@
{
"id": "00005052332aa8d4625dd7775e94e6d800000005",
"relative_path": "Assets/Materials/default_pbr.material.json",
"type": "material"
}

View File

@ -0,0 +1,10 @@
body {
font-family: sans-serif;
color: #f2f4f8;
}
.root-panel {
width: 420px;
margin: 40px auto;
padding: 24px;
background: rgba(16, 18, 24, 0.85);
}

View File

@ -0,0 +1,5 @@
{
"id": "00005052332a9f74171e39271225dd0800000004",
"relative_path": "Assets/UI/main_menu.rcss",
"type": "ui_stylesheet"
}

View File

@ -0,0 +1,11 @@
<rml>
<head>
<link type="text/rcss" href="main_menu.rcss" />
</head>
<body>
<div class="root-panel">
<h1>MetaCore</h1>
<p>Runtime UI scaffold</p>
</div>
</body>
</rml>

View File

@ -0,0 +1,5 @@
{
"id": "00005052332a822841724324849eccb900000003",
"relative_path": "Assets/UI/main_menu.rml",
"type": "ui_document"
}

View File

@ -0,0 +1,19 @@
{
"records": [
{
"id": "00005052332a822841724324849eccb900000003",
"relative_path": "Assets/UI/main_menu.rml",
"type": "ui_document"
},
{
"id": "00005052332a9f74171e39271225dd0800000004",
"relative_path": "Assets/UI/main_menu.rcss",
"type": "ui_stylesheet"
},
{
"id": "00005052332aa8d4625dd7775e94e6d800000005",
"relative_path": "Assets/Materials/default_pbr.material.json",
"type": "material"
}
]
}

View File

@ -0,0 +1,9 @@
{
"layout_name": "Default",
"hierarchy_visible": true,
"inspector_visible": true,
"project_visible": true,
"console_visible": true,
"runtime_preview_visible": true,
"toolbar_visible": true
}

View File

@ -0,0 +1,8 @@
{
"name": "MetaCoreTest",
"scenes": [
"Scenes/Main.mcscene.json"
],
"startup_scene": "Scenes/Main.mcscene.json",
"version": "0.1.0"
}

View File

@ -0,0 +1,76 @@
{
"name": "Main",
"objects": [
{
"camera": {
"far_clip": 1000,
"fov_degrees": 60,
"near_clip": 0.100000001490116,
"primary": true
},
"editor_metadata": {
"editor_only": false,
"locked": false,
"selected": false
},
"id": "0000505233216b341f8e0361195336fe00000001",
"name": "Main Camera",
"parent_id": "",
"transform": {
"position": [
0,
-10,
3
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
]
}
},
{
"editor_metadata": {
"editor_only": false,
"locked": false,
"selected": false
},
"id": "00005052332197bcd649b574240bc6be00000002",
"light": {
"color": [
1,
1,
1
],
"intensity": 1,
"range": 10,
"spot_angle_degrees": 45,
"type": "directional"
},
"name": "Directional Light",
"parent_id": "",
"transform": {
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
]
}
}
]
}

View File

@ -0,0 +1,108 @@
{
"name": "Main",
"objects": [
{
"camera": {
"far_clip": 1000,
"fov_degrees": 60,
"near_clip": 0.100000001490116,
"primary": true
},
"editor_metadata": {
"editor_only": false,
"locked": false,
"selected": false
},
"id": "00005052333cdf54bd42defb639e412c00000006",
"name": "Main Camera",
"parent_id": "",
"transform": {
"position": [
0,
-10,
3
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
]
}
},
{
"editor_metadata": {
"editor_only": false,
"locked": false,
"selected": false
},
"id": "00005052333ceef49f76de9c4d0fd70200000007",
"light": {
"color": [
1,
1,
1
],
"intensity": 1,
"range": 10,
"spot_angle_degrees": 45,
"type": "directional"
},
"name": "Directional Light",
"parent_id": "",
"transform": {
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
]
}
},
{
"editor_metadata": {
"editor_only": false,
"locked": false,
"selected": false
},
"id": "00005052333cf6c426b72f1a10f785ee00000008",
"mesh_renderer": {
"material_asset_id": "",
"mesh_asset_id": "mesh.cube",
"visible": true
},
"name": "Cube",
"parent_id": "",
"transform": {
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
]
}
}
]
}

114
cmake/MetaCorePanda3D.cmake Normal file
View File

@ -0,0 +1,114 @@
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()

View File

@ -0,0 +1,60 @@
param(
[Parameter(Mandatory = $true)]
[string]$Version,
[Parameter(Mandatory = $true)]
[string]$Architecture,
[Parameter(Mandatory = $true)]
[string]$InstallDir
)
$ErrorActionPreference = "Stop"
if (Test-Path (Join-Path $InstallDir "include\\pandaFramework.h")) {
Write-Host "MetaCore Panda3D SDK already prepared at $InstallDir"
exit 0
}
$installParent = Split-Path -Parent $InstallDir
New-Item -ItemType Directory -Force -Path $installParent | Out-Null
$downloadCacheDir = Join-Path $PSScriptRoot "..\\.metacore\\downloads"
New-Item -ItemType Directory -Force -Path $downloadCacheDir | Out-Null
$installerName = "Panda3D-SDK-$Version-$Architecture.exe"
$installerPath = Join-Path $downloadCacheDir $installerName
$downloadUrl = "https://www.panda3d.org/download/panda3d-$Version/$installerName"
if (-not (Test-Path $installerPath)) {
Write-Host "MetaCore downloading Panda3D SDK from $downloadUrl"
& curl.exe -L --fail --output $installerPath $downloadUrl
if ($LASTEXITCODE -ne 0) {
throw "MetaCore failed to download Panda3D SDK."
}
}
if (Test-Path $InstallDir) {
Remove-Item -Recurse -Force $InstallDir
}
New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
$installDirFullPath = (Resolve-Path $InstallDir).Path
Write-Host "MetaCore installing Panda3D SDK into $installDirFullPath"
$installerLaunchPath = Join-Path $downloadCacheDir ("launch-" + $installerName)
Copy-Item -Force $installerPath $installerLaunchPath
$installProcess = Start-Process -FilePath $installerLaunchPath -ArgumentList @("/S", "/D=$installDirFullPath") -Wait -PassThru
if ($installProcess.ExitCode -ne 0) {
throw "MetaCore Panda3D installer failed with exit code $($installProcess.ExitCode)."
}
Remove-Item -Force $installerLaunchPath -ErrorAction SilentlyContinue
if (-not (Test-Path (Join-Path $installDirFullPath "include\\pandaFramework.h"))) {
throw "MetaCore Panda3D installation finished but expected headers were not found."
}
Write-Host "MetaCore Panda3D SDK is ready at $installDirFullPath"

View File

@ -2,20 +2,13 @@
"name": "metacore",
"version-string": "1.0.0",
"dependencies": [
{
"name": "glad",
"features": [
"gl-api-33"
]
},
"glfw3",
"glm",
{
"name": "imgui",
"default-features": false,
"features": [
"docking-experimental",
"glfw-binding",
"win32-binding",
"opengl3-binding"
]
}