1use 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}