Skip to main content

traaains/shape/
meshes.rs

1//! Implementation of the [Meshable] trait for different shapes.
2
3use bevy::{
4    asset::RenderAssetUsages,
5    math::{Vec2, Vec3},
6    mesh::{Indices, Mesh, PrimitiveTopology},
7};
8use bezier::{Bezier, Cubic, Linear, Quadratic};
9use shapes::lerp::{Lerp, LerpIterator};
10
11use crate::{sim::track::STANDARD_GAUGE, utils::Meshable};
12
13const MESH_STEPS: usize = 24;
14
15pub struct BezierMeshProps {
16    steps: usize,
17    thickness: f32,
18}
19
20impl Default for BezierMeshProps {
21    fn default() -> Self {
22        Self {
23            steps: MESH_STEPS,
24            thickness: STANDARD_GAUGE,
25        }
26    }
27}
28
29impl Meshable for Linear {
30    type Props = BezierMeshProps;
31
32    fn mesh(&self, props: Self::Props) -> Mesh {
33        let offset = (self.coeffs[1] - self.coeffs[0]).normalize().perp() * props.thickness / 2.0;
34
35        let positions = vec![
36            (self.coeffs[0] + offset).extend(0.0),
37            (self.coeffs[0] - offset).extend(0.0),
38            (self.coeffs[1] + offset).extend(0.0),
39            (self.coeffs[1] - offset).extend(0.0),
40        ];
41
42        Mesh::new(
43            PrimitiveTopology::TriangleList,
44            RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
45        )
46        .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
47        .with_inserted_indices(Indices::U32(vec![0, 1, 2, 1, 2, 3]))
48    }
49}
50
51fn bezier_mesh(bezier: &impl Bezier<Derivative = impl Lerp>, props: BezierMeshProps) -> Mesh {
52    let half_thickness = props.thickness / 2.0;
53    let d = bezier.derivative();
54
55    let positions: Vec<Vec3> = LerpIterator::new(bezier, props.steps)
56        .zip((0..props.steps).map(|i| d.lerp(i as f32 / (props.steps - 1) as f32)))
57        .map_while(|(p, d)| {
58            if d == Vec2::ZERO {
59                return None;
60            }
61
62            Some([
63                (p + d.perp().normalize() * half_thickness).extend(0.0),
64                (p - d.perp().normalize() * half_thickness).extend(0.0),
65            ])
66        })
67        .flatten()
68        .collect();
69
70    let n_of_positions = positions.len();
71
72    Mesh::new(
73        PrimitiveTopology::TriangleList,
74        RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
75    )
76    .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
77    .with_inserted_indices(Indices::U32(
78        (0..(n_of_positions.saturating_sub(2)) as u32)
79            .into_iter()
80            .flat_map(|i| [i, i + 1, i + 2])
81            .collect(),
82    ))
83}
84
85impl Meshable for Quadratic {
86    type Props = BezierMeshProps;
87
88    fn mesh(&self, props: Self::Props) -> Mesh {
89        bezier_mesh(self, props)
90    }
91}
92
93impl Meshable for Cubic {
94    type Props = BezierMeshProps;
95
96    fn mesh(&self, props: Self::Props) -> Mesh {
97        bezier_mesh(self, props)
98    }
99}