solstice_2d/shared/
lines.rs

1use crate::Shader;
2use solstice::mesh::MultiMesh;
3use solstice::{
4    mesh::{MappedVertexMesh, VertexMesh},
5    vertex::{AttributeType, Vertex, VertexFormat},
6    Context,
7};
8
9const SHADER_SRC: &str = include_str!("lines.glsl");
10
11#[repr(C)]
12#[derive(bytemuck::Zeroable, bytemuck::Pod, Vertex, Copy, Clone, Debug)]
13pub struct Position {
14    point: [f32; 3],
15}
16
17#[repr(C)]
18#[derive(bytemuck::Zeroable, bytemuck::Pod, Copy, Clone, Debug)]
19pub struct LineVertex {
20    pub position: [f32; 3],
21    pub width: f32,
22    pub color: [f32; 4],
23}
24
25impl Default for LineVertex {
26    fn default() -> Self {
27        Self {
28            position: [0., 0., 0.],
29            width: 1.0,
30            color: [1., 1., 1., 1.],
31        }
32    }
33}
34
35impl Vertex for LineVertex {
36    fn build_bindings() -> &'static [VertexFormat] {
37        const OFFSET: usize = std::mem::size_of::<LineVertex>();
38        &[
39            VertexFormat {
40                name: "position1",
41                offset: 0,
42                atype: AttributeType::F32F32F32,
43                normalize: false,
44            },
45            VertexFormat {
46                name: "width1",
47                offset: std::mem::size_of::<[f32; 3]>(),
48                atype: AttributeType::F32,
49                normalize: false,
50            },
51            VertexFormat {
52                name: "color1",
53                offset: std::mem::size_of::<[f32; 3]>() + std::mem::size_of::<f32>(),
54                atype: AttributeType::F32F32F32F32,
55                normalize: false,
56            },
57            VertexFormat {
58                name: "position2",
59                offset: OFFSET,
60                atype: AttributeType::F32F32F32,
61                normalize: false,
62            },
63            VertexFormat {
64                name: "width2",
65                offset: OFFSET + std::mem::size_of::<[f32; 3]>(),
66                atype: AttributeType::F32,
67                normalize: false,
68            },
69            VertexFormat {
70                name: "color2",
71                offset: OFFSET + std::mem::size_of::<[f32; 3]>() + std::mem::size_of::<f32>(),
72                atype: AttributeType::F32F32F32F32,
73                normalize: false,
74            },
75        ]
76    }
77}
78
79#[allow(unused)]
80const SEGMENT_VERTS: [Position; 6] = [
81    Position {
82        point: [0.0, -0.5, 0.],
83    },
84    Position {
85        point: [1.0, -0.5, 0.],
86    },
87    Position {
88        point: [1.0, 0.5, 0.],
89    },
90    Position {
91        point: [0.0, -0.5, 0.],
92    },
93    Position {
94        point: [1.0, 0.5, 0.],
95    },
96    Position {
97        point: [0.0, 0.5, 0.],
98    },
99];
100
101fn round_cap_join_geometry(resolution: usize) -> Vec<Position> {
102    let mut instance_round_round = vec![
103        Position {
104            point: [0., -0.5, 0.],
105        },
106        Position {
107            point: [0., -0.5, 1.],
108        },
109        Position {
110            point: [0., 0.5, 1.],
111        },
112        Position {
113            point: [0., -0.5, 0.],
114        },
115        Position {
116            point: [0., 0.5, 1.],
117        },
118        Position {
119            point: [0., 0.5, 0.],
120        },
121    ];
122
123    const PI: f32 = std::f32::consts::PI;
124
125    // Add the left cap.
126    for step in 0..resolution {
127        let theta0 = PI / 2. + ((step + 0) as f32 * PI) / resolution as f32;
128        let theta1 = PI / 2. + ((step + 1) as f32 * PI) / resolution as f32;
129        instance_round_round.push(Position {
130            point: [0., 0., 0.],
131        });
132        instance_round_round.push(Position {
133            point: [0.5 * theta0.cos(), 0.5 * theta0.sin(), 0.],
134        });
135        instance_round_round.push(Position {
136            point: [0.5 * theta1.cos(), 0.5 * theta1.sin(), 0.],
137        });
138    }
139    // Add the right cap.
140    for step in 0..resolution {
141        let theta0 = (3. * PI) / 2. + ((step + 0) as f32 * PI) / resolution as f32;
142        let theta1 = (3. * PI) / 2. + ((step + 1) as f32 * PI) / resolution as f32;
143        instance_round_round.push(Position {
144            point: [0., 0., 1.],
145        });
146        instance_round_round.push(Position {
147            point: [0.5 * theta0.cos(), 0.5 * theta0.sin(), 1.],
148        });
149        instance_round_round.push(Position {
150            point: [0.5 * theta1.cos(), 0.5 * theta1.sin(), 1.],
151        });
152    }
153
154    instance_round_round
155}
156
157pub struct LineWorkspace {
158    segment_geometry: VertexMesh<Position>,
159    positions: MappedVertexMesh<LineVertex>,
160    offset: usize,
161    unmapped: bool,
162
163    shader: Shader,
164}
165
166impl LineWorkspace {
167    pub fn new(ctx: &mut Context) -> Result<Self, super::GraphicsError> {
168        Self::with_capacity(ctx, 10_000)
169    }
170
171    pub fn with_capacity(ctx: &mut Context, capacity: usize) -> Result<Self, super::GraphicsError> {
172        let segment_geometry = round_cap_join_geometry(50);
173        let segment_geometry = VertexMesh::with_data(ctx, &segment_geometry)?;
174        // let segment_geometry = VertexMesh::with_data(ctx, &SEGMENT_VERTS)?;
175        let positions = MappedVertexMesh::new(ctx, capacity)?;
176
177        let shader = Shader::with(SHADER_SRC, ctx)?;
178
179        Ok(Self {
180            segment_geometry,
181            positions,
182            offset: 0,
183            unmapped: false,
184            shader,
185        })
186    }
187
188    pub fn can_buffer(&self, verts: &[LineVertex]) -> bool {
189        self.offset + verts.len() < self.positions.get_vertices().len()
190    }
191
192    pub fn add_points(&mut self, verts: &[LineVertex]) {
193        if self.unmapped {
194            self.unmapped = false;
195            self.offset = 0;
196        }
197        self.positions.set_vertices(verts, self.offset);
198        self.offset += verts.len()
199    }
200
201    pub fn shader(&self) -> &Shader {
202        &self.shader
203    }
204
205    pub fn inner(&self) -> solstice::Geometry<MultiMesh> {
206        use solstice::mesh::*;
207
208        let mesh = self.positions.inner();
209        let instance_count = (self.offset as u32).saturating_sub(1);
210
211        let draw_range = 0..(self.segment_geometry.len());
212        let attached = self.segment_geometry.attach_with_step(mesh, 1);
213        solstice::Geometry {
214            mesh: attached,
215            draw_range,
216            draw_mode: solstice::DrawMode::Triangles,
217            instance_count,
218        }
219    }
220
221    pub fn geometry(&mut self, ctx: &mut Context) -> solstice::Geometry<MultiMesh> {
222        use solstice::mesh::*;
223
224        let mesh = self.positions.unmap(ctx);
225        let instance_count = (self.offset as u32).saturating_sub(1);
226        self.unmapped = true;
227
228        let draw_range = 0..(self.segment_geometry.len());
229        let attached = self.segment_geometry.attach_with_step(mesh, 1);
230        solstice::Geometry {
231            mesh: attached,
232            draw_range,
233            draw_mode: solstice::DrawMode::Triangles,
234            instance_count,
235        }
236    }
237}