]> git.tdb.fi Git - libs/game.git/commitdiff
Add an event for collisions between rigid bodies
authorMikko Rasa <tdb@tdb.fi>
Sun, 13 Apr 2025 13:51:05 +0000 (16:51 +0300)
committerMikko Rasa <tdb@tdb.fi>
Mon, 14 Apr 2025 10:17:49 +0000 (13:17 +0300)
RigidBody now takes a setup with a flag to enable collision reporting.

examples/playground/source/fixture.cpp
examples/playground/source/setups.mgs
examples/playground/source/toy.cpp
source/game/events.h
source/game/physicssystem.cpp
source/game/physicssystem.h
source/game/rigidbody.h
source/game/setups.mgs

index fea4944ec19cdd6e7120b145639c44dab565436b..07ecfe621fae84a9d37384f74b7a264282cc2121 100644 (file)
@@ -5,6 +5,6 @@ using namespace Msp;
 Fixture::Fixture(Game::Handle<Game::Entity> p, const Setup &s, const Game::TransformValues &t):
        Entity(p, t),
        setup(s),
-       rigid_body(this),
+       rigid_body(this, setup.body),
        shape(this, setup.shape)
 { }
index 24fddadd71adf3f01368a3f186d1ab88472f3b57..61a997c328e925935753b24791a8b49007282af8 100644 (file)
@@ -3,9 +3,11 @@ import "msp/game/setups";
 entity Fixture
 {
        field shape Shape;
+       field body RigidBody;
 };
 
 entity Toy
 {
        field shape Shape;
+       field body RigidBody;
 };
index 6ee30240871d685205b4fa2ef9fefb02ca8f5fb1..df5560600aa70b5c0f397c582862a88a5cf4bb3c 100644 (file)
@@ -5,7 +5,7 @@ using namespace Msp;
 Toy::Toy(Game::Handle<Game::Entity> p, const Setup &s, const Game::TransformValues &t):
        Entity(p, t),
        setup(s),
-       rigid_body(this),
+       rigid_body(this, setup.body),
        shape(this, setup.shape),
        motion(this)
 { }
index 82fab2feb1ee032a36cf7a523ac6076400a904ff..65d657c8d027b4b2a3380cc43d81fed4af890e74 100644 (file)
@@ -2,6 +2,7 @@
 #define MSP_GAME_EVENTS_H_
 
 #include <cstdint>
+#include <msp/linal/vector.h>
 #include "handle.h"
 
 namespace Msp::Game {
@@ -9,6 +10,7 @@ namespace Msp::Game {
 class Camera;
 class Component;
 class Entity;
+class Shape;
 class Stage;
 
 namespace Events {
@@ -89,6 +91,13 @@ struct RemotePlayerExited
        std::uint32_t id;
 };
 
+struct Collision
+{
+       Handle<Shape> shape1;
+       Handle<Shape> shape2;
+       LinAl::Vector<float, 3> position;
+};
+
 } // namespace Events
 } // namespace Msp::Game
 
index 5571cd44150755e6e064614f75861a12b8cabdfd..210706cfe7d71c5b79af3ef121334fa6e03c2cb2 100644 (file)
@@ -24,6 +24,7 @@ enum
 
 PhysicsSystem::PhysicsSystem(Stage &s):
        System(s),
+       event_source(stage.get_event_bus()),
        event_observer(stage.get_event_bus()),
        world(stage.get_threads()),
        monitor(event_observer, rigid_bodies)
@@ -33,6 +34,8 @@ PhysicsSystem::PhysicsSystem(Stage &s):
        declare_dependency<RigidBody>(READ_FRESH);
 
        monitor.set_changed_callback([this](auto &b){ simulated_rigid_body_changed(b); });
+
+       world.set_contact_callback([this](const auto &... a){ on_collision(a...); });
 }
 
 RaycastHit PhysicsSystem::cast_ray(const Geometry::Ray<float, 3> &ray)
@@ -73,6 +76,7 @@ void PhysicsSystem::early_tick()
                                Handle<Transform> transform = srb->entity->get_transform();
                                Physics::MotionType motion = (srb->motion ? Physics::DYNAMIC : Physics::STATIC);
                                srb->physics_body = make_unique<Physics::RigidBody>(world, srb->shape->get_shape(), transform->get_position(), transform->get_rotation(), motion);
