From 330a93fac81dfdd0d2de2d0a53d36f0b586b004d Mon Sep 17 00:00:00 2001 From: Steins7 Date: Thu, 18 Aug 2022 22:07:33 +0200 Subject: [PATCH] Implemented more of TextureSprite + added texture modification functions + added texture offset support --- src/canvas.rs | 4 +- src/lib.rs | 5 +- src/main.rs | 28 ++++++++++++ src/renderer.rs | 119 +++++++++++++++++++++++++++++++----------------- src/sprite.rs | 79 ++++++++++++++++++++++++++------ src/texture.rs | 52 ++++++++++++++------- src/utils.rs | 15 +++++- 7 files changed, 224 insertions(+), 78 deletions(-) diff --git a/src/canvas.rs b/src/canvas.rs index 8683d81..e3095b5 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -69,8 +69,8 @@ impl Canvas { } //--Create functions-- - pub fn create_texture_sprite(&mut self, _size: Size) -> TextureSprite { - TextureSprite::create(self.default_texture.clone()) + pub fn create_texture_sprite(&mut self, size: Size) -> TextureSprite { + TextureSprite::create(self.default_texture.clone(), size) } pub fn create_text_sprite(&mut self, _size: Size, _scale: f32) -> TextSprite { diff --git a/src/lib.rs b/src/lib.rs index 63b0111..749bec6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,10 @@ mod renderer; use utils::Size; -pub fn run_canvas>(title: &'static str, size: Size, mut app: A) -> ! { +pub fn run_canvas>(title: &'static str, + size: Size, mut app: A) + -> ! +{ use winit::{ event_loop::EventLoop, window::WindowBuilder, diff --git a/src/main.rs b/src/main.rs index a2c7135..bd11462 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + use canvas::{ Application, Canvas, @@ -8,6 +11,7 @@ use canvas::{ use std::{ rc::Rc, cell::RefCell, + time::Instant, }; fn setup_logger() -> Result<(), fern::InitError> { @@ -39,6 +43,9 @@ fn setup_logger() -> Result<(), fern::InitError> { struct ExampleState { pub texture: Texture, pub tex_sprite: TextureSprite, + pub sub_sprite: TextureSprite, + pub last_instant: Instant, + pub last_offset: u32, } struct ExampleApp {} @@ -55,13 +62,21 @@ impl Application for ExampleApp { .unwrap(); let mut tex_sprite = canvas.create_texture_sprite(Size {w: 20, h: 20}); tex_sprite.set_texture(texture.clone(), Some(Position {x: 0, y: 0})); + + let mut sub_sprite = canvas.create_texture_sprite(Size {w: 350, h: 427}); + sub_sprite.set_texture(texture.clone(), Some(Position {x: 350, y: 0})); canvas.clear(); canvas.update(); + let last_instant = Instant::now(); + Ok(ExampleState { texture, tex_sprite, + sub_sprite, + last_instant, + last_offset: 0, }) } @@ -73,6 +88,17 @@ impl Application for ExampleApp { shape::{Shape, Rectangle}, }; + let now = Instant::now(); + let elapsed = now.duration_since(state.last_instant).as_millis(); + state.last_instant = now; + debug!("frame time: {}ms", elapsed); + + //state.sub_sprite.for_each(|pix| unsafe {pix.flat = pix.flat.wrapping_add(1);}); + + state.last_offset += 1; + state.sub_sprite.set_texture(state.texture.clone(), + Some(Position {x: state.last_offset, y: 0})); + // inputs //if canvas.key_pressed(Key::A) { // unimplemented!(); @@ -126,8 +152,10 @@ impl Application for ExampleApp { //txt_sprite.set_alpha(255); //txt_sprite.set_scale(0.5); //canvas.draw(&txt_sprite); + canvas.draw(&state.tex_sprite); + canvas.draw(&state.sub_sprite); canvas.update(); Ok(()) diff --git a/src/renderer.rs b/src/renderer.rs index 657e081..a3bd213 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -208,9 +208,40 @@ impl WgpuRenderer { multiview: None, }) }; - - let texture_quad = GpuMesh::create(&device, &TEXTURE_QUAD); - let quad_mesh = GpuMesh::create(&device, &QUAD); + + let texture_quad = { + let mesh = Mesh { + vertices: vec![ + TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] }, + TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] }, + TextureVertex { position: [1.0, 1.0], tex_coords: [1.0, 0.0] }, + TextureVertex { position: [0.0, 1.0], tex_coords: [0.0, 0.0] }, + ], + indices: vec![ + 0, 1, 2, + 0, 2, 3, + ], + }; + + GpuMesh::create(&device, &mesh) + }; + + let quad_mesh = { + let mesh = Mesh { + vertices: vec![ + ColorVertex { position: [0.0, 0.0], color: [1.0, 0.0, 0.0] }, + ColorVertex { position: [1.0, 0.0], color: [0.0, 1.0, 0.0] }, + ColorVertex { position: [1.0, 1.0], color: [0.0, 0.0, 1.0] }, + ColorVertex { position: [0.0, 1.0], color: [0.5, 0.5, 0.5] }, + ], + indices: vec![ + 0, 1, 2, + 0, 2, 3, + ], + }; + + GpuMesh::create(&device, &mesh) + }; let output = Some(surface.get_current_texture() .map_err(|_| "Failed to create SurfaceTexture")?); @@ -250,14 +281,18 @@ impl WgpuRenderer { let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); match sprite.render_data() { - RenderData::Texture (texture) => { + RenderData::Texture ((mesh, texture)) => { let mut texture = texture.borrow_mut(); - if texture.is_synced() == false { + if texture.is_synced == false { texture.update(&mut self.queue); } - let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + //TODO avoid recreating buffers every time + let gpu_mesh = GpuMesh::create(&self.device, &mesh); + + let mut encoder = self.device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Render Encoder"), }); @@ -267,18 +302,16 @@ impl WgpuRenderer { view: &view, resolve_target: None, ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: true, - }, + load: wgpu::LoadOp::Load, store: true, }, })], depth_stencil_attachment: None, }); render_pass.set_pipeline(&self.texture_render_pipeline); - render_pass.set_vertex_buffer(0, self.texture_quad.vertex_buffer.slice(..)); - render_pass.set_index_buffer(self.texture_quad.index_buffer.slice(..), + render_pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..)); + render_pass.set_index_buffer(gpu_mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint16); - render_pass.set_bind_group(0, texture.bind_group(), &[]); + render_pass.set_bind_group(0, &texture.bind_group, &[]); render_pass.draw_indexed(0..self.texture_quad.index_number, 0, 0..1); drop(render_pass); @@ -372,7 +405,7 @@ impl GpuMesh { let vertex_buffer = device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(mesh.vertices), + contents: bytemuck::cast_slice(&mesh.vertices), usage: wgpu::BufferUsages::VERTEX, } ); @@ -380,7 +413,7 @@ impl GpuMesh { let index_buffer = device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Index Buffer"), - contents: bytemuck::cast_slice(mesh.indices), + contents: bytemuck::cast_slice(&mesh.indices), usage: wgpu::BufferUsages::INDEX, } ); @@ -395,7 +428,7 @@ impl GpuMesh { } pub enum RenderData<'a> { - Texture (&'a Rc>), + Texture ((&'a Mesh, &'a Rc>)), Shape (&'a GpuMesh), } @@ -442,36 +475,36 @@ impl ColorVertex { } } -pub struct Mesh<'a, V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> { - pub vertices: &'a[V], - pub indices: &'a[u16], +pub struct Mesh { + pub vertices: Vec, + pub indices: Vec, } -pub const TEXTURE_QUAD: Mesh<'static, TextureVertex> = Mesh { - vertices: &[ - TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] }, - TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] }, - TextureVertex { position: [1.0, 1.0], tex_coords: [1.0, 0.0] }, - TextureVertex { position: [0.0, 1.0], tex_coords: [0.0, 0.0] }, - ], - indices: &[ - 0, 1, 2, - 0, 2, 3, - ], -}; - -const QUAD: Mesh<'static, ColorVertex> = Mesh { - vertices: &[ - ColorVertex { position: [0.0, 0.0], color: [1.0, 0.0, 0.0] }, - ColorVertex { position: [1.0, 0.0], color: [0.0, 1.0, 0.0] }, - ColorVertex { position: [1.0, 1.0], color: [0.0, 0.0, 1.0] }, - ColorVertex { position: [0.0, 1.0], color: [0.5, 0.5, 0.5] }, - ], - indices: &[ - 0, 1, 2, - 0, 2, 3, - ], -}; +//pub const TEXTURE_QUAD: Mesh = Mesh { +// vertices: [ +// TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] }, +// TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] }, +// TextureVertex { position: [1.0, 1.0], tex_coords: [1.0, 0.0] }, +// TextureVertex { position: [0.0, 1.0], tex_coords: [0.0, 0.0] }, +// ], +// indices: [ +// 0, 1, 2, +// 0, 2, 3, +// ], +//}; +// +//const QUAD: Mesh = Mesh { +// vertices: [ +// ColorVertex { position: [0.0, 0.0], color: [1.0, 0.0, 0.0] }, +// ColorVertex { position: [1.0, 0.0], color: [0.0, 1.0, 0.0] }, +// ColorVertex { position: [1.0, 1.0], color: [0.0, 0.0, 1.0] }, +// ColorVertex { position: [0.0, 1.0], color: [0.5, 0.5, 0.5] }, +// ], +// indices: [ +// 0, 1, 2, +// 0, 2, 3, +// ], +//}; impl From for wgpu::Color { diff --git a/src/sprite.rs b/src/sprite.rs index 5c50470..d12d155 100644 --- a/src/sprite.rs +++ b/src/sprite.rs @@ -2,7 +2,7 @@ use log::{debug, error, info, trace, warn}; use crate::{ - renderer::{GpuMesh, RenderData}, + renderer::{Mesh, RenderData, TextureVertex}, shape::Shape, texture::Texture, utils::{Pixel, Position, Size}, @@ -31,38 +31,91 @@ pub struct TextureSprite { position: Position, scale: f32, matrix: Matrix4, + mesh: Mesh, texture: Texture, + texture_offset: Position, + texture_size: Size, } impl TextureSprite { - pub fn create(texture: Texture) -> Self { + pub fn create(texture: Texture, size: Size) -> Self { - let position = Position::default(); + let position = Position::origin(); let scale = 1.0; let matrix = Matrix4::from_scale(scale) + Matrix4::from_translation(position.into()); + let mesh = Mesh { + vertices: vec![ + TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] }, + TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] }, + TextureVertex { position: [1.0, 1.0], tex_coords: [1.0, 0.0] }, + TextureVertex { position: [0.0, 1.0], tex_coords: [0.0, 0.0] }, + ], + indices: vec![ + 0, 1, 2, + 0, 2, 3, + ], + }; + Self { position, scale, matrix, + mesh, texture, + texture_offset: position, + texture_size: size, } } - pub fn set_texture(&mut self, texture: Texture, _offset: Option) { + pub fn set_texture(&mut self, texture: Texture, offset: Option) { + + // update texture self.texture = texture; + let size = self.texture.get_size(); + + // compute normalized coordinates + self.texture_offset = offset.unwrap_or(Position::origin()); + let x_size = self.texture_size.w as f32 / size.w as f32; + let y_size = self.texture_size.h as f32 / size.h as f32; + let x_offset = self.texture_offset.x as f32 / size.w as f32; + let y_offset = self.texture_offset.y as f32 / size.h as f32; + + // generate new sprite mesh + self.mesh = Mesh { + vertices: vec![ + TextureVertex { + position: [0.0, 0.0], + tex_coords: [x_offset , y_offset + y_size], + }, + TextureVertex { + position: [1.0, 0.0], + tex_coords: [x_offset + x_size, y_offset + y_size], + }, + TextureVertex { + position: [1.0, 1.0], + tex_coords: [x_offset + x_size, y_offset ], + }, + TextureVertex { + position: [0.0, 1.0], + tex_coords: [x_offset , y_offset ], + }, + ], + indices: vec![ + 0, 1, 2, + 0, 2, 3, + ], + }; } - pub fn set_pixel(&mut self, _pos: Position, _pix: Pixel) { - todo!(); + pub fn set_pixel(&mut self, pos: Position, pix: Pixel) { + //TODO check pos ? + self.texture.set_pixel(self.texture_offset + pos, pix); } - pub fn iter(&self) -> Iter<'_, Pixel> { - todo!(); - } - - pub fn iter_mut(&mut self) -> IterMut<'_, Pixel> { - todo!(); + pub fn for_each(&mut self, func: F) { + //TODO check pos ? + self.texture.for_each_in_area(func, self.texture_offset, self.texture_size); } } @@ -85,7 +138,7 @@ impl Sprite for TextureSprite { } fn render_data(&self) -> RenderData { - RenderData::Texture (&self.texture.gpu_texture()) + RenderData::Texture ((&self.mesh, &self.texture.gpu_texture())) } } diff --git a/src/texture.rs b/src/texture.rs index c85803f..600dfb2 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -42,9 +42,9 @@ impl GpuTexture { let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let sampler = device.create_sampler( &wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, + address_mode_u: wgpu::AddressMode::Repeat, + address_mode_v: wgpu::AddressMode::Repeat, + address_mode_w: wgpu::AddressMode::Repeat, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest, @@ -78,14 +78,6 @@ impl GpuTexture { } } - pub fn bind_group(&self) -> &wgpu::BindGroup { - &self.bind_group - } - - pub fn is_synced(&self) -> bool { - self.is_synced - } - pub fn update(&mut self, queue: &wgpu::Queue) { queue.write_texture( @@ -123,21 +115,47 @@ impl Texture { } } - pub fn set_pixel(&mut self, _pos: Position, _pix: Pixel) { - todo!(); + pub fn set_pixel(&mut self, pos: Position, pix: Pixel) { + + //TODO check pos ? + let mut gpu_texture = self.gpu_texture.borrow_mut(); + let width = gpu_texture.size.w; + gpu_texture.buffer[(pos.x + pos.y*width) as usize] = pix; + gpu_texture.is_synced = false; } - pub fn iter(&self) -> Iter<'_, Pixel> { - todo!(); + pub fn for_each(&mut self, mut func: F) { + + let mut gpu_texture = self.gpu_texture.borrow_mut(); + for pix in &mut gpu_texture.buffer { + func(pix); + } + gpu_texture.is_synced = false; } - pub fn iter_mut(&mut self) -> IterMut<'_, Pixel> { - todo!(); + pub fn for_each_in_area(&mut self, + mut func: F, + offset: Position, + size: Size) + { + //TODO check offset and pos ? + let mut gpu_texture = self.gpu_texture.borrow_mut(); + let width = gpu_texture.size.w; + for x in offset.x..(offset.x + size.w) { + for y in offset.y..(offset.y + size.h) { + func(&mut gpu_texture.buffer[(x + y*width) as usize]); + } + } + gpu_texture.is_synced = false; } pub fn gpu_texture(&self) -> &Rc> { //TODO improve that &self.gpu_texture } + + pub fn get_size(&self) -> Size { + self.gpu_texture.borrow().size + } } diff --git a/src/utils.rs b/src/utils.rs index d4d628c..4e5959e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -14,7 +14,7 @@ pub struct Position { impl Position { - pub fn default() -> Self { + pub fn origin() -> Self { Position { x: 0, y: 0, @@ -29,6 +29,17 @@ impl From for Vector3 { } } +impl std::ops::Add for Position { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Position { + x: self.x + rhs.x, + y: self.y + rhs.y, + } + } +} + //--Size struct------------------------------------------------------------------------------------- #[derive(Copy, Clone)] pub struct Size { @@ -80,7 +91,7 @@ pub struct Rgba { #[derive(Copy, Clone, bytemuck::Zeroable)] pub union Pixel { - flat: u32, + pub flat: u32, rgba: Rgba, }