fix(workspace): do not fire signals from physics thread as lua has to always be executed from main thread

This commit is contained in:
maelstrom 2025-07-13 18:37:49 +02:00
parent 0ded4ac7fb
commit dca5c483c4
3 changed files with 45 additions and 10 deletions

View file

@ -29,25 +29,27 @@ 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<Part*>(pair.getBody1()->getUserData())->shared<Part>();
auto part1 = reinterpret_cast<Part*>(pair.getBody2()->getUserData())->shared<Part>();
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<Part*>(pair.getBody1()->getUserData())->shared<Part>();
contact.part1 = reinterpret_cast<Part*>(pair.getBody2()->getUserData())->shared<Part>();
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();
@ -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) {
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> part) {
if (globalPhysicsLock.try_lock()) {
updatePartPhysics(part);

View file

@ -7,6 +7,7 @@
#include <list>
#include <memory>
#include <mutex>
#include <queue>
#include <reactphysics3d/body/RigidBody.h>
#include <reactphysics3d/engine/EventListener.h>
#include <reactphysics3d/engine/PhysicsCommon.h>
@ -52,6 +53,15 @@ struct QueueItem {
} action;
};
struct ContactItem {
std::shared_ptr<Part> part0;
std::shared_ptr<Part> 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<std::shared_ptr<Part>> simulatedBodies;
std::list<QueueItem> bodyQueue;
std::queue<ContactItem> 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<const RaycastResult> CastRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional<RaycastFilter> filter = std::nullopt, unsigned short categoryMaskBits = 0xFFFF);
};

View file

@ -140,6 +140,7 @@ void PlaceDocument::timerEvent(QTimerEvent* evt) {
placeWidget->repaint();
placeWidget->updateCycle();
gDataModel->GetService<ScriptContext>()->RunSleepingThreads();
gDataModel->GetService<Workspace>()->ProcessContactEvents();
}