diff --git a/Cargo.lock b/Cargo.lock index 94418d4..0b87e89 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,15 +95,6 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "arrayref" version = "0.3.6" @@ -427,16 +418,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "codespan-reporting" version = "0.11.1" @@ -727,6 +708,15 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +[[package]] +name = "glam" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad83ab008a4fa3b31dfa713dd41b5a9bdea1e94e4cf1e2fc274ffbd49b0271d3" +dependencies = [ + "bytemuck", +] + [[package]] name = "gloo-timers" version = "0.2.6" @@ -791,7 +781,7 @@ checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a" dependencies = [ "bitflags", "gpu-descriptor-types", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -812,6 +802,15 @@ dependencies = [ "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]] name = "hassle-rs" version = "0.9.0" @@ -827,6 +826,16 @@ dependencies = [ "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]] name = "hermit-abi" version = "0.2.6" @@ -874,7 +883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -990,7 +999,8 @@ dependencies = [ "async-trait", "bytemuck", "cfg-if", - "cgmath", + "glam", + "hecs", "image", "instant", "specs", @@ -1569,7 +1579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "102269e720bb814df57e136161cad841f2b6f411e003ac748fc48aaf2363bea3" dependencies = [ "arrayvec", - "hashbrown", + "hashbrown 0.12.3", "mopa", "rayon", "shred-derive", @@ -1673,7 +1683,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea85dac2880f84d4025ff5ace80cda6d8bc43bc88b6a389b9277fcf894b51e9" dependencies = [ "crossbeam-queue", - "hashbrown", + "hashbrown 0.12.3", "hibitset", "log", "rayon", @@ -1694,6 +1704,12 @@ dependencies = [ "syn", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spirv" version = "0.2.0+1.5.4" diff --git a/Cargo.toml b/Cargo.toml index 77430fd..d27ee74 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,10 +17,12 @@ cfg-if = "1" bytemuck = { version = "1.12", features = [ "derive" ] } image = { version = "0.24", default-features = false, features = ["png", "jpeg"] } anyhow = "1.0" -cgmath = "0.18" +#cgmath = "0.18" tobj = { version = "3.2.1", features = [ "async", ]} instant = "0.1" async-trait = "0.1.65" specs = { version = "0.18.0", features = [ "derive" ] } +hecs = "0.10.3" +glam = { version = "0.24.0", features = ["bytemuck"] } diff --git a/res/happy-tree.png b/res/happy-tree.png old mode 100644 new mode 100755 diff --git a/res/shader.wgsl b/res/shader/base_2d.wgsl similarity index 81% rename from res/shader.wgsl rename to res/shader/base_2d.wgsl index 047129c..01e6b99 100755 --- a/res/shader.wgsl +++ b/res/shader/base_2d.wgsl @@ -10,13 +10,16 @@ struct VertexOutput { @location(0) tex_coords: vec2, } +@group(1) @binding(0) +var u_model_transform: mat4x4; + @vertex fn vs_main( model: VertexInput, ) -> VertexOutput { var out: VertexOutput; out.tex_coords = model.tex_coords; - out.clip_position = vec4(model.position, 1.0); + out.clip_position = u_model_transform * vec4(model.position, 1.0); return out; } diff --git a/src/ecs/components/mod.rs b/src/ecs/components/mod.rs old mode 100644 new mode 100755 index 99a2f12..c199d73 --- a/src/ecs/components/mod.rs +++ b/src/ecs/components/mod.rs @@ -1 +1,2 @@ -pub mod model_2d; \ No newline at end of file +pub mod model_2d; +pub mod transform; \ No newline at end of file diff --git a/src/ecs/components/model_2d.rs b/src/ecs/components/model_2d.rs old mode 100644 new mode 100755 index 112c3a4..ece71f9 --- a/src/ecs/components/model_2d.rs +++ b/src/ecs/components/model_2d.rs @@ -1,4 +1,18 @@ -#[derive(Component, Debug, Default)] -pub struct Model2d { - pub vertices: Vec, +use specs::{Component, DenseVecStorage}; + +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 + } + } } \ No newline at end of file diff --git a/src/ecs/components/transform.rs b/src/ecs/components/transform.rs new file mode 100755 index 0000000..ed69a85 --- /dev/null +++ b/src/ecs/components/transform.rs @@ -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 + } + } +} \ No newline at end of file diff --git a/src/ecs/mod.rs b/src/ecs/mod.rs old mode 100644 new mode 100755 diff --git a/src/game.rs b/src/game.rs index bf7395e..035d6c4 100755 --- a/src/game.rs +++ b/src/game.rs @@ -1,9 +1,8 @@ -use std::{sync::Arc}; +use std::{sync::Arc, cell::RefCell}; use async_std::{task::block_on, sync::Mutex}; -use specs::{Dispatcher, World}; - +use hecs::World; use tracing::{metadata::LevelFilter, info, debug, warn}; use tracing_subscriber::{ 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 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, renderer: Box, world: Arc>, - system_dispatchers: Vec>, } -impl<'a, 'b> GameLoop<'a, 'b> { - pub async fn new(window: Arc, world: Arc>, system_dispatchers: Vec>) -> GameLoop<'a, 'b> { +impl GameLoop { + pub async fn new(window: Arc, world: Arc>) -> GameLoop { Self { window: Arc::clone(&window), renderer: Box::new(BasicRenderer::create_with_window(window).await), world, - system_dispatchers, } } @@ -43,10 +40,10 @@ impl<'a, 'b> GameLoop<'a, 'b> { } 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() { dispatcher.dispatch(&world); - } + } */ } async fn input_update(&mut self, event: &InputEvent) -> Option { @@ -120,8 +117,41 @@ impl<'a, 'b> GameLoop<'a, 'b> { }, Event::RedrawRequested(window_id) if window_id == self.window.id() => { + // Update the world 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::(); + + 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 { Ok(_) => {} // 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>>, - system_dispatchers: Option>>, } -impl<'a, 'b> Default for Game<'a, 'b> { +impl Default for Game { fn default() -> Self { Self { world: None, - system_dispatchers: None, } } } -impl<'a, 'b> Game<'static, 'static> { - pub async fn initialize() -> Game<'static, 'static> { +impl Game { + pub async fn initialize() -> Game { let filter = FilterFn::new(|metadata| { metadata.module_path() .unwrap_or_else(|| metadata.target()) @@ -184,27 +212,13 @@ impl<'a, 'b> Game<'static, 'static> { self } - pub fn with_dispatchers(&mut self, dispatchers: Vec>) -> &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) { 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 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| { g_loop.run_sync(event, control_flow); diff --git a/src/main.rs b/src/main.rs index 96b7637..a518f65 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,20 @@ -mod system; mod game; mod render; mod input_event; mod resources; +mod ecs; +use ecs::components::model_2d::Model2dComponent; + +use ecs::components::transform::TransformComponent; 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 { x: i32, y: i32, @@ -28,7 +35,7 @@ impl Point2d { } } -#[derive(Component, Debug, Default)] +#[derive(Debug, Default)] struct Point3d { x: 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 fn main() { let mut world = World::new(); - world.register::(); + + 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::(); world.register::(); world.create_entity() @@ -89,7 +101,7 @@ async fn main() { Game::initialize().await .with_world(world) .with_dispatcher(dispatcher) - .run().await; + .run().await; */ /* let mut game = Game::initialize().await; game.run().await; */ diff --git a/src/render/desc_buf_lay.rs b/src/render/desc_buf_lay.rs old mode 100644 new mode 100755 diff --git a/src/render/material.rs b/src/render/material.rs new file mode 100755 index 0000000..d89ae1c --- /dev/null +++ b/src/render/material.rs @@ -0,0 +1,7 @@ +use super::texture::Texture; + +#[derive(Clone)] +pub struct Material { + pub shader_id: u32, + pub texture: Texture, +} \ No newline at end of file diff --git a/src/render/mesh.rs b/src/render/mesh.rs old mode 100644 new mode 100755 index 5ad9014..419e2d6 --- a/src/render/mesh.rs +++ b/src/render/mesh.rs @@ -2,28 +2,23 @@ use wgpu::{BindGroup, BindGroupLayout}; use super::{vertex::Vertex, render_buffer::{BufferStorage}}; +#[derive(Clone)] pub struct Mesh { - //material - //texture: Option, pub vertices: Vec, pub indices: Option>, - pub buffer_vertex: BufferStorage, + /* pub buffer_vertex: BufferStorage, pub buffer_indices: Option, // TOOD: make optional pub texture_bindgroup: Option, - pub texture_layout: Option + pub texture_layout: Option */ } impl Mesh { - pub fn new(vertices: Vec, indices: Option>, buffer_vertex: BufferStorage, buffer_indices: Option, texture_bindgroup: Option, texture_layout: Option) -> Self { + pub fn new(vertices: Vec, indices: Option>) -> Self { Self { vertices, indices, - buffer_vertex, - buffer_indices, - texture_bindgroup, - texture_layout, } } } \ No newline at end of file diff --git a/src/render/mod.rs b/src/render/mod.rs old mode 100644 new mode 100755 index 1db13c7..130b3bd --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -5,4 +5,7 @@ pub mod desc_buf_lay; pub mod render_buffer; pub mod render_job; pub mod mesh; -pub mod texture; \ No newline at end of file +pub mod texture; +pub mod shader_loader; +pub mod material; +pub mod transform; \ No newline at end of file diff --git a/src/render/render_buffer.rs b/src/render/render_buffer.rs old mode 100644 new mode 100755 diff --git a/src/render/render_job.rs b/src/render/render_job.rs old mode 100644 new mode 100755 index 665a406..5ed1cdd --- a/src/render/render_job.rs +++ b/src/render/render_job.rs @@ -1,17 +1,31 @@ -use super::{mesh::Mesh}; +use hecs::Entity; + +use super::{mesh::Mesh, material::Material}; pub struct RenderJob { - mesh: Mesh + mesh: Mesh, + material: Material, + entity: Entity, } impl RenderJob { - pub fn new(mesh: Mesh) -> Self { + pub fn new(mesh: Mesh, material: Material, entity: Entity) -> Self { Self { mesh, + material, + entity, } } pub fn mesh(&self)-> &Mesh { &self.mesh } + + pub fn material(&self)-> &Material { + &self.material + } + + pub fn entity(&self)-> Entity { + self.entity + } } \ No newline at end of file diff --git a/src/render/render_pipeline.rs b/src/render/render_pipeline.rs old mode 100644 new mode 100755 index c78ca9d..5325e54 --- a/src/render/render_pipeline.rs +++ b/src/render/render_pipeline.rs @@ -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}; pub struct FullRenderPipeline { layout: PipelineLayout, wgpu_pipeline: RenderPipeline, - jobs: Vec, } impl FullRenderPipeline { - pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, shader: &wgpu::ShaderModule, jobs: Vec) -> Self { + pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, shader: &wgpu::ShaderModule, buffer_layouts: Vec, bind_group_layouts: Vec<&BindGroupLayout>) -> FullRenderPipeline { // Extract the layouts from all the jobs - let mut buffer_layouts = vec![]; + /* let mut buffer_layouts = vec![]; let mut bind_group_layouts = vec![]; for job in jobs.iter() { // 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() { bind_group_layouts.push(layout); } - } + } */ let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { @@ -72,7 +71,6 @@ impl FullRenderPipeline { Self { layout: render_pipeline_layout, wgpu_pipeline: render_pipeline, - jobs, } } @@ -86,10 +84,11 @@ impl FullRenderPipeline { &self.wgpu_pipeline } - pub fn render<'a>(&'a self, pass: &mut RenderPass<'a>, vertices: Range, instances: Range) { - pass.set_pipeline(&self.wgpu_pipeline); + pub fn render<'a>(&'a self, jobs: &'a Vec, pass: &'a mut RenderPass<'a>, vertices: Range, instances: Range) { + todo!() + /* pass.set_pipeline(&self.wgpu_pipeline); - for job in self.jobs.iter() { + for job in jobs.iter() { let mesh = job.mesh(); 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.draw(vertices.clone(), instances.clone()); } - } + } */ } } \ No newline at end of file diff --git a/src/render/renderer.rs b/src/render/renderer.rs index d9b11fb..a3443e5 100755 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -1,23 +1,43 @@ +use std::cell::RefCell; +use std::collections::HashMap; use std::sync::Arc; use std::borrow::Cow; +use async_std::sync::Mutex; use async_trait::async_trait; +use wgpu::{BindGroup, BindGroupLayout}; use wgpu::util::DeviceExt; 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 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}; #[async_trait] pub trait Renderer { + async fn prepare(&mut self, main_world: &World); async fn render(&mut self) -> Result<(), wgpu::SurfaceError>; async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize); fn surface_size(&self) -> winit::dpi::PhysicalSize; - fn add_render_pipeline(&mut self, pipeline: FullRenderPipeline); + fn add_render_pipeline(&mut self, shader_id: u32, pipeline: Arc); +} + +struct RenderBufferStorage { + buffer_vertex: BufferStorage, + buffer_indices: Option, + + render_texture: Option, + texture_bindgroup: Option, + texture_layout: Option } pub struct BasicRenderer { @@ -30,7 +50,14 @@ pub struct BasicRenderer { pub clear_color: wgpu::Color, - pub render_pipelines: Vec, + pub render_pipelines: HashMap>, + pub render_jobs: HashMap>, + + buffer_storage: HashMap, // TODO: clean up left over buffers from deleted entities/components + + transform_uniform: Transform, + transform_bind_group: wgpu::BindGroup, + transform_buffer: wgpu::Buffer, } impl BasicRenderer { @@ -89,8 +116,8 @@ impl BasicRenderer { surface.configure(&device, &config); - 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_bytes = include_bytes!("../../res/happy-tree.png"); + let diffuse_texture = RenderTexture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap(); */ let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { @@ -117,7 +144,171 @@ impl BasicRenderer { 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 { layout: &texture_bind_group_layout, entries: &[ @@ -134,63 +325,53 @@ impl BasicRenderer { } ); - - let shader_src = resources::load_string("shader.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 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, + RenderBufferStorage { + buffer_vertex: BufferStorage::new(vertex_buffer, 0, None), + buffer_indices, + render_texture: None, + texture_layout: None, + texture_bindgroup: Some(diffuse_bind_group), } } } #[async_trait] 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) { + self.render_pipelines.insert(shader_id, pipeline); + } async fn render(&mut self) -> Result<(), wgpu::SurfaceError> { + let jobs = &self.render_jobs; + let output = self.surface.get_current_texture()?; 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. - 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 { - label: Some("Basic Renderer's Render Pass"), + label: Some("Render Pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &view, resolve_target: None, @@ -213,7 +394,34 @@ impl Renderer for BasicRenderer { 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())); @@ -234,8 +442,4 @@ impl Renderer for BasicRenderer { fn surface_size(&self) -> winit::dpi::PhysicalSize { self.size } - - fn add_render_pipeline(&mut self, pipeline: FullRenderPipeline) { - self.render_pipelines.push(pipeline); - } } \ No newline at end of file diff --git a/src/render/shader_loader.rs b/src/render/shader_loader.rs new file mode 100755 index 0000000..e69de29 diff --git a/src/render/texture.rs b/src/render/texture.rs old mode 100644 new mode 100755 index 69a46bf..9e60174 --- a/src/render/texture.rs +++ b/src/render/texture.rs @@ -1,19 +1,42 @@ +use std::sync::Arc; + use image::GenericImageView; -#[allow(dead_code)] +#[derive(Clone)] pub struct Texture { - texture: wgpu::Texture, - view: wgpu::TextureView, - sampler: wgpu::Sampler, + texture_id: u32, + pub img: image::DynamicImage, } impl Texture { - pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], label: &str) -> anyhow::Result { + pub fn from_bytes(bytes: &[u8]) -> anyhow::Result { 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, + view: Arc, + sampler: Arc, +} + +impl RenderTexture { + pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], texture_id: u32, label: &str) -> anyhow::Result { + 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 { + pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage, texture_id: u32, label: Option<&str>) -> anyhow::Result { let rgba = img.to_rgba8(); 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 } diff --git a/src/render/transform.rs b/src/render/transform.rs new file mode 100755 index 0000000..6e566af --- /dev/null +++ b/src/render/transform.rs @@ -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 + } +} \ No newline at end of file diff --git a/src/render/vertex.rs b/src/render/vertex.rs old mode 100644 new mode 100755 diff --git a/src/resources.rs b/src/resources.rs old mode 100644 new mode 100755