diff --git a/examples/testbed/assets/texture-sep/uvgrid.png b/examples/testbed/assets/texture-sep/uvgrid.png index 06aad7c..a55586c 100644 Binary files a/examples/testbed/assets/texture-sep/uvgrid.png and b/examples/testbed/assets/texture-sep/uvgrid.png differ 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 0000000..06aad7c Binary files /dev/null and b/examples/testbed/assets/texture-sep/uvgrid.png.bk differ 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);