#![allow(warnings)]
use crate::{graphics::*, Context, GameError, GameResult};
use lyon::path::builder::PathBuilder;
use lyon::path::Polygon;
use lyon::tessellation as t;
use lyon::{self, math::Point as LPoint};
pub use self::t::{FillOptions, FillRule, LineCap, LineJoin, StrokeOptions};
use crate::graphics::context::default_shader;
use cgmath::{Matrix4, Point2, Transform, Vector2, Vector3, Vector4};
use miniquad::{Buffer, BufferType, PassAction};
use std::cell::RefCell;
use std::convert::TryInto;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct Vertex {
pub pos: [f32; 2],
pub uv: [f32; 2],
pub color: [f32; 4],
}
#[derive(Debug, Clone)]
pub struct MeshBuilder {
buffer: t::geometry_builder::VertexBuffers<Vertex, u16>,
texture: Option<miniquad::Texture>,
tex_filter: Option<FilterMode>,
tex_clones_hack: Option<Arc<()>>,
}
impl Default for MeshBuilder {
fn default() -> Self {
Self {
buffer: t::VertexBuffers::new(),
texture: None,
tex_filter: None,
tex_clones_hack: None,
}
}
}
impl MeshBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn line<P>(&mut self, points: &[P], width: f32, color: Color) -> GameResult<&mut Self>
where
P: Into<mint::Point2<f32>> + Clone,
{
self.polyline(DrawMode::stroke(width), points, color)
}
pub fn circle<P>(
&mut self,
mode: DrawMode,
point: P,
radius: f32,
tolerance: f32,
color: Color,
) -> GameResult<&mut Self>
where
P: Into<mint::Point2<f32>>,
{
assert!(
tolerance > 0.0,
"Tolerances <= 0 are invalid, see https://github.com/ggez/ggez/issues/892"
);
{
let point = point.into();
let buffers = &mut self.buffer;
let vb = VertexBuilder { color };
match mode {
DrawMode::Fill(fill_options) => {
let mut tessellator = t::FillTessellator::new();
let _ = tessellator.tessellate_circle(
t::math::point(point.x, point.y),
radius,
&fill_options.with_tolerance(tolerance),
&mut t::BuffersBuilder::new(buffers, vb),
);
}
DrawMode::Stroke(options) => {
let mut tessellator = t::StrokeTessellator::new();
let _ = tessellator.tessellate_circle(
t::math::point(point.x, point.y),
radius,
&options.with_tolerance(tolerance),
&mut t::BuffersBuilder::new(buffers, vb),
);
}
};
}
Ok(self)
}
pub fn ellipse<P>(
&mut self,
mode: DrawMode,
point: P,
radius1: f32,
radius2: f32,
tolerance: f32,
color: Color,
) -> GameResult<&mut Self>
where
P: Into<mint::Point2<f32>>,
{
assert!(
tolerance > 0.0,
"Tolerances <= 0 are invalid, see https://github.com/ggez/ggez/issues/892"
);
{
let buffers = &mut self.buffer;
let point = point.into();
let vb = VertexBuilder { color };
match mode {
DrawMode::Fill(fill_options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let mut tessellator = t::FillTessellator::new();
let _ = tessellator.tessellate_ellipse(
t::math::point(point.x, point.y),
t::math::vector(radius1, radius2),
t::math::Angle { radians: 0.0 },
t::path::Winding::Positive,
&fill_options.with_tolerance(tolerance),
builder,
);
}
DrawMode::Stroke(options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let mut tessellator = t::StrokeTessellator::new();
let _ = tessellator.tessellate_ellipse(
t::math::point(point.x, point.y),
t::math::vector(radius1, radius2),
t::math::Angle { radians: 0.0 },
t::path::Winding::Positive,
&options.with_tolerance(tolerance),
builder,
);
}
};
}
Ok(self)
}
pub fn polyline<P>(
&mut self,
mode: DrawMode,
points: &[P],
color: Color,
) -> GameResult<&mut Self>
where
P: Into<mint::Point2<f32>> + Clone,
{
if points.len() < 2 {
return Err(GameError::LyonError(
"MeshBuilder::polyline() got a list of < 2 points".to_string(),
));
}
self.polyline_inner(mode, points, false, color)
}
pub fn polygon<P>(
&mut self,
mode: DrawMode,
points: &[P],
color: Color,
) -> GameResult<&mut Self>
where
P: Into<mint::Point2<f32>> + Clone,
{
if points.len() < 3 {
return Err(GameError::LyonError(
"MeshBuilder::polygon() got a list of < 3 points".to_string(),
));
}
self.polyline_inner(mode, points, true, color)
}
fn polyline_inner<P>(
&mut self,
mode: DrawMode,
points: &[P],
is_closed: bool,
color: Color,
) -> GameResult<&mut Self>
where
P: Into<mint::Point2<f32>> + Clone,
{
let vb = VertexBuilder { color };
self.polyline_with_vertex_builder(mode, points, is_closed, vb)
}
pub fn polyline_with_vertex_builder<P, V>(
&mut self,
mode: DrawMode,
points: &[P],
is_closed: bool,
vb: V,
) -> GameResult<&mut Self>
where
P: Into<mint::Point2<f32>> + Clone,
V: t::StrokeVertexConstructor<Vertex> + t::FillVertexConstructor<Vertex>,
{
{
assert!(points.len() > 1);
let buffers = &mut self.buffer;
let points: Vec<LPoint> = points
.iter()
.cloned()
.map(|p| {
let mint_point: mint::Point2<f32> = p.into();
t::math::point(mint_point.x, mint_point.y)
})
.collect();
let polygon = Polygon {
points: &points,
closed: is_closed,
};
match mode {
DrawMode::Fill(options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let tessellator = &mut t::FillTessellator::new();
let _ = tessellator.tessellate_polygon(polygon, &options, builder)?;
}
DrawMode::Stroke(options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let tessellator = &mut t::StrokeTessellator::new();
let _ = tessellator.tessellate_polygon(polygon, &options, builder)?;
}
};
}
Ok(self)
}
pub fn rectangle(
&mut self,
mode: DrawMode,
bounds: Rect,
color: Color,
) -> GameResult<&mut Self> {
{
let buffers = &mut self.buffer;
let rect = t::math::rect(bounds.x, bounds.y, bounds.w, bounds.h);
let vb = VertexBuilder { color };
match mode {
DrawMode::Fill(fill_options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let mut tessellator = t::FillTessellator::new();
let _ = tessellator.tessellate_rectangle(&rect, &fill_options, builder);
}
DrawMode::Stroke(options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let mut tessellator = t::StrokeTessellator::new();
let _ = tessellator.tessellate_rectangle(&rect, &options, builder);
}
};
}
Ok(self)
}
pub fn rounded_rectangle(
&mut self,
mode: DrawMode,
bounds: Rect,
radius: f32,
color: Color,
) -> GameResult<&mut Self> {
{
let buffers = &mut self.buffer;
let rect = t::math::rect(bounds.x, bounds.y, bounds.w, bounds.h);
let radii = t::path::builder::BorderRadii::new(radius);
let vb = VertexBuilder { color };
let mut path_builder = t::path::Path::builder();
path_builder.add_rounded_rectangle(&rect, &radii, t::path::Winding::Positive);
let path = path_builder.build();
match mode {
DrawMode::Fill(fill_options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let mut tessellator = t::FillTessellator::new();
let _ = tessellator.tessellate_path(&path, &fill_options, builder);
}
DrawMode::Stroke(options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let mut tessellator = t::StrokeTessellator::new();
let _ = tessellator.tessellate_path(&path, &options, builder);
}
};
}
Ok(self)
}
pub fn triangles<P>(&mut self, triangles: &[P], color: Color) -> GameResult<&mut Self>
where
P: Into<mint::Point2<f32>> + Clone,
{
{
if (triangles.len() % 3) != 0 {
return Err(GameError::LyonError(String::from(
"Called Mesh::triangles() with points that have a length not a multiple of 3.",
)));
}
let tris = triangles
.iter()
.cloned()
.map(|p| {
let mint_point = p.into();
lyon::math::point(mint_point.x, mint_point.y)
})
.collect::<Vec<_>>();
let tris = tris.chunks(3);
let vb = VertexBuilder { color };
for tri in tris {
assert_eq!(tri.len(), 3);
let first_index: u16 = self.buffer.vertices.len().try_into().unwrap();
self.buffer.vertices.push(vb.new_vertex(tri[0]));
self.buffer.vertices.push(vb.new_vertex(tri[1]));
self.buffer.vertices.push(vb.new_vertex(tri[2]));
self.buffer.indices.push(first_index);
self.buffer.indices.push(first_index + 1);
self.buffer.indices.push(first_index + 2);
}
}
Ok(self)
}
pub fn texture(&mut self, image: Image) -> GameResult<&mut Self> {
self.tex_filter = Some(image.filter());
self.tex_clones_hack = Some(image.texture_clones_hack.clone());
self.texture = Some(image.texture);
Ok(self)
}
pub fn set_filter(&mut self, filter: FilterMode) {
self.tex_filter = Some(filter);
}
pub fn filter(&self) -> Option<FilterMode> {
self.tex_filter
}
pub fn raw<V>(
&mut self,
verts: &[V],
indices: &[u16],
texture: Option<Image>,
) -> GameResult<&mut Self>
where
V: Into<Vertex> + Clone,
{
assert!(self.buffer.vertices.len() + verts.len() < (u16::MAX as usize));
assert!(self.buffer.indices.len() + indices.len() < (u16::MAX as usize));
let next_idx = self.buffer.vertices.len() as u16;
let vertices = verts.iter().cloned().map(|v: V| -> Vertex { v.into() });
let indices = indices.iter().map(|i| (*i) + next_idx);
self.buffer.vertices.extend(vertices);
self.buffer.indices.extend(indices);
if let Some(image) = texture {
self.tex_filter = Some(image.filter());
self.tex_clones_hack = Some(image.texture_clones_hack.clone());
self.texture = Some(image.texture);
}
Ok(self)
}
pub fn build(
&self,
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
) -> GameResult<Mesh> {
let vertex_buffer = miniquad::Buffer::immutable(
quad_ctx,
miniquad::BufferType::VertexBuffer,
&self.buffer.vertices[..],
);
let index_buffer = miniquad::Buffer::immutable(
quad_ctx,
miniquad::BufferType::IndexBuffer,
&self.buffer.indices[..],
);
let attribute_buffer = Buffer::stream(
quad_ctx,
BufferType::VertexBuffer,
std::mem::size_of::<InstanceAttributes>(), );
if let Some((filter, texture)) = self.tex_filter.zip(self.texture) {
texture.set_filter(quad_ctx, filter);
}
let bindings = miniquad::Bindings {
vertex_buffers: vec![vertex_buffer, attribute_buffer],
index_buffer,
images: self
.texture
.map_or(vec![ctx.gfx_context.white_texture], |texture| vec![texture]),
};
let rect = bbox_for_vertices(&self.buffer.vertices).expect("No vertices in MeshBuilder");
Ok(Mesh {
bindings,
blend_mode: None,
rect,
texture_clones_hack: self.tex_clones_hack.clone(),
})
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
struct VertexBuilder {
color: Color,
}
impl VertexBuilder {
fn new_vertex(self, position: LPoint) -> Vertex {
Vertex {
pos: [position.x, position.y],
uv: [position.x, position.y],
color: self.color.into(),
}
}
}
impl t::StrokeVertexConstructor<Vertex> for VertexBuilder {
fn new_vertex(&mut self, vertex: t::StrokeVertex) -> Vertex {
let position = vertex.position();
Vertex {
pos: [position.x, position.y],
uv: [0.0, 0.0],
color: self.color.into(),
}
}
}
impl t::FillVertexConstructor<Vertex> for VertexBuilder {
fn new_vertex(&mut self, vertex: t::FillVertex) -> Vertex {
let position = vertex.position();
Vertex {
pos: [position.x, position.y],
uv: [0.0, 0.0],
color: self.color.into(),
}
}
}
#[derive(Debug)]
pub struct Mesh {
bindings: miniquad::Bindings,
blend_mode: Option<BlendMode>,
rect: Rect,
texture_clones_hack: Option<Arc<()>>,
}
impl Drop for Mesh {
fn drop(&mut self) {
let delete_texture = self
.texture_clones_hack
.as_ref()
.map_or(false, |arc| Arc::strong_count(arc) == 1);
crate::graphics::add_dropped_bindings(self.bindings.clone(), delete_texture);
}
}
impl Mesh {
pub fn new_line<P>(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
points: &[P],
width: f32,
color: Color,
) -> GameResult<Mesh>
where
P: Into<mint::Point2<f32>> + Clone,
{
let mut mb = MeshBuilder::new();
let _ = mb.polyline(DrawMode::stroke(width), points, color);
mb.build(ctx, quad_ctx)
}
pub fn new_circle<P>(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
mode: DrawMode,
point: P,
radius: f32,
tolerance: f32,
color: Color,
) -> GameResult<Mesh>
where
P: Into<mint::Point2<f32>>,
{
let mut mb = MeshBuilder::new();
let _ = mb.circle(mode, point, radius, tolerance, color);
mb.build(ctx, quad_ctx)
}
pub fn new_ellipse<P>(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
mode: DrawMode,
point: P,
radius1: f32,
radius2: f32,
tolerance: f32,
color: Color,
) -> GameResult<Mesh>
where
P: Into<mint::Point2<f32>>,
{
let mut mb = MeshBuilder::new();
let _ = mb.ellipse(mode, point, radius1, radius2, tolerance, color);
mb.build(ctx, quad_ctx)
}
pub fn new_polyline<P>(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
mode: DrawMode,
points: &[P],
color: Color,
) -> GameResult<Mesh>
where
P: Into<mint::Point2<f32>> + Clone,
{
let mut mb = MeshBuilder::new();
let _ = mb.polyline(mode, points, color);
mb.build(ctx, quad_ctx)
}
pub fn new_polygon<P>(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
mode: DrawMode,
points: &[P],
color: Color,
) -> GameResult<Mesh>
where
P: Into<mint::Point2<f32>> + Clone,
{
if points.len() < 3 {
return Err(GameError::LyonError(
"Mesh::new_polygon() got a list of < 3 points".to_string(),
));
}
let mut mb = MeshBuilder::new();
let _ = mb.polygon(mode, points, color);
mb.build(ctx, quad_ctx)
}
pub fn new_rectangle(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
mode: DrawMode,
bounds: Rect,
color: Color,
) -> GameResult<Mesh> {
let mut mb = MeshBuilder::new();
let _ = mb.rectangle(mode, bounds, color);
mb.build(ctx, quad_ctx)
}
pub fn new_rounded_rectangle(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
mode: DrawMode,
bounds: Rect,
radius: f32,
color: Color,
) -> GameResult<Mesh> {
let mut mb = MeshBuilder::new();
let _ = mb.rounded_rectangle(mode, bounds, radius, color);
mb.build(ctx, quad_ctx)
}
pub fn from_triangles<P>(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
triangles: &[P],
color: Color,
) -> GameResult<Mesh>
where
P: Into<mint::Point2<f32>> + Clone,
{
let mut mb = MeshBuilder::new();
let _ = mb.triangles(triangles, color);
mb.build(ctx, quad_ctx)
}
pub fn from_raw<V>(
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
verts: &[V],
indices: &[u16],
image: Option<Image>,
) -> GameResult<Mesh>
where
V: Into<Vertex> + Clone,
{
if verts.len() > (u16::MAX as usize) {
let msg = format!(
"Tried to build a mesh with {} vertices, max is u16::MAX",
verts.len()
);
return Err(GameError::LyonError(msg));
}
if indices.len() > (u16::MAX as usize) {
let msg = format!(
"Tried to build a mesh with {} indices, max is u16::MAX",
indices.len()
);
return Err(GameError::LyonError(msg));
}
if verts.len() < 3 {
let msg = format!("Trying to build mesh with < 3 vertices, this is usually due to invalid input to a `Mesh` or MeshBuilder`.");
return Err(GameError::LyonError(msg));
}
if indices.len() < 3 {
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);
return Err(GameError::LyonError(msg));
}
if indices.len() % 3 != 0 {
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`.");
return Err(GameError::LyonError(msg));
}
let vertex_buffer =
miniquad::Buffer::immutable(quad_ctx, miniquad::BufferType::VertexBuffer, &verts[..]);
let index_buffer =
miniquad::Buffer::immutable(quad_ctx, miniquad::BufferType::IndexBuffer, &indices[..]);
let attribute_buffer = Buffer::stream(
quad_ctx,
BufferType::VertexBuffer,
std::mem::size_of::<InstanceAttributes>(), );
let verts: Vec<Vertex> = verts.iter().cloned().map(Into::into).collect();
let rect = bbox_for_vertices(&verts).expect(
"No vertices in MeshBuilder; should never happen since we already checked this",
);
let (images, texture_clones_hack) = image
.map_or((vec![ctx.gfx_context.white_texture], None), |image| {
(vec![image.texture], Some(image.texture_clones_hack.clone()))
});
let bindings = miniquad::Bindings {
vertex_buffers: vec![vertex_buffer, attribute_buffer],
index_buffer,
images,
};
Ok(Mesh {
bindings,
blend_mode: None,
rect,
texture_clones_hack,
})
}
}
impl Drawable for Mesh {
fn draw(
&self,
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
param: DrawParam,
) -> GameResult {
let instance = InstanceAttributes::from(¶m);
self.bindings.vertex_buffers[1].update(quad_ctx, &[instance]);
let pass = ctx.framebuffer();
quad_ctx.begin_pass(pass, PassAction::Nothing);
quad_ctx.apply_bindings(&self.bindings);
let shader_id = *ctx.gfx_context.current_shader.borrow();
let current_shader = &mut ctx.gfx_context.shaders[shader_id];
quad_ctx.apply_pipeline(¤t_shader.pipeline);
apply_uniforms(ctx, quad_ctx, shader_id, None);
let mut custom_blend = false;
if let Some(blend_mode) = self.blend_mode() {
custom_blend = true;
crate::graphics::set_current_blend_mode(quad_ctx, blend_mode)
}
quad_ctx.draw(0, self.bindings.index_buffer.size() as i32 / 2, 1);
if custom_blend {
crate::graphics::restore_blend_mode(ctx, quad_ctx);
}
quad_ctx.end_render_pass();
Ok(())
}
fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
self.blend_mode = mode;
}
fn blend_mode(&self) -> Option<BlendMode> {
self.blend_mode
}
fn dimensions(&self, _ctx: &mut Context) -> Option<Rect> {
Some(self.rect)
}
}
fn bbox_for_vertices(verts: &[Vertex]) -> Option<Rect> {
if verts.is_empty() {
return None;
}
let [x0, y0] = verts[0].pos;
let mut x_max = x0;
let mut x_min = x0;
let mut y_max = y0;
let mut y_min = y0;
for v in verts {
let x = v.pos[0];
let y = v.pos[1];
x_max = f32::max(x_max, x);
x_min = f32::min(x_min, x);
y_max = f32::max(y_max, y);
y_min = f32::min(y_min, y);
}
Some(Rect {
w: x_max - x_min,
h: y_max - y_min,
x: x_min,
y: y_min,
})
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MeshIdx(pub usize);
#[derive(Debug)]
pub struct MeshBatch {
mesh: Mesh,
instance_params: Vec<DrawParam>,
gpu_instance_params: Vec<InstanceAttributes>,
instance_buffer_dirty: bool,
}
impl MeshBatch {
pub fn new(mesh: Mesh) -> GameResult<MeshBatch> {
Ok(MeshBatch {
mesh,
instance_params: Vec::new(),
gpu_instance_params: vec![],
instance_buffer_dirty: true,
})
}
pub fn clear(&mut self) {
self.instance_params.clear();
self.instance_buffer_dirty = true;
}
pub fn get_instance_params(&self) -> &[DrawParam] {
&self.instance_params
}
pub fn get_instance_params_mut(&mut self) -> &mut [DrawParam] {
&mut self.instance_params
}
pub fn add<P>(&mut self, param: P) -> MeshIdx
where
P: Into<DrawParam>,
{
self.instance_params.push(param.into());
self.instance_buffer_dirty = true;
MeshIdx(self.instance_params.len() - 1)
}
pub fn set<P>(&mut self, handle: MeshIdx, param: P) -> GameResult
where
P: Into<DrawParam>,
{
if handle.0 < self.instance_params.len() {
self.instance_params[handle.0] = param.into();
self.instance_buffer_dirty = true;
Ok(())
} else {
Err(GameError::RenderError(String::from("Index out of bounds")))
}
}
pub fn set_range<P>(&mut self, first_handle: MeshIdx, params: &[P]) -> GameResult
where
P: Into<DrawParam> + Copy,
{
let first_param = first_handle.0;
let num_params = params.len();
if first_param < self.instance_params.len()
&& (first_param + num_params) <= self.instance_params.len()
{
for (i, item) in params.iter().enumerate().take(num_params) {
self.instance_params[first_param + i] = (*item).into();
}
self.instance_buffer_dirty = true;
Ok(())
} else {
Err(GameError::RenderError(String::from("Range out of bounds")))
}
}
pub fn flush_range(
&mut self,
_ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
first_handle: MeshIdx,
count: usize,
) -> GameResult {
let first_param = first_handle.0;
let slice_end = first_param + count;
if first_param < self.instance_params.len() && slice_end <= self.instance_params.len() {
let needs_new_buffer = self.gpu_instance_params.len() < slice_end;
let mut mesh = &mut self.mesh;
let mut gpu_instance_params = &mut self.gpu_instance_params;
if needs_new_buffer {
gpu_instance_params
.resize(self.instance_params.len(), InstanceAttributes::default());
let buffer = Buffer::stream(
quad_ctx,
BufferType::VertexBuffer,
std::mem::size_of::<InstanceAttributes>() * self.instance_params.len(),
);
mesh.bindings.vertex_buffers[1].delete();
mesh.bindings.vertex_buffers[1] = buffer;
}
let slice = if needs_new_buffer {
&self.instance_params
} else {
&self.instance_params[first_param..slice_end]
};
for (n, param) in slice.iter().enumerate() {
let instance = InstanceAttributes {
model: param.trans.to_bare_matrix().into(),
source: Vector4::new(param.src.x, param.src.y, param.src.w, param.src.h),
color: Vector4::new(param.color.r, param.color.g, param.color.b, param.color.a),
};
gpu_instance_params[n] = instance;
}
mesh.bindings.vertex_buffers[1].update(quad_ctx, &gpu_instance_params[..]);
self.instance_buffer_dirty = false;
Ok(())
} else {
Err(GameError::RenderError(String::from("Range out of bounds")))
}
}
pub fn flush(
&mut self,
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
) -> GameResult {
self.flush_range(ctx, quad_ctx, MeshIdx(0), self.instance_params.len())
}
pub fn draw(
&mut self,
ctx: &mut Context,
quad_ctx: &mut miniquad::graphics::GraphicsContext,
param: DrawParam,
) -> GameResult {
if !self.instance_params.is_empty() {
if self.instance_buffer_dirty {
self.flush(ctx, quad_ctx)?;
}
let pass = ctx.framebuffer();
quad_ctx.begin_pass(pass, PassAction::Nothing);
quad_ctx.apply_bindings(&self.mesh.bindings);
let shader_id = *ctx.gfx_context.current_shader.borrow();
let current_shader = &mut ctx.gfx_context.shaders[shader_id];
quad_ctx.apply_pipeline(¤t_shader.pipeline);
apply_uniforms(
ctx,
quad_ctx,
shader_id,
Some(cgmath::Matrix4::from(param.trans.to_bare_matrix())),
);
let mut custom_blend = false;
if let Some(blend_mode) = self.blend_mode() {
custom_blend = true;
crate::graphics::set_current_blend_mode(quad_ctx, blend_mode)
}
quad_ctx.draw(
0,
self.mesh.bindings.index_buffer.size() as i32 / 2,
self.instance_params.len() as i32,
);
if custom_blend {
crate::graphics::restore_blend_mode(ctx, quad_ctx);
}
quad_ctx.end_render_pass();
}
Ok(())
}
pub fn dimensions(&self, ctx: &mut Context) -> Option<Rect> {
if self.instance_params.is_empty() {
return None;
}
if let Some(dimensions) = self.mesh.dimensions(ctx) {
self.instance_params
.iter()
.map(|¶m| transform_rect(dimensions, param))
.fold(None, |acc: Option<Rect>, rect| {
Some(if let Some(acc) = acc {
acc.combine_with(rect)
} else {
rect
})
})
} else {
None
}
}
pub fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
self.mesh.set_blend_mode(mode)
}
pub fn blend_mode(&self) -> Option<BlendMode> {
self.mesh.blend_mode()
}
}