Significant render refactoring, implement transformation matrix

This commit is contained in:
SeanOMik 2023-05-14 15:18:18 -04:00
parent 22865bbc33
commit 432c54004e
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
23 changed files with 594 additions and 182 deletions

64
Cargo.lock generated
View File

@ -95,15 +95,6 @@ version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]]
name = "approx"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.6" version = "0.3.6"
@ -427,16 +418,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "cgmath"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
dependencies = [
"approx",
"num-traits",
]
[[package]] [[package]]
name = "codespan-reporting" name = "codespan-reporting"
version = "0.11.1" version = "0.11.1"
@ -727,6 +708,15 @@ version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
[[package]]
name = "glam"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad83ab008a4fa3b31dfa713dd41b5a9bdea1e94e4cf1e2fc274ffbd49b0271d3"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "gloo-timers" name = "gloo-timers"
version = "0.2.6" version = "0.2.6"
@ -791,7 +781,7 @@ checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"gpu-descriptor-types", "gpu-descriptor-types",
"hashbrown", "hashbrown 0.12.3",
] ]
[[package]] [[package]]
@ -812,6 +802,15 @@ dependencies = [
"ahash 0.7.6", "ahash 0.7.6",
] ]
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash 0.8.3",
]
[[package]] [[package]]
name = "hassle-rs" name = "hassle-rs"
version = "0.9.0" version = "0.9.0"
@ -827,6 +826,16 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "hecs"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d2711ad60b74f2f3d0c0ac338a58410a5249da44005971ae806d2925e6b5167"
dependencies = [
"hashbrown 0.13.2",
"spin",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.2.6" version = "0.2.6"
@ -874,7 +883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown 0.12.3",
] ]
[[package]] [[package]]
@ -990,7 +999,8 @@ dependencies = [
"async-trait", "async-trait",
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",
"cgmath", "glam",
"hecs",
"image", "image",
"instant", "instant",
"specs", "specs",
@ -1569,7 +1579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "102269e720bb814df57e136161cad841f2b6f411e003ac748fc48aaf2363bea3" checksum = "102269e720bb814df57e136161cad841f2b6f411e003ac748fc48aaf2363bea3"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"hashbrown", "hashbrown 0.12.3",
"mopa", "mopa",
"rayon", "rayon",
"shred-derive", "shred-derive",
@ -1673,7 +1683,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea85dac2880f84d4025ff5ace80cda6d8bc43bc88b6a389b9277fcf894b51e9" checksum = "4ea85dac2880f84d4025ff5ace80cda6d8bc43bc88b6a389b9277fcf894b51e9"
dependencies = [ dependencies = [
"crossbeam-queue", "crossbeam-queue",
"hashbrown", "hashbrown 0.12.3",
"hibitset", "hibitset",
"log", "log",
"rayon", "rayon",
@ -1694,6 +1704,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "spirv" name = "spirv"
version = "0.2.0+1.5.4" version = "0.2.0+1.5.4"

View File

@ -17,10 +17,12 @@ cfg-if = "1"
bytemuck = { version = "1.12", features = [ "derive" ] } bytemuck = { version = "1.12", features = [ "derive" ] }
image = { version = "0.24", default-features = false, features = ["png", "jpeg"] } image = { version = "0.24", default-features = false, features = ["png", "jpeg"] }
anyhow = "1.0" anyhow = "1.0"
cgmath = "0.18" #cgmath = "0.18"
tobj = { version = "3.2.1", features = [ tobj = { version = "3.2.1", features = [
"async", "async",
]} ]}
instant = "0.1" instant = "0.1"
async-trait = "0.1.65" async-trait = "0.1.65"
specs = { version = "0.18.0", features = [ "derive" ] } specs = { version = "0.18.0", features = [ "derive" ] }
hecs = "0.10.3"
glam = { version = "0.24.0", features = ["bytemuck"] }

0
res/happy-tree.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -10,13 +10,16 @@ struct VertexOutput {
@location(0) tex_coords: vec2<f32>, @location(0) tex_coords: vec2<f32>,
} }
@group(1) @binding(0)
var<uniform> u_model_transform: mat4x4<f32>;
@vertex @vertex
fn vs_main( fn vs_main(
model: VertexInput, model: VertexInput,
) -> VertexOutput { ) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.tex_coords = model.tex_coords; out.tex_coords = model.tex_coords;
out.clip_position = vec4<f32>(model.position, 1.0); out.clip_position = u_model_transform * vec4<f32>(model.position, 1.0);
return out; return out;
} }

3
src/ecs/components/mod.rs Normal file → Executable file
View File

@ -1 +1,2 @@
pub mod model_2d; pub mod model_2d;
pub mod transform;

20
src/ecs/components/model_2d.rs Normal file → Executable file
View File

@ -1,4 +1,18 @@
#[derive(Component, Debug, Default)] use specs::{Component, DenseVecStorage};
pub struct Model2d {
pub vertices: Vec<Vertex>, use crate::render::{vertex::Vertex, mesh::Mesh, material::Material};
#[derive(Component, Clone)]
pub struct Model2dComponent {
pub mesh: Mesh,
pub material: Material,
}
impl Model2dComponent {
pub fn new(mesh: Mesh, material: Material) -> Self {
Self {
mesh,
material
}
}
} }

28
src/ecs/components/transform.rs Executable file
View File

@ -0,0 +1,28 @@
use specs::{Component, DenseVecStorage};
use crate::render::transform::Transform;
#[derive(Component, Clone)]
pub struct TransformComponent {
pub transform: Transform,
}
impl Default for TransformComponent {
fn default() -> Self {
Self {
transform: Transform::default(),
}
}
}
impl TransformComponent {
pub fn new() -> Self {
Self::default()
}
pub fn from_transform(transform: Transform) -> Self {
Self {
transform
}
}
}

0
src/ecs/mod.rs Normal file → Executable file
View File

View File

@ -1,9 +1,8 @@
use std::{sync::Arc}; use std::{sync::Arc, cell::RefCell};
use async_std::{task::block_on, sync::Mutex}; use async_std::{task::block_on, sync::Mutex};
use specs::{Dispatcher, World}; use hecs::World;
use tracing::{metadata::LevelFilter, info, debug, warn}; use tracing::{metadata::LevelFilter, info, debug, warn};
use tracing_subscriber::{ use tracing_subscriber::{
layer::{Layer, SubscriberExt}, layer::{Layer, SubscriberExt},
@ -13,24 +12,22 @@ use tracing_subscriber::{
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}}; use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}};
use crate::{render::renderer::{Renderer, BasicRenderer}, input_event::InputEvent}; use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::components::{model_2d::Model2dComponent, transform::TransformComponent}};
struct GameLoop<'a, 'b> { struct GameLoop {
window: Arc<Window>, window: Arc<Window>,
renderer: Box<dyn Renderer>, renderer: Box<dyn Renderer>,
world: Arc<Mutex<World>>, world: Arc<Mutex<World>>,
system_dispatchers: Vec<Dispatcher<'a, 'b>>,
} }
impl<'a, 'b> GameLoop<'a, 'b> { impl GameLoop {
pub async fn new(window: Arc<Window>, world: Arc<Mutex<World>>, system_dispatchers: Vec<Dispatcher<'a, 'b>>) -> GameLoop<'a, 'b> { pub async fn new(window: Arc<Window>, world: Arc<Mutex<World>>) -> GameLoop {
Self { Self {
window: Arc::clone(&window), window: Arc::clone(&window),
renderer: Box::new(BasicRenderer::create_with_window(window).await), renderer: Box::new(BasicRenderer::create_with_window(window).await),
world, world,
system_dispatchers,
} }
} }
@ -43,10 +40,10 @@ impl<'a, 'b> GameLoop<'a, 'b> {
} }
async fn update(&mut self) { async fn update(&mut self) {
let world = self.world.lock().await; /* let world = self.world.lock().await;
for dispatcher in self.system_dispatchers.iter_mut() { for dispatcher in self.system_dispatchers.iter_mut() {
dispatcher.dispatch(&world); dispatcher.dispatch(&world);
} } */
} }
async fn input_update(&mut self, event: &InputEvent) -> Option<ControlFlow> { async fn input_update(&mut self, event: &InputEvent) -> Option<ControlFlow> {
@ -120,8 +117,41 @@ impl<'a, 'b> GameLoop<'a, 'b> {
}, },
Event::RedrawRequested(window_id) if window_id == self.window.id() => { Event::RedrawRequested(window_id) if window_id == self.window.id() => {
// Update the world
self.update().await; self.update().await;
// Collect models from the ecs world
/* let mut jobs = vec![];
let world = self.world.lock().await;
let model2d_storage = world.read_storage::<Model2d>();
for model in model2d_storage.join() {
let job = RenderJob::new(&model.mesh);
jobs.push(job);
}
// drop some stuff for the borrow checker
drop(model2d_storage);
drop(world);
for job in jobs.iter() {
println!("job");
} */
let mut world = self.world.lock().await;
for (ent, (transform,)) in world.query_mut::<(&mut TransformComponent,)>() {
transform.transform.translate_vec3(glam::Vec3::new(0.0, 0.001, 0.0));
}
for (ent, (transform,)) in world.query::<(&TransformComponent,)>().iter() {
println!("transform pos: {:?}", transform.transform);
}
self.renderer.as_mut().prepare(&world).await;
drop(world);
match self.renderer.as_mut().render().await { match self.renderer.as_mut().render().await {
Ok(_) => {} Ok(_) => {}
// Reconfigure the surface if lost // Reconfigure the surface if lost
@ -141,22 +171,20 @@ impl<'a, 'b> GameLoop<'a, 'b> {
} }
} }
pub struct Game<'a, 'b> { pub struct Game {
world: Option<Arc<Mutex<World>>>, world: Option<Arc<Mutex<World>>>,
system_dispatchers: Option<Vec<Dispatcher<'a, 'b>>>,
} }
impl<'a, 'b> Default for Game<'a, 'b> { impl Default for Game {
fn default() -> Self { fn default() -> Self {
Self { Self {
world: None, world: None,
system_dispatchers: None,
} }
} }
} }
impl<'a, 'b> Game<'static, 'static> { impl Game {
pub async fn initialize() -> Game<'static, 'static> { pub async fn initialize() -> Game {
let filter = FilterFn::new(|metadata| { let filter = FilterFn::new(|metadata| {
metadata.module_path() metadata.module_path()
.unwrap_or_else(|| metadata.target()) .unwrap_or_else(|| metadata.target())
@ -184,27 +212,13 @@ impl<'a, 'b> Game<'static, 'static> {
self self
} }
pub fn with_dispatchers(&mut self, dispatchers: Vec<Dispatcher<'static, 'static>>) -> &mut Self {
self.system_dispatchers = Some(dispatchers);
self
}
pub fn with_dispatcher(&mut self, dispatcher: Dispatcher<'static, 'static>) -> &mut Self {
self.system_dispatchers.get_or_insert_with(|| Vec::new()).push(dispatcher);
self
}
pub async fn run(&mut self) { pub async fn run(&mut self) {
let world = self.world.take().expect("ECS World was never given to Game!"); let world = self.world.take().expect("ECS World was never given to Game!");
let system_dispatchers = self.system_dispatchers
.take().unwrap_or_else(|| Vec::new());
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap()); let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
let mut g_loop = GameLoop::new(Arc::clone(&window), world, system_dispatchers).await; let mut g_loop = GameLoop::new(Arc::clone(&window), world).await;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
g_loop.run_sync(event, control_flow); g_loop.run_sync(event, control_flow);

View File

@ -1,13 +1,20 @@
mod system;
mod game; mod game;
mod render; mod render;
mod input_event; mod input_event;
mod resources; mod resources;
mod ecs;
use ecs::components::model_2d::Model2dComponent;
use ecs::components::transform::TransformComponent;
use game::Game; use game::Game;
use specs::*; use hecs::World;
#[derive(Component, Debug, Default)] use crate::render::material::Material;
use crate::render::texture::Texture;
//use specs::*;
#[derive(Debug, Default)]
struct Point2d { struct Point2d {
x: i32, x: i32,
y: i32, y: i32,
@ -28,7 +35,7 @@ impl Point2d {
} }
} }
#[derive(Component, Debug, Default)] #[derive(Debug, Default)]
struct Point3d { struct Point3d {
x: i32, x: i32,
y: i32, y: i32,
@ -51,29 +58,34 @@ impl Point3d {
} }
} }
struct TestingSystem;
impl<'a> System<'a> for TestingSystem {
// These are the resources required for execution.
// You can also define a struct and `#[derive(SystemData)]`,
// see the `full` example.
type SystemData = (ReadStorage<'a, Point2d>,);
fn run(&mut self, (point,): Self::SystemData) {
// The `.join()` combines multiple component storages,
// so we get access to all entities which have
// both a position and a velocity.
for (point,) in (&point,).join() {
//point.0 += vel.0;
println!("Entity at: {}", point);
}
}
}
#[async_std::main] #[async_std::main]
async fn main() { async fn main() {
let mut world = World::new(); let mut world = World::new();
world.register::<Point2d>();
world.spawn((Point2d::new(10, 10), Point3d::new(50, 50, 50)));
let diffuse_bytes = include_bytes!("../res/happy-tree.png");
let diffuse_texture = Texture::from_bytes(diffuse_bytes).unwrap();
world.spawn((Model2dComponent::new(
render::mesh::Mesh {
vertices: crate::render::vertex::VERTICES.to_vec(),
indices: Some(crate::render::vertex::INDICES.to_vec())
}, Material {
shader_id: 0,
texture: diffuse_texture
}),
TransformComponent::new(),
));
for (id, (point2d,)) in world.query::<(&Point2d,)>().iter() {
println!("Entity {} is at 2d position: {:?}", id.id(), point2d);
}
Game::initialize().await
.with_world(world)
.run().await;
/* world.register::<Point2d>();
world.register::<Point3d>(); world.register::<Point3d>();
world.create_entity() world.create_entity()
@ -89,7 +101,7 @@ async fn main() {
Game::initialize().await Game::initialize().await
.with_world(world) .with_world(world)
.with_dispatcher(dispatcher) .with_dispatcher(dispatcher)
.run().await; .run().await; */
/* let mut game = Game::initialize().await; /* let mut game = Game::initialize().await;
game.run().await; */ game.run().await; */

0
src/render/desc_buf_lay.rs Normal file → Executable file
View File

7
src/render/material.rs Executable file
View File

@ -0,0 +1,7 @@
use super::texture::Texture;
#[derive(Clone)]
pub struct Material {
pub shader_id: u32,
pub texture: Texture,
}

13
src/render/mesh.rs Normal file → Executable file
View File

@ -2,28 +2,23 @@ use wgpu::{BindGroup, BindGroupLayout};
use super::{vertex::Vertex, render_buffer::{BufferStorage}}; use super::{vertex::Vertex, render_buffer::{BufferStorage}};
#[derive(Clone)]
pub struct Mesh { pub struct Mesh {
//material
//texture: Option<Texture>,
pub vertices: Vec<Vertex>, pub vertices: Vec<Vertex>,
pub indices: Option<Vec<u16>>, pub indices: Option<Vec<u16>>,
pub buffer_vertex: BufferStorage, /* pub buffer_vertex: BufferStorage,
pub buffer_indices: Option<BufferStorage>, // TOOD: make optional pub buffer_indices: Option<BufferStorage>, // TOOD: make optional
pub texture_bindgroup: Option<BindGroup>, pub texture_bindgroup: Option<BindGroup>,
pub texture_layout: Option<BindGroupLayout> pub texture_layout: Option<BindGroupLayout> */
} }
impl Mesh { impl Mesh {
pub fn new(vertices: Vec<Vertex>, indices: Option<Vec<u16>>, buffer_vertex: BufferStorage, buffer_indices: Option<BufferStorage>, texture_bindgroup: Option<BindGroup>, texture_layout: Option<BindGroupLayout>) -> Self { pub fn new(vertices: Vec<Vertex>, indices: Option<Vec<u16>>) -> Self {
Self { Self {
vertices, vertices,
indices, indices,
buffer_vertex,
buffer_indices,
texture_bindgroup,
texture_layout,
} }
} }
} }

5
src/render/mod.rs Normal file → Executable file
View File

@ -5,4 +5,7 @@ pub mod desc_buf_lay;
pub mod render_buffer; pub mod render_buffer;
pub mod render_job; pub mod render_job;
pub mod mesh; pub mod mesh;
pub mod texture; pub mod texture;
pub mod shader_loader;
pub mod material;
pub mod transform;

0
src/render/render_buffer.rs Normal file → Executable file
View File

20
src/render/render_job.rs Normal file → Executable file
View File

@ -1,17 +1,31 @@
use super::{mesh::Mesh}; use hecs::Entity;
use super::{mesh::Mesh, material::Material};
pub struct RenderJob { pub struct RenderJob {
mesh: Mesh mesh: Mesh,
material: Material,
entity: Entity,
} }
impl RenderJob { impl RenderJob {
pub fn new(mesh: Mesh) -> Self { pub fn new(mesh: Mesh, material: Material, entity: Entity) -> Self {
Self { Self {
mesh, mesh,
material,
entity,
} }
} }
pub fn mesh(&self)-> &Mesh { pub fn mesh(&self)-> &Mesh {
&self.mesh &self.mesh
} }
pub fn material(&self)-> &Material {
&self.material
}
pub fn entity(&self)-> Entity {
self.entity
}
} }

21
src/render/render_pipeline.rs Normal file → Executable file
View File

@ -1,19 +1,18 @@
use std::ops::Range; use std::{ops::Range, cell::Ref};
use wgpu::{PipelineLayout, RenderPipeline, RenderPass}; use wgpu::{PipelineLayout, RenderPipeline, RenderPass, VertexBufferLayout, BindGroupLayout};
use super::{render_job::RenderJob, vertex::Vertex, desc_buf_lay::DescVertexBufferLayout}; use super::{render_job::RenderJob, vertex::Vertex, desc_buf_lay::DescVertexBufferLayout};
pub struct FullRenderPipeline { pub struct FullRenderPipeline {
layout: PipelineLayout, layout: PipelineLayout,
wgpu_pipeline: RenderPipeline, wgpu_pipeline: RenderPipeline,
jobs: Vec<RenderJob>,
} }
impl FullRenderPipeline { impl FullRenderPipeline {
pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, shader: &wgpu::ShaderModule, jobs: Vec<RenderJob>) -> Self { pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, shader: &wgpu::ShaderModule, buffer_layouts: Vec<VertexBufferLayout>, bind_group_layouts: Vec<&BindGroupLayout>) -> FullRenderPipeline {
// Extract the layouts from all the jobs // Extract the layouts from all the jobs
let mut buffer_layouts = vec![]; /* let mut buffer_layouts = vec![];
let mut bind_group_layouts = vec![]; let mut bind_group_layouts = vec![];
for job in jobs.iter() { for job in jobs.iter() {
// Push layout for the vertex buffer, index buffer doesn't need one // Push layout for the vertex buffer, index buffer doesn't need one
@ -22,7 +21,7 @@ impl FullRenderPipeline {
if let Some(layout) = job.mesh().texture_layout.as_ref() { if let Some(layout) = job.mesh().texture_layout.as_ref() {
bind_group_layouts.push(layout); bind_group_layouts.push(layout);
} }
} } */
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
@ -72,7 +71,6 @@ impl FullRenderPipeline {
Self { Self {
layout: render_pipeline_layout, layout: render_pipeline_layout,
wgpu_pipeline: render_pipeline, wgpu_pipeline: render_pipeline,
jobs,
} }
} }
@ -86,10 +84,11 @@ impl FullRenderPipeline {
&self.wgpu_pipeline &self.wgpu_pipeline
} }
pub fn render<'a>(&'a self, pass: &mut RenderPass<'a>, vertices: Range<u32>, instances: Range<u32>) { pub fn render<'a>(&'a self, jobs: &'a Vec<RenderJob>, pass: &'a mut RenderPass<'a>, vertices: Range<u32>, instances: Range<u32>) {
pass.set_pipeline(&self.wgpu_pipeline); todo!()
/* pass.set_pipeline(&self.wgpu_pipeline);
for job in self.jobs.iter() { for job in jobs.iter() {
let mesh = job.mesh(); let mesh = job.mesh();
if let Some(tex) = mesh.texture_bindgroup.as_ref() { if let Some(tex) = mesh.texture_bindgroup.as_ref() {
@ -106,6 +105,6 @@ impl FullRenderPipeline {
pass.set_vertex_buffer(mesh.buffer_vertex.slot(), mesh.buffer_vertex.buffer().slice(..)); pass.set_vertex_buffer(mesh.buffer_vertex.slot(), mesh.buffer_vertex.buffer().slice(..));
pass.draw(vertices.clone(), instances.clone()); pass.draw(vertices.clone(), instances.clone());
} }
} } */
} }
} }

View File

@ -1,23 +1,43 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::borrow::Cow; use std::borrow::Cow;
use async_std::sync::Mutex;
use async_trait::async_trait; use async_trait::async_trait;
use wgpu::{BindGroup, BindGroupLayout};
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use winit::window::Window; use winit::window::Window;
use hecs::{World, Entity};
use crate::ecs::components::model_2d::Model2dComponent;
use crate::ecs::components::transform::TransformComponent;
use crate::resources; use crate::resources;
use super::texture::Texture; use super::desc_buf_lay::DescVertexBufferLayout;
use super::texture::RenderTexture;
use super::transform::Transform;
use super::{render_pipeline::FullRenderPipeline, vertex::{VERTICES}, render_buffer::BufferStorage, render_job::RenderJob, mesh::Mesh}; use super::{render_pipeline::FullRenderPipeline, vertex::{VERTICES}, render_buffer::BufferStorage, render_job::RenderJob, mesh::Mesh};
#[async_trait] #[async_trait]
pub trait Renderer { pub trait Renderer {
async fn prepare(&mut self, main_world: &World);
async fn render(&mut self) -> Result<(), wgpu::SurfaceError>; async fn render(&mut self) -> Result<(), wgpu::SurfaceError>;
async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>); async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>);
fn surface_size(&self) -> winit::dpi::PhysicalSize<u32>; fn surface_size(&self) -> winit::dpi::PhysicalSize<u32>;
fn add_render_pipeline(&mut self, pipeline: FullRenderPipeline); fn add_render_pipeline(&mut self, shader_id: u32, pipeline: Arc<FullRenderPipeline>);
}
struct RenderBufferStorage {
buffer_vertex: BufferStorage,
buffer_indices: Option<BufferStorage>,
render_texture: Option<RenderTexture>,
texture_bindgroup: Option<BindGroup>,
texture_layout: Option<BindGroupLayout>
} }
pub struct BasicRenderer { pub struct BasicRenderer {
@ -30,7 +50,14 @@ pub struct BasicRenderer {
pub clear_color: wgpu::Color, pub clear_color: wgpu::Color,
pub render_pipelines: Vec<FullRenderPipeline>, pub render_pipelines: HashMap<u32, Arc<FullRenderPipeline>>,
pub render_jobs: HashMap<u32, Vec<RenderJob>>,
buffer_storage: HashMap<Entity, RenderBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
transform_uniform: Transform,
transform_bind_group: wgpu::BindGroup,
transform_buffer: wgpu::Buffer,
} }
impl BasicRenderer { impl BasicRenderer {
@ -89,8 +116,8 @@ impl BasicRenderer {
surface.configure(&device, &config); surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("../../res/happy-tree.png"); /* let diffuse_bytes = include_bytes!("../../res/happy-tree.png");
let diffuse_texture = Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap(); let diffuse_texture = RenderTexture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap(); */
let texture_bind_group_layout = let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -117,7 +144,171 @@ impl BasicRenderer {
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
}); });
let diffuse_bind_group = device.create_bind_group( let transform_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,
},
],
label: Some("texture_bind_group_layout"),
});
/* let diffuse_bind_group = device.create_bind_group(
&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(diffuse_texture.view()),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(diffuse_texture.sampler()),
}
],
label: Some("diffuse_bind_group"),
}
); */
let shader_src = resources::load_string("shader/base_2d.wgsl").await.expect("Failed to load shader!");
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shader"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(&shader_src)),
});
let transform_uniform = Transform::new();
let transform_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[transform_uniform]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}
);
let transform_bind_group_layout = 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("transform_bind_group_layout"),
});
let transform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &transform_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: transform_buffer.as_entire_binding(),
}
],
label: Some("transform_bind_group"),
});
let mut pipelines = HashMap::new();
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&device, &config, &shader,
vec![super::vertex::Vertex::desc(),], vec![&texture_bind_group_layout, &transform_bind_group_layout])));
Self {
window,
surface,
device,
queue,
config,
size,
clear_color: wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
},
render_pipelines: pipelines,
render_jobs: HashMap::new(),
buffer_storage: HashMap::new(),
transform_uniform,
transform_buffer,
transform_bind_group,
}
}
fn create_model_buffers(&mut self, model_2d: &Model2dComponent) -> RenderBufferStorage {
let mesh = &model_2d.mesh;
let vertex_buffer = self.device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(mesh.vertices.as_slice()),
usage: wgpu::BufferUsages::VERTEX,
}
);
let buffer_indices = match mesh.indices.as_ref() {
Some(indices) => {
let index_buffer = self.device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(&indices),
usage: wgpu::BufferUsages::INDEX,
}
);
let buffer_indices = BufferStorage::new(index_buffer, 0, Some(indices.len() as u32));
Some(buffer_indices)
},
None => {
None
}
};
let texture_img = &model_2d.material.texture;
let diffuse_texture = RenderTexture::from_image(&self.device, &self.queue, &texture_img.img, 0, Some("happy-tree.png")).unwrap();
let texture_bind_group_layout =
self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
// 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 diffuse_bind_group = self.device.create_bind_group(
&wgpu::BindGroupDescriptor { &wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &texture_bind_group_layout,
entries: &[ entries: &[
@ -134,63 +325,53 @@ impl BasicRenderer {
} }
); );
RenderBufferStorage {
let shader_src = resources::load_string("shader.wgsl").await.expect("Failed to load shader!"); buffer_vertex: BufferStorage::new(vertex_buffer, 0, None),
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { buffer_indices,
label: Some("Shader"), render_texture: None,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(&shader_src)), texture_layout: None,
}); texture_bindgroup: Some(diffuse_bind_group),
let vertex_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(super::vertex::VERTICES),
usage: wgpu::BufferUsages::VERTEX,
}
);
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(super::vertex::INDICES),
usage: wgpu::BufferUsages::INDEX,
}
);
let job = RenderJob::new(Mesh::new(
super::vertex::VERTICES.to_vec(), Some(super::vertex::INDICES.to_vec()),
BufferStorage::new(vertex_buffer, 0, None),
Some(BufferStorage::new(index_buffer, 0, Some(super::vertex::INDICES.len() as u32))),
Some(diffuse_bind_group), Some(texture_bind_group_layout)
));
let pipelines = vec![
FullRenderPipeline::new(&device, &config, &shader,
vec![job,])
];
Self {
window,
surface,
device,
queue,
config,
size,
clear_color: wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
},
render_pipelines: pipelines,
} }
} }
} }
#[async_trait] #[async_trait]
impl Renderer for BasicRenderer { impl Renderer for BasicRenderer {
async fn prepare(&mut self, main_world: &World) {
for (entity, (model, transform)) in main_world.query::<(&Model2dComponent, Option<&TransformComponent>)>().iter() {
let job = RenderJob::new(model.mesh.clone(), model.material.clone(), entity);
// Insert the new job into the queue
if let Some(shader_jobs) = self.render_jobs.get_mut(&model.material.shader_id) {
shader_jobs.push(job);
} else {
self.render_jobs.insert(model.material.shader_id, vec![job]);
}
if self.buffer_storage.get(&entity).is_none() {
let buffers = self.create_model_buffers(model);
self.buffer_storage.insert(entity, buffers);
} else {
let mesh = &model.mesh;
// update existing buffers
}
if let Some(transform) = transform {
self.transform_uniform = transform.transform;
self.queue.write_buffer(&self.transform_buffer, 0, bytemuck::cast_slice(&[self.transform_uniform]));
}
}
}
fn add_render_pipeline(&mut self, shader_id: u32, pipeline: Arc<FullRenderPipeline>) {
self.render_pipelines.insert(shader_id, pipeline);
}
async fn render(&mut self) -> Result<(), wgpu::SurfaceError> { async fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
let jobs = &self.render_jobs;
let output = self.surface.get_current_texture()?; let output = self.surface.get_current_texture()?;
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
@ -199,9 +380,9 @@ impl Renderer for BasicRenderer {
}); });
// Loop through all the render pipelines and draw the buffers. // Loop through all the render pipelines and draw the buffers.
for pipeline in self.render_pipelines.iter() { for (shader_id, pipeline) in self.render_pipelines.iter() {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Basic Renderer's Render Pass"), label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment { color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view, view: &view,
resolve_target: None, resolve_target: None,
@ -213,7 +394,34 @@ impl Renderer for BasicRenderer {
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
pipeline.render(&mut render_pass, 0..VERTICES.len() as u32, 0..1); render_pass.set_pipeline(pipeline.get_wgpu_pipeline());
let pipeline_jobs = jobs.get(shader_id);
if let Some(jobs) = pipeline_jobs {
for job in jobs.iter() {
let mesh = job.mesh();
let buffers = self.buffer_storage.get(&job.entity()).unwrap(); // TODO: create buffers if not made
if let Some(tex) = buffers.texture_bindgroup.as_ref() {
render_pass.set_bind_group(0, &tex, &[]);
}
render_pass.set_bind_group(1, &self.transform_bind_group, &[]);
if let Some(indices) = buffers.buffer_indices.as_ref() {
let indices_len = indices.count().unwrap(); // index buffers will have count, if not thats a bug
render_pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..));
render_pass.set_index_buffer(indices.buffer().slice(..), wgpu::IndexFormat::Uint16);
render_pass.draw_indexed(0..indices_len, 0, 0..1);
} else {
render_pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..));
render_pass.draw(0..mesh.vertices.len() as u32, 0..1);
}
}
//pipeline.render(&jobs, &mut render_pass, 0..VERTICES.len() as u32, 0..1);
}
} }
self.queue.submit(std::iter::once(encoder.finish())); self.queue.submit(std::iter::once(encoder.finish()));
@ -234,8 +442,4 @@ impl Renderer for BasicRenderer {
fn surface_size(&self) -> winit::dpi::PhysicalSize<u32> { fn surface_size(&self) -> winit::dpi::PhysicalSize<u32> {
self.size self.size
} }
fn add_render_pipeline(&mut self, pipeline: FullRenderPipeline) {
self.render_pipelines.push(pipeline);
}
} }

