feat(editor): added surface editing tools

This commit is contained in:
maelstrom 2025-04-07 17:37:54 +02:00
parent 980dc249e9
commit 3f44e38788
9 changed files with 145 additions and 37 deletions

View file

@ -42,8 +42,8 @@ void main()
aNormal == vec3(0, -1, 0) ? FaceBottom : aNormal == vec3(0, -1, 0) ? FaceBottom :
aNormal == vec3(1, 0, 0) ? FaceRight : aNormal == vec3(1, 0, 0) ? FaceRight :
aNormal == vec3(-1, 0, 0) ? FaceLeft : aNormal == vec3(-1, 0, 0) ? FaceLeft :
aNormal == vec3(0, 0, 1) ? FaceFront : aNormal == vec3(0, 0, -1) ? FaceFront :
aNormal == vec3(0, 0, -1) ? FaceBack : -1; aNormal == vec3(0, 0, 1) ? FaceBack : -1;
vSurfaceZ = surfaces[vFace]; vSurfaceZ = surfaces[vFace];
// if (surfaces[vFace] > SurfaceUniversal) vSurfaceZ = 0; // if (surfaces[vFace] > SurfaceUniversal) vSurfaceZ = 0;

View file

@ -8,6 +8,7 @@
#include "datatypes/cframe.h" #include "datatypes/cframe.h"
#include "datatypes/color3.h" #include "datatypes/color3.h"
#include "datatypes/vector.h" #include "datatypes/vector.h"
#include "rendering/surface.h"
#include <reactphysics3d/reactphysics3d.h> #include <reactphysics3d/reactphysics3d.h>
namespace rp = reactphysics3d; namespace rp = reactphysics3d;
@ -37,6 +38,13 @@ public:
bool anchored = false; bool anchored = false;
rp::RigidBody* rigidBody = nullptr; rp::RigidBody* rigidBody = nullptr;
SurfaceType topSurface = SurfaceType::SurfaceStuds;
SurfaceType bottomSurface = SurfaceType::SurfaceInlets;
SurfaceType leftSurface = SurfaceType::SurfaceSmooth;
SurfaceType rightSurface = SurfaceType::SurfaceSmooth;
SurfaceType frontSurface = SurfaceType::SurfaceSmooth;
SurfaceType backSurface = SurfaceType::SurfaceSmooth;
Part(); Part();
Part(PartConstructParams params); Part(PartConstructParams params);

View file

@ -109,14 +109,6 @@ void renderParts() {
// }); // });
studsTexture->activate(0); studsTexture->activate(0);
shader->set("studs", 0); shader->set("studs", 0);
// shader->set("surfaces[1]", SurfaceStuds);
shader->set("surfaces[1]", SurfaceStuds);
shader->set("surfaces[4]", SurfaceInlets);
shader->set("surfaces[0]", SurfaceStuds);
shader->set("surfaces[2]", SurfaceStuds);
shader->set("surfaces[3]", SurfaceStuds);
shader->set("surfaces[5]", SurfaceStuds);
// Pre-calculate the normal matrix for the shader // Pre-calculate the normal matrix for the shader
@ -147,6 +139,13 @@ void renderParts() {
shader->set("texScale", part->size); shader->set("texScale", part->size);
shader->set("transparency", part->transparency); shader->set("transparency", part->transparency);
shader->set("surfaces[" + std::to_string(NormalId::Right) + "]", part->rightSurface);
shader->set("surfaces[" + std::to_string(NormalId::Top) + "]", part->topSurface);
shader->set("surfaces[" + std::to_string(NormalId::Back) + "]", part->backSurface);
shader->set("surfaces[" + std::to_string(NormalId::Left) + "]", part->leftSurface);
shader->set("surfaces[" + std::to_string(NormalId::Bottom) + "]", part->bottomSurface);
shader->set("surfaces[" + std::to_string(NormalId::Front) + "]", part->frontSurface);
CUBE_MESH->bind(); CUBE_MESH->bind();
glDrawArrays(GL_TRIANGLES, 0, CUBE_MESH->vertexCount); glDrawArrays(GL_TRIANGLES, 0, CUBE_MESH->vertexCount);
} }
@ -170,6 +169,13 @@ void renderParts() {
shader->set("texScale", part->size); shader->set("texScale", part->size);
shader->set("transparency", part->transparency); shader->set("transparency", part->transparency);
shader->set("surfaces[" + std::to_string(NormalId::Right) + "]", part->rightSurface);
shader->set("surfaces[" + std::to_string(NormalId::Top) + "]", part->topSurface);
shader->set("surfaces[" + std::to_string(NormalId::Back) + "]", part->backSurface);
shader->set("surfaces[" + std::to_string(NormalId::Left) + "]", part->leftSurface);
shader->set("surfaces[" + std::to_string(NormalId::Bottom) + "]", part->bottomSurface);
shader->set("surfaces[" + std::to_string(NormalId::Front) + "]", part->frontSurface);
CUBE_MESH->bind(); CUBE_MESH->bind();
glDrawArrays(GL_TRIANGLES, 0, CUBE_MESH->vertexCount); glDrawArrays(GL_TRIANGLES, 0, CUBE_MESH->vertexCount);
} }

