Perform major code refactor
The rendering is moved from the renderer to the sprites. As a result, WgpuRenderer is now more of a rendering context while the renderer in itself is the context + the sprites
This commit is contained in:
parent
f025bef6c9
commit
d5d36cfadc
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -36,9 +36,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "approx"
|
name = "approx"
|
||||||
version = "0.4.0"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278"
|
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
@ -158,6 +158,7 @@ dependencies = [
|
|||||||
"pollster",
|
"pollster",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
|
"wgpu-core",
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
"wgpu-types",
|
"wgpu-types",
|
||||||
"winit",
|
"winit",
|
||||||
@ -190,10 +191,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "cgmath"
|
name = "cgmath"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"approx",
|
"approx",
|
||||||
|
"bytemuck",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1443,9 +1443,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-core"
|
name = "wgpu-core"
|
||||||
version = "0.13.1"
|
version = "0.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "266ca6be6004fd1b2a768023b1cb0afbf7af0cbffaba19af25c5792d44e74784"
|
checksum = "89b92788dec9d0c1bed849a1b83f01b2ee12819bf04a79c90f68e4173f7b5ba2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bit-vec",
|
"bit-vec",
|
||||||
|
|||||||
@ -6,9 +6,9 @@ use raw_window_handle::HasRawWindowHandle;
|
|||||||
use crate::{
|
use crate::{
|
||||||
io::{Key, Scroll},
|
io::{Key, Scroll},
|
||||||
sprite::{Sprite, TextureSprite, TextSprite, ShapeSprite},
|
sprite::{Sprite, TextureSprite, TextSprite, ShapeSprite},
|
||||||
texture::Texture,
|
texture::TextureHandle,
|
||||||
utils::{Size, Pixel, Position, Color},
|
utils::{Size, Pixel, Position, Color},
|
||||||
renderer::WgpuRenderer,
|
renderer::{WgpuRenderer, Texture},
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@ -16,6 +16,7 @@ use std::{
|
|||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//--Application trait-------------------------------------------------------------------------------
|
//--Application trait-------------------------------------------------------------------------------
|
||||||
pub trait Application<S> {
|
pub trait Application<S> {
|
||||||
|
|
||||||
@ -28,13 +29,13 @@ pub trait Application<S> {
|
|||||||
pub struct Canvas {
|
pub struct Canvas {
|
||||||
renderer: WgpuRenderer,
|
renderer: WgpuRenderer,
|
||||||
clear_color: Pixel,
|
clear_color: Pixel,
|
||||||
default_texture: Texture,
|
default_texture: TextureHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Canvas {
|
impl Canvas {
|
||||||
|
|
||||||
pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size)
|
pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size)
|
||||||
-> Result<Canvas, &'static str>
|
-> Result<Canvas, &'static str>
|
||||||
{
|
{
|
||||||
let mut renderer = WgpuRenderer::create(window, size).await?;
|
let mut renderer = WgpuRenderer::create(window, size).await?;
|
||||||
renderer.clear(Color::WHITE);
|
renderer.clear(Color::WHITE);
|
||||||
@ -58,7 +59,8 @@ impl Canvas {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Texture::create(renderer.create_gpu_texture(&rgba, img_size))
|
let texture = Texture::create(&renderer, &rgba, img_size);
|
||||||
|
TextureHandle::from_texture(texture)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -71,10 +73,10 @@ impl Canvas {
|
|||||||
//--Create functions--
|
//--Create functions--
|
||||||
pub fn create_texture_sprite(&mut self, size: Size) -> TextureSprite {
|
pub fn create_texture_sprite(&mut self, size: Size) -> TextureSprite {
|
||||||
TextureSprite::create(
|
TextureSprite::create(
|
||||||
self.default_texture.clone(),
|
self.default_texture.clone(),
|
||||||
size,
|
size,
|
||||||
|
&self.renderer,
|
||||||
self.renderer.create_texture_mesh(),
|
self.renderer.create_texture_mesh(),
|
||||||
self.renderer.create_matrix_uniform(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,18 +88,18 @@ impl Canvas {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_texture(&mut self, _size: Size, _background: Option<Pixel>)
|
pub fn create_texture(&mut self, _size: Size, _background: Option<Pixel>)
|
||||||
-> Rc<RefCell<Texture>>
|
-> Rc<RefCell<Texture>>
|
||||||
{
|
{
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_texture_from_file(&mut self,
|
pub fn create_texture_from_file(&mut self,
|
||||||
file: &'static str,
|
file: &'static str,
|
||||||
_offset: Option<Position>,
|
_offset: Option<Position>,
|
||||||
_size: Option<Size>,
|
_size: Option<Size>,
|
||||||
_background: Option<Pixel>)
|
_background: Option<Pixel>)
|
||||||
-> Result<Texture, &'static str>
|
-> Result<TextureHandle, &'static str>
|
||||||
{
|
{
|
||||||
use image::{
|
use image::{
|
||||||
io::Reader,
|
io::Reader,
|
||||||
@ -117,7 +119,8 @@ impl Canvas {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Texture::create(self.renderer.create_gpu_texture(&rgba, img_size)))
|
let texture = Texture::create(&self.renderer, &rgba, img_size);
|
||||||
|
Ok(TextureHandle::from_texture(texture))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_texture_from_bytes(&mut self,
|
pub fn create_texture_from_bytes(&mut self,
|
||||||
@ -137,7 +140,7 @@ impl Canvas {
|
|||||||
pub fn draw<S: Sprite>(&mut self, sprite: &mut S) {
|
pub fn draw<S: Sprite>(&mut self, sprite: &mut S) {
|
||||||
|
|
||||||
//update texture
|
//update texture
|
||||||
self.renderer.render(sprite);
|
sprite.render(&mut self.renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_clear_color(&mut self, color: Pixel) {
|
pub fn set_clear_color(&mut self, color: Pixel) {
|
||||||
|
|||||||
@ -15,8 +15,8 @@ mod renderer;
|
|||||||
|
|
||||||
use utils::Size;
|
use utils::Size;
|
||||||
|
|
||||||
pub fn run_canvas<S, A>(title: &'static str, size: Size, _: A) -> !
|
pub fn run_canvas<S, A>(title: &'static str, size: Size, _: A) -> !
|
||||||
where
|
where
|
||||||
S: 'static + Sized,
|
S: 'static + Sized,
|
||||||
A: 'static + Application<S>,
|
A: 'static + Application<S>,
|
||||||
{
|
{
|
||||||
@ -77,7 +77,7 @@ where
|
|||||||
canvas.clear();
|
canvas.clear();
|
||||||
let _ = canvas.update();
|
let _ = canvas.update();
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => ()
|
_ => ()
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
32
src/main.rs
32
src/main.rs
@ -2,15 +2,15 @@
|
|||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
use canvas::{
|
use canvas::{
|
||||||
Application,
|
Application,
|
||||||
Canvas,
|
Canvas,
|
||||||
texture::Texture,
|
texture::TextureHandle,
|
||||||
sprite::TextureSprite,
|
sprite::TextureSprite,
|
||||||
utils::Position,
|
utils::Position,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
time::Instant,
|
time::Instant, arch::x86_64::_mm_sub_epi8,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn setup_logger() -> Result<(), fern::InitError> {
|
fn setup_logger() -> Result<(), fern::InitError> {
|
||||||
@ -32,15 +32,14 @@ fn setup_logger() -> Result<(), fern::InitError> {
|
|||||||
message
|
message
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.level(log::LevelFilter::Debug)
|
.level(log::LevelFilter::Info)
|
||||||
.chain(std::io::stdout())
|
.chain(std::io::stdout())
|
||||||
.chain(fern::log_file("output.log")?)
|
.chain(fern::log_file("output.log")?)
|
||||||
.apply()?;
|
.apply()?; Ok(())
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExampleState {
|
struct ExampleState {
|
||||||
pub texture: Texture,
|
pub texture: TextureHandle,
|
||||||
pub tex_sprite: TextureSprite,
|
pub tex_sprite: TextureSprite,
|
||||||
pub sub_sprite: TextureSprite,
|
pub sub_sprite: TextureSprite,
|
||||||
pub last_instant: Instant,
|
pub last_instant: Instant,
|
||||||
@ -54,16 +53,14 @@ 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::{
|
use canvas::utils::Size;
|
||||||
utils::{Size},
|
|
||||||
};
|
|
||||||
|
|
||||||
//// 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);
|
||||||
|
|
||||||
let mut sub_sprite = canvas.create_texture_sprite(Size {w: 200, h: 200});
|
let mut sub_sprite = canvas.create_texture_sprite(Size {w: 200, h: 200});
|
||||||
sub_sprite.set_texture(texture.clone(), Some(Position {x: 350, y: 0}), 1.0);
|
sub_sprite.set_texture(texture.clone(), Some(Position {x: 350, y: 0}), 1.0);
|
||||||
|
|
||||||
@ -84,9 +81,7 @@ impl Application<ExampleState> for ExampleApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn tick(state: &mut ExampleState, canvas: &mut Canvas) -> Result<(), &'static str> {
|
fn tick(state: &mut ExampleState, canvas: &mut Canvas) -> Result<(), &'static str> {
|
||||||
use canvas::{
|
use canvas::sprite::Sprite;
|
||||||
sprite::Sprite,
|
|
||||||
};
|
|
||||||
|
|
||||||
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();
|
||||||
@ -99,10 +94,11 @@ impl Application<ExampleState> for ExampleApp {
|
|||||||
state.last_pos.y += 1;
|
state.last_pos.y += 1;
|
||||||
state.last_rot += 1.0;
|
state.last_rot += 1.0;
|
||||||
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_sprite.set_rotation(state.last_rot);
|
state.sub_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
|
||||||
@ -136,7 +132,7 @@ impl Application<ExampleState> for ExampleApp {
|
|||||||
//bas_sprite.set_color(Color::WHITE);
|
//bas_sprite.set_color(Color::WHITE);
|
||||||
//canvas.draw(&bas_sprite);
|
//canvas.draw(&bas_sprite);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//// scaled sprite of a manually drawed texture
|
//// scaled sprite of a manually drawed texture
|
||||||
//let texture = canvas.create_texture(Size {w: 20, h: 20}, Some(Color::WHITE));
|
//let texture = canvas.create_texture(Size {w: 20, h: 20}, Some(Color::WHITE));
|
||||||
@ -158,8 +154,8 @@ impl Application<ExampleState> for ExampleApp {
|
|||||||
//txt_sprite.set_alpha(255);
|
//txt_sprite.set_alpha(255);
|
||||||
//txt_sprite.set_scale(0.5);
|
//txt_sprite.set_scale(0.5);
|
||||||
//canvas.draw(&txt_sprite);
|
//canvas.draw(&txt_sprite);
|
||||||
|
|
||||||
|
|
||||||
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.update();
|
canvas.update();
|
||||||
|
|||||||
395
src/renderer.rs
395
src/renderer.rs
@ -5,30 +5,33 @@ use raw_window_handle::HasRawWindowHandle;
|
|||||||
use wgpu;
|
use wgpu;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
sprite::{Sprite, MATRIX_SIZE},
|
|
||||||
texture::{GpuTexture},
|
|
||||||
utils::{Pixel, Size},
|
utils::{Pixel, Size},
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
pub mod utils;
|
||||||
rc::Rc,
|
use utils::{TextureVertex, Mesh, GpuMesh};
|
||||||
cell::RefCell,
|
|
||||||
};
|
mod texture;
|
||||||
|
pub use texture::Texture as Texture;
|
||||||
|
|
||||||
|
mod uniform;
|
||||||
|
pub use uniform::Uniform as Uniform;
|
||||||
|
|
||||||
|
mod matrix;
|
||||||
|
pub use matrix::ModelMatrix as ModelMatrix;
|
||||||
|
|
||||||
//--Renderer struct---------------------------------------------------------------------------------
|
//--Renderer struct---------------------------------------------------------------------------------
|
||||||
pub struct WgpuRenderer {
|
pub struct WgpuRenderer {
|
||||||
surface: wgpu::Surface,
|
surface: wgpu::Surface,
|
||||||
device: wgpu::Device,
|
pub device: wgpu::Device,
|
||||||
queue: wgpu::Queue,
|
pub queue: wgpu::Queue,
|
||||||
config: wgpu::SurfaceConfiguration,
|
pub config: wgpu::SurfaceConfiguration,
|
||||||
|
|
||||||
surface_size: Size,
|
surface_size: Size,
|
||||||
output: Option<wgpu::SurfaceTexture>,
|
pub output: Option<wgpu::SurfaceTexture>,
|
||||||
|
|
||||||
matrix_bind_group_layout: wgpu::BindGroupLayout,
|
pub matrix_layout: wgpu::BindGroupLayout,
|
||||||
|
pub texture_layout: wgpu::BindGroupLayout,
|
||||||
texture_bind_group_layout: wgpu::BindGroupLayout,
|
|
||||||
texture_render_pipeline: wgpu::RenderPipeline,
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
shape_render_pipeline: wgpu::RenderPipeline,
|
shape_render_pipeline: wgpu::RenderPipeline,
|
||||||
@ -38,8 +41,8 @@ pub struct WgpuRenderer {
|
|||||||
|
|
||||||
impl WgpuRenderer {
|
impl WgpuRenderer {
|
||||||
|
|
||||||
pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size)
|
pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size)
|
||||||
-> Result<Self, &'static str>
|
-> Result<Self, &'static str>
|
||||||
{
|
{
|
||||||
if size.w == 0 || size.h == 0 {
|
if size.w == 0 || size.h == 0 {
|
||||||
return Err("window has zero as at least one of its dimensions");
|
return Err("window has zero as at least one of its dimensions");
|
||||||
@ -92,118 +95,20 @@ impl WgpuRenderer {
|
|||||||
let output = Some(surface.get_current_texture()
|
let output = Some(surface.get_current_texture()
|
||||||
.map_err(|_| "Failed to create SurfaceTexture")?);
|
.map_err(|_| "Failed to create SurfaceTexture")?);
|
||||||
|
|
||||||
let matrix_bind_group_layout =
|
let matrix_layout = utils::new_matrix_layout(&device);
|
||||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
let texture_layout = utils::new_texture_layout(&device);
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::VERTEX,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: false,
|
|
||||||
min_binding_size: None,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
label: Some("matrix_bind_group_layout"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let texture_bind_group_layout =
|
|
||||||
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,
|
|
||||||
// This should match the filterable field of the
|
|
||||||
// corresponding Texture entry above.
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
label: Some("texture_bind_group_layout"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let texture_render_pipeline = {
|
|
||||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
||||||
label: Some("texture shader"),
|
|
||||||
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/texture.wgsl").into()),
|
|
||||||
});
|
|
||||||
|
|
||||||
let render_pipeline_layout =
|
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: Some("texture render pipeline layout"),
|
|
||||||
bind_group_layouts: &[
|
|
||||||
&matrix_bind_group_layout,
|
|
||||||
&texture_bind_group_layout,
|
|
||||||
],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: Some("texture render pipeline"),
|
|
||||||
layout: Some(&render_pipeline_layout),
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: &shader,
|
|
||||||
entry_point: "vs_main",
|
|
||||||
buffers: &[
|
|
||||||
TextureVertex::desc(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: &shader,
|
|
||||||
entry_point: "fs_main",
|
|
||||||
targets: &[Some(wgpu::ColorTargetState {
|
|
||||||
format: config.format,
|
|
||||||
blend: Some(wgpu::BlendState::REPLACE),
|
|
||||||
write_mask: wgpu::ColorWrites::ALL,
|
|
||||||
})],
|
|
||||||
}),
|
|
||||||
primitive: wgpu::PrimitiveState {
|
|
||||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
|
||||||
strip_index_format: None,
|
|
||||||
front_face: wgpu::FrontFace::Ccw,
|
|
||||||
cull_mode: None,//Some(wgpu::Face::Back),
|
|
||||||
// Setting this to anything other than Fill requires
|
|
||||||
// Features::NON_FILL_POLYGON_MODE
|
|
||||||
polygon_mode: wgpu::PolygonMode::Fill,
|
|
||||||
// Requires Features::DEPTH_CLIP_CONTROL
|
|
||||||
unclipped_depth: false,
|
|
||||||
// Requires Features::CONSERVATIVE_RASTERIZATION
|
|
||||||
conservative: false,
|
|
||||||
},
|
|
||||||
depth_stencil: None,
|
|
||||||
// multisampling, we don't need it
|
|
||||||
multisample: wgpu::MultisampleState {
|
|
||||||
count: 1,
|
|
||||||
mask: !0,
|
|
||||||
alpha_to_coverage_enabled: false,
|
|
||||||
},
|
|
||||||
multiview: None,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let shape_render_pipeline = {
|
let shape_render_pipeline = {
|
||||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: Some("shape shader"),
|
label: Some("shape shader"),
|
||||||
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/shape.wgsl").into()),
|
source: wgpu::ShaderSource::Wgsl(include_str!("sprite/shaders/shape.wgsl").into()),
|
||||||
});
|
});
|
||||||
|
|
||||||
let render_pipeline_layout =
|
let render_pipeline_layout =
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("shape render pipeline layout"),
|
label: Some("shape render pipeline layout"),
|
||||||
bind_group_layouts: &[
|
bind_group_layouts: &[
|
||||||
&matrix_bind_group_layout,
|
&matrix_layout,
|
||||||
],
|
],
|
||||||
push_constant_ranges: &[],
|
push_constant_ranges: &[],
|
||||||
});
|
});
|
||||||
@ -230,24 +135,24 @@ impl WgpuRenderer {
|
|||||||
primitive: wgpu::PrimitiveState {
|
primitive: wgpu::PrimitiveState {
|
||||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
strip_index_format: None,
|
strip_index_format: None,
|
||||||
front_face: wgpu::FrontFace::Ccw,
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
cull_mode: Some(wgpu::Face::Back),
|
cull_mode: Some(wgpu::Face::Back),
|
||||||
// Setting this to anything other than Fill requires
|
// Setting this to anything other than Fill requires
|
||||||
// Features::NON_FILL_POLYGON_MODE
|
// Features::NON_FILL_POLYGON_MODE
|
||||||
polygon_mode: wgpu::PolygonMode::Fill,
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
// Requires Features::DEPTH_CLIP_CONTROL
|
// Requires Features::DEPTH_CLIP_CONTROL
|
||||||
unclipped_depth: false,
|
unclipped_depth: false,
|
||||||
// Requires Features::CONSERVATIVE_RASTERIZATION
|
// Requires Features::CONSERVATIVE_RASTERIZATION
|
||||||
conservative: false,
|
conservative: false,
|
||||||
},
|
},
|
||||||
depth_stencil: None,
|
depth_stencil: None,
|
||||||
// multisampling, we don't need it
|
// multisampling, we don't need it
|
||||||
multisample: wgpu::MultisampleState {
|
multisample: wgpu::MultisampleState {
|
||||||
count: 1,
|
count: 1,
|
||||||
mask: !0,
|
mask: !0,
|
||||||
alpha_to_coverage_enabled: false,
|
alpha_to_coverage_enabled: false,
|
||||||
},
|
},
|
||||||
multiview: None,
|
multiview: None,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -275,88 +180,15 @@ impl WgpuRenderer {
|
|||||||
config,
|
config,
|
||||||
surface_size,
|
surface_size,
|
||||||
output,
|
output,
|
||||||
matrix_bind_group_layout,
|
matrix_layout,
|
||||||
texture_bind_group_layout,
|
texture_layout,
|
||||||
texture_render_pipeline,
|
|
||||||
shape_render_pipeline,
|
shape_render_pipeline,
|
||||||
quad_mesh,
|
quad_mesh,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_gpu_texture(&mut self, buf: &[u8], size: Size) -> GpuTexture {
|
|
||||||
GpuTexture::create(&self.device, &self.texture_bind_group_layout, buf, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_texture_mesh(&self) -> GpuMesh<TextureVertex, 4, 6> {
|
pub fn create_texture_mesh(&self) -> GpuMesh<TextureVertex, 4, 6> {
|
||||||
GpuMesh::create(&self.device, TEXTURE_QUAD)
|
GpuMesh::create(&self.device, TEXTURE_QUAD)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_matrix_uniform(&self) -> GpuUniform<MATRIX_SIZE> {
|
|
||||||
GpuUniform::create(&self.device, &self.matrix_bind_group_layout)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render<S: Sprite>(&mut self, sprite: &mut S) {
|
|
||||||
|
|
||||||
let output = match &self.output {
|
|
||||||
Some(out) => out,
|
|
||||||
None => {
|
|
||||||
self.create_output();
|
|
||||||
&self.output.as_ref().unwrap()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
//TODO move that to output struct ?
|
|
||||||
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
|
|
||||||
match sprite.render_data() {
|
|
||||||
RenderData::Texture ((matrix, gpu_mesh, texture)) => {
|
|
||||||
let mut texture = texture.borrow_mut();
|
|
||||||
|
|
||||||
let matrix_bind_group = matrix.get_bind_group(&mut self.queue);
|
|
||||||
|
|
||||||
if texture.is_synced == false {
|
|
||||||
texture.update(&mut self.queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if gpu_mesh.is_synced() == false {
|
|
||||||
gpu_mesh.update(&mut self.queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut encoder = self.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
||||||
label: Some("Render Encoder"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: Some("Render Pass"),
|
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
||||||
view: &view,
|
|
||||||
resolve_target: None,
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Load, store: true, },
|
|
||||||
})],
|
|
||||||
depth_stencil_attachment: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
render_pass.set_pipeline(&self.texture_render_pipeline);
|
|
||||||
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, matrix_bind_group, &[]);
|
|
||||||
render_pass.set_bind_group(1, &texture.bind_group, &[]);
|
|
||||||
//render_pass.set_push_constants(
|
|
||||||
// wgpu::ShaderStages::VERTEX,
|
|
||||||
// 0,
|
|
||||||
// bytemuck::bytes_of(&(self.aspect_matrix))// * matrix.get_matrix()))
|
|
||||||
//);
|
|
||||||
render_pass.draw_indexed(0..gpu_mesh.index_number, 0, 0..1);
|
|
||||||
|
|
||||||
drop(render_pass);
|
|
||||||
self.queue.submit(std::iter::once(encoder.finish()));
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self, color: Pixel) {
|
pub fn clear(&mut self, color: Pixel) {
|
||||||
@ -396,7 +228,7 @@ impl WgpuRenderer {
|
|||||||
if size.w == 0 || size.h == 0 {
|
if size.w == 0 || size.h == 0 {
|
||||||
panic!("window has zero as at least one of its dimensions");
|
panic!("window has zero as at least one of its dimensions");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.surface_size = size;
|
self.surface_size = size;
|
||||||
//self.aspect_matrix = {
|
//self.aspect_matrix = {
|
||||||
// use cgmath::{Basis3, Rotation3, Deg};
|
// use cgmath::{Basis3, Rotation3, Deg};
|
||||||
@ -421,7 +253,7 @@ impl WgpuRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_output(&mut self) {
|
pub fn create_output(&mut self) {
|
||||||
self.output = Some(
|
self.output = Some(
|
||||||
self.surface.get_current_texture()
|
self.surface.get_current_texture()
|
||||||
.map_err(|err| match err {
|
.map_err(|err| match err {
|
||||||
@ -436,162 +268,7 @@ impl WgpuRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--GpuMesh struct----------------------------------------------------------------------------------
|
|
||||||
pub struct GpuMesh<V, const V_NB: usize, const I_NB: usize>
|
|
||||||
where
|
|
||||||
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
|
|
||||||
{
|
|
||||||
vertex_buffer: wgpu::Buffer,
|
|
||||||
index_buffer: wgpu::Buffer,
|
|
||||||
index_number: u32,
|
|
||||||
|
|
||||||
mesh: Mesh<V, V_NB, I_NB>,
|
|
||||||
is_synced: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V, const V_NB: usize, const I_NB: usize> GpuMesh<V, V_NB, I_NB>
|
|
||||||
where
|
|
||||||
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
|
|
||||||
{
|
|
||||||
|
|
||||||
pub fn create(device: &wgpu::Device, mesh: Mesh<V, V_NB, I_NB>) -> Self {
|
|
||||||
use std::mem::size_of;
|
|
||||||
use wgpu_types::BufferUsages as Usages;
|
|
||||||
|
|
||||||
let vertex_buffer = device.create_buffer(
|
|
||||||
&wgpu_types::BufferDescriptor {
|
|
||||||
label: Some("Vertex Buffer"),
|
|
||||||
size: (V_NB * size_of::<V>()) as u64,
|
|
||||||
usage: Usages::VERTEX | Usages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor {
|
|
||||||
label: Some("Index Buffer"),
|
|
||||||
size: (I_NB * size_of::<u16>()) as u64,
|
|
||||||
usage: Usages::INDEX | Usages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
vertex_buffer,
|
|
||||||
index_buffer,
|
|
||||||
index_number: mesh.indices.len() as u32,
|
|
||||||
mesh,
|
|
||||||
is_synced: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_mesh(&mut self, mesh: Mesh<V, V_NB, I_NB>) {
|
|
||||||
|
|
||||||
self.mesh = mesh;
|
|
||||||
self.is_synced = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_synced(&self) -> bool {
|
|
||||||
self.is_synced
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, queue: &wgpu::Queue) {
|
|
||||||
|
|
||||||
queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&self.mesh.vertices));
|
|
||||||
queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&self.mesh.indices));
|
|
||||||
|
|
||||||
self.is_synced = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//--GpuUniform struct-------------------------------------------------------------------------------
|
|
||||||
pub struct GpuUniform<const S: usize> {
|
|
||||||
uniform: wgpu::Buffer,
|
|
||||||
bind_group: wgpu::BindGroup,
|
|
||||||
data: [u8; S],
|
|
||||||
is_synced: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const S: usize> GpuUniform<S> {
|
|
||||||
|
|
||||||
pub fn create(device: &wgpu::Device, layout: &wgpu::BindGroupLayout) -> Self {
|
|
||||||
|
|
||||||
let uniform = device.create_buffer(
|
|
||||||
&wgpu_types::BufferDescriptor {
|
|
||||||
label: Some("Uniform Buffer"),
|
|
||||||
size: S as u64,
|
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: uniform.as_entire_binding(),
|
|
||||||
}
|
|
||||||
],
|
|
||||||
label: Some("uniform_bind_group"),
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
uniform,
|
|
||||||
bind_group,
|
|
||||||
data: [0; S],
|
|
||||||
is_synced: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, data: [u8; S]) {
|
|
||||||
|
|
||||||
self.data = data;
|
|
||||||
self.is_synced = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_bind_group(&mut self, queue: &wgpu::Queue) -> &wgpu::BindGroup {
|
|
||||||
|
|
||||||
if self.is_synced == false {
|
|
||||||
queue.write_buffer(&self.uniform, 0, &self.data);
|
|
||||||
self.is_synced = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
&self.bind_group
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub enum RenderData<'a> {
|
|
||||||
Texture ((
|
|
||||||
&'a mut GpuUniform<MATRIX_SIZE>,
|
|
||||||
&'a mut GpuMesh<TextureVertex, 4, 6>,
|
|
||||||
&'a Rc<RefCell<GpuTexture>>,
|
|
||||||
)),
|
|
||||||
Shape (&'a GpuMesh<ColorVertex, 4, 6>),
|
|
||||||
}
|
|
||||||
|
|
||||||
//--Renderer struct utils---------------------------------------------------------------------------
|
//--Renderer struct utils---------------------------------------------------------------------------
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
pub struct TextureVertex {
|
|
||||||
pub position: [f32; 2],
|
|
||||||
pub tex_coords: [f32; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextureVertex {
|
|
||||||
|
|
||||||
const ATTRIBS: [wgpu::VertexAttribute; 2] =
|
|
||||||
wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2];
|
|
||||||
|
|
||||||
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
|
||||||
wgpu::VertexBufferLayout {
|
|
||||||
array_stride: std::mem::size_of::<TextureVertex>() as wgpu::BufferAddress,
|
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
|
||||||
attributes: &Self::ATTRIBS,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
pub struct ColorVertex {
|
pub struct ColorVertex {
|
||||||
@ -613,14 +290,6 @@ impl ColorVertex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Mesh<V, const V_NB: usize, const I_NB: usize>
|
|
||||||
where
|
|
||||||
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
|
|
||||||
{
|
|
||||||
pub vertices: [V; V_NB],
|
|
||||||
pub indices: [u16; I_NB],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const TEXTURE_QUAD: Mesh<TextureVertex, 4, 6> = Mesh {
|
pub const TEXTURE_QUAD: Mesh<TextureVertex, 4, 6> = Mesh {
|
||||||
vertices: [
|
vertices: [
|
||||||
TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] },
|
TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] },
|
||||||
|
|||||||
97
src/renderer/matrix.rs
Normal file
97
src/renderer/matrix.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
|
use crate:: utils::Position;
|
||||||
|
|
||||||
|
use super::{Uniform, WgpuRenderer};
|
||||||
|
|
||||||
|
use cgmath::{
|
||||||
|
Deg,
|
||||||
|
Vector2,
|
||||||
|
Matrix3,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const MATRIX_SIZE: usize = 48;//std::mem::size_of::<Matrix3<f32>>();
|
||||||
|
|
||||||
|
//--ModelMatrix struct------------------------------------------------------------------------------
|
||||||
|
pub struct ModelMatrix {
|
||||||
|
position: Position,
|
||||||
|
rotation: Deg<f32>,
|
||||||
|
scale: f32,
|
||||||
|
|
||||||
|
uniform: Uniform<MATRIX_SIZE>,
|
||||||
|
is_synced: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModelMatrix {
|
||||||
|
|
||||||
|
pub fn new(renderer: &WgpuRenderer,
|
||||||
|
pos: Position,
|
||||||
|
rot: f32,
|
||||||
|
scale: f32)
|
||||||
|
-> Self
|
||||||
|
{
|
||||||
|
|
||||||
|
Self {
|
||||||
|
position: pos,
|
||||||
|
rotation: Deg (rot),
|
||||||
|
scale,
|
||||||
|
|
||||||
|
uniform: Uniform::create(renderer, &renderer.matrix_layout),
|
||||||
|
is_synced: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default(renderer: &WgpuRenderer) -> Self {
|
||||||
|
Self::new(renderer, Position::origin(), 0.0, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_position(&mut self, pos: Position) {
|
||||||
|
self.position = pos;
|
||||||
|
self.is_synced = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_rotation(&mut self, rot: f32) {
|
||||||
|
self.rotation = Deg (rot);
|
||||||
|
self.is_synced = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_scale(&mut self, scale: f32) {
|
||||||
|
self.scale = scale;
|
||||||
|
self.is_synced = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_uniform(&mut self) -> &mut Uniform<MATRIX_SIZE> {
|
||||||
|
use cgmath::{Basis3, Rotation3};
|
||||||
|
|
||||||
|
if self.is_synced == false {
|
||||||
|
|
||||||
|
let pos_vec = Vector2 {
|
||||||
|
x: self.position.x as f32,
|
||||||
|
y: self.position.y as f32,
|
||||||
|
};
|
||||||
|
let rotation_mat = Matrix3::from(Basis3::from_angle_z(self.rotation));
|
||||||
|
let scale_mat = Matrix3::from_scale(self.scale);
|
||||||
|
let translation_mat = Matrix3::from_translation(pos_vec);
|
||||||
|
let aspect_mat = Matrix3::from_nonuniform_scale(1.0/1280.0, 1.0/720.0);
|
||||||
|
|
||||||
|
let matrix = aspect_mat * translation_mat * rotation_mat * scale_mat;
|
||||||
|
let mat_bytes: [u8; 36] = bytemuck::bytes_of(&matrix).try_into().unwrap();
|
||||||
|
let mut bytes = [0; 48];
|
||||||
|
for i in 0..12 {
|
||||||
|
bytes[i] = mat_bytes[i];
|
||||||
|
}
|
||||||
|
for i in 0..12 {
|
||||||
|
bytes[i+16] = mat_bytes[i+12];
|
||||||
|
}
|
||||||
|
for i in 0..12 {
|
||||||
|
bytes[i+32] = mat_bytes[i+24];
|
||||||
|
}
|
||||||
|
self.uniform.update(bytes);
|
||||||
|
self.is_synced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
&mut self.uniform
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
98
src/renderer/texture.rs
Normal file
98
src/renderer/texture.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
utils::{Pixel, Size},
|
||||||
|
renderer::WgpuRenderer,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Texture {
|
||||||
|
pub texture: wgpu::Texture,
|
||||||
|
pub bind_group: wgpu::BindGroup,
|
||||||
|
|
||||||
|
pub buffer: Vec<Pixel>,
|
||||||
|
pub size: Size,
|
||||||
|
pub is_synced: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Texture {
|
||||||
|
|
||||||
|
pub fn create(renderer: &WgpuRenderer,
|
||||||
|
buffer: &[u8],
|
||||||
|
size: Size)
|
||||||
|
-> Self
|
||||||
|
{
|
||||||
|
|
||||||
|
let texture = renderer.device.create_texture(
|
||||||
|
&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: size.into(),
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
let sampler = renderer.device.create_sampler(
|
||||||
|
&wgpu::SamplerDescriptor {
|
||||||
|
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,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let bind_group = renderer.device.create_bind_group(
|
||||||
|
&wgpu::BindGroupDescriptor {
|
||||||
|
layout: &renderer.texture_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: Some("texture_bind_group"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
texture,
|
||||||
|
bind_group,
|
||||||
|
buffer: Vec::from(bytemuck::cast_slice(buffer)),
|
||||||
|
size,
|
||||||
|
is_synced: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, queue: &wgpu::Queue) {
|
||||||
|
|
||||||
|
queue.write_texture(
|
||||||
|
wgpu::ImageCopyTexture {
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
|
texture: &self.texture,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: wgpu::Origin3d::ZERO,
|
||||||
|
},
|
||||||
|
bytemuck::cast_slice(&self.buffer),
|
||||||
|
wgpu::ImageDataLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: std::num::NonZeroU32::new(4 * self.size.w),
|
||||||
|
rows_per_image: std::num::NonZeroU32::new(self.size.h),
|
||||||
|
},
|
||||||
|
self.size.into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.is_synced = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
62
src/renderer/uniform.rs
Normal file
62
src/renderer/uniform.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
|
use super::WgpuRenderer;
|
||||||
|
|
||||||
|
//--Uniform struct----------------------------------------------------------------------------------
|
||||||
|
pub struct Uniform<const S: usize> {
|
||||||
|
buffer: wgpu::Buffer,
|
||||||
|
bind_group: wgpu::BindGroup,
|
||||||
|
data: [u8; S],
|
||||||
|
is_synced: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const S: usize> Uniform<S> {
|
||||||
|
|
||||||
|
pub fn create(renderer: &WgpuRenderer, layout: &wgpu::BindGroupLayout) -> Self {
|
||||||
|
|
||||||
|
let buffer = renderer.device.create_buffer(
|
||||||
|
&wgpu_types::BufferDescriptor {
|
||||||
|
label: Some("Uniform Buffer"),
|
||||||
|
size: S as u64,
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let bind_group = renderer.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: buffer.as_entire_binding(),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: Some("uniform_bind_group"),
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
buffer,
|
||||||
|
bind_group,
|
||||||
|
data: [0; S],
|
||||||
|
is_synced: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, data: [u8; S]) {
|
||||||
|
|
||||||
|
self.data = data;
|
||||||
|
self.is_synced = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bind_group(&mut self, queue: &wgpu::Queue) -> &wgpu::BindGroup {
|
||||||
|
|
||||||
|
if self.is_synced == false {
|
||||||
|
queue.write_buffer(&self.buffer, 0, &self.data);
|
||||||
|
self.is_synced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
&self.bind_group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
164
src/renderer/utils.rs
Normal file
164
src/renderer/utils.rs
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
|
//--Bind group layouts------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Creates the layout bind_group used by all matrixes of the renderer
|
||||||
|
pub fn new_matrix_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
|
||||||
|
|
||||||
|
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: Some("matrix_bind_group_layout"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Crates the layout bind group used by all textures of the renderer
|
||||||
|
pub fn new_texture_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
|
||||||
|
|
||||||
|
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,
|
||||||
|
// This should match the filterable field of the
|
||||||
|
// corresponding Texture entry above.
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: Some("texture_bind_group_layout"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//--TextureVertex struct----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct TextureVertex {
|
||||||
|
pub position: [f32; 2],
|
||||||
|
pub tex_coords: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextureVertex {
|
||||||
|
|
||||||
|
const ATTRIBS: [wgpu::VertexAttribute; 2] =
|
||||||
|
wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2];
|
||||||
|
|
||||||
|
pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||||
|
wgpu::VertexBufferLayout {
|
||||||
|
array_stride: std::mem::size_of::<TextureVertex>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
attributes: &Self::ATTRIBS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--Mesh struct-------------------------------------------------------------------------------------
|
||||||
|
pub struct Mesh<V, const V_NB: usize, const I_NB: usize>
|
||||||
|
where
|
||||||
|
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
|
||||||
|
{
|
||||||
|
pub vertices: [V; V_NB],
|
||||||
|
pub indices: [u16; I_NB],
|
||||||
|
}
|
||||||
|
|
||||||
|
//--GpuMesh struct----------------------------------------------------------------------------------
|
||||||
|
pub struct GpuMesh<V, const V_NB: usize, const I_NB: usize>
|
||||||
|
where
|
||||||
|
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
|
||||||
|
{
|
||||||
|
vertex_buffer: wgpu::Buffer,
|
||||||
|
index_buffer: wgpu::Buffer,
|
||||||
|
index_number: u32,
|
||||||
|
|
||||||
|
mesh: Mesh<V, V_NB, I_NB>,
|
||||||
|
is_synced: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, const V_NB: usize, const I_NB: usize> GpuMesh<V, V_NB, I_NB>
|
||||||
|
where
|
||||||
|
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
|
||||||
|
{
|
||||||
|
|
||||||
|
pub fn create(device: &wgpu::Device, mesh: Mesh<V, V_NB, I_NB>) -> Self {
|
||||||
|
use std::mem::size_of;
|
||||||
|
use wgpu_types::BufferUsages as Usages;
|
||||||
|
|
||||||
|
let vertex_buffer = device.create_buffer(
|
||||||
|
&wgpu_types::BufferDescriptor {
|
||||||
|
label: Some("Vertex Buffer"),
|
||||||
|
size: (V_NB * size_of::<V>()) as u64,
|
||||||
|
usage: Usages::VERTEX | Usages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor {
|
||||||
|
label: Some("Index Buffer"),
|
||||||
|
size: (I_NB * size_of::<u16>()) as u64,
|
||||||
|
usage: Usages::INDEX | Usages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
vertex_buffer,
|
||||||
|
index_buffer,
|
||||||
|
index_number: mesh.indices.len() as u32,
|
||||||
|
mesh,
|
||||||
|
is_synced: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mesh(&mut self, mesh: Mesh<V, V_NB, I_NB>) {
|
||||||
|
|
||||||
|
self.mesh = mesh;
|
||||||
|
self.is_synced = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_synced(&self) -> bool {
|
||||||
|
self.is_synced
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, queue: &wgpu::Queue) {
|
||||||
|
|
||||||
|
queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&self.mesh.vertices));
|
||||||
|
queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&self.mesh.indices));
|
||||||
|
|
||||||
|
self.is_synced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_vertex_buffer_slice(&self) -> wgpu::BufferSlice {
|
||||||
|
self.vertex_buffer.slice(..)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_index_buffer_slice(&self) -> wgpu::BufferSlice {
|
||||||
|
self.index_buffer.slice(..)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_index_number(&self) -> u32 {
|
||||||
|
self.index_number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
232
src/sprite.rs
232
src/sprite.rs
@ -2,232 +2,24 @@
|
|||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
renderer::{Mesh, GpuMesh, RenderData, TextureVertex, GpuUniform},
|
renderer::WgpuRenderer,
|
||||||
shape::Shape,
|
shape::Shape,
|
||||||
texture::Texture,
|
|
||||||
utils::{Pixel, Position, Size},
|
utils::{Pixel, Position, Size},
|
||||||
};
|
};
|
||||||
|
|
||||||
use cgmath::{
|
use cgmath::Matrix4;
|
||||||
Deg,
|
|
||||||
Vector2,
|
mod texture_sprite;
|
||||||
Matrix3,
|
pub use texture_sprite::TextureSprite;
|
||||||
Matrix4,
|
|
||||||
};
|
|
||||||
|
|
||||||
//--Sprite trait------------------------------------------------------------------------------------
|
//--Sprite trait------------------------------------------------------------------------------------
|
||||||
pub trait Sprite {
|
pub trait Sprite {
|
||||||
|
|
||||||
fn set_position(&mut self, pos: Position);
|
fn set_position(&mut self, pos: Position);
|
||||||
fn set_rotation(&mut self, rot: f32);
|
fn set_rotation(&mut self, rot: f32);
|
||||||
fn set_alpha(&mut self, alpha: u8);
|
fn set_alpha(&mut self, alpha: u8);
|
||||||
fn set_scale(&mut self, scale: f32);
|
fn set_scale(&mut self, scale: f32);
|
||||||
fn render_data(&mut self) -> RenderData;
|
fn render(&mut self, renderer: &mut WgpuRenderer);
|
||||||
}
|
|
||||||
|
|
||||||
//--ModelMatrix struct------------------------------------------------------------------------------
|
|
||||||
pub struct ModelMatrix {
|
|
||||||
position: Position,
|
|
||||||
rotation: Deg<f32>,
|
|
||||||
scale: f32,
|
|
||||||
|
|
||||||
uniform: GpuUniform<MATRIX_SIZE>,
|
|
||||||
is_synced: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModelMatrix {
|
|
||||||
|
|
||||||
pub fn new(pos: Position,
|
|
||||||
rot: f32,
|
|
||||||
scale: f32,
|
|
||||||
uniform: GpuUniform<MATRIX_SIZE>)
|
|
||||||
-> Self
|
|
||||||
{
|
|
||||||
|
|
||||||
Self {
|
|
||||||
position: pos,
|
|
||||||
rotation: Deg (rot),
|
|
||||||
scale,
|
|
||||||
|
|
||||||
uniform,
|
|
||||||
is_synced: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default(uniform: GpuUniform<MATRIX_SIZE>) -> Self {
|
|
||||||
Self::new(Position::origin(), 0.0, 1.0, uniform)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_position(&mut self, pos: Position) {
|
|
||||||
self.position = pos;
|
|
||||||
self.is_synced = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_rotation(&mut self, rot: f32) {
|
|
||||||
self.rotation = Deg (rot);
|
|
||||||
self.is_synced = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_scale(&mut self, scale: f32) {
|
|
||||||
self.scale = scale;
|
|
||||||
self.is_synced = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_uniform(&mut self) -> &mut GpuUniform<MATRIX_SIZE> {
|
|
||||||
use cgmath::{Basis3, Rotation3};
|
|
||||||
|
|
||||||
if self.is_synced == false {
|
|
||||||
|
|
||||||
let pos_vec = Vector2 {
|
|
||||||
x: self.position.x as f32,
|
|
||||||
y: self.position.y as f32,
|
|
||||||
};
|
|
||||||
let rotation_mat = Matrix3::from(Basis3::from_angle_z(self.rotation));
|
|
||||||
let scale_mat = Matrix3::from_scale(self.scale);
|
|
||||||
let translation_mat = Matrix3::from_translation(pos_vec);
|
|
||||||
let aspect_mat = Matrix3::from_nonuniform_scale(1.0/1280.0, 1.0/720.0);
|
|
||||||
|
|
||||||
let matrix = aspect_mat * translation_mat * rotation_mat * scale_mat;
|
|
||||||
let mat_bytes: [u8; 36] = bytemuck::bytes_of(&matrix).try_into().unwrap();
|
|
||||||
let mut bytes = [0; 48];
|
|
||||||
for i in 0..12 {
|
|
||||||
bytes[i] = mat_bytes[i];
|
|
||||||
}
|
|
||||||
for i in 0..12 {
|
|
||||||
bytes[i+16] = mat_bytes[i+12];
|
|
||||||
}
|
|
||||||
for i in 0..12 {
|
|
||||||
bytes[i+32] = mat_bytes[i+24];
|
|
||||||
}
|
|
||||||
self.uniform.update(bytes);
|
|
||||||
self.is_synced = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
&mut self.uniform
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MATRIX_SIZE: usize = 48;//std::mem::size_of::<Matrix3<f32>>();
|
|
||||||
|
|
||||||
//--TextureSprite struct----------------------------------------------------------------------------
|
|
||||||
pub struct TextureSprite {
|
|
||||||
matrix: ModelMatrix,
|
|
||||||
gpu_mesh: GpuMesh<TextureVertex, 4, 6>,
|
|
||||||
inner_size: Size, //TODO move to f32
|
|
||||||
|
|
||||||
texture: Texture,
|
|
||||||
texture_offset: Position,
|
|
||||||
texture_scale: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextureSprite {
|
|
||||||
|
|
||||||
pub fn create(texture: Texture,
|
|
||||||
size: Size,
|
|
||||||
gpu_mesh: GpuMesh<TextureVertex, 4, 6>,
|
|
||||||
matrix_uniform: GpuUniform<MATRIX_SIZE>)
|
|
||||||
-> Self
|
|
||||||
{
|
|
||||||
let mut sprite = Self {
|
|
||||||
matrix: ModelMatrix::default(matrix_uniform),
|
|
||||||
gpu_mesh,
|
|
||||||
inner_size: size,
|
|
||||||
|
|
||||||
texture: texture.clone(),
|
|
||||||
texture_offset: Position::origin(),
|
|
||||||
texture_scale: 1.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
//generate proper mesh
|
|
||||||
//TODO improve that
|
|
||||||
sprite.set_texture(texture, None, 1.0);
|
|
||||||
sprite
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_texture(&mut self, texture: Texture, offset: Option<Position>, scale: f32) {
|
|
||||||
|
|
||||||
// update texture
|
|
||||||
self.texture = texture;
|
|
||||||
self.texture_scale = scale;
|
|
||||||
let size = self.texture.get_size();
|
|
||||||
|
|
||||||
// compute normalized coordinates
|
|
||||||
self.texture_offset = offset.unwrap_or(Position::origin());
|
|
||||||
let x_size = self.inner_size.w as f32 / size.w as f32 * scale;
|
|
||||||
let y_size = self.inner_size.h as f32 / size.h as f32 * scale;
|
|
||||||
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;
|
|
||||||
|
|
||||||
// compute mesh size
|
|
||||||
let w = self.inner_size.w as f32;
|
|
||||||
let h = self.inner_size.h as f32;
|
|
||||||
|
|
||||||
// generate new sprite mesh
|
|
||||||
let mesh = Mesh {
|
|
||||||
vertices: [
|
|
||||||
TextureVertex {
|
|
||||||
position: [-w, -h],
|
|
||||||
tex_coords: [x_offset , y_offset + y_size],
|
|
||||||
},
|
|
||||||
TextureVertex {
|
|
||||||
position: [ w, -h],
|
|
||||||
tex_coords: [x_offset + x_size, y_offset + y_size],
|
|
||||||
},
|
|
||||||
TextureVertex {
|
|
||||||
position: [ w, h],
|
|
||||||
tex_coords: [x_offset + x_size, y_offset ],
|
|
||||||
},
|
|
||||||
TextureVertex {
|
|
||||||
position: [-w, h],
|
|
||||||
tex_coords: [x_offset , y_offset ],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
indices: [
|
|
||||||
0, 1, 2,
|
|
||||||
0, 2, 3,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
self.gpu_mesh.set_mesh(mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_pixel(&mut self, pos: Position, pix: Pixel) {
|
|
||||||
//TODO check pos ?
|
|
||||||
self.texture.set_pixel(self.texture_offset + pos, pix);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn for_each<F: FnMut(&mut Pixel)>(&mut self, func: F) {
|
|
||||||
//TODO check pos ?
|
|
||||||
//TODO take scale into account
|
|
||||||
self.texture.for_each_in_area(func, self.texture_offset, self.inner_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sprite for TextureSprite {
|
|
||||||
|
|
||||||
fn set_position(&mut self, pos: Position) {
|
|
||||||
self.matrix.set_position(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_rotation(&mut self, rot: f32) {
|
|
||||||
self.matrix.set_rotation(rot);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_alpha(&mut self, _alpha: u8) {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_scale(&mut self, scale: f32) {
|
|
||||||
self.matrix.set_scale(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_data(&mut self) -> RenderData {
|
|
||||||
RenderData::Texture ((
|
|
||||||
self.matrix.get_uniform(),
|
|
||||||
&mut self.gpu_mesh,
|
|
||||||
&self.texture.gpu_texture(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--TextSprite struct-------------------------------------------------------------------------------
|
//--TextSprite struct-------------------------------------------------------------------------------
|
||||||
@ -250,7 +42,7 @@ impl TextSprite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Sprite for TextSprite {
|
impl Sprite for TextSprite {
|
||||||
|
|
||||||
fn set_position(&mut self, _pos: Position) {
|
fn set_position(&mut self, _pos: Position) {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
@ -267,7 +59,7 @@ impl Sprite for TextSprite {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_data(&mut self) -> RenderData {
|
fn render(&mut self, _renderer: &mut WgpuRenderer) {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,7 +84,7 @@ impl ShapeSprite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Sprite for ShapeSprite {
|
impl Sprite for ShapeSprite {
|
||||||
|
|
||||||
fn set_position(&mut self, _pos: Position) {
|
fn set_position(&mut self, _pos: Position) {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
@ -308,8 +100,8 @@ impl Sprite for ShapeSprite {
|
|||||||
fn set_scale(&mut self, _scale: f32) {
|
fn set_scale(&mut self, _scale: f32) {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_data(&mut self) -> RenderData {
|
fn render(&mut self, _renderer: &mut WgpuRenderer) {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
271
src/sprite/texture_sprite.rs
Normal file
271
src/sprite/texture_sprite.rs
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
|
use wgpu;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
texture::TextureHandle,
|
||||||
|
renderer::{
|
||||||
|
utils::{TextureVertex, GpuMesh},
|
||||||
|
WgpuRenderer, ModelMatrix,
|
||||||
|
},
|
||||||
|
utils::{Size, Position, Pixel},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::Sprite;
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
thread_local!(static PIPELINE : RefCell<Option<wgpu::RenderPipeline>> = RefCell::new(None));
|
||||||
|
|
||||||
|
//--TextureSprite struct----------------------------------------------------------------------------
|
||||||
|
pub struct TextureSprite {
|
||||||
|
matrix: ModelMatrix,
|
||||||
|
gpu_mesh: GpuMesh<TextureVertex, 4, 6>,
|
||||||
|
inner_size: Size, //TODO move to f32
|
||||||
|
|
||||||
|
texture: TextureHandle,
|
||||||
|
texture_offset: Position,
|
||||||
|
texture_scale: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextureSprite {
|
||||||
|
|
||||||
|
pub fn create(texture: TextureHandle,
|
||||||
|
size: Size,
|
||||||
|
renderer: &WgpuRenderer,
|
||||||
|
gpu_mesh: GpuMesh<TextureVertex, 4, 6>)
|
||||||
|
-> Self
|
||||||
|
{
|
||||||
|
// initialize pipeline if needed
|
||||||
|
PIPELINE.with(|cell| {
|
||||||
|
if cell.borrow().is_none() {
|
||||||
|
cell.replace(Some(initialize_pipeline(renderer)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut sprite = Self {
|
||||||
|
matrix: ModelMatrix::default(renderer),
|
||||||
|
gpu_mesh,
|
||||||
|
inner_size: size,
|
||||||
|
|
||||||
|
texture: texture.clone(),
|
||||||
|
texture_offset: Position::origin(),
|
||||||
|
texture_scale: 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
//generate proper mesh
|
||||||
|
//TODO improve that
|
||||||
|
sprite.set_texture(texture, None, 1.0);
|
||||||
|
sprite
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_texture(&mut self, texture: TextureHandle, offset: Option<Position>, scale: f32) {
|
||||||
|
use crate::renderer::utils::Mesh;
|
||||||
|
|
||||||
|
// update texture
|
||||||
|
self.texture = texture;
|
||||||
|
self.texture_scale = scale;
|
||||||
|
let size = self.texture.get_size();
|
||||||
|
|
||||||
|
// compute normalized coordinates
|
||||||
|
self.texture_offset = offset.unwrap_or(Position::origin());
|
||||||
|
let x_size = self.inner_size.w as f32 / size.w as f32 * scale;
|
||||||
|
let y_size = self.inner_size.h as f32 / size.h as f32 * scale;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// compute mesh size
|
||||||
|
let w = self.inner_size.w as f32;
|
||||||
|
let h = self.inner_size.h as f32;
|
||||||
|
|
||||||
|
// generate new sprite mesh
|
||||||
|
let mesh = Mesh {
|
||||||
|
vertices: [
|
||||||
|
TextureVertex {
|
||||||
|
position: [-w, -h],
|
||||||
|
tex_coords: [x_offset , y_offset + y_size],
|
||||||
|
},
|
||||||
|
TextureVertex {
|
||||||
|
position: [ w, -h],
|
||||||
|
tex_coords: [x_offset + x_size, y_offset + y_size],
|
||||||
|
},
|
||||||
|
TextureVertex {
|
||||||
|
position: [ w, h],
|
||||||
|
tex_coords: [x_offset + x_size, y_offset ],
|
||||||
|
},
|
||||||
|
TextureVertex {
|
||||||
|
position: [-w, h],
|
||||||
|
tex_coords: [x_offset , y_offset ],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
indices: [
|
||||||
|
0, 1, 2,
|
||||||
|
0, 2, 3,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
self.gpu_mesh.set_mesh(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_pixel(&mut self, pos: Position, pix: Pixel) {
|
||||||
|
//TODO check pos ?
|
||||||
|
self.texture.set_pixel(self.texture_offset + pos, pix);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_each<F: FnMut(&mut Pixel)>(&mut self, func: F) {
|
||||||
|
//TODO check pos ?
|
||||||
|
//TODO take scale into account
|
||||||
|
self.texture.for_each_in_area(func, self.texture_offset, self.inner_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self)
|
||||||
|
{
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize_pipeline(renderer: &WgpuRenderer) -> wgpu::RenderPipeline {
|
||||||
|
|
||||||
|
let shader = renderer.device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: Some("texture shader"),
|
||||||
|
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/texture.wgsl").into()),
|
||||||
|
});
|
||||||
|
|
||||||
|
let render_pipeline_layout =
|
||||||
|
renderer.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some("texture render pipeline layout"),
|
||||||
|
bind_group_layouts: &[
|
||||||
|
&renderer.matrix_layout,
|
||||||
|
&renderer.texture_layout,
|
||||||
|
],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
renderer.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: Some("texture render pipeline"),
|
||||||
|
layout: Some(&render_pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "vs_main",
|
||||||
|
buffers: &[
|
||||||
|
TextureVertex::desc(),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "fs_main",
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: renderer.config.format,
|
||||||
|
blend: Some(wgpu::BlendState::REPLACE),
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
cull_mode: None,//Some(wgpu::Face::Back),
|
||||||
|
// Setting this to anything other than Fill requires
|
||||||
|
// Features::NON_FILL_POLYGON_MODE
|
||||||
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
|
// Requires Features::DEPTH_CLIP_CONTROL
|
||||||
|
unclipped_depth: false,
|
||||||
|
// Requires Features::CONSERVATIVE_RASTERIZATION
|
||||||
|
conservative: false,
|
||||||
|
},
|
||||||
|
depth_stencil: None,
|
||||||
|
// multisampling, we don't need it
|
||||||
|
multisample: wgpu::MultisampleState {
|
||||||
|
count: 1,
|
||||||
|
mask: !0,
|
||||||
|
alpha_to_coverage_enabled: false,
|
||||||
|
},
|
||||||
|
multiview: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sprite for TextureSprite {
|
||||||
|
|
||||||
|
fn set_position(&mut self, pos: Position) {
|
||||||
|
self.matrix.set_position(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_rotation(&mut self, rot: f32) {
|
||||||
|
self.matrix.set_rotation(rot);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_alpha(&mut self, _alpha: u8) {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_scale(&mut self, scale: f32) {
|
||||||
|
self.matrix.set_scale(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, renderer: &mut WgpuRenderer) {
|
||||||
|
|
||||||
|
let output = match &renderer.output {
|
||||||
|
Some(out) => out,
|
||||||
|
None => {
|
||||||
|
renderer.create_output();
|
||||||
|
&renderer.output.as_ref().unwrap()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO move that to output struct ?
|
||||||
|
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
let texture = &self.texture.texture();
|
||||||
|
|
||||||
|
if texture.borrow().is_synced == false {
|
||||||
|
//TODO modify that
|
||||||
|
texture.borrow_mut().update(&mut renderer.queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.gpu_mesh.is_synced() == false {
|
||||||
|
self.gpu_mesh.update(&mut renderer.queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut encoder = renderer.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
|
label: Some("Render Encoder"),
|
||||||
|
});
|
||||||
|
|
||||||
|
PIPELINE.with(|pipeline| {
|
||||||
|
let pipeline = pipeline.borrow();
|
||||||
|
let texture_bind_group = &texture.borrow().bind_group;
|
||||||
|
|
||||||
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: Some("Render Pass"),
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Load, store: true, },
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
render_pass.set_pipeline(pipeline.as_ref().unwrap());
|
||||||
|
render_pass.set_vertex_buffer(0, self.gpu_mesh.get_vertex_buffer_slice());
|
||||||
|
render_pass.set_index_buffer(self.gpu_mesh.get_index_buffer_slice(),
|
||||||
|
wgpu::IndexFormat::Uint16);
|
||||||
|
render_pass.set_bind_group(0,
|
||||||
|
self.matrix.get_uniform().get_bind_group(&renderer.queue),
|
||||||
|
&[]);
|
||||||
|
render_pass.set_bind_group(1, texture_bind_group, &[]);
|
||||||
|
//render_pass.set_push_constants(
|
||||||
|
// wgpu::ShaderStages::VERTEX,
|
||||||
|
// 0,
|
||||||
|
// bytemuck::bytes_of(&(self.aspect_matrix))// * matrix.get_matrix()))
|
||||||
|
//);
|
||||||
|
render_pass.draw_indexed(0..self.gpu_mesh.get_index_number(), 0, 0..1);
|
||||||
|
|
||||||
|
drop(render_pass);
|
||||||
|
});
|
||||||
|
|
||||||
|
renderer.queue.submit(std::iter::once(encoder.finish()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
145
src/texture.rs
145
src/texture.rs
@ -1,160 +1,73 @@
|
|||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
use crate::utils::{Pixel, Position, Size};
|
use crate::{
|
||||||
|
utils::{Pixel, Position, Size},
|
||||||
|
renderer::Texture,
|
||||||
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
};
|
};
|
||||||
|
|
||||||
//--GpuTexture struct-------------------------------------------------------------------------------
|
//--TextureHandle struct----------------------------------------------------------------------------
|
||||||
pub struct GpuTexture {
|
|
||||||
pub texture: wgpu::Texture,
|
|
||||||
pub bind_group: wgpu::BindGroup,
|
|
||||||
|
|
||||||
pub buffer: Vec<Pixel>,
|
|
||||||
pub size: Size,
|
|
||||||
pub is_synced: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GpuTexture {
|
|
||||||
|
|
||||||
pub fn create(device: &wgpu::Device,
|
|
||||||
layout: &wgpu::BindGroupLayout,
|
|
||||||
buffer: &[u8],
|
|
||||||
size: Size)
|
|
||||||
-> Self
|
|
||||||
{
|
|
||||||
let texture = device.create_texture(
|
|
||||||
&wgpu::TextureDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: size.into(),
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
|
||||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
let sampler = device.create_sampler(
|
|
||||||
&wgpu::SamplerDescriptor {
|
|
||||||
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,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let bind_group = device.create_bind_group(
|
|
||||||
&wgpu::BindGroupDescriptor {
|
|
||||||
layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
|
||||||
}
|
|
||||||
],
|
|
||||||
label: Some("texture_bind_group"),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
texture,
|
|
||||||
bind_group,
|
|
||||||
buffer: Vec::from(bytemuck::cast_slice(buffer)),
|
|
||||||
size,
|
|
||||||
is_synced: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, queue: &wgpu::Queue) {
|
|
||||||
|
|
||||||
queue.write_texture(
|
|
||||||
wgpu::ImageCopyTexture {
|
|
||||||
aspect: wgpu::TextureAspect::All,
|
|
||||||
texture: &self.texture,
|
|
||||||
mip_level: 0,
|
|
||||||
origin: wgpu::Origin3d::ZERO,
|
|
||||||
},
|
|
||||||
bytemuck::cast_slice(&self.buffer),
|
|
||||||
wgpu::ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: std::num::NonZeroU32::new(4 * self.size.w),
|
|
||||||
rows_per_image: std::num::NonZeroU32::new(self.size.h),
|
|
||||||
},
|
|
||||||
self.size.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.is_synced = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//--Texture struct----------------------------------------------------------------------------------
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Texture {
|
pub struct TextureHandle {
|
||||||
gpu_texture: Rc<RefCell<GpuTexture>>,
|
texture: Rc<RefCell<Texture>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Texture {
|
impl TextureHandle {
|
||||||
|
|
||||||
pub fn create(gpu_texture: GpuTexture) -> Self {
|
pub fn from_texture(texture: Texture) -> Self {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
gpu_texture: Rc::new(RefCell::new(gpu_texture)),
|
texture: Rc::new(RefCell::new(texture)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_pixel(&mut self, pos: Position, pix: Pixel) {
|
pub fn set_pixel(&mut self, pos: Position, pix: Pixel) {
|
||||||
|
|
||||||
//TODO check pos ?
|
//TODO check pos ?
|
||||||
let mut gpu_texture = self.gpu_texture.borrow_mut();
|
let mut texture = self.texture.borrow_mut();
|
||||||
let width = gpu_texture.size.w;
|
let width = texture.size.w;
|
||||||
gpu_texture.buffer[(pos.x + pos.y*width) as usize] = pix;
|
texture.buffer[(pos.x + pos.y*width) as usize] = pix;
|
||||||
gpu_texture.is_synced = false;
|
texture.is_synced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_each<F: FnMut(&mut Pixel)>(&mut self, mut func: F) {
|
pub fn for_each<F: FnMut(&mut Pixel)>(&mut self, mut func: F) {
|
||||||
|
|
||||||
let mut gpu_texture = self.gpu_texture.borrow_mut();
|
let mut texture = self.texture.borrow_mut();
|
||||||
for pix in &mut gpu_texture.buffer {
|
for pix in &mut texture.buffer {
|
||||||
func(pix);
|
func(pix);
|
||||||
}
|
}
|
||||||
gpu_texture.is_synced = false;
|
texture.is_synced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_each_in_area<F: FnMut(&mut Pixel)>(&mut self,
|
pub fn for_each_in_area<F: FnMut(&mut Pixel)>(&mut self,
|
||||||
mut func: F,
|
mut func: F,
|
||||||
offset: Position,
|
offset: Position,
|
||||||
size: Size)
|
size: Size)
|
||||||
{
|
{
|
||||||
//TODO check offset and pos ?
|
//TODO check offset and pos ?
|
||||||
let mut gpu_texture = self.gpu_texture.borrow_mut();
|
let mut texture = self.texture.borrow_mut();
|
||||||
let width = gpu_texture.size.w;
|
let width = texture.size.w;
|
||||||
for x in offset.x..(offset.x + size.w) {
|
for x in offset.x..(offset.x + size.w) {
|
||||||
for y in offset.y..(offset.y + size.h) {
|
for y in offset.y..(offset.y + size.h) {
|
||||||
func(&mut gpu_texture.buffer[(x + y*width) as usize]);
|
func(&mut texture.buffer[(x + y*width) as usize]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gpu_texture.is_synced = false;
|
texture.is_synced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gpu_texture(&self) -> &Rc<RefCell<GpuTexture>> {
|
pub fn texture(&self) -> &Rc<RefCell<Texture>> {
|
||||||
//TODO improve that
|
//TODO improve that
|
||||||
&self.gpu_texture
|
&self.texture
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_size(&self) -> Size {
|
pub fn get_size(&self) -> Size {
|
||||||
self.gpu_texture.borrow().size
|
self.texture.borrow().size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user