0
src/render/shader_loader.rs Executable file
View File

72
src/render/texture.rs Normal file → Executable file
View File

@ -1,19 +1,42 @@
use std::sync::Arc;
use image::GenericImageView; use image::GenericImageView;
#[allow(dead_code)] #[derive(Clone)]
pub struct Texture { pub struct Texture {
texture: wgpu::Texture, texture_id: u32,
view: wgpu::TextureView, pub img: image::DynamicImage,
sampler: wgpu::Sampler,
} }
impl Texture { impl Texture {
pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], label: &str) -> anyhow::Result<Self> { pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
let img = image::load_from_memory(bytes)?; let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label))
Ok(Self {
texture_id: 0,
img,
})
}
}
#[derive(Clone)]
#[allow(dead_code)]
pub struct RenderTexture {
texture_id: u32,
texture: Arc<wgpu::Texture>,
view: Arc<wgpu::TextureView>,
sampler: Arc<wgpu::Sampler>,
}
impl RenderTexture {
pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], texture_id: u32, label: &str) -> anyhow::Result<Self> {
let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, texture_id, Some(label))
} }
pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage, label: Option<&str> ) -> anyhow::Result<Self> { pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage, texture_id: u32, label: Option<&str>) -> anyhow::Result<Self> {
let rgba = img.to_rgba8(); let rgba = img.to_rgba8();
let dimensions = img.dimensions(); let dimensions = img.dimensions();
@ -64,10 +87,41 @@ impl Texture {
} }
); );
Ok(Self { texture, view, sampler }) Ok(Self {
texture_id,
texture: Arc::new(texture),
view: Arc::new(view),
sampler: Arc::new(sampler),
})
} }
pub fn texture(&self) -> &wgpu::Texture { pub fn update_texture(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, texture: &Texture) {
let rgba = texture.img.to_rgba8();
let dimensions = texture.img.dimensions();
let size = wgpu::Extent3d {
width: dimensions.0,
height: dimensions.1,
depth_or_array_layers: 1,
};
queue.write_texture(
wgpu::ImageCopyTexture {
aspect: wgpu::TextureAspect::All,
texture: &self.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
&rgba,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0),
rows_per_image: std::num::NonZeroU32::new(dimensions.1),
},
size,
);
}
pub fn inner_texture(&self) -> &wgpu::Texture {
&self.texture &self.texture
} }

46
src/render/transform.rs Executable file
View File

@ -0,0 +1,46 @@
use glam::Vec3;
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Transform {
matrix: glam::Mat4,
}
impl Default for Transform {
fn default() -> Self {
Self {
matrix: glam::Mat4::IDENTITY
}
}
}
impl Transform {
pub fn new() -> Self {
Self::default()
}
pub fn from_glam(matrix: glam::Mat4) -> Self {
Self {
matrix,
}
}
pub fn translate_vec3(&mut self, vec: Vec3) -> &mut Self {
/* let first_col = self.matrix.col(0);
let second_col = self.matrix.col(1);
let third_col = self.matrix.col(2);
//let fourth_col = self.matrix.col_mut(3);
let w_axis = &mut self.matrix.w_axis;
//first_col.w
//fourth_col = fourth_col.x * vec.x + second_col * vec.y + third_col * vec.z + fourth_col.clone();
w_axis.x += vec.x;
w_axis.y += vec.y;
w_axis.z += vec.z; */
let translation = glam::Mat4::from_translation(vec);
self.matrix *= translation;
self
}
}

0
src/render/vertex.rs Normal file → Executable file
View File

0
src/resources.rs Normal file → Executable file
View File