From 6826d43c8b0f2d851b75fcbcd38aba2e45907e2b Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Wed, 15 Nov 2023 19:47:22 -0500 Subject: [PATCH] Switch to desktop --- .../testbed/assets/texture-sep/uvgrid.png | Bin 40222 -> 20559 bytes .../testbed/assets/texture-sep/uvgrid.png.bk | Bin 0 -> 40222 bytes examples/testbed/src/main.rs | 7 +- src/render/light/mod.rs | 22 +- src/render/light/point.rs | 3 + src/render/material.rs | 30 +++ src/render/render_buffer.rs | 213 ++++++++++++++++++ src/render/renderer.rs | 34 ++- src/render/shaders/base.wgsl | 27 ++- 9 files changed, 313 insertions(+), 23 deletions(-) create mode 100644 examples/testbed/assets/texture-sep/uvgrid.png.bk diff --git a/examples/testbed/assets/texture-sep/uvgrid.png b/examples/testbed/assets/texture-sep/uvgrid.png index 06aad7cf181d5a3a5517f554fc5f03282ced7f31..a55586ca60a7a32b5825185030e062ce4deae7fa 100644 GIT binary patch literal 20559 zcmeI4u}d656vp4~4d-f3Tor;yB!e*^DB+5rkj6PZlRFEM5D+DiEd(0{ArP)gRs~5& zlZ3PZ8(Ry-CPlI-Q>03hCL$>k0x2wmSY+nBi#Pv)#jm*A;W4|nJKWBE@B6+tFXm?N zb$0Z3h)8F(Qob*uY*R^;w})o~BU|>+zFt{)EOP6H`vkS!i6794$Ud*IfZ&7N>?-|EMQ8QOxH%@F^ zp-;~IKpaJV65f1SY5u}_$AUcd1Cde%*?2QO^#4}sjl4|w0TqV%)O;T7xwvQ7A(vkF z17Q&4%+7Lmd&|R_G!3#YFCZH>rswvb`%j$7aa-%KnADg|;^Bp|KM0AOJ~uhCbg*=2yUMci0$f6HAFw&H83wSKtb?SDoYw+G zC<7Q5C<6$qI?o5+2VDwfU|^dqn-67x)?q~_C<7=1C{qysrTDWFF;eT z20{a&fzUu`=W_u<1EGP?5oR@KZ$ovn^C4b8tdclQQQa`=x_l9%E=FC9x)^oqwH+9B zG3r7Y1XLKg3NBPPR5vICR5w&NhmCyI4W+G7yGv~^^=%rZ?Q$q>W8Z&06zTfy_62Cx z)>EX9?!g`h?)}6%1c-4+j%p*SjnF-)Hlo_d?ae^~M*>FzSLbuEN@A6~yt>En>TPv* cDrxnvC&M3ktghyy4nGuc|t5hsizSOMdV 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 diff --git a/examples/testbed/assets/texture-sep/uvgrid.png.bk b/examples/testbed/assets/texture-sep/uvgrid.png.bk new file mode 100644 index 0000000000000000000000000000000000000000..06aad7cf181d5a3a5517f554fc5f03282ced7f31 GIT binary patch literal 40222 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 literal 0 HcmV?d00001 diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index b7dc7cf..004a5ab 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -69,7 +69,8 @@ async fn main() { let mut resman = world.get_resource_mut::().unwrap(); //let diffuse_texture = resman.request::("assets/happy-tree.png").unwrap(); let antique_camera_model = resman.request::("assets/AntiqueCamera.glb").unwrap(); - let cube_model = resman.request::("assets/cube-texture-bin.glb").unwrap(); + //let cube_model = resman.request::("assets/cube-texture-bin.glb").unwrap(); + let cube_model = resman.request::("assets/texture-sep/texture-sep.gltf").unwrap(); drop(resman); /* world.spawn(( @@ -113,6 +114,10 @@ async fn main() { constant: 1.0, linear: 0.045, quadratic: 0.0075, + + ambient: 0.2, + diffuse: 0.5, + specular: 1.0, }, TransformComponent::from(light_tran), ModelComponent(cube_model), diff --git a/src/render/light/mod.rs b/src/render/light/mod.rs index 67de61c..f3f9650 100644 --- a/src/render/light/mod.rs +++ b/src/render/light/mod.rs @@ -1,5 +1,5 @@ pub mod point; -use std::{collections::{VecDeque, HashMap}, num::NonZeroU64, marker::PhantomData}; +use std::{collections::{VecDeque, HashMap}, num::{NonZeroU64, NonZeroU32}, marker::PhantomData}; use edict::query::EpochOf; pub use point::*; @@ -10,7 +10,7 @@ use std::mem; use crate::{math::Transform, ecs::components::TransformComponent}; -const MAX_LIGHT_COUNT: usize = 32; +const MAX_LIGHT_COUNT: usize = 16; pub struct LightBuffer { _phantom: PhantomData, @@ -106,7 +106,7 @@ impl LightUniformBuffers { pub fn new(device: &wgpu::Device) -> Self { let buffer = device.create_buffer( &wgpu::BufferDescriptor { - label: Some("Lights Uniform buffer"), + label: Some("UB_Lights"), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, size: (mem::size_of::() * MAX_LIGHT_COUNT) as u64, mapped_at_creation: false, @@ -126,7 +126,7 @@ impl LightUniformBuffers { count: None, } ], - label: Some("lights_bind_group_layout"), + label: Some("BGL_Lights"), }); let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -143,7 +143,7 @@ impl LightUniformBuffers { ) } ], - label: Some("light_bind_group"), + label: Some("BG_Lights"), }); let point_lights = LightBuffer::new(&bindgroup_layout, MAX_LIGHT_COUNT); @@ -199,6 +199,12 @@ pub struct PointLightUniform { pub linear: f32, /// The quadratic factor used in the quadratic attenuation calculation. pub quadratic: f32, + + pub ambient: f32, + pub diffuse: f32, + pub specular: f32, + + pub _padding: u32, } impl PointLightUniform { @@ -213,6 +219,12 @@ impl PointLightUniform { constant: light.constant, linear: light.linear, quadratic: light.quadratic, + + ambient: light.ambient, + diffuse: light.diffuse, + specular: light.specular, + + _padding: 0, } } } \ No newline at end of file diff --git a/src/render/light/point.rs b/src/render/light/point.rs index a2b1f2c..dc59011 100644 --- a/src/render/light/point.rs +++ b/src/render/light/point.rs @@ -6,4 +6,7 @@ pub struct PointLight { pub constant: f32, pub linear: f32, pub quadratic: f32, + pub ambient: f32, + pub diffuse: f32, + pub specular: f32, } \ No newline at end of file diff --git a/src/render/material.rs b/src/render/material.rs index ceccc57..71306d8 100755 --- a/src/render/material.rs +++ b/src/render/material.rs @@ -24,4 +24,34 @@ impl Material { shininess: 32.0, } } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)] +pub struct MaterialUniform { + pub ambient: glam::Vec4, + //pub _padding: u32, + pub diffuse: glam::Vec4, + //pub _padding2: u32, + pub specular: glam::Vec4, + //pub _padding3: u32, + pub shininess: f32, + pub _padding1: [u32; 3], +} + +impl From<&Material> for MaterialUniform { + fn from(value: &Material) -> Self { + Self { + ambient: glam::Vec4::new(value.ambient.x, value.ambient.y, value.ambient.z, 0.0), + diffuse: glam::Vec4::new(value.diffuse.x, value.diffuse.y, value.diffuse.z, 0.0), + specular: glam::Vec4::new(value.specular.x, value.specular.y, value.specular.z, 0.0), + shininess: value.shininess, + _padding1: [0; 3] + + /* _padding: 0, + _padding2: 0, + _padding3: 0, + _padding4: [0; 3], */ + } + } } \ No newline at end of file diff --git a/src/render/render_buffer.rs b/src/render/render_buffer.rs index 016568a..e0b0e08 100755 --- a/src/render/render_buffer.rs +++ b/src/render/render_buffer.rs @@ -1,3 +1,7 @@ +use std::{sync::Arc, num::NonZeroU32}; + +use wgpu::util::DeviceExt; + use super::{desc_buf_lay::DescVertexBufferLayout, vertex::Vertex}; #[allow(dead_code)] @@ -17,6 +21,215 @@ impl RenderBuffer { } } +pub struct BindGroupPair { + pub bindgroup: wgpu::BindGroup, + pub layout: Arc, +} + +impl BindGroupPair { + pub fn new_from_layout(device: &wgpu::Device, layout: Arc, entries: &[wgpu::BindGroupEntry<'_>]) -> Self { + let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: layout.as_ref(), + entries, + label: None, + }); + + Self { + bindgroup, + layout, + } + } +} + +pub struct BufferWrapper { + pub bindgroup_pair: Option, + pub inner_buf: wgpu::Buffer, + pub len: usize, +} + +impl BufferWrapper { + pub fn new_init(device: &wgpu::Device, descriptor: &wgpu::util::BufferInitDescriptor<'_>, bind_group: Option) -> Self { + let buffer = device.create_buffer_init(descriptor); + + Self { + bindgroup_pair: bind_group, + inner_buf: buffer, + len: 0, + } + } + + pub fn new(device: &wgpu::Device, descriptor: &wgpu::BufferDescriptor<'_>, bind_group: Option) -> Self { + let buffer = device.create_buffer(descriptor); + + Self { + bindgroup_pair: bind_group, + inner_buf: buffer, + len: 0, + } + } + + pub fn builder() -> BufferWrapperBuilder { + BufferWrapperBuilder::new() + } +} + +#[derive(Default)] +pub struct BufferWrapperBuilder { + buffer_usage: Option, + buffer_dynamic_offset: bool, // default false + label_prefix: Option, + contents: Option>, + count: Option, + size: Option, + bind_group_pair: Option, + bind_group_visibility: Option, +} + +impl BufferWrapperBuilder { + pub fn new() -> Self { + Self::default() + } + + pub fn buffer_usage(mut self, usages: wgpu::BufferUsages) -> Self { + self.buffer_usage = Some(usages); + self + } + + pub fn buffer_dynamic_offset(mut self, val: bool) -> Self { + self.buffer_dynamic_offset = val; + self + } + + pub fn visibility(mut self, val: wgpu::ShaderStages) -> Self { + self.bind_group_visibility = Some(val); + self + } + + pub fn bind_group_pair(mut self, pair: BindGroupPair) -> Self { + self.bind_group_pair = Some(pair); + self + } + + pub fn label_prefix(mut self, val: &str) -> Self { + self.label_prefix = Some(val.into()); + self + } + + pub fn contents(mut self, val: &[T]) -> Self + where + T: bytemuck::NoUninit + { + self.contents = Some(bytemuck::cast_slice(val).to_vec()); + self.count = if val.len() > 1 { + Some(val.len() as u32) + } else { None }; + + self + } + + pub fn size(mut self, val: usize) -> Self { + self.size = Some(val as u64); + self + } + + fn format_label(&self, prefix: &str) -> Option { + match self.label_prefix.as_ref() { + Some(v) => Some(format!("{}{}", prefix, v)), + None => None + } + } + + /// Finish building the BufferWrapper + /// + /// You must specify a value for `buffer_usage` + /// + /// If you did not provide a BindGroupPair with `BufferWrapperBuilder::bind_group_pair`, + /// one will be created for the buffer. A value for `visibility` is required. + /// You would need to provide `size` or `contents`: + /// * `size` - The size (in bytes) of the buffer. + /// * `contents` - The contents to initialize the buffer with. + /// + /// If a field is missing, a panic will occur. + pub fn finish(self, device: &wgpu::Device) -> BufferWrapper { + let buf_usage = self.buffer_usage.expect("Buffer usage was not set"); + let buffer = if let Some(contents) = self.contents.as_ref() { + device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: self.format_label("B_").as_deref(), + contents: bytemuck::cast_slice(contents.as_slice()), + usage: buf_usage, + } + ) + } else { + device.create_buffer( + &wgpu::BufferDescriptor { + label: self.format_label("B_").as_deref(), + usage: buf_usage, + size: self.size.unwrap(), + mapped_at_creation: false, + } + ) + }; + + let bg_pair = match self.bind_group_pair { + Some(v) => v, + None => { + let buffer_ty = { + let val; + if buf_usage.contains(wgpu::BufferUsages::UNIFORM) { + val = wgpu::BufferBindingType::Uniform; + } else if buf_usage.contains(wgpu::BufferUsages::STORAGE) { + val = wgpu::BufferBindingType::Storage { read_only: false }; // TODO: take in to see if its readonly + } else { + panic!("Unknown buffer binding type!"); + } + + val + }; + let bg_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: self.bind_group_visibility.expect("Expected bind_group_visibility to be set!"), + ty: wgpu::BindingType::Buffer { + ty: buffer_ty, + has_dynamic_offset: self.buffer_dynamic_offset, + min_binding_size: None, + }, + count: self.count.and_then(|v| NonZeroU32::try_from(v).ok()), + } + ], + label: self.format_label("BGL_").as_deref(), + }); + let bg_layout = Arc::new(bg_layout); + + + let bg = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bg_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: buffer.as_entire_binding(), + } + ], + label: self.format_label("BG_").as_deref(), + }); + + BindGroupPair { + bindgroup: bg, + layout: bg_layout, + } + } + }; + + BufferWrapper { + bindgroup_pair: Some(bg_pair), + inner_buf: buffer, + len: self.count.unwrap_or_default() as usize, + } + } +} + #[derive(Debug)] pub struct BufferStorage { buffer: wgpu::Buffer, diff --git a/src/render/renderer.rs b/src/render/renderer.rs index 7078581..1a1afa3 100755 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -19,11 +19,14 @@ use crate::ecs::components::model::ModelComponent; use crate::ecs::components::transform::TransformComponent; use crate::math::{Transform, self}; use crate::render::light::PointLightUniform; +use crate::render::material::MaterialUniform; +use crate::render::render_buffer::{BufferWrapperBuilder, BindGroupPair}; use super::camera::{RenderCamera, CameraUniform}; use super::desc_buf_lay::DescVertexBufferLayout; use super::light::{PointLight, LightUniformBuffers}; use super::material::Material; +use super::render_buffer::BufferWrapper; use super::texture::RenderTexture; use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers}; use super::vertex::Vertex; @@ -91,8 +94,7 @@ pub struct BasicRenderer { default_texture_bind_group: BindGroup, depth_buffer_texture: RenderTexture, - material_buffer: wgpu::Buffer, - material_bind_group: wgpu::BindGroup, + material_buffer: BufferWrapper, light_buffers: LightUniformBuffers, } @@ -127,7 +129,9 @@ impl BasicRenderer { limits: if cfg!(target_arch = "wasm32") { wgpu::Limits::downlevel_webgl2_defaults() } else { - wgpu::Limits::default() + let mut v = wgpu::Limits::default(); + v.max_bind_groups = 5; + v }, label: None, }, @@ -251,6 +255,13 @@ impl BasicRenderer { let light_uniform_buffers = LightUniformBuffers::new(&device); + let mat_buffer = BufferWrapperBuilder::new() + .buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST) + .visibility(wgpu::ShaderStages::FRAGMENT) + .contents(&[MaterialUniform::default()]) + //.size(mem::size_of::()) + .finish(&device); + let mut s = Self { window, surface, @@ -282,6 +293,7 @@ impl BasicRenderer { entity_last_transforms: HashMap::new(), light_buffers: light_uniform_buffers, + material_buffer: mat_buffer, }; // create the default pipelines @@ -289,7 +301,7 @@ impl BasicRenderer { pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader, vec![super::vertex::Vertex::desc(),], vec![&s.texture_bind_group_layout, &s.transform_buffers.bindgroup_layout, &camera_bind_group_layout, - &s.light_buffers.bindgroup_layout]))); + &s.light_buffers.bindgroup_layout, &s.material_buffer.bindgroup_pair.as_ref().unwrap().layout]))); s.render_pipelines = pipelines; s @@ -384,8 +396,8 @@ impl BasicRenderer { fn create_mesh_buffers(&mut self, mesh: &Mesh, transform_indices: TransformBufferIndices) -> MeshBufferStorage { let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(mesh); + let material = Material::from_resource(&self.device, &self.queue, &mesh.material()); let diffuse_bindgroup = if let Some(model_texture) = &mesh.material().base_color_texture { - let material = Material::from_resource(&self.device, &self.queue, &mesh.material()); let diffuse_texture = &material.diffuse_texture; let diffuse_bind_group = self.device.create_bind_group( @@ -410,10 +422,15 @@ impl BasicRenderer { None }; + //let mat = Material::from_resource(&self.device, &self.queue, ) + let uni = MaterialUniform::from(&material); + self.queue.write_buffer(&self.material_buffer.inner_buf, 0, bytemuck::bytes_of(&uni)); + debug!("Wrote material to buffer"); + MeshBufferStorage { buffer_vertex: vertex_buffer, buffer_indices, - material: None, + material: Some(material), texture_bindgroup: diffuse_bindgroup, } } @@ -427,6 +444,9 @@ impl BasicRenderer { let indices = self.transform_buffers.update_or_insert(&self.queue, &self.render_limits, entity, || ( transform.calculate_mat4(), glam::Mat3::from_quat(transform.rotation) )); + //let mat = Material::from_resource(&self.device, &self.queue, ) + //self.queue.write_buffer(&self.material_buffer.inner_buf, 0, bytemuck::bytes_of(t)) + #[allow(clippy::map_entry)] if !self.mesh_buffers.contains_key(&mesh.uuid) { // create the mesh's buffers @@ -587,6 +607,8 @@ impl Renderer for BasicRenderer { // bind light //render_pass.set_bind_group(3, &self.point_light_bind_group, &[]); render_pass.set_bind_group(3, &self.light_buffers.bindgroup, &[]); + + render_pass.set_bind_group(4, &self.material_buffer.bindgroup_pair.as_ref().unwrap().bindgroup, &[]); ////self.light_buffers.bind_lights(&mut render_pass, 3); diff --git a/src/render/shaders/base.wgsl b/src/render/shaders/base.wgsl index f372fe7..d02a678 100755 --- a/src/render/shaders/base.wgsl +++ b/src/render/shaders/base.wgsl @@ -1,6 +1,6 @@ // Vertex shader -const max_light_count: u32 = 32u; +const max_light_count: u32 = 16u; struct VertexInput { @location(0) position: vec3, @@ -23,10 +23,15 @@ struct CameraUniform { struct PointLight { position: vec4, color: vec4, + intensity: f32, constant: f32, linear: f32, quadratic: f32, + + ambient: f32, + diffuse: f32, + specular: f32, }; struct Lights { @@ -65,9 +70,9 @@ fn vs_main( // Fragment shader struct Material { - ambient: vec3, - diffuse: vec3, - specular: vec3, + ambient: vec4, + diffuse: vec4, + specular: vec4, shininess: f32, } @@ -77,7 +82,7 @@ var t_diffuse: texture_2d; var s_diffuse: sampler; @group(4) @binding(0) -var u_material: Material; +var u_material: Material; @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { @@ -99,13 +104,13 @@ fn blinn_phong_point_light(world_pos: vec3, world_norm: vec3, point_li // We don't need (or want) much ambient light, so 0.1 is fine //let ambient_strength = 0.1; - var ambient_color = light_color * material.ambient; + var ambient_color = light_color * material.ambient.xyz; //// diffuse //// let light_dir = normalize(light_pos - world_pos); let diffuse_strength = max(dot(world_norm, light_dir), 0.0); - var diffuse_color = light_color * (diffuse_strength * material.diffuse); + var diffuse_color = light_color * (diffuse_strength * material.diffuse.xyz); //// end of diffuse //// //// specular //// @@ -113,7 +118,7 @@ fn blinn_phong_point_light(world_pos: vec3, world_norm: vec3, point_li let half_dir = normalize(view_dir + light_dir); let specular_strength = pow(max(dot(world_norm, half_dir), 0.0), material.shininess); - var specular_color = specular_strength * (light_color * material.specular); + var specular_color = specular_strength * (light_color * material.specular.xyz); //// end of specular //// //// point light attenuation //// @@ -121,9 +126,9 @@ fn blinn_phong_point_light(world_pos: vec3, world_norm: vec3, point_li let attenuation = 1.0 / (point_light.constant + point_light.linear * distance + point_light.quadratic * (distance * distance)); - ambient_color *= attenuation * point_light.intensity; - diffuse_color *= attenuation * point_light.intensity; - specular_color *= attenuation * point_light.intensity; + ambient_color *= attenuation * point_light.intensity * point_light.ambient; + diffuse_color *= attenuation * point_light.intensity * point_light.diffuse; + specular_color *= attenuation * point_light.intensity * point_light.specular; //// end of point light attenuation //// return (ambient_color + diffuse_color + specular_color);