Improve editor viewport gizmo and Panda3D camera integration
This commit is contained in:
parent
633156e3be
commit
ea6291fba3
11
.codex/environments/environment.toml
Normal file
11
.codex/environments/environment.toml
Normal file
@ -0,0 +1,11 @@
|
||||
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
|
||||
version = 1
|
||||
name = "MetaCore"
|
||||
|
||||
[setup]
|
||||
script = ""
|
||||
|
||||
[[actions]]
|
||||
name = "运行"
|
||||
icon = "run"
|
||||
command = "D:\\MetaCore\\build\\vs2022-release\\Release\\./MetaCoreEditorApp.exe"
|
||||
@ -160,6 +160,7 @@ set(METACORE_EDITOR_HEADERS
|
||||
set(METACORE_EDITOR_PRIVATE_HEADERS
|
||||
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h
|
||||
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h
|
||||
third_party/ImGuizmo/ImGuizmo.h
|
||||
)
|
||||
|
||||
set(METACORE_EDITOR_SOURCES
|
||||
@ -168,6 +169,7 @@ set(METACORE_EDITOR_SOURCES
|
||||
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp
|
||||
Source/MetaCoreEditor/Private/MetaCoreEditorContext.cpp
|
||||
Source/MetaCoreEditor/Private/MetaCoreEditorModule.cpp
|
||||
third_party/ImGuizmo/ImGuizmo.cpp
|
||||
)
|
||||
|
||||
add_library(MetaCoreEditor STATIC
|
||||
@ -181,6 +183,7 @@ target_include_directories(MetaCoreEditor
|
||||
Source/MetaCoreEditor/Public
|
||||
PRIVATE
|
||||
Source/MetaCoreEditor/Private
|
||||
third_party/ImGuizmo
|
||||
)
|
||||
|
||||
target_link_libraries(MetaCoreEditor
|
||||
|
||||
@ -62,3 +62,9 @@ cmake --build --preset build-release
|
||||
- `Inspector` 中编辑 `Transform`
|
||||
- `Alt+左键 / 中键 / 滚轮 / F` 相机交互
|
||||
- 模块注册接口与内置编辑器模块
|
||||
|
||||
## 技术支持连接
|
||||
|
||||
https://github.com/CedricGuillemet/ImGuizmo
|
||||
|
||||
https://docs.panda3d.org/1.9/cpp/programming/using-cpp/index
|
||||
@ -136,62 +136,7 @@ public:
|
||||
std::string GetPanelTitle() const override { return "场景"; }
|
||||
bool IsOpenByDefault() const override { return true; }
|
||||
|
||||
void DrawPanel(MetaCoreEditorContext& editorContext) override {
|
||||
MetaCoreSceneViewportState& viewportState = editorContext.GetSceneViewportState();
|
||||
ImVec2 viewportSize = ImGui::GetContentRegionAvail();
|
||||
if (viewportSize.x < 1.0F) {
|
||||
viewportSize.x = 1.0F;
|
||||
}
|
||||
if (viewportSize.y < 1.0F) {
|
||||
viewportSize.y = 1.0F;
|
||||
}
|
||||
|
||||
const ImVec2 viewportScreenPosition = ImGui::GetCursorScreenPos();
|
||||
ImGui::InvisibleButton("MetaCoreSceneViewport", viewportSize, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonMiddle);
|
||||
|
||||
const ImVec2 mainViewportPosition = ImGui::GetMainViewport()->Pos;
|
||||
viewportState.Left = viewportScreenPosition.x - mainViewportPosition.x;
|
||||
viewportState.Top = viewportScreenPosition.y - mainViewportPosition.y;
|
||||
viewportState.Width = viewportSize.x;
|
||||
viewportState.Height = viewportSize.y;
|
||||
viewportState.Hovered = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
|
||||
viewportState.Focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
|
||||
|
||||
if (viewportState.Focused && !ImGui::GetIO().WantCaptureKeyboard && editorContext.GetInput().WasKeyPressed(MetaCoreInputKey::Focus)) {
|
||||
if (MetaCoreGameObject* selectedObject = editorContext.GetSelectedGameObject(); selectedObject != nullptr) {
|
||||
editorContext.GetCameraController().FocusGameObject(*selectedObject);
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "相机已聚焦到当前对象");
|
||||
}
|
||||
}
|
||||
|
||||
editorContext.GetCameraController().Update(viewportState, editorContext.GetInput());
|
||||
|
||||
MetaCoreSceneView sceneView = editorContext.GetCameraController().BuildSceneView();
|
||||
sceneView.SelectedObjectId = editorContext.GetSelectedObjectId();
|
||||
editorContext.GetViewportRenderer().SetViewportRect(MetaCoreViewportRect{
|
||||
viewportState.Left,
|
||||
viewportState.Top,
|
||||
viewportState.Width,
|
||||
viewportState.Height
|
||||
});
|
||||
editorContext.GetViewportRenderer().RenderSceneToViewport(editorContext.GetScene(), sceneView);
|
||||
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
drawList->AddRect(
|
||||
viewportScreenPosition,
|
||||
ImVec2(viewportScreenPosition.x + viewportSize.x, viewportScreenPosition.y + viewportSize.y),
|
||||
IM_COL32(96, 108, 122, 220),
|
||||
0.0F,
|
||||
0,
|
||||
1.0F
|
||||
);
|
||||
|
||||
const char* helpText = "Alt+左键 环绕 | 中键 平移 | 滚轮 缩放 | F 聚焦";
|
||||
drawList->AddText(
|
||||
ImVec2(viewportScreenPosition.x + 12.0F, viewportScreenPosition.y + 10.0F),
|
||||
IM_COL32(220, 224, 230, 220),
|
||||
helpText
|
||||
);
|
||||
void DrawPanel(MetaCoreEditorContext&) override {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -7,16 +7,27 @@
|
||||
#include <imgui_internal.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#include <imgui_impl_win32.h>
|
||||
#include "ImGuizmo.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <glm/ext/matrix_clip_space.hpp>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/geometric.hpp>
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
#include <glm/gtx/matrix_decompose.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
@ -80,6 +91,194 @@ void MetaCoreConfigureChineseFont() {
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
glm::mat4 MetaCoreBuildPandaBasisMatrix() {
|
||||
glm::mat4 basis(1.0F);
|
||||
basis[0] = glm::vec4(1.0F, 0.0F, 0.0F, 0.0F);
|
||||
basis[1] = glm::vec4(0.0F, 0.0F, 1.0F, 0.0F);
|
||||
basis[2] = glm::vec4(0.0F, -1.0F, 0.0F, 0.0F);
|
||||
basis[3] = glm::vec4(0.0F, 0.0F, 0.0F, 1.0F);
|
||||
return basis;
|
||||
}
|
||||
|
||||
glm::vec3 MetaCoreToPandaSpacePoint(const glm::vec3& value) {
|
||||
return glm::vec3(value.x, -value.z, value.y);
|
||||
}
|
||||
|
||||
glm::vec3 MetaCoreToPandaSpaceVector(const glm::vec3& value) {
|
||||
return glm::vec3(value.x, -value.z, value.y);
|
||||
}
|
||||
|
||||
glm::vec3 MetaCoreFromPandaSpacePoint(const glm::vec3& value) {
|
||||
return glm::vec3(value.x, value.z, -value.y);
|
||||
}
|
||||
|
||||
glm::mat4 MetaCoreBuildPandaSpaceTransformMatrix(const MetaCoreTransformComponent& transform) {
|
||||
const glm::mat4 basis = MetaCoreBuildPandaBasisMatrix();
|
||||
const glm::mat4 inverseBasis = glm::inverse(basis);
|
||||
return basis * MetaCoreBuildTransformMatrix(transform) * inverseBasis;
|
||||
}
|
||||
|
||||
glm::mat4 MetaCoreConvertPandaSpaceMatrixToMetaCoreMatrix(const glm::mat4& pandaSpaceMatrix) {
|
||||
const glm::mat4 basis = MetaCoreBuildPandaBasisMatrix();
|
||||
const glm::mat4 inverseBasis = glm::inverse(basis);
|
||||
return inverseBasis * pandaSpaceMatrix * basis;
|
||||
}
|
||||
|
||||
void MetaCoreApplyMatrixToTransform(const glm::mat4& matrix, MetaCoreTransformComponent& transform) {
|
||||
glm::vec3 skew{};
|
||||
glm::vec4 perspective{};
|
||||
glm::quat rotation{};
|
||||
glm::decompose(matrix, transform.Scale, rotation, transform.Position, skew, perspective);
|
||||
transform.RotationEulerDegrees = glm::degrees(glm::eulerAngles(rotation));
|
||||
}
|
||||
|
||||
void MetaCoreApplyTranslationFromMatrix(const glm::mat4& matrix, MetaCoreTransformComponent& transform) {
|
||||
transform.Position = glm::vec3(matrix[3]);
|
||||
}
|
||||
|
||||
void MetaCoreApplyTranslationFromPandaSpaceMatrix(const glm::mat4& matrix, MetaCoreTransformComponent& transform) {
|
||||
transform.Position = MetaCoreFromPandaSpacePoint(glm::vec3(matrix[3]));
|
||||
}
|
||||
|
||||
void MetaCoreApplyTransformFromPandaSpaceMatrix(const glm::mat4& matrix, MetaCoreTransformComponent& transform) {
|
||||
MetaCoreApplyMatrixToTransform(MetaCoreConvertPandaSpaceMatrixToMetaCoreMatrix(matrix), transform);
|
||||
}
|
||||
|
||||
ImGuizmo::OPERATION MetaCoreToImGuizmoOperation(MetaCoreGizmoOperation operation) {
|
||||
switch (operation) {
|
||||
case MetaCoreGizmoOperation::Translate:
|
||||
return ImGuizmo::TRANSLATE;
|
||||
case MetaCoreGizmoOperation::Rotate:
|
||||
return ImGuizmo::ROTATE;
|
||||
case MetaCoreGizmoOperation::Scale:
|
||||
return ImGuizmo::SCALE;
|
||||
case MetaCoreGizmoOperation::None:
|
||||
default:
|
||||
return ImGuizmo::TRANSLATE;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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 = MetaCoreBuildTransformMatrix(gameObject.Transform);
|
||||
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() {
|
||||
@ -96,12 +295,12 @@ bool MetaCoreEditorApp::Initialize() {
|
||||
}
|
||||
|
||||
Window_.SetNativeWindowMessageHandler([](void* nativeWindowHandle, unsigned int message, std::uintptr_t wparam, std::intptr_t lparam) {
|
||||
ImGui_ImplWin32_WndProcHandler(
|
||||
return ImGui_ImplWin32_WndProcHandler(
|
||||
static_cast<HWND>(nativeWindowHandle),
|
||||
message,
|
||||
static_cast<WPARAM>(wparam),
|
||||
static_cast<LPARAM>(lparam)
|
||||
);
|
||||
) != 0;
|
||||
});
|
||||
|
||||
if (!RenderDevice_.Initialize(Window_)) {
|
||||
@ -153,6 +352,7 @@ int MetaCoreEditorApp::Run() {
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
ImGuizmo::BeginFrame();
|
||||
|
||||
DrawEditorFrame();
|
||||
|
||||
@ -243,10 +443,8 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
EnsureDefaultDockLayout(dockSpaceId);
|
||||
ImGui::End();
|
||||
|
||||
MetaCoreIEditorPanelProvider* scenePanelProvider = nullptr;
|
||||
for (const auto& panelProvider : ModuleRegistry_.GetPanelProviders()) {
|
||||
if (panelProvider->GetPanelId() == "Scene") {
|
||||
scenePanelProvider = panelProvider.get();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -284,23 +482,250 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
}
|
||||
}
|
||||
|
||||
if (scenePanelProvider != nullptr && ModuleRegistry_.IsPanelOpen("Scene")) {
|
||||
if (ModuleRegistry_.IsPanelOpen("Scene")) {
|
||||
ImGuiDockNode* centralNode = ImGui::DockBuilderGetCentralNode(dockSpaceId);
|
||||
if (centralNode != nullptr) {
|
||||
const ImVec2 overlayPosition = centralNode->Pos;
|
||||
const ImVec2 overlaySize = centralNode->Size;
|
||||
constexpr float sceneToolbarHeight = 34.0F;
|
||||
MetaCoreSceneViewportState& viewportState = EditorContext_->GetSceneViewportState();
|
||||
const ImVec2 mainViewportPosition = ImGui::GetMainViewport()->Pos;
|
||||
viewportState.Left = centralNode->Pos.x - mainViewportPosition.x;
|
||||
viewportState.Top = centralNode->Pos.y - mainViewportPosition.y + sceneToolbarHeight;
|
||||
viewportState.Width = centralNode->Size.x;
|
||||
if (viewportState.Width < 1.0F) {
|
||||
viewportState.Width = 1.0F;
|
||||
}
|
||||
viewportState.Height = centralNode->Size.y - sceneToolbarHeight;
|
||||
if (viewportState.Height < 1.0F) {
|
||||
viewportState.Height = 1.0F;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(overlayPosition);
|
||||
ImGui::SetNextWindowSize(overlaySize);
|
||||
const ImVec2 mousePosition = ImGui::GetMousePos();
|
||||
viewportState.Hovered =
|
||||
mousePosition.x >= centralNode->Pos.x &&
|
||||
mousePosition.x <= centralNode->Pos.x + centralNode->Size.x &&
|
||||
mousePosition.y >= centralNode->Pos.y + sceneToolbarHeight &&
|
||||
mousePosition.y <= centralNode->Pos.y + centralNode->Size.y;
|
||||
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) {
|
||||
EditorContext_->GetCameraController().FocusGameObject(*selectedObject);
|
||||
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "相机已聚焦到当前对象");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ImGui::GetIO().WantCaptureKeyboard) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Q)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::None);
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_W)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Translate);
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_E)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Rotate);
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_R)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Scale);
|
||||
}
|
||||
}
|
||||
|
||||
MetaCoreSceneView sceneView = EditorContext_->GetCameraController().BuildSceneView();
|
||||
sceneView.SelectedObjectId = EditorContext_->GetSelectedObjectId();
|
||||
|
||||
ViewportRenderer_.SetViewportRect(MetaCoreViewportRect{
|
||||
viewportState.Left,
|
||||
viewportState.Top,
|
||||
viewportState.Width,
|
||||
viewportState.Height
|
||||
});
|
||||
ViewportRenderer_.RenderSceneToViewport(Scene_, sceneView);
|
||||
|
||||
bool gizmoHovering = false;
|
||||
bool gizmoUsing = false;
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(centralNode->Pos.x, centralNode->Pos.y + sceneToolbarHeight));
|
||||
ImGui::SetNextWindowSize(ImVec2(viewportState.Width, viewportState.Height));
|
||||
ImGui::SetNextWindowBgAlpha(0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0F, 0.0F));
|
||||
|
||||
constexpr ImGuiWindowFlags gizmoCanvasFlags =
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoDocking |
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
ImGuiWindowFlags_NoNavFocus |
|
||||
ImGuiWindowFlags_NoBackground |
|
||||
ImGuiWindowFlags_NoInputs;
|
||||
|
||||
bool gizmoCanvasOpen = true;
|
||||
ImGui::Begin("MetaCoreSceneGizmoCanvas", &gizmoCanvasOpen, gizmoCanvasFlags);
|
||||
|
||||
// Guard against degenerate camera and aspect ratio.
|
||||
const float cameraDistance = glm::distance(sceneView.CameraPosition, sceneView.CameraTarget);
|
||||
const float gizmoAspect = viewportState.Width / viewportState.Height;
|
||||
|
||||
MetaCoreGameObject* selectedObject = EditorContext_->GetSelectedGameObject();
|
||||
if (selectedObject != nullptr &&
|
||||
EditorContext_->GetGizmoOperation() != MetaCoreGizmoOperation::None &&
|
||||
cameraDistance >= 0.001F &&
|
||||
gizmoAspect >= 0.001F) {
|
||||
|
||||
// Per reference guide: use Panda matrices directly.
|
||||
// MetaCoreConvertPandaMatrixToGlm correctly handles Panda row-major→GLM column-major.
|
||||
// TryGetEditorCameraMatrices returns:
|
||||
// viewMatrix = inverse(EditorCamera.get_mat(SceneRoot))
|
||||
// projectionMatrix = lens->get_projection_mat()
|
||||
glm::mat4 gizmoViewMatrix(1.0F);
|
||||
glm::mat4 gizmoProjectionMatrix(1.0F);
|
||||
if (!RenderDevice_.TryGetEditorCameraMatrices(gizmoViewMatrix, gizmoProjectionMatrix)) {
|
||||
gizmoProjectionMatrix = glm::perspective(
|
||||
glm::radians(sceneView.VerticalFieldOfViewDegrees),
|
||||
gizmoAspect, 0.05F, 500.0F
|
||||
);
|
||||
}
|
||||
|
||||
// Model matrix: Panda world matrix used directly.
|
||||
glm::mat4 transformMatrix = MetaCoreBuildPandaSpaceTransformMatrix(selectedObject->Transform);
|
||||
(void)ViewportRenderer_.TryGetObjectWorldMatrix(selectedObject->Id, transformMatrix);
|
||||
const glm::mat4 originalTransformMatrix = transformMatrix;
|
||||
|
||||
ImGuizmo::SetOrthographic(false);
|
||||
ImGuizmo::Enable(true);
|
||||
ImGuizmo::AllowAxisFlip(true);
|
||||
ImGuizmo::SetGizmoSizeClipSpace(0.30F);
|
||||
ImGuizmo::SetDrawlist(ImGui::GetWindowDrawList());
|
||||
ImGuizmo::SetRect(
|
||||
ImGui::GetWindowPos().x,
|
||||
ImGui::GetWindowPos().y,
|
||||
viewportState.Width,
|
||||
viewportState.Height
|
||||
);
|
||||
ImGuizmo::Manipulate(
|
||||
glm::value_ptr(gizmoViewMatrix),
|
||||
glm::value_ptr(gizmoProjectionMatrix),
|
||||
MetaCoreToImGuizmoOperation(EditorContext_->GetGizmoOperation()),
|
||||
MetaCoreToImGuizmoMode(EditorContext_->GetGizmoMode()),
|
||||
glm::value_ptr(transformMatrix)
|
||||
);
|
||||
|
||||
gizmoHovering = ImGuizmo::IsOver();
|
||||
gizmoUsing = ImGuizmo::IsUsing();
|
||||
if (gizmoUsing) {
|
||||
// transformMatrix is in Panda space — apply directly.
|
||||
switch (EditorContext_->GetGizmoOperation()) {
|
||||
case MetaCoreGizmoOperation::Translate:
|
||||
MetaCoreApplyTranslationFromPandaSpaceMatrix(transformMatrix, selectedObject->Transform);
|
||||
break;
|
||||
case MetaCoreGizmoOperation::Rotate:
|
||||
case MetaCoreGizmoOperation::Scale:
|
||||
MetaCoreApplyTransformFromPandaSpaceMatrix(transformMatrix, selectedObject->Transform);
|
||||
break;
|
||||
case MetaCoreGizmoOperation::None:
|
||||
default:
|
||||
transformMatrix = originalTransformMatrix;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr float viewManipulateSize = 112.0F;
|
||||
const ImVec2 canvasPosition = ImGui::GetWindowPos();
|
||||
ImGuizmo::ViewManipulate(
|
||||
glm::value_ptr(gizmoViewMatrix),
|
||||
cameraDistance,
|
||||
ImVec2(canvasPosition.x + viewportState.Width - viewManipulateSize - 16.0F, canvasPosition.y + 16.0F),
|
||||
ImVec2(viewManipulateSize, viewManipulateSize),
|
||||
IM_COL32(26, 30, 36, 180)
|
||||
);
|
||||
|
||||
if (ImGuizmo::IsUsingViewManipulate()) {
|
||||
// gizmoViewMatrix modified → camera moved. Extract Panda-space camera pos.
|
||||
const glm::mat4 newCameraWorld = glm::inverse(gizmoViewMatrix);
|
||||
const glm::vec3 newPandaEye = glm::vec3(newCameraWorld[3]);
|
||||
MetaCoreSceneView manipulatedSceneView = sceneView;
|
||||
manipulatedSceneView.CameraPosition = MetaCoreFromPandaSpacePoint(newPandaEye);
|
||||
manipulatedSceneView.CameraTarget = selectedObject->Transform.Position;
|
||||
EditorContext_->GetCameraController().ApplySceneView(manipulatedSceneView);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(3);
|
||||
|
||||
if (!gizmoUsing) {
|
||||
EditorContext_->GetCameraController().Update(viewportState, EditorContext_->GetInput());
|
||||
sceneView = EditorContext_->GetCameraController().BuildSceneView();
|
||||
sceneView.SelectedObjectId = EditorContext_->GetSelectedObjectId();
|
||||
}
|
||||
|
||||
if (viewportState.Hovered && !gizmoHovering && !gizmoUsing && !ImGui::GetIO().WantCaptureMouse && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
const MetaCoreId pickedObjectId = MetaCorePickGameObjectFromViewport(
|
||||
Scene_,
|
||||
sceneView,
|
||||
viewportState,
|
||||
EditorContext_->GetInput().GetCursorPosition()
|
||||
);
|
||||
if (pickedObjectId != 0 && pickedObjectId != EditorContext_->GetSelectedObjectId()) {
|
||||
EditorContext_->SetSelectedObjectId(pickedObjectId);
|
||||
if (const MetaCoreGameObject* selectedObject = EditorContext_->GetSelectedGameObject(); selectedObject != nullptr) {
|
||||
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "视口已选中对象: " + selectedObject->Name);
|
||||
}
|
||||
sceneView.SelectedObjectId = pickedObjectId;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(centralNode->Pos);
|
||||
ImGui::SetNextWindowSize(centralNode->Size);
|
||||
ImGui::SetNextWindowBgAlpha(0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0F, 0.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
|
||||
|
||||
constexpr ImGuiWindowFlags sceneOverlayFlags =
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoDocking |
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoInputs |
|
||||
ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
ImGuiWindowFlags_NoNavFocus |
|
||||
ImGuiWindowFlags_NoBackground;
|
||||
|
||||
bool sceneOverlayOpen = true;
|
||||
ImGui::Begin("MetaCoreSceneViewportOverlay", &sceneOverlayOpen, sceneOverlayFlags);
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const ImVec2 overlayOrigin = ImGui::GetWindowPos();
|
||||
const ImVec2 overlaySize = ImGui::GetWindowSize();
|
||||
drawList->AddRectFilled(
|
||||
overlayOrigin,
|
||||
ImVec2(overlayOrigin.x + overlaySize.x, overlayOrigin.y + sceneToolbarHeight),
|
||||
IM_COL32(28, 31, 36, 220)
|
||||
);
|
||||
drawList->AddLine(
|
||||
ImVec2(overlayOrigin.x, overlayOrigin.y + sceneToolbarHeight),
|
||||
ImVec2(overlayOrigin.x + overlaySize.x, overlayOrigin.y + sceneToolbarHeight),
|
||||
IM_COL32(72, 84, 98, 220),
|
||||
1.0F
|
||||
);
|
||||
|
||||
const char* helpText = "Alt+左键 环绕 | 中键 平移 | 滚轮 缩放 | F 聚焦";
|
||||
drawList->AddText(
|
||||
ImVec2(overlayOrigin.x + 14.0F, overlayOrigin.y + 9.0F),
|
||||
IM_COL32(220, 224, 230, 220),
|
||||
helpText
|
||||
);
|
||||
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopStyleVar(3);
|
||||
|
||||
ImGui::SetNextWindowPos(centralNode->Pos);
|
||||
ImGui::SetNextWindowSize(ImVec2(centralNode->Size.x, sceneToolbarHeight));
|
||||
ImGui::SetNextWindowBgAlpha(0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.0F, 4.0F));
|
||||
|
||||
constexpr ImGuiWindowFlags sceneToolbarFlags =
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoDocking |
|
||||
ImGuiWindowFlags_NoMove |
|
||||
@ -309,14 +734,55 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
ImGuiWindowFlags_NoNavFocus |
|
||||
ImGuiWindowFlags_NoBackground;
|
||||
|
||||
bool sceneOpen = true;
|
||||
ImGui::Begin("MetaCoreSceneOverlay", &sceneOpen, sceneOverlayFlags);
|
||||
scenePanelProvider->DrawPanel(*EditorContext_);
|
||||
bool sceneToolbarOpen = true;
|
||||
ImGui::Begin("MetaCoreSceneToolbarOverlay", &sceneToolbarOpen, sceneToolbarFlags);
|
||||
ImGui::SetCursorPos(ImVec2(390.0F, 4.0F));
|
||||
const auto drawToolbarToggle = [](const char* label, bool selected) {
|
||||
if (selected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.20F, 0.36F, 0.62F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.25F, 0.42F, 0.70F, 1.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.18F, 0.31F, 0.54F, 1.0F));
|
||||
}
|
||||
const bool clicked = ImGui::Button(label, ImVec2(38.0F, 24.0F));
|
||||
if (selected) {
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
return clicked;
|
||||
};
|
||||
|
||||
if (drawToolbarToggle("Q", EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::None)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::None);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (drawToolbarToggle("W", EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::Translate)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Translate);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (drawToolbarToggle("E", EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::Rotate)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Rotate);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (drawToolbarToggle("R", EditorContext_->GetGizmoOperation() == MetaCoreGizmoOperation::Scale)) {
|
||||
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Scale);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(10.0F, 0.0F));
|
||||
ImGui::SameLine();
|
||||
if (drawToolbarToggle("Local", EditorContext_->GetGizmoMode() == MetaCoreGizmoMode::Local)) {
|
||||
EditorContext_->SetGizmoMode(MetaCoreGizmoMode::Local);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (drawToolbarToggle("World", EditorContext_->GetGizmoMode() == MetaCoreGizmoMode::World)) {
|
||||
EditorContext_->SetGizmoMode(MetaCoreGizmoMode::World);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleVar(3);
|
||||
} else {
|
||||
ViewportRenderer_.SetViewportRect(MetaCoreViewportRect{});
|
||||
}
|
||||
} else {
|
||||
ViewportRenderer_.SetViewportRect(MetaCoreViewportRect{});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -50,6 +50,20 @@ void MetaCoreEditorCameraController::FocusGameObject(const MetaCoreGameObject& g
|
||||
Distance_ = std::max(3.5F, scaleRadius * 4.0F + 2.5F);
|
||||
}
|
||||
|
||||
void MetaCoreEditorCameraController::ApplySceneView(const MetaCoreSceneView& sceneView) {
|
||||
Target_ = sceneView.CameraTarget;
|
||||
FieldOfViewDegrees_ = sceneView.VerticalFieldOfViewDegrees;
|
||||
const glm::vec3 offset = sceneView.CameraPosition - sceneView.CameraTarget;
|
||||
Distance_ = std::max(0.1F, glm::length(offset));
|
||||
if (Distance_ <= 0.1001F) {
|
||||
return;
|
||||
}
|
||||
|
||||
const glm::vec3 forward = glm::normalize(sceneView.CameraTarget - sceneView.CameraPosition);
|
||||
PitchDegrees_ = glm::degrees(std::asin(glm::clamp(forward.y, -1.0F, 1.0F)));
|
||||
YawDegrees_ = glm::degrees(std::atan2(forward.x, forward.z));
|
||||
}
|
||||
|
||||
MetaCoreSceneView MetaCoreEditorCameraController::BuildSceneView() const {
|
||||
MetaCoreSceneView sceneView;
|
||||
sceneView.CameraPosition = GetCameraPosition();
|
||||
|
||||
@ -16,16 +16,17 @@ public:
|
||||
void FocusGameObject(const MetaCoreGameObject& gameObject);
|
||||
[[nodiscard]] MetaCoreSceneView BuildSceneView() const;
|
||||
[[nodiscard]] glm::vec3 GetCameraPosition() const;
|
||||
void ApplySceneView(const MetaCoreSceneView& sceneView);
|
||||
|
||||
private:
|
||||
[[nodiscard]] glm::vec3 GetForwardDirection() const;
|
||||
[[nodiscard]] glm::vec3 GetRightDirection() const;
|
||||
[[nodiscard]] glm::vec3 GetUpDirection() const;
|
||||
|
||||
glm::vec3 Target_{0.0F, 0.7F, 0.0F};
|
||||
float Distance_ = 7.5F;
|
||||
float YawDegrees_ = 38.0F;
|
||||
float PitchDegrees_ = -28.0F;
|
||||
glm::vec3 Target_{0.0F, 0.5F, 0.0F};
|
||||
float Distance_ = 8.5F;
|
||||
float YawDegrees_ = 45.0F;
|
||||
float PitchDegrees_ = -32.0F;
|
||||
float FieldOfViewDegrees_ = 60.0F;
|
||||
};
|
||||
|
||||
|
||||
@ -46,6 +46,10 @@ MetaCoreId MetaCoreEditorContext::GetSelectedObjectId() const { return SelectedO
|
||||
void MetaCoreEditorContext::SetSelectedObjectId(MetaCoreId objectId) { SelectedObjectId_ = objectId; }
|
||||
MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() { return Scene_.FindGameObject(SelectedObjectId_); }
|
||||
const MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() const { return Scene_.FindGameObject(SelectedObjectId_); }
|
||||
MetaCoreGizmoOperation MetaCoreEditorContext::GetGizmoOperation() const { return GizmoOperation_; }
|
||||
void MetaCoreEditorContext::SetGizmoOperation(MetaCoreGizmoOperation operation) { GizmoOperation_ = operation; }
|
||||
MetaCoreGizmoMode MetaCoreEditorContext::GetGizmoMode() const { return GizmoMode_; }
|
||||
void MetaCoreEditorContext::SetGizmoMode(MetaCoreGizmoMode mode) { GizmoMode_ = mode; }
|
||||
|
||||
void MetaCoreEditorContext::AddConsoleMessage(MetaCoreLogLevel level, const std::string& category, const std::string& message) {
|
||||
LogService_.AddEntry(level, category, message);
|
||||
|
||||
@ -17,6 +17,18 @@ struct MetaCoreGameObject;
|
||||
class MetaCoreEditorModuleRegistry;
|
||||
class MetaCoreEditorCameraController;
|
||||
|
||||
enum class MetaCoreGizmoOperation {
|
||||
None = 0,
|
||||
Translate,
|
||||
Rotate,
|
||||
Scale
|
||||
};
|
||||
|
||||
enum class MetaCoreGizmoMode {
|
||||
Local = 0,
|
||||
World
|
||||
};
|
||||
|
||||
struct MetaCoreSceneViewportState {
|
||||
float Left = 0.0F;
|
||||
float Top = 0.0F;
|
||||
@ -57,6 +69,10 @@ public:
|
||||
void SetSelectedObjectId(MetaCoreId objectId);
|
||||
[[nodiscard]] MetaCoreGameObject* GetSelectedGameObject();
|
||||
[[nodiscard]] const MetaCoreGameObject* GetSelectedGameObject() const;
|
||||
[[nodiscard]] MetaCoreGizmoOperation GetGizmoOperation() const;
|
||||
void SetGizmoOperation(MetaCoreGizmoOperation operation);
|
||||
[[nodiscard]] MetaCoreGizmoMode GetGizmoMode() const;
|
||||
void SetGizmoMode(MetaCoreGizmoMode mode);
|
||||
void AddConsoleMessage(MetaCoreLogLevel level, const std::string& category, const std::string& message);
|
||||
void SetDockLayoutBuilt(bool built);
|
||||
[[nodiscard]] bool HasDockLayoutBuilt() const;
|
||||
@ -71,6 +87,8 @@ private:
|
||||
std::unique_ptr<MetaCoreEditorCameraController> CameraController_;
|
||||
MetaCoreSceneViewportState SceneViewportState_{};
|
||||
MetaCoreId SelectedObjectId_ = 0;
|
||||
MetaCoreGizmoOperation GizmoOperation_ = MetaCoreGizmoOperation::Translate;
|
||||
MetaCoreGizmoMode GizmoMode_ = MetaCoreGizmoMode::Local;
|
||||
bool DockLayoutBuilt_ = false;
|
||||
};
|
||||
|
||||
|
||||
@ -40,6 +40,11 @@ bool MetaCoreInput::IsMouseButtonDown(MetaCoreMouseButton button) const {
|
||||
return CurrentMouseStates_[static_cast<std::size_t>(button)];
|
||||
}
|
||||
|
||||
bool MetaCoreInput::WasMouseButtonPressed(MetaCoreMouseButton button) const {
|
||||
const std::size_t buttonIndex = static_cast<std::size_t>(button);
|
||||
return CurrentMouseStates_[buttonIndex] && !PreviousMouseStates_[buttonIndex];
|
||||
}
|
||||
|
||||
float MetaCoreInput::GetMouseWheelDelta() const {
|
||||
return MouseWheelDelta_;
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "MetaCorePlatform/MetaCoreWindow.h"
|
||||
|
||||
#include "graphicsWindow.h"
|
||||
#include "graphicsWindowProc.h"
|
||||
#include "load_prc_file.h"
|
||||
#include "nativeWindowHandle.h"
|
||||
#include "pandaFramework.h"
|
||||
@ -13,6 +12,7 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@ -55,6 +55,11 @@ bool MetaCoreIsVirtualKeyDown(int virtualKey) {
|
||||
return (GetAsyncKeyState(virtualKey) & 0x8000) != 0;
|
||||
}
|
||||
|
||||
std::unordered_map<HWND, class MetaCoreWindow::MetaCoreWindowImpl*>& MetaCoreGetWindowMap() {
|
||||
static std::unordered_map<HWND, class MetaCoreWindow::MetaCoreWindowImpl*> windowMap;
|
||||
return windowMap;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class MetaCoreWindow::MetaCoreWindowImpl {
|
||||
@ -65,37 +70,41 @@ public:
|
||||
HWND NativeHandle = nullptr;
|
||||
MetaCoreInput Input{};
|
||||
MetaCoreNativeWindowMessageHandler MessageHandler{};
|
||||
std::unique_ptr<class MetaCoreWindowMessageProc> MessageProc{};
|
||||
WNDPROC OriginalWindowProc = nullptr;
|
||||
std::chrono::steady_clock::time_point LastFrameTime = std::chrono::steady_clock::now();
|
||||
float DeltaSeconds = 1.0F / 60.0F;
|
||||
bool Initialized = false;
|
||||
bool CloseRequested = false;
|
||||
};
|
||||
|
||||
class MetaCoreWindowMessageProc final : public GraphicsWindowProc {
|
||||
public:
|
||||
explicit MetaCoreWindowMessageProc(MetaCoreWindow::MetaCoreWindowImpl& owner)
|
||||
: Owner_(owner) {
|
||||
LRESULT CALLBACK MetaCoreWindowSubclassProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
auto& windowMap = MetaCoreGetWindowMap();
|
||||
const auto iterator = windowMap.find(hwnd);
|
||||
if (iterator == windowMap.end() || iterator->second == nullptr) {
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
LONG wnd_proc(GraphicsWindow*, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) override;
|
||||
|
||||
private:
|
||||
MetaCoreWindow::MetaCoreWindowImpl& Owner_;
|
||||
};
|
||||
|
||||
LONG MetaCoreWindowMessageProc::wnd_proc(GraphicsWindow*, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
if (Owner_.MessageHandler) {
|
||||
Owner_.MessageHandler(hwnd, msg, static_cast<std::uintptr_t>(wparam), static_cast<std::intptr_t>(lparam));
|
||||
MetaCoreWindow::MetaCoreWindowImpl& owner = *iterator->second;
|
||||
bool handledByEditor = false;
|
||||
if (owner.MessageHandler) {
|
||||
handledByEditor = owner.MessageHandler(hwnd, msg, static_cast<std::uintptr_t>(wparam), static_cast<std::intptr_t>(lparam));
|
||||
}
|
||||
|
||||
if (msg == WM_MOUSEWHEEL) {
|
||||
Owner_.Input.AddMouseWheelDelta(static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam)) / static_cast<float>(WHEEL_DELTA));
|
||||
owner.Input.AddMouseWheelDelta(static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam)) / static_cast<float>(WHEEL_DELTA));
|
||||
} else if (msg == WM_CLOSE) {
|
||||
Owner_.CloseRequested = true;
|
||||
owner.CloseRequested = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (handledByEditor) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (owner.OriginalWindowProc != nullptr) {
|
||||
return CallWindowProc(owner.OriginalWindowProc, hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
MetaCoreWindow::MetaCoreWindow()
|
||||
@ -133,10 +142,10 @@ bool MetaCoreWindow::Initialize(int width, int height, const std::string& title)
|
||||
return false;
|
||||
}
|
||||
|
||||
Impl_->MessageProc = std::make_unique<MetaCoreWindowMessageProc>(*Impl_);
|
||||
if (Impl_->GraphicsWindowHandle->supports_window_procs()) {
|
||||
Impl_->GraphicsWindowHandle->add_window_proc(Impl_->MessageProc.get());
|
||||
}
|
||||
MetaCoreGetWindowMap()[Impl_->NativeHandle] = Impl_.get();
|
||||
Impl_->OriginalWindowProc = reinterpret_cast<WNDPROC>(
|
||||
SetWindowLongPtr(Impl_->NativeHandle, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&MetaCoreWindowSubclassProc))
|
||||
);
|
||||
|
||||
Impl_->CloseRequested = false;
|
||||
Impl_->LastFrameTime = std::chrono::steady_clock::now();
|
||||
@ -149,12 +158,13 @@ void MetaCoreWindow::Shutdown() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Impl_->GraphicsWindowHandle != nullptr && Impl_->MessageProc != nullptr && Impl_->GraphicsWindowHandle->supports_window_procs()) {
|
||||
Impl_->GraphicsWindowHandle->remove_window_proc(Impl_->MessageProc.get());
|
||||
if (Impl_->NativeHandle != nullptr) {
|
||||
MetaCoreGetWindowMap().erase(Impl_->NativeHandle);
|
||||
if (Impl_->OriginalWindowProc != nullptr) {
|
||||
SetWindowLongPtr(Impl_->NativeHandle, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(Impl_->OriginalWindowProc));
|
||||
}
|
||||
}
|
||||
|
||||
Impl_->MessageProc.reset();
|
||||
|
||||
if (Impl_->WindowFrameworkHandle != nullptr) {
|
||||
Impl_->Framework.close_window(Impl_->WindowFrameworkHandle);
|
||||
Impl_->WindowFrameworkHandle = nullptr;
|
||||
@ -162,6 +172,7 @@ void MetaCoreWindow::Shutdown() {
|
||||
|
||||
Impl_->GraphicsWindowHandle = nullptr;
|
||||
Impl_->NativeHandle = nullptr;
|
||||
Impl_->OriginalWindowProc = nullptr;
|
||||
Impl_->Framework.close_framework();
|
||||
Impl_->Initialized = false;
|
||||
}
|
||||
|
||||
@ -49,6 +49,9 @@ public:
|
||||
// 判断鼠标按键当前是否按下。
|
||||
[[nodiscard]] bool IsMouseButtonDown(MetaCoreMouseButton button) const;
|
||||
|
||||
// 判断鼠标按键是否在本帧刚刚按下。
|
||||
[[nodiscard]] bool WasMouseButtonPressed(MetaCoreMouseButton button) const;
|
||||
|
||||
// 返回当前帧累计的滚轮值。
|
||||
[[nodiscard]] float GetMouseWheelDelta() const;
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ namespace MetaCore {
|
||||
class MetaCoreRenderDevice;
|
||||
|
||||
// 定义宿主窗口层向上暴露的原生消息回调,用于把 Win32 消息转交给 ImGui。
|
||||
using MetaCoreNativeWindowMessageHandler = std::function<void(void*, unsigned int, std::uintptr_t, std::intptr_t)>;
|
||||
using MetaCoreNativeWindowMessageHandler = std::function<bool(void*, unsigned int, std::uintptr_t, std::intptr_t)>;
|
||||
|
||||
// 封装 Panda3D 窗口、时间步进与基础输入采集。
|
||||
class MetaCoreWindow {
|
||||
|
||||
@ -40,4 +40,8 @@ void MetaCoreEditorViewportRenderer::RenderSceneToViewport(const MetaCoreScene&
|
||||
SceneBridge_.SyncScene(scene);
|
||||
}
|
||||
|
||||
bool MetaCoreEditorViewportRenderer::TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const {
|
||||
return SceneBridge_.TryGetObjectWorldMatrix(objectId, worldMatrix);
|
||||
}
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -8,6 +8,12 @@
|
||||
#include "ambientLight.h"
|
||||
#include "camera.h"
|
||||
#include "directionalLight.h"
|
||||
#include "geom.h"
|
||||
#include "geomNode.h"
|
||||
#include "geomTriangles.h"
|
||||
#include "geomVertexData.h"
|
||||
#include "geomVertexFormat.h"
|
||||
#include "geomVertexWriter.h"
|
||||
#include "lineSegs.h"
|
||||
#include "loader.h"
|
||||
#include "nodePath.h"
|
||||
@ -18,6 +24,7 @@
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
@ -59,11 +66,28 @@ LMatrix4f MetaCoreConvertTransformToPanda(const MetaCoreTransformComponent& tran
|
||||
}
|
||||
|
||||
LPoint3f MetaCoreToPandaPoint(const glm::vec3& value) {
|
||||
return LPoint3f(value.x, value.z, value.y);
|
||||
// MetaCore 使用右手系的 X 右 / Y 上 / Z 前。
|
||||
// Panda3D 使用右手系的 X 右 / Y 前 / Z 上。
|
||||
// 仅交换 Y/Z 会引入镜像,因此这里同时对前向轴取反,保持手性一致。
|
||||
return LPoint3f(value.x, -value.z, value.y);
|
||||
}
|
||||
|
||||
LVector3f MetaCoreToPandaVector(const glm::vec3& value) {
|
||||
return LVector3f(value.x, value.z, value.y);
|
||||
return LVector3f(value.x, -value.z, value.y);
|
||||
}
|
||||
|
||||
LVecBase3f MetaCoreToPandaScale(const glm::vec3& value) {
|
||||
return LVecBase3f(value.x, value.z, value.y);
|
||||
}
|
||||
|
||||
void MetaCoreApplyTransformToPandaNode(const MetaCoreTransformComponent& transform, NodePath& nodePath) {
|
||||
nodePath.set_pos(MetaCoreToPandaPoint(transform.Position));
|
||||
nodePath.set_hpr(
|
||||
transform.RotationEulerDegrees.y,
|
||||
transform.RotationEulerDegrees.x,
|
||||
transform.RotationEulerDegrees.z
|
||||
);
|
||||
nodePath.set_scale(MetaCoreToPandaScale(transform.Scale));
|
||||
}
|
||||
|
||||
NodePath MetaCoreCreateGridNode(const NodePath& parentNode) {
|
||||
@ -102,6 +126,83 @@ NodePath MetaCoreCreateAxisNode(const NodePath& parentNode) {
|
||||
return parentNode.attach_new_node(axisBuilder.create());
|
||||
}
|
||||
|
||||
NodePath MetaCoreCreateUnitCubeNode(const NodePath& parentNode) {
|
||||
PT(GeomVertexData) vertexData = new GeomVertexData(
|
||||
"MetaCoreUnitCube",
|
||||
GeomVertexFormat::get_v3n3(),
|
||||
Geom::UH_static
|
||||
);
|
||||
|
||||
GeomVertexWriter vertexWriter(vertexData, "vertex");
|
||||
GeomVertexWriter normalWriter(vertexData, "normal");
|
||||
|
||||
const auto addFace = [&](const glm::vec3& normal, const glm::vec3& a, const glm::vec3& b, const glm::vec3& c, const glm::vec3& d) {
|
||||
const glm::vec3 corners[4] = {a, b, c, d};
|
||||
for (const glm::vec3& corner : corners) {
|
||||
const LPoint3f pandaPoint = MetaCoreToPandaPoint(corner);
|
||||
const LVector3f pandaNormal = MetaCoreToPandaVector(normal);
|
||||
vertexWriter.add_data3(pandaPoint);
|
||||
normalWriter.add_data3(pandaNormal);
|
||||
}
|
||||
};
|
||||
|
||||
addFace(glm::vec3(0.0F, 0.0F, 1.0F),
|
||||
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));
|
||||
addFace(glm::vec3(0.0F, 0.0F, -1.0F),
|
||||
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));
|
||||
addFace(glm::vec3(-1.0F, 0.0F, 0.0F),
|
||||
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));
|
||||
addFace(glm::vec3(1.0F, 0.0F, 0.0F),
|
||||
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));
|
||||
addFace(glm::vec3(0.0F, 1.0F, 0.0F),
|
||||
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));
|
||||
addFace(glm::vec3(0.0F, -1.0F, 0.0F),
|
||||
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));
|
||||
|
||||
PT(GeomTriangles) triangles = new GeomTriangles(Geom::UH_static);
|
||||
for (int faceIndex = 0; faceIndex < 6; ++faceIndex) {
|
||||
const int baseVertex = faceIndex * 4;
|
||||
triangles->add_vertices(baseVertex + 0, baseVertex + 1, baseVertex + 2);
|
||||
triangles->add_vertices(baseVertex + 0, baseVertex + 2, baseVertex + 3);
|
||||
}
|
||||
|
||||
PT(Geom) geom = new Geom(vertexData);
|
||||
geom->add_primitive(triangles);
|
||||
|
||||
PT(GeomNode) geomNode = new GeomNode("MetaCoreUnitCubeNode");
|
||||
geomNode->add_geom(geom);
|
||||
return parentNode.attach_new_node(geomNode);
|
||||
}
|
||||
|
||||
glm::mat4 MetaCoreConvertPandaMatrixToGlm(const LMatrix4f& matrix) {
|
||||
glm::mat4 result(1.0F);
|
||||
for (int col = 0; col < 4; ++col) {
|
||||
for (int row = 0; row < 4; ++row) {
|
||||
// Transpose mathematical matrix to switch from Panda's v*M to GLM's M*v.
|
||||
result[col][row] = matrix.get_cell(col, row);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class MetaCorePandaSceneBridge::MetaCorePandaSceneBridgeImpl {
|
||||
@ -207,7 +308,7 @@ void MetaCorePandaSceneBridge::SyncScene(const MetaCoreScene& scene) {
|
||||
}
|
||||
|
||||
objectState.RootNode.set_name(gameObject.Name);
|
||||
objectState.RootNode.set_mat(MetaCoreConvertTransformToPanda(gameObject.Transform));
|
||||
MetaCoreApplyTransformToPandaNode(gameObject.Transform, objectState.RootNode);
|
||||
|
||||
if (gameObject.ParentId != 0) {
|
||||
const auto parentIterator = Impl_->ObjectStates.find(gameObject.ParentId);
|
||||
@ -222,7 +323,7 @@ void MetaCorePandaSceneBridge::SyncScene(const MetaCoreScene& scene) {
|
||||
|
||||
if (gameObject.MeshRenderer.has_value()) {
|
||||
if (objectState.MeshNode.is_empty()) {
|
||||
objectState.MeshNode = windowFrameworkHandle->load_model(objectState.RootNode, Filename("models/box"));
|
||||
objectState.MeshNode = MetaCoreCreateUnitCubeNode(objectState.RootNode);
|
||||
}
|
||||
|
||||
if (!objectState.MeshNode.is_empty()) {
|
||||
@ -309,9 +410,31 @@ void MetaCorePandaSceneBridge::ApplySceneView(const MetaCoreSceneView& sceneView
|
||||
if (cameraNode != nullptr) {
|
||||
auto* perspectiveLens = DCAST(PerspectiveLens, cameraNode->get_lens());
|
||||
if (perspectiveLens != nullptr) {
|
||||
perspectiveLens->set_fov(sceneView.VerticalFieldOfViewDegrees);
|
||||
const float aspect = std::max(0.001F, perspectiveLens->get_aspect_ratio());
|
||||
const float vFovRad = glm::radians(sceneView.VerticalFieldOfViewDegrees);
|
||||
const float hFovDeg = glm::degrees(2.0F * std::atan(aspect * std::tan(vFovRad * 0.5F)));
|
||||
perspectiveLens->set_fov(LVecBase2f(std::max(0.001F, hFovDeg), std::max(0.001F, sceneView.VerticalFieldOfViewDegrees)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MetaCorePandaSceneBridge::TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const {
|
||||
if (Impl_ == nullptr || Impl_->RenderDevice == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* sceneRootHandle = static_cast<NodePath*>(Impl_->RenderDevice->GetNativeSceneRootHandle());
|
||||
if (sceneRootHandle == nullptr || sceneRootHandle->is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto objectIterator = Impl_->ObjectStates.find(objectId);
|
||||
if (objectIterator == Impl_->ObjectStates.end() || objectIterator->second.RootNode.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
worldMatrix = MetaCoreConvertPandaMatrixToGlm(objectIterator->second.RootNode.get_mat(*sceneRootHandle));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -11,11 +11,29 @@
|
||||
#include "perspectiveLens.h"
|
||||
#include "windowFramework.h"
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
namespace {
|
||||
|
||||
glm::mat4 MetaCoreConvertPandaMatrixToGlm(const LMatrix4f& matrix) {
|
||||
glm::mat4 result(1.0F);
|
||||
for (int col = 0; col < 4; ++col) {
|
||||
for (int row = 0; row < 4; ++row) {
|
||||
// Transpose mathematical matrix: result[col][row] is GLM(row, col).
|
||||
// We want GLM(row, col) = Panda(col, row) to switch v*M to M*v.
|
||||
result[col][row] = matrix.get_cell(col, row);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class MetaCoreRenderDevice::MetaCoreRenderDeviceImpl {
|
||||
public:
|
||||
MetaCoreWindow* Window = nullptr;
|
||||
@ -153,6 +171,12 @@ void MetaCoreRenderDevice::SetSceneViewportRect(const MetaCoreViewportRect& view
|
||||
1.0F - clampedBottom,
|
||||
1.0F - clampedTop
|
||||
);
|
||||
|
||||
if (auto* cameraNode = DCAST(Camera, Impl_->EditorCamera.node()); cameraNode != nullptr) {
|
||||
if (auto* perspectiveLens = DCAST(PerspectiveLens, cameraNode->get_lens()); perspectiveLens != nullptr) {
|
||||
perspectiveLens->set_aspect_ratio(viewportRect.Width / viewportRect.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* MetaCoreRenderDevice::GetNativeWindowFrameworkHandle() const {
|
||||
@ -167,4 +191,28 @@ void* MetaCoreRenderDevice::GetNativeEditorCameraHandle() const {
|
||||
return Impl_->EditorCamera.is_empty() ? nullptr : static_cast<void*>(&Impl_->EditorCamera);
|
||||
}
|
||||
|
||||
bool MetaCoreRenderDevice::TryGetEditorCameraMatrices(glm::mat4& viewMatrix, glm::mat4& projectionMatrix) const {
|
||||
if (!Impl_->Initialized || Impl_->EditorCamera.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* cameraNode = DCAST(Camera, Impl_->EditorCamera.node());
|
||||
if (cameraNode == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* lens = cameraNode->get_lens();
|
||||
if (lens == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// view = inverse of camera world matrix (Panda world → Panda camera space).
|
||||
const glm::mat4 cameraWorldMatrix = MetaCoreConvertPandaMatrixToGlm(Impl_->EditorCamera.get_mat(Impl_->SceneRoot));
|
||||
viewMatrix = glm::inverse(cameraWorldMatrix);
|
||||
|
||||
// Panda's native projection matrix works correctly once mathematically transposed.
|
||||
projectionMatrix = MetaCoreConvertPandaMatrixToGlm(lens->get_projection_mat());
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -23,6 +23,9 @@ public:
|
||||
// 将场景状态同步到 Panda3D,并更新编辑器相机。
|
||||
void RenderSceneToViewport(const MetaCoreScene& scene, const MetaCoreSceneView& sceneView);
|
||||
|
||||
// 返回 Panda3D 中对象当前真实使用的世界矩阵。
|
||||
[[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const;
|
||||
|
||||
private:
|
||||
MetaCoreRenderDevice* RenderDevice_ = nullptr;
|
||||
MetaCorePandaSceneBridge SceneBridge_{};
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "MetaCoreFoundation/MetaCoreId.h"
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace MetaCore {
|
||||
@ -26,6 +30,9 @@ public:
|
||||
// 更新编辑器相机与当前选中对象高亮状态。
|
||||
void ApplySceneView(const MetaCoreSceneView& sceneView);
|
||||
|
||||
// 返回 Panda3D 场景中对象当前真实使用的世界矩阵。
|
||||
[[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const;
|
||||
|
||||
private:
|
||||
class MetaCorePandaSceneBridgeImpl;
|
||||
std::unique_ptr<MetaCorePandaSceneBridgeImpl> Impl_{};
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
#include "MetaCoreRender/MetaCoreRenderTypes.h"
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace MetaCore {
|
||||
@ -40,6 +42,9 @@ public:
|
||||
// 返回编辑器相机节点原生对象指针,仅供 Panda3D 桥接层使用。
|
||||
[[nodiscard]] void* GetNativeEditorCameraHandle() const;
|
||||
|
||||
// 返回 Panda3D 当前真实使用的编辑器相机视图矩阵和投影矩阵。
|
||||
[[nodiscard]] bool TryGetEditorCameraMatrices(glm::mat4& viewMatrix, glm::mat4& projectionMatrix) const;
|
||||
|
||||
private:
|
||||
class MetaCoreRenderDeviceImpl;
|
||||
|
||||
|
||||
3164
third_party/ImGuizmo/ImGuizmo.cpp
vendored
Normal file
3164
third_party/ImGuizmo/ImGuizmo.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
306
third_party/ImGuizmo/ImGuizmo.h
vendored
Normal file
306
third_party/ImGuizmo/ImGuizmo.h
vendored
Normal file
@ -0,0 +1,306 @@
|
||||
// https://github.com/CedricGuillemet/ImGuizmo
|
||||
// v1.92.5 WIP
|
||||
//
|
||||
// The MIT License(MIT)
|
||||
//
|
||||
// Copyright(c) 2016-2021 Cedric Guillemet
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files(the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions :
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// History :
|
||||
// 2019/11/03 View gizmo
|
||||
// 2016/09/11 Behind camera culling. Scaling Delta matrix not multiplied by source matrix scales. local/world rotation and translation fixed. Display message is incorrect (X: ... Y:...) in local mode.
|
||||
// 2016/09/09 Hatched negative axis. Snapping. Documentation update.
|
||||
// 2016/09/04 Axis switch and translation plan autohiding. Scale transform stability improved
|
||||
// 2016/09/01 Mogwai changed to Manipulate. Draw debug cube. Fixed inverted scale. Mixing scale and translation/rotation gives bad results.
|
||||
// 2016/08/31 First version
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// Future (no order):
|
||||
//
|
||||
// - Multi view
|
||||
// - display rotation/translation/scale infos in local/world space and not only local
|
||||
// - finish local/world matrix application
|
||||
// - OPERATION as bitmask
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// Example
|
||||
#if 0
|
||||
void EditTransform(const Camera& camera, matrix_t& matrix)
|
||||
{
|
||||
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
|
||||
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
|
||||
if (ImGui::IsKeyPressed(90))
|
||||
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
||||
if (ImGui::IsKeyPressed(69))
|
||||
mCurrentGizmoOperation = ImGuizmo::ROTATE;
|
||||
if (ImGui::IsKeyPressed(82)) // r Key
|
||||
mCurrentGizmoOperation = ImGuizmo::SCALE;
|
||||
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
|
||||
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
|
||||
mCurrentGizmoOperation = ImGuizmo::ROTATE;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
|
||||
mCurrentGizmoOperation = ImGuizmo::SCALE;
|
||||
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
|
||||
ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale);
|
||||
ImGui::InputFloat3("Tr", matrixTranslation, 3);
|
||||
ImGui::InputFloat3("Rt", matrixRotation, 3);
|
||||
ImGui::InputFloat3("Sc", matrixScale, 3);
|
||||
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16);
|
||||
|
||||
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
|
||||
{
|
||||
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
|
||||
mCurrentGizmoMode = ImGuizmo::LOCAL;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
|
||||
mCurrentGizmoMode = ImGuizmo::WORLD;
|
||||
}
|
||||
static bool useSnap(false);
|
||||
if (ImGui::IsKeyPressed(83))
|
||||
useSnap = !useSnap;
|
||||
ImGui::Checkbox("", &useSnap);
|
||||
ImGui::SameLine();
|
||||
vec_t snap;
|
||||
switch (mCurrentGizmoOperation)
|
||||
{
|
||||
case ImGuizmo::TRANSLATE:
|
||||
snap = config.mSnapTranslation;
|
||||
ImGui::InputFloat3("Snap", &snap.x);
|
||||
break;
|
||||
case ImGuizmo::ROTATE:
|
||||
snap = config.mSnapRotation;
|
||||
ImGui::InputFloat("Angle Snap", &snap.x);
|
||||
break;
|
||||
case ImGuizmo::SCALE:
|
||||
snap = config.mSnapScale;
|
||||
ImGui::InputFloat("Scale Snap", &snap.x);
|
||||
break;
|
||||
}
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
|
||||
ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL);
|
||||
}
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_IMGUI_API
|
||||
#include "imconfig.h"
|
||||
#endif
|
||||
#ifndef IMGUI_API
|
||||
#define IMGUI_API
|
||||
#endif
|
||||
|
||||
#ifndef IMGUIZMO_NAMESPACE
|
||||
#define IMGUIZMO_NAMESPACE ImGuizmo
|
||||
#endif
|
||||
|
||||
struct ImGuiWindow;
|
||||
|
||||
namespace IMGUIZMO_NAMESPACE
|
||||
{
|
||||
// call inside your own window and before Manipulate() in order to draw gizmo to that window.
|
||||
// Or pass a specific ImDrawList to draw to (e.g. ImGui::GetForegroundDrawList()).
|
||||
IMGUI_API void SetDrawlist(ImDrawList* drawlist = nullptr);
|
||||
|
||||
// call BeginFrame right after ImGui_XXXX_NewFrame();
|
||||
IMGUI_API void BeginFrame();
|
||||
|
||||
// this is necessary because when imguizmo is compiled into a dll, and imgui into another
|
||||
// globals are not shared between them.
|
||||
// More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam
|
||||
// expose method to set imgui context
|
||||
IMGUI_API void SetImGuiContext(ImGuiContext* ctx);
|
||||
|
||||
// return true if mouse cursor is over any gizmo control (axis, plan or screen component)
|
||||
IMGUI_API bool IsOver();
|
||||
|
||||
// return true if mouse IsOver or if the gizmo is in moving state
|
||||
IMGUI_API bool IsUsing();
|
||||
|
||||
// return true if the view gizmo is in moving state
|
||||
IMGUI_API bool IsUsingViewManipulate();
|
||||
// only check if your mouse is over the view manipulator - no matter whether it's active or not
|
||||
IMGUI_API bool IsViewManipulateHovered();
|
||||
|
||||
// return true if any gizmo is in moving state
|
||||
IMGUI_API bool IsUsingAny();
|
||||
|
||||
// enable/disable the gizmo. Stay in the state until next call to Enable.
|
||||
// gizmo is rendered with gray half transparent color when disabled
|
||||
IMGUI_API void Enable(bool enable);
|
||||
|
||||
// helper functions for manualy editing translation/rotation/scale with an input float
|
||||
// translation, rotation and scale float points to 3 floats each
|
||||
// Angles are in degrees (more suitable for human editing)
|
||||
// example:
|
||||
// float matrixTranslation[3], matrixRotation[3], matrixScale[3];
|
||||
// ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale);
|
||||
// ImGui::InputFloat3("Tr", matrixTranslation, 3);
|
||||
// ImGui::InputFloat3("Rt", matrixRotation, 3);
|
||||
// ImGui::InputFloat3("Sc", matrixScale, 3);
|
||||
// ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
|
||||
//
|
||||
// These functions have some numerical stability issues for now. Use with caution.
|
||||
IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale);
|
||||
IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix);
|
||||
|
||||
IMGUI_API void SetRect(float x, float y, float width, float height);
|
||||
// default is false
|
||||
IMGUI_API void SetOrthographic(bool isOrthographic);
|
||||
|
||||
// Render a cube with face color corresponding to face normal. Usefull for debug/tests
|
||||
IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount);
|
||||
IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize);
|
||||
|
||||
// call it when you want a gizmo
|
||||
// Needs view and projection matrices.
|
||||
// matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional
|
||||
// translation is applied in world space
|
||||
enum OPERATION
|
||||
{
|
||||
TRANSLATE_X = (1u << 0),
|
||||
TRANSLATE_Y = (1u << 1),
|
||||
TRANSLATE_Z = (1u << 2),
|
||||
ROTATE_X = (1u << 3),
|
||||
ROTATE_Y = (1u << 4),
|
||||
ROTATE_Z = (1u << 5),
|
||||
ROTATE_SCREEN = (1u << 6),
|
||||
SCALE_X = (1u << 7),
|
||||
SCALE_Y = (1u << 8),
|
||||
SCALE_Z = (1u << 9),
|
||||
BOUNDS = (1u << 10),
|
||||
SCALE_XU = (1u << 11),
|
||||
SCALE_YU = (1u << 12),
|
||||
SCALE_ZU = (1u << 13),
|
||||
|
||||
TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z,
|
||||
ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN,
|
||||
SCALE = SCALE_X | SCALE_Y | SCALE_Z,
|
||||
SCALEU = SCALE_XU | SCALE_YU | SCALE_ZU, // universal
|
||||
UNIVERSAL = TRANSLATE | ROTATE | SCALEU
|
||||
};
|
||||
|
||||
inline OPERATION operator|(OPERATION lhs, OPERATION rhs)
|
||||
{
|
||||
return static_cast<OPERATION>(static_cast<int>(lhs) | static_cast<int>(rhs));
|
||||
}
|
||||
|
||||
enum MODE
|
||||
{
|
||||
LOCAL,
|
||||
WORLD
|
||||
};
|
||||
|
||||
IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL);
|
||||
//
|
||||
// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
|
||||
// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
|
||||
// other software are using the same mechanics. But just in case, you are now warned!
|
||||
//
|
||||
IMGUI_API void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
|
||||
|
||||
// use this version if you did not call Manipulate before and you are just using ViewManipulate
|
||||
IMGUI_API void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
|
||||
|
||||
IMGUI_API void SetAlternativeWindow(ImGuiWindow* window);
|
||||
|
||||
[[deprecated("Use PushID/PopID instead.")]]
|
||||
IMGUI_API void SetID(int id);
|
||||
|
||||
// ID stack/scopes
|
||||
// Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui.
|
||||
// - Those questions are answered and impacted by understanding of the ID stack system:
|
||||
// - "Q: Why is my widget not reacting when I click on it?"
|
||||
// - "Q: How can I have widgets with an empty label?"
|
||||
// - "Q: How can I have multiple widgets with the same label?"
|
||||
// - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely
|
||||
// want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them.
|
||||
// - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others.
|
||||
// - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID,
|
||||
// whereas "str_id" denote a string that is only used as an ID and not normally displayed.
|
||||
IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string).
|
||||
IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string).
|
||||
IMGUI_API void PushID(const void* ptr_id); // push pointer into the ID stack (will hash pointer).
|
||||
IMGUI_API void PushID(int int_id); // push integer into the ID stack (will hash integer).
|
||||
IMGUI_API void PopID(); // pop from the ID stack.
|
||||
IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself
|
||||
IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end);
|
||||
IMGUI_API ImGuiID GetID(const void* ptr_id);
|
||||
|
||||
// return true if the cursor is over the operation's gizmo
|
||||
IMGUI_API bool IsOver(OPERATION op);
|
||||
IMGUI_API void SetGizmoSizeClipSpace(float value);
|
||||
|
||||
// Allow axis to flip
|
||||
// When true (default), the guizmo axis flip for better visibility
|
||||
// When false, they always stay along the positive world/local axis
|
||||
IMGUI_API void AllowAxisFlip(bool value);
|
||||
|
||||
// Configure the limit where axis are hidden
|
||||
IMGUI_API void SetAxisLimit(float value);
|
||||
// Set an axis mask to permanently hide a given axis (true -> hidden, false -> shown)
|
||||
IMGUI_API void SetAxisMask(bool x, bool y, bool z);
|
||||
// Configure the limit where planes are hiden
|
||||
IMGUI_API void SetPlaneLimit(float value);
|
||||
// from a x,y,z point in space and using Manipulation view/projection matrix, check if mouse is in pixel radius distance of that projected point
|
||||
IMGUI_API bool IsOver(float* position, float pixelRadius);
|
||||
|
||||
enum COLOR
|
||||
{
|
||||
DIRECTION_X, // directionColor[0]
|
||||
DIRECTION_Y, // directionColor[1]
|
||||
DIRECTION_Z, // directionColor[2]
|
||||
PLANE_X, // planeColor[0]
|
||||
PLANE_Y, // planeColor[1]
|
||||
PLANE_Z, // planeColor[2]
|
||||
SELECTION, // selectionColor
|
||||
INACTIVE, // inactiveColor
|
||||
TRANSLATION_LINE, // translationLineColor
|
||||
SCALE_LINE,
|
||||
ROTATION_USING_BORDER,
|
||||
ROTATION_USING_FILL,
|
||||
HATCHED_AXIS_LINES,
|
||||
TEXT,
|
||||
TEXT_SHADOW,
|
||||
COUNT
|
||||
};
|
||||
|
||||
struct Style
|
||||
{
|
||||
IMGUI_API Style();
|
||||
|
||||
float TranslationLineThickness; // Thickness of lines for translation gizmo
|
||||
float TranslationLineArrowSize; // Size of arrow at the end of lines for translation gizmo
|
||||
float RotationLineThickness; // Thickness of lines for rotation gizmo
|
||||
float RotationOuterLineThickness; // Thickness of line surrounding the rotation gizmo
|
||||
float ScaleLineThickness; // Thickness of lines for scale gizmo
|
||||
float ScaleLineCircleSize; // Size of circle at the end of lines for scale gizmo
|
||||
float HatchedAxisLineThickness; // Thickness of hatched axis lines
|
||||
float CenterCircleSize; // Size of circle at the center of the translate/scale gizmo
|
||||
|
||||
ImVec4 Colors[COLOR::COUNT];
|
||||
};
|
||||
|
||||
IMGUI_API Style& GetStyle();
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user