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/base/instance.h"
|
||||||
#include "objects/joint/jointinstance.h"
|
#include "objects/joint/jointinstance.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
class Part;
|
|
||||||
class Workspace;
|
|
||||||
|
|
||||||
class Snap : public JointInstance {
|
class Snap : public JointInstance {
|
||||||
rp::FixedJoint* joint = nullptr;
|
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/color3.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "objects/base/member.h"
|
#include "objects/base/member.h"
|
||||||
|
#include "objects/joint/weld.h"
|
||||||
#include "objects/jointsservice.h"
|
#include "objects/jointsservice.h"
|
||||||
#include "objects/joint/jointinstance.h"
|
#include "objects/joint/jointinstance.h"
|
||||||
#include "objects/joint/snap.h"
|
#include "objects/joint/snap.h"
|
||||||
|
@ -260,26 +261,41 @@ SurfaceType Part::surfaceFromFace(NormalId face) {
|
||||||
return SurfaceSmooth; // Unreachable
|
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
|
// 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) {
|
for (auto joint : primaryJoints) {
|
||||||
if (joint.expired() || joint.lock()->part1.expired()) continue;
|
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 false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Part::checkJointContinuityUp(std::shared_ptr<Part> otherPart) {
|
||||||
|
if (shared<Part>() == otherPart) return false;
|
||||||
for (auto joint : secondaryJoints) {
|
for (auto joint : secondaryJoints) {
|
||||||
if (joint.expired() || joint.lock()->part0.expired()) continue;
|
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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() {
|
void Part::MakeJoints() {
|
||||||
// Algorithm: Find nearby parts
|
// Algorithm: Find nearby parts
|
||||||
// Make sure parts are not dependant on each other (via primary/secondaryJoints)
|
// Make sure parts are not dependant on each other (via primary/secondaryJoints)
|
||||||
|
@ -311,26 +327,22 @@ void Part::MakeJoints() {
|
||||||
float dot = myWorldNormal.Dot(otherWorldNormal);
|
float dot = myWorldNormal.Dot(otherWorldNormal);
|
||||||
if (dot > -0.99) continue; // Surface is pointing opposite to ours
|
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 (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 mySurface = surfaceFromFace(faceFromNormal(myFace));
|
||||||
SurfaceType otherSurface = surfaceFromFace(faceFromNormal(otherFace));
|
SurfaceType otherSurface = surfaceFromFace(faceFromNormal(otherFace));
|
||||||
|
|
||||||
if (mySurface == SurfaceSmooth) continue; // We're not responsible for any joints
|
auto joint_ = makeJointFromSurfaces(mySurface, otherSurface);
|
||||||
else if (mySurface == SurfaceWeld || mySurface == SurfaceGlue
|
if (!joint_) continue;
|
||||||
|| (mySurface == SurfaceStuds && (otherSurface == SurfaceInlets || otherSurface == SurfaceUniversal))
|
std::shared_ptr<JointInstance> joint = joint_.value();
|
||||||
|| (mySurface == SurfaceInlets && (otherSurface == SurfaceStuds || otherSurface == SurfaceUniversal))
|
joint->part0 = shared<Part>();
|
||||||
|| (mySurface == SurfaceUniversal && (otherSurface == SurfaceStuds || otherSurface == SurfaceInlets || otherSurface == SurfaceUniversal))) { // Always make a weld no matter what the other surface is
|
joint->part1 = otherPart->shared<Part>();
|
||||||
std::shared_ptr<Snap> joint = Snap::New();
|
joint->c1 = cframe;
|
||||||
joint->part0 = shared<Part>();
|
joint->c0 = otherPart->cframe;
|
||||||
joint->part1 = otherPart->shared<Part>();
|
dataModel().value()->GetService<JointsService>()->AddChild(joint);
|
||||||
joint->c1 = cframe;
|
joint->UpdateProperty("Part0");
|
||||||
joint->c0 = otherPart->cframe;
|
|
||||||
dataModel().value()->GetService<JointsService>()->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());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
#include "rendering/surface.h"
|
#include "rendering/surface.h"
|
||||||
|
#include <optional>
|
||||||
#include <reactphysics3d/reactphysics3d.h>
|
#include <reactphysics3d/reactphysics3d.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -37,7 +38,9 @@ protected:
|
||||||
void untrackJoint(std::shared_ptr<JointInstance>);
|
void untrackJoint(std::shared_ptr<JointInstance>);
|
||||||
|
|
||||||
SurfaceType surfaceFromFace(NormalId);
|
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;
|
friend JointInstance;
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ enum FilterResult {
|
||||||
|
|
||||||
class Part;
|
class Part;
|
||||||
class Snap;
|
class Snap;
|
||||||
|
class Weld;
|
||||||
|
|
||||||
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
|
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ class Workspace : public Service {
|
||||||
|
|
||||||
friend Part;
|
friend Part;
|
||||||
friend Snap;
|
friend Snap;
|
||||||
|
friend Weld;
|
||||||
protected:
|
protected:
|
||||||
void InitService() override;
|
void InitService() override;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <glm/vector_relational.hpp>
|
#include <glm/vector_relational.hpp>
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
#include <qsoundeffect.h>
|
#include <qsoundeffect.h>
|
||||||
|
#include <string>
|
||||||
#include "mainglwidget.h"
|
#include "mainglwidget.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "math_helper.h"
|
#include "math_helper.h"
|
||||||
|
@ -445,6 +446,7 @@ void MainGLWidget::updateCycle() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int partId = 1;
|
||||||
void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
|
void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
|
||||||
if (evt->key() == Qt::Key_W) moveZ = 1;
|
if (evt->key() == Qt::Key_W) moveZ = 1;
|
||||||
else if (evt->key() == Qt::Key_S) 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),
|
.color = glm::vec3(1.0f, 0.5f, 0.31f),
|
||||||
}));
|
}));
|
||||||
gWorkspace()->SyncPartPhysics(lastPart);
|
gWorkspace()->SyncPartPhysics(lastPart);
|
||||||
|
lastPart->name = "Part" + std::to_string(partId++);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt->key() == Qt::Key_U)
|
if (evt->key() == Qt::Key_U)
|
||||||
|
|
|
@ -395,6 +395,12 @@ void MainWindow::connectActionHandlers() {
|
||||||
gDataModel = newModel;
|
gDataModel = newModel;
|
||||||
newModel->Init();
|
newModel->Init();
|
||||||
ui->explorerView->updateRoot(newModel);
|
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, [&]() {
|
connect(ui->actionDelete, &QAction::triggered, this, [&]() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue