]> git.tdb.fi Git - libs/game.git/commitdiff
Basic integration of physics
authorMikko Rasa <tdb@tdb.fi>
Sat, 15 Mar 2025 17:36:59 +0000 (19:36 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sat, 15 Mar 2025 17:38:38 +0000 (19:38 +0200)
Build
source/game/director.cpp
source/game/director.h
source/game/motion.cpp [new file with mode: 0644]
source/game/motion.h [new file with mode: 0644]
source/game/physicssystem.cpp [new file with mode: 0644]
source/game/physicssystem.h [new file with mode: 0644]
source/game/rigidbody.h [new file with mode: 0644]
source/game/stage.cpp
source/game/stage.h

diff --git a/Build b/Build
index f32bc01fe935deec920561d844a7f1a90b748b55..9b836bbec9367fffc2029fe7916e88e0d663f470 100644 (file)
--- a/Build
+++ b/Build
@@ -16,6 +16,7 @@ package "mspgame"
        {
                source "source/game";
                require "mspnet";
+               require "mspphysics";
                install true;
                install_map
                {
index 53c54547b87c6356a78964ae2f7b8388c4fb0ca0..d2e02065f42fe15331a911625ca92d594a811360 100644 (file)
@@ -30,7 +30,7 @@ Director::~Director()
 
 Stage &Director::create_stage(const string &n)
 {
-       Stage &stage = *stages.emplace_back(std::make_unique<Stage>(n, reflector, resources));
+       Stage &stage = *stages.emplace_back(std::make_unique<Stage>(n, thread_pool, reflector, resources));
        stage.add_system<TransformPropagator>();
        event_source.emit<Events::StageCreated>(stage);
        return stage;
index 9ae322cda6d47c3dbcefb42500c80d4d6a0d5d0c..c8c7710e1bb7c55ca7b66a4b8f88153f9c3fa781 100644 (file)
@@ -4,6 +4,7 @@
 #include <memory>
 #include <optional>
 #include <vector>
+#include <msp/core/threadpool.h>
 #include <msp/datafile/collection.h>
 #include <msp/io/eventdispatcher.h>
 #include <msp/time/timedelta.h>
@@ -29,6 +30,7 @@ private:
        std::optional<AccessGuard> access_guard;
        Reflection::Reflector reflector;
        DataFile::Collection &resources;
+       ThreadPool thread_pool;
        IO::EventDispatcher io_dispatcher;
        EventBus event_bus;
        EventSource event_source;
diff --git a/source/game/motion.cpp b/source/game/motion.cpp
new file mode 100644 (file)
index 0000000..c04f63a
--- /dev/null
@@ -0,0 +1,20 @@
+#include "motion.h"
+
+namespace Msp::Game {
+
+void Motion::set_values(const MotionValues &v)
+{
+       write() = v;
+}
+
+void Motion::set_linear_velocity(const LinAl::Vector<float, 3> &v)
+{
+       write().linear_velocity = v;
+}
+
+void Motion::set_angular_velocity(const LinAl::Vector<float, 3> &v)
+{
+       write().angular_velocity = v;
+}
+
+} // namespace Msp::Game
diff --git a/source/game/motion.h b/source/game/motion.h
new file mode 100644 (file)
index 0000000..0c58471
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef MSP_GAME_MOTION_H_
+#define MSP_GAME_MOTION_H_
+
+#include <msp/linal/vector.h>
+#include "component.h"
+
+namespace Msp::Game {
+
+struct MSPGAME_API MotionValues
+{
+       LinAl::Vector<float, 3> linear_velocity;
+       LinAl::Vector<float, 3> angular_velocity;
+};
+
+class MSPGAME_API Motion: public BufferedComponent<MotionValues>
+{
+public:
+       Motion(Handle<Entity> e): BufferedComponent(e) { }
+
+       void set_values(const MotionValues &);
+       void set_linear_velocity(const LinAl::Vector<float, 3> &);
+       void set_angular_velocity(const LinAl::Vector<float, 3> &);
+       const MotionValues &get_values() const { return read(); }
+       const LinAl::Vector<float, 3> &get_linear_velocity() const { return read().linear_velocity; }
+       const LinAl::Vector<float, 3> &get_angular_velocity() const { return read().angular_velocity; }
+};
+
+} // namespace Msp::Game
+
+#endif
diff --git a/source/game/physicssystem.cpp b/source/game/physicssystem.cpp
new file mode 100644 (file)
index 0000000..4afb09c
--- /dev/null
@@ -0,0 +1,88 @@
+#include "physicssystem.h"
+#include "entity.h"
+#include "motion.h"
+#include "rigidbody.h"
+#include "shape.h"
+#include "transform.h"
+#include "transformpropagator.h"
+
+using namespace std;
+
+namespace Msp::Game {
+
+PhysicsSystem::PhysicsSystem(Stage &s):
+       System(s),
+       event_observer(stage.get_event_bus()),
+       world(stage.get_threads()),
+       monitor(event_observer, rigid_bodies)
+{
+       declare_dependency<Transform>(CHAINED_UPDATE);
+       declare_dependency<Motion>(CHAINED_UPDATE);
+       declare_dependency<TransformPropagator>(RUN_BEFORE);
+
+       monitor.set_changed_callback([this](auto &b){ simulated_rigid_body_changed(b); });
+}
+
+void PhysicsSystem::simulated_rigid_body_changed(SimulatedRigidBody &body)
+{
+       if(!body.physics_body)
+       {
+               auto i = lower_bound(pending, body.entity);
+               if(i==pending.end() || *i!=body.entity)
+                       pending.insert(i, body.entity);
+       }
+}
+
+void PhysicsSystem::early_tick()
+{
+       for(Handle<Entity> e: pending)
+       {
+               auto i = lower_bound_member(rigid_bodies, e, &SimulatedRigidBody::entity);
+               if(i==rigid_bodies.end() || i->entity!=e)
+                       continue;
+
+               if(i->body && i->shape && !i->physics_body)
+               {
+                       Handle<Transform> transform = i->entity->get_transform();
+                       Physics::MotionType motion = (i->motion ? Physics::DYNAMIC : Physics::STATIC);
+                       i->physics_body = make_unique<Physics::RigidBody>(world, i->shape->get_shape(), transform->get_position(), transform->get_rotation(), motion);
+               }
+       }
+       pending.clear();
+}
+
+void PhysicsSystem::tick(Time::TimeDelta dt)
+{
+       for(SimulatedRigidBody &b: rigid_bodies)
+       {
+               Handle<Transform> transform = b.entity->get_transform();
+               if(transform->get_read_generation()!=b.transform_generation)
+               {
+                       const TransformValues &tv = transform->get_values();
+                       b.physics_body->set_transform(tv.position, tv.rotation);
+               }
+               if(b.motion && b.motion->get_read_generation()!=b.motion_generation)
+               {
+                       const MotionValues &mv = b.motion->get_values();
+                       b.physics_body->set_velocities(mv.linear_velocity, mv.angular_velocity);
+               }
+       }
+
+       world.step(dt/Time::sec);
+
+       for(SimulatedRigidBody &b: rigid_bodies)
+       {
+               Handle<Transform> transform = b.entity->get_transform();
+               transform->set_position(b.physics_body->get_position());
+               transform->set_rotation(b.physics_body->get_rotation());
+               b.transform_generation = transform->get_write_generation();
+               if(b.motion)
+               {
+                       b.motion->set_linear_velocity(b.physics_body->get_linear_velocity());
+                       b.motion->set_angular_velocity(b.physics_body->get_angular_velocity());
+                       b.motion_generation = b.motion->get_write_generation();
+               }
+       }
+}
+
+} // namespace Msp::Game
diff --git a/source/game/physicssystem.h b/source/game/physicssystem.h
new file mode 100644 (file)
index 0000000..6ac254e
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef MSP_GAME_PHYSICSSYSTEM_H_
+#define MSP_GAME_PHYSICSSYSTEM_H_
+
+#include <msp/physics/rigidbody.h>
+#include <msp/physics/world.h>
+#include "archetype.h"
+#include "mspgame_api.h"
+#include "system.h"
+
+namespace Msp::Game {
+
+class Entity;
+class Motion;
+class RigidBody;
+class Shape;
+
+class MSPGAME_API PhysicsSystem: public System
+{
+private:
+       struct SimulatedRigidBody
+       {
+               Handle<Entity> entity;
+               Handle<RigidBody> body;
+               Handle<Shape> shape;
+               Handle<Motion> motion;
+               std::unique_ptr<Physics::RigidBody> physics_body;
+               uint8_t transform_generation = 0;
+               uint8_t motion_generation = 0;
+
+               using Components = ArchetypeComponents<&SimulatedRigidBody::body, &SimulatedRigidBody::shape, &SimulatedRigidBody::motion>;
+
+               SimulatedRigidBody(Handle<Entity> e): entity(e) { }
+       };
+
+       EventObserver event_observer;
+       Physics::World world;
+       std::vector<SimulatedRigidBody> rigid_bodies;
+       std::vector<Handle<Entity>> pending;
+       ArchetypeMonitor<SimulatedRigidBody> monitor;
+
+public:
+       PhysicsSystem(Stage &);
+
+private:
+       void simulated_rigid_body_changed(SimulatedRigidBody &);
+
+public:
+       void early_tick() override;
+       void tick(Time::TimeDelta) override;
+};
+
+} // namespace Msp::Game
+
+#endif
diff --git a/source/game/rigidbody.h b/source/game/rigidbody.h
new file mode 100644 (file)
index 0000000..4de6738
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef MSP_GAME_RIGIDBODY_H_
+#define MSP_GAME_RIGIDBODY_H_
+
+#include "component.h"
+#include "mspgame_api.h"
+
+namespace Msp::Game {
+
+class MSPGAME_API RigidBody: public Component
+{
+public:
+       RigidBody(Handle<Entity> e): Component(e) { }
+};
+
+} // namespace Msp::Game
+
+#endif
index 5a1e90433c52b13fde20a01e117ae7da0359eafd..530811bd47ad936464d3445255b659ec5b30b578 100644 (file)
@@ -10,8 +10,9 @@ using namespace std;
 
 namespace Msp::Game {
 
-Stage::Stage(const string &n, Reflection::Reflector &f, DataFile::Collection &r):
+Stage::Stage(const string &n, ThreadPool &t, Reflection::Reflector &f, DataFile::Collection &r):
        name(n),
+       threads(t),
        reflector(f),
        resources(r),
        pools(reflector),
index 8e98d76482a2262c50ff3fac708f99de7f16fd79..b6a8c08a6ec47447e497ae2f15abe6d06f6dfb87 100644 (file)
@@ -2,6 +2,7 @@
 #define MSP_GAME_STAGE_H_
 
 #include <memory>
+#include <msp/core/threadpool.h>
 #include <msp/datafile/collection.h>
 #include <msp/time/timedelta.h>
 #include "eventbus.h"
@@ -28,6 +29,7 @@ public:
 
 private:
        std::string name;
+       ThreadPool &threads;
        Reflection::Reflector &reflector;
        DataFile::Collection &resources;
        PoolPool pools;
@@ -43,10 +45,11 @@ private:
        bool pending_reschedule = false;
 
 public:
-       Stage(const std::string &, Reflection::Reflector &, DataFile::Collection &);
+       Stage(const std::string &, ThreadPool &, Reflection::Reflector &, DataFile::Collection &);
        ~Stage();
 
        const std::string &get_name() const { return name; }
+       ThreadPool &get_threads() const { return threads; }
        Reflection::Reflector &get_reflector() const { return reflector; }
        DataFile::Collection &get_resources() const { return resources; }
        PoolPool &get_pools() { return pools; }