mirror of
https://github.com/lisk77/comet.git
synced 2025-10-23 21:38:50 +00:00
feat: text can now be rendered, though only in the setup right now (will make a new render pass for that)
This commit is contained in:
parent
0507703284
commit
b2578f7673
3 changed files with 215 additions and 117 deletions
|
|
@ -36,6 +36,7 @@ pub struct Renderer2D<'a> {
|
||||||
num_indices: u32,
|
num_indices: u32,
|
||||||
clear_color: Color,
|
clear_color: Color,
|
||||||
diffuse_texture: Texture,
|
diffuse_texture: Texture,
|
||||||
|
diffuse_bind_group_layout: wgpu::BindGroupLayout,
|
||||||
diffuse_bind_group: wgpu::BindGroup,
|
diffuse_bind_group: wgpu::BindGroup,
|
||||||
graphic_resource_manager: GraphicResourceManager,
|
graphic_resource_manager: GraphicResourceManager,
|
||||||
camera: RenderCamera,
|
camera: RenderCamera,
|
||||||
|
|
@ -286,6 +287,7 @@ impl<'a> Renderer2D<'a> {
|
||||||
num_indices,
|
num_indices,
|
||||||
clear_color,
|
clear_color,
|
||||||
diffuse_texture,
|
diffuse_texture,
|
||||||
|
diffuse_bind_group_layout: texture_bind_group_layout,
|
||||||
diffuse_bind_group,
|
diffuse_bind_group,
|
||||||
graphic_resource_manager,
|
graphic_resource_manager,
|
||||||
camera,
|
camera,
|
||||||
|
|
@ -614,34 +616,10 @@ impl<'a> Renderer2D<'a> {
|
||||||
self.diffuse_bind_group = diffuse_bind_group;
|
self.diffuse_bind_group = diffuse_bind_group;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_texture_atlas(&mut self) {
|
fn switch_texture(&mut self, to: Texture) {
|
||||||
self.diffuse_texture = Texture::from_image(&self.device, &self.queue, self.graphic_resource_manager.texture_atlas().atlas(), None, false).unwrap();
|
self.diffuse_texture = to;
|
||||||
|
|
||||||
let texture_bind_group_layout =
|
|
||||||
self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
multisampled: false,
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
label: Some("texture_bind_group_layout"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let diffuse_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
let diffuse_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
layout: &texture_bind_group_layout,
|
layout: &self.diffuse_bind_group_layout,
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupEntry {
|
wgpu::BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
|
|
@ -658,49 +636,13 @@ impl<'a> Renderer2D<'a> {
|
||||||
self.diffuse_bind_group = diffuse_bind_group;
|
self.diffuse_bind_group = diffuse_bind_group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_texture_atlas(&mut self) {
|
||||||
|
self.switch_texture(Texture::from_image(&self.device, &self.queue, self.graphic_resource_manager.texture_atlas().atlas(), None, false).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
fn set_font_atlas(&mut self, font: String) {
|
fn set_font_atlas(&mut self, font: String) {
|
||||||
let font_atlas = self.graphic_resource_manager.fonts().iter().find(|f| f.name() == font).unwrap();
|
let font_atlas = self.graphic_resource_manager.fonts().iter().find(|f| f.name() == font).unwrap();
|
||||||
self.diffuse_texture = Texture::from_image(&self.device, &self.queue, font_atlas.glyphs().atlas(), None, false).unwrap();
|
self.switch_texture(Texture::from_image(&self.device, &self.queue, font_atlas.glyphs().atlas(), None, false).unwrap());
|
||||||
|
|
||||||
let texture_bind_group_layout =
|
|
||||||
self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
multisampled: false,
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
label: Some("texture_bind_group_layout"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let diffuse_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
layout: &texture_bind_group_layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&self.diffuse_texture.view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::Sampler(&self.diffuse_texture.sampler),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
label: Some("diffuse_bind_group"),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.diffuse_bind_group = diffuse_bind_group;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_project_root() -> std::io::Result<PathBuf> {
|
fn get_project_root() -> std::io::Result<PathBuf> {
|
||||||
|
|
@ -821,10 +763,10 @@ impl<'a> Renderer2D<'a> {
|
||||||
((dim_x as f32/ self.config.width as f32) * 0.5, (dim_y as f32/ self.config.height as f32) * 0.5);
|
((dim_x as f32/ self.config.width as f32) * 0.5, (dim_y as f32/ self.config.height as f32) * 0.5);
|
||||||
|
|
||||||
let vertices: &mut Vec<Vertex> = &mut vec![
|
let vertices: &mut Vec<Vertex> = &mut vec![
|
||||||
Vertex :: new ( [-bound_x + position.x(), bound_y + position.y(), 0.0 + position.z()], [region.x0(), region.y0()], [0.0, 0.0, 0.0, 0.0] ),
|
Vertex :: new ( [-bound_x + position.x(), bound_y + position.y(), 0.0 + position.z()], [region.u0(), region.v0()], [0.0, 0.0, 0.0, 0.0] ),
|
||||||
Vertex :: new ( [-bound_x + position.x(), -bound_y + position.y(), 0.0 + position.z()], [region.x0(), region.y1()], [0.0, 0.0, 0.0, 0.0] ),
|
Vertex :: new ( [-bound_x + position.x(), -bound_y + position.y(), 0.0 + position.z()], [region.u0(), region.v1()], [0.0, 0.0, 0.0, 0.0] ),
|
||||||
Vertex :: new ( [ bound_x + position.x(), -bound_y + position.y(), 0.0 + position.z()], [region.x1(), region.y1()], [0.0, 0.0, 0.0, 0.0] ) ,
|
Vertex :: new ( [ bound_x + position.x(), -bound_y + position.y(), 0.0 + position.z()], [region.u1(), region.v1()], [0.0, 0.0, 0.0, 0.0] ) ,
|
||||||
Vertex :: new ( [ bound_x + position.x(), bound_y + position.y(), 0.0 + position.z()], [region.x1(), region.y0()], [0.0, 0.0, 0.0, 0.0] )
|
Vertex :: new ( [ bound_x + position.x(), bound_y + position.y(), 0.0 + position.z()], [region.u1(), region.v0()], [0.0, 0.0, 0.0, 0.0] )
|
||||||
];
|
];
|
||||||
|
|
||||||
let buffer_size = self.vertex_data.len() as u16;
|
let buffer_size = self.vertex_data.len() as u16;
|
||||||
|
|
@ -838,17 +780,56 @@ impl<'a> Renderer2D<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A function to draw text at a given position.
|
/// A function to draw text at a given position.
|
||||||
pub fn draw_text_at(&mut self, text: &str, font: String, position: Point3) {
|
pub fn draw_text_at(&mut self, text: &str, font: String, size: f32, position: Point3) {
|
||||||
self.set_font_atlas(font.clone());
|
self.set_font_atlas(font.clone());
|
||||||
|
|
||||||
let mut x = position.x();
|
let screen_position = Point3::new(position.x()/self.config.width as f32, position.y()/self.config.height as f32, position.z());
|
||||||
let mut y = position.y();
|
let scale_factor = size / self.graphic_resource_manager.fonts().iter().find(|f| f.name() == font).unwrap().size();
|
||||||
|
|
||||||
for c in text.chars() {
|
let line_height = (self.graphic_resource_manager.fonts().iter().find(|f| f.name() == font).unwrap().line_height() / self.config.height as f32) * scale_factor;
|
||||||
self.draw_texture_at(font.clone() + &c.to_string(), Point3::new(x, y, position.z()));
|
let lines = text.split("\n").collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
let mut x_offset = 0.0;
|
||||||
|
let mut y_offset = 0.0;
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
for c in line.chars() {
|
||||||
|
let region = self.get_glyph_region(c, font.clone());
|
||||||
|
let (dim_x, dim_y) = region.dimensions();
|
||||||
|
|
||||||
|
let w = (dim_x as f32 / self.config.width as f32) * scale_factor;
|
||||||
|
let h = (dim_y as f32 / self.config.height as f32) * scale_factor;
|
||||||
|
|
||||||
|
let offset_x_px = (region.offset_x() / self.config.width as f32) * scale_factor;
|
||||||
|
let offset_y_px = (region.offset_y() / self.config.height as f32) * scale_factor;
|
||||||
|
|
||||||
|
let glyph_left = screen_position.x() + x_offset + offset_x_px;
|
||||||
|
let glyph_top = screen_position.y() - offset_y_px - y_offset;
|
||||||
|
let glyph_right = glyph_left + w;
|
||||||
|
let glyph_bottom = glyph_top - h;
|
||||||
|
|
||||||
|
let vertices: &mut Vec<Vertex> = &mut vec![
|
||||||
|
Vertex::new([ glyph_left, glyph_top, screen_position.z() ], [region.u0(), region.v0()], [0.0; 4]),
|
||||||
|
Vertex::new([ glyph_left, glyph_bottom, screen_position.z() ], [region.u0(), region.v1()], [0.0; 4]),
|
||||||
|
Vertex::new([ glyph_right, glyph_bottom, screen_position.z() ], [region.u1(), region.v1()], [0.0; 4]),
|
||||||
|
Vertex::new([ glyph_right, glyph_top, screen_position.z() ], [region.u1(), region.v0()], [0.0; 4]),
|
||||||
|
];
|
||||||
|
|
||||||
|
let buffer_size = self.vertex_data.len() as u16;
|
||||||
|
let indices: &mut Vec<u16> = &mut vec![
|
||||||
|
buffer_size, buffer_size + 1, buffer_size + 3,
|
||||||
|
buffer_size + 1, buffer_size + 2, buffer_size + 3,
|
||||||
|
];
|
||||||
|
|
||||||
|
x_offset += (region.advance() / self.config.width as f32) * scale_factor;
|
||||||
|
|
||||||
|
self.push_to_buffers(vertices, indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
y_offset += line_height;
|
||||||
|
x_offset = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_texture_atlas();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_priority_camera(&self, cameras: Vec<Camera2D>) -> usize {
|
fn find_priority_camera(&self, cameras: Vec<Camera2D>) -> usize {
|
||||||
|
|
@ -953,10 +934,10 @@ impl<'a> Renderer2D<'a> {
|
||||||
let buffer_size = vertex_buffer.len() as u16;
|
let buffer_size = vertex_buffer.len() as u16;
|
||||||
|
|
||||||
vertex_buffer.append(&mut vec![
|
vertex_buffer.append(&mut vec![
|
||||||
Vertex :: new ( [-bound_x + position.x(), bound_y + position.y(), 0.0], [region.x0(), region.y0()], [0.0, 0.0, 0.0, 0.0] ),
|
Vertex :: new ( [-bound_x + position.x(), bound_y + position.y(), 0.0], [region.u0(), region.v0()], [0.0, 0.0, 0.0, 0.0] ),
|
||||||
Vertex :: new ( [-bound_x + position.x(), -bound_y + position.y(), 0.0], [region.x0(), region.y1()], [0.0, 0.0, 0.0, 0.0] ),
|
Vertex :: new ( [-bound_x + position.x(), -bound_y + position.y(), 0.0], [region.u0(), region.v1()], [0.0, 0.0, 0.0, 0.0] ),
|
||||||
Vertex :: new ( [ bound_x + position.x(), -bound_y + position.y(), 0.0], [region.x1(), region.y1()], [0.0, 0.0, 0.0, 0.0] ) ,
|
Vertex :: new ( [ bound_x + position.x(), -bound_y + position.y(), 0.0], [region.u1(), region.v1()], [0.0, 0.0, 0.0, 0.0] ) ,
|
||||||
Vertex :: new ( [ bound_x + position.x(), bound_y + position.y(), 0.0], [region.x1(), region.y0()], [0.0, 0.0, 0.0, 0.0] )
|
Vertex :: new ( [ bound_x + position.x(), bound_y + position.y(), 0.0], [region.u1(), region.v0()], [0.0, 0.0, 0.0, 0.0] )
|
||||||
]);
|
]);
|
||||||
|
|
||||||
index_buffer.append(&mut vec![
|
index_buffer.append(&mut vec![
|
||||||
|
|
@ -1125,10 +1106,10 @@ impl<'a> Renderer2D<'a> {
|
||||||
let buffer_size = vertex_buffer.len() as u16;
|
let buffer_size = vertex_buffer.len() as u16;
|
||||||
|
|
||||||
vertex_buffer.append(&mut vec![
|
vertex_buffer.append(&mut vec![
|
||||||
Vertex::new([-bound_x + position.x(), bound_y + position.y(), 0.0], [region.x0(), region.y0()], [0.0, 0.0, 0.0, 0.0]),
|
Vertex::new([-bound_x + position.x(), bound_y + position.y(), 0.0], [region.u0(), region.v0()], [0.0, 0.0, 0.0, 0.0]),
|
||||||
Vertex::new([-bound_x + position.x(), -bound_y + position.y(), 0.0], [region.x0(), region.y1()], [0.0, 0.0, 0.0, 0.0]),
|
Vertex::new([-bound_x + position.x(), -bound_y + position.y(), 0.0], [region.u0(), region.v1()], [0.0, 0.0, 0.0, 0.0]),
|
||||||
Vertex::new([bound_x + position.x(), -bound_y + position.y(), 0.0], [region.x1(), region.y1()], [0.0, 0.0, 0.0, 0.0]),
|
Vertex::new([bound_x + position.x(), -bound_y + position.y(), 0.0], [region.u1(), region.v1()], [0.0, 0.0, 0.0, 0.0]),
|
||||||
Vertex::new([bound_x + position.x(), bound_y + position.y(), 0.0], [region.x1(), region.y0()], [0.0, 0.0, 0.0, 0.0])
|
Vertex::new([bound_x + position.x(), bound_y + position.y(), 0.0], [region.u1(), region.v0()], [0.0, 0.0, 0.0, 0.0])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
index_buffer.append(&mut vec![
|
index_buffer.append(&mut vec![
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,31 @@
|
||||||
use image::{DynamicImage, Rgba, RgbaImage};
|
use image::{DynamicImage, Rgba, RgbaImage};
|
||||||
use ab_glyph::{FontArc, PxScale, ScaleFont, Glyph, point, Font as AbFont};
|
use ab_glyph::{FontArc, PxScale, ScaleFont, Glyph, point, Font as AbFont};
|
||||||
|
use comet_log::debug;
|
||||||
use crate::texture_atlas::{TextureAtlas, TextureRegion};
|
use crate::texture_atlas::{TextureAtlas, TextureRegion};
|
||||||
|
|
||||||
|
pub struct GlyphData {
|
||||||
|
pub name: String,
|
||||||
|
pub render: DynamicImage,
|
||||||
|
pub advance: f32,
|
||||||
|
pub offset_x: f32,
|
||||||
|
pub offset_y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Font {
|
pub struct Font {
|
||||||
name: String,
|
name: String,
|
||||||
|
size: f32,
|
||||||
|
line_height: f32,
|
||||||
glyphs: TextureAtlas,
|
glyphs: TextureAtlas,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Font {
|
impl Font {
|
||||||
pub fn new(path: &str, size: f32) -> Self {
|
pub fn new(path: &str, size: f32) -> Self {
|
||||||
|
let (glyphs, line_height) = Self::generate_atlas(path, size);
|
||||||
Font {
|
Font {
|
||||||
name: path.to_string(),
|
name: path.to_string(),
|
||||||
glyphs: Self::generate_atlas(path, size)
|
size,
|
||||||
|
line_height,
|
||||||
|
glyphs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,6 +33,14 @@ impl Font {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> f32 {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn line_height(&self) -> f32 {
|
||||||
|
self.line_height
|
||||||
|
}
|
||||||
|
|
||||||
pub fn glyphs(&self) -> &TextureAtlas {
|
pub fn glyphs(&self) -> &TextureAtlas {
|
||||||
&self.glyphs
|
&self.glyphs
|
||||||
}
|
}
|
||||||
|
|
@ -27,31 +49,41 @@ impl Font {
|
||||||
self.glyphs.textures().get(&ch.to_string())
|
self.glyphs.textures().get(&ch.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_atlas(path: &str, size: f32) -> TextureAtlas {
|
fn generate_atlas(path: &str, size: f32) -> (TextureAtlas, f32) {
|
||||||
let font_data = std::fs::read(path).expect("Failed to read font file");
|
let font_data = std::fs::read(path).expect("Failed to read font file");
|
||||||
let font = FontArc::try_from_vec(font_data).expect("Failed to load font");
|
let font = FontArc::try_from_vec(font_data).expect("Failed to load font");
|
||||||
|
|
||||||
let scale = PxScale::from(size);
|
let scale = PxScale::from(size);
|
||||||
let scaled_font = font.as_scaled(scale);
|
let scaled_font = font.as_scaled(scale);
|
||||||
|
|
||||||
let mut names = Vec::new();
|
let mut glyphs: Vec<GlyphData> = Vec::new();
|
||||||
let mut images = Vec::new();
|
|
||||||
|
|
||||||
for code_point in 0x0020..=0x007E {
|
for code_point in 0x0020..=0x007E {
|
||||||
if let Some(ch) = std::char::from_u32(code_point) {
|
if let Some(ch) = std::char::from_u32(code_point) {
|
||||||
if font.glyph_id(ch).0 == 0 {
|
let glyph_id = font.glyph_id(ch);
|
||||||
|
if glyph_id.0 == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
names.push(ch.to_string());
|
if ch == ' ' {
|
||||||
|
let advance = scaled_font.h_advance(glyph_id);
|
||||||
|
glyphs.push(GlyphData {
|
||||||
|
name: ch.to_string(),
|
||||||
|
render: DynamicImage::new_rgba8(0, 0), // no bitmap
|
||||||
|
advance,
|
||||||
|
offset_x: 0.0,
|
||||||
|
offset_y: 0.0,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let glyph = Glyph {
|
let glyph = Glyph {
|
||||||
id: font.glyph_id(ch),
|
id: glyph_id,
|
||||||
scale,
|
scale,
|
||||||
position: point(0.0, 0.0),
|
position: point(0.0, 0.0),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(outline) = scaled_font.outline_glyph(glyph) {
|
if let Some(outline) = scaled_font.outline_glyph(glyph.clone()) {
|
||||||
let bounds = outline.px_bounds();
|
let bounds = outline.px_bounds();
|
||||||
let width = bounds.width().ceil() as u32;
|
let width = bounds.width().ceil() as u32;
|
||||||
let height = bounds.height().ceil() as u32;
|
let height = bounds.height().ceil() as u32;
|
||||||
|
|
@ -70,11 +102,19 @@ impl Font {
|
||||||
image.put_pixel(x, y, Rgba([255, 255, 255, alpha]));
|
image.put_pixel(x, y, Rgba([255, 255, 255, alpha]));
|
||||||
});
|
});
|
||||||
|
|
||||||
images.push(DynamicImage::ImageRgba8(image));
|
glyphs.push(
|
||||||
|
GlyphData {
|
||||||
|
name: ch.to_string(),
|
||||||
|
render: DynamicImage::ImageRgba8(image),
|
||||||
|
advance: scaled_font.h_advance(glyph_id),
|
||||||
|
offset_x: bounds.min.x,
|
||||||
|
offset_y: bounds.min.y,
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureAtlas::from_textures(names, images)
|
(TextureAtlas::from_glyphs(glyphs), scaled_font.ascent() - scaled_font.descent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,47 +4,66 @@ use std::time::Instant;
|
||||||
use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat};
|
use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat};
|
||||||
use comet_log::*;
|
use comet_log::*;
|
||||||
use wgpu::{Device, FilterMode, TextureFormat, TextureUsages};
|
use wgpu::{Device, FilterMode, TextureFormat, TextureUsages};
|
||||||
|
use crate::font::GlyphData;
|
||||||
use crate::Texture;
|
use crate::Texture;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TextureRegion {
|
pub struct TextureRegion {
|
||||||
x0: f32,
|
u0: f32,
|
||||||
y0: f32,
|
v0: f32,
|
||||||
x1: f32,
|
u1: f32,
|
||||||
y1: f32,
|
v1: f32,
|
||||||
dimensions: (u32, u32)
|
dimensions: (u32, u32),
|
||||||
|
advance: f32,
|
||||||
|
offset_x: f32,
|
||||||
|
offset_y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureRegion {
|
impl TextureRegion {
|
||||||
pub fn new(x0: f32, y0: f32, x1: f32, y1: f32, dimensions: (u32, u32)) -> Self {
|
pub fn new(u0: f32, v0: f32, u1: f32, v1: f32, dimensions: (u32, u32), advance: f32, offset_x: f32, offset_y: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
x0,
|
u0,
|
||||||
y0,
|
v0,
|
||||||
x1,
|
u1,
|
||||||
y1,
|
v1,
|
||||||
dimensions
|
dimensions,
|
||||||
|
advance,
|
||||||
|
offset_x,
|
||||||
|
offset_y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn x0(&self) -> f32 {
|
pub fn u0(&self) -> f32 {
|
||||||
self.x0
|
self.u0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn x1(&self) -> f32 {
|
pub fn u1(&self) -> f32 {
|
||||||
self.x1
|
self.u1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn y0(&self) -> f32 {
|
pub fn v0(&self) -> f32 {
|
||||||
self.y0
|
self.v0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn y1(&self) -> f32 {
|
pub fn v1(&self) -> f32 {
|
||||||
self.y1
|
self.v1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dimensions(&self) -> (u32, u32) {
|
pub fn dimensions(&self) -> (u32, u32) {
|
||||||
self.dimensions
|
self.dimensions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn advance(&self) -> f32 {
|
||||||
|
self.advance
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset_x(&self) -> f32 {
|
||||||
|
self.offset_x
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset_y(&self) -> f32 {
|
||||||
|
self.offset_y
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -152,7 +171,10 @@ impl TextureAtlas {
|
||||||
y_offset as f32 / height as f32,
|
y_offset as f32 / height as f32,
|
||||||
(x_offset + texture.width()) as f32 / width as f32,
|
(x_offset + texture.width()) as f32 / width as f32,
|
||||||
(y_offset + texture.height()) as f32 / height as f32,
|
(y_offset + texture.height()) as f32 / height as f32,
|
||||||
texture.dimensions()
|
texture.dimensions(),
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
));
|
));
|
||||||
x_offset += texture.width();
|
x_offset += texture.width();
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +223,10 @@ impl TextureAtlas {
|
||||||
y_offset as f32 / height as f32,
|
y_offset as f32 / height as f32,
|
||||||
(x_offset + texture.width()) as f32 / width as f32,
|
(x_offset + texture.width()) as f32 / width as f32,
|
||||||
(y_offset + texture.height()) as f32 / height as f32,
|
(y_offset + texture.height()) as f32 / height as f32,
|
||||||
texture.dimensions()
|
texture.dimensions(),
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
));
|
));
|
||||||
x_offset += texture.width();
|
x_offset += texture.width();
|
||||||
}
|
}
|
||||||
|
|
@ -214,6 +239,58 @@ impl TextureAtlas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_glyphs(mut glyphs: Vec<GlyphData>) -> Self {
|
||||||
|
glyphs.sort_by(|a, b| b.render.height().cmp(&a.render.height()));
|
||||||
|
|
||||||
|
let height = Self::calculate_atlas_height(
|
||||||
|
&glyphs.iter().map(|g| g.render.clone()).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
let width = Self::calculate_atlas_width(
|
||||||
|
&glyphs.iter().map(|g| g.render.clone()).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut base = DynamicImage::new_rgba8(width, height);
|
||||||
|
let mut regions = HashMap::new();
|
||||||
|
let mut current_row_height = glyphs[0].render.height();
|
||||||
|
let mut x_offset: u32 = 0;
|
||||||
|
let mut y_offset: u32 = 0;
|
||||||
|
|
||||||
|
for g in glyphs.iter() {
|
||||||
|
let glyph_w = g.render.width();
|
||||||
|
let glyph_h = g.render.height();
|
||||||
|
|
||||||
|
if glyph_h != current_row_height {
|
||||||
|
y_offset += current_row_height;
|
||||||
|
x_offset = 0;
|
||||||
|
current_row_height = glyph_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::insert_texture_at(&mut base, &g.render, x_offset, y_offset);
|
||||||
|
|
||||||
|
let u0 = x_offset as f32 / width as f32;
|
||||||
|
let v0 = y_offset as f32 / height as f32;
|
||||||
|
let u1 = (x_offset + glyph_w) as f32 / width as f32;
|
||||||
|
let v1 = (y_offset + glyph_h) as f32 / height as f32;
|
||||||
|
|
||||||
|
let region = TextureRegion::new(
|
||||||
|
u0, v0, u1, v1,
|
||||||
|
(glyph_w, glyph_h),
|
||||||
|
g.advance,
|
||||||
|
g.offset_x,
|
||||||
|
g.offset_y,
|
||||||
|
);
|
||||||
|
|
||||||
|
regions.insert(g.name.clone(), region);
|
||||||
|
|
||||||
|
x_offset += glyph_w;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureAtlas {
|
||||||
|
atlas: base,
|
||||||
|
textures: regions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn atlas(&self) -> &DynamicImage {
|
pub fn atlas(&self) -> &DynamicImage {
|
||||||
&self.atlas
|
&self.atlas
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue