From 4bfca68fb06148809391ffe3a1fce2cad6256c42 Mon Sep 17 00:00:00 2001 From: maelstrom Date: Sun, 23 Feb 2025 16:22:17 +0100 Subject: [PATCH] feat: grid snapping wrt/ scaling and moving --- assets/icons/editor/snap05.png | Bin 0 -> 868 bytes assets/icons/editor/snap1.png | Bin 0 -> 836 bytes assets/icons/editor/snapoff.png | Bin 0 -> 865 bytes editor/mainglwidget.cpp | 36 ++++++++++++++++--- editor/mainglwidget.h | 4 +++ editor/mainwindow.cpp | 23 ++++++++---- editor/mainwindow.h | 10 +++++- editor/mainwindow.ui | 60 +++++++++++++++++++++++++++++++- 8 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 assets/icons/editor/snap05.png create mode 100644 assets/icons/editor/snap1.png create mode 100644 assets/icons/editor/snapoff.png diff --git a/assets/icons/editor/snap05.png b/assets/icons/editor/snap05.png new file mode 100644 index 0000000000000000000000000000000000000000..17e732b2d2e5a7ae48f8648ff6d8a1e396b2b414 GIT binary patch literal 868 zcmV-q1DpJbP)EX>4Tx04R}tkv&MmKpe$iQ>7wR2MdZgWN4l2qD35Q6^c+H)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jf7n~Gbq{ROvg%+_M9QWhhy~o`17$P#Wj5$e4q3ig%hmWs!QJ&TP+@E7W&07rciNtYcm^SeS@yw=e zaNZ{lv68G3pA%1-bV1@rt}7nDaW1+n@XU~zNzW69h{a+DD;>;ArbZki4y&3@`9juX zmGc&7tx{vHd-4|s^ZLp%*J+L;iA5|yf(QjQlu?0=D6Kjv7Sgnz=;9x9{Svtpa+Sfz zv4AQx$gUs!4}N!R6{e@Wq+kN*eQ}(R2oTr>nsvwdK6aeu3E+PQuJpFQ+5l!hNw2rH z@DVVy4P0EeHDwRD+yO?P4B3=j$xl-#6oB_L`lcK(d<*oidA+svaryvcsH@ZsaBv7r z6e)YX%e(tJd;9lHtG^%L>~hk4TmRhv000JJOGiWiy8t@?I|pH5F8}}l32;bRa{vG? zBLDy{BLR4&KXw2B00(qQO+^Rk0v8VkBen2NWdHyG8FWQhbVF}#ZDnqB07G(RVRU6= zAa`kWXdp*PO;A^X4i^9b0X<1XK~z}7)mA$W!Y~ZAqe@Ij+$S?9U~0Jl8)xYuXeUm< z-1}5w>SU%!RY@G%4HP_8D){~EGyoeHN`Cb4p0tgR!+=EyJiMppFsPO7y%&+doN5dB zoJ7tI)Tq7*Kri0Ek>=b$4ftI-GxI(v5|{%Is-@suzZeO}H3+IL;34}k2f>jO0Eq~2 zmLGOkvXWSq)2iaYHN?yW#;HxZl6q$%a0YnGYp!yxmIKcnWYt;+tjePfJ literal 0 HcmV?d00001 diff --git a/assets/icons/editor/snap1.png b/assets/icons/editor/snap1.png new file mode 100644 index 0000000000000000000000000000000000000000..d96eba18b1e35c1f22eafad05171b779aa939f85 GIT binary patch literal 836 zcmV-K1H1f*P)EX>4Tx04R}tkv&MmKpe$iQ>7wR2MdZgWN4l2qD35Q6^c+H)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jf7n~Gbq{ROvg%+_M9QWhhy~o`17$P#Wj5$e4q3ig%hmWs!QJ&TP+@E7W&07rciNtYcm^SeS@yw=e zaNZ{lv68G3pA%1-bV1@rt}7nDaW1+n@XU~zNzW69h{a+DD;>;ArbZki4y&3@`9juX zmGc&7tx{vHd-4|s^ZLp%*J+L;iA5|yf(QjQlu?0=D6Kjv7Sgnz=;9x9{Svtpa+Sfz zv4AQx$gUs!4}N!R6{e@Wq+kN*eQ}(R2oTr>nsvwdK6aeu3E+PQuJpFQ+5l!hNw2rH z@DVVy4P0EeHDwRD+yO?P4B3=j$xl-#6oB_L`lcK(d<*oidA+svaryvcsH@ZsaBv7r z6e)YX%e(tJd;9lHtG^%L>~hk4TmRhv000JJOGiWiy8t@?I|pH5F8}}l32;bRa{vG? zBLDy{BLR4&KXw2B00(qQO+^Rk0v8Vk6?#h8`~Uy|8FWQhbVF}#ZDnqB07G(RVRU6= zAa`kWXdp*PO;A^X4i^9b0Ub$1K~z}7)mAYMgD?!Vqe@Jb_>bqlpcC{1-TD?DQJDGy zX8wcJiOHQJA)twEQia}z1iACwB?KTdQ1Vhw#M*@~!+=Qy^hEp~2K8iTB9RD;O>H5c zi-@^_nyH@x(mT;gbjS_VkiV3jnXmIr1jfky)Kc=$dRrn~XaPU9h1_TFd*B_p0ZFr1W3YU{SDBuRL%FBY-L1uDboEjXb29rAwE6iSO6+7s_-WVkR zkyDBi`e#clPP=bY+HZCsRRKl2z}f@pJ;Fg|ILrB+Du1y9+88v~xMN0;G6u~}Y6V`P zZCg(L&2b|D06@fD2Ts>@P}ep7uLDa|RRy6;nVEkoAd3Um9!T;(gaaQ{ma*7mCaymK O0000EX>4Tx04R}tkv&MmKpe$iQ>7wR2MdZgWN4l2qD35Q6^c+H)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jf7n~Gbq{ROvg%+_M9QWhhy~o`17$P#Wj5$e4q3ig%hmWs!QJ&TP+@E7W&07rciNtYcm^SeS@yw=e zaNZ{lv68G3pA%1-bV1@rt}7nDaW1+n@XU~zNzW69h{a+DD;>;ArbZki4y&3@`9juX zmGc&7tx{vHd-4|s^ZLp%*J+L;iA5|yf(QjQlu?0=D6Kjv7Sgnz=;9x9{Svtpa+Sfz zv4AQx$gUs!4}N!R6{e@Wq+kN*eQ}(R2oTr>nsvwdK6aeu3E+PQuJpFQ+5l!hNw2rH z@DVVy4P0EeHDwRD+yO?P4B3=j$xl-#6oB_L`lcK(d<*oidA+svaryvcsH@ZsaBv7r z6e)YX%e(tJd;9lHtG^%L>~hk4TmRhv000JJOGiWiy8t@?I|pH5F8}}l32;bRa{vG? zBLDy{BLR4&KXw2B00(qQO+^Rk0v8Vk1Q7c84gdfE8FWQhbVF}#ZDnqB07G(RVRU6= zAa`kWXdp*PO;A^X4i^9b0Xj)UK~z}7)mA$WgD?!Wqe@JbdY{ZaK_}=1y7ep^qA>LY zow*NEr%vt^35CR;Kn3+2?DumBz{Z7|Loe^i+W0&SxC(%m_w*hHGqa=jDiA2KE*U-r zA*q2D)(^$doA)!&oEm5u{xoK0+9wABrQw0Kb~x8B1;TL+0_&3D!TXSdpvVP)Q~*+v zpQ^jKj`5ioL|o3oX#BLufjCun8b7T`EoYTA2cl#+3*ZVc^D75}8!laBhtf-C3ZjI0 zQUNJ2ODh#@HUpduL3S>fn*WF^VG?^TZHEbfWJ4}+5{TSY?v^mOf+(_JngdPR!x`;6 z!okMnG(VAFte;5%ZXK6CQN<{Qi0GlXKngfmhyHMZ@EuZB_dq=A$00000NkvXXu0mjf``>G| literal 0 HcmV?d00001 diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index 0168bd5..bbe2898 100644 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -2,10 +2,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -14,6 +16,7 @@ #include "datatypes/cframe.h" #include "editorcommon.h" +#include "mainwindow.h" #include "objects/handles.h" #include "physics/util.h" #include "qcursor.h" @@ -166,16 +169,28 @@ void MainGLWidget::handleHandleDrag(QMouseEvent* evt) { glm::vec3 handlePoint, rb; get_closest_points_between_segments(axisSegment0, axisSegment1, mouseSegment0, mouseSegment1, handlePoint, rb); - if (selectedTool == SelectedTool::MOVE) - editorToolHandles->adornee->lock()->cframe = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint); - else if (selectedTool == SelectedTool::SCALE) { + if (selectedTool == SelectedTool::MOVE) { + glm::vec3 newPos = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position(); + glm::vec3 oldPos = editorToolHandles->adornee->lock()->cframe.Position(); + glm::vec3 diff = newPos - oldPos; + + // Apply snapping + if (snappingFactor()) diff = glm::floor(diff / snappingFactor()) * snappingFactor(); + newPos = diff + oldPos; + + editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe.Rotation() + newPos; + } else if (selectedTool == SelectedTool::SCALE) { glm::vec3 handlePos = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position(); // Find change in handles, and negate difference in sign between axes glm::vec3 diff = handlePos - glm::vec3(editorToolHandles->adornee->lock()->position()); + + // Apply snapping + if (snappingFactor()) diff = glm::floor(diff / snappingFactor()) * snappingFactor(); + editorToolHandles->adornee->lock()->size += diff * glm::sign(draggingHandle->normal); - // If alt is not pressed, also reposition the part such that only the dragged side gets lengthened + // If ctrl is not pressed, also reposition the part such that only the dragged side gets lengthened if (!(evt->modifiers() & Qt::ControlModifier)) editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + (diff / 2.0f); } @@ -297,4 +312,17 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) { void MainGLWidget::keyReleaseEvent(QKeyEvent* evt) { if (evt->key() == Qt::Key_W || evt->key() == Qt::Key_S) moveZ = 0; else if (evt->key() == Qt::Key_A || evt->key() == Qt::Key_D) moveX = 0; +} + +MainWindow* MainGLWidget::mainWindow() { + return dynamic_cast(window()); +} + +float MainGLWidget::snappingFactor() { + switch (mainWindow()->snappingMode) { + case GridSnappingMode::SNAP_1_STUD: return 1; + case GridSnappingMode::SNAP_05_STUDS: return 0.5; + case GridSnappingMode::SNAP_OFF: return 0; + } + return 0; } \ No newline at end of file diff --git a/editor/mainglwidget.h b/editor/mainglwidget.h index c596f65..8998f6e 100644 --- a/editor/mainglwidget.h +++ b/editor/mainglwidget.h @@ -1,6 +1,7 @@ #ifndef MAINGLWIDGET_H #define MAINGLWIDGET_H +#include "mainwindow.h" #include "objects/part.h" #include "qevent.h" #include @@ -31,6 +32,9 @@ protected: void mouseReleaseEvent(QMouseEvent* evt) override; void keyPressEvent(QKeyEvent* evt) override; void keyReleaseEvent(QKeyEvent* evt) override; + + MainWindow* mainWindow(); + float snappingFactor(); }; #endif // MAINGLWIDGET_H diff --git a/editor/mainwindow.cpp b/editor/mainwindow.cpp index a53befe..8a53f19 100644 --- a/editor/mainwindow.cpp +++ b/editor/mainwindow.cpp @@ -40,11 +40,18 @@ MainWindow::MainWindow(QWidget *parent) ConnectSelectionChangeHandler(); - connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = SelectedTool::SELECT; updateSelectedTool(); }); - connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::MOVE : SelectedTool::SELECT; updateSelectedTool(); }); - connect(ui->actionToolScale, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::SCALE : SelectedTool::SELECT; updateSelectedTool(); }); - connect(ui->actionToolRotate, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::ROTATE : SelectedTool::SELECT; updateSelectedTool(); }); + connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = SelectedTool::SELECT; updateToolbars(); }); + connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::MOVE : SelectedTool::SELECT; updateToolbars(); }); + connect(ui->actionToolScale, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::SCALE : SelectedTool::SELECT; updateToolbars(); }); + connect(ui->actionToolRotate, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::ROTATE : SelectedTool::SELECT; updateToolbars(); }); ui->actionToolSelect->setChecked(true); + selectedTool = SelectedTool::SELECT; + + connect(ui->actionGridSnap1, &QAction::triggered, this, [&]() { snappingMode = GridSnappingMode::SNAP_1_STUD; updateToolbars(); }); + connect(ui->actionGridSnap05, &QAction::triggered, this, [&]() { snappingMode = GridSnappingMode::SNAP_05_STUDS; updateToolbars(); }); + connect(ui->actionGridSnapOff, &QAction::triggered, this, [&]() { snappingMode = GridSnappingMode::SNAP_OFF; updateToolbars(); }); + ui->actionGridSnap1->setChecked(true); + snappingMode = GridSnappingMode::SNAP_1_STUD; connect(ui->actionToggleSimulation, &QAction::triggered, this, [&]() { simulationPlaying = !simulationPlaying; @@ -80,7 +87,7 @@ MainWindow::MainWindow(QWidget *parent) }); addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { - updateSelectedTool(); + updateToolbars(); }); // ui->explorerView->Init(ui); @@ -134,12 +141,16 @@ void MainWindow::timerEvent(QTimerEvent* evt) { ui->mainWidget->updateCycle(); } -void MainWindow::updateSelectedTool() { +void MainWindow::updateToolbars() { ui->actionToolSelect->setChecked(selectedTool == SelectedTool::SELECT); ui->actionToolMove->setChecked(selectedTool == SelectedTool::MOVE); ui->actionToolScale->setChecked(selectedTool == SelectedTool::SCALE); ui->actionToolRotate->setChecked(selectedTool == SelectedTool::ROTATE); + ui->actionGridSnap1->setChecked(snappingMode == GridSnappingMode::SNAP_1_STUD); + ui->actionGridSnap05->setChecked(snappingMode == GridSnappingMode::SNAP_05_STUDS); + ui->actionGridSnapOff->setChecked(snappingMode == GridSnappingMode::SNAP_OFF); + if (selectedTool == SelectedTool::MOVE) editorToolHandles->worldMode = true; if (selectedTool == SelectedTool::SCALE) editorToolHandles->worldMode = false; diff --git a/editor/mainwindow.h b/editor/mainwindow.h index 4879fe0..4dd279e 100644 --- a/editor/mainwindow.h +++ b/editor/mainwindow.h @@ -8,6 +8,12 @@ #include #include +enum GridSnappingMode { + SNAP_1_STUD, + SNAP_05_STUDS, + SNAP_OFF, +}; + QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; @@ -22,11 +28,13 @@ public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); + GridSnappingMode snappingMode; + Ui::MainWindow *ui; private: QBasicTimer timer; - void updateSelectedTool(); + void updateToolbars(); void timerEvent(QTimerEvent*) override; void ConnectSelectionChangeHandler(); }; diff --git a/editor/mainwindow.ui b/editor/mainwindow.ui index f4bf562..af91020 100644 --- a/editor/mainwindow.ui +++ b/editor/mainwindow.ui @@ -41,7 +41,7 @@ 0 0 1027 - 29 + 22 @@ -115,6 +115,10 @@ + + + + @@ -257,6 +261,60 @@ F5 + + + true + + + + assets/icons/editor/snap1.pngassets/icons/editor/snap1.png + + + 1-Stud Snapping + + + Set grid snapping to 1 stud + + + QAction::MenuRole::NoRole + + + + + true + + + + assets/icons/editor/snap05.pngassets/icons/editor/snap05.png + + + 1/2-Stud Snapping + + + Set grid snapping to 1/2 studs + + + QAction::MenuRole::NoRole + + + + + true + + + + assets/icons/editor/snapoff.pngassets/icons/editor/snapoff.png + + + No Grid Snapping + + + Turn grid snapping off + + + QAction::MenuRole::NoRole + +