Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- use luminance::context::GraphicsContext;
- use luminance::pixel::NormR8UI;
- use luminance::shader::program::Program;
- use luminance::tess::{Mode, TessBuilder};
- use luminance::texture::{Dim2, Flat, Texture};
- use luminance_glfw::{
- Action, GlfwSurface, Key, MouseButton, Surface as _, WindowDim, WindowEvent, WindowOpt,
- };
- fn main() {
- // Window
- let mut surface = GlfwSurface::new(
- WindowDim::Windowed(500, 500),
- "KleiPlane Luminance",
- WindowOpt::default(),
- )
- .expect("GLFW: Surface creation");
- let mut render_set = RenderSet::new(&mut surface);
- let pre_text = include_str!("../text_example.txt").into();
- let (text_program, _): (Program<TextVertexSemantics, (), TextShaderInterface>, _) =
- Program::from_strings(None, TEXT_VS_STR, None, TEXT_FS_STR).unwrap();
- use glyph_brush::{BrushAction, BrushError, GlyphBrushBuilder, Section};
- let inconsolata: &[u8] = include_bytes!("../inconsolata.ttf");
- let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(inconsolata).build();
- let mut loop_helper = spin_sleep::LoopHelper::builder()
- .report_interval_s(1.)
- .build_with_target_rate(60.);
- let mut text_tess = TessBuilder::new(&mut surface).set_vertex_nb(1).build().unwrap();
- let mut back_buffer = surface.back_buffer().unwrap();
- let mut resized = false;
- let (_width, _height) = glyph_brush.texture_dimensions();
- let mut text_texture: Texture<Flat, Dim2, NormR8UI> = get_empty_texture(_width, _height, &mut surface);
- let mut keep_vertices = Vec::new();
- 'app: loop {
- // achieve 60 fps
- let _delta = loop_helper.loop_start();
- // handle events
- for event in surface.poll_events() {
- match event {
- WindowEvent::Close | WindowEvent::Key(Key::Escape, _, Action::Release, _) => {
- break 'app;
- }
- WindowEvent::FramebufferSize(_width, _height) => {
- render_set.projection = Transform3D::<f32, f32, f32>::ortho(
- 0.,
- _width as f32,
- _height as f32,
- 0.,
- -1.,
- 1.,
- )
- .to_row_arrays();
- resized = true;
- }
- _ => (),
- };
- }
- // when resized, reset backbuffer to new size
- if resized {
- back_buffer = surface.back_buffer().unwrap();
- resized = false;
- };
- //
- glyph_brush.queue(Section {
- text: pre_text,
- scale: glyph_brush::rusttype::Scale::uniform(18.),
- screen_position: (100.0, 100.0),
- bounds: (1000., 1000.),
- ..Section::default()
- });
- // create texture & verticess for text drawing
- let mut brush_action;
- loop {
- brush_action = glyph_brush.process_queued(
- |rect, tex_data| {
- upload_texture_bytes(
- &mut text_texture,
- rect.min.x as u32,
- rect.min.y as u32,
- rect.width() as u32,
- rect.height() as u32,
- tex_data,
- );
- },
- to_vertex,
- );
- match brush_action {
- Ok(_) => break,
- Err(BrushError::TextureTooSmall { suggested, .. }) => {
- eprint!("\r \r");
- eprintln!("Resizing glyph texture");
- },
- }
- }
- // create vertices
- match brush_action {
- Ok(BrushAction::Draw(vertices)) => {
- println!("generated, {}", vertices.len());
- if !vertices.is_empty() {
- keep_vertices = vertices;
- }
- },
- _ => (),
- }
- if !keep_vertices.is_empty() {
- text_tess = TessBuilder::new(&mut surface)
- .set_mode(Mode::TriangleStrip)
- .set_vertex_nb(4)
- .add_instances(&keep_vertices)
- .build()
- .unwrap();
- }
- // rendering code goes here
- surface.pipeline_builder().pipeline(
- &back_buffer,
- [1.0, 1.0, 1.0, 1.0],
- |pipeline, mut shd_gate| {
- if !keep_vertices.is_empty() {
- let text_bound_texture = pipeline.bind_texture(&text_texture);
- shd_gate.shade(&text_program, |shader, mut render_gate| {
- shader.transform.update(render_set.projection);
- shader.font_tex.update(&text_bound_texture);
- render_gate.render(render_set.render_state, |mut tess_gate| {
- tess_gate.render(&text_tess);
- });
- });
- }
- },
- );
- surface.swap_buffers();
- if let Some(rate) = loop_helper.report_rate() {
- println!("{:.0} FPS", rate);
- }
- loop_helper.loop_sleep();
- }
- }
- use crate::engine::V2;
- struct Rect {
- pub min: V2,
- pub max: V2,
- }
- impl Rect {
- pub fn width(&self) -> f32 {
- self.max.x - self.min.x
- }
- pub fn height(&self) -> f32 {
- self.max.y - self.min.y
- }
- }
- use crate::engine::common::*;
- use euclid::Transform3D;
- #[inline]
- fn to_vertex(
- glyph_brush::GlyphVertex {
- mut tex_coords,
- pixel_coords,
- bounds,
- color,
- z,
- }: glyph_brush::GlyphVertex,
- ) -> TextVertex {
- let gl_bounds = bounds;
- let mut gl_rect = Rect {
- min: V2::new(pixel_coords.min.x as f32, pixel_coords.min.y as f32),
- max: V2::new(pixel_coords.max.x as f32, pixel_coords.max.y as f32),
- };
- // handle overlapping bounds, modify uv_rect to preserve texture aspect
- if gl_rect.max.x > gl_bounds.max.x {
- let old_width = gl_rect.width();
- gl_rect.max.x = gl_bounds.max.x;
- tex_coords.max.x = tex_coords.min.x + tex_coords.width() * gl_rect.width() / old_width;
- }
- if gl_rect.min.x < gl_bounds.min.x {
- let old_width = gl_rect.width();
- gl_rect.min.x = gl_bounds.min.x;
- tex_coords.min.x = tex_coords.max.x - tex_coords.width() * gl_rect.width() / old_width;
- }
- if gl_rect.max.y > gl_bounds.max.y {
- let old_height = gl_rect.height();
- gl_rect.max.y = gl_bounds.max.y;
- tex_coords.max.y = tex_coords.min.y + tex_coords.height() * gl_rect.height() / old_height;
- }
- if gl_rect.min.y < gl_bounds.min.y {
- let old_height = gl_rect.height();
- gl_rect.min.y = gl_bounds.min.y;
- tex_coords.min.y = tex_coords.max.y - tex_coords.height() * gl_rect.height() / old_height;
- }
- TextVertex {
- left_top: VertexLeftTop::new([gl_rect.min.x, gl_rect.max.y, z]),
- right_bottom: VertexRightBottom::new([gl_rect.max.x, gl_rect.min.y]),
- tex_left_top: VertexTexLeftTop::new([tex_coords.min.x, tex_coords.max.y]),
- tex_right_bottom: VertexTexRightBottom::new([tex_coords.max.x, tex_coords.min.y]),
- color: VertexColor::new(color),
- }
- }
- /* common.rs */
- use luminance::{
- linear::M44,
- pipeline::BoundTexture,
- pixel::{NormRGBA8UI, NormUnsigned},
- shader::program::Uniform,
- texture::{Dim2, Flat, GenMipmaps, Sampler, Texture},
- };
- use luminance_derive::{Semantics, UniformInterface, Vertex};
- use luminance_glfw::GlfwSurface;
- use rgb::*;
- use euclid::Vector2D;
- use luminance::pixel::NormR8UI;
- // love this
- pub type V2 = Vector2D<f32, f32>;
- // standard shaders
- pub const TEXTURE_VS_STR: &str = include_str!("../../shaders/texture.vert");
- pub const TEXTURE_FS_STR: &str = include_str!("../../shaders/texture.frag");
- pub const SPRITESHEET_VS_STR: &str = include_str!("../../shaders/spritesheet.vert");
- pub const SPRITESHEET_FS_STR: &str = include_str!("../../shaders/spritesheet.frag");
- pub const TEXT_VS_STR: &str = include_str!("../../shaders/text.vert");
- pub const TEXT_FS_STR: &str = include_str!("../../shaders/text.frag");
- #[derive(Copy, Clone, Debug, Semantics)]
- pub enum VertexSemantics {
- #[sem(name = "position", repr = "[f32; 2]", wrapper = "VertexPosition")]
- Position,
- #[sem(
- name = "tex_coords",
- repr = "[f32; 2]",
- wrapper = "VertexTexCoordinates"
- )]
- TexCoordinates,
- }
- #[derive(Vertex)]
- #[vertex(sem = "VertexSemantics")]
- pub struct Vertex {
- position: VertexPosition,
- tex_coords: VertexTexCoordinates,
- }
- #[derive(Copy, Clone, Debug, Semantics)]
- pub enum TextVertexSemantics {
- #[sem(name = "left_top", repr = "[f32; 3]", wrapper = "VertexLeftTop")]
- LeftTop,
- #[sem(
- name = "right_bottom",
- repr = "[f32; 2]",
- wrapper = "VertexRightBottom"
- )]
- RightBottom,
- #[sem(name = "tex_left_top", repr = "[f32; 2]", wrapper = "VertexTexLeftTop")]
- TexLeftBottom,
- #[sem(
- name = "tex_right_bottom",
- repr = "[f32; 2]",
- wrapper = "VertexTexRightBottom"
- )]
- TexRightBottom,
- #[sem(name = "color", repr = "[f32; 4]", wrapper = "VertexColor")]
- Color,
- }
- #[derive(Clone, Debug, Vertex)]
- #[vertex(sem = "TextVertexSemantics", instanced = "true")]
- pub struct TextVertex {
- pub left_top: VertexLeftTop,
- pub right_bottom: VertexRightBottom,
- pub tex_left_top: VertexTexLeftTop,
- pub tex_right_bottom: VertexTexRightBottom,
- pub color: VertexColor,
- }
- pub const QUAD_VERTICES: [Vertex; 4] = [
- Vertex {
- position: VertexPosition::new([-0.5, -0.5]),
- tex_coords: VertexTexCoordinates::new([0., 0.]),
- },
- Vertex {
- position: VertexPosition::new([-0.5, 0.5]),
- tex_coords: VertexTexCoordinates::new([0., 1.]),
- },
- Vertex {
- position: VertexPosition::new([0.5, 0.5]),
- tex_coords: VertexTexCoordinates::new([1., 1.]),
- },
- Vertex {
- position: VertexPosition::new([0.5, -0.5]),
- tex_coords: VertexTexCoordinates::new([1., 0.]),
- },
- ];
- #[derive(UniformInterface)]
- pub struct ShaderInterface {
- #[uniform(name = "projection")]
- pub projection: Uniform<M44>,
- #[uniform(name = "model")]
- pub model: Uniform<M44>,
- #[uniform(name = "image")]
- pub image: Uniform<&'static BoundTexture<'static, Flat, Dim2, NormUnsigned>>,
- #[uniform(name = "color")]
- pub color: Uniform<[f32; 4]>,
- }
- #[derive(UniformInterface)]
- pub struct SpritesheetShaderInterface {
- #[uniform(name = "projection")]
- pub projection: Uniform<M44>,
- #[uniform(name = "model")]
- pub model: Uniform<M44>,
- #[uniform(name = "image")]
- pub image: Uniform<&'static BoundTexture<'static, Flat, Dim2, NormUnsigned>>,
- #[uniform(name = "frame")]
- pub frame: Uniform<f32>,
- #[uniform(name = "frame_amount")]
- pub frame_amount: Uniform<f32>,
- }
- #[derive(UniformInterface)]
- pub struct TextShaderInterface {
- #[uniform(name = "transform")]
- pub transform: Uniform<M44>,
- #[uniform(name = "font_tex")]
- pub font_tex: Uniform<&'static BoundTexture<'static, Flat, Dim2, NormUnsigned>>,
- }
- // specific sampler for pixel art defined
- pub const TEXTURE_SAMPLER: Sampler = Sampler {
- wrap_r: luminance::texture::Wrap::ClampToEdge,
- wrap_s: luminance::texture::Wrap::ClampToEdge,
- wrap_t: luminance::texture::Wrap::ClampToEdge,
- min_filter: luminance::texture::MinFilter::Nearest,
- mag_filter: luminance::texture::MagFilter::Nearest,
- depth_comparison: None,
- };
- // loads a luminance texture from a png file
- pub fn load_texture(
- surface: &mut GlfwSurface,
- path: &'static str,
- ) -> Texture<Flat, Dim2, NormRGBA8UI> {
- let img = lodepng::decode32_file(path).unwrap();
- let bytes: &[u8] = img.buffer.as_bytes();
- let texture = Texture::new(
- surface,
- [img.width as u32, img.height as u32],
- 0,
- &TEXTURE_SAMPLER,
- )
- .expect("luminance texture creation");
- // no mipmaps
- texture.upload_raw(GenMipmaps::No, &bytes).unwrap();
- texture
- }
- pub const TEXT_SAMPLER: Sampler = Sampler {
- wrap_r: luminance::texture::Wrap::ClampToEdge,
- wrap_s: luminance::texture::Wrap::ClampToEdge,
- wrap_t: luminance::texture::Wrap::ClampToEdge,
- min_filter: luminance::texture::MinFilter::Linear,
- mag_filter: luminance::texture::MagFilter::Linear,
- depth_comparison: None,
- };
- pub fn get_empty_texture(
- width: u32,
- height: u32,
- surface: &mut GlfwSurface,
- ) -> Texture<Flat, Dim2, NormR8UI> {
- let texture = Texture::new(surface, [width, height], 0, &TEXT_SAMPLER)
- .expect("luminance texture creation");
- texture
- }
- pub fn upload_texture_bytes(
- texture: &mut Texture<Flat, Dim2, NormR8UI>,
- x_offset: u32,
- y_offset: u32,
- width: u32,
- height: u32,
- bytes: &[u8],
- ) {
- texture.upload_part_raw(
- GenMipmaps::No,
- [x_offset, y_offset],
- [width, height],
- &bytes
- ).unwrap();
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement