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#[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 pub fn new() -> Self {
104 Self::default()
105 }
106
107 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 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 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 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 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 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 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 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 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 let mint_point = p.into();
392 lyon::math::point(mint_point.x, mint_point.y)
393 })
394 .collect::<Vec<_>>();
400 let tris = tris.chunks(3);
401 let vb = VertexBuilder { color };
402 for tri in tris {
403 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 pub fn texture(&mut self, image: Image) -> GameResult<&mut Self> {
419 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 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 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 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>(), );
493 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#[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 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 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 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 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 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 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 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 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 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 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>(), );
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 }
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(¶m);
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(¤t_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 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#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
899pub struct MeshIdx(pub usize);
900
901#[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 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 pub fn clear(&mut self) {
929 self.instance_params.clear();
930 self.instance_buffer_dirty = true;
931 }
932
933 pub fn get_instance_params(&self) -> &[DrawParam] {
935 &self.instance_params
936 }
937
938 pub fn get_instance_params_mut(&mut self) -> &mut [DrawParam] {
944 &mut self.instance_params
945 }
946
947 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 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 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 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 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 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 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(¤t_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 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 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(|¶m| 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 pub fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
1154 self.mesh.set_blend_mode(mode)
1155 }
1156
1157 pub fn blend_mode(&self) -> Option<BlendMode> {
1159 self.mesh.blend_mode()
1160 }
1161}