good_web_game/graphics/
mesh.rs

1#![allow(warnings)]
2use crate::{graphics::*, Context, GameError, GameResult};
3use lyon::path::builder::PathBuilder;
4use lyon::path::Polygon;
5use lyon::tessellation as t;
6use lyon::{self, math::Point as LPoint};
7
8pub use self::t::{FillOptions, FillRule, LineCap, LineJoin, StrokeOptions};
9
10use crate::graphics::context::default_shader;
11use cgmath::{Matrix4, Point2, Transform, Vector2, Vector3, Vector4};
12use miniquad::{Buffer, BufferType, PassAction};
13use std::cell::RefCell;
14use std::convert::TryInto;
15use std::sync::Arc;
16
17#[derive(Debug, Clone, PartialEq)]
18#[repr(C)]
19pub struct Vertex {
20    pub pos: [f32; 2],
21    pub uv: [f32; 2],
22    pub color: [f32; 4],
23}
24
25/// A builder for creating [`Mesh`](struct.Mesh.html)es.
26///
27/// This allows you to easily make one `Mesh` containing
28/// many different complex pieces of geometry.  They don't
29/// have to be connected to each other, and will all be
30/// drawn at once.
31///
32/// Note that this doesn't try very hard to handle degenerate cases.  It can easily break if you
33/// tell it to do things that result in a circle of radius 0, a line of width 0, an infintessimally
34/// skinny triangle, or other mathematically inconvenient things like that.
35///
36/// The following example shows how to build a mesh containing a line and a circle:
37///
38/// ```rust,no_run
39/// # use ggez::*;
40/// # use ggez::graphics::*;
41/// # fn main() -> GameResult {
42/// # let ctx = &mut ContextBuilder::new("foo", "bar").build().unwrap().0;
43/// let mesh: Mesh = MeshBuilder::new()
44///     .line(&[glam::vec2(20.0, 20.0), glam::vec2(40.0, 20.0)], 4.0, (255, 0, 0).into())?
45///     .circle(DrawMode::fill(), glam::vec2(60.0, 38.0), 40.0, 1.0, (0, 255, 0).into())?
46///     .build(ctx)?;
47/// # Ok(()) }
48/// ```
49/// A more sophisticated example:
50///
51/// ```rust,no_run
52/// use ggez::{Context, GameResult};
53/// use ggez::graphics::{self, DrawMode, MeshBuilder};
54///
55/// fn draw_danger_signs(ctx: &mut Context) -> GameResult {
56///     // Initialize a builder instance.
57///     let mesh = MeshBuilder::new()
58///         // Add vertices for 3 lines (in an approximate equilateral triangle).
59///         .line(
60///             &[
61///                 glam::vec2(0.0, 0.0),
62///                 glam::vec2(-30.0, 52.0),
63///                 glam::vec2(30.0, 52.0),
64///                 glam::vec2(0.0, 0.0),
65///             ],
66///             1.0,
67///             graphics::Color::WHITE,
68///         )?
69///         // Add vertices for an exclamation mark!
70///         .ellipse(DrawMode::fill(), glam::vec2(0.0, 25.0), 2.0, 15.0, 2.0, graphics::Color::WHITE,)?
71///         .circle(DrawMode::fill(), glam::vec2(0.0, 45.0), 2.0, 2.0, graphics::Color::WHITE,)?
72///         // Finalize then unwrap. Unwrapping via `?` operator either yields the final `Mesh`,
73///         // or propagates the error (note return type).
74///         .build(ctx)?;
75///     // Draw 3 meshes in a line, 1st and 3rd tilted by 1 radian.
76///     graphics::draw(ctx, &mesh, (glam::vec2(50.0, 50.0), -1.0, graphics::Color::WHITE))?;
77///     graphics::draw(ctx, &mesh, (glam::vec2(150.0, 50.0), 0.0, graphics::Color::WHITE))?;
78///     graphics::draw(ctx, &mesh, (glam::vec2(250.0, 50.0), 1.0, graphics::Color::WHITE))?;
79///     Ok(())
80/// }
81/// ```
82#[derive(Debug, Clone)]
83pub struct MeshBuilder {
84    buffer: t::geometry_builder::VertexBuffers<Vertex, u16>,
85    texture: Option<miniquad::Texture>,
86    tex_filter: Option<FilterMode>,
87    tex_clones_hack: Option<Arc<()>>,
88}
89
90impl Default for MeshBuilder {
91    fn default() -> Self {
92        Self {
93            buffer: t::VertexBuffers::new(),
94            texture: None,
95            tex_filter: None,
96            tex_clones_hack: None,
97        }
98    }
99}
100
101impl MeshBuilder {
102    /// Create a new `MeshBuilder`.
103    pub fn new() -> Self {
104        Self::default()
105    }
106
107    /// Create a new mesh for a line of one or more connected segments.
108    pub fn line<P>(&mut self, points: &[P], width: f32, color: Color) -> GameResult<&mut Self>
109    where
110        P: Into<mint::Point2<f32>> + Clone,
111    {
112        self.polyline(DrawMode::stroke(width), points, color)
113    }
114
115    /// Create a new mesh for a circle.
116    ///
117    /// For the meaning of the `tolerance` parameter, [see here](https://docs.rs/lyon_geom/0.11.0/lyon_geom/#flattening).
118    pub fn circle<P>(
119        &mut self,
120        mode: DrawMode,
121        point: P,
122        radius: f32,
123        tolerance: f32,
124        color: Color,
125    ) -> GameResult<&mut Self>
126    where
127        P: Into<mint::Point2<f32>>,
128    {
129        assert!(
130            tolerance > 0.0,
131            "Tolerances <= 0 are invalid, see https://github.com/ggez/ggez/issues/892"
132        );
133        {
134            let point = point.into();
135            let buffers = &mut self.buffer;
136            let vb = VertexBuilder { color };
137            match mode {
138                DrawMode::Fill(fill_options) => {
139                    let mut tessellator = t::FillTessellator::new();
140                    let _ = tessellator.tessellate_circle(
141                        t::math::point(point.x, point.y),
142                        radius,
143                        &fill_options.with_tolerance(tolerance),
144                        &mut t::BuffersBuilder::new(buffers, vb),
145                    );
146                }
147                DrawMode::Stroke(options) => {
148                    let mut tessellator = t::StrokeTessellator::new();
149                    let _ = tessellator.tessellate_circle(
150                        t::math::point(point.x, point.y),
151                        radius,
152                        &options.with_tolerance(tolerance),
153                        &mut t::BuffersBuilder::new(buffers, vb),
154                    );
155                }
156            };
157        }
158        Ok(self)
159    }
160
161    /// Create a new mesh for an ellipse.
162    ///
163    /// For the meaning of the `tolerance` parameter, [see here](https://docs.rs/lyon_geom/0.11.0/lyon_geom/#flattening).
164    pub fn ellipse<P>(
165        &mut self,
166        mode: DrawMode,
167        point: P,
168        radius1: f32,
169        radius2: f32,
170        tolerance: f32,
171        color: Color,
172    ) -> GameResult<&mut Self>
173    where
174        P: Into<mint::Point2<f32>>,
175    {
176        assert!(
177            tolerance > 0.0,
178            "Tolerances <= 0 are invalid, see https://github.com/ggez/ggez/issues/892"
179        );
180        {
181            let buffers = &mut self.buffer;
182            let point = point.into();
183            let vb = VertexBuilder { color };
184            match mode {
185                DrawMode::Fill(fill_options) => {
186                    let builder = &mut t::BuffersBuilder::new(buffers, vb);
187                    let mut tessellator = t::FillTessellator::new();
188                    let _ = tessellator.tessellate_ellipse(
189                        t::math::point(point.x, point.y),
190                        t::math::vector(radius1, radius2),
191                        t::math::Angle { radians: 0.0 },
192                        t::path::Winding::Positive,
193                        &fill_options.with_tolerance(tolerance),
194                        builder,
195                    );
196                }
197                DrawMode::Stroke(options) => {
198                    let builder = &mut t::BuffersBuilder::new(buffers, vb);
199                    let mut tessellator = t::StrokeTessellator::new();
200                    let _ = tessellator.tessellate_ellipse(
201                        t::math::point(point.x, point.y),
202                        t::math::vector(radius1, radius2),
203                        t::math::Angle { radians: 0.0 },
204                        t::path::Winding::Positive,
205                        &options.with_tolerance(tolerance),
206                        builder,
207                    );
208                }
209            };
210        }
211        Ok(self)
212    }
213
214    /// Create a new mesh for a series of connected lines.
215    pub fn polyline<P>(
216        &mut self,
217        mode: DrawMode,
218        points: &[P],
219        color: Color,
220    ) -> GameResult<&mut Self>
221    where
222        P: Into<mint::Point2<f32>> + Clone,
223    {
224        if points.len() < 2 {
225            return Err(GameError::LyonError(
226                "MeshBuilder::polyline() got a list of < 2 points".to_string(),
227            ));
228        }
229
230        self.polyline_inner(mode, points, false, color)
231    }
232
233    /// Create a new mesh for a closed polygon.
234    /// The points given must be in clockwise order,
235    /// otherwise at best the polygon will not draw.
236    pub fn polygon<P>(
237        &mut self,
238        mode: DrawMode,
239        points: &[P],
240        color: Color,
241    ) -> GameResult<&mut Self>
242    where
243        P: Into<mint::Point2<f32>> + Clone,
244    {
245        if points.len() < 3 {
246            return Err(GameError::LyonError(
247                "MeshBuilder::polygon() got a list of < 3 points".to_string(),
248            ));
249        }
250
251        self.polyline_inner(mode, points, true, color)
252    }
253
254    fn polyline_inner<P>(
255        &mut self,
256        mode: DrawMode,
257        points: &[P],
258        is_closed: bool,
259        color: Color,
260    ) -> GameResult<&mut Self>
261    where
262        P: Into<mint::Point2<f32>> + Clone,
263    {
264        let vb = VertexBuilder { color };
265        self.polyline_with_vertex_builder(mode, points, is_closed, vb)
266    }
267
268    /// Create a new mesh for a given polyline using a custom vertex builder.
269    /// The points given must be in clockwise order.
270    pub fn polyline_with_vertex_builder<P, V>(
271        &mut self,
272        mode: DrawMode,
273        points: &[P],
274        is_closed: bool,
275        vb: V,
276    ) -> GameResult<&mut Self>
277    where
278        P: Into<mint::Point2<f32>> + Clone,
279        V: t::StrokeVertexConstructor<Vertex> + t::FillVertexConstructor<Vertex>,
280    {
281        {
282            assert!(points.len() > 1);
283            let buffers = &mut self.buffer;
284            let points: Vec<LPoint> = points
285                .iter()
286                .cloned()
287                .map(|p| {
288                    let mint_point: mint::Point2<f32> = p.into();
289                    t::math::point(mint_point.x, mint_point.y)
290                })
291                .collect();
292            let polygon = Polygon {
293                points: &points,
294                closed: is_closed,
295            };
296            match mode {
297                DrawMode::Fill(options) => {
298                    let builder = &mut t::BuffersBuilder::new(buffers, vb);
299                    let tessellator = &mut t::FillTessellator::new();
300                    let _ = tessellator.tessellate_polygon(polygon, &options, builder)?;
301                }
302                DrawMode::Stroke(options) => {
303                    let builder = &mut t::BuffersBuilder::new(buffers, vb);
304                    let tessellator = &mut t::StrokeTessellator::new();
305                    let _ = tessellator.tessellate_polygon(polygon, &options, builder)?;
306                }
307            };
308        }
309        Ok(self)
310    }
311
312    /// Create a new mesh for a rectangle.
313    pub fn rectangle(
314        &mut self,
315        mode: DrawMode,
316        bounds: Rect,
317        color: Color,
318    ) -> GameResult<&mut Self> {
319        {
320            let buffers = &mut self.buffer;
321            let rect = t::math::rect(bounds.x, bounds.y, bounds.w, bounds.h);
322            let vb = VertexBuilder { color };
323            match mode {
324                DrawMode::Fill(fill_options) => {
325                    let builder = &mut t::BuffersBuilder::new(buffers, vb);
326                    let mut tessellator = t::FillTessellator::new();
327                    let _ = tessellator.tessellate_rectangle(&rect, &fill_options, builder);
328                }
329                DrawMode::Stroke(options) => {
330                    let builder = &mut t::BuffersBuilder::new(buffers, vb);
331                    let mut tessellator = t::StrokeTessellator::new();
332                    let _ = tessellator.tessellate_rectangle(&rect, &options, builder);
333                }
334            };
335        }
336        Ok(self)
337    }
338
339    /// Create a new mesh for a rounded rectangle.
340    pub fn rounded_rectangle(
341        &mut self,
342        mode: DrawMode,
343        bounds: Rect,
344        radius: f32,
345        color: Color,
346    ) -> GameResult<&mut Self> {
347        {
348            let buffers = &mut self.buffer;
349            let rect = t::math::rect(bounds.x, bounds.y, bounds.w, bounds.h);
350            let radii = t::path::builder::BorderRadii::new(radius);
351            let vb = VertexBuilder { color };
352            let mut path_builder = t::path::Path::builder();
353            path_builder.add_rounded_rectangle(&rect, &radii, t::path::Winding::Positive);
354            let path = path_builder.build();
355
356            match mode {
357                DrawMode::Fill(fill_options) => {
358                    let builder = &mut t::BuffersBuilder::new(buffers, vb);
359                    let mut tessellator = t::FillTessellator::new();
360                    let _ = tessellator.tessellate_path(&path, &fill_options, builder);
361                }
362                DrawMode::Stroke(options) => {
363                    let builder = &mut t::BuffersBuilder::new(buffers, vb);
364                    let mut tessellator = t::StrokeTessellator::new();
365                    let _ = tessellator.tessellate_path(&path, &options, builder);
366                }
367            };
368        }
369        Ok(self)
370    }
371
372    /// Create a new [`Mesh`](struct.Mesh.html) from a raw list of triangles.
373    /// The length of the list must be a multiple of 3.
374    ///
375    /// Currently does not support UV's or indices.
376    pub fn triangles<P>(&mut self, triangles: &[P], color: Color) -> GameResult<&mut Self>
377    where
378        P: Into<mint::Point2<f32>> + Clone,
379    {
380        {
381            if (triangles.len() % 3) != 0 {
382                return Err(GameError::LyonError(String::from(
383                    "Called Mesh::triangles() with points that have a length not a multiple of 3.",
384                )));
385            }
386            let tris = triangles
387                .iter()
388                .cloned()
389                .map(|p| {
390                    // Gotta turn ggez Point2's into lyon points
391                    let mint_point = p.into();
392                    lyon::math::point(mint_point.x, mint_point.y)
393                })
394                // Removing this collect might be nice, but is not easy.
395                // We can chunk a slice, but can't chunk an arbitrary
396                // iterator.
397                // Using the itertools crate doesn't really make anything
398                // nicer, so we'll just live with it.
399                .collect::<Vec<_>>();
400            let tris = tris.chunks(3);
401            let vb = VertexBuilder { color };
402            for tri in tris {
403                // Ideally this assert makes bounds-checks only happen once.
404                assert_eq!(tri.len(), 3);
405                let first_index: u16 = self.buffer.vertices.len().try_into().unwrap();
406                self.buffer.vertices.push(vb.new_vertex(tri[0]));
407                self.buffer.vertices.push(vb.new_vertex(tri[1]));
408                self.buffer.vertices.push(vb.new_vertex(tri[2]));
409                self.buffer.indices.push(first_index);
410                self.buffer.indices.push(first_index + 1);
411                self.buffer.indices.push(first_index + 2);
412            }
413        }
414        Ok(self)
415    }
416
417    /// Takes an `Image` to apply to the mesh.
418    pub fn texture(&mut self, image: Image) -> GameResult<&mut Self> {
419        // we can't move out of Image, because it implements Drop
420        self.tex_filter = Some(image.filter());
421        self.tex_clones_hack = Some(image.texture_clones_hack.clone());
422        self.texture = Some(image.texture);
423        Ok(self)
424    }
425
426    pub fn set_filter(&mut self, filter: FilterMode) {
427        self.tex_filter = Some(filter);
428    }
429
430    pub fn filter(&self) -> Option<FilterMode> {
431        self.tex_filter
432    }
433
434    /// Creates a `Mesh` from a raw list of triangles defined from vertices
435    /// and indices.  You may also
436    /// supply an `Image` to use as a texture, if you pass `None`, it will
437    /// just use a pure white texture.
438    ///
439    /// This is the most primitive mesh-creation method, but allows you full
440    /// control over the tesselation and texturing.  It has the same constraints
441    /// as `Mesh::from_raw()`.
442    pub fn raw<V>(
443        &mut self,
444        verts: &[V],
445        indices: &[u16],
446        texture: Option<Image>,
447    ) -> GameResult<&mut Self>
448    where
449        V: Into<Vertex> + Clone,
450    {
451        assert!(self.buffer.vertices.len() + verts.len() < (u16::MAX as usize));
452        assert!(self.buffer.indices.len() + indices.len() < (u16::MAX as usize));
453        let next_idx = self.buffer.vertices.len() as u16;
454        // Can we remove the clone here?
455        // I can't find a way to, because `into()` consumes its source and
456        // `Borrow` or `AsRef` aren't really right.
457        // EDIT: We can, but at a small cost to user-friendlyness, see:
458        //       https://github.com/ggez/ggez/issues/940
459        let vertices = verts.iter().cloned().map(|v: V| -> Vertex { v.into() });
460        let indices = indices.iter().map(|i| (*i) + next_idx);
461        self.buffer.vertices.extend(vertices);
462        self.buffer.indices.extend(indices);
463        if let Some(image) = texture {
464            self.tex_filter = Some(image.filter());
465            self.tex_clones_hack = Some(image.texture_clones_hack.clone());
466            self.texture = Some(image.texture);
467        }
468        Ok(self)
469    }
470
471    /// Takes the accumulated geometry and load it into GPU memory,
472    /// creating a single `Mesh`.
473    pub fn build(
474        &self,
475        ctx: &mut Context,
476        quad_ctx: &mut miniquad::graphics::GraphicsContext,
477    ) -> GameResult<Mesh> {
478        let vertex_buffer = miniquad::Buffer::immutable(
479            quad_ctx,
480            miniquad::BufferType::VertexBuffer,
481            &self.buffer.vertices[..],
482        );
483        let index_buffer = miniquad::Buffer::immutable(
484            quad_ctx,
485            miniquad::BufferType::IndexBuffer,
486            &self.buffer.indices[..],
487        );
488        let attribute_buffer = Buffer::stream(
489            quad_ctx,
490            BufferType::VertexBuffer,
491            std::mem::size_of::<InstanceAttributes>(), // start out with space for one instance
492        );
493        // make sure to set the filter before building
494        if let Some((filter, texture)) = self.tex_filter.zip(self.texture) {
495            texture.set_filter(quad_ctx, filter);
496        }
497        let bindings = miniquad::Bindings {
498            vertex_buffers: vec![vertex_buffer, attribute_buffer],
499            index_buffer,
500            images: self
501                .texture
502                .map_or(vec![ctx.gfx_context.white_texture], |texture| vec![texture]),
503        };
504        let rect = bbox_for_vertices(&self.buffer.vertices).expect("No vertices in MeshBuilder");
505
506        Ok(Mesh {
507            bindings,
508            blend_mode: None,
509            rect,
510            texture_clones_hack: self.tex_clones_hack.clone(),
511        })
512    }
513}
514
515#[derive(Copy, Clone, PartialEq, Debug)]
516struct VertexBuilder {
517    color: Color,
518}
519
520impl VertexBuilder {
521    fn new_vertex(self, position: LPoint) -> Vertex {
522        Vertex {
523            pos: [position.x, position.y],
524            uv: [position.x, position.y],
525            color: self.color.into(),
526        }
527    }
528}
529
530impl t::StrokeVertexConstructor<Vertex> for VertexBuilder {
531    fn new_vertex(&mut self, vertex: t::StrokeVertex) -> Vertex {
532        let position = vertex.position();
533        Vertex {
534            pos: [position.x, position.y],
535            uv: [0.0, 0.0],
536            color: self.color.into(),
537        }
538    }
539}
540
541impl t::FillVertexConstructor<Vertex> for VertexBuilder {
542    fn new_vertex(&mut self, vertex: t::FillVertex) -> Vertex {
543        let position = vertex.position();
544        Vertex {
545            pos: [position.x, position.y],
546            uv: [0.0, 0.0],
547            color: self.color.into(),
548        }
549    }
550}
551
552/// 2D polygon mesh.
553///
554/// All of its creation methods are just shortcuts for doing the same operation
555/// via a [`MeshBuilder`](struct.MeshBuilder.html).
556#[derive(Debug)]
557pub struct Mesh {
558    bindings: miniquad::Bindings,
559    blend_mode: Option<BlendMode>,
560    rect: Rect,
561    texture_clones_hack: Option<Arc<()>>,
562}
563
564impl Drop for Mesh {
565    fn drop(&mut self) {
566        let delete_texture = self
567            .texture_clones_hack
568            .as_ref()
569            .map_or(false, |arc| Arc::strong_count(arc) == 1);
570        crate::graphics::add_dropped_bindings(self.bindings.clone(), delete_texture);
571    }
572}
573
574impl Mesh {
575    /// Create a new mesh for a line of one or more connected segments.
576    pub fn new_line<P>(
577        ctx: &mut Context,
578        quad_ctx: &mut miniquad::graphics::GraphicsContext,
579        points: &[P],
580        width: f32,
581        color: Color,
582    ) -> GameResult<Mesh>
583    where
584        P: Into<mint::Point2<f32>> + Clone,
585    {
586        let mut mb = MeshBuilder::new();
587        let _ = mb.polyline(DrawMode::stroke(width), points, color);
588        mb.build(ctx, quad_ctx)
589    }
590
591    /// Create a new mesh for a circle.
592    pub fn new_circle<P>(
593        ctx: &mut Context,
594        quad_ctx: &mut miniquad::graphics::GraphicsContext,
595        mode: DrawMode,
596        point: P,
597        radius: f32,
598        tolerance: f32,
599        color: Color,
600    ) -> GameResult<Mesh>
601    where
602        P: Into<mint::Point2<f32>>,
603    {
604        let mut mb = MeshBuilder::new();
605        let _ = mb.circle(mode, point, radius, tolerance, color);
606        mb.build(ctx, quad_ctx)
607    }
608
609    /// Create a new mesh for an ellipse.
610    pub fn new_ellipse<P>(
611        ctx: &mut Context,
612        quad_ctx: &mut miniquad::graphics::GraphicsContext,
613        mode: DrawMode,
614        point: P,
615        radius1: f32,
616        radius2: f32,
617        tolerance: f32,
618        color: Color,
619    ) -> GameResult<Mesh>
620    where
621        P: Into<mint::Point2<f32>>,
622    {
623        let mut mb = MeshBuilder::new();
624        let _ = mb.ellipse(mode, point, radius1, radius2, tolerance, color);
625        mb.build(ctx, quad_ctx)
626    }
627
628    /// Create a new mesh for series of connected lines.
629    pub fn new_polyline<P>(
630        ctx: &mut Context,
631        quad_ctx: &mut miniquad::graphics::GraphicsContext,
632        mode: DrawMode,
633        points: &[P],
634        color: Color,
635    ) -> GameResult<Mesh>
636    where
637        P: Into<mint::Point2<f32>> + Clone,
638    {
639        let mut mb = MeshBuilder::new();
640        let _ = mb.polyline(mode, points, color);
641        mb.build(ctx, quad_ctx)
642    }
643
644    /// Create a new mesh for closed polygon.
645    /// The points given must be in clockwise order,
646    /// otherwise at best the polygon will not draw.
647    pub fn new_polygon<P>(
648        ctx: &mut Context,
649        quad_ctx: &mut miniquad::graphics::GraphicsContext,
650        mode: DrawMode,
651        points: &[P],
652        color: Color,
653    ) -> GameResult<Mesh>
654    where
655        P: Into<mint::Point2<f32>> + Clone,
656    {
657        if points.len() < 3 {
658            return Err(GameError::LyonError(
659                "Mesh::new_polygon() got a list of < 3 points".to_string(),
660            ));
661        }
662        let mut mb = MeshBuilder::new();
663        let _ = mb.polygon(mode, points, color);
664        mb.build(ctx, quad_ctx)
665    }
666
667    /// Create a new mesh for a rectangle
668    pub fn new_rectangle(
669        ctx: &mut Context,
670        quad_ctx: &mut miniquad::graphics::GraphicsContext,
671        mode: DrawMode,
672        bounds: Rect,
673        color: Color,
674    ) -> GameResult<Mesh> {
675        let mut mb = MeshBuilder::new();
676        let _ = mb.rectangle(mode, bounds, color);
677        mb.build(ctx, quad_ctx)
678    }
679
680    /// Create a new mesh for a rounded rectangle
681    pub fn new_rounded_rectangle(
682        ctx: &mut Context,
683        quad_ctx: &mut miniquad::graphics::GraphicsContext,
684        mode: DrawMode,
685        bounds: Rect,
686        radius: f32,
687        color: Color,
688    ) -> GameResult<Mesh> {
689        let mut mb = MeshBuilder::new();
690        let _ = mb.rounded_rectangle(mode, bounds, radius, color);
691        mb.build(ctx, quad_ctx)
692    }
693
694    /// Create a new `Mesh` from a raw list of triangle points.
695    pub fn from_triangles<P>(
696        ctx: &mut Context,
697        quad_ctx: &mut miniquad::graphics::GraphicsContext,
698        triangles: &[P],
699        color: Color,
700    ) -> GameResult<Mesh>
701    where
702        P: Into<mint::Point2<f32>> + Clone,
703    {
704        let mut mb = MeshBuilder::new();
705        let _ = mb.triangles(triangles, color);
706        mb.build(ctx, quad_ctx)
707    }
708
709    /// Creates a `Mesh` from a raw list of triangles defined from points
710    /// and indices, with the given UV texture coordinates.  You may also
711    /// supply an `Image` to use as a texture, if you pass `None`, it will
712    /// just use a pure white texture.
713    ///
714    /// This is the most primitive mesh-creation method, but allows you full
715    /// control over the tesselation and texturing.
716    /// As such it will panic or produce incorrect/invalid output (that may later
717    /// cause drawing to panic), if:
718    ///
719    ///  * `indices` contains a value out of bounds of `verts`
720    ///  * `verts` is longer than `u16::MAX` elements.
721    pub fn from_raw<V>(
722        ctx: &mut Context,
723        quad_ctx: &mut miniquad::graphics::GraphicsContext,
724        verts: &[V],
725        indices: &[u16],
726        image: Option<Image>,
727    ) -> GameResult<Mesh>
728    where
729        V: Into<Vertex> + Clone,
730    {
731        // Sanity checks to return early with helpful error messages.
732        if verts.len() > (u16::MAX as usize) {
733            let msg = format!(
734                "Tried to build a mesh with {} vertices, max is u16::MAX",
735                verts.len()
736            );
737            return Err(GameError::LyonError(msg));
738        }
739        if indices.len() > (u16::MAX as usize) {
740            let msg = format!(
741                "Tried to build a mesh with {} indices, max is u16::MAX",
742                indices.len()
743            );
744            return Err(GameError::LyonError(msg));
745        }
746        if verts.len() < 3 {
747            let msg = format!("Trying to build mesh with < 3 vertices, this is usually due to invalid input to a `Mesh` or MeshBuilder`.");
748            return Err(GameError::LyonError(msg));
749        }
750        if indices.len() < 3 {
751            let msg = format!("Trying to build mesh with < 3 indices, this is usually due to invalid input to a `Mesh` or MeshBuilder`.  Indices:\n {:#?}", indices);
752            return Err(GameError::LyonError(msg));
753        }
754
755        if indices.len() % 3 != 0 {
756            let msg = format!("Trying to build mesh with an array of indices that is not a multiple of 3, this is usually due to invalid input to a `Mesh` or MeshBuilder`.");
757            return Err(GameError::LyonError(msg));
758        }
759
760        let vertex_buffer =
761            miniquad::Buffer::immutable(quad_ctx, miniquad::BufferType::VertexBuffer, &verts[..]);
762        let index_buffer =
763            miniquad::Buffer::immutable(quad_ctx, miniquad::BufferType::IndexBuffer, &indices[..]);
764        let attribute_buffer = Buffer::stream(
765            quad_ctx,
766            BufferType::VertexBuffer,
767            std::mem::size_of::<InstanceAttributes>(), // start out with space for one instance
768        );
769
770        let verts: Vec<Vertex> = verts.iter().cloned().map(Into::into).collect();
771        let rect = bbox_for_vertices(&verts).expect(
772            "No vertices in MeshBuilder; should never happen since we already checked this",
773        );
774
775        let (images, texture_clones_hack) = image
776            .map_or((vec![ctx.gfx_context.white_texture], None), |image| {
777                (vec![image.texture], Some(image.texture_clones_hack.clone()))
778            });
779
780        let bindings = miniquad::Bindings {
781            vertex_buffers: vec![vertex_buffer, attribute_buffer],
782            index_buffer,
783            images,
784        };
785
786        Ok(Mesh {
787            bindings,
788            blend_mode: None,
789            rect,
790            texture_clones_hack,
791        })
792    }
793    /*
794    /// Replaces the vertices in the `Mesh` with the given ones.  This MAY be faster
795    /// than re-creating a `Mesh` with [`Mesh::from_raw()`](#method.from_raw) due to
796    /// reusing memory instead of allocating and deallocating it, both on the CPU and
797    /// GPU side.  There's too much variation in implementations and drivers to promise
798    /// it will actually be faster though.  At worst, it will be the same speed.
799    //pub fn set_vertices(&mut self, _ctx: &mut Context, _verts: &[Vertex], _indices: &[u16]) {
800    // This is in principle faster than throwing away an existing mesh and
801    // creating a new one with `Mesh::from_raw()`, but really only because it
802    // doesn't take `Into<Vertex>` and so doesn't need to create an intermediate
803    // `Vec`.  It still creates a new GPU buffer and replaces the old one instead
804    // of just copying into the old one.
805    // TODO: By default we create `Mesh` with a read-only GPU buffer, which I am
806    // a little hesitant to change... partially because doing that with
807    // `Image` has caused some subtle edge case bugs.
808    // It's not terribly hard to do in principle though, just tedious;
809    // start at `Factory::create_vertex_buffer_with_slice()`, drill down to
810    // <https://docs.rs/gfx/0.17.1/gfx/traits/trait.Factory.html#tymethod.create_buffer_raw>,
811    // and fill in the bits between with the appropriate values.
812    // let (vbuf, slice) = ctx
813    //     .gfx_context
814    //     .factory
815    //     .create_vertex_buffer_with_slice(verts, indices);
816    // self.buffer = vbuf;
817    // self.slice = slice;
818    //unimplemented!()
819    //}
820     */
821}
822
823impl Drawable for Mesh {
824    fn draw(
825        &self,
826        ctx: &mut Context,
827        quad_ctx: &mut miniquad::graphics::GraphicsContext,
828        param: DrawParam,
829    ) -> GameResult {
830        let instance = InstanceAttributes::from(&param);
831        self.bindings.vertex_buffers[1].update(quad_ctx, &[instance]);
832
833        let pass = ctx.framebuffer();
834
835        quad_ctx.begin_pass(pass, PassAction::Nothing);
836        quad_ctx.apply_bindings(&self.bindings);
837
838        let shader_id = *ctx.gfx_context.current_shader.borrow();
839        let current_shader = &mut ctx.gfx_context.shaders[shader_id];
840        quad_ctx.apply_pipeline(&current_shader.pipeline);
841
842        apply_uniforms(ctx, quad_ctx, shader_id, None);
843
844        let mut custom_blend = false;
845        if let Some(blend_mode) = self.blend_mode() {
846            custom_blend = true;
847            crate::graphics::set_current_blend_mode(quad_ctx, blend_mode)
848        }
849
850        quad_ctx.draw(0, self.bindings.index_buffer.size() as i32 / 2, 1);
851
852        // restore default blend mode
853        if custom_blend {
854            crate::graphics::restore_blend_mode(ctx, quad_ctx);
855        }
856
857        quad_ctx.end_render_pass();
858
859        Ok(())
860    }
861    fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
862        self.blend_mode = mode;
863    }
864    fn blend_mode(&self) -> Option<BlendMode> {
865        self.blend_mode
866    }
867    fn dimensions(&self, _ctx: &mut Context) -> Option<Rect> {
868        Some(self.rect)
869    }
870}
871
872fn bbox_for_vertices(verts: &[Vertex]) -> Option<Rect> {
873    if verts.is_empty() {
874        return None;
875    }
876    let [x0, y0] = verts[0].pos;
877    let mut x_max = x0;
878    let mut x_min = x0;
879    let mut y_max = y0;
880    let mut y_min = y0;
881    for v in verts {
882        let x = v.pos[0];
883        let y = v.pos[1];
884        x_max = f32::max(x_max, x);
885        x_min = f32::min(x_min, x);
886        y_max = f32::max(y_max, y);
887        y_min = f32::min(y_min, y);
888    }
889    Some(Rect {
890        w: x_max - x_min,
891        h: y_max - y_min,
892        x: x_min,
893        y: y_min,
894    })
895}
896
897/// An index of a particular instance in a `MeshBatch`
898#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
899pub struct MeshIdx(pub usize);
900
901/// Mesh that will be rendered with hardware instancing.
902/// Use this when you have a lot of similar geometry which does not move around often.
903#[derive(Debug)]
904pub struct MeshBatch {
905    mesh: Mesh,
906    instance_params: Vec<DrawParam>,
907    gpu_instance_params: Vec<InstanceAttributes>,
908    instance_buffer_dirty: bool,
909}
910
911impl MeshBatch {
912    /// Creates a new mesh batch.
913    ///
914    /// Takes ownership of the `Mesh`.
915    pub fn new(mesh: Mesh) -> GameResult<MeshBatch> {
916        Ok(MeshBatch {
917            mesh,
918            instance_params: Vec::new(),
919            gpu_instance_params: vec![],
920            instance_buffer_dirty: true,
921        })
922    }
923
924    /// Removes all instances from the batch.
925    ///
926    /// Calling this invalidates the entire buffer, however this will
927    /// not automatically deallocate graphics card memory or flush the buffer.
928    pub fn clear(&mut self) {
929        self.instance_params.clear();
930        self.instance_buffer_dirty = true;
931    }
932
933    /// Returns a reference to mesh instances
934    pub fn get_instance_params(&self) -> &[DrawParam] {
935        &self.instance_params
936    }
937
938    /// Returns a mutable reference to mesh instances.
939    ///
940    /// Please note that manually altering items in this slice
941    /// will not automatically invalidate the buffer, you will
942    /// have to manually call `flush()` or `flush_range()` later.
943    pub fn get_instance_params_mut(&mut self) -> &mut [DrawParam] {
944        &mut self.instance_params
945    }
946
947    /// Adds a new instance to the mesh batch
948    ///
949    /// Returns a handle with which to modify the instance using
950    /// [`set()`](#method.set)
951    ///
952    /// Calling this invalidates the entire buffer and will result in
953    /// flusing it on the next [`graphics::draw()`](../fn.draw.html) call.
954    pub fn add<P>(&mut self, param: P) -> MeshIdx
955    where
956        P: Into<DrawParam>,
957    {
958        self.instance_params.push(param.into());
959        self.instance_buffer_dirty = true;
960        MeshIdx(self.instance_params.len() - 1)
961    }
962
963    /// Alters an instance in the batch to use the given draw params.
964    ///
965    /// Calling this invalidates the entire buffer and will result in
966    /// flusing it on the next [`graphics::draw()`](../fn.draw.html) call.
967    ///
968    /// This might cause performance issues with large batches, to avoid this
969    /// consider using `flush_range` to explicitly invalidate required data slice.
970    pub fn set<P>(&mut self, handle: MeshIdx, param: P) -> GameResult
971    where
972        P: Into<DrawParam>,
973    {
974        if handle.0 < self.instance_params.len() {
975            self.instance_params[handle.0] = param.into();
976            self.instance_buffer_dirty = true;
977            Ok(())
978        } else {
979            Err(GameError::RenderError(String::from("Index out of bounds")))
980        }
981    }
982
983    /// Alters a range of instances in the batch to use the given draw params
984    ///
985    /// Calling this invalidates the entire buffer and will result in
986    /// flusing it on the next [`graphics::draw()`](../fn.draw.html) call.
987    ///
988    /// This might cause performance issues with large batches, to avoid this
989    /// consider using `flush_range` to explicitly invalidate required data slice.
990    pub fn set_range<P>(&mut self, first_handle: MeshIdx, params: &[P]) -> GameResult
991    where
992        P: Into<DrawParam> + Copy,
993    {
994        let first_param = first_handle.0;
995        let num_params = params.len();
996        if first_param < self.instance_params.len()
997            && (first_param + num_params) <= self.instance_params.len()
998        {
999            for (i, item) in params.iter().enumerate().take(num_params) {
1000                self.instance_params[first_param + i] = (*item).into();
1001            }
1002            self.instance_buffer_dirty = true;
1003            Ok(())
1004        } else {
1005            Err(GameError::RenderError(String::from("Range out of bounds")))
1006        }
1007    }
1008
1009    /// Immediately sends specified slice of data in the batch to the graphics card.
1010    ///
1011    /// Calling this counts as a full buffer flush, but only flushes the data within
1012    /// the provided range, anything outside of this range will not be touched.
1013    ///
1014    /// Use it for updating small portions of large batches.
1015    pub fn flush_range(
1016        &mut self,
1017        _ctx: &mut Context,
1018        quad_ctx: &mut miniquad::graphics::GraphicsContext,
1019        first_handle: MeshIdx,
1020        count: usize,
1021    ) -> GameResult {
1022        let first_param = first_handle.0;
1023        let slice_end = first_param + count;
1024        if first_param < self.instance_params.len() && slice_end <= self.instance_params.len() {
1025            let needs_new_buffer = self.gpu_instance_params.len() < slice_end;
1026
1027            let mut mesh = &mut self.mesh;
1028            let mut gpu_instance_params = &mut self.gpu_instance_params;
1029
1030            if needs_new_buffer {
1031                gpu_instance_params
1032                    .resize(self.instance_params.len(), InstanceAttributes::default());
1033
1034                let buffer = Buffer::stream(
1035                    quad_ctx,
1036                    BufferType::VertexBuffer,
1037                    std::mem::size_of::<InstanceAttributes>() * self.instance_params.len(),
1038                );
1039
1040                mesh.bindings.vertex_buffers[1].delete();
1041                mesh.bindings.vertex_buffers[1] = buffer;
1042            }
1043
1044            let slice = if needs_new_buffer {
1045                &self.instance_params
1046            } else {
1047                &self.instance_params[first_param..slice_end]
1048            };
1049
1050            for (n, param) in slice.iter().enumerate() {
1051                let instance = InstanceAttributes {
1052                    model: param.trans.to_bare_matrix().into(),
1053                    source: Vector4::new(param.src.x, param.src.y, param.src.w, param.src.h),
1054                    color: Vector4::new(param.color.r, param.color.g, param.color.b, param.color.a),
1055                };
1056                gpu_instance_params[n] = instance;
1057            }
1058
1059            // TODO: if `update` had an offset parameter we could really only update parts of the buffer, just like intended
1060            mesh.bindings.vertex_buffers[1].update(quad_ctx, &gpu_instance_params[..]);
1061
1062            self.instance_buffer_dirty = false;
1063            Ok(())
1064        } else {
1065            Err(GameError::RenderError(String::from("Range out of bounds")))
1066        }
1067    }
1068
1069    /// Immediately sends all data in the batch to the graphics card.
1070    ///
1071    /// In general, [`graphics::draw()`](../fn.draw.html) on the `MeshBatch`
1072    /// will do this automatically when buffer contents are updated.
1073    pub fn flush(
1074        &mut self,
1075        ctx: &mut Context,
1076        quad_ctx: &mut miniquad::graphics::GraphicsContext,
1077    ) -> GameResult {
1078        self.flush_range(ctx, quad_ctx, MeshIdx(0), self.instance_params.len())
1079    }
1080
1081    /// Draws the drawable onto the rendering target.
1082    pub fn draw(
1083        &mut self,
1084        ctx: &mut Context,
1085        quad_ctx: &mut miniquad::graphics::GraphicsContext,
1086        param: DrawParam,
1087    ) -> GameResult {
1088        if !self.instance_params.is_empty() {
1089            if self.instance_buffer_dirty {
1090                self.flush(ctx, quad_ctx)?;
1091            }
1092
1093            let pass = ctx.framebuffer();
1094            quad_ctx.begin_pass(pass, PassAction::Nothing);
1095            quad_ctx.apply_bindings(&self.mesh.bindings);
1096
1097            let shader_id = *ctx.gfx_context.current_shader.borrow();
1098            let current_shader = &mut ctx.gfx_context.shaders[shader_id];
1099            quad_ctx.apply_pipeline(&current_shader.pipeline);
1100
1101            apply_uniforms(
1102                ctx,
1103                quad_ctx,
1104                shader_id,
1105                Some(cgmath::Matrix4::from(param.trans.to_bare_matrix())),
1106            );
1107
1108            let mut custom_blend = false;
1109            if let Some(blend_mode) = self.blend_mode() {
1110                custom_blend = true;
1111                crate::graphics::set_current_blend_mode(quad_ctx, blend_mode)
1112            }
1113
1114            quad_ctx.draw(
1115                0,
1116                self.mesh.bindings.index_buffer.size() as i32 / 2,
1117                self.instance_params.len() as i32,
1118            );
1119
1120            // restore default blend mode
1121            if custom_blend {
1122                crate::graphics::restore_blend_mode(ctx, quad_ctx);
1123            }
1124
1125            quad_ctx.end_render_pass();
1126        }
1127
1128        Ok(())
1129    }
1130
1131    /// Returns a bounding box in the form of a `Rect`.
1132    pub fn dimensions(&self, ctx: &mut Context) -> Option<Rect> {
1133        if self.instance_params.is_empty() {
1134            return None;
1135        }
1136        if let Some(dimensions) = self.mesh.dimensions(ctx) {
1137            self.instance_params
1138                .iter()
1139                .map(|&param| transform_rect(dimensions, param))
1140                .fold(None, |acc: Option<Rect>, rect| {
1141                    Some(if let Some(acc) = acc {
1142                        acc.combine_with(rect)
1143                    } else {
1144                        rect
1145                    })
1146                })
1147        } else {
1148            None
1149        }
1150    }
1151
1152    /// Sets the blend mode to be used when drawing this drawable.
1153    pub fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
1154        self.mesh.set_blend_mode(mode)
1155    }
1156
1157    /// Gets the blend mode to be used when drawing this drawable.
1158    pub fn blend_mode(&self) -> Option<BlendMode> {
1159        self.mesh.blend_mode()
1160    }
1161}