diff --git a/examples/simple.rs b/examples/simple.rs index 8a6a1c3f22..cb89a1a7c3 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -168,10 +168,17 @@ fn main() { let cube = Mesh::load(MeshType::Cube); let plane = Mesh::load(MeshType::Plane{ size: 24.5 }); + let quad = Mesh::load(MeshType::Quad { + north_west: math::vec2(100.0, 200.0), + north_east: math::vec2(200.0, 200.0), + south_west: math::vec2(100.0, 100.0), + south_east: math::vec2(200.0, 100.0), + }); let mut mesh_storage = AssetStorage::::new(); let _cube_handle = mesh_storage.add(cube, "cube"); let plane_handle = mesh_storage.add(plane, "plane"); + let quad_handle = mesh_storage.add(quad, "quad"); world.resources.insert(mesh_storage); let transform_system_bundle = transform_system_bundle::build(&mut world); @@ -254,6 +261,7 @@ fn main() { far: 1000.0, aspect_ratio: 1.0, }), + ActiveCamera, LocalToWorld(Mat4::look_at_rh( Vec3::new(6.0, -40.0, 20.0), Vec3::new(0.0, 0.0, 0.0), @@ -262,6 +270,31 @@ fn main() { ) ]); + world.insert((), vec![ + + // camera + ( + Camera::new(CameraType::Orthographic { + left: 0.0, + right: 0.0, + bottom: 0.0, + top: 0.0, + near: 0.0, + far: 1.0, + }), + ActiveCamera2d, + ) + ]); + + world.insert((), vec![ + + // camera + ( + quad_handle, + Mesh2d, + ) + ]); + let mut rng = StdRng::from_entropy(); for _ in 0 .. 70000 { create_person(&mut world, _cube_handle.clone(), diff --git a/src/application.rs b/src/application.rs index 6a8bc6835a..6e78df20e6 100644 --- a/src/application.rs +++ b/src/application.rs @@ -22,12 +22,14 @@ impl Application { fn add_default_passes(&mut self) { self.render_graph.add_render_resource_manager(Box::new(render_resources::MaterialResourceManager)); self.render_graph.add_render_resource_manager(Box::new(render_resources::LightResourceManager::new(10))); - self.render_graph.add_render_resource_manager(Box::new(render_resources::CameraResourceManager)); + self.render_graph.add_render_resource_manager(Box::new(render_resources::GlobalResourceManager)); + self.render_graph.add_render_resource_manager(Box::new(render_resources::Global2dResourceManager)); let depth_format = wgpu::TextureFormat::Depth32Float; self.render_graph.set_pass("forward", Box::new(ForwardPass::new(depth_format))); self.render_graph.set_pipeline("forward", "forward", Box::new(ForwardPipeline::new())); self.render_graph.set_pipeline("forward", "forward_instanced", Box::new(ForwardInstancedPipeline::new(depth_format))); + self.render_graph.set_pipeline("forward", "ui", Box::new(UiPipeline::new())); } fn update(&mut self) { diff --git a/src/render/camera.rs b/src/render/camera.rs index 7a4a469958..4e39a82621 100644 --- a/src/render/camera.rs +++ b/src/render/camera.rs @@ -1,12 +1,23 @@ use crate::math::Mat4; +pub struct ActiveCamera; +pub struct ActiveCamera2d; + pub enum CameraType { Projection { fov: f32, aspect_ratio: f32, near: f32, far: f32 - } + }, + Orthographic { + left: f32, + right: f32, + bottom: f32, + top: f32, + near: f32, + far: f32, + }, } pub struct Camera { @@ -26,18 +37,29 @@ impl Camera { match &mut self.camera_type { CameraType::Projection { aspect_ratio, fov, near, far } => { *aspect_ratio = width as f32 / height as f32; - self.view_matrix = get_projection_matrix(*fov, *aspect_ratio, *near, *far) + self.view_matrix = get_perspective_projection_matrix(*fov, *aspect_ratio, *near, *far) + }, + CameraType::Orthographic { left, right, bottom, top, near, far} => { + *right = width as f32; + *top = height as f32; + self.view_matrix = get_orthographic_projection_matrix(*left, *right, *bottom, *top, *near, *far) } } } } -pub fn get_projection_matrix(fov: f32, aspect_ratio: f32, near: f32, far: f32) -> Mat4 { +pub fn get_perspective_projection_matrix(fov: f32, aspect_ratio: f32, near: f32, far: f32) -> Mat4 { let projection = Mat4::perspective_rh_gl(fov, aspect_ratio, near, far); opengl_to_wgpu_matrix() * projection } +pub fn get_orthographic_projection_matrix(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Mat4 { + let projection = Mat4::orthographic_rh_gl(left, right, bottom, top, near, far); + + opengl_to_wgpu_matrix() * projection +} + pub fn opengl_to_wgpu_matrix() -> Mat4 { Mat4::from_cols_array(&[ 1.0, 0.0, 0.0, 0.0, diff --git a/src/render/light.rs b/src/render/light.rs index d9d618eb55..5b4b18d684 100644 --- a/src/render/light.rs +++ b/src/render/light.rs @@ -20,7 +20,7 @@ pub struct LightRaw { impl LightRaw { pub fn from(light: &Light, transform: &math::Mat4, translation: &Translation) -> LightRaw { - let proj = camera::get_projection_matrix(light.fov, 1.0, light.depth.start, light.depth.end) * *transform; + let proj = camera::get_perspective_projection_matrix(light.fov, 1.0, light.depth.start, light.depth.end) * *transform; let (x, y, z) = translation.0.into(); LightRaw { proj: proj.to_cols_array_2d(), diff --git a/src/render/mesh.rs b/src/render/mesh.rs index 2627f4e6ad..cbdc207898 100644 --- a/src/render/mesh.rs +++ b/src/render/mesh.rs @@ -1,12 +1,21 @@ -use crate::{vertex::Vertex, asset::Asset}; +use crate::{vertex::Vertex, asset::Asset, math::*}; use wgpu::{Buffer, Device}; use zerocopy::AsBytes; +// TODO: this is pretty dirty. work out a cleaner way to distinguish between 3d and 2d meshes +pub struct Mesh2d; + pub enum MeshType { Cube, Plane { size: f32 }, + Quad { + north_west: Vec2, + north_east: Vec2, + south_west: Vec2, + south_east: Vec2, + } } pub struct Mesh { @@ -33,6 +42,7 @@ impl Asset for Mesh { let (vertices, indices) = match descriptor { MeshType::Cube => create_cube(), MeshType::Plane { size } => create_plane(size), + MeshType::Quad { north_west, north_east, south_west, south_east } => create_quad(north_west, north_east, south_west, south_east), }; Mesh { @@ -44,6 +54,18 @@ impl Asset for Mesh { } } +pub fn create_quad(north_west: Vec2, north_east: Vec2, south_west: Vec2, south_east: Vec2) -> (Vec, Vec) { + let vertex_data = [ + Vertex::from(([south_west.x(), south_west.y(), 0.0], [0.0, 0.0, 1.0])), + Vertex::from(([north_west.x(), north_west.y(), 0.0], [0.0, 0.0, 1.0])), + Vertex::from(([north_east.x(), north_east.y(), 0.0], [0.0, 0.0, 1.0])), + Vertex::from(([south_east.x(), south_east.y(), 0.0], [0.0, 0.0, 1.0])), + ]; + + let index_data: &[u16] = &[ 0, 1, 2, 0, 2, 3 ]; + return (vertex_data.to_vec(), index_data.to_vec()); +} + pub fn create_cube() -> (Vec, Vec) { let vertex_data = [ // top (0, 0, 1) @@ -98,6 +120,7 @@ pub fn create_plane(size: f32) -> (Vec, Vec) { Vertex::from(([-size, size, 0.0], [0.0, 0.0, 1.0])), ]; + // TODO: make sure this order is correct let index_data: &[u16] = &[0, 1, 2, 2, 1, 3]; (vertex_data.to_vec(), index_data.to_vec()) diff --git a/src/render/passes/forward/forward_pipeline.rs b/src/render/passes/forward/forward_pipeline.rs index 316c9a8ce5..0706c431fb 100644 --- a/src/render/passes/forward/forward_pipeline.rs +++ b/src/render/passes/forward/forward_pipeline.rs @@ -127,7 +127,7 @@ impl Pipeline for ForwardPipeline { let mut mesh_query = <(Read, Read>)>::query() .filter(!component::()); - for (entity, mesh) in mesh_query.iter_immutable(world) { + for (material, mesh) in mesh_query.iter_immutable(world) { let current_mesh_id = *mesh.id.read().unwrap(); let mut should_load_mesh = last_mesh_id == None; @@ -144,7 +144,7 @@ impl Pipeline for ForwardPipeline { } if let Some(ref mesh_asset) = mesh_storage.get(*mesh.id.read().unwrap()) { - pass.set_bind_group(1, entity.bind_group.as_ref().unwrap(), &[]); + pass.set_bind_group(1, material.bind_group.as_ref().unwrap(), &[]); pass.draw_indexed(0 .. mesh_asset.indices.len() as u32, 0, 0 .. 1); }; diff --git a/src/render/passes/forward_shadow/mod.rs b/src/render/passes/forward_shadow/mod.rs index 7f69c8e293..7db700ff44 100644 --- a/src/render/passes/forward_shadow/mod.rs +++ b/src/render/passes/forward_shadow/mod.rs @@ -147,10 +147,10 @@ impl Pipeline for ForwardShadowPassNew { pass.set_bind_group(0, self.bind_group.as_ref().unwrap(), &[]); let mut mesh_storage = world.resources.get_mut::>().unwrap(); - for (entity, mesh) in mesh_query.iter_immutable(world) { + for (material, mesh) in mesh_query.iter_immutable(world) { if let Some(mesh_asset) = mesh_storage.get(*mesh.id.read().unwrap()) { mesh_asset.setup_buffers(&render_graph.device); - pass.set_bind_group(1, entity.bind_group.as_ref().unwrap(), &[]); + pass.set_bind_group(1, material.bind_group.as_ref().unwrap(), &[]); pass.set_index_buffer(mesh_asset.index_buffer.as_ref().unwrap(), 0); pass.set_vertex_buffers(0, &[(&mesh_asset.vertex_buffer.as_ref().unwrap(), 0)]); pass.draw_indexed(0 .. mesh_asset.indices.len() as u32, 0, 0 .. 1); diff --git a/src/render/passes/mod.rs b/src/render/passes/mod.rs index 2ad6658ecf..b935e788a9 100644 --- a/src/render/passes/mod.rs +++ b/src/render/passes/mod.rs @@ -3,8 +3,10 @@ mod forward; mod forward_shadow; mod forward_instanced; mod shadow; +mod ui; pub use forward::{ForwardUniforms, ForwardPipeline, ForwardPass}; pub use forward_shadow::{ForwardShadowPassNew}; pub use forward_instanced::ForwardInstancedPipeline; -pub use shadow::ShadowPass; \ No newline at end of file +pub use shadow::ShadowPass; +pub use ui::UiPipeline; \ No newline at end of file diff --git a/src/render/passes/shadow/shadow_pipeline.rs b/src/render/passes/shadow/shadow_pipeline.rs index a0f5ac32a2..90ad28541e 100644 --- a/src/render/passes/shadow/shadow_pipeline.rs +++ b/src/render/passes/shadow/shadow_pipeline.rs @@ -146,11 +146,11 @@ impl Pipeline for ShadowPipeline { .resources .get_mut::>() .unwrap(); - for (entity, mesh) in mesh_query.iter_immutable(world) { + for (material, mesh) in mesh_query.iter_immutable(world) { if let Some(mesh_asset) = mesh_storage.get(*mesh.id.read().unwrap()) { mesh_asset.setup_buffers(&render_graph.device); - pass.set_bind_group(1, entity.bind_group.as_ref().unwrap(), &[]); + pass.set_bind_group(1, material.bind_group.as_ref().unwrap(), &[]); pass.set_index_buffer(&mesh_asset.index_buffer.as_ref().unwrap(), 0); pass.set_vertex_buffers(0, &[(&mesh_asset.vertex_buffer.as_ref().unwrap(), 0)]); pass.draw_indexed(0..mesh_asset.indices.len() as u32, 0, 0..1); diff --git a/src/render/passes/ui/mod.rs b/src/render/passes/ui/mod.rs new file mode 100644 index 0000000000..ceb2aecd55 --- /dev/null +++ b/src/render/passes/ui/mod.rs @@ -0,0 +1,149 @@ +use crate::{render::*, asset::*, render::mesh::*}; +use legion::prelude::*; +use wgpu::SwapChainOutput; + +pub struct UiPipeline { + pub pipeline: Option, + pub depth_format: wgpu::TextureFormat, + pub bind_group: Option, +} + +impl UiPipeline { + pub fn new() -> Self { + UiPipeline { + pipeline: None, + bind_group: None, + depth_format: wgpu::TextureFormat::Depth32Float + } + } +} + +impl Pipeline for UiPipeline { + fn initialize(&mut self, render_graph: &mut RenderGraphData, _: &mut World) { + let vs_bytes = shader::load_glsl( + include_str!("ui.vert"), + shader::ShaderStage::Vertex, + ); + let fs_bytes = shader::load_glsl( + include_str!("ui.frag"), + shader::ShaderStage::Fragment, + ); + + let bind_group_layout = + render_graph.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, // global_2d + visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::UniformBuffer { dynamic: false }, + }, + ], + }); + + self.bind_group = Some({ + + let global_2d_uniform_buffer = render_graph.get_uniform_buffer(render_resources::GLOBAL_2D_UNIFORM_BUFFER_NAME).unwrap(); + + // Create bind group + render_graph.device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: global_2d_uniform_buffer.get_binding_resource(), + }, + ], + }) + }); + + let pipeline_layout = render_graph.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&bind_group_layout], + }); + + let vertex_buffer_descriptor = get_vertex_buffer_descriptor(); + + let vs_module = render_graph.device.create_shader_module(&vs_bytes); + let fs_module = render_graph.device.create_shader_module(&fs_bytes); + + self.pipeline = Some(render_graph.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::None, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[ + wgpu::ColorStateDescriptor { + format: render_graph.swap_chain_descriptor.format, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + ], + depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { + format: self.depth_format, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_read_mask: 0, + stencil_write_mask: 0, + }), + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[vertex_buffer_descriptor], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + })); + } + + fn render(&mut self, render_graph: &RenderGraphData, pass: &mut wgpu::RenderPass, _: &SwapChainOutput, world: &mut World) { + pass.set_bind_group(0, self.bind_group.as_ref().unwrap(), &[]); + + let mut mesh_storage = world.resources.get_mut::>().unwrap(); + let mut last_mesh_id = None; + let mut mesh_query = + <(Read>, Read)>::query() + .filter(!component::()); + for (mesh, _) in mesh_query.iter_immutable(world) { + let current_mesh_id = *mesh.id.read().unwrap(); + + let mut should_load_mesh = last_mesh_id == None; + if let Some(last) = last_mesh_id { + should_load_mesh = last != current_mesh_id; + } + + if should_load_mesh { + if let Some(mesh_asset) = mesh_storage.get(*mesh.id.read().unwrap()) { + mesh_asset.setup_buffers(&render_graph.device); + pass.set_index_buffer(mesh_asset.index_buffer.as_ref().unwrap(), 0); + pass.set_vertex_buffers(0, &[(&mesh_asset.vertex_buffer.as_ref().unwrap(), 0)]); + }; + } + + if let Some(ref mesh_asset) = mesh_storage.get(*mesh.id.read().unwrap()) { + pass.draw_indexed(0 .. mesh_asset.indices.len() as u32, 0, 0 .. 1); + }; + + last_mesh_id = Some(current_mesh_id); + } + } + + fn resize(&mut self, _: &RenderGraphData) { + } + + fn get_pipeline(&self) -> &wgpu::RenderPipeline { + self.pipeline.as_ref().unwrap() + } +} \ No newline at end of file diff --git a/src/render/passes/ui/ui.frag b/src/render/passes/ui/ui.frag new file mode 100644 index 0000000000..9673b6c4ce --- /dev/null +++ b/src/render/passes/ui/ui.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) in vec3 v_Normal; +layout(location = 1) in vec4 v_Position; + +layout(location = 0) out vec4 o_Target; + +void main() { + o_Target = vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/src/render/passes/ui/ui.vert b/src/render/passes/ui/ui.vert new file mode 100644 index 0000000000..4fa1006812 --- /dev/null +++ b/src/render/passes/ui/ui.vert @@ -0,0 +1,17 @@ +#version 450 + +layout(location = 0) in vec4 a_Pos; +layout(location = 1) in vec4 a_Normal; + +layout(location = 0) out vec3 v_Normal; +layout(location = 1) out vec4 v_Position; + +layout(set = 0, binding = 0) uniform Globals { + mat4 u_ViewProj; +}; + +void main() { + v_Normal = vec3(a_Normal.xyz); + v_Position = vec4(a_Pos); + gl_Position = u_ViewProj * v_Position; +} diff --git a/src/render/render_resources/global_2d_resource_manager.rs b/src/render/render_resources/global_2d_resource_manager.rs new file mode 100644 index 0000000000..ca81e11c8c --- /dev/null +++ b/src/render/render_resources/global_2d_resource_manager.rs @@ -0,0 +1,51 @@ +use crate::{render::*, math}; + +use legion::prelude::*; +use std::mem; +use zerocopy::{AsBytes, FromBytes}; + +pub const GLOBAL_2D_UNIFORM_BUFFER_NAME: &str = "global_2d"; + +#[repr(C)] +#[derive(Clone, Copy, AsBytes, FromBytes)] +pub struct Global2dUniforms { + pub projection_matrix: [[f32; 4]; 4], +} + +pub struct Global2dResourceManager; + +impl RenderResourceManager for Global2dResourceManager { + fn initialize(&self, render_graph: &mut RenderGraphData, _: &mut World) { + let uniform_size = mem::size_of::() as wgpu::BufferAddress; + let ui_uniforms = Global2dUniforms { + projection_matrix: math::Mat4::identity().to_cols_array_2d(), + }; + + let buffer = render_graph.device.create_buffer_with_data( + ui_uniforms.as_bytes(), + wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + ); + + let uniform_buffer = UniformBuffer { + buffer: buffer, + size: uniform_size, + }; + render_graph.set_uniform_buffer(GLOBAL_2D_UNIFORM_BUFFER_NAME, uniform_buffer); + } + + fn update<'a>(&mut self, _render_graph: &mut RenderGraphData, _encoder: &'a mut wgpu::CommandEncoder, _world: &mut World) { + + } + + fn resize<'a>(&self, render_graph: &mut RenderGraphData, encoder: &'a mut wgpu::CommandEncoder, world: &mut World) { + for (mut camera, _) in <(Write, Read)>::query().iter(world) { + camera.update(render_graph.swap_chain_descriptor.width, render_graph.swap_chain_descriptor.height); + let camera_matrix: [[f32; 4]; 4] = camera.view_matrix.to_cols_array_2d(); + let matrix_size = mem::size_of::<[[f32; 4]; 4]>() as u64; + let temp_camera_buffer = + render_graph.device.create_buffer_with_data(camera_matrix.as_bytes(), wgpu::BufferUsage::COPY_SRC); + let global_2d_uniform_buffer = render_graph.get_uniform_buffer(GLOBAL_2D_UNIFORM_BUFFER_NAME).unwrap(); + encoder.copy_buffer_to_buffer(&temp_camera_buffer, 0, &global_2d_uniform_buffer.buffer, 0, matrix_size); + } + } +} \ No newline at end of file diff --git a/src/render/render_resources/camera_resource_manager.rs b/src/render/render_resources/global_resource_manager.rs similarity index 89% rename from src/render/render_resources/camera_resource_manager.rs rename to src/render/render_resources/global_resource_manager.rs index b131c082f2..6229abedb0 100644 --- a/src/render/render_resources/camera_resource_manager.rs +++ b/src/render/render_resources/global_resource_manager.rs @@ -6,9 +6,9 @@ use zerocopy::AsBytes; pub const FORWARD_UNIFORM_BUFFER_NAME: &str = "forward"; -pub struct CameraResourceManager; +pub struct GlobalResourceManager; -impl RenderResourceManager for CameraResourceManager { +impl RenderResourceManager for GlobalResourceManager { fn initialize(&self, render_graph: &mut RenderGraphData, world: &mut World) { let light_count = >::query().iter_immutable(world).count(); let forward_uniforms = ForwardUniforms { @@ -32,7 +32,7 @@ impl RenderResourceManager for CameraResourceManager { } fn resize<'a>(&self, render_graph: &mut RenderGraphData, encoder: &'a mut wgpu::CommandEncoder, world: &mut World) { - for (mut camera, local_to_world) in <(Write, Read)>::query().iter(world) { + for (mut camera, local_to_world, _) in <(Write, Read, Read)>::query().iter(world) { camera.update(render_graph.swap_chain_descriptor.width, render_graph.swap_chain_descriptor.height); let camera_matrix: [[f32; 4]; 4] = (camera.view_matrix * local_to_world.0).to_cols_array_2d(); let matrix_size = mem::size_of::<[[f32; 4]; 4]>() as u64; diff --git a/src/render/render_resources/mod.rs b/src/render/render_resources/mod.rs index 13c859e6f8..520301b42f 100644 --- a/src/render/render_resources/mod.rs +++ b/src/render/render_resources/mod.rs @@ -1,7 +1,9 @@ mod light_resource_manager; -mod camera_resource_manager; +mod global_resource_manager; +mod global_2d_resource_manager; mod material_resource_manager; pub use light_resource_manager::*; -pub use camera_resource_manager::*; +pub use global_resource_manager::*; +pub use global_2d_resource_manager::*; pub use material_resource_manager::*; \ No newline at end of file