+                               srb->physics_body->set_report_contacts(srb->body->reports_collisions());
 
                                unsigned layer = (motion==Physics::STATIC ? CollisionLayer::STATIC : CollisionLayer::MOVING);
                                uint32_t collision_mask = (1<<CollisionLayer::STATIC) | (1<<CollisionLayer::MOVING);
@@ -80,7 +84,7 @@ void PhysicsSystem::early_tick()
                                        collision_mask |= 1<<CollisionLayer::RAYCAST;
                                srb->physics_body->set_collisions(layer, collision_mask);
 
-                               if(srb->shape->is_raycast_target())
+                               if(srb->body->reports_collisions() || srb->shape->is_raycast_target())
                                {
                                        auto j = lower_bound_member(collider_lookup, srb->physics_body.get(), &Collider::physics_body);
                                        collider_lookup.emplace(j, srb->physics_body.get(), srb->shape);
@@ -135,6 +139,20 @@ void PhysicsSystem::tick(Time::TimeDelta dt)
                        b.motion_generation = b.motion->get_write_generation();
                }
        }
+
+       for(const Events::Collision &c: collision_queue)
+               event_source.emit<Events::Collision>(c);
+       collision_queue.clear();
+}
+
+void PhysicsSystem::on_collision(const Physics::RigidBody &body1, const Physics::RigidBody &body2, const LinAl::Vector<float, 3> &pos)
+{
+       if(Collider *c1 = lookup_member(collider_lookup, &body1, &Collider::physics_body))
+               if(Collider *c2 = lookup_member(collider_lookup, &body2, &Collider::physics_body))
+               {
+                       MutexLock lock(collision_mutex);
+                       collision_queue.emplace_back(c1->shape, c2->shape, pos);
+               }
 }
 
 } // namespace Msp::Game
index 2365eaa872b499cf54e2639cb46d36924561569a..1826e97668a93a5942041ba2d3c58a9c9eb6cb8b 100644 (file)
@@ -25,6 +25,9 @@ struct RaycastHit
 
 class MSPGAME_API PhysicsSystem: public System
 {
+public:
+       using EventSource = Game::EventSource<Events::Collision>;
+
 private:
        struct SimulatedRigidBody
        {
@@ -45,17 +48,22 @@ private:
 
        struct Collider
        {
-               Physics::RigidBody *physics_body = nullptr;
+               const Physics::RigidBody *physics_body = nullptr;
                Handle<Shape> shape;
        };
 
+       EventSource event_source;
        EventObserver event_observer;
        Physics::World world;
+
        std::vector<SimulatedRigidBody> rigid_bodies;
        std::vector<Collider> collider_lookup;
        std::vector<Handle<Entity>> pending;
        ArchetypeMonitor<SimulatedRigidBody> monitor;
 
+       std::vector<Events::Collision> collision_queue;
+       Mutex collision_mutex;
+
 public:
        PhysicsSystem(Stage &);
 
@@ -67,6 +75,9 @@ private:
 public:
        void early_tick() override;
        void tick(Time::TimeDelta) override;
+
+private:
+       void on_collision(const Physics::RigidBody &, const Physics::RigidBody &, const LinAl::Vector<float, 3> &);
 };
 
 } // namespace Msp::Game
index 1d1a19d203c9e4935ab5ab7169bad89714a21d2e..45d790e3ecca03417aa52844b99a42db07b14f46 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef MSP_GAME_RIGIDBODY_H_
 #define MSP_GAME_RIGIDBODY_H_
 
+#include <msp/game/setups.h>
 #include "component.h"
 #include "mspgame_api.h"
 
@@ -14,10 +15,17 @@ struct RigidBodyData
 class MSPGAME_API RigidBody: public BufferedComponent<RigidBodyData>
 {
 public:
-       RigidBody(Handle<Entity> e): BufferedComponent(e) { }
+       using Setup = RigidBodySetup;
+
+private:
+       const Setup &setup;
+
+public:
+       RigidBody(Handle<Entity> e, const Setup &s): BufferedComponent(e), setup(s) { }
 
        void set_kinematic(bool);
        bool is_kinematic() const { return read().kinematic; }
+       bool reports_collisions() const { return setup.report_collisions; }
 };
 
 } // namespace Msp::Game
index d5ccfa301af5e808ee2a718a69c41c88ed7d4558..b6e7005f0f382877269f72ffbc40aa0b9d65da88 100644 (file)
@@ -54,3 +54,8 @@ component HeightmapTerrain
        field elevation_scale float { default "0.01f"; };
        field technique_name string;
 };
+
+component RigidBody
+{
+       field report_collisions bool { default "false"; };
+};