rust/hwphysics/src/physics.rs
author Pekka Ristola <pekkarr@protonmail.com>
Mon, 27 Jan 2025 19:08:05 +0100
changeset 16067 dbdb98dafd80
parent 15828 44b49f255e31
permissions -rw-r--r--
Add support for ffmpeg 6.0 - Use the new send_frame/receive_packet API for encoding - Use the new channel layout API for audio - Fix audio recording - Copy codec parameters to the stream parameters - Set correct pts for audio frames - Read audio samples from file directly to the refcounted AVFrame buffer instead of the `g_pSamples` buffer - Use global AVPackets allocated with `av_packet_alloc` - Stop trying to write more audio frames when `WriteAudioFrame` fails with a negative error code - Fix segfault with `g_pContainer->url`. The field has to be allocated with `av_malloc` before writing to it. It's set to `NULL` by default. - Properly free allocations with `avcodec_free_context` and `avformat_free_context`

use crate::{
    common::{GearId, Millis},
    data::GearDataManager,
};
use fpnum::*;

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[repr(transparent)]
pub struct PositionData(pub FPPoint);

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[repr(transparent)]
pub struct VelocityData(pub FPPoint);

pub struct AffectedByWind;

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();
    }
}

pub struct PhysicsProcessor {
    gravity: FPNum,
    wind: FPNum,
    position_updates: PositionUpdates,
}

impl PhysicsProcessor {
    pub fn register_components(data: &mut GearDataManager) {
        data.register::<PositionData>();
        data.register::<VelocityData>();
        data.register::<AffectedByWind>();
    }

    pub fn new() -> Self {
        Self {
            gravity: fp!(1 / 10),
            wind: fp!(0),
            position_updates: PositionUpdates::new(64),
        }
    }

    pub fn process(&mut self, data: &mut GearDataManager, time_step: Millis) -> &PositionUpdates {
        if time_step == Millis::new(1) {
            self.process_impl::<true>(data, time_step)
        } else {
            self.process_impl::<false>(data, time_step)
        }
    }

    fn process_impl<const SINGLE_TICK: bool>(
        &mut self,
        data: &mut GearDataManager,
        time_step: Millis,
    ) -> &PositionUpdates {
        let fp_step = if SINGLE_TICK {
            fp!(1)
        } else {
            time_step.to_fixed()
        };
        let gravity = FPPoint::unit_y() * (self.gravity * fp_step);
        let wind = FPPoint::unit_x() * (self.wind * fp_step);

        self.position_updates.clear();

        data.iter()
            .with_tags::<&AffectedByWind>()
            .run(|(vel,): (&mut VelocityData,)| {
                vel.0 += wind;
            });

        data.iter().run_id(
            |gear_id, (pos, vel): (&mut PositionData, &mut VelocityData)| {
                let old_pos = pos.0;
                vel.0 += gravity;
                pos.0 += if SINGLE_TICK { vel.0 } else { vel.0 * fp_step };
                self.position_updates.push(gear_id, &old_pos, &pos.0)
            },
        );

        &self.position_updates
    }
}