tetra/graphics/
mesh.rs

1//! Functions and types relating to meshes and shape drawing.
2//!
3//! # Performance
4//!
5//! This module gives you very low level control over the geometry that you're rendering - while that's useful,
6//! it requires you to be a bit more careful about performance than other areas of Tetra's API. Ensure that you
7//! read the docs for the various buffer/mesh types to understand their performance characteristics before
8//! using them.
9
10pub use lyon_tessellation::path::builder::BorderRadii;
11
12use std::rc::Rc;
13
14use bytemuck::{Pod, Zeroable};
15use lyon_tessellation::geom::euclid::Point2D;
16use lyon_tessellation::math::{Angle, Box2D, Point, Vector};
17use lyon_tessellation::path::{Polygon, Winding};
18use lyon_tessellation::{
19    BuffersBuilder, FillOptions, FillTessellator, FillVertex, FillVertexConstructor, StrokeOptions,
20    StrokeTessellator, StrokeVertex, StrokeVertexConstructor, VertexBuffers,
21};
22
23use crate::graphics::{self, Color, DrawParams, Rectangle, Texture};
24use crate::math::Vec2;
25use crate::platform::{RawIndexBuffer, RawVertexBuffer};
26use crate::Context;
27use crate::{Result, TetraError};
28
29/// An individual piece of vertex data.
30#[repr(C)]
31#[derive(Debug, Default, Copy, Clone, PartialEq)]
32pub struct Vertex {
33    /// The position of the vertex, in screen co-ordinates.
34    ///
35    /// The transform matrix will be applied to this value, followed by a projection
36    /// from screen co-ordinates to device co-ordinates.
37    pub position: Vec2<f32>,
38
39    /// The texture co-ordinates that should be sampled for this vertex.
40    ///
41    /// Both the X and the Y should be between 0.0 and 1.0.
42    pub uv: Vec2<f32>,
43
44    /// The color of the vertex.
45    ///
46    /// This will be multiplied by the `color` of the `DrawParams` when drawing a
47    /// mesh.
48    pub color: Color,
49}
50
51impl Vertex {
52    /// Creates a new vertex.
53    pub fn new(position: Vec2<f32>, uv: Vec2<f32>, color: Color) -> Vertex {
54        Vertex {
55            position,
56            uv,
57            color,
58        }
59    }
60}
61
62// SAFETY: While the contract for `Pod` states that all fields should also be `Pod`,
63// that isn't possible without upstream changes. All of the fields meet the
64// *requirements* to be `Pod`, however, so this should not be unsound.
65unsafe impl Pod for Vertex {}
66unsafe impl Zeroable for Vertex {}
67
68/// The expected usage of a GPU buffer.
69///
70/// The GPU may optionally use this to optimize data storage and access.
71#[derive(Copy, Clone, Debug, PartialEq, Eq)]
72pub enum BufferUsage {
73    /// The buffer's data is not expected to change after creation.
74    Static,
75
76    /// The buffer's data is expected to change occasionally after creation.
77    Dynamic,
78
79    /// The buffer's data is expected to change every frame.
80    Stream,
81}
82
83/// The ordering of the vertices in a piece of geometry.
84#[derive(Copy, Clone, Debug, PartialEq, Eq)]
85pub enum VertexWinding {
86    /// The vertices are in clockwise order.
87    Clockwise,
88
89    /// The vertices are in counter-clockwise order.
90    CounterClockwise,
91}
92
93impl VertexWinding {
94    /// Returns the opposite winding, compared to `self`.
95    pub fn flipped(self) -> VertexWinding {
96        match self {
97            VertexWinding::Clockwise => VertexWinding::CounterClockwise,
98            VertexWinding::CounterClockwise => VertexWinding::Clockwise,
99        }
100    }
101}
102
103/// Vertex data, stored in GPU memory.
104///
105/// This data can be drawn to the screen via a [`Mesh`].
106///
107/// # Performance
108///
109/// When you create or modify a vertex buffer, you are effectively 'uploading' data to the GPU, which
110/// can be relatively slow. You should try to minimize how often you do this - for example, if a piece
111/// of geometry does not change from frame to frame, reuse the buffer instead of recreating it.
112///
113/// You can clone a vertex buffer cheaply, as it is a [reference-counted](https://doc.rust-lang.org/std/rc/struct.Rc.html)
114/// handle to a GPU resource. However, this does mean that modifying a buffer (e.g.
115/// calling `set_data`) will also affect any clones that exist of it.
116///
117#[derive(Clone, Debug, PartialEq)]
118pub struct VertexBuffer {
119    handle: Rc<RawVertexBuffer>,
120}
121
122impl VertexBuffer {
123    /// Creates a new vertex buffer.
124    ///
125    /// The buffer will be created with the [`BufferUsage::Dynamic`] usage hint - this can
126    /// be overridden via the [`with_usage`](Self::with_usage) constructor.
127    ///
128    /// # Errors
129    ///
130    /// * [`TetraError::PlatformError`] will be returned if the underlying
131    ///   graphics API encounters an error.
132    pub fn new(ctx: &mut Context, vertices: &[Vertex]) -> Result<VertexBuffer> {
133        VertexBuffer::with_usage(ctx, vertices, BufferUsage::Dynamic)
134    }
135
136    /// Creates a new vertex buffer, with the specified usage hint.
137    ///
138    /// The GPU may optionally use the usage hint to optimize data storage and access.
139    ///
140    /// # Errors
141    ///
142    /// * [`TetraError::PlatformError`] will be returned if the underlying
143    ///   graphics API encounters an error.
144    pub fn with_usage(
145        ctx: &mut Context,
146        vertices: &[Vertex],
147        usage: BufferUsage,
148    ) -> Result<VertexBuffer> {
149        let buffer = ctx.device.new_vertex_buffer(vertices.len(), usage)?;
150
151        ctx.device.set_vertex_buffer_data(&buffer, vertices, 0);
152
153        Ok(VertexBuffer {
154            handle: Rc::new(buffer),
155        })
156    }
157
158    /// Uploads new vertex data to the GPU.
159    ///
160    /// # Panics
161    ///
162    /// Panics if the offset is out of bounds.
163    pub fn set_data(&self, ctx: &mut Context, vertices: &[Vertex], offset: usize) {
164        ctx.device
165            .set_vertex_buffer_data(&self.handle, vertices, offset);
166    }
167
168    /// Creates a mesh using this buffer.
169    ///
170    /// This is a shortcut for calling [`Mesh::new`].
171    pub fn into_mesh(self) -> Mesh {
172        Mesh::new(self)
173    }
174}
175
176/// Index data, stored in GPU memory.
177///
178/// An index buffer can be used as part of a [`Mesh`], in order to describe which vertex data should be drawn,
179/// and what order it should be drawn in.
180///
181/// For example, to draw a square with raw vertex data, you need to use six vertices (two triangles,
182/// with three vertices each). This is inefficient, as two of those vertices are shared by the two
183/// triangles! Using an index buffer, you can instruct the graphics card to use vertices
184/// multiple times while constructing your square.
185///
186/// Index data is made up of [`u32`] values, each of which correspond to the zero-based index of a vertex.
187/// For example, to get the mesh to draw the third vertex, then the first, then the second, you would
188/// create an index buffer containing `[2, 0, 1]`.
189///
190/// # Performance
191///
192/// When you create or modify an index buffer, you are effectively 'uploading' data to the GPU, which
193/// can be relatively slow. You should try to minimize how often you do this - for example, if a piece
194/// of geometry does not change from frame to frame, reuse the buffer instead of recreating it.
195///
196/// You can clone an index buffer cheaply, as it is a [reference-counted](https://doc.rust-lang.org/std/rc/struct.Rc.html)
197/// handle to a GPU resource. However, this does mean that modifying a buffer (e.g.
198/// calling `set_data`) will also affect any clones that exist of it.
199#[derive(Clone, Debug, PartialEq)]
200pub struct IndexBuffer {
201    handle: Rc<RawIndexBuffer>,
202}
203
204impl IndexBuffer {
205    /// Creates a new index buffer.
206    ///
207    /// The buffer will be created with the [`BufferUsage::Dynamic`] usage hint - this can
208    /// be overridden via the [`with_usage`](Self::with_usage) constructor.
209    ///
210    /// # Errors
211    ///
212    /// * [`TetraError::PlatformError`] will be returned if the underlying
213    ///   graphics API encounters an error.
214    pub fn new(ctx: &mut Context, indices: &[u32]) -> Result<IndexBuffer> {
215        IndexBuffer::with_usage(ctx, indices, BufferUsage::Dynamic)
216    }
217
218    /// Creates a new index buffer, with the specified usage hint.
219    ///
220    /// The GPU may optionally use the usage hint to optimize data storage and access.
221    ///
222    /// # Errors
223    ///
224    /// * [`TetraError::PlatformError`] will be returned if the underlying
225    ///   graphics API encounters an error.
226    pub fn with_usage(
227        ctx: &mut Context,
228        indices: &[u32],
229        usage: BufferUsage,
230    ) -> Result<IndexBuffer> {
231        let buffer = ctx.device.new_index_buffer(indices.len(), usage)?;
232
233        ctx.device.set_index_buffer_data(&buffer, indices, 0);
234
235        Ok(IndexBuffer {
236            handle: Rc::new(buffer),
237        })
238    }
239
240    /// Sends new index data to the GPU.
241    ///
242    /// # Panics
243    ///
244    /// Panics if the offset is out of bounds.
245    pub fn set_data(&self, ctx: &mut Context, indices: &[u32], offset: usize) {
246        ctx.device
247            .set_index_buffer_data(&self.handle, indices, offset);
248    }
249}
250
251#[derive(Copy, Clone, Debug)]
252struct DrawRange {
253    start: usize,
254    count: usize,
255}
256
257/// Ways of drawing a shape.
258#[derive(Copy, Clone, Debug)]
259pub enum ShapeStyle {
260    /// A filled shape.
261    Fill,
262    /// An outlined shape with the specified stroke width.
263    Stroke(f32),
264}
265
266/// A 2D mesh that can be drawn to the screen.
267///
268/// A `Mesh` is a wrapper for a [`VertexBuffer`], which allows it to be drawn in combination with several
269/// optional modifiers:
270///
271/// * A [`Texture`] that individual vertices can sample from.
272/// * An [`IndexBuffer`] that can be used to modify the order/subset of vertices that are drawn.
273/// * A winding order, which determines which side of the geometry is front-facing.
274/// * A backface culling flag, which determines whether back-facing geometry should be drawn.
275/// * A draw range, which can be used to draw subsections of the mesh.
276///
277/// Without a texture set, the mesh will be drawn in white - the `color` attribute on the [vertex data](Vertex) or
278/// [`DrawParams`] can be used to change this.
279///
280/// # Performance
281///
282/// Creating or cloning a mesh is a very cheap operation, as they are effectively just bundles
283/// of resources that live on the GPU (such as buffers and textures). However, creating or
284/// modifying those underlying resources may be slow - make sure you read the docs for
285/// each type to understand their performance characteristics.
286///
287/// Note that, unlike most rendering in Tetra, mesh rendering is *not* batched by default - each time you
288/// draw the mesh will result in a seperate draw call.
289///
290/// # Examples
291///
292/// The [`mesh`](https://github.com/17cupsofcoffee/tetra/blob/main/examples/mesh.rs) example demonstrates
293/// how to build and draw a simple mesh.
294///
295/// The [`shapes`](https://github.com/17cupsofcoffee/tetra/blob/main/examples/shapes.rs) example demonstrates
296/// how to draw primitive shapes, both through the simplified API on `Mesh`, and the more powerful
297/// [`GeometryBuilder`] API.  
298#[derive(Clone, Debug)]
299pub struct Mesh {
300    vertex_buffer: VertexBuffer,
301    index_buffer: Option<IndexBuffer>,
302    texture: Option<Texture>,
303    draw_range: Option<DrawRange>,
304    winding: VertexWinding,
305    backface_culling: bool,
306}
307
308impl Mesh {
309    /// Creates a new mesh, using the provided vertex buffer.
310    pub fn new(vertex_buffer: VertexBuffer) -> Mesh {
311        Mesh {
312            vertex_buffer,
313            index_buffer: None,
314            texture: None,
315            draw_range: None,
316            winding: VertexWinding::CounterClockwise,
317            backface_culling: true,
318        }
319    }
320
321    /// Creates a new mesh, using the provided vertex and index buffers.
322    pub fn indexed(vertex_buffer: VertexBuffer, index_buffer: IndexBuffer) -> Mesh {
323        Mesh {
324            vertex_buffer,
325            index_buffer: Some(index_buffer),
326            texture: None,
327            winding: VertexWinding::CounterClockwise,
328            draw_range: None,
329            backface_culling: true,
330        }
331    }
332
333    /// Draws the mesh to the screen (or to a canvas, if one is enabled).
334    pub fn draw<P>(&self, ctx: &mut Context, params: P)
335    where
336        P: Into<DrawParams>,
337    {
338        self.draw_instanced(ctx, 1, params);
339    }
340
341    /// Draws multiple instances of the mesh to the screen (or to a canvas,
342    /// if one is enabled).
343    ///
344    /// You will need to use a custom [`Shader`](crate::graphics::Shader) in order to pass unique
345    /// properties to each instance. Currently, the easiest way of doing this is via uniform
346    /// arrays - however, there is a hardware-determined limit on how many uniform locations
347    /// an individual shader can use, so this may not work if you're rendering a large
348    /// number of objects.
349    ///
350    /// This should usually only be used for complex meshes - instancing can be inefficient
351    /// for simple geometry (e.g. quads). That said, as with all things performance-related,
352    /// benchmark it before coming to any conclusions!
353    pub fn draw_instanced<P>(&self, ctx: &mut Context, instances: usize, params: P)
354    where
355        P: Into<DrawParams>,
356    {
357        graphics::flush(ctx);
358
359        let texture = self
360            .texture
361            .as_ref()
362            .unwrap_or(&ctx.graphics.default_texture);
363
364        let shader = ctx
365            .graphics
366            .shader
367            .as_ref()
368            .unwrap_or(&ctx.graphics.default_shader);
369
370        let params = params.into();
371        let model_matrix = params.to_matrix();
372
373        // TODO: Failing to apply the defaults should be handled more gracefully than this,
374        // but we can't do that without breaking changes.
375        let _ = shader.set_default_uniforms(
376            &mut ctx.device,
377            ctx.graphics.projection_matrix * ctx.graphics.transform_matrix * model_matrix,
378            params.color,
379        );
380
381        ctx.device.cull_face(self.backface_culling);
382
383        // Because canvas rendering is effectively done upside-down, the winding order is the opposite
384        // of what you'd expect in that case.
385        ctx.device.front_face(match &ctx.graphics.canvas {
386            None => self.winding,
387            Some(_) => self.winding.flipped(),
388        });
389
390        let (start, count) = match (self.draw_range, &self.index_buffer) {
391            (Some(d), _) => (d.start, d.count),
392            (_, Some(i)) => (0, i.handle.count()),
393            (_, None) => (0, self.vertex_buffer.handle.count()),
394        };
395
396        ctx.device.draw_instanced(
397            &self.vertex_buffer.handle,
398            self.index_buffer.as_ref().map(|i| &*i.handle),
399            &texture.data.handle,
400            &shader.data.handle,
401            start,
402            count,
403            instances,
404        );
405    }
406
407    /// Gets a reference to the vertex buffer contained within this mesh.
408    pub fn vertex_buffer(&self) -> &VertexBuffer {
409        &self.vertex_buffer
410    }
411
412    /// Sets the vertex buffer that will be used when drawing the mesh.
413    pub fn set_vertex_buffer(&mut self, vertex_buffer: VertexBuffer) {
414        self.vertex_buffer = vertex_buffer;
415    }
416
417    /// Gets a reference to the index buffer contained within this mesh.
418    ///
419    /// Returns [`None`] if this mesh does not currently have an index buffer attatched.
420    pub fn index_buffer(&self) -> Option<&IndexBuffer> {
421        self.index_buffer.as_ref()
422    }
423
424    /// Sets the index buffer that will be used when drawing the mesh.
425    pub fn set_index_buffer(&mut self, index_buffer: IndexBuffer) {
426        self.index_buffer = Some(index_buffer);
427    }
428
429    /// Resets the mesh to no longer use indexed drawing.
430    pub fn reset_index_buffer(&mut self) {
431        self.index_buffer = None;
432    }
433
434    /// Gets a reference to the texture contained within this mesh.
435    ///
436    /// Returns [`None`] if this mesh does not currently have an texture attatched.
437    pub fn texture(&self) -> Option<&Texture> {
438        self.texture.as_ref()
439    }
440
441    /// Sets the texture that will be used when drawing the mesh.
442    pub fn set_texture(&mut self, texture: Texture) {
443        self.texture = Some(texture);
444    }
445
446    /// Resets the mesh to be untextured.
447    pub fn reset_texture(&mut self) {
448        self.texture = None;
449    }
450
451    /// Returns which winding order represents front-facing geometry in this mesh.
452    ///
453    /// Back-facing geometry will be culled (not rendered) by default, but
454    /// this can be changed via [`set_backface_culling`](Self::set_backface_culling).
455    ///
456    /// The default winding order is counter-clockwise.
457    pub fn front_face_winding(&self) -> VertexWinding {
458        self.winding
459    }
460
461    /// Sets which winding order represents front-facing geometry in this mesh.
462    ///
463    /// Back-facing geometry will be culled (not rendered) by default, but
464    /// this can be changed via [`set_backface_culling`](Self::set_backface_culling).
465    ///
466    /// The default winding order is counter-clockwise.
467    pub fn set_front_face_winding(&mut self, winding: VertexWinding) {
468        self.winding = winding;
469    }
470
471    /// Returns whether or not this mesh will cull (not render) back-facing geometry.
472    ///
473    /// By default, backface culling is enabled, counter-clockwise vertices are
474    /// considered front-facing, and clockwise vertices are considered back-facing.
475    /// This can be modified via [`set_backface_culling`](Self::set_backface_culling) and
476    /// [`set_front_face_winding`](Self::set_front_face_winding).
477    pub fn backface_culling(&self) -> bool {
478        self.backface_culling
479    }
480
481    /// Sets whether or not this mesh will cull (not render) back-facing geometry.
482    ///
483    /// By default, backface culling is enabled, counter-clockwise vertices are
484    /// considered front-facing, and clockwise vertices are considered back-facing.
485    /// This can be modified via this function and [`set_front_face_winding`](Self::set_front_face_winding).
486    pub fn set_backface_culling(&mut self, enabled: bool) {
487        self.backface_culling = enabled;
488    }
489
490    /// Sets the range of vertices (or indices, if the mesh is indexed) that should be included
491    /// when drawing this mesh.
492    ///
493    /// This can be useful if you have a large mesh but you only want to want to draw a
494    /// subsection of it, or if you want to draw a mesh in multiple stages.
495    pub fn set_draw_range(&mut self, start: usize, count: usize) {
496        self.draw_range = Some(DrawRange { start, count });
497    }
498
499    /// Sets the mesh to include all of its data when drawing.
500    pub fn reset_draw_range(&mut self) {
501        self.draw_range = None;
502    }
503}
504
505/// # Shape constructors
506impl Mesh {
507    /// Creates a new rectangle mesh.
508    ///
509    /// If you need to draw multiple shapes, consider using [`GeometryBuilder`] to generate a combined mesh
510    /// instead.
511    ///
512    /// # Errors
513    ///
514    /// * [`TetraError::TessellationError`] will be returned if the shape
515    ///   could not be turned into vertex data.
516    /// * [`TetraError::PlatformError`] will be returned if the underlying
517    ///   graphics API encounters an error.
518    pub fn rectangle(ctx: &mut Context, style: ShapeStyle, rectangle: Rectangle) -> Result<Mesh> {
519        GeometryBuilder::new()
520            .rectangle(style, rectangle)?
521            .build_mesh(ctx)
522    }
523
524    /// Creates a new rounded rectangle mesh.
525    ///
526    /// If you need to draw multiple shapes, consider using [`GeometryBuilder`] to generate a combined mesh
527    /// instead.
528    ///
529    /// # Errors
530    ///
531    /// * [`TetraError::TessellationError`] will be returned if the shape
532    ///   could not be turned into vertex data.
533    /// * [`TetraError::PlatformError`] will be returned if the underlying
534    ///   graphics API encounters an error.
535    pub fn rounded_rectangle(
536        ctx: &mut Context,
537        style: ShapeStyle,
538        rectangle: Rectangle,
539        radii: BorderRadii,
540    ) -> Result<Mesh> {
541        GeometryBuilder::new()
542            .rounded_rectangle(style, rectangle, radii)?
543            .build_mesh(ctx)
544    }
545
546    /// Creates a new circle mesh.
547    ///
548    /// If you need to draw multiple shapes, consider using [`GeometryBuilder`] to generate a combined mesh
549    /// instead.
550    ///
551    /// # Errors
552    ///
553    /// * [`TetraError::TessellationError`] will be returned if the shape
554    ///   could not be turned into vertex data.
555    /// * [`TetraError::PlatformError`] will be returned if the underlying
556    ///   graphics API encounters an error.
557    pub fn circle(
558        ctx: &mut Context,
559        style: ShapeStyle,
560        center: Vec2<f32>,
561        radius: f32,
562    ) -> Result<Mesh> {
563        GeometryBuilder::new()
564            .circle(style, center, radius)?
565            .build_mesh(ctx)
566    }
567
568    /// Creates a new ellipse mesh.
569    ///
570    /// If you need to draw multiple shapes, consider using [`GeometryBuilder`] to generate a combined mesh
571    /// instead.
572    ///
573    /// # Errors
574    ///
575    /// * [`TetraError::TessellationError`] will be returned if the shape
576    ///   could not be turned into vertex data.
577    /// * [`TetraError::PlatformError`] will be returned if the underlying
578    ///   graphics API encounters an error.
579    pub fn ellipse(
580        ctx: &mut Context,
581        style: ShapeStyle,
582        center: Vec2<f32>,
583        radii: Vec2<f32>,
584    ) -> Result<Mesh> {
585        GeometryBuilder::new()
586            .ellipse(style, center, radii)?
587            .build_mesh(ctx)
588    }
589
590    /// Creates a new polygon mesh.
591    ///
592    /// If you need to draw multiple shapes, consider using [`GeometryBuilder`] to generate a combined mesh
593    /// instead.
594    ///
595    /// # Errors
596    ///
597    /// * [`TetraError::TessellationError`] will be returned if the shape
598    ///   could not be turned into vertex data.
599    /// * [`TetraError::PlatformError`] will be returned if the underlying
600    ///   graphics API encounters an error.
601    pub fn polygon(ctx: &mut Context, style: ShapeStyle, points: &[Vec2<f32>]) -> Result<Mesh> {
602        GeometryBuilder::new()
603            .polygon(style, points)?
604            .build_mesh(ctx)
605    }
606
607    /// Creates a new polyline mesh.
608    ///
609    /// If you need to draw multiple shapes, consider using [`GeometryBuilder`] to generate a combined mesh
610    /// instead.
611    ///
612    /// # Errors
613    ///
614    /// * [`TetraError::TessellationError`] will be returned if the shape
615    ///   could not be turned into vertex data.
616    /// * [`TetraError::PlatformError`] will be returned if the underlying
617    ///   graphics API encounters an error.
618    pub fn polyline(ctx: &mut Context, stroke_width: f32, points: &[Vec2<f32>]) -> Result<Mesh> {
619        GeometryBuilder::new()
620            .polyline(stroke_width, points)?
621            .build_mesh(ctx)
622    }
623}
624
625impl From<VertexBuffer> for Mesh {
626    fn from(buffer: VertexBuffer) -> Self {
627        Mesh::new(buffer)
628    }
629}
630
631fn to_box2d(rectangle: Rectangle) -> Box2D {
632    Box2D::new(
633        Point2D::new(rectangle.x, rectangle.y),
634        Point2D::new(rectangle.right(), rectangle.bottom()),
635    )
636}
637
638struct TetraVertexConstructor(Color);
639
640impl FillVertexConstructor<Vertex> for TetraVertexConstructor {
641    fn new_vertex(&mut self, vertex: FillVertex) -> Vertex {
642        let position = vertex.position();
643
644        Vertex::new(Vec2::new(position.x, position.y), Vec2::zero(), self.0)
645    }
646}
647
648impl StrokeVertexConstructor<Vertex> for TetraVertexConstructor {
649    fn new_vertex(&mut self, vertex: StrokeVertex) -> Vertex {
650        let position = vertex.position();
651
652        Vertex::new(Vec2::new(position.x, position.y), Vec2::zero(), self.0)
653    }
654}
655
656/// A builder for creating primitive shape geometry, and associated buffers/meshes.
657///
658/// # Performance
659///
660/// `GeometryBuilder` stores the generated vertex and index data in a pair of `Vec`s. This means that creating
661/// a new builder (as well as cloning an existing one) will allocate memory. Consider reusing a `GeometryBuilder`
662/// if you need to reuse the generated data, or if you need to create new data every frame.
663///
664/// Creating buffers/meshes from the generated geometry is a fairly expensive operation. Try to avoid creating
665/// lots of seperate buffers/meshes, and pack multiple shapes into the same buffers/mesh if
666/// they don't move relative to each other.
667///
668/// # Examples
669///
670/// The [`shapes`](https://github.com/17cupsofcoffee/tetra/blob/main/examples/shapes.rs) example demonstrates
671/// how to draw primitive shapes, both through the simplified API on [`Mesh`], and the more powerful
672/// `GeometryBuilder` API.  
673#[derive(Debug, Clone)]
674pub struct GeometryBuilder {
675    data: VertexBuffers<Vertex, u32>,
676    color: Color,
677}
678
679impl GeometryBuilder {
680    /// Creates a new empty geometry builder.
681    pub fn new() -> GeometryBuilder {
682        GeometryBuilder {
683            data: VertexBuffers::new(),
684            color: Color::WHITE,
685        }
686    }
687
688    /// Adds a rectangle.
689    ///
690    /// # Errors
691    ///
692    /// * [`TetraError::TessellationError`] will be returned if the shape
693    ///   could not be turned into vertex data.
694    pub fn rectangle(
695        &mut self,
696        style: ShapeStyle,
697        rectangle: Rectangle,
698    ) -> Result<&mut GeometryBuilder> {
699        let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
700
701        match style {
702            ShapeStyle::Fill => {
703                let options = FillOptions::default();
704                let mut tessellator = FillTessellator::new();
705                tessellator
706                    .tessellate_rectangle(&to_box2d(rectangle), &options, &mut builder)
707                    .map_err(TetraError::TessellationError)?;
708            }
709
710            ShapeStyle::Stroke(width) => {
711                let options = StrokeOptions::default().with_line_width(width);
712                let mut tessellator = StrokeTessellator::new();
713                tessellator
714                    .tessellate_rectangle(&to_box2d(rectangle), &options, &mut builder)
715                    .map_err(TetraError::TessellationError)?;
716            }
717        }
718
719        Ok(self)
720    }
721
722    /// Adds a rounded rectangle.
723    ///
724    /// # Errors
725    ///
726    /// * [`TetraError::TessellationError`] will be returned if the shape
727    ///   could not be turned into vertex data.
728    pub fn rounded_rectangle(
729        &mut self,
730        style: ShapeStyle,
731        rectangle: Rectangle,
732        radii: BorderRadii,
733    ) -> Result<&mut GeometryBuilder> {
734        let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
735
736        match style {
737            ShapeStyle::Fill => {
738                let options = FillOptions::default();
739                let mut tessellator = FillTessellator::new();
740                let mut builder = tessellator.builder(&options, &mut builder);
741                builder.add_rounded_rectangle(&to_box2d(rectangle), &radii, Winding::Positive);
742                builder.build().map_err(TetraError::TessellationError)?;
743            }
744
745            ShapeStyle::Stroke(width) => {
746                let options = StrokeOptions::default().with_line_width(width);
747                let mut tessellator = StrokeTessellator::new();
748                let mut builder = tessellator.builder(&options, &mut builder);
749                builder.add_rounded_rectangle(&to_box2d(rectangle), &radii, Winding::Positive);
750                builder.build().map_err(TetraError::TessellationError)?;
751            }
752        }
753
754        Ok(self)
755    }
756
757    /// Adds a circle.
758    ///
759    /// # Errors
760    ///
761    /// * [`TetraError::TessellationError`] will be returned if the shape
762    ///   could not be turned into vertex data.
763    pub fn circle(
764        &mut self,
765        style: ShapeStyle,
766        center: Vec2<f32>,
767        radius: f32,
768    ) -> Result<&mut GeometryBuilder> {
769        let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
770
771        match style {
772            ShapeStyle::Fill => {
773                let options = FillOptions::default();
774                let mut tessellator = FillTessellator::new();
775
776                tessellator
777                    .tessellate_circle(
778                        Point::new(center.x, center.y),
779                        radius,
780                        &options,
781                        &mut builder,
782                    )
783                    .map_err(TetraError::TessellationError)?;
784            }
785
786            ShapeStyle::Stroke(width) => {
787                let options = StrokeOptions::default().with_line_width(width);
788                let mut tessellator = StrokeTessellator::new();
789
790                tessellator
791                    .tessellate_circle(
792                        Point::new(center.x, center.y),
793                        radius,
794                        &options,
795                        &mut builder,
796                    )
797                    .map_err(TetraError::TessellationError)?;
798            }
799        }
800
801        Ok(self)
802    }
803
804    /// Adds an ellipse.
805    ///
806    /// # Errors
807    ///
808    /// * [`TetraError::TessellationError`] will be returned if the shape
809    ///   could not be turned into vertex data.
810    pub fn ellipse(
811        &mut self,
812        style: ShapeStyle,
813        center: Vec2<f32>,
814        radii: Vec2<f32>,
815    ) -> Result<&mut GeometryBuilder> {
816        let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
817
818        match style {
819            ShapeStyle::Fill => {
820                let options = FillOptions::default();
821                let mut tessellator = FillTessellator::new();
822
823                tessellator
824                    .tessellate_ellipse(
825                        Point::new(center.x, center.y),
826                        Vector::new(radii.x, radii.y),
827                        Angle::radians(0.0),
828                        Winding::Positive,
829                        &options,
830                        &mut builder,
831                    )
832                    .map_err(TetraError::TessellationError)?;
833            }
834
835            ShapeStyle::Stroke(width) => {
836                let options = StrokeOptions::default().with_line_width(width);
837                let mut tessellator = StrokeTessellator::new();
838
839                tessellator
840                    .tessellate_ellipse(
841                        Point::new(center.x, center.y),
842                        Vector::new(radii.x, radii.y),
843                        Angle::radians(0.0),
844                        Winding::Positive,
845                        &options,
846                        &mut builder,
847                    )
848                    .map_err(TetraError::TessellationError)?;
849            }
850        }
851
852        Ok(self)
853    }
854
855    /// Adds a polygon.
856    ///
857    /// # Errors
858    ///
859    /// * [`TetraError::TessellationError`] will be returned if the shape
860    ///   could not be turned into vertex data.
861    pub fn polygon(
862        &mut self,
863        style: ShapeStyle,
864        points: &[Vec2<f32>],
865    ) -> Result<&mut GeometryBuilder> {
866        let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
867
868        let points: Vec<Point> = points
869            .iter()
870            .map(|point| Point::new(point.x, point.y))
871            .collect();
872
873        let polygon = Polygon {
874            points: &points,
875            closed: true,
876        };
877
878        match style {
879            ShapeStyle::Fill => {
880                let options = FillOptions::default();
881                let mut tessellator = FillTessellator::new();
882
883                tessellator
884                    .tessellate_polygon(polygon, &options, &mut builder)
885                    .map_err(TetraError::TessellationError)?;
886            }
887
888            ShapeStyle::Stroke(width) => {
889                let options = StrokeOptions::default().with_line_width(width);
890                let mut tessellator = StrokeTessellator::new();
891
892                tessellator
893                    .tessellate_polygon(polygon, &options, &mut builder)
894                    .map_err(TetraError::TessellationError)?;
895            }
896        }
897
898        Ok(self)
899    }
900
901    /// Adds a polyline.
902    ///
903    /// # Errors
904    ///
905    /// * [`TetraError::TessellationError`] will be returned if the shape
906    ///   could not be turned into vertex data.
907    pub fn polyline(
908        &mut self,
909        stroke_width: f32,
910        points: &[Vec2<f32>],
911    ) -> Result<&mut GeometryBuilder> {
912        let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
913
914        let points: Vec<Point> = points
915            .iter()
916            .map(|point| Point::new(point.x, point.y))
917            .collect();
918
919        let polygon = Polygon {
920            points: &points,
921            closed: false,
922        };
923
924        let options = StrokeOptions::default().with_line_width(stroke_width);
925        let mut tessellator = StrokeTessellator::new();
926
927        tessellator
928            .tessellate_polygon(polygon, &options, &mut builder)
929            .map_err(TetraError::TessellationError)?;
930
931        Ok(self)
932    }
933
934    /// Sets the color that will be used for subsequent shapes.
935    ///
936    /// You can also use [`DrawParams::color`](super::DrawParams) to tint an entire mesh -
937    /// this method only needs to be used if you want to display multiple colors in a
938    /// single piece of geometry.
939    pub fn set_color(&mut self, color: Color) -> &mut GeometryBuilder {
940        self.color = color;
941        self
942    }
943
944    /// Clears the geometry builder's data.
945    pub fn clear(&mut self) -> &mut GeometryBuilder {
946        self.data.vertices.clear();
947        self.data.indices.clear();
948
949        self
950    }
951
952    /// Returns a view of the generated vertex data.
953    pub fn vertices(&self) -> &[Vertex] {
954        &self.data.vertices
955    }
956
957    /// Returns a view of the generated index data.
958    pub fn indices(&self) -> &[u32] {
959        &self.data.indices
960    }
961
962    /// Consumes the builder, returning the generated geometry.
963    pub fn into_data(self) -> (Vec<Vertex>, Vec<u32>) {
964        (self.data.vertices, self.data.indices)
965    }
966
967    /// Builds a vertex and index buffer from the generated geometry.
968    ///
969    /// This involves uploading the geometry to the GPU, and is a fairly expensive operation.
970    ///
971    /// # Errors
972    ///
973    /// * [`TetraError::PlatformError`] will be returned if the underlying
974    ///   graphics API encounters an error.
975    pub fn build_buffers(&self, ctx: &mut Context) -> Result<(VertexBuffer, IndexBuffer)> {
976        Ok((
977            VertexBuffer::new(ctx, &self.data.vertices)?,
978            IndexBuffer::new(ctx, &self.data.indices)?,
979        ))
980    }
981
982    /// Builds a mesh from the generated geometry.
983    ///
984    /// This involves uploading the geometry to the GPU, and is a fairly expensive operation.
985    ///
986    /// # Errors
987    ///
988    /// * [`TetraError::PlatformError`] will be returned if the underlying
989    ///   graphics API encounters an error.
990    pub fn build_mesh(&self, ctx: &mut Context) -> Result<Mesh> {
991        let (vertex_buffer, index_buffer) = self.build_buffers(ctx)?;
992
993        Ok(Mesh::indexed(vertex_buffer, index_buffer))
994    }
995}
996
997impl Default for GeometryBuilder {
998    fn default() -> Self {
999        GeometryBuilder::new()
1000    }
1001}