diff options
author | hnOsmium0001 <[email protected]> | 2022-05-07 23:13:14 -0700 |
---|---|---|
committer | hnOsmium0001 <[email protected]> | 2022-05-07 23:13:14 -0700 |
commit | c2cba4424ea902f062e6af4733670f2be0494889 (patch) | |
tree | 39b1e9215a5b22a58792517bc67dcbb2b185ffa7 | |
parent | eb4e202ee8d6e32aebf252cc107f6ec8dfd75b8f (diff) |
Add more camera controls (3D mode, scroll scaling)
-rw-r--r-- | source/App.cpp | 4 | ||||
-rw-r--r-- | source/Camera.cpp | 30 | ||||
-rw-r--r-- | source/Camera.hpp | 10 | ||||
-rw-r--r-- | source/EditorCorePrivate.cpp | 198 | ||||
-rw-r--r-- | source/EditorCorePrivate.hpp | 16 |
5 files changed, 197 insertions, 61 deletions
diff --git a/source/App.cpp b/source/App.cpp index 9e8a298..45a7545 100644 --- a/source/App.cpp +++ b/source/App.cpp @@ -24,8 +24,8 @@ App::App() #endif mMainCamera.name = "Main Camera"s; - mMainCamera.Move(glm::vec3(0, 0, 1)); - mMainCamera.LookAtAngle(glm::vec3(0, 0, -1)); + mMainCamera.SetEyePos(glm::vec3(0, 0, 1)); + mMainCamera.SetTargetDirection(glm::vec3(0, 0, -1)); mMainCamera.SetHasPerspective(false); } diff --git a/source/Camera.cpp b/source/Camera.cpp index 0508508..9041d80 100644 --- a/source/Camera.cpp +++ b/source/Camera.cpp @@ -5,23 +5,23 @@ #include <glm/gtc/matrix_transform.hpp> Camera::Camera() - : pos(10.0f, 10.0f, 5.0f) - , lookAt(0.0, 0.0f, 0.0f) + : eye(0.0f, 0.0f, 0.0f) + , target(0.0, 0.0f, -1.0f) , perspective{ false } { } -void Camera::Move(glm::vec3 pos) { - auto lookVector = this->lookAt - /*Old pos*/ this->pos; - this->pos = pos; - this->lookAt = pos + lookVector; +void Camera::SetEyePos(glm::vec3 pos) { + auto lookVector = this->target - /*Old pos*/ this->eye; + this->eye = pos; + this->target = pos + lookVector; } -void Camera::LookAtAngle(glm::vec3 lookVector) { - this->lookAt = this->pos + lookVector; +void Camera::SetTargetPos(glm::vec3 pos) { + this->target = pos; } -void Camera::LookAtPos(glm::vec3 pos) { - this->lookAt = pos; +void Camera::SetTargetDirection(glm::vec3 lookVector) { + this->target = this->eye + lookVector; } void Camera::SetHasPerspective(bool perspective) { @@ -29,7 +29,7 @@ void Camera::SetHasPerspective(bool perspective) { } glm::mat4 Camera::CalcViewMatrix() const { - return glm::lookAt(pos, lookAt, glm::vec3(0, 1, 0)); + return glm::lookAt(eye, target, glm::vec3(0, 1, 0)); } glm::mat4 Camera::CalcProjectionMatrix() const { @@ -37,9 +37,9 @@ glm::mat4 Camera::CalcProjectionMatrix() const { return glm::perspective(90.0f, AppConfig::mainWindowAspectRatio, 0.1f, 1000.0f); } else { return glm::ortho( - pos.x - AppConfig::mainWidnowWidth / 2, - pos.x + AppConfig::mainWidnowWidth / 2, - pos.y - AppConfig::mainWindowHeight / 2, - pos.y + AppConfig::mainWindowHeight / 2); + eye.x - AppConfig::mainWidnowWidth / 2, + eye.x + AppConfig::mainWidnowWidth / 2, + eye.y - AppConfig::mainWindowHeight / 2, + eye.y + AppConfig::mainWindowHeight / 2); } } diff --git a/source/Camera.hpp b/source/Camera.hpp index c7ca6de..8cfcd0d 100644 --- a/source/Camera.hpp +++ b/source/Camera.hpp @@ -6,16 +6,16 @@ class Camera { public: std::string name; - glm::vec3 pos; - glm::vec3 lookAt; + glm::vec3 eye; + glm::vec3 target; bool perspective; public: Camera(); - void Move(glm::vec3 pos); - void LookAtAngle(glm::vec3 lookVector); - void LookAtPos(glm::vec3 pos); + void SetEyePos(glm::vec3 pos); + void SetTargetPos(glm::vec3 pos); + void SetTargetDirection(glm::vec3 lookVector); bool HasPerspective() const { return perspective; } void SetHasPerspective(bool perspective); diff --git a/source/EditorCorePrivate.cpp b/source/EditorCorePrivate.cpp index e4fdc54..38ba8f6 100644 --- a/source/EditorCorePrivate.cpp +++ b/source/EditorCorePrivate.cpp @@ -26,6 +26,9 @@ #include <cstdint> #include <cstdlib> #include <functional> +#include <glm/gtc/quaternion.hpp> +#include <glm/gtc/type_ptr.hpp> +#include <glm/gtx/quaternion.hpp> #include <limits> #include <memory> #include <string> @@ -315,8 +318,8 @@ EditorInstance::EditorInstance(App* app) : mApp{ app } , mEdContentBrowser(&mEdInspector) { mEditorCamera.name = "Editor Camera"s; - mEditorCamera.Move(glm::vec3(50, 40, 50)); - mEditorCamera.LookAtPos(glm::vec3(0, 0, 0)); + mEditorCamera.SetEyePos(glm::vec3(0, 0, 200)); + mEditorCamera.SetTargetPos(glm::vec3(0, 0, 0)); mEditorCamera.SetHasPerspective(true); app->BindActiveCamera(&mEditorCamera); } @@ -371,46 +374,114 @@ void EditorInstance::Show() { auto& camera = *mApp->GetActiveCamera(); ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y); - ImGuizmo::SetDrawlist(ImGui::GetForegroundDrawList()); + ImGuizmo::SetDrawlist(ImGui::GetBackgroundDrawList()); - auto cameraPos = camera.pos; - { - if (ImGui::IsMouseClicked(ImGuiMouseButton_Right) && !io.WantCaptureMouse && !mDragCam_Happening) { - mDragCam_CamInitial = camera.pos; - mDragCam_CursorInitial = ImGui::GetMousePos(); - mDragCam_Happening = true; - } - if (mDragCam_Happening) { - auto newPos = ImGui::GetMousePos(); - // NOTE: we are emulating as if the mouse is dragging the "canvas", through moving the camera in the opposite direction of the natural position delta - cameraPos.x = mDragCam_CamInitial.x + mDragCam_CursorInitial.x - newPos.x; - cameraPos.y = mDragCam_CamInitial.y + -(mDragCam_CursorInitial.y - newPos.y); // Invert Y delta because ImGui uses top-left origin (mouse moving down translates to positive value, but in our coordinate system down is negative) + if (IsCurrentCameraEditor() && mEcm == ECM_Side3D) { + float viewManipulateRight = io.DisplaySize.x; + float viewManipulateTop = 0; - // Draw movement indicator - auto drawList = ImGui::GetForegroundDrawList(); - ImGui::DrawArrow(drawList, ImVec2(mDragCam_CursorInitial.x, mDragCam_CursorInitial.y), newPos, IM_COL32(0, 255, 255, 255)); - } - if (ImGui::IsMouseReleased(ImGuiMouseButton_Right)) { - mDragCam_Happening = false; + // TODO get rid of this massive hack: how to manage const better for intuitively read-only, but write doesn't-care data? + auto& lastFrameInfo = const_cast<RendererFrameInfo&>(mApp->GetWorldRenderer()->GetLastFrameInfo()); + auto& view = lastFrameInfo.matrixView; + auto& proj = lastFrameInfo.matrixProj; + ImGuizmo::ViewManipulate( + glm::value_ptr(view), + 200.0f, // TODO + ImVec2(viewManipulateRight - 128, viewManipulateTop), + ImVec2(128, 128), + 0x10101010); + + // TODO draw this as a part of the world so it doesn't block objects +#if 0 + glm::mat4 identity(1.00f); + ImGuizmo::DrawGrid( + glm::value_ptr(view), + glm::value_ptr(proj), + glm::value_ptr(identity), + 100.f); +#endif + + // Extract eye and target position from view matrix + // - View matrix transforms world space to view space + // - Inverse view matrix should transform view space into world space + // - In view space, camera's pos is (0,0,0) and the look/forward vector should be (0,0,-1) + auto invView = glm::inverse(view); + camera.eye = invView * glm::vec4(0, 0, 0, 1); + camera.target = camera.eye + glm::vec3(invView * glm::vec4(0, 0, -1, 1)); + + { // Camera controls + auto cameraPos = camera.eye; + auto cameraForward = glm::normalize(camera.target - camera.eye); + // Always move on the horzontal flat plane + cameraForward.y = 0.0f; + + if (mMoveCamKeyboard) { + constexpr float kCameraMoveSpeed = 5.0f; + if (ImGui::IsKeyDown(ImGuiKey_W)) { + cameraPos += kCameraMoveSpeed * cameraForward; + } + if (ImGui::IsKeyDown(ImGuiKey_S)) { + auto cameraBack = glm::normalize(-cameraForward); + cameraPos += kCameraMoveSpeed * cameraBack; + } + if (ImGui::IsKeyDown(ImGuiKey_A)) { + auto cameraLeft = glm::normalize(glm::cross(cameraForward, glm::vec3(0, 1, 0))); + cameraPos += kCameraMoveSpeed * cameraLeft; + } + if (ImGui::IsKeyDown(ImGuiKey_D)) { + auto cameraLeft = glm::normalize(glm::cross(cameraForward, glm::vec3(0, 1, 0))); + auto cameraRight = -cameraLeft; + cameraPos += kCameraMoveSpeed * cameraRight; + } + } + + camera.SetEyePos(cameraPos); } + } else { + { // Camera controls + auto cameraPos = camera.eye; - if (mMoveCamKeyboard) { - constexpr float kCameraMoveSpeed = 5.0f; - if (ImGui::IsKeyDown(ImGuiKey_W)) { - cameraPos.y += kCameraMoveSpeed; + if (ImGui::IsMouseClicked(ImGuiMouseButton_Right) && !io.WantCaptureMouse && !mDragCam_Happening) { + mDragCam_CamInitial = camera.eye; + mDragCam_CursorInitial = ImGui::GetMousePos(); + mDragCam_Happening = true; } - if (ImGui::IsKeyDown(ImGuiKey_S)) { - cameraPos.y -= kCameraMoveSpeed; + if (mDragCam_Happening) { + auto newPos = ImGui::GetMousePos(); + // NOTE: we are emulating as if the mouse is dragging the "canvas", through moving the camera in the opposite direction of the natural position delta + cameraPos.x = mDragCam_CamInitial.x + mDragCam_CursorInitial.x - newPos.x; + cameraPos.y = mDragCam_CamInitial.y + -(mDragCam_CursorInitial.y - newPos.y); // Invert Y delta because ImGui uses top-left origin (mouse moving down translates to positive value, but in our coordinate system down is negative) + + // Draw movement indicator + auto drawList = ImGui::GetForegroundDrawList(); + ImGui::DrawArrow(drawList, ImVec2(mDragCam_CursorInitial.x, mDragCam_CursorInitial.y), newPos, IM_COL32(0, 255, 255, 255)); } - if (ImGui::IsKeyDown(ImGuiKey_A)) { - cameraPos.x -= kCameraMoveSpeed; + if (ImGui::IsMouseReleased(ImGuiMouseButton_Right)) { + mDragCam_Happening = false; + } + + if (mMoveCamKeyboard) { + if (ImGui::IsKeyDown(ImGuiKey_W)) { + cameraPos.y += mMoveCamSlideSpeed; + } + if (ImGui::IsKeyDown(ImGuiKey_S)) { + cameraPos.y -= mMoveCamSlideSpeed; + } + if (ImGui::IsKeyDown(ImGuiKey_A)) { + cameraPos.x -= mMoveCamSlideSpeed; + } + if (ImGui::IsKeyDown(ImGuiKey_D)) { + cameraPos.x += mMoveCamSlideSpeed; + } } - if (ImGui::IsKeyDown(ImGuiKey_D)) { - cameraPos.x += kCameraMoveSpeed; + + if (mMoveCamScrollWheel) { + cameraPos.z = std::clamp(cameraPos.z + io.MouseWheel, 0.1f, 100.0f); } + + camera.SetEyePos(cameraPos); } } - camera.Move(cameraPos); if (mWindowVisible_Inspector) { ImGui::Begin("Inspector"); @@ -503,12 +574,16 @@ void EditorInstance::ShowWorldProperties() { auto& camera = *mApp->GetActiveCamera(); ImGui::TextUnformatted("Active camera:"); ImGui::Indent(); -#define CAMERA_POS_TEXT "at (%.3f, %.3f, %.3f)\nlooking at (%.3f, %.3f, %.3f)", camera.pos.x, camera.pos.y, camera.pos.z, camera.lookAt.x, camera.lookAt.y, camera.lookAt.z +#define CAMERA_POS_TEXT "at (%.3f, %.3f, %.3f)\nlooking at (%.3f, %.3f, %.3f)", camera.eye.x, camera.eye.y, camera.eye.z, camera.target.x, camera.target.y, camera.target.z ImGui::TextUnformatted(camera.name.c_str()); ImGui::Text(CAMERA_POS_TEXT); if (ImGui::BeginPopupContextItem("##CTXMENU")) { if (ImGui::MenuItem("Reset to origin")) { - camera.Move(glm::vec3(1.0f, 1.0f, 1.0f)); + camera.SetEyePos(glm::vec3(1.0f, 1.0f, 1.0f)); + } + if (ImGui::MenuItem("Reset completely")) { + camera.eye = glm::vec3(0, 0, 1); + camera.target = glm::vec3(0, 0, 0); } if (ImGui::MenuItem("Copy")) { char buffer[2048]; @@ -519,7 +594,53 @@ void EditorInstance::ShowWorldProperties() { ImGui::EndPopup(); } #undef CAMERA_POS_TEXT + if (IsCurrentCameraEditor()) { + const char* preview; + switch (mEcm) { + case ECM_2D: preview = "2D view"; break; + case ECM_Side3D: preview = "Side 3D view"; break; + } + if (ImGui::BeginCombo("Mode", preview)) { + if (ImGui::Selectable("2D view", mEcm == ECM_2D)) { + if (mEcm != ECM_2D) { + mEcm = ECM_2D; + + // TODO project eye to world plane + + camera.SetHasPerspective(false); + } + } + if (ImGui::Selectable("Side 3D view", mEcm == ECM_Side3D)) { + if (mEcm != ECM_Side3D) { + mEcm = ECM_Side3D; + + auto origEye = camera.eye; + auto origTarget = camera.target; + + // New setup: focus on the point of world plane that we were originally "hovering above" + camera.target = origEye; + camera.target.z = 0.0f; + + // New setup: move the eye back at an angle + glm::vec3 vec; + vec.x = std::cos(60.0f); + vec.y = 0.0f; + vec.z = std::sin(60.0f); + camera.eye = camera.target + 200.0f * vec; + + camera.SetHasPerspective(true); + } + } + ImGui::EndCombo(); + } + } + ImGui::Checkbox("Move camera with WASD", &mMoveCamKeyboard); + ImGui::SliderFloat("Camera slide speed", &mMoveCamSlideSpeed, 0.1f, 10.0f); + + ImGui::Checkbox("Move camera with scoll wheel", &mMoveCamScrollWheel); + ImGui::SliderFloat("Camera scroll speed", &mMoveCamScrollSpeed, 0.01, 10.0f); + ImGui::Unindent(); } @@ -614,12 +735,11 @@ void EditorInstance::ShowInspector(GameObject* object) { auto ShowGuizmo = [&](/*array[6]*/ float* bounds = nullptr) { glm::mat4 identityMatrix(1.00f); - // TODO get rid of this massive hack: how to manage const better for intuitively read-only, but write doesn't-care data? - auto& lastFrameInfo = const_cast<RendererFrameInfo&>(mApp->GetWorldRenderer()->GetLastFrameInfo()); + auto& lastFrameInfo = mApp->GetWorldRenderer()->GetLastFrameInfo(); auto& cameraViewMat = lastFrameInfo.matrixView; - float* cameraView = &cameraViewMat[0][0]; + const float* cameraView = &cameraViewMat[0][0]; auto& cameraProjMat = lastFrameInfo.matrixProj; - float* cameraProj = &cameraProjMat[0][0]; + const float* cameraProj = &cameraProjMat[0][0]; auto objectPos = object->GetPos(); auto objectScale = object->GetScale(); @@ -743,7 +863,7 @@ void EditorInstance::ShowInspector(GameObject* object) { sg->SetSize(glm::vec3(bounds[3] - bounds[0], bounds[4] - bounds[1], bounds[5] - bounds[2])); auto size = sg->GetSize(); - if (ImGui::InputFloat2("Size", &size.x)) { + if (ImGui::InputFloat3("Size", &size.x)) { sg->SetSize(size); } diff --git a/source/EditorCorePrivate.hpp b/source/EditorCorePrivate.hpp index 566131b..616ca45 100644 --- a/source/EditorCorePrivate.hpp +++ b/source/EditorCorePrivate.hpp @@ -67,6 +67,14 @@ struct GuizmoState { // TODO editor undo stack class EditorInstance : public IEditor { +public: + enum EditorCameraMode { + // "Game mode": the camera views from the side, where the forward vector is perpendicular to the world plane + ECM_2D, + // "Editor mode": equivalent to Unity's 3D mode, the camera is completely free and controlled as a 3d camera + ECM_Side3D, + }; + private: App* mApp; GameObject* mPopupCurrent_GameObject = nullptr; @@ -79,6 +87,9 @@ private: glm::vec3 mDragCam_CamInitial; ImVec2 mDragCam_CursorInitial; int mSpriteView_Frame; + float mMoveCamScrollSpeed = 1.0f; + float mMoveCamSlideSpeed = 10.0f; + EditorCameraMode mEcm = ECM_2D; bool mSpriteView_OpenNextFrame = false; bool mWindowVisible_ImGuiDemo = false; bool mWindowVisible_CommandPalette = false; @@ -88,6 +99,7 @@ private: bool mWindowVisible_WorldProperties = true; bool mDragCam_Happening = false; bool mMoveCamKeyboard = false; + bool mMoveCamScrollWheel = false; public: EditorInstance(App* app); @@ -102,6 +114,10 @@ public: void OpenSpriteViewer(SpriteDefinition* sprite) override; private: + bool IsCurrentCameraEditor() const { + return !mApp->IsGameRunning(); + } + void ShowWorldProperties(); void ShowInspector(IresObject* ires); |