rust/hwphysics/src/lib.rs
changeset 14080 c6745a1c827a
child 14165 165e43c3ed59
equal deleted inserted replaced
14079:fa2ed06c2f55 14080:c6745a1c827a
       
     1 use fpnum::*;
       
     2 use integral_geometry::{
       
     3     Point, Size, GridIndex
       
     4 };
       
     5 
       
     6 type Index = u16;
       
     7 
       
     8 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
       
     9 struct PhysicsData {
       
    10     position: FPPoint,
       
    11     velocity: FPPoint,
       
    12 }
       
    13 
       
    14 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
       
    15 struct CollisionData {
       
    16     bounds: CircleBounds
       
    17 }
       
    18 
       
    19 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
       
    20 struct ContactData {
       
    21     elasticity: FPNum,
       
    22     friction: FPNum
       
    23 }
       
    24 
       
    25 pub struct PhysicsCollection {
       
    26     positions: Vec<FPPoint>,
       
    27     velocities: Vec<FPPoint>
       
    28 }
       
    29 
       
    30 impl PhysicsCollection {
       
    31     fn push(&mut self, data: PhysicsData) {
       
    32         self.positions.push(data.position);
       
    33         self.velocities.push(data.velocity);
       
    34     }
       
    35 
       
    36     fn iter_mut_pos(&mut self) -> impl Iterator<Item = (&mut FPPoint, &FPPoint)> {
       
    37         self.positions.iter_mut().zip(self.velocities.iter())
       
    38     }
       
    39 }
       
    40 
       
    41 pub struct JoinedData {
       
    42     physics: PhysicsData,
       
    43     collision: CollisionData,
       
    44     contact: ContactData
       
    45 }
       
    46 
       
    47 pub struct World {
       
    48     enabled_physics: PhysicsCollection,
       
    49     disabled_physics: Vec<PhysicsData>,
       
    50 
       
    51     collision: Vec<CollisionData>,
       
    52     grid: Grid,
       
    53 
       
    54     physics_cleanup: Vec<PhysicsData>,
       
    55     collision_output: Vec<(Index, Index)>
       
    56 }
       
    57 
       
    58 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
       
    59 struct CircleBounds {
       
    60     center: FPPoint,
       
    61     radius: FPNum
       
    62 }
       
    63 
       
    64 impl CircleBounds {
       
    65     fn intersects(&self, other: &CircleBounds) -> bool {
       
    66         (other.center - self.center).is_in_range(self.radius + other.radius)
       
    67     }
       
    68 }
       
    69 
       
    70 fn fppoint_round(point: &FPPoint) -> Point {
       
    71     Point::new(point.x().round() as i32, point.y().round() as i32)
       
    72 }
       
    73 
       
    74 struct GridBin {
       
    75     refs: Vec<Index>,
       
    76     static_entries: Vec<CircleBounds>,
       
    77     dynamic_entries: Vec<CircleBounds>
       
    78 }
       
    79 
       
    80 impl GridBin {
       
    81     fn new() -> Self {
       
    82         Self {
       
    83             refs: vec![],
       
    84             static_entries: vec![],
       
    85             dynamic_entries: vec![]
       
    86         }
       
    87     }
       
    88 }
       
    89 
       
    90 const GRID_BIN_SIZE: usize = 256;
       
    91 
       
    92 struct Grid {
       
    93     bins: Vec<GridBin>,
       
    94     space_size: Size,
       
    95     bins_count: Size,
       
    96     index: GridIndex
       
    97 }
       
    98 
       
    99 impl Grid {
       
   100     fn new(size: Size) -> Self {
       
   101         assert!(size.is_power_of_two());
       
   102         let bins_count =
       
   103             Size::new(size.width / GRID_BIN_SIZE,
       
   104                       size.height / GRID_BIN_SIZE);
       
   105 
       
   106         Self {
       
   107             bins: (0..bins_count.area()).map(|_| GridBin::new()).collect(),
       
   108             space_size: size,
       
   109             bins_count,
       
   110             index: Size::square(GRID_BIN_SIZE).to_grid_index()
       
   111         }
       
   112     }
       
   113 
       
   114     fn bin_index(&self, position: &FPPoint) -> Point {
       
   115         self.index.map(fppoint_round(position))
       
   116     }
       
   117 
       
   118     fn lookup_bin(&mut self, position: &FPPoint) -> &mut GridBin {
       
   119         let index = self.bin_index(position);
       
   120         &mut self.bins[index.x as usize * self.bins_count.width + index.y as usize]
       
   121     }
       
   122 
       
   123     fn insert_static(&mut self, index: Index, position: &FPPoint, bounds: &CircleBounds) {
       
   124         self.lookup_bin(position).static_entries.push(*bounds)
       
   125     }
       
   126 
       
   127     fn insert_dynamic(&mut self, index: Index, position: &FPPoint, bounds: &CircleBounds) {
       
   128         self.lookup_bin(position).dynamic_entries.push(*bounds)
       
   129     }
       
   130 
       
   131     fn check_collisions(&self, collisions: &mut Vec<(Index, Index)>) {
       
   132         for bin in &self.bins {
       
   133             for bounds in &bin.dynamic_entries {
       
   134                 for other in &bin.dynamic_entries {
       
   135                     if bounds.intersects(other) && bounds != other {
       
   136                         collisions.push((0, 0))
       
   137                     }
       
   138                 }
       
   139 
       
   140                 for other in &bin.static_entries {
       
   141                     if bounds.intersects(other) {
       
   142                         collisions.push((0, 0))
       
   143                     }
       
   144                 }
       
   145             }
       
   146         }
       
   147     }
       
   148 }
       
   149 
       
   150 impl World {
       
   151     pub fn step(&mut self, time_step: FPNum) {
       
   152         for (pos, vel) in self.enabled_physics.iter_mut_pos() {
       
   153             *pos += *vel
       
   154         }
       
   155 
       
   156         self.grid.check_collisions(&mut self.collision_output);
       
   157     }
       
   158 
       
   159     pub fn add_gear(&mut self, data: JoinedData) {
       
   160         if data.physics.velocity == FPPoint::zero() {
       
   161             self.disabled_physics.push(data.physics);
       
   162             self.grid.insert_static(0, &data.physics.position, &data.collision.bounds);
       
   163         } else {
       
   164             self.enabled_physics.push(data.physics);
       
   165             self.grid.insert_dynamic(0, &data.physics.position, &data.collision.bounds);
       
   166         }
       
   167     }
       
   168 }
       
   169 
       
   170 #[cfg(test)]
       
   171 mod tests {
       
   172 
       
   173 }