]> git.tdb.fi Git - libs/game.git/commitdiff
Add raycast functionality
authorMikko Rasa <tdb@tdb.fi>
Sun, 30 Mar 2025 12:35:35 +0000 (15:35 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sun, 30 Mar 2025 12:35:35 +0000 (15:35 +0300)
Includes a helper function in the Camera component to create rays.

source/game/camera.cpp
source/game/camera.h
source/game/physicssystem.cpp
source/game/physicssystem.h
source/game/setups_special.cpp
source/game/setups_special.h
source/game/shape.h

index 02880cec8d3a223691f785b02aa183ec96f77c3c..927b620501e9cf5a7453de0fb04537e41602976a 100644 (file)
@@ -37,4 +37,16 @@ void Camera::set_size(const LinAl::Vector<float, 2> &s)
        d.size = s;
 }
 
+Geometry::Ray<float, 3> Camera::create_ray(const LinAl::Vector<float, 2> &ndc) const
+{
+       const Data &d = read();
+       if(d.is_orthographic())
+               return {{ d.size.x*ndc.x/2.0f, d.size.y*ndc.y/2.0f, d.near_clip }, { 0.0f, 0.0f, -1.0f }};
+       else
+       {
+               float hh = tan(d.fov_y/2.0f);
+               return {{ 0.0f, 0.0f, 0.0f }, { d.get_aspect()*ndc.x*hh, ndc.y*hh, -1.0f }};
+       }
+}
+
 } // namespace Msp::Game
index 0d817c6f6a98998e38a771b76c27dde881ac1e76..90a573b97b744551433a86d741f2fd1bdded0943 100644 (file)
@@ -43,6 +43,8 @@ public:
        float get_near_clip() const { return read().near_clip; }
        float get_far_clip() const { return read().far_clip; }
        CameraScaling get_scaling() const { return setup.scaling; }
+
+       Geometry::Ray<float, 3> create_ray(const LinAl::Vector<float, 2> &) const;
 };
 
 } // namespace Msp::Game
index 621b9d1b74c6fbeff269076ef934ede4500ad7d1..32f71e2da0c3a318a18a34956fa5c1c23f77144d 100644 (file)
@@ -15,7 +15,8 @@ namespace CollisionLayer {
 enum
 {
        STATIC,
-       MOVING
+       MOVING,
+       RAYCAST
 };
 
 } // namespace CollisionLayer
@@ -33,6 +34,19 @@ PhysicsSystem::PhysicsSystem(Stage &s):
        monitor.set_changed_callback([this](auto &b){ simulated_rigid_body_changed(b); });
 }
 
+RaycastHit PhysicsSystem::cast_ray(const Geometry::Ray<float, 3> &ray)
+{
+       Physics::RaycastHit result = world.cast_ray(ray, static_cast<unsigned>(CollisionLayer::RAYCAST));
+       if(!result.body)
+               return {};
+
+       auto i = lower_bound_member(collider_lookup, result.body, &Collider::physics_body);
+       if(i==collider_lookup.end() || i->physics_body!=result.body)
+               return {};
+
+       return { i->shape, result.distance, result.position };
+}
+
 void PhysicsSystem::simulated_rigid_body_changed(SimulatedRigidBody &body)
 {
        if(!body.physics_body)
@@ -41,6 +55,12 @@ void PhysicsSystem::simulated_rigid_body_changed(SimulatedRigidBody &body)
                if(i==pending.end() || *i!=body.entity)
                        pending.insert(i, body.entity);
        }
+       else if(!body.body)
+       {
+               auto i = lower_bound_member(collider_lookup, body.physics_body.get(), &Collider::physics_body);
+               if(i!=collider_lookup.end() && i->physics_body==body.physics_body.get())
+                       collider_lookup.erase(i);
+       }
 }
 
 void PhysicsSystem::early_tick()
@@ -58,7 +78,11 @@ void PhysicsSystem::early_tick()
                        i->physics_body = make_unique<Physics::RigidBody>(world, i->shape->get_shape(), transform->get_position(), transform->get_rotation(), motion);
                        unsigned layer = (motion==Physics::STATIC ? CollisionLayer::STATIC : CollisionLayer::MOVING);
                        uint32_t collision_mask = (1<<CollisionLayer::STATIC) | (1<<CollisionLayer::MOVING);
+                       if(i->shape->is_raycast_target())
+                               collision_mask |= 1<<CollisionLayer::RAYCAST;
                        i->physics_body->set_collisions(layer, collision_mask);
+                       auto j = lower_bound_member(collider_lookup, i->physics_body.get(), &Collider::physics_body);
+                       collider_lookup.emplace(j, i->physics_body.get(), i->shape);
                }
        }
        pending.clear();
index 6ac254ec67556cc24dccf47e657bf972afdcb96a..5c342082e7e3d5ca60c87b1d99520fb70ba56bf1 100644 (file)
@@ -14,6 +14,15 @@ class Motion;
 class RigidBody;
 class Shape;
 
+struct RaycastHit
+{
+       Handle<Shape> shape;
+       float distance = 0.0f;
+       LinAl::Vector<float, 3> position;
+
+       explicit operator bool() const { return static_cast<bool>(shape); }
+};
+
 class MSPGAME_API PhysicsSystem: public System
 {
 private:
@@ -32,15 +41,24 @@ private:
                SimulatedRigidBody(Handle<Entity> e): entity(e) { }
        };
 
+       struct Collider
+       {
+               Physics::RigidBody *physics_body = nullptr;
+               Handle<Shape> shape;
+       };
+
        EventObserver event_observer;
        Physics::World world;
        std::vector<SimulatedRigidBody> rigid_bodies;
+       std::vector<Collider> collider_lookup;
        std::vector<Handle<Entity>> pending;
        ArchetypeMonitor<SimulatedRigidBody> monitor;
 
 public:
        PhysicsSystem(Stage &);
 
+       RaycastHit cast_ray(const Geometry::Ray<float, 3> &);
+
 private:
        void simulated_rigid_body_changed(SimulatedRigidBody &);
 
index 2f7075bde894cb977ebe9fc79f97399fba0c96d2..0421539b5338fe1210325c7e8d3fbb2adcf2578f 100644 (file)
@@ -13,6 +13,7 @@ ShapeSetup::Loader::Loader(ShapeSetup &s):
 
 void ShapeSetup::Loader::init_actions()
 {
+       add("raycast_target", &ShapeSetup::raycast_target);
        add("render_detail", &ShapeSetup::render_detail);
        add("technique_name", &ShapeSetup::technique_name);
 }
index 1674a7ca208027d767e1adf90991a0a106e882fa..24264935bc8e3c35d5e71b535e491f2df84c1222 100644 (file)
@@ -27,6 +27,7 @@ struct MSPGAME_API ShapeSetup
        std::unique_ptr<Geometry::Shape<float, 3>> shape;
        std::string technique_name;
        unsigned render_detail = 4;
+       bool raycast_target = true;
 };
 
 } // namespace Msp::Game
index 384c04753e84164dbd5cb2a7670c994908cf3655..c941746e94cd212983cb625e7f5a56e7dc7d4b0d 100644 (file)
@@ -21,6 +21,7 @@ public:
        const Geometry::Shape<float, 3> &get_shape() const { return *setup.shape; }
        const std::string &get_technique_name() const { return setup.technique_name; }
        unsigned get_render_detail() const { return setup.render_detail; }
+       bool is_raycast_target() const { return setup.raycast_target; }
 };
 
 } // namespace Msp::Game