diff --git a/core/src/objects/joint/snap.h b/core/src/objects/joint/snap.h index 174cc9e..bac4262 100644 --- a/core/src/objects/joint/snap.h +++ b/core/src/objects/joint/snap.h @@ -3,10 +3,6 @@ #include "objects/base/instance.h" #include "objects/joint/jointinstance.h" #include -#include - -class Part; -class Workspace; class Snap : public JointInstance { rp::FixedJoint* joint = nullptr; diff --git a/core/src/objects/joint/weld.cpp b/core/src/objects/joint/weld.cpp new file mode 100644 index 0000000..d2c7cef --- /dev/null +++ b/core/src/objects/joint/weld.cpp @@ -0,0 +1,59 @@ +#include "weld.h" + +#include "datatypes/cframe.h" +#include "objects/datamodel.h" +#include "objects/joint/jointinstance.h" +#include "objects/jointsservice.h" +#include "objects/part.h" +#include "objects/workspace.h" +#include +#include +#include + +const InstanceType Weld::TYPE = { + .super = &JointInstance::TYPE, + .className = "Weld", + .constructor = &Weld::Create, +}; + +const InstanceType* Weld::GetClass() { + return &TYPE; +} + +Weld::Weld(): JointInstance(&TYPE) { +} + +Weld::~Weld() { +} + +void Weld::buildJoint() { + // Only if both parts are set, are not the same part, are part of a workspace, and are part of the same workspace, we build the joint + if (part0.expired() || part1.expired() || part0.lock() == part1.lock() || !workspaceOfPart(part0.lock()) || workspaceOfPart(part0.lock()) != workspaceOfPart(part1.lock())) return; + + // Don't build the joint if we're not part of either a workspace or JointsService + if ((!GetParent() || GetParent().value()->GetClass() != &JointsService::TYPE) && !workspace()) return; + + std::shared_ptr workspace = workspaceOfPart(part0.lock()).value(); + if (!workspace->physicsWorld) return; + + // Update Part1's rotation and cframe prior to creating the joint as reactphysics3d locks rotation based on how it + // used to be rather than specifying an anchor rotation, so whatever. + Data::CFrame newFrame = part0.lock()->cframe * (c1.Inverse() * c0); + part1.lock()->cframe = newFrame; + workspace->SyncPartPhysics(part1.lock()); + + // printf("c1.Rotation: "); + // printVec(c1.ToEulerAnglesXYZ()); + rp::FixedJointInfo jointInfo(part0.lock()->rigidBody, part1.lock()->rigidBody, (c0.Inverse() * c1).Position()); + this->joint = dynamic_cast(workspace->physicsWorld->createJoint(jointInfo)); + jointWorkspace = workspace; +} + +// !!! REMINDER: This has to be called manually when parts are destroyed/removed from the workspace, or joints will linger +void Weld::breakJoint() { + // If the joint doesn't exist, or its workspace expired (not our problem anymore), then no need to do anything + if (!this->joint || jointWorkspace.expired() || !jointWorkspace.lock()->physicsWorld) return; + + jointWorkspace.lock()->physicsWorld->destroyJoint(this->joint); + this->joint = nullptr; +} \ No newline at end of file diff --git a/core/src/objects/joint/weld.h b/core/src/objects/joint/weld.h new file mode 100644 index 0000000..318eef9 --- /dev/null +++ b/core/src/objects/joint/weld.h @@ -0,0 +1,21 @@ +#pragma once + +#include "objects/base/instance.h" +#include "objects/joint/jointinstance.h" +#include + +class Weld : public JointInstance { + rp::FixedJoint* joint = nullptr; + + virtual void buildJoint() override; + virtual void breakJoint() override; +public: + const static InstanceType TYPE; + + Weld(); + ~Weld(); + + static inline std::shared_ptr New() { return std::make_shared(); }; + static inline std::shared_ptr Create() { return std::make_shared(); }; + virtual const InstanceType* GetClass() override; +}; \ No newline at end of file diff --git a/core/src/objects/part.cpp b/core/src/objects/part.cpp index 79f1205..67413fe 100644 --- a/core/src/objects/part.cpp +++ b/core/src/objects/part.cpp @@ -6,6 +6,7 @@ #include "datatypes/color3.h" #include "datatypes/vector.h" #include "objects/base/member.h" +#include "objects/joint/weld.h" #include "objects/jointsservice.h" #include "objects/joint/jointinstance.h" #include "objects/joint/snap.h" @@ -260,26 +261,41 @@ SurfaceType Part::surfaceFromFace(NormalId face) { return SurfaceSmooth; // Unreachable } -bool Part::checkJointContinuinty(std::shared_ptr otherPart) { +bool Part::checkJointContinuity(std::shared_ptr otherPart) { // Make sure that the two parts don't depend on one another - if (shared() == otherPart) return false; + return checkJointContinuityUp(otherPart) && checkJointContinuityDown(otherPart); +} +bool Part::checkJointContinuityDown(std::shared_ptr otherPart) { + if (shared() == otherPart) return false; for (auto joint : primaryJoints) { if (joint.expired() || joint.lock()->part1.expired()) continue; - if (!joint.lock()->part1.lock()->checkJointContinuinty(otherPart)) + if (!joint.lock()->part1.lock()->checkJointContinuityDown(otherPart)) return false; } + return true; +} +bool Part::checkJointContinuityUp(std::shared_ptr otherPart) { + if (shared() == otherPart) return false; for (auto joint : secondaryJoints) { if (joint.expired() || joint.lock()->part0.expired()) continue; - if (!joint.lock()->part0.lock()->checkJointContinuinty(otherPart)) + if (!joint.lock()->part0.lock()->checkJointContinuityUp(otherPart)) return false; } - return true; } +std::optional> makeJointFromSurfaces(SurfaceType a, SurfaceType b) { + if (a == SurfaceWeld || b == SurfaceWeld || a == SurfaceGlue || b == SurfaceGlue) return Weld::New(); + if ((a == SurfaceStuds && (b == SurfaceInlets || b == SurfaceUniversal)) + || (a == SurfaceInlets && (b == SurfaceStuds || b == SurfaceUniversal)) + || (a == SurfaceUniversal && (b == SurfaceStuds || b == SurfaceInlets || b == SurfaceUniversal))) + return Snap::New(); + return std::nullopt; +} + void Part::MakeJoints() { // Algorithm: Find nearby parts // Make sure parts are not dependant on each other (via primary/secondaryJoints) @@ -311,26 +327,22 @@ void Part::MakeJoints() { float dot = myWorldNormal.Dot(otherWorldNormal); if (dot > -0.99) continue; // Surface is pointing opposite to ours if (abs(surfacePointLocalToMyFrame.Z()) > 0.05) continue; // Surfaces are within 0.05 studs of one another - if (!checkJointContinuinty(otherPart)) continue; + if (!checkJointContinuity(otherPart)) continue; SurfaceType mySurface = surfaceFromFace(faceFromNormal(myFace)); SurfaceType otherSurface = surfaceFromFace(faceFromNormal(otherFace)); - if (mySurface == SurfaceSmooth) continue; // We're not responsible for any joints - else if (mySurface == SurfaceWeld || mySurface == SurfaceGlue - || (mySurface == SurfaceStuds && (otherSurface == SurfaceInlets || otherSurface == SurfaceUniversal)) - || (mySurface == SurfaceInlets && (otherSurface == SurfaceStuds || otherSurface == SurfaceUniversal)) - || (mySurface == SurfaceUniversal && (otherSurface == SurfaceStuds || otherSurface == SurfaceInlets || otherSurface == SurfaceUniversal))) { // Always make a weld no matter what the other surface is - std::shared_ptr joint = Snap::New(); - joint->part0 = shared(); - joint->part1 = otherPart->shared(); - joint->c1 = cframe; - joint->c0 = otherPart->cframe; - dataModel().value()->GetService()->AddChild(joint); - joint->UpdateProperty("Part0"); + auto joint_ = makeJointFromSurfaces(mySurface, otherSurface); + if (!joint_) continue; + std::shared_ptr joint = joint_.value(); + joint->part0 = shared(); + joint->part1 = otherPart->shared(); + joint->c1 = cframe; + joint->c0 = otherPart->cframe; + dataModel().value()->GetService()->AddChild(joint); + joint->UpdateProperty("Part0"); - Logger::debugf("Made joint between %s and %s!\n", name.c_str(), otherPart->name.c_str()); - } + Logger::debugf("Made joint between %s and %s!\n", name.c_str(), otherPart->name.c_str()); } } } diff --git a/core/src/objects/part.h b/core/src/objects/part.h index c780f58..cc78c09 100644 --- a/core/src/objects/part.h +++ b/core/src/objects/part.h @@ -8,6 +8,7 @@ #include "datatypes/vector.h" #include "objects/base/instance.h" #include "rendering/surface.h" +#include #include #include @@ -37,7 +38,9 @@ protected: void untrackJoint(std::shared_ptr); SurfaceType surfaceFromFace(NormalId); - bool checkJointContinuinty(std::shared_ptr); + bool checkJointContinuity(std::shared_ptr); + bool checkJointContinuityUp(std::shared_ptr); + bool checkJointContinuityDown(std::shared_ptr); friend JointInstance; diff --git a/core/src/objects/workspace.h b/core/src/objects/workspace.h index 2295588..d5859e8 100644 --- a/core/src/objects/workspace.h +++ b/core/src/objects/workspace.h @@ -25,6 +25,7 @@ enum FilterResult { class Part; class Snap; +class Weld; typedef std::function)> RaycastFilter; @@ -34,6 +35,7 @@ class Workspace : public Service { friend Part; friend Snap; + friend Weld; protected: void InitService() override; bool initialized = false; diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index bf05df0..0c35813 100755 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "mainglwidget.h" #include "common.h" #include "math_helper.h" @@ -445,6 +446,7 @@ void MainGLWidget::updateCycle() { } +int partId = 1; void MainGLWidget::keyPressEvent(QKeyEvent* evt) { if (evt->key() == Qt::Key_W) moveZ = 1; else if (evt->key() == Qt::Key_S) moveZ = -1; @@ -460,6 +462,7 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) { .color = glm::vec3(1.0f, 0.5f, 0.31f), })); gWorkspace()->SyncPartPhysics(lastPart); + lastPart->name = "Part" + std::to_string(partId++); } if (evt->key() == Qt::Key_U) diff --git a/editor/mainwindow.cpp b/editor/mainwindow.cpp index e59fab6..7877de2 100644 --- a/editor/mainwindow.cpp +++ b/editor/mainwindow.cpp @@ -395,6 +395,12 @@ void MainWindow::connectActionHandlers() { gDataModel = newModel; newModel->Init(); ui->explorerView->updateRoot(newModel); + + // Reset running state + runState = RUN_STOPPED; + ui->actionRunSimulation->setEnabled(true); + ui->actionPauseSimulation->setEnabled(false); + ui->actionStopSimulation->setEnabled(false); }); connect(ui->actionDelete, &QAction::triggered, this, [&]() {