refactor(handles): some refactoring with handles
This commit is contained in:
parent
882a215b36
commit
18b573db97
8 changed files with 94 additions and 106 deletions
|
@ -11,7 +11,7 @@ std::shared_ptr<DataModel> gDataModel = DataModel::New();
|
|||
std::shared_ptr<DataModel> editModeDataModel = gDataModel;
|
||||
std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
|
||||
std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
|
||||
std::shared_ptr<Handles> editorToolHandles = Handles::New();
|
||||
Handles editorToolHandles;
|
||||
|
||||
|
||||
std::vector<InstanceRefWeak> currentSelection;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
#include "objects/base/instance.h"
|
||||
#include "objects/handles.h"
|
||||
#include "handles.h"
|
||||
#include "objects/workspace.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "camera.h"
|
||||
|
@ -22,7 +22,7 @@ extern std::shared_ptr<DataModel> editModeDataModel;
|
|||
inline std::shared_ptr<Workspace> gWorkspace() { return std::dynamic_pointer_cast<Workspace>(gDataModel->services["Workspace"]); }
|
||||
extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
|
||||
extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
|
||||
extern std::shared_ptr<Handles> editorToolHandles;
|
||||
extern Handles editorToolHandles;
|
||||
|
||||
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer = false);
|
||||
const std::vector<InstanceRefWeak> getSelection();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include <glm/ext/scalar_common.hpp>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <reactphysics3d/collision/RaycastInfo.h>
|
||||
#include <reactphysics3d/engine/PhysicsCommon.h>
|
||||
|
@ -23,16 +24,23 @@ static CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0,
|
|||
static rp3d::PhysicsCommon common;
|
||||
static rp3d::PhysicsWorld* world = common.createPhysicsWorld();
|
||||
|
||||
Handles::Handles(): Instance(&TYPE) {
|
||||
std::shared_ptr<Part> getHandleAdornee() {
|
||||
for (std::weak_ptr<Instance> inst : getSelection()) {
|
||||
if (!inst.expired() && inst.lock()->IsA<Part>())
|
||||
return inst.lock()->CastTo<Part>().expect();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
CFrame Handles::GetCFrameOfHandle(HandleFace face) {
|
||||
if (adornee.expired()) return CFrame(glm::vec3(0,0,0), (Vector3)glm::vec3(0,0,0));
|
||||
CFrame getCFrameOfHandle(HandleFace face) {
|
||||
auto adornee = getHandleAdornee();
|
||||
if (adornee == nullptr) return CFrame(glm::vec3(0,0,0), (Vector3)glm::vec3(0,0,0));
|
||||
|
||||
CFrame localFrame = worldMode ? CFrame::IDENTITY + adornee.lock()->position() : adornee.lock()->cframe;
|
||||
CFrame localFrame = editorToolHandles.worldMode ? CFrame::IDENTITY + adornee->position() : adornee->cframe;
|
||||
|
||||
Vector3 handleNormal = face.normal;
|
||||
if (nixAxes)
|
||||
if (editorToolHandles.nixAxes)
|
||||
handleNormal = XYZToZXY * face.normal;
|
||||
|
||||
// We don't want this to align with local * face.normal, or else we have problems.
|
||||
|
@ -40,35 +48,36 @@ CFrame Handles::GetCFrameOfHandle(HandleFace face) {
|
|||
if (glm::abs(glm::dot(glm::vec3(localFrame.Rotation() * handleNormal), upAxis)) > 0.9999f)
|
||||
upAxis = glm::vec3(0, 1, 0);
|
||||
|
||||
glm::vec3 partSize = handlesType == HandlesType::RotateHandles ? glm::vec3(glm::max(adornee.lock()->size.x, adornee.lock()->size.y, adornee.lock()->size.z)) : adornee.lock()->size;
|
||||
Vector3 handleOffset = this->worldMode ? ((Vector3::ONE * 2.f) + adornee.lock()->GetAABB() * 0.5f) : Vector3(2.f + partSize * 0.5f);
|
||||
glm::vec3 partSize = editorToolHandles.handlesType == HandlesType::RotateHandles ? glm::vec3(glm::max(adornee->size.x, adornee->size.y, adornee->size.z)) : adornee->size;
|
||||
Vector3 handleOffset = editorToolHandles.worldMode ? ((Vector3::ONE * 2.f) + adornee->GetAABB() * 0.5f) : Vector3(2.f + partSize * 0.5f);
|
||||
Vector3 handlePos = localFrame * (handleOffset * handleNormal);
|
||||
CFrame cframe(handlePos, handlePos + localFrame.Rotation() * -handleNormal, upAxis);
|
||||
|
||||
return cframe;
|
||||
}
|
||||
|
||||
CFrame Handles::PartCFrameFromHandlePos(HandleFace face, Vector3 newPos) {
|
||||
if (adornee.expired()) return CFrame(glm::vec3(0,0,0), (Vector3)glm::vec3(0,0,0));
|
||||
CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos) {
|
||||
auto adornee = getHandleAdornee();
|
||||
if (adornee == nullptr) return CFrame(glm::vec3(0,0,0), (Vector3)glm::vec3(0,0,0));
|
||||
|
||||
CFrame localFrame = worldMode ? CFrame::IDENTITY + adornee.lock()->position() : adornee.lock()->cframe;
|
||||
CFrame localFrame = editorToolHandles.worldMode ? CFrame::IDENTITY + adornee->position() : adornee->cframe;
|
||||
CFrame inverseFrame = localFrame.Inverse();
|
||||
Vector3 handleOffset = this->worldMode ? ((Vector3::ONE * 2.f) + adornee.lock()->GetAABB() * 0.5f) : Vector3(2.f + adornee.lock()->size * 0.5f);
|
||||
Vector3 handleOffset = editorToolHandles.worldMode ? ((Vector3::ONE * 2.f) + adornee->GetAABB() * 0.5f) : Vector3(2.f + adornee->size * 0.5f);
|
||||
|
||||
Vector3 handlePos = localFrame * (handleOffset * face.normal);
|
||||
|
||||
// glm::vec3 localPos = inverseFrame * newPos;
|
||||
glm::vec3 newPartPos = newPos - localFrame.Rotation() * (handleOffset * face.normal);
|
||||
return adornee.lock()->cframe.Rotation() + newPartPos;
|
||||
return adornee->cframe.Rotation() + newPartPos;
|
||||
}
|
||||
|
||||
std::optional<HandleFace> Handles::RaycastHandle(rp3d::Ray ray) {
|
||||
std::optional<HandleFace> raycastHandle(rp3d::Ray ray) {
|
||||
for (HandleFace face : HandleFace::Faces) {
|
||||
CFrame cframe = GetCFrameOfHandle(face);
|
||||
CFrame cframe = getCFrameOfHandle(face);
|
||||
// Implement manual detection via boxes instead of... this shit
|
||||
// This code also hardly works, and is not good at all... Hooo nope.
|
||||
rp3d::RigidBody* body = world->createRigidBody(CFrame::IDENTITY + cframe.Position());
|
||||
body->addCollider(common.createBoxShape(cframe.Rotation() * Vector3(HandleSize(face) / 2.f)), rp3d::Transform::identity());
|
||||
body->addCollider(common.createBoxShape(cframe.Rotation() * Vector3(handleSize(face) / 2.f)), rp3d::Transform::identity());
|
||||
|
||||
rp3d::RaycastInfo info;
|
||||
if (body->raycast(ray, info)) {
|
||||
|
@ -82,8 +91,8 @@ std::optional<HandleFace> Handles::RaycastHandle(rp3d::Ray ray) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
Vector3 Handles::HandleSize(HandleFace face) {
|
||||
if (handlesType == HandlesType::MoveHandles)
|
||||
Vector3 handleSize(HandleFace face) {
|
||||
if (editorToolHandles.handlesType == HandlesType::MoveHandles)
|
||||
return glm::vec3(0.5f, 0.5f, 2.f);
|
||||
return glm::vec3(1,1,1);
|
||||
}
|
43
core/src/handles.h
Normal file
43
core/src/handles.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "datatypes/cframe.h"
|
||||
#include "objects/part.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
class HandleFace {
|
||||
HandleFace(int index, glm::vec3 normal) : index(index), normal(normal){}
|
||||
|
||||
public:
|
||||
int index;
|
||||
glm::vec3 normal;
|
||||
|
||||
static HandleFace XPos;
|
||||
static HandleFace XNeg;
|
||||
static HandleFace YPos;
|
||||
static HandleFace YNeg;
|
||||
static HandleFace ZPos;
|
||||
static HandleFace ZNeg;
|
||||
static std::array<HandleFace, 6> Faces;
|
||||
};
|
||||
|
||||
enum HandlesType {
|
||||
MoveHandles,
|
||||
ScaleHandles,
|
||||
RotateHandles,
|
||||
};
|
||||
|
||||
struct Handles {
|
||||
bool nixAxes = false; // XYZ -> ZXY, used with rotation
|
||||
bool active;
|
||||
HandlesType handlesType;
|
||||
|
||||
// World-space handles vs local-space handles
|
||||
bool worldMode = false;
|
||||
};
|
||||
|
||||
std::shared_ptr<Part> getHandleAdornee();
|
||||
CFrame getCFrameOfHandle(HandleFace face);
|
||||
CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos);
|
||||
Vector3 handleSize(HandleFace face);
|
||||
std::optional<HandleFace> raycastHandle(rp3d::Ray ray);
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "datatypes/cframe.h"
|
||||
#include "objects/annotation.h"
|
||||
#include "objects/base/service.h"
|
||||
#include "objects/part.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <reactphysics3d/body/RigidBody.h>
|
||||
|
||||
class HandleFace {
|
||||
HandleFace(int index, glm::vec3 normal) : index(index), normal(normal){}
|
||||
|
||||
public:
|
||||
int index;
|
||||
glm::vec3 normal;
|
||||
|
||||
static HandleFace XPos;
|
||||
static HandleFace XNeg;
|
||||
static HandleFace YPos;
|
||||
static HandleFace YNeg;
|
||||
static HandleFace ZPos;
|
||||
static HandleFace ZNeg;
|
||||
static std::array<HandleFace, 6> Faces;
|
||||
};
|
||||
|
||||
enum HandlesType {
|
||||
MoveHandles,
|
||||
ScaleHandles,
|
||||
RotateHandles,
|
||||
};
|
||||
|
||||
class DEF_INST_(abstract) Handles : public Instance {
|
||||
AUTOGEN_PREAMBLE
|
||||
public:
|
||||
bool nixAxes = false; // XYZ -> ZXY, used with rotation
|
||||
bool active;
|
||||
std::weak_ptr<Part> adornee;
|
||||
HandlesType handlesType;
|
||||
|
||||
// inline std::weak_ptr<Part> GetAdornee() { return adornee; }
|
||||
// inline void SetAdornee(std::weak_ptr<Part> newAdornee) { this->adornee = newAdornee; updateAdornee(); };
|
||||
|
||||
Handles();
|
||||
|
||||
// World-space handles vs local-space handles
|
||||
bool worldMode = false;
|
||||
CFrame GetCFrameOfHandle(HandleFace face);
|
||||
CFrame PartCFrameFromHandlePos(HandleFace face, Vector3 newPos);
|
||||
Vector3 HandleSize(HandleFace face);
|
||||
std::optional<HandleFace> RaycastHandle(rp3d::Ray ray);
|
||||
|
||||
static inline std::shared_ptr<Handles> New() { return std::make_shared<Handles>(); };
|
||||
};
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/color3.h"
|
||||
#include "objects/handles.h"
|
||||
#include "handles.h"
|
||||
#include "rendering/torus.h"
|
||||
#include "shader.h"
|
||||
#include "mesh.h"
|
||||
|
@ -266,7 +266,7 @@ void renderSkyBox() {
|
|||
}
|
||||
|
||||
void renderHandles() {
|
||||
if (editorToolHandles->adornee.expired() || !editorToolHandles->active) return;
|
||||
if (!editorToolHandles.active) return;
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glCullFace(GL_BACK);
|
||||
|
@ -292,8 +292,8 @@ void renderHandles() {
|
|||
handleShader->set("viewPos", camera.cameraPos);
|
||||
|
||||
for (auto face : HandleFace::Faces) {
|
||||
glm::mat4 model = editorToolHandles->GetCFrameOfHandle(face);
|
||||
model = glm::scale(model, (glm::vec3)editorToolHandles->HandleSize(face));
|
||||
glm::mat4 model = getCFrameOfHandle(face);
|
||||
model = glm::scale(model, (glm::vec3)handleSize(face));
|
||||
handleShader->set("model", model);
|
||||
handleShader->set("material", Material {
|
||||
.diffuse = glm::abs(face.normal),
|
||||
|
@ -303,7 +303,7 @@ void renderHandles() {
|
|||
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(model)));
|
||||
handleShader->set("normalMatrix", normalMatrix);
|
||||
|
||||
if (editorToolHandles->handlesType == HandlesType::MoveHandles) {
|
||||
if (editorToolHandles.handlesType == HandlesType::MoveHandles) {
|
||||
ARROW_MESH->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, ARROW_MESH->vertexCount);
|
||||
} else {
|
||||
|
@ -319,7 +319,7 @@ void renderHandles() {
|
|||
identityShader->set("aColor", glm::vec3(0.f, 1.f, 1.f));
|
||||
|
||||
for (auto face : HandleFace::Faces) {
|
||||
CFrame cframe = editorToolHandles->GetCFrameOfHandle(face);
|
||||
CFrame cframe = getCFrameOfHandle(face);
|
||||
glm::vec4 screenPos = projection * view * glm::vec4((glm::vec3)cframe.Position(), 1.0f);
|
||||
|
||||
if (screenPos.z < 0) continue;
|
||||
|
@ -458,7 +458,7 @@ void renderOutlines() {
|
|||
}
|
||||
|
||||
void renderRotationArcs() {
|
||||
if (editorToolHandles->adornee.expired() || editorToolHandles->handlesType != HandlesType::RotateHandles) return;
|
||||
if (!editorToolHandles.active || editorToolHandles.handlesType != HandlesType::RotateHandles) return;
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <qsoundeffect.h>
|
||||
#include <string>
|
||||
#include "mainglwidget.h"
|
||||
#include "handles.h"
|
||||
#include "logger.h"
|
||||
#include "mainwindow.h"
|
||||
#include "common.h"
|
||||
|
@ -171,21 +172,21 @@ inline glm::vec3 vec3fy(glm::vec4 vec) {
|
|||
|
||||
// Taken from Godot's implementation of moving handles (godot/editor/plugins/gizmos/gizmo_3d_helper.cpp)
|
||||
void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
||||
if (!isMouseDragging || !draggingHandle|| editorToolHandles->adornee.expired() || !editorToolHandles->active) return;
|
||||
if (!isMouseDragging || !draggingHandle|| !editorToolHandles.active) return;
|
||||
|
||||
QPoint position = evt->pos();
|
||||
|
||||
auto part = editorToolHandles->adornee.lock();
|
||||
auto part = getHandleAdornee();
|
||||
|
||||
// This was actually quite a difficult problem to solve, managing to get the handle to go underneath the cursor
|
||||
|
||||
glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height()));
|
||||
pointDir = glm::normalize(pointDir);
|
||||
|
||||
CFrame handleCFrame = editorToolHandles->GetCFrameOfHandle(draggingHandle.value());
|
||||
CFrame handleCFrame = getCFrameOfHandle(draggingHandle.value());
|
||||
|
||||
// Current frame. Identity frame if worldMode == true, selected object's frame if worldMode == false
|
||||
CFrame frame = editorToolHandles->worldMode ? CFrame::IDENTITY + part->position() : part->cframe.Rotation();
|
||||
CFrame frame = editorToolHandles.worldMode ? CFrame::IDENTITY + part->position() : part->cframe.Rotation();
|
||||
|
||||
// Segment from axis stretching -4096 to +4096 rel to handle's position
|
||||
glm::vec3 axisSegment0 = handleCFrame.Position() + (-handleCFrame.LookVector() * 4096.0f);
|
||||
|
@ -200,7 +201,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
|||
get_closest_points_between_segments(axisSegment0, axisSegment1, mouseSegment0, mouseSegment1, handlePoint, rb);
|
||||
|
||||
// Find new part position
|
||||
glm::vec3 centerPoint = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position();
|
||||
glm::vec3 centerPoint = partCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position();
|
||||
|
||||
// Apply snapping in the current frame
|
||||
glm::vec3 diff = centerPoint - (glm::vec3)part->position();
|
||||
|
@ -258,10 +259,10 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
|||
// Also implemented based on Godot: [c7ea8614](godot/editor/plugins/canvas_item_editor_plugin.cpp#L1490)
|
||||
glm::vec2 startPoint;
|
||||
void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
||||
if (!isMouseDragging || !draggingHandle || editorToolHandles->adornee.expired() || !editorToolHandles->active) return;
|
||||
if (!isMouseDragging || !draggingHandle || !editorToolHandles.active) return;
|
||||
|
||||
glm::vec2 destPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
||||
auto part = editorToolHandles->adornee.lock();
|
||||
auto part = getHandleAdornee();
|
||||
|
||||
// Calculate part pos as screen point
|
||||
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)width() / (float)height(), 0.1f, 1000.0f);
|
||||
|
@ -299,8 +300,8 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
|||
}
|
||||
|
||||
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
||||
if (editorToolHandles->adornee.expired() || !editorToolHandles->active) return std::nullopt;
|
||||
return editorToolHandles->RaycastHandle(rp3d::Ray(glmToRp(camera.cameraPos), glmToRp(glm::normalize(pointDir)) * 50000));
|
||||
if (!editorToolHandles.active) return std::nullopt;
|
||||
return ::raycastHandle(rp3d::Ray(glmToRp(camera.cameraPos), glmToRp(glm::normalize(pointDir)) * 50000));
|
||||
}
|
||||
|
||||
void MainGLWidget::handleCursorChange(QMouseEvent* evt) {
|
||||
|
@ -363,7 +364,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
|||
auto handle = raycastHandle(pointDir);
|
||||
if (handle.has_value()) {
|
||||
startPoint = glm::vec2(evt->pos().x(), evt->pos().y());
|
||||
initialFrame = editorToolHandles->adornee.lock()->cframe;
|
||||
initialFrame = getHandleAdornee()->cframe;
|
||||
isMouseDragging = true;
|
||||
draggingHandle = handle;
|
||||
return;
|
||||
|
|
|
@ -100,16 +100,6 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
});
|
||||
|
||||
connectActionHandlers();
|
||||
|
||||
// Update handles
|
||||
addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) {
|
||||
editorToolHandles->adornee = {};
|
||||
if (newSelection.size() == 0) return;
|
||||
InstanceRef inst = newSelection[0].lock();
|
||||
if (inst->GetClass() != &Part::TYPE) return;
|
||||
|
||||
editorToolHandles->adornee = std::dynamic_pointer_cast<Part>(inst);
|
||||
});
|
||||
|
||||
// Update properties
|
||||
addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) {
|
||||
|
@ -456,11 +446,11 @@ void MainWindow::updateToolbars() {
|
|||
ui->actionGridSnap05->setChecked(snappingMode == GridSnappingMode::SNAP_05_STUDS);
|
||||
ui->actionGridSnapOff->setChecked(snappingMode == GridSnappingMode::SNAP_OFF);
|
||||
|
||||
editorToolHandles->worldMode = (selectedTool == TOOL_SCALE || selectedTool == TOOL_ROTATE) ? false : worldSpaceTransforms;
|
||||
editorToolHandles->nixAxes = selectedTool == TOOL_ROTATE;
|
||||
editorToolHandles.worldMode = (selectedTool == TOOL_SCALE || selectedTool == TOOL_ROTATE) ? false : worldSpaceTransforms;
|
||||
editorToolHandles.nixAxes = selectedTool == TOOL_ROTATE;
|
||||
|
||||
editorToolHandles->active = selectedTool > TOOL_SELECT && selectedTool < TOOL_SMOOTH;
|
||||
editorToolHandles->handlesType =
|
||||
editorToolHandles.active = selectedTool > TOOL_SELECT && selectedTool < TOOL_SMOOTH;
|
||||
editorToolHandles.handlesType =
|
||||
selectedTool == TOOL_MOVE ? HandlesType::MoveHandles
|
||||
: selectedTool == TOOL_SCALE ? HandlesType::ScaleHandles
|
||||
: selectedTool == TOOL_ROTATE ? HandlesType::RotateHandles
|
||||
|
|
Loading…
Add table
Reference in a new issue