fix(joints): break joint when member part is updated/destroyed
This commit is contained in:
parent
884a735d5e
commit
4005bf1cb5
7 changed files with 142 additions and 11 deletions
|
@ -17,6 +17,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "ptr_helpers.h"
|
||||||
|
|
||||||
// Static so that this variable name is "local" to this source file
|
// Static so that this variable name is "local" to this source file
|
||||||
const InstanceType Instance::TYPE = {
|
const InstanceType Instance::TYPE = {
|
||||||
|
@ -55,17 +56,6 @@ Instance::Instance(const InstanceType* type) {
|
||||||
Instance::~Instance () {
|
Instance::~Instance () {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool operator ==(std::optional<std::weak_ptr<T>> a, std::optional<std::weak_ptr<T>> b) {
|
|
||||||
return (!a.has_value() || a.value().expired()) && (!b.has_value() || b.value().expired())
|
|
||||||
|| (a.has_value() && !a.value().expired()) && (b.has_value() && !b.value().expired()) && a.value().lock() == b.value().lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool operator ==(std::weak_ptr<T> a, std::weak_ptr<T> b) {
|
|
||||||
return a.expired() && b.expired() || (!a.expired() && !b.expired() && a.lock() == b.lock());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::weak_ptr<T> optional_to_weak(std::optional<std::shared_ptr<T>> a) {
|
std::weak_ptr<T> optional_to_weak(std::optional<std::shared_ptr<T>> a) {
|
||||||
return a ? a.value() : std::weak_ptr<T>();
|
return a ? a.value() : std::weak_ptr<T>();
|
||||||
|
@ -151,6 +141,13 @@ std::optional<std::shared_ptr<Instance>> Instance::GetParent() {
|
||||||
return parent.lock();
|
return parent.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Instance::Destroy() {
|
||||||
|
if (parentLocked) return;
|
||||||
|
// TODO: Implement proper distruction stuff
|
||||||
|
SetParent(std::nullopt);
|
||||||
|
parentLocked = true;
|
||||||
|
}
|
||||||
|
|
||||||
static std::shared_ptr<Instance> DUMMY_INSTANCE;
|
static std::shared_ptr<Instance> DUMMY_INSTANCE;
|
||||||
DescendantsIterator Instance::GetDescendantsStart() {
|
DescendantsIterator Instance::GetDescendantsStart() {
|
||||||
return DescendantsIterator(GetChildren().size() > 0 ? GetChildren()[0] : DUMMY_INSTANCE);
|
return DescendantsIterator(GetChildren().size() > 0 ? GetChildren()[0] : DUMMY_INSTANCE);
|
||||||
|
|
|
@ -91,6 +91,7 @@ public:
|
||||||
std::optional<std::shared_ptr<Instance>> GetParent();
|
std::optional<std::shared_ptr<Instance>> GetParent();
|
||||||
bool IsParentLocked();
|
bool IsParentLocked();
|
||||||
inline const std::vector<std::shared_ptr<Instance>> GetChildren() { return children; }
|
inline const std::vector<std::shared_ptr<Instance>> GetChildren() { return children; }
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
DescendantsIterator GetDescendantsStart();
|
DescendantsIterator GetDescendantsStart();
|
||||||
DescendantsIterator GetDescendantsEnd();
|
DescendantsIterator GetDescendantsEnd();
|
||||||
|
|
|
@ -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/snap.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
@ -172,12 +173,18 @@ void Part::OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std
|
||||||
if (workspace())
|
if (workspace())
|
||||||
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
|
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
|
||||||
|
|
||||||
|
// Destroy joints
|
||||||
|
if (!workspace()) BreakJoints();
|
||||||
|
|
||||||
// TODO: Sleeping bodies that touch this one also need to be updated
|
// TODO: Sleeping bodies that touch this one also need to be updated
|
||||||
}
|
}
|
||||||
|
|
||||||
void Part::onUpdated(std::string property) {
|
void Part::onUpdated(std::string property) {
|
||||||
if (workspace())
|
if (workspace())
|
||||||
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
|
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
|
||||||
|
|
||||||
|
// When position/rotation/size is manually edited, break all joints, they don't apply anymore
|
||||||
|
BreakJoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expands provided extents to fit point
|
// Expands provided extents to fit point
|
||||||
|
@ -206,4 +213,72 @@ Vector3 Part::GetAABB() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (min - max).Abs() / 2;
|
return (min - max).Abs() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Part::BreakJoints() {
|
||||||
|
for (std::weak_ptr<Snap> joint : primaryJoints) {
|
||||||
|
if (joint.expired()) continue;
|
||||||
|
joint.lock()->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::weak_ptr<Snap> joint : secondaryJoints) {
|
||||||
|
if (joint.expired()) continue;
|
||||||
|
joint.lock()->Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Part::trackJoint(std::shared_ptr<Snap> joint) {
|
||||||
|
if (!joint->part0.expired() && joint->part0.lock() == shared_from_this()) {
|
||||||
|
for (auto it = primaryJoints.begin(); it != primaryJoints.end();) {
|
||||||
|
// Clean expired refs
|
||||||
|
if (it->expired()) {
|
||||||
|
primaryJoints.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the joint is already tracked, skip
|
||||||
|
if (it->lock() == joint)
|
||||||
|
return;
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
primaryJoints.push_back(joint);
|
||||||
|
} else if (!joint->part1.expired() && joint->part1.lock() == shared_from_this()) {
|
||||||
|
for (auto it = secondaryJoints.begin(); it != secondaryJoints.end();) {
|
||||||
|
// Clean expired refs
|
||||||
|
if (it->expired()) {
|
||||||
|
secondaryJoints.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the joint is already tracked, skip
|
||||||
|
if (it->lock() == joint)
|
||||||
|
return;
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
secondaryJoints.push_back(joint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Part::untrackJoint(std::shared_ptr<Snap> joint) {
|
||||||
|
for (auto it = primaryJoints.begin(); it != primaryJoints.end();) {
|
||||||
|
// Clean expired refs
|
||||||
|
if (it->expired() || it->lock() == joint) {
|
||||||
|
primaryJoints.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = secondaryJoints.begin(); it != secondaryJoints.end();) {
|
||||||
|
// Clean expired refs
|
||||||
|
if (it->expired() || it->lock() == joint) {
|
||||||
|
secondaryJoints.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
it++;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
#include "rendering/surface.h"
|
#include "rendering/surface.h"
|
||||||
#include <reactphysics3d/reactphysics3d.h>
|
#include <reactphysics3d/reactphysics3d.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace rp = reactphysics3d;
|
namespace rp = reactphysics3d;
|
||||||
|
|
||||||
|
@ -23,8 +24,20 @@ struct PartConstructParams {
|
||||||
bool locked = false;
|
bool locked = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Snap;
|
||||||
|
|
||||||
class Part : public Instance {
|
class Part : public Instance {
|
||||||
protected:
|
protected:
|
||||||
|
// Joints where this part is Part0
|
||||||
|
std::vector<std::weak_ptr<Snap>> primaryJoints;
|
||||||
|
// Joints where this part is Part1
|
||||||
|
std::vector<std::weak_ptr<Snap>> secondaryJoints;
|
||||||
|
|
||||||
|
void trackJoint(std::shared_ptr<Snap>);
|
||||||
|
void untrackJoint(std::shared_ptr<Snap>);
|
||||||
|
|
||||||
|
friend Snap;
|
||||||
|
|
||||||
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent) override;
|
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent) override;
|
||||||
void onUpdated(std::string);
|
void onUpdated(std::string);
|
||||||
public:
|
public:
|
||||||
|
@ -58,6 +71,9 @@ public:
|
||||||
|
|
||||||
inline Data::Vector3 position() { return cframe.Position(); }
|
inline Data::Vector3 position() { return cframe.Position(); }
|
||||||
|
|
||||||
|
void MakeJoints();
|
||||||
|
void BreakJoints();
|
||||||
|
|
||||||
// Calculate size of axis-aligned bounding box
|
// Calculate size of axis-aligned bounding box
|
||||||
Data::Vector3 GetAABB();
|
Data::Vector3 GetAABB();
|
||||||
};
|
};
|
|
@ -4,10 +4,12 @@
|
||||||
#include "datatypes/ref.h"
|
#include "datatypes/ref.h"
|
||||||
#include "objects/datamodel.h"
|
#include "objects/datamodel.h"
|
||||||
#include "objects/jointsservice.h"
|
#include "objects/jointsservice.h"
|
||||||
|
#include "objects/part.h"
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <reactphysics3d/constraint/FixedJoint.h>
|
#include <reactphysics3d/constraint/FixedJoint.h>
|
||||||
#include <reactphysics3d/engine/PhysicsWorld.h>
|
#include <reactphysics3d/engine/PhysicsWorld.h>
|
||||||
|
#include "ptr_helpers.h"
|
||||||
|
|
||||||
const InstanceType Snap::TYPE = {
|
const InstanceType Snap::TYPE = {
|
||||||
.super = &Instance::TYPE,
|
.super = &Instance::TYPE,
|
||||||
|
@ -59,10 +61,33 @@ void Snap::OnAncestryChanged(std::optional<std::shared_ptr<Instance>>, std::opti
|
||||||
}
|
}
|
||||||
|
|
||||||
void Snap::onUpdated(std::string property) {
|
void Snap::onUpdated(std::string property) {
|
||||||
|
// Add ourselves to the attached parts, or remove, if applicable
|
||||||
|
|
||||||
|
// Parts differ, delete
|
||||||
|
if (part0 != oldPart0 && !oldPart0.expired()) {
|
||||||
|
oldPart0.lock()->untrackJoint(shared<Snap>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part1 != oldPart1 && !oldPart1.expired()) {
|
||||||
|
oldPart1.lock()->untrackJoint(shared<Snap>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parts differ, add
|
||||||
|
if (part0 != oldPart0 && !part0.expired()) {
|
||||||
|
part0.lock()->trackJoint(shared<Snap>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part1 != oldPart1 && !part1.expired()) {
|
||||||
|
part1.lock()->trackJoint(shared<Snap>());
|
||||||
|
}
|
||||||
|
|
||||||
// Destroy and rebuild the joint, if applicable
|
// Destroy and rebuild the joint, if applicable
|
||||||
|
|
||||||
breakJoint();
|
breakJoint();
|
||||||
buildJoint();
|
buildJoint();
|
||||||
|
|
||||||
|
oldPart0 = part0;
|
||||||
|
oldPart1 = part1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Snap::buildJoint() {
|
void Snap::buildJoint() {
|
||||||
|
|
|
@ -10,6 +10,9 @@ class Workspace;
|
||||||
class Snap : public Instance {
|
class Snap : public Instance {
|
||||||
rp::FixedJoint* joint = nullptr;
|
rp::FixedJoint* joint = nullptr;
|
||||||
|
|
||||||
|
std::weak_ptr<Part> oldPart0;
|
||||||
|
std::weak_ptr<Part> oldPart1;
|
||||||
|
|
||||||
// The workspace the joint was created in, if it exists
|
// The workspace the joint was created in, if it exists
|
||||||
std::weak_ptr<Workspace> jointWorkspace;
|
std::weak_ptr<Workspace> jointWorkspace;
|
||||||
protected:
|
protected:
|
||||||
|
|
14
core/src/ptr_helpers.h
Normal file
14
core/src/ptr_helpers.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
#include <optional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool operator ==(std::optional<std::weak_ptr<T>> a, std::optional<std::weak_ptr<T>> b) {
|
||||||
|
return (!a.has_value() || a.value().expired()) && (!b.has_value() || b.value().expired())
|
||||||
|
|| (a.has_value() && !a.value().expired()) && (b.has_value() && !b.value().expired()) && a.value().lock() == b.value().lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool operator ==(std::weak_ptr<T> a, std::weak_ptr<T> b) {
|
||||||
|
return a.expired() && b.expired() || (!a.expired() && !b.expired() && a.lock() == b.lock());
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue