nannou/draw/primitive/
mesh.rs

1use crate::color::conv::IntoLinSrgba;
2use crate::draw::mesh::vertex::{self, Point, TexCoords, Vertex};
3use crate::draw::primitive::Primitive;
4use crate::draw::properties::spatial::{orientation, position};
5use crate::draw::properties::{ColorScalar, LinSrgba, SetColor, SetOrientation, SetPosition};
6use crate::draw::{self, Drawing};
7use crate::geom;
8use crate::wgpu;
9use std::ops;
10
11/// The mesh type prior to being initialised with vertices or indices.
12#[derive(Clone, Debug, Default)]
13pub struct Vertexless;
14
15/// Properties related to drawing an arbitrary mesh of colours, geometry and texture.
16#[derive(Clone, Debug)]
17pub struct Mesh {
18    position: position::Properties,
19    orientation: orientation::Properties,
20    vertex_range: ops::Range<usize>,
21    index_range: ops::Range<usize>,
22    vertex_mode: draw::renderer::VertexMode,
23    fill_color: Option<FillColor>,
24    texture_view: Option<wgpu::TextureView>,
25}
26
27#[derive(Clone, Debug, Default)]
28struct FillColor(Option<LinSrgba>);
29
30// A simple iterator for flattening a fixed-size array of indices.
31struct FlattenIndices<I> {
32    iter: I,
33    index: usize,
34    vertex_start_index: usize,
35    current: [usize; 3],
36}
37
38pub type DrawingMesh<'a> = Drawing<'a, Mesh>;
39
40impl Vertexless {
41    /// Describe the mesh with a sequence of textured points.
42    ///
43    /// Each of the vertices must be represented as a tuple containing the point and tex
44    /// coordinates in that order, e.g. `(point, tex_coords)`. `point` may be of any type that
45    /// implements `Into<Point3>` and `tex_coords` may be of any type that implements
46    /// `Into<Point2>`.
47    pub fn points_textured<I, P, T>(
48        self,
49        inner_mesh: &mut draw::Mesh,
50        texture_view: &dyn wgpu::ToTextureView,
51        points: I,
52    ) -> Mesh
53    where
54        I: IntoIterator<Item = (P, T)>,
55        P: Into<Point>,
56        T: Into<TexCoords>,
57    {
58        let points = points.into_iter().map(|(p, t)| {
59            let point = p.into();
60            let color = vertex::DEFAULT_VERTEX_COLOR;
61            let tex_coords = t.into();
62            ((point, color), tex_coords).into()
63        });
64        let vertex_mode = draw::renderer::VertexMode::Texture;
65        self.points_inner(
66            inner_mesh,
67            points,
68            vertex_mode,
69            Some(texture_view.to_texture_view()),
70        )
71    }
72
73    /// Describe the mesh with a sequence of colored points.
74    ///
75    /// Each of the points must be represented as a tuple containing the point and the color in
76    /// that order, e.g. `(point, color)`. `point` may be of any type that implements
77    /// `Into<Point3>` and `color` may be of any type that implements `IntoLinSrgba`.
78    pub fn points_colored<I, P, C>(self, inner_mesh: &mut draw::Mesh, points: I) -> Mesh
79    where
80        I: IntoIterator<Item = (P, C)>,
81        P: Into<Point>,
82        C: IntoLinSrgba<ColorScalar>,
83    {
84        let vertices = points.into_iter().map(|(p, c)| {
85            let point = p.into();
86            let color = c.into_lin_srgba();
87            let tex_coords = vertex::default_tex_coords();
88            ((point, color), tex_coords).into()
89        });
90        let vertex_mode = draw::renderer::VertexMode::Color;
91        self.points_inner(inner_mesh, vertices, vertex_mode, None)
92    }
93
94    /// Describe the mesh with a sequence of points.
95    ///
96    /// The given iterator may yield any type that can be converted directly into `Point3`s.
97    ///
98    /// This method assumes that the entire mesh should be coloured with a single colour. If a
99    /// colour is not specified via one of the builder methods, a default colour will be retrieved
100    /// from the inner `Theme`.
101    pub fn points<I>(self, inner_mesh: &mut draw::Mesh, points: I) -> Mesh
102    where
103        I: IntoIterator,
104        I::Item: Into<Point>,
105    {
106        let vertices = points.into_iter().map(|p| {
107            let point = p.into();
108            let color = vertex::DEFAULT_VERTEX_COLOR;
109            let tex_coords = vertex::default_tex_coords();
110            ((point, color), tex_coords).into()
111        });
112        let vertex_mode = draw::renderer::VertexMode::Color;
113        let mut mesh = self.points_inner(inner_mesh, vertices, vertex_mode, None);
114        mesh.fill_color = Some(FillColor(None));
115        mesh
116    }
117
118    fn points_inner<I>(
119        self,
120        inner_mesh: &mut draw::Mesh,
121        vertices: I,
122        vertex_mode: draw::renderer::VertexMode,
123        texture_view: Option<wgpu::TextureView>,
124    ) -> Mesh
125    where
126        I: Iterator<Item = Vertex>,
127    {
128        let v_start = inner_mesh.points().len();
129        let i_start = inner_mesh.indices().len();
130        for (i, vertex) in vertices.enumerate() {
131            inner_mesh.push_vertex(vertex);
132            inner_mesh.push_index((v_start + i) as u32);
133        }
134        let v_end = inner_mesh.points().len();
135        let i_end = inner_mesh.indices().len();
136        Mesh::new(v_start..v_end, i_start..i_end, vertex_mode, texture_view)
137    }
138
139    /// Describe the mesh with a sequence of textured triangles.
140    ///
141    /// Each of the vertices must be represented as a tuple containing the point and tex
142    /// coordinates in that order, e.g. `(point, tex_coords)`. `point` may be of any type that
143    /// implements `Into<Point3>` and `tex_coords` may be of any type that implements
144    /// `Into<Point2>`.
145    pub fn tris_textured<I, P, T>(
146        self,
147        inner_mesh: &mut draw::Mesh,
148        texture_view: &dyn wgpu::ToTextureView,
149        tris: I,
150    ) -> Mesh
151    where
152        I: IntoIterator<Item = geom::Tri<(P, T)>>,
153        P: Into<Point>,
154        T: Into<TexCoords>,
155    {
156        let points = tris
157            .into_iter()
158            .map(|t| t.map_vertices(|(p, t)| (p.into(), t.into())))
159            .flat_map(geom::Tri::vertices);
160        self.points_textured(inner_mesh, texture_view, points)
161    }
162
163    /// Describe the mesh with a sequence of colored triangles.
164    ///
165    /// Each of the vertices must be represented as a tuple containing the point and the color in
166    /// that order, e.g. `(point, color)`. `point` may be of any type that implements `Into<Point3>`
167    /// and `color` may be of any type that implements `IntoLinSrgba`.
168    pub fn tris_colored<I, P, C>(self, inner_mesh: &mut draw::Mesh, tris: I) -> Mesh
169    where
170        I: IntoIterator<Item = geom::Tri<(P, C)>>,
171        P: Into<Point>,
172        C: IntoLinSrgba<ColorScalar>,
173    {
174        let points = tris
175            .into_iter()
176            .map(|t| t.map_vertices(|(p, c)| (p.into(), c.into_lin_srgba())))
177            .flat_map(geom::Tri::vertices);
178        self.points_colored(inner_mesh, points)
179    }
180
181    /// Describe the mesh with a sequence of triangles.
182    ///
183    /// Each triangle may be composed of any vertex type that may be converted directly into
184    /// `Point3`s.
185    ///
186    /// This method assumes that the entire mesh should be coloured with a single colour. If a
187    /// colour is not specified via one of the builder methods, a default colour will be retrieved
188    /// from the inner `Theme`.
189    pub fn tris<I, V>(self, inner_mesh: &mut draw::Mesh, tris: I) -> Mesh
190    where
191        I: IntoIterator<Item = geom::Tri<V>>,
192        V: Into<Point>,
193    {
194        let points = tris
195            .into_iter()
196            .map(|t| t.map_vertices(Into::into))
197            .flat_map(geom::Tri::vertices);
198        self.points(inner_mesh, points)
199    }
200
201    /// Describe the mesh with the given indexed, textured points.
202    ///
203    /// Each trio of `indices` describes a single triangle made up of colored `points`.
204    ///
205    /// Each of the `points` must be represented as a tuple containing the point and the texture
206    /// coordinates in that order, e.g. `(point, tex_coords)`. `point` may be of any type that
207    /// implements `Into<Point3>` and `tex_coords` may be of any type that implements
208    /// `Into<Point2>`.
209    pub fn indexed_textured<V, I, P, T>(
210        self,
211        inner_mesh: &mut draw::Mesh,
212        texture_view: &dyn wgpu::ToTextureView,
213        points: V,
214        indices: I,
215    ) -> Mesh
216    where
217        V: IntoIterator<Item = (P, T)>,
218        I: IntoIterator<Item = usize>,
219        P: Into<Point>,
220        T: Into<TexCoords>,
221    {
222        let vertices = points.into_iter().map(|(p, t)| {
223            let point = p.into();
224            let color = vertex::DEFAULT_VERTEX_COLOR;
225            let tex_coords = t.into();
226            ((point, color), tex_coords).into()
227        });
228        let vertex_mode = draw::renderer::VertexMode::Texture;
229        self.indexed_inner(
230            inner_mesh,
231            vertices,
232            indices,
233            vertex_mode,
234            Some(texture_view.to_texture_view()),
235        )
236    }
237
238    /// Describe the mesh with the given indexed, colored points.
239    ///
240    /// Each trio of `indices` describes a single triangle made up of colored `points`.
241    ///
242    /// Each of the `points` must be represented as a tuple containing the point and the color in
243    /// that order, e.g. `(point, color)`. `point` may be of any type that implements
244    /// `Into<Point3>` and `color` may be of any type that implements `IntoLinSrgba`.
245    pub fn indexed_colored<V, I, P, C>(
246        self,
247        inner_mesh: &mut draw::Mesh,
248        points: V,
249        indices: I,
250    ) -> Mesh
251    where
252        V: IntoIterator<Item = (P, C)>,
253        I: IntoIterator<Item = usize>,
254        P: Into<Point>,
255        C: IntoLinSrgba<ColorScalar>,
256    {
257        let vertices = points.into_iter().map(|(p, c)| {
258            let point = p.into();
259            let color = c.into_lin_srgba();
260            let tex_coords = vertex::default_tex_coords();
261            ((point, color), tex_coords).into()
262        });
263        let vertex_mode = draw::renderer::VertexMode::Color;
264        self.indexed_inner(inner_mesh, vertices, indices, vertex_mode, None)
265    }
266
267    /// Describe the mesh with the given indexed points.
268    ///
269    /// Each trio of `indices` describes a single triangle made up of `points`.
270    ///
271    /// Each point may be any type that may be converted directly into the `Point3` type.
272    pub fn indexed<V, I>(self, inner_mesh: &mut draw::Mesh, points: V, indices: I) -> Mesh
273    where
274        V: IntoIterator,
275        V::Item: Into<Point>,
276        I: IntoIterator<Item = usize>,
277    {
278        let vertices = points.into_iter().map(|p| {
279            let point = p.into();
280            let color = vertex::DEFAULT_VERTEX_COLOR;
281            let tex_coords = vertex::default_tex_coords();
282            ((point, color), tex_coords).into()
283        });
284        let vertex_mode = draw::renderer::VertexMode::Color;
285        let mut mesh = self.indexed_inner(inner_mesh, vertices, indices, vertex_mode, None);
286        mesh.fill_color = Some(FillColor(None));
287        mesh
288    }
289
290    fn indexed_inner<V, I>(
291        self,
292        inner_mesh: &mut draw::Mesh,
293        vertices: V,
294        indices: I,
295        vertex_mode: draw::renderer::VertexMode,
296        texture_view: Option<wgpu::TextureView>,
297    ) -> Mesh
298    where
299        V: IntoIterator<Item = Vertex>,
300        I: IntoIterator<Item = usize>,
301    {
302        let v_start = inner_mesh.points().len();
303        let i_start = inner_mesh.indices().len();
304        inner_mesh.extend_vertices(vertices);
305        inner_mesh.extend_indices(indices.into_iter().map(|ix| ix as u32));
306        let v_end = inner_mesh.points().len();
307        let i_end = inner_mesh.indices().len();
308        Mesh::new(v_start..v_end, i_start..i_end, vertex_mode, texture_view)
309    }
310}
311
312impl Mesh {
313    // Initialise a new `Mesh` with its ranges into the intermediary mesh, ready for drawing.
314    fn new(
315        vertex_range: ops::Range<usize>,
316        index_range: ops::Range<usize>,
317        vertex_mode: draw::renderer::VertexMode,
318        texture_view: Option<wgpu::TextureView>,
319    ) -> Self {
320        let orientation = Default::default();
321        let position = Default::default();
322        let fill_color = None;
323        Mesh {
324            orientation,
325            position,
326            vertex_range,
327            index_range,
328            vertex_mode,
329            fill_color,
330            texture_view,
331        }
332    }
333}
334
335impl<'a> Drawing<'a, Vertexless> {
336    /// Describe the mesh with a sequence of points.
337    ///
338    /// The given iterator may yield any type that can be converted directly into `Point3`s.
339    ///
340    /// This method assumes that the entire mesh should be coloured with a single colour. If a
341    /// colour is not specified via one of the builder methods, a default colour will be retrieved
342    /// from the inner `Theme`.
343    pub fn points<I>(self, points: I) -> DrawingMesh<'a>
344    where
345        I: IntoIterator,
346        I::Item: Into<Point>,
347    {
348        self.map_ty_with_context(|ty, ctxt| ty.points(ctxt.mesh, points))
349    }
350
351    /// Describe the mesh with a sequence of colored points.
352    ///
353    /// Each of the points must be represented as a tuple containing the point and the color in
354    /// that order, e.g. `(point, color)`. `point` may be of any type that implements
355    /// `Into<Point3>` and `color` may be of any type that implements `IntoLinSrgba`.
356    pub fn points_colored<I, P, C>(self, points: I) -> DrawingMesh<'a>
357    where
358        I: IntoIterator<Item = (P, C)>,
359        P: Into<Point>,
360        C: IntoLinSrgba<ColorScalar>,
361    {
362        self.map_ty_with_context(|ty, ctxt| ty.points_colored(ctxt.mesh, points))
363    }
364
365    /// Describe the mesh with a sequence of textured points.
366    ///
367    /// Each of the vertices must be represented as a tuple containing the point and tex
368    /// coordinates in that order, e.g. `(point, tex_coords)`. `point` may be of any type that
369    /// implements `Into<Point3>` and `tex_coords` may be of any type that implements
370    /// `Into<Point2>`.
371    pub fn points_textured<I, P, T>(
372        self,
373        view: &dyn wgpu::ToTextureView,
374        points: I,
375    ) -> DrawingMesh<'a>
376    where
377        I: IntoIterator<Item = (P, T)>,
378        P: Into<Point>,
379        T: Into<TexCoords>,
380    {
381        self.map_ty_with_context(|ty, ctxt| ty.points_textured(ctxt.mesh, view, points))
382    }
383
384    /// Describe the mesh with a sequence of triangles.
385    ///
386    /// Each triangle may be composed of any vertex type that may be converted directly into
387    /// `Point3`s.
388    ///
389    /// This method assumes that the entire mesh should be coloured with a single colour. If a
390    /// colour is not specified via one of the builder methods, a default colour will be retrieved
391    /// from the inner `Theme`.
392    pub fn tris<I, V>(self, tris: I) -> DrawingMesh<'a>
393    where
394        I: IntoIterator<Item = geom::Tri<V>>,
395        V: Into<Point>,
396    {
397        self.map_ty_with_context(|ty, ctxt| ty.tris(ctxt.mesh, tris))
398    }
399
400    /// Describe the mesh with a sequence of colored triangles.
401    ///
402    /// Each of the vertices must be represented as a tuple containing the point and the color in
403    /// that order, e.g. `(point, color)`. `point` may be of any type that implements `Into<Point3>`
404    /// and `color` may be of any type that implements `IntoLinSrgba`.
405    pub fn tris_colored<I, P, C>(self, tris: I) -> DrawingMesh<'a>
406    where
407        I: IntoIterator<Item = geom::Tri<(P, C)>>,
408        P: Into<Point>,
409        C: IntoLinSrgba<ColorScalar>,
410    {
411        self.map_ty_with_context(|ty, ctxt| ty.tris_colored(ctxt.mesh, tris))
412    }
413
414    /// Describe the mesh with a sequence of textured triangles.
415    ///
416    /// Each of the vertices must be represented as a tuple containing the point and tex
417    /// coordinates in that order, e.g. `(point, tex_coords)`. `point` may be of any type that
418    /// implements `Into<Point3>` and `tex_coords` may be of any type that implements
419    /// `Into<Point2>`.
420    pub fn tris_textured<I, P, T>(self, view: &dyn wgpu::ToTextureView, tris: I) -> DrawingMesh<'a>
421    where
422        I: IntoIterator<Item = geom::Tri<(P, T)>>,
423        P: Into<Point>,
424        T: Into<TexCoords>,
425    {
426        self.map_ty_with_context(|ty, ctxt| ty.tris_textured(ctxt.mesh, view, tris))
427    }
428
429    /// Describe the mesh with the given indexed points.
430    ///
431    /// Each trio of `indices` describes a single triangle made up of `points`.
432    ///
433    /// Each point may be any type that may be converted directly into the `Point3` type.
434    pub fn indexed<V, I>(self, points: V, indices: I) -> DrawingMesh<'a>
435    where
436        V: IntoIterator,
437        V::Item: Into<Point>,
438        I: IntoIterator<Item = usize>,
439    {
440        self.map_ty_with_context(|ty, ctxt| ty.indexed(ctxt.mesh, points, indices))
441    }
442
443    /// Describe the mesh with the given indexed, colored points.
444    ///
445    /// Each trio of `indices` describes a single triangle made up of colored `points`.
446    ///
447    /// Each of the `points` must be represented as a tuple containing the point and the color in
448    /// that order, e.g. `(point, color)`. `point` may be of any type that implements
449    /// `Into<Point3>` and `color` may be of any type that implements `IntoLinSrgba`.
450    pub fn indexed_colored<V, I, P, C>(self, points: V, indices: I) -> DrawingMesh<'a>
451    where
452        V: IntoIterator<Item = (P, C)>,
453        I: IntoIterator<Item = usize>,
454        P: Into<Point>,
455        C: IntoLinSrgba<ColorScalar>,
456    {
457        self.map_ty_with_context(|ty, ctxt| ty.indexed_colored(ctxt.mesh, points, indices))
458    }
459
460    /// Describe the mesh with the given indexed, textured points.
461    ///
462    /// Each trio of `indices` describes a single triangle made up of colored `points`.
463    ///
464    /// Each of the `points` must be represented as a tuple containing the point and the texture
465    /// coordinates in that order, e.g. `(point, tex_coords)`. `point` may be of any type that
466    /// implements `Into<Point3>` and `tex_coords` may be of any type that implements
467    /// `Into<Point2>`.
468    pub fn indexed_textured<V, I, P, T>(
469        self,
470        view: &dyn wgpu::ToTextureView,
471        points: V,
472        indices: I,
473    ) -> DrawingMesh<'a>
474    where
475        V: IntoIterator<Item = (P, T)>,
476        I: IntoIterator<Item = usize>,
477        P: Into<Point>,
478        T: Into<TexCoords>,
479    {
480        self.map_ty_with_context(|ty, ctxt| ty.indexed_textured(ctxt.mesh, view, points, indices))
481    }
482}
483
484impl draw::renderer::RenderPrimitive for Mesh {
485    fn render_primitive(
486        self,
487        ctxt: draw::renderer::RenderContext,
488        mesh: &mut draw::Mesh,
489    ) -> draw::renderer::PrimitiveRender {
490        let Mesh {
491            orientation,
492            position,
493            vertex_range,
494            index_range,
495            vertex_mode,
496            fill_color,
497            texture_view,
498        } = self;
499
500        // Determine the transform to apply to vertices.
501        let global_transform = *ctxt.transform;
502        let local_transform = position.transform() * orientation.transform();
503        let transform = global_transform * local_transform;
504
505        // We need to update the indices to point to where vertices will be in the new mesh.
506        let old_mesh_vertex_start = vertex_range.start as u32;
507        let new_mesh_vertex_start = mesh.raw_vertex_count() as u32;
508        let indices = index_range
509            .map(|i| ctxt.intermediary_mesh.indices()[i])
510            .map(|i| new_mesh_vertex_start + i - old_mesh_vertex_start);
511
512        // A small function for transforming a point via the transform matrix.
513        let transform_point = |p: geom::Point3| -> geom::Point3 { transform.transform_point3(p) };
514
515        // Color the vertices based on whether or not we should fill, then extend the mesh!
516        match fill_color {
517            Some(fill) => {
518                let theme_prim = draw::theme::Primitive::Mesh;
519                let color = fill
520                    .0
521                    .unwrap_or_else(|| ctxt.theme.fill_lin_srgba(&theme_prim));
522                let vertices = vertex_range.map(|i| {
523                    let point = transform_point(ctxt.intermediary_mesh.points()[i]);
524                    let tex_coords = ctxt.intermediary_mesh.tex_coords()[i];
525                    ((point, color), tex_coords).into()
526                });
527                mesh.extend(vertices, indices);
528            }
529            None => {
530                let vertices = vertex_range.map(|i| {
531                    let point = transform_point(ctxt.intermediary_mesh.points()[i]);
532                    let color = ctxt.intermediary_mesh.colors()[i];
533                    let tex_coords = ctxt.intermediary_mesh.tex_coords()[i];
534                    ((point, color), tex_coords).into()
535                });
536                mesh.extend(vertices, indices);
537            }
538        }
539
540        draw::renderer::PrimitiveRender {
541            texture_view,
542            vertex_mode,
543        }
544    }
545}
546
547impl<I> Iterator for FlattenIndices<I>
548where
549    I: Iterator<Item = [usize; 3]>,
550{
551    type Item = usize;
552    fn next(&mut self) -> Option<Self::Item> {
553        loop {
554            if self.index < self.current.len() {
555                let ix = self.current[self.index];
556                self.index += 1;
557                return Some(self.vertex_start_index + ix);
558            }
559            match self.iter.next() {
560                None => return None,
561                Some(trio) => {
562                    self.current = trio;
563                    self.index = 0;
564                }
565            }
566        }
567    }
568}
569
570impl SetOrientation for Mesh {
571    fn properties(&mut self) -> &mut orientation::Properties {
572        SetOrientation::properties(&mut self.orientation)
573    }
574}
575
576impl SetPosition for Mesh {
577    fn properties(&mut self) -> &mut position::Properties {
578        SetPosition::properties(&mut self.position)
579    }
580}
581
582impl SetColor<ColorScalar> for Mesh {
583    fn rgba_mut(&mut self) -> &mut Option<LinSrgba> {
584        &mut self.fill_color.get_or_insert_with(Default::default).0
585    }
586}
587
588impl From<Vertexless> for Primitive {
589    fn from(prim: Vertexless) -> Self {
590        Primitive::MeshVertexless(prim)
591    }
592}
593
594impl From<Mesh> for Primitive {
595    fn from(prim: Mesh) -> Self {
596        Primitive::Mesh(prim)
597    }
598}
599
600impl Into<Option<Vertexless>> for Primitive {
601    fn into(self) -> Option<Vertexless> {
602        match self {
603            Primitive::MeshVertexless(prim) => Some(prim),
604            _ => None,
605        }
606    }
607}
608
609impl Into<Option<Mesh>> for Primitive {
610    fn into(self) -> Option<Mesh> {
611        match self {
612            Primitive::Mesh(prim) => Some(prim),
613            _ => None,
614        }
615    }
616}