rust/hwphysics/src/physics.rs
author Wuzzy <Wuzzy2@mail.ru>
Tue, 27 Aug 2019 23:16:42 +0200
changeset 15375 5a934f83d5eb
parent 15287 478d5372eb4a
child 15385 6e3e5be8b2e2
permissions -rw-r--r--
Tempoary (!!!) workaround for incorrect key combination description in frontend This workaround fixes the incorrect string while preserving translations and the 1.0.0 string freeze. Remember to revert this commit and fix the string in binds.cpp after the 1.0.0 release!

use crate::common::{GearData, GearDataLookup, GearDataProcessor, GearId, Millis};
use fpnum::*;

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct PhysicsData {
    pub position: FPPoint,
    pub velocity: FPPoint,
}

impl GearData for PhysicsData {}

impl PhysicsData {
    pub fn new(position: FPPoint, velocity: FPPoint) -> Self {
        Self { position, velocity }
    }
}

pub struct DynamicPhysicsCollection {
    gear_ids: Vec<GearId>,
    positions: Vec<FPPoint>,
    velocities: Vec<FPPoint>,
}

impl DynamicPhysicsCollection {
    fn new() -> Self {
        Self {
            gear_ids: Vec::new(),
            positions: Vec::new(),
            velocities: Vec::new(),
        }
    }

    fn len(&self) -> usize {
        self.gear_ids.len()
    }

    fn push(&mut self, gear_id: GearId, physics: PhysicsData) -> u16 {
        self.gear_ids.push(gear_id);
        self.positions.push(physics.position);
        self.velocities.push(physics.velocity);

        (self.gear_ids.len() - 1) as u16
    }

    fn remove(&mut self, index: usize) -> Option<GearId> {
        self.gear_ids.swap_remove(index);
        self.positions.swap_remove(index);
        self.velocities.swap_remove(index);

        self.gear_ids.get(index).cloned()
    }

    fn iter_pos_update(&mut self) -> impl Iterator<Item = (GearId, (&mut FPPoint, &FPPoint))> {
        self.gear_ids
            .iter()
            .cloned()
            .zip(self.positions.iter_mut().zip(self.velocities.iter()))
    }
}

pub struct StaticPhysicsCollection {
    gear_ids: Vec<GearId>,
    positions: Vec<FPPoint>,
}

impl StaticPhysicsCollection {
    fn new() -> Self {
        Self {
            gear_ids: Vec::new(),
            positions: Vec::new(),
        }
    }

    fn push(&mut self, gear_id: GearId, physics: PhysicsData) -> u16 {
        self.gear_ids.push(gear_id);
        self.positions.push(physics.position);

        (self.gear_ids.len() - 1) as u16
    }

    fn remove(&mut self, index: usize) -> Option<GearId> {
        self.gear_ids.swap_remove(index);
        self.positions.swap_remove(index);

        self.gear_ids.get(index).cloned()
    }
}

pub struct PhysicsProcessor {
    gear_lookup: GearDataLookup<bool>,
    dynamic_physics: DynamicPhysicsCollection,
    static_physics: StaticPhysicsCollection,

    physics_cleanup: Vec<GearId>,
    position_updates: PositionUpdates,
}

pub struct PositionUpdates {
    pub gear_ids: Vec<GearId>,
    pub shifts: Vec<(FPPoint, FPPoint)>,
}

impl PositionUpdates {
    pub fn new(capacity: usize) -> Self {
        Self {
            gear_ids: Vec::with_capacity(capacity),
            shifts: Vec::with_capacity(capacity),
        }
    }

    pub fn push(&mut self, gear_id: GearId, old_position: &FPPoint, new_position: &FPPoint) {
        self.gear_ids.push(gear_id);
        self.shifts.push((*old_position, *new_position));
    }

    pub fn iter(&self) -> impl Iterator<Item = (GearId, &FPPoint, &FPPoint)> {
        self.gear_ids
            .iter()
            .cloned()
            .zip(self.shifts.iter())
            .map(|(id, (from, to))| (id, from, to))
    }

    pub fn clear(&mut self) {
        self.gear_ids.clear();
        self.shifts.clear();
    }
}

impl PhysicsProcessor {
    pub fn new() -> Self {
        Self {
            gear_lookup: GearDataLookup::new(),
            dynamic_physics: DynamicPhysicsCollection::new(),
            static_physics: StaticPhysicsCollection::new(),
            physics_cleanup: Vec::new(),
            position_updates: PositionUpdates::new(0),
        }
    }

    pub fn process(&mut self, time_step: Millis) -> &PositionUpdates {
        let fp_step = time_step.to_fixed();
        self.position_updates.clear();
        for (gear_id, (pos, vel)) in self.dynamic_physics.iter_pos_update() {
            let old_pos = *pos;
            *pos += *vel * fp_step;
            if !vel.is_zero() {
                self.position_updates.push(gear_id, &old_pos, pos)
            } else {
                self.physics_cleanup.push(gear_id)
            }
        }
        &self.position_updates
    }
}

impl GearDataProcessor<PhysicsData> for PhysicsProcessor {
    fn add(&mut self, gear_id: GearId, gear_data: PhysicsData) {
        let is_dynamic = !gear_data.velocity.is_zero();
        let index = if is_dynamic {
            self.dynamic_physics.push(gear_id, gear_data)
        } else {
            self.static_physics.push(gear_id, gear_data)
        };

        self.gear_lookup.add(gear_id, index, is_dynamic);
    }

    fn remove(&mut self, gear_id: GearId) {
        if let Some(entry) = self.gear_lookup.get(gear_id) {
            let relocated_gear_id = if *entry.value() {
                self.dynamic_physics.remove(entry.index() as usize)
            } else {
                self.static_physics.remove(entry.index() as usize)
            };

            if let Some(id) = relocated_gear_id {
                let index = entry.index();
                self.gear_lookup[id].set_index(index);
            }
        }
    }

    fn get(&mut self, gear_id: GearId) -> Option<PhysicsData> {
        if let Some(entry) = self.gear_lookup.get(gear_id) {
            let data = if *entry.value() {
                PhysicsData {
                    position: self.dynamic_physics.positions[entry.index() as usize],
                    velocity: self.dynamic_physics.velocities[entry.index() as usize],
                }
            } else {
                PhysicsData {
                    position: self.static_physics.positions[entry.index() as usize],
                    velocity: FPPoint::zero(),
                }
            };
            Some(data)
        } else {
            None
        }
    }
}