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