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

View File

@ -86,35 +86,35 @@ private:
return false;
}
if (lhs.Camera.has_value() != rhs.Camera.has_value()) {
if (lhs.HasComponent<MetaCoreCameraComponent>() != rhs.HasComponent<MetaCoreCameraComponent>()) {
return false;
}
if (lhs.Camera.has_value()) {
if (!MetaCoreNearlyEqual(lhs.Camera->FieldOfViewDegrees, rhs.Camera->FieldOfViewDegrees) ||
!MetaCoreNearlyEqual(lhs.Camera->NearClip, rhs.Camera->NearClip) ||
!MetaCoreNearlyEqual(lhs.Camera->FarClip, rhs.Camera->FarClip) ||
lhs.Camera->IsPrimary != rhs.Camera->IsPrimary) {
if (lhs.HasComponent<MetaCoreCameraComponent>()) {
if (!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().FieldOfViewDegrees, rhs.GetComponent<MetaCoreCameraComponent>().FieldOfViewDegrees) ||
!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().NearClip, rhs.GetComponent<MetaCoreCameraComponent>().NearClip) ||
!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().FarClip, rhs.GetComponent<MetaCoreCameraComponent>().FarClip) ||
lhs.GetComponent<MetaCoreCameraComponent>().IsPrimary != rhs.GetComponent<MetaCoreCameraComponent>().IsPrimary) {
return false;
}
}
if (lhs.MeshRenderer.has_value() != rhs.MeshRenderer.has_value()) {
if (lhs.HasComponent<MetaCoreMeshRendererComponent>() != rhs.HasComponent<MetaCoreMeshRendererComponent>()) {
return false;
}
if (lhs.MeshRenderer.has_value()) {
if (lhs.MeshRenderer->BuiltinMesh != rhs.MeshRenderer->BuiltinMesh ||
!MetaCoreNearlyEqualVec3(lhs.MeshRenderer->BaseColor, rhs.MeshRenderer->BaseColor) ||
lhs.MeshRenderer->Visible != rhs.MeshRenderer->Visible) {
if (lhs.HasComponent<MetaCoreMeshRendererComponent>()) {
if (lhs.GetComponent<MetaCoreMeshRendererComponent>().BuiltinMesh != rhs.GetComponent<MetaCoreMeshRendererComponent>().BuiltinMesh ||
!MetaCoreNearlyEqualVec3(lhs.GetComponent<MetaCoreMeshRendererComponent>().BaseColor, rhs.GetComponent<MetaCoreMeshRendererComponent>().BaseColor) ||
lhs.GetComponent<MetaCoreMeshRendererComponent>().Visible != rhs.GetComponent<MetaCoreMeshRendererComponent>().Visible) {
return false;
}
}
if (lhs.Light.has_value() != rhs.Light.has_value()) {
if (lhs.HasComponent<MetaCoreLightComponent>() != rhs.HasComponent<MetaCoreLightComponent>()) {
return false;
}
if (lhs.Light.has_value()) {
if (!MetaCoreNearlyEqualVec3(lhs.Light->Color, rhs.Light->Color) ||
!MetaCoreNearlyEqual(lhs.Light->Intensity, rhs.Light->Intensity)) {
if (lhs.HasComponent<MetaCoreLightComponent>()) {
if (!MetaCoreNearlyEqualVec3(lhs.GetComponent<MetaCoreLightComponent>().Color, rhs.GetComponent<MetaCoreLightComponent>().Color) ||
!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreLightComponent>().Intensity, rhs.GetComponent<MetaCoreLightComponent>().Intensity)) {
return false;
}
}
@ -330,8 +330,8 @@ void MetaCoreEditorContext::SelectRangeByOrderedIds(const std::vector<MetaCoreId
MetaCoreId MetaCoreEditorContext::GetSelectedObjectId() const { return ActiveObjectId_; }
void MetaCoreEditorContext::SetSelectedObjectId(MetaCoreId objectId) { SelectOnly(objectId); }
MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() { return Scene_.FindGameObject(ActiveObjectId_); }
const MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() const { return Scene_.FindGameObject(ActiveObjectId_); }
MetaCoreGameObject MetaCoreEditorContext::GetSelectedGameObject() { return Scene_.FindGameObject(ActiveObjectId_); }
const MetaCoreGameObject MetaCoreEditorContext::GetSelectedGameObject() const { return Scene_.FindGameObject(ActiveObjectId_); }
MetaCoreGizmoOperation MetaCoreEditorContext::GetGizmoOperation() const { return GizmoOperation_; }
void MetaCoreEditorContext::SetGizmoOperation(MetaCoreGizmoOperation operation) { GizmoOperation_ = operation; }
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) {
const MetaCoreGameObject* object = scene.FindGameObject(objectId);
if (object == nullptr) {
const MetaCoreGameObject object = scene.FindGameObject(objectId);
if (!object) {
return glm::mat4(1.0F);
}
glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object->Transform);
MetaCoreId parentId = object->ParentId;
glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object.GetComponent<MetaCoreTransformComponent>());
MetaCoreId parentId = object.GetParentId();
while (parentId != 0) {
const MetaCoreGameObject* parentObject = scene.FindGameObject(parentId);
if (parentObject == nullptr) {
const MetaCoreGameObject parentObject = scene.FindGameObject(parentId);
if (!parentObject) {
break;
}
worldMatrix = MetaCoreBuildTransformMatrix(parentObject->Transform) * worldMatrix;
parentId = parentObject->ParentId;
worldMatrix = MetaCoreBuildTransformMatrix(parentObject.GetComponent<MetaCoreTransformComponent>()) * worldMatrix;
parentId = parentObject.GetParentId();
}
return worldMatrix;
@ -170,14 +170,14 @@ MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
return cacheIterator->second;
}
const MetaCoreGameObject* object = scene.FindGameObject(objectId);
if (object == nullptr) {
const MetaCoreGameObject object = scene.FindGameObject(objectId);
if (!object) {
return glm::mat4(1.0F);
}
glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object->Transform);
if (object->ParentId != 0) {
worldMatrix = self(self, object->ParentId) * worldMatrix;
glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object.GetComponent<MetaCoreTransformComponent>());
if (object.GetParentId() != 0) {
worldMatrix = self(self, object.GetParentId()) * worldMatrix;
}
worldMatrixCache.emplace(objectId, worldMatrix);
@ -188,11 +188,11 @@ MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
float bestHitDistance = std::numeric_limits<float>::max();
for (const MetaCoreGameObject& gameObject : scene.GetGameObjects()) {
if (!gameObject.MeshRenderer.has_value() || !gameObject.MeshRenderer->Visible) {
if (!gameObject.HasComponent<MetaCoreMeshRendererComponent>() || !gameObject.GetComponent<MetaCoreMeshRendererComponent>().Visible) {
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::vec3 localRayOrigin = glm::vec3(inverseWorldMatrix * glm::vec4(rayOrigin, 1.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;
if (hitDistance >= 0.0F && hitDistance < bestHitDistance) {
bestHitDistance = hitDistance;
bestObjectId = gameObject.Id;
bestObjectId = gameObject.GetId();
}
}
}
@ -285,8 +285,8 @@ void MetaCoreSceneInteractionService::FocusSelectedObject(MetaCoreEditorContext&
}
if (selectedObjectIds.size() == 1) {
MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject();
if (selectedObject != nullptr) {
MetaCoreGameObject selectedObject = editorContext.GetSelectedGameObject();
if (selectedObject) {
editorContext.GetCameraController().FocusGameObject(*selectedObject);
}
return;
@ -296,19 +296,19 @@ void MetaCoreSceneInteractionService::FocusSelectedObject(MetaCoreEditorContext&
glm::vec3 boundsMin(0.0F);
glm::vec3 boundsMax(0.0F);
for (const MetaCoreId selectedObjectId : selectedObjectIds) {
const MetaCoreGameObject* selectedObject = editorContext.GetScene().FindGameObject(selectedObjectId);
if (selectedObject == nullptr) {
const MetaCoreGameObject selectedObject = editorContext.GetScene().FindGameObject(selectedObjectId);
if (!selectedObject) {
continue;
}
const float extent = std::max({
std::abs(selectedObject->Transform.Scale.x),
std::abs(selectedObject->Transform.Scale.y),
std::abs(selectedObject->Transform.Scale.z),
std::abs(selectedObject.GetComponent<MetaCoreTransformComponent>().Scale.x),
std::abs(selectedObject.GetComponent<MetaCoreTransformComponent>().Scale.y),
std::abs(selectedObject.GetComponent<MetaCoreTransformComponent>().Scale.z),
0.5F
});
const glm::vec3 objectMin = selectedObject->Transform.Position - glm::vec3(extent);
const glm::vec3 objectMax = selectedObject->Transform.Position + glm::vec3(extent);
const glm::vec3 objectMin = selectedObject.GetComponent<MetaCoreTransformComponent>().Position - glm::vec3(extent);
const glm::vec3 objectMax = selectedObject.GetComponent<MetaCoreTransformComponent>().Position + glm::vec3(extent);
if (!initialized) {
boundsMin = objectMin;
@ -338,18 +338,18 @@ void MetaCoreSceneInteractionService::FocusVisibleScene(MetaCoreEditorContext& e
glm::vec3 boundsMax(0.0F);
for (const MetaCoreGameObject& gameObject : gameObjects) {
if (gameObject.MeshRenderer.has_value() && !gameObject.MeshRenderer->Visible) {
if (gameObject.HasComponent<MetaCoreMeshRendererComponent>() && !gameObject.GetComponent<MetaCoreMeshRendererComponent>().Visible) {
continue;
}
const float extent = std::max({
std::abs(gameObject.Transform.Scale.x),
std::abs(gameObject.Transform.Scale.y),
std::abs(gameObject.Transform.Scale.z),
std::abs(gameObject.GetComponent<MetaCoreTransformComponent>().Scale.x),
std::abs(gameObject.GetComponent<MetaCoreTransformComponent>().Scale.y),
std::abs(gameObject.GetComponent<MetaCoreTransformComponent>().Scale.z),
0.5F
});
const glm::vec3 objectMin = gameObject.Transform.Position - glm::vec3(extent);
const glm::vec3 objectMax = gameObject.Transform.Position + glm::vec3(extent);
const glm::vec3 objectMin = gameObject.GetComponent<MetaCoreTransformComponent>().Position - glm::vec3(extent);
const glm::vec3 objectMax = gameObject.GetComponent<MetaCoreTransformComponent>().Position + glm::vec3(extent);
if (!initialized) {
boundsMin = objectMin;
@ -378,8 +378,8 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
return;
}
MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject();
if (selectedObject == nullptr) {
MetaCoreGameObject selectedObject = editorContext.GetSelectedGameObject();
if (!selectedObject) {
return;
}
@ -392,7 +392,7 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
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;
ImGuizmo::SetOrthographic(false);
@ -429,12 +429,12 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
if (gizmoUsing && gizmoMatrix != originalWorldMatrixMetaCore) {
glm::mat4 newLocalMatrix = gizmoMatrix;
if (selectedObject->ParentId != 0) {
const glm::mat4 parentWorldMatrixMetaCore = MetaCoreBuildWorldTransformMatrix(editorContext.GetScene(), selectedObject->ParentId);
if (selectedObject.GetParentId() != 0) {
const glm::mat4 parentWorldMatrixMetaCore = MetaCoreBuildWorldTransformMatrix(editorContext.GetScene(), selectedObject.GetParentId());
newLocalMatrix = glm::inverse(parentWorldMatrixMetaCore) * gizmoMatrix;
}
MetaCoreApplyMatrixToTransform(newLocalMatrix, selectedObject->Transform);
MetaCoreApplyMatrixToTransform(newLocalMatrix, selectedObject.GetComponent<MetaCoreTransformComponent>());
}
HandleGizmoBeginUse(editorContext, gizmoUsing);
@ -545,13 +545,13 @@ void MetaCoreSceneInteractionService::DrawViewportToolbar(MetaCoreEditorContext&
if (selectedObjectIds.empty()) {
ImGui::TextDisabled("Selection: None");
} else if (selectedObjectIds.size() == 1) {
const MetaCoreGameObject* activeObject = editorContext.GetSelectedGameObject();
const MetaCoreGameObject activeObject = editorContext.GetSelectedGameObject();
ImGui::TextDisabled(
"Selection: %s",
activeObject != nullptr ? activeObject->Name.c_str() : "<Missing>"
);
} else {
const MetaCoreGameObject* activeObject = editorContext.GetSelectedGameObject();
const MetaCoreGameObject activeObject = editorContext.GetSelectedGameObject();
ImGui::TextDisabled(
"Selection: %zu objects | Active: %s",
selectedObjectIds.size(),

View File

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

View File

@ -1,6 +1,7 @@
#pragma once
#include "MetaCoreFoundation/MetaCoreAssetGuid.h"
#include "MetaCoreFoundation/MetaCoreAssetTypes.h"
#include "MetaCoreFoundation/MetaCoreId.h"
#include "MetaCoreFoundation/MetaCorePackage.h"
#include "MetaCoreFoundation/MetaCoreReflection.h"
@ -154,6 +155,45 @@ public:
[[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 {
public:
[[nodiscard]] virtual bool CookAsset(const MetaCoreAssetGuid& assetGuid) = 0;

View File

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

View File

@ -35,6 +35,9 @@ struct MetaCoreTextureAssetDocument {
MC_PROPERTY()
std::string Name{};
MC_PROPERTY()
std::string StableImportKey{};
MC_PROPERTY()
std::filesystem::path SourcePath{};
@ -66,6 +69,12 @@ struct MetaCoreMeshSubMeshDocument {
MC_PROPERTY()
std::int32_t MaterialSlotIndex = -1;
MC_PROPERTY()
std::uint32_t FirstIndex = 0;
MC_PROPERTY()
std::uint32_t IndexCount = 0;
};
MC_STRUCT()
@ -78,6 +87,9 @@ struct MetaCoreMeshAssetDocument {
MC_PROPERTY()
std::string Name{};
MC_PROPERTY()
std::string StableImportKey{};
MC_PROPERTY()
std::int32_t VertexCount = 0;
@ -110,6 +122,9 @@ struct MetaCoreMaterialAssetDocument {
MC_PROPERTY()
std::string Name{};
MC_PROPERTY()
std::string StableImportKey{};
MC_PROPERTY()
MetaCoreMaterialShaderModel ShaderModel = MetaCoreMaterialShaderModel::PbrMetalRough;
@ -157,6 +172,76 @@ enum class MetaCoreModelAssetKind {
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()
struct MetaCoreImportedGltfMeshDocument {
MC_GENERATED_BODY()
@ -206,6 +291,9 @@ struct MetaCoreImportedGltfNodeDocument {
MC_PROPERTY()
std::string Name{};
MC_PROPERTY()
std::string StableNodePath{};
MC_PROPERTY()
std::int32_t ParentIndex = -1;
@ -241,6 +329,12 @@ struct MetaCoreImportedGltfAssetDocument {
MC_PROPERTY()
std::string SourceFormat{};
MC_PROPERTY()
MetaCoreModelImportSettingsDocument ImportSettings{};
MC_PROPERTY()
MetaCoreModelImportReportDocument ImportReport{};
MC_PROPERTY()
std::vector<MetaCoreImportedGltfMeshDocument> Meshes{};
@ -271,6 +365,9 @@ struct MetaCoreModelAssetDocument {
MC_PROPERTY()
std::string AssetType{};
MC_PROPERTY()
std::string RootDisplayName{};
MC_PROPERTY()
std::string ImporterId{};
@ -286,6 +383,12 @@ struct MetaCoreModelAssetDocument {
MC_PROPERTY()
std::string SourceFormat{};
MC_PROPERTY()
MetaCoreModelImportSettingsDocument ImportSettings{};
MC_PROPERTY()
MetaCoreModelImportReportDocument ImportReport{};
MC_PROPERTY()
std::vector<MetaCoreImportedGltfMeshDocument> Meshes{};

View File

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

View File

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

View File

@ -189,7 +189,7 @@ bool MetaCoreWindow::Initialize(int width, int height, const std::string& title)
return true;
}
MetaCoreWindowTrace("metacore.window: configure backend");
MetaCoreWindowTrace("metacore.window: configure runtime backend");
MetaCoreConfigurePanda3D(title);
MetaCoreWindowTrace("metacore.window: 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");
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
MetaCoreWindowTrace("metacore.window: set foreground");

View File

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

View File

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

View File

@ -63,12 +63,12 @@ bool MetaCoreEditorViewportRenderer::Initialize(MetaCoreRenderDevice& renderDevi
MetaCoreTrace("metacore.render: viewport init enter");
Shutdown();
MetaCoreTrace("metacore.render: viewport scene bridge begin");
MetaCoreTrace("metacore.render: viewport scene runtime bind begin");
if (!SceneBridge_.Initialize(renderDevice)) {
MetaCoreTrace("metacore.render: viewport scene bridge failed");
MetaCoreTrace("metacore.render: viewport scene runtime bind failed");
return false;
}
MetaCoreTrace("metacore.render: viewport scene bridge done");
MetaCoreTrace("metacore.render: viewport scene runtime ready");
RenderDevice_ = &renderDevice;
ViewportRect_ = MetaCoreViewportRect{};
@ -108,20 +108,13 @@ void MetaCoreEditorViewportRenderer::RenderSceneToViewport(const MetaCoreScene&
RenderDevice_->SetSceneViewportRect(ViewportRect_);
SceneBridge_.ApplySceneView(sceneView);
#if defined(_DEBUG)
if (!DebugSceneSyncOverrideEnabled_) {
(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
static bool loggedDebugRenderableSync = 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()) {
SceneSyncDegraded_ = true;
LastSceneSyncFailure_ = SceneBridge_.GetLastRuntimeSyncFailure();
@ -133,6 +126,22 @@ void MetaCoreEditorViewportRenderer::RenderSceneToViewport(const MetaCoreScene&
LastSceneSyncFailure_.clear();
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) {

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,18 @@
#pragma once
#include "MetaCoreFoundation/MetaCoreId.h"
#include "MetaCoreFoundation/MetaCoreAssetGuid.h"
#include "MetaCoreFoundation/MetaCoreId.h"
#include "MetaCoreFoundation/MetaCoreReflection.h"
#include "MetaCoreScene/MetaCoreComponents.h"
#include <entt/entt.hpp>
#include <optional>
#include <string>
namespace MetaCore {
class MetaCoreScene;
MC_STRUCT()
struct MetaCorePrefabInstanceMetadata {
MC_GENERATED_BODY()
@ -24,9 +27,88 @@ struct MetaCorePrefabInstanceMetadata {
MetaCoreId PrefabInstanceRootId = 0;
};
// Component structs for basic properties
MC_STRUCT()
// 表示一个 Unity 风格的场景对象。
struct MetaCoreGameObject {
struct MetaCoreIdComponent {
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_PROPERTY()

View File

@ -1,10 +1,12 @@
#pragma once
#include "MetaCoreFoundation/MetaCoreReflection.h"
#include "MetaCoreScene/MetaCoreSceneDocument.h"
#include "MetaCoreScene/MetaCoreGameObject.h"
#include "MetaCoreScene/MetaCoreSceneDocument.h"
#include <entt/entt.hpp>
#include <vector>
#include <unordered_map>
namespace MetaCore {
@ -13,61 +15,85 @@ struct MetaCoreSceneSnapshot {
MC_GENERATED_BODY()
MC_PROPERTY()
std::vector<MetaCoreGameObject> GameObjects;
std::vector<MetaCoreGameObjectData> GameObjects;
};
// 负责存储和查询场景中的全部对象。
class MetaCoreScene {
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]] const MetaCoreGameObject* FindGameObject(MetaCoreId objectId) const;
[[nodiscard]] MetaCoreGameObject FindGameObject(MetaCoreId objectId);
[[nodiscard]] MetaCoreGameObject FindGameObject(MetaCoreId objectId) const;
// 返回全部对象。
[[nodiscard]] std::vector<MetaCoreGameObject>& GetGameObjects();
[[nodiscard]] const std::vector<MetaCoreGameObject>& GetGameObjects() const;
[[nodiscard]] std::vector<MetaCoreGameObject> GetGameObjects();
[[nodiscard]] std::vector<MetaCoreGameObject> GetGameObjects() const;
// 返回根节点对象列表。
[[nodiscard]] std::vector<MetaCoreId> GetRootObjectIds() const;
// 返回某个对象的直接子对象列表。
[[nodiscard]] std::vector<MetaCoreId> GetChildrenOf(MetaCoreId parentId) const;
// 按层级前序遍历顺序返回对象标识列表。
[[nodiscard]] std::vector<MetaCoreId> BuildHierarchyPreorder() const;
// 返回指定根对象的完整子树标识(含自身)。
[[nodiscard]] std::vector<MetaCoreId> GetSubtreeObjectIds(MetaCoreId rootId) const;
// 判断对象是否是另一个对象的后代。
[[nodiscard]] bool IsDescendantOf(MetaCoreId objectId, MetaCoreId ancestorId) const;
// 重命名对象。
bool RenameGameObject(MetaCoreId objectId, const std::string& name);
// 删除对象(包含其子树)。返回实际删除的全部对象标识。
[[nodiscard]] std::vector<MetaCoreId> DeleteGameObjects(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);
// 场景快照捕获与恢复。
[[nodiscard]] MetaCoreSceneSnapshot CaptureSnapshot() const;
void RestoreSnapshot(const MetaCoreSceneSnapshot& snapshot);
entt::registry& GetRegistry() { return Registry_; }
const entt::registry& GetRegistry() const { return Registry_; }
private:
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();
// 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

View File

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

Binary file not shown.

View File

@ -193,7 +193,7 @@ void MetaCoreTestScenePackageProjectStartupLoad() {
MetaCore::MetaCoreSceneDocument sceneDocument;
sceneDocument.Name = "Main";
const MetaCore::MetaCoreScene sourceScene = MetaCore::MetaCoreCreateDefaultScene();
sceneDocument.GameObjects = sourceScene.GetGameObjects();
sceneDocument.GameObjects = sourceScene.CaptureSnapshot().GameObjects;
const std::filesystem::path scenePath = tempProjectRoot / "Scenes" / "Main.mcscene";
MetaCoreExpect(MetaCore::MetaCoreWriteScenePackage(scenePath, sceneDocument), "应能写入二进制场景包");
@ -334,7 +334,7 @@ void MetaCoreTestAssetDatabaseRenamePathUpdatesProjectDescriptor() {
MetaCore::MetaCoreSceneDocument sceneDocument;
sceneDocument.Name = "Main";
sceneDocument.GameObjects = MetaCore::MetaCoreCreateDefaultScene().GetGameObjects();
sceneDocument.GameObjects = MetaCore::MetaCoreCreateDefaultScene().CaptureSnapshot().GameObjects;
MetaCoreExpect(
MetaCore::MetaCoreWriteScenePackage(tempProjectRoot / "Scenes" / "Main.mcscene", sceneDocument),
"应能写入场景包"
@ -396,7 +396,7 @@ void MetaCoreTestAssetDatabaseMovePathUpdatesProjectDescriptor() {
MetaCore::MetaCoreSceneDocument sceneDocument;
sceneDocument.Name = "Main";
sceneDocument.GameObjects = MetaCore::MetaCoreCreateDefaultScene().GetGameObjects();
sceneDocument.GameObjects = MetaCore::MetaCoreCreateDefaultScene().CaptureSnapshot().GameObjects;
MetaCoreExpect(
MetaCore::MetaCoreWriteScenePackage(tempProjectRoot / "Scenes" / "Main.mcscene", sceneDocument),
"应能写入场景包"
@ -567,6 +567,7 @@ void MetaCoreTestImportPipelineAndCook() {
std::filesystem::temp_directory_path() / "MetaCoreImportCookProject";
std::filesystem::remove_all(tempProjectRoot);
std::filesystem::create_directories(tempProjectRoot / "Assets");
std::filesystem::create_directories(tempProjectRoot / "Assets" / "Textures");
std::filesystem::create_directories(tempProjectRoot / "Scenes");
std::filesystem::create_directories(tempProjectRoot / "Library");
@ -631,6 +632,7 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
std::filesystem::temp_directory_path() / "MetaCoreProjectGltfImporterProject";
std::filesystem::remove_all(tempProjectRoot);
std::filesystem::create_directories(tempProjectRoot / "Assets");
std::filesystem::create_directories(tempProjectRoot / "Assets" / "Textures");
std::filesystem::create_directories(tempProjectRoot / "Scenes");
std::filesystem::create_directories(tempProjectRoot / "ConfigRuntime");
std::filesystem::create_directories(tempProjectRoot / "UiDocuments");
@ -649,6 +651,15 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
<< "}\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);
gltfFile
@ -659,7 +670,23 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
<< " \"meshes\": [{\"name\": \"ValveMesh\", \"primitives\": [{\"material\": 0}]}],\n"
<< " \"materials\": [{\"name\": \"ValveMaterial\", \"pbrMetallicRoughness\": {\"baseColorTexture\": {\"index\": 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";
}
@ -687,9 +714,11 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
const auto assetDatabase = moduleRegistry.ResolveService<MetaCore::MetaCoreIAssetDatabaseService>();
const auto packageService = moduleRegistry.ResolveService<MetaCore::MetaCoreIPackageService>();
const auto reflectionRegistry = moduleRegistry.ResolveService<MetaCore::MetaCoreIReflectionRegistry>();
const auto assetEditingService = moduleRegistry.ResolveService<MetaCore::MetaCoreIAssetEditingService>();
MetaCoreExpect(assetDatabase != nullptr, "应解析到 AssetDatabaseService");
MetaCoreExpect(packageService != nullptr, "应解析到 PackageService");
MetaCoreExpect(reflectionRegistry != nullptr, "应解析到 ReflectionRegistry");
MetaCoreExpect(assetEditingService != nullptr, "应解析到 AssetEditingService");
const auto& project = assetDatabase->GetProjectDescriptor();
MetaCoreExpect(project.RuntimePath.filename() == "ConfigRuntime", "项目应读取 runtime_directory");
@ -725,7 +754,7 @@ void MetaCoreTestProjectDescriptorAndGltfImporterSkeleton() {
MetaCoreExpect(gltfDocument.Meshes.front().PrimitiveCount == 1, "glTF mesh 骨架应写入 primitive 数");
MetaCoreExpect(!gltfDocument.Materials.empty() && gltfDocument.Materials.front().Name == "ValveMaterial", "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.GeneratedMaterialAssets.size() == gltfDocument.Materials.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.GeneratedTextureAssets.front().AssetGuid.IsValid(), "生成的纹理资源应有 GUID");
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");
MetaCoreExpect(glbRecord.has_value(), "应能找到 glb 资源记录");
@ -882,20 +944,25 @@ void MetaCoreTestInstantiateImportedModelAssetIntoScene() {
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr, "实例化后应能找到根对象");
MetaCoreExpect(instantiatedRoot->Name == "Pump", "实例化根对象名称应来自导入节点");
MetaCoreExpect(instantiatedRoot->MeshRenderer.has_value(), "导入模型实例应带 MeshRenderer");
MetaCoreExpect(instantiatedRoot->MeshRenderer->MeshSource == MetaCore::MetaCoreMeshSourceKind::Asset, "导入模型实例应使用 Asset MeshSource");
MetaCoreExpect(instantiatedRoot->MeshRenderer->MeshAssetGuid.IsValid(), "导入模型实例应绑定 MeshAssetGuid");
MetaCoreExpect(!instantiatedRoot->MeshRenderer->MaterialAssetGuids.empty(), "导入模型实例应绑定材质资源");
MetaCoreExpect(instantiatedRoot->Name == "Pump.gltf", "model root should use source file name");
const std::vector<MetaCore::MetaCoreId> instantiatedChildren = scene.GetChildrenOf(*instantiatedRootId);
MetaCoreExpect(instantiatedChildren.size() == 1, "model root should contain imported node");
const MetaCore::MetaCoreGameObject* instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
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(
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(
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, "实例化后应选中新对象");
coreServicesModule->Shutdown(moduleRegistry);
@ -981,8 +1048,9 @@ void MetaCoreTestInstantiateMultiNodeModelAssetIntoScene() {
MetaCore::MetaCoreImportedGltfMeshDocument meshDocument;
meshDocument.Name = "ValveMesh";
meshDocument.PrimitiveCount = 1;
meshDocument.PrimitiveCount = 2;
meshDocument.MaterialSlots.push_back(0);
meshDocument.MaterialSlots.push_back(1);
document.Meshes.push_back(meshDocument);
MetaCore::MetaCoreMaterialAssetDocument materialAsset;
@ -990,9 +1058,26 @@ void MetaCoreTestInstantiateMultiNodeModelAssetIntoScene() {
materialAsset.Name = "ValveMaterial";
document.GeneratedMaterialAssets.push_back(materialAsset);
MetaCore::MetaCoreMaterialAssetDocument secondaryMaterialAsset;
secondaryMaterialAsset.AssetGuid = MetaCore::MetaCoreAssetGuid::Generate();
secondaryMaterialAsset.Name = "ValveSecondaryMaterial";
document.GeneratedMaterialAssets.push_back(secondaryMaterialAsset);
MetaCore::MetaCoreMeshAssetDocument meshAsset;
meshAsset.AssetGuid = MetaCore::MetaCoreAssetGuid::Generate();
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);
const auto payload = MetaCore::MetaCoreSerializeToBytes(document, reflectionRegistry->GetTypeRegistry());
@ -1021,20 +1106,27 @@ void MetaCoreTestInstantiateMultiNodeModelAssetIntoScene() {
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr, "多节点实例化后应能找到根对象");
MetaCoreExpect(instantiatedRoot->Name == "AssemblyRoot", "多节点实例化根对象应保留节点名称");
MetaCoreExpect(!instantiatedRoot->MeshRenderer.has_value(), "无 mesh 的根节点不应强制带 MeshRenderer");
MetaCoreExpect(instantiatedRoot->Name == "Assembly.gltf", "multi-node model root should use source file name");
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) {
return object.Name == "Valve_A";
});
MetaCoreExpect(childIterator != scene.GetGameObjects().end(), "多节点实例化应创建子节点对象");
MetaCoreExpect(childIterator->ParentId == instantiatedRoot->Id, "子节点应挂在根节点下");
MetaCoreExpect(childIterator->MeshRenderer.has_value(), "带 mesh 的子节点应带 MeshRenderer");
MetaCoreExpect(childIterator->MeshRenderer->MeshAssetGuid == meshAsset.AssetGuid, "子节点应绑定生成 mesh 资源");
MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids.size() == 1, "子节点应绑定材质槽位");
MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids.front() == materialAsset.AssetGuid, "子节点应绑定生成材质资源");
MetaCoreExpect(editorContext.GetActiveObjectId() == instantiatedRoot->Id, "多节点实例化后应选中根对象");
MetaCoreExpect(childIterator != scene.GetGameObjects().end(), "multi-node instantiation should create child node object");
MetaCoreExpect(childIterator->ParentId == importedRootIterator->Id, "child node should be parented under imported root node");
MetaCoreExpect(childIterator->MeshRenderer.has_value(), "mesh child should have MeshRenderer");
MetaCoreExpect(childIterator->MeshRenderer->MeshAssetGuid == meshAsset.AssetGuid, "child node should bind generated mesh asset");
MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids.size() == 2, "child node should bind all material slots");
MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids.front() == materialAsset.AssetGuid, "child node should bind generated material asset");
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);
moduleRegistry.ShutdownServices();
_putenv_s("METACORE_PROJECT_PATH", "");
@ -1105,11 +1197,14 @@ void MetaCoreTestModelReimportKeepsGeneratedAssetReferencesStable() {
MetaCoreExpect(instantiatedRootId.has_value(), "应能实例化模型资源");
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr && instantiatedRoot->MeshRenderer.has_value(), "应能找到实例化后的模型对象");
const MetaCore::MetaCoreAssetGuid originalMeshGuid = instantiatedRoot->MeshRenderer->MeshAssetGuid;
const MetaCore::MetaCoreAssetGuid originalMaterialGuid = instantiatedRoot->MeshRenderer->MaterialAssetGuids.front();
MetaCoreExpect(instantiatedRoot != nullptr, "instantiated model root should exist");
const std::vector<MetaCore::MetaCoreId> instantiatedChildren = scene.GetChildrenOf(*instantiatedRootId);
MetaCoreExpect(instantiatedChildren.size() == 1, "instantiated model root should contain renderable child");
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);
gltfFile
@ -1145,11 +1240,12 @@ void MetaCoreTestModelReimportKeepsGeneratedAssetReferencesStable() {
MetaCoreExpect(refreshedDocument.GeneratedMaterialAssets.front().AssetGuid == originalMaterialGuid, "重导入后材质子资源 GUID 应保持稳定");
instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr && instantiatedRoot->MeshRenderer.has_value(), "重导入后场景实例仍应存在");
MetaCoreExpect(instantiatedRoot->MeshRenderer->MeshAssetGuid == originalMeshGuid, "重导入后场景实例的 mesh 引用应保持");
MetaCoreExpect(!instantiatedRoot->MeshRenderer->MaterialAssetGuids.empty(), "重导入后场景实例应保留材质引用");
MetaCoreExpect(instantiatedRoot->MeshRenderer->MaterialAssetGuids.front() == originalMaterialGuid, "重导入后场景实例的材质引用应保持");
MetaCoreExpect(instantiatedRoot != nullptr, "reimport should keep model root alive");
instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
MetaCoreExpect(instantiatedRenderable != nullptr && instantiatedRenderable->MeshRenderer.has_value(), "reimport should keep renderable instance alive");
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);
moduleRegistry.ShutdownServices();
_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;
document.Name = "Main";
const MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene();
document.GameObjects = scene.GetGameObjects();
document.GameObjects = scene.CaptureSnapshot().GameObjects;
return document;
}

View File

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