View file

@ -0,0 +1,23 @@
#include "surface.h"
#include "datatypes/vector.h"
static std::array<Data::Vector3, 6> FACE_NORMALS = {{
{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 },
{ -1, 0, 0 },
{ 0, -1, 0 },
{ 0, 0, -1 },
}};
NormalId faceFromNormal(Data::Vector3 normal) {
for (int face = 0; face < 6; face++) {
if (normal.Dot(FACE_NORMALS[face]) > 0.99)
return (NormalId)face;
}
return (NormalId)-1;
}
Data::Vector3 normalFromFace(NormalId face) {
return FACE_NORMALS[face];
}

View file

@ -1,5 +1,14 @@
#pragma once #pragma once
enum NormalId {
Right = 0,
Top = 1,
Back = 2,
Left = 3,
Bottom = 4,
Front = 5
};
enum SurfaceType { enum SurfaceType {
SurfaceSmooth = 0, SurfaceSmooth = 0,
SurfaceGlue = 1, SurfaceGlue = 1,
@ -7,4 +16,8 @@ enum SurfaceType {
SurfaceStuds = 3, SurfaceStuds = 3,
SurfaceInlets = 4, SurfaceInlets = 4,
SurfaceUniversal = 5, SurfaceUniversal = 5,
}; };
namespace Data { class Vector3; }
NormalId faceFromNormal(Data::Vector3);
Data::Vector3 normalFromFace(NormalId);

View file

@ -37,6 +37,7 @@
#include "mainglwidget.h" #include "mainglwidget.h"
#include "math_helper.h" #include "math_helper.h"
#include "rendering/surface.h"
static Data::CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1)); static Data::CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
@ -130,7 +131,7 @@ bool isMouseDragging = false;
std::optional<std::weak_ptr<Part>> draggingObject; std::optional<std::weak_ptr<Part>> draggingObject;
std::optional<HandleFace> draggingHandle; std::optional<HandleFace> draggingHandle;
void MainGLWidget::handleObjectDrag(QMouseEvent* evt) { void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
if (!isMouseDragging || !draggingObject) return; if (!isMouseDragging || !draggingObject || mainWindow()->selectedTool >= TOOL_SMOOTH) return;
QPoint position = evt->pos(); QPoint position = evt->pos();
@ -214,12 +215,12 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
// printf("Post-snap: (%f, %f, %f)\n", diff.x, diff.y, diff.z); // printf("Post-snap: (%f, %f, %f)\n", diff.x, diff.y, diff.z);
switch (mainWindow()->selectedTool) { switch (mainWindow()->selectedTool) {
case SelectedTool::MOVE: { case TOOL_MOVE: {
// Add difference // Add difference
editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + diff; editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + diff;
} break; } break;
case SelectedTool::SCALE: { case TOOL_SCALE: {
// Find local difference // Find local difference
glm::vec3 localDiff = frame.Inverse() * diff; glm::vec3 localDiff = frame.Inverse() * diff;
// Find outwarwd difference // Find outwarwd difference
@ -316,11 +317,11 @@ void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) {
handleCursorChange(evt); handleCursorChange(evt);
switch (mainWindow()->selectedTool) { switch (mainWindow()->selectedTool) {
case SelectedTool::MOVE: case TOOL_MOVE:
case SelectedTool::SCALE: case TOOL_SCALE:
handleLinearTransform(evt); handleLinearTransform(evt);
break; break;
case SelectedTool::ROTATE: case TOOL_ROTATE:
handleRotationalTransform(evt); handleRotationalTransform(evt);
break; break;
default: default:
@ -356,6 +357,24 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
std::shared_ptr<Part> part = partFromBody(rayHit->body); std::shared_ptr<Part> part = partFromBody(rayHit->body);
if (part->name == "Baseplate") return; if (part->name == "Baseplate") return;
// Handle surface tool
if (mainWindow()->selectedTool >= TOOL_SMOOTH) {
Data::Vector3 localNormal = part->cframe.Inverse().Rotation() * rayHit->worldNormal;
NormalId face = faceFromNormal(localNormal);
SurfaceType surface = SurfaceType(mainWindow()->selectedTool - TOOL_SMOOTH);
switch (face) {
case Right: part->rightSurface = surface; break;
case Top: part->topSurface = surface; break;
case Back: part->backSurface = surface; break;
case Left: part->leftSurface = surface; break;
case Bottom: part->bottomSurface = surface; break;
case Front: part->frontSurface = surface; break;
default: break;
}
return;
}
//part.selected = true; //part.selected = true;
isMouseDragging = true; isMouseDragging = true;
draggingObject = part; draggingObject = part;

View file

@ -109,12 +109,19 @@ MainWindow::MainWindow(QWidget *parent)
ui->explorerView->buildContextMenu(); ui->explorerView->buildContextMenu();
connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = SelectedTool::SELECT; updateToolbars(); }); connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = TOOL_SELECT; updateToolbars(); });
connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::MOVE : SelectedTool::SELECT; updateToolbars(); }); connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_MOVE : TOOL_SELECT; updateToolbars(); });
connect(ui->actionToolScale, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::SCALE : SelectedTool::SELECT; updateToolbars(); }); connect(ui->actionToolScale, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_SCALE : TOOL_SELECT; updateToolbars(); });
connect(ui->actionToolRotate, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::ROTATE : SelectedTool::SELECT; updateToolbars(); }); connect(ui->actionToolRotate, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_ROTATE : TOOL_SELECT; updateToolbars(); });
connect(ui->actionToolSmooth, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_SMOOTH : TOOL_SELECT; updateToolbars(); });
connect(ui->actionToolGlue, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_GLUE : TOOL_SELECT; updateToolbars(); });
connect(ui->actionToolWeld, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_WELD : TOOL_SELECT; updateToolbars(); });
connect(ui->actionToolStuds, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_STUDS : TOOL_SELECT; updateToolbars(); });
connect(ui->actionToolInlets, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_INLETS : TOOL_SELECT; updateToolbars(); });
connect(ui->actionToolUniversal, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_UNIVERSAL : TOOL_SELECT; updateToolbars(); });
ui->actionToolSelect->setChecked(true); ui->actionToolSelect->setChecked(true);
selectedTool = SelectedTool::SELECT; selectedTool = TOOL_SELECT;
connect(ui->actionGridSnap1, &QAction::triggered, this, [&]() { snappingMode = GridSnappingMode::SNAP_1_STUD; updateToolbars(); }); 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->actionGridSnap05, &QAction::triggered, this, [&]() { snappingMode = GridSnappingMode::SNAP_05_STUDS; updateToolbars(); });
@ -421,23 +428,30 @@ void MainWindow::timerEvent(QTimerEvent* evt) {
} }
void MainWindow::updateToolbars() { void MainWindow::updateToolbars() {
ui->actionToolSelect->setChecked(selectedTool == SelectedTool::SELECT); ui->actionToolSelect->setChecked(selectedTool == TOOL_SELECT);
ui->actionToolMove->setChecked(selectedTool == SelectedTool::MOVE); ui->actionToolMove->setChecked(selectedTool == TOOL_MOVE);
ui->actionToolScale->setChecked(selectedTool == SelectedTool::SCALE); ui->actionToolScale->setChecked(selectedTool == TOOL_SCALE);
ui->actionToolRotate->setChecked(selectedTool == SelectedTool::ROTATE); ui->actionToolRotate->setChecked(selectedTool == TOOL_ROTATE);
ui->actionToolSmooth->setChecked(selectedTool == TOOL_SMOOTH);
ui->actionToolGlue->setChecked(selectedTool == TOOL_GLUE);
ui->actionToolWeld->setChecked(selectedTool == TOOL_WELD);
ui->actionToolStuds->setChecked(selectedTool == TOOL_STUDS);
ui->actionToolInlets->setChecked(selectedTool == TOOL_INLETS);
ui->actionToolUniversal->setChecked(selectedTool == TOOL_UNIVERSAL);
ui->actionGridSnap1->setChecked(snappingMode == GridSnappingMode::SNAP_1_STUD); ui->actionGridSnap1->setChecked(snappingMode == GridSnappingMode::SNAP_1_STUD);
ui->actionGridSnap05->setChecked(snappingMode == GridSnappingMode::SNAP_05_STUDS); ui->actionGridSnap05->setChecked(snappingMode == GridSnappingMode::SNAP_05_STUDS);
ui->actionGridSnapOff->setChecked(snappingMode == GridSnappingMode::SNAP_OFF); ui->actionGridSnapOff->setChecked(snappingMode == GridSnappingMode::SNAP_OFF);
editorToolHandles->worldMode = (selectedTool == SelectedTool::SCALE || selectedTool == SelectedTool::ROTATE) ? false : worldSpaceTransforms; editorToolHandles->worldMode = (selectedTool == TOOL_SCALE || selectedTool == TOOL_ROTATE) ? false : worldSpaceTransforms;
editorToolHandles->nixAxes = selectedTool == SelectedTool::ROTATE; editorToolHandles->nixAxes = selectedTool == TOOL_ROTATE;
editorToolHandles->active = selectedTool != SelectedTool::SELECT; editorToolHandles->active = selectedTool > TOOL_SELECT && selectedTool < TOOL_SMOOTH;
editorToolHandles->handlesType = editorToolHandles->handlesType =
selectedTool == SelectedTool::MOVE ? HandlesType::MoveHandles selectedTool == TOOL_MOVE ? HandlesType::MoveHandles
: selectedTool == SelectedTool::SCALE ? HandlesType::ScaleHandles : selectedTool == TOOL_SCALE ? HandlesType::ScaleHandles
: selectedTool == SelectedTool::ROTATE ? HandlesType::RotateHandles : selectedTool == TOOL_ROTATE ? HandlesType::RotateHandles
: HandlesType::ScaleHandles; : HandlesType::ScaleHandles;
} }

