From dca5c483c4ec2768f95458d1728089b7d3de6029 Mon Sep 17 00:00:00 2001 From: maelstrom Date: Sun, 13 Jul 2025 18:37:49 +0200 Subject: [PATCH] fix(workspace): do not fire signals from physics thread as lua has to always be executed from main thread --- core/src/objects/service/workspace.cpp | 39 +++++++++++++++++++------- core/src/objects/service/workspace.h | 15 ++++++++++ editor/placedocument.cpp | 1 + 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/core/src/objects/service/workspace.cpp b/core/src/objects/service/workspace.cpp index 38dba8e..3c1816d 100644 --- a/core/src/objects/service/workspace.cpp +++ b/core/src/objects/service/workspace.cpp @@ -29,26 +29,28 @@ Workspace::~Workspace() { 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; - auto part0 = reinterpret_cast(pair.getBody1()->getUserData())->shared(); - auto part1 = reinterpret_cast(pair.getBody2()->getUserData())->shared(); + if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactStay) + continue; - 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) }); - } + 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) { -for (size_t i = 0; i < data.getNbOverlappingPairs(); i++) { + 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; @@ -64,6 +66,7 @@ for (size_t i = 0; i < data.getNbOverlappingPairs(); i++) { part1->TouchEnded->Fire({ (Variant)InstanceRef(part0) }); } } + workspace->contactQueueLock.unlock(); } void Workspace::InitService() { @@ -143,6 +146,22 @@ void Workspace::updatePartPhysics(std::shared_ptr part) { 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) { if (globalPhysicsLock.try_lock()) { updatePartPhysics(part); diff --git a/core/src/objects/service/workspace.h b/core/src/objects/service/workspace.h index 4111680..4cb8e21 100644 --- a/core/src/objects/service/workspace.h +++ b/core/src/objects/service/workspace.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,15 @@ struct QueueItem { } action; }; +struct ContactItem { + std::shared_ptr part0; + std::shared_ptr part1; + enum { + CONTACTITEM_TOUCHED, + CONTACTITEM_TOUCHENDED, + } action; +}; + class Workspace; class PhysicsEventListener : public rp::EventListener { friend Workspace; @@ -66,8 +76,12 @@ class PhysicsEventListener : public rp::EventListener { class DEF_INST_SERVICE_(explorer_icon="workspace") Workspace : public Service { AUTOGEN_PREAMBLE + friend PhysicsEventListener; + std::list> simulatedBodies; std::list bodyQueue; + std::queue contactQueue; + std::mutex contactQueueLock; rp::PhysicsWorld* physicsWorld; static rp::PhysicsCommon* physicsCommon; PhysicsEventListener physicsEventListener; @@ -97,6 +111,7 @@ public: 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); }; \ No newline at end of file diff --git a/editor/placedocument.cpp b/editor/placedocument.cpp index 3ed1253..98e9a8d 100644 --- a/editor/placedocument.cpp +++ b/editor/placedocument.cpp @@ -140,6 +140,7 @@ void PlaceDocument::timerEvent(QTimerEvent* evt) { placeWidget->repaint(); placeWidget->updateCycle(); gDataModel->GetService()->RunSleepingThreads(); + gDataModel->GetService()->ProcessContactEvents(); }