Includes a helper function in the Camera component to create rays.
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
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
enum
{
STATIC,
- MOVING
+ MOVING,
+ RAYCAST
};
} // namespace CollisionLayer
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)
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()
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();
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:
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 &);
void ShapeSetup::Loader::init_actions()
{
+ add("raycast_target", &ShapeSetup::raycast_target);
add("render_detail", &ShapeSetup::render_detail);
add("technique_name", &ShapeSetup::technique_name);
}
std::unique_ptr<Geometry::Shape<float, 3>> shape;
std::string technique_name;
unsigned render_detail = 4;
+ bool raycast_target = true;
};
} // namespace Msp::Game
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