From 9437faadaa1dabd11edf9d2bb031d4a491da0b9a Mon Sep 17 00:00:00 2001 From: maelstrom Date: Sat, 1 Feb 2025 22:55:07 +0100 Subject: [PATCH] feat: viewport click-to-select --- editor/mainglwidget.cpp | 38 ++++++++-------------------------- editor/panes/explorermodel.cpp | 4 ++++ editor/panes/explorermodel.h | 1 + editor/panes/explorerview.cpp | 10 +++++++++ src/camera.cpp | 17 +++++++++++++++ src/camera.h | 3 ++- src/common.cpp | 22 +++++++++++++++++++- src/common.h | 10 +++++++-- src/objects/part.h | 1 + src/physics/util.h | 9 ++++++++ 10 files changed, 82 insertions(+), 33 deletions(-) diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index 6b82f16..d9f0d9d 100644 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -7,8 +7,10 @@ #include #include #include +#include #include "GLFW/glfw3.h" +#include "physics/util.h" #include "qcursor.h" #include "qevent.h" #include "qnamespace.h" @@ -82,36 +84,14 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) { QPoint position = evt->pos(); rp::Body* rayHitTarget = NULL; - // VVV Thank goodness for this person's answer - // https://stackoverflow.com/a/30005258/16255372 - - // glm::vec3 worldPos = camera.cameraPos + glm::vec3(glm::vec4(float(position.x()) / width() - 0.5f, float(position.y()) / height() - 0.5f, 0, 0) * camera.getLookAt()); - glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)width() / (float)height(), 0.1f, 100.0f); - glm::mat4 view = glm::lookAt(glm::vec3(0), camera.cameraFront, camera.cameraUp); - glm::mat4 inverseViewport = glm::inverse(projection * view); - - glm::vec2 ndc = glm::vec2(float(position.x()) / width() * 2.f - 1.f, -float(position.y()) / height() * 2.f + 1.f); - glm::vec4 world = glm::normalize(inverseViewport * glm::vec4(ndc, 1, 1)); - glm::vec3 flat = glm::vec3(world) / world.w; // https://stackoverflow.com/a/68870587/16255372 - - printf("At: %f; %f; %f\n", world.x, world.y, world.z); - - workspace->AddChild(lastPart = Part::New({ - .position = camera.cameraPos + glm::vec3(world) * 10.f, - .rotation = glm::vec3(0), - .scale = glm::vec3(1, 1, 1), - .material = Material { - .diffuse = glm::vec3(1.0f, 0.5f, 0.31f), - .specular = glm::vec3(0.5f, 0.5f, 0.5f), - .shininess = 32.0f, - }, - .anchored = true, - })); - syncPartPhysics(lastPart); - - castRay(camera.cameraPos, world, 500, new FirstRayHit(&rayHitTarget)); + glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height())); + castRay(camera.cameraPos, pointDir, 50000, new FirstRayHit(&rayHitTarget)); if (!rayHitTarget) return; - printf("Hit: %s\n", reinterpret_cast(rayHitTarget->getUserData())->name.c_str()); + std::shared_ptr part = partFromBody(rayHitTarget); + + //part.selected = true; + setSelection(std::vector { part }); + return; } default: return; diff --git a/editor/panes/explorermodel.cpp b/editor/panes/explorermodel.cpp index bf133a0..75d3265 100644 --- a/editor/panes/explorermodel.cpp +++ b/editor/panes/explorermodel.cpp @@ -71,6 +71,10 @@ QModelIndex ExplorerModel::toIndex(InstanceRef item) { return QModelIndex{}; } +QModelIndex ExplorerModel::ObjectToIndex(InstanceRef item) { + return toIndex(item); +} + QModelIndex ExplorerModel::parent(const QModelIndex &index) const { if (!index.isValid()) return {}; diff --git a/editor/panes/explorermodel.h b/editor/panes/explorermodel.h index ceadde2..aee8860 100644 --- a/editor/panes/explorermodel.h +++ b/editor/panes/explorermodel.h @@ -41,6 +41,7 @@ public: Qt::DropActions supportedDragActions() const override; Qt::DropActions supportedDropActions() const override; InstanceRef fromIndex(const QModelIndex index) const; + QModelIndex ObjectToIndex(InstanceRef item); private: InstanceRef rootItem; QModelIndex toIndex(InstanceRef item); diff --git a/editor/panes/explorerview.cpp b/editor/panes/explorerview.cpp index cc3cbe1..9573151 100644 --- a/editor/panes/explorerview.cpp +++ b/editor/panes/explorerview.cpp @@ -1,6 +1,7 @@ #include "explorerview.h" #include "explorermodel.h" #include "common.h" +#include "objects/base/instance.h" #include "qabstractitemmodel.h" #include "qaction.h" #include "qnamespace.h" @@ -27,6 +28,15 @@ ExplorerView::ExplorerView(QWidget* parent): contextMenu.exec(this->viewport()->mapToGlobal(point)); }); + addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { + this->clearSelection(); + for (InstanceRefWeak inst : newSelection) { + if (inst.expired()) continue; + QModelIndex index = this->model.ObjectToIndex(inst.lock()); + this->selectionModel()->select(index, QItemSelectionModel::SelectionFlag::Select); + } + }); + buildContextMenu(); } diff --git a/src/camera.cpp b/src/camera.cpp index d0d14d7..7814e71 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -1,4 +1,5 @@ #include "camera.h" +#include #include Camera::Camera(glm::vec3 initalPosition) { @@ -53,3 +54,19 @@ void Camera::processRotation(float deltaX, float deltaY) { if(pitch < -89.0f) pitch = -89.0f; } + +glm::vec3 Camera::getScreenDirection(glm::vec2 screenPos, glm::vec2 screenSize) { + // VVV Thank goodness for this person's answer + // https://stackoverflow.com/a/30005258/16255372 + + // glm::vec3 worldPos = camera.cameraPos + glm::vec3(glm::vec4(float(position.x()) / width() - 0.5f, float(position.y()) / height() - 0.5f, 0, 0) * camera.getLookAt()); + glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)screenSize.x / (float)screenSize.y, 0.1f, 100.0f); + glm::mat4 view = glm::lookAt(glm::vec3(0), this->cameraFront, this->cameraUp); + glm::mat4 inverseViewport = glm::inverse(projection * view); + + glm::vec2 ndc = glm::vec2(screenPos.x / screenSize.x * 2.f - 1.f, -screenPos.y / screenSize.y * 2.f + 1.f); + glm::vec4 world = glm::normalize(inverseViewport * glm::vec4(ndc, 1, 1)); + //glm::vec3 flat = glm::vec3(world) / world.w; // https://stackoverflow.com/a/68870587/16255372 + + return glm::vec3(world); +} \ No newline at end of file diff --git a/src/camera.h b/src/camera.h index cd6d588..4adafcd 100644 --- a/src/camera.h +++ b/src/camera.h @@ -24,7 +24,8 @@ public: Camera(glm::vec3 initialPosition); glm::mat4 getLookAt(); + /** Converts a set of screen coords to a direction from the camera's pos */ + glm::vec3 getScreenDirection(glm::vec2 screenPos, glm::vec2 screenSize); void processRotation(float deltaX, float deltaY); void processMovement(Direction direction, float deltaTime); - }; diff --git a/src/common.cpp b/src/common.cpp index cd35566..183c5f6 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -7,4 +7,24 @@ Camera camera(glm::vec3(0.0, 0.0, 3.0)); //std::vector parts; std::shared_ptr workspace = Workspace::New(); std::optional hierarchyPreUpdateHandler; -std::optional hierarchyPostUpdateHandler; \ No newline at end of file +std::optional hierarchyPostUpdateHandler; + + +std::vector currentSelection; +std::vector selectionUpdateHandlers; + +void setSelection(std::vector newSelection, bool fromExplorer) { + for (SelectionUpdateHandler handler : selectionUpdateHandlers) { + handler(currentSelection, newSelection, fromExplorer); + } + + currentSelection = newSelection; +} + +const std::vector getSelection() { + return currentSelection; +} + +void addSelectionListener(SelectionUpdateHandler handler) { + selectionUpdateHandlers.push_back(handler); +} \ No newline at end of file diff --git a/src/common.h b/src/common.h index ef3886f..2600cfc 100644 --- a/src/common.h +++ b/src/common.h @@ -1,4 +1,5 @@ #pragma once +#include "objects/base/instance.h" #include "objects/workspace.h" #include "camera.h" #include @@ -8,10 +9,15 @@ class Instance; // typedef std::function element, std::optional> newParent)> HierarchyUpdateHandler; typedef std::function oldParent, std::optional newParent)> HierarchyPreUpdateHandler; typedef std::function oldParent, std::optional newParent)> HierarchyPostUpdateHandler; +typedef std::function oldSelection, std::vector newSelection, bool fromExplorer)> SelectionUpdateHandler; -// TEMPORARY COMMON DATA FOR DIFFERENT INTERNAL COMPONENTS +// TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS extern Camera camera; extern std::shared_ptr workspace; extern std::optional hierarchyPreUpdateHandler; -extern std::optional hierarchyPostUpdateHandler; \ No newline at end of file +extern std::optional hierarchyPostUpdateHandler; + +void setSelection(std::vector newSelection, bool fromExplorer = false); +const std::vector getSelection(); +void addSelectionListener(SelectionUpdateHandler handler); \ No newline at end of file diff --git a/src/objects/part.h b/src/objects/part.h index 6d604d5..4268533 100644 --- a/src/objects/part.h +++ b/src/objects/part.h @@ -31,6 +31,7 @@ public: glm::quat rotation = glm::identity(); glm::vec3 scale; Material material; + bool selected = false; bool anchored = false; rp::RigidBody* rigidBody = nullptr; diff --git a/src/physics/util.h b/src/physics/util.h index 2b1a2b9..82e487f 100644 --- a/src/physics/util.h +++ b/src/physics/util.h @@ -1,9 +1,12 @@ #pragma once #include +#include +#include #include #include #include #include +#include "objects/part.h" namespace rp = reactphysics3d; @@ -21,4 +24,10 @@ inline glm::vec3 rpToGlm(rp::Vector3 vec) { inline glm::quat rpToGlm(rp::Quaternion quat) { return glm::quat(quat.w, quat.x, quat.y, quat.z); +} + +inline std::shared_ptr partFromBody(rp::Body* body) { + Part* raw = reinterpret_cast(body->getUserData()); + std::shared_ptr shared = std::dynamic_pointer_cast(raw->shared_from_this()); + return shared; } \ No newline at end of file