Compare commits

..

No commits in common. "0ec78da7b58ceaa1e268bba835780948d9e626c8" and "0a637f22fc08bdd44e3718bfc3e492fd40c085af" have entirely different histories.

8 changed files with 106 additions and 190 deletions

View File

@ -28,7 +28,7 @@ pub trait Application<S> {
//--Canvas struct----------------------------------------------------------------------------------- //--Canvas struct-----------------------------------------------------------------------------------
pub struct Canvas { pub struct Canvas {
default_texture: TextureHandle, default_texture: TextureHandle,
clear_color: Color, clear_color: Pixel,
renderer: WgpuRenderer, renderer: WgpuRenderer,
} }
@ -149,7 +149,7 @@ impl Canvas {
sprite.render(&mut self.renderer); sprite.render(&mut self.renderer);
} }
pub fn set_clear_color(&mut self, color: Color) { pub fn set_clear_color(&mut self, color: Pixel) {
self.clear_color = color; self.clear_color = color;
} }

View File

@ -55,8 +55,8 @@ impl Application<ExampleState> for ExampleApp {
fn init(canvas: &mut Canvas) -> Result<ExampleState, &'static str> { fn init(canvas: &mut Canvas) -> Result<ExampleState, &'static str> {
use canvas::{ use canvas::{
utils::{Size, Color}, utils::Size,
sprite::{Sprite, Center}, sprite::Sprite,
}; };
//// 20 x 20 sprite of a picture //// 20 x 20 sprite of a picture
@ -64,24 +64,23 @@ impl Application<ExampleState> for ExampleApp {
.unwrap(); .unwrap();
let mut tex_sprite = canvas.create_texture_sprite(Size {w: 1280, h: 720}); let mut tex_sprite = canvas.create_texture_sprite(Size {w: 1280, h: 720});
tex_sprite.set_texture(texture.clone(), Some(Position {x: 0, y: 0}), 1.0); tex_sprite.set_texture(texture.clone(), Some(Position {x: 0, y: 0}), 1.0);
tex_sprite.set_center(Center::BotLeft); tex_sprite.set_center(Position {x:-1280/2, y:-720/2});
tex_sprite.set_position(Position {x: 0, y: 0}); tex_sprite.set_position(Position {x: 0, y: 0});
let mut sub_sprite = canvas.create_texture_sprite(Size {w: 100, h: 100}); let mut sub_sprite = canvas.create_texture_sprite(Size {w: 100, h: 100});
sub_sprite.set_texture(texture.clone(), Some(Position {x: 0, y: 0}), 1.0); sub_sprite.set_texture(texture.clone(), Some(Position {x: 0, y: 0}), 1.0);
sub_sprite.set_position(Position {x: 100, y: 100}); sub_sprite.set_position(Position {x: 0, y: 0});
sub_sprite.set_scale(2.0); sub_sprite.set_scale(2.0);
let mut sub_sprite2 = canvas.create_texture_sprite(Size {w: 200, h: 200}); let mut sub_sprite2 = canvas.create_texture_sprite(Size {w: 200, h: 200});
sub_sprite2.set_texture(texture.clone(), Some(Position {x: 100, y: 0}), 1.0); sub_sprite2.set_texture(texture.clone(), Some(Position {x: 100, y: 0}), 1.0);
sub_sprite2.set_center(Center::Custom(Position {x:100, y:100})); sub_sprite2.set_center(Position {x:100, y:100});
sub_sprite2.set_rotation(0.0); sub_sprite2.set_rotation(0.0);
sub_sprite2.set_position(Position {x: 100, y: 100}); sub_sprite2.set_position(Position {x: 0, y: 0});
let mut txt_sprite = canvas.create_text_sprite("00", Size {w: 100, h: 100}, 22.0); let mut txt_sprite = canvas.create_text_sprite("00", Size {w: 100, h: 100}, 22.0);
txt_sprite.set_center(Center::BotLeft); txt_sprite.set_center(Position {x:0, y:0});
txt_sprite.set_position(Position {x:100, y:100}); txt_sprite.set_position(Position {x:100, y:100});
txt_sprite.set_bg_color(Color::WHITE);
txt_sprite.set_scale(1.0); txt_sprite.set_scale(1.0);
canvas.clear(); canvas.clear();
@ -116,13 +115,13 @@ impl Application<ExampleState> for ExampleApp {
state.frame_counter += 1; state.frame_counter += 1;
if state.frame_counter >= 60 { if state.frame_counter >= 60 {
state.frame_counter = 0; state.frame_counter = 0;
state.txt_sprite.set_text(&format!("{}", elapsed)); // state.txt_sprite.set_text(&format!("{}", elapsed));
state.txt_sprite.set_text_size(elapsed as f32); // state.txt_sprite.set_text_size(elapsed as f32);
} }
match state.frame_counter { match state.frame_counter {
0 => state.txt_sprite.set_fg_color(Color::RED), 0 => state.txt_sprite.set_color(Color::RED),
20 => state.txt_sprite.set_fg_color(Color::BLUE), 20 => state.txt_sprite.set_color(Color::BLUE),
40 => state.txt_sprite.set_fg_color(Color::GREEN), 40 => state.txt_sprite.set_color(Color::GREEN),
_ => (), _ => (),
} }
//state.sub_sprite.for_each(|pix| unsafe {pix.flat = pix.flat.wrapping_add(1);}); //state.sub_sprite.for_each(|pix| unsafe {pix.flat = pix.flat.wrapping_add(1);});
@ -135,8 +134,8 @@ impl Application<ExampleState> for ExampleApp {
state.tex_sprite.set_texture(state.texture.clone(), None, state.last_rot/100.0); state.tex_sprite.set_texture(state.texture.clone(), None, state.last_rot/100.0);
// state.sub_sprite.set_texture(state.texture.clone(), // state.sub_sprite.set_texture(state.texture.clone(),
// Some(Position {x: state.last_offset, y: 0}), 1.0); // Some(Position {x: state.last_offset, y: 0}), 1.0);
// state.sub_sprite.set_position(state.last_pos); state.sub_sprite.set_position(state.last_pos);
// state.sub_sprite2.set_position(state.last_pos); state.sub_sprite2.set_position(state.last_pos);
state.sub_sprite.set_rotation(state.last_rot); state.sub_sprite.set_rotation(state.last_rot);
state.sub_sprite2.set_rotation(state.last_rot + 45.0); state.sub_sprite2.set_rotation(state.last_rot + 45.0);

View File

@ -4,7 +4,7 @@ use log::{debug, error, info, trace, warn};
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
use wgpu; use wgpu;
use crate::utils::{Color, Size}; use crate::utils::{Pixel, Size};
pub mod utils; pub mod utils;
@ -100,7 +100,7 @@ impl WgpuRenderer {
self.surface_size self.surface_size
} }
pub fn clear(&mut self, color: Color) { pub fn clear(&mut self, color: Pixel) {
let view = self.create_texture_view(); let view = self.create_texture_view();
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
@ -225,23 +225,16 @@ impl WgpuRenderer {
//--Renderer struct utils--------------------------------------------------------------------------- //--Renderer struct utils---------------------------------------------------------------------------
//impl From<Pixel> for wgpu::Color { impl From<Pixel> for wgpu::Color {
//
// fn from(value: Pixel) -> Self {
// Self {
// r: f64::from(value.r) / 255.0,
// g: f64::from(value.g) / 255.0,
// b: f64::from(value.b) / 255.0,
// a: f64::from(value.a) / 255.0,
// }
// }
//}
impl From<Color> for wgpu::Color { fn from(pix: Pixel) -> Self {
let rgba = pix.to_rgba();
fn from(value: Color) -> Self { Self {
value.into_wgpu_color() r: (rgba.r as f64) / 255.0,
} g: (rgba.g as f64) / 255.0,
b: (rgba.b as f64) / 255.0,
a: (rgba.a as f64) / 255.0,
}
}
} }

View File

@ -19,16 +19,6 @@ use crate::{
//--External imports-------------------------------------------------------------------------------- //--External imports--------------------------------------------------------------------------------
//--Center enum-------------------------------------------------------------------------------------
pub enum Center {
Geometric,
TopLeft,
TopRight,
BotLeft,
BotRight,
Custom(Position),
}
//--Sprite trait------------------------------------------------------------------------------------ //--Sprite trait------------------------------------------------------------------------------------
/// The interface for everything that can be rendered by the engine. /// The interface for everything that can be rendered by the engine.
@ -41,7 +31,7 @@ pub trait Sprite {
fn set_position(&mut self, position: Position); fn set_position(&mut self, position: Position);
/// Sets the center of the [`Sprite`] for rotations as well as translations /// Sets the center of the [`Sprite`] for rotations as well as translations
fn set_center(&mut self, center: Center); fn set_center(&mut self, center: Position);
/// Sets the rotation of the [`Sprite`] on the screen /// Sets the rotation of the [`Sprite`] on the screen
fn set_rotation(&mut self, rotation: f32); fn set_rotation(&mut self, rotation: f32);

View File

@ -5,7 +5,7 @@ use log::{debug, error, info, trace, warn};
use crate::{ use crate::{
renderer::WgpuRenderer, renderer::WgpuRenderer,
sprite::{Sprite, Center}, sprite::Sprite,
shape::Shape, shape::Shape,
utils::{Pixel, Position, Size}, utils::{Pixel, Position, Size},
}; };
@ -39,7 +39,7 @@ impl Sprite for ShapeSprite {
todo!(); todo!();
} }
fn set_center(&mut self, _center: Center) { fn set_center(&mut self, _center: Position) {
todo!(); todo!();
} }

View File

@ -5,7 +5,7 @@ use log::{debug, error, info, trace, warn};
use crate::{ use crate::{
renderer::WgpuRenderer, renderer::WgpuRenderer,
sprite::{Sprite, Center}, sprite::Sprite,
utils::{Pixel, Color, Position, Size}, utils::{Pixel, Color, Position, Size},
}; };
use super::TextureSprite; use super::TextureSprite;
@ -18,8 +18,7 @@ pub struct TextSprite {
text: String, text: String,
text_size: f32, text_size: f32,
fg_color: Color, text_color: Pixel,
bg_color: Color,
font: fontdue::Font, font: fontdue::Font,
layout: fontdue::layout::Layout, layout: fontdue::layout::Layout,
@ -42,8 +41,29 @@ impl TextSprite {
layout.append(&[&font], &TextStyle::new(text, text_size, 0)); layout.append(&[&font], &TextStyle::new(text, text_size, 0));
//draw text to the texture //draw text to the texture
let raw_texture : Vec::<Pixel> let mut raw_texture : Vec::<Pixel>
= vec![Color::NONE.into(); (size.h * size.w).try_into().unwrap()]; = vec![Color::NONE; (size.h * size.w).try_into().unwrap()];
//draw each glyph at the corresponding position on the texture
for glyph in layout.glyphs() {
let (_metrics, bitmap) = font.rasterize_config(glyph.key);
let glyph_x = glyph.x as usize;
let glyph_y = glyph.y as usize;
let size_w = size.w as usize;
let size_h = size.h as usize;
//only try to draw a glyph if its origin fits in the texture
if glyph_x < size_w && glyph_y < size_h {
let origin = glyph_x + size_w * glyph_y;
//glyphs that do not fit on the texture are cropped at the edge of the latter
for x in 0..std::cmp::min(glyph.width, size_w - glyph_x) {
for y in 0..std::cmp::min(glyph.height, size_h - glyph_y) {
raw_texture[origin + x + y * size_w]
= Color::BLACK.with_alpha(bitmap[x + y * glyph.width]);
} } } }
//create the texture //create the texture
let texture = Texture::create(renderer, bytemuck::cast_slice(&raw_texture), size); let texture = Texture::create(renderer, bytemuck::cast_slice(&raw_texture), size);
@ -55,11 +75,10 @@ impl TextSprite {
texture_sprite, texture_sprite,
text: text.to_string(), text: text.to_string(),
text_size, text_size,
fg_color: Color::BLACK, text_color: Color::BLACK,
bg_color: Color::NONE,
font, font,
layout, layout,
text_generation_needed: true, text_generation_needed: false,
} }
} }
@ -73,18 +92,9 @@ impl TextSprite {
self.text_generation_needed = true; self.text_generation_needed = true;
} }
pub fn set_fg_color(&mut self, color: Color) { pub fn set_color(&mut self, color: Pixel) {
if self.fg_color != color { self.text_color = color;
self.fg_color = color; self.text_generation_needed = true;
self.text_generation_needed = true;
}
}
pub fn set_bg_color(&mut self, color: Color) {
if self.bg_color != color {
self.bg_color = color;
self.text_generation_needed = true;
}
} }
fn generate_text(&mut self) { fn generate_text(&mut self) {
@ -94,7 +104,7 @@ impl TextSprite {
&fontdue::layout::TextStyle::new(&self.text, self.text_size, 0)); &fontdue::layout::TextStyle::new(&self.text, self.text_size, 0));
let size = self.texture_sprite.get_size(); let size = self.texture_sprite.get_size();
self.texture_sprite.fill(self.bg_color.into()); self.texture_sprite.fill(Color::NONE);
//draw each glyph at the corresponding position on the texture //draw each glyph at the corresponding position on the texture
for glyph in self.layout.glyphs() { for glyph in self.layout.glyphs() {
@ -112,15 +122,12 @@ impl TextSprite {
for x in 0..std::cmp::min(glyph.width, size_w - glyph_x) { for x in 0..std::cmp::min(glyph.width, size_w - glyph_x) {
for y in 0..std::cmp::min(glyph.height, size_h - glyph_y) { for y in 0..std::cmp::min(glyph.height, size_h - glyph_y) {
self.fg_color.set_alpha(bitmap[x + y * glyph.width]);
self.texture_sprite.set_pixel( self.texture_sprite.set_pixel(
Position { Position {
x: (glyph_x + x) as i32, x: (glyph_x + x) as i32,
y: (glyph_y + y) as i32, y: (glyph_y + y) as i32,
}, },
self.text_color.with_alpha(bitmap[x + y * glyph.width])
Color::alpha_blend(&self.fg_color, &self.bg_color).into()
); );
} } } } } } } }
} }
@ -142,7 +149,7 @@ impl Sprite for TextSprite {
self.texture_sprite.set_position(position); self.texture_sprite.set_position(position);
} }
fn set_center(&mut self, center: Center) { fn set_center(&mut self, center: Position) {
self.texture_sprite.set_center(center); self.texture_sprite.set_center(center);
} }

View File

@ -3,7 +3,7 @@ use log::{debug, error, info, trace, warn};
//--Internal imports-------------------------------------------------------------------------------- //--Internal imports--------------------------------------------------------------------------------
use super::{Sprite, Center}; use super::Sprite;
use crate::{ use crate::{
texture::TextureHandle, texture::TextureHandle,
@ -148,28 +148,8 @@ impl Sprite for TextureSprite {
self.matrix.set_position(position); self.matrix.set_position(position);
} }
fn set_center(&mut self, center: Center) { fn set_center(&mut self, center: Position) {
let center_pos = match center { self.matrix.set_center(center);
Center::Geometric => Position {x: 0, y: 0},
Center::TopLeft => Position {
x: -(self.inner_size.w as i32) / 2,
y: (self.inner_size.h as i32) / 2
},
Center::TopRight => Position {
x: (self.inner_size.w as i32) / 2,
y: (self.inner_size.h as i32) / 2
},
Center::BotLeft => Position {
x: -(self.inner_size.w as i32) / 2,
y: -(self.inner_size.h as i32) / 2
},
Center::BotRight => Position {
x: (self.inner_size.w as i32) / 2,
y: -(self.inner_size.h as i32) / 2
},
Center::Custom(pos) => pos,
};
self.matrix.set_center(center_pos);
} }
fn set_rotation(&mut self, rotation: f32) { fn set_rotation(&mut self, rotation: f32) {

View File

@ -105,110 +105,57 @@ impl From<Size> for Vector2<f32> {
} }
//--Pixel struct------------------------------------------------------------------------------------ //--Pixel struct------------------------------------------------------------------------------------
#[repr(packed, C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
#[derive(Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] #[repr(C)]
pub struct Pixel { pub struct Rgba {
pub r: u8, pub r: u8,
pub g: u8, pub g: u8,
pub b: u8, pub b: u8,
pub a: u8, pub a: u8,
} }
impl From<Color> for Pixel { #[derive(Copy, Clone, bytemuck::Zeroable)]
pub union Pixel {
fn from(value: Color) -> Self { pub flat: u32,
value.into_pixel() rgba: Rgba,
}
} }
unsafe impl bytemuck::Pod for Pixel {}
impl Pixel { impl Pixel {
pub const fn from_hex(hex: u32) -> Self { pub fn rgb(r: u8, g: u8, b: u8) -> Pixel {
let bytes = hex.to_be_bytes(); Pixel {
Self { r: bytes[0], g: bytes[1], b: bytes[2], a: bytes[3] } rgba: Rgba{r, g, b, a: 255},
} }
}
pub fn rgba(r: u8, g: u8, b: u8, a: u8) -> Pixel {
Pixel {
rgba: Rgba{r, g, b, a},
}
}
pub fn with_alpha(self, a: u8) -> Pixel {
unsafe { Pixel::rgba(self.rgba.r, self.rgba.g, self.rgba.b, a) }
}
pub fn to_rgba(&self) -> Rgba {
unsafe { self.rgba }
}
} }
//--Color values------------------------------------------------------------------------------------ //--Color values------------------------------------------------------------------------------------
#[non_exhaustive] #[non_exhaustive]
#[derive(Copy, Clone, PartialEq)] pub struct Color;
pub struct Color {
r: f32,
g: f32,
b: f32,
a: f32,
}
impl From<Pixel> for Color {
fn from(value: Pixel) -> Self {
Self {
r: f32::from(value.r) / 255.0,
g: f32::from(value.g) / 255.0,
b: f32::from(value.b) / 255.0,
a: f32::from(value.a) / 255.0,
}
}
}
impl Color { impl Color {
pub const NONE: Pixel = Pixel {flat: 0x00000000};
pub fn set_alpha(&mut self, a: u8) { pub const WHITE: Pixel = Pixel {flat: 0xffffffff};
self.a = f32::from(a) / 255.0; pub const BLACK: Pixel = Pixel {flat: 0xff000000};
} pub const RED: Pixel = Pixel {flat: 0xff0000ff};
pub const GREEN: Pixel = Pixel {flat: 0xff00ff00};
pub fn from_rgb(r: u8, g: u8, b: u8) -> Self { pub const BLUE: Pixel = Pixel {flat: 0xffff0000};
Pixel { r, g, b, a: 255 }.into()
}
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Pixel { r, g, b, a }.into()
}
pub fn from_hex(hex: u32) -> Self {
Pixel::from_hex(hex).into()
}
pub fn into_pixel(self) -> Pixel {
//unsafe block is used here for "to_int_unchecked" since the struct's invariant garentees
//that the values are within [0.0, 1.0], meaning we won't ever overflow an u8
unsafe {
Pixel {
r: self.r.to_int_unchecked::<u8>() * 255,
g: self.g.to_int_unchecked::<u8>() * 255,
b: self.b.to_int_unchecked::<u8>() * 255,
a: self.a.to_int_unchecked::<u8>() * 255,
}
}
}
pub fn into_wgpu_color(self) -> wgpu::Color {
wgpu::Color {
r: f64::from(self.r),
g: f64::from(self.g),
b: f64::from(self.b),
a: f64::from(self.a),
}
}
pub fn alpha_blend(&self, other: &Self) -> Self {
//apply alpha compisting's "over" operator, see
//[https://en.wikipedia.org/wiki/Alpha_compositing] for more detail
let a = self.a + other.a * (1.0 - self.a);
Self {
r: (self.r * self.a + other.r * other.a * (1.0 - self.a)) / a,
g: (self.g * self.a + other.g * other.a * (1.0 - self.a)) / a,
b: (self.b * self.a + other.b * other.a * (1.0 - self.a)) / a,
a,
}
}
pub const NONE: Self = Self {r: 0.0, g: 0.0, b: 0.0, a: 0.0};
pub const WHITE: Self = Self {r: 1.0, g: 1.0, b: 1.0, a: 1.0};
pub const BLACK: Self = Self {r: 0.0, g: 0.0, b: 0.0, a: 1.0};
pub const RED: Self = Self {r: 1.0, g: 0.0, b: 0.0, a: 1.0};
pub const GREEN: Self = Self {r: 0.0, g: 1.0, b: 0.0, a: 1.0};
pub const BLUE: Self = Self {r: 0.0, g: 0.0, b: 1.0, a: 1.0};
} }