diff --git a/core/src/datatypes/cframe.cpp b/core/src/datatypes/cframe.cpp index 791f308..9d0aeba 100644 --- a/core/src/datatypes/cframe.cpp +++ b/core/src/datatypes/cframe.cpp @@ -2,11 +2,15 @@ #include "datatypes/vector.h" #include "physics/util.h" #include +#include +#include #include #define GLM_ENABLE_EXPERIMENTAL #include // #include "meta.h" // IWYU pragma: keep +const Data::CFrame Data::CFrame::IDENTITY(glm::vec3(0, 0, 0), glm::mat3(1.f)); + Data::CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22) : translation(x, y, z) , rotation({ @@ -37,13 +41,9 @@ glm::mat3 lookAt(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3 up) Data::Vector3 f = (lookAt - position).Unit(); // Forward/Look Data::Vector3 u = up.Unit(); // Up Data::Vector3 s = f.Cross(u).Unit(); // Right - u = s.Cross(u); + u = s.Cross(f); - return { - { s.X(), u.X(), -f.X() }, - { s.Y(), u.Y(), -f.Y() }, - { s.Z(), u.Z(), -f.Z() }, - }; + return { s, u, f }; } Data::CFrame::CFrame(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3 up) @@ -85,8 +85,21 @@ Data::CFrame Data::CFrame::FromEulerAnglesXYZ(Data::Vector3 vector) { return Data::CFrame(Data::Vector3::ZERO, glm::column(mat, 2), (Data::Vector3)glm::column(mat, 1)); // Getting LookAt (3rd) and Up (2nd) vectors } +Data::CFrame Data::CFrame::Inverse() const { + return CFrame { -translation * glm::transpose(glm::inverse(rotation)), glm::inverse(rotation) }; +} + + // Operators +Data::CFrame Data::CFrame::operator *(Data::CFrame otherFrame) const { + return CFrame { this->translation + this->rotation * otherFrame.translation, this->rotation * otherFrame.rotation }; +} + +Data::Vector3 Data::CFrame::operator *(Data::Vector3 vector) const { + return this->translation + this->rotation * vector; +} + Data::CFrame Data::CFrame::operator +(Data::Vector3 vector) const { return CFrame { this->translation + glm::vec3(vector), this->rotation }; } diff --git a/core/src/datatypes/cframe.h b/core/src/datatypes/cframe.h index fbbe37e..a768073 100644 --- a/core/src/datatypes/cframe.h +++ b/core/src/datatypes/cframe.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -28,6 +30,8 @@ namespace Data { CFrame(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3 up = Data::Vector3(0, 1, 0)); ~CFrame(); + static const CFrame IDENTITY; + virtual const TypeInfo& GetType() const override; static const TypeInfo TYPE; @@ -41,18 +45,21 @@ namespace Data { //inline static CFrame identity() { } inline Vector3 Position() const { return translation; } inline CFrame Rotation() const { return CFrame { glm::vec3(0, 0, 0), rotation }; } + CFrame Inverse() const; inline float X() const { return translation.x; } inline float Y() const { return translation.y; } inline float Z() const { return translation.z; } inline Vector3 RightVector() { return glm::column(rotation, 0); } inline Vector3 UpVector() { return glm::column(rotation, 1); } - inline Vector3 LookVector() { return glm::column(rotation, 2); } + inline Vector3 LookVector() { return -glm::column(rotation, 2); } Vector3 ToEulerAnglesXYZ(); static CFrame FromEulerAnglesXYZ(Data::Vector3); // Operators + Data::CFrame operator *(Data::CFrame) const; + Data::Vector3 operator *(Data::Vector3) const; Data::CFrame operator +(Data::Vector3) const; Data::CFrame operator -(Data::Vector3) const; }; diff --git a/core/src/datatypes/vector.cpp b/core/src/datatypes/vector.cpp index c5501e7..f854695 100644 --- a/core/src/datatypes/vector.cpp +++ b/core/src/datatypes/vector.cpp @@ -25,6 +25,14 @@ Data::Vector3::operator glm::vec3() const { return vector; }; Data::Vector3::operator rp::Vector3() const { return rp::Vector3(X(), Y(), Z()); }; // Operators +Data::Vector3 Data::Vector3::operator *(float scale) const { + return Data::Vector3(this->X() * scale, this->Y() * scale, this->Z() * scale); +} + +Data::Vector3 Data::Vector3::operator /(float scale) const { + return Data::Vector3(this->X() / scale, this->Y() / scale, this->Z() / scale); +} + Data::Vector3 Data::Vector3::operator +(Data::Vector3 other) const { return Data::Vector3(this->X() + other.X(), this->Y() + other.Y(), this->Z() + other.Z()); } diff --git a/core/src/datatypes/vector.h b/core/src/datatypes/vector.h index 178eb08..10f1017 100644 --- a/core/src/datatypes/vector.h +++ b/core/src/datatypes/vector.h @@ -40,6 +40,8 @@ namespace Data { float Dot(Data::Vector3) const; // Operators + Data::Vector3 operator *(float) const; + Data::Vector3 operator /(float) const; Data::Vector3 operator +(Data::Vector3) const; Data::Vector3 operator -(Data::Vector3) const; Data::Vector3 operator -() const; diff --git a/core/src/objects/handles.cpp b/core/src/objects/handles.cpp index beeed8e..0b0470a 100644 --- a/core/src/objects/handles.cpp +++ b/core/src/objects/handles.cpp @@ -1,4 +1,5 @@ #include "handles.h" +#include "common.h" #include "datatypes/cframe.h" #include "datatypes/vector.h" #include @@ -36,19 +37,30 @@ Handles::Handles(): Instance(&TYPE) { Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) { if (!adornee.has_value() || adornee->expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0)); - // return adornee->lock()->cframe + face.normal * 5.f; - if (worldMode) - return adornee->lock()->cframe + (adornee->lock()->size * 0.5f * face.normal) + face.normal * 2.f; - return adornee->lock()->cframe + glm::vec3(glm::mat4(adornee->lock()->cframe.Rotation()) * glm::vec4((adornee->lock()->size * 0.5f * face.normal) + face.normal * 2.f, 0)); + Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee->lock()->position() : adornee->lock()->cframe; + + // We don't want this to align with local * face.normal, or else we have problems. + glm::vec3 upAxis(0, 0, 1); + if (glm::abs(glm::dot(glm::vec3(localFrame.Rotation() * face.normal), upAxis)) > 0.9999f) + upAxis = glm::vec3(0, 1, 0); + + Data::Vector3 handlePos = localFrame * ((2.f + adornee->lock()->size * 0.5f) * face.normal); + Data::CFrame cframe(handlePos, handlePos + localFrame.Rotation() * face.normal, upAxis); + + return cframe; } Data::CFrame Handles::PartCFrameFromHandlePos(HandleFace face, Data::Vector3 newPos) { if (!adornee.has_value() || adornee->expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0)); - // return adornee->lock()->cframe + face.normal * 5.f; - if (worldMode) - return adornee->lock()->cframe.Rotation() + newPos - (adornee->lock()->size * 0.5f * face.normal) - face.normal * 2.f; - return adornee->lock()->cframe.Rotation() + newPos - glm::vec3(glm::mat4(adornee->lock()->cframe.Rotation()) * glm::vec4((adornee->lock()->size * 0.5f * face.normal) + face.normal * 2.f, 0)); + Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee->lock()->position() : adornee->lock()->cframe; + Data::CFrame inverseFrame = localFrame.Inverse(); + + Data::Vector3 handlePos = localFrame * ((2.f + adornee->lock()->size * 0.5f) * face.normal); + + // glm::vec3 localPos = inverseFrame * newPos; + glm::vec3 newPartPos = newPos - localFrame.Rotation() * ((2.f + adornee->lock()->size * 0.5f) * face.normal); + return adornee->lock()->cframe.Rotation() + newPartPos; } std::optional Handles::RaycastHandle(rp3d::Ray ray) { diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index 40928bd..c3a5f67 100644 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -109,16 +109,21 @@ void MainGLWidget::handleHandleDrag(QMouseEvent* evt) { QPoint position = evt->pos(); + auto part = editorToolHandles->adornee->lock(); + // 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); Data::CFrame handleCFrame = editorToolHandles->GetCFrameOfHandle(draggingHandle.value()); + + // Current frame. Identity frame if worldMode == true, selected object's frame if worldMode == false + Data::CFrame frame = editorToolHandles->worldMode ? Data::CFrame::IDENTITY + part->position() : part->cframe.Rotation(); // Segment from axis stretching -4096 to +4096 rel to handle's position - glm::vec3 axisSegment0 = handleCFrame.Position() + glm::abs(draggingHandle->normal) * 4096.0f; - glm::vec3 axisSegment1 = handleCFrame.Position() + glm::abs(draggingHandle->normal) * -4096.0f; + glm::vec3 axisSegment0 = handleCFrame.Position() + (-handleCFrame.LookVector() * 4096.0f); + glm::vec3 axisSegment1 = handleCFrame.Position() + (-handleCFrame.LookVector() * -4096.0f); // Segment from camera stretching 4096 forward glm::vec3 mouseSegment0 = camera.cameraPos; @@ -128,31 +133,16 @@ void MainGLWidget::handleHandleDrag(QMouseEvent* evt) { glm::vec3 handlePoint, rb; get_closest_points_between_segments(axisSegment0, axisSegment1, mouseSegment0, mouseSegment1, handlePoint, rb); - if (mainWindow()->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; + // Find new part position + glm::vec3 centerPoint = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position(); - editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe.Rotation() + newPos; - } else if (mainWindow()->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(); + // Apply snapping in the current frame + glm::vec3 diff = centerPoint - (glm::vec3)editorToolHandles->adornee->lock()->position(); + // auto odiff = diff; + if (snappingFactor()) diff = frame * (glm::round(glm::vec3(frame.Inverse() * diff) * snappingFactor()) / snappingFactor()); - editorToolHandles->adornee->lock()->size += diff * glm::sign(draggingHandle->normal); - - // 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); - } + // Add difference + editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + diff; syncPartPhysics(std::dynamic_pointer_cast(editorToolHandles->adornee->lock())); } diff --git a/editor/mainwindow.cpp b/editor/mainwindow.cpp index 522bb3a..58f04a1 100644 --- a/editor/mainwindow.cpp +++ b/editor/mainwindow.cpp @@ -84,6 +84,7 @@ MainWindow::MainWindow(QWidget *parent) ConnectSelectionChangeHandler(); }); + // Update handles addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { editorToolHandles->adornee = std::nullopt; if (newSelection.size() == 0) return; @@ -93,6 +94,14 @@ MainWindow::MainWindow(QWidget *parent) editorToolHandles->adornee = std::dynamic_pointer_cast(inst); }); + // Update properties + addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) { + if (newSelection.size() == 0) return; + if (newSelection.size() > 1) + ui->propertiesView->setSelected(std::nullopt); + ui->propertiesView->setSelected(newSelection[0].lock()); + }); + // ui->explorerView->Init(ui); simulationInit(); @@ -110,7 +119,7 @@ MainWindow::MainWindow(QWidget *parent) workspace()->AddChild(ui->mainWidget->lastPart = Part::New({ .position = glm::vec3(0), - .rotation = glm::vec3(0), + .rotation = glm::vec3(0.5, 2, 1), .size = glm::vec3(4, 1.2, 2), .color = glm::vec3(0.639216f, 0.635294f, 0.647059f), })); @@ -118,14 +127,14 @@ MainWindow::MainWindow(QWidget *parent) } void MainWindow::ConnectSelectionChangeHandler() { - connect(ui->explorerView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) { - if (selected.count() == 0) return; + // connect(ui->explorerView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) { + // if (selected.count() == 0) return; - std::optional inst = selected.count() == 0 ? std::nullopt - : std::make_optional(((Instance*)selected.indexes()[0].internalPointer())->shared_from_this()); + // std::optional inst = selected.count() == 0 ? std::nullopt + // : std::make_optional(((Instance*)selected.indexes()[0].internalPointer())->shared_from_this()); - ui->propertiesView->setSelected(inst); - }); + // ui->propertiesView->setSelected(inst); + // }); } static std::chrono::time_point lastTime = std::chrono::steady_clock::now(); @@ -154,6 +163,7 @@ void MainWindow::updateToolbars() { ui->actionGridSnap05->setChecked(snappingMode == GridSnappingMode::SNAP_05_STUDS); ui->actionGridSnapOff->setChecked(snappingMode == GridSnappingMode::SNAP_OFF); + // editorToolHandles->worldMode = false; if (selectedTool == SelectedTool::MOVE) editorToolHandles->worldMode = true; if (selectedTool == SelectedTool::SCALE) editorToolHandles->worldMode = false; editorToolHandles->active = selectedTool != SelectedTool::SELECT;