refactor(physics): moved implementation-specific workspace functions into physics/world

This commit is contained in:
maelstrom 2025-08-19 18:19:48 +02:00
parent d922dd3727
commit bb81aff4fd
8 changed files with 274 additions and 276 deletions

View file

@ -57,6 +57,9 @@ set(SOURCES
src/rendering/font.h
src/rendering/defaultmeshes.h
src/rendering/texture3d.cpp
src/physics/util.h
src/physics/world.h
src/physics/world.cpp
src/logger.cpp
src/handles.h
src/timeutil.h
@ -126,7 +129,6 @@ set(SOURCES
src/panic.h
src/lua/instancelib.cpp
src/timeutil.cpp
src/physics/util.h
src/luaapis.h
src/math_helper.h
)

View file

@ -29,8 +29,7 @@ BasePart::BasePart(const InstanceType* type, PartConstructParams params): PVInst
BasePart::~BasePart() {
// This relies on physicsCommon still existing. Be very careful.
if (this->rigidBody && workspace()) {
workspace().value()->DestroyRigidBody(rigidBody);
this->rigidBody = nullptr;
workspace().value()->RemoveBody(shared<BasePart>());
}
}
@ -53,6 +52,7 @@ void BasePart::OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWor
}
void BasePart::OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) {
oldWorkspace->RemoveBody(shared<BasePart>());
}
void BasePart::onUpdated(std::string property) {

View file

@ -32,7 +32,7 @@ struct PartConstructParams {
bool locked = false;
};
class Workspace;
class PhysWorld;
class DEF_INST_ABSTRACT_(explorer_icon="part") BasePart : public PVInstance {
AUTOGEN_PREAMBLE
@ -52,7 +52,7 @@ protected:
bool checkSurfacesTouching(CFrame surfaceFrame, Vector3 size, Vector3 myFace, Vector3 otherFace, std::shared_ptr<BasePart> otherPart);
friend JointInstance;
friend Workspace;
friend PhysWorld;
virtual void OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWorkspace, std::shared_ptr<Workspace> newWorkspace) override;
virtual void OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) override;

View file

