use crate::context::DebugId;
use crate::error::GameError;
use crate::graphics::*;
use gfx::traits::FactoryExt;
use lyon;
use lyon::tessellation as t;
pub use self::t::{FillOptions, FillRule, LineCap, LineJoin, StrokeOptions};
#[derive(Debug, Clone)]
pub struct MeshBuilder {
buffer: t::geometry_builder::VertexBuffers<Vertex, u32>,
image: Option<Image>,
}
impl Default for MeshBuilder {
fn default() -> Self {
Self {
buffer: t::VertexBuffers::new(),
image: 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,
) -> &mut Self
where
P: Into<mint::Point2<f32>>,
{
{
let point = point.into();
let buffers = &mut self.buffer;
let vb = VertexBuilder { color };
match mode {
DrawMode::Fill(fill_options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let _ = t::basic_shapes::fill_circle(
t::math::point(point.x, point.y),
radius,
&fill_options.with_tolerance(tolerance),
builder,
);
}
DrawMode::Stroke(options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let _ = t::basic_shapes::stroke_circle(
t::math::point(point.x, point.y),
radius,
&options.with_tolerance(tolerance),
builder,
);
}
};
}
self
}
pub fn ellipse<P>(
&mut self,
mode: DrawMode,
point: P,
radius1: f32,
radius2: f32,
tolerance: f32,
color: Color,
) -> &mut Self
where
P: Into<mint::Point2<f32>>,
{
{
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 _ = t::basic_shapes::fill_ellipse(
t::math::point(point.x, point.y),
t::math::vector(radius1, radius2),
t::math::Angle { radians: 0.0 },
&fill_options.with_tolerance(tolerance),
builder,
);
}
DrawMode::Stroke(options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let _ = t::basic_shapes::stroke_ellipse(
t::math::point(point.x, point.y),
t::math::vector(radius1, radius2),
t::math::Angle { radians: 0.0 },
&options.with_tolerance(tolerance),
builder,
);
}
};
}
self
}
pub fn polyline<P>(
&mut self,
mode: DrawMode,
points: &[P],
color: Color,
) -> GameResult<&mut Self>
where
P: Into<mint::Point2<f32>> + Clone,
{
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,
{
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,
{
{
assert!(points.len() > 1);
let buffers = &mut self.buffer;
let points = points.into_iter().cloned().map(|p| {
let mint_point: mint::Point2<f32> = p.into();
t::math::point(mint_point.x, mint_point.y)
});
let vb = VertexBuilder { color };
match mode {
DrawMode::Fill(options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let tessellator = &mut t::FillTessellator::new();
let _ = t::basic_shapes::fill_polyline(points, tessellator, &options, builder)?;
}
DrawMode::Stroke(options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let _ = t::basic_shapes::stroke_polyline(points, is_closed, &options, builder);
}
};
}
Ok(self)
}
pub fn rectangle(&mut self, mode: DrawMode, bounds: Rect, color: Color) -> &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 _ = t::basic_shapes::fill_rectangle(&rect, &fill_options, builder);
}
DrawMode::Stroke(options) => {
let builder = &mut t::BuffersBuilder::new(buffers, vb);
let _ = t::basic_shapes::stroke_rectangle(&rect, &options, builder);
}
};
}
self
}
pub fn triangles<P>(&mut self, triangles: &[P], color: Color) -> GameResult<&mut Self>
where
P: Into<mint::Point2<f32>> + Clone,
{
{
assert_eq!(triangles.len() % 3, 0);
let tris = triangles
.iter()
.cloned()
.map(|p| {
let mint_point = p.into();
let np = lyon::math::point(mint_point.x, mint_point.y);
let nv = lyon::math::vector(mint_point.x, mint_point.y);
t::FillVertex {
position: np,
normal: nv,
}
})
.collect::<Vec<_>>();
let tris = tris.chunks(3);
let vb = VertexBuilder { color };
let builder: &mut t::BuffersBuilder<_, _, _, _> =
&mut t::BuffersBuilder::new(&mut self.buffer, vb);
use lyon::tessellation::GeometryBuilder;
builder.begin_geometry();
for tri in tris {
assert!(tri.len() == 3);
let fst = tri[0];
let snd = tri[1];
let thd = tri[2];
let i1 = builder.add_vertex(fst)?;
let i2 = builder.add_vertex(snd)?;
let i3 = builder.add_vertex(thd)?;
builder.add_triangle(i1, i2, i3);
}
let _ = builder.end_geometry();
}
Ok(self)
}
pub fn texture(&mut self, texture: Image) -> &mut Self {
self.image = Some(texture);
self
}
pub fn from_raw<V>(&mut self, verts: &[V], indices: &[u32], texture: Option<Image>) -> &mut Self
where
V: Into<Vertex> + Clone,
{
assert!(self.buffer.vertices.len() + verts.len() < (std::u32::MAX as usize));
assert!(self.buffer.indices.len() + indices.len() < (std::u32::MAX as usize));
let next_idx = self.buffer.vertices.len() as u32;
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);
self.image = texture;
self
}
pub fn build(&self, ctx: &mut Context) -> GameResult<Mesh> {
let (vbuf, slice) = ctx
.gfx_context
.factory
.create_vertex_buffer_with_slice(&self.buffer.vertices[..], &self.buffer.indices[..]);
let rect = bbox_for_vertices(&self.buffer.vertices)
.ok_or(GameError::RenderError("No vertices in MeshBuilder".into()))?;
Ok(Mesh {
buffer: vbuf,
slice,
blend_mode: None,
image: self
.image
.clone()
.unwrap_or(ctx.gfx_context.white_image.clone()),
debug_id: DebugId::get(ctx),
rect,
})
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
struct VertexBuilder {
color: Color,
}
impl t::VertexConstructor<t::FillVertex, Vertex> for VertexBuilder {
fn new_vertex(&mut self, vertex: t::FillVertex) -> Vertex {
Vertex {
pos: [vertex.position.x, vertex.position.y],
uv: [vertex.position.x, vertex.position.y],
color: self.color.into(),
}
}
}
impl t::VertexConstructor<t::StrokeVertex, Vertex> for VertexBuilder {
fn new_vertex(&mut self, vertex: t::StrokeVertex) -> Vertex {
Vertex {
pos: [vertex.position.x, vertex.position.y],
uv: [0.0, 0.0],
color: self.color.into(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Mesh {
buffer: gfx::handle::Buffer<gfx_device_gl::Resources, Vertex>,
slice: gfx::Slice<gfx_device_gl::Resources>,
blend_mode: Option<BlendMode>,
image: Image,
debug_id: DebugId,
rect: Rect,
}
impl Mesh {
pub fn new_line<P>(
ctx: &mut Context,
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)
}
pub fn new_circle<P>(
ctx: &mut Context,
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)
}
pub fn new_ellipse<P>(
ctx: &mut Context,
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)
}
pub fn new_polyline<P>(
ctx: &mut Context,
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)
}
pub fn new_polygon<P>(
ctx: &mut Context,
mode: DrawMode,
points: &[P],
color: Color,
) -> GameResult<Mesh>
where
P: Into<mint::Point2<f32>> + Clone,
{
let mut mb = MeshBuilder::new();
let _ = mb.polygon(mode, points, color);
mb.build(ctx)
}
pub fn new_rectangle(
ctx: &mut Context,
mode: DrawMode,
bounds: Rect,
color: Color,
) -> GameResult<Mesh> {
let mut mb = MeshBuilder::new();
let _ = mb.rectangle(mode, bounds, color);
mb.build(ctx)
}
pub fn from_triangles<P>(ctx: &mut Context, 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)
}
pub fn from_raw<V>(
ctx: &mut Context,
verts: &[V],
indices: &[u32],
texture: Option<Image>,
) -> Mesh
where
V: Into<Vertex> + Clone,
{
let verts: Vec<Vertex> = verts.iter().cloned().map(|v| v.into()).collect();
let rect = bbox_for_vertices(&verts).expect("No vertices in MeshBuilder");
let (vbuf, slice) = ctx
.gfx_context
.factory
.create_vertex_buffer_with_slice(&verts[..], indices);
Mesh {
buffer: vbuf,
slice,
blend_mode: None,
image: texture.unwrap_or(ctx.gfx_context.white_image.clone()),
debug_id: DebugId::get(ctx),
rect,
}
}
pub fn set_vertices(&mut self, ctx: &mut Context, verts: &[Vertex], indices: &[u32]) {
let (vbuf, slice) = ctx
.gfx_context
.factory
.create_vertex_buffer_with_slice(verts, indices);
self.buffer = vbuf;
self.slice = slice;
}
}
impl Drawable for Mesh {
fn draw(&self, ctx: &mut Context, param: DrawParam) -> GameResult {
self.debug_id.assert(ctx);
let gfx = &mut ctx.gfx_context;
gfx.update_instance_properties(param.into())?;
gfx.data.vbuf = self.buffer.clone();
let texture = self.image.texture.clone();
let sampler = gfx
.samplers
.get_or_insert(self.image.sampler_info, gfx.factory.as_mut());
let typed_thingy = gfx.backend_spec.raw_to_typed_shader_resource(texture);
gfx.data.tex = (typed_thingy, sampler);
gfx.draw(Some(&self.slice))?;
Ok(())
}
fn dimensions(&self, _ctx: &mut Context) -> Option<Rect> {
Some(self.rect)
}
fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
self.blend_mode = mode;
}
fn blend_mode(&self) -> Option<BlendMode> {
self.blend_mode
}
}
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,
})
}