@@ -32,13 +32,72 @@ pub use rigid_body_2d::{
3232 RigidBodyType ,
3333} ;
3434
35+ const DEFAULT_COLLISION_FILTER_GROUP : u32 = u32:: MAX ;
36+ const DEFAULT_COLLISION_FILTER_MASK : u32 = u32:: MAX ;
3537const DEFAULT_GRAVITY_X : f32 = 0.0 ;
3638const DEFAULT_GRAVITY_Y : f32 = -9.81 ;
3739const DEFAULT_TIMESTEP_SECONDS : f32 = 1.0 / 60.0 ;
3840const DEFAULT_SUBSTEPS : u32 = 1 ;
3941
4042static NEXT_WORLD_ID : AtomicU64 = AtomicU64 :: new ( 1 ) ;
4143
44+ /// Indicates whether a collision pair started or ended contact this step.
45+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
46+ pub enum CollisionEventKind {
47+ /// The two bodies started touching during the most recent simulation step.
48+ Started ,
49+ /// The two bodies stopped touching during the most recent simulation step.
50+ Ended ,
51+ }
52+
53+ /// Describes a body pair collision observed during simulation stepping.
54+ #[ derive( Debug , Clone , Copy , PartialEq ) ]
55+ pub struct CollisionEvent {
56+ /// The event transition kind for this body pair.
57+ pub kind : CollisionEventKind ,
58+ /// The first body participating in the collision pair.
59+ pub body_a : RigidBody2D ,
60+ /// The second body participating in the collision pair.
61+ pub body_b : RigidBody2D ,
62+ /// The representative contact point, when available.
63+ pub contact_point : Option < [ f32 ; 2 ] > ,
64+ /// The representative contact normal, when available.
65+ pub normal : Option < [ f32 ; 2 ] > ,
66+ /// The representative penetration depth, when available.
67+ pub penetration : Option < f32 > ,
68+ }
69+
70+ /// Configures collider collision group and mask bitfields.
71+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
72+ pub struct CollisionFilter {
73+ /// The membership bitfield for this collider.
74+ pub group : u32 ,
75+ /// The bitfield describing which groups this collider can collide with.
76+ pub mask : u32 ,
77+ }
78+
79+ impl Default for CollisionFilter {
80+ fn default ( ) -> Self {
81+ return Self {
82+ group : DEFAULT_COLLISION_FILTER_GROUP ,
83+ mask : DEFAULT_COLLISION_FILTER_MASK ,
84+ } ;
85+ }
86+ }
87+
88+ /// Describes the nearest body hit by a ray query.
89+ #[ derive( Debug , Clone , Copy , PartialEq ) ]
90+ pub struct RaycastHit {
91+ /// The rigid body hit by the ray.
92+ pub body : RigidBody2D ,
93+ /// The world-space hit position.
94+ pub point : [ f32 ; 2 ] ,
95+ /// The world-space hit normal.
96+ pub normal : [ f32 ; 2 ] ,
97+ /// The non-negative distance from the origin to the hit point.
98+ pub distance : f32 ,
99+ }
100+
42101/// A 2D physics simulation world.
43102pub struct PhysicsWorld2D {
44103 world_id : u64 ,
@@ -82,6 +141,55 @@ impl PhysicsWorld2D {
82141 pub fn timestep_seconds ( & self ) -> f32 {
83142 return self . timestep_seconds ;
84143 }
144+
145+ /// Returns collision events collected during the most recent step.
146+ ///
147+ /// # Returns
148+ /// Returns an iterator over collision events emitted by the world.
149+ pub fn collision_events ( & self ) -> impl Iterator < Item = CollisionEvent > {
150+ return std:: iter:: empty ( ) ;
151+ }
152+
153+ /// Returns all bodies whose colliders contain the provided point.
154+ ///
155+ /// # Arguments
156+ /// - `point`: The world-space point to test.
157+ ///
158+ /// # Returns
159+ /// Returns a vector of matching rigid body handles.
160+ pub fn query_point ( & self , _point : [ f32 ; 2 ] ) -> Vec < RigidBody2D > {
161+ return Vec :: new ( ) ;
162+ }
163+
164+ /// Returns all bodies whose colliders overlap the provided axis-aligned box.
165+ ///
166+ /// # Arguments
167+ /// - `min`: The minimum world-space corner of the query box.
168+ /// - `max`: The maximum world-space corner of the query box.
169+ ///
170+ /// # Returns
171+ /// Returns a vector of matching rigid body handles.
172+ pub fn query_aabb ( & self , _min : [ f32 ; 2 ] , _max : [ f32 ; 2 ] ) -> Vec < RigidBody2D > {
173+ return Vec :: new ( ) ;
174+ }
175+
176+ /// Returns the nearest rigid body hit by the provided ray.
177+ ///
178+ /// # Arguments
179+ /// - `origin`: The ray origin in world space.
180+ /// - `dir`: The ray direction in world space.
181+ /// - `max_dist`: The maximum query distance.
182+ ///
183+ /// # Returns
184+ /// Returns the nearest hit, if one exists.
185+ pub fn raycast (
186+ & self ,
187+ _origin : [ f32 ; 2 ] ,
188+ _dir : [ f32 ; 2 ] ,
189+ _max_dist : f32 ,
190+ ) -> Option < RaycastHit > {
191+ return None ;
192+ }
85193}
86194
87195/// Builder for `PhysicsWorld2D`.
@@ -404,4 +512,28 @@ mod tests {
404512
405513 return ;
406514 }
515+
516+ /// Exposes stable defaults for collider collision filtering.
517+ #[ test]
518+ fn collision_filter_default_collides_with_all_groups ( ) {
519+ let filter = CollisionFilter :: default ( ) ;
520+
521+ assert_eq ! ( filter. group, u32 :: MAX ) ;
522+ assert_eq ! ( filter. mask, u32 :: MAX ) ;
523+
524+ return ;
525+ }
526+
527+ /// Returns empty query and event results for an empty world.
528+ #[ test]
529+ fn empty_world_collision_queries_return_no_results ( ) {
530+ let world = PhysicsWorld2DBuilder :: new ( ) . build ( ) . unwrap ( ) ;
531+
532+ assert_eq ! ( world. collision_events( ) . count( ) , 0 ) ;
533+ assert ! ( world. query_point( [ 0.0 , 0.0 ] ) . is_empty( ) ) ;
534+ assert ! ( world. query_aabb( [ -1.0 , -1.0 ] , [ 1.0 , 1.0 ] ) . is_empty( ) ) ;
535+ assert_eq ! ( world. raycast( [ 0.0 , 0.0 ] , [ 1.0 , 0.0 ] , 10.0 ) , None ) ;
536+
537+ return ;
538+ }
407539}
0 commit comments