diff --git a/core/src/datatypes/cframe.cpp b/core/src/datatypes/cframe.cpp index d65a6df..ddc5cfd 100644 --- a/core/src/datatypes/cframe.cpp +++ b/core/src/datatypes/cframe.cpp @@ -3,6 +3,7 @@ #include "physics/util.h" #include #include +#include #include #include #define GLM_ENABLE_EXPERIMENTAL @@ -83,7 +84,7 @@ Data::Vector3 Data::CFrame::ToEulerAnglesXYZ() { Data::CFrame Data::CFrame::FromEulerAnglesXYZ(Data::Vector3 vector) { glm::mat3 mat = glm::eulerAngleXYZ(vector.X(), vector.Y(), vector.Z()); - return Data::CFrame(Data::Vector3::ZERO, glm::column(mat, 2), (Data::Vector3)glm::column(mat, 1)); // Getting LookAt (3rd) and Up (2nd) vectors + return Data::CFrame((glm::vec3)Data::Vector3::ZERO, mat); } Data::CFrame Data::CFrame::Inverse() const { diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index ccd65de..4336cdc 100644 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -10,12 +10,14 @@ #include #include #include +#include #include #include #include #include #include "datatypes/cframe.h" +#include "datatypes/vector.h" #include "editorcommon.h" #include "logger.h" #include "mainwindow.h" @@ -33,7 +35,6 @@ #include "rendering/shader.h" #include "mainglwidget.h" -#include "../core/src/rendering/defaultmeshes.h" #include "math_helper.h" MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent) { @@ -161,11 +162,8 @@ inline glm::vec3 vec3fy(glm::vec4 vec) { return vec / vec.w; } -QPoint lastPoint; -void MainGLWidget::handleHandleDrag(QMouseEvent* evt) { - QPoint cLastPoint = lastPoint; - lastPoint = evt->pos(); - +// 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 || !editorToolHandles->active) return; QPoint position = evt->pos(); @@ -204,7 +202,6 @@ void MainGLWidget::handleHandleDrag(QMouseEvent* evt) { // printf("Post-snap: (%f, %f, %f)\n", diff.x, diff.y, diff.z); switch (mainWindow()->selectedTool) { - case SelectedTool::SELECT: break; case SelectedTool::MOVE: { // Add difference editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + diff; @@ -228,14 +225,54 @@ void MainGLWidget::handleHandleDrag(QMouseEvent* evt) { part->cframe = part->cframe + diff * 0.5f; } break; - case SelectedTool::ROTATE: { - // TODO: Implement rotation - } break; + default: + Logger::error("Invalid tool was set to be handled by handleLinearTransform\n"); } syncPartPhysics(std::dynamic_pointer_cast(editorToolHandles->adornee->lock())); } +// Also implemented based on Godot: [c7ea8614](godot/editor/plugins/canvas_item_editor_plugin.cpp#L1490) +glm::vec2 startPoint; +QPoint _lastPoint; +void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) { + QPoint lastPoint = _lastPoint; + _lastPoint = evt->pos(); + + glm::vec2 startPoint(lastPoint.x(), lastPoint.y()); + + if (!isMouseDragging || !draggingHandle || !editorToolHandles->adornee || !editorToolHandles->active) return; + + glm::vec2 destPoint = glm::vec2(evt->pos().x(), evt->pos().y()); + auto part = editorToolHandles->adornee->lock(); + + // Calculate part pos as screen point + glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)width() / (float)height(), 0.1f, 1000.0f); + glm::mat4 view = camera.getLookAt(); + glm::vec4 screenPos = projection * view * glm::vec4((glm::vec3)part->cframe.Position(), 1.f); + screenPos /= screenPos.w * 2; + screenPos += 0.5f; + screenPos = glm::vec4(screenPos.x, 1-screenPos.y, 0, 0); + screenPos *= glm::vec4(width(), height(), 0, 0); + + // https://wumbo.net/formulas/angle-between-two-vectors-2d/ + glm::vec2 initVec = glm::normalize(startPoint - (glm::vec2)screenPos); + glm::vec2 destVec = glm::normalize(destPoint - (glm::vec2)screenPos); + float angle = atan2f(initVec.x * destVec.y - initVec.y * destVec.x, initVec.x * destVec.x + initVec.y * destVec.y); + + float sign = glm::sign(initVec.x) * glm::sign(destVec.x); + // printf(": %f\n", part->cframe.ToEulerAnglesXYZ().Y()); + // printVec(part->cframe.ToEulerAnglesXYZ()); + + Data::Vector3 angles = part->cframe.ToEulerAnglesXYZ(); + // Make angles positive, for convenience + // angles = glm::mod(glm::vec3(angles + glm::vec3(0, 0.01, 0) + glm::vec3(2*glm::pi())), glm::pi()); + angles = angles + glm::sign(angles.X()) * glm::vec3(0, angle, 0); + part->cframe = Data::CFrame::FromEulerAnglesXYZ(angles) + part->cframe.Position(); + + syncPartPhysics(std::dynamic_pointer_cast(editorToolHandles->adornee->lock())); +} + std::optional MainGLWidget::raycastHandle(glm::vec3 pointDir) { if (!editorToolHandles->adornee.has_value() || !editorToolHandles->active) return std::nullopt; return editorToolHandles->RaycastHandle(rp3d::Ray(glmToRp(camera.cameraPos), glmToRp(glm::normalize(pointDir)) * 50000)); @@ -263,8 +300,19 @@ void MainGLWidget::handleCursorChange(QMouseEvent* evt) { void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) { handleCameraRotate(evt); handleObjectDrag(evt); - handleHandleDrag(evt); handleCursorChange(evt); + + switch (mainWindow()->selectedTool) { + case SelectedTool::MOVE: + case SelectedTool::SCALE: + handleLinearTransform(evt); + break; + case SelectedTool::ROTATE: + handleRotationalTransform(evt); + break; + default: + break; + } } void MainGLWidget::mousePressEvent(QMouseEvent* evt) { @@ -282,6 +330,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) { // raycast handles auto handle = raycastHandle(pointDir); if (handle.has_value()) { + startPoint = glm::vec2(evt->pos().x(), evt->pos().y()); isMouseDragging = true; draggingHandle = handle; return; diff --git a/editor/mainglwidget.h b/editor/mainglwidget.h index 8998f6e..786ce31 100644 --- a/editor/mainglwidget.h +++ b/editor/mainglwidget.h @@ -23,7 +23,8 @@ protected: void handleCameraRotate(QMouseEvent* evt); void handleObjectDrag(QMouseEvent* evt); - void handleHandleDrag(QMouseEvent* evt); + void handleLinearTransform(QMouseEvent* evt); + void handleRotationalTransform(QMouseEvent* evt); void handleCursorChange(QMouseEvent* evt); std::optional raycastHandle(glm::vec3 pointDir);