Compare commits

...

4 commits

16 changed files with 165 additions and 15 deletions

View file

@ -2,11 +2,19 @@
#include <chrono> #include <chrono>
#include <QMouseEvent> #include <QMouseEvent>
#include <glm/ext/matrix_projection.hpp>
#include <glm/ext/vector_float3.hpp>
#include <glm/geometric.hpp>
#include <glm/matrix.hpp>
#include <reactphysics3d/collision/RaycastInfo.h>
#include <vector>
#include "GLFW/glfw3.h" #include "GLFW/glfw3.h"
#include "physics/util.h"
#include "qcursor.h" #include "qcursor.h"
#include "qevent.h" #include "qevent.h"
#include "qnamespace.h" #include "qnamespace.h"
#include "qwindowdefs.h"
#include "rendering/renderer.h" #include "rendering/renderer.h"
#include "physics/simulation.h" #include "physics/simulation.h"
#include "camera.h" #include "camera.h"
@ -50,11 +58,44 @@ void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) {
// QCursor::setPos(lastMousePos); // QCursor::setPos(lastMousePos);
} }
void MainGLWidget::mousePressEvent(QMouseEvent* evt) { class FirstRayHit : public rp::RaycastCallback {
if (evt->button() != Qt::RightButton) return; rp::Body** target;
lastMousePos = evt->pos(); virtual rp::decimal notifyRaycastHit(const rp::RaycastInfo& raycastInfo) override {
isMouseDragging = true; if (reinterpret_cast<Part*>(raycastInfo.body->getUserData())->name == "Baseplate") return 1;
*target = raycastInfo.body;
return 0;
}
public:
FirstRayHit(rp::Body** target) : target(target) {}
};
void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
switch(evt->button()) {
// Camera drag
case Qt::RightButton: {
lastMousePos = evt->pos();
isMouseDragging = true;
return;
// Clicking on objects
} case Qt::LeftButton: {
QPoint position = evt->pos();
rp::Body* rayHitTarget = NULL;
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;
std::shared_ptr<Part> part = partFromBody(rayHitTarget);
//part.selected = true;
setSelection(std::vector<InstanceRefWeak> { part });
return;
} default:
return;
}
} }
void MainGLWidget::mouseReleaseEvent(QMouseEvent* evt) { void MainGLWidget::mouseReleaseEvent(QMouseEvent* evt) {

View file

@ -71,6 +71,10 @@ QModelIndex ExplorerModel::toIndex(InstanceRef item) {
return QModelIndex{}; return QModelIndex{};
} }
QModelIndex ExplorerModel::ObjectToIndex(InstanceRef item) {
return toIndex(item);
}
QModelIndex ExplorerModel::parent(const QModelIndex &index) const { QModelIndex ExplorerModel::parent(const QModelIndex &index) const {
if (!index.isValid()) if (!index.isValid())
return {}; return {};

View file

@ -41,6 +41,7 @@ public:
Qt::DropActions supportedDragActions() const override; Qt::DropActions supportedDragActions() const override;
Qt::DropActions supportedDropActions() const override; Qt::DropActions supportedDropActions() const override;
InstanceRef fromIndex(const QModelIndex index) const; InstanceRef fromIndex(const QModelIndex index) const;
QModelIndex ObjectToIndex(InstanceRef item);
private: private:
InstanceRef rootItem; InstanceRef rootItem;
QModelIndex toIndex(InstanceRef item); QModelIndex toIndex(InstanceRef item);

View file

@ -1,6 +1,7 @@
#include "explorerview.h" #include "explorerview.h"
#include "explorermodel.h" #include "explorermodel.h"
#include "common.h" #include "common.h"
#include "objects/base/instance.h"
#include "qabstractitemmodel.h" #include "qabstractitemmodel.h"
#include "qaction.h" #include "qaction.h"
#include "qnamespace.h" #include "qnamespace.h"
@ -27,6 +28,15 @@ ExplorerView::ExplorerView(QWidget* parent):
contextMenu.exec(this->viewport()->mapToGlobal(point)); 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(); buildContextMenu();
} }

View file

@ -1,4 +1,5 @@
#include "camera.h" #include "camera.h"
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp> #include <glm/ext/matrix_transform.hpp>
Camera::Camera(glm::vec3 initalPosition) { Camera::Camera(glm::vec3 initalPosition) {
@ -53,3 +54,19 @@ void Camera::processRotation(float deltaX, float deltaY) {
if(pitch < -89.0f) if(pitch < -89.0f)
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); Camera(glm::vec3 initialPosition);
glm::mat4 getLookAt(); 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 processRotation(float deltaX, float deltaY);
void processMovement(Direction direction, float deltaTime); 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::shared_ptr<Workspace> workspace = Workspace::New();
std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler; std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler; 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 #pragma once
#include "objects/base/instance.h"
#include "objects/workspace.h" #include "objects/workspace.h"
#include "camera.h" #include "camera.h"
#include <functional> #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(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)> HierarchyPreUpdateHandler;
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPostUpdateHandler; 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 Camera camera;
extern std::shared_ptr<Workspace> workspace; extern std::shared_ptr<Workspace> workspace;
extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler; extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler; 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

@ -38,7 +38,19 @@ Instance::Instance(InstanceType* type) {
Instance::~Instance () { Instance::~Instance () {
} }
void Instance::SetParent(std::optional<std::shared_ptr<Instance>> newParent) { // TODO: Test this
bool Instance::ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent) {
for (std::optional<std::shared_ptr<Instance>> currentParent = newParent; currentParent.has_value(); currentParent = currentParent.value()->GetParent()) {
if (currentParent.value() == this->shared_from_this())
return false;
}
return true;
}
bool Instance::SetParent(std::optional<std::shared_ptr<Instance>> newParent) {
if (!ancestryContinuityCheck(newParent))
return false;
auto lastParent = GetParent(); auto lastParent = GetParent();
if (hierarchyPreUpdateHandler.has_value()) hierarchyPreUpdateHandler.value()(this->shared_from_this(), lastParent, newParent); if (hierarchyPreUpdateHandler.has_value()) hierarchyPreUpdateHandler.value()(this->shared_from_this(), lastParent, newParent);
// If we currently have a parent, remove ourselves from it before adding ourselves to the new one // If we currently have a parent, remove ourselves from it before adding ourselves to the new one
@ -56,6 +68,7 @@ void Instance::SetParent(std::optional<std::shared_ptr<Instance>> newParent) {
if (hierarchyPostUpdateHandler.has_value()) hierarchyPostUpdateHandler.value()(this->shared_from_this(), lastParent, newParent); if (hierarchyPostUpdateHandler.has_value()) hierarchyPostUpdateHandler.value()(this->shared_from_this(), lastParent, newParent);
this->OnParentUpdated(lastParent, newParent); this->OnParentUpdated(lastParent, newParent);
return true;
} }
std::optional<std::shared_ptr<Instance>> Instance::GetParent() { std::optional<std::shared_ptr<Instance>> Instance::GetParent() {
@ -83,6 +96,7 @@ tl::expected<void, MemberNotFound> Instance::SetPropertyValue(std::string name,
if (!meta) return tl::make_unexpected(MemberNotFound()); if (!meta) return tl::make_unexpected(MemberNotFound());
meta->codec.write(value, meta->backingField); meta->codec.write(value, meta->backingField);
if (meta->updateCallback) meta->updateCallback.value()(name);
return {}; return {};
} }

View file

@ -35,6 +35,8 @@ private:
std::vector<std::shared_ptr<Instance>> children; std::vector<std::shared_ptr<Instance>> children;
std::optional<std::vector<std::string>> cachedMemberList; std::optional<std::vector<std::string>> cachedMemberList;
bool ancestryContinuityCheck(std::optional<std::shared_ptr<Instance>> newParent);
protected: protected:
std::unique_ptr<MemberMap> memberMap; std::unique_ptr<MemberMap> memberMap;
@ -48,7 +50,7 @@ public:
// Instance is abstract, so it should not implement GetClass directly // Instance is abstract, so it should not implement GetClass directly
virtual InstanceType* GetClass() = 0; virtual InstanceType* GetClass() = 0;
void SetParent(std::optional<std::shared_ptr<Instance>> newParent); bool SetParent(std::optional<std::shared_ptr<Instance>> newParent);
std::optional<std::shared_ptr<Instance>> GetParent(); std::optional<std::shared_ptr<Instance>> GetParent();
inline const std::vector<std::shared_ptr<Instance>> GetChildren() { return children; } inline const std::vector<std::shared_ptr<Instance>> GetChildren() { return children; }

View file

@ -2,6 +2,7 @@
#include "../../datatypes/base.h" #include "../../datatypes/base.h"
#include "datatypes/meta.h" #include "datatypes/meta.h"
#include <functional>
#include <map> #include <map>
#include <memory> #include <memory>
#include <optional> #include <optional>
@ -32,10 +33,16 @@ constexpr FieldCodec fieldCodecOf() {
}; };
} }
template <typename T>
std::function<void(std::string name)> memberFunctionOf(void(T::*func)(std::string), T* obj) {
return std::bind(func, obj, std::placeholders::_1);
}
struct PropertyMeta { struct PropertyMeta {
void* backingField; void* backingField;
const Data::TypeInfo* type; const Data::TypeInfo* type;
FieldCodec codec; FieldCodec codec;
std::optional<std::function<void(std::string name)>> updateCallback;
}; };
typedef std::variant<PropertyMeta> MemberMeta; typedef std::variant<PropertyMeta> MemberMeta;

View file

@ -4,6 +4,7 @@
#include "objects/base/member.h" #include "objects/base/member.h"
#include <memory> #include <memory>
#include <optional> #include <optional>
#include "physics/simulation.h"
static InstanceType TYPE_ { static InstanceType TYPE_ {
.super = Instance::TYPE, .super = Instance::TYPE,
@ -23,11 +24,10 @@ Part::Part(): Part(PartConstructParams {}) {
Part::Part(PartConstructParams params): Instance(&TYPE_), position(params.position), rotation(params.rotation), Part::Part(PartConstructParams params): Instance(&TYPE_), position(params.position), rotation(params.rotation),
scale(params.scale), material(params.material), anchored(params.anchored) { scale(params.scale), material(params.material), anchored(params.anchored) {
this->memberMap = std::make_unique<MemberMap>(MemberMap { this->memberMap = std::make_unique<MemberMap>(MemberMap {
.super = std::move(this->memberMap), .super = std::move(this->memberMap),
.members = { .members = {
{ "Anchored", { .backingField = &anchored, .type = &Data::Bool::TYPE, .codec = fieldCodecOf<Data::Bool, bool>() } } { "Anchored", { .backingField = &anchored, .type = &Data::Bool::TYPE, .codec = fieldCodecOf<Data::Bool, bool>(), .updateCallback = memberFunctionOf(&Part::onUpdated, this) } }
} }
}); });
} }
@ -47,3 +47,7 @@ void Part::OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, s
// TODO: Sleeping bodies that touch this one also need to be updated // TODO: Sleeping bodies that touch this one also need to be updated
} }
void Part::onUpdated(std::string property) {
syncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
}

View file

@ -22,6 +22,7 @@ struct PartConstructParams {
class Part : public Instance { class Part : public Instance {
protected: protected:
void OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) override; void OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) override;
void onUpdated(std::string);
public: public:
static InstanceType* TYPE; static InstanceType* TYPE;
@ -30,6 +31,7 @@ public:
glm::quat rotation = glm::identity<glm::quat>(); glm::quat rotation = glm::identity<glm::quat>();
glm::vec3 scale; glm::vec3 scale;
Material material; Material material;
bool selected = false;
bool anchored = false; bool anchored = false;
rp::RigidBody* rigidBody = nullptr; rp::RigidBody* rigidBody = nullptr;

View file

@ -2,12 +2,14 @@
#include <glm/ext/matrix_float3x3.hpp> #include <glm/ext/matrix_float3x3.hpp>
#include <glm/gtc/quaternion.hpp> #include <glm/gtc/quaternion.hpp>
#include <memory> #include <memory>
#include <reactphysics3d/collision/RaycastInfo.h>
#include <reactphysics3d/collision/shapes/BoxShape.h> #include <reactphysics3d/collision/shapes/BoxShape.h>
#include <reactphysics3d/collision/shapes/CollisionShape.h> #include <reactphysics3d/collision/shapes/CollisionShape.h>
#include <reactphysics3d/components/RigidBodyComponents.h> #include <reactphysics3d/components/RigidBodyComponents.h>
#include <reactphysics3d/engine/EventListener.h> #include <reactphysics3d/engine/EventListener.h>
#include <reactphysics3d/engine/PhysicsCommon.h> #include <reactphysics3d/engine/PhysicsCommon.h>
#include <reactphysics3d/mathematics/Quaternion.h> #include <reactphysics3d/mathematics/Quaternion.h>
#include <reactphysics3d/mathematics/Ray.h>
#include <reactphysics3d/mathematics/Transform.h> #include <reactphysics3d/mathematics/Transform.h>
#include <reactphysics3d/mathematics/Vector3.h> #include <reactphysics3d/mathematics/Vector3.h>
#include <reactphysics3d/memory/DefaultAllocator.h> #include <reactphysics3d/memory/DefaultAllocator.h>
@ -36,6 +38,8 @@ void simulationInit() {
world = physicsCommon->createPhysicsWorld(); world = physicsCommon->createPhysicsWorld();
world->setGravity(rp::Vector3(0, -196.2, 0)); world->setGravity(rp::Vector3(0, -196.2, 0));
world->setEventListener(&eventListener);
} }
void syncPartPhysics(std::shared_ptr<Part> part) { void syncPartPhysics(std::shared_ptr<Part> part) {
@ -58,7 +62,7 @@ void syncPartPhysics(std::shared_ptr<Part> part) {
part->rigidBody->addCollider(shape, rp::Transform()); part->rigidBody->addCollider(shape, rp::Transform());
part->rigidBody->setType(part->anchored ? rp::BodyType::STATIC : rp::BodyType::DYNAMIC); part->rigidBody->setType(part->anchored ? rp::BodyType::STATIC : rp::BodyType::DYNAMIC);
world->setEventListener(&eventListener); part->rigidBody->setUserData(&*part);
} }
void physicsStep(float deltaTime) { void physicsStep(float deltaTime) {
@ -76,3 +80,8 @@ void physicsStep(float deltaTime) {
part->rotation = rpToGlm(transform.getOrientation()); part->rotation = rpToGlm(transform.getOrientation());
} }
} }
void castRay(glm::vec3 point, glm::vec3 rotation, float maxLength, rp::RaycastCallback* callback) {
rp::Ray ray(glmToRp(point), glmToRp(glm::normalize(rotation)) * maxLength);
world->raycast(ray, callback);
}

View file

@ -1,8 +1,11 @@
#pragma once #pragma once
#include "../objects/part.h" #include "../objects/part.h"
#include <glm/ext/vector_float3.hpp>
#include <memory> #include <memory>
#include <reactphysics3d/collision/RaycastInfo.h>
void simulationInit(); void simulationInit();
void syncPartPhysics(std::shared_ptr<Part> part); void syncPartPhysics(std::shared_ptr<Part> part);
void physicsStep(float deltaTime); void physicsStep(float deltaTime);
void castRay(glm::vec3 point, glm::vec3 rotation, float maxLength, rp::RaycastCallback* callback);

View file

@ -1,9 +1,12 @@
#pragma once #pragma once
#include <glm/ext/vector_float3.hpp> #include <glm/ext/vector_float3.hpp>
#include <memory>
#include <reactphysics3d/body/Body.h>
#include <reactphysics3d/mathematics/Quaternion.h> #include <reactphysics3d/mathematics/Quaternion.h>
#include <reactphysics3d/mathematics/Vector3.h> #include <reactphysics3d/mathematics/Vector3.h>
#include <reactphysics3d/mathematics/mathematics.h> #include <reactphysics3d/mathematics/mathematics.h>
#include <glm/ext.hpp> #include <glm/ext.hpp>
#include "objects/part.h"
namespace rp = reactphysics3d; namespace rp = reactphysics3d;
@ -22,3 +25,9 @@ inline glm::vec3 rpToGlm(rp::Vector3 vec) {
inline glm::quat rpToGlm(rp::Quaternion quat) { inline glm::quat rpToGlm(rp::Quaternion quat) {
return glm::quat(quat.w, quat.x, quat.y, quat.z); 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;
}