1use crate::{
2 context::DrawContext,
3 sprite::SpriteTexture,
4 utils::{Drawable, ShaderRef, Vertex},
5};
6use smallvec::SmallVec;
7use spitfire_core::{Triangle, VertexStream};
8use spitfire_glow::{
9 graphics::{Graphics, GraphicsBatch},
10 renderer::{GlowBlending, GlowUniformValue},
11};
12use std::{
13 borrow::Cow,
14 cell::RefCell,
15 collections::HashMap,
16 f32::consts::{PI, TAU},
17};
18use vek::{Mat4, Rect, Rgba, Vec2};
19
20#[derive(Debug, Default, Clone)]
21pub struct PrimitivesEmitter {
22 pub shader: Option<ShaderRef>,
23 pub textures: SmallVec<[SpriteTexture; 4]>,
24 pub uniforms: HashMap<Cow<'static, str>, GlowUniformValue>,
25 pub blending: Option<GlowBlending>,
26 pub screen_space: bool,
27}
28
29impl PrimitivesEmitter {
30 pub fn single(texture: SpriteTexture) -> Self {
31 Self {
32 textures: vec![texture].into(),
33 ..Default::default()
34 }
35 }
36
37 pub fn shader(mut self, value: ShaderRef) -> Self {
38 self.shader = Some(value);
39 self
40 }
41
42 pub fn texture(mut self, value: SpriteTexture) -> Self {
43 self.textures.push(value);
44 self
45 }
46
47 pub fn uniform(mut self, key: Cow<'static, str>, value: GlowUniformValue) -> Self {
48 self.uniforms.insert(key, value);
49 self
50 }
51
52 pub fn blending(mut self, value: GlowBlending) -> Self {
53 self.blending = Some(value);
54 self
55 }
56
57 pub fn screen_space(mut self, value: bool) -> Self {
58 self.screen_space = value;
59 self
60 }
61
62 pub fn emit_lines<I: IntoIterator<Item = Vec2<f32>>>(&self, vertices: I) -> LinesDraw<I> {
63 LinesDraw {
64 emitter: self,
65 vertices: RefCell::new(Some(vertices)),
66 region: Rect {
67 x: 0.0,
68 y: 0.0,
69 w: 1.0,
70 h: 1.0,
71 },
72 page: 0.0,
73 tint: Rgba::white(),
74 thickness: 1.0,
75 looped: false,
76 }
77 }
78
79 pub fn emit_brush<I: IntoIterator<Item = (Vec2<f32>, f32, Rgba<f32>)>>(
80 &self,
81 vertices: I,
82 ) -> BrushDraw<I> {
83 BrushDraw {
84 emitter: self,
85 vertices: RefCell::new(Some(vertices)),
86 region: Rect {
87 x: 0.0,
88 y: 0.0,
89 w: 1.0,
90 h: 1.0,
91 },
92 page: 0.0,
93 }
94 }
95
96 pub fn emit_triangles<I: IntoIterator<Item = [Vertex; 3]>>(
97 &self,
98 vertices: I,
99 ) -> TrianglesDraw<I> {
100 TrianglesDraw {
101 emitter: self,
102 vertices: RefCell::new(Some(vertices)),
103 tint: Rgba::white(),
104 }
105 }
106
107 pub fn emit_triangle_fan<I: IntoIterator<Item = Vertex>>(
108 &self,
109 vertices: I,
110 ) -> TriangleFanDraw<I> {
111 TriangleFanDraw {
112 emitter: self,
113 vertices: RefCell::new(Some(vertices)),
114 }
115 }
116
117 pub fn emit_triangle_strip<I: IntoIterator<Item = Vertex>>(
118 &self,
119 vertices: I,
120 ) -> TriangleStripDraw<I> {
121 TriangleStripDraw {
122 emitter: self,
123 vertices: RefCell::new(Some(vertices)),
124 }
125 }
126
127 pub fn emit_regular_polygon(
128 &self,
129 vertices: usize,
130 position: Vec2<f32>,
131 radius: f32,
132 ) -> RegularPolygonDraw {
133 RegularPolygonDraw {
134 emitter: self,
135 vertices,
136 position,
137 radius,
138 region: Rect {
139 x: 0.0,
140 y: 0.0,
141 w: 1.0,
142 h: 1.0,
143 },
144 page: 0.0,
145 tint: Rgba::white(),
146 }
147 }
148
149 pub fn emit_circle(
150 &self,
151 position: Vec2<f32>,
152 radius: f32,
153 maximum_error: f32,
154 ) -> RegularPolygonDraw {
155 RegularPolygonDraw {
156 emitter: self,
157 vertices: (PI / (1.0 - maximum_error / radius).acos()).ceil() as _,
158 position,
159 radius,
160 region: Rect {
161 x: 0.0,
162 y: 0.0,
163 w: 1.0,
164 h: 1.0,
165 },
166 page: 0.0,
167 tint: Rgba::white(),
168 }
169 }
170
171 fn stream_transformed(
172 &self,
173 context: &mut DrawContext,
174 graphics: &mut Graphics<Vertex>,
175 f: impl FnMut(&mut VertexStream<Vertex, GraphicsBatch>),
176 ) {
177 let batch = GraphicsBatch {
178 shader: context.shader(self.shader.as_ref()),
179 uniforms: self
180 .uniforms
181 .iter()
182 .map(|(k, v)| (k.clone(), v.to_owned()))
183 .chain(std::iter::once((
184 "u_projection_view".into(),
185 GlowUniformValue::M4(
186 if self.screen_space {
187 graphics.main_camera.screen_matrix()
188 } else {
189 graphics.main_camera.world_matrix()
190 }
191 .into_col_array(),
192 ),
193 )))
194 .chain(self.textures.iter().enumerate().map(|(index, texture)| {
195 (texture.sampler.clone(), GlowUniformValue::I1(index as _))
196 }))
197 .collect(),
198 textures: self
199 .textures
200 .iter()
201 .filter_map(|texture| {
202 Some((context.texture(Some(&texture.texture))?, texture.filtering))
203 })
204 .collect(),
205 blending: self.blending.unwrap_or_else(|| context.top_blending()),
206 scissor: None,
207 };
208 graphics.stream.batch_optimized(batch);
209 let transform = Mat4::from(context.top_transform());
210 graphics.stream.transformed(f, |vertex| {
211 let point = transform.mul_point(Vec2::from(vertex.position));
212 vertex.position[0] = point.x;
213 vertex.position[1] = point.y;
214 });
215 }
216}
217
218pub struct LinesDraw<'a, I: IntoIterator<Item = Vec2<f32>>> {
219 emitter: &'a PrimitivesEmitter,
220 vertices: RefCell<Option<I>>,
221 pub region: Rect<f32, f32>,
222 pub page: f32,
223 pub tint: Rgba<f32>,
224 pub thickness: f32,
225 pub looped: bool,
226}
227
228impl<I: IntoIterator<Item = Vec2<f32>>> LinesDraw<'_, I> {
229 pub fn region_page(mut self, region: Rect<f32, f32>, page: f32) -> Self {
230 self.region = region;
231 self.page = page;
232 self
233 }
234
235 pub fn tint(mut self, value: Rgba<f32>) -> Self {
236 self.tint = value;
237 self
238 }
239
240 pub fn thickness(mut self, value: f32) -> Self {
241 self.thickness = value;
242 self
243 }
244
245 pub fn looped(mut self, value: bool) -> Self {
246 self.looped = value;
247 self
248 }
249}
250
251impl<I: IntoIterator<Item = Vec2<f32>>> Drawable for LinesDraw<'_, I> {
252 fn draw(&self, context: &mut DrawContext, graphics: &mut Graphics<Vertex>) {
253 fn push(
254 stream: &mut VertexStream<Vertex, GraphicsBatch>,
255 region: Rect<f32, f32>,
256 page: f32,
257 color: [f32; 4],
258 prev: Vec2<f32>,
259 next: Vec2<f32>,
260 normal: Vec2<f32>,
261 ) {
262 stream.extend(
263 [
264 Vertex {
265 position: (prev - normal).into_array(),
266 uv: [region.x, region.y, page],
267 color,
268 },
269 Vertex {
270 position: (prev + normal).into_array(),
271 uv: [region.x + region.w, region.y, page],
272 color,
273 },
274 Vertex {
275 position: (next + normal).into_array(),
276 uv: [region.x + region.w, region.y + region.h, page],
277 color,
278 },
279 Vertex {
280 position: (next - normal).into_array(),
281 uv: [region.x, region.y + region.h, page],
282 color,
283 },
284 ],
285 [Triangle { a: 0, b: 1, c: 2 }, Triangle { a: 2, b: 3, c: 0 }],
286 );
287 }
288
289 self.emitter
290 .stream_transformed(context, graphics, |stream| {
291 if let Some(vertices) = self.vertices.borrow_mut().take() {
292 let mut vertices = vertices.into_iter();
293 let Some(mut prev) = vertices.next() else {
294 return;
295 };
296 let start = prev;
297 let color = self.tint.into_array();
298 for next in vertices {
299 let tangent = next - prev;
300 let normal = Vec2 {
301 x: tangent.y,
302 y: -tangent.x,
303 }
304 .try_normalized()
305 .unwrap_or_default()
306 * self.thickness;
307 push(stream, self.region, self.page, color, prev, next, normal);
308 prev = next;
309 }
310 if self.looped {
311 let tangent = start - prev;
312 let normal = Vec2 {
313 x: tangent.y,
314 y: -tangent.x,
315 }
316 .try_normalized()
317 .unwrap_or_default()
318 * self.thickness;
319 push(stream, self.region, self.page, color, prev, start, normal);
320 }
321 }
322 });
323 }
324}
325
326pub struct BrushDraw<'a, I: IntoIterator<Item = (Vec2<f32>, f32, Rgba<f32>)>> {
327 emitter: &'a PrimitivesEmitter,
328 vertices: RefCell<Option<I>>,
329 pub region: Rect<f32, f32>,
330 pub page: f32,
331}
332
333impl<I: IntoIterator<Item = (Vec2<f32>, f32, Rgba<f32>)>> BrushDraw<'_, I> {
334 pub fn region_page(mut self, region: Rect<f32, f32>, page: f32) -> Self {
335 self.region = region;
336 self.page = page;
337 self
338 }
339}
340
341impl<I: IntoIterator<Item = (Vec2<f32>, f32, Rgba<f32>)>> Drawable for BrushDraw<'_, I> {
342 fn draw(&self, context: &mut DrawContext, graphics: &mut Graphics<Vertex>) {
343 fn push(
344 stream: &mut VertexStream<Vertex, GraphicsBatch>,
345 region: Rect<f32, f32>,
346 page: f32,
347 prev: (Vec2<f32>, f32, Rgba<f32>),
348 next: (Vec2<f32>, f32, Rgba<f32>),
349 normal_prev: Vec2<f32>,
350 normal_next: Vec2<f32>,
351 ) {
352 stream.extend(
353 [
354 Vertex {
355 position: ((prev.0 + next.0) * 0.5).into_array(),
356 uv: [region.x, region.y, page],
357 color: ((prev.2 + next.2) * 0.5).into_array(),
358 },
359 Vertex {
360 position: (prev.0 - normal_prev * prev.1).into_array(),
361 uv: [region.x, region.y, page],
362 color: prev.2.into_array(),
363 },
364 Vertex {
365 position: (prev.0 + normal_prev * prev.1).into_array(),
366 uv: [region.x + region.w, region.y, page],
367 color: prev.2.into_array(),
368 },
369 Vertex {
370 position: (next.0 + normal_next * next.1).into_array(),
371 uv: [region.x + region.w, region.y + region.h, page],
372 color: next.2.into_array(),
373 },
374 Vertex {
375 position: (next.0 - normal_next * next.1).into_array(),
376 uv: [region.x, region.y + region.h, page],
377 color: next.2.into_array(),
378 },
379 ],
380 [
381 Triangle { a: 0, b: 1, c: 2 },
382 Triangle { a: 0, b: 2, c: 3 },
383 Triangle { a: 0, b: 3, c: 4 },
384 Triangle { a: 0, b: 4, c: 1 },
385 ],
386 );
387 }
388
389 self.emitter
390 .stream_transformed(context, graphics, |stream| {
391 if let Some(vertices) = self.vertices.borrow_mut().take() {
392 let mut vertices = vertices.into_iter().peekable();
393 let Some(mut prev) = vertices.next() else {
394 return;
395 };
396 let mut prev_tangent = Option::<Vec2<f32>>::None;
397 while let Some(curr) = vertices.next() {
398 let next = vertices.peek().copied();
399 let curr_tangent = (curr.0 - prev.0).try_normalized().unwrap_or_default();
400 let tangent = prev_tangent
401 .replace(curr_tangent)
402 .and_then(|tangent| (curr_tangent + tangent).try_normalized())
403 .unwrap_or(curr_tangent);
404 let next_tangent = next
405 .and_then(|next| (next.0 - curr.0).try_normalized())
406 .and_then(|tangent| (curr_tangent + tangent).try_normalized())
407 .unwrap_or(curr_tangent);
408 let normal_prev = Vec2 {
409 x: tangent.y,
410 y: -tangent.x,
411 }
412 .try_normalized()
413 .unwrap_or_default();
414 let normal_next = Vec2 {
415 x: next_tangent.y,
416 y: -next_tangent.x,
417 }
418 .try_normalized()
419 .unwrap_or_default();
420 push(
421 stream,
422 self.region,
423 self.page,
424 prev,
425 curr,
426 normal_prev,
427 normal_next,
428 );
429 prev = curr;
430 }
431 }
432 });
433 }
434}
435
436pub struct TrianglesDraw<'a, I: IntoIterator<Item = [Vertex; 3]>> {
437 emitter: &'a PrimitivesEmitter,
438 vertices: RefCell<Option<I>>,
439 pub tint: Rgba<f32>,
440}
441
442impl<I: IntoIterator<Item = [Vertex; 3]>> Drawable for TrianglesDraw<'_, I> {
443 fn draw(&self, context: &mut DrawContext, graphics: &mut Graphics<Vertex>) {
444 self.emitter
445 .stream_transformed(context, graphics, |stream| {
446 if let Some(vertices) = self.vertices.borrow_mut().take() {
447 unsafe {
448 let start = stream.vertices().len();
449 stream.extend_vertices(vertices.into_iter().flatten());
450 let end = stream.vertices().len();
451 stream.extend_triangles(
452 false,
453 (start..end).step_by(3).map(|index| Triangle {
454 a: index as u32,
455 b: index as u32 + 1,
456 c: index as u32 + 2,
457 }),
458 );
459 }
460 }
461 });
462 }
463}
464
465pub struct TriangleFanDraw<'a, I: IntoIterator<Item = Vertex>> {
466 emitter: &'a PrimitivesEmitter,
467 vertices: RefCell<Option<I>>,
468}
469
470impl<I: IntoIterator<Item = Vertex>> Drawable for TriangleFanDraw<'_, I> {
471 fn draw(&self, context: &mut DrawContext, graphics: &mut Graphics<Vertex>) {
472 self.emitter
473 .stream_transformed(context, graphics, |stream| {
474 if let Some(vertices) = self.vertices.borrow_mut().take() {
475 stream.triangle_fan(vertices);
476 }
477 });
478 }
479}
480
481pub struct TriangleStripDraw<'a, I: IntoIterator<Item = Vertex>> {
482 emitter: &'a PrimitivesEmitter,
483 vertices: RefCell<Option<I>>,
484}
485
486impl<I: IntoIterator<Item = Vertex>> Drawable for TriangleStripDraw<'_, I> {
487 fn draw(&self, context: &mut DrawContext, graphics: &mut Graphics<Vertex>) {
488 self.emitter
489 .stream_transformed(context, graphics, |stream| {
490 if let Some(vertices) = self.vertices.borrow_mut().take() {
491 stream.triangle_strip(vertices);
492 }
493 });
494 }
495}
496
497pub struct RegularPolygonDraw<'a> {
498 emitter: &'a PrimitivesEmitter,
499 vertices: usize,
500 position: Vec2<f32>,
501 radius: f32,
502 pub region: Rect<f32, f32>,
503 pub page: f32,
504 pub tint: Rgba<f32>,
505}
506
507impl RegularPolygonDraw<'_> {
508 pub fn region_page(mut self, region: Rect<f32, f32>, page: f32) -> Self {
509 self.region = region;
510 self.page = page;
511 self
512 }
513
514 pub fn tint(mut self, value: Rgba<f32>) -> Self {
515 self.tint = value;
516 self
517 }
518}
519
520impl Drawable for RegularPolygonDraw<'_> {
521 fn draw(&self, context: &mut DrawContext, graphics: &mut Graphics<Vertex>) {
522 let color = self.tint.into_array();
523 self.emitter
524 .stream_transformed(context, graphics, |stream| {
525 stream.triangle_fan((0..=self.vertices).map(|index| {
526 let angle = TAU / self.vertices as f32 * index as f32;
527 let (y, x) = angle.sin_cos();
528 let u = (x + 1.0) * 0.5;
529 let v = (y + 1.0) * 0.5;
530 Vertex {
531 position: [
532 self.position.x + x * self.radius,
533 self.position.y + y * self.radius,
534 ],
535 uv: [
536 self.region.x + self.region.w * u,
537 self.region.y + self.region.h * v,
538 self.page,
539 ],
540 color,
541 }
542 }));
543 });
544 }
545}