Extract scene interaction logic into a dedicated service
This commit is contained in:
parent
3409fec66b
commit
2ed3737c90
@ -156,6 +156,7 @@ set(METACORE_EDITOR_HEADERS
|
||||
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorCommandService.h
|
||||
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorContext.h
|
||||
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorModule.h
|
||||
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreSceneInteractionService.h
|
||||
)
|
||||
|
||||
set(METACORE_EDITOR_PRIVATE_HEADERS
|
||||
@ -171,6 +172,7 @@ set(METACORE_EDITOR_SOURCES
|
||||
Source/MetaCoreEditor/Private/MetaCoreEditorCommandService.cpp
|
||||
Source/MetaCoreEditor/Private/MetaCoreEditorContext.cpp
|
||||
Source/MetaCoreEditor/Private/MetaCoreEditorModule.cpp
|
||||
Source/MetaCoreEditor/Private/MetaCoreSceneInteractionService.cpp
|
||||
third_party/ImGuizmo/ImGuizmo.cpp
|
||||
)
|
||||
|
||||
|
||||
10
SandboxProject/Library/Layout/NullEditorShell_snapshot.txt
Normal file
10
SandboxProject/Library/Layout/NullEditorShell_snapshot.txt
Normal file
@ -0,0 +1,10 @@
|
||||
Shell=NullEditorShell, Actions=11, Panels=8, Hierarchy=4, Assets=3, Inspector=UI Root, Console=3, Status=Focused selected object, Toolbar=on, RuntimePreview=on
|
||||
Workspace[Default]
|
||||
- Main Menu (main_menu) @ TopMenuBar visible=true
|
||||
- Toolbar (toolbar) @ TopToolbar visible=true
|
||||
- Hierarchy (hierarchy) @ LeftSidebar visible=true
|
||||
- Project (project) @ LeftSidebar visible=true
|
||||
- Viewport (viewport) @ CenterViewport visible=true
|
||||
- Inspector (inspector) @ RightSidebar visible=true
|
||||
- Console (console) @ BottomConsole visible=true
|
||||
- Runtime Preview (runtime_preview) @ BottomRuntimePreview visible=true
|
||||
@ -103,6 +103,38 @@
|
||||
1
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"editor_metadata": {
|
||||
"editor_only": false,
|
||||
"locked": false,
|
||||
"selected": false
|
||||
},
|
||||
"id": "0000399eef6307dc90859f938da98af700000003",
|
||||
"name": "UI Root",
|
||||
"parent_id": "",
|
||||
"transform": {
|
||||
"position": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"rotation": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
"ui_document": {
|
||||
"document_path": "Assets/UI/main_menu.rml",
|
||||
"stylesheet_path": "Assets/UI/main_menu.rcss",
|
||||
"visible": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -26,9 +26,7 @@
|
||||
#include <glm/gtx/matrix_decompose.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
@ -174,136 +172,6 @@ ImGuizmo::MODE MetaCoreToImGuizmoMode(MetaCoreGizmoMode mode) {
|
||||
return mode == MetaCoreGizmoMode::World ? ImGuizmo::WORLD : ImGuizmo::LOCAL;
|
||||
}
|
||||
|
||||
MetaCoreId MetaCorePickGameObjectFromViewport(
|
||||
const MetaCoreScene& scene,
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreSceneViewportState& viewportState,
|
||||
const glm::vec2& cursorPosition
|
||||
) {
|
||||
if (viewportState.Width <= 1.0F || viewportState.Height <= 1.0F) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const glm::vec2 localCursor(
|
||||
cursorPosition.x - viewportState.Left,
|
||||
cursorPosition.y - viewportState.Top
|
||||
);
|
||||
|
||||
if (localCursor.x < 0.0F || localCursor.y < 0.0F || localCursor.x > viewportState.Width || localCursor.y > viewportState.Height) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const glm::mat4 viewMatrix = glm::lookAt(
|
||||
sceneView.CameraPosition,
|
||||
sceneView.CameraTarget,
|
||||
sceneView.CameraUp
|
||||
);
|
||||
const glm::mat4 projectionMatrix = glm::perspective(
|
||||
glm::radians(sceneView.VerticalFieldOfViewDegrees),
|
||||
viewportState.Width / viewportState.Height,
|
||||
0.05F,
|
||||
500.0F
|
||||
);
|
||||
|
||||
const float normalizedX = (localCursor.x / viewportState.Width) * 2.0F - 1.0F;
|
||||
const float normalizedY = 1.0F - (localCursor.y / viewportState.Height) * 2.0F;
|
||||
const glm::vec4 rayStartClip(normalizedX, normalizedY, -1.0F, 1.0F);
|
||||
const glm::vec4 rayEndClip(normalizedX, normalizedY, 1.0F, 1.0F);
|
||||
const glm::mat4 inverseViewProjection = glm::inverse(projectionMatrix * viewMatrix);
|
||||
glm::vec4 rayStartWorld = inverseViewProjection * rayStartClip;
|
||||
glm::vec4 rayEndWorld = inverseViewProjection * rayEndClip;
|
||||
if (std::abs(rayStartWorld.w) < 0.0001F || std::abs(rayEndWorld.w) < 0.0001F) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rayStartWorld /= rayStartWorld.w;
|
||||
rayEndWorld /= rayEndWorld.w;
|
||||
|
||||
const glm::vec3 rayOrigin(rayStartWorld);
|
||||
const glm::vec3 rayDirection = glm::normalize(glm::vec3(rayEndWorld - rayStartWorld));
|
||||
|
||||
std::unordered_map<MetaCoreId, glm::mat4> worldMatrixCache;
|
||||
const auto buildWorldMatrix = [&](const auto& self, MetaCoreId objectId) -> glm::mat4 {
|
||||
if (objectId == 0) {
|
||||
return glm::mat4(1.0F);
|
||||
}
|
||||
|
||||
if (const auto cacheIterator = worldMatrixCache.find(objectId); cacheIterator != worldMatrixCache.end()) {
|
||||
return cacheIterator->second;
|
||||
}
|
||||
|
||||
const MetaCoreGameObject* object = scene.FindGameObject(objectId);
|
||||
if (object == nullptr) {
|
||||
return glm::mat4(1.0F);
|
||||
}
|
||||
|
||||
glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object->Transform);
|
||||
if (object->ParentId != 0) {
|
||||
worldMatrix = self(self, object->ParentId) * worldMatrix;
|
||||
}
|
||||
|
||||
worldMatrixCache.emplace(objectId, worldMatrix);
|
||||
return worldMatrix;
|
||||
};
|
||||
|
||||
MetaCoreId bestObjectId = 0;
|
||||
float bestHitDistance = std::numeric_limits<float>::max();
|
||||
|
||||
for (const MetaCoreGameObject& gameObject : scene.GetGameObjects()) {
|
||||
if (!gameObject.MeshRenderer.has_value() || !gameObject.MeshRenderer->Visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const glm::mat4 worldMatrix = buildWorldMatrix(buildWorldMatrix, gameObject.Id);
|
||||
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)));
|
||||
|
||||
constexpr glm::vec3 localBoundsMin(-0.5F, -0.5F, -0.5F);
|
||||
constexpr glm::vec3 localBoundsMax(0.5F, 0.5F, 0.5F);
|
||||
|
||||
float nearDistance = 0.0F;
|
||||
float farDistance = bestHitDistance;
|
||||
bool hit = true;
|
||||
|
||||
for (int axisIndex = 0; axisIndex < 3; ++axisIndex) {
|
||||
const float rayAxisOrigin = localRayOrigin[axisIndex];
|
||||
const float rayAxisDirection = localRayDirection[axisIndex];
|
||||
|
||||
if (std::abs(rayAxisDirection) < 0.0001F) {
|
||||
if (rayAxisOrigin < localBoundsMin[axisIndex] || rayAxisOrigin > localBoundsMax[axisIndex]) {
|
||||
hit = false;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
float axisNearDistance = (localBoundsMin[axisIndex] - rayAxisOrigin) / rayAxisDirection;
|
||||
float axisFarDistance = (localBoundsMax[axisIndex] - rayAxisOrigin) / rayAxisDirection;
|
||||
if (axisNearDistance > axisFarDistance) {
|
||||
std::swap(axisNearDistance, axisFarDistance);
|
||||
}
|
||||
|
||||
nearDistance = std::max(nearDistance, axisNearDistance);
|
||||
farDistance = std::min(farDistance, axisFarDistance);
|
||||
if (nearDistance > farDistance) {
|
||||
hit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit && farDistance >= 0.0F) {
|
||||
const float hitDistance = nearDistance >= 0.0F ? nearDistance : farDistance;
|
||||
if (hitDistance >= 0.0F && hitDistance < bestHitDistance) {
|
||||
bestHitDistance = hitDistance;
|
||||
bestObjectId = gameObject.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestObjectId;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MetaCoreEditorApp::~MetaCoreEditorApp() {
|
||||
@ -435,10 +303,6 @@ void MetaCoreEditorApp::ShutdownImGui() {
|
||||
}
|
||||
|
||||
void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
static bool gizmoWasUsing = false;
|
||||
static bool hasGizmoBeforeSnapshot = false;
|
||||
static MetaCoreEditorStateSnapshot gizmoBeforeSnapshot{};
|
||||
|
||||
const ImGuiViewport* mainViewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(mainViewport->WorkPos);
|
||||
ImGui::SetNextWindowSize(mainViewport->WorkSize);
|
||||
@ -639,10 +503,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
|
||||
gizmoHovering = ImGuizmo::IsOver();
|
||||
gizmoUsing = ImGuizmo::IsUsing();
|
||||
if (gizmoUsing && !gizmoWasUsing) {
|
||||
gizmoBeforeSnapshot = EditorContext_->CaptureStateSnapshot();
|
||||
hasGizmoBeforeSnapshot = true;
|
||||
}
|
||||
SceneInteractionService_.HandleGizmoBeginUse(*EditorContext_, gizmoUsing);
|
||||
if (gizmoUsing) {
|
||||
// transformMatrix is in Panda space — apply directly.
|
||||
switch (EditorContext_->GetGizmoOperation()) {
|
||||
@ -691,38 +552,17 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
}
|
||||
|
||||
if (viewportState.Hovered && !gizmoHovering && !gizmoUsing && !ImGui::GetIO().WantCaptureMouse && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
const MetaCoreId pickedObjectId = MetaCorePickGameObjectFromViewport(
|
||||
const MetaCoreId pickedObjectId = SceneInteractionService_.PickGameObjectFromViewport(
|
||||
Scene_,
|
||||
sceneView,
|
||||
viewportState,
|
||||
EditorContext_->GetInput().GetCursorPosition()
|
||||
);
|
||||
const bool ctrlDown = ImGui::GetIO().KeyCtrl;
|
||||
const bool shiftDown = ImGui::GetIO().KeyShift;
|
||||
if (pickedObjectId != 0) {
|
||||
if (shiftDown) {
|
||||
EditorContext_->SelectRangeByOrderedIds(Scene_.BuildHierarchyPreorder(), pickedObjectId, ctrlDown);
|
||||
} else if (ctrlDown) {
|
||||
EditorContext_->ToggleSelection(pickedObjectId);
|
||||
} else {
|
||||
EditorContext_->SelectOnly(pickedObjectId);
|
||||
}
|
||||
if (const MetaCoreGameObject* selectedObject = EditorContext_->GetSelectedGameObject(); selectedObject != nullptr) {
|
||||
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "视口已选中对象: " + selectedObject->Name);
|
||||
}
|
||||
sceneView.SelectedObjectId = EditorContext_->GetActiveObjectId();
|
||||
} else if (!ctrlDown && !shiftDown) {
|
||||
EditorContext_->ClearSelection();
|
||||
sceneView.SelectedObjectId = 0;
|
||||
}
|
||||
SceneInteractionService_.ApplyViewportSelection(*EditorContext_, Scene_, pickedObjectId);
|
||||
sceneView.SelectedObjectId = EditorContext_->GetActiveObjectId();
|
||||
}
|
||||
|
||||
if (!gizmoUsing && gizmoWasUsing && hasGizmoBeforeSnapshot) {
|
||||
const MetaCoreEditorStateSnapshot gizmoAfterSnapshot = EditorContext_->CaptureStateSnapshot();
|
||||
(void)EditorContext_->CommitStateTransition("修改变换", gizmoBeforeSnapshot, gizmoAfterSnapshot, true);
|
||||
hasGizmoBeforeSnapshot = false;
|
||||
}
|
||||
gizmoWasUsing = gizmoUsing;
|
||||
SceneInteractionService_.HandleGizmoEndUse(*EditorContext_, gizmoUsing);
|
||||
|
||||
ImGui::SetNextWindowPos(centralNode->Pos);
|
||||
ImGui::SetNextWindowSize(centralNode->Size);
|
||||
@ -830,13 +670,11 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
|
||||
ImGui::PopStyleVar(3);
|
||||
} else {
|
||||
gizmoWasUsing = false;
|
||||
hasGizmoBeforeSnapshot = false;
|
||||
SceneInteractionService_.ResetFrameState();
|
||||
ViewportRenderer_.SetViewportRect(MetaCoreViewportRect{});
|
||||
}
|
||||
} else {
|
||||
gizmoWasUsing = false;
|
||||
hasGizmoBeforeSnapshot = false;
|
||||
SceneInteractionService_.ResetFrameState();
|
||||
ViewportRenderer_.SetViewportRect(MetaCoreViewportRect{});
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,207 @@
|
||||
#include "MetaCoreEditor/MetaCoreSceneInteractionService.h"
|
||||
|
||||
#include "MetaCoreScene/MetaCoreScene.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/ext/matrix_clip_space.hpp>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
namespace {
|
||||
|
||||
glm::mat4 MetaCoreBuildTransformMatrix(const MetaCoreTransformComponent& transform) {
|
||||
const glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0F), transform.Position);
|
||||
const glm::mat4 rotationMatrix = glm::yawPitchRoll(
|
||||
glm::radians(transform.RotationEulerDegrees.y),
|
||||
glm::radians(transform.RotationEulerDegrees.x),
|
||||
glm::radians(transform.RotationEulerDegrees.z)
|
||||
);
|
||||
const glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0F), transform.Scale);
|
||||
return translationMatrix * rotationMatrix * scaleMatrix;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void MetaCoreSceneInteractionService::ResetFrameState() {
|
||||
GizmoWasUsing_ = false;
|
||||
HasGizmoBeforeSnapshot_ = false;
|
||||
}
|
||||
|
||||
MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
|
||||
const MetaCoreScene& scene,
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreSceneViewportState& viewportState,
|
||||
const glm::vec2& cursorPosition
|
||||
) const {
|
||||
if (viewportState.Width <= 1.0F || viewportState.Height <= 1.0F) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const glm::vec2 localCursor(
|
||||
cursorPosition.x - viewportState.Left,
|
||||
cursorPosition.y - viewportState.Top
|
||||
);
|
||||
|
||||
if (localCursor.x < 0.0F || localCursor.y < 0.0F || localCursor.x > viewportState.Width || localCursor.y > viewportState.Height) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const glm::mat4 viewMatrix = glm::lookAt(
|
||||
sceneView.CameraPosition,
|
||||
sceneView.CameraTarget,
|
||||
sceneView.CameraUp
|
||||
);
|
||||
const glm::mat4 projectionMatrix = glm::perspective(
|
||||
glm::radians(sceneView.VerticalFieldOfViewDegrees),
|
||||
viewportState.Width / viewportState.Height,
|
||||
0.05F,
|
||||
500.0F
|
||||
);
|
||||
|
||||
const float normalizedX = (localCursor.x / viewportState.Width) * 2.0F - 1.0F;
|
||||
const float normalizedY = 1.0F - (localCursor.y / viewportState.Height) * 2.0F;
|
||||
const glm::vec4 rayStartClip(normalizedX, normalizedY, -1.0F, 1.0F);
|
||||
const glm::vec4 rayEndClip(normalizedX, normalizedY, 1.0F, 1.0F);
|
||||
const glm::mat4 inverseViewProjection = glm::inverse(projectionMatrix * viewMatrix);
|
||||
glm::vec4 rayStartWorld = inverseViewProjection * rayStartClip;
|
||||
glm::vec4 rayEndWorld = inverseViewProjection * rayEndClip;
|
||||
if (std::abs(rayStartWorld.w) < 0.0001F || std::abs(rayEndWorld.w) < 0.0001F) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rayStartWorld /= rayStartWorld.w;
|
||||
rayEndWorld /= rayEndWorld.w;
|
||||
|
||||
const glm::vec3 rayOrigin(rayStartWorld);
|
||||
const glm::vec3 rayDirection = glm::normalize(glm::vec3(rayEndWorld - rayStartWorld));
|
||||
|
||||
std::unordered_map<MetaCoreId, glm::mat4> worldMatrixCache;
|
||||
const auto buildWorldMatrix = [&](const auto& self, MetaCoreId objectId) -> glm::mat4 {
|
||||
if (objectId == 0) {
|
||||
return glm::mat4(1.0F);
|
||||
}
|
||||
|
||||
if (const auto cacheIterator = worldMatrixCache.find(objectId); cacheIterator != worldMatrixCache.end()) {
|
||||
return cacheIterator->second;
|
||||
}
|
||||
|
||||
const MetaCoreGameObject* object = scene.FindGameObject(objectId);
|
||||
if (object == nullptr) {
|
||||
return glm::mat4(1.0F);
|
||||
}
|
||||
|
||||
glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(object->Transform);
|
||||
if (object->ParentId != 0) {
|
||||
worldMatrix = self(self, object->ParentId) * worldMatrix;
|
||||
}
|
||||
|
||||
worldMatrixCache.emplace(objectId, worldMatrix);
|
||||
return worldMatrix;
|
||||
};
|
||||
|
||||
MetaCoreId bestObjectId = 0;
|
||||
float bestHitDistance = std::numeric_limits<float>::max();
|
||||
|
||||
for (const MetaCoreGameObject& gameObject : scene.GetGameObjects()) {
|
||||
if (!gameObject.MeshRenderer.has_value() || !gameObject.MeshRenderer->Visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const glm::mat4 worldMatrix = buildWorldMatrix(buildWorldMatrix, gameObject.Id);
|
||||
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)));
|
||||
|
||||
constexpr glm::vec3 localBoundsMin(-0.5F, -0.5F, -0.5F);
|
||||
constexpr glm::vec3 localBoundsMax(0.5F, 0.5F, 0.5F);
|
||||
|
||||
float nearDistance = 0.0F;
|
||||
float farDistance = bestHitDistance;
|
||||
bool hit = true;
|
||||
|
||||
for (int axisIndex = 0; axisIndex < 3; ++axisIndex) {
|
||||
const float rayAxisOrigin = localRayOrigin[axisIndex];
|
||||
const float rayAxisDirection = localRayDirection[axisIndex];
|
||||
|
||||
if (std::abs(rayAxisDirection) < 0.0001F) {
|
||||
if (rayAxisOrigin < localBoundsMin[axisIndex] || rayAxisOrigin > localBoundsMax[axisIndex]) {
|
||||
hit = false;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
float axisNearDistance = (localBoundsMin[axisIndex] - rayAxisOrigin) / rayAxisDirection;
|
||||
float axisFarDistance = (localBoundsMax[axisIndex] - rayAxisOrigin) / rayAxisDirection;
|
||||
if (axisNearDistance > axisFarDistance) {
|
||||
std::swap(axisNearDistance, axisFarDistance);
|
||||
}
|
||||
|
||||
nearDistance = std::max(nearDistance, axisNearDistance);
|
||||
farDistance = std::min(farDistance, axisFarDistance);
|
||||
if (nearDistance > farDistance) {
|
||||
hit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit && farDistance >= 0.0F) {
|
||||
const float hitDistance = nearDistance >= 0.0F ? nearDistance : farDistance;
|
||||
if (hitDistance >= 0.0F && hitDistance < bestHitDistance) {
|
||||
bestHitDistance = hitDistance;
|
||||
bestObjectId = gameObject.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestObjectId;
|
||||
}
|
||||
|
||||
void MetaCoreSceneInteractionService::ApplyViewportSelection(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreScene& scene,
|
||||
MetaCoreId pickedObjectId
|
||||
) const {
|
||||
const bool ctrlDown = ImGui::GetIO().KeyCtrl;
|
||||
const bool shiftDown = ImGui::GetIO().KeyShift;
|
||||
if (pickedObjectId != 0) {
|
||||
if (shiftDown) {
|
||||
editorContext.SelectRangeByOrderedIds(scene.BuildHierarchyPreorder(), pickedObjectId, ctrlDown);
|
||||
} else if (ctrlDown) {
|
||||
editorContext.ToggleSelection(pickedObjectId);
|
||||
} else {
|
||||
editorContext.SelectOnly(pickedObjectId);
|
||||
}
|
||||
if (const MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject(); selectedObject != nullptr) {
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "视口已选中对象: " + selectedObject->Name);
|
||||
}
|
||||
} else if (!ctrlDown && !shiftDown) {
|
||||
editorContext.ClearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreSceneInteractionService::HandleGizmoBeginUse(MetaCoreEditorContext& editorContext, bool gizmoUsing) {
|
||||
if (gizmoUsing && !GizmoWasUsing_) {
|
||||
GizmoBeforeSnapshot_ = editorContext.CaptureStateSnapshot();
|
||||
HasGizmoBeforeSnapshot_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreSceneInteractionService::HandleGizmoEndUse(MetaCoreEditorContext& editorContext, bool gizmoUsing) {
|
||||
if (!gizmoUsing && GizmoWasUsing_ && HasGizmoBeforeSnapshot_) {
|
||||
const MetaCoreEditorStateSnapshot gizmoAfterSnapshot = editorContext.CaptureStateSnapshot();
|
||||
(void)editorContext.CommitStateTransition("修改变换", GizmoBeforeSnapshot_, gizmoAfterSnapshot, true);
|
||||
HasGizmoBeforeSnapshot_ = false;
|
||||
}
|
||||
GizmoWasUsing_ = gizmoUsing;
|
||||
}
|
||||
|
||||
} // namespace MetaCore
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "MetaCoreEditor/MetaCoreEditorContext.h"
|
||||
#include "MetaCoreEditor/MetaCoreEditorModule.h"
|
||||
#include "MetaCoreEditor/MetaCoreSceneInteractionService.h"
|
||||
|
||||
#include "MetaCoreFoundation/MetaCoreLogService.h"
|
||||
#include "MetaCorePlatform/MetaCoreWindow.h"
|
||||
@ -38,6 +39,7 @@ private:
|
||||
MetaCoreScene Scene_{};
|
||||
MetaCoreLogService LogService_{};
|
||||
MetaCoreEditorModuleRegistry ModuleRegistry_{};
|
||||
MetaCoreSceneInteractionService SceneInteractionService_{};
|
||||
std::vector<std::unique_ptr<MetaCoreIModule>> Modules_{};
|
||||
std::unique_ptr<MetaCoreEditorContext> EditorContext_{};
|
||||
bool Initialized_ = false;
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "MetaCoreEditor/MetaCoreEditorContext.h"
|
||||
#include "MetaCoreRender/MetaCoreRenderTypes.h"
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
class MetaCoreScene;
|
||||
|
||||
// 管理 Scene 视口交互:拾取选择与 Gizmo 变换提交。
|
||||
class MetaCoreSceneInteractionService {
|
||||
public:
|
||||
void ResetFrameState();
|
||||
|
||||
[[nodiscard]] MetaCoreId PickGameObjectFromViewport(
|
||||
const MetaCoreScene& scene,
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreSceneViewportState& viewportState,
|
||||
const glm::vec2& cursorPosition
|
||||
) const;
|
||||
|
||||
void ApplyViewportSelection(MetaCoreEditorContext& editorContext, const MetaCoreScene& scene, MetaCoreId pickedObjectId) const;
|
||||
void HandleGizmoBeginUse(MetaCoreEditorContext& editorContext, bool gizmoUsing);
|
||||
void HandleGizmoEndUse(MetaCoreEditorContext& editorContext, bool gizmoUsing);
|
||||
|
||||
private:
|
||||
bool GizmoWasUsing_ = false;
|
||||
bool HasGizmoBeforeSnapshot_ = false;
|
||||
MetaCoreEditorStateSnapshot GizmoBeforeSnapshot_{};
|
||||
};
|
||||
|
||||
} // namespace MetaCore
|
||||
Loading…
Reference in New Issue
Block a user