feat: viewport click-to-select

This commit is contained in:
maelstrom 2025-02-01 22:55:07 +01:00
parent 4caa65a437
commit 9437faadaa
10 changed files with 82 additions and 33 deletions

View file

@ -7,8 +7,10 @@
#include <glm/geometric.hpp>
#include <glm/matrix.hpp>
#include <reactphysics3d/collision/RaycastInfo.h>
#include <vector>
#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<Part*>(rayHitTarget->getUserData())->name.c_str());
std::shared_ptr<Part> part = partFromBody(rayHitTarget);
//part.selected = true;
setSelection(std::vector<InstanceRefWeak> { part });
return;
} default:
return;

View file

@ -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 {};

View file

@ -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);

View file

@ -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();
}

View file

@ -1,4 +1,5 @@
#include "camera.h"
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
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);
}

View file

@ -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);
};

View file

@ -8,3 +8,23 @@ Camera camera(glm::vec3(0.0, 0.0, 3.0));
std::shared_ptr<Workspace> workspace = Workspace::New();
std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
std::vector<InstanceRefWeak> currentSelection;
std::vector<SelectionUpdateHandler> selectionUpdateHandlers;
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer) {
for (SelectionUpdateHandler handler : selectionUpdateHandlers) {
handler(currentSelection, newSelection, fromExplorer);
}
currentSelection = newSelection;
}
const std::vector<InstanceRefWeak> getSelection() {
return currentSelection;
}
void addSelectionListener(SelectionUpdateHandler handler) {
selectionUpdateHandlers.push_back(handler);
}

View file

@ -1,4 +1,5 @@
#pragma once
#include "objects/base/instance.h"
#include "objects/workspace.h"
#include "camera.h"
#include <functional>
@ -8,10 +9,15 @@ class Instance;
// typedef std::function<void(std::shared_ptr<Instance> element, std::optional<std::shared_ptr<Instance>> newParent)> HierarchyUpdateHandler;
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPreUpdateHandler;
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPostUpdateHandler;
typedef std::function<void(std::vector<InstanceRefWeak> oldSelection, std::vector<InstanceRefWeak> 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> workspace;
extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer = false);
const std::vector<InstanceRefWeak> getSelection();
void addSelectionListener(SelectionUpdateHandler handler);

View file

@ -31,6 +31,7 @@ public:
glm::quat rotation = glm::identity<glm::quat>();
glm::vec3 scale;
Material material;
bool selected = false;
bool anchored = false;
rp::RigidBody* rigidBody = nullptr;

View file

@ -1,9 +1,12 @@
#pragma once
#include <glm/ext/vector_float3.hpp>
#include <memory>
#include <reactphysics3d/body/Body.h>
#include <reactphysics3d/mathematics/Quaternion.h>
#include <reactphysics3d/mathematics/Vector3.h>
#include <reactphysics3d/mathematics/mathematics.h>
#include <glm/ext.hpp>
#include "objects/part.h"
namespace rp = reactphysics3d;
@ -22,3 +25,9 @@ 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<Part> partFromBody(rp::Body* body) {
Part* raw = reinterpret_cast<Part*>(body->getUserData());
std::shared_ptr<Part> shared = std::dynamic_pointer_cast<Part>(raw->shared_from_this());
return shared;
}