refactor: 迁移GameObject到EnTT ECS架构并修复编译错误

This commit is contained in:
ayuan9957 2026-05-12 15:29:16 +08:00
parent 3b1aa895aa
commit c8021a9206
35 changed files with 94009 additions and 1177 deletions

View File

@ -193,6 +193,7 @@ add_library(MetaCoreScene STATIC
target_include_directories(MetaCoreScene target_include_directories(MetaCoreScene
PUBLIC PUBLIC
Source/MetaCoreScene/Public Source/MetaCoreScene/Public
third_party
) )
target_link_libraries(MetaCoreScene target_link_libraries(MetaCoreScene

File diff suppressed because it is too large Load Diff

View File

@ -295,17 +295,17 @@ void MetaCoreDrawSelectionBoundsOverlay(
ImVec2 groupMax(-FLT_MAX, -FLT_MAX); ImVec2 groupMax(-FLT_MAX, -FLT_MAX);
for (const MetaCoreId selectedObjectId : selectedObjectIds) { for (const MetaCoreId selectedObjectId : selectedObjectIds) {
const MetaCoreGameObject* selectedObject = scene.FindGameObject(selectedObjectId); const MetaCoreGameObject selectedObject = scene.FindGameObject(selectedObjectId);
if (selectedObject == nullptr) { if (!selectedObject) {
continue; continue;
} }
const glm::vec3 halfExtents( const glm::vec3 halfExtents(
std::max(std::abs(selectedObject->Transform.Scale.x), 0.5F) * 0.5F, std::max(std::abs(selectedObject.GetComponent<MetaCoreTransformComponent>().Scale.x), 0.5F) * 0.5F,
std::max(std::abs(selectedObject->Transform.Scale.y), 0.5F) * 0.5F, std::max(std::abs(selectedObject.GetComponent<MetaCoreTransformComponent>().Scale.y), 0.5F) * 0.5F,
std::max(std::abs(selectedObject->Transform.Scale.z), 0.5F) * 0.5F std::max(std::abs(selectedObject.GetComponent<MetaCoreTransformComponent>().Scale.z), 0.5F) * 0.5F
); );
const glm::vec3 center = selectedObject->Transform.Position; const glm::vec3 center = selectedObject.GetComponent<MetaCoreTransformComponent>().Position;
const std::array<glm::vec3, 8> corners = { const std::array<glm::vec3, 8> corners = {
center + glm::vec3(-halfExtents.x, -halfExtents.y, -halfExtents.z), center + glm::vec3(-halfExtents.x, -halfExtents.y, -halfExtents.z),
@ -347,7 +347,7 @@ void MetaCoreDrawSelectionBoundsOverlay(
const ImU32 strokeColor = isActive ? activeBoxColor : boxColor; const ImU32 strokeColor = isActive ? activeBoxColor : boxColor;
drawList->AddRectFilled(minPoint, maxPoint, fillColor, 2.0F); drawList->AddRectFilled(minPoint, maxPoint, fillColor, 2.0F);
drawList->AddRect(minPoint, maxPoint, strokeColor, 2.0F, 0, isActive ? 2.0F : 1.5F); drawList->AddRect(minPoint, maxPoint, strokeColor, 2.0F, 0, isActive ? 2.0F : 1.5F);
drawList->AddText(ImVec2(minPoint.x, minPoint.y - 18.0F), strokeColor, selectedObject->Name.c_str()); drawList->AddText(ImVec2(minPoint.x, minPoint.y - 18.0F), strokeColor, selectedObject.GetName().c_str());
} }
if (selectedObjectIds.size() > 1 && hasGroupBounds) { if (selectedObjectIds.size() > 1 && hasGroupBounds) {
@ -360,6 +360,73 @@ void MetaCoreDrawSelectionBoundsOverlay(
} }
} }
void MetaCoreDrawCompatibilityScenePreviewOverlay(
const MetaCoreScene& scene,
const MetaCoreSceneView& sceneView,
const MetaCoreSceneViewportState& viewportState
) {
if (viewportState.Width <= 1.0F || viewportState.Height <= 1.0F) {
return;
}
ImDrawList* drawList = ImGui::GetForegroundDrawList();
for (const MetaCoreGameObject& gameObject : scene.GetGameObjects()) {
if (!gameObject.HasComponent<MetaCoreMeshRendererComponent>() || !gameObject.GetComponent<MetaCoreMeshRendererComponent>().Visible) {
continue;
}
const glm::vec3 halfExtents(
std::max(std::abs(gameObject.GetComponent<MetaCoreTransformComponent>().Scale.x), 0.5F) * 0.5F,
std::max(std::abs(gameObject.GetComponent<MetaCoreTransformComponent>().Scale.y), 0.5F) * 0.5F,
std::max(std::abs(gameObject.GetComponent<MetaCoreTransformComponent>().Scale.z), 0.5F) * 0.5F
);
const glm::vec3 center = gameObject.GetComponent<MetaCoreTransformComponent>().Position;
const std::array<glm::vec3, 8> corners = {
center + glm::vec3(-halfExtents.x, -halfExtents.y, -halfExtents.z),
center + glm::vec3( halfExtents.x, -halfExtents.y, -halfExtents.z),
center + glm::vec3(-halfExtents.x, halfExtents.y, -halfExtents.z),
center + glm::vec3( halfExtents.x, halfExtents.y, -halfExtents.z),
center + glm::vec3(-halfExtents.x, -halfExtents.y, halfExtents.z),
center + glm::vec3( halfExtents.x, -halfExtents.y, halfExtents.z),
center + glm::vec3(-halfExtents.x, halfExtents.y, halfExtents.z),
center + glm::vec3( halfExtents.x, halfExtents.y, halfExtents.z)
};
bool anyPointVisible = false;
ImVec2 minPoint(FLT_MAX, FLT_MAX);
ImVec2 maxPoint(-FLT_MAX, -FLT_MAX);
for (const glm::vec3& corner : corners) {
ImVec2 screenPoint{};
if (!MetaCoreProjectWorldPointToViewport(sceneView, viewportState, corner, screenPoint)) {
continue;
}
anyPointVisible = true;
minPoint.x = std::min(minPoint.x, screenPoint.x);
minPoint.y = std::min(minPoint.y, screenPoint.y);
maxPoint.x = std::max(maxPoint.x, screenPoint.x);
maxPoint.y = std::max(maxPoint.y, screenPoint.y);
}
if (!anyPointVisible) {
continue;
}
const auto toByte = [](float value) -> int {
return static_cast<int>(std::clamp(value, 0.0F, 1.0F) * 255.0F);
};
const ImU32 fillColor = IM_COL32(
toByte(gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.r),
toByte(gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.g),
toByte(gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.b),
105
);
const ImU32 strokeColor = IM_COL32(210, 226, 245, 190);
drawList->AddRectFilled(minPoint, maxPoint, fillColor, 2.0F);
drawList->AddRect(minPoint, maxPoint, strokeColor, 2.0F, 0, 1.2F);
drawList->AddText(ImVec2(minPoint.x + 4.0F, minPoint.y + 4.0F), strokeColor, gameObject.GetName().c_str());
}
}
void MetaCoreDrawSelectionHierarchyOverlay( void MetaCoreDrawSelectionHierarchyOverlay(
const MetaCoreEditorContext& editorContext, const MetaCoreEditorContext& editorContext,
const MetaCoreScene& scene, const MetaCoreScene& scene,
@ -376,20 +443,20 @@ void MetaCoreDrawSelectionHierarchyOverlay(
const ImU32 lineColor = IM_COL32(255, 230, 160, 140); const ImU32 lineColor = IM_COL32(255, 230, 160, 140);
for (const MetaCoreId selectedObjectId : selectedObjectIds) { for (const MetaCoreId selectedObjectId : selectedObjectIds) {
const MetaCoreGameObject* childObject = scene.FindGameObject(selectedObjectId); const MetaCoreGameObject childObject = scene.FindGameObject(selectedObjectId);
if (childObject == nullptr || childObject->ParentId == 0 || !selectedSet.contains(childObject->ParentId)) { if (!childObject || childObject.GetParentId() == 0 || !selectedSet.contains(childObject.GetParentId())) {
continue; continue;
} }
const MetaCoreGameObject* parentObject = scene.FindGameObject(childObject->ParentId); const MetaCoreGameObject parentObject = scene.FindGameObject(childObject.GetParentId());
if (parentObject == nullptr) { if (!parentObject) {
continue; continue;
} }
ImVec2 childPoint{}; ImVec2 childPoint{};
ImVec2 parentPoint{}; ImVec2 parentPoint{};
if (!MetaCoreProjectWorldPointToViewport(sceneView, viewportState, childObject->Transform.Position, childPoint) || if (!MetaCoreProjectWorldPointToViewport(sceneView, viewportState, childObject.GetComponent<MetaCoreTransformComponent>().Position, childPoint) ||
!MetaCoreProjectWorldPointToViewport(sceneView, viewportState, parentObject->Transform.Position, parentPoint)) { !MetaCoreProjectWorldPointToViewport(sceneView, viewportState, parentObject.GetComponent<MetaCoreTransformComponent>().Position, parentPoint)) {
continue; continue;
} }
@ -413,11 +480,11 @@ void MetaCoreDrawSelectionGroupCenterOverlay(
glm::vec3 center(0.0F); glm::vec3 center(0.0F);
int validObjectCount = 0; int validObjectCount = 0;
for (const MetaCoreId selectedObjectId : selectedObjectIds) { for (const MetaCoreId selectedObjectId : selectedObjectIds) {
const MetaCoreGameObject* selectedObject = scene.FindGameObject(selectedObjectId); const MetaCoreGameObject selectedObject = scene.FindGameObject(selectedObjectId);
if (selectedObject == nullptr) { if (!selectedObject) {
continue; continue;
} }
center += selectedObject->Transform.Position; center += selectedObject.GetComponent<MetaCoreTransformComponent>().Position;
++validObjectCount; ++validObjectCount;
} }
@ -644,8 +711,8 @@ bool MetaCoreEditorApp::Initialize() {
MetaCoreTraceStartup("metacore.app: select default focus object"); MetaCoreTraceStartup("metacore.app: select default focus object");
if (EditorContext_->GetSelectedObjectId() == 0) { if (EditorContext_->GetSelectedObjectId() == 0) {
for (const MetaCoreGameObject& object : Scene_.GetGameObjects()) { for (const MetaCoreGameObject& object : Scene_.GetGameObjects()) {
if (object.Name == "Cube" || object.Name == "Demo Cube") { if (object.GetName() == "Cube" || object.GetName() == "Demo Cube") {
EditorContext_->SetSelectedObjectId(object.Id); EditorContext_->SetSelectedObjectId(object.GetId());
EditorContext_->GetCameraController().FocusGameObject(object); EditorContext_->GetCameraController().FocusGameObject(object);
break; break;
} }
@ -667,12 +734,12 @@ bool MetaCoreEditorApp::Initialize() {
EditorContext_->AddConsoleMessage( EditorContext_->AddConsoleMessage(
MetaCoreLogLevel::Warning, MetaCoreLogLevel::Warning,
"Render", "Render",
"Debug 兼容模式已启用:MetaCore 场景对象同步已临时禁用,窗口可稳定运行,但 3D 场景内容不会完整显示" "Debug 兼容模式已启用:当前优先保证 MetaCore 编辑器稳定运行,部分 3D 场景内容会暂时简化显示"
); );
EditorContext_->AddConsoleMessage( EditorContext_->AddConsoleMessage(
MetaCoreLogLevel::Info, MetaCoreLogLevel::Info,
"Render", "Render",
"如需调试底层场景同步,可设置环境变量 METACORE_ENABLE_DEBUG_SCENE_SYNC=1 后重启编辑器" "如需继续排查场景同步问题,可设置开发环境变量 METACORE_ENABLE_DEBUG_SCENE_SYNC=1 后重启编辑器"
); );
} }
if (loadedStartupScene) { if (loadedStartupScene) {
@ -726,10 +793,10 @@ void MetaCoreEditorApp::Shutdown() {
} }
bool MetaCoreEditorApp::InitializeImGui() { bool MetaCoreEditorApp::InitializeImGui() {
MetaCoreTraceStartup("metacore.ui: imgui begin"); MetaCoreTraceStartup("metacore.ui: initialize begin");
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
ImGui::CreateContext(); ImGui::CreateContext();
MetaCoreTraceStartup("metacore.ui: imgui context created"); MetaCoreTraceStartup("metacore.ui: context created");
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
@ -738,23 +805,23 @@ bool MetaCoreEditorApp::InitializeImGui() {
io.IniFilename = nullptr; io.IniFilename = nullptr;
io.LogFilename = nullptr; io.LogFilename = nullptr;
#endif #endif
MetaCoreTraceStartup("metacore.ui: configure font begin"); MetaCoreTraceStartup("metacore.ui: font setup begin");
MetaCoreConfigureChineseFont(); MetaCoreConfigureChineseFont();
MetaCoreTraceStartup("metacore.ui: configure font done"); MetaCoreTraceStartup("metacore.ui: font setup done");
MetaCoreApplyEditorStyle(); MetaCoreApplyEditorStyle();
MetaCoreTraceStartup("metacore.ui: style done"); MetaCoreTraceStartup("metacore.ui: editor style ready");
if (!ImGui_ImplWin32_InitForOpenGL(Window_.GetNativeWindowHandle())) { if (!ImGui_ImplWin32_InitForOpenGL(Window_.GetNativeWindowHandle())) {
MetaCoreTraceStartup("metacore.ui: win32 init failed"); MetaCoreTraceStartup("metacore.ui: platform backend init failed");
return false; return false;
} }
MetaCoreTraceStartup("metacore.ui: win32 init done"); MetaCoreTraceStartup("metacore.ui: platform backend ready");
if (!ImGui_ImplOpenGL3_Init("#version 130")) { if (!ImGui_ImplOpenGL3_Init("#version 130")) {
MetaCoreTraceStartup("metacore.ui: opengl3 init failed"); MetaCoreTraceStartup("metacore.ui: graphics backend init failed");
return false; return false;
} }
MetaCoreTraceStartup("metacore.ui: opengl3 init done"); MetaCoreTraceStartup("metacore.ui: graphics backend ready");
return true; return true;
} }
@ -857,7 +924,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
if (ModuleRegistry_.IsPanelOpen("Scene")) { if (ModuleRegistry_.IsPanelOpen("Scene")) {
ImGuiDockNode* centralNode = ImGui::DockBuilderGetCentralNode(dockSpaceId); ImGuiDockNode* centralNode = ImGui::DockBuilderGetCentralNode(dockSpaceId);
if (centralNode != nullptr) { if (centralNode != nullptr) {
// Viewport Tabs (Scene / Game) // MetaCore main viewport keeps a Unity-like Scene / Game mental model.
ImGui::SetNextWindowPos(centralNode->Pos); ImGui::SetNextWindowPos(centralNode->Pos);
ImGui::SetNextWindowSize(ImVec2(centralNode->Size.x, 30.0F)); ImGui::SetNextWindowSize(ImVec2(centralNode->Size.x, 30.0F));
ImGui::Begin("ViewportTabs", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); ImGui::Begin("ViewportTabs", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
@ -894,7 +961,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
viewportState.Focused = viewportState.Hovered && !ImGui::GetIO().WantCaptureMouse; viewportState.Focused = viewportState.Hovered && !ImGui::GetIO().WantCaptureMouse;
if (viewportState.Focused && !ImGui::GetIO().WantCaptureKeyboard && EditorContext_->GetInput().WasKeyPressed(MetaCoreInputKey::Focus)) { if (viewportState.Focused && !ImGui::GetIO().WantCaptureKeyboard && EditorContext_->GetInput().WasKeyPressed(MetaCoreInputKey::Focus)) {
if (MetaCoreGameObject* selectedObject = EditorContext_->GetSelectedGameObject(); selectedObject != nullptr) { if (MetaCoreGameObject selectedObject = EditorContext_->GetSelectedGameObject(); selectedObject) {
EditorContext_->GetCameraController().FocusGameObject(*selectedObject); EditorContext_->GetCameraController().FocusGameObject(*selectedObject);
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "相机已聚焦到当前对象"); EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "相机已聚焦到当前对象");
} }
@ -991,6 +1058,9 @@ void MetaCoreEditorApp::DrawEditorFrame() {
MetaCoreDrawViewportGridOverlay(*EditorContext_, viewportState); MetaCoreDrawViewportGridOverlay(*EditorContext_, viewportState);
if (ViewportRenderer_.IsSceneSyncDegraded()) {
MetaCoreDrawCompatibilityScenePreviewOverlay(Scene_, sceneView, viewportState);
}
MetaCoreDrawWorldOriginOverlay(*EditorContext_, sceneView, viewportState); MetaCoreDrawWorldOriginOverlay(*EditorContext_, sceneView, viewportState);
MetaCoreDrawSelectionBoundsOverlay(*EditorContext_, Scene_, sceneView, viewportState); MetaCoreDrawSelectionBoundsOverlay(*EditorContext_, Scene_, sceneView, viewportState);
MetaCoreDrawSelectionGroupCenterOverlay(*EditorContext_, Scene_, sceneView, viewportState); MetaCoreDrawSelectionGroupCenterOverlay(*EditorContext_, Scene_, sceneView, viewportState);
@ -1021,12 +1091,12 @@ void MetaCoreEditorApp::DrawEditorFrame() {
overlayDrawList->AddText( overlayDrawList->AddText(
ImVec2(overlayMin.x + 12.0F, overlayMin.y + 28.0F), ImVec2(overlayMin.x + 12.0F, overlayMin.y + 28.0F),
IM_COL32(240, 226, 208, 255), IM_COL32(240, 226, 208, 255),
"已暂时关闭 MetaCore 场景对象同步,避免视口导致编辑器崩溃" "当前优先保证 MetaCore 编辑器稳定运行3D 视图会先显示可渲染对象"
); );
overlayDrawList->AddText( overlayDrawList->AddText(
ImVec2(overlayMin.x + 12.0F, overlayMin.y + 44.0F), ImVec2(overlayMin.x + 12.0F, overlayMin.y + 44.0F),
IM_COL32(209, 199, 184, 255), IM_COL32(209, 199, 184, 255),
"可设置 METACORE_ENABLE_DEBUG_SCENE_SYNC=1 进行强制测试" "Camera/Light 等非渲染节点暂时简化,可设置 METACORE_ENABLE_DEBUG_SCENE_SYNC=1 继续开发测试"
); );
if (!ViewportRenderer_.GetLastSceneSyncFailure().empty()) { if (!ViewportRenderer_.GetLastSceneSyncFailure().empty()) {
overlayDrawList->AddText( overlayDrawList->AddText(

View File

@ -86,35 +86,35 @@ private:
return false; return false;
} }
if (lhs.Camera.has_value() != rhs.Camera.has_value()) { if (lhs.HasComponent<MetaCoreCameraComponent>() != rhs.HasComponent<MetaCoreCameraComponent>()) {
return false; return false;
} }
if (lhs.Camera.has_value()) { if (lhs.HasComponent<MetaCoreCameraComponent>()) {
if (!MetaCoreNearlyEqual(lhs.Camera->FieldOfViewDegrees, rhs.Camera->FieldOfViewDegrees) || if (!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().FieldOfViewDegrees, rhs.GetComponent<MetaCoreCameraComponent>().FieldOfViewDegrees) ||
!MetaCoreNearlyEqual(lhs.Camera->NearClip, rhs.Camera->NearClip) || !MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().NearClip, rhs.GetComponent<MetaCoreCameraComponent>().NearClip) ||
!MetaCoreNearlyEqual(lhs.Camera->FarClip, rhs.Camera->FarClip) || !MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().FarClip, rhs.GetComponent<MetaCoreCameraComponent>().FarClip) ||
lhs.Camera->IsPrimary != rhs.Camera->IsPrimary) { lhs.GetComponent<MetaCoreCameraComponent>().IsPrimary != rhs.GetComponent<MetaCoreCameraComponent>().IsPrimary) {
return false; return false;
} }
} }
if (lhs.MeshRenderer.has_value() != rhs.MeshRenderer.has_value()) { if (lhs.HasComponent<MetaCoreMeshRendererComponent>() != rhs.HasComponent<MetaCoreMeshRendererComponent>()) {
return false; return false;
} }
if (lhs.MeshRenderer.has_value()) { if (lhs.HasComponent<MetaCoreMeshRendererComponent>()) {
if (lhs.MeshRenderer->BuiltinMesh != rhs.MeshRenderer->BuiltinMesh || if (lhs.GetComponent<MetaCoreMeshRendererComponent>().BuiltinMesh != rhs.GetComponent<MetaCoreMeshRendererComponent>().BuiltinMesh ||
!MetaCoreNearlyEqualVec3(lhs.MeshRenderer->BaseColor, rhs.MeshRenderer->BaseColor) || !MetaCoreNearlyEqualVec3(lhs.GetComponent<MetaCoreMeshRendererComponent>().BaseColor, rhs.GetComponent<MetaCoreMeshRendererComponent>().BaseColor) ||
lhs.MeshRenderer->Visible != rhs.MeshRenderer->Visible) { lhs.GetComponent<MetaCoreMeshRendererComponent>().Visible != rhs.GetComponent<MetaCoreMeshRendererComponent>().Visible) {
return false; return false;
} }
} }
if (lhs.Light.has_value() != rhs.Light.has_value()) { if (lhs.HasComponent<MetaCoreLightComponent>() != rhs.HasComponent<MetaCoreLightComponent>()) {
return false; return false;
} }
if (lhs.Light.has_value()) { if (lhs.HasComponent<MetaCoreLightComponent>()) {
if (!MetaCoreNearlyEqualVec3(lhs.Light->Color, rhs.Light->Color) || if (!MetaCoreNearlyEqualVec3(lhs.GetComponent<MetaCoreLightComponent>().Color, rhs.GetComponent<MetaCoreLightComponent>().Color) ||
!MetaCoreNearlyEqual(lhs.Light->Intensity, rhs.Light->Intensity)) { !MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreLightComponent>().Intensity, rhs.GetComponent<MetaCoreLightComponent>().Intensity)) {
return false; return false;
} }
} }
@ -330,8 +330,8 @@ void MetaCoreEditorContext::SelectRangeByOrderedIds(const std::vector<MetaCoreId
MetaCoreId MetaCoreEditorContext::GetSelectedObjectId() const { return ActiveObjectId_; } MetaCoreId MetaCoreEditorContext::GetSelectedObjectId() const { return ActiveObjectId_; }
void MetaCoreEditorContext::SetSelectedObjectId(MetaCoreId objectId) { SelectOnly(objectId); } void MetaCoreEditorContext::SetSelectedObjectId(MetaCoreId objectId) { SelectOnly(objectId); }
MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() { return Scene_.FindGameObject(ActiveObjectId_); } MetaCoreGameObject MetaCoreEditorContext::GetSelectedGameObject() { return Scene_.FindGameObject(ActiveObjectId_); }
const MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() const { return Scene_.FindGameObject(ActiveObjectId_); } const MetaCoreGameObject MetaCoreEditorContext::GetSelectedGameObject() const { return Scene_.FindGameObject(ActiveObjectId_); }
MetaCoreGizmoOperation MetaCoreEditorContext::GetGizmoOperation() const { return GizmoOperation_; } MetaCoreGizmoOperation MetaCoreEditorContext::GetGizmoOperation() const { return GizmoOperation_; }
void MetaCoreEditorContext::SetGizmoOperation(MetaCoreGizmoOperation operation) { GizmoOperation_ = operation; } void MetaCoreEditorContext::SetGizmoOperation(MetaCoreGizmoOperation operation) { GizmoOperation_ = operation; }
MetaCoreGizmoMode MetaCoreEditorContext::GetGizmoMode() const { return GizmoMode_; } MetaCoreGizmoMode MetaCoreEditorContext::GetGizmoMode() const { return GizmoMode_; }

View File

@ -39,21 +39,21 @@ glm::mat4 MetaCoreBuildTransformMatrix(const MetaCoreTransformComponent& transfo
} }
glm::mat4 MetaCoreBuildWorldTransformMatrix(const MetaCoreScene& scene, MetaCoreId objectId) { glm::mat4 MetaCoreBuildWorldTransformMatrix(const MetaCoreScene& scene, MetaCoreId objectId) {
const MetaCoreGameObject* object = scene.FindGameObject(objectId); const MetaCoreGameObject object = scene.FindGameObject(objectId);
if (object == nullptr) { if (!object) {
return glm::mat4(1.0F); return glm::mat4(1.0F);
} }
glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object->Transform); glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object.GetComponent<MetaCoreTransformComponent>());
MetaCoreId parentId = object->ParentId; MetaCoreId parentId = object.GetParentId();
while (parentId != 0) { while (parentId != 0) {
const MetaCoreGameObject* parentObject = scene.FindGameObject(parentId); const MetaCoreGameObject parentObject = scene.FindGameObject(parentId);
if (parentObject == nullptr) { if (!parentObject) {
break; break;
} }
worldMatrix = MetaCoreBuildTransformMatrix(parentObject->Transform) * worldMatrix; worldMatrix = MetaCoreBuildTransformMatrix(parentObject.GetComponent<MetaCoreTransformComponent>()) * worldMatrix;
parentId = parentObject->ParentId; parentId = parentObject.GetParentId();
} }
return worldMatrix; return worldMatrix;
@ -170,14 +170,14 @@ MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
return cacheIterator->second; return cacheIterator->second;
} }
const MetaCoreGameObject* object = scene.FindGameObject(objectId); const MetaCoreGameObject object = scene.FindGameObject(objectId);
if (object == nullptr) { if (!object) {
return glm::mat4(1.0F); return glm::mat4(1.0F);
} }
glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object->Transform); glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object.GetComponent<MetaCoreTransformComponent>());
if (object->ParentId != 0) { if (object.GetParentId() != 0) {
worldMatrix = self(self, object->ParentId) * worldMatrix; worldMatrix = self(self, object.GetParentId()) * worldMatrix;
} }
worldMatrixCache.emplace(objectId, worldMatrix); worldMatrixCache.emplace(objectId, worldMatrix);
@ -188,11 +188,11 @@ MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
float bestHitDistance = std::numeric_limits<float>::max(); float bestHitDistance = std::numeric_limits<float>::max();
for (const MetaCoreGameObject& gameObject : scene.GetGameObjects()) { for (const MetaCoreGameObject& gameObject : scene.GetGameObjects()) {
if (!gameObject.MeshRenderer.has_value() || !gameObject.MeshRenderer->Visible) { if (!gameObject.HasComponent<MetaCoreMeshRendererComponent>() || !gameObject.GetComponent<MetaCoreMeshRendererComponent>().Visible) {
continue; continue;
} }
const glm::mat4 worldMatrix = buildWorldMatrix(buildWorldMatrix, gameObject.Id); const glm::mat4 worldMatrix = buildWorldMatrix(buildWorldMatrix, gameObject.GetId());
const glm::mat4 inverseWorldMatrix = glm::inverse(worldMatrix); const glm::mat4 inverseWorldMatrix = glm::inverse(worldMatrix);
const glm::vec3 localRayOrigin = glm::vec3(inverseWorldMatrix * glm::vec4(rayOrigin, 1.0F)); const glm::vec3 localRayOrigin = glm::vec3(inverseWorldMatrix * glm::vec4(rayOrigin, 1.0F));
const glm::vec3 localRayDirection = glm::normalize(glm::vec3(inverseWorldMatrix * glm::vec4(rayDirection, 0.0F))); const glm::vec3 localRayDirection = glm::normalize(glm::vec3(inverseWorldMatrix * glm::vec4(rayDirection, 0.0F)));
@ -234,7 +234,7 @@ MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
const float hitDistance = nearDistance >= 0.0F ? nearDistance : farDistance; const float hitDistance = nearDistance >= 0.0F ? nearDistance : farDistance;
if (hitDistance >= 0.0F && hitDistance < bestHitDistance) { if (hitDistance >= 0.0F && hitDistance < bestHitDistance) {
bestHitDistance = hitDistance; bestHitDistance = hitDistance;
bestObjectId = gameObject.Id; bestObjectId = gameObject.GetId();
} }
} }
} }
@ -285,8 +285,8 @@ void MetaCoreSceneInteractionService::FocusSelectedObject(MetaCoreEditorContext&
} }
if (selectedObjectIds.size() == 1) { if (selectedObjectIds.size() == 1) {
MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject(); MetaCoreGameObject selectedObject = editorContext.GetSelectedGameObject();
if (selectedObject != nullptr) { if (selectedObject) {
editorContext.GetCameraController().FocusGameObject(*selectedObject); editorContext.GetCameraController().FocusGameObject(*selectedObject);
} }
return; return;
@ -296,19 +296,19 @@ void MetaCoreSceneInteractionService::FocusSelectedObject(MetaCoreEditorContext&
glm::vec3 boundsMin(0.0F); glm::vec3 boundsMin(0.0F);
glm::vec3 boundsMax(0.0F); glm::vec3 boundsMax(0.0F);
for (const MetaCoreId selectedObjectId : selectedObjectIds) { for (const MetaCoreId selectedObjectId : selectedObjectIds) {
const MetaCoreGameObject* selectedObject = editorContext.GetScene().FindGameObject(selectedObjectId); const MetaCoreGameObject selectedObject = editorContext.GetScene().FindGameObject(selectedObjectId);
if (selectedObject == nullptr) { if (!selectedObject) {
continue; continue;
} }
const float extent = std::max({ const float extent = std::max({
std::abs(selectedObject->Transform.Scale.x), std::abs(selectedObject.GetComponent<MetaCoreTransformComponent>().Scale.x),
std::abs(selectedObject->Transform.Scale.y), std::abs(selectedObject.GetComponent<MetaCoreTransformComponent>().Scale.y),
std::abs(selectedObject->Transform.Scale.z), std::abs(selectedObject.GetComponent<MetaCoreTransformComponent>().Scale.z),
0.5F 0.5F
}); });
const glm::vec3 objectMin = selectedObject->Transform.Position - glm::vec3(extent); const glm::vec3 objectMin = selectedObject.GetComponent<MetaCoreTransformComponent>().Position - glm::vec3(extent);
const glm::vec3 objectMax = selectedObject->Transform.Position + glm::vec3(extent); const glm::vec3 objectMax = selectedObject.GetComponent<MetaCoreTransformComponent>().Position + glm::vec3(extent);
if (!initialized) { if (!initialized) {
boundsMin = objectMin; boundsMin = objectMin;
@ -338,18 +338,18 @@ void MetaCoreSceneInteractionService::FocusVisibleScene(MetaCoreEditorContext& e
glm::vec3 boundsMax(0.0F); glm::vec3 boundsMax(0.0F);
for (const MetaCoreGameObject& gameObject : gameObjects) { for (const MetaCoreGameObject& gameObject : gameObjects) {
if (gameObject.MeshRenderer.has_value() && !gameObject.MeshRenderer->Visible) { if (gameObject.HasComponent<MetaCoreMeshRendererComponent>() && !gameObject.GetComponent<MetaCoreMeshRendererComponent>().Visible) {
continue; continue;
} }
const float extent = std::max({ const float extent = std::max({
std::abs(gameObject.Transform.Scale.x), std::abs(gameObject.GetComponent<MetaCoreTransformComponent>().Scale.x),
std::abs(gameObject.Transform.Scale.y), std::abs(gameObject.GetComponent<MetaCoreTransformComponent>().Scale.y),
std::abs(gameObject.Transform.Scale.z), std::abs(gameObject.GetComponent<MetaCoreTransformComponent>().Scale.z),
0.5F 0.5F
}); });
const glm::vec3 objectMin = gameObject.Transform.Position - glm::vec3(extent); const glm::vec3 objectMin = gameObject.GetComponent<MetaCoreTransformComponent>().Position - glm::vec3(extent);
const glm::vec3 objectMax = gameObject.Transform.Position + glm::vec3(extent); const glm::vec3 objectMax = gameObject.GetComponent<MetaCoreTransformComponent>().Position + glm::vec3(extent);
if (!initialized) { if (!initialized) {
boundsMin = objectMin; boundsMin = objectMin;
@ -378,8 +378,8 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
return; return;
} }
MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject(); MetaCoreGameObject selectedObject = editorContext.GetSelectedGameObject();
if (selectedObject == nullptr) { if (!selectedObject) {
return; return;
} }
@ -392,7 +392,7 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
gizmoAspect, 0.05F, 500.0F gizmoAspect, 0.05F, 500.0F
); );
glm::mat4 worldMatrixMetaCore = MetaCoreBuildWorldTransformMatrix(editorContext.GetScene(), selectedObject->Id); glm::mat4 worldMatrixMetaCore = MetaCoreBuildWorldTransformMatrix(editorContext.GetScene(), selectedObject.GetId());
const glm::mat4 originalWorldMatrixMetaCore = worldMatrixMetaCore; const glm::mat4 originalWorldMatrixMetaCore = worldMatrixMetaCore;
ImGuizmo::SetOrthographic(false); ImGuizmo::SetOrthographic(false);
@ -429,12 +429,12 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
if (gizmoUsing && gizmoMatrix != originalWorldMatrixMetaCore) { if (gizmoUsing && gizmoMatrix != originalWorldMatrixMetaCore) {
glm::mat4 newLocalMatrix = gizmoMatrix; glm::mat4 newLocalMatrix = gizmoMatrix;
if (selectedObject->ParentId != 0) { if (selectedObject.GetParentId() != 0) {
const glm::mat4 parentWorldMatrixMetaCore = MetaCoreBuildWorldTransformMatrix(editorContext.GetScene(), selectedObject->ParentId); const glm::mat4 parentWorldMatrixMetaCore = MetaCoreBuildWorldTransformMatrix(editorContext.GetScene(), selectedObject.GetParentId());
newLocalMatrix = glm::inverse(parentWorldMatrixMetaCore) * gizmoMatrix; newLocalMatrix = glm::inverse(parentWorldMatrixMetaCore) * gizmoMatrix;
} }
MetaCoreApplyMatrixToTransform(newLocalMatrix, selectedObject->Transform); MetaCoreApplyMatrixToTransform(newLocalMatrix, selectedObject.GetComponent<MetaCoreTransformComponent>());
} }
HandleGizmoBeginUse(editorContext, gizmoUsing); HandleGizmoBeginUse(editorContext, gizmoUsing);
@ -545,13 +545,13 @@ void MetaCoreSceneInteractionService::DrawViewportToolbar(MetaCoreEditorContext&
if (selectedObjectIds.empty()) { if (selectedObjectIds.empty()) {
ImGui::TextDisabled("Selection: None"); ImGui::TextDisabled("Selection: None");
} else if (selectedObjectIds.size() == 1) { } else if (selectedObjectIds.size() == 1) {
const MetaCoreGameObject* activeObject = editorContext.GetSelectedGameObject(); const MetaCoreGameObject activeObject = editorContext.GetSelectedGameObject();
ImGui::TextDisabled( ImGui::TextDisabled(
"Selection: %s", "Selection: %s",
activeObject != nullptr ? activeObject->Name.c_str() : "<Missing>" activeObject != nullptr ? activeObject->Name.c_str() : "<Missing>"
); );
} else { } else {
const MetaCoreGameObject* activeObject = editorContext.GetSelectedGameObject(); const MetaCoreGameObject activeObject = editorContext.GetSelectedGameObject();
ImGui::TextDisabled( ImGui::TextDisabled(
"Selection: %zu objects | Active: %s", "Selection: %zu objects | Active: %s",
selectedObjectIds.size(), selectedObjectIds.size(),

View File

@ -17,7 +17,6 @@ namespace MetaCore {
class MetaCoreIModule; class MetaCoreIModule;
// 负责组合窗口、渲染器、ImGui 和编辑器模块,形成完整的编辑器程序。
class MetaCoreEditorApp { class MetaCoreEditorApp {
public: public:
MetaCoreEditorApp() = default; MetaCoreEditorApp() = default;

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "MetaCoreFoundation/MetaCoreAssetGuid.h" #include "MetaCoreFoundation/MetaCoreAssetGuid.h"
#include "MetaCoreFoundation/MetaCoreAssetTypes.h"
#include "MetaCoreFoundation/MetaCoreId.h" #include "MetaCoreFoundation/MetaCoreId.h"
#include "MetaCoreFoundation/MetaCorePackage.h" #include "MetaCoreFoundation/MetaCorePackage.h"
#include "MetaCoreFoundation/MetaCoreReflection.h" #include "MetaCoreFoundation/MetaCoreReflection.h"
@ -154,6 +155,45 @@ public:
[[nodiscard]] virtual bool ReimportAsset(const MetaCoreAssetGuid& assetGuid) = 0; [[nodiscard]] virtual bool ReimportAsset(const MetaCoreAssetGuid& assetGuid) = 0;
}; };
struct MetaCoreResolvedGeneratedAssetRecord {
MetaCoreAssetRecord SourceAsset{};
std::string GeneratedKind{};
std::size_t GeneratedIndex = 0;
};
class MetaCoreIAssetEditingService : public MetaCoreIEditorService {
public:
[[nodiscard]] virtual std::optional<MetaCoreModelAssetDocument> LoadModelAsset(
const MetaCoreAssetGuid& assetGuid
) const = 0;
[[nodiscard]] virtual std::optional<MetaCoreMeshAssetDocument> LoadMeshAsset(
const MetaCoreAssetGuid& meshGuid
) const = 0;
[[nodiscard]] virtual std::optional<MetaCoreMaterialAssetDocument> LoadMaterialAsset(
const MetaCoreAssetGuid& materialGuid
) const = 0;
[[nodiscard]] virtual std::optional<MetaCoreTextureAssetDocument> LoadTextureAsset(
const MetaCoreAssetGuid& textureGuid
) const = 0;
[[nodiscard]] virtual bool SaveModelImportSettings(
const MetaCoreAssetGuid& modelGuid,
const MetaCoreModelImportSettingsDocument& importSettings
) = 0;
[[nodiscard]] virtual bool SaveMaterialAsset(
const MetaCoreAssetGuid& materialGuid,
const MetaCoreMaterialAssetDocument& document
) = 0;
[[nodiscard]] virtual std::optional<MetaCoreResolvedGeneratedAssetRecord> ResolveGeneratedAsset(
const MetaCoreAssetGuid& assetGuid
) const = 0;
};
class MetaCoreICookService : public MetaCoreIEditorService { class MetaCoreICookService : public MetaCoreIEditorService {
public: public:
[[nodiscard]] virtual bool CookAsset(const MetaCoreAssetGuid& assetGuid) = 0; [[nodiscard]] virtual bool CookAsset(const MetaCoreAssetGuid& assetGuid) = 0;

View File

@ -9,7 +9,6 @@ namespace MetaCore {
class MetaCoreScene; class MetaCoreScene;
// 管理 Scene 视口交互:拾取选择与 Gizmo 变换提交。
class MetaCoreSceneInteractionService { class MetaCoreSceneInteractionService {
public: public:
void ResetFrameState(); void ResetFrameState();
@ -24,8 +23,6 @@ public:
void ApplyViewportSelection(MetaCoreEditorContext& editorContext, MetaCoreId pickedObjectId) const; void ApplyViewportSelection(MetaCoreEditorContext& editorContext, MetaCoreId pickedObjectId) const;
void HandleGizmoBeginUse(MetaCoreEditorContext& editorContext, bool gizmoUsing); void HandleGizmoBeginUse(MetaCoreEditorContext& editorContext, bool gizmoUsing);
void HandleGizmoEndUse(MetaCoreEditorContext& editorContext, bool gizmoUsing); void HandleGizmoEndUse(MetaCoreEditorContext& editorContext, bool gizmoUsing);
// Unity-style modular interactive features
void FocusSelectedObject(MetaCoreEditorContext& editorContext) const; void FocusSelectedObject(MetaCoreEditorContext& editorContext) const;
void FocusVisibleScene(MetaCoreEditorContext& editorContext) const; void FocusVisibleScene(MetaCoreEditorContext& editorContext) const;
void HandleGizmoManipulation(MetaCoreEditorContext& editorContext); void HandleGizmoManipulation(MetaCoreEditorContext& editorContext);

View File

@ -35,6 +35,9 @@ struct MetaCoreTextureAssetDocument {
MC_PROPERTY() MC_PROPERTY()
std::string Name{}; std::string Name{};
MC_PROPERTY()
std::string StableImportKey{};
MC_PROPERTY() MC_PROPERTY()
std::filesystem::path SourcePath{}; std::filesystem::path SourcePath{};
@ -66,6 +69,12 @@ struct MetaCoreMeshSubMeshDocument {
MC_PROPERTY() MC_PROPERTY()
std::int32_t MaterialSlotIndex = -1; std::int32_t MaterialSlotIndex = -1;
MC_PROPERTY()
std::uint32_t FirstIndex = 0;
MC_PROPERTY()
std::uint32_t IndexCount = 0;
}; };
MC_STRUCT() MC_STRUCT()
@ -78,6 +87,9 @@ struct MetaCoreMeshAssetDocument {
MC_PROPERTY() MC_PROPERTY()
std::string Name{}; std::string Name{};
MC_PROPERTY()
std::string StableImportKey{};
MC_PROPERTY() MC_PROPERTY()
std::int32_t VertexCount = 0; std::int32_t VertexCount = 0;
@ -110,6 +122,9 @@ struct MetaCoreMaterialAssetDocument {
MC_PROPERTY() MC_PROPERTY()
std::string Name{}; std::string Name{};
MC_PROPERTY()
std::string StableImportKey{};
MC_PROPERTY() MC_PROPERTY()
MetaCoreMaterialShaderModel ShaderModel = MetaCoreMaterialShaderModel::PbrMetalRough; MetaCoreMaterialShaderModel ShaderModel = MetaCoreMaterialShaderModel::PbrMetalRough;
@ -157,6 +172,76 @@ enum class MetaCoreModelAssetKind {
Generic Generic
}; };
MC_ENUM()
enum class MetaCoreModelImportUpAxis {
PreserveSource = 0,
YUp,
ZUp,
XUp
};
MC_ENUM()
enum class MetaCoreImportMessageSeverity {
Info = 0,
Warning,
Error
};
MC_ENUM()
enum class MetaCoreModelImportResult {
Success = 0,
Warning,
Error
};
MC_STRUCT()
struct MetaCoreModelImportSettingsDocument {
MC_GENERATED_BODY()
MC_PROPERTY()
float ScaleFactor = 1.0F;
MC_PROPERTY()
MetaCoreModelImportUpAxis UpAxis = MetaCoreModelImportUpAxis::PreserveSource;
MC_PROPERTY()
bool PreserveHierarchy = true;
MC_PROPERTY()
bool PreserveEmptyNodes = true;
MC_PROPERTY()
bool PreserveMaterialSlots = true;
};
MC_STRUCT()
struct MetaCoreImportMessageDocument {
MC_GENERATED_BODY()
MC_PROPERTY()
MetaCoreImportMessageSeverity Severity = MetaCoreImportMessageSeverity::Info;
MC_PROPERTY()
std::string Code{};
MC_PROPERTY()
std::string Message{};
};
MC_STRUCT()
struct MetaCoreModelImportReportDocument {
MC_GENERATED_BODY()
MC_PROPERTY()
MetaCoreModelImportResult Result = MetaCoreModelImportResult::Success;
MC_PROPERTY()
bool UsedFallback = false;
MC_PROPERTY()
std::vector<MetaCoreImportMessageDocument> Messages{};
};
MC_STRUCT() MC_STRUCT()
struct MetaCoreImportedGltfMeshDocument { struct MetaCoreImportedGltfMeshDocument {
MC_GENERATED_BODY() MC_GENERATED_BODY()
@ -206,6 +291,9 @@ struct MetaCoreImportedGltfNodeDocument {
MC_PROPERTY() MC_PROPERTY()
std::string Name{}; std::string Name{};
MC_PROPERTY()
std::string StableNodePath{};
MC_PROPERTY() MC_PROPERTY()
std::int32_t ParentIndex = -1; std::int32_t ParentIndex = -1;
@ -241,6 +329,12 @@ struct MetaCoreImportedGltfAssetDocument {
MC_PROPERTY() MC_PROPERTY()
std::string SourceFormat{}; std::string SourceFormat{};
MC_PROPERTY()
MetaCoreModelImportSettingsDocument ImportSettings{};
MC_PROPERTY()
MetaCoreModelImportReportDocument ImportReport{};
MC_PROPERTY() MC_PROPERTY()
std::vector<MetaCoreImportedGltfMeshDocument> Meshes{}; std::vector<MetaCoreImportedGltfMeshDocument> Meshes{};
@ -271,6 +365,9 @@ struct MetaCoreModelAssetDocument {
MC_PROPERTY() MC_PROPERTY()
std::string AssetType{}; std::string AssetType{};
MC_PROPERTY()
std::string RootDisplayName{};
MC_PROPERTY() MC_PROPERTY()
std::string ImporterId{}; std::string ImporterId{};
@ -286,6 +383,12 @@ struct MetaCoreModelAssetDocument {
MC_PROPERTY() MC_PROPERTY()
std::string SourceFormat{}; std::string SourceFormat{};
MC_PROPERTY()
MetaCoreModelImportSettingsDocument ImportSettings{};
MC_PROPERTY()
MetaCoreModelImportReportDocument ImportReport{};
MC_PROPERTY() MC_PROPERTY()
std::vector<MetaCoreImportedGltfMeshDocument> Meshes{}; std::vector<MetaCoreImportedGltfMeshDocument> Meshes{};

View File

@ -4,16 +4,16 @@
namespace MetaCore { namespace MetaCore {
// MetaCore 全局对象标识类型。 // Global object identifier used by MetaCore scene and runtime systems.
using MetaCoreId = std::uint64_t; using MetaCoreId = std::uint64_t;
// 负责为场景对象和运行时实体分配递增标识。 // Allocates monotonically increasing identifiers for scene objects and runtime entities.
class MetaCoreIdGenerator { class MetaCoreIdGenerator {
public: public:
// 生成一个新的全局唯一标识。 // Generates a new process-local identifier.
static MetaCoreId Generate(); static MetaCoreId Generate();
// 确保后续生成的标识严格大于给定值。 // Ensures future generated identifiers are greater than the provided value.
static void EnsureAbove(MetaCoreId value); static void EnsureAbove(MetaCoreId value);
}; };

View File

@ -5,30 +5,22 @@
namespace MetaCore { namespace MetaCore {
// 定义 MetaCore 控制台消息的级别。
enum class MetaCoreLogLevel { enum class MetaCoreLogLevel {
Info, Info,
Warning, Warning,
Error Error
}; };
// 表示一条会显示在编辑器控制台中的日志记录。
struct MetaCoreLogEntry { struct MetaCoreLogEntry {
MetaCoreLogLevel Level = MetaCoreLogLevel::Info; MetaCoreLogLevel Level = MetaCoreLogLevel::Info;
std::string Category; std::string Category;
std::string Message; std::string Message;
}; };
// 提供最基础的日志收集能力,供编辑器控制台和调试输出复用。
class MetaCoreLogService { class MetaCoreLogService {
public: public:
// 写入一条新的日志。
void AddEntry(MetaCoreLogLevel level, std::string category, std::string message); void AddEntry(MetaCoreLogLevel level, std::string category, std::string message);
// 返回当前收集到的全部日志。
[[nodiscard]] const std::vector<MetaCoreLogEntry>& GetEntries() const; [[nodiscard]] const std::vector<MetaCoreLogEntry>& GetEntries() const;
// 清空全部日志记录。
void Clear(); void Clear();
private: private:

View File

@ -189,7 +189,7 @@ bool MetaCoreWindow::Initialize(int width, int height, const std::string& title)
return true; return true;
} }
MetaCoreWindowTrace("metacore.window: configure backend"); MetaCoreWindowTrace("metacore.window: configure runtime backend");
MetaCoreConfigurePanda3D(title); MetaCoreConfigurePanda3D(title);
MetaCoreWindowTrace("metacore.window: open framework"); MetaCoreWindowTrace("metacore.window: open framework");
Impl_->Framework.open_framework(); Impl_->Framework.open_framework();
@ -200,7 +200,7 @@ bool MetaCoreWindow::Initialize(int width, int height, const std::string& title)
MetaCoreWindowTrace("metacore.window: set window size"); MetaCoreWindowTrace("metacore.window: set window size");
windowProperties.set_size(width, height); windowProperties.set_size(width, height);
MetaCoreWindowTrace("metacore.window: skip backend title due to ABI issues"); MetaCoreWindowTrace("metacore.window: skip runtime title handoff due to ABI issues");
// windowProperties.set_title(title); // This triggers bad_alloc in VS2022 Debug vs Panda3D VS2019 SDK // windowProperties.set_title(title); // This triggers bad_alloc in VS2022 Debug vs Panda3D VS2019 SDK
MetaCoreWindowTrace("metacore.window: set foreground"); MetaCoreWindowTrace("metacore.window: set foreground");

View File

@ -3,10 +3,10 @@
#include <glm/vec2.hpp> #include <glm/vec2.hpp>
#include <array> #include <array>
#include <cstddef>
namespace MetaCore { namespace MetaCore {
// 定义编辑器当前阶段需要识别的按键。
enum class MetaCoreInputKey : unsigned int { enum class MetaCoreInputKey : unsigned int {
LeftAlt = 0, LeftAlt = 0,
RightAlt, RightAlt,
@ -15,7 +15,6 @@ enum class MetaCoreInputKey : unsigned int {
Count Count
}; };
// 定义编辑器当前阶段需要识别的鼠标按键。
enum class MetaCoreMouseButton : unsigned int { enum class MetaCoreMouseButton : unsigned int {
Left = 0, Left = 0,
Right, Right,
@ -23,43 +22,20 @@ enum class MetaCoreMouseButton : unsigned int {
Count Count
}; };
// 统一封装窗口层采集后的输入状态,避免上层直接依赖 Win32 或 Panda3D 细节。
class MetaCoreInput { class MetaCoreInput {
public: public:
// 在每帧开始时重置瞬时状态并保存上一帧快照。
void BeginFrame(); void BeginFrame();
// 由窗口层写入按键状态。
void SetKeyState(MetaCoreInputKey key, bool isDown); void SetKeyState(MetaCoreInputKey key, bool isDown);
// 由窗口层写入鼠标按键状态。
void SetMouseButtonState(MetaCoreMouseButton button, bool isDown); void SetMouseButtonState(MetaCoreMouseButton button, bool isDown);
// 由窗口层写入当前鼠标位置。
void SetCursorPosition(const glm::vec2& cursorPosition); void SetCursorPosition(const glm::vec2& cursorPosition);
// 由窗口消息累加滚轮值,正值表示向前滚动。
void AddMouseWheelDelta(float delta); void AddMouseWheelDelta(float delta);
// 判断按键当前是否处于按下状态。
[[nodiscard]] bool IsKeyDown(MetaCoreInputKey key) const; [[nodiscard]] bool IsKeyDown(MetaCoreInputKey key) const;
// 判断按键是否在本帧刚刚按下。
[[nodiscard]] bool WasKeyPressed(MetaCoreInputKey key) const; [[nodiscard]] bool WasKeyPressed(MetaCoreInputKey key) const;
// 判断鼠标按键当前是否按下。
[[nodiscard]] bool IsMouseButtonDown(MetaCoreMouseButton button) const; [[nodiscard]] bool IsMouseButtonDown(MetaCoreMouseButton button) const;
// 判断鼠标按键是否在本帧刚刚按下。
[[nodiscard]] bool WasMouseButtonPressed(MetaCoreMouseButton button) const; [[nodiscard]] bool WasMouseButtonPressed(MetaCoreMouseButton button) const;
// 返回当前帧累计的滚轮值。
[[nodiscard]] float GetMouseWheelDelta() const; [[nodiscard]] float GetMouseWheelDelta() const;
// 返回当前鼠标位置。
[[nodiscard]] const glm::vec2& GetCursorPosition() const; [[nodiscard]] const glm::vec2& GetCursorPosition() const;
// 返回相对上一帧的鼠标位移。
[[nodiscard]] const glm::vec2& GetCursorDelta() const; [[nodiscard]] const glm::vec2& GetCursorDelta() const;
private: private:

View File

@ -12,10 +12,8 @@ namespace MetaCore {
class MetaCoreRenderDevice; class MetaCoreRenderDevice;
// 定义宿主窗口层向上暴露的原生消息回调,用于把 Win32 消息转交给 ImGui。
using MetaCoreNativeWindowMessageHandler = std::function<bool(void*, unsigned int, std::uintptr_t, std::intptr_t)>; using MetaCoreNativeWindowMessageHandler = std::function<bool(void*, unsigned int, std::uintptr_t, std::intptr_t)>;
// 封装 Panda3D 窗口、时间步进与基础输入采集。
class MetaCoreWindow { class MetaCoreWindow {
public: public:
class MetaCoreWindowImpl; class MetaCoreWindowImpl;
@ -23,50 +21,25 @@ public:
MetaCoreWindow(); MetaCoreWindow();
~MetaCoreWindow(); ~MetaCoreWindow();
// 创建 Panda3D 主窗口并准备输入采集。
bool Initialize(int width, int height, const std::string& title); bool Initialize(int width, int height, const std::string& title);
// 释放窗口与 Panda3D 宿主资源。
void Shutdown(); void Shutdown();
// 处理窗口事件、更新时间步进并刷新输入状态。
void BeginFrame(); void BeginFrame();
// 为未来扩展保留的帧结束入口。
void EndFrame(); void EndFrame();
// 返回窗口是否请求退出。
[[nodiscard]] bool ShouldClose() const; [[nodiscard]] bool ShouldClose() const;
// 主动请求窗口退出。
void RequestClose(); void RequestClose();
// 返回原生窗口句柄,供 ImGui Win32 backend 初始化使用。
[[nodiscard]] void* GetNativeWindowHandle() const; [[nodiscard]] void* GetNativeWindowHandle() const;
// 返回 PandaFramework 原生对象指针,仅供底层桥接层使用。
[[nodiscard]] void* GetNativeFrameworkHandle() const; [[nodiscard]] void* GetNativeFrameworkHandle() const;
// 返回 WindowFramework 原生对象指针,仅供底层桥接层使用。
[[nodiscard]] void* GetNativeWindowFrameworkHandle() const; [[nodiscard]] void* GetNativeWindowFrameworkHandle() const;
// 返回 GraphicsWindow 原生对象指针,仅供底层桥接层使用。
[[nodiscard]] void* GetNativeGraphicsWindowHandle() const; [[nodiscard]] void* GetNativeGraphicsWindowHandle() const;
// 返回当前输入对象。
[[nodiscard]] MetaCoreInput& GetInput(); [[nodiscard]] MetaCoreInput& GetInput();
[[nodiscard]] const MetaCoreInput& GetInput() const; [[nodiscard]] const MetaCoreInput& GetInput() const;
// 返回当前帧耗时,单位为秒。
[[nodiscard]] float GetDeltaSeconds() const; [[nodiscard]] float GetDeltaSeconds() const;
// 返回当前窗口尺寸。
[[nodiscard]] std::pair<int, int> GetWindowSize() const; [[nodiscard]] std::pair<int, int> GetWindowSize() const;
// 返回当前帧缓冲尺寸。
[[nodiscard]] std::pair<int, int> GetFramebufferSize() const; [[nodiscard]] std::pair<int, int> GetFramebufferSize() const;
// 注册原生消息回调。
void SetNativeWindowMessageHandler(MetaCoreNativeWindowMessageHandler handler); void SetNativeWindowMessageHandler(MetaCoreNativeWindowMessageHandler handler);
private: private:

View File

@ -63,12 +63,12 @@ bool MetaCoreEditorViewportRenderer::Initialize(MetaCoreRenderDevice& renderDevi
MetaCoreTrace("metacore.render: viewport init enter"); MetaCoreTrace("metacore.render: viewport init enter");
Shutdown(); Shutdown();
MetaCoreTrace("metacore.render: viewport scene bridge begin"); MetaCoreTrace("metacore.render: viewport scene runtime bind begin");
if (!SceneBridge_.Initialize(renderDevice)) { if (!SceneBridge_.Initialize(renderDevice)) {
MetaCoreTrace("metacore.render: viewport scene bridge failed"); MetaCoreTrace("metacore.render: viewport scene runtime bind failed");
return false; return false;
} }
MetaCoreTrace("metacore.render: viewport scene bridge done"); MetaCoreTrace("metacore.render: viewport scene runtime ready");
RenderDevice_ = &renderDevice; RenderDevice_ = &renderDevice;
ViewportRect_ = MetaCoreViewportRect{}; ViewportRect_ = MetaCoreViewportRect{};
@ -108,20 +108,13 @@ void MetaCoreEditorViewportRenderer::RenderSceneToViewport(const MetaCoreScene&
RenderDevice_->SetSceneViewportRect(ViewportRect_); RenderDevice_->SetSceneViewportRect(ViewportRect_);
SceneBridge_.ApplySceneView(sceneView); SceneBridge_.ApplySceneView(sceneView);
#if defined(_DEBUG) #if defined(_DEBUG)
if (!DebugSceneSyncOverrideEnabled_) { static bool loggedDebugRenderableSync = false;
(void)scene;
static bool loggedDebugSyncSkip = false;
SceneSyncDegraded_ = true;
if (!loggedDebugSyncSkip) {
MetaCoreTrace("metacore.render: skipping debug scene sync due to backend runtime instability");
loggedDebugSyncSkip = true;
}
return;
}
#else
#endif
SceneSyncDegraded_ = false; SceneSyncDegraded_ = false;
SceneBridge_.SyncScene(scene); if (!loggedDebugRenderableSync) {
MetaCoreTrace("metacore.render: debug scene sync active; syncing renderable objects to the MetaCore viewport");
loggedDebugRenderableSync = true;
}
SceneBridge_.SyncScene(scene, true);
if (SceneBridge_.HasRuntimeSyncFailure()) { if (SceneBridge_.HasRuntimeSyncFailure()) {
SceneSyncDegraded_ = true; SceneSyncDegraded_ = true;
LastSceneSyncFailure_ = SceneBridge_.GetLastRuntimeSyncFailure(); LastSceneSyncFailure_ = SceneBridge_.GetLastRuntimeSyncFailure();
@ -133,6 +126,22 @@ void MetaCoreEditorViewportRenderer::RenderSceneToViewport(const MetaCoreScene&
LastSceneSyncFailure_.clear(); LastSceneSyncFailure_.clear();
HasLoggedSceneSyncFailure_ = false; HasLoggedSceneSyncFailure_ = false;
} }
return;
#else
SceneSyncDegraded_ = false;
SceneBridge_.SyncScene(scene, false);
if (SceneBridge_.HasRuntimeSyncFailure()) {
SceneSyncDegraded_ = true;
LastSceneSyncFailure_ = SceneBridge_.GetLastRuntimeSyncFailure();
if (!HasLoggedSceneSyncFailure_) {
MetaCoreTrace(("metacore.render: scene sync degraded after runtime failure: " + LastSceneSyncFailure_).c_str());
HasLoggedSceneSyncFailure_ = true;
}
} else {
LastSceneSyncFailure_.clear();
HasLoggedSceneSyncFailure_ = false;
}
#endif
} }
void MetaCoreEditorViewportRenderer::SetProjectRootPath(const std::filesystem::path& projectRootPath) { void MetaCoreEditorViewportRenderer::SetProjectRootPath(const std::filesystem::path& projectRootPath) {

File diff suppressed because it is too large Load Diff

View File

@ -3,33 +3,23 @@
#include "MetaCoreRender/MetaCorePandaSceneBridge.h" #include "MetaCoreRender/MetaCorePandaSceneBridge.h"
#include "MetaCoreRender/MetaCoreRenderTypes.h" #include "MetaCoreRender/MetaCoreRenderTypes.h"
#include <filesystem>
#include <string>
namespace MetaCore { namespace MetaCore {
class MetaCoreRenderDevice; class MetaCoreRenderDevice;
class MetaCoreScene; class MetaCoreScene;
// 管理编辑器中央场景面板与 Panda3D DisplayRegion 之间的同步。
class MetaCoreEditorViewportRenderer { class MetaCoreEditorViewportRenderer {
public: public:
// 初始化 Panda3D 场景桥接与显示区域驱动。
bool Initialize(MetaCoreRenderDevice& renderDevice); bool Initialize(MetaCoreRenderDevice& renderDevice);
// 释放桥接层与内部缓存。
void Shutdown(); void Shutdown();
// 写入当前 Scene 面板像素矩形。
void SetViewportRect(const MetaCoreViewportRect& viewportRect); void SetViewportRect(const MetaCoreViewportRect& viewportRect);
// 将场景状态同步到 Panda3D并更新编辑器相机。
void RenderSceneToViewport(const MetaCoreScene& scene, const MetaCoreSceneView& sceneView); void RenderSceneToViewport(const MetaCoreScene& scene, const MetaCoreSceneView& sceneView);
// 设置当前项目根目录,用于模型加载时解析绝对路径。
void SetProjectRootPath(const std::filesystem::path& projectRootPath); void SetProjectRootPath(const std::filesystem::path& projectRootPath);
// 返回 Panda3D 中对象当前真实使用的世界矩阵。
[[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const; [[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const;
// 当前是否处于 Debug 兼容降级模式。
[[nodiscard]] bool IsSceneSyncDegraded() const; [[nodiscard]] bool IsSceneSyncDegraded() const;
[[nodiscard]] bool IsDebugSceneSyncOverrideEnabled() const; [[nodiscard]] bool IsDebugSceneSyncOverrideEnabled() const;
[[nodiscard]] const std::string& GetLastSceneSyncFailure() const; [[nodiscard]] const std::string& GetLastSceneSyncFailure() const;

View File

@ -6,6 +6,7 @@
#include <filesystem> #include <filesystem>
#include <memory> #include <memory>
#include <string>
namespace MetaCore { namespace MetaCore {
@ -13,29 +14,17 @@ class MetaCoreRenderDevice;
class MetaCoreScene; class MetaCoreScene;
struct MetaCoreSceneView; struct MetaCoreSceneView;
// 负责在 MetaCoreScene 与 Panda3D NodePath 场景之间建立同步关系。
class MetaCorePandaSceneBridge { class MetaCorePandaSceneBridge {
public: public:
MetaCorePandaSceneBridge(); MetaCorePandaSceneBridge();
~MetaCorePandaSceneBridge(); ~MetaCorePandaSceneBridge();
// 准备 Panda3D 场景根节点、调试网格与编辑器相机。
bool Initialize(MetaCoreRenderDevice& renderDevice); bool Initialize(MetaCoreRenderDevice& renderDevice);
// 释放桥接层创建的 Panda3D 节点。
void Shutdown(); void Shutdown();
// 设置当前项目根目录,用于解析资源文件的绝对路径。
// 必须在项目加载后调用,否则模型加载会失败退化为立方体。
void SetProjectRootPath(const std::filesystem::path& projectRootPath); void SetProjectRootPath(const std::filesystem::path& projectRootPath);
void SyncScene(const MetaCoreScene& scene, bool compatibilityMeshOnly = false);
// 将 MetaCoreScene 当前状态同步到 Panda3D 场景图。
void SyncScene(const MetaCoreScene& scene);
// 更新编辑器相机与当前选中对象高亮状态。
void ApplySceneView(const MetaCoreSceneView& sceneView); void ApplySceneView(const MetaCoreSceneView& sceneView);
// 返回 Panda3D 场景中对象当前真实使用的世界矩阵。
[[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const; [[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const;
[[nodiscard]] bool HasRuntimeSyncFailure() const; [[nodiscard]] bool HasRuntimeSyncFailure() const;
[[nodiscard]] const std::string& GetLastRuntimeSyncFailure() const; [[nodiscard]] const std::string& GetLastRuntimeSyncFailure() const;

View File

@ -12,37 +12,20 @@ class MetaCoreEditorViewportRenderer;
class MetaCorePandaSceneBridge; class MetaCorePandaSceneBridge;
class MetaCoreWindow; class MetaCoreWindow;
// 封装 Panda3D 图形引擎、主窗口和中央场景显示区域。
class MetaCoreRenderDevice { class MetaCoreRenderDevice {
public: public:
MetaCoreRenderDevice(); MetaCoreRenderDevice();
~MetaCoreRenderDevice(); ~MetaCoreRenderDevice();
// 绑定 Panda3D 窗口与图形引擎,准备后续场景渲染。
bool Initialize(MetaCoreWindow& window); bool Initialize(MetaCoreWindow& window);
// 释放 Panda3D 渲染相关资源。
void Shutdown(); void Shutdown();
// 提交 Panda3D 场景绘制。
void RenderFrame() const; void RenderFrame() const;
// 提交当前帧翻转,确保 ImGui 叠加结果显示出来。
void PresentFrame() const; void PresentFrame() const;
// 根据 ImGui 场景面板像素矩形更新 Panda3D DisplayRegion。
void SetSceneViewportRect(const MetaCoreViewportRect& viewportRect); void SetSceneViewportRect(const MetaCoreViewportRect& viewportRect);
// 返回 WindowFramework 原生对象指针,仅供 Panda3D 桥接层使用。
[[nodiscard]] void* GetNativeWindowFrameworkHandle() const; [[nodiscard]] void* GetNativeWindowFrameworkHandle() const;
// 返回场景根节点原生对象指针,仅供 Panda3D 桥接层使用。
[[nodiscard]] void* GetNativeSceneRootHandle() const; [[nodiscard]] void* GetNativeSceneRootHandle() const;
// 返回编辑器相机节点原生对象指针,仅供 Panda3D 桥接层使用。
[[nodiscard]] void* GetNativeEditorCameraHandle() const; [[nodiscard]] void* GetNativeEditorCameraHandle() const;
// 返回 Panda3D 当前真实使用的编辑器相机视图矩阵和投影矩阵。
[[nodiscard]] bool TryGetEditorCameraMatrices(glm::mat4& viewMatrix, glm::mat4& projectionMatrix) const; [[nodiscard]] bool TryGetEditorCameraMatrices(glm::mat4& viewMatrix, glm::mat4& projectionMatrix) const;
private: private:

View File

@ -6,7 +6,6 @@
namespace MetaCore { namespace MetaCore {
// 描述场景在宿主窗口中的像素矩形区域。
struct MetaCoreViewportRect { struct MetaCoreViewportRect {
float Left = 0.0F; float Left = 0.0F;
float Top = 0.0F; float Top = 0.0F;
@ -14,7 +13,6 @@ struct MetaCoreViewportRect {
float Height = 1.0F; float Height = 1.0F;
}; };
// 描述一次 Panda3D 场景同步所需的相机与选择参数。
struct MetaCoreSceneView { struct MetaCoreSceneView {
glm::vec3 CameraPosition{0.0F, 2.2F, 6.5F}; glm::vec3 CameraPosition{0.0F, 2.2F, 6.5F};
glm::vec3 CameraTarget{0.0F, 0.7F, 0.0F}; glm::vec3 CameraTarget{0.0F, 0.7F, 0.0F};
@ -23,7 +21,6 @@ struct MetaCoreSceneView {
MetaCoreId SelectedObjectId = 0; MetaCoreId SelectedObjectId = 0;
}; };
// 定义二进制网格顶点的内存布局必须与导入器MetaCoreBuiltinCoreServicesModule中的定义严格一致。
struct MetaCoreMeshVertex { struct MetaCoreMeshVertex {
float px, py, pz; float px, py, pz;
float nx, ny, nz; float nx, ny, nz;

View File

@ -31,8 +31,8 @@ void MetaCoreRuntimeDataDispatcher::ApplyUpdates(const std::vector<MetaCoreRunti
continue; continue;
} }
MetaCoreGameObject* gameObject = Scene_.FindGameObject(bindingDefinition.TargetObjectId); MetaCoreGameObject gameObject = Scene_.FindGameObject(bindingDefinition.TargetObjectId);
if (gameObject == nullptr) { if (!gameObject) {
MarkBindingFault(bindingDefinition.BindingId, "Target object missing"); MarkBindingFault(bindingDefinition.BindingId, "Target object missing");
continue; continue;
} }
@ -41,31 +41,31 @@ void MetaCoreRuntimeDataDispatcher::ApplyUpdates(const std::vector<MetaCoreRunti
switch (bindingDefinition.Target) { switch (bindingDefinition.Target) {
case MetaCoreRuntimeBindingTarget::TransformPosition: case MetaCoreRuntimeBindingTarget::TransformPosition:
if (update.Value.Type == MetaCoreRuntimeValueType::Vec3) { if (update.Value.Type == MetaCoreRuntimeValueType::Vec3) {
gameObject->Transform.Position = update.Value.Vec3Value; gameObject.GetComponent<MetaCoreTransformComponent>().Position = update.Value.Vec3Value;
applied = true; applied = true;
} }
break; break;
case MetaCoreRuntimeBindingTarget::MeshRendererVisible: case MetaCoreRuntimeBindingTarget::MeshRendererVisible:
if (update.Value.Type == MetaCoreRuntimeValueType::Bool && gameObject->MeshRenderer.has_value()) { if (update.Value.Type == MetaCoreRuntimeValueType::Bool && gameObject.HasComponent<MetaCoreMeshRendererComponent>()) {
gameObject->MeshRenderer->Visible = update.Value.BoolValue; gameObject.GetComponent<MetaCoreMeshRendererComponent>().Visible = update.Value.BoolValue;
applied = true; applied = true;
} }
break; break;
case MetaCoreRuntimeBindingTarget::MeshRendererBaseColor: case MetaCoreRuntimeBindingTarget::MeshRendererBaseColor:
if (update.Value.Type == MetaCoreRuntimeValueType::Vec3 && gameObject->MeshRenderer.has_value()) { if (update.Value.Type == MetaCoreRuntimeValueType::Vec3 && gameObject.HasComponent<MetaCoreMeshRendererComponent>()) {
gameObject->MeshRenderer->BaseColor = update.Value.Vec3Value; gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor = update.Value.Vec3Value;
applied = true; applied = true;
} }
break; break;
case MetaCoreRuntimeBindingTarget::LightIntensity: case MetaCoreRuntimeBindingTarget::LightIntensity:
if (update.Value.Type == MetaCoreRuntimeValueType::Double && gameObject->Light.has_value()) { if (update.Value.Type == MetaCoreRuntimeValueType::Double && gameObject.HasComponent<MetaCoreLightComponent>()) {
gameObject->Light->Intensity = static_cast<float>(update.Value.DoubleValue); gameObject.GetComponent<MetaCoreLightComponent>().Intensity = static_cast<float>(update.Value.DoubleValue);
applied = true; applied = true;
} }
break; break;
case MetaCoreRuntimeBindingTarget::LightColor: case MetaCoreRuntimeBindingTarget::LightColor:
if (update.Value.Type == MetaCoreRuntimeValueType::Vec3 && gameObject->Light.has_value()) { if (update.Value.Type == MetaCoreRuntimeValueType::Vec3 && gameObject.HasComponent<MetaCoreLightComponent>()) {
gameObject->Light->Color = update.Value.Vec3Value; gameObject.GetComponent<MetaCoreLightComponent>().Color = update.Value.Vec3Value;
applied = true; applied = true;
} }
break; break;

View File

@ -41,50 +41,63 @@ std::string MetaCoreBuildCopyName(const std::string& name) {
} // namespace } // namespace
MetaCoreGameObject& MetaCoreScene::CreateGameObject(const std::string& name, MetaCoreId parentId) { MetaCoreGameObject MetaCoreScene::CreateGameObject(const std::string& name, MetaCoreId parentId) {
GameObjects_.push_back(MetaCoreGameObject{ return CreateGameObjectWithId(MetaCoreIdGenerator::Generate(), name, parentId);
MetaCoreIdGenerator::Generate(),
parentId,
name,
MetaCoreTransformComponent{},
std::nullopt,
std::nullopt,
std::nullopt
});
return GameObjects_.back();
} }
MetaCoreGameObject* MetaCoreScene::FindGameObject(MetaCoreId objectId) { MetaCoreGameObject MetaCoreScene::CreateGameObjectWithId(MetaCoreId id, const std::string& name, MetaCoreId parentId) {
for (MetaCoreGameObject& object : GameObjects_) { entt::entity entity = Registry_.create();
if (object.Id == objectId) { IdToEntity_[id] = entity;
return &object;
} MetaCoreGameObject obj(entity, this);
obj.AddComponent<MetaCoreIdComponent>().Id = id;
obj.AddComponent<MetaCoreNameComponent>().Name = name;
obj.AddComponent<MetaCoreHierarchyComponent>().ParentId = parentId;
obj.AddComponent<MetaCoreTransformComponent>();
return obj;
}
MetaCoreGameObject MetaCoreScene::FindGameObject(MetaCoreId objectId) {
auto it = IdToEntity_.find(objectId);
if (it != IdToEntity_.end()) {
return MetaCoreGameObject(it->second, this);
} }
return nullptr; return MetaCoreGameObject();
} }
const MetaCoreGameObject* MetaCoreScene::FindGameObject(MetaCoreId objectId) const { MetaCoreGameObject MetaCoreScene::FindGameObject(MetaCoreId objectId) const {
for (const MetaCoreGameObject& object : GameObjects_) { auto it = IdToEntity_.find(objectId);
if (object.Id == objectId) { if (it != IdToEntity_.end()) {
return &object; return MetaCoreGameObject(it->second, const_cast<MetaCoreScene*>(this));
}
} }
return nullptr; return MetaCoreGameObject();
} }
std::vector<MetaCoreGameObject>& MetaCoreScene::GetGameObjects() { std::vector<MetaCoreGameObject> MetaCoreScene::GetGameObjects() {
return GameObjects_; std::vector<MetaCoreGameObject> objects;
objects.reserve(IdToEntity_.size());
for (auto [id, entity] : IdToEntity_) {
objects.emplace_back(entity, this);
}
return objects;
} }
const std::vector<MetaCoreGameObject>& MetaCoreScene::GetGameObjects() const { std::vector<MetaCoreGameObject> MetaCoreScene::GetGameObjects() const {
return GameObjects_; std::vector<MetaCoreGameObject> objects;
objects.reserve(IdToEntity_.size());
for (auto [id, entity] : IdToEntity_) {
objects.emplace_back(entity, const_cast<MetaCoreScene*>(this));
}
return objects;
} }
std::vector<MetaCoreId> MetaCoreScene::GetRootObjectIds() const { std::vector<MetaCoreId> MetaCoreScene::GetRootObjectIds() const {
std::vector<MetaCoreId> rootIds; std::vector<MetaCoreId> rootIds;
for (const MetaCoreGameObject& object : GameObjects_) { auto view = Registry_.view<MetaCoreIdComponent, MetaCoreHierarchyComponent>();
if (object.ParentId == 0) { for (auto entity : view) {
rootIds.push_back(object.Id); if (view.get<MetaCoreHierarchyComponent>(entity).ParentId == 0) {
rootIds.push_back(view.get<MetaCoreIdComponent>(entity).Id);
} }
} }
return rootIds; return rootIds;
@ -92,16 +105,17 @@ std::vector<MetaCoreId> MetaCoreScene::GetRootObjectIds() const {
std::vector<MetaCoreId> MetaCoreScene::GetChildrenOf(MetaCoreId parentId) const { std::vector<MetaCoreId> MetaCoreScene::GetChildrenOf(MetaCoreId parentId) const {
std::vector<MetaCoreId> childIds; std::vector<MetaCoreId> childIds;
for (const MetaCoreGameObject& object : GameObjects_) { auto view = Registry_.view<MetaCoreIdComponent, MetaCoreHierarchyComponent>();
if (object.ParentId == parentId) { for (auto entity : view) {
childIds.push_back(object.Id); if (view.get<MetaCoreHierarchyComponent>(entity).ParentId == parentId) {
childIds.push_back(view.get<MetaCoreIdComponent>(entity).Id);
} }
} }
return childIds; return childIds;
} }
void MetaCoreScene::CollectPreorder(MetaCoreId objectId, std::vector<MetaCoreId>& output) const { void MetaCoreScene::CollectPreorder(MetaCoreId objectId, std::vector<MetaCoreId>& output) const {
if (FindGameObject(objectId) == nullptr) { if (!FindGameObject(objectId)) {
return; return;
} }
@ -130,30 +144,30 @@ bool MetaCoreScene::IsDescendantOf(MetaCoreId objectId, MetaCoreId ancestorId) c
return false; return false;
} }
const MetaCoreGameObject* currentObject = FindGameObject(objectId); MetaCoreGameObject currentObject = FindGameObject(objectId);
while (currentObject != nullptr && currentObject->ParentId != 0) { while (currentObject && currentObject.GetParentId() != 0) {
if (currentObject->ParentId == ancestorId) { if (currentObject.GetParentId() == ancestorId) {
return true; return true;
} }
currentObject = FindGameObject(currentObject->ParentId); currentObject = FindGameObject(currentObject.GetParentId());
} }
return false; return false;
} }
bool MetaCoreScene::RenameGameObject(MetaCoreId objectId, const std::string& name) { bool MetaCoreScene::RenameGameObject(MetaCoreId objectId, const std::string& name) {
MetaCoreGameObject* object = FindGameObject(objectId); MetaCoreGameObject object = FindGameObject(objectId);
if (object == nullptr || name.empty()) { if (!object || name.empty()) {
return false; return false;
} }
object->Name = name; object.GetName() = name;
return true; return true;
} }
std::vector<MetaCoreId> MetaCoreScene::DeleteGameObjects(const std::vector<MetaCoreId>& objectIds) { std::vector<MetaCoreId> MetaCoreScene::DeleteGameObjects(const std::vector<MetaCoreId>& objectIds) {
std::unordered_set<MetaCoreId> selectedIds; std::unordered_set<MetaCoreId> selectedIds;
for (MetaCoreId objectId : objectIds) { for (MetaCoreId objectId : objectIds) {
if (FindGameObject(objectId) != nullptr) { if (FindGameObject(objectId)) {
selectedIds.insert(objectId); selectedIds.insert(objectId);
} }
} }
@ -165,14 +179,14 @@ std::vector<MetaCoreId> MetaCoreScene::DeleteGameObjects(const std::vector<MetaC
std::vector<MetaCoreId> rootIds; std::vector<MetaCoreId> rootIds;
rootIds.reserve(selectedIds.size()); rootIds.reserve(selectedIds.size());
for (MetaCoreId objectId : selectedIds) { for (MetaCoreId objectId : selectedIds) {
const MetaCoreGameObject* object = FindGameObject(objectId); MetaCoreGameObject object = FindGameObject(objectId);
bool hasSelectedAncestor = false; bool hasSelectedAncestor = false;
while (object != nullptr && object->ParentId != 0) { while (object && object.GetParentId() != 0) {
if (selectedIds.contains(object->ParentId)) { if (selectedIds.contains(object.GetParentId())) {
hasSelectedAncestor = true; hasSelectedAncestor = true;
break; break;
} }
object = FindGameObject(object->ParentId); object = FindGameObject(object.GetParentId());
} }
if (!hasSelectedAncestor) { if (!hasSelectedAncestor) {
rootIds.push_back(objectId); rootIds.push_back(objectId);
@ -188,20 +202,23 @@ std::vector<MetaCoreId> MetaCoreScene::DeleteGameObjects(const std::vector<MetaC
std::vector<MetaCoreId> deletedIds; std::vector<MetaCoreId> deletedIds;
deletedIds.reserve(idsToDelete.size()); deletedIds.reserve(idsToDelete.size());
std::erase_if(GameObjects_, [&](const MetaCoreGameObject& object) {
if (!idsToDelete.contains(object.Id)) { for (MetaCoreId id : idsToDelete) {
return false; auto it = IdToEntity_.find(id);
if (it != IdToEntity_.end()) {
Registry_.destroy(it->second);
IdToEntity_.erase(it);
deletedIds.push_back(id);
} }
deletedIds.push_back(object.Id); }
return true;
});
return deletedIds; return deletedIds;
} }
std::vector<MetaCoreId> MetaCoreScene::DuplicateGameObjects(const std::vector<MetaCoreId>& objectIds) { std::vector<MetaCoreId> MetaCoreScene::DuplicateGameObjects(const std::vector<MetaCoreId>& objectIds) {
std::unordered_set<MetaCoreId> selectedIds; std::unordered_set<MetaCoreId> selectedIds;
for (MetaCoreId objectId : objectIds) { for (MetaCoreId objectId : objectIds) {
if (FindGameObject(objectId) != nullptr) { if (FindGameObject(objectId)) {
selectedIds.insert(objectId); selectedIds.insert(objectId);
} }
} }
@ -217,14 +234,14 @@ std::vector<MetaCoreId> MetaCoreScene::DuplicateGameObjects(const std::vector<Me
continue; continue;
} }
const MetaCoreGameObject* object = FindGameObject(objectId); MetaCoreGameObject object = FindGameObject(objectId);
bool hasSelectedAncestor = false; bool hasSelectedAncestor = false;
while (object != nullptr && object->ParentId != 0) { while (object && object.GetParentId() != 0) {
if (selectedIds.contains(object->ParentId)) { if (selectedIds.contains(object.GetParentId())) {
hasSelectedAncestor = true; hasSelectedAncestor = true;
break; break;
} }
object = FindGameObject(object->ParentId); object = FindGameObject(object.GetParentId());
} }
if (!hasSelectedAncestor) { if (!hasSelectedAncestor) {
@ -232,52 +249,61 @@ std::vector<MetaCoreId> MetaCoreScene::DuplicateGameObjects(const std::vector<Me
} }
} }
std::vector<MetaCoreGameObject> clonedObjects;
std::vector<MetaCoreId> duplicatedRootIds; std::vector<MetaCoreId> duplicatedRootIds;
const auto cloneSubtree = [&](const auto& self, MetaCoreId sourceId, MetaCoreId duplicatedParentId, bool isRoot) -> void { const auto cloneSubtree = [&](const auto& self, MetaCoreId sourceId, MetaCoreId duplicatedParentId, bool isRoot) -> void {
const MetaCoreGameObject* sourceObject = FindGameObject(sourceId); MetaCoreGameObject sourceObject = FindGameObject(sourceId);
if (sourceObject == nullptr) { if (!sourceObject) {
return; return;
} }
MetaCoreGameObject clonedObject = *sourceObject; MetaCoreId newId = MetaCoreIdGenerator::Generate();
clonedObject.Id = MetaCoreIdGenerator::Generate(); std::string newName = isRoot ? MetaCoreBuildCopyName(sourceObject.GetName()) : sourceObject.GetName();
clonedObject.ParentId = duplicatedParentId; MetaCoreGameObject clonedObject = CreateGameObjectWithId(newId, newName, duplicatedParentId);
clonedObject.GetComponent<MetaCoreTransformComponent>() = sourceObject.GetComponent<MetaCoreTransformComponent>();
if (sourceObject.HasComponent<MetaCoreCameraComponent>())
clonedObject.AddComponent<MetaCoreCameraComponent>(sourceObject.GetComponent<MetaCoreCameraComponent>());
if (sourceObject.HasComponent<MetaCoreMeshRendererComponent>())
clonedObject.AddComponent<MetaCoreMeshRendererComponent>(sourceObject.GetComponent<MetaCoreMeshRendererComponent>());
if (sourceObject.HasComponent<MetaCoreLightComponent>())
clonedObject.AddComponent<MetaCoreLightComponent>(sourceObject.GetComponent<MetaCoreLightComponent>());
if (sourceObject.HasComponent<MetaCorePrefabInstanceMetadata>())
clonedObject.AddComponent<MetaCorePrefabInstanceMetadata>(sourceObject.GetComponent<MetaCorePrefabInstanceMetadata>());
if (isRoot) { if (isRoot) {
clonedObject.Name = MetaCoreBuildCopyName(sourceObject->Name); duplicatedRootIds.push_back(newId);
duplicatedRootIds.push_back(clonedObject.Id);
} }
clonedObjects.push_back(std::move(clonedObject));
const MetaCoreId newObjectId = clonedObjects.back().Id;
for (MetaCoreId childId : GetChildrenOf(sourceId)) { for (MetaCoreId childId : GetChildrenOf(sourceId)) {
self(self, childId, newObjectId, false); self(self, childId, newId, false);
} }
}; };
for (MetaCoreId rootId : rootIds) { for (MetaCoreId rootId : rootIds) {
const MetaCoreGameObject* rootObject = FindGameObject(rootId); MetaCoreGameObject rootObject = FindGameObject(rootId);
if (rootObject == nullptr) { if (!rootObject) {
continue; continue;
} }
cloneSubtree(cloneSubtree, rootId, rootObject->ParentId, true); cloneSubtree(cloneSubtree, rootId, rootObject.GetParentId(), true);
} }
GameObjects_.insert(GameObjects_.end(), clonedObjects.begin(), clonedObjects.end());
return duplicatedRootIds; return duplicatedRootIds;
} }
bool MetaCoreScene::ReparentGameObjects(const std::vector<MetaCoreId>& objectIds, MetaCoreId newParentId, bool keepWorldTransform) { bool MetaCoreScene::ReparentGameObjects(const std::vector<MetaCoreId>& objectIds, MetaCoreId newParentId, bool keepWorldTransform) {
if (newParentId != 0 && FindGameObject(newParentId) == nullptr) { if (newParentId != 0 && !FindGameObject(newParentId)) {
return false; return false;
} }
std::unordered_set<MetaCoreId> selectedIds; std::unordered_set<MetaCoreId> selectedIds;
for (MetaCoreId objectId : objectIds) { for (MetaCoreId objectId : objectIds) {
if (FindGameObject(objectId) != nullptr) { if (FindGameObject(objectId)) {
selectedIds.insert(objectId); selectedIds.insert(objectId);
} }
} }
@ -293,14 +319,14 @@ bool MetaCoreScene::ReparentGameObjects(const std::vector<MetaCoreId>& objectIds
continue; continue;
} }
const MetaCoreGameObject* object = FindGameObject(objectId); MetaCoreGameObject object = FindGameObject(objectId);
bool hasSelectedAncestor = false; bool hasSelectedAncestor = false;
while (object != nullptr && object->ParentId != 0) { while (object && object.GetParentId() != 0) {
if (selectedIds.contains(object->ParentId)) { if (selectedIds.contains(object.GetParentId())) {
hasSelectedAncestor = true; hasSelectedAncestor = true;
break; break;
} }
object = FindGameObject(object->ParentId); object = FindGameObject(object.GetParentId());
} }
if (!hasSelectedAncestor) { if (!hasSelectedAncestor) {
@ -321,17 +347,17 @@ bool MetaCoreScene::ReparentGameObjects(const std::vector<MetaCoreId>& objectIds
std::unordered_map<MetaCoreId, glm::mat4> worldMatrices; std::unordered_map<MetaCoreId, glm::mat4> worldMatrices;
if (keepWorldTransform) { if (keepWorldTransform) {
const auto buildWorldMatrix = [&](const auto& self, MetaCoreId objectId) -> glm::mat4 { const auto buildWorldMatrix = [&](const auto& self, MetaCoreId objectId) -> glm::mat4 {
const MetaCoreGameObject* object = FindGameObject(objectId); MetaCoreGameObject object = FindGameObject(objectId);
if (object == nullptr) { if (!object) {
return glm::mat4(1.0F); return glm::mat4(1.0F);
} }
const glm::mat4 localMatrix = MetaCoreBuildTransformMatrix(object->Transform); const glm::mat4 localMatrix = MetaCoreBuildTransformMatrix(object.GetComponent<MetaCoreTransformComponent>());
if (object->ParentId == 0) { if (object.GetParentId() == 0) {
return localMatrix; return localMatrix;
} }
return self(self, object->ParentId) * localMatrix; return self(self, object.GetParentId()) * localMatrix;
}; };
for (MetaCoreId rootId : rootIds) { for (MetaCoreId rootId : rootIds) {
@ -344,9 +370,9 @@ bool MetaCoreScene::ReparentGameObjects(const std::vector<MetaCoreId>& objectIds
} }
for (MetaCoreId rootId : rootIds) { for (MetaCoreId rootId : rootIds) {
MetaCoreGameObject* object = FindGameObject(rootId); MetaCoreGameObject object = FindGameObject(rootId);
if (object != nullptr) { if (object) {
object->ParentId = newParentId; object.SetParentId(newParentId);
} }
} }
@ -356,8 +382,8 @@ bool MetaCoreScene::ReparentGameObjects(const std::vector<MetaCoreId>& objectIds
const glm::mat4 inverseParentWorldMatrix = glm::inverse(newParentWorldMatrix); const glm::mat4 inverseParentWorldMatrix = glm::inverse(newParentWorldMatrix);
for (MetaCoreId rootId : rootIds) { for (MetaCoreId rootId : rootIds) {
MetaCoreGameObject* object = FindGameObject(rootId); MetaCoreGameObject object = FindGameObject(rootId);
if (object == nullptr) { if (!object) {
continue; continue;
} }
@ -367,7 +393,7 @@ bool MetaCoreScene::ReparentGameObjects(const std::vector<MetaCoreId>& objectIds
} }
const glm::mat4 newLocalMatrix = inverseParentWorldMatrix * matrixIterator->second; const glm::mat4 newLocalMatrix = inverseParentWorldMatrix * matrixIterator->second;
MetaCoreApplyMatrixToTransform(newLocalMatrix, object->Transform); MetaCoreApplyMatrixToTransform(newLocalMatrix, object.GetComponent<MetaCoreTransformComponent>());
} }
} }
@ -376,16 +402,47 @@ bool MetaCoreScene::ReparentGameObjects(const std::vector<MetaCoreId>& objectIds
MetaCoreSceneSnapshot MetaCoreScene::CaptureSnapshot() const { MetaCoreSceneSnapshot MetaCoreScene::CaptureSnapshot() const {
MetaCoreSceneSnapshot snapshot; MetaCoreSceneSnapshot snapshot;
snapshot.GameObjects = GameObjects_; auto objects = GetGameObjects();
snapshot.GameObjects.reserve(objects.size());
for (auto obj : objects) {
MetaCoreGameObjectData data;
data.Id = obj.GetId();
data.ParentId = obj.GetParentId();
data.Name = obj.GetName();
data.Transform = obj.GetComponent<MetaCoreTransformComponent>();
if (obj.HasComponent<MetaCoreCameraComponent>())
data.Camera = obj.GetComponent<MetaCoreCameraComponent>();
if (obj.HasComponent<MetaCoreMeshRendererComponent>())
data.MeshRenderer = obj.GetComponent<MetaCoreMeshRendererComponent>();
if (obj.HasComponent<MetaCoreLightComponent>())
data.Light = obj.GetComponent<MetaCoreLightComponent>();
if (obj.HasComponent<MetaCorePrefabInstanceMetadata>())
data.PrefabInstance = obj.GetComponent<MetaCorePrefabInstanceMetadata>();
snapshot.GameObjects.push_back(std::move(data));
}
return snapshot; return snapshot;
} }
void MetaCoreScene::RestoreSnapshot(const MetaCoreSceneSnapshot& snapshot) { void MetaCoreScene::RestoreSnapshot(const MetaCoreSceneSnapshot& snapshot) {
GameObjects_ = snapshot.GameObjects; Registry_.clear();
IdToEntity_.clear();
MetaCoreId maxId = 0; MetaCoreId maxId = 0;
for (const MetaCoreGameObject& object : GameObjects_) { for (const MetaCoreGameObjectData& data : snapshot.GameObjects) {
maxId = std::max(maxId, object.Id); maxId = std::max(maxId, data.Id);
MetaCoreGameObject obj = CreateGameObjectWithId(data.Id, data.Name, data.ParentId);
obj.GetComponent<MetaCoreTransformComponent>() = data.Transform;
if (data.Camera.has_value())
obj.AddComponent<MetaCoreCameraComponent>(data.Camera.value());
if (data.MeshRenderer.has_value())
obj.AddComponent<MetaCoreMeshRendererComponent>(data.MeshRenderer.value());
if (data.Light.has_value())
obj.AddComponent<MetaCoreLightComponent>(data.Light.value());
if (data.PrefabInstance.has_value())
obj.AddComponent<MetaCorePrefabInstanceMetadata>(data.PrefabInstance.value());
} }
MetaCoreIdGenerator::EnsureAbove(maxId); MetaCoreIdGenerator::EnsureAbove(maxId);
} }
@ -393,36 +450,35 @@ void MetaCoreScene::RestoreSnapshot(const MetaCoreSceneSnapshot& snapshot) {
MetaCoreScene MetaCoreCreateDefaultScene() { MetaCoreScene MetaCoreCreateDefaultScene() {
MetaCoreScene scene; MetaCoreScene scene;
MetaCoreGameObject& mainCamera = scene.CreateGameObject("Main Camera"); MetaCoreGameObject mainCamera = scene.CreateGameObject("Main Camera");
mainCamera.Camera = MetaCoreCameraComponent{}; mainCamera.AddComponent<MetaCoreCameraComponent>().IsPrimary = true;
mainCamera.Camera->IsPrimary = true; mainCamera.GetComponent<MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 2.2F, 6.5F);
mainCamera.Transform.Position = glm::vec3(0.0F, 2.2F, 6.5F); mainCamera.GetComponent<MetaCoreTransformComponent>().RotationEulerDegrees = glm::vec3(-15.0F, 0.0F, 0.0F);
mainCamera.Transform.RotationEulerDegrees = glm::vec3(-15.0F, 0.0F, 0.0F);
MetaCoreGameObject& directionalLight = scene.CreateGameObject("Directional Light"); MetaCoreGameObject directionalLight = scene.CreateGameObject("Directional Light");
directionalLight.Light = MetaCoreLightComponent{}; directionalLight.AddComponent<MetaCoreLightComponent>();
directionalLight.Transform.RotationEulerDegrees = glm::vec3(-45.0F, 30.0F, 0.0F); directionalLight.GetComponent<MetaCoreTransformComponent>().RotationEulerDegrees = glm::vec3(-45.0F, 30.0F, 0.0F);
MetaCoreGameObject& cube = scene.CreateGameObject("Cube"); MetaCoreGameObject cube = scene.CreateGameObject("Cube");
cube.MeshRenderer = MetaCoreMeshRendererComponent{}; cube.AddComponent<MetaCoreMeshRendererComponent>();
cube.Transform.Position = glm::vec3(0.0F, 0.5F, 0.0F); cube.GetComponent<MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 0.5F, 0.0F);
MetaCoreGameObject& valve = scene.CreateGameObject("Valve"); MetaCoreGameObject valve = scene.CreateGameObject("Valve");
valve.MeshRenderer = MetaCoreMeshRendererComponent{}; auto& valveMesh = valve.AddComponent<MetaCoreMeshRendererComponent>();
valve.Transform.Position = glm::vec3(-2.2F, 0.5F, 0.0F); valve.GetComponent<MetaCoreTransformComponent>().Position = glm::vec3(-2.2F, 0.5F, 0.0F);
valve.MeshRenderer->BaseColor = glm::vec3(0.85F, 0.45F, 0.20F); valveMesh.BaseColor = glm::vec3(0.85F, 0.45F, 0.20F);
MetaCoreGameObject& tank = scene.CreateGameObject("Tank"); MetaCoreGameObject tank = scene.CreateGameObject("Tank");
tank.MeshRenderer = MetaCoreMeshRendererComponent{}; auto& tankMesh = tank.AddComponent<MetaCoreMeshRendererComponent>();
tank.Transform.Position = glm::vec3(2.2F, 0.5F, 0.0F); tank.GetComponent<MetaCoreTransformComponent>().Position = glm::vec3(2.2F, 0.5F, 0.0F);
tank.Transform.Scale = glm::vec3(1.4F, 1.4F, 1.4F); tank.GetComponent<MetaCoreTransformComponent>().Scale = glm::vec3(1.4F, 1.4F, 1.4F);
tank.MeshRenderer->BaseColor = glm::vec3(0.35F, 0.65F, 0.90F); tankMesh.BaseColor = glm::vec3(0.35F, 0.65F, 0.90F);
MetaCoreGameObject& alarmBeacon = scene.CreateGameObject("Alarm Beacon"); MetaCoreGameObject alarmBeacon = scene.CreateGameObject("Alarm Beacon");
alarmBeacon.Light = MetaCoreLightComponent{}; auto& beaconLight = alarmBeacon.AddComponent<MetaCoreLightComponent>();
alarmBeacon.Light->Intensity = 0.0F; beaconLight.Intensity = 0.0F;
alarmBeacon.Light->Color = glm::vec3(1.0F, 0.15F, 0.1F); beaconLight.Color = glm::vec3(1.0F, 0.15F, 0.1F);
alarmBeacon.Transform.Position = glm::vec3(0.0F, 2.5F, 0.0F); alarmBeacon.GetComponent<MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 2.5F, 0.0F);
return scene; return scene;
} }

View File

@ -5,6 +5,7 @@
#include <glm/vec3.hpp> #include <glm/vec3.hpp>
#include <cstdint>
#include <optional> #include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
@ -12,7 +13,6 @@
namespace MetaCore { namespace MetaCore {
MC_ENUM() MC_ENUM()
// 定义第一阶段内置的网格类型。
enum class MetaCoreBuiltinMeshType { enum class MetaCoreBuiltinMeshType {
Cube Cube
}; };
@ -31,7 +31,6 @@ enum class MetaCoreMeshAlphaMode {
}; };
MC_STRUCT() MC_STRUCT()
// 表示 Unity 风格对象的变换组件。
struct MetaCoreTransformComponent { struct MetaCoreTransformComponent {
MC_GENERATED_BODY() MC_GENERATED_BODY()
@ -44,7 +43,6 @@ struct MetaCoreTransformComponent {
}; };
MC_STRUCT() MC_STRUCT()
// 表示场景中用于观察的摄像机组件。
struct MetaCoreCameraComponent { struct MetaCoreCameraComponent {
MC_GENERATED_BODY() MC_GENERATED_BODY()
@ -59,7 +57,6 @@ struct MetaCoreCameraComponent {
}; };
MC_STRUCT() MC_STRUCT()
// 表示一个最小可用的网格渲染组件。
struct MetaCoreMeshRendererComponent { struct MetaCoreMeshRendererComponent {
MC_GENERATED_BODY() MC_GENERATED_BODY()
@ -72,8 +69,22 @@ struct MetaCoreMeshRendererComponent {
MC_PROPERTY() MC_PROPERTY()
std::vector<MetaCoreAssetGuid> MaterialAssetGuids{}; std::vector<MetaCoreAssetGuid> MaterialAssetGuids{};
MC_PROPERTY() MC_PROPERTY()
MetaCoreAssetGuid SourceModelAssetGuid{};
MC_PROPERTY()
std::string SourceModelPath{}; std::string SourceModelPath{};
MC_PROPERTY() MC_PROPERTY()
std::string SourceNodePath{};
MC_PROPERTY()
MetaCoreAssetGuid BaseColorTextureGuid{};
MC_PROPERTY()
MetaCoreAssetGuid MetallicRoughnessTextureGuid{};
MC_PROPERTY()
MetaCoreAssetGuid NormalTextureGuid{};
MC_PROPERTY()
MetaCoreAssetGuid EmissiveTextureGuid{};
MC_PROPERTY()
MetaCoreAssetGuid AoTextureGuid{};
MC_PROPERTY()
std::string BaseColorTexturePath{}; std::string BaseColorTexturePath{};
MC_PROPERTY() MC_PROPERTY()
std::string MetallicRoughnessTexturePath{}; std::string MetallicRoughnessTexturePath{};
@ -107,7 +118,6 @@ struct MetaCoreMeshRendererComponent {
}; };
MC_STRUCT() MC_STRUCT()
// 表示第一阶段场景中的方向光组件。
struct MetaCoreLightComponent { struct MetaCoreLightComponent {
MC_GENERATED_BODY() MC_GENERATED_BODY()

View File

@ -1,15 +1,18 @@
#pragma once #pragma once
#include "MetaCoreFoundation/MetaCoreId.h"
#include "MetaCoreFoundation/MetaCoreAssetGuid.h" #include "MetaCoreFoundation/MetaCoreAssetGuid.h"
#include "MetaCoreFoundation/MetaCoreId.h"
#include "MetaCoreFoundation/MetaCoreReflection.h" #include "MetaCoreFoundation/MetaCoreReflection.h"
#include "MetaCoreScene/MetaCoreComponents.h" #include "MetaCoreScene/MetaCoreComponents.h"
#include <entt/entt.hpp>
#include <optional> #include <optional>
#include <string> #include <string>
namespace MetaCore { namespace MetaCore {
class MetaCoreScene;
MC_STRUCT() MC_STRUCT()
struct MetaCorePrefabInstanceMetadata { struct MetaCorePrefabInstanceMetadata {
MC_GENERATED_BODY() MC_GENERATED_BODY()
@ -24,9 +27,88 @@ struct MetaCorePrefabInstanceMetadata {
MetaCoreId PrefabInstanceRootId = 0; MetaCoreId PrefabInstanceRootId = 0;
}; };
// Component structs for basic properties
MC_STRUCT() MC_STRUCT()
// 表示一个 Unity 风格的场景对象。 struct MetaCoreIdComponent {
struct MetaCoreGameObject { MC_GENERATED_BODY()
MC_PROPERTY()
MetaCoreId Id = 0;
};
MC_STRUCT()
struct MetaCoreHierarchyComponent {
MC_GENERATED_BODY()
MC_PROPERTY()
MetaCoreId ParentId = 0;
};
MC_STRUCT()
struct MetaCoreNameComponent {
MC_GENERATED_BODY()
MC_PROPERTY()
std::string Name;
};
// The ECS handle
class MetaCoreGameObject {
public:
MetaCoreGameObject() = default;
MetaCoreGameObject(entt::entity handle, MetaCoreScene* scene)
: EntityHandle(handle), Scene(scene) {}
template<typename T, typename... Args>
T& AddComponent(Args&&... args) {
// Implementation will be in MetaCoreScene.h due to circular dependency
return InternalAddComponent<T>(std::forward<Args>(args)...);
}
template<typename T>
T& GetComponent() {
return InternalGetComponent<T>();
}
template<typename T>
const T& GetComponent() const {
return InternalGetComponent<T>();
}
template<typename T>
bool HasComponent() const {
return InternalHasComponent<T>();
}
template<typename T>
void RemoveComponent() {
InternalRemoveComponent<T>();
}
operator bool() const { return EntityHandle != entt::null && Scene != nullptr; }
bool operator==(const MetaCoreGameObject& other) const { return EntityHandle == other.EntityHandle && Scene == other.Scene; }
bool operator!=(const MetaCoreGameObject& other) const { return !(*this == other); }
entt::entity GetEntityHandle() const { return EntityHandle; }
MetaCoreScene* GetScene() const { return Scene; }
// Helpers
MetaCoreId GetId() const;
MetaCoreId GetParentId() const;
void SetParentId(MetaCoreId id);
std::string& GetName();
const std::string& GetName() const;
private:
template<typename T, typename... Args> T& InternalAddComponent(Args&&... args);
template<typename T> T& InternalGetComponent() const;
template<typename T> bool InternalHasComponent() const;
template<typename T> void InternalRemoveComponent();
entt::entity EntityHandle{entt::null};
MetaCoreScene* Scene = nullptr;
};
// Old struct retained for serialization/snapshot MVP
MC_STRUCT()
struct MetaCoreGameObjectData {
MC_GENERATED_BODY() MC_GENERATED_BODY()
MC_PROPERTY() MC_PROPERTY()

View File

@ -1,10 +1,12 @@
#pragma once #pragma once
#include "MetaCoreFoundation/MetaCoreReflection.h" #include "MetaCoreFoundation/MetaCoreReflection.h"
#include "MetaCoreScene/MetaCoreSceneDocument.h"
#include "MetaCoreScene/MetaCoreGameObject.h" #include "MetaCoreScene/MetaCoreGameObject.h"
#include "MetaCoreScene/MetaCoreSceneDocument.h"
#include <entt/entt.hpp>
#include <vector> #include <vector>
#include <unordered_map>
namespace MetaCore { namespace MetaCore {
@ -13,61 +15,85 @@ struct MetaCoreSceneSnapshot {
MC_GENERATED_BODY() MC_GENERATED_BODY()
MC_PROPERTY() MC_PROPERTY()
std::vector<MetaCoreGameObject> GameObjects; std::vector<MetaCoreGameObjectData> GameObjects;
}; };
// 负责存储和查询场景中的全部对象。
class MetaCoreScene { class MetaCoreScene {
public: public:
// 创建一个新的场景对象。 MetaCoreGameObject CreateGameObject(const std::string& name, MetaCoreId parentId = 0);
MetaCoreGameObject& CreateGameObject(const std::string& name, MetaCoreId parentId = 0); MetaCoreGameObject CreateGameObjectWithId(MetaCoreId id, const std::string& name, MetaCoreId parentId = 0);
// 通过标识查找场景对象。 [[nodiscard]] MetaCoreGameObject FindGameObject(MetaCoreId objectId);
[[nodiscard]] MetaCoreGameObject* FindGameObject(MetaCoreId objectId); [[nodiscard]] MetaCoreGameObject FindGameObject(MetaCoreId objectId) const;
[[nodiscard]] const MetaCoreGameObject* FindGameObject(MetaCoreId objectId) const;
// 返回全部对象。 [[nodiscard]] std::vector<MetaCoreGameObject> GetGameObjects();
[[nodiscard]] std::vector<MetaCoreGameObject>& GetGameObjects(); [[nodiscard]] std::vector<MetaCoreGameObject> GetGameObjects() const;
[[nodiscard]] const std::vector<MetaCoreGameObject>& GetGameObjects() const;
// 返回根节点对象列表。
[[nodiscard]] std::vector<MetaCoreId> GetRootObjectIds() const; [[nodiscard]] std::vector<MetaCoreId> GetRootObjectIds() const;
// 返回某个对象的直接子对象列表。
[[nodiscard]] std::vector<MetaCoreId> GetChildrenOf(MetaCoreId parentId) const; [[nodiscard]] std::vector<MetaCoreId> GetChildrenOf(MetaCoreId parentId) const;
// 按层级前序遍历顺序返回对象标识列表。
[[nodiscard]] std::vector<MetaCoreId> BuildHierarchyPreorder() const; [[nodiscard]] std::vector<MetaCoreId> BuildHierarchyPreorder() const;
// 返回指定根对象的完整子树标识(含自身)。
[[nodiscard]] std::vector<MetaCoreId> GetSubtreeObjectIds(MetaCoreId rootId) const; [[nodiscard]] std::vector<MetaCoreId> GetSubtreeObjectIds(MetaCoreId rootId) const;
// 判断对象是否是另一个对象的后代。
[[nodiscard]] bool IsDescendantOf(MetaCoreId objectId, MetaCoreId ancestorId) const; [[nodiscard]] bool IsDescendantOf(MetaCoreId objectId, MetaCoreId ancestorId) const;
// 重命名对象。
bool RenameGameObject(MetaCoreId objectId, const std::string& name); bool RenameGameObject(MetaCoreId objectId, const std::string& name);
// 删除对象(包含其子树)。返回实际删除的全部对象标识。
[[nodiscard]] std::vector<MetaCoreId> DeleteGameObjects(const std::vector<MetaCoreId>& objectIds); [[nodiscard]] std::vector<MetaCoreId> DeleteGameObjects(const std::vector<MetaCoreId>& objectIds);
// 复制对象(包含其子树)。返回复制后顶层对象标识。
[[nodiscard]] std::vector<MetaCoreId> DuplicateGameObjects(const std::vector<MetaCoreId>& objectIds); [[nodiscard]] std::vector<MetaCoreId> DuplicateGameObjects(const std::vector<MetaCoreId>& objectIds);
// 批量重挂接对象到新父节点。
bool ReparentGameObjects(const std::vector<MetaCoreId>& objectIds, MetaCoreId newParentId, bool keepWorldTransform); bool ReparentGameObjects(const std::vector<MetaCoreId>& objectIds, MetaCoreId newParentId, bool keepWorldTransform);
// 场景快照捕获与恢复。
[[nodiscard]] MetaCoreSceneSnapshot CaptureSnapshot() const; [[nodiscard]] MetaCoreSceneSnapshot CaptureSnapshot() const;
void RestoreSnapshot(const MetaCoreSceneSnapshot& snapshot); void RestoreSnapshot(const MetaCoreSceneSnapshot& snapshot);
entt::registry& GetRegistry() { return Registry_; }
const entt::registry& GetRegistry() const { return Registry_; }
private: private:
void CollectPreorder(MetaCoreId objectId, std::vector<MetaCoreId>& output) const; void CollectPreorder(MetaCoreId objectId, std::vector<MetaCoreId>& output) const;
std::vector<MetaCoreGameObject> GameObjects_{}; entt::registry Registry_;
std::unordered_map<MetaCoreId, entt::entity> IdToEntity_;
}; };
// 创建一个包含默认相机、灯光和立方体的启动场景。
MetaCoreScene MetaCoreCreateDefaultScene(); MetaCoreScene MetaCoreCreateDefaultScene();
// MetaCoreGameObject inline implementations
template<typename T, typename... Args>
T& MetaCoreGameObject::InternalAddComponent(Args&&... args) {
return Scene->GetRegistry().emplace<T>(EntityHandle, std::forward<Args>(args)...);
}
template<typename T>
T& MetaCoreGameObject::InternalGetComponent() const {
return Scene->GetRegistry().get<T>(EntityHandle);
}
template<typename T>
bool MetaCoreGameObject::InternalHasComponent() const {
return Scene->GetRegistry().any_of<T>(EntityHandle);
}
template<typename T>
void MetaCoreGameObject::InternalRemoveComponent() {
Scene->GetRegistry().remove<T>(EntityHandle);
}
inline MetaCoreId MetaCoreGameObject::GetId() const {
return GetComponent<MetaCoreIdComponent>().Id;
}
inline MetaCoreId MetaCoreGameObject::GetParentId() const {
return GetComponent<MetaCoreHierarchyComponent>().ParentId;
}
inline void MetaCoreGameObject::SetParentId(MetaCoreId id) {
GetComponent<MetaCoreHierarchyComponent>().ParentId = id;
}
inline std::string& MetaCoreGameObject::GetName() {
return GetComponent<MetaCoreNameComponent>().Name;
}
inline const std::string& MetaCoreGameObject::GetName() const {
return GetComponent<MetaCoreNameComponent>().Name;
}
} // namespace MetaCore } // namespace MetaCore

View File

@ -31,7 +31,7 @@ struct MetaCoreSceneDocument {
std::string Name{}; std::string Name{};
MC_PROPERTY() MC_PROPERTY()
std::vector<MetaCoreGameObject> GameObjects{}; std::vector<MetaCoreGameObjectData> GameObjects{};
MC_PROPERTY() MC_PROPERTY()
MetaCoreEditorSelectionSnapshot Selection{}; MetaCoreEditorSelectionSnapshot Selection{};

Binary file not shown.

View File

@ -193,7 +193,7 @@ void MetaCoreTestScenePackageProjectStartupLoad() {
MetaCore::MetaCoreSceneDocument sceneDocument; MetaCore::MetaCoreSceneDocument sceneDocument;
sceneDocument.Name = "Main"; sceneDocument.Name = "Main";
const MetaCore::MetaCoreScene sourceScene = MetaCore::MetaCoreCreateDefaultScene(); const MetaCore::MetaCoreScene sourceScene = MetaCore::MetaCoreCreateDefaultScene();
sceneDocument.GameObjects = sourceScene.GetGameObjects(); sceneDocument.GameObjects = sourceScene.CaptureSnapshot().GameObjects;
const std::filesystem::path scenePath = tempProjectRoot / "Scenes" / "Main.mcscene"; const std::filesystem::path scenePath = tempProjectRoot / "Scenes" / "Main.mcscene";
MetaCoreExpect(MetaCore::MetaCoreWriteScenePackage(scenePath, sceneDocument), "应能写入二进制场景包"); MetaCoreExpect(MetaCore::MetaCoreWriteScenePackage(scenePath, sceneDocument), "应能写入二进制场景包");
@ -334,7 +334,7 @@ void MetaCoreTestAssetDatabaseRenamePathUpdatesProjectDescriptor() {
MetaCore::MetaCoreSceneDocument sceneDocument; MetaCore::MetaCoreSceneDocument sceneDocument;
sceneDocument.Name = "Main"; sceneDocument.Name = "Main";
sceneDocument.GameObjects = MetaCore::MetaCoreCreateDefaultScene().GetGameObjects(); sceneDocument.GameObjects = MetaCore::MetaCoreCreateDefaultScene().CaptureSnapshot().GameObjects;
MetaCoreExpect( MetaCoreExpect(
MetaCore::MetaCoreWriteScenePackage(tempProjectRoot / "Scenes" / "Main.mcscene", sceneDocument), MetaCore::MetaCoreWriteScenePackage(tempProjectRoot / "Scenes" / "Main.mcscene", sceneDocument),
"应能写入场景包" "应能写入场景包"
@ -396,7 +396,7 @@ void MetaCoreTestAssetDatabaseMovePathUpdatesProjectDescriptor() {
MetaCore::MetaCoreSceneDocument sceneDocument; MetaCore::MetaCoreSceneDocument sceneDocument;
sceneDocument.Name = "Main"; sceneDocument.Name = "Main";
sceneDocument.GameObjects = MetaCore::MetaCoreCreateDefaultScene().GetGameObjects(); sceneDocument.GameObjects = MetaCore::MetaCoreCreateDefaultScene().CaptureSnapshot().GameObjects;
MetaCoreExpect( MetaCoreExpect(
MetaCore::MetaCoreWriteScenePackage(tempProjectRoot / "Scenes" / "Main.mcscene", sceneDocument), MetaCore::MetaCoreWriteScenePackage(tempProjectRoot / "Scenes" / "Main.mcscene", sceneDocument),
"应能写入场景包" "应能写入场景包"
@ -567,6 +567,7 @@ void MetaCoreTestImportPipelineAndCook() {
std::filesystem::temp_directory_path() / "MetaCoreImportCookProject"; std::filesystem::temp_directory_path() / "MetaCoreImportCookProject";
std::filesystem::remove_all(tempProjectRoot); std::filesystem::remove_all(tempProjectRoot);
std::filesystem::create_directories(tempProjectRoot / "Assets"); std::filesystem::create_directories(tempProjectRoot / "Assets");
std::filesystem::create_directories(tempProjectRoot / "Assets" / "Textures");
std::filesystem::create_directories(tempProjectRoot / "Scenes"); std::filesystem::create_directories(tempProjectRoot / "Scenes");
std::filesystem::create_directories(tempProjectRoot / "Library"); std::filesystem::create_directories(tempProjectRoot / "Library");
@ -631,6 +632,7 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
std::filesystem::temp_directory_path() / "MetaCoreProjectGltfImporterProject"; std::filesystem::temp_directory_path() / "MetaCoreProjectGltfImporterProject";
std::filesystem::remove_all(tempProjectRoot); std::filesystem::remove_all(tempProjectRoot);
std::filesystem::create_directories(tempProjectRoot / "Assets"); std::filesystem::create_directories(tempProjectRoot / "Assets");
std::filesystem::create_directories(tempProjectRoot / "Assets" / "Textures");
std::filesystem::create_directories(tempProjectRoot / "Scenes"); std::filesystem::create_directories(tempProjectRoot / "Scenes");
std::filesystem::create_directories(tempProjectRoot / "ConfigRuntime"); std::filesystem::create_directories(tempProjectRoot / "ConfigRuntime");
std::filesystem::create_directories(tempProjectRoot / "UiDocuments"); std::filesystem::create_directories(tempProjectRoot / "UiDocuments");
@ -649,6 +651,15 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
<< "}\n"; << "}\n";
} }
{
const unsigned char valveTextureData[] = {
0x50, 0x36, 0x0A, 0x31, 0x20, 0x31, 0x0A, 0x32,
0x35, 0x35, 0x0A, 0xFF, 0x80, 0x40
};
std::ofstream textureFile(tempProjectRoot / "Assets" / "Textures" / "ValveBaseColor.ppm", std::ios::binary | std::ios::trunc);
textureFile.write(reinterpret_cast<const char*>(valveTextureData), sizeof(valveTextureData));
}
{ {
std::ofstream gltfFile(tempProjectRoot / "Assets" / "Valve.gltf", std::ios::trunc); std::ofstream gltfFile(tempProjectRoot / "Assets" / "Valve.gltf", std::ios::trunc);
gltfFile gltfFile
@ -659,7 +670,23 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
<< " \"meshes\": [{\"name\": \"ValveMesh\", \"primitives\": [{\"material\": 0}]}],\n" << " \"meshes\": [{\"name\": \"ValveMesh\", \"primitives\": [{\"material\": 0}]}],\n"
<< " \"materials\": [{\"name\": \"ValveMaterial\", \"pbrMetallicRoughness\": {\"baseColorTexture\": {\"index\": 0}}}],\n" << " \"materials\": [{\"name\": \"ValveMaterial\", \"pbrMetallicRoughness\": {\"baseColorTexture\": {\"index\": 0}}}],\n"
<< " \"textures\": [{\"source\": 0}],\n" << " \"textures\": [{\"source\": 0}],\n"
<< " \"images\": [{\"uri\": \"Textures/ValveBaseColor.png\"}]\n" << " \"images\": [{\"uri\": \"Textures/ValveBaseColor.ppm\"}]\n"
<< "}\n";
}
{
std::ofstream gltfFile(tempProjectRoot / "Assets" / "EmbeddedTexture.gltf", std::ios::trunc);
gltfFile
<< "{\n"
<< " \"asset\": {\"version\": \"2.0\"},\n"
<< " \"scenes\": [{\"nodes\": [0]}],\n"
<< " \"nodes\": [{\"mesh\": 0, \"name\": \"EmbeddedTextureNode\"}],\n"
<< " \"meshes\": [{\"name\": \"EmbeddedTextureMesh\", \"primitives\": [{\"material\": 0}]}],\n"
<< " \"materials\": [{\"name\": \"EmbeddedTextureMaterial\", \"pbrMetallicRoughness\": {\"baseColorTexture\": {\"index\": 0}}}],\n"
<< " \"textures\": [{\"source\": 0}],\n"
<< " \"images\": [{\"bufferView\": 0, \"mimeType\": \"image/x-portable-pixmap\", \"name\": \"EmbeddedBaseColor\"}],\n"
<< " \"bufferViews\": [{\"buffer\": 0, \"byteOffset\": 0, \"byteLength\": 14}],\n"
<< " \"buffers\": [{\"byteLength\": 14, \"uri\": \"data:application/octet-stream;base64,UDYKMSAxCjI1NQr/gEA=\"}]\n"
<< "}\n"; << "}\n";
} }
@ -687,9 +714,11 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
const auto assetDatabase = moduleRegistry.ResolveService<MetaCore::MetaCoreIAssetDatabaseService>(); const auto assetDatabase = moduleRegistry.ResolveService<MetaCore::MetaCoreIAssetDatabaseService>();
const auto packageService = moduleRegistry.ResolveService<MetaCore::MetaCoreIPackageService>(); const auto packageService = moduleRegistry.ResolveService<MetaCore::MetaCoreIPackageService>();
const auto reflectionRegistry = moduleRegistry.ResolveService<MetaCore::MetaCoreIReflectionRegistry>(); const auto reflectionRegistry = moduleRegistry.ResolveService<MetaCore::MetaCoreIReflectionRegistry>();
const auto assetEditingService = moduleRegistry.ResolveService<MetaCore::MetaCoreIAssetEditingService>();
MetaCoreExpect(assetDatabase != nullptr, "应解析到 AssetDatabaseService"); MetaCoreExpect(assetDatabase != nullptr, "应解析到 AssetDatabaseService");
MetaCoreExpect(packageService != nullptr, "应解析到 PackageService"); MetaCoreExpect(packageService != nullptr, "应解析到 PackageService");
MetaCoreExpect(reflectionRegistry != nullptr, "应解析到 ReflectionRegistry"); MetaCoreExpect(reflectionRegistry != nullptr, "应解析到 ReflectionRegistry");
MetaCoreExpect(assetEditingService != nullptr, "应解析到 AssetEditingService");
const auto& project = assetDatabase->GetProjectDescriptor(); const auto& project = assetDatabase->GetProjectDescriptor();
MetaCoreExpect(project.RuntimePath.filename() == "ConfigRuntime", "项目应读取 runtime_directory"); MetaCoreExpect(project.RuntimePath.filename() == "ConfigRuntime", "项目应读取 runtime_directory");
@ -725,7 +754,7 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
MetaCoreExpect(gltfDocument.Meshes.front().PrimitiveCount == 1, "glTF mesh 骨架应写入 primitive 数"); MetaCoreExpect(gltfDocument.Meshes.front().PrimitiveCount == 1, "glTF mesh 骨架应写入 primitive 数");
MetaCoreExpect(!gltfDocument.Materials.empty() && gltfDocument.Materials.front().Name == "ValveMaterial", "glTF 导入文档应提取材质名称"); MetaCoreExpect(!gltfDocument.Materials.empty() && gltfDocument.Materials.front().Name == "ValveMaterial", "glTF 导入文档应提取材质名称");
MetaCoreExpect(!gltfDocument.Textures.empty(), "glTF 导入文档应记录引用贴图"); MetaCoreExpect(!gltfDocument.Textures.empty(), "glTF 导入文档应记录引用贴图");
MetaCoreExpect(gltfDocument.Textures.front().SourcePath == std::filesystem::path("Textures") / "ValveBaseColor.png", "glTF 导入文档应保存贴图路径"); MetaCoreExpect(gltfDocument.Textures.front().SourcePath == std::filesystem::path("Textures") / "ValveBaseColor.ppm", "glTF 导入文档应保存贴图路径");
MetaCoreExpect(gltfDocument.GeneratedMeshAssets.size() == gltfDocument.Meshes.size(), "glTF 导入文档应生成 mesh 资源骨架"); MetaCoreExpect(gltfDocument.GeneratedMeshAssets.size() == gltfDocument.Meshes.size(), "glTF 导入文档应生成 mesh 资源骨架");
MetaCoreExpect(gltfDocument.GeneratedMaterialAssets.size() == gltfDocument.Materials.size(), "glTF 导入文档应生成材质资源骨架"); MetaCoreExpect(gltfDocument.GeneratedMaterialAssets.size() == gltfDocument.Materials.size(), "glTF 导入文档应生成材质资源骨架");
MetaCoreExpect(gltfDocument.GeneratedTextureAssets.size() == gltfDocument.Textures.size(), "glTF 导入文档应生成纹理资源骨架"); MetaCoreExpect(gltfDocument.GeneratedTextureAssets.size() == gltfDocument.Textures.size(), "glTF 导入文档应生成纹理资源骨架");
@ -733,6 +762,39 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
MetaCoreExpect(gltfDocument.GeneratedMaterialAssets.front().AssetGuid.IsValid(), "生成的材质资源应有 GUID"); MetaCoreExpect(gltfDocument.GeneratedMaterialAssets.front().AssetGuid.IsValid(), "生成的材质资源应有 GUID");
MetaCoreExpect(gltfDocument.GeneratedTextureAssets.front().AssetGuid.IsValid(), "生成的纹理资源应有 GUID"); MetaCoreExpect(gltfDocument.GeneratedTextureAssets.front().AssetGuid.IsValid(), "生成的纹理资源应有 GUID");
MetaCoreExpect(gltfDocument.GeneratedMaterialAssets.front().BaseColorTexture.IsValid(), "材质资源应引用生成的贴图资源"); MetaCoreExpect(gltfDocument.GeneratedMaterialAssets.front().BaseColorTexture.IsValid(), "材质资源应引用生成的贴图资源");
const std::filesystem::path gltfGeneratedTextureRuntimePath =
tempProjectRoot / "Library" / "RuntimeAssets" / "Textures" /
(gltfDocument.GeneratedTextureAssets.front().AssetGuid.ToString() + ".mcasset");
MetaCoreExpect(std::filesystem::exists(gltfGeneratedTextureRuntimePath), "glTF generated texture should write a runtime texture package");
const auto gltfGeneratedTextureDocument =
assetEditingService->LoadTextureAsset(gltfDocument.GeneratedTextureAssets.front().AssetGuid);
MetaCoreExpect(gltfGeneratedTextureDocument.has_value(), "AssetEditingService 应能读取外部贴图 runtime 资源");
MetaCoreExpect(gltfGeneratedTextureDocument->Width == 1 && gltfGeneratedTextureDocument->Height == 1, "外部贴图 runtime 资源应带有真实尺寸");
const auto embeddedTextureRecord =
assetDatabase->FindAssetByRelativePath(std::filesystem::path("Assets") / "EmbeddedTexture.gltf");
MetaCoreExpect(embeddedTextureRecord.has_value(), "应能找到内嵌贴图 glTF 资源记录");
const auto embeddedTexturePackage = packageService->ReadPackage(tempProjectRoot / embeddedTextureRecord->PackagePath);
MetaCoreExpect(embeddedTexturePackage.has_value(), "内嵌贴图 glTF 应生成包文件");
MetaCore::MetaCoreModelAssetDocument embeddedTextureDocument;
MetaCoreExpect(
!embeddedTexturePackage->PayloadSections.empty() &&
MetaCore::MetaCoreDeserializeFromBytes(
embeddedTexturePackage->PayloadSections.front(),
embeddedTextureDocument,
reflectionRegistry->GetTypeRegistry()
),
"内嵌贴图 glTF 包应能反序列化为模型资产"
);
MetaCoreExpect(!embeddedTextureDocument.GeneratedTextureAssets.empty(), "内嵌贴图 glTF 应生成纹理子资源");
const std::filesystem::path embeddedTextureRuntimePath =
tempProjectRoot / "Library" / "RuntimeAssets" / "Textures" /
(embeddedTextureDocument.GeneratedTextureAssets.front().AssetGuid.ToString() + ".mcasset");
MetaCoreExpect(std::filesystem::exists(embeddedTextureRuntimePath), "内嵌 bufferView 贴图应生成 runtime 纹理包");
const auto embeddedRuntimeTextureDocument =
assetEditingService->LoadTextureAsset(embeddedTextureDocument.GeneratedTextureAssets.front().AssetGuid);
MetaCoreExpect(embeddedRuntimeTextureDocument.has_value(), "AssetEditingService 应能读取内嵌贴图 runtime 资源");
MetaCoreExpect(embeddedRuntimeTextureDocument->Width == 1 && embeddedRuntimeTextureDocument->Height == 1, "内嵌贴图 runtime 资源应带有真实尺寸");
const auto glbRecord = assetDatabase->FindAssetByRelativePath(std::filesystem::path("Assets") / "Tank.glb"); const auto glbRecord = assetDatabase->FindAssetByRelativePath(std::filesystem::path("Assets") / "Tank.glb");
MetaCoreExpect(glbRecord.has_value(), "应能找到 glb 资源记录"); MetaCoreExpect(glbRecord.has_value(), "应能找到 glb 资源记录");
@ -882,20 +944,25 @@ void MetaCoreTestInstantiateImportedModelAssetIntoScene() {
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId); const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr, "实例化后应能找到根对象"); MetaCoreExpect(instantiatedRoot != nullptr, "实例化后应能找到根对象");
MetaCoreExpect(instantiatedRoot->Name == "Pump", "实例化根对象名称应来自导入节点"); MetaCoreExpect(instantiatedRoot->Name == "Pump.gltf", "model root should use source file name");
MetaCoreExpect(instantiatedRoot->MeshRenderer.has_value(), "导入模型实例应带 MeshRenderer"); const std::vector<MetaCore::MetaCoreId> instantiatedChildren = scene.GetChildrenOf(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot->MeshRenderer->MeshSource == MetaCore::MetaCoreMeshSourceKind::Asset, "导入模型实例应使用 Asset MeshSource"); MetaCoreExpect(instantiatedChildren.size() == 1, "model root should contain imported node");
MetaCoreExpect(instantiatedRoot->MeshRenderer->MeshAssetGuid.IsValid(), "导入模型实例应绑定 MeshAssetGuid"); const MetaCore::MetaCoreGameObject* instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
MetaCoreExpect(!instantiatedRoot->MeshRenderer->MaterialAssetGuids.empty(), "导入模型实例应绑定材质资源"); MetaCoreExpect(instantiatedRenderable != nullptr, "instantiated renderable node should exist");
MetaCoreExpect(instantiatedRenderable->Name == "Pump", "renderable child name should come from imported node");
MetaCoreExpect(instantiatedRenderable->MeshRenderer.has_value(), "renderable node should have MeshRenderer");
MetaCoreExpect(instantiatedRenderable->MeshRenderer->MeshSource == MetaCore::MetaCoreMeshSourceKind::Asset, "renderable node should use asset mesh source");
MetaCoreExpect(instantiatedRenderable->MeshRenderer->MeshAssetGuid.IsValid(), "renderable node should bind mesh asset guid");
MetaCoreExpect(!instantiatedRenderable->MeshRenderer->MaterialAssetGuids.empty(), "renderable node should bind material assets");
MetaCoreExpect( MetaCoreExpect(
instantiatedRoot->MeshRenderer->SourceModelPath == (std::filesystem::path("Assets") / "Pump.gltf").generic_string(), instantiatedRenderable->MeshRenderer->SourceModelPath == (std::filesystem::path("Assets") / "Pump.gltf").generic_string(),
"导入模型实例应记录源模型路径" "renderable node should record source model path"
); );
MetaCoreExpect( MetaCoreExpect(
instantiatedRoot->MeshRenderer->BaseColorTexturePath == (std::filesystem::path("Textures") / "PumpBaseColor.png").generic_string(), instantiatedRenderable->MeshRenderer->BaseColorTexturePath == (std::filesystem::path("Textures") / "PumpBaseColor.png").generic_string(),
"导入模型实例应带入基础颜色贴图路径" "renderable node should record base color texture path"
); );
MetaCoreExpect(instantiatedRoot->MeshRenderer->BaseColor.x == 1.0F, "导入模型实例应带入默认材质基础颜色"); MetaCoreExpect(instantiatedRenderable->MeshRenderer->BaseColor.x == 1.0F, "renderable node should carry base color");
MetaCoreExpect(editorContext.GetActiveObjectId() == *instantiatedRootId, "实例化后应选中新对象"); MetaCoreExpect(editorContext.GetActiveObjectId() == *instantiatedRootId, "实例化后应选中新对象");
coreServicesModule->Shutdown(moduleRegistry); coreServicesModule->Shutdown(moduleRegistry);
@ -981,8 +1048,9 @@ void MetaCoreTestInstantiateMultiNodeModelAssetIntoScene() {
MetaCore::MetaCoreImportedGltfMeshDocument meshDocument; MetaCore::MetaCoreImportedGltfMeshDocument meshDocument;
meshDocument.Name = "ValveMesh"; meshDocument.Name = "ValveMesh";
meshDocument.PrimitiveCount = 1; meshDocument.PrimitiveCount = 2;
meshDocument.MaterialSlots.push_back(0); meshDocument.MaterialSlots.push_back(0);
meshDocument.MaterialSlots.push_back(1);
document.Meshes.push_back(meshDocument); document.Meshes.push_back(meshDocument);
MetaCore::MetaCoreMaterialAssetDocument materialAsset; MetaCore::MetaCoreMaterialAssetDocument materialAsset;
@ -990,9 +1058,26 @@ void MetaCoreTestInstantiateMultiNodeModelAssetIntoScene() {
materialAsset.Name = "ValveMaterial"; materialAsset.Name = "ValveMaterial";
document.GeneratedMaterialAssets.push_back(materialAsset); document.GeneratedMaterialAssets.push_back(materialAsset);
MetaCore::MetaCoreMaterialAssetDocument secondaryMaterialAsset;
secondaryMaterialAsset.AssetGuid = MetaCore::MetaCoreAssetGuid::Generate();
secondaryMaterialAsset.Name = "ValveSecondaryMaterial";
document.GeneratedMaterialAssets.push_back(secondaryMaterialAsset);
MetaCore::MetaCoreMeshAssetDocument meshAsset; MetaCore::MetaCoreMeshAssetDocument meshAsset;
meshAsset.AssetGuid = MetaCore::MetaCoreAssetGuid::Generate(); meshAsset.AssetGuid = MetaCore::MetaCoreAssetGuid::Generate();
meshAsset.Name = "ValveMesh"; meshAsset.Name = "ValveMesh";
MetaCore::MetaCoreMeshSubMeshDocument firstSubMesh;
firstSubMesh.Name = "Primitive_0";
firstSubMesh.MaterialSlotIndex = 0;
firstSubMesh.FirstIndex = 0;
firstSubMesh.IndexCount = 3;
meshAsset.SubMeshes.push_back(firstSubMesh);
MetaCore::MetaCoreMeshSubMeshDocument secondSubMesh;
secondSubMesh.Name = "Primitive_1";
secondSubMesh.MaterialSlotIndex = 1;
secondSubMesh.FirstIndex = 3;
secondSubMesh.IndexCount = 3;
meshAsset.SubMeshes.push_back(secondSubMesh);
document.GeneratedMeshAssets.push_back(meshAsset); document.GeneratedMeshAssets.push_back(meshAsset);
const auto payload = MetaCore::MetaCoreSerializeToBytes(document, reflectionRegistry->GetTypeRegistry()); const auto payload = MetaCore::MetaCoreSerializeToBytes(document, reflectionRegistry->GetTypeRegistry());
@ -1021,20 +1106,27 @@ void MetaCoreTestInstantiateMultiNodeModelAssetIntoScene() {
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId); const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr, "多节点实例化后应能找到根对象"); MetaCoreExpect(instantiatedRoot != nullptr, "多节点实例化后应能找到根对象");
MetaCoreExpect(instantiatedRoot->Name == "AssemblyRoot", "多节点实例化根对象应保留节点名称"); MetaCoreExpect(instantiatedRoot->Name == "Assembly.gltf", "multi-node model root should use source file name");
MetaCoreExpect(!instantiatedRoot->MeshRenderer.has_value(), "无 mesh 的根节点不应强制带 MeshRenderer"); MetaCoreExpect(!instantiatedRoot->MeshRenderer.has_value(), "asset root should not force MeshRenderer");
const auto importedRootIterator = std::find_if(scene.GetGameObjects().begin(), scene.GetGameObjects().end(), [&](const MetaCore::MetaCoreGameObject& object) {
return object.Name == "AssemblyRoot";
});
MetaCoreExpect(importedRootIterator != scene.GetGameObjects().end(), "multi-node instantiation should create imported root node");
MetaCoreExpect(importedRootIterator->ParentId == instantiatedRoot->Id, "imported root node should be parented under asset root");
MetaCoreExpect(!importedRootIterator->MeshRenderer.has_value(), "imported empty root should not force MeshRenderer");
const auto childIterator = std::find_if(scene.GetGameObjects().begin(), scene.GetGameObjects().end(), [&](const MetaCore::MetaCoreGameObject& object) { const auto childIterator = std::find_if(scene.GetGameObjects().begin(), scene.GetGameObjects().end(), [&](const MetaCore::MetaCoreGameObject& object) {
return object.Name == "Valve_A"; return object.Name == "Valve_A";
}); });
MetaCoreExpect(childIterator != scene.GetGameObjects().end(), "多节点实例化应创建子节点对象"); MetaCoreExpect(childIterator != scene.GetGameObjects().end(), "multi-node instantiation should create child node object");
MetaCoreExpect(childIterator->ParentId == instantiatedRoot->Id, "子节点应挂在根节点下"); MetaCoreExpect(childIterator->ParentId == importedRootIterator->Id, "child node should be parented under imported root node");
MetaCoreExpect(childIterator->MeshRenderer.has_value(), "带 mesh 的子节点应带 MeshRenderer"); MetaCoreExpect(childIterator->MeshRenderer.has_value(), "mesh child should have MeshRenderer");
MetaCoreExpect(childIterator->MeshRenderer->MeshAssetGuid == meshAsset.AssetGuid, "子节点应绑定生成 mesh 资源"); MetaCoreExpect(childIterator->MeshRenderer->MeshAssetGuid == meshAsset.AssetGuid, "child node should bind generated mesh asset");
MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids.size() == 1, "子节点应绑定材质槽位"); MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids.size() == 2, "child node should bind all material slots");
MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids.front() == materialAsset.AssetGuid, "子节点应绑定生成材质资源"); MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids.front() == materialAsset.AssetGuid, "child node should bind generated material asset");
MetaCoreExpect(editorContext.GetActiveObjectId() == instantiatedRoot->Id, "多节点实例化后应选中根对象"); MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids[1] == secondaryMaterialAsset.AssetGuid, "child node should bind secondary material asset");
MetaCoreExpect(editorContext.GetActiveObjectId() == instantiatedRoot->Id, "multi-node instantiation should select asset root");
coreServicesModule->Shutdown(moduleRegistry); coreServicesModule->Shutdown(moduleRegistry);
moduleRegistry.ShutdownServices(); moduleRegistry.ShutdownServices();
_putenv_s("METACORE_PROJECT_PATH", ""); _putenv_s("METACORE_PROJECT_PATH", "");
@ -1105,11 +1197,14 @@ void MetaCoreTestModelReimportKeepsGeneratedAssetReferencesStable() {
MetaCoreExpect(instantiatedRootId.has_value(), "应能实例化模型资源"); MetaCoreExpect(instantiatedRootId.has_value(), "应能实例化模型资源");
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId); const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr && instantiatedRoot->MeshRenderer.has_value(), "应能找到实例化后的模型对象"); MetaCoreExpect(instantiatedRoot != nullptr, "instantiated model root should exist");
const std::vector<MetaCore::MetaCoreId> instantiatedChildren = scene.GetChildrenOf(*instantiatedRootId);
const MetaCore::MetaCoreAssetGuid originalMeshGuid = instantiatedRoot->MeshRenderer->MeshAssetGuid; MetaCoreExpect(instantiatedChildren.size() == 1, "instantiated model root should contain renderable child");
const MetaCore::MetaCoreAssetGuid originalMaterialGuid = instantiatedRoot->MeshRenderer->MaterialAssetGuids.front(); const MetaCore::MetaCoreGameObject* instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
MetaCoreExpect(instantiatedRenderable != nullptr && instantiatedRenderable->MeshRenderer.has_value(), "instantiated renderable model object should exist");
const MetaCore::MetaCoreAssetGuid originalMeshGuid = instantiatedRenderable->MeshRenderer->MeshAssetGuid;
const MetaCore::MetaCoreAssetGuid originalMaterialGuid = instantiatedRenderable->MeshRenderer->MaterialAssetGuids.front();
{ {
std::ofstream gltfFile(modelPath, std::ios::trunc); std::ofstream gltfFile(modelPath, std::ios::trunc);
gltfFile gltfFile
@ -1145,11 +1240,12 @@ void MetaCoreTestModelReimportKeepsGeneratedAssetReferencesStable() {
MetaCoreExpect(refreshedDocument.GeneratedMaterialAssets.front().AssetGuid == originalMaterialGuid, "重导入后材质子资源 GUID 应保持稳定"); MetaCoreExpect(refreshedDocument.GeneratedMaterialAssets.front().AssetGuid == originalMaterialGuid, "重导入后材质子资源 GUID 应保持稳定");
instantiatedRoot = scene.FindGameObject(*instantiatedRootId); instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr && instantiatedRoot->MeshRenderer.has_value(), "重导入后场景实例仍应存在"); MetaCoreExpect(instantiatedRoot != nullptr, "reimport should keep model root alive");
MetaCoreExpect(instantiatedRoot->MeshRenderer->MeshAssetGuid == originalMeshGuid, "重导入后场景实例的 mesh 引用应保持"); instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
MetaCoreExpect(!instantiatedRoot->MeshRenderer->MaterialAssetGuids.empty(), "重导入后场景实例应保留材质引用"); MetaCoreExpect(instantiatedRenderable != nullptr && instantiatedRenderable->MeshRenderer.has_value(), "reimport should keep renderable instance alive");
MetaCoreExpect(instantiatedRoot->MeshRenderer->MaterialAssetGuids.front() == originalMaterialGuid, "重导入后场景实例的材质引用应保持"); MetaCoreExpect(instantiatedRenderable->MeshRenderer->MeshAssetGuid == originalMeshGuid, "reimport should keep mesh reference stable");
MetaCoreExpect(!instantiatedRenderable->MeshRenderer->MaterialAssetGuids.empty(), "reimport should keep material references");
MetaCoreExpect(instantiatedRenderable->MeshRenderer->MaterialAssetGuids.front() == originalMaterialGuid, "reimport should keep material reference stable");
coreServicesModule->Shutdown(moduleRegistry); coreServicesModule->Shutdown(moduleRegistry);
moduleRegistry.ShutdownServices(); moduleRegistry.ShutdownServices();
_putenv_s("METACORE_PROJECT_PATH", ""); _putenv_s("METACORE_PROJECT_PATH", "");

89894
third_party/entt/entt.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ namespace {
MetaCore::MetaCoreSceneDocument document; MetaCore::MetaCoreSceneDocument document;
document.Name = "Main"; document.Name = "Main";
const MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene(); const MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene();
document.GameObjects = scene.GetGameObjects(); document.GameObjects = scene.CaptureSnapshot().GameObjects;
return document; return document;
} }

View File

@ -3,6 +3,7 @@
"version-string": "1.0.0", "version-string": "1.0.0",
"dependencies": [ "dependencies": [
"glm", "glm",
"entt",
{ {
"name": "imgui", "name": "imgui",
"default-features": false, "default-features": false,