diff --git a/core/src/common.cpp b/core/src/common.cpp index 8ac5714..30e330c 100644 --- a/core/src/common.cpp +++ b/core/src/common.cpp @@ -11,7 +11,7 @@ std::shared_ptr gDataModel = DataModel::New(); std::shared_ptr editModeDataModel = gDataModel; std::optional hierarchyPreUpdateHandler; std::optional hierarchyPostUpdateHandler; -std::shared_ptr editorToolHandles = Handles::New(); +Handles editorToolHandles; std::vector currentSelection; diff --git a/core/src/common.h b/core/src/common.h index 4234adb..57b7456 100644 --- a/core/src/common.h +++ b/core/src/common.h @@ -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 editModeDataModel; inline std::shared_ptr gWorkspace() { return std::dynamic_pointer_cast(gDataModel->services["Workspace"]); } extern std::optional hierarchyPreUpdateHandler; extern std::optional hierarchyPostUpdateHandler; -extern std::shared_ptr editorToolHandles; +extern Handles editorToolHandles; void setSelection(std::vector newSelection, bool fromExplorer = false); const std::vector getSelection(); diff --git a/core/src/objects/handles.cpp b/core/src/handles.cpp similarity index 59% rename from core/src/objects/handles.cpp rename to core/src/handles.cpp index c5a5151..1f77ec1 100644 --- a/core/src/objects/handles.cpp +++ b/core/src/handles.cpp @@ -3,6 +3,7 @@ #include "datatypes/cframe.h" #include "datatypes/vector.h" #include +#include #include #include #include @@ -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 getHandleAdornee() { + for (std::weak_ptr inst : getSelection()) { + if (!inst.expired() && inst.lock()->IsA()) + return inst.lock()->CastTo().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 Handles::RaycastHandle(rp3d::Ray ray) { +std::optional 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 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); } \ No newline at end of file diff --git a/core/src/handles.h b/core/src/handles.h new file mode 100644 index 0000000..7805922 --- /dev/null +++ b/core/src/handles.h @@ -0,0 +1,43 @@ +#pragma once + +#include "datatypes/cframe.h" +#include "objects/part.h" +#include +#include + +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 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 getHandleAdornee(); +CFrame getCFrameOfHandle(HandleFace face); +CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos); +Vector3 handleSize(HandleFace face); +std::optional raycastHandle(rp3d::Ray ray); \ No newline at end of file diff --git a/core/src/objects/handles.h b/core/src/objects/handles.h deleted file mode 100644 index a127b0b..0000000 --- a/core/src/objects/handles.h +++ /dev/null @@ -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 -#include -#include - -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 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 adornee; - HandlesType handlesType; - - // inline std::weak_ptr GetAdornee() { return adornee; } - // inline void SetAdornee(std::weak_ptr 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 RaycastHandle(rp3d::Ray ray); - - static inline std::shared_ptr New() { return std::make_shared(); }; -}; \ No newline at end of file diff --git a/core/src/rendering/renderer.cpp b/core/src/rendering/renderer.cpp index 032a93d..eb6ed3c 100644 --- a/core/src/rendering/renderer.cpp +++ b/core/src/rendering/renderer.cpp @@ -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); diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index f2b6ee3..2444059 100755 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -5,6 +5,7 @@ #include #include #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 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; diff --git a/editor/mainwindow.cpp b/editor/mainwindow.cpp index 09abcd5..c096063 100644 --- a/editor/mainwindow.cpp +++ b/editor/mainwindow.cpp @@ -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(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