Rebuild MetaCore Unity-like prototype

This commit is contained in:
ayuan9957 2026-03-20 10:39:34 +08:00
commit a637100607
43 changed files with 2941 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
build/
vcpkg_installed/
downloads/
.vs/
CMakeUserPresets.json
imgui.ini
*.obj
*.pdb
*.ilk
*.lib
*.exe
*.vcxproj*
*.sln
*.log

View File

@ -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;
}

View File

@ -0,0 +1,6 @@
#include <iostream>
int main() {
std::cout << "MetaCorePlayer 目前保留为后续运行时模块接入入口。\n";
return 0;
}

253
CMakeLists.txt Normal file
View File

@ -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"
"$<TARGET_FILE_DIR:${target_name}>"
)
endif()
endfunction()
find_package(OpenGL REQUIRED)
find_package(glad CONFIG REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
find_package(imgui CONFIG REQUIRED)
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()

59
CMakePresets.json Normal file
View File

@ -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
}
}
]
}

50
README.md Normal file
View File

@ -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`
- 轨道/平移/缩放相机控制
- 模块注册接口与内置编辑器模块

View File

@ -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 <imgui.h>
#include <GLFW/glfw3.h>
#include <glm/gtc/type_ptr.hpp>
#include <array>
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
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<MetaCoreId> 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<void*>(static_cast<intptr_t>(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<int>(editorContext.GetScene().GetGameObjects().size()));
ImGui::SameLine();
if (const MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject(); selectedObject != nullptr) {
ImGui::Text("当前对象: %s", selectedObject->Name.c_str());
} else {
ImGui::TextUnformatted("当前对象: 无");
}
ImGui::Separator();
ImVec2 viewportSize = ImGui::GetContentRegionAvail();
if (viewportSize.x < 1.0F) {
viewportSize.x = 1.0F;
}
if (viewportSize.y < 1.0F) {
viewportSize.y = 1.0F;
}
viewportState.Width = viewportSize.x;
viewportState.Height = viewportSize.y;
editorContext.GetViewportRenderer().Resize(static_cast<int>(viewportSize.x), static_cast<int>(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<void*>(static_cast<intptr_t>(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<unsigned long long>(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<char, 256> NameBuffer_{};
};
class MetaCoreBuiltinEditorModule final : public MetaCoreIModule {
public:
std::string GetModuleName() const override { return "MetaCoreBuiltinEditorModule"; }
void Startup(MetaCoreEditorModuleRegistry& moduleRegistry) override {
moduleRegistry.RegisterMenuProvider(std::make_unique<MetaCoreDefaultMenuProvider>());
moduleRegistry.RegisterPanelProvider(std::make_unique<MetaCoreHierarchyPanelProvider>());
moduleRegistry.RegisterPanelProvider(std::make_unique<MetaCoreScenePanelProvider>());
moduleRegistry.RegisterPanelProvider(std::make_unique<MetaCoreInspectorPanelProvider>());
moduleRegistry.RegisterPanelProvider(std::make_unique<MetaCoreProjectPanelProvider>());
moduleRegistry.RegisterPanelProvider(std::make_unique<MetaCoreConsolePanelProvider>());
moduleRegistry.RegisterInspectorDrawer(std::make_unique<MetaCoreTransformInspectorDrawer>());
}
void Shutdown(MetaCoreEditorModuleRegistry&) override {
}
};
} // namespace
std::unique_ptr<MetaCoreIModule> MetaCoreCreateBuiltinEditorModule() {
return std::make_unique<MetaCoreBuiltinEditorModule>();
}
} // namespace MetaCore

View File

@ -0,0 +1,12 @@
#pragma once
#include "MetaCoreEditor/MetaCoreEditorModule.h"
#include <memory>
namespace MetaCore {
// 创建内置 Unity-like 编辑器模块。
std::unique_ptr<MetaCoreIModule> MetaCoreCreateBuiltinEditorModule();
} // namespace MetaCore

View File

@ -0,0 +1,264 @@
#include "MetaCoreEditor/MetaCoreEditorApp.h"
#include "MetaCoreBuiltinEditorModule.h"
#include "MetaCoreEditorCameraController.h"
#include <imgui.h>
#include <imgui_internal.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <cstdlib>
#include <filesystem>
#include <glm/vec4.hpp>
#include <memory>
#include <vector>
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<std::filesystem::path> 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<MetaCoreEditorContext>(
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

View File

@ -0,0 +1,83 @@
#include "MetaCoreEditorCameraController.h"
#include "MetaCorePlatform/MetaCoreInput.h"
#include "MetaCoreScene/MetaCoreGameObject.h"
#include <imgui.h>
#include <GLFW/glfw3.h>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <algorithm>
#include <cmath>
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

View File

@ -0,0 +1,34 @@
#pragma once
#include "MetaCoreEditor/MetaCoreEditorContext.h"
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
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

View File

@ -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<MetaCoreEditorCameraController>()) {
}
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

View File

@ -0,0 +1,41 @@
#include "MetaCoreEditor/MetaCoreEditorModule.h"
#include <utility>
namespace MetaCore {
void MetaCoreEditorModuleRegistry::RegisterMenuProvider(std::unique_ptr<MetaCoreIMenuProvider> provider) {
MenuProviders_.push_back(std::move(provider));
}
void MetaCoreEditorModuleRegistry::RegisterPanelProvider(std::unique_ptr<MetaCoreIEditorPanelProvider> provider) {
PanelOpenStates_.emplace(provider->GetPanelId(), provider->IsOpenByDefault());
PanelProviders_.push_back(std::move(provider));
}
void MetaCoreEditorModuleRegistry::RegisterInspectorDrawer(std::unique_ptr<MetaCoreIInspectorDrawer> drawer) {
InspectorDrawers_.push_back(std::move(drawer));
}
const std::vector<std::unique_ptr<MetaCoreIMenuProvider>>& MetaCoreEditorModuleRegistry::GetMenuProviders() const {
return MenuProviders_;
}
const std::vector<std::unique_ptr<MetaCoreIEditorPanelProvider>>& MetaCoreEditorModuleRegistry::GetPanelProviders() const {
return PanelProviders_;
}
const std::vector<std::unique_ptr<MetaCoreIInspectorDrawer>>& 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

View File

@ -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 <memory>
#include <vector>
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<std::unique_ptr<MetaCoreIModule>> Modules_{};
std::unique_ptr<MetaCoreEditorContext> EditorContext_{};
bool Initialized_ = false;
};
} // namespace MetaCore

View File

@ -0,0 +1,77 @@
#pragma once
#include "MetaCoreFoundation/MetaCoreId.h"
#include "MetaCoreFoundation/MetaCoreLogService.h"
#include <memory>
#include <string>
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<MetaCoreEditorCameraController> CameraController_;
MetaCoreSceneViewportState SceneViewportState_{};
MetaCoreId SelectedObjectId_ = 0;
bool DockLayoutBuilt_ = false;
};
} // namespace MetaCore

View File

@ -0,0 +1,70 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
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<MetaCoreIMenuProvider> provider);
void RegisterPanelProvider(std::unique_ptr<MetaCoreIEditorPanelProvider> provider);
void RegisterInspectorDrawer(std::unique_ptr<MetaCoreIInspectorDrawer> drawer);
[[nodiscard]] const std::vector<std::unique_ptr<MetaCoreIMenuProvider>>& GetMenuProviders() const;
[[nodiscard]] const std::vector<std::unique_ptr<MetaCoreIEditorPanelProvider>>& GetPanelProviders() const;
[[nodiscard]] const std::vector<std::unique_ptr<MetaCoreIInspectorDrawer>>& GetInspectorDrawers() const;
bool& AccessPanelOpenState(const std::string& panelId);
[[nodiscard]] bool IsPanelOpen(const std::string& panelId) const;
private:
std::vector<std::unique_ptr<MetaCoreIMenuProvider>> MenuProviders_{};
std::vector<std::unique_ptr<MetaCoreIEditorPanelProvider>> PanelProviders_{};
std::vector<std::unique_ptr<MetaCoreIInspectorDrawer>> InspectorDrawers_{};
std::unordered_map<std::string, bool> 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

View File

@ -0,0 +1,17 @@
#include "MetaCoreFoundation/MetaCoreId.h"
#include <atomic>
namespace MetaCore {
namespace {
std::atomic<std::uint64_t> GMetaCoreNextId{1};
} // namespace
MetaCoreId MetaCoreIdGenerator::Generate() {
return GMetaCoreNextId.fetch_add(1, std::memory_order_relaxed);
}
} // namespace MetaCore

View File

@ -0,0 +1,23 @@
#include "MetaCoreFoundation/MetaCoreLogService.h"
#include <utility>
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<MetaCoreLogEntry>& MetaCoreLogService::GetEntries() const {
return Entries_;
}
void MetaCoreLogService::Clear() {
Entries_.clear();
}
} // namespace MetaCore

View File

@ -0,0 +1,17 @@
#pragma once
#include <cstdint>
namespace MetaCore {
// MetaCore 全局对象标识类型。
using MetaCoreId = std::uint64_t;
// 负责为场景对象和运行时实体分配递增标识。
class MetaCoreIdGenerator {
public:
// 生成一个新的全局唯一标识。
static MetaCoreId Generate();
};
} // namespace MetaCore

View File

@ -0,0 +1,38 @@
#pragma once
#include <string>
#include <vector>
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<MetaCoreLogEntry>& GetEntries() const;
// 清空全部日志记录。
void Clear();
private:
std::vector<MetaCoreLogEntry> Entries_;
};
} // namespace MetaCore

View File

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

View File

@ -0,0 +1,99 @@
#include "MetaCorePlatform/MetaCoreWindow.h"
#include <GLFW/glfw3.h>
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<float>(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<int, int> MetaCoreWindow::GetWindowSize() const {
int width = 0;
int height = 0;
glfwGetWindowSize(NativeHandle_, &width, &height);
return {width, height};
}
std::pair<int, int> MetaCoreWindow::GetFramebufferSize() const {
int width = 0;
int height = 0;
glfwGetFramebufferSize(NativeHandle_, &width, &height);
return {width, height};
}
} // namespace MetaCore

View File

@ -0,0 +1,41 @@
#pragma once
#include <glm/vec2.hpp>
#include <array>
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<bool, 512> CurrentKeyStates_{};
std::array<bool, 512> PreviousKeyStates_{};
std::array<bool, 8> CurrentMouseStates_{};
std::array<bool, 8> PreviousMouseStates_{};
glm::vec2 CursorPosition_{0.0F, 0.0F};
glm::vec2 CursorDelta_{0.0F, 0.0F};
};
} // namespace MetaCore

View File

@ -0,0 +1,56 @@
#pragma once
#include "MetaCorePlatform/MetaCoreInput.h"
#include <string>
#include <utility>
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<int, int> GetWindowSize() const;
// 返回帧缓冲尺寸。
[[nodiscard]] std::pair<int, int> GetFramebufferSize() const;
private:
GLFWwindow* NativeHandle_ = nullptr;
MetaCoreInput Input_{};
float DeltaSeconds_ = 1.0F / 60.0F;
double LastFrameTime_ = 0.0;
};
} // namespace MetaCore

View File

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

View File

@ -0,0 +1,116 @@
#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h"
#include "MetaCoreRender/MetaCoreSceneRenderer.h"
#include "MetaCoreScene/MetaCoreScene.h"
#include <glad/glad.h>
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

View File

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

View File

@ -0,0 +1,45 @@
#include "MetaCoreRender/MetaCoreRenderDevice.h"
#include <glad/glad.h>
#include <GLFW/glfw3.h>
namespace MetaCore {
bool MetaCoreRenderDevice::Initialize(GLFWwindow* nativeWindow) {
if (Initialized_) {
return true;
}
if (gladLoadGLLoader(reinterpret_cast<GLADloadproc>(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

View File

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

View File

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

View File

@ -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

View File

@ -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

View File

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

View File

@ -0,0 +1,31 @@
#pragma once
#include <glm/vec4.hpp>
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

View File

@ -0,0 +1,18 @@
#pragma once
#include "MetaCoreFoundation/MetaCoreId.h"
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
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

View File

@ -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

View File

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

View File

@ -0,0 +1,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<MetaCoreGameObject>& MetaCoreScene::GetGameObjects() {
return GameObjects_;
}
const std::vector<MetaCoreGameObject>& MetaCoreScene::GetGameObjects() const {
return GameObjects_;
}
std::vector<MetaCoreId> MetaCoreScene::GetRootObjectIds() const {
std::vector<MetaCoreId> rootIds;
for (const MetaCoreGameObject& object : GameObjects_) {
if (object.ParentId == 0) {
rootIds.push_back(object.Id);
}
}
return rootIds;
}
std::vector<MetaCoreId> MetaCoreScene::GetChildrenOf(MetaCoreId parentId) const {
std::vector<MetaCoreId> 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

View File

@ -0,0 +1,43 @@
#pragma once
#include <glm/vec3.hpp>
#include <optional>
#include <string>
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

View File

@ -0,0 +1,22 @@
#pragma once
#include "MetaCoreFoundation/MetaCoreId.h"
#include "MetaCoreScene/MetaCoreComponents.h"
#include <optional>
#include <string>
namespace MetaCore {
// 表示一个 Unity 风格的场景对象。
struct MetaCoreGameObject {
MetaCoreId Id = 0;
MetaCoreId ParentId = 0;
std::string Name;
MetaCoreTransformComponent Transform{};
std::optional<MetaCoreCameraComponent> Camera;
std::optional<MetaCoreMeshRendererComponent> MeshRenderer;
std::optional<MetaCoreLightComponent> Light;
};
} // namespace MetaCore

View File

@ -0,0 +1,36 @@
#pragma once
#include "MetaCoreScene/MetaCoreGameObject.h"
#include <vector>
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<MetaCoreGameObject>& GetGameObjects();
[[nodiscard]] const std::vector<MetaCoreGameObject>& GetGameObjects() const;
// 返回根节点对象列表。
[[nodiscard]] std::vector<MetaCoreId> GetRootObjectIds() const;
// 返回某个对象的直接子对象列表。
[[nodiscard]] std::vector<MetaCoreId> GetChildrenOf(MetaCoreId parentId) const;
private:
std::vector<MetaCoreGameObject> GameObjects_{};
};
// 创建一个包含默认相机、灯光和立方体的启动场景。
MetaCoreScene MetaCoreCreateDefaultScene();
} // namespace MetaCore

View File

@ -0,0 +1,54 @@
#include "MetaCoreEditor/MetaCoreEditorModule.h"
#include "MetaCoreFoundation/MetaCoreLogService.h"
#include "MetaCoreScene/MetaCoreScene.h"
#include <cstdlib>
#include <iostream>
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<MetaCoreDummyPanelProvider>());
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;
}

23
vcpkg.json Normal file
View File

@ -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"
]
}
]
}