aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorhnOsmium0001 <[email protected]>2022-05-07 23:13:14 -0700
committerhnOsmium0001 <[email protected]>2022-05-07 23:13:14 -0700
commitc2cba4424ea902f062e6af4733670f2be0494889 (patch)
tree39b1e9215a5b22a58792517bc67dcbb2b185ffa7 /source
parenteb4e202ee8d6e32aebf252cc107f6ec8dfd75b8f (diff)
Add more camera controls (3D mode, scroll scaling)
Diffstat (limited to 'source')
-rw-r--r--source/App.cpp4
-rw-r--r--source/Camera.cpp30
-rw-r--r--source/Camera.hpp10
-rw-r--r--source/EditorCorePrivate.cpp198
-rw-r--r--source/EditorCorePrivate.hpp16
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);