diff --git a/Cargo.lock b/Cargo.lock index 84ec05e..c166bf4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,6 +349,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "combine" version = "4.6.7" @@ -1096,6 +1105,9 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "mars" version = "0.1.0" +dependencies = [ + "colored", +] [[package]] name = "memchr" diff --git a/game.core b/game.core new file mode 100644 index 0000000..e541f3a Binary files /dev/null and b/game.core differ diff --git a/src/app.rs b/src/app.rs index 37f3ede..0907c8e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,312 +1,345 @@ use std::collections::HashMap; // Credit of most code to https://sotrh.github.io/learn-wgpu/ since I'm not familiar with wgpu +use crate::render::Renderer; +use crate::world::{SimpleObject, World}; +use glam::{Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4}; use std::sync::Arc; -use glam::{Vec2, Affine3A, Vec3, Vec4, Mat4, EulerRot, Quat}; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; use winit::dpi::PhysicalPosition; #[cfg(target_arch = "wasm32")] use winit::platform::web::EventLoopExtWebSys; use winit::{ - application::ApplicationHandler, - event::*, - event_loop::{ActiveEventLoop, EventLoop}, - keyboard::{KeyCode, PhysicalKey}, - window::Window, + application::ApplicationHandler, + event::*, + event_loop::{ActiveEventLoop, EventLoop}, + keyboard::{KeyCode, PhysicalKey}, + window::Window, }; -use crate::render; -use crate::render::{Renderer, SimpleTexture}; -use crate::world::{SimpleObject, World}; struct Controller { - buttons: HashMap, - keys: HashMap, - mouse: Vec2, + buttons: HashMap, + keys: HashMap, + mouse: Vec2, } impl Controller { - fn new() -> Controller { - Controller { - buttons: Default::default(), - keys: HashMap::new(), - mouse: Vec2::new(0.0,0.0), + fn new() -> Controller { + Controller { + buttons: Default::default(), + keys: HashMap::new(), + mouse: Vec2::new(0.0, 0.0), + } } - } } pub struct AppState { - world: World, - controller: Controller, - window: Arc, - clients: Vec, + world: World, + controller: Controller, + window: Arc, + clients: Vec, } -const BLOCKS: i32 = 50; +const BLOCKS: i32 = 1; impl AppState { - // We don't need this to be async right now, - // but we will in the next tutorial - pub async fn new(window: Arc) -> Result> { - let size = window.inner_size(); - let mut world = World::new(); - world.add_renderer(Renderer::new(&window).await?); - let mut clients = Vec::new(); - { - let file = world.renderer.as_mut().unwrap().load_from_gltf( include_bytes!("assets/cube.glb")); - let block = file.first_object().unwrap(); - //let color = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/color.png")); - //let normal = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/normal.png")); - //let roughness = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png")); - //let mat = renderer.new_material(color,normal,roughness); - for x in -BLOCKS..BLOCKS { - for y in -BLOCKS..BLOCKS { - let block = block.hard_clone(); - world.add_object(block.clone()); - { - let mut model = block.0.borrow_mut(); - model.model.as_mut().unwrap().instance.transform = Mat4::from_translation(Vec3::new(-x as f32 * 3.0,0.0,-y as f32 * 3.0)) - * Mat4::from_rotation_z((x * y) as f32 / 1.23); - model.model.as_mut().unwrap().instance.color = Vec4::new(0.3,(x + BLOCKS) as f32 / BLOCKS as f32,(y + BLOCKS) as f32 / BLOCKS as f32,1.0); - } - clients.push(block) + // We don't need this to be async right now, + // but we will in the next tutorial + pub async fn new(window: Arc) -> Result> { + let mut world = World::new(); + world.add_renderer(Renderer::new(&window).await?); + let mut clients = Vec::new(); + { + let file = world + .renderer + .as_mut() + .unwrap() + .load_from_gltf(include_bytes!("assets/cube.glb")); + let block = file.first_object().unwrap(); + let skybox = world.renderer.as_mut().unwrap().load_texture_from_bytes( + include_bytes!("assets/skybox1.png"), + Some(image::ImageFormat::Png), + ); + world.renderer.as_mut().unwrap().set_skybox(skybox); + //let color = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/color.png")); + //let normal = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/normal.png")); + //let roughness = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png")); + //let mat = renderer.new_material(color,normal,roughness); + for x in -BLOCKS..BLOCKS { + for y in -BLOCKS..BLOCKS { + let block = block.hard_clone(); + world.add_object(block.clone()); + { + let mut model = block.0.borrow_mut(); + model.model.as_mut().unwrap().instance.transform = Mat4::from_translation( + Vec3::new(-x as f32 * 3.0, -8.0, -y as f32 * 3.0), + ) + * Mat4::from_rotation_z((x * y) as f32 / 1.23); + model.model.as_mut().unwrap().instance.color = Vec4::new( + 0.3, + (x + BLOCKS) as f32 / BLOCKS as f32, + (y + BLOCKS) as f32 / BLOCKS as f32, + 1.0, + ); + } + clients.push(block) + } + } } - } + Ok(Self { + world, + clients, + controller: Controller::new(), + window, + }) } - Ok(Self { - world, - clients, - controller: Controller::new(), - window, - }) - } - pub fn bounds(&self) -> (u32,u32) { - let size = self.window.inner_size(); - (size.width,size.height) - } - - pub fn resize(&mut self, width: u32, height: u32) { - if width > 0 && height > 0 { - let max = 2048; - self.world.renderer.as_mut().unwrap().resize(width,height); + pub fn bounds(&self) -> (u32, u32) { + let size = self.window.inner_size(); + (size.width, size.height) } - } - fn update(&mut self) { - // ... - } - - fn handle_key(&mut self, event_loop: &ActiveEventLoop, code: KeyCode, is_pressed: bool) { - match (code, is_pressed) { - (KeyCode::Escape, true) => event_loop.exit(), - (KeyCode::Space, true) => {}, - (KeyCode::KeyR, true) => { - self.world.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY - } - _ => {} + pub fn resize(&mut self, width: u32, height: u32) { + if width > 0 && height > 0 { + self.world.renderer.as_mut().unwrap().resize(width, height); + } } - self.controller.keys.insert(code, is_pressed); - } - fn handle_mouse_moved(&mut self, position: PhysicalPosition) { - if let Some(true) = self.controller.buttons.get(&MouseButton::Right) { - self.world.renderer.as_mut().unwrap().eye.rotate(position.x as f32 - self.controller.mouse.x, position.y as f32 - self.controller.mouse.y); + fn update(&mut self) { + // ... } - self.controller.mouse = Vec2::new(position.x as f32, position.y as f32); - } - fn handle_mouse_button(&mut self, button: MouseButton, state: ElementState ) { - self.controller.buttons.insert(button,state.is_pressed()); - } + fn handle_key(&mut self, event_loop: &ActiveEventLoop, code: KeyCode, is_pressed: bool) { + match (code, is_pressed) { + (KeyCode::Escape, true) => event_loop.exit(), + (KeyCode::Space, true) => {} + (KeyCode::KeyR, true) => { + self.world.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY + } + _ => {} + } + self.controller.keys.insert(code, is_pressed); + } + + fn handle_mouse_moved(&mut self, position: PhysicalPosition) { + if let Some(true) = self.controller.buttons.get(&MouseButton::Right) { + self.world.renderer.as_mut().unwrap().eye.rotate( + position.x as f32 - self.controller.mouse.x, + position.y as f32 - self.controller.mouse.y, + ); + } + self.controller.mouse = Vec2::new(position.x as f32, position.y as f32); + } + + fn handle_mouse_button(&mut self, button: MouseButton, state: ElementState) { + self.controller.buttons.insert(button, state.is_pressed()); + } } pub struct App { - #[cfg(target_arch = "wasm32")] - proxy: Option>, - state: Option, + #[cfg(target_arch = "wasm32")] + proxy: Option>, + state: Option, } impl App { - pub fn new(#[cfg(target_arch = "wasm32")] event_loop: &EventLoop) -> Self { - #[cfg(target_arch = "wasm32")] - let proxy = Some(event_loop.create_proxy()); - Self { - state: None, - #[cfg(target_arch = "wasm32")] - proxy, + pub fn new(#[cfg(target_arch = "wasm32")] event_loop: &EventLoop) -> Self { + #[cfg(target_arch = "wasm32")] + let proxy = Some(event_loop.create_proxy()); + Self { + state: None, + #[cfg(target_arch = "wasm32")] + proxy, + } } - } } impl ApplicationHandler for App { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + #[allow(unused_mut)] + let mut window_attributes = Window::default_attributes(); + + #[cfg(target_arch = "wasm32")] + { + use wasm_bindgen::JsCast; + use winit::platform::web::WindowAttributesExtWebSys; + + const CANVAS_ID: &str = "canvas"; + + let window = wgpu::web_sys::window().unwrap_throw(); + let document = window.document().unwrap_throw(); + let canvas = document.get_element_by_id(CANVAS_ID).unwrap_throw(); + let html_canvas_element = canvas.unchecked_into(); + window_attributes = window_attributes.with_canvas(Some(html_canvas_element)); + } + + let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); + + #[cfg(not(target_arch = "wasm32"))] + { + // If we are not on web we can use pollster to + // await the window creation + self.state = Some(pollster::block_on(AppState::new(window)).unwrap()); + } + + #[cfg(target_arch = "wasm32")] + { + // Run the future asynchronously and use the + // proxy to send the results to the event loop + if let Some(proxy) = self.proxy.take() { + wasm_bindgen_futures::spawn_local(async move { + assert!( + proxy + .send_event( + AppState::new(window) + .await + .expect("Unable to create canvas!!!") + ) + .is_ok() + ) + }); + } + } + } + #[allow(unused_mut)] - let mut window_attributes = Window::default_attributes(); - - #[cfg(target_arch = "wasm32")] - { - use wasm_bindgen::JsCast; - use winit::platform::web::WindowAttributesExtWebSys; - - const CANVAS_ID: &str = "canvas"; - - let window = wgpu::web_sys::window().unwrap_throw(); - let document = window.document().unwrap_throw(); - let canvas = document.get_element_by_id(CANVAS_ID).unwrap_throw(); - let html_canvas_element = canvas.unchecked_into(); - window_attributes = window_attributes.with_canvas(Some(html_canvas_element)); + fn user_event(&mut self, _event_loop: &ActiveEventLoop, mut event: AppState) { + // This is where proxy.send_event() ends up + #[cfg(target_arch = "wasm32")] + { + event.window.request_redraw(); + event.resize( + event.window.inner_size().width, + event.window.inner_size().height, + ); + } + self.state = Some(event); } - let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); - - #[cfg(not(target_arch = "wasm32"))] - { - // If we are not on web we can use pollster to - // await the window creation - self.state = Some(pollster::block_on(AppState::new(window)).unwrap()); - } - - #[cfg(target_arch = "wasm32")] - { - // Run the future asynchronously and use the - // proxy to send the results to the event loop - if let Some(proxy) = self.proxy.take() { - wasm_bindgen_futures::spawn_local(async move { - assert!( - proxy - .send_event( - AppState::new(window) - .await - .expect("Unable to create canvas!!!") - ) - .is_ok() - ) - }); - } - } - } - - #[allow(unused_mut)] - fn user_event(&mut self, _event_loop: &ActiveEventLoop, mut event: AppState) { - // This is where proxy.send_event() ends up - #[cfg(target_arch = "wasm32")] - { - event.window.request_redraw(); - event.resize( - event.window.inner_size().width, - event.window.inner_size().height, - ); - } - self.state = Some(event); - } - - fn window_event( - &mut self, - event_loop: &ActiveEventLoop, - _window_id: winit::window::WindowId, - event: WindowEvent, - ) { - let state = match &mut self.state { - Some(canvas) => canvas, - None => return, - }; - - match event { - WindowEvent::CloseRequested => event_loop.exit(), - WindowEvent::Resized(size) => state.resize(size.width, size.height), - WindowEvent::RedrawRequested => { - state.update(); - let mut movement = Vec3::new(0.0,0.0,0.0); - - let pressed = |keycode: KeyCode| { - if let Some(true) = state.controller.keys.get(&keycode) { - true - } else { - false - } + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: winit::window::WindowId, + event: WindowEvent, + ) { + let state = match &mut self.state { + Some(canvas) => canvas, + None => return, }; - if pressed(KeyCode::KeyA) { - movement.x -= 1.0; - } - if pressed(KeyCode::KeyD) { - movement.x += 1.0; - } - if pressed(KeyCode::KeyW) { - movement.z += 1.0; - } - if pressed(KeyCode::KeyS) { - movement.z -= 1.0; - } - if pressed(KeyCode::KeyE) { - movement.y += 1.0; - } - if pressed(KeyCode::KeyQ) { - movement.y -= 1.0; - } + match event { + WindowEvent::CloseRequested => event_loop.exit(), + WindowEvent::Resized(size) => state.resize(size.width, size.height), + WindowEvent::RedrawRequested => { + state.update(); + let mut movement = Vec3::new(0.0, 0.0, 0.0); - state.world.renderer.as_mut().unwrap().eye.control(movement * 0.1); - match state.world.renderer.as_mut().unwrap().render(&state.window) { - Ok(_) => {} - Err(e) => { - // Log the error and exit gracefully - log::error!("{e}"); - event_loop.exit(); - } + let pressed = |keycode: KeyCode| { + if let Some(true) = state.controller.keys.get(&keycode) { + true + } else { + false + } + }; + + if pressed(KeyCode::KeyA) { + movement.x -= 1.0; + } + if pressed(KeyCode::KeyD) { + movement.x += 1.0; + } + if pressed(KeyCode::KeyW) { + movement.z += 1.0; + } + if pressed(KeyCode::KeyS) { + movement.z -= 1.0; + } + if pressed(KeyCode::KeyE) { + movement.y += 1.0; + } + if pressed(KeyCode::KeyQ) { + movement.y -= 1.0; + } + + state + .world + .renderer + .as_mut() + .unwrap() + .eye + .control(movement * 0.1); + match state.world.renderer.as_mut().unwrap().render(&state.window) { + Ok(_) => {} + Err(e) => { + // Log the error and exit gracefully + log::error!("{e}"); + event_loop.exit(); + } + } + for object in state.clients.iter() { + object + .0 + .borrow_mut() + .model + .as_mut() + .unwrap() + .instance + .transform *= Mat4::from_rotation_translation( + Quat::from_euler(EulerRot::XYZ, 0.001, -0.001, 0.001), + Vec3::new(0.0, 0.0, 0.0), + ); + } + } + WindowEvent::MouseInput { + button, + state: element, + .. + } => state.handle_mouse_button(button, element), + WindowEvent::CursorMoved { position: pos, .. } => state.handle_mouse_moved(pos), + WindowEvent::KeyboardInput { + event: + KeyEvent { + physical_key: PhysicalKey::Code(code), + state: key_state, + .. + }, + .. + } => state.handle_key(event_loop, code, key_state.is_pressed()), + _ => {} } - for object in state.clients.iter() { - object.0.borrow_mut().model.as_mut().unwrap().instance.transform *= Mat4::from_rotation_translation( - Quat::from_euler(EulerRot::XYZ,0.001,-0.001,0.001), - Vec3::new(0.0,0.0,0.0) - ); - } - } - WindowEvent::MouseInput { button, state: element, .. } => state.handle_mouse_button(button,element), - WindowEvent::CursorMoved { position: pos, .. } => state.handle_mouse_moved(pos), - WindowEvent::KeyboardInput { - event: - KeyEvent { - physical_key: PhysicalKey::Code(code), - state: key_state, - .. - }, - .. - } => state.handle_key(event_loop, code, key_state.is_pressed()), - _ => {} } - } } pub fn run() -> anyhow::Result<()> { - #[cfg(not(target_arch = "wasm32"))] - { - env_logger::init(); - } - #[cfg(target_arch = "wasm32")] - { - console_log::init_with_level(log::Level::Info).unwrap_throw(); - } + #[cfg(not(target_arch = "wasm32"))] + { + env_logger::init(); + } + #[cfg(target_arch = "wasm32")] + { + console_log::init_with_level(log::Level::Info).unwrap_throw(); + } - let event_loop = EventLoop::with_user_event().build()?; - #[cfg(not(target_arch = "wasm32"))] - { - let mut app = App::new(); - event_loop.run_app(&mut app)?; - } - #[cfg(target_arch = "wasm32")] - { - let app = App::new(&event_loop); - event_loop.spawn_app(app); - } + let event_loop = EventLoop::with_user_event().build()?; + #[cfg(not(target_arch = "wasm32"))] + { + let mut app = App::new(); + event_loop.run_app(&mut app)?; + } + #[cfg(target_arch = "wasm32")] + { + let app = App::new(&event_loop); + event_loop.spawn_app(app); + } - Ok(()) + Ok(()) } #[cfg(target_arch = "wasm32")] #[wasm_bindgen(start)] pub fn run_web() -> Result<(), wasm_bindgen::JsValue> { - console_error_panic_hook::set_once(); - run().unwrap_throw(); + console_error_panic_hook::set_once(); + run().unwrap_throw(); - Ok(()) + Ok(()) } diff --git a/src/assets/SimpleShader.wgsl b/src/assets/SimpleShader.wgsl index 1f22a81..7a9a592 100644 --- a/src/assets/SimpleShader.wgsl +++ b/src/assets/SimpleShader.wgsl @@ -4,16 +4,26 @@ struct Environment { dir: vec4, } -struct View { +struct Eye { + // from camera to screen + proj: mat4x4, + // from screen to camera + inv: mat4x4, + // world to camera view: mat4x4, + // camera transform frame: mat4x4, } // Vertex shader @group(0) @binding(0) -var view: View; +var eye: Eye; @group(0) @binding(1) var environment: Environment; +@group(0) @binding(2) +var sky_sampler: sampler; +@group(0) @binding(3) +var sky_texture: texture_2d; struct VertexInput { @location(0) position: vec3, @@ -60,7 +70,7 @@ fn vs_main( out.world_normal = model_rot_matrix * model.normal; var world_position: vec4 = model_matrix * vec4(model.position, 1.0); out.world_position = world_position.xyz; - out.clip_position = view.view * world_position; + out.clip_position = eye.proj * eye.view * world_position; return out; } @@ -75,23 +85,79 @@ var t_normal: texture_2d; @group(1) @binding(3) var t_rough: texture_2d; +fn sky_aspect(look: vec3) -> vec4 { + var pi = 3.14159; + let u_angle = atan2(look.x,look.z); + let u = (u_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1 + let v_angle = atan2(-look.y,sqrt(look.x * look.x + look.z * look.z)); + let v = (v_angle/pi) + 0.5;//(v_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1 + let uv = vec2(u,v); // Get UV on skybox + return textureSample(sky_texture, sky_sampler, uv); +} + +fn rotation(mat: mat4x4) -> mat3x3 { + return mat3x3( + mat[0].xyz, + mat[1].xyz, + mat[2].xyz, + ); +} + +fn translation(mat: mat4x4) -> vec4 { + //return vec4(mat[0][3],mat[1][3],mat[2][3],mat[3][3]); + return mat[3]; +} + @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); let object_normal: vec4 = textureSample(t_normal, s_diffuse, in.tex_coords); - let tangent_normal = object_normal.xyz * 2.0 - 1.0; - let light_dir = normalize(environment.dir.xyz); - let view_dir = normalize(view.frame[3].xyz - in.world_position); - let half_dir = normalize(view_dir + light_dir); + let diffuse_color = object_color.xyz; + let specular_color = vec3(0.0,0.0,0.0); + let reflection = sky_aspect(reflect(normalize(in.world_position-translation(eye.frame).xyz),normalize(in.world_normal))); - let diffuse_strength = max(dot(tangent_normal, light_dir), 0.0); - let diffuse_color = environment.light.xyz * diffuse_strength; + //let result = (environment.ambient.xyz + diffuse_color + specular_color) * object_color.xyz; - let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0); - let specular_color = specular_strength * environment.light.xyz; + //let light_dir = normalize(environment.dir.xyz); + //let view_dir = normalize(eye.frame[3].xyz - in.world_position); + //let half_dir = normalize(view_dir + light_dir); + + //let diffuse_strength = max(dot(tangent_normal, light_dir), 0.0); + //let diffuse_color = environment.light.xyz * diffuse_strength; + + //let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0); + //let specular_color = specular_strength * environment.light.xyz; let result = (environment.ambient.xyz + diffuse_color.xyz + specular_color.xyz) * object_color.xyz; - return vec4(result.xyz,object_color.a); -} \ No newline at end of file + return vec4(reflection.xyz,object_color.a); +} + +struct SkyOutput { + @builtin(position) position: vec4, + @location(0) pos: vec4 // unadulterated by WGSL +} + +const TRI_VERTICES = array( + vec4(-1.0, -1.0, 1.0, 1.0), + vec4(-1.0, 1.0, 1.0, 1.0), + vec4( 1.0, -1.0, 1.0, 1.0), + vec4( 1.0, 1.0, 1.0, 1.0), + vec4(-1.0, 1.0, 1.0, 1.0), + vec4( 1.0, -1.0, 1.0, 1.0), +); + +@vertex +fn vs_sky(@builtin(vertex_index) index: u32) -> SkyOutput { + var out: SkyOutput; + out.position = TRI_VERTICES[index]; + out.pos = out.position; + return out; +} + +@fragment +fn fs_sky(in: SkyOutput) -> @location(0) vec4 { + let look = rotation(eye.frame) * in.pos.xyz; + return sky_aspect(look); +} diff --git a/src/assets/cube.glb b/src/assets/cube.glb new file mode 100644 index 0000000..461ea6c Binary files /dev/null and b/src/assets/cube.glb differ diff --git a/src/assets/skybox1.png b/src/assets/skybox1.png new file mode 100644 index 0000000..a526667 Binary files /dev/null and b/src/assets/skybox1.png differ diff --git a/src/assets/test.png b/src/assets/test.png deleted file mode 100755 index 9de6980..0000000 Binary files a/src/assets/test.png and /dev/null differ diff --git a/src/render/eye.rs b/src/render/eye.rs index 4d03e14..11b7112 100644 --- a/src/render/eye.rs +++ b/src/render/eye.rs @@ -1,131 +1,187 @@ +use crate::render::{MaterialProperties, SimpleTexture}; use bytemuck::{Pod, Zeroable}; -use env_logger::Env; -use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4}; use glam::camera::lh::proj::directx::perspective; -use wgpu::{Device, Queue}; +use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4}; use wgpu::util::DeviceExt; +use wgpu::{Device, Queue}; pub(crate) struct Eye { - pub(crate) frame: Affine3A, - pub(crate) environment: Environment, - aspect_ratio: f32, - z_near: f32, - z_far: f32, - fov_y: f32, - pub(crate) environment_buffer: wgpu::Buffer, - pub(crate) buffer: wgpu::Buffer, - pub(crate) layout: wgpu::BindGroupLayout, - pub(crate) group: wgpu::BindGroup, + pub(crate) frame: Affine3A, + pub(crate) environment: Environment, + aspect_ratio: f32, + z_near: f32, + z_far: f32, + fov_y: f32, + pub(crate) environment_buffer: wgpu::Buffer, + pub(crate) camera_buffer: wgpu::Buffer, + pub(crate) layout: wgpu::BindGroupLayout, + pub(crate) group: wgpu::BindGroup, } #[repr(C)] #[derive(Pod, Copy, Clone, Zeroable)] -struct Environment { - ambient: Vec4, - light: Vec4, - dir: Vec4, +pub struct Environment { + ambient: Vec4, + light: Vec4, + dir: Vec4, } impl Eye { - pub(crate) fn view(&self) -> Mat4 { - let projection = perspective(self.fov_y, self.aspect_ratio, self.z_near, self.z_far); - projection * self.frame.inverse() - } - pub(crate) fn write(&mut self, queue: &Queue) { - queue.write_buffer(&self.buffer,0,bytemuck::cast_slice(&[ - self.view(), - Mat4::from_mat3_translation(self.frame.matrix3.into(), Vec3::from(self.frame.translation)) - ])); - queue.write_buffer(&self.environment_buffer,0,bytemuck::cast_slice(&[self.environment])); - } - pub(crate) fn new(device: &Device, width: u32, height: u32) -> Eye { - let buffer = device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("Camera Buffer"), - contents: bytemuck::cast_slice(&[Mat4::from_translation(Vec3::new(0.0,2.0,-8.0)),Mat4::IDENTITY]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - } - ); - - let dir = Vec3::new(1.0,0.5,1.0).normalize(); - let environment = Environment { - ambient: Vec4::new(0.15,0.15,0.15, 0.0), - light: Vec4::new(1.0,1.0,1.0, 0.0), - dir: Vec4::new(dir.x,dir.y,dir.z,0.0), - }; - - let environment_buffer = device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some(""), - contents: bytemuck::cast_slice(&[environment]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - } - ); - - let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - } - ], - label: Some("eye_bind_group_layout"), - }); - - let group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: buffer.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: environment_buffer.as_entire_binding(), - } - ], - label: Some("eye_bind_group"), - }); - - Eye { - aspect_ratio: width as f32 / height as f32, - frame: Affine3A::IDENTITY, - fov_y: 90.0, - z_near: 0.1, - z_far: 1000.0, - buffer, - group, - layout, - environment, - environment_buffer, + pub(crate) fn write(&mut self, queue: &Queue) { + let camera = Mat4::from_mat3_translation( + self.frame.matrix3.into(), + Vec3::from(self.frame.translation), + ); + let projection = perspective(self.fov_y, self.aspect_ratio, self.z_near, self.z_far); + queue.write_buffer( + &self.camera_buffer, + 0, + bytemuck::cast_slice(&[ + projection, + camera * projection.inverse(), + camera.inverse(), + camera, + ]), + ); + queue.write_buffer( + &self.environment_buffer, + 0, + bytemuck::cast_slice(&[self.environment]), + ); } - } - pub(crate) fn resize(&mut self, width: u32, height: u32) { - self.aspect_ratio = width as f32 / height as f32 - } - pub(crate) fn control(&mut self, delta: Vec3) { - self.frame *= Affine3A::from_translation(delta); - } - pub(crate) fn rotate(&mut self, yaw: f32, pitch: f32) { - let (mut y,mut x,z) = self.frame.matrix3.to_euler(EulerRot::YXZ); - x = (x + pitch * 0.005).clamp(-1.4,1.4); - y += yaw * 0.005; - self.frame.matrix3 = Mat3A::from_euler(EulerRot::YXZ,y,x,z); - } -} \ No newline at end of file + pub(crate) fn new(device: &Device, width: u32, height: u32, skybox: SimpleTexture) -> Eye { + let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Camera Buffer"), + contents: bytemuck::cast_slice(&[ + Mat4::from_translation(Vec3::new(0.0, 2.0, -8.0)), + Mat4::IDENTITY, + Mat4::IDENTITY, + Mat4::IDENTITY, + ]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let dir = Vec3::new(1.0, 0.5, 1.0).normalize(); + let environment = Environment { + ambient: Vec4::new(0.15, 0.15, 0.15, 0.0), + light: Vec4::new(1.0, 1.0, 1.0, 0.0), + dir: Vec4::new(dir.x, dir.y, dir.z, 0.0), + }; + + let environment_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(""), + contents: bytemuck::cast_slice(&[environment]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 3, + 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("eye_bind_group_layout"), + }); + + let group = Eye::bind_group(&layout, device, &camera_buffer, &environment_buffer, skybox); + + Eye { + aspect_ratio: width as f32 / height as f32, + frame: Affine3A::IDENTITY, + fov_y: 90.0, + z_near: 0.1, + z_far: 1000.0, + camera_buffer, + group, + layout, + environment, + environment_buffer, + } + } + fn bind_group( + layout: &wgpu::BindGroupLayout, + device: &wgpu::Device, + camera: &wgpu::Buffer, + environment: &wgpu::Buffer, + skybox: SimpleTexture, + ) -> wgpu::BindGroup { + device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: camera.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: environment.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::Sampler( + &MaterialProperties::default().sampler(device), + ), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(&skybox.view), + }, + ], + label: Some("eye_bind_group"), + }) + } + pub fn skybox(&mut self, device: &wgpu::Device, texture: SimpleTexture) { + self.group = Eye::bind_group( + &self.layout, + device, + &self.camera_buffer, + &self.environment_buffer, + texture, + ) + } + pub(crate) fn resize(&mut self, width: u32, height: u32) { + self.aspect_ratio = width as f32 / height as f32 + } + pub(crate) fn control(&mut self, delta: Vec3) { + self.frame *= Affine3A::from_translation(delta); + } + pub(crate) fn rotate(&mut self, yaw: f32, pitch: f32) { + let (mut y, mut x, z) = self.frame.matrix3.to_euler(EulerRot::YXZ); + x = (x + pitch * 0.005).clamp(-1.4, 1.4); + y += yaw * 0.005; + self.frame.matrix3 = Mat3A::from_euler(EulerRot::YXZ, y, x, z); + } +} diff --git a/src/render/mod.rs b/src/render/mod.rs index b35b4a5..2687354 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,1043 +1,1251 @@ pub mod eye; -use std::cell::RefCell; +use crate::render::eye::Eye; +use crate::world::{Shape, SimpleColliderData, SimpleLight, SimpleObject, SimpleObjectData}; +use bytemuck::{Pod, Zeroable}; use glam::prelude::*; -use std::collections::{HashMap}; +use gltf::Semantic; +use image::EncodableLayout; +use std::cell::RefCell; +use std::collections::HashMap; use std::error::Error; use std::fmt::{Debug, Display, Formatter}; use std::hash::{BuildHasherDefault, Hash, Hasher}; -use std::ops::{Range}; +use std::ops::Range; use std::rc::Rc; use std::sync::Arc; -use bytemuck::{Pod, Zeroable}; -use gltf::{Gltf, Node, Semantic}; -use image::EncodableLayout; -use wgpu::{Device, Queue}; use wgpu::naga::{FastHashMap, FastHashSet}; use wgpu::util::DeviceExt; +use wgpu::{Device, Queue}; use winit::window::Window; -use crate::render::eye::Eye; -use crate::world::{Shape, SimpleColliderData, SimpleLight, SimpleObject, SimpleObjectData}; -const DEFAULT_VERTICES: [SimpleVertex;0] = []; +const DEFAULT_VERTICES: [SimpleVertex; 0] = []; #[repr(C)] -#[derive(Pod,Zeroable,Copy,Clone)] +#[derive(Pod, Zeroable, Copy, Clone)] pub struct SimpleModelInstance { - pub transform: Mat4, - pub color: Vec4, - pub lights: [u16;16], - pub point_lights: u32, - pub spot_lights: u32, - pub metal: f32, - pub rough: f32, + pub transform: Mat4, + pub color: Vec4, + pub lights: [u16; 16], + pub point_lights: u32, + pub spot_lights: u32, + pub metal: f32, + pub rough: f32, } #[repr(C)] -#[derive(Pod,Copy,Clone,Zeroable)] +#[derive(Pod, Copy, Clone, Zeroable)] pub struct SimpleLightInstance { - pub location: Vec4, - pub rotation: Vec4, - pub color: Vec4, + pub location: Vec4, + pub rotation: Vec4, + pub color: Vec4, } #[derive(Clone)] pub struct SimpleLightData { - pub index: usize, - pub instance: SimpleLightInstance, - pub transform: Affine3, + pub index: usize, + pub instance: SimpleLightInstance, + pub transform: Affine3, } #[derive(Clone)] pub struct SimpleModelData { - pub instance: SimpleModelInstance, - pub material: Id, - pub mesh: Id, + pub instance: SimpleModelInstance, + pub material: Id, + pub mesh: Id, } pub struct SimpleInstances { - light_count: usize, - light_buffer: wgpu::Buffer, - instance_count: usize, - instance_buffer: wgpu::Buffer, - //light_ref_last: usize, - //light_ref_buffer: wgpu::Buffer, - objects: FastHashMap,FastHashMap,FastHashSet>>, - lights: FastHashMap, + light_count: usize, + light_buffer: wgpu::Buffer, + instance_count: usize, + instance_buffer: wgpu::Buffer, + //light_ref_last: usize, + //light_ref_buffer: wgpu::Buffer, + objects: + FastHashMap, FastHashMap, FastHashSet>>, + lights: FastHashMap, } enum SimpleRenderCode { - Material(wgpu::BindGroup), - Mesh((wgpu::Buffer,u32),Option<(wgpu::Buffer,u32)>), - Draw(Range) + Material(wgpu::BindGroup), + Mesh((wgpu::Buffer, u32), Option<(wgpu::Buffer, u32)>), + Draw(Range), } struct SimpleRenderProgram(Vec); impl SimpleRenderProgram { - fn push(&mut self, item: SimpleRenderCode) { - self.0.push(item) - } - fn new() -> SimpleRenderProgram { - SimpleRenderProgram(Vec::new()) - } - fn render(self, pass: &mut wgpu::RenderPass) { - let mut count = 0; - let mut indexed = false; - for code in self.0 { - match code { - SimpleRenderCode::Material(material) => { - pass.set_bind_group(Renderer::SIMPLE_RENDER_TEXTURE_GROUP_POSITION,&material,&[]) - } - SimpleRenderCode::Mesh(vertices,indices) => { - pass.set_vertex_buffer(0, vertices.0.slice(..)); - if let Some(indices) = indices { - pass.set_index_buffer(indices.0.slice(..),wgpu::IndexFormat::Uint32); - count = indices.1; - indexed = true; - } else { - count = vertices.1; - indexed = false; - } - } - SimpleRenderCode::Draw(instances) => { - if indexed { - pass.draw_indexed(0..count, 0, instances) - } else { - pass.draw(0..count, instances); - } - } - } + fn push(&mut self, item: SimpleRenderCode) { + self.0.push(item) + } + fn new() -> SimpleRenderProgram { + SimpleRenderProgram(Vec::new()) + } + fn render(self, pass: &mut wgpu::RenderPass) { + let mut count = 0; + let mut indexed = false; + for code in self.0 { + match code { + SimpleRenderCode::Material(material) => pass.set_bind_group( + Renderer::SIMPLE_RENDER_TEXTURE_GROUP_POSITION, + &material, + &[], + ), + SimpleRenderCode::Mesh(vertices, indices) => { + pass.set_vertex_buffer(0, vertices.0.slice(..)); + if let Some(indices) = indices { + pass.set_index_buffer(indices.0.slice(..), wgpu::IndexFormat::Uint32); + count = indices.1; + indexed = true; + } else { + count = vertices.1; + indexed = false; + } + } + SimpleRenderCode::Draw(instances) => { + if indexed { + pass.draw_indexed(0..count, 0, instances) + } else { + pass.draw(0..count, instances); + } + } + } + } } - } } impl SimpleInstances { - const MIN_SIZE: u64 = 64; - pub fn add_object(&mut self, object: SimpleObject) { - if let Some(ref model) = object.0.borrow().model { - self.instance_count += 1; - self.objects - .entry(model.mesh.clone()).or_insert(FastHashMap::with_hasher(BuildHasherDefault::new())) - .entry(model.material.clone()).or_insert(FastHashSet::with_hasher(BuildHasherDefault::new())) - .insert(object.clone()); - } - } - - pub fn remove_object(&mut self, object: SimpleObject) { - if let Some(ref model) = object.0.borrow().model { - self.instance_count -= 1; - self.objects - .entry(model.mesh.clone()).or_insert(FastHashMap::with_hasher(BuildHasherDefault::new())) - .entry(model.material.clone()).or_insert(FastHashSet::with_hasher(BuildHasherDefault::new())) - .remove(&object); - } - } - - pub fn add_light(&mut self, light: SimpleLight) { - self.light_count += 1; - self.lights.insert(light,0); - } - - pub fn remove_light(&mut self, light: &SimpleLight) { - self.light_count -= 1; - self.lights.remove(light); - } - - pub fn reallocate_buffer(device: &wgpu::Device, queue: &wgpu::Queue, buffer: &mut wgpu::Buffer, count: usize, item_size: usize) { - let size = buffer.size() / item_size as wgpu::BufferAddress; - if count > SimpleInstances::MIN_SIZE as usize { - let mut reallocate: Option = None; - if count < (size / 2) as usize { - reallocate = Some(count / 2); - } else if count > size as usize { - reallocate = Some(count * 2); - } - if let Some(new_size) = reallocate { - *buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Instance Buffer"), - size: (item_size * new_size) as wgpu::BufferAddress, - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX, - mapped_at_creation: false, - }); - } - } - } - pub fn write_lights(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { - //SimpleInstances::reallocate_buffer(device, queue, &mut self.light_ref_buffer, self.light_ref_last, size_of::()); - /* - let mut light_ref_buffer = queue.write_buffer_with( - &self.light_ref_buffer, - 0 as wgpu::BufferAddress, - wgpu::BufferSize::new(self.instance_buffer.size()).unwrap() - ).unwrap(); - */ - SimpleInstances::reallocate_buffer(device, queue, &mut self.light_buffer, self.light_count, size_of::()); - let mut buffer = queue.write_buffer_with( - &self.light_buffer, - 0 as wgpu::BufferAddress, - wgpu::BufferSize::new(self.light_buffer.size()).unwrap(), - ).unwrap(); - let stride = size_of::(); - for (new_index,(light,index)) in self.lights.iter_mut().enumerate() { - *index = new_index + 1; - let begin = *index * stride; - buffer.slice(begin..begin + stride).copy_from_slice(bytemuck::cast_slice(&[light.0.borrow().instance])); - } - } - pub fn write_instances(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) -> SimpleRenderProgram { - let mut program = SimpleRenderProgram::new(); - SimpleInstances::reallocate_buffer(device, queue, &mut self.instance_buffer, self.instance_count, size_of::()); - let mut buffer = queue.write_buffer_with( - &self.instance_buffer, - 0 as wgpu::BufferAddress, - wgpu::BufferSize::new(self.instance_buffer.size()).unwrap() - ).unwrap(); - let mut index: u32 = 0; - let stride = size_of::(); - for (mesh,materials) in self.objects.iter() { - program.push(SimpleRenderCode::Mesh(mesh.it.vertices.clone(), mesh.it.indices.clone())); - for (material, objects) in materials.iter() { - program.push(SimpleRenderCode::Material(material.it.group.clone())); - let before = index; - for object in objects.iter() { - let begin = index as usize * stride; - buffer.slice(begin .. begin + stride).copy_from_slice(bytemuck::cast_slice(&[object.0.borrow().model.as_ref().unwrap().instance])); - index += 1; + const MIN_SIZE: u64 = 64; + pub fn add_object(&mut self, object: SimpleObject) { + if let Some(ref model) = object.0.borrow().model { + self.instance_count += 1; + self.objects + .entry(model.mesh.clone()) + .or_insert(FastHashMap::with_hasher(BuildHasherDefault::new())) + .entry(model.material.clone()) + .or_insert(FastHashSet::with_hasher(BuildHasherDefault::new())) + .insert(object.clone()); } - program.push(SimpleRenderCode::Draw(Range::from(before..index))); - } - }; - program - } - - fn desc() -> wgpu::VertexBufferLayout<'static> { - use std::mem; - wgpu::VertexBufferLayout { - array_stride: size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Instance, - attributes: &[ // todo: is this too big? - wgpu::VertexAttribute { - offset: 0, - shader_location: 4, - format: wgpu::VertexFormat::Float32x4, - }, - wgpu::VertexAttribute { - offset: size_of::<[f32; 4]>() as wgpu::BufferAddress, - shader_location: 5, - format: wgpu::VertexFormat::Float32x4, - }, - wgpu::VertexAttribute { - offset: size_of::<[f32; 8]>() as wgpu::BufferAddress, - shader_location: 6, - format: wgpu::VertexFormat::Float32x4, - }, - wgpu::VertexAttribute { - offset: size_of::<[f32; 12]>() as wgpu::BufferAddress, - shader_location: 7, - format: wgpu::VertexFormat::Float32x4, - }, - wgpu::VertexAttribute { - offset: size_of::<[f32; 16]>() as wgpu::BufferAddress, - shader_location: 8, - format: wgpu::VertexFormat::Float32x4, - }, - wgpu::VertexAttribute { - offset: size_of::<[f32; 16]>() as wgpu::BufferAddress, - shader_location: 9, - format: wgpu::VertexFormat::Float32x4, - }, - wgpu::VertexAttribute { - offset: size_of::<[f32; 16]>() as wgpu::BufferAddress, - shader_location: 10, - format: wgpu::VertexFormat::Float32x4, - }, - wgpu::VertexAttribute { - offset: size_of::<[f32; 16]>() as wgpu::BufferAddress, - shader_location: 11, - format: wgpu::VertexFormat::Float32x4, - }, - ], } - } - pub fn new(device: &wgpu::Device, queue: &wgpu::Queue) -> SimpleInstances { - let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Instance Buffer"), - size: (size_of::() * SimpleInstances::MIN_SIZE as usize) as wgpu::BufferAddress, - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - let light_buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Light Buffer"), - size: (size_of::() * SimpleInstances::MIN_SIZE as usize) as wgpu::BufferAddress, - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - let light_ref_buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Light Ref Buffer"), - size: (size_of::() * SimpleInstances::MIN_SIZE as usize) as wgpu::BufferAddress, - usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false - }); - SimpleInstances { - light_count: 0, - //light_ref_last: 0, - instance_count: 0, - light_buffer, - instance_buffer, - //light_ref_buffer, - objects: FastHashMap::with_hasher(BuildHasherDefault::new()), - lights: Default::default(), + pub fn remove_object(&mut self, object: SimpleObject) { + if let Some(ref model) = object.0.borrow().model { + self.instance_count -= 1; + self.objects + .entry(model.mesh.clone()) + .or_insert(FastHashMap::with_hasher(BuildHasherDefault::new())) + .entry(model.material.clone()) + .or_insert(FastHashSet::with_hasher(BuildHasherDefault::new())) + .remove(&object); + } + } + + pub fn add_light(&mut self, light: SimpleLight) { + self.light_count += 1; + self.lights.insert(light, 0); + } + + pub fn remove_light(&mut self, light: &SimpleLight) { + self.light_count -= 1; + self.lights.remove(light); + } + + pub fn reallocate_buffer( + device: &wgpu::Device, + buffer: &mut wgpu::Buffer, + count: usize, + item_size: usize, + ) { + let size = buffer.size() / item_size as wgpu::BufferAddress; + if count > SimpleInstances::MIN_SIZE as usize { + let mut reallocate: Option = None; + if count < (size / 2) as usize { + reallocate = Some(count / 2); + } else if count > size as usize { + reallocate = Some(count * 2); + } + if let Some(new_size) = reallocate { + *buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Instance Buffer"), + size: (item_size * new_size) as wgpu::BufferAddress, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX, + mapped_at_creation: false, + }); + } + } + } + pub fn write_lights(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { + //SimpleInstances::reallocate_buffer(device, queue, &mut self.light_ref_buffer, self.light_ref_last, size_of::()); + /* + let mut light_ref_buffer = queue.write_buffer_with( + &self.light_ref_buffer, + 0 as wgpu::BufferAddress, + wgpu::BufferSize::new(self.instance_buffer.size()).unwrap() + ).unwrap(); + */ + SimpleInstances::reallocate_buffer( + device, + &mut self.light_buffer, + self.light_count, + size_of::(), + ); + let mut buffer = queue + .write_buffer_with( + &self.light_buffer, + 0 as wgpu::BufferAddress, + wgpu::BufferSize::new(self.light_buffer.size()).unwrap(), + ) + .unwrap(); + let stride = size_of::(); + for (new_index, (light, index)) in self.lights.iter_mut().enumerate() { + *index = new_index + 1; + let begin = *index * stride; + buffer + .slice(begin..begin + stride) + .copy_from_slice(bytemuck::cast_slice(&[light.0.borrow().instance])); + } + } + fn write_instances( + &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + ) -> SimpleRenderProgram { + let mut program = SimpleRenderProgram::new(); + SimpleInstances::reallocate_buffer( + device, + &mut self.instance_buffer, + self.instance_count, + size_of::(), + ); + let mut buffer = queue + .write_buffer_with( + &self.instance_buffer, + 0 as wgpu::BufferAddress, + wgpu::BufferSize::new(self.instance_buffer.size()).unwrap(), + ) + .unwrap(); + let mut index: u32 = 0; + let stride = size_of::(); + for (mesh, materials) in self.objects.iter() { + program.push(SimpleRenderCode::Mesh( + mesh.it.vertices.clone(), + mesh.it.indices.clone(), + )); + for (material, objects) in materials.iter() { + program.push(SimpleRenderCode::Material(material.it.group.clone())); + let before = index; + for object in objects.iter() { + let begin = index as usize * stride; + buffer + .slice(begin..begin + stride) + .copy_from_slice(bytemuck::cast_slice(&[object + .0 + .borrow() + .model + .as_ref() + .unwrap() + .instance])); + index += 1; + } + program.push(SimpleRenderCode::Draw(Range::from(before..index))); + } + } + program + } + + fn desc() -> wgpu::VertexBufferLayout<'static> { + wgpu::VertexBufferLayout { + array_stride: size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Instance, + attributes: &[ + // todo: is this too big? + wgpu::VertexAttribute { + offset: 0, + shader_location: 4, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: size_of::<[f32; 4]>() as wgpu::BufferAddress, + shader_location: 5, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: size_of::<[f32; 8]>() as wgpu::BufferAddress, + shader_location: 6, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: size_of::<[f32; 12]>() as wgpu::BufferAddress, + shader_location: 7, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: size_of::<[f32; 16]>() as wgpu::BufferAddress, + shader_location: 8, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: size_of::<[f32; 16]>() as wgpu::BufferAddress, + shader_location: 9, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: size_of::<[f32; 16]>() as wgpu::BufferAddress, + shader_location: 10, + format: wgpu::VertexFormat::Float32x4, + }, + wgpu::VertexAttribute { + offset: size_of::<[f32; 16]>() as wgpu::BufferAddress, + shader_location: 11, + format: wgpu::VertexFormat::Float32x4, + }, + ], + } + } + + pub fn new(device: &wgpu::Device) -> SimpleInstances { + let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Instance Buffer"), + size: (size_of::() * SimpleInstances::MIN_SIZE as usize) + as wgpu::BufferAddress, + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + let light_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Light Buffer"), + size: (size_of::() * SimpleInstances::MIN_SIZE as usize) + as wgpu::BufferAddress, + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + /*let light_ref_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Light Ref Buffer"), + size: (size_of::() * SimpleInstances::MIN_SIZE as usize) as wgpu::BufferAddress, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + });*/ + SimpleInstances { + light_count: 0, + //light_ref_last: 0, + instance_count: 0, + light_buffer, + instance_buffer, + //light_ref_buffer, + objects: FastHashMap::with_hasher(BuildHasherDefault::new()), + lights: Default::default(), + } } - } } pub struct Renderer { - id_count: u64, - surface: wgpu::Surface<'static>, - config: wgpu::SurfaceConfiguration, - device: wgpu::Device, - queue: wgpu::Queue, - pub(crate) eye: eye::Eye, - material_layout: wgpu::BindGroupLayout, - default_texture: SimpleTexture, - pipeline: wgpu::RenderPipeline, - depth_texture: SimpleTexture, - pub(crate) instances: SimpleInstances, + id_count: u64, + surface: wgpu::Surface<'static>, + config: wgpu::SurfaceConfiguration, + device: wgpu::Device, + queue: wgpu::Queue, + pub(crate) eye: eye::Eye, + material_layout: wgpu::BindGroupLayout, + default_texture: SimpleTexture, + default_material: SimpleMaterial, + sky_pipeline: wgpu::RenderPipeline, + instance_pipeline: wgpu::RenderPipeline, + depth_texture: SimpleTexture, + pub(crate) instances: SimpleInstances, } #[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] struct SimpleVertex { - position: [f32; 3], - normal: [f32; 3], - tex_coord: [f32; 2], - tangent: [f32; 3], - bitangent: [f32; 3], + position: [f32; 3], + normal: [f32; 3], + tex_coord: [f32; 2], + tangent: [f32; 3], + bitangent: [f32; 3], } impl SimpleVertex { - fn desc() -> wgpu::VertexBufferLayout<'static> { - wgpu::VertexBufferLayout { - array_stride: size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[ - wgpu::VertexAttribute { - offset: 0, - shader_location: 0, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - offset: size_of::<[f32; 3]>() as wgpu::BufferAddress, - shader_location: 1, - format: wgpu::VertexFormat::Float32x3, - }, - wgpu::VertexAttribute { - offset: size_of::<[f32; 6]>() as wgpu::BufferAddress, - shader_location: 2, - format: wgpu::VertexFormat::Float32x2, + fn desc() -> wgpu::VertexBufferLayout<'static> { + wgpu::VertexBufferLayout { + array_stride: size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: size_of::<[f32; 6]>() as wgpu::BufferAddress, + shader_location: 2, + format: wgpu::VertexFormat::Float32x2, + }, + ], } - ] } - } } #[derive(Debug, Clone, PartialEq)] pub struct SimpleMesh { - indices: Option<(wgpu::Buffer,u32)>, - vertices: (wgpu::Buffer, u32), + indices: Option<(wgpu::Buffer, u32)>, + vertices: (wgpu::Buffer, u32), } #[derive(Debug, Clone, Hash, Eq, PartialEq)] -pub(crate) struct SimpleTexture { - texture: wgpu::Texture, - view: wgpu::TextureView, +pub struct SimpleTexture { + texture: wgpu::Texture, + view: wgpu::TextureView, } -struct TextureProperties { - width: u32, - height: u32, +pub struct TextureProperties { + width: u32, + height: u32, } +// todo: use texture compression! impl SimpleTexture { - pub fn load(device: &Device, queue: &Queue, slice: impl AsRef<[u8]>, properties: TextureProperties) -> SimpleTexture { - let size = wgpu::Extent3d { - width: properties.width, - height: properties.height, - depth_or_array_layers: 1, - }; - let diffuse_texture = device.create_texture( - &wgpu::TextureDescriptor { - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - label: Some("texture"), - view_formats: &[], - } - ); - let diffuse_texture_view = diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default()); - queue.write_texture( - wgpu::TexelCopyTextureInfo { - texture: &diffuse_texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - slice.as_ref(), - wgpu::TexelCopyBufferLayout { - offset: 0, - bytes_per_row: Some(4 * size.width), - rows_per_image: Some(size.height), - }, - size, - ); - SimpleTexture { - texture: diffuse_texture, - view: diffuse_texture_view, + pub fn load( + device: &Device, + queue: &Queue, + slice: impl AsRef<[u8]>, + properties: TextureProperties, + ) -> SimpleTexture { + let size = wgpu::Extent3d { + width: properties.width, + height: properties.height, + depth_or_array_layers: 1, + }; + let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor { + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + label: Some("texture"), + view_formats: &[], + }); + let diffuse_texture_view = + diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default()); + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: &diffuse_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + slice.as_ref(), + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(4 * size.width), + rows_per_image: Some(size.height), + }, + size, + ); + SimpleTexture { + texture: diffuse_texture, + view: diffuse_texture_view, + } } - } } #[derive(Clone)] pub struct SimpleMaterial { - group: wgpu::BindGroup, + group: wgpu::BindGroup, } #[derive(Clone)] pub struct Id { - id: u64, - it: T, + id: u64, + it: T, } impl Hash for Id { - fn hash(&self, state: &mut H) { - self.id.hash(state) - } + fn hash(&self, state: &mut H) { + self.id.hash(state) + } } impl PartialEq for Id { - fn eq(&self, other: &Self) -> bool { - other.id == self.id - } + fn eq(&self, other: &Self) -> bool { + other.id == self.id + } } impl Eq for Id {} struct MaterialProperties { - edge: wgpu::AddressMode, - filter: wgpu::FilterMode, + edge: wgpu::AddressMode, + filter: wgpu::FilterMode, +} + +impl Default for MaterialProperties { + fn default() -> MaterialProperties { + MaterialProperties { + edge: wgpu::AddressMode::Repeat, + filter: wgpu::FilterMode::Linear, + } + } +} + +impl MaterialProperties { + fn sampler(&self, device: &wgpu::Device) -> wgpu::Sampler { + device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: self.edge, + address_mode_v: self.edge, + address_mode_w: self.edge, + mag_filter: self.filter, + min_filter: self.filter, + mipmap_filter: wgpu::MipmapFilterMode::Nearest, + ..Default::default() + }) + } } impl SimpleMaterial { - fn new(renderer: &Renderer, base: &SimpleTexture, normal: &SimpleTexture, reflect: &SimpleTexture, config: MaterialProperties) -> SimpleMaterial { - let sampler = renderer.device.create_sampler(&wgpu::SamplerDescriptor { - address_mode_u: config.edge, - address_mode_v: config.edge, - address_mode_w: config.edge, - mag_filter: config.filter, - min_filter: config.filter, - mipmap_filter: wgpu::MipmapFilterMode::Nearest, - ..Default::default() - }); - let group = renderer.device.create_bind_group( - &wgpu::BindGroupDescriptor { - layout: &renderer.material_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Sampler(&sampler), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(&base.view), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::TextureView(&normal.view), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: wgpu::BindingResource::TextureView(&reflect.view), - }, - ], - label: Some("diffuse_bind_group"), - } - ); - SimpleMaterial { - group, + fn new( + device: &wgpu::Device, + layout: &wgpu::BindGroupLayout, + base: &SimpleTexture, + normal: &SimpleTexture, + reflect: &SimpleTexture, + config: MaterialProperties, + ) -> SimpleMaterial { + let sampler = config.sampler(&device); + let group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&base.view), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::TextureView(&normal.view), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(&reflect.view), + }, + ], + label: Some("diffuse_bind_group"), + }); + SimpleMaterial { group } } - } } #[derive(Debug)] struct PoolError(String); impl Display for PoolError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f,"{}",self.0) - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } } impl Error for PoolError {} pub struct SimpleTreeNode { - object: Option, - children: Vec, - name: String, + object: Option, + children: Vec, + name: String, } impl SimpleTreeNode { - pub fn first_object(&self) -> Option { - if let Some(object) = self.object.clone() { - Some(object) - } else { - for child in self.children.iter() { - if let Some(object) = child.first_object() { - return Some(object) + pub fn first_object(&self) -> Option { + if let Some(object) = self.object.clone() { + Some(object) + } else { + for child in self.children.iter() { + if let Some(object) = child.first_object() { + return Some(object); + } + } + None } - } - None } - } } -type GltfVertexBufferKey = (Option,Option,Option,Option); +type GltfVertexBufferKey = (Option, Option, Option, Option); impl Renderer { - const SIMPLE_RENDER_EYE_GROUP_POSITION: u32 = 0; - const SIMPLE_RENDER_TEXTURE_GROUP_POSITION: u32 = 1; - const SIMPLE_RENDER_MODEL_GROUP_POSITION: u32 = 2; - pub fn new_id(&mut self, value: T) -> Id { - self.id_count += 1; - Id { - it: value, - id: self.id_count, + const SIMPLE_RENDER_EYE_GROUP_POSITION: u32 = 0; + const SIMPLE_RENDER_TEXTURE_GROUP_POSITION: u32 = 1; + const SIMPLE_RENDER_MODEL_GROUP_POSITION: u32 = 2; + pub fn new_id(&mut self, value: T) -> Id { + self.id_count += 1; + Id { + it: value, + id: self.id_count, + } } - } - pub fn load_texture_from_bytes(&mut self, slice: impl AsRef<[u8]>) -> SimpleTexture { - let image = image::load_from_memory(slice.as_ref()); - if let Ok(data) = image { - let data = data.into_rgba8(); - SimpleTexture::load(&self.device, &self.queue, data.as_bytes(), TextureProperties { - width: data.width(), - height: data.height(), - }) - } else { - self.default_texture.clone() - } - } - pub fn new_material(&mut self, base: &SimpleTexture, normal: &SimpleTexture, reflect: &SimpleTexture) -> Id { - self.new_id(SimpleMaterial::new(self, base, normal, reflect, MaterialProperties { - edge: wgpu::AddressMode::Repeat, - filter: wgpu::FilterMode::Linear, - })) - } - pub fn new_texture_from_gltf(&mut self, info: &gltf::texture::Texture, images: &Vec) -> SimpleTexture { - if let Some(image) = images.get(info.source().index()) { - let mut new_pixels = Vec::new(); - let pixels: &Vec; - match image.format { - gltf::image::Format::R8G8B8 => { - for pixel in image.pixels.chunks(3) { - new_pixels.push(pixel[0]); - new_pixels.push(pixel[0]); - new_pixels.push(pixel[0]); - new_pixels.push(255); - } - pixels = &new_pixels; - } - gltf::image::Format::R8G8B8A8 => { - pixels = &image.pixels - } - _ => return self.default_texture.clone() - } - SimpleTexture::load(&self.device, &self.queue, pixels.as_slice(), TextureProperties { - width: image.width, - height: image.height, - }) - } else { - self.default_texture.clone() - } - } - pub fn new_mesh_from_gltf( - &mut self, - primitive: gltf::Primitive, - meshes: &mut HashMap, - buffers: &[gltf::buffer::Data] - ) -> Id { - let position_index = primitive.get(&Semantic::Positions).and_then(|it| it.view()).and_then(|it| Some(it.buffer().index())); - let normals_index = primitive.get(&Semantic::Normals).and_then(|it| it.view()).and_then(|it| Some(it.buffer().index())); - let tex_coords_index = primitive.get(&Semantic::TexCoords(0)).and_then(|it| it.view()).and_then(|it| Some(it.buffer().index())); - let indices_index = primitive.indices().and_then(|it| it.view()).and_then(|it| Some(it.buffer().index())); - let tangent_index = primitive.get(&Semantic::Tangents).and_then(|it| it.view()).and_then(|it| Some(it.buffer().index())); - let key = (position_index,normals_index,tex_coords_index,indices_index); - self.new_id(meshes.entry(key).or_insert_with(|| { - let mut tangents = false; - let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); - let mut vertex_data: Vec; - if let Some(positions) = reader.read_positions() { - vertex_data = Vec::with_capacity(positions.len()); - let mut normal = reader.read_normals().map(|it| it.into_iter()); - let mut tex_coord = reader.read_tex_coords(0).map(|it| it.into_f32().into_iter()); - let mut tangent = reader.read_tangents().map(|it| it.into_iter()); - tangents = tangent.is_some(); - for position in positions { - vertex_data.push(SimpleVertex { - position, - normal: if let Some(ref mut normals) = normal { - if let Some(normal) = normals.next() { - normal - } else { - [0.0;3] - } - } else { - [0.0;3] - }, - tex_coord: if let Some(ref mut tex_coords) = tex_coord { - tex_coords.next() - } else { - None - }.unwrap_or([0.0;2]), - tangent: [0.0;3], // todo: tangent from model - bitangent: [0.0;3], - }) - } - } else { - vertex_data = Vec::new(); - } - let vertex_buffer = self.device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(vertex_data.as_slice()), - usage: wgpu::BufferUsages::VERTEX, - } - ); - let vertices_result = (vertex_buffer,vertex_data.len() as u32); - let indices_result = if let Some(indices) = reader.read_indices() { - let index_data: Vec = indices.into_u32().collect(); - Some((self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Index Buffer"), - contents: bytemuck::cast_slice(index_data.as_slice()), - usage: wgpu::BufferUsages::INDEX, - }), index_data.len() as u32)) - } else { - None - }; - SimpleMesh { - indices: indices_result, - vertices: vertices_result - } - }).clone()) - } - pub fn load_node_from_gltf( - &mut self, - node: gltf::Node, - meshes: &mut HashMap, - textures: &mut HashMap, - images: &Vec, - buffers: &Vec, - ) -> SimpleTreeNode { - let mut tree_node = SimpleTreeNode { - object: None, - children: Vec::new(), - name: node.name().unwrap_or("Node").to_string() - }; - if let Some(mesh) = node.mesh() { - let len = mesh.primitives().len(); - for primitive in mesh.primitives() { - let pbr = primitive.material().pbr_metallic_roughness(); - let material = primitive.material(); - let color = pbr.base_color_factor(); - let metal = pbr.metallic_factor(); - let rough = pbr.roughness_factor(); - let light = primitive.material().emissive_factor(); - let base = match pbr.base_color_texture() { - Some(info) => textures.entry(info.texture().source().index()).or_insert_with(|| self.new_texture_from_gltf(&info.texture(), &images)).clone(), - None => self.default_texture.clone() - }; - let reflect = match pbr.metallic_roughness_texture() { - Some(info) => textures.entry(info.texture().source().index()).or_insert_with(|| self.new_texture_from_gltf(&info.texture(), &images)).clone(), - None => self.default_texture.clone() - }; - let normal = match material.normal_texture() { - Some(info) => textures.entry(info.texture().source().index()).or_insert_with(|| self.new_texture_from_gltf(&info.texture(), &images)).clone(), - None => self.default_texture.clone() - }; - let material = self.new_material(&base, &normal, &reflect); - let mesh = self.new_mesh_from_gltf(primitive,meshes,buffers); - let object = SimpleObject(Rc::new(RefCell::new(SimpleObjectData { - model: Some(SimpleModelData { - instance: SimpleModelInstance { - transform: Mat4::default(), - color: Vec4::from_array(color), - lights: [0;16], - point_lights: 0, - spot_lights: 0, - metal, - rough, - }, - material, - mesh, - }), - collider: SimpleColliderData { - transform: Mat4::default(), - shape: Shape::None, - radius: 0.0, - }, - }))); - if len == 1 { - tree_node.object = Some(object); + pub fn load_texture_from_bytes( + &mut self, + slice: impl AsRef<[u8]>, + format: Option, + ) -> SimpleTexture { + let image = if let Some(format) = format { + image::load_from_memory_with_format(slice.as_ref(), format) } else { - tree_node.children.push(SimpleTreeNode { - object: Some(object), + image::load_from_memory(slice.as_ref()) + }; + if let Ok(data) = image { + let data = data.into_rgba8(); + SimpleTexture::load( + &self.device, + &self.queue, + data.as_bytes(), + TextureProperties { + width: data.width(), + height: data.height(), + }, + ) + } else { + println!("failed to load texture! error: {:?}", image.unwrap_err()); + self.default_texture.clone() + } + } + pub fn new_material( + &mut self, + base: &SimpleTexture, + normal: &SimpleTexture, + reflect: &SimpleTexture, + ) -> Id { + self.new_id(SimpleMaterial::new( + &self.device, + &self.material_layout, + base, + normal, + reflect, + MaterialProperties { + edge: wgpu::AddressMode::Repeat, + filter: wgpu::FilterMode::Linear, + }, + )) + } + pub fn new_texture_from_gltf( + &mut self, + info: &gltf::texture::Texture, + images: &Vec, + ) -> SimpleTexture { + if let Some(image) = images.get(info.source().index()) { + let mut new_pixels = Vec::new(); + let pixels: &Vec; + match image.format { + gltf::image::Format::R8G8B8 => { + for pixel in image.pixels.chunks(3) { + new_pixels.push(pixel[0]); + new_pixels.push(pixel[0]); + new_pixels.push(pixel[0]); + new_pixels.push(255); + } + pixels = &new_pixels; + } + gltf::image::Format::R8G8B8A8 => pixels = &image.pixels, + _ => return self.default_texture.clone(), + } + SimpleTexture::load( + &self.device, + &self.queue, + pixels.as_slice(), + TextureProperties { + width: image.width, + height: image.height, + }, + ) + } else { + self.default_texture.clone() + } + } + pub fn new_mesh_from_gltf( + &mut self, + primitive: gltf::Primitive, + meshes: &mut HashMap, + buffers: &[gltf::buffer::Data], + ) -> Id { + let position_index = primitive + .get(&Semantic::Positions) + .and_then(|it| it.view()) + .and_then(|it| Some(it.buffer().index())); + let normals_index = primitive + .get(&Semantic::Normals) + .and_then(|it| it.view()) + .and_then(|it| Some(it.buffer().index())); + let tex_coords_index = primitive + .get(&Semantic::TexCoords(0)) + .and_then(|it| it.view()) + .and_then(|it| Some(it.buffer().index())); + let indices_index = primitive + .indices() + .and_then(|it| it.view()) + .and_then(|it| Some(it.buffer().index())); + let tangent_index = primitive + .get(&Semantic::Tangents) + .and_then(|it| it.view()) + .and_then(|it| Some(it.buffer().index())); + let key = ( + position_index, + normals_index, + tex_coords_index, + indices_index, + ); + self.new_id( + meshes + .entry(key) + .or_insert_with(|| { + let mut tangents = false; + let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); + let mut vertex_data: Vec; + if let Some(positions) = reader.read_positions() { + vertex_data = Vec::with_capacity(positions.len()); + let mut normal = reader.read_normals().map(|it| it.into_iter()); + let mut tex_coord = reader + .read_tex_coords(0) + .map(|it| it.into_f32().into_iter()); + let mut tangent = reader.read_tangents().map(|it| it.into_iter()); + tangents = tangent.is_some(); + for position in positions { + vertex_data.push(SimpleVertex { + position, + normal: if let Some(ref mut normals) = normal { + if let Some(normal) = normals.next() { + normal + } else { + [0.0; 3] + } + } else { + [0.0; 3] + }, + tex_coord: if let Some(ref mut tex_coords) = tex_coord { + tex_coords.next() + } else { + None + } + .unwrap_or([0.0; 2]), + tangent: [0.0; 3], // todo: tangent from model + bitangent: [0.0; 3], + }) + } + } else { + vertex_data = Vec::new(); + } + let vertex_buffer = + self.device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(vertex_data.as_slice()), + usage: wgpu::BufferUsages::VERTEX, + }); + let vertices_result = (vertex_buffer, vertex_data.len() as u32); + let indices_result = if let Some(indices) = reader.read_indices() { + let index_data: Vec = indices.into_u32().collect(); + Some(( + self.device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(index_data.as_slice()), + usage: wgpu::BufferUsages::INDEX, + }), + index_data.len() as u32, + )) + } else { + None + }; + SimpleMesh { + indices: indices_result, + vertices: vertices_result, + } + }) + .clone(), + ) + } + pub fn load_node_from_gltf( + &mut self, + node: gltf::Node, + meshes: &mut HashMap, + textures: &mut HashMap, + images: &Vec, + buffers: &Vec, + ) -> SimpleTreeNode { + let mut tree_node = SimpleTreeNode { + object: None, children: Vec::new(), - name: "Primitive".to_string() - }) - } - } - } - for node in node.children() { - tree_node.children.push(self.load_node_from_gltf(node,meshes,textures,images,buffers)); - } - tree_node - } - pub fn load_from_gltf(&mut self, slice: impl AsRef<[u8]>) -> SimpleTreeNode { - let mut root = SimpleTreeNode { - object: None, - children: Vec::new(), - name: "Root".to_string(), - }; - if let Ok((document, buffers, images)) = gltf::import_slice(slice) { - let mut meshes: HashMap = HashMap::new(); - let mut textures: HashMap = HashMap::new(); - for scene in document.scenes() { - let mut scene_node = SimpleTreeNode { - object: None, - children: Vec::new(), - name: scene.name().unwrap_or("Scene").to_string(), + name: node.name().unwrap_or("Node").to_string(), }; - for node in scene.nodes() { - scene_node.children.push(self.load_node_from_gltf(node, &mut meshes, &mut textures, &images, &buffers)); + if let Some(mesh) = node.mesh() { + let len = mesh.primitives().len(); + for primitive in mesh.primitives() { + let pbr = primitive.material().pbr_metallic_roughness(); + let material = primitive.material(); + let color = pbr.base_color_factor(); + let metal = pbr.metallic_factor(); + let rough = pbr.roughness_factor(); + let light = primitive.material().emissive_factor(); + let base = match pbr.base_color_texture() { + Some(info) => textures + .entry(info.texture().source().index()) + .or_insert_with(|| self.new_texture_from_gltf(&info.texture(), &images)) + .clone(), + None => self.default_texture.clone(), + }; + let reflect = match pbr.metallic_roughness_texture() { + Some(info) => textures + .entry(info.texture().source().index()) + .or_insert_with(|| self.new_texture_from_gltf(&info.texture(), &images)) + .clone(), + None => self.default_texture.clone(), + }; + let normal = match material.normal_texture() { + Some(info) => textures + .entry(info.texture().source().index()) + .or_insert_with(|| self.new_texture_from_gltf(&info.texture(), &images)) + .clone(), + None => self.default_texture.clone(), + }; + let material = self.new_material(&base, &normal, &reflect); + let mesh = self.new_mesh_from_gltf(primitive, meshes, buffers); + let object = SimpleObject(Rc::new(RefCell::new(SimpleObjectData { + model: Some(SimpleModelData { + instance: SimpleModelInstance { + transform: Mat4::default(), + color: Vec4::from_array(color), + lights: [0; 16], + point_lights: 0, + spot_lights: 0, + metal, + rough, + }, + material, + mesh, + }), + collider: SimpleColliderData { + transform: Mat4::default(), + shape: Shape::None, + radius: 0.0, + }, + }))); + if len == 1 { + tree_node.object = Some(object); + } else { + tree_node.children.push(SimpleTreeNode { + object: Some(object), + children: Vec::new(), + name: "Primitive".to_string(), + }) + } + } } - root.children.push(scene_node) - } + for node in node.children() { + tree_node + .children + .push(self.load_node_from_gltf(node, meshes, textures, images, buffers)); + } + tree_node } - root - } - pub fn resize(&mut self, width: u32, height: u32) { - self.surface.configure(&self.device, &self.config); - self.eye.resize(width, height); - } - pub async fn new(window: &Arc) -> anyhow::Result { - let bounds = window.inner_size(); - let width = bounds.width; - let height = bounds.height; - // The instance is a handle to our GPU - // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - #[cfg(not(target_arch = "wasm32"))] - backends: wgpu::Backends::PRIMARY, - #[cfg(target_arch = "wasm32")] - backends: wgpu::Backends::GL, - flags: Default::default(), - memory_budget_thresholds: Default::default(), - backend_options: Default::default(), - display: None, - }); - - let surface = instance.create_surface(window.clone())?; - - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await?; - - let (device, queue) = adapter - .request_device(&wgpu::DeviceDescriptor { - label: None, - required_features: wgpu::Features::empty(), - experimental_features: wgpu::ExperimentalFeatures::disabled(), - // WebGL doesn't support all of wgpu's features, so if - // we're building for the web we'll have to disable some. - required_limits: if cfg!(target_arch = "wasm32") { - wgpu::Limits::downlevel_webgl2_defaults() - } else { - wgpu::Limits::default() - }, - memory_hints: Default::default(), - trace: wgpu::Trace::Off, - }) - .await?; - - let surface_caps = surface.get_capabilities(&adapter); - // Shader code in this tutorial assumes an sRGB surface texture. Using a different - // one will result in all the colors coming out darker. If you want to support non - // sRGB surfaces, you'll need to account for that when drawing to the frame. - let surface_format = surface_caps - .formats - .iter() - .find(|f| f.is_srgb()) - .copied() - .unwrap_or(surface_caps.formats[0]); - let config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: surface_format, - width, - height, - present_mode: surface_caps.present_modes[0], - alpha_mode: surface_caps.alpha_modes[0], - view_formats: vec![], - desired_maximum_frame_latency: 2, - }; - - let eye = Eye::new(&device, width, height); - - let shader = device.create_shader_module(wgpu::include_wgsl!("../assets/SimpleShader.wgsl")); - - let texture = SimpleTexture::load(&device, &queue, [255,255,255,255], TextureProperties { - width: 1, - height: 1, - }); - - let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - 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: 2, - 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: 3, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, + pub fn load_from_gltf(&mut self, slice: impl AsRef<[u8]>) -> SimpleTreeNode { + let mut root = SimpleTreeNode { + object: None, + children: Vec::new(), + name: "Root".to_string(), + }; + if let Ok((document, buffers, images)) = gltf::import_slice(slice) { + let mut meshes: HashMap = HashMap::new(); + let mut textures: HashMap = HashMap::new(); + for scene in document.scenes() { + let mut scene_node = SimpleTreeNode { + object: None, + children: Vec::new(), + name: scene.name().unwrap_or("Scene").to_string(), + }; + for node in scene.nodes() { + scene_node.children.push(self.load_node_from_gltf( + node, + &mut meshes, + &mut textures, + &images, + &buffers, + )); + } + root.children.push(scene_node) + } } - ], - label: Some("texture_bind_group_layout"), - }); + root + } + pub fn resize(&mut self, width: u32, height: u32) { + self.surface.configure(&self.device, &self.config); + self.eye.resize(width, height); + } + pub fn set_skybox(&mut self, skybox: SimpleTexture) { + self.eye.skybox(&self.device, skybox); + } + pub async fn new(window: &Arc) -> anyhow::Result { + let bounds = window.inner_size(); + let width = bounds.width; + let height = bounds.height; + // The instance is a handle to our GPU + // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + #[cfg(not(target_arch = "wasm32"))] + backends: wgpu::Backends::PRIMARY, + #[cfg(target_arch = "wasm32")] + backends: wgpu::Backends::GL, + flags: Default::default(), + memory_budget_thresholds: Default::default(), + backend_options: Default::default(), + display: None, + }); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[ - Some(&eye.layout), - Some(&texture_bind_group_layout), - ], - immediate_size: 0, - }); + let surface = instance.create_surface(window.clone())?; - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: Some("vs_main"), - buffers: &[ - SimpleVertex::desc(),SimpleInstances::desc(), - ], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - // 3. - module: &shader, - entry_point: Some("fs_main"), - targets: &[Some(wgpu::ColorTargetState { - // 4. - format: config.format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: Some(true), - depth_compare: Some(wgpu::CompareFunction::Less), - stencil: wgpu::StencilState::default(), - bias: wgpu::DepthBiasState::default(), - }), - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview_mask: None, - cache: None, - }); + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + compatible_surface: Some(&surface), + force_fallback_adapter: false, + }) + .await?; - let size = wgpu::Extent3d { // 2. - width: config.width.max(1), - height: config.height.max(1), - depth_or_array_layers: 1, - }; - let desc = wgpu::TextureDescriptor { - label: Some("depth texture"), - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Depth32Float, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT // 3. + let (device, queue) = adapter + .request_device(&wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::empty(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), + // WebGL doesn't support all of wgpu's features, so if + // we're building for the web we'll have to disable some. + required_limits: if cfg!(target_arch = "wasm32") { + wgpu::Limits::downlevel_webgl2_defaults() + } else { + wgpu::Limits::default() + }, + memory_hints: Default::default(), + trace: wgpu::Trace::Off, + }) + .await?; + + let surface_caps = surface.get_capabilities(&adapter); + // Shader code in this tutorial assumes an sRGB surface texture. Using a different + // one will result in all the colors coming out darker. If you want to support non + // sRGB surfaces, you'll need to account for that when drawing to the frame. + let surface_format = surface_caps + .formats + .iter() + .find(|f| f.is_srgb()) + .copied() + .unwrap_or(surface_caps.formats[0]); + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: surface_format, + width, + height, + present_mode: surface_caps.present_modes[0], + alpha_mode: surface_caps.alpha_modes[0], + view_formats: vec![], + desired_maximum_frame_latency: 2, + }; + + let shader = + device.create_shader_module(wgpu::include_wgsl!("../assets/SimpleShader.wgsl")); + + let default_texture = SimpleTexture::load( + &device, + &queue, + [0, 0, 255, 255], + TextureProperties { + width: 1, + height: 1, + }, + ); + + let eye = Eye::new(&device, width, height, default_texture.clone()); + let texture_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + 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: 2, + 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: 3, + 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 pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[Some(&eye.layout), Some(&texture_bind_group_layout)], + immediate_size: 0, + }); + + let instance_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_main"), + buffers: &[SimpleVertex::desc(), SimpleInstances::desc()], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }, + fragment: Some(wgpu::FragmentState { + // 3. + module: &shader, + entry_point: Some("fs_main"), + targets: &[Some(wgpu::ColorTargetState { + // 4. + format: config.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }), + primitive: Default::default(), /*wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }*/ + depth_stencil: Some(wgpu::DepthStencilState { + stencil: wgpu::StencilState::default(), + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::Less), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview_mask: None, + cache: None, + }); + + //todo: use a pipeline cache! + let sky_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Sky Pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_sky"), + compilation_options: Default::default(), + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_sky"), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + // 4. + format: config.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + front_face: wgpu::FrontFace::Cw, + ..Default::default() + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: Some(false), + depth_compare: Some(wgpu::CompareFunction::LessEqual), + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState::default(), + multiview_mask: None, + cache: None, + }); + + let size = wgpu::Extent3d { + // 2. + width: config.width.max(1), + height: config.height.max(1), + depth_or_array_layers: 1, + }; + let desc = wgpu::TextureDescriptor { + label: Some("depth texture"), + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Depth32Float, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT // 3. | wgpu::TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }; - let depth_texture = device.create_texture(&desc); - let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default()); - let depth_sampler = device.create_sampler( - &wgpu::SamplerDescriptor { // 4. - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::MipmapFilterMode::Nearest, - compare: Some(wgpu::CompareFunction::LessEqual), // 5. - lod_min_clamp: 0.0, - lod_max_clamp: 100.0, - ..Default::default() - } - ); + view_formats: &[], + }; + let depth_texture = device.create_texture(&desc); + let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default()); + let depth_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + // 4. + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::MipmapFilterMode::Nearest, + compare: Some(wgpu::CompareFunction::LessEqual), // 5. + lod_min_clamp: 0.0, + lod_max_clamp: 100.0, + ..Default::default() + }); - let instances = SimpleInstances::new(&device,&queue); + let instances = SimpleInstances::new(&device); - let staging_belt = wgpu::util::StagingBelt::new(device.clone(), 0x1024); + let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&DEFAULT_VERTICES), + usage: wgpu::BufferUsages::VERTEX, + }); - let buffer = device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(&DEFAULT_VERTICES), - usage: wgpu::BufferUsages::VERTEX, - } - ); + let default_material = SimpleMaterial::new( + &device, + &texture_bind_group_layout, + &default_texture, + &default_texture, + &default_texture, + MaterialProperties::default(), + ); - Ok(Renderer { - id_count: 0, - material_layout: texture_bind_group_layout, - default_texture: texture, - depth_texture: SimpleTexture { - texture: depth_texture, - view: depth_view, - }, - instances, - pipeline, - surface, - config, - device, - queue, - eye, - }) - } - pub(crate) fn render(&mut self, window: &Arc) -> anyhow::Result<()> { - window.request_redraw(); - - let output = match self.surface.get_current_texture() { - wgpu::CurrentSurfaceTexture::Success(surface_texture) => surface_texture, - wgpu::CurrentSurfaceTexture::Suboptimal(surface_texture) => { - self.surface.configure(&self.device, &self.config); - surface_texture - } - wgpu::CurrentSurfaceTexture::Timeout - | wgpu::CurrentSurfaceTexture::Occluded - | wgpu::CurrentSurfaceTexture::Validation => { - // Skip this frame - return Ok(()); - } - wgpu::CurrentSurfaceTexture::Outdated => { - self.surface.configure(&self.device, &self.config); - return Ok(()); - } - wgpu::CurrentSurfaceTexture::Lost => { - // You could recreate the devices and all resources - // created with it here, but we'll just bail - anyhow::bail!("Lost device"); - } - }; - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - let program = self.instances.write_instances(&self.device,&self.queue); - - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - { - let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - depth_slice: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(Default::default()), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &self.depth_texture.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: wgpu::StoreOp::Store, - }), - stencil_ops: None, - }), - occlusion_query_set: None, - timestamp_writes: None, - multiview_mask: None, - }); - - pass.set_pipeline(&self.pipeline); - pass.set_bind_group(0, &self.eye.group, &[]); - pass.set_vertex_buffer(1, self.instances.instance_buffer.slice(..)); - program.render(&mut pass); + Ok(Renderer { + id_count: 0, + material_layout: texture_bind_group_layout, + default_texture, + default_material, + depth_texture: SimpleTexture { + texture: depth_texture, + view: depth_view, + }, + instances, + sky_pipeline, + instance_pipeline, + surface, + config, + device, + queue, + eye, + }) } + pub(crate) fn render(&mut self, window: &Arc) -> anyhow::Result<()> { + window.request_redraw(); + let output = match self.surface.get_current_texture() { + wgpu::CurrentSurfaceTexture::Success(surface_texture) => surface_texture, + wgpu::CurrentSurfaceTexture::Suboptimal(surface_texture) => { + self.surface.configure(&self.device, &self.config); + surface_texture + } + wgpu::CurrentSurfaceTexture::Timeout + | wgpu::CurrentSurfaceTexture::Occluded + | wgpu::CurrentSurfaceTexture::Validation => { + // Skip this frame + return Ok(()); + } + wgpu::CurrentSurfaceTexture::Outdated => { + self.surface.configure(&self.device, &self.config); + return Ok(()); + } + wgpu::CurrentSurfaceTexture::Lost => { + // You could recreate the devices and all resources + // created with it here, but we'll just bail + anyhow::bail!("Lost device"); + } + }; + let view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); - self.queue.submit(Some(encoder.finish())); + let program = self.instances.write_instances(&self.device, &self.queue); - self.eye.write(&self.queue); + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); - output.present(); + { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + depth_slice: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.5, + g: 0.6, + b: 0.8, + a: 1.0, + }), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: wgpu::StoreOp::Store, + }), + stencil_ops: None, + }), + occlusion_query_set: None, + timestamp_writes: None, + multiview_mask: None, + }); - Ok(()) - } -} \ No newline at end of file + pass.set_pipeline(&self.instance_pipeline); + pass.set_bind_group(0, &self.eye.group, &[]); + pass.set_vertex_buffer(1, self.instances.instance_buffer.slice(..)); + program.render(&mut pass); + + pass.set_bind_group(1, &self.default_material.group, &[]); + + pass.set_pipeline(&self.sky_pipeline); + pass.draw(0..6, 0..1); + } + + self.queue.submit(Some(encoder.finish())); + + self.eye.write(&self.queue); + + output.present(); + + Ok(()) + } +} diff --git a/src/world/mod.rs b/src/world/mod.rs index 94875fa..3802219 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -1,223 +1,228 @@ +use crate::render::{Renderer, SimpleLightData, SimpleModelData}; +use glam::{DVec3, IVec3, Mat4, UVec3, Vec3, Vec4Swizzles}; use std::cell::RefCell; use std::hash::{BuildHasherDefault, Hash, Hasher}; use std::ops::{Div, Mul}; use std::rc::Rc; -use glam::{DVec3, IVec3, Mat4, UVec3, Vec3, Vec4Swizzles}; use wgpu::naga::{FastHashMap, FastHashSet}; -use crate::render::{SimpleModelData, SimpleLightData, Renderer}; #[derive(Clone)] pub enum Shape { - Block(Vec3), - Sphere(f32), - None, + Block(Vec3), + Sphere(f32), + None, } #[derive(Clone)] pub struct SimpleColliderData { - pub transform: Mat4, - pub shape: Shape, - pub radius: f32, + pub transform: Mat4, + pub shape: Shape, + pub radius: f32, } #[derive(Clone)] pub struct SimpleObjectData { - pub model: Option, - pub collider: SimpleColliderData, + pub model: Option, + pub collider: SimpleColliderData, } #[derive(Clone)] pub struct SimpleLight(pub Rc>); impl PartialEq for SimpleLight { - fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.0, &other.0) - } + fn eq(&self, other: &Self) -> bool { + Rc::ptr_eq(&self.0, &other.0) + } } impl Eq for SimpleLight {} impl Hash for SimpleLight { - fn hash(&self, state: &mut H) { - (self.0.as_ptr() as *const RefCell).hash(state) - } + fn hash(&self, state: &mut H) { + (self.0.as_ptr() as *const RefCell).hash(state) + } } #[derive(Clone)] pub struct SimpleObject(pub Rc>); impl SimpleObject { - pub(crate) fn hard_clone(&self) -> SimpleObject { - SimpleObject { - 0: Rc::new(RefCell::new(SimpleObjectData { - model: self.0.borrow().model.clone(), - collider: self.0.borrow().collider.clone(), - })), + pub(crate) fn hard_clone(&self) -> SimpleObject { + SimpleObject { + 0: Rc::new(RefCell::new(SimpleObjectData { + model: self.0.borrow().model.clone(), + collider: self.0.borrow().collider.clone(), + })), + } } - } } impl PartialEq for SimpleObject { - fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.0,&other.0) - } + fn eq(&self, other: &Self) -> bool { + Rc::ptr_eq(&self.0, &other.0) + } } impl Eq for SimpleObject {} impl Hash for SimpleObject { - fn hash(&self, state: &mut H) { - (self.0.as_ptr() as *const RefCell).hash(state); - } + fn hash(&self, state: &mut H) { + (self.0.as_ptr() as *const RefCell).hash(state); + } } #[derive(Eq, Hash, PartialEq, Clone)] enum Interest { - Light(SimpleLight), - Object(SimpleObject), + Light(SimpleLight), + Object(SimpleObject), } struct Block { - debug: Option, - volume: u8, - velocity: Vec3, - material: u8, - interests: FastHashSet, - blocks: [Option>;64], + debug: Option, + volume: u8, + velocity: Vec3, + material: u8, + interests: FastHashSet, + blocks: [Option>; 64], } const TREE_ATTACK: usize = 4; const TREE_FLOOR: usize = 4; impl Block { - fn new() -> Block { - Block { - debug: None, - volume: 0, - velocity: Vec3::ZERO, - material: 0, - interests: FastHashSet::with_hasher(BuildHasherDefault::default()), - blocks: [const { None }; const { TREE_ATTACK * TREE_ATTACK * TREE_ATTACK }], - } - } - fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32, block_size: f32) { - let cs = block_size / const { TREE_ATTACK as f32 }; - if rad < cs || block_size >= TREE_FLOOR as f32 { - let rel = ((pos + block_size / 2.0) / cs).round().as_uvec3(); - let new_pos = pos - (rel.as_vec3() + cs / 2.0); - let index = (rel.x + rel.y * 4 + rel.z * 16) as usize; - match self.blocks[index] { - Some(ref mut block) => { - block.place_pos(interest,new_pos,rad,cs); - }, - None => { - let mut block = Box::new(Block::new()); - block.place_pos(interest,new_pos,rad,cs); - self.blocks[index] = Some(block); + fn new() -> Block { + Block { + debug: None, + volume: 0, + velocity: Vec3::ZERO, + material: 0, + interests: FastHashSet::with_hasher(BuildHasherDefault::default()), + blocks: [const { None }; const { TREE_ATTACK * TREE_ATTACK * TREE_ATTACK }], } - } - } else { - self.interests.insert(interest); } - } - fn debug_step(&mut self, value: bool) { - match self.debug { - Some(ref mut debug) => { - if value { - + fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32, block_size: f32) { + let cs = block_size / const { TREE_ATTACK as f32 }; + if rad < cs || block_size >= TREE_FLOOR as f32 { + let rel = ((pos + block_size / 2.0) / cs).round().as_uvec3(); + let new_pos = pos - (rel.as_vec3() + cs / 2.0); + let index = (rel.x + rel.y * 4 + rel.z * 16) as usize; + match self.blocks[index] { + Some(ref mut block) => { + block.place_pos(interest, new_pos, rad, cs); + } + None => { + let mut block = Box::new(Block::new()); + block.place_pos(interest, new_pos, rad, cs); + self.blocks[index] = Some(block); + } + } } else { - + self.interests.insert(interest); + } + } + fn debug_step(&mut self, value: bool) { + match self.debug { + Some(ref mut debug) => { + if value { + } else { + } + } + None => { + if value { + } else { + } + } + } + for maybe_block in self.blocks.iter_mut() { + if let Some(block) = maybe_block { + block.debug_step(value) + } } - }, - None => { - if value { - - } else { - - } - } } - } } pub struct World { - chunk_size: u32, - map: FastHashMap, - pub renderer: Option, - debug_world_map: bool, - debug_world_map_object: Option, + chunk_size: u32, + map: FastHashMap, + pub renderer: Option, + debug_world_map: bool, + debug_world_map_object: Option, } impl World { - pub fn new() -> World { - World { - chunk_size: 512, - map: FastHashMap::with_hasher(BuildHasherDefault::default()), - renderer: None, - debug_world_map: false, - debug_world_map_object: None, - } - } - pub fn add_renderer(&mut self, renderer: Renderer) { - self.renderer = Some(renderer); - self.debug_world_map_object = self.renderer.as_mut().unwrap().load_from_gltf(include_bytes!("../assets/debug.glb")).first_object() - } - pub fn add_object(&mut self, object: SimpleObject) { - self.place(Interest::Object(object.clone())); - if let Some(ref mut renderer) = self.renderer { - renderer.instances.add_object(object) - } - } - pub fn set_debug(&mut self, value: bool) { - self.debug_world_map = value; - } - pub fn light_step(&mut self) { - - } - pub fn object_step(&mut self) { - - } - pub fn debug_step(&mut self) { - self.light_step(); - self.object_step(); - for (index, item) in self.map.iter_mut() { - item.debug_step(self.debug_world_map); - } - } - fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32) { - let cs = self.chunk_size as f32; - let c_rad = ((cs / 2.0) * (cs / 2.0)) * 3.0; - for x in (pos.x - rad).div(cs) as i32..=(pos.x + rad).div(cs).ceil() as i32 { - for y in (pos.y - rad).div(cs) as i32..=(pos.y + rad).div(cs).ceil() as i32 { - for z in (pos.z - rad).div(cs) as i32..=(pos.z + rad).div(cs).ceil() as i32 { - let block_pos = IVec3::new(x,y,z); - let block_pos_f32 = block_pos.as_vec3(); - if block_pos_f32.distance_squared(pos) < rad + c_rad { - self.map.entry(block_pos).or_insert_with(|| { - let mut block = Block::new(); - block.place_pos( - interest.clone(), - pos - block_pos_f32, - rad, - self.chunk_size as f32, - ); - block - }); - } + pub fn new() -> World { + World { + chunk_size: 512, + map: FastHashMap::with_hasher(BuildHasherDefault::default()), + renderer: None, + debug_world_map: false, + debug_world_map_object: None, } - } } - } - fn place(&mut self, interest: Interest) { - let (pos,rad) = match &interest { - Interest::Light(light) => { - let light = light.0.borrow(); - (light.instance.location.xyz(),light.instance.color.length()) - }, - Interest::Object(object) => { - let collider = &object.0.borrow().collider; - (collider.transform.to_scale_rotation_translation().2,collider.radius) - }, - }; - self.place_pos(interest,pos,rad); - } -} \ No newline at end of file + pub fn add_renderer(&mut self, renderer: Renderer) { + self.renderer = Some(renderer); + self.debug_world_map_object = self + .renderer + .as_mut() + .unwrap() + .load_from_gltf(include_bytes!("../assets/cube.glb")) + .first_object() + } + pub fn add_object(&mut self, object: SimpleObject) { + self.place(Interest::Object(object.clone())); + if let Some(ref mut renderer) = self.renderer { + renderer.instances.add_object(object) + } + } + pub fn set_debug(&mut self, value: bool) { + self.debug_world_map = value; + } + pub fn light_step(&mut self) {} + pub fn object_step(&mut self) {} + pub fn debug_step(&mut self) { + self.light_step(); + self.object_step(); + for (index, item) in self.map.iter_mut() { + item.debug_step(self.debug_world_map); + } + } + fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32) { + let cs = self.chunk_size as f32; + let c_rad = ((cs / 2.0) * (cs / 2.0)) * 3.0; + for x in (pos.x - rad).div(cs) as i32..=(pos.x + rad).div(cs).ceil() as i32 { + for y in (pos.y - rad).div(cs) as i32..=(pos.y + rad).div(cs).ceil() as i32 { + for z in (pos.z - rad).div(cs) as i32..=(pos.z + rad).div(cs).ceil() as i32 { + let block_pos = IVec3::new(x, y, z); + let block_pos_f32 = block_pos.as_vec3(); + if block_pos_f32.distance_squared(pos) < rad + c_rad { + self.map.entry(block_pos).or_insert_with(|| { + let mut block = Block::new(); + block.place_pos( + interest.clone(), + pos - block_pos_f32, + rad, + self.chunk_size as f32, + ); + block + }); + } + } + } + } + } + fn place(&mut self, interest: Interest) { + let (pos, rad) = match &interest { + Interest::Light(light) => { + let light = light.0.borrow(); + (light.instance.location.xyz(), light.instance.color.length()) + } + Interest::Object(object) => { + let collider = &object.0.borrow().collider; + ( + collider.transform.to_scale_rotation_translation().2, + collider.radius, + ) + } + }; + self.place_pos(interest, pos, rad); + } +}