From bb81aff4fd26e12bed7d0f589ab3231900db9403 Mon Sep 17 00:00:00 2001 From: maelstrom Date: Tue, 19 Aug 2025 18:19:48 +0200 Subject: [PATCH] refactor(physics): moved implementation-specific workspace functions into physics/world --- core/CMakeLists.txt | 4 +- core/src/objects/part/basepart.cpp | 4 +- core/src/objects/part/basepart.h | 4 +- core/src/objects/service/workspace.cpp | 223 ++----------------------- core/src/objects/service/workspace.h | 62 +------ core/src/physics/world.cpp | 194 +++++++++++++++++++++ core/src/physics/world.h | 58 +++++++ editor/placedocument.cpp | 1 - 8 files changed, 274 insertions(+), 276 deletions(-) create mode 100644 core/src/physics/world.cpp create mode 100644 core/src/physics/world.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 99e3e71..7372394 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -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 ) diff --git a/core/src/objects/part/basepart.cpp b/core/src/objects/part/basepart.cpp index 037bb95..a21d2a7 100644 --- a/core/src/objects/part/basepart.cpp +++ b/core/src/objects/part/basepart.cpp @@ -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()); } } @@ -53,6 +52,7 @@ void BasePart::OnWorkspaceAdded(std::optional> oldWor } void BasePart::OnWorkspaceRemoved(std::shared_ptr oldWorkspace) { + oldWorkspace->RemoveBody(shared()); } void BasePart::onUpdated(std::string property) { diff --git a/core/src/objects/part/basepart.h b/core/src/objects/part/basepart.h index f100a78..8564571 100644 --- a/core/src/objects/part/basepart.h +++ b/core/src/objects/part/basepart.h @@ -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 otherPart); friend JointInstance; - friend Workspace; + friend PhysWorld; virtual void OnWorkspaceAdded(std::optional> oldWorkspace, std::shared_ptr newWorkspace) override; virtual void OnWorkspaceRemoved(std::shared_ptr oldWorkspace) override; diff --git a/core/src/objects/service/workspace.cpp b/core/src/objects/service/workspace.cpp index 8465c79..4295e1e 100644 --- a/core/src/objects/service/workspace.cpp +++ b/core/src/objects/service/workspace.cpp @@ -12,79 +12,18 @@ #include "physics/util.h" #include "timeutil.h" #include -#include -#include -#include -rp::PhysicsCommon* Workspace::physicsCommon = new rp::PhysicsCommon; - -Workspace::Workspace(): Service(&TYPE), physicsEventListener(this) { - physicsWorld = physicsCommon->createPhysicsWorld(); +Workspace::Workspace(): Service(&TYPE), physicsWorld(std::make_shared()) { } -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(pair.getBody1()->getUserData())->shared(); - contact.part1 = reinterpret_cast(pair.getBody2()->getUserData())->shared(); - 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(pair.getBody1()->getUserData())->shared(); - auto part1 = reinterpret_cast(pair.getBody2()->getUserData())->shared(); - - 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 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 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 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 motor = joint.lock()->CastTo().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 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 filter; - - std::optional 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 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 filter = std::nullopt) : startPos(startPos), filter(filter) {} - std::optional getNearestHit() { return nearestHit; }; -}; - -std::optional Workspace::CastRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional 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 part) { - simulatedBodies.push_back(part); -} - -void Workspace::RemoveBody(std::shared_ptr part) { - auto it = std::find(simulatedBodies.begin(), simulatedBodies.end(), part); - simulatedBodies.erase(it); + // return joint; + return nullptr; } \ No newline at end of file diff --git a/core/src/objects/service/workspace.h b/core/src/objects/service/workspace.h index b2724d0..9288ed6 100644 --- a/core/src/objects/service/workspace.h +++ b/core/src/objects/service/workspace.h @@ -2,35 +2,13 @@ #include "objects/annotation.h" #include "objects/base/service.h" +#include "physics/world.h" #include "utils.h" #include #include #include #include #include -#include -#include -#include -#include - -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)> RaycastFilter; - -struct QueueItem { - std::shared_ptr part; - enum { - QUEUEITEM_ADD, - QUEUEITEM_REMOVE, - } action; -}; - struct ContactItem { std::shared_ptr part0; std::shared_ptr 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> simulatedBodies; std::queue contactQueue; std::mutex contactQueueLock; - rp::PhysicsWorld* physicsWorld; - static rp::PhysicsCommon* physicsCommon; - PhysicsEventListener physicsEventListener; - void updatePartPhysics(std::shared_ptr part); + std::shared_ptr 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 New() { return std::make_shared(); }; static inline std::shared_ptr Create() { return std::make_shared(); }; - void AddBody(std::shared_ptr part); - void RemoveBody(std::shared_ptr part); - void DestroyRigidBody(rp::RigidBody* rigidBody); + inline void AddBody(std::shared_ptr part) { physicsWorld->addBody(part); } + inline void RemoveBody(std::shared_ptr part) { physicsWorld->removeBody(part); } void SyncPartPhysics(std::shared_ptr part); rp::Joint* CreateJoint(const rp::JointInfo& jointInfo); void DestroyJoint(rp::Joint* joint); - void ProcessContactEvents(); void PhysicsStep(float deltaTime); - std::optional CastRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional filter = std::nullopt, unsigned short categoryMaskBits = 0xFFFF); + inline std::optional CastRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional filter = std::nullopt, unsigned short categoryMaskBits = 0xFFFF) { return physicsWorld->castRay(point, rotation, maxLength, filter, categoryMaskBits); } }; \ No newline at end of file diff --git a/core/src/physics/world.cpp b/core/src/physics/world.cpp new file mode 100644 index 0000000..981bb66 --- /dev/null +++ b/core/src/physics/world.cpp @@ -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(pair.getBody1()->getUserData())->shared(); + auto part1 = reinterpret_cast(pair.getBody2()->getUserData())->shared(); + + 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(pair.getBody1()->getUserData())->shared(); + auto part1 = reinterpret_cast(pair.getBody2()->getUserData())->shared(); + + 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 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 motor = joint.lock()->CastTo().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 part) { + simulatedBodies.push_back(part); +} + +void PhysWorld::removeBody(std::shared_ptr part) { + auto it = std::find(simulatedBodies.begin(), simulatedBodies.end(), part); + simulatedBodies.erase(it); +} + +void PhysWorld::syncBodyProperties(std::shared_ptr 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 filter; + + std::optional 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 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 filter = std::nullopt) : startPos(startPos), filter(filter) {} + std::optional getNearestHit() { return nearestHit; }; +}; + +std::optional PhysWorld::castRay(Vector3 point, Vector3 rotation, float maxLength, std::optional 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(); +} \ No newline at end of file diff --git a/core/src/physics/world.h b/core/src/physics/world.h new file mode 100644 index 0000000..32514a4 --- /dev/null +++ b/core/src/physics/world.h @@ -0,0 +1,58 @@ +#pragma once + +#include "datatypes/vector.h" +#include +#include +#include +#include + +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)> 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 { + rp::PhysicsWorld* worldImpl; + PhysicsEventListener physicsEventListener; + std::list> simulatedBodies; + +public: + PhysWorld(); + ~PhysWorld(); + + void step(float deltaTime); + + void addBody(std::shared_ptr); + void removeBody(std::shared_ptr); + inline const std::list>& getSimulatedBodies() { return simulatedBodies; } + void syncBodyProperties(std::shared_ptr); + std::optional castRay(Vector3 point, Vector3 rotation, float maxLength, std::optional filter, unsigned short categoryMaskBits); +}; \ No newline at end of file diff --git a/editor/placedocument.cpp b/editor/placedocument.cpp index 0cc214e..0eaf46e 100644 --- a/editor/placedocument.cpp +++ b/editor/placedocument.cpp @@ -94,7 +94,6 @@ void PlaceDocument::timerEvent(QTimerEvent* evt) { placeWidget->updateCycle(); gDataModel->GetService()->RunSleepingThreads(); if (_runState == RUN_RUNNING) gDataModel->GetService()->PhysicsStep(0.033); - gDataModel->GetService()->ProcessContactEvents(); }