feat: grid snapping wrt/ scaling and moving

This commit is contained in:
maelstrom 2025-02-23 16:22:17 +01:00
parent 32964df4c3
commit 4bfca68fb0
8 changed files with 121 additions and 12 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

View file

@ -2,10 +2,12 @@
#include <chrono>
#include <QMouseEvent>
#include <glm/common.hpp>
#include <glm/ext/matrix_projection.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/ext/vector_float3.hpp>
#include <glm/geometric.hpp>
#include <glm/gtc/round.hpp>
#include <glm/matrix.hpp>
#include <memory>
#include <optional>
@ -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<MainWindow*>(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;
}

View file

@ -1,6 +1,7 @@
#ifndef MAINGLWIDGET_H
#define MAINGLWIDGET_H
#include "mainwindow.h"
#include "objects/part.h"
#include "qevent.h"
#include <QOpenGLWidget>
@ -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

View file

@ -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;

View file

@ -8,6 +8,12 @@
#include <QMainWindow>
#include <QLineEdit>
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();
};

View file

@ -41,7 +41,7 @@
<x>0</x>
<y>0</y>
<width>1027</width>
<height>29</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -115,6 +115,10 @@
<addaction name="actionToolScale"/>
<addaction name="actionToolRotate"/>
<addaction name="separator"/>
<addaction name="actionGridSnap1"/>
<addaction name="actionGridSnap05"/>
<addaction name="actionGridSnapOff"/>
<addaction name="separator"/>
<addaction name="actionToggleSimulation"/>
</widget>
<action name="actionAddPart">
@ -257,6 +261,60 @@
<string>F5</string>
</property>
</action>
<action name="actionGridSnap1">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset>
<normaloff>assets/icons/editor/snap1.png</normaloff>assets/icons/editor/snap1.png</iconset>
</property>
<property name="text">
<string>1-Stud Snapping</string>
</property>
<property name="toolTip">
<string>Set grid snapping to 1 stud</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionGridSnap05">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset>
<normaloff>assets/icons/editor/snap05.png</normaloff>assets/icons/editor/snap05.png</iconset>
</property>
<property name="text">
<string>1/2-Stud Snapping</string>
</property>
<property name="toolTip">
<string>Set grid snapping to 1/2 studs</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionGridSnapOff">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset>
<normaloff>assets/icons/editor/snapoff.png</normaloff>assets/icons/editor/snapoff.png</iconset>
</property>
<property name="text">
<string>No Grid Snapping</string>
</property>
<property name="toolTip">
<string>Turn grid snapping off</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
</widget>
<customwidgets>
<customwidget>