@ -12,79 +12,18 @@
#include "physics/util.h"
#include "timeutil.h"
#include <memory>
#include <reactphysics3d/collision/CollisionCallback.h>
#include <reactphysics3d/collision/OverlapCallback.h>
#include <reactphysics3d/engine/PhysicsCommon.h>
rp::PhysicsCommon* Workspace::physicsCommon = new rp::PhysicsCommon;
Workspace::Workspace(): Service(&TYPE), physicsEventListener(this) {
physicsWorld = physicsCommon->createPhysicsWorld();
Workspace::Workspace(): Service(&TYPE), physicsWorld(std::make_shared<PhysWorld>()) {
}
Workspace::~Workspace() {
if (physicsCommon)
physicsCommon->destroyPhysicsWorld(physicsWorld);
}
PhysicsEventListener::PhysicsEventListener(Workspace* parent) : workspace(parent) {}
void PhysicsEventListener::onContact(const rp::CollisionCallback::CallbackData& data) {
workspace->contactQueueLock.lock();
for (size_t i = 0; i < data.getNbContactPairs(); i++) {
auto pair = data.getContactPair(i);
auto type = pair.getEventType();
if (type == rp::CollisionCallback::ContactPair::EventType::ContactStay) continue;
if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactStay)
continue;
ContactItem contact;
contact.part0 = reinterpret_cast<BasePart*>(pair.getBody1()->getUserData())->shared<BasePart>();
contact.part1 = reinterpret_cast<BasePart*>(pair.getBody2()->getUserData())->shared<BasePart>();
contact.action = type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactStart ? ContactItem::CONTACTITEM_TOUCHED : ContactItem::CONTACTITEM_TOUCHENDED;
workspace->contactQueue.push(contact);
}
workspace->contactQueueLock.unlock();
}
void PhysicsEventListener::onTrigger(const rp::OverlapCallback::CallbackData& data) {
workspace->contactQueueLock.lock();
for (size_t i = 0; i < data.getNbOverlappingPairs(); i++) {
auto pair = data.getOverlappingPair(i);
auto type = pair.getEventType();
if (type == rp::OverlapCallback::OverlapPair::EventType::OverlapStay) continue;
auto part0 = reinterpret_cast<BasePart*>(pair.getBody1()->getUserData())->shared<BasePart>();
auto part1 = reinterpret_cast<BasePart*>(pair.getBody2()->getUserData())->shared<BasePart>();
if (type == reactphysics3d::OverlapCallback::OverlapPair::EventType::OverlapStart) {
part0->Touched->Fire({ (Variant)InstanceRef(part1) });
part1->Touched->Fire({ (Variant)InstanceRef(part0) });
} else if (type == reactphysics3d::OverlapCallback::OverlapPair::EventType::OverlapExit) {
part0->TouchEnded->Fire({ (Variant)InstanceRef(part1) });
part1->TouchEnded->Fire({ (Variant)InstanceRef(part0) });
}
}
workspace->contactQueueLock.unlock();
}
Workspace::~Workspace() = default;
void Workspace::InitService() {
if (initialized) return;
initialized = true;
physicsWorld->setGravity(rp::Vector3(0, -196.2, 0));
// world->setContactsPositionCorrectionTechnique(rp3d::ContactsPositionCorrectionTechnique::BAUMGARTE_CONTACTS);
// physicsWorld->setNbIterationsPositionSolver(2000);
// physicsWorld->setNbIterationsVelocitySolver(2000);
// physicsWorld->setSleepLinearVelocity(10);
// physicsWorld->setSleepAngularVelocity(5);
physicsWorld->setEventListener(&physicsEventListener);
// Create meshes
WedgePart::createWedgeShape(physicsCommon);
// WedgePart::createWedgeShape(physicsCommon);
}
void Workspace::OnRun() {
@ -110,83 +49,14 @@ void Workspace::OnRun() {
}
}
void Workspace::updatePartPhysics(std::shared_ptr<BasePart> part) {
rp::Transform transform = part->cframe;
if (!part->rigidBody) {
part->rigidBody = physicsWorld->createRigidBody(transform);
} else {
part->rigidBody->setTransform(transform);
}
part->updateCollider(physicsCommon);
part->rigidBody->setType(part->anchored ? rp::BodyType::STATIC : rp::BodyType::DYNAMIC);
part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11);
part->rigidBody->getCollider(0)->setIsSimulationCollider(part->canCollide);
part->rigidBody->getCollider(0)->setIsTrigger(!part->canCollide);
rp::Material& material = part->rigidBody->getCollider(0)->getMaterial();
material.setFrictionCoefficient(0.35);
material.setMassDensity(1.f);
//https://github.com/DanielChappuis/reactphysics3d/issues/170#issuecomment-691514860
part->rigidBody->updateMassFromColliders();
part->rigidBody->updateLocalInertiaTensorFromColliders();
part->rigidBody->setLinearVelocity(part->velocity);
// part->rigidBody->setMass(density * part->size.x * part->size.y * part->size.z);
part->rigidBody->setUserData(&*part);
}
void Workspace::ProcessContactEvents() {
contactQueueLock.lock();
while (!contactQueue.empty()) {
ContactItem& contact = contactQueue.front();
contactQueue.pop();
if (contact.action == ContactItem::CONTACTITEM_TOUCHED) {
contact.part0->Touched->Fire({ (Variant)InstanceRef(contact.part1) });
contact.part1->Touched->Fire({ (Variant)InstanceRef(contact.part0) });
} else if (contact.action == ContactItem::CONTACTITEM_TOUCHENDED) {
contact.part0->TouchEnded->Fire({ (Variant)InstanceRef(contact.part1) });
contact.part1->TouchEnded->Fire({ (Variant)InstanceRef(contact.part0) });
}
}
contactQueueLock.unlock();
}
void Workspace::SyncPartPhysics(std::shared_ptr<BasePart> part) {
updatePartPhysics(part);
physicsWorld->syncBodyProperties(part);
}
tu_time_t physTime;
void Workspace::PhysicsStep(float deltaTime) {
tu_time_t startTime = tu_clock_micros();
std::scoped_lock lock(globalPhysicsLock);
physicsWorld->update(std::min(deltaTime / 2, (1/60.f)));
// TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance
for (std::shared_ptr<BasePart> part : simulatedBodies) {
if (!part->rigidBody) continue;
// Sync properties
const rp::Transform& transform = part->rigidBody->getTransform();
part->cframe = CFrame(transform);
part->velocity = part->rigidBody->getLinearVelocity();
// part->rigidBody->enableGravity(true);
// RotateV/Motor joint
for (auto& joint : part->secondaryJoints) {
if (joint.expired() || !joint.lock()->IsA("RotateV")) continue;
std::shared_ptr<JointInstance> motor = joint.lock()->CastTo<JointInstance>().expect();
float rate = motor->part0.lock()->GetSurfaceParamB(-motor->c0.LookVector().Unit()) * 30;
// part->rigidBody->enableGravity(false);
part->rigidBody->setAngularVelocity(-(motor->part0.lock()->cframe * motor->c0).LookVector() * rate);
}
physicsWorld->step(deltaTime);
for (std::shared_ptr<BasePart> part : physicsWorld->getSimulatedBodies()) {
// Destroy fallen parts
if (part->cframe.Position().Y() < this->fallenPartsDestroyHeight) {
auto parent = part->GetParent();
@ -197,88 +67,15 @@ void Workspace::PhysicsStep(float deltaTime) {
parent.value()->Destroy();
}
}
physTime = tu_clock_micros() - startTime;
}
RaycastResult::RaycastResult(const rp::RaycastInfo& raycastInfo)
: worldPoint(raycastInfo.worldPoint)
, worldNormal(raycastInfo.worldNormal)
, hitFraction(raycastInfo.hitFraction)
, triangleIndex(raycastInfo.triangleIndex)
, body(raycastInfo.body)
, collider(raycastInfo.collider) {}
class NearestRayHit : public rp::RaycastCallback {
rp::Vector3 startPos;
std::optional<RaycastFilter> filter;
std::optional<RaycastResult> nearestHit;
float nearestHitDistance = -1;
// Order is not guaranteed, so we have to figure out the nearest object using a more sophisticated algorith,
rp::decimal notifyRaycastHit(const rp::RaycastInfo& raycastInfo) override {
// If the detected object is further away than the nearest object, continue.
int distance = (raycastInfo.worldPoint - startPos).length();
if (nearestHitDistance != -1 && distance >= nearestHitDistance)
return 1;
if (!filter) {
nearestHit = raycastInfo;
nearestHitDistance = distance;
return 1;
}
std::shared_ptr<BasePart> part = partFromBody(raycastInfo.body);
FilterResult result = filter.value()(part);
if (result == FilterResult::BLOCK) {
nearestHit = std::nullopt;
nearestHitDistance = distance;
return 1;
} else if (result == FilterResult::TARGET) {
nearestHit = raycastInfo;
nearestHitDistance = distance;
return 1;
}
return 1;
};
public:
NearestRayHit(rp::Vector3 startPos, std::optional<RaycastFilter> filter = std::nullopt) : startPos(startPos), filter(filter) {}
std::optional<const RaycastResult> getNearestHit() { return nearestHit; };
};
std::optional<const RaycastResult> Workspace::CastRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional<RaycastFilter> filter, unsigned short categoryMaskBits) {
// std::scoped_lock lock(globalPhysicsLock);
rp::Ray ray(glmToRp(point), glmToRp(glm::normalize(rotation)) * maxLength);
NearestRayHit rayHit(glmToRp(point), filter);
physicsWorld->raycast(ray, &rayHit, categoryMaskBits);
return rayHit.getNearestHit();
}
void Workspace::DestroyRigidBody(rp::RigidBody* rigidBody) {
std::scoped_lock lock(globalPhysicsLock);
physicsWorld->destroyRigidBody(rigidBody);
}
void Workspace::DestroyJoint(rp::Joint* joint) {
std::scoped_lock lock(globalPhysicsLock);
physicsWorld->destroyJoint(joint);
// physicsWorld->destroyJoint(joint);
}
rp::Joint* Workspace::CreateJoint(const rp::JointInfo& jointInfo) {
std::scoped_lock lock(globalPhysicsLock);
rp::Joint* joint = physicsWorld->createJoint(jointInfo);
// rp::Joint* joint = physicsWorld->createJoint(jointInfo);
return joint;
}
void Workspace::AddBody(std::shared_ptr<BasePart> part) {
simulatedBodies.push_back(part);
}
void Workspace::RemoveBody(std::shared_ptr<BasePart> part) {
auto it = std::find(simulatedBodies.begin(), simulatedBodies.end(), part);
simulatedBodies.erase(it);
// return joint;
return nullptr;
}

View file

@ -2,35 +2,13 @@
#include "objects/annotation.h"
#include "objects/base/service.h"
#include "physics/world.h"
#include "utils.h"
#include <glm/ext/vector_float3.hpp>
#include <list>
#include <memory>
#include <mutex>
#include <queue>
#include <reactphysics3d/body/RigidBody.h>
#include <reactphysics3d/engine/EventListener.h>
#include <reactphysics3d/engine/PhysicsCommon.h>
#include <reactphysics3d/engine/PhysicsWorld.h>
namespace rp = reactphysics3d;
struct RaycastResult {
rp::Vector3 worldPoint;
rp::Vector3 worldNormal;
rp::decimal hitFraction;
int triangleIndex;
rp::Body* body;
rp::Collider* collider;
RaycastResult(const rp::RaycastInfo& raycastInfo);
};
enum FilterResult {
TARGET, // The object is captured
BLOCK, // The object blocks any objects behind it, but is not captured
PASS, // The object is transparent, ignore it
};
class BasePart;
class Snap;
@ -38,16 +16,6 @@ class Weld;
class Rotate;
class RotateV;
typedef std::function<FilterResult(std::shared_ptr<BasePart>)> RaycastFilter;
struct QueueItem {
std::shared_ptr<BasePart> part;
enum {
QUEUEITEM_ADD,
QUEUEITEM_REMOVE,
} action;
};
struct ContactItem {
std::shared_ptr<BasePart> part0;
std::shared_ptr<BasePart> part1;
@ -57,30 +25,13 @@ struct ContactItem {
} action;
};
class Workspace;
class PhysicsEventListener : public rp::EventListener {
friend Workspace;
Workspace* workspace;
PhysicsEventListener(Workspace*);
void onContact(const rp::CollisionCallback::CallbackData&) override;
void onTrigger(const rp::OverlapCallback::CallbackData&) override;
};
class DEF_INST_SERVICE_(explorer_icon="workspace") Workspace : public Service {
AUTOGEN_PREAMBLE
friend PhysicsEventListener;
std::list<std::shared_ptr<BasePart>> simulatedBodies;
std::queue<ContactItem> contactQueue;
std::mutex contactQueueLock;
rp::PhysicsWorld* physicsWorld;
static rp::PhysicsCommon* physicsCommon;
PhysicsEventListener physicsEventListener;
void updatePartPhysics(std::shared_ptr<BasePart> part);
std::shared_ptr<PhysWorld> physicsWorld;
protected:
void InitService() override;
void OnRun() override;
@ -90,7 +41,6 @@ public:
Workspace();
~Workspace();
std::mutex globalPhysicsLock;
std::recursive_mutex queueLock;
DEF_PROP float fallenPartsDestroyHeight = -500;
@ -98,15 +48,13 @@ public:
// static inline std::shared_ptr<Workspace> New() { return std::make_shared<Workspace>(); };
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Workspace>(); };
void AddBody(std::shared_ptr<BasePart> part);
void RemoveBody(std::shared_ptr<BasePart> part);
void DestroyRigidBody(rp::RigidBody* rigidBody);
inline void AddBody(std::shared_ptr<BasePart> part) { physicsWorld->addBody(part); }
inline void RemoveBody(std::shared_ptr<BasePart> part) { physicsWorld->removeBody(part); }
void SyncPartPhysics(std::shared_ptr<BasePart> part);
rp::Joint* CreateJoint(const rp::JointInfo& jointInfo);
void DestroyJoint(rp::Joint* joint);
void ProcessContactEvents();
void PhysicsStep(float deltaTime);
std::optional<const RaycastResult> CastRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional<RaycastFilter> filter = std::nullopt, unsigned short categoryMaskBits = 0xFFFF);
inline std::optional<const RaycastResult> CastRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional<RaycastFilter> filter = std::nullopt, unsigned short categoryMaskBits = 0xFFFF) { return physicsWorld->castRay(point, rotation, maxLength, filter, categoryMaskBits); }
};

