diff --git a/src/render/renderer/default_state.rs b/src/render/renderer/default_state.rs new file mode 100644 index 0000000..47b5ad1 --- /dev/null +++ b/src/render/renderer/default_state.rs @@ -0,0 +1,320 @@ +use cgmath::prelude::*; +use rayon::prelude::*; +use wgpu::util::DeviceExt; +use winit::event::{ElementState, KeyboardInput, MouseButton, WindowEvent}; + +use crate::{ + camera, + model::{self, DrawLight, DrawModel}, + render, resources, texture, CameraUniform, Instance, LightUniform, NUM_INSTANCES_PER_ROW, +}; + +use super::Renderer; + +pub struct DefaultState { + renderer: Box, + obj_model: model::Model, + camera: camera::Camera, + projection: camera::Projection, + camera_controller: camera::CameraController, + camera_uniform: CameraUniform, + camera_buffer: wgpu::Buffer, + camera_bind_group: wgpu::BindGroup, + instances: Vec, + #[allow(dead_code)] + instance_buffer: wgpu::Buffer, + depth_texture: texture::Texture, + light_uniform: LightUniform, + light_buffer: wgpu::Buffer, + light_bind_group: wgpu::BindGroup, + #[allow(dead_code)] + debug_material: model::Material, + mouse_pressed: bool, + pipelines: render::Pipelines, +} + +impl super::State for DefaultState { + fn new(renderer: Box) -> Self + where + Self: Sized, + { + let global_bind_layout = render::GlobalBindLayout::new(&renderer.device); + let pipelines = + render::Pipelines::new(&global_bind_layout, &renderer.device, &renderer.config); + + let camera = camera::Camera::new((0.0, 5.0, 10.0), cgmath::Deg(-90.0), cgmath::Deg(-20.0)); + let projection = camera::Projection::new( + renderer.config.width, + renderer.config.height, + cgmath::Deg(45.0), + 0.1, + 100.0, + ); + let camera_controller = camera::CameraController::new(4.0, 0.4); + + let mut camera_uniform = CameraUniform::new(); + camera_uniform.update_view_proj(&camera, &projection); + + let camera_buffer = renderer + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Camera Buffer"), + contents: bytemuck::cast_slice(&[camera_uniform]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + const SPACE_BETWEEN: f32 = 3.0; + let iter = { + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + (0..NUM_INSTANCES_PER_ROW) + .into_iter() + } else { + (0..NUM_INSTANCES_PER_ROW) + .into_par_iter() + } + } + }; + let instances = iter + .clone() + .flat_map(|z| { + // UPDATED! + iter.clone().map(move |x| { + let x = SPACE_BETWEEN * (x as f32 - NUM_INSTANCES_PER_ROW as f32 / 2.0); + let z = SPACE_BETWEEN * (z as f32 - NUM_INSTANCES_PER_ROW as f32 / 2.0); + + let position = cgmath::Vector3 { x, y: 0.0, z }; + + let rotation = if position.is_zero() { + cgmath::Quaternion::from_axis_angle( + cgmath::Vector3::unit_z(), + cgmath::Deg(0.0), + ) + } else { + cgmath::Quaternion::from_axis_angle(position.normalize(), cgmath::Deg(45.0)) + }; + + Instance { position, rotation } + }) + }) + .collect::>(); + + let instance_data = instances.iter().map(Instance::to_raw).collect::>(); + let instance_buffer = + (&renderer.device).create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Instance Buffer"), + contents: bytemuck::cast_slice(&instance_data), + usage: wgpu::BufferUsages::VERTEX, + }); + + let camera_bind_group = renderer + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + layout: global_bind_layout.get_camera_bind_layout(), + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: camera_buffer.as_entire_binding(), + }], + label: Some("camera_bind_group"), + }); + + let obj_model = async_std::task::block_on(resources::load_model( + "cube.obj", + &renderer.device, + &renderer.queue, + global_bind_layout.get_texture_bind_layout(), + )) + .unwrap(); + + let light_uniform = LightUniform { + position: [2.0, 2.0, 2.0], + _padding: 0, + color: [1.0, 1.0, 1.0], + _padding2: 0, + }; + + let light_buffer = renderer + .device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Light VB"), + contents: bytemuck::cast_slice(&[light_uniform]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let light_bind_group = renderer + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + layout: global_bind_layout.get_light_bind_layout(), + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: light_buffer.as_entire_binding(), + }], + label: None, + }); + + let depth_texture = texture::Texture::create_depth_texture( + &renderer.device, + &renderer.config, + "depth_texture", + ); + + let debug_material = { + let diffuse_bytes = include_bytes!("../../../res/cobble-diffuse.png"); + let normal_bytes = include_bytes!("../../../res/cobble-normal.png"); + + let diffuse_texture = texture::Texture::from_bytes( + &renderer.device, + &renderer.queue, + diffuse_bytes, + "res/alt-diffuse.png", + false, + ) + .unwrap(); + let normal_texture = texture::Texture::from_bytes( + &renderer.device, + &renderer.queue, + normal_bytes, + "res/alt-normal.png", + true, + ) + .unwrap(); + + model::Material::new( + &renderer.device, + "alt-material", + diffuse_texture, + normal_texture, + global_bind_layout.get_texture_bind_layout(), + ) + }; + + Self { + obj_model, + camera, + projection, + camera_controller, + camera_buffer, + camera_bind_group, + camera_uniform, + instances, + instance_buffer, + depth_texture, + light_uniform, + light_buffer, + light_bind_group, + #[allow(dead_code)] + debug_material, + mouse_pressed: false, + pipelines, + renderer, + } + } + + fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { + self.projection.resize(new_size.width, new_size.height); + self.depth_texture = texture::Texture::create_depth_texture( + &self.renderer.device, + &self.renderer.config, + "depth_texture", + ); + } + + fn input(&mut self, event: &winit::event::WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: + KeyboardInput { + virtual_keycode: Some(key), + state, + .. + }, + .. + } => self.camera_controller.process_keyboard(*key, *state), + WindowEvent::MouseWheel { delta, .. } => { + self.camera_controller.process_scroll(delta); + true + } + WindowEvent::MouseInput { + button: MouseButton::Left, + state, + .. + } => { + self.mouse_pressed = *state == ElementState::Pressed; + true + } + _ => false, + } + } + + fn update(&mut self, dt: instant::Duration) { + self.camera_controller.update_camera(&mut self.camera, dt); + self.camera_uniform + .update_view_proj(&self.camera, &self.projection); + self.renderer.queue.write_buffer( + &self.camera_buffer, + 0, + bytemuck::cast_slice(&[self.camera_uniform]), + ); + + // Update the light + let old_position: cgmath::Vector3<_> = self.light_uniform.position.into(); + self.light_uniform.position = + (cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0)) + * old_position) + .into(); + self.renderer.queue.write_buffer( + &self.light_buffer, + 0, + bytemuck::cast_slice(&[self.light_uniform]), + ); + } + + fn render( + &mut self, + view: &wgpu::TextureView, + encoder: &mut wgpu::CommandEncoder, + ) -> Result<(), wgpu::SurfaceError> { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }), + store: true, + }, + }], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: true, + }), + stencil_ops: None, + }), + }); + + render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..)); + render_pass.set_pipeline(self.pipelines.get_light_pipeline()); + render_pass.draw_light_model( + &self.obj_model, + &self.camera_bind_group, + &self.light_bind_group, + ); + + render_pass.set_pipeline(self.pipelines.get_render_pipeline()); + render_pass.draw_model_instanced( + &self.obj_model, + 0..self.instances.len() as u32, + &self.camera_bind_group, + &self.light_bind_group, + ); + + Ok(()) + } +} diff --git a/src/render/renderer/mod.rs b/src/render/renderer/mod.rs index 49150b9..bb751ef 100644 --- a/src/render/renderer/mod.rs +++ b/src/render/renderer/mod.rs @@ -1,11 +1,14 @@ mod renderer; - pub use renderer::Renderer; + +mod default_state; +pub use default_state::DefaultState; + use wgpu::{TextureView, CommandEncoder}; use winit::event::WindowEvent; pub trait State { - fn new(renderer: &Renderer) -> Self where Self: Sized; + fn new(renderer: Box) -> Self where Self: Sized; fn resize(&mut self, new_size: winit::dpi::PhysicalSize); fn input(&mut self, event: &WindowEvent) -> bool; fn update(&mut self, dt: instant::Duration); diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index 1097b7e..5766393 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -5,11 +5,11 @@ use winit::{event::WindowEvent, window::Window}; use super::State; pub struct Renderer { - surface: wgpu::Surface, - device: wgpu::Device, - queue: wgpu::Queue, - size: winit::dpi::PhysicalSize, - config: wgpu::SurfaceConfiguration, + pub surface: wgpu::Surface, + pub device: wgpu::Device, + pub queue: wgpu::Queue, + pub size: winit::dpi::PhysicalSize, + pub config: wgpu::SurfaceConfiguration, state: Option> }