From a6371006076c55a126828c3d00cc076ec50e567a Mon Sep 17 00:00:00 2001 From: ayuan9957 <107920784+ayuan9957@users.noreply.github.com> Date: Fri, 20 Mar 2026 10:39:34 +0800 Subject: [PATCH] Rebuild MetaCore Unity-like prototype --- .gitignore | 14 + Apps/MetaCoreEditor/main.cpp | 12 + Apps/MetaCorePlayer/main.cpp | 6 + CMakeLists.txt | 253 +++++++++++++ CMakePresets.json | 59 +++ README.md | 50 +++ .../Private/MetaCoreBuiltinEditorModule.cpp | 338 ++++++++++++++++++ .../Private/MetaCoreBuiltinEditorModule.h | 12 + .../Private/MetaCoreEditorApp.cpp | 264 ++++++++++++++ .../MetaCoreEditorCameraController.cpp | 83 +++++ .../Private/MetaCoreEditorCameraController.h | 34 ++ .../Private/MetaCoreEditorContext.cpp | 57 +++ .../Private/MetaCoreEditorModule.cpp | 41 +++ .../Public/MetaCoreEditor/MetaCoreEditorApp.h | 46 +++ .../MetaCoreEditor/MetaCoreEditorContext.h | 77 ++++ .../MetaCoreEditor/MetaCoreEditorModule.h | 70 ++++ .../MetaCoreFoundation/Private/MetaCoreId.cpp | 17 + .../Private/MetaCoreLogService.cpp | 23 ++ .../Public/MetaCoreFoundation/MetaCoreId.h | 17 + .../MetaCoreFoundation/MetaCoreLogService.h | 38 ++ .../Private/MetaCoreInput.cpp | 59 +++ .../Private/MetaCoreWindow.cpp | 99 +++++ .../Public/MetaCorePlatform/MetaCoreInput.h | 41 +++ .../Public/MetaCorePlatform/MetaCoreWindow.h | 56 +++ .../Private/MetaCoreDebugGeometry.cpp | 110 ++++++ .../MetaCoreEditorViewportRenderer.cpp | 116 ++++++ .../MetaCoreRender/Private/MetaCoreMesh.cpp | 82 +++++ .../Private/MetaCoreRenderDevice.cpp | 45 +++ .../Private/MetaCoreSceneRenderer.cpp | 209 +++++++++++ .../Private/MetaCoreShaderProgram.cpp | 110 ++++++ .../MetaCoreRender/MetaCoreDebugGeometry.h | 31 ++ .../MetaCoreEditorViewportRenderer.h | 45 +++ .../Public/MetaCoreRender/MetaCoreMesh.h | 47 +++ .../MetaCoreRender/MetaCoreRenderDevice.h | 31 ++ .../MetaCoreRender/MetaCoreRenderTypes.h | 18 + .../MetaCoreRender/MetaCoreSceneRenderer.h | 26 ++ .../MetaCoreRender/MetaCoreShaderProgram.h | 41 +++ .../MetaCoreScene/Private/MetaCoreScene.cpp | 86 +++++ .../Public/MetaCoreScene/MetaCoreComponents.h | 43 +++ .../Public/MetaCoreScene/MetaCoreGameObject.h | 22 ++ .../Public/MetaCoreScene/MetaCoreScene.h | 36 ++ tests/MetaCoreSmokeTests.cpp | 54 +++ vcpkg.json | 23 ++ 43 files changed, 2941 insertions(+) create mode 100644 .gitignore create mode 100644 Apps/MetaCoreEditor/main.cpp create mode 100644 Apps/MetaCorePlayer/main.cpp create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 README.md create mode 100644 Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp create mode 100644 Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h create mode 100644 Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp create mode 100644 Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp create mode 100644 Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h create mode 100644 Source/MetaCoreEditor/Private/MetaCoreEditorContext.cpp create mode 100644 Source/MetaCoreEditor/Private/MetaCoreEditorModule.cpp create mode 100644 Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorApp.h create mode 100644 Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorContext.h create mode 100644 Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorModule.h create mode 100644 Source/MetaCoreFoundation/Private/MetaCoreId.cpp create mode 100644 Source/MetaCoreFoundation/Private/MetaCoreLogService.cpp create mode 100644 Source/MetaCoreFoundation/Public/MetaCoreFoundation/MetaCoreId.h create mode 100644 Source/MetaCoreFoundation/Public/MetaCoreFoundation/MetaCoreLogService.h create mode 100644 Source/MetaCorePlatform/Private/MetaCoreInput.cpp create mode 100644 Source/MetaCorePlatform/Private/MetaCoreWindow.cpp create mode 100644 Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreInput.h create mode 100644 Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreWindow.h create mode 100644 Source/MetaCoreRender/Private/MetaCoreDebugGeometry.cpp create mode 100644 Source/MetaCoreRender/Private/MetaCoreEditorViewportRenderer.cpp create mode 100644 Source/MetaCoreRender/Private/MetaCoreMesh.cpp create mode 100644 Source/MetaCoreRender/Private/MetaCoreRenderDevice.cpp create mode 100644 Source/MetaCoreRender/Private/MetaCoreSceneRenderer.cpp create mode 100644 Source/MetaCoreRender/Private/MetaCoreShaderProgram.cpp create mode 100644 Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreDebugGeometry.h create mode 100644 Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreEditorViewportRenderer.h create mode 100644 Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreMesh.h create mode 100644 Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderDevice.h create mode 100644 Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderTypes.h create mode 100644 Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreSceneRenderer.h create mode 100644 Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreShaderProgram.h create mode 100644 Source/MetaCoreScene/Private/MetaCoreScene.cpp create mode 100644 Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreComponents.h create mode 100644 Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreGameObject.h create mode 100644 Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreScene.h create mode 100644 tests/MetaCoreSmokeTests.cpp create mode 100644 vcpkg.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4129747 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +build/ +vcpkg_installed/ +downloads/ +.vs/ +CMakeUserPresets.json +imgui.ini +*.obj +*.pdb +*.ilk +*.lib +*.exe +*.vcxproj* +*.sln +*.log diff --git a/Apps/MetaCoreEditor/main.cpp b/Apps/MetaCoreEditor/main.cpp new file mode 100644 index 0000000..2446133 --- /dev/null +++ b/Apps/MetaCoreEditor/main.cpp @@ -0,0 +1,12 @@ +#include "MetaCoreEditor/MetaCoreEditorApp.h" + +int main() { + MetaCore::MetaCoreEditorApp editorApp; + if (!editorApp.Initialize()) { + return 1; + } + + const int exitCode = editorApp.Run(); + editorApp.Shutdown(); + return exitCode; +} diff --git a/Apps/MetaCorePlayer/main.cpp b/Apps/MetaCorePlayer/main.cpp new file mode 100644 index 0000000..4c1e56e --- /dev/null +++ b/Apps/MetaCorePlayer/main.cpp @@ -0,0 +1,6 @@ +#include + +int main() { + std::cout << "MetaCorePlayer 目前保留为后续运行时模块接入入口。\n"; + return 0; +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..375e8f1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,253 @@ +cmake_minimum_required(VERSION 3.26) + +project(MetaCore + VERSION 1.0.0 + DESCRIPTION "MetaCore Unity-like editor prototype" + LANGUAGES CXX +) + +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(EXISTS "${CMAKE_SOURCE_DIR}/vcpkg_installed/x64-windows/share/glad/gladConfig.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") + +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" + "$" + ) + 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) + +set(METACORE_FOUNDATION_HEADERS + Source/MetaCoreFoundation/Public/MetaCoreFoundation/MetaCoreId.h + Source/MetaCoreFoundation/Public/MetaCoreFoundation/MetaCoreLogService.h +) + +set(METACORE_FOUNDATION_SOURCES + Source/MetaCoreFoundation/Private/MetaCoreId.cpp + Source/MetaCoreFoundation/Private/MetaCoreLogService.cpp +) + +add_library(MetaCoreFoundation STATIC + ${METACORE_FOUNDATION_HEADERS} + ${METACORE_FOUNDATION_SOURCES} +) + +target_include_directories(MetaCoreFoundation + PUBLIC + Source/MetaCoreFoundation/Public +) + +if(MSVC) + target_compile_options(MetaCoreFoundation PRIVATE /W4 /permissive- /EHsc) +else() + target_compile_options(MetaCoreFoundation PRIVATE -Wall -Wextra -Wpedantic) +endif() + +set(METACORE_PLATFORM_HEADERS + Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreInput.h + Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreWindow.h +) + +set(METACORE_PLATFORM_SOURCES + Source/MetaCorePlatform/Private/MetaCoreInput.cpp + Source/MetaCorePlatform/Private/MetaCoreWindow.cpp +) + +add_library(MetaCorePlatform STATIC + ${METACORE_PLATFORM_HEADERS} + ${METACORE_PLATFORM_SOURCES} +) + +target_include_directories(MetaCorePlatform + PUBLIC + Source/MetaCorePlatform/Public +) + +target_link_libraries(MetaCorePlatform + PUBLIC + MetaCoreFoundation + glfw +) + +if(MSVC) + target_compile_options(MetaCorePlatform PRIVATE /W4 /permissive- /EHsc) +else() + target_compile_options(MetaCorePlatform PRIVATE -Wall -Wextra -Wpedantic) +endif() + +set(METACORE_SCENE_HEADERS + Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreComponents.h + Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreGameObject.h + Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreScene.h +) + +set(METACORE_SCENE_SOURCES + Source/MetaCoreScene/Private/MetaCoreScene.cpp +) + +add_library(MetaCoreScene STATIC + ${METACORE_SCENE_HEADERS} + ${METACORE_SCENE_SOURCES} +) + +target_include_directories(MetaCoreScene + PUBLIC + Source/MetaCoreScene/Public +) + +target_link_libraries(MetaCoreScene + PUBLIC + MetaCoreFoundation + glm::glm +) + +if(MSVC) + target_compile_options(MetaCoreScene PRIVATE /W4 /permissive- /EHsc) +else() + target_compile_options(MetaCoreScene PRIVATE -Wall -Wextra -Wpedantic) +endif() + +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 +) + +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 +) + +add_library(MetaCoreRender STATIC + ${METACORE_RENDER_HEADERS} + ${METACORE_RENDER_SOURCES} +) + +target_include_directories(MetaCoreRender + PUBLIC + Source/MetaCoreRender/Public +) + +target_link_libraries(MetaCoreRender + PUBLIC + MetaCoreFoundation + MetaCoreScene + MetaCorePlatform + OpenGL::GL + glad::glad + glm::glm +) + +if(MSVC) + target_compile_options(MetaCoreRender PRIVATE /W4 /permissive- /EHsc) +else() + target_compile_options(MetaCoreRender PRIVATE -Wall -Wextra -Wpedantic) +endif() + +set(METACORE_EDITOR_HEADERS + Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorModule.h + Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorContext.h + Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorApp.h +) + +set(METACORE_EDITOR_PRIVATE_HEADERS + 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 +) + +add_library(MetaCoreEditor STATIC + ${METACORE_EDITOR_HEADERS} + ${METACORE_EDITOR_PRIVATE_HEADERS} + ${METACORE_EDITOR_SOURCES} +) + +target_include_directories(MetaCoreEditor + PUBLIC + Source/MetaCoreEditor/Public + PRIVATE + Source/MetaCoreEditor/Private +) + +target_link_libraries(MetaCoreEditor + PUBLIC + MetaCoreFoundation + MetaCorePlatform + MetaCoreScene + MetaCoreRender + glm::glm + imgui::imgui +) + +if(MSVC) + target_compile_options(MetaCoreEditor PRIVATE /W4 /permissive- /EHsc) +else() + target_compile_options(MetaCoreEditor PRIVATE -Wall -Wextra -Wpedantic) +endif() + +add_executable(MetaCoreEditorApp + Apps/MetaCoreEditor/main.cpp +) + +target_link_libraries(MetaCoreEditorApp + PRIVATE + MetaCoreEditor +) +metacore_copy_runtime_dlls(MetaCoreEditorApp) + +add_executable(MetaCorePlayer + Apps/MetaCorePlayer/main.cpp +) + +target_link_libraries(MetaCorePlayer + PRIVATE + MetaCoreFoundation +) +metacore_copy_runtime_dlls(MetaCorePlayer) + +if(METACORE_BUILD_TESTS) + enable_testing() + + add_executable(MetaCoreSmokeTests + Tests/MetaCoreSmokeTests.cpp + ) + + target_link_libraries(MetaCoreSmokeTests + PRIVATE + MetaCoreEditor + ) + metacore_copy_runtime_dlls(MetaCoreSmokeTests) + + add_test(NAME MetaCoreSmokeTests COMMAND MetaCoreSmokeTests) +endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..0c496b4 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,59 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 26, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "generator": "Visual Studio 17 2022", + "binaryDir": "${sourceDir}/build/${presetName}", + "toolchainFile": "D:/vcpkg/scripts/buildsystems/vcpkg.cmake", + "cacheVariables": { + "CMAKE_CXX_STANDARD": "20", + "METACORE_BUILD_TESTS": "ON" + } + }, + { + "name": "vs2022-debug", + "inherits": "base", + "displayName": "VS2022 Debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "vs2022-release", + "inherits": "base", + "displayName": "VS2022 Release", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ], + "buildPresets": [ + { + "name": "build-debug", + "configurePreset": "vs2022-debug", + "configuration": "Debug" + }, + { + "name": "build-release", + "configurePreset": "vs2022-release", + "configuration": "Release" + } + ], + "testPresets": [ + { + "name": "test-debug", + "configurePreset": "vs2022-debug", + "configuration": "Debug", + "output": { + "outputOnFailure": true + } + } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..46c9cb9 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# MetaCore + +MetaCore 是一个以 Unity 工作流为参考的纯 C++ 编辑器原型工程。 + +## 当前目标 + +- 使用 `GLFW + OpenGL + Dear ImGui Docking` 搭建最小可运行编辑器 +- 使用 `GameObject + Component` 模型组织场景 +- 先把 `Hierarchy / Scene / Inspector / Project / Console` 跑通 +- 先体现清晰的渲染架构,再以模块化方式持续增加功能 + +## 技术栈 + +- `C++20` +- `CMake` +- `vcpkg` +- `GLFW` +- `glad` +- `OpenGL 3.3` +- `glm` +- `Dear ImGui Docking` + +## 目录结构 + +- `Source/MetaCoreFoundation`:基础服务 +- `Source/MetaCorePlatform`:窗口与输入 +- `Source/MetaCoreScene`:场景与对象模型 +- `Source/MetaCoreRender`:OpenGL 渲染架构 +- `Source/MetaCoreEditor`:编辑器模块与 Unity-like 界面 +- `Apps/MetaCoreEditor`:编辑器程序入口 +- `Apps/MetaCorePlayer`:运行时占位入口 +- `Tests`:基础烟雾测试 + +## 构建 + +```powershell +cmake --preset vs2022-debug +cmake --build --preset build-debug +ctest --preset test-debug +``` + +## 当前完成内容 + +- 单窗口 MetaCore 编辑器主循环 +- 场景视口离屏渲染 +- 网格、坐标轴、立方体绘制 +- `Hierarchy` 选择 +- `Inspector` 中编辑 `Transform` +- 轨道/平移/缩放相机控制 +- 模块注册接口与内置编辑器模块 diff --git a/Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp b/Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp new file mode 100644 index 0000000..6dfed1c --- /dev/null +++ b/Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp @@ -0,0 +1,338 @@ +#include "MetaCoreBuiltinEditorModule.h" + +#include "MetaCoreEditor/MetaCoreEditorContext.h" +#include "MetaCoreEditorCameraController.h" +#include "MetaCorePlatform/MetaCoreWindow.h" +#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h" +#include "MetaCoreRender/MetaCoreRenderTypes.h" +#include "MetaCoreScene/MetaCoreScene.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace MetaCore { + +namespace { + +void MetaCoreDrawHierarchyNode(MetaCoreEditorContext& editorContext, MetaCoreId objectId) { + MetaCoreScene& scene = editorContext.GetScene(); + MetaCoreGameObject* gameObject = scene.FindGameObject(objectId); + if (gameObject == nullptr) { + return; + } + + const std::vector childIds = scene.GetChildrenOf(objectId); + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanFullWidth; + if (childIds.empty()) { + flags |= ImGuiTreeNodeFlags_Leaf; + } + if (editorContext.GetSelectedObjectId() == objectId) { + flags |= ImGuiTreeNodeFlags_Selected; + } + + const bool nodeOpened = ImGui::TreeNodeEx(reinterpret_cast(static_cast(objectId)), flags, "%s", gameObject->Name.c_str()); + if (ImGui::IsItemClicked()) { + editorContext.SetSelectedObjectId(objectId); + editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Hierarchy", "已选中对象: " + gameObject->Name); + } + + if (nodeOpened) { + for (MetaCoreId childId : childIds) { + MetaCoreDrawHierarchyNode(editorContext, childId); + } + ImGui::TreePop(); + } +} + +class MetaCoreDefaultMenuProvider final : public MetaCoreIMenuProvider { +public: + std::string GetProviderId() const override { return "MetaCoreDefaultMenuProvider"; } + + void DrawMenuBar(MetaCoreEditorContext& editorContext) override { + if (ImGui::BeginMenu("文件")) { + if (ImGui::MenuItem("重置布局")) { + editorContext.SetDockLayoutBuilt(false); + } + if (ImGui::MenuItem("退出")) { + glfwSetWindowShouldClose(editorContext.GetWindow().GetNativeHandle(), GLFW_TRUE); + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("编辑")) { + ImGui::MenuItem("撤销", "Ctrl+Z", false, false); + ImGui::MenuItem("重做", "Ctrl+Y", false, false); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("资源")) { + ImGui::MenuItem("导入资源", nullptr, false, false); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("游戏对象")) { + if (ImGui::MenuItem("创建空对象")) { + MetaCoreGameObject& object = editorContext.GetScene().CreateGameObject("GameObject"); + editorContext.SetSelectedObjectId(object.Id); + editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "已创建空对象"); + } + if (ImGui::MenuItem("创建立方体")) { + MetaCoreGameObject& cube = editorContext.GetScene().CreateGameObject("Cube"); + cube.MeshRenderer = MetaCoreMeshRendererComponent{}; + cube.Transform.Position = glm::vec3(0.0F, 0.5F, 0.0F); + editorContext.SetSelectedObjectId(cube.Id); + editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "已创建立方体"); + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("组件")) { + ImGui::MenuItem("添加组件", nullptr, false, false); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("窗口")) { + for (const auto& panelProvider : editorContext.GetModuleRegistry().GetPanelProviders()) { + bool& panelOpen = editorContext.GetModuleRegistry().AccessPanelOpenState(panelProvider->GetPanelId()); + ImGui::MenuItem(panelProvider->GetPanelTitle().c_str(), nullptr, &panelOpen); + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("帮助")) { + ImGui::MenuItem("MetaCore Unity-like Prototype", nullptr, false, false); + ImGui::EndMenu(); + } + } +}; + +class MetaCoreHierarchyPanelProvider final : public MetaCoreIEditorPanelProvider { +public: + std::string GetPanelId() const override { return "Hierarchy"; } + std::string GetPanelTitle() const override { return "层级"; } + bool IsOpenByDefault() const override { return true; } + + void DrawPanel(MetaCoreEditorContext& editorContext) override { + ImGui::TextUnformatted("SampleScene"); + ImGui::Separator(); + + for (MetaCoreId rootId : editorContext.GetScene().GetRootObjectIds()) { + MetaCoreDrawHierarchyNode(editorContext, rootId); + } + } +}; + +class MetaCoreScenePanelProvider final : public MetaCoreIEditorPanelProvider { +public: + std::string GetPanelId() const override { return "Scene"; } + std::string GetPanelTitle() const override { return "场景"; } + bool IsOpenByDefault() const override { return true; } + + 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(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; + } + if (viewportSize.y < 1.0F) { + viewportSize.y = 1.0F; + } + + viewportState.Width = viewportSize.x; + viewportState.Height = viewportSize.y; + + editorContext.GetViewportRenderer().Resize(static_cast(viewportSize.x), static_cast(viewportSize.y)); + + MetaCoreSceneView sceneView; + sceneView.ViewMatrix = editorContext.GetCameraController().BuildViewMatrix(); + sceneView.ProjectionMatrix = editorContext.GetCameraController().BuildProjectionMatrix(viewportSize.x / viewportSize.y); + sceneView.CameraPosition = editorContext.GetCameraController().GetCameraPosition(); + sceneView.SelectedObjectId = editorContext.GetSelectedObjectId(); + editorContext.GetViewportRenderer().RenderSceneToViewport(editorContext.GetScene(), sceneView); + + ImGui::Image( + reinterpret_cast(static_cast(editorContext.GetViewportRenderer().GetColorTextureHandle())), + viewportSize, + ImVec2(0.0F, 1.0F), + ImVec2(1.0F, 0.0F) + ); + + viewportState.Hovered = ImGui::IsItemHovered(); + viewportState.Focused = viewportState.Focused || ImGui::IsItemFocused(); + + ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 28.0F); + ImGui::TextDisabled("Alt+左键 环绕 | 中键 平移 | 滚轮 缩放 | F 聚焦"); + } +}; + +class MetaCoreProjectPanelProvider final : public MetaCoreIEditorPanelProvider { +public: + std::string GetPanelId() const override { return "Project"; } + std::string GetPanelTitle() const override { return "项目"; } + bool IsOpenByDefault() const override { return true; } + + void DrawPanel(MetaCoreEditorContext&) override { + if (ImGui::TreeNodeEx("Assets", ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::BulletText("Scenes"); + ImGui::BulletText("Materials"); + ImGui::TreePop(); + } + + if (ImGui::TreeNodeEx("Packages", ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::TextDisabled("当前阶段未接入真实包管理。"); + ImGui::TreePop(); + } + + ImGui::Separator(); + ImGui::TextDisabled("当前无真实资源导入流水线。"); + } +}; + +class MetaCoreConsolePanelProvider final : public MetaCoreIEditorPanelProvider { +public: + std::string GetPanelId() const override { return "Console"; } + std::string GetPanelTitle() const override { return "控制台"; } + bool IsOpenByDefault() const override { return true; } + + void DrawPanel(MetaCoreEditorContext& editorContext) override { + for (const MetaCoreLogEntry& entry : editorContext.GetLogService().GetEntries()) { + ImVec4 textColor = ImVec4(0.85F, 0.85F, 0.88F, 1.0F); + if (entry.Level == MetaCoreLogLevel::Warning) { + textColor = ImVec4(0.95F, 0.75F, 0.25F, 1.0F); + } else if (entry.Level == MetaCoreLogLevel::Error) { + textColor = ImVec4(0.95F, 0.35F, 0.30F, 1.0F); + } + ImGui::TextColored(textColor, "[%s] %s", entry.Category.c_str(), entry.Message.c_str()); + } + } +}; + +class MetaCoreTransformInspectorDrawer final : public MetaCoreIInspectorDrawer { +public: + std::string GetDrawerId() const override { return "Transform"; } + bool Supports(const MetaCoreGameObject&) const override { return true; } + + void DrawInspector(MetaCoreEditorContext&, MetaCoreGameObject& gameObject) override { + if (ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::DragFloat3("Position", glm::value_ptr(gameObject.Transform.Position), 0.05F); + ImGui::DragFloat3("Rotation", glm::value_ptr(gameObject.Transform.RotationEulerDegrees), 0.5F); + ImGui::DragFloat3("Scale", glm::value_ptr(gameObject.Transform.Scale), 0.05F, 0.05F, 100.0F); + } + } +}; + +class MetaCoreInspectorPanelProvider final : public MetaCoreIEditorPanelProvider { +public: + std::string GetPanelId() const override { return "Inspector"; } + std::string GetPanelTitle() const override { return "检查器"; } + bool IsOpenByDefault() const override { return true; } + + void DrawPanel(MetaCoreEditorContext& editorContext) override { + MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject(); + if (selectedObject == nullptr) { + ImGui::TextUnformatted("当前没有选中对象。"); + return; + } + + if (BoundObjectId_ != selectedObject->Id) { + BoundObjectId_ = selectedObject->Id; + NameBuffer_.fill('\0'); + std::snprintf(NameBuffer_.data(), NameBuffer_.size(), "%s", selectedObject->Name.c_str()); + } + + ImGui::Text("对象 ID: %llu", static_cast(selectedObject->Id)); + if (ImGui::InputText("名称", NameBuffer_.data(), NameBuffer_.size())) { + selectedObject->Name = NameBuffer_.data(); + } + + if (ImGui::Button("聚焦对象")) { + editorContext.GetCameraController().FocusGameObject(*selectedObject); + } + + ImGui::Separator(); + + for (const auto& drawer : editorContext.GetModuleRegistry().GetInspectorDrawers()) { + if (drawer->Supports(*selectedObject)) { + drawer->DrawInspector(editorContext, *selectedObject); + } + } + + if (selectedObject->Camera.has_value()) { + ImGui::Separator(); + ImGui::TextUnformatted("Camera"); + ImGui::Text("FOV: %.1f", selectedObject->Camera->FieldOfViewDegrees); + } + + if (selectedObject->Light.has_value()) { + ImGui::Separator(); + ImGui::TextUnformatted("Light"); + ImGui::DragFloat("Intensity", &selectedObject->Light->Intensity, 0.05F, 0.0F, 8.0F); + } + + if (selectedObject->MeshRenderer.has_value()) { + ImGui::Separator(); + ImGui::TextUnformatted("MeshRenderer"); + ImGui::ColorEdit3("BaseColor", glm::value_ptr(selectedObject->MeshRenderer->BaseColor)); + ImGui::Checkbox("Visible", &selectedObject->MeshRenderer->Visible); + } + } + +private: + MetaCoreId BoundObjectId_ = 0; + std::array NameBuffer_{}; +}; + +class MetaCoreBuiltinEditorModule final : public MetaCoreIModule { +public: + std::string GetModuleName() const override { return "MetaCoreBuiltinEditorModule"; } + + void Startup(MetaCoreEditorModuleRegistry& moduleRegistry) override { + moduleRegistry.RegisterMenuProvider(std::make_unique()); + moduleRegistry.RegisterPanelProvider(std::make_unique()); + moduleRegistry.RegisterPanelProvider(std::make_unique()); + moduleRegistry.RegisterPanelProvider(std::make_unique()); + moduleRegistry.RegisterPanelProvider(std::make_unique()); + moduleRegistry.RegisterPanelProvider(std::make_unique()); + moduleRegistry.RegisterInspectorDrawer(std::make_unique()); + } + + void Shutdown(MetaCoreEditorModuleRegistry&) override { + } +}; + +} // namespace + +std::unique_ptr MetaCoreCreateBuiltinEditorModule() { + return std::make_unique(); +} + +} // namespace MetaCore diff --git a/Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h b/Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h new file mode 100644 index 0000000..8375406 --- /dev/null +++ b/Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h @@ -0,0 +1,12 @@ +#pragma once + +#include "MetaCoreEditor/MetaCoreEditorModule.h" + +#include + +namespace MetaCore { + +// 创建内置 Unity-like 编辑器模块。 +std::unique_ptr MetaCoreCreateBuiltinEditorModule(); + +} // namespace MetaCore diff --git a/Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp b/Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp new file mode 100644 index 0000000..d479492 --- /dev/null +++ b/Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp @@ -0,0 +1,264 @@ +#include "MetaCoreEditor/MetaCoreEditorApp.h" + +#include "MetaCoreBuiltinEditorModule.h" +#include "MetaCoreEditorCameraController.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace MetaCore { + +namespace { + +void MetaCoreApplyEditorStyle() { + ImGuiStyle& style = ImGui::GetStyle(); + ImGui::StyleColorsDark(); + style.WindowRounding = 2.0F; + style.FrameRounding = 2.0F; + style.TabRounding = 2.0F; + 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_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); + style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.22F, 0.27F, 0.32F, 1.0F); + style.Colors[ImGuiCol_Button] = ImVec4(0.18F, 0.21F, 0.25F, 1.0F); + style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.23F, 0.28F, 0.33F, 1.0F); + 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); +} + +void MetaCoreConfigureChineseFont() { + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->AddFontDefault(); + +#if defined(_WIN32) + std::vector candidatePaths; + char* windowsDirectory = nullptr; + std::size_t windowsDirectoryLength = 0; + if (_dupenv_s(&windowsDirectory, &windowsDirectoryLength, "WINDIR") == 0 && windowsDirectory != nullptr) { + const std::filesystem::path fontsPath = std::filesystem::path(windowsDirectory) / "Fonts"; + candidatePaths.push_back(fontsPath / "msyh.ttc"); + candidatePaths.push_back(fontsPath / "msyhbd.ttc"); + candidatePaths.push_back(fontsPath / "simhei.ttf"); + free(windowsDirectory); + } + + for (const auto& candidatePath : candidatePaths) { + if (std::filesystem::exists(candidatePath)) { + ImFont* chineseFont = io.Fonts->AddFontFromFileTTF( + candidatePath.string().c_str(), + 18.0F, + nullptr, + io.Fonts->GetGlyphRangesChineseFull() + ); + if (chineseFont != nullptr) { + io.FontDefault = chineseFont; + break; + } + } + } +#endif +} + +} // namespace + +MetaCoreEditorApp::~MetaCoreEditorApp() { + Shutdown(); +} + +bool MetaCoreEditorApp::Initialize() { + if (Initialized_) { + return true; + } + + if (!Window_.Initialize(1600, 900, "MetaCore Editor")) { + return false; + } + + if (!RenderDevice_.Initialize(Window_.GetNativeHandle())) { + return false; + } + + if (!InitializeImGui()) { + return false; + } + + if (!ViewportRenderer_.Initialize()) { + return false; + } + + Scene_ = MetaCoreCreateDefaultScene(); + Modules_.push_back(MetaCoreCreateBuiltinEditorModule()); + for (const auto& module : Modules_) { + module->Startup(ModuleRegistry_); + } + + EditorContext_ = std::make_unique( + Window_, + RenderDevice_, + ViewportRenderer_, + Scene_, + LogService_, + ModuleRegistry_ + ); + + for (const MetaCoreGameObject& object : Scene_.GetGameObjects()) { + if (object.Name == "Cube") { + EditorContext_->SetSelectedObjectId(object.Id); + EditorContext_->GetCameraController().FocusGameObject(object); + break; + } + } + + EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "System", "MetaCore 编辑器已初始化"); + EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Render", "OpenGL 场景视口已准备完成"); + + Initialized_ = true; + return true; +} + +int MetaCoreEditorApp::Run() { + while (!Window_.ShouldClose()) { + Window_.BeginFrame(); + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_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)); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + Window_.EndFrame(); + } + + return 0; +} + +void MetaCoreEditorApp::Shutdown() { + if (!Initialized_) { + return; + } + + for (const auto& module : Modules_) { + module->Shutdown(ModuleRegistry_); + } + Modules_.clear(); + EditorContext_.reset(); + ViewportRenderer_.Shutdown(); + ShutdownImGui(); + RenderDevice_.Shutdown(); + Window_.Shutdown(); + Initialized_ = false; +} + +bool MetaCoreEditorApp::InitializeImGui() { + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + MetaCoreConfigureChineseFont(); + MetaCoreApplyEditorStyle(); + + if (!ImGui_ImplGlfw_InitForOpenGL(Window_.GetNativeHandle(), true)) { + return false; + } + + if (!ImGui_ImplOpenGL3_Init("#version 330")) { + return false; + } + + return true; +} + +void MetaCoreEditorApp::ShutdownImGui() { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); +} + +void MetaCoreEditorApp::DrawEditorFrame() { + const ImGuiViewport* mainViewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(mainViewport->WorkPos); + ImGui::SetNextWindowSize(mainViewport->WorkSize); + ImGui::SetNextWindowViewport(mainViewport->ID); + + constexpr ImGuiWindowFlags hostWindowFlags = + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoBringToFrontOnFocus | + ImGuiWindowFlags_NoNavFocus | + ImGuiWindowFlags_MenuBar; + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0F, 0.0F)); + ImGui::Begin("MetaCoreMainDockSpace", nullptr, hostWindowFlags); + ImGui::PopStyleVar(3); + + if (ImGui::BeginMenuBar()) { + for (const auto& menuProvider : ModuleRegistry_.GetMenuProviders()) { + menuProvider->DrawMenuBar(*EditorContext_); + } + ImGui::EndMenuBar(); + } + + const ImGuiID dockSpaceId = ImGui::GetID("MetaCoreDockSpaceId"); + ImGui::DockSpace(dockSpaceId, ImVec2(0.0F, 0.0F), ImGuiDockNodeFlags_PassthruCentralNode); + EnsureDefaultDockLayout(dockSpaceId); + ImGui::End(); + + for (const auto& panelProvider : ModuleRegistry_.GetPanelProviders()) { + bool& panelOpen = ModuleRegistry_.AccessPanelOpenState(panelProvider->GetPanelId()); + if (!panelOpen) { + continue; + } + + ImGui::Begin(panelProvider->GetPanelTitle().c_str(), &panelOpen); + panelProvider->DrawPanel(*EditorContext_); + ImGui::End(); + } +} + +void MetaCoreEditorApp::EnsureDefaultDockLayout(unsigned int dockSpaceId) { + if (EditorContext_->HasDockLayoutBuilt()) { + return; + } + + ImGui::DockBuilderRemoveNode(dockSpaceId); + ImGui::DockBuilderAddNode(dockSpaceId, ImGuiDockNodeFlags_DockSpace); + ImGui::DockBuilderSetNodeSize(dockSpaceId, ImGui::GetMainViewport()->Size); + + ImGuiID mainNodeId = dockSpaceId; + const ImGuiID leftNodeId = ImGui::DockBuilderSplitNode(mainNodeId, ImGuiDir_Left, 0.18F, nullptr, &mainNodeId); + const ImGuiID rightNodeId = ImGui::DockBuilderSplitNode(mainNodeId, ImGuiDir_Right, 0.24F, nullptr, &mainNodeId); + 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); + ImGui::DockBuilderFinish(dockSpaceId); + + EditorContext_->SetDockLayoutBuilt(true); +} + +} // namespace MetaCore diff --git a/Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp b/Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp new file mode 100644 index 0000000..a81a646 --- /dev/null +++ b/Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp @@ -0,0 +1,83 @@ +#include "MetaCoreEditorCameraController.h" + +#include "MetaCorePlatform/MetaCoreInput.h" +#include "MetaCoreScene/MetaCoreGameObject.h" + +#include + +#include +#include +#include + +#include +#include + +namespace MetaCore { + +void MetaCoreEditorCameraController::Update(const MetaCoreSceneViewportState& viewportState, const MetaCoreInput& input) { + if (!viewportState.Hovered && !viewportState.Focused) { + return; + } + + ImGuiIO& io = ImGui::GetIO(); + + 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); + if (viewportState.Hovered && altDown && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { + YawDegrees_ += io.MouseDelta.x * 0.35F; + PitchDegrees_ -= io.MouseDelta.y * 0.35F; + PitchDegrees_ = std::clamp(PitchDegrees_, -85.0F, 85.0F); + } + + if (viewportState.Hovered && ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) { + const glm::vec3 right = GetRightDirection(); + const glm::vec3 up = GetUpDirection(); + const float panScale = 0.01F * Distance_; + Target_ -= right * io.MouseDelta.x * panScale; + Target_ += up * io.MouseDelta.y * panScale; + } +} + +void MetaCoreEditorCameraController::FocusGameObject(const MetaCoreGameObject& gameObject) { + Target_ = gameObject.Transform.Position; + const float scaleRadius = std::max({gameObject.Transform.Scale.x, gameObject.Transform.Scale.y, gameObject.Transform.Scale.z}); + 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); +} + +glm::vec3 MetaCoreEditorCameraController::GetCameraPosition() const { + return Target_ - GetForwardDirection() * Distance_; +} + +glm::vec3 MetaCoreEditorCameraController::GetForwardDirection() const { + const float yawRadians = glm::radians(YawDegrees_); + const float pitchRadians = glm::radians(PitchDegrees_); + + glm::vec3 forward; + forward.x = std::cos(pitchRadians) * std::sin(yawRadians); + forward.y = std::sin(pitchRadians); + forward.z = std::cos(pitchRadians) * std::cos(yawRadians); + return glm::normalize(forward); +} + +glm::vec3 MetaCoreEditorCameraController::GetRightDirection() const { + return glm::normalize(glm::cross(GetForwardDirection(), glm::vec3(0.0F, 1.0F, 0.0F))); +} + +glm::vec3 MetaCoreEditorCameraController::GetUpDirection() const { + return glm::normalize(glm::cross(GetRightDirection(), GetForwardDirection())); +} + +} // namespace MetaCore diff --git a/Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h b/Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h new file mode 100644 index 0000000..37833a9 --- /dev/null +++ b/Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h @@ -0,0 +1,34 @@ +#pragma once + +#include "MetaCoreEditor/MetaCoreEditorContext.h" + +#include +#include + +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]] glm::vec3 GetCameraPosition() const; + +private: + [[nodiscard]] glm::vec3 GetForwardDirection() const; + [[nodiscard]] glm::vec3 GetRightDirection() const; + [[nodiscard]] glm::vec3 GetUpDirection() const; + + glm::vec3 Target_{0.0F, 0.7F, 0.0F}; + float Distance_ = 7.5F; + float YawDegrees_ = 38.0F; + float PitchDegrees_ = -28.0F; + float FieldOfViewDegrees_ = 60.0F; +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreEditor/Private/MetaCoreEditorContext.cpp b/Source/MetaCoreEditor/Private/MetaCoreEditorContext.cpp new file mode 100644 index 0000000..9f62311 --- /dev/null +++ b/Source/MetaCoreEditor/Private/MetaCoreEditorContext.cpp @@ -0,0 +1,57 @@ +#include "MetaCoreEditor/MetaCoreEditorContext.h" + +#include "MetaCoreEditorCameraController.h" +#include "MetaCorePlatform/MetaCoreWindow.h" +#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h" +#include "MetaCoreRender/MetaCoreRenderDevice.h" +#include "MetaCoreScene/MetaCoreScene.h" + +namespace MetaCore { + +MetaCoreEditorContext::MetaCoreEditorContext( + MetaCoreWindow& window, + MetaCoreRenderDevice& renderDevice, + MetaCoreEditorViewportRenderer& viewportRenderer, + MetaCoreScene& scene, + MetaCoreLogService& logService, + MetaCoreEditorModuleRegistry& moduleRegistry +) + : Window_(window), + RenderDevice_(renderDevice), + ViewportRenderer_(viewportRenderer), + Scene_(scene), + LogService_(logService), + ModuleRegistry_(moduleRegistry), + CameraController_(std::make_unique()) { +} + +MetaCoreEditorContext::~MetaCoreEditorContext() = default; + +MetaCoreWindow& MetaCoreEditorContext::GetWindow() { return Window_; } +MetaCoreInput& MetaCoreEditorContext::GetInput() { return Window_.GetInput(); } +const MetaCoreInput& MetaCoreEditorContext::GetInput() const { return Window_.GetInput(); } +MetaCoreRenderDevice& MetaCoreEditorContext::GetRenderDevice() { return RenderDevice_; } +MetaCoreEditorViewportRenderer& MetaCoreEditorContext::GetViewportRenderer() { return ViewportRenderer_; } +MetaCoreScene& MetaCoreEditorContext::GetScene() { return Scene_; } +const MetaCoreScene& MetaCoreEditorContext::GetScene() const { return Scene_; } +MetaCoreLogService& MetaCoreEditorContext::GetLogService() { return LogService_; } +const MetaCoreLogService& MetaCoreEditorContext::GetLogService() const { return LogService_; } +MetaCoreEditorModuleRegistry& MetaCoreEditorContext::GetModuleRegistry() { return ModuleRegistry_; } +const MetaCoreEditorModuleRegistry& MetaCoreEditorContext::GetModuleRegistry() const { return ModuleRegistry_; } +MetaCoreSceneViewportState& MetaCoreEditorContext::GetSceneViewportState() { return SceneViewportState_; } +const MetaCoreSceneViewportState& MetaCoreEditorContext::GetSceneViewportState() const { return SceneViewportState_; } +MetaCoreEditorCameraController& MetaCoreEditorContext::GetCameraController() { return *CameraController_; } +const MetaCoreEditorCameraController& MetaCoreEditorContext::GetCameraController() const { return *CameraController_; } +MetaCoreId MetaCoreEditorContext::GetSelectedObjectId() const { return SelectedObjectId_; } +void MetaCoreEditorContext::SetSelectedObjectId(MetaCoreId objectId) { SelectedObjectId_ = objectId; } +MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() { return Scene_.FindGameObject(SelectedObjectId_); } +const MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() const { return Scene_.FindGameObject(SelectedObjectId_); } + +void MetaCoreEditorContext::AddConsoleMessage(MetaCoreLogLevel level, const std::string& category, const std::string& message) { + LogService_.AddEntry(level, category, message); +} + +void MetaCoreEditorContext::SetDockLayoutBuilt(bool built) { DockLayoutBuilt_ = built; } +bool MetaCoreEditorContext::HasDockLayoutBuilt() const { return DockLayoutBuilt_; } + +} // namespace MetaCore diff --git a/Source/MetaCoreEditor/Private/MetaCoreEditorModule.cpp b/Source/MetaCoreEditor/Private/MetaCoreEditorModule.cpp new file mode 100644 index 0000000..76bc135 --- /dev/null +++ b/Source/MetaCoreEditor/Private/MetaCoreEditorModule.cpp @@ -0,0 +1,41 @@ +#include "MetaCoreEditor/MetaCoreEditorModule.h" + +#include + +namespace MetaCore { + +void MetaCoreEditorModuleRegistry::RegisterMenuProvider(std::unique_ptr provider) { + MenuProviders_.push_back(std::move(provider)); +} + +void MetaCoreEditorModuleRegistry::RegisterPanelProvider(std::unique_ptr provider) { + PanelOpenStates_.emplace(provider->GetPanelId(), provider->IsOpenByDefault()); + PanelProviders_.push_back(std::move(provider)); +} + +void MetaCoreEditorModuleRegistry::RegisterInspectorDrawer(std::unique_ptr drawer) { + InspectorDrawers_.push_back(std::move(drawer)); +} + +const std::vector>& MetaCoreEditorModuleRegistry::GetMenuProviders() const { + return MenuProviders_; +} + +const std::vector>& MetaCoreEditorModuleRegistry::GetPanelProviders() const { + return PanelProviders_; +} + +const std::vector>& MetaCoreEditorModuleRegistry::GetInspectorDrawers() const { + return InspectorDrawers_; +} + +bool& MetaCoreEditorModuleRegistry::AccessPanelOpenState(const std::string& panelId) { + return PanelOpenStates_[panelId]; +} + +bool MetaCoreEditorModuleRegistry::IsPanelOpen(const std::string& panelId) const { + const auto iterator = PanelOpenStates_.find(panelId); + return iterator != PanelOpenStates_.end() ? iterator->second : false; +} + +} // namespace MetaCore diff --git a/Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorApp.h b/Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorApp.h new file mode 100644 index 0000000..1421a1d --- /dev/null +++ b/Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorApp.h @@ -0,0 +1,46 @@ +#pragma once + +#include "MetaCoreEditor/MetaCoreEditorContext.h" +#include "MetaCoreEditor/MetaCoreEditorModule.h" + +#include "MetaCoreFoundation/MetaCoreLogService.h" +#include "MetaCorePlatform/MetaCoreWindow.h" +#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h" +#include "MetaCoreRender/MetaCoreRenderDevice.h" +#include "MetaCoreScene/MetaCoreScene.h" + +#include +#include + +namespace MetaCore { + +class MetaCoreIModule; + +// 负责组合窗口、渲染器、ImGui 和编辑器模块,形成完整的编辑器程序。 +class MetaCoreEditorApp { +public: + MetaCoreEditorApp() = default; + ~MetaCoreEditorApp(); + + bool Initialize(); + int Run(); + void Shutdown(); + +private: + bool InitializeImGui(); + void ShutdownImGui(); + void DrawEditorFrame(); + void EnsureDefaultDockLayout(unsigned int dockSpaceId); + + MetaCoreWindow Window_{}; + MetaCoreRenderDevice RenderDevice_{}; + MetaCoreEditorViewportRenderer ViewportRenderer_{}; + MetaCoreScene Scene_{}; + MetaCoreLogService LogService_{}; + MetaCoreEditorModuleRegistry ModuleRegistry_{}; + std::vector> Modules_{}; + std::unique_ptr EditorContext_{}; + bool Initialized_ = false; +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorContext.h b/Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorContext.h new file mode 100644 index 0000000..cbb6398 --- /dev/null +++ b/Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorContext.h @@ -0,0 +1,77 @@ +#pragma once + +#include "MetaCoreFoundation/MetaCoreId.h" +#include "MetaCoreFoundation/MetaCoreLogService.h" + +#include +#include + +namespace MetaCore { + +class MetaCoreWindow; +class MetaCoreInput; +class MetaCoreRenderDevice; +class MetaCoreEditorViewportRenderer; +class MetaCoreScene; +struct MetaCoreGameObject; +class MetaCoreEditorModuleRegistry; +class MetaCoreEditorCameraController; + +// 描述场景视口当前的尺寸与焦点状态。 +struct MetaCoreSceneViewportState { + float Width = 1.0F; + float Height = 1.0F; + bool Hovered = false; + bool Focused = false; +}; + +// 在编辑器各模块之间共享运行时状态。 +class MetaCoreEditorContext { +public: + MetaCoreEditorContext( + MetaCoreWindow& window, + MetaCoreRenderDevice& renderDevice, + MetaCoreEditorViewportRenderer& viewportRenderer, + MetaCoreScene& scene, + MetaCoreLogService& logService, + MetaCoreEditorModuleRegistry& moduleRegistry + ); + ~MetaCoreEditorContext(); + + [[nodiscard]] MetaCoreWindow& GetWindow(); + [[nodiscard]] MetaCoreInput& GetInput(); + [[nodiscard]] const MetaCoreInput& GetInput() const; + [[nodiscard]] MetaCoreRenderDevice& GetRenderDevice(); + [[nodiscard]] MetaCoreEditorViewportRenderer& GetViewportRenderer(); + [[nodiscard]] MetaCoreScene& GetScene(); + [[nodiscard]] const MetaCoreScene& GetScene() const; + [[nodiscard]] MetaCoreLogService& GetLogService(); + [[nodiscard]] const MetaCoreLogService& GetLogService() const; + [[nodiscard]] MetaCoreEditorModuleRegistry& GetModuleRegistry(); + [[nodiscard]] const MetaCoreEditorModuleRegistry& GetModuleRegistry() const; + [[nodiscard]] MetaCoreSceneViewportState& GetSceneViewportState(); + [[nodiscard]] const MetaCoreSceneViewportState& GetSceneViewportState() const; + [[nodiscard]] MetaCoreEditorCameraController& GetCameraController(); + [[nodiscard]] const MetaCoreEditorCameraController& GetCameraController() const; + [[nodiscard]] MetaCoreId GetSelectedObjectId() const; + void SetSelectedObjectId(MetaCoreId objectId); + [[nodiscard]] MetaCoreGameObject* GetSelectedGameObject(); + [[nodiscard]] const MetaCoreGameObject* GetSelectedGameObject() const; + void AddConsoleMessage(MetaCoreLogLevel level, const std::string& category, const std::string& message); + void SetDockLayoutBuilt(bool built); + [[nodiscard]] bool HasDockLayoutBuilt() const; + +private: + MetaCoreWindow& Window_; + MetaCoreRenderDevice& RenderDevice_; + MetaCoreEditorViewportRenderer& ViewportRenderer_; + MetaCoreScene& Scene_; + MetaCoreLogService& LogService_; + MetaCoreEditorModuleRegistry& ModuleRegistry_; + std::unique_ptr CameraController_; + MetaCoreSceneViewportState SceneViewportState_{}; + MetaCoreId SelectedObjectId_ = 0; + bool DockLayoutBuilt_ = false; +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorModule.h b/Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorModule.h new file mode 100644 index 0000000..f0e3630 --- /dev/null +++ b/Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorModule.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace MetaCore { + +class MetaCoreEditorContext; +struct MetaCoreGameObject; + +// 定义可向编辑器注册菜单的扩展接口。 +class MetaCoreIMenuProvider { +public: + virtual ~MetaCoreIMenuProvider() = default; + [[nodiscard]] virtual std::string GetProviderId() const = 0; + 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; + virtual void DrawPanel(MetaCoreEditorContext& editorContext) = 0; +}; + +// 定义可向 Inspector 注册组件绘制器的扩展接口。 +class MetaCoreIInspectorDrawer { +public: + virtual ~MetaCoreIInspectorDrawer() = default; + [[nodiscard]] virtual std::string GetDrawerId() const = 0; + [[nodiscard]] virtual bool Supports(const MetaCoreGameObject& gameObject) const = 0; + virtual void DrawInspector(MetaCoreEditorContext& editorContext, MetaCoreGameObject& gameObject) = 0; +}; + +// 管理编辑器中全部可扩展菜单、面板和 Inspector 绘制器。 +class MetaCoreEditorModuleRegistry { +public: + void RegisterMenuProvider(std::unique_ptr provider); + void RegisterPanelProvider(std::unique_ptr provider); + void RegisterInspectorDrawer(std::unique_ptr drawer); + + [[nodiscard]] const std::vector>& GetMenuProviders() const; + [[nodiscard]] const std::vector>& GetPanelProviders() const; + [[nodiscard]] const std::vector>& GetInspectorDrawers() const; + + bool& AccessPanelOpenState(const std::string& panelId); + [[nodiscard]] bool IsPanelOpen(const std::string& panelId) const; + +private: + std::vector> MenuProviders_{}; + std::vector> PanelProviders_{}; + std::vector> InspectorDrawers_{}; + std::unordered_map PanelOpenStates_{}; +}; + +// 定义一个可注册到编辑器中的模块生命周期接口。 +class MetaCoreIModule { +public: + virtual ~MetaCoreIModule() = default; + [[nodiscard]] virtual std::string GetModuleName() const = 0; + virtual void Startup(MetaCoreEditorModuleRegistry& moduleRegistry) = 0; + virtual void Shutdown(MetaCoreEditorModuleRegistry& moduleRegistry) = 0; +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreFoundation/Private/MetaCoreId.cpp b/Source/MetaCoreFoundation/Private/MetaCoreId.cpp new file mode 100644 index 0000000..d0a42b4 --- /dev/null +++ b/Source/MetaCoreFoundation/Private/MetaCoreId.cpp @@ -0,0 +1,17 @@ +#include "MetaCoreFoundation/MetaCoreId.h" + +#include + +namespace MetaCore { + +namespace { + +std::atomic GMetaCoreNextId{1}; + +} // namespace + +MetaCoreId MetaCoreIdGenerator::Generate() { + return GMetaCoreNextId.fetch_add(1, std::memory_order_relaxed); +} + +} // namespace MetaCore diff --git a/Source/MetaCoreFoundation/Private/MetaCoreLogService.cpp b/Source/MetaCoreFoundation/Private/MetaCoreLogService.cpp new file mode 100644 index 0000000..5ba67d6 --- /dev/null +++ b/Source/MetaCoreFoundation/Private/MetaCoreLogService.cpp @@ -0,0 +1,23 @@ +#include "MetaCoreFoundation/MetaCoreLogService.h" + +#include + +namespace MetaCore { + +void MetaCoreLogService::AddEntry(MetaCoreLogLevel level, std::string category, std::string message) { + Entries_.push_back(MetaCoreLogEntry{ + level, + std::move(category), + std::move(message) + }); +} + +const std::vector& MetaCoreLogService::GetEntries() const { + return Entries_; +} + +void MetaCoreLogService::Clear() { + Entries_.clear(); +} + +} // namespace MetaCore diff --git a/Source/MetaCoreFoundation/Public/MetaCoreFoundation/MetaCoreId.h b/Source/MetaCoreFoundation/Public/MetaCoreFoundation/MetaCoreId.h new file mode 100644 index 0000000..787a0c5 --- /dev/null +++ b/Source/MetaCoreFoundation/Public/MetaCoreFoundation/MetaCoreId.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace MetaCore { + +// MetaCore 全局对象标识类型。 +using MetaCoreId = std::uint64_t; + +// 负责为场景对象和运行时实体分配递增标识。 +class MetaCoreIdGenerator { +public: + // 生成一个新的全局唯一标识。 + static MetaCoreId Generate(); +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreFoundation/Public/MetaCoreFoundation/MetaCoreLogService.h b/Source/MetaCoreFoundation/Public/MetaCoreFoundation/MetaCoreLogService.h new file mode 100644 index 0000000..e5c697c --- /dev/null +++ b/Source/MetaCoreFoundation/Public/MetaCoreFoundation/MetaCoreLogService.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace MetaCore { + +// 定义 MetaCore 控制台消息的级别。 +enum class MetaCoreLogLevel { + Info, + Warning, + Error +}; + +// 表示一条会显示在编辑器控制台中的日志记录。 +struct MetaCoreLogEntry { + MetaCoreLogLevel Level = MetaCoreLogLevel::Info; + std::string Category; + std::string Message; +}; + +// 提供最基础的日志收集能力,供编辑器控制台和调试输出复用。 +class MetaCoreLogService { +public: + // 写入一条新的日志。 + void AddEntry(MetaCoreLogLevel level, std::string category, std::string message); + + // 返回当前收集到的全部日志。 + [[nodiscard]] const std::vector& GetEntries() const; + + // 清空全部日志记录。 + void Clear(); + +private: + std::vector Entries_; +}; + +} // namespace MetaCore diff --git a/Source/MetaCorePlatform/Private/MetaCoreInput.cpp b/Source/MetaCorePlatform/Private/MetaCoreInput.cpp new file mode 100644 index 0000000..aafac35 --- /dev/null +++ b/Source/MetaCorePlatform/Private/MetaCoreInput.cpp @@ -0,0 +1,59 @@ +#include "MetaCorePlatform/MetaCoreInput.h" + +#include + +namespace MetaCore { + +void MetaCoreInput::BeginFrame(GLFWwindow* nativeWindow) { + PreviousKeyStates_ = CurrentKeyStates_; + PreviousMouseStates_ = CurrentMouseStates_; + + for (int key = 0; key < static_cast(CurrentKeyStates_.size()); ++key) { + CurrentKeyStates_[static_cast(key)] = glfwGetKey(nativeWindow, key) == GLFW_PRESS; + } + + for (int button = 0; button < static_cast(CurrentMouseStates_.size()); ++button) { + CurrentMouseStates_[static_cast(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(cursorX), static_cast(cursorY)); + CursorDelta_ = CursorPosition_ - previousPosition; +} + +bool MetaCoreInput::IsKeyDown(int key) const { + if (key < 0 || key >= static_cast(CurrentKeyStates_.size())) { + return false; + } + return CurrentKeyStates_[static_cast(key)]; +} + +bool MetaCoreInput::WasKeyPressed(int key) const { + if (key < 0 || key >= static_cast(CurrentKeyStates_.size())) { + return false; + } + + const std::size_t index = static_cast(key); + return CurrentKeyStates_[index] && !PreviousKeyStates_[index]; +} + +bool MetaCoreInput::IsMouseButtonDown(int button) const { + if (button < 0 || button >= static_cast(CurrentMouseStates_.size())) { + return false; + } + return CurrentMouseStates_[static_cast(button)]; +} + +const glm::vec2& MetaCoreInput::GetCursorPosition() const { + return CursorPosition_; +} + +const glm::vec2& MetaCoreInput::GetCursorDelta() const { + return CursorDelta_; +} + +} // namespace MetaCore diff --git a/Source/MetaCorePlatform/Private/MetaCoreWindow.cpp b/Source/MetaCorePlatform/Private/MetaCoreWindow.cpp new file mode 100644 index 0000000..097a957 --- /dev/null +++ b/Source/MetaCorePlatform/Private/MetaCoreWindow.cpp @@ -0,0 +1,99 @@ +#include "MetaCorePlatform/MetaCoreWindow.h" + +#include + +namespace MetaCore { + +MetaCoreWindow::~MetaCoreWindow() { + Shutdown(); +} + +bool MetaCoreWindow::Initialize(int width, int height, const std::string& title) { + if (NativeHandle_ != nullptr) { + return true; + } + + if (glfwInit() != GLFW_TRUE) { + 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(); + return false; + } + + glfwMakeContextCurrent(NativeHandle_); + glfwSwapInterval(1); + LastFrameTime_ = glfwGetTime(); + return true; +} + +void MetaCoreWindow::Shutdown() { + if (NativeHandle_ != nullptr) { + glfwDestroyWindow(NativeHandle_); + NativeHandle_ = nullptr; + glfwTerminate(); + } +} + +void MetaCoreWindow::BeginFrame() { + glfwPollEvents(); + + const double currentTime = glfwGetTime(); + DeltaSeconds_ = static_cast(currentTime - LastFrameTime_); + LastFrameTime_ = currentTime; + + if (DeltaSeconds_ <= 0.0F) { + DeltaSeconds_ = 1.0F / 60.0F; + } + + Input_.BeginFrame(NativeHandle_); +} + +void MetaCoreWindow::EndFrame() const { + glfwSwapBuffers(NativeHandle_); +} + +bool MetaCoreWindow::ShouldClose() const { + return NativeHandle_ == nullptr || glfwWindowShouldClose(NativeHandle_) == GLFW_TRUE; +} + +GLFWwindow* MetaCoreWindow::GetNativeHandle() const { + return NativeHandle_; +} + +MetaCoreInput& MetaCoreWindow::GetInput() { + return Input_; +} + +const MetaCoreInput& MetaCoreWindow::GetInput() const { + return Input_; +} + +float MetaCoreWindow::GetDeltaSeconds() const { + return DeltaSeconds_; +} + +std::pair MetaCoreWindow::GetWindowSize() const { + int width = 0; + int height = 0; + glfwGetWindowSize(NativeHandle_, &width, &height); + return {width, height}; +} + +std::pair MetaCoreWindow::GetFramebufferSize() const { + int width = 0; + int height = 0; + glfwGetFramebufferSize(NativeHandle_, &width, &height); + return {width, height}; +} + +} // namespace MetaCore diff --git a/Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreInput.h b/Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreInput.h new file mode 100644 index 0000000..3b05c2d --- /dev/null +++ b/Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreInput.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include + +struct GLFWwindow; + +namespace MetaCore { + +// 统一封装窗口轮询后的键鼠状态,避免上层直接依赖 GLFW 轮询细节。 +class MetaCoreInput { +public: + // 由窗口层在每帧轮询事件后刷新一次输入状态。 + void BeginFrame(GLFWwindow* nativeWindow); + + // 判断按键当前是否处于按下状态。 + [[nodiscard]] bool IsKeyDown(int key) const; + + // 判断按键是否在本帧刚刚按下。 + [[nodiscard]] bool WasKeyPressed(int key) const; + + // 判断鼠标按键当前是否按下。 + [[nodiscard]] bool IsMouseButtonDown(int button) const; + + // 返回当前鼠标位置。 + [[nodiscard]] const glm::vec2& GetCursorPosition() const; + + // 返回本帧鼠标相对上一帧的位移。 + [[nodiscard]] const glm::vec2& GetCursorDelta() const; + +private: + std::array CurrentKeyStates_{}; + std::array PreviousKeyStates_{}; + std::array CurrentMouseStates_{}; + std::array PreviousMouseStates_{}; + glm::vec2 CursorPosition_{0.0F, 0.0F}; + glm::vec2 CursorDelta_{0.0F, 0.0F}; +}; + +} // namespace MetaCore diff --git a/Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreWindow.h b/Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreWindow.h new file mode 100644 index 0000000..9656d5a --- /dev/null +++ b/Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreWindow.h @@ -0,0 +1,56 @@ +#pragma once + +#include "MetaCorePlatform/MetaCoreInput.h" + +#include +#include + +struct GLFWwindow; + +namespace MetaCore { + +// 负责管理主窗口、时间步进和最基础的事件轮询。 +class MetaCoreWindow { +public: + MetaCoreWindow() = default; + ~MetaCoreWindow(); + + // 创建主窗口并初始化 OpenGL 上下文。 + bool Initialize(int width, int height, const std::string& title); + + // 释放窗口及 GLFW 资源。 + void Shutdown(); + + // 轮询事件并刷新输入状态。 + void BeginFrame(); + + // 交换前后缓冲区。 + void EndFrame() const; + + // 返回窗口是否请求关闭。 + [[nodiscard]] bool ShouldClose() const; + + // 返回原生 GLFW 窗口指针。 + [[nodiscard]] GLFWwindow* GetNativeHandle() const; + + // 返回当前输入对象。 + [[nodiscard]] MetaCoreInput& GetInput(); + [[nodiscard]] const MetaCoreInput& GetInput() const; + + // 返回当前帧的时间间隔,单位为秒。 + [[nodiscard]] float GetDeltaSeconds() const; + + // 返回窗口尺寸。 + [[nodiscard]] std::pair GetWindowSize() const; + + // 返回帧缓冲尺寸。 + [[nodiscard]] std::pair GetFramebufferSize() const; + +private: + GLFWwindow* NativeHandle_ = nullptr; + MetaCoreInput Input_{}; + float DeltaSeconds_ = 1.0F / 60.0F; + double LastFrameTime_ = 0.0; +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreRender/Private/MetaCoreDebugGeometry.cpp b/Source/MetaCoreRender/Private/MetaCoreDebugGeometry.cpp new file mode 100644 index 0000000..d1b864a --- /dev/null +++ b/Source/MetaCoreRender/Private/MetaCoreDebugGeometry.cpp @@ -0,0 +1,110 @@ +#include "MetaCoreRender/MetaCoreDebugGeometry.h" + +#include + +#include + +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 gridVertices; + std::vector 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(step), 0.0F, -static_cast(gridHalfExtent)), glm::vec3(0.0F), lineColor)); + gridVertices.push_back(MetaCoreMakeVertex(glm::vec3(static_cast(step), 0.0F, static_cast(gridHalfExtent)), glm::vec3(0.0F), lineColor)); + gridIndices.push_back(gridIndex++); + gridIndices.push_back(gridIndex++); + + gridVertices.push_back(MetaCoreMakeVertex(glm::vec3(-static_cast(gridHalfExtent), 0.0F, static_cast(step)), glm::vec3(0.0F), lineColor)); + gridVertices.push_back(MetaCoreMakeVertex(glm::vec3(static_cast(gridHalfExtent), 0.0F, static_cast(step)), glm::vec3(0.0F), lineColor)); + gridIndices.push_back(gridIndex++); + gridIndices.push_back(gridIndex++); + } + + std::vector 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 axesIndices{0, 1, 2, 3, 4, 5}; + + const glm::vec4 cubeColor(1.0F, 1.0F, 1.0F, 1.0F); + std::vector 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 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 diff --git a/Source/MetaCoreRender/Private/MetaCoreEditorViewportRenderer.cpp b/Source/MetaCoreRender/Private/MetaCoreEditorViewportRenderer.cpp new file mode 100644 index 0000000..a3a33c2 --- /dev/null +++ b/Source/MetaCoreRender/Private/MetaCoreEditorViewportRenderer.cpp @@ -0,0 +1,116 @@ +#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h" + +#include "MetaCoreRender/MetaCoreSceneRenderer.h" +#include "MetaCoreScene/MetaCoreScene.h" + +#include + +namespace MetaCore { + +bool MetaCoreEditorViewportRenderer::Initialize() { + Shutdown(); + + SceneRenderer_ = new MetaCoreSceneRenderer(); + if (!SceneRenderer_->Initialize()) { + Shutdown(); + return false; + } + + RebuildFramebuffer(); + return FramebufferHandle_ != 0; +} + +void MetaCoreEditorViewportRenderer::Shutdown() { + DestroyFramebuffer(); + + if (SceneRenderer_ != nullptr) { + SceneRenderer_->Shutdown(); + delete SceneRenderer_; + SceneRenderer_ = nullptr; + } +} + +void MetaCoreEditorViewportRenderer::Resize(int width, int height) { + width = (width > 1) ? width : 1; + height = (height > 1) ? height : 1; + + if (width == Width_ && height == Height_) { + return; + } + + Width_ = width; + Height_ = height; + RebuildFramebuffer(); +} + +void MetaCoreEditorViewportRenderer::RenderSceneToViewport(const MetaCoreScene& scene, const MetaCoreSceneView& sceneView) { + if (FramebufferHandle_ == 0 || SceneRenderer_ == 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; + } +} + +} // namespace MetaCore diff --git a/Source/MetaCoreRender/Private/MetaCoreMesh.cpp b/Source/MetaCoreRender/Private/MetaCoreMesh.cpp new file mode 100644 index 0000000..efa26c4 --- /dev/null +++ b/Source/MetaCoreRender/Private/MetaCoreMesh.cpp @@ -0,0 +1,82 @@ +#include "MetaCoreRender/MetaCoreMesh.h" + +#include + +namespace MetaCore { + +MetaCoreMesh::~MetaCoreMesh() { + Shutdown(); +} + +bool MetaCoreMesh::Build(const std::vector& vertices, const std::vector& indices, MetaCorePrimitiveType primitiveType) { + Shutdown(); + + PrimitiveType_ = primitiveType; + IndexCount_ = static_cast(indices.size()); + + glGenVertexArrays(1, &VertexArrayHandle_); + glGenBuffers(1, &VertexBufferHandle_); + glGenBuffers(1, &IndexBufferHandle_); + + glBindVertexArray(VertexArrayHandle_); + + glBindBuffer(GL_ARRAY_BUFFER, VertexBufferHandle_); + glBufferData( + GL_ARRAY_BUFFER, + static_cast(vertices.size() * sizeof(MetaCoreVertex)), + vertices.data(), + GL_STATIC_DRAW + ); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferHandle_); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + static_cast(indices.size() * sizeof(std::uint32_t)), + indices.data(), + GL_STATIC_DRAW + ); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(MetaCoreVertex), reinterpret_cast(offsetof(MetaCoreVertex, Position))); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(MetaCoreVertex), reinterpret_cast(offsetof(MetaCoreVertex, Normal))); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(MetaCoreVertex), reinterpret_cast(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(IndexCount_), + GL_UNSIGNED_INT, + nullptr + ); + glBindVertexArray(0); +} + +} // namespace MetaCore diff --git a/Source/MetaCoreRender/Private/MetaCoreRenderDevice.cpp b/Source/MetaCoreRender/Private/MetaCoreRenderDevice.cpp new file mode 100644 index 0000000..84d6c86 --- /dev/null +++ b/Source/MetaCoreRender/Private/MetaCoreRenderDevice.cpp @@ -0,0 +1,45 @@ +#include "MetaCoreRender/MetaCoreRenderDevice.h" + +#include +#include + +namespace MetaCore { + +bool MetaCoreRenderDevice::Initialize(GLFWwindow* nativeWindow) { + if (Initialized_) { + return true; + } + + if (gladLoadGLLoader(reinterpret_cast(glfwGetProcAddress)) == 0) { + return false; + } + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); + glDisable(GL_BLEND); + Initialized_ = nativeWindow != nullptr; + return Initialized_; +} + +void MetaCoreRenderDevice::Shutdown() { + 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::Clear(const glm::vec4& clearColor) const { + glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +bool MetaCoreRenderDevice::IsInitialized() const { + return Initialized_; +} + +} // namespace MetaCore diff --git a/Source/MetaCoreRender/Private/MetaCoreSceneRenderer.cpp b/Source/MetaCoreRender/Private/MetaCoreSceneRenderer.cpp new file mode 100644 index 0000000..386d27a --- /dev/null +++ b/Source/MetaCoreRender/Private/MetaCoreSceneRenderer.cpp @@ -0,0 +1,209 @@ +#include "MetaCoreRender/MetaCoreSceneRenderer.h" + +#include "MetaCoreRender/MetaCoreDebugGeometry.h" +#include "MetaCoreRender/MetaCoreShaderProgram.h" +#include "MetaCoreScene/MetaCoreScene.h" + +#include +#include + +#include +#include + +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 diff --git a/Source/MetaCoreRender/Private/MetaCoreShaderProgram.cpp b/Source/MetaCoreRender/Private/MetaCoreShaderProgram.cpp new file mode 100644 index 0000000..7576d67 --- /dev/null +++ b/Source/MetaCoreRender/Private/MetaCoreShaderProgram.cpp @@ -0,0 +1,110 @@ +#include "MetaCoreRender/MetaCoreShaderProgram.h" + +#include + +#include + +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 logBuffer(static_cast((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 logBuffer(static_cast((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 diff --git a/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreDebugGeometry.h b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreDebugGeometry.h new file mode 100644 index 0000000..27b6043 --- /dev/null +++ b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreDebugGeometry.h @@ -0,0 +1,31 @@ +#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 diff --git a/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreEditorViewportRenderer.h b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreEditorViewportRenderer.h new file mode 100644 index 0000000..c2349e9 --- /dev/null +++ b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreEditorViewportRenderer.h @@ -0,0 +1,45 @@ +#pragma once + +#include "MetaCoreRender/MetaCoreRenderTypes.h" + +namespace MetaCore { + +class MetaCoreScene; + +// 管理编辑器场景视口所需的离屏帧缓冲。 +class MetaCoreEditorViewportRenderer { +public: + // 初始化离屏渲染器和场景渲染器。 + bool Initialize(); + + // 释放离屏缓冲与场景渲染器资源。 + void Shutdown(); + + // 根据新的视口尺寸重建帧缓冲。 + void Resize(int width, int height); + + // 将场景渲染到当前离屏纹理中。 + 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; +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreMesh.h b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreMesh.h new file mode 100644 index 0000000..32eb7bc --- /dev/null +++ b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreMesh.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include +#include + +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& vertices, const std::vector& 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 diff --git a/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderDevice.h b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderDevice.h new file mode 100644 index 0000000..41ecfea --- /dev/null +++ b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderDevice.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +struct GLFWwindow; + +namespace MetaCore { + +// 负责初始化 glad,并管理当前 OpenGL 上下文的基础状态。 +class MetaCoreRenderDevice { +public: + // 初始化 OpenGL 函数加载与默认渲染状态。 + bool Initialize(GLFWwindow* nativeWindow); + + // 关闭设备并重置初始化状态。 + void Shutdown(); + + // 开始绘制默认帧缓冲。 + void BeginFrame(int width, int height, const glm::vec4& clearColor) const; + + // 清理默认帧缓冲。 + void Clear(const glm::vec4& clearColor) const; + + // 返回设备是否已初始化。 + [[nodiscard]] bool IsInitialized() const; + +private: + bool Initialized_ = false; +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderTypes.h b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderTypes.h new file mode 100644 index 0000000..bb7fb9a --- /dev/null +++ b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreRenderTypes.h @@ -0,0 +1,18 @@ +#pragma once + +#include "MetaCoreFoundation/MetaCoreId.h" + +#include +#include + +namespace MetaCore { + +// 描述一次场景渲染所需的视图参数。 +struct MetaCoreSceneView { + glm::mat4 ViewMatrix{1.0F}; + glm::mat4 ProjectionMatrix{1.0F}; + glm::vec3 CameraPosition{0.0F, 0.0F, 0.0F}; + MetaCoreId SelectedObjectId = 0; +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreSceneRenderer.h b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreSceneRenderer.h new file mode 100644 index 0000000..4e99525 --- /dev/null +++ b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreSceneRenderer.h @@ -0,0 +1,26 @@ +#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 diff --git a/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreShaderProgram.h b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreShaderProgram.h new file mode 100644 index 0000000..ac5f899 --- /dev/null +++ b/Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreShaderProgram.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +#include + +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 diff --git a/Source/MetaCoreScene/Private/MetaCoreScene.cpp b/Source/MetaCoreScene/Private/MetaCoreScene.cpp new file mode 100644 index 0000000..8d8557c --- /dev/null +++ b/Source/MetaCoreScene/Private/MetaCoreScene.cpp @@ -0,0 +1,86 @@ +#include "MetaCoreScene/MetaCoreScene.h" + +#include "MetaCoreFoundation/MetaCoreId.h" + +namespace MetaCore { + +MetaCoreGameObject& MetaCoreScene::CreateGameObject(const std::string& name, MetaCoreId parentId) { + GameObjects_.push_back(MetaCoreGameObject{ + MetaCoreIdGenerator::Generate(), + parentId, + name, + MetaCoreTransformComponent{}, + std::nullopt, + std::nullopt, + std::nullopt + }); + return GameObjects_.back(); +} + +MetaCoreGameObject* MetaCoreScene::FindGameObject(MetaCoreId objectId) { + for (MetaCoreGameObject& object : GameObjects_) { + if (object.Id == objectId) { + return &object; + } + } + return nullptr; +} + +const MetaCoreGameObject* MetaCoreScene::FindGameObject(MetaCoreId objectId) const { + for (const MetaCoreGameObject& object : GameObjects_) { + if (object.Id == objectId) { + return &object; + } + } + return nullptr; +} + +std::vector& MetaCoreScene::GetGameObjects() { + return GameObjects_; +} + +const std::vector& MetaCoreScene::GetGameObjects() const { + return GameObjects_; +} + +std::vector MetaCoreScene::GetRootObjectIds() const { + std::vector rootIds; + for (const MetaCoreGameObject& object : GameObjects_) { + if (object.ParentId == 0) { + rootIds.push_back(object.Id); + } + } + return rootIds; +} + +std::vector MetaCoreScene::GetChildrenOf(MetaCoreId parentId) const { + std::vector childIds; + for (const MetaCoreGameObject& object : GameObjects_) { + if (object.ParentId == parentId) { + childIds.push_back(object.Id); + } + } + return childIds; +} + +MetaCoreScene MetaCoreCreateDefaultScene() { + MetaCoreScene scene; + + MetaCoreGameObject& mainCamera = scene.CreateGameObject("Main Camera"); + mainCamera.Camera = MetaCoreCameraComponent{}; + mainCamera.Camera->IsPrimary = true; + mainCamera.Transform.Position = glm::vec3(0.0F, 2.2F, 6.5F); + mainCamera.Transform.RotationEulerDegrees = glm::vec3(-15.0F, 0.0F, 0.0F); + + MetaCoreGameObject& directionalLight = scene.CreateGameObject("Directional Light"); + directionalLight.Light = MetaCoreLightComponent{}; + directionalLight.Transform.RotationEulerDegrees = glm::vec3(-45.0F, 30.0F, 0.0F); + + MetaCoreGameObject& cube = scene.CreateGameObject("Cube"); + cube.MeshRenderer = MetaCoreMeshRendererComponent{}; + cube.Transform.Position = glm::vec3(0.0F, 0.5F, 0.0F); + + return scene; +} + +} // namespace MetaCore diff --git a/Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreComponents.h b/Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreComponents.h new file mode 100644 index 0000000..6c594cf --- /dev/null +++ b/Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreComponents.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include +#include + +namespace MetaCore { + +// 定义第一阶段内置的网格类型。 +enum class MetaCoreBuiltinMeshType { + Cube +}; + +// 表示 Unity 风格对象的变换组件。 +struct MetaCoreTransformComponent { + glm::vec3 Position{0.0F, 0.0F, 0.0F}; + glm::vec3 RotationEulerDegrees{0.0F, 0.0F, 0.0F}; + glm::vec3 Scale{1.0F, 1.0F, 1.0F}; +}; + +// 表示场景中用于观察的摄像机组件。 +struct MetaCoreCameraComponent { + float FieldOfViewDegrees = 60.0F; + float NearClip = 0.1F; + float FarClip = 100.0F; + bool IsPrimary = false; +}; + +// 表示一个最小可用的网格渲染组件。 +struct MetaCoreMeshRendererComponent { + MetaCoreBuiltinMeshType BuiltinMesh = MetaCoreBuiltinMeshType::Cube; + glm::vec3 BaseColor{0.75F, 0.78F, 0.84F}; + bool Visible = true; +}; + +// 表示第一阶段场景中的方向光组件。 +struct MetaCoreLightComponent { + glm::vec3 Color{1.0F, 1.0F, 1.0F}; + float Intensity = 1.5F; +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreGameObject.h b/Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreGameObject.h new file mode 100644 index 0000000..d406ed2 --- /dev/null +++ b/Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreGameObject.h @@ -0,0 +1,22 @@ +#pragma once + +#include "MetaCoreFoundation/MetaCoreId.h" +#include "MetaCoreScene/MetaCoreComponents.h" + +#include +#include + +namespace MetaCore { + +// 表示一个 Unity 风格的场景对象。 +struct MetaCoreGameObject { + MetaCoreId Id = 0; + MetaCoreId ParentId = 0; + std::string Name; + MetaCoreTransformComponent Transform{}; + std::optional Camera; + std::optional MeshRenderer; + std::optional Light; +}; + +} // namespace MetaCore diff --git a/Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreScene.h b/Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreScene.h new file mode 100644 index 0000000..a4c5aab --- /dev/null +++ b/Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreScene.h @@ -0,0 +1,36 @@ +#pragma once + +#include "MetaCoreScene/MetaCoreGameObject.h" + +#include + +namespace MetaCore { + +// 负责存储和查询场景中的全部对象。 +class MetaCoreScene { +public: + // 创建一个新的场景对象。 + MetaCoreGameObject& CreateGameObject(const std::string& name, MetaCoreId parentId = 0); + + // 通过标识查找场景对象。 + [[nodiscard]] MetaCoreGameObject* FindGameObject(MetaCoreId objectId); + [[nodiscard]] const MetaCoreGameObject* FindGameObject(MetaCoreId objectId) const; + + // 返回全部对象。 + [[nodiscard]] std::vector& GetGameObjects(); + [[nodiscard]] const std::vector& GetGameObjects() const; + + // 返回根节点对象列表。 + [[nodiscard]] std::vector GetRootObjectIds() const; + + // 返回某个对象的直接子对象列表。 + [[nodiscard]] std::vector GetChildrenOf(MetaCoreId parentId) const; + +private: + std::vector GameObjects_{}; +}; + +// 创建一个包含默认相机、灯光和立方体的启动场景。 +MetaCoreScene MetaCoreCreateDefaultScene(); + +} // namespace MetaCore diff --git a/tests/MetaCoreSmokeTests.cpp b/tests/MetaCoreSmokeTests.cpp new file mode 100644 index 0000000..c49441c --- /dev/null +++ b/tests/MetaCoreSmokeTests.cpp @@ -0,0 +1,54 @@ +#include "MetaCoreEditor/MetaCoreEditorModule.h" +#include "MetaCoreFoundation/MetaCoreLogService.h" +#include "MetaCoreScene/MetaCoreScene.h" + +#include +#include + +namespace { + +void MetaCoreExpect(bool condition, const char* message) { + if (!condition) { + std::cerr << "MetaCoreSmokeTests failed: " << message << '\n'; + std::exit(1); + } +} + +class MetaCoreDummyPanelProvider final : public MetaCore::MetaCoreIEditorPanelProvider { +public: + std::string GetPanelId() const override { return "Dummy"; } + std::string GetPanelTitle() const override { return "Dummy"; } + bool IsOpenByDefault() const override { return false; } + void DrawPanel(MetaCore::MetaCoreEditorContext&) override {} +}; + +} // namespace + +int main() { + MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene(); + MetaCoreExpect(scene.GetGameObjects().size() == 3, "默认场景应包含三个对象"); + + bool hasCamera = false; + bool hasLight = false; + bool hasCube = false; + for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) { + hasCamera = hasCamera || object.Camera.has_value(); + hasLight = hasLight || object.Light.has_value(); + hasCube = hasCube || object.MeshRenderer.has_value(); + } + + MetaCoreExpect(hasCamera, "默认场景应包含摄像机"); + MetaCoreExpect(hasLight, "默认场景应包含光源"); + MetaCoreExpect(hasCube, "默认场景应包含立方体"); + + MetaCore::MetaCoreEditorModuleRegistry moduleRegistry; + moduleRegistry.RegisterPanelProvider(std::make_unique()); + MetaCoreExpect(!moduleRegistry.AccessPanelOpenState("Dummy"), "面板默认状态应为关闭"); + + MetaCore::MetaCoreLogService logService; + logService.AddEntry(MetaCore::MetaCoreLogLevel::Info, "Test", "Smoke"); + MetaCoreExpect(logService.GetEntries().size() == 1, "日志服务应记录一条日志"); + + std::cout << "MetaCoreSmokeTests passed\n"; + return 0; +} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..f3a2ec6 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,23 @@ +{ + "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", + "opengl3-binding" + ] + } + ] +}