Compare commits
7 Commits
e4ccf95a2f
...
0a637f22fc
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a637f22fc | |||
| c90f4a3148 | |||
| d28325f2ff | |||
| b524ad07e0 | |||
| 67f802dd77 | |||
| 8a2d8bcda7 | |||
| 075e8e996f |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
|||||||
/target
|
/target
|
||||||
*.log
|
*.log
|
||||||
*.bkp
|
*.bkp
|
||||||
|
*.perf
|
||||||
|
*.data
|
||||||
|
*.html
|
||||||
|
|||||||
30
Cargo.lock
generated
30
Cargo.lock
generated
@ -147,6 +147,7 @@ dependencies = [
|
|||||||
"cgmath",
|
"cgmath",
|
||||||
"chrono",
|
"chrono",
|
||||||
"fern",
|
"fern",
|
||||||
|
"fontdue",
|
||||||
"image",
|
"image",
|
||||||
"log",
|
"log",
|
||||||
"pollster",
|
"pollster",
|
||||||
@ -504,6 +505,16 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fontdue"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a62391ecb864cf12ed06b2af4eda2e609b97657950d6a8f06841b17726ab253"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.11.2",
|
||||||
|
"ttf-parser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -578,7 +589,7 @@ checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"gpu-descriptor-types",
|
"gpu-descriptor-types",
|
||||||
"hashbrown",
|
"hashbrown 0.12.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -590,6 +601,15 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@ -666,7 +686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown 0.12.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1319,6 +1339,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ttf-parser"
|
||||||
|
version = "0.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
|||||||
@ -13,6 +13,7 @@ fern = { version = "^0.6.1", features = ["colored"] }
|
|||||||
bitflags = "^1.3.2"
|
bitflags = "^1.3.2"
|
||||||
cgmath = { version = "^0.18.0", features = ["bytemuck"] }
|
cgmath = { version = "^0.18.0", features = ["bytemuck"] }
|
||||||
pollster = "^0.2.5"
|
pollster = "^0.2.5"
|
||||||
|
fontdue = "0.7.2"
|
||||||
|
|
||||||
# surface creation
|
# surface creation
|
||||||
winit = "^0.26.1"
|
winit = "^0.26.1"
|
||||||
@ -33,3 +34,6 @@ features = ["png", "jpeg"]
|
|||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
cgmath = { path = "cgmath" }
|
cgmath = { path = "cgmath" }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = 1
|
||||||
|
|
||||||
|
|||||||
BIN
assets/DejaVuSansMono.ttf
Normal file
BIN
assets/DejaVuSansMono.ttf
Normal file
Binary file not shown.
@ -80,8 +80,14 @@ impl Canvas {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_text_sprite(&mut self, _size: Size, _scale: f32) -> TextSprite {
|
pub fn create_text_sprite(&mut self, text: &'static str, size: Size, text_size: f32)
|
||||||
unimplemented!();
|
-> TextSprite {
|
||||||
|
TextSprite::new(
|
||||||
|
text,
|
||||||
|
size,
|
||||||
|
text_size,
|
||||||
|
&self.renderer,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_shape_sprite(&mut self) -> ShapeSprite {
|
pub fn create_shape_sprite(&mut self) -> ShapeSprite {
|
||||||
|
|||||||
@ -106,7 +106,7 @@ mod tests {
|
|||||||
message
|
message
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.level(log::LevelFilter::Trace)
|
.level(log::LevelFilter::Debug)
|
||||||
.chain(std::io::stdout())
|
.chain(std::io::stdout())
|
||||||
.chain(fern::log_file("output.log")?)
|
.chain(fern::log_file("output.log")?)
|
||||||
.apply()?;
|
.apply()?;
|
||||||
|
|||||||
68
src/main.rs
68
src/main.rs
@ -5,7 +5,7 @@ use canvas::{
|
|||||||
Application,
|
Application,
|
||||||
Canvas,
|
Canvas,
|
||||||
texture::TextureHandle,
|
texture::TextureHandle,
|
||||||
sprite::TextureSprite,
|
sprite::{TextureSprite, TextSprite},
|
||||||
utils::Position,
|
utils::Position,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,10 +40,13 @@ struct ExampleState {
|
|||||||
pub texture: TextureHandle,
|
pub texture: TextureHandle,
|
||||||
pub tex_sprite: TextureSprite,
|
pub tex_sprite: TextureSprite,
|
||||||
pub sub_sprite: TextureSprite,
|
pub sub_sprite: TextureSprite,
|
||||||
|
pub sub_sprite2: TextureSprite,
|
||||||
|
pub txt_sprite: TextSprite,
|
||||||
pub last_instant: Instant,
|
pub last_instant: Instant,
|
||||||
pub last_offset: u32,
|
pub last_offset: u32,
|
||||||
pub last_pos: Position,
|
pub last_pos: Position,
|
||||||
pub last_rot: f32,
|
pub last_rot: f32,
|
||||||
|
pub frame_counter: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExampleApp {}
|
struct ExampleApp {}
|
||||||
@ -51,16 +54,34 @@ struct ExampleApp {}
|
|||||||
impl Application<ExampleState> for ExampleApp {
|
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::utils::Size;
|
use canvas::{
|
||||||
|
utils::Size,
|
||||||
|
sprite::Sprite,
|
||||||
|
};
|
||||||
|
|
||||||
//// 20 x 20 sprite of a picture
|
//// 20 x 20 sprite of a picture
|
||||||
let texture = canvas.create_texture_from_file("assets/camel.jpg", None, None, None)
|
let texture = canvas.create_texture_from_file("assets/camel.jpg", None, None, None)
|
||||||
.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(Position {x:-1280/2, y:-720/2});
|
||||||
|
tex_sprite.set_position(Position {x: 0, y: 0});
|
||||||
|
|
||||||
let mut sub_sprite = canvas.create_texture_sprite(Size {w: 200, h: 200});
|
let mut sub_sprite = canvas.create_texture_sprite(Size {w: 100, h: 100});
|
||||||
sub_sprite.set_texture(texture.clone(), Some(Position {x: 350, y: 0}), 1.0);
|
sub_sprite.set_texture(texture.clone(), Some(Position {x: 0, y: 0}), 1.0);
|
||||||
|
sub_sprite.set_position(Position {x: 0, y: 0});
|
||||||
|
sub_sprite.set_scale(2.0);
|
||||||
|
|
||||||
|
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_center(Position {x:100, y:100});
|
||||||
|
sub_sprite2.set_rotation(0.0);
|
||||||
|
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);
|
||||||
|
txt_sprite.set_center(Position {x:0, y:0});
|
||||||
|
txt_sprite.set_position(Position {x:100, y:100});
|
||||||
|
txt_sprite.set_scale(1.0);
|
||||||
|
|
||||||
canvas.clear();
|
canvas.clear();
|
||||||
canvas.update();
|
canvas.update();
|
||||||
@ -71,32 +92,54 @@ impl Application<ExampleState> for ExampleApp {
|
|||||||
texture,
|
texture,
|
||||||
tex_sprite,
|
tex_sprite,
|
||||||
sub_sprite,
|
sub_sprite,
|
||||||
|
sub_sprite2,
|
||||||
|
txt_sprite,
|
||||||
last_instant,
|
last_instant,
|
||||||
last_offset: 0,
|
last_offset: 0,
|
||||||
last_pos: Position::origin(),
|
last_pos: Position {x: 0, y: 0},
|
||||||
last_rot: 0.0,
|
last_rot: 0.0,
|
||||||
|
frame_counter: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tick(state: &mut ExampleState, canvas: &mut Canvas) -> Result<(), &'static str> {
|
fn tick(state: &mut ExampleState, canvas: &mut Canvas) -> Result<(), &'static str> {
|
||||||
use canvas::sprite::Sprite;
|
use canvas::{
|
||||||
|
sprite::Sprite,
|
||||||
|
utils::Color,
|
||||||
|
};
|
||||||
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let elapsed = now.duration_since(state.last_instant).as_millis();
|
let elapsed = now.duration_since(state.last_instant).as_millis();
|
||||||
state.last_instant = now;
|
state.last_instant = now;
|
||||||
debug!("frame time: {}ms", elapsed);
|
debug!("frame time: {}ms", elapsed);
|
||||||
|
state.frame_counter += 1;
|
||||||
|
if state.frame_counter >= 60 {
|
||||||
|
state.frame_counter = 0;
|
||||||
|
// state.txt_sprite.set_text(&format!("{}", elapsed));
|
||||||
|
// state.txt_sprite.set_text_size(elapsed as f32);
|
||||||
|
}
|
||||||
|
match state.frame_counter {
|
||||||
|
0 => state.txt_sprite.set_color(Color::RED),
|
||||||
|
20 => state.txt_sprite.set_color(Color::BLUE),
|
||||||
|
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);});
|
||||||
|
|
||||||
state.last_offset += 1;
|
// state.last_offset += 1;
|
||||||
state.last_pos.y += 1;
|
//state.last_pos.x += 1;
|
||||||
|
//state.last_pos.y += 1;
|
||||||
state.last_rot += 1.0;
|
state.last_rot += 1.0;
|
||||||
|
debug!("{:#?}", state.last_pos);
|
||||||
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_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.txt_sprite.set_rotation(state.last_rot);
|
||||||
//state.sub_sprite.set_scale(state.last_rot/1000.0);
|
//state.sub_sprite.set_scale(state.last_rot/1000.0);
|
||||||
|
|
||||||
// inputs
|
// inputs
|
||||||
@ -108,6 +151,7 @@ impl Application<ExampleState> for ExampleApp {
|
|||||||
// unimplemented!();
|
// unimplemented!();
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
|
//
|
||||||
//match canvas.get_key_presses() {
|
//match canvas.get_key_presses() {
|
||||||
// Key::A => unimplemented!(),
|
// Key::A => unimplemented!(),
|
||||||
// _ => (),
|
// _ => (),
|
||||||
@ -156,6 +200,8 @@ impl Application<ExampleState> for ExampleApp {
|
|||||||
|
|
||||||
canvas.draw(&mut state.tex_sprite);
|
canvas.draw(&mut state.tex_sprite);
|
||||||
canvas.draw(&mut state.sub_sprite);
|
canvas.draw(&mut state.sub_sprite);
|
||||||
|
canvas.draw(&mut state.sub_sprite2);
|
||||||
|
canvas.draw(&mut state.txt_sprite);
|
||||||
canvas.update();
|
canvas.update();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -96,6 +96,10 @@ impl WgpuRenderer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_surface_size(&self) -> Size {
|
||||||
|
self.surface_size
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self, color: Pixel) {
|
pub fn clear(&mut self, color: Pixel) {
|
||||||
|
|
||||||
let view = self.create_texture_view();
|
let view = self.create_texture_view();
|
||||||
|
|||||||
@ -16,8 +16,10 @@ pub const MATRIX_SIZE: usize = 48;//std::mem::size_of::<Matrix3<f32>>();
|
|||||||
//--ModelMatrix struct------------------------------------------------------------------------------
|
//--ModelMatrix struct------------------------------------------------------------------------------
|
||||||
pub struct ModelMatrix {
|
pub struct ModelMatrix {
|
||||||
position: Position,
|
position: Position,
|
||||||
|
center: Position,
|
||||||
rotation: Deg<f32>,
|
rotation: Deg<f32>,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
|
aspect_matrix: Matrix3<f32>,
|
||||||
|
|
||||||
uniform: Uniform<MATRIX_SIZE>,
|
uniform: Uniform<MATRIX_SIZE>,
|
||||||
is_synced: bool,
|
is_synced: bool,
|
||||||
@ -26,19 +28,27 @@ pub struct ModelMatrix {
|
|||||||
impl ModelMatrix {
|
impl ModelMatrix {
|
||||||
|
|
||||||
pub fn new(renderer: &WgpuRenderer,
|
pub fn new(renderer: &WgpuRenderer,
|
||||||
pos: Position,
|
position: Position,
|
||||||
rot: f32,
|
rotation: f32,
|
||||||
scale: f32)
|
scale: f32)
|
||||||
-> Self
|
-> Self
|
||||||
{
|
{
|
||||||
|
let surface_size: Vector2<f32> = renderer.get_surface_size().into();
|
||||||
|
//A factor 2 is used since the gpu coords go from -1.0 to 1.0 while the engine coords only
|
||||||
|
//go from 0 to 1
|
||||||
|
let aspect_matrix = Matrix3::from_nonuniform_scale(2.0 / surface_size.x,
|
||||||
|
2.0 / surface_size.y)
|
||||||
|
* Matrix3::from_translation(-surface_size / 2.0);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
position: pos,
|
position,
|
||||||
rotation: Deg (rot),
|
center: Position::origin(),
|
||||||
scale,
|
rotation: Deg(rotation),
|
||||||
|
scale,
|
||||||
|
aspect_matrix,
|
||||||
|
|
||||||
uniform: Uniform::create(renderer, &renderer.matrix_layout),
|
uniform: Uniform::create(renderer, &renderer.matrix_layout),
|
||||||
is_synced: false,
|
is_synced: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,13 +56,8 @@ impl ModelMatrix {
|
|||||||
Self::new(renderer, Position::origin(), 0.0, 1.0)
|
Self::new(renderer, Position::origin(), 0.0, 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_position(&mut self, pos: Position) {
|
pub fn set_position(&mut self, position: Position) {
|
||||||
self.position = pos;
|
self.position = position;
|
||||||
self.is_synced = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_rotation(&mut self, rot: f32) {
|
|
||||||
self.rotation = Deg (rot);
|
|
||||||
self.is_synced = false;
|
self.is_synced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,22 +66,37 @@ impl ModelMatrix {
|
|||||||
self.is_synced = false;
|
self.is_synced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_rotation(&mut self, rotation: f32) {
|
||||||
|
self.rotation = Deg(rotation);
|
||||||
|
self.is_synced = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_center(&mut self, center: Position) {
|
||||||
|
self.center = center;
|
||||||
|
self.is_synced = false;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_uniform(&mut self) -> &mut Uniform<MATRIX_SIZE> {
|
pub fn get_uniform(&mut self) -> &mut Uniform<MATRIX_SIZE> {
|
||||||
use cgmath::{Basis3, Rotation3};
|
use cgmath::{Basis3, Rotation3};
|
||||||
|
|
||||||
if self.is_synced == false {
|
if self.is_synced == false {
|
||||||
|
|
||||||
let pos_vec = Vector2 {
|
let position = Matrix3::from_translation(self.position.into());
|
||||||
x: self.position.x as f32,
|
let scale = Matrix3::from_scale(self.scale);
|
||||||
y: self.position.y as f32,
|
let center = Matrix3::from_translation((self.center * -1.0).into());
|
||||||
};
|
let rotation = Matrix3::from(Basis3::from_angle_z(self.rotation));
|
||||||
let rotation_mat = Matrix3::from(Basis3::from_angle_z(self.rotation));
|
|
||||||
let scale_mat = Matrix3::from_scale(self.scale);
|
//to be read in reverse: scale is applied first, then the center offset is applied in
|
||||||
let translation_mat = Matrix3::from_translation(pos_vec);
|
//reverse, so that it lines up with the world's origin. The rotation is then applied
|
||||||
let aspect_mat = Matrix3::from_nonuniform_scale(1.0/1280.0, 1.0/720.0);
|
//around the world's origin (and ence, the center). FInally, the translation is applied
|
||||||
|
//before the aspect matrix can move use from window-space (w x h) to normalized space
|
||||||
|
//(-1.0 to 1.0)
|
||||||
|
let matrix = self.aspect_matrix * position * rotation * center * scale;
|
||||||
|
|
||||||
let matrix = aspect_mat * translation_mat * rotation_mat * scale_mat;
|
|
||||||
let mat_bytes: [u8; 36] = bytemuck::bytes_of(&matrix).try_into().unwrap();
|
let mat_bytes: [u8; 36] = bytemuck::bytes_of(&matrix).try_into().unwrap();
|
||||||
|
|
||||||
|
//wgsl uses 16 bytes-aligned matrixes, but ours is 12 bytes-aligned. Fix that by adding
|
||||||
|
//4 paddings bytes after each 12 bytes
|
||||||
let mut bytes = [0; 48];
|
let mut bytes = [0; 48];
|
||||||
for i in 0..12 {
|
for i in 0..12 {
|
||||||
bytes[i] = mat_bytes[i];
|
bytes[i] = mat_bytes[i];
|
||||||
|
|||||||
@ -123,7 +123,8 @@ where
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor {
|
let index_buffer = device.create_buffer(
|
||||||
|
&wgpu_types::BufferDescriptor {
|
||||||
label: Some("Index Buffer"),
|
label: Some("Index Buffer"),
|
||||||
size: (I_NB * size_of::<u16>()) as u64,
|
size: (I_NB * size_of::<u16>()) as u64,
|
||||||
usage: Usages::INDEX | Usages::COPY_DST,
|
usage: Usages::INDEX | Usages::COPY_DST,
|
||||||
|
|||||||
@ -27,19 +27,22 @@ use crate::{
|
|||||||
/// well as the unique function necessary for it to be rendered
|
/// well as the unique function necessary for it to be rendered
|
||||||
pub trait Sprite {
|
pub trait Sprite {
|
||||||
|
|
||||||
/// Set the position of the [Sprite] on the screen
|
/// Sets the position of the [`Sprite`] on the screen
|
||||||
fn set_position(&mut self, pos: Position);
|
fn set_position(&mut self, position: Position);
|
||||||
|
|
||||||
/// Set the rotation of the [Sprite] on the screen
|
/// Sets the center of the [`Sprite`] for rotations as well as translations
|
||||||
fn set_rotation(&mut self, rot: f32);
|
fn set_center(&mut self, center: Position);
|
||||||
|
|
||||||
/// Set the alpha of the [Sprite] on the screen
|
/// Sets the rotation of the [`Sprite`] on the screen
|
||||||
|
fn set_rotation(&mut self, rotation: f32);
|
||||||
|
|
||||||
|
/// Sets the alpha of the [`Sprite`] on the screen
|
||||||
fn set_alpha(&mut self, alpha: u8);
|
fn set_alpha(&mut self, alpha: u8);
|
||||||
|
|
||||||
/// Set the scale of the [Sprite] on the screen
|
/// Sets the scale of the [`Sprite`] on the screen
|
||||||
fn set_scale(&mut self, scale: f32);
|
fn set_scale(&mut self, scale: f32);
|
||||||
|
|
||||||
/// Renders the [Sprite] using the given rendering context
|
/// Renders the [`Sprite`] using the given rendering context
|
||||||
fn render(&mut self, renderer: &mut WgpuRenderer);
|
fn render(&mut self, renderer: &mut WgpuRenderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,12 +14,12 @@ struct VertexOutput {
|
|||||||
var<uniform> model_matrix: mat3x3<f32>;
|
var<uniform> model_matrix: mat3x3<f32>;
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_main(model: VertexInput) -> VertexOutput {
|
fn vs_main(vertex: VertexInput) -> VertexOutput {
|
||||||
|
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
out.tex_coords = model.tex_coords;
|
out.tex_coords = vertex.tex_coords;
|
||||||
out.clip_position
|
out.clip_position
|
||||||
= vec4<f32>(model_matrix * vec3<f32>(model.position, 1.0), 1.0);
|
= vec4<f32>(model_matrix * vec3<f32>(vertex.position, 1.0), 1.0);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,11 +35,15 @@ impl ShapeSprite {
|
|||||||
|
|
||||||
impl Sprite for ShapeSprite {
|
impl Sprite for ShapeSprite {
|
||||||
|
|
||||||
fn set_position(&mut self, _pos: Position) {
|
fn set_position(&mut self, _position: Position) {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_rotation(&mut self, _rot: f32) {
|
fn set_center(&mut self, _center: Position) {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_rotation(&mut self, _rotation: f32) {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,52 +6,174 @@ use log::{debug, error, info, trace, warn};
|
|||||||
use crate::{
|
use crate::{
|
||||||
renderer::WgpuRenderer,
|
renderer::WgpuRenderer,
|
||||||
sprite::Sprite,
|
sprite::Sprite,
|
||||||
utils::{Pixel, Position, Size},
|
utils::{Pixel, Color, Position, Size},
|
||||||
};
|
};
|
||||||
|
use super::TextureSprite;
|
||||||
|
|
||||||
//--External imports--------------------------------------------------------------------------------
|
//--External imports--------------------------------------------------------------------------------
|
||||||
|
|
||||||
use cgmath::Matrix4;
|
|
||||||
|
|
||||||
//--TextSprite struct-------------------------------------------------------------------------------
|
//--TextSprite struct-------------------------------------------------------------------------------
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct TextSprite {
|
pub struct TextSprite {
|
||||||
matrix: Matrix4<f32>,
|
texture_sprite: TextureSprite,
|
||||||
size: Size,
|
|
||||||
text: &'static str, //TODO: temporary
|
text: String,
|
||||||
|
text_size: f32,
|
||||||
|
text_color: Pixel,
|
||||||
|
|
||||||
|
font: fontdue::Font,
|
||||||
|
layout: fontdue::layout::Layout,
|
||||||
|
text_generation_needed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextSprite {
|
impl TextSprite {
|
||||||
|
|
||||||
pub fn set_text(&mut self, _text: &'static str) {
|
pub fn new(text: &'static str, size: Size, text_size: f32, renderer: &WgpuRenderer) -> Self {
|
||||||
todo!();
|
use crate::{
|
||||||
|
renderer::Texture,
|
||||||
|
texture::TextureHandle,
|
||||||
|
};
|
||||||
|
use fontdue::layout::{CoordinateSystem, Layout, TextStyle};
|
||||||
|
|
||||||
|
let font = Self::configure_font(text_size);
|
||||||
|
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
||||||
|
|
||||||
|
//configure text layout to fit sprite size, keep default settings
|
||||||
|
layout.append(&[&font], &TextStyle::new(text, text_size, 0));
|
||||||
|
|
||||||
|
//draw text to the texture
|
||||||
|
let mut raw_texture : Vec::<Pixel>
|
||||||
|
= 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
|
||||||
|
let texture = Texture::create(renderer, bytemuck::cast_slice(&raw_texture), size);
|
||||||
|
let texture_handle = TextureHandle::from_texture(texture);
|
||||||
|
|
||||||
|
let texture_sprite = TextureSprite::new(texture_handle, size, &renderer);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
texture_sprite,
|
||||||
|
text: text.to_string(),
|
||||||
|
text_size,
|
||||||
|
text_color: Color::BLACK,
|
||||||
|
font,
|
||||||
|
layout,
|
||||||
|
text_generation_needed: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_color(&mut self, _color: Pixel) {
|
pub fn set_text(&mut self, text: &str) {
|
||||||
todo!();
|
self.text = text.to_string();
|
||||||
|
self.text_generation_needed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_text_size(&mut self, text_size: f32) {
|
||||||
|
self.text_size = text_size;
|
||||||
|
self.text_generation_needed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_color(&mut self, color: Pixel) {
|
||||||
|
self.text_color = color;
|
||||||
|
self.text_generation_needed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_text(&mut self) {
|
||||||
|
|
||||||
|
self.layout.clear();
|
||||||
|
self.layout.append(&[&self.font],
|
||||||
|
&fontdue::layout::TextStyle::new(&self.text, self.text_size, 0));
|
||||||
|
|
||||||
|
let size = self.texture_sprite.get_size();
|
||||||
|
self.texture_sprite.fill(Color::NONE);
|
||||||
|
|
||||||
|
//draw each glyph at the corresponding position on the texture
|
||||||
|
for glyph in self.layout.glyphs() {
|
||||||
|
let (_metrics, bitmap) = self.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 {
|
||||||
|
|
||||||
|
//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) {
|
||||||
|
|
||||||
|
self.texture_sprite.set_pixel(
|
||||||
|
Position {
|
||||||
|
x: (glyph_x + x) as i32,
|
||||||
|
y: (glyph_y + y) as i32,
|
||||||
|
},
|
||||||
|
self.text_color.with_alpha(bitmap[x + y * glyph.width])
|
||||||
|
);
|
||||||
|
} } } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_font(text_size: f32) -> fontdue::Font
|
||||||
|
{
|
||||||
|
let font = include_bytes!("../../assets/DejaVuSansMono.ttf");
|
||||||
|
let settings = fontdue::FontSettings {
|
||||||
|
scale: text_size,
|
||||||
|
..fontdue::FontSettings::default()
|
||||||
|
};
|
||||||
|
fontdue::Font::from_bytes(&font[..], settings).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sprite for TextSprite {
|
impl Sprite for TextSprite {
|
||||||
|
|
||||||
fn set_position(&mut self, _pos: Position) {
|
fn set_position(&mut self, position: Position) {
|
||||||
todo!();
|
self.texture_sprite.set_position(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_rotation(&mut self, _rot: f32) {
|
fn set_center(&mut self, center: Position) {
|
||||||
todo!();
|
self.texture_sprite.set_center(center);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_alpha(&mut self, _alpha: u8) {
|
fn set_rotation(&mut self, rotation: f32) {
|
||||||
todo!();
|
self.texture_sprite.set_rotation(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_scale(&mut self, _scale: f32) {
|
fn set_alpha(&mut self, alpha: u8) {
|
||||||
todo!();
|
self.texture_sprite.set_alpha(alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, _renderer: &mut WgpuRenderer) {
|
fn set_scale(&mut self, scale: f32) {
|
||||||
todo!();
|
self.texture_sprite.set_scale(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, renderer: &mut WgpuRenderer) {
|
||||||
|
|
||||||
|
// generate text if any setting changed
|
||||||
|
if self.text_generation_needed == true {
|
||||||
|
self.generate_text();
|
||||||
|
self.text_generation_needed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.texture_sprite.render(renderer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -91,11 +91,20 @@ impl TextureSprite {
|
|||||||
self.texture.for_each_in_area(func, self.offset, self.inner_size);
|
self.texture.for_each_in_area(func, self.offset, self.inner_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fill(&mut self, pixel: Pixel) {
|
||||||
|
self.texture.fill(pixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_size(&self) -> Size {
|
||||||
|
self.texture.get_size()
|
||||||
|
}
|
||||||
|
|
||||||
fn update_vertices(&mut self, renderer: &WgpuRenderer) {
|
fn update_vertices(&mut self, renderer: &WgpuRenderer) {
|
||||||
|
|
||||||
debug!("Updating vertices...");
|
debug!("Updating vertices...");
|
||||||
let size = self.texture.get_size();
|
let size = self.texture.get_size();
|
||||||
|
|
||||||
|
|
||||||
// compute normalized coordinates
|
// compute normalized coordinates
|
||||||
let x_size = self.inner_size.w as f32 / size.w as f32 * self.scale;
|
let x_size = self.inner_size.w as f32 / size.w as f32 * self.scale;
|
||||||
let y_size = self.inner_size.h as f32 / size.h as f32 * self.scale;
|
let y_size = self.inner_size.h as f32 / size.h as f32 * self.scale;
|
||||||
@ -103,8 +112,10 @@ impl TextureSprite {
|
|||||||
let y_offset = self.offset.y as f32 / size.h as f32;
|
let y_offset = self.offset.y as f32 / size.h as f32;
|
||||||
|
|
||||||
// compute mesh size
|
// compute mesh size
|
||||||
let w = self.inner_size.w as f32;
|
|
||||||
let h = self.inner_size.h as f32;
|
//divide by 2 since the quad's coords go from -0.5 to 0.5
|
||||||
|
let w = self.inner_size.w as f32 / 2.0;
|
||||||
|
let h = self.inner_size.h as f32 / 2.0;
|
||||||
|
|
||||||
let mesh = [
|
let mesh = [
|
||||||
TextureVertex {
|
TextureVertex {
|
||||||
@ -133,12 +144,16 @@ impl TextureSprite {
|
|||||||
|
|
||||||
impl Sprite for TextureSprite {
|
impl Sprite for TextureSprite {
|
||||||
|
|
||||||
fn set_position(&mut self, pos: Position) {
|
fn set_position(&mut self, position: Position) {
|
||||||
self.matrix.set_position(pos);
|
self.matrix.set_position(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_rotation(&mut self, rot: f32) {
|
fn set_center(&mut self, center: Position) {
|
||||||
self.matrix.set_rotation(rot);
|
self.matrix.set_center(center);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_rotation(&mut self, rotation: f32) {
|
||||||
|
self.matrix.set_rotation(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_alpha(&mut self, _alpha: u8) {
|
fn set_alpha(&mut self, _alpha: u8) {
|
||||||
@ -235,7 +250,7 @@ fn initialize_pipeline(renderer: &WgpuRenderer) -> wgpu::RenderPipeline {
|
|||||||
entry_point: "fs_main",
|
entry_point: "fs_main",
|
||||||
targets: &[Some(wgpu::ColorTargetState {
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
format: renderer.config.format,
|
format: renderer.config.format,
|
||||||
blend: Some(wgpu::BlendState::REPLACE),
|
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
|
||||||
write_mask: wgpu::ColorWrites::ALL,
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
})],
|
})],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -30,7 +30,7 @@ impl TextureHandle {
|
|||||||
|
|
||||||
//TODO check pos ?
|
//TODO check pos ?
|
||||||
let mut texture = self.texture.borrow_mut();
|
let mut texture = self.texture.borrow_mut();
|
||||||
let width = texture.size.w;
|
let width = texture.size.w as i32;
|
||||||
texture.buffer[(pos.x + pos.y*width) as usize] = pix;
|
texture.buffer[(pos.x + pos.y*width) as usize] = pix;
|
||||||
texture.is_synced = false;
|
texture.is_synced = false;
|
||||||
}
|
}
|
||||||
@ -51,15 +51,21 @@ impl TextureHandle {
|
|||||||
{
|
{
|
||||||
//TODO check offset and pos ?
|
//TODO check offset and pos ?
|
||||||
let mut texture = self.texture.borrow_mut();
|
let mut texture = self.texture.borrow_mut();
|
||||||
let width = texture.size.w;
|
let width = texture.size.w as i32;
|
||||||
for x in offset.x..(offset.x + size.w) {
|
for x in offset.x..(offset.x + size.w as i32) {
|
||||||
for y in offset.y..(offset.y + size.h) {
|
for y in offset.y..(offset.y + size.h as i32) {
|
||||||
func(&mut texture.buffer[(x + y*width) as usize]);
|
func(&mut texture.buffer[(x + y*width) as usize]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
texture.is_synced = false;
|
texture.is_synced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fill (&mut self, pixel: Pixel) {
|
||||||
|
let mut texture = self.texture.borrow_mut();
|
||||||
|
texture.buffer.fill(pixel);
|
||||||
|
texture.is_synced = false;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn texture(&self) -> &Rc<RefCell<Texture>> {
|
pub fn texture(&self) -> &Rc<RefCell<Texture>> {
|
||||||
//TODO improve that
|
//TODO improve that
|
||||||
&self.texture
|
&self.texture
|
||||||
@ -70,4 +76,3 @@ impl TextureHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
49
src/utils.rs
49
src/utils.rs
@ -3,13 +3,13 @@ use log::{debug, error, info, trace, warn};
|
|||||||
|
|
||||||
use winit;
|
use winit;
|
||||||
|
|
||||||
use cgmath::Vector3;
|
use cgmath::{Vector3, Vector2};
|
||||||
|
|
||||||
//--Position struct---------------------------------------------------------------------------------
|
//--Position struct---------------------------------------------------------------------------------
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Position {
|
pub struct Position {
|
||||||
pub x: u32,
|
pub x: i32,
|
||||||
pub y: u32,
|
pub y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
@ -29,6 +29,13 @@ impl From<Position> for Vector3<f32> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Position> for Vector2<f32> {
|
||||||
|
|
||||||
|
fn from(pos: Position) -> Self {
|
||||||
|
Self::new(pos.x as f32, pos.y as f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::ops::Add for Position {
|
impl std::ops::Add for Position {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
@ -40,6 +47,17 @@ impl std::ops::Add for Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<f32> for Position {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
|
Position {
|
||||||
|
x: ((self.x as f32) * rhs) as i32,
|
||||||
|
y: ((self.y as f32) * rhs) as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//--Size struct-------------------------------------------------------------------------------------
|
//--Size struct-------------------------------------------------------------------------------------
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
@ -79,6 +97,13 @@ impl From<winit::dpi::PhysicalSize<u32>> for Size {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Size> for Vector2<f32> {
|
||||||
|
|
||||||
|
fn from(size: Size) -> Self {
|
||||||
|
Self::new(size.w as f32, size.h as f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//--Pixel struct------------------------------------------------------------------------------------
|
//--Pixel struct------------------------------------------------------------------------------------
|
||||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -105,6 +130,16 @@ impl Pixel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
pub fn to_rgba(&self) -> Rgba {
|
||||||
unsafe { self.rgba }
|
unsafe { self.rgba }
|
||||||
}
|
}
|
||||||
@ -117,10 +152,10 @@ pub struct Color;
|
|||||||
impl Color {
|
impl Color {
|
||||||
pub const NONE: Pixel = Pixel {flat: 0x00000000};
|
pub const NONE: Pixel = Pixel {flat: 0x00000000};
|
||||||
pub const WHITE: Pixel = Pixel {flat: 0xffffffff};
|
pub const WHITE: Pixel = Pixel {flat: 0xffffffff};
|
||||||
pub const BLACK: Pixel = Pixel {flat: 0x000000ff};
|
pub const BLACK: Pixel = Pixel {flat: 0xff000000};
|
||||||
pub const RED: Pixel = Pixel {flat: 0xff0000ff};
|
pub const RED: Pixel = Pixel {flat: 0xff0000ff};
|
||||||
pub const GREEN: Pixel = Pixel {flat: 0x00ff00ff};
|
pub const GREEN: Pixel = Pixel {flat: 0xff00ff00};
|
||||||
pub const BLUE: Pixel = Pixel {flat: 0x0000ffff};
|
pub const BLUE: Pixel = Pixel {flat: 0xffff0000};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user