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}