View file

@ -11,10 +11,17 @@
#include <qfiledialog.h> #include <qfiledialog.h>
enum SelectedTool { enum SelectedTool {
SELECT, TOOL_SELECT,
MOVE, TOOL_MOVE,
SCALE, TOOL_SCALE,
ROTATE, TOOL_ROTATE,
TOOL_SMOOTH,
TOOL_GLUE,
TOOL_WELD,
TOOL_STUDS,
TOOL_INLETS,
TOOL_UNIVERSAL,
}; };
enum GridSnappingMode { enum GridSnappingMode {

View file

@ -40,7 +40,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1612</width> <width>1027</width>
<height>30</height> <height>30</height>
</rect> </rect>
</property> </property>
@ -545,6 +545,9 @@
</property> </property>
</action> </action>
<action name="actionToolStuds"> <action name="actionToolStuds">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon"> <property name="icon">
<iconset theme="surface-studs"/> <iconset theme="surface-studs"/>
</property> </property>
@ -559,6 +562,9 @@
</property> </property>
</action> </action>
<action name="actionToolInlets"> <action name="actionToolInlets">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon"> <property name="icon">
<iconset theme="surface-inlets"/> <iconset theme="surface-inlets"/>
</property> </property>
@ -573,6 +579,9 @@
</property> </property>
</action> </action>
<action name="actionToolUniversal"> <action name="actionToolUniversal">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon"> <property name="icon">
<iconset theme="surface-universal"/> <iconset theme="surface-universal"/>
</property> </property>
@ -587,6 +596,9 @@
</property> </property>
</action> </action>
<action name="actionToolSmooth"> <action name="actionToolSmooth">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon"> <property name="icon">
<iconset theme="surface-smooth"/> <iconset theme="surface-smooth"/>
</property> </property>
@ -601,6 +613,9 @@
</property> </property>
</action> </action>
<action name="actionToolWeld"> <action name="actionToolWeld">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon"> <property name="icon">
<iconset theme="surface-weld"/> <iconset theme="surface-weld"/>
</property> </property>
@ -615,6 +630,9 @@
</property> </property>
</action> </action>
<action name="actionToolGlue"> <action name="actionToolGlue">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon"> <property name="icon">
<iconset theme="surface-glue"/> <iconset theme="surface-glue"/>
</property> </property>