fix(joint): error in continuity check leading to infinity loop then segfault
This commit is contained in:
parent
6a461143a4
commit
d4f7582780
8 changed files with 127 additions and 25 deletions
|
@ -3,10 +3,6 @@
|
|||
#include "objects/base/instance.h"
|
||||
#include "objects/joint/jointinstance.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
class Part;
|
||||
class Workspace;
|
||||
|
||||
class Snap : public JointInstance {
|
||||
rp::FixedJoint* joint = nullptr;
|
||||
|
|
59
core/src/objects/joint/weld.cpp
Normal file
59
core/src/objects/joint/weld.cpp
Normal file
|
@ -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 <memory>
|
||||
#include <reactphysics3d/constraint/FixedJoint.h>
|
||||
#include <reactphysics3d/engine/PhysicsWorld.h>
|
||||
|
||||
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> 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<rp::FixedJoint*>(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;
|
||||
}
|
21
core/src/objects/joint/weld.h
Normal file
21
core/src/objects/joint/weld.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "objects/base/instance.h"
|
||||
#include "objects/joint/jointinstance.h"
|
||||
#include <memory>
|
||||
|
||||
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<Weld> New() { return std::make_shared<Weld>(); };
|
||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Weld>(); };
|
||||
virtual const InstanceType* GetClass() override;
|
||||
};
|
|
@ -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<Part> otherPart) {
|
||||
bool Part::checkJointContinuity(std::shared_ptr<Part> otherPart) {
|
||||
// Make sure that the two parts don't depend on one another
|
||||
|
||||
if (shared<Part>() == otherPart) return false;
|
||||
return checkJointContinuityUp(otherPart) && checkJointContinuityDown(otherPart);
|
||||
}
|
||||
|
||||
bool Part::checkJointContinuityDown(std::shared_ptr<Part> otherPart) {
|
||||
if (shared<Part>() == 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<Part> otherPart) {
|
||||
if (shared<Part>() == 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<std::shared_ptr<JointInstance>> 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,17 +327,14 @@ 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<Snap> joint = Snap::New();
|
||||
auto joint_ = makeJointFromSurfaces(mySurface, otherSurface);
|
||||
if (!joint_) continue;
|
||||
std::shared_ptr<JointInstance> joint = joint_.value();
|
||||
joint->part0 = shared<Part>();
|
||||
joint->part1 = otherPart->shared<Part>();
|
||||
joint->c1 = cframe;
|
||||
|
@ -334,7 +347,6 @@ void Part::MakeJoints() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Part::trackJoint(std::shared_ptr<JointInstance> joint) {
|
||||
if (!joint->part0.expired() && joint->part0.lock() == shared_from_this()) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "datatypes/vector.h"
|
||||
#include "objects/base/instance.h"
|
||||
#include "rendering/surface.h"
|
||||
#include <optional>
|
||||
#include <reactphysics3d/reactphysics3d.h>
|
||||
#include <vector>
|
||||
|
||||
|
@ -37,7 +38,9 @@ protected:
|
|||
void untrackJoint(std::shared_ptr<JointInstance>);
|
||||
|
||||
SurfaceType surfaceFromFace(NormalId);
|
||||
bool checkJointContinuinty(std::shared_ptr<Part>);
|
||||
bool checkJointContinuity(std::shared_ptr<Part>);
|
||||
bool checkJointContinuityUp(std::shared_ptr<Part>);
|
||||
bool checkJointContinuityDown(std::shared_ptr<Part>);
|
||||
|
||||
friend JointInstance;
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ enum FilterResult {
|
|||
|
||||
class Part;
|
||||
class Snap;
|
||||
class Weld;
|
||||
|
||||
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
|
||||
|
||||
|
@ -34,6 +35,7 @@ class Workspace : public Service {
|
|||
|
||||
friend Part;
|
||||
friend Snap;
|
||||
friend Weld;
|
||||
protected:
|
||||
void InitService() override;
|
||||
bool initialized = false;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <glm/vector_relational.hpp>
|
||||
#include <qnamespace.h>
|
||||
#include <qsoundeffect.h>
|
||||
#include <string>
|
||||
#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)
|
||||
|
|
|
@ -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, [&]() {
|
||||
|
|
Loading…
Add table
Reference in a new issue