use cgmath::SquareMatrix;
use cgmath::Zero;
use glow::*;
pub use owned_ttf_parser::OutlineBuilder as PathBuilder;
use owned_ttf_parser::AsFaceRef;
use std::convert::AsRef;
use std::f32::consts::PI;
use std::collections::HashMap;
use std::sync::Arc;
pub mod ffi;
#[derive(Copy, Clone, Debug)]
struct PathVertex { position: [f32; 2] }
pub struct TextRenderer {
font: owned_ttf_parser::OwnedFace,
cache: HashMap<char, lyon::tessellation::VertexBuffers<PathVertex, u16>>,
texture_cache: HashMap<char, u32>, pub units_per_em: u16
}
impl TextRenderer {
pub fn new (font: Vec<u8>) -> Option<Self> {
let owned_face = owned_ttf_parser::OwnedFace::from_vec(font, 0).ok()?;
let units_per_em = owned_face.as_face_ref().units_per_em()?;
Some(Self {
font: owned_face,
cache: HashMap::new(),
texture_cache: HashMap::new(),
units_per_em
})
}
fn create_path_from_char(&self, c: char) -> Path {
self.create_path(self.font.as_face_ref().glyph_index(c).unwrap())
}
fn get_horizontal_advance(&self, c: char) -> Option<u16> {
self.font.as_face_ref().glyph_hor_advance(self.font.as_face_ref().glyph_index(c)?)
}
fn get_vertical_advance(&self, c: char) -> Option<u16> {
self.font.as_face_ref().glyph_ver_advance(self.font.as_face_ref().glyph_index(c)?)
}
fn create_path(&self, glyph: owned_ttf_parser::GlyphId) -> Path {
let mut path = Path::new();
self.font.as_face_ref().outline_glyph(glyph, &mut path);
path
}
fn height(&self) -> i16 {
self.font.as_face_ref().height()
}
fn line_gap(&self) -> i16 {
self.font.as_face_ref().line_gap()
}
fn create_texture(&self, ctx: &Renderer, color: Color, path: lyon::lyon_tessellation::VertexBuffers<PathVertex, u16>, width: u16, height: u16) -> u32 {
unsafe {
let framebuffer_name = ctx.gl.create_framebuffer().unwrap();
ctx.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer_name));
let texture = ctx.gl.create_texture().unwrap();
ctx.gl.bind_texture(glow::TEXTURE_2D, Some(texture));
ctx.gl.tex_image_2d(glow::TEXTURE_2D, 0, glow::RGBA as i32, width as i32, height as i32, 0, glow::RGBA, glow::UNSIGNED_BYTE, None);
ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::LINEAR as i32);
ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::NEAREST as i32);
ctx.gl.framebuffer_texture(glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, Some(texture), 0);
ctx.gl.draw_buffers(&[glow::COLOR_ATTACHMENT0]);
ctx.gl.viewport(0, 0, width as i32, height as i32);
ctx.gl.bind_vertex_array(Some(ctx.vertex_array));
ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(ctx.vertex_buffer));
ctx.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ctx.index_buffer));
ctx.gl.enable_vertex_attrib_array(0);
ctx.gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, 0, 0);
ctx.gl.use_program(Some(ctx.program));
let indices = Renderer::fill_buffers_from_raw_geometry(&ctx.gl, path);
let loc = &ctx
.gl
.get_uniform_location(ctx.program, "transform")
.unwrap();
let mut mat = cgmath::Matrix4::identity();
let final_mat = cgmath::ortho(0., width as f32, height as f32, 0., 0., 1.) * mat * cgmath::Matrix4::from_nonuniform_scale(1., -1., 1.);
let proj: &[f32; 16] = final_mat.as_ref();
ctx.gl.uniform_matrix_4_f32_slice(Some(loc), false, proj);
let loc = &ctx.gl.get_uniform_location(ctx.program, "col").unwrap();
ctx.gl.uniform_4_f32(Some(loc), color.r, color.g, color.b, color.a);
ctx.gl
.draw_elements(glow::TRIANGLES, indices, glow::UNSIGNED_SHORT, 0);
ctx.gl.framebuffer_texture(glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, None, 0);
ctx.gl.delete_framebuffer(framebuffer_name);
ctx.gl.bind_framebuffer(glow::FRAMEBUFFER, None);
ctx.gl.bind_texture(glow::TEXTURE_2D, None);
ctx.gl.viewport(0, 0, ctx.width as i32, ctx.height as i32);
texture
}
}
fn get_texture(&mut self, renderer: &Renderer, c: char) -> u32 {
match self.texture_cache.get(&c) {
Some(texture) => *texture,
None => {
let tess = self.tessellate(c);
let texture = self.create_texture(&renderer, Color::from_f(1., 1., 1., 1.), tess, self.get_horizontal_advance(c).unwrap(), (self.height() as f32/1.15) as u16);
self.texture_cache.insert(c, texture);
println!("Created texture! {}", texture);
texture
}
}
}
fn tessellate(&mut self, c: char) -> lyon::tessellation::VertexBuffers<PathVertex, u16> {
match self.cache.get(&c) {
Some(geometry) => geometry.clone(),
None => {
let path = self.create_path_from_char(c);
use lyon::tessellation::*;
let path = path.ctx.build();
let mut geometry: lyon::tessellation::VertexBuffers<PathVertex, u16> = lyon::tessellation::VertexBuffers::new();
let mut tessellator = lyon::tessellation::FillTessellator::new();
{
tessellator.tessellate_path(
&path,
&FillOptions::default().with_tolerance(4.),
&mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
PathVertex {
position: vertex.position().to_array(),
}
}),
).unwrap();
}
self.cache.insert(c, geometry.clone());
geometry
}
}
}
}
pub struct Path {
ctx: lyon::path::path::Builder,
}
impl Path {
pub fn new () -> Self {
let builder = lyon::path::Path::builder();
Self {
ctx: builder
}
}
pub fn close (&mut self) {
self.ctx.close();
}
pub fn move_to(&mut self, x: f32, y: f32) {
self.ctx.begin(lyon::math::point(x, y));
}
pub fn line_to(&mut self, x: f32, y: f32) {
self.ctx.line_to(lyon::math::point(x, y));
}
pub fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
self.ctx.quadratic_bezier_to(lyon::math::point(x1, y1), lyon::math::point(x, y));
}
pub fn curve_to(
&mut self,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
x: f32,
y: f32
) {
self.ctx.cubic_bezier_to(lyon::math::point(x1, y1), lyon::math::point(x2, y2), lyon::math::point(x, y));
}
}
impl owned_ttf_parser::OutlineBuilder for Path {
fn close (&mut self) {
self.ctx.close();
}
fn move_to(&mut self, x: f32, y: f32) {
self.close();
self.ctx.begin(lyon::math::point(x, -y));
}
fn line_to(&mut self, x: f32, y: f32) {
self.ctx.line_to(lyon::math::point(x, -y));
}
fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
self.ctx.quadratic_bezier_to(lyon::math::point(x1, -y1), lyon::math::point(x, -y));
}
fn curve_to(
&mut self,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
x: f32,
y: f32
) {
self.ctx.cubic_bezier_to(lyon::math::point(x1, -y1), lyon::math::point(x2, -y2), lyon::math::point(x, -y));
}
}
fn compile_shader (gl: &glow::Context, vertex_shader_source: &str, fragment_shader_source: &str) -> u32 {
unsafe {
let program = gl.create_program().expect("Cannot create program");
let shader_sources = [
(glow::VERTEX_SHADER, vertex_shader_source),
(glow::FRAGMENT_SHADER, fragment_shader_source),
];
let mut shaders = Vec::with_capacity(shader_sources.len());
for (shader_type, shader_source) in shader_sources.iter() {
let shader = gl
.create_shader(*shader_type)
.expect("Cannot create shader");
gl.shader_source(shader, &format!("{}\n{}", "#version 410", shader_source));
gl.compile_shader(shader);
if !gl.get_shader_compile_status(shader) {
std::panic::panic_any(gl.get_shader_info_log(shader));
}
gl.attach_shader(program, shader);
shaders.push(shader);
}
gl.link_program(program);
if !gl.get_program_link_status(program) {
std::panic::panic_any(gl.get_program_info_log(program));
}
for shader in shaders {
gl.detach_shader(program, shader);
gl.delete_shader(shader);
}
program
}
}
#[derive(Clone)]
#[repr(C)]
pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl Color {
pub fn from_f(r: f32, g: f32, b: f32, a: f32) -> Self {
Self { r, g, b, a }
}
pub fn from_8(r: u8, g: u8, b: u8, a: u8) -> Self {
Self {
r: r as f32 / 255.,
g: g as f32 / 255.,
b: b as f32 / 255.,
a: a as f32 / 255.,
}
}
}
#[repr(C)]
pub enum GeometryStyle {
Fill,
Stroke
}
enum Command {
Triangle {
x: f32,
y: f32,
},
Circle {
x: f32,
y: f32,
r: f32,
color: Color,
transform: cgmath::Matrix4<f32>,
},
Rectangle {
x: f32,
y: f32,
width: f32,
height: f32,
angle: f32,
color: Color,
transform: cgmath::Matrix4<f32>,
},
Polygon {
x: f32,
y: f32,
r: f32,
sides: u8,
color: Color,
transform: cgmath::Matrix4<f32>,
},
Geometry {
color: Color,
style: GeometryStyle,
path: Path,
transform: cgmath::Matrix4<f32>
},
RawGeometry {
color: Color,
path: lyon::tessellation::VertexBuffers<PathVertex, u16>,
transform: cgmath::Matrix4<f32>
},
Texture {
name: u32,
x: f32,
y: f32,
width: f32,
height: f32,
angle: f32,
transform: cgmath::Matrix4<f32>
}
}
pub struct Renderer {
gl: Context, program: u32, texture_program: u32, command_stack: Vec<Command>, projection: cgmath::Matrix4<f32>,
vertex_array: u32,
vertex_buffer: u32,
index_buffer: u32,
transform: cgmath::Matrix4<f32>,
old_transform: cgmath::Matrix4<f32>,
width: u32,
height: u32
}
impl Renderer {
pub fn new<F: FnMut(&str) -> *const std::os::raw::c_void>(function_loader: F) -> Self {
unsafe {
let gl = glow::Context::from_loader_function(function_loader);
let program = compile_shader(&gl,
r#"layout(location = 0) in vec2 vertexPosition_modelspace;
//out vec2 vert;
uniform mat4 transform;
void main() {
gl_Position = transform * vec4(vertexPosition_modelspace, 0.0, 1.0);
//gl_Position.xy += pos;
gl_Position.w = 1.0;
}"#,
r#"
layout(location = 0) out vec4 color;
uniform vec4 col;
void main(){
color = col;
}"#,
);
let texture_program = compile_shader(&gl,
r#"layout(location = 0) in vec2 vertexPosition_modelspace;
out vec2 vert;
uniform mat4 transform;
void main() {
gl_Position = transform * vec4(vertexPosition_modelspace, 0.0, 1.0);
vert = vertexPosition_modelspace;
//gl_Position.xy += pos;
gl_Position.w = 1.0;
}"#,
r#"
layout(location = 0) out vec4 color;
uniform sampler2D col;
uniform vec2 imgSize;
in vec2 vert;
void main(){
color = texture(col, vec2(vert.x / imgSize.x, vert.y / imgSize.y));
}"#,
);
gl.use_program(Some(program));
gl.clear_color(0.1, 0.2, 0.3, 1.0);
let mut m_viewport: [i32; 7] = [0; 7];
gl.get_parameter_i32_slice(glow::VIEWPORT, &mut m_viewport);
let vertex_array = gl.create_vertex_array().unwrap();
gl.bind_vertex_array(Some(vertex_array));
let vertex_buffer = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer));
let index_buffer = gl.create_buffer().unwrap();
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer));
gl.enable_vertex_attrib_array(0);
gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, 0, 0);
gl.enable(glow::BLEND);
gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
Self {
gl,
program,
texture_program,
command_stack: Vec::new(), projection: cgmath::ortho(
0.,
m_viewport[2] as f32,
m_viewport[3] as f32,
0.,
0.,
1.,
),
vertex_array,
vertex_buffer,
index_buffer,
transform: cgmath::Matrix4::identity(),
old_transform: cgmath::Matrix4::identity(),
width: m_viewport[2] as u32,
height: m_viewport[3] as u32
}
}
}
pub fn flush(&mut self) {
unsafe {
self.gl.bind_vertex_array(Some(self.vertex_array));
self.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer));
self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buffer));
self.gl.enable_vertex_attrib_array(0);
self.gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, 0, 0);
self.gl.use_program(Some(self.program));
self.gl
.clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT);
for command in self.command_stack.drain(0..) {
match command {
Command::Triangle { x, y } => {
let vertex_array = self.gl.create_vertex_array().unwrap();
self.gl.bind_vertex_array(Some(vertex_array));
let vertex_buffer_data = [-50., -50., 0., 50., -50., 0., 0., 50., 0.];
let vertex_buffer = self.gl.create_buffer().unwrap();
self.gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer));
self.gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
&std::mem::transmute::<[f32; 9], [u8; 36]>(vertex_buffer_data),
glow::DYNAMIC_DRAW,
);
self.gl.enable_vertex_attrib_array(0);
self.gl
.vertex_attrib_pointer_f32(0, 3, glow::FLOAT, false, 0, 0);
let loc = &self
.gl
.get_uniform_location(self.program, "transform")
.unwrap();
let mut mat = cgmath::Matrix4::zero();
mat.w.x = x;
mat.w.y = y;
let final_mat = mat + self.projection;
let proj: &[f32; 16] = final_mat.as_ref();
self.gl.uniform_matrix_4_f32_slice(Some(loc), false, proj);
self.gl.draw_arrays(glow::TRIANGLES, 0, 3);
self.gl.delete_vertex_array(vertex_array);
self.gl.delete_buffer(vertex_buffer);
}
Command::Circle {
x,
y,
r,
color,
transform,
} => {
self.gl.use_program(Some(self.program));
let max = PI * 2.;
let mut vertices = Vec::with_capacity(max as usize + 1);
let mut i = 0.;
let points_calculation = r * 0.64;
let points = if points_calculation > 32. { points_calculation } else { 32. };
while i < max {
vertices.push(i.cos() * r);
vertices.push(i.sin() * r);
i += max / points;
}
vertices.push(i.cos() * r);
vertices.push(i.sin() * r);
let mut vertex_buffer_data = Vec::<u8>::with_capacity(vertices.len() * 4);
for float in vertices.iter() {
vertex_buffer_data.extend_from_slice(&float.to_le_bytes());
}
self.gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
vertex_buffer_data.as_ref(),
glow::DYNAMIC_DRAW,
);
let loc = &self
.gl
.get_uniform_location(self.program, "transform")
.unwrap();
let mut mat = cgmath::Matrix4::identity();
mat.w.x = x;
mat.w.y = y;
let final_mat = self.projection * transform * mat;
let proj: &[f32; 16] = final_mat.as_ref();
self.gl.uniform_matrix_4_f32_slice(Some(loc), false, proj);
let loc = &self.gl.get_uniform_location(self.program, "col").unwrap();
self.gl.uniform_4_f32(Some(loc), color.r, color.g, color.b, color.a);
self.gl
.draw_arrays(glow::TRIANGLE_FAN, 0, vertices.len() as i32);
}
Command::Polygon {
x,
y,
r,
color,
sides,
transform,
} => {
self.gl.use_program(Some(self.program));
let max = PI * 2.;
let mut vertices = Vec::with_capacity(max as usize + 1);
let mut i = 0.;
let points = sides as f32;
while i < max {
vertices.push(i.cos() * r);
vertices.push(i.sin() * r);
i += max / points;
}
vertices.push(i.cos() * r);
vertices.push(i.sin() * r);
let mut vertex_buffer_data = Vec::<u8>::with_capacity(vertices.len() * 4);
for float in vertices.iter() {
vertex_buffer_data.extend_from_slice(&float.to_le_bytes());
}
self.gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
vertex_buffer_data.as_ref(),
glow::DYNAMIC_DRAW,
);
let loc = &self
.gl
.get_uniform_location(self.program, "transform")
.unwrap();
let mut mat = cgmath::Matrix4::identity();
mat.w.x = x;
mat.w.y = y;
let final_mat = self.projection * transform * mat;
let proj: &[f32; 16] = final_mat.as_ref();
self.gl.uniform_matrix_4_f32_slice(Some(loc), false, proj);
let loc = &self.gl.get_uniform_location(self.program, "col").unwrap();
self.gl.uniform_4_f32(Some(loc), color.r, color.g, color.b, color.a);
self.gl
.draw_arrays(glow::TRIANGLE_FAN, 0, vertices.len() as i32);
}
Command::Rectangle {
x,
y,
width,
height,
color,
angle,
transform,
} => {
self.gl.use_program(Some(self.program));
let vertex_buffer_data = [
0., 0., width, height, 0., height, width, 0., width, height, 0., 0.,
];
self.gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
&std::mem::transmute::<[f32; 12], [u8; 48]>(vertex_buffer_data),
glow::DYNAMIC_DRAW,
);
let loc = &self
.gl
.get_uniform_location(self.program, "transform")
.unwrap();
let mut mat = cgmath::Matrix4::identity();
mat.w.x = x;
mat.w.y = y;
mat.x.x = angle.cos();
mat.x.y = -angle.sin();
mat.y.x = angle.sin();
mat.y.y = angle.cos();
let final_mat = self.projection * transform * mat;
let proj: &[f32; 16] = final_mat.as_ref();
self.gl.uniform_matrix_4_f32_slice(Some(loc), false, proj);
let loc = &self.gl.get_uniform_location(self.program, "col").unwrap();
self.gl.uniform_4_f32(Some(loc), color.r, color.g, color.b, color.a);
self.gl
.draw_arrays(glow::TRIANGLES, 0, vertex_buffer_data.len() as i32);
},
Command::Texture {
x,
y,
width,
height,
name,
angle,
transform,
} => {
self.gl.use_program(Some(self.texture_program));
self.gl.bind_texture(glow::TEXTURE_2D, Some(name));
let vertex_buffer_data = [
0., 0., width, height, 0., height, width, 0., width, height, 0., 0.,
];
self.gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
&std::mem::transmute::<[f32; 12], [u8; 48]>(vertex_buffer_data),
glow::DYNAMIC_DRAW,
);
let loc = &self
.gl
.get_uniform_location(self.texture_program, "transform")
.unwrap();
let mut mat = cgmath::Matrix4::identity();
mat.w.x = x;
mat.w.y = y;
mat.x.x = angle.cos();
mat.x.y = -angle.sin();
mat.y.x = angle.sin();
mat.y.y = angle.cos();
let final_mat = self.projection * transform * mat;
let proj: &[f32; 16] = final_mat.as_ref();
self.gl.uniform_matrix_4_f32_slice(Some(loc), false, proj);
let loc = &self.gl.get_uniform_location(self.texture_program, "col").unwrap();
self.gl.uniform_1_u32(Some(loc), name);
let loc = &self.gl.get_uniform_location(self.texture_program, "imgSize").unwrap();
self.gl.uniform_2_f32(Some(loc), width, height);
self.gl
.draw_arrays(glow::TRIANGLES, 0, vertex_buffer_data.len() as i32);
},
Command::Geometry {
color,
transform,
path,
style
} => {
self.gl.use_program(Some(self.program));
use lyon::tessellation::*;
let path = path.ctx.build();
let mut geometry: lyon::tessellation::VertexBuffers<PathVertex, u16> = lyon::tessellation::VertexBuffers::new();
let mut tessellator = lyon::tessellation::FillTessellator::new();
{
tessellator.tessellate_path(
&path,
&FillOptions::default(),
&mut BuffersBuilder::new(&mut geometry, |vertex: FillVertex| {
PathVertex {
position: vertex.position().to_array(),
}
}),
).unwrap();
}
let indices = Self::fill_buffers_from_raw_geometry(&self.gl, geometry);
let loc = &self
.gl
.get_uniform_location(self.program, "transform")
.unwrap();
let mut mat = cgmath::Matrix4::identity();
let final_mat = self.projection * transform * mat; let proj: &[f32; 16] = final_mat.as_ref();
self.gl.uniform_matrix_4_f32_slice(Some(loc), false, proj);
let loc = &self.gl.get_uniform_location(self.program, "col").unwrap();
self.gl.uniform_4_f32(Some(loc), color.r, color.g, color.b, color.a);
self.gl
.draw_elements(glow::TRIANGLES, indices, glow::UNSIGNED_SHORT, 0);
},
Command::RawGeometry {
color,
transform,
path
} => {
self.gl.use_program(Some(self.program));
let indices = Self::fill_buffers_from_raw_geometry(&self.gl, path);
let loc = &self
.gl
.get_uniform_location(self.program, "transform")
.unwrap();
let mut mat = cgmath::Matrix4::identity();
let final_mat = self.projection * transform * mat; let proj: &[f32; 16] = final_mat.as_ref();
self.gl.uniform_matrix_4_f32_slice(Some(loc), false, proj);
let loc = &self.gl.get_uniform_location(self.program, "col").unwrap();
self.gl.uniform_4_f32(Some(loc), color.r, color.g, color.b, color.a);
self.gl
.draw_elements(glow::TRIANGLES, indices, glow::UNSIGNED_SHORT, 0);
}
}
}
self.transform = cgmath::Matrix4::identity();
}
}
fn fill_buffers_from_raw_geometry(gl: &glow::Context, geometry: lyon::tessellation::VertexBuffers<PathVertex, u16>) -> i32 {
unsafe {
let mut vertex_buffer_data = vec![];
for vertex in geometry.vertices {
vertex_buffer_data.push(vertex.position[0]);
vertex_buffer_data.push(vertex.position[1]);
}
let mut vertices = Vec::<u8>::with_capacity(vertex_buffer_data.len() * 4);
for float in vertex_buffer_data.iter() {
vertices.extend_from_slice(&float.to_le_bytes());
}
gl.buffer_data_u8_slice(
glow::ARRAY_BUFFER,
vertices.as_ref(),
glow::DYNAMIC_DRAW,
);
let mut indices = Vec::<u8>::with_capacity(geometry.indices.len() * 2);
for float in geometry.indices.iter() {
indices.extend_from_slice(&float.to_le_bytes());
}
gl.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER, indices.as_ref(), glow::DYNAMIC_DRAW);
geometry.indices.len() as i32
}
}
pub fn triangle(&mut self, x: f32, y: f32) {
self.command_stack.push(Command::Triangle { x, y })
}
pub fn texture(&mut self, x: f32, y: f32, width: f32, height: f32, angle: f32, name: u32) {
self.command_stack.push(Command::Texture {
x,
y,
width,
height,
angle,
name,
transform: self.transform.clone(),
})
}
pub fn rect(&mut self, x: f32, y: f32, width: f32, height: f32, angle: f32, color: Color) {
self.command_stack.push(Command::Rectangle {
x,
y,
width,
height,
color,
angle,
transform: self.transform.clone(),
})
}
pub fn text(&mut self, text: &str, color: Color, font: &mut TextRenderer, x: f32, y: f32, scale: f32, line_break_length: f32) {
let old_transform = self.transform.clone();
self.translate(x, y);
let scale_factor = scale / font.units_per_em as f32;
self.scale(scale_factor, scale_factor);
self.translate(0., font.height() as f32/1.5);
let mut offset = 0.;
for glyph in text.chars() {
if offset * scale_factor >= line_break_length {
self.translate(-offset, font.height() as f32/1.15);
offset = 0.;
}
if glyph == '\n' {
self.translate(-offset, font.height() as f32/1.15);
offset = 0.;
continue;
}
let tex = font.get_texture(&self, glyph);
self.texture(0., 0., font.get_horizontal_advance(glyph).unwrap() as f32, font.height() as f32/1.15, 0., tex);
let advance = font.get_horizontal_advance(glyph).unwrap() as f32;
offset += advance;
self.translate(advance, 0.);
}
self.transform = old_transform;
}
pub fn circle(&mut self, x: f32, y: f32, r: f32, color: Color) {
self.command_stack.push(Command::Circle {
x,
y,
r,
color,
transform: self.transform.clone(),
})
}
pub fn polygon(&mut self, x: f32, y: f32, r: f32, sides: u8, color: Color) {
self.command_stack.push(Command::Polygon {
x,
y,
r,
color,
sides,
transform: self.transform.clone(),
})
}
pub fn path(&mut self, path: Path, style: GeometryStyle, color: Color) {
self.command_stack.push(Command::Geometry {
color,
path,
transform: self.transform.clone(),
style
})
}
fn raw_geometry(&mut self, path: lyon::tessellation::VertexBuffers<PathVertex, u16>, color: Color) {
self.command_stack.push(Command::RawGeometry {
color,
path,
transform: self.transform.clone(),
})
}
pub fn resize(&mut self, width: i32, height: i32) {
unsafe {
self.width = width as u32;
self.height = height as u32;
self.projection = cgmath::ortho(0., width as f32, height as f32, 0., 0., 1.);
self.gl.viewport(0, 0, width, height); }
}
pub fn clean(&self) {
unsafe {
self.gl.delete_program(self.program); self.gl.delete_vertex_array(self.vertex_array); self.gl.delete_buffer(self.vertex_buffer);
self.gl.delete_buffer(self.index_buffer);
}
}
pub fn set_clear_color(&self, color: Color) {
unsafe {
self.gl.clear_color(color.r, color.g, color.b, color.a);
}
}
pub fn scale(&mut self, x: f32, y: f32) {
self.transform = self.transform * cgmath::Matrix4::from_nonuniform_scale(x, y, 1.);
}
pub fn rotate(&mut self, x: f32) {
self.transform = self.transform * cgmath::Matrix4::from_angle_z(cgmath::Rad(x));
}
pub fn translate(&mut self, x: f32, y: f32) {
self.transform = self.transform * cgmath::Matrix4::from_translation(cgmath::vec3(x, y, 0.));
}
pub fn save(&mut self) {
self.old_transform = self.transform;
}
pub fn restore(&mut self) {
self.transform = self.old_transform;
}
pub fn reset(&mut self) {
self.transform = cgmath::Matrix4::identity();
}
}