rocketsim 0.2.4

Simulate Rocket League games at maximum efficiency
Documentation
use glam::{Affine3A, Vec3A, Vec4};

use super::polyhedral_convex_shape::PolyhedralConvexShape;
use crate::{
    bullet::{
        collision::{
            dispatch::quad_ray_callbacks::{BridgeTriQuadRayCallback, QuadRayResultCallback},
            narrowphase::gjk::calc_time_of_impact,
        },
        linear_math::max_dot,
    },
    shared::{Aabb, QuadRayInfo},
};

pub struct ConvexHullShape {
    polyhedral_convex_shape: PolyhedralConvexShape,
    unscaled_points: Box<[Vec3A]>,
    simd_unscaled_points: Box<[[Vec4; 3]]>,
}

impl ConvexHullShape {
    pub fn new(unscaled_points: Box<[Vec3A]>) -> Self {
        let simd_unscaled_points: Box<_> = unscaled_points
            .as_chunks::<4>()
            .0
            .iter()
            .map(|chunk| {
                [
                    Vec4::new(chunk[0].x, chunk[1].x, chunk[2].x, chunk[3].x),
                    Vec4::new(chunk[0].y, chunk[1].y, chunk[2].y, chunk[3].y),
                    Vec4::new(chunk[0].z, chunk[1].z, chunk[2].z, chunk[3].z),
                ]
            })
            .collect();

        Self {
            polyhedral_convex_shape: PolyhedralConvexShape::new(
                &simd_unscaled_points,
                &unscaled_points,
            ),
            unscaled_points,
            simd_unscaled_points,
        }
    }

    #[inline]
    pub fn local_get_supporting_vertex_without_margin(&self, vec: Vec3A) -> Vec3A {
        max_dot(&self.simd_unscaled_points, &self.unscaled_points, vec)
    }

    pub fn local_get_supporting_vertex(&self, vec: Vec3A) -> Vec3A {
        let mut sup_vertex = self.local_get_supporting_vertex_without_margin(vec);

        debug_assert_ne!(self.polyhedral_convex_shape.get_margin(), 0.0);
        let vec_norm = vec.normalize_or(Vec3A::NEG_ONE);
        sup_vertex += self.polyhedral_convex_shape.get_margin() * vec_norm;

        sup_vertex
    }

    #[inline]
    pub const fn get_margin(&self) -> f32 {
        self.polyhedral_convex_shape.get_margin()
    }

    fn get_ident_aabb_slow(&self) -> Aabb {
        let margin = self.get_margin();

        let mut aabb = Aabb::ZERO;
        for i in 0..3 {
            let mut vec = Vec3A::ZERO;

            vec[i] = 1.0;
            let sv = self.local_get_supporting_vertex(vec);
            aabb.max[i] = sv[i] + margin;

            vec[i] = -1.0;
            let sv = self.local_get_supporting_vertex(vec);
            aabb.min[i] = sv[i] - margin;
        }

        aabb
    }

    #[inline]
    pub const fn get_ident_aabb(&self) -> &Aabb {
        self.polyhedral_convex_shape.get_ident_aabb()
    }

    #[inline]
    pub fn get_aabb(&self, trans: &Affine3A) -> Aabb {
        self.polyhedral_convex_shape.get_aabb(trans)
    }

    pub fn calculate_local_intertia(&self, mass: f32) -> Vec3A {
        let margin = self.polyhedral_convex_shape.get_margin();

        let aabb = self.get_ident_aabb_slow();
        let half_extents = (aabb.max - aabb.min) * 0.5;

        let l = 2.0 * (half_extents + margin);
        let l2 = l * l;
        let scaled_mass = mass * 0.083_333_33;

        scaled_mass * Vec3A::new(l2.y + l2.z, l2.x + l2.z, l2.x + l2.y)
    }

    pub fn perform_quad_raycast<T: QuadRayResultCallback>(
        &self,
        result_callback: &mut BridgeTriQuadRayCallback<'_, T>,
        ray_info: &QuadRayInfo<'_>,
    ) {
        let hull_aabb = self.get_ident_aabb();
        if !ray_info.aabb.intersects(hull_aabb) {
            return;
        }

        let (origins, inv_dirs) = ray_info.calc_pos_dir();
        let mask = QuadRayInfo::intersect_quad_ray_aabb(
            &origins,
            &inv_dirs,
            hull_aabb,
            result_callback.hit_fraction,
        );

        for i in 0..4 {
            if (mask & (1 << i)) == 0 {
                continue;
            }

            self.internal_perform_raycast(
                result_callback,
                ray_info.ray_sources[i],
                ray_info.ray_targets[i],
                i,
            );
        }
    }

    fn internal_perform_raycast<T: QuadRayResultCallback>(
        &self,
        result_callback: &mut BridgeTriQuadRayCallback<'_, T>,
        ray_source: Vec3A,
        ray_target: Vec3A,
        ray_idx: usize,
    ) {
        if let Some(cast_result) = calc_time_of_impact(self, ray_source, ray_target) {
            result_callback.report_hit(cast_result.normal, cast_result.fraction, ray_idx);
        }
    }
}