pub use lyon_tessellation::path::builder::BorderRadii;
use std::rc::Rc;
use bytemuck::{Pod, Zeroable};
use lyon_tessellation::geom::euclid::Point2D;
use lyon_tessellation::math::{Angle, Box2D, Point, Vector};
use lyon_tessellation::path::{Polygon, Winding};
use lyon_tessellation::{
BuffersBuilder, FillOptions, FillTessellator, FillVertex, FillVertexConstructor, StrokeOptions,
StrokeTessellator, StrokeVertex, StrokeVertexConstructor, VertexBuffers,
};
use crate::graphics::{self, Color, DrawParams, Rectangle, Texture};
use crate::math::Vec2;
use crate::platform::{RawIndexBuffer, RawVertexBuffer};
use crate::Context;
use crate::{Result, TetraError};
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, PartialEq)]
pub struct Vertex {
pub position: Vec2<f32>,
pub uv: Vec2<f32>,
pub color: Color,
}
impl Vertex {
pub fn new(position: Vec2<f32>, uv: Vec2<f32>, color: Color) -> Vertex {
Vertex {
position,
uv,
color,
}
}
}
unsafe impl Pod for Vertex {}
unsafe impl Zeroable for Vertex {}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BufferUsage {
Static,
Dynamic,
Stream,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum VertexWinding {
Clockwise,
CounterClockwise,
}
impl VertexWinding {
pub fn flipped(self) -> VertexWinding {
match self {
VertexWinding::Clockwise => VertexWinding::CounterClockwise,
VertexWinding::CounterClockwise => VertexWinding::Clockwise,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct VertexBuffer {
handle: Rc<RawVertexBuffer>,
}
impl VertexBuffer {
pub fn new(ctx: &mut Context, vertices: &[Vertex]) -> Result<VertexBuffer> {
VertexBuffer::with_usage(ctx, vertices, BufferUsage::Dynamic)
}
pub fn with_usage(
ctx: &mut Context,
vertices: &[Vertex],
usage: BufferUsage,
) -> Result<VertexBuffer> {
let buffer = ctx.device.new_vertex_buffer(vertices.len(), usage)?;
ctx.device.set_vertex_buffer_data(&buffer, vertices, 0);
Ok(VertexBuffer {
handle: Rc::new(buffer),
})
}
pub fn set_data(&self, ctx: &mut Context, vertices: &[Vertex], offset: usize) {
ctx.device
.set_vertex_buffer_data(&self.handle, vertices, offset);
}
pub fn into_mesh(self) -> Mesh {
Mesh::new(self)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct IndexBuffer {
handle: Rc<RawIndexBuffer>,
}
impl IndexBuffer {
pub fn new(ctx: &mut Context, indices: &[u32]) -> Result<IndexBuffer> {
IndexBuffer::with_usage(ctx, indices, BufferUsage::Dynamic)
}
pub fn with_usage(
ctx: &mut Context,
indices: &[u32],
usage: BufferUsage,
) -> Result<IndexBuffer> {
let buffer = ctx.device.new_index_buffer(indices.len(), usage)?;
ctx.device.set_index_buffer_data(&buffer, indices, 0);
Ok(IndexBuffer {
handle: Rc::new(buffer),
})
}
pub fn set_data(&self, ctx: &mut Context, indices: &[u32], offset: usize) {
ctx.device
.set_index_buffer_data(&self.handle, indices, offset);
}
}
#[derive(Copy, Clone, Debug)]
struct DrawRange {
start: usize,
count: usize,
}
#[derive(Copy, Clone, Debug)]
pub enum ShapeStyle {
Fill,
Stroke(f32),
}
#[derive(Clone, Debug)]
pub struct Mesh {
vertex_buffer: VertexBuffer,
index_buffer: Option<IndexBuffer>,
texture: Option<Texture>,
draw_range: Option<DrawRange>,
winding: VertexWinding,
backface_culling: bool,
}
impl Mesh {
pub fn new(vertex_buffer: VertexBuffer) -> Mesh {
Mesh {
vertex_buffer,
index_buffer: None,
texture: None,
draw_range: None,
winding: VertexWinding::CounterClockwise,
backface_culling: true,
}
}
pub fn indexed(vertex_buffer: VertexBuffer, index_buffer: IndexBuffer) -> Mesh {
Mesh {
vertex_buffer,
index_buffer: Some(index_buffer),
texture: None,
winding: VertexWinding::CounterClockwise,
draw_range: None,
backface_culling: true,
}
}
pub fn draw<P>(&self, ctx: &mut Context, params: P)
where
P: Into<DrawParams>,
{
self.draw_instanced(ctx, 1, params);
}
pub fn draw_instanced<P>(&self, ctx: &mut Context, instances: usize, params: P)
where
P: Into<DrawParams>,
{
graphics::flush(ctx);
let texture = self
.texture
.as_ref()
.unwrap_or(&ctx.graphics.default_texture);
let shader = ctx
.graphics
.shader
.as_ref()
.unwrap_or(&ctx.graphics.default_shader);
let params = params.into();
let model_matrix = params.to_matrix();
let _ = shader.set_default_uniforms(
&mut ctx.device,
ctx.graphics.projection_matrix * ctx.graphics.transform_matrix * model_matrix,
params.color,
);
ctx.device.cull_face(self.backface_culling);
ctx.device.front_face(match &ctx.graphics.canvas {
None => self.winding,
Some(_) => self.winding.flipped(),
});
let (start, count) = match (self.draw_range, &self.index_buffer) {
(Some(d), _) => (d.start, d.count),
(_, Some(i)) => (0, i.handle.count()),
(_, None) => (0, self.vertex_buffer.handle.count()),
};
ctx.device.draw_instanced(
&self.vertex_buffer.handle,
self.index_buffer.as_ref().map(|i| &*i.handle),
&texture.data.handle,
&shader.data.handle,
start,
count,
instances,
);
}
pub fn vertex_buffer(&self) -> &VertexBuffer {
&self.vertex_buffer
}
pub fn set_vertex_buffer(&mut self, vertex_buffer: VertexBuffer) {
self.vertex_buffer = vertex_buffer;
}
pub fn index_buffer(&self) -> Option<&IndexBuffer> {
self.index_buffer.as_ref()
}
pub fn set_index_buffer(&mut self, index_buffer: IndexBuffer) {
self.index_buffer = Some(index_buffer);
}
pub fn reset_index_buffer(&mut self) {
self.index_buffer = None;
}
pub fn texture(&self) -> Option<&Texture> {
self.texture.as_ref()
}
pub fn set_texture(&mut self, texture: Texture) {
self.texture = Some(texture);
}
pub fn reset_texture(&mut self) {
self.texture = None;
}
pub fn front_face_winding(&self) -> VertexWinding {
self.winding
}
pub fn set_front_face_winding(&mut self, winding: VertexWinding) {
self.winding = winding;
}
pub fn backface_culling(&self) -> bool {
self.backface_culling
}
pub fn set_backface_culling(&mut self, enabled: bool) {
self.backface_culling = enabled;
}
pub fn set_draw_range(&mut self, start: usize, count: usize) {
self.draw_range = Some(DrawRange { start, count });
}
pub fn reset_draw_range(&mut self) {
self.draw_range = None;
}
}
impl Mesh {
pub fn rectangle(ctx: &mut Context, style: ShapeStyle, rectangle: Rectangle) -> Result<Mesh> {
GeometryBuilder::new()
.rectangle(style, rectangle)?
.build_mesh(ctx)
}
pub fn rounded_rectangle(
ctx: &mut Context,
style: ShapeStyle,
rectangle: Rectangle,
radii: BorderRadii,
) -> Result<Mesh> {
GeometryBuilder::new()
.rounded_rectangle(style, rectangle, radii)?
.build_mesh(ctx)
}
pub fn circle(
ctx: &mut Context,
style: ShapeStyle,
center: Vec2<f32>,
radius: f32,
) -> Result<Mesh> {
GeometryBuilder::new()
.circle(style, center, radius)?
.build_mesh(ctx)
}
pub fn ellipse(
ctx: &mut Context,
style: ShapeStyle,
center: Vec2<f32>,
radii: Vec2<f32>,
) -> Result<Mesh> {
GeometryBuilder::new()
.ellipse(style, center, radii)?
.build_mesh(ctx)
}
pub fn polygon(ctx: &mut Context, style: ShapeStyle, points: &[Vec2<f32>]) -> Result<Mesh> {
GeometryBuilder::new()
.polygon(style, points)?
.build_mesh(ctx)
}
pub fn polyline(ctx: &mut Context, stroke_width: f32, points: &[Vec2<f32>]) -> Result<Mesh> {
GeometryBuilder::new()
.polyline(stroke_width, points)?
.build_mesh(ctx)
}
}
impl From<VertexBuffer> for Mesh {
fn from(buffer: VertexBuffer) -> Self {
Mesh::new(buffer)
}
}
fn to_box2d(rectangle: Rectangle) -> Box2D {
Box2D::new(
Point2D::new(rectangle.x, rectangle.y),
Point2D::new(rectangle.right(), rectangle.bottom()),
)
}
struct TetraVertexConstructor(Color);
impl FillVertexConstructor<Vertex> for TetraVertexConstructor {
fn new_vertex(&mut self, vertex: FillVertex) -> Vertex {
let position = vertex.position();
Vertex::new(Vec2::new(position.x, position.y), Vec2::zero(), self.0)
}
}
impl StrokeVertexConstructor<Vertex> for TetraVertexConstructor {
fn new_vertex(&mut self, vertex: StrokeVertex) -> Vertex {
let position = vertex.position();
Vertex::new(Vec2::new(position.x, position.y), Vec2::zero(), self.0)
}
}
#[derive(Debug, Clone)]
pub struct GeometryBuilder {
data: VertexBuffers<Vertex, u32>,
color: Color,
}
impl GeometryBuilder {
pub fn new() -> GeometryBuilder {
GeometryBuilder {
data: VertexBuffers::new(),
color: Color::WHITE,
}
}
pub fn rectangle(
&mut self,
style: ShapeStyle,
rectangle: Rectangle,
) -> Result<&mut GeometryBuilder> {
let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
match style {
ShapeStyle::Fill => {
let options = FillOptions::default();
let mut tessellator = FillTessellator::new();
tessellator
.tessellate_rectangle(&to_box2d(rectangle), &options, &mut builder)
.map_err(TetraError::TessellationError)?;
}
ShapeStyle::Stroke(width) => {
let options = StrokeOptions::default().with_line_width(width);
let mut tessellator = StrokeTessellator::new();
tessellator
.tessellate_rectangle(&to_box2d(rectangle), &options, &mut builder)
.map_err(TetraError::TessellationError)?;
}
}
Ok(self)
}
pub fn rounded_rectangle(
&mut self,
style: ShapeStyle,
rectangle: Rectangle,
radii: BorderRadii,
) -> Result<&mut GeometryBuilder> {
let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
match style {
ShapeStyle::Fill => {
let options = FillOptions::default();
let mut tessellator = FillTessellator::new();
let mut builder = tessellator.builder(&options, &mut builder);
builder.add_rounded_rectangle(&to_box2d(rectangle), &radii, Winding::Positive);
builder.build().map_err(TetraError::TessellationError)?;
}
ShapeStyle::Stroke(width) => {
let options = StrokeOptions::default().with_line_width(width);
let mut tessellator = StrokeTessellator::new();
let mut builder = tessellator.builder(&options, &mut builder);
builder.add_rounded_rectangle(&to_box2d(rectangle), &radii, Winding::Positive);
builder.build().map_err(TetraError::TessellationError)?;
}
}
Ok(self)
}
pub fn circle(
&mut self,
style: ShapeStyle,
center: Vec2<f32>,
radius: f32,
) -> Result<&mut GeometryBuilder> {
let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
match style {
ShapeStyle::Fill => {
let options = FillOptions::default();
let mut tessellator = FillTessellator::new();
tessellator
.tessellate_circle(
Point::new(center.x, center.y),
radius,
&options,
&mut builder,
)
.map_err(TetraError::TessellationError)?;
}
ShapeStyle::Stroke(width) => {
let options = StrokeOptions::default().with_line_width(width);
let mut tessellator = StrokeTessellator::new();
tessellator
.tessellate_circle(
Point::new(center.x, center.y),
radius,
&options,
&mut builder,
)
.map_err(TetraError::TessellationError)?;
}
}
Ok(self)
}
pub fn ellipse(
&mut self,
style: ShapeStyle,
center: Vec2<f32>,
radii: Vec2<f32>,
) -> Result<&mut GeometryBuilder> {
let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
match style {
ShapeStyle::Fill => {
let options = FillOptions::default();
let mut tessellator = FillTessellator::new();
tessellator
.tessellate_ellipse(
Point::new(center.x, center.y),
Vector::new(radii.x, radii.y),
Angle::radians(0.0),
Winding::Positive,
&options,
&mut builder,
)
.map_err(TetraError::TessellationError)?;
}
ShapeStyle::Stroke(width) => {
let options = StrokeOptions::default().with_line_width(width);
let mut tessellator = StrokeTessellator::new();
tessellator
.tessellate_ellipse(
Point::new(center.x, center.y),
Vector::new(radii.x, radii.y),
Angle::radians(0.0),
Winding::Positive,
&options,
&mut builder,
)
.map_err(TetraError::TessellationError)?;
}
}
Ok(self)
}
pub fn polygon(
&mut self,
style: ShapeStyle,
points: &[Vec2<f32>],
) -> Result<&mut GeometryBuilder> {
let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
let points: Vec<Point> = points
.iter()
.map(|point| Point::new(point.x, point.y))
.collect();
let polygon = Polygon {
points: &points,
closed: true,
};
match style {
ShapeStyle::Fill => {
let options = FillOptions::default();
let mut tessellator = FillTessellator::new();
tessellator
.tessellate_polygon(polygon, &options, &mut builder)
.map_err(TetraError::TessellationError)?;
}
ShapeStyle::Stroke(width) => {
let options = StrokeOptions::default().with_line_width(width);
let mut tessellator = StrokeTessellator::new();
tessellator
.tessellate_polygon(polygon, &options, &mut builder)
.map_err(TetraError::TessellationError)?;
}
}
Ok(self)
}
pub fn polyline(
&mut self,
stroke_width: f32,
points: &[Vec2<f32>],
) -> Result<&mut GeometryBuilder> {
let mut builder = BuffersBuilder::new(&mut self.data, TetraVertexConstructor(self.color));
let points: Vec<Point> = points
.iter()
.map(|point| Point::new(point.x, point.y))
.collect();
let polygon = Polygon {
points: &points,
closed: false,
};
let options = StrokeOptions::default().with_line_width(stroke_width);
let mut tessellator = StrokeTessellator::new();
tessellator
.tessellate_polygon(polygon, &options, &mut builder)
.map_err(TetraError::TessellationError)?;
Ok(self)
}
pub fn set_color(&mut self, color: Color) -> &mut GeometryBuilder {
self.color = color;
self
}
pub fn clear(&mut self) -> &mut GeometryBuilder {
self.data.vertices.clear();
self.data.indices.clear();
self
}
pub fn vertices(&self) -> &[Vertex] {
&self.data.vertices
}
pub fn indices(&self) -> &[u32] {
&self.data.indices
}
pub fn into_data(self) -> (Vec<Vertex>, Vec<u32>) {
(self.data.vertices, self.data.indices)
}
pub fn build_buffers(&self, ctx: &mut Context) -> Result<(VertexBuffer, IndexBuffer)> {
Ok((
VertexBuffer::new(ctx, &self.data.vertices)?,
IndexBuffer::new(ctx, &self.data.indices)?,
))
}
pub fn build_mesh(&self, ctx: &mut Context) -> Result<Mesh> {
let (vertex_buffer, index_buffer) = self.build_buffers(ctx)?;
Ok(Mesh::indexed(vertex_buffer, index_buffer))
}
}
impl Default for GeometryBuilder {
fn default() -> Self {
GeometryBuilder::new()
}
}