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
|
set(METACORE_EDITOR_PRIVATE_HEADERS
|
||||||
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h
|
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h
|
||||||
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h
|
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h
|
||||||
|
third_party/ImGuizmo/ImGuizmo.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(METACORE_EDITOR_SOURCES
|
set(METACORE_EDITOR_SOURCES
|
||||||
@ -168,6 +169,7 @@ set(METACORE_EDITOR_SOURCES
|
|||||||
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp
|
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp
|
||||||
Source/MetaCoreEditor/Private/MetaCoreEditorContext.cpp
|
Source/MetaCoreEditor/Private/MetaCoreEditorContext.cpp
|
||||||
Source/MetaCoreEditor/Private/MetaCoreEditorModule.cpp
|
Source/MetaCoreEditor/Private/MetaCoreEditorModule.cpp
|
||||||
|
third_party/ImGuizmo/ImGuizmo.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(MetaCoreEditor STATIC
|
add_library(MetaCoreEditor STATIC
|
||||||
@ -181,6 +183,7 @@ target_include_directories(MetaCoreEditor
|
|||||||
Source/MetaCoreEditor/Public
|
Source/MetaCoreEditor/Public
|
||||||
PRIVATE
|
PRIVATE
|
||||||
Source/MetaCoreEditor/Private
|
Source/MetaCoreEditor/Private
|
||||||
|
third_party/ImGuizmo
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(MetaCoreEditor
|
target_link_libraries(MetaCoreEditor
|
||||||
|
|||||||
@ -62,3 +62,9 @@ cmake --build --preset build-release
|
|||||||
- `Inspector` 中编辑 `Transform`
|
- `Inspector` 中编辑 `Transform`
|
||||||
- `Alt+左键 / 中键 / 滚轮 / F` 相机交互
|
- `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 "场景"; }
|
std::string GetPanelTitle() const override { return "场景"; }
|
||||||
bool IsOpenByDefault() const override { return true; }
|
bool IsOpenByDefault() const override { return true; }
|
||||||
|
|
||||||
void DrawPanel(MetaCoreEditorContext& editorContext) override {
|
void DrawPanel(MetaCoreEditorContext&) 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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,16 +7,27 @@
|
|||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
#include <imgui_impl_opengl3.h>
|
#include <imgui_impl_opengl3.h>
|
||||||
#include <imgui_impl_win32.h>
|
#include <imgui_impl_win32.h>
|
||||||
|
#include "ImGuizmo.h"
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#endif
|
#endif
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <filesystem>
|
#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 <glm/vec4.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <limits>
|
||||||
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
@ -80,6 +91,194 @@ void MetaCoreConfigureChineseFont() {
|
|||||||
#endif
|
#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
|
} // namespace
|
||||||
|
|
||||||
MetaCoreEditorApp::~MetaCoreEditorApp() {
|
MetaCoreEditorApp::~MetaCoreEditorApp() {
|
||||||
@ -96,12 +295,12 @@ bool MetaCoreEditorApp::Initialize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Window_.SetNativeWindowMessageHandler([](void* nativeWindowHandle, unsigned int message, std::uintptr_t wparam, std::intptr_t lparam) {
|
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),
|
static_cast<HWND>(nativeWindowHandle),
|
||||||
message,
|
message,
|
||||||
static_cast<WPARAM>(wparam),
|
static_cast<WPARAM>(wparam),
|
||||||
static_cast<LPARAM>(lparam)
|
static_cast<LPARAM>(lparam)
|
||||||
);
|
) != 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!RenderDevice_.Initialize(Window_)) {
|
if (!RenderDevice_.Initialize(Window_)) {
|
||||||
@ -153,6 +352,7 @@ int MetaCoreEditorApp::Run() {
|
|||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
ImGui_ImplWin32_NewFrame();
|
ImGui_ImplWin32_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
ImGuizmo::BeginFrame();
|
||||||
|
|
||||||
DrawEditorFrame();
|
DrawEditorFrame();
|
||||||
|
|
||||||
@ -243,10 +443,8 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
|||||||
EnsureDefaultDockLayout(dockSpaceId);
|
EnsureDefaultDockLayout(dockSpaceId);
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
MetaCoreIEditorPanelProvider* scenePanelProvider = nullptr;
|
|
||||||
for (const auto& panelProvider : ModuleRegistry_.GetPanelProviders()) {
|
for (const auto& panelProvider : ModuleRegistry_.GetPanelProviders()) {
|
||||||
if (panelProvider->GetPanelId() == "Scene") {
|
if (panelProvider->GetPanelId() == "Scene") {
|
||||||
scenePanelProvider = panelProvider.get();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,23 +482,250 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scenePanelProvider != nullptr && ModuleRegistry_.IsPanelOpen("Scene")) {
|
if (ModuleRegistry_.IsPanelOpen("Scene")) {
|
||||||
ImGuiDockNode* centralNode = ImGui::DockBuilderGetCentralNode(dockSpaceId);
|
ImGuiDockNode* centralNode = ImGui::DockBuilderGetCentralNode(dockSpaceId);
|
||||||
if (centralNode != nullptr) {
|
if (centralNode != nullptr) {
|
||||||
const ImVec2 overlayPosition = centralNode->Pos;
|
constexpr float sceneToolbarHeight = 34.0F;
|
||||||
const ImVec2 overlaySize = centralNode->Size;
|
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);
|
const ImVec2 mousePosition = ImGui::GetMousePos();
|
||||||
ImGui::SetNextWindowSize(overlaySize);
|
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::SetNextWindowBgAlpha(0.0F);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F);
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0F, 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 =
|
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_NoDecoration |
|
||||||
ImGuiWindowFlags_NoDocking |
|
ImGuiWindowFlags_NoDocking |
|
||||||
ImGuiWindowFlags_NoMove |
|
ImGuiWindowFlags_NoMove |
|
||||||
@ -309,14 +734,55 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
|||||||
ImGuiWindowFlags_NoNavFocus |
|
ImGuiWindowFlags_NoNavFocus |
|
||||||
ImGuiWindowFlags_NoBackground;
|
ImGuiWindowFlags_NoBackground;
|
||||||
|
|
||||||
bool sceneOpen = true;
|
bool sceneToolbarOpen = true;
|
||||||
ImGui::Begin("MetaCoreSceneOverlay", &sceneOpen, sceneOverlayFlags);
|
ImGui::Begin("MetaCoreSceneToolbarOverlay", &sceneToolbarOpen, sceneToolbarFlags);
|
||||||
scenePanelProvider->DrawPanel(*EditorContext_);
|
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::End();
|
||||||
|
|
||||||
ImGui::PopStyleColor(3);
|
|
||||||
ImGui::PopStyleVar(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);
|
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 MetaCoreEditorCameraController::BuildSceneView() const {
|
||||||
MetaCoreSceneView sceneView;
|
MetaCoreSceneView sceneView;
|
||||||
sceneView.CameraPosition = GetCameraPosition();
|
sceneView.CameraPosition = GetCameraPosition();
|
||||||
|
|||||||
@ -16,16 +16,17 @@ public:
|
|||||||
void FocusGameObject(const MetaCoreGameObject& gameObject);
|
void FocusGameObject(const MetaCoreGameObject& gameObject);
|
||||||
[[nodiscard]] MetaCoreSceneView BuildSceneView() const;
|
[[nodiscard]] MetaCoreSceneView BuildSceneView() const;
|
||||||
[[nodiscard]] glm::vec3 GetCameraPosition() const;
|
[[nodiscard]] glm::vec3 GetCameraPosition() const;
|
||||||
|
void ApplySceneView(const MetaCoreSceneView& sceneView);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] glm::vec3 GetForwardDirection() const;
|
[[nodiscard]] glm::vec3 GetForwardDirection() const;
|
||||||
[[nodiscard]] glm::vec3 GetRightDirection() const;
|
[[nodiscard]] glm::vec3 GetRightDirection() const;
|
||||||
[[nodiscard]] glm::vec3 GetUpDirection() const;
|
[[nodiscard]] glm::vec3 GetUpDirection() const;
|
||||||
|
|
||||||
glm::vec3 Target_{0.0F, 0.7F, 0.0F};
|
glm::vec3 Target_{0.0F, 0.5F, 0.0F};
|
||||||
float Distance_ = 7.5F;
|
float Distance_ = 8.5F;
|
||||||
float YawDegrees_ = 38.0F;
|
float YawDegrees_ = 45.0F;
|
||||||
float PitchDegrees_ = -28.0F;
|
float PitchDegrees_ = -32.0F;
|
||||||
float FieldOfViewDegrees_ = 60.0F;
|
float FieldOfViewDegrees_ = 60.0F;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,10 @@ MetaCoreId MetaCoreEditorContext::GetSelectedObjectId() const { return SelectedO
|
|||||||
void MetaCoreEditorContext::SetSelectedObjectId(MetaCoreId objectId) { SelectedObjectId_ = objectId; }
|
void MetaCoreEditorContext::SetSelectedObjectId(MetaCoreId objectId) { SelectedObjectId_ = objectId; }
|
||||||
MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() { return Scene_.FindGameObject(SelectedObjectId_); }
|
MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() { return Scene_.FindGameObject(SelectedObjectId_); }
|
||||||
const MetaCoreGameObject* MetaCoreEditorContext::GetSelectedGameObject() const { 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) {
|
void MetaCoreEditorContext::AddConsoleMessage(MetaCoreLogLevel level, const std::string& category, const std::string& message) {
|
||||||
LogService_.AddEntry(level, category, message);
|
LogService_.AddEntry(level, category, message);
|
||||||
|
|||||||
@ -17,6 +17,18 @@ struct MetaCoreGameObject;
|
|||||||
class MetaCoreEditorModuleRegistry;
|
class MetaCoreEditorModuleRegistry;
|
||||||
class MetaCoreEditorCameraController;
|
class MetaCoreEditorCameraController;
|
||||||
|
|
||||||
|
enum class MetaCoreGizmoOperation {
|
||||||
|
None = 0,
|
||||||
|
Translate,
|
||||||
|
Rotate,
|
||||||
|
Scale
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MetaCoreGizmoMode {
|
||||||
|
Local = 0,
|
||||||
|
World
|
||||||
|
};
|
||||||
|
|
||||||
struct MetaCoreSceneViewportState {
|
struct MetaCoreSceneViewportState {
|
||||||
float Left = 0.0F;
|
float Left = 0.0F;
|
||||||
float Top = 0.0F;
|
float Top = 0.0F;
|
||||||
@ -57,6 +69,10 @@ public:
|
|||||||
void SetSelectedObjectId(MetaCoreId objectId);
|
void SetSelectedObjectId(MetaCoreId objectId);
|
||||||
[[nodiscard]] MetaCoreGameObject* GetSelectedGameObject();
|
[[nodiscard]] MetaCoreGameObject* GetSelectedGameObject();
|
||||||
[[nodiscard]] const MetaCoreGameObject* GetSelectedGameObject() const;
|
[[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 AddConsoleMessage(MetaCoreLogLevel level, const std::string& category, const std::string& message);
|
||||||
void SetDockLayoutBuilt(bool built);
|
void SetDockLayoutBuilt(bool built);
|
||||||
[[nodiscard]] bool HasDockLayoutBuilt() const;
|
[[nodiscard]] bool HasDockLayoutBuilt() const;
|
||||||
@ -71,6 +87,8 @@ private:
|
|||||||
std::unique_ptr<MetaCoreEditorCameraController> CameraController_;
|
std::unique_ptr<MetaCoreEditorCameraController> CameraController_;
|
||||||
MetaCoreSceneViewportState SceneViewportState_{};
|
MetaCoreSceneViewportState SceneViewportState_{};
|
||||||
MetaCoreId SelectedObjectId_ = 0;
|
MetaCoreId SelectedObjectId_ = 0;
|
||||||
|
MetaCoreGizmoOperation GizmoOperation_ = MetaCoreGizmoOperation::Translate;
|
||||||
|
MetaCoreGizmoMode GizmoMode_ = MetaCoreGizmoMode::Local;
|
||||||
bool DockLayoutBuilt_ = false;
|
bool DockLayoutBuilt_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,11 @@ bool MetaCoreInput::IsMouseButtonDown(MetaCoreMouseButton button) const {
|
|||||||
return CurrentMouseStates_[static_cast<std::size_t>(button)];
|
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 {
|
float MetaCoreInput::GetMouseWheelDelta() const {
|
||||||
return MouseWheelDelta_;
|
return MouseWheelDelta_;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
#include "MetaCorePlatform/MetaCoreWindow.h"
|
#include "MetaCorePlatform/MetaCoreWindow.h"
|
||||||
|
|
||||||
#include "graphicsWindow.h"
|
#include "graphicsWindow.h"
|
||||||
#include "graphicsWindowProc.h"
|
|
||||||
#include "load_prc_file.h"
|
#include "load_prc_file.h"
|
||||||
#include "nativeWindowHandle.h"
|
#include "nativeWindowHandle.h"
|
||||||
#include "pandaFramework.h"
|
#include "pandaFramework.h"
|
||||||
@ -13,6 +12,7 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <unordered_map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@ -55,6 +55,11 @@ bool MetaCoreIsVirtualKeyDown(int virtualKey) {
|
|||||||
return (GetAsyncKeyState(virtualKey) & 0x8000) != 0;
|
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
|
} // namespace
|
||||||
|
|
||||||
class MetaCoreWindow::MetaCoreWindowImpl {
|
class MetaCoreWindow::MetaCoreWindowImpl {
|
||||||
@ -65,37 +70,41 @@ public:
|
|||||||
HWND NativeHandle = nullptr;
|
HWND NativeHandle = nullptr;
|
||||||
MetaCoreInput Input{};
|
MetaCoreInput Input{};
|
||||||
MetaCoreNativeWindowMessageHandler MessageHandler{};
|
MetaCoreNativeWindowMessageHandler MessageHandler{};
|
||||||
std::unique_ptr<class MetaCoreWindowMessageProc> MessageProc{};
|
WNDPROC OriginalWindowProc = nullptr;
|
||||||
std::chrono::steady_clock::time_point LastFrameTime = std::chrono::steady_clock::now();
|
std::chrono::steady_clock::time_point LastFrameTime = std::chrono::steady_clock::now();
|
||||||
float DeltaSeconds = 1.0F / 60.0F;
|
float DeltaSeconds = 1.0F / 60.0F;
|
||||||
bool Initialized = false;
|
bool Initialized = false;
|
||||||
bool CloseRequested = false;
|
bool CloseRequested = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MetaCoreWindowMessageProc final : public GraphicsWindowProc {
|
LRESULT CALLBACK MetaCoreWindowSubclassProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||||
public:
|
auto& windowMap = MetaCoreGetWindowMap();
|
||||||
explicit MetaCoreWindowMessageProc(MetaCoreWindow::MetaCoreWindowImpl& owner)
|
const auto iterator = windowMap.find(hwnd);
|
||||||
: Owner_(owner) {
|
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;
|
MetaCoreWindow::MetaCoreWindowImpl& owner = *iterator->second;
|
||||||
|
bool handledByEditor = false;
|
||||||
private:
|
if (owner.MessageHandler) {
|
||||||
MetaCoreWindow::MetaCoreWindowImpl& Owner_;
|
handledByEditor = owner.MessageHandler(hwnd, msg, static_cast<std::uintptr_t>(wparam), static_cast<std::intptr_t>(lparam));
|
||||||
};
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg == WM_MOUSEWHEEL) {
|
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) {
|
} 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()
|
MetaCoreWindow::MetaCoreWindow()
|
||||||
@ -133,10 +142,10 @@ bool MetaCoreWindow::Initialize(int width, int height, const std::string& title)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Impl_->MessageProc = std::make_unique<MetaCoreWindowMessageProc>(*Impl_);
|
MetaCoreGetWindowMap()[Impl_->NativeHandle] = Impl_.get();
|
||||||
if (Impl_->GraphicsWindowHandle->supports_window_procs()) {
|
Impl_->OriginalWindowProc = reinterpret_cast<WNDPROC>(
|
||||||
Impl_->GraphicsWindowHandle->add_window_proc(Impl_->MessageProc.get());
|
SetWindowLongPtr(Impl_->NativeHandle, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&MetaCoreWindowSubclassProc))
|
||||||
}
|
);
|
||||||
|
|
||||||
Impl_->CloseRequested = false;
|
Impl_->CloseRequested = false;
|
||||||
Impl_->LastFrameTime = std::chrono::steady_clock::now();
|
Impl_->LastFrameTime = std::chrono::steady_clock::now();
|
||||||
@ -149,12 +158,13 @@ void MetaCoreWindow::Shutdown() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Impl_->GraphicsWindowHandle != nullptr && Impl_->MessageProc != nullptr && Impl_->GraphicsWindowHandle->supports_window_procs()) {
|
if (Impl_->NativeHandle != nullptr) {
|
||||||
Impl_->GraphicsWindowHandle->remove_window_proc(Impl_->MessageProc.get());
|
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) {
|
if (Impl_->WindowFrameworkHandle != nullptr) {
|
||||||
Impl_->Framework.close_window(Impl_->WindowFrameworkHandle);
|
Impl_->Framework.close_window(Impl_->WindowFrameworkHandle);
|
||||||
Impl_->WindowFrameworkHandle = nullptr;
|
Impl_->WindowFrameworkHandle = nullptr;
|
||||||
@ -162,6 +172,7 @@ void MetaCoreWindow::Shutdown() {
|
|||||||
|
|
||||||
Impl_->GraphicsWindowHandle = nullptr;
|
Impl_->GraphicsWindowHandle = nullptr;
|
||||||
Impl_->NativeHandle = nullptr;
|
Impl_->NativeHandle = nullptr;
|
||||||
|
Impl_->OriginalWindowProc = nullptr;
|
||||||
Impl_->Framework.close_framework();
|
Impl_->Framework.close_framework();
|
||||||
Impl_->Initialized = false;
|
Impl_->Initialized = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,6 +49,9 @@ public:
|
|||||||
// 判断鼠标按键当前是否按下。
|
// 判断鼠标按键当前是否按下。
|
||||||
[[nodiscard]] bool IsMouseButtonDown(MetaCoreMouseButton button) const;
|
[[nodiscard]] bool IsMouseButtonDown(MetaCoreMouseButton button) const;
|
||||||
|
|
||||||
|
// 判断鼠标按键是否在本帧刚刚按下。
|
||||||
|
[[nodiscard]] bool WasMouseButtonPressed(MetaCoreMouseButton button) const;
|
||||||
|
|
||||||
// 返回当前帧累计的滚轮值。
|
// 返回当前帧累计的滚轮值。
|
||||||
[[nodiscard]] float GetMouseWheelDelta() const;
|
[[nodiscard]] float GetMouseWheelDelta() const;
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ namespace MetaCore {
|
|||||||
class MetaCoreRenderDevice;
|
class MetaCoreRenderDevice;
|
||||||
|
|
||||||
// 定义宿主窗口层向上暴露的原生消息回调,用于把 Win32 消息转交给 ImGui。
|
// 定义宿主窗口层向上暴露的原生消息回调,用于把 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 窗口、时间步进与基础输入采集。
|
// 封装 Panda3D 窗口、时间步进与基础输入采集。
|
||||||
class MetaCoreWindow {
|
class MetaCoreWindow {
|
||||||
|
|||||||
@ -40,4 +40,8 @@ void MetaCoreEditorViewportRenderer::RenderSceneToViewport(const MetaCoreScene&
|
|||||||
SceneBridge_.SyncScene(scene);
|
SceneBridge_.SyncScene(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MetaCoreEditorViewportRenderer::TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const {
|
||||||
|
return SceneBridge_.TryGetObjectWorldMatrix(objectId, worldMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace MetaCore
|
} // namespace MetaCore
|
||||||
|
|||||||
@ -8,6 +8,12 @@
|
|||||||
#include "ambientLight.h"
|
#include "ambientLight.h"
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "directionalLight.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 "lineSegs.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
#include "nodePath.h"
|
#include "nodePath.h"
|
||||||
@ -18,6 +24,7 @@
|
|||||||
#define GLM_ENABLE_EXPERIMENTAL
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
#include <glm/ext/matrix_transform.hpp>
|
#include <glm/ext/matrix_transform.hpp>
|
||||||
#include <glm/gtx/euler_angles.hpp>
|
#include <glm/gtx/euler_angles.hpp>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -59,11 +66,28 @@ LMatrix4f MetaCoreConvertTransformToPanda(const MetaCoreTransformComponent& tran
|
|||||||
}
|
}
|
||||||
|
|
||||||
LPoint3f MetaCoreToPandaPoint(const glm::vec3& value) {
|
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) {
|
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) {
|
NodePath MetaCoreCreateGridNode(const NodePath& parentNode) {
|
||||||
@ -102,6 +126,83 @@ NodePath MetaCoreCreateAxisNode(const NodePath& parentNode) {
|
|||||||
return parentNode.attach_new_node(axisBuilder.create());
|
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
|
} // namespace
|
||||||
|
|
||||||
class MetaCorePandaSceneBridge::MetaCorePandaSceneBridgeImpl {
|
class MetaCorePandaSceneBridge::MetaCorePandaSceneBridgeImpl {
|
||||||
@ -207,7 +308,7 @@ void MetaCorePandaSceneBridge::SyncScene(const MetaCoreScene& scene) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
objectState.RootNode.set_name(gameObject.Name);
|
objectState.RootNode.set_name(gameObject.Name);
|
||||||
objectState.RootNode.set_mat(MetaCoreConvertTransformToPanda(gameObject.Transform));
|
MetaCoreApplyTransformToPandaNode(gameObject.Transform, objectState.RootNode);
|
||||||
|
|
||||||
if (gameObject.ParentId != 0) {
|
if (gameObject.ParentId != 0) {
|
||||||
const auto parentIterator = Impl_->ObjectStates.find(gameObject.ParentId);
|
const auto parentIterator = Impl_->ObjectStates.find(gameObject.ParentId);
|
||||||
@ -222,7 +323,7 @@ void MetaCorePandaSceneBridge::SyncScene(const MetaCoreScene& scene) {
|
|||||||
|
|
||||||
if (gameObject.MeshRenderer.has_value()) {
|
if (gameObject.MeshRenderer.has_value()) {
|
||||||
if (objectState.MeshNode.is_empty()) {
|
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()) {
|
if (!objectState.MeshNode.is_empty()) {
|
||||||
@ -309,9 +410,31 @@ void MetaCorePandaSceneBridge::ApplySceneView(const MetaCoreSceneView& sceneView
|
|||||||
if (cameraNode != nullptr) {
|
if (cameraNode != nullptr) {
|
||||||
auto* perspectiveLens = DCAST(PerspectiveLens, cameraNode->get_lens());
|
auto* perspectiveLens = DCAST(PerspectiveLens, cameraNode->get_lens());
|
||||||
if (perspectiveLens != nullptr) {
|
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
|
} // namespace MetaCore
|
||||||
|
|||||||
@ -11,11 +11,29 @@
|
|||||||
#include "perspectiveLens.h"
|
#include "perspectiveLens.h"
|
||||||
#include "windowFramework.h"
|
#include "windowFramework.h"
|
||||||
|
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace MetaCore {
|
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 {
|
class MetaCoreRenderDevice::MetaCoreRenderDeviceImpl {
|
||||||
public:
|
public:
|
||||||
MetaCoreWindow* Window = nullptr;
|
MetaCoreWindow* Window = nullptr;
|
||||||
@ -153,6 +171,12 @@ void MetaCoreRenderDevice::SetSceneViewportRect(const MetaCoreViewportRect& view
|
|||||||
1.0F - clampedBottom,
|
1.0F - clampedBottom,
|
||||||
1.0F - clampedTop
|
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 {
|
void* MetaCoreRenderDevice::GetNativeWindowFrameworkHandle() const {
|
||||||
@ -167,4 +191,28 @@ void* MetaCoreRenderDevice::GetNativeEditorCameraHandle() const {
|
|||||||
return Impl_->EditorCamera.is_empty() ? nullptr : static_cast<void*>(&Impl_->EditorCamera);
|
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
|
} // namespace MetaCore
|
||||||
|
|||||||
@ -23,6 +23,9 @@ public:
|
|||||||
// 将场景状态同步到 Panda3D,并更新编辑器相机。
|
// 将场景状态同步到 Panda3D,并更新编辑器相机。
|
||||||
void RenderSceneToViewport(const MetaCoreScene& scene, const MetaCoreSceneView& sceneView);
|
void RenderSceneToViewport(const MetaCoreScene& scene, const MetaCoreSceneView& sceneView);
|
||||||
|
|
||||||
|
// 返回 Panda3D 中对象当前真实使用的世界矩阵。
|
||||||
|
[[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MetaCoreRenderDevice* RenderDevice_ = nullptr;
|
MetaCoreRenderDevice* RenderDevice_ = nullptr;
|
||||||
MetaCorePandaSceneBridge SceneBridge_{};
|
MetaCorePandaSceneBridge SceneBridge_{};
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "MetaCoreFoundation/MetaCoreId.h"
|
||||||
|
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace MetaCore {
|
namespace MetaCore {
|
||||||
@ -26,6 +30,9 @@ public:
|
|||||||
// 更新编辑器相机与当前选中对象高亮状态。
|
// 更新编辑器相机与当前选中对象高亮状态。
|
||||||
void ApplySceneView(const MetaCoreSceneView& sceneView);
|
void ApplySceneView(const MetaCoreSceneView& sceneView);
|
||||||
|
|
||||||
|
// 返回 Panda3D 场景中对象当前真实使用的世界矩阵。
|
||||||
|
[[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class MetaCorePandaSceneBridgeImpl;
|
class MetaCorePandaSceneBridgeImpl;
|
||||||
std::unique_ptr<MetaCorePandaSceneBridgeImpl> Impl_{};
|
std::unique_ptr<MetaCorePandaSceneBridgeImpl> Impl_{};
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "MetaCoreRender/MetaCoreRenderTypes.h"
|
#include "MetaCoreRender/MetaCoreRenderTypes.h"
|
||||||
|
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace MetaCore {
|
namespace MetaCore {
|
||||||
@ -40,6 +42,9 @@ public:
|
|||||||
// 返回编辑器相机节点原生对象指针,仅供 Panda3D 桥接层使用。
|
// 返回编辑器相机节点原生对象指针,仅供 Panda3D 桥接层使用。
|
||||||
[[nodiscard]] void* GetNativeEditorCameraHandle() const;
|
[[nodiscard]] void* GetNativeEditorCameraHandle() const;
|
||||||
|
|
||||||
|
// 返回 Panda3D 当前真实使用的编辑器相机视图矩阵和投影矩阵。
|
||||||
|
[[nodiscard]] bool TryGetEditorCameraMatrices(glm::mat4& viewMatrix, glm::mat4& projectionMatrix) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class MetaCoreRenderDeviceImpl;
|
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