194
core/src/physics/world.cpp Normal file
View file

@ -0,0 +1,194 @@
#include "world.h"
#include "datatypes/variant.h"
#include "datatypes/vector.h"
#include "objects/joint/jointinstance.h"
#include "objects/part/basepart.h"
#include "physics/util.h"
#include "timeutil.h"
rp3d::PhysicsCommon physicsCommon;
PhysWorld::PhysWorld() : physicsEventListener(this) {
worldImpl = physicsCommon.createPhysicsWorld();
worldImpl->setGravity(rp::Vector3(0, -196.2, 0));
// world->setContactsPositionCorrectionTechnique(rp3d::ContactsPositionCorrectionTechnique::BAUMGARTE_CONTACTS);
// physicsWorld->setNbIterationsPositionSolver(2000);
// physicsWorld->setNbIterationsVelocitySolver(2000);
// physicsWorld->setSleepLinearVelocity(10);
// physicsWorld->setSleepAngularVelocity(5);
worldImpl->setEventListener(&physicsEventListener);
}
PhysWorld::~PhysWorld() {
physicsCommon.destroyPhysicsWorld(worldImpl);
}
PhysicsEventListener::PhysicsEventListener(PhysWorld* world) : world(world) {}
void PhysicsEventListener::onContact(const rp::CollisionCallback::CallbackData& data) {
for (size_t i = 0; i < data.getNbContactPairs(); i++) {
auto pair = data.getContactPair(i);
auto type = pair.getEventType();
if (type == rp::CollisionCallback::ContactPair::EventType::ContactStay) continue;
auto part0 = reinterpret_cast<BasePart*>(pair.getBody1()->getUserData())->shared<BasePart>();
auto part1 = reinterpret_cast<BasePart*>(pair.getBody2()->getUserData())->shared<BasePart>();
if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactStart) {
part0->Touched->Fire({ (Variant)InstanceRef(part1) });
part1->Touched->Fire({ (Variant)InstanceRef(part0) });
} else if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactExit) {
part0->TouchEnded->Fire({ (Variant)InstanceRef(part1) });
part1->TouchEnded->Fire({ (Variant)InstanceRef(part0) });
}
}
}
void PhysicsEventListener::onTrigger(const rp::OverlapCallback::CallbackData& data) {
for (size_t i = 0; i < data.getNbOverlappingPairs(); i++) {
auto pair = data.getOverlappingPair(i);
auto type = pair.getEventType();
if (type == rp::OverlapCallback::OverlapPair::EventType::OverlapStay) continue;
auto part0 = reinterpret_cast<BasePart*>(pair.getBody1()->getUserData())->shared<BasePart>();
auto part1 = reinterpret_cast<BasePart*>(pair.getBody2()->getUserData())->shared<BasePart>();
if (type == reactphysics3d::OverlapCallback::OverlapPair::EventType::OverlapStart) {
part0->Touched->Fire({ (Variant)InstanceRef(part1) });
part1->Touched->Fire({ (Variant)InstanceRef(part0) });
} else if (type == reactphysics3d::OverlapCallback::OverlapPair::EventType::OverlapExit) {
part0->TouchEnded->Fire({ (Variant)InstanceRef(part1) });
part1->TouchEnded->Fire({ (Variant)InstanceRef(part0) });
}
}
}
tu_time_t physTime;
void PhysWorld::step(float deltaTime) {
tu_time_t startTime = tu_clock_micros();
worldImpl->update(std::min(deltaTime / 2, (1/60.f)));
// TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance
for (std::shared_ptr<BasePart> part : simulatedBodies) {
if (!part->rigidBody) continue;
// Sync properties
const rp::Transform& transform = part->rigidBody->getTransform();
part->cframe = CFrame(transform);
part->velocity = part->rigidBody->getLinearVelocity();
// part->rigidBody->enableGravity(true);
// RotateV/Motor joint
for (auto& joint : part->secondaryJoints) {
if (joint.expired() || !joint.lock()->IsA("RotateV")) continue;
std::shared_ptr<JointInstance> motor = joint.lock()->CastTo<JointInstance>().expect();
float rate = motor->part0.lock()->GetSurfaceParamB(-motor->c0.LookVector().Unit()) * 30;
// part->rigidBody->enableGravity(false);
part->rigidBody->setAngularVelocity(-(motor->part0.lock()->cframe * motor->c0).LookVector() * rate);
}
}
physTime = tu_clock_micros() - startTime;
}
void PhysWorld::addBody(std::shared_ptr<BasePart> part) {
simulatedBodies.push_back(part);
}
void PhysWorld::removeBody(std::shared_ptr<BasePart> part) {
auto it = std::find(simulatedBodies.begin(), simulatedBodies.end(), part);
simulatedBodies.erase(it);
}
void PhysWorld::syncBodyProperties(std::shared_ptr<BasePart> part) {
rp::Transform transform = part->cframe;
if (!part->rigidBody) {
part->rigidBody = worldImpl->createRigidBody(transform);
} else {
part->rigidBody->setTransform(transform);
}
part->updateCollider(&physicsCommon);
part->rigidBody->setType(part->anchored ? rp::BodyType::STATIC : rp::BodyType::DYNAMIC);
part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11);
part->rigidBody->getCollider(0)->setIsSimulationCollider(part->canCollide);
part->rigidBody->getCollider(0)->setIsTrigger(!part->canCollide);
rp::Material& material = part->rigidBody->getCollider(0)->getMaterial();
material.setFrictionCoefficient(0.35);
material.setMassDensity(1.f);
//https://github.com/DanielChappuis/reactphysics3d/issues/170#issuecomment-691514860
part->rigidBody->updateMassFromColliders();
part->rigidBody->updateLocalInertiaTensorFromColliders();
part->rigidBody->setLinearVelocity(part->velocity);
// part->rigidBody->setMass(density * part->size.x * part->size.y * part->size.z);
part->rigidBody->setUserData(&*part);
}
// Ray-casting
RaycastResult::RaycastResult(const rp::RaycastInfo& raycastInfo)
: worldPoint(raycastInfo.worldPoint)
, worldNormal(raycastInfo.worldNormal)
, hitFraction(raycastInfo.hitFraction)
, triangleIndex(raycastInfo.triangleIndex)
, body(raycastInfo.body)
, collider(raycastInfo.collider) {}
class NearestRayHit : public rp::RaycastCallback {
rp::Vector3 startPos;
std::optional<RaycastFilter> filter;
std::optional<RaycastResult> nearestHit;
float nearestHitDistance = -1;
// Order is not guaranteed, so we have to figure out the nearest object using a more sophisticated algorith,
rp::decimal notifyRaycastHit(const rp::RaycastInfo& raycastInfo) override {
// If the detected object is further away than the nearest object, continue.
int distance = (raycastInfo.worldPoint - startPos).length();
if (nearestHitDistance != -1 && distance >= nearestHitDistance)
return 1;
if (!filter) {
nearestHit = raycastInfo;
nearestHitDistance = distance;
return 1;
}
std::shared_ptr<BasePart> part = partFromBody(raycastInfo.body);
FilterResult result = filter.value()(part);
if (result == FilterResult::BLOCK) {
nearestHit = std::nullopt;
nearestHitDistance = distance;
return 1;
} else if (result == FilterResult::TARGET) {
nearestHit = raycastInfo;
nearestHitDistance = distance;
return 1;
}
return 1;
};
public:
NearestRayHit(rp::Vector3 startPos, std::optional<RaycastFilter> filter = std::nullopt) : startPos(startPos), filter(filter) {}
std::optional<const RaycastResult> getNearestHit() { return nearestHit; };
};
std::optional<const RaycastResult> PhysWorld::castRay(Vector3 point, Vector3 rotation, float maxLength, std::optional<RaycastFilter> filter, unsigned short categoryMaskBits) {
// std::scoped_lock lock(globalPhysicsLock);
rp::Ray ray(glmToRp(point), glmToRp(glm::normalize((glm::vec3)rotation)) * maxLength);
NearestRayHit rayHit(glmToRp(point), filter);
worldImpl->raycast(ray, &rayHit, categoryMaskBits);
return rayHit.getNearestHit();
}

58
core/src/physics/world.h Normal file
View file

@ -0,0 +1,58 @@
#pragma once
#include "datatypes/vector.h"
#include <functional>
#include <list>
#include <memory>
#include <reactphysics3d/reactphysics3d.h>
namespace rp = reactphysics3d;
struct RaycastResult {
rp::Vector3 worldPoint;
rp::Vector3 worldNormal;
rp::decimal hitFraction;
int triangleIndex;
rp::Body* body;
rp::Collider* collider;
RaycastResult(const rp::RaycastInfo& raycastInfo);
};
enum FilterResult {
TARGET, // The object is captured
BLOCK, // The object blocks any objects behind it, but is not captured
PASS, // The object is transparent, ignore it
};
class BasePart;
typedef std::function<FilterResult(std::shared_ptr<BasePart>)> RaycastFilter;
class PhysWorld;
class PhysicsEventListener : public rp::EventListener {
friend PhysWorld;
PhysWorld* world;
PhysicsEventListener(PhysWorld*);
void onContact(const rp::CollisionCallback::CallbackData&) override;
void onTrigger(const rp::OverlapCallback::CallbackData&) override;
};
class PhysWorld : std::enable_shared_from_this<PhysWorld> {
rp::PhysicsWorld* worldImpl;
PhysicsEventListener physicsEventListener;
std::list<std::shared_ptr<BasePart>> simulatedBodies;
public:
PhysWorld();
~PhysWorld();
void step(float deltaTime);
void addBody(std::shared_ptr<BasePart>);
void removeBody(std::shared_ptr<BasePart>);
inline const std::list<std::shared_ptr<BasePart>>& getSimulatedBodies() { return simulatedBodies; }
void syncBodyProperties(std::shared_ptr<BasePart>);
std::optional<const RaycastResult> castRay(Vector3 point, Vector3 rotation, float maxLength, std::optional<RaycastFilter> filter, unsigned short categoryMaskBits);
};

View file

@ -94,7 +94,6 @@ void PlaceDocument::timerEvent(QTimerEvent* evt) {
placeWidget->updateCycle();
gDataModel->GetService<ScriptContext>()->RunSleepingThreads();
if (_runState == RUN_RUNNING) gDataModel->GetService<Workspace>()->PhysicsStep(0.033);
gDataModel->GetService<Workspace>()->ProcessContactEvents();
}