From b12c88c478fccdf1fa7c647cdfb57ffc910c7658 Mon Sep 17 00:00:00 2001 From: ayuan9957 <107920784+ayuan9957@users.noreply.github.com> Date: Tue, 23 Jun 2026 16:09:40 +0800 Subject: [PATCH] feat(editor): add scene view selection markers --- .../Private/MetaCoreEditorApp.cpp | 133 +++++++++++++++++- 1 file changed, 127 insertions(+), 6 deletions(-) diff --git a/Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp b/Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp index df91b80..bdb8921 100644 --- a/Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp +++ b/Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp @@ -4,6 +4,7 @@ #include "MetaCoreEditor/MetaCoreEditorServices.h" #include "MetaCoreFoundation/MetaCoreProject.h" #include "MetaCoreEditorCameraController.h" +#include "MetaCoreScene/MetaCoreTransformUtils.h" #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -297,6 +299,26 @@ bool MetaCoreProjectWorldPointToViewport( return true; } +glm::mat4 MetaCoreBuildEditorOverlayWorldMatrix(const MetaCoreScene& scene, MetaCoreId objectId) { + const MetaCoreGameObject object = scene.FindGameObject(objectId); + if (!object) { + return glm::mat4(1.0F); + } + + glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object.GetComponent()); + MetaCoreId parentId = object.GetParentId(); + while (parentId != 0) { + const MetaCoreGameObject parentObject = scene.FindGameObject(parentId); + if (!parentObject) { + break; + } + worldMatrix = MetaCoreBuildTransformMatrix(parentObject.GetComponent()) * worldMatrix; + parentId = parentObject.GetParentId(); + } + + return worldMatrix; +} + void MetaCoreDrawWorldOriginOverlay( const MetaCoreEditorContext&, const MetaCoreSceneView&, @@ -459,13 +481,112 @@ void MetaCoreDrawViewportGridOverlay( } void MetaCoreDrawSelectionBoundsOverlay( - const MetaCoreEditorContext&, - const MetaCoreScene&, - const MetaCoreSceneView&, - const MetaCoreSceneViewportState& + const MetaCoreEditorContext& editorContext, + const MetaCoreScene& scene, + const MetaCoreSceneView& sceneView, + const MetaCoreSceneViewportState& viewportState ) { - // 用户要求去掉左下角的黄色框和文字 - return; + if (!editorContext.GetShowViewportSelectionOverlay() || viewportState.Width <= 1.0F || viewportState.Height <= 1.0F) { + return; + } + + ImDrawList* drawList = ImGui::GetForegroundDrawList(); + const ImVec2 clipMin(viewportState.Left, viewportState.Top); + const ImVec2 clipMax(viewportState.Left + viewportState.Width, viewportState.Top + viewportState.Height); + + const auto drawObjectMarker = [&](MetaCoreId objectId, ImU32 color, float thickness, float minimumSize) { + const MetaCoreGameObject gameObject = scene.FindGameObject(objectId); + if (!gameObject) { + return; + } + if (gameObject.HasComponent() && !gameObject.GetComponent().Visible) { + return; + } + + const glm::mat4 worldMatrix = MetaCoreBuildEditorOverlayWorldMatrix(scene, objectId); + const std::array localCorners = { + glm::vec3(-0.5F, -0.5F, -0.5F), + glm::vec3( 0.5F, -0.5F, -0.5F), + glm::vec3(-0.5F, 0.5F, -0.5F), + glm::vec3( 0.5F, 0.5F, -0.5F), + glm::vec3(-0.5F, -0.5F, 0.5F), + glm::vec3( 0.5F, -0.5F, 0.5F), + glm::vec3(-0.5F, 0.5F, 0.5F), + glm::vec3( 0.5F, 0.5F, 0.5F) + }; + + bool anyPointVisible = false; + ImVec2 minPoint(FLT_MAX, FLT_MAX); + ImVec2 maxPoint(-FLT_MAX, -FLT_MAX); + for (const glm::vec3& localCorner : localCorners) { + const glm::vec3 worldCorner = glm::vec3(worldMatrix * glm::vec4(localCorner, 1.0F)); + ImVec2 screenPoint{}; + if (!MetaCoreProjectWorldPointToViewport(sceneView, viewportState, worldCorner, 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) { + const glm::vec3 worldPosition = glm::vec3(worldMatrix[3]); + ImVec2 screenPoint{}; + if (!MetaCoreProjectWorldPointToViewport(sceneView, viewportState, worldPosition, screenPoint)) { + return; + } + minPoint = screenPoint; + maxPoint = screenPoint; + } + + const float halfMinimumSize = minimumSize * 0.5F; + if (maxPoint.x - minPoint.x < minimumSize) { + const float centerX = (minPoint.x + maxPoint.x) * 0.5F; + minPoint.x = centerX - halfMinimumSize; + maxPoint.x = centerX + halfMinimumSize; + } + if (maxPoint.y - minPoint.y < minimumSize) { + const float centerY = (minPoint.y + maxPoint.y) * 0.5F; + minPoint.y = centerY - halfMinimumSize; + maxPoint.y = centerY + halfMinimumSize; + } + + minPoint.x = std::clamp(minPoint.x, clipMin.x, clipMax.x); + minPoint.y = std::clamp(minPoint.y, clipMin.y, clipMax.y); + maxPoint.x = std::clamp(maxPoint.x, clipMin.x, clipMax.x); + maxPoint.y = std::clamp(maxPoint.y, clipMin.y, clipMax.y); + if (maxPoint.x <= minPoint.x || maxPoint.y <= minPoint.y) { + return; + } + + const float bracketLength = std::min({18.0F, (maxPoint.x - minPoint.x) * 0.35F, (maxPoint.y - minPoint.y) * 0.35F}); + drawList->AddLine(minPoint, ImVec2(minPoint.x + bracketLength, minPoint.y), color, thickness); + drawList->AddLine(minPoint, ImVec2(minPoint.x, minPoint.y + bracketLength), color, thickness); + drawList->AddLine(ImVec2(maxPoint.x, minPoint.y), ImVec2(maxPoint.x - bracketLength, minPoint.y), color, thickness); + drawList->AddLine(ImVec2(maxPoint.x, minPoint.y), ImVec2(maxPoint.x, minPoint.y + bracketLength), color, thickness); + drawList->AddLine(ImVec2(minPoint.x, maxPoint.y), ImVec2(minPoint.x + bracketLength, maxPoint.y), color, thickness); + drawList->AddLine(ImVec2(minPoint.x, maxPoint.y), ImVec2(minPoint.x, maxPoint.y - bracketLength), color, thickness); + drawList->AddLine(maxPoint, ImVec2(maxPoint.x - bracketLength, maxPoint.y), color, thickness); + drawList->AddLine(maxPoint, ImVec2(maxPoint.x, maxPoint.y - bracketLength), color, thickness); + }; + + drawList->PushClipRect(clipMin, clipMax, true); + const MetaCoreId hoveredObjectId = editorContext.GetHoveredObjectId(); + if (hoveredObjectId != 0 && !editorContext.IsObjectSelected(hoveredObjectId)) { + drawObjectMarker(hoveredObjectId, IM_COL32(110, 175, 255, 190), 1.5F, 18.0F); + } + for (const MetaCoreId selectedObjectId : editorContext.GetSelectedObjectIds()) { + const bool isActiveObject = selectedObjectId == editorContext.GetActiveObjectId(); + drawObjectMarker( + selectedObjectId, + isActiveObject ? IM_COL32(235, 87, 87, 245) : IM_COL32(235, 135, 135, 205), + isActiveObject ? 2.0F : 1.5F, + 22.0F + ); + } + drawList->PopClipRect(); } void MetaCoreDrawCompatibilityScenePreviewOverlay(