refactor(physics): refactored part code into PhysRigidBody

This commit is contained in:
maelstrom 2025-08-19 21:22:19 +02:00
parent 4c0f24066c
commit 577ad312b8
9 changed files with 86 additions and 87 deletions

View file

@ -27,15 +27,14 @@ BasePart::BasePart(const InstanceType* type, PartConstructParams params): PVInst
BasePart::~BasePart() { BasePart::~BasePart() {
// This relies on physicsCommon still existing. Be very careful. // This relies on physicsCommon still existing. Be very careful.
if (this->rigidBody && workspace() != nullptr) { if (workspace() != nullptr) {
workspace()->RemoveBody(shared<BasePart>()); workspace()->RemoveBody(shared<BasePart>());
} }
} }
void BasePart::OnAncestryChanged(nullable std::shared_ptr<Instance> child, nullable std::shared_ptr<Instance> newParent) { void BasePart::OnAncestryChanged(nullable std::shared_ptr<Instance> child, nullable std::shared_ptr<Instance> newParent) {
if (this->rigidBody) this->rigidBody.setActive(workspace() != nullptr);
this->rigidBody->setIsActive(workspace() != nullptr);
if (workspace() != nullptr) if (workspace() != nullptr)
workspace()->SyncPartPhysics(std::dynamic_pointer_cast<BasePart>(this->shared_from_this())); workspace()->SyncPartPhysics(std::dynamic_pointer_cast<BasePart>(this->shared_from_this()));

View file

@ -1,6 +1,5 @@
#pragma once #pragma once
#include <list>
#include <memory> #include <memory>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/ext.hpp> #include <glm/ext.hpp>
@ -10,16 +9,10 @@
#include "datatypes/vector.h" #include "datatypes/vector.h"
#include "objects/base/instance.h" #include "objects/base/instance.h"
#include "enum/surface.h" #include "enum/surface.h"
#include <mutex>
#include <optional>
#include <reactphysics3d/body/RigidBody.h>
#include <reactphysics3d/engine/PhysicsCommon.h>
#include <reactphysics3d/reactphysics3d.h>
#include <vector> #include <vector>
#include "objects/annotation.h" #include "objects/annotation.h"
#include "objects/pvinstance.h" #include "objects/pvinstance.h"
#include "physics/world.h"
namespace rp = reactphysics3d;
// For easy construction from C++. Maybe should be removed? // For easy construction from C++. Maybe should be removed?
struct PartConstructParams { struct PartConstructParams {
@ -59,8 +52,6 @@ protected:
void OnAncestryChanged(nullable std::shared_ptr<Instance> child, nullable std::shared_ptr<Instance> newParent) override; void OnAncestryChanged(nullable std::shared_ptr<Instance> child, nullable std::shared_ptr<Instance> newParent) override;
void onUpdated(std::string); void onUpdated(std::string);
virtual void updateCollider(rp::PhysicsCommon* common) = 0;
BasePart(const InstanceType*); BasePart(const InstanceType*);
BasePart(const InstanceType*, PartConstructParams params); BasePart(const InstanceType*, PartConstructParams params);
public: public:
@ -110,7 +101,7 @@ public:
DEF_SIGNAL SignalSource Touched; DEF_SIGNAL SignalSource Touched;
DEF_SIGNAL SignalSource TouchEnded; DEF_SIGNAL SignalSource TouchEnded;
rp::RigidBody* rigidBody = nullptr; PhysRigidBody rigidBody;
inline SurfaceType GetSurfaceFromFace(NormalId face) { return surfaceFromFace(face); } inline SurfaceType GetSurfaceFromFace(NormalId face) { return surfaceFromFace(face); }
float GetSurfaceParamA(Vector3 face); float GetSurfaceParamA(Vector3 face);

View file

@ -4,39 +4,11 @@
#include <glm/common.hpp> #include <glm/common.hpp>
Part::Part(): BasePart(&TYPE) { Part::Part(): BasePart(&TYPE) {
_lastShape = shape;
_lastSize = size;
} }
Part::Part(PartConstructParams params): BasePart(&TYPE, params) { Part::Part(PartConstructParams params): BasePart(&TYPE, params) {
_lastShape = shape;
_lastSize = size;
}
void Part::updateCollider(rp::PhysicsCommon* common) {
rp::CollisionShape* physShape;
if (shape == PartType::Ball) {
physShape = common->createSphereShape(glm::min(size.X(), size.Y(), size.Z()) * 0.5f);
} else if (shape == PartType::Block) {
physShape = common->createBoxShape(glmToRp(size * glm::vec3(0.5f)));
}
// Recreate the rigidbody if the shape changes
if (rigidBody->getNbColliders() > 0 && (_lastShape != shape || _lastSize != size)) {
// TODO: This causes Touched to get called twice. Fix this.
rigidBody->removeCollider(rigidBody->getCollider(0));
rigidBody->addCollider(physShape, rp::Transform());
}
if (rigidBody->getNbColliders() == 0)
rigidBody->addCollider(physShape, rp::Transform());
_lastShape = shape;
_lastSize = size;
} }
Vector3 Part::GetEffectiveSize() { Vector3 Part::GetEffectiveSize() {
return shape == PartType::Ball ? (Vector3)glm::vec3(glm::min(size.X(), size.Y(), size.Z())) : size; return shape == PartType::Ball ? (Vector3)glm::vec3(glm::min(size.X(), size.Y(), size.Z())) : size;
} }

View file

@ -6,11 +6,7 @@
class DEF_INST Part : public BasePart { class DEF_INST Part : public BasePart {
AUTOGEN_PREAMBLE AUTOGEN_PREAMBLE
PartType _lastShape;
Vector3 _lastSize;
protected: protected:
void updateCollider(rp::PhysicsCommon* common) override;
public: public:
Part(); Part();

View file

@ -12,21 +12,21 @@ WedgePart::WedgePart(PartConstructParams params): BasePart(&TYPE, params) {
} }
void WedgePart::updateCollider(rp::PhysicsCommon* common) { // void WedgePart::updateCollider(rp::PhysicsCommon* common) {
Logger::fatalError("Wedges are currently disabled! Please do not use them or your editor may crash\n"); // Logger::fatalError("Wedges are currently disabled! Please do not use them or your editor may crash\n");
rp::ConvexMeshShape* shape = common->createConvexMeshShape(wedgePhysMesh, glmToRp(size * glm::vec3(0.5f))); // rp::ConvexMeshShape* shape = common->createConvexMeshShape(wedgePhysMesh, glmToRp(size * glm::vec3(0.5f)));
// Recreate the rigidbody if the shape changes // // Recreate the rigidbody if the shape changes
if (rigidBody->getNbColliders() > 0 // if (rigidBody->getNbColliders() > 0
&& dynamic_cast<rp::ConvexMeshShape*>(rigidBody->getCollider(0)->getCollisionShape())->getScale() != shape->getScale()) { // && dynamic_cast<rp::ConvexMeshShape*>(rigidBody->getCollider(0)->getCollisionShape())->getScale() != shape->getScale()) {
// TODO: This causes Touched to get called twice. Fix this. // // TODO: This causes Touched to get called twice. Fix this.
rigidBody->removeCollider(rigidBody->getCollider(0)); // rigidBody->removeCollider(rigidBody->getCollider(0));
rigidBody->addCollider(shape, rp::Transform()); // rigidBody->addCollider(shape, rp::Transform());
} // }
if (rigidBody->getNbColliders() == 0) // if (rigidBody->getNbColliders() == 0)
rigidBody->addCollider(shape, rp::Transform()); // rigidBody->addCollider(shape, rp::Transform());
} // }
void WedgePart::createWedgeShape(rp::PhysicsCommon* common) { void WedgePart::createWedgeShape(rp::PhysicsCommon* common) {
// https://www.reactphysics3d.com/documentation/index.html#creatingbody // https://www.reactphysics3d.com/documentation/index.html#creatingbody

View file

@ -7,7 +7,6 @@ class DEF_INST_(hidden) WedgePart : public BasePart {
AUTOGEN_PREAMBLE AUTOGEN_PREAMBLE
protected: protected:
void updateCollider(rp::PhysicsCommon* common) override;
static void createWedgeShape(rp::PhysicsCommon* common); static void createWedgeShape(rp::PhysicsCommon* common);
friend Workspace; friend Workspace;

View file

@ -61,7 +61,7 @@ PartAssembly PartAssembly::FromSelection(std::shared_ptr<Selection> selection) {
void PartAssembly::SetCollisionsEnabled(bool enabled) { void PartAssembly::SetCollisionsEnabled(bool enabled) {
for (auto part : parts) { for (auto part : parts) {
part->rigidBody->getCollider(0)->setIsWorldQueryCollider(enabled); part->rigidBody.setCollisionsEnabled(enabled);
} }
} }

View file

@ -5,7 +5,7 @@
#include "objects/part/basepart.h" #include "objects/part/basepart.h"
#include "physics/util.h" #include "physics/util.h"
#include <reactphysics3d/constraint/FixedJoint.h> #include <reactphysics3d/constraint/FixedJoint.h>
#include "reactphysics3d/constraint/HingeJoint.h" #include <reactphysics3d/constraint/HingeJoint.h>
#include "timeutil.h" #include "timeutil.h"
#include <memory> #include <memory>
#include "objects/service/workspace.h" #include "objects/service/workspace.h"
@ -78,12 +78,12 @@ void PhysWorld::step(float deltaTime) {
// TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance // TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance
for (std::shared_ptr<BasePart> part : simulatedBodies) { for (std::shared_ptr<BasePart> part : simulatedBodies) {
if (!part->rigidBody) continue; if (!part->rigidBody.rigidBody) continue;
// Sync properties // Sync properties
const rp::Transform& transform = part->rigidBody->getTransform(); const rp::Transform& transform = part->rigidBody.rigidBody->getTransform();
part->cframe = CFrame(transform); part->cframe = CFrame(transform);
part->velocity = part->rigidBody->getLinearVelocity(); part->velocity = part->rigidBody.rigidBody->getLinearVelocity();
// part->rigidBody->enableGravity(true); // part->rigidBody->enableGravity(true);
// RotateV/Motor joint // RotateV/Motor joint
@ -93,7 +93,7 @@ void PhysWorld::step(float deltaTime) {
std::shared_ptr<JointInstance> motor = joint.lock()->CastTo<JointInstance>().expect(); std::shared_ptr<JointInstance> motor = joint.lock()->CastTo<JointInstance>().expect();
float rate = motor->part0.lock()->GetSurfaceParamB(-motor->c0.LookVector().Unit()) * 30; float rate = motor->part0.lock()->GetSurfaceParamB(-motor->c0.LookVector().Unit()) * 30;
// part->rigidBody->enableGravity(false); // part->rigidBody->enableGravity(false);
part->rigidBody->setAngularVelocity(-(motor->part0.lock()->cframe * motor->c0).LookVector() * rate); part->rigidBody.rigidBody->setAngularVelocity(-(motor->part0.lock()->cframe * motor->c0).LookVector() * rate);
} }
} }
@ -111,32 +111,32 @@ void PhysWorld::removeBody(std::shared_ptr<BasePart> part) {
void PhysWorld::syncBodyProperties(std::shared_ptr<BasePart> part) { void PhysWorld::syncBodyProperties(std::shared_ptr<BasePart> part) {
rp::Transform transform = part->cframe; rp::Transform transform = part->cframe;
if (!part->rigidBody) { if (!part->rigidBody.rigidBody) {
part->rigidBody = worldImpl->createRigidBody(transform); part->rigidBody.rigidBody = worldImpl->createRigidBody(transform);
} else { } else {
part->rigidBody->setTransform(transform); part->rigidBody.rigidBody->setTransform(transform);
} }
part->updateCollider(&physicsCommon); part->rigidBody.updateCollider(part);
part->rigidBody->setType(part->anchored ? rp::BodyType::STATIC : rp::BodyType::DYNAMIC); part->rigidBody.rigidBody->setType(part->anchored ? rp::BodyType::STATIC : rp::BodyType::DYNAMIC);
part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11); part->rigidBody.rigidBody->getCollider(0)->setCollisionCategoryBits(0b11);
part->rigidBody->getCollider(0)->setIsSimulationCollider(part->canCollide); part->rigidBody.rigidBody->getCollider(0)->setIsSimulationCollider(part->canCollide);
part->rigidBody->getCollider(0)->setIsTrigger(!part->canCollide); part->rigidBody.rigidBody->getCollider(0)->setIsTrigger(!part->canCollide);
rp::Material& material = part->rigidBody->getCollider(0)->getMaterial(); rp::Material& material = part->rigidBody.rigidBody->getCollider(0)->getMaterial();
material.setFrictionCoefficient(0.35); material.setFrictionCoefficient(0.35);
material.setMassDensity(1.f); material.setMassDensity(1.f);
//https://github.com/DanielChappuis/reactphysics3d/issues/170#issuecomment-691514860 //https://github.com/DanielChappuis/reactphysics3d/issues/170#issuecomment-691514860
part->rigidBody->updateMassFromColliders(); part->rigidBody.rigidBody->updateMassFromColliders();
part->rigidBody->updateLocalInertiaTensorFromColliders(); part->rigidBody.rigidBody->updateLocalInertiaTensorFromColliders();
part->rigidBody->setLinearVelocity(part->velocity); part->rigidBody.rigidBody->setLinearVelocity(part->velocity);
// part->rigidBody->setMass(density * part->size.x * part->size.y * part->size.z); // part->rigidBody->setMass(density * part->size.x * part->size.y * part->size.z);
part->rigidBody->setUserData(&*part); part->rigidBody.rigidBody->setUserData(&*part);
} }
// Ray-casting // Ray-casting
@ -199,8 +199,8 @@ std::optional<const RaycastResult> PhysWorld::castRay(Vector3 point, Vector3 rot
PhysJoint PhysWorld::createJoint(PhysJointInfo& type, std::shared_ptr<BasePart> part0, std::shared_ptr<BasePart> part1) { PhysJoint PhysWorld::createJoint(PhysJointInfo& type, std::shared_ptr<BasePart> part0, std::shared_ptr<BasePart> part1) {
// error checking // error checking
if (part0->rigidBody == nullptr if (part0->rigidBody.rigidBody == nullptr
|| part1->rigidBody == nullptr || part1->rigidBody.rigidBody == nullptr
|| !part0->workspace() || !part0->workspace()
|| !part1->workspace() || !part1->workspace()
|| part0->workspace()->physicsWorld != shared_from_this() || part0->workspace()->physicsWorld != shared_from_this()
@ -208,15 +208,15 @@ PhysJoint PhysWorld::createJoint(PhysJointInfo& type, std::shared_ptr<BasePart>
) { Logger::fatalError("Failed to create joint between two parts due to the call being invalid"); panic(); }; ) { Logger::fatalError("Failed to create joint between two parts due to the call being invalid"); panic(); };
if (PhysJointGlueInfo* info = dynamic_cast<PhysJointGlueInfo*>(&type)) { if (PhysJointGlueInfo* info = dynamic_cast<PhysJointGlueInfo*>(&type)) {
return worldImpl->createJoint(rp::FixedJointInfo(part0->rigidBody, part1->rigidBody, info->anchorPoint)); return worldImpl->createJoint(rp::FixedJointInfo(part0->rigidBody.rigidBody, part1->rigidBody.rigidBody, info->anchorPoint));
} else if (PhysJointWeldInfo* info = dynamic_cast<PhysJointWeldInfo*>(&type)) { } else if (PhysJointWeldInfo* info = dynamic_cast<PhysJointWeldInfo*>(&type)) {
return worldImpl->createJoint(rp::FixedJointInfo(part0->rigidBody, part1->rigidBody, info->anchorPoint)); return worldImpl->createJoint(rp::FixedJointInfo(part0->rigidBody.rigidBody, part1->rigidBody.rigidBody, info->anchorPoint));
} else if (PhysJointSnapInfo* info = dynamic_cast<PhysJointSnapInfo*>(&type)) { } else if (PhysJointSnapInfo* info = dynamic_cast<PhysJointSnapInfo*>(&type)) {
return worldImpl->createJoint(rp::FixedJointInfo(part0->rigidBody, part1->rigidBody, info->anchorPoint)); return worldImpl->createJoint(rp::FixedJointInfo(part0->rigidBody.rigidBody, part1->rigidBody.rigidBody, info->anchorPoint));
} else if (PhysJointHingeInfo* info = dynamic_cast<PhysJointHingeInfo*>(&type)) { } else if (PhysJointHingeInfo* info = dynamic_cast<PhysJointHingeInfo*>(&type)) {
return worldImpl->createJoint(rp::HingeJointInfo(part0->rigidBody, part1->rigidBody, info->anchorPoint, info->rotationAxis)); return worldImpl->createJoint(rp::HingeJointInfo(part0->rigidBody.rigidBody, part1->rigidBody.rigidBody, info->anchorPoint, info->rotationAxis));
} else if (PhysJointMotorInfo* info = dynamic_cast<PhysJointMotorInfo*>(&type)) { } else if (PhysJointMotorInfo* info = dynamic_cast<PhysJointMotorInfo*>(&type)) {
auto implInfo = rp::HingeJointInfo(part0->rigidBody, part1->rigidBody, info->anchorPoint, info->rotationAxis); auto implInfo = rp::HingeJointInfo(part0->rigidBody.rigidBody, part1->rigidBody.rigidBody, info->anchorPoint, info->rotationAxis);
implInfo.isCollisionEnabled = false; implInfo.isCollisionEnabled = false;
return worldImpl->createJoint(implInfo); return worldImpl->createJoint(implInfo);
@ -230,5 +230,30 @@ PhysJoint PhysWorld::createJoint(PhysJointInfo& type, std::shared_ptr<BasePart>
} }
void PhysWorld::destroyJoint(PhysJoint joint) { void PhysWorld::destroyJoint(PhysJoint joint) {
worldImpl->destroyJoint(joint.joint);
}
void PhysRigidBody::updateCollider(std::shared_ptr<BasePart> bPart) {
if (std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(bPart)) {
rp::CollisionShape* physShape;
if (part->shape == PartType::Ball) {
physShape = physicsCommon.createSphereShape(glm::min(part->size.X(), part->size.Y(), part->size.Z()) * 0.5f);
} else if (part->shape == PartType::Block) {
physShape = physicsCommon.createBoxShape(glmToRp(part->size * glm::vec3(0.5f)));
}
// Recreate the rigidbody if the shape changes
if (rigidBody->getNbColliders() > 0 && (_lastShape != part->shape || _lastSize != part->size)) {
// TODO: This causes Touched to get called twice. Fix this.
rigidBody->removeCollider(rigidBody->getCollider(0));
rigidBody->addCollider(physShape, rp::Transform());
}
if (rigidBody->getNbColliders() == 0)
rigidBody->addCollider(physShape, rp::Transform());
_lastShape = part->shape;
_lastSize = part->size;
}
} }

View file

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "datatypes/vector.h" #include "datatypes/vector.h"
#include "enum/part.h"
#include "reactphysics3d/body/RigidBody.h"
#include <functional> #include <functional>
#include <list> #include <list>
#include <memory> #include <memory>
@ -55,6 +57,21 @@ public:
inline PhysJoint() {} inline PhysJoint() {}
}; };
class PhysRigidBody {
rp::RigidBody* rigidBody = nullptr;
inline PhysRigidBody(rp::RigidBody* rigidBody) : rigidBody(rigidBody) {}
Vector3 _lastSize;
PartType _lastShape;
friend PhysWorld;
public:
inline PhysRigidBody() {}
inline void setActive(bool active) { if (!rigidBody) return; rigidBody->setIsActive(active); }
inline void setCollisionsEnabled(bool enabled) { if (!rigidBody) return; rigidBody->getCollider(0)->setIsWorldQueryCollider(enabled); }
void updateCollider(std::shared_ptr<BasePart>);
};
class PhysWorld : public std::enable_shared_from_this<PhysWorld> { class PhysWorld : public std::enable_shared_from_this<PhysWorld> {
rp::PhysicsWorld* worldImpl; rp::PhysicsWorld* worldImpl;
PhysicsEventListener physicsEventListener; PhysicsEventListener physicsEventListener;