diff --git a/src/app.rs b/src/app.rs index 7c68fad..3c9653d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,7 +1,7 @@ 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::{ModelData, Renderer}; -use crate::world::{World, ObjectData, OctreeDebug}; +use crate::render::{loader, Renderer}; +use crate::world::{World, ObjectData}; use glam::{Affine3, Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4}; use std::sync::Arc; #[cfg(target_arch = "wasm32")] @@ -17,7 +17,11 @@ use winit::{ window::Window, }; use crate::list::Id; +use crate::render::instance::{Material, ModelData}; +use crate::render::instance::material::MaterialProperties; +use crate::render::texture::Texture; use crate::state; +use crate::world::debug::OctreeDebug; struct Controller { buttons: HashMap, @@ -55,36 +59,31 @@ impl AppState { let mut world = state.worlds.make(World::new()); state.renderer = Some(Renderer::new(&window).await?); let renderer = state.renderer.as_mut().unwrap(); - let debug_file = renderer.load_from_gltf(include_bytes!("assets/debug.glb")); + let debug_file = loader::load_from_gltf(renderer,include_bytes!("assets/debug.glb")); let debug_model = debug_file.first_object().unwrap().model.unwrap(); { - let file = renderer.load_from_gltf(include_bytes!("assets/sphere.glb")); + let file = loader::load_from_gltf(renderer,include_bytes!("assets/sphere.glb")); let mut block = file.first_object().unwrap(); - let skybox = renderer.load_texture_from_bytes( - include_bytes!("assets/skybox2.png"), - Some(image::ImageFormat::Png), - ); - renderer.set_skybox(skybox); - let color = renderer.load_texture_from_bytes(include_bytes!("assets/plank/color.png"), None); - let normal = renderer.load_texture_from_bytes(include_bytes!("assets/plank/normal.png"), None); - let roughness = renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png"), None); - let mat = renderer.new_material(&color, &normal, &roughness); + let skybox = Texture::load_from_file_bytes(renderer,include_bytes!("assets/skybox2.png"), None); + renderer.set_skybox(skybox.unwrap()); + let color = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/color.png"), None); + let normal = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/normal.png"), None); + let roughness = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/roughness.png"), None); + let mat = Material::new(renderer, &color.unwrap(), &normal.unwrap(), &roughness.unwrap(), MaterialProperties::default()); block.model.as_mut().unwrap().material = mat; for x in -BLOCKS..=BLOCKS { for y in -BLOCKS..=BLOCKS { let id = world.add_object(state.objects.make(block.clone())); let block = state.objects.get(&id); - { - block.affine = Affine3::from_translation( - Vec3::new(-x as f32 * 3.0, -2.0, -y as f32 * 3.0), - ); - block.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, - ); - } + block.set_affine(Affine3::from_translation( + Vec3::new(-x as f32 * 3.0, -2.0, -y as f32 * 3.0) + )); + block.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, + ); } } } diff --git a/src/lib.rs b/src/lib.rs index 45cd487..edba5d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,23 @@ #![recursion_limit = "256"] + +use std::error::Error; +use std::fmt::{Display, Formatter}; + pub mod app; pub mod render; pub mod web; pub mod world; pub mod list; -pub mod state; \ No newline at end of file +pub mod state; +pub mod net; + +#[derive(Debug)] +struct PoolError(String); + +impl Display for PoolError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Error for PoolError {} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9ae830d..7b3961b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ #![recursion_limit = "256"] -use game::*; +use pool::*; fn main() { app::run().unwrap(); } diff --git a/src/net/mod.rs b/src/net/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/render/eye.rs b/src/render/eye.rs index 83ca7e2..17fce37 100644 --- a/src/render/eye.rs +++ b/src/render/eye.rs @@ -1,9 +1,10 @@ -use crate::render::{MaterialProperties, Texture}; +use crate::render::{Texture}; use bytemuck::{Pod, Zeroable}; use glam::camera::lh::proj::directx::perspective; use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4}; use wgpu::util::DeviceExt; use wgpu::{Device, Queue}; +use crate::render::instance::material::MaterialProperties; pub(crate) struct Eye { pub(crate) frame: Affine3A, @@ -16,6 +17,7 @@ pub(crate) struct Eye { pub(crate) camera_buffer: wgpu::Buffer, pub(crate) layout: wgpu::BindGroupLayout, pub(crate) group: wgpu::BindGroup, + pub sky_pipeline: wgpu::RenderPipeline, } #[repr(C)] @@ -49,7 +51,7 @@ impl Eye { bytemuck::cast_slice(&[self.environment]), ); } - pub(crate) fn new(device: &Device, width: u32, height: u32, skybox: Texture) -> Eye { + pub(crate) fn new(device: &Device, config: &wgpu::SurfaceConfiguration, width: u32, height: u32, skybox: Texture) -> Eye { let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Camera Buffer"), contents: bytemuck::cast_slice(&[ @@ -116,6 +118,51 @@ impl Eye { label: Some("eye_bind_group_layout"), }); + let shader = device.create_shader_module(wgpu::include_wgsl!("sky.wgsl")); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[Some(&layout)], + immediate_size: 0, + }); + + //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 group = Eye::bind_group(&layout, device, &camera_buffer, &environment_buffer, skybox); Eye { @@ -129,6 +176,7 @@ impl Eye { layout, environment, environment_buffer, + sky_pipeline, } } fn bind_group( diff --git a/src/assets/SimpleShader.wgsl b/src/render/instance/instance.wgsl similarity index 86% rename from src/assets/SimpleShader.wgsl rename to src/render/instance/instance.wgsl index 87ac451..e882b6e 100644 --- a/src/assets/SimpleShader.wgsl +++ b/src/render/instance/instance.wgsl @@ -15,7 +15,6 @@ struct Eye { frame: mat4x4, } -// Vertex shader @group(0) @binding(0) var eye: Eye; @group(0) @binding(1) @@ -143,32 +142,4 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { let result = (diffuse_effect.xyz + specular_color.xyz); return vec4(result.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); -} +} \ No newline at end of file diff --git a/src/render/instance/material.rs b/src/render/instance/material.rs new file mode 100644 index 0000000..cf994d2 --- /dev/null +++ b/src/render/instance/material.rs @@ -0,0 +1,130 @@ +use wgpu::Device; +use crate::list::{FreeList, Id}; +use crate::render::Renderer; +use crate::render::texture::Texture; + +pub struct Materials { + pub(crate) layout: wgpu::BindGroupLayout, + pub(crate) list: FreeList, + default: Material, +} + +impl Materials { + pub(crate) fn new(device: &Device, default: Texture) -> Materials { + fn texture_bind_group_layout_entry(binding: u32) -> wgpu::BindGroupLayoutEntry { + wgpu::BindGroupLayoutEntry { + binding, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + } + } + + let 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, + }, + texture_bind_group_layout_entry(1), + texture_bind_group_layout_entry(2), + texture_bind_group_layout_entry(3), + ], + label: Some("texture_bind_group_layout"), + }); + + let default = Material::_new( + &device, + &layout, + &default, + &default, + &default, + MaterialProperties::default(), + ); + + Materials { + layout, + list: Default::default(), + default, + } + } +} + +#[derive(Clone)] +pub struct Material { + pub(crate) group: wgpu::BindGroup, +} + +pub struct MaterialProperties { + pub(crate) edge: wgpu::AddressMode, + pub(crate) filter: wgpu::FilterMode, +} + +impl Default for MaterialProperties { + fn default() -> MaterialProperties { + MaterialProperties { + edge: wgpu::AddressMode::Repeat, + filter: wgpu::FilterMode::Linear, + } + } +} + +impl MaterialProperties { + pub(crate) 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 Material { + pub(crate) fn _new( + device: &wgpu::Device, + layout: &wgpu::BindGroupLayout, + base: &Texture, + normal: &Texture, + reflect: &Texture, + config: MaterialProperties, + ) -> Material { + let sampler = config.sampler(&device); + let group = device.create_bind_group(&wgpu::BindGroupDescriptor { + 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"), + }); + Material { group } + } + pub fn new(renderer: &mut Renderer, base: &Texture, normal: &Texture, reflect: &Texture, config: MaterialProperties) -> Id { + renderer.instances.materials.list.make(Material::_new(&renderer.device, &renderer.instances.materials.layout, base, normal, reflect, config)).id + } +} \ No newline at end of file diff --git a/src/render/instance/mod.rs b/src/render/instance/mod.rs new file mode 100644 index 0000000..b7384dd --- /dev/null +++ b/src/render/instance/mod.rs @@ -0,0 +1,376 @@ +pub mod material; + +use std::ops::Range; +use bytemuck::{Pod, Zeroable}; +use glam::{Affine3, Mat4, Vec4}; +use wgpu::{BindGroup, Device}; +use wgpu::naga::FastHashMap; +use crate::list::{FreeList, Id, RefId}; +use crate::render::{instance, Renderer}; +use crate::render::eye::Eye; +pub(crate) use crate::render::instance::material::{Material, Materials}; +use crate::render::mesh::{Mesh, TangentVertex}; +use crate::render::texture::Texture; +use crate::world::ObjectData; + +#[repr(C)] +#[derive(Pod, Zeroable, Copy, Clone)] +pub struct ModelInstance { + 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)] +pub struct LightInstance { + pub location: Vec4, + pub rotation: Vec4, + pub color: Vec4, +} + +#[derive(Clone)] +pub struct LightData { + pub instance: LightInstance, + pub transform: Affine3, +} + +#[derive(Clone)] +pub struct ModelData { + pub instance: ModelInstance, + pub material: Id, + pub mesh: Id, +} + +pub struct Instances { + pub materials: Materials, + light_instances: FreeList, + model_instances: FreeList, + pub(crate) pipeline: wgpu::RenderPipeline, + light_count: usize, + light_buffer: wgpu::Buffer, + model_count: usize, + pub(crate) model_buffer: wgpu::Buffer, + models_flag: bool, + pub(crate) program: Program, + models: FastHashMap, FastHashMap, FastHashMap,bool>>>, +} + +pub enum Code { + Material(Material), + Mesh(Mesh), + Draw(Range), +} + +pub struct Program(pub Vec); + +impl Program { + fn push(&mut self, item: Code) { + self.0.push(item) + } + fn new() -> Program { + Program(Vec::new()) + } + pub(crate) fn render(self, pass: &mut wgpu::RenderPass) { + let mut count = 0; + let mut indexed = false; + for code in self.0 { + match code { + Code::Material(material) => pass.set_bind_group( + Renderer::SIMPLE_RENDER_TEXTURE_GROUP_POSITION, + &material.group, + &[], + ), + Code::Mesh(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; + } + } + Code::Draw(instances) => { + if indexed { + pass.draw_indexed(0..count, 0, instances) + } else { + pass.draw(0..count, instances); + } + } + } + } + } +} + +impl Instances { + const MIN_SIZE: u64 = 64; + pub fn register_object(&mut self, mut object: RefId) { + let model = object.model.as_mut().unwrap(); + self.model_count += 1; + self.models + .entry(model.mesh.clone()).or_default() + .entry(model.material.clone()).or_default() + .insert(object.into(),self.models_flag); + } + /*pub fn register_light(&mut self, light: Id) { + self.light_count += 1; + self.lights.insert(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 > Instances::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(); + */ + Instances::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])); + } + }*/ + pub(crate) fn write_instances( + &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + mesh_list: &mut FreeList, + objects_list: &mut FreeList, + ) { + Instances::reallocate_buffer( + device, + &mut self.model_buffer, + self.model_count, + size_of::(), + ); + let mut buffer = queue + .write_buffer_with( + &self.model_buffer, + 0 as wgpu::BufferAddress, + wgpu::BufferSize::new(self.model_buffer.size()).unwrap(), + ) + .unwrap(); + let mut index: u32 = 0; + let stride = size_of::(); + for (mesh, materials) in self.models.iter_mut() { + self.program.push(Code::Mesh(mesh_list.get(mesh).clone())); + for (material, objects) in materials.iter_mut() { + self.program.push(Code::Material(self.materials.list.get(material).clone())); + let before = index; + objects.retain(|object,flag| { + let object = objects_list.get(object); + if *flag == self.models_flag { + let begin = index as usize * stride; + buffer.slice(begin..begin + stride).copy_from_slice(bytemuck::cast_slice(&[object.model.as_mut().unwrap().instance])); + index += 1; + true + } else { + false + } + }); + self.program.push(Code::Draw(before..index)); + } + } + //println!("objects: {}",index); + self.models_flag = !self.models_flag; + } + + pub(crate) 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, eye: &Eye, config: &wgpu::SurfaceConfiguration, default: Texture) -> Instances { + let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Instance Buffer"), + size: (size_of::() * Instances::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::() * Instances::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, + });*/ + + let materials = Materials::new(device, default); + + let shader = device.create_shader_module(wgpu::include_wgsl!("instance.wgsl")); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[Some(&eye.layout), Some(&materials.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: &[TangentVertex::desc(), Instances::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, + }); + + Instances { + materials, + light_instances: Default::default(), + model_instances: Default::default(), + pipeline: instance_pipeline, + light_count: 0, + //light_ref_last: 0, + model_count: 0, + light_buffer, + model_buffer: instance_buffer, + //light_ref_buffer, + //lights: Default::default(), + models_flag: false, + program: Program(Vec::new()), + models: Default::default(), + } + } + + pub fn render() { + + } +} \ No newline at end of file diff --git a/src/render/loader.rs b/src/render/loader.rs new file mode 100644 index 0000000..0f0ee8b --- /dev/null +++ b/src/render/loader.rs @@ -0,0 +1,302 @@ +use std::collections::HashMap; +use glam::{Mat4, Vec3, Vec4}; +use gltf::Semantic; +use wgpu::{Device, Queue}; +use wgpu::util::DeviceExt; +use crate::list::{FreeList, Id, RefId}; +use crate::render::instance::{Material, Materials, ModelData, ModelInstance}; +use crate::render::instance::material::MaterialProperties; +use crate::render::mesh::{Mesh, TangentVertex}; +use crate::render::texture::{Texture, TextureProperties}; +use crate::render::{Renderer, Textures}; +use crate::world::{ObjectData, AABB, ColliderData, Shape}; + +pub struct TreeNode { + object: Option, + children: Vec, + name: String, +} + +impl TreeNode { + 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 + } + } +} + +type GltfVertexBufferKey = (Option, Option, Option, Option); + +pub fn new_texture_from_gltf( + device: &Device, + queue: &Queue, + info: &gltf::texture::Texture, + images: &Vec, +) -> Option { + 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 None, + } + Some(Texture::load( + device, + queue, + pixels.as_slice(), + TextureProperties { + width: image.width, + height: image.height, + }, + )) + } else { + None + } +} +pub fn new_mesh_from_gltf( + device: &Device, + primitive: gltf::Primitive, + meshes: &mut HashMap, + buffers: &[gltf::buffer::Data], +) -> Mesh { + let position_index = primitive + .get(&Semantic::Positions) + .and_then(|it| it.view()).map(|it| it.buffer().index()); + let normals_index = primitive + .get(&Semantic::Normals) + .and_then(|it| it.view()).map(|it| it.buffer().index()); + let tex_coords_index = primitive + .get(&Semantic::TexCoords(0)) + .and_then(|it| it.view()).map(|it| it.buffer().index()); + let indices_index = primitive + .indices() + .and_then(|it| it.view()).map(|it| it.buffer().index()); + let tangent_index = primitive + .get(&Semantic::Tangents) + .and_then(|it| it.view()).map(|it| it.buffer().index()); + let key = ( + position_index, + normals_index, + tex_coords_index, + indices_index, + ); + let insert = || { + let mut tangents = false; + let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); + let mut vertex_data: Vec; + let mut aabb = AABB(Vec3::ZERO,Vec3::ZERO); + 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 { + aabb = aabb.extend_to(Vec3::from(position)); + vertex_data.push(TangentVertex { + 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 = + 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(( + 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 + }; + Mesh { + indices: indices_result, + vertices: vertices_result, + aabb, + } + }; + meshes.entry(key).or_insert_with(insert).clone() +} + +pub fn load_node_from_gltf( + node: gltf::Node, + device: &Device, + queue: &Queue, + mesh_map: &mut HashMap, + texture_map: &mut HashMap, + materials: &mut Materials, + textures: &Textures, + meshes: &mut FreeList, + images: &Vec, + buffers: &Vec, +) -> TreeNode { + let mut tree_node = TreeNode { + 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) => texture_map + .entry(info.texture().source().index()) + .or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone())) + .clone(), + None => textures.default.clone(), + }; + let reflect = match pbr.metallic_roughness_texture() { + Some(info) => texture_map + .entry(info.texture().source().index()) + .or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone())) + .clone(), + None => textures.default.clone(), + }; + let normal = match material.normal_texture() { + Some(info) => texture_map + .entry(info.texture().source().index()) + .or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone())) + .clone(), + None => textures.default.clone(), + }; + let material = materials.list.make(Material::_new( + &device, + &materials.layout, + &base, + &normal, + &reflect, + MaterialProperties { + edge: wgpu::AddressMode::Repeat, + filter: wgpu::FilterMode::Linear, + }, + )).into();; + let mesh = new_mesh_from_gltf(device, primitive, mesh_map, buffers); + let aabb = mesh.aabb; + let object = ObjectData { + model: Some(ModelData { + instance: ModelInstance { + transform: Mat4::default(), + color: Vec4::from_array(color), + lights: [0; 16], + point_lights: 0, + spot_lights: 0, + metal, + rough, + }, + material, + mesh: meshes.make(mesh).id, + }), + collider: ColliderData { shape: Shape::Sphere(aabb.1.distance(aabb.0)) }, + affine: Default::default(), + asleep: false, + }; + if len == 1 { + tree_node.object = Some(object); + } else { + tree_node.children.push(TreeNode { + object: Some(object), + children: Vec::new(), + name: "Primitive".to_string(), + }) + } + } + } + for node in node.children() { + tree_node + .children + .push(load_node_from_gltf(node, device, queue, mesh_map, texture_map, materials, textures, meshes, images, buffers)); + } + tree_node +} +pub fn _load_from_gltf(device: &Device, queue: &Queue, meshes: &mut FreeList, materials: &mut Materials, textures: &mut Textures, slice: impl AsRef<[u8]>) -> TreeNode { + let mut root = TreeNode { + object: None, + children: Vec::new(), + name: "Root".to_string(), + }; + if let Ok((document, buffers, images)) = gltf::import_slice(slice) { + let mut mesh_map: HashMap = HashMap::new(); + let mut texture_map: HashMap = HashMap::new(); + for scene in document.scenes() { + let mut scene_node = TreeNode { + object: None, + children: Vec::new(), + name: scene.name().unwrap_or("Scene").to_string(), + }; + for node in scene.nodes() { + scene_node.children.push(load_node_from_gltf( + node, + device, + queue, + &mut mesh_map, + &mut texture_map, + materials, + textures, + meshes, + &images, + &buffers, + )); + } + root.children.push(scene_node) + } + } + root +} + +pub fn load_from_gltf(renderer: &mut Renderer, slice: impl AsRef<[u8]>) -> TreeNode { + _load_from_gltf(&renderer.device, &renderer.queue, &mut renderer.meshes, &mut renderer.instances.materials, &mut renderer.textures, slice) +} \ No newline at end of file diff --git a/src/render/mesh.rs b/src/render/mesh.rs new file mode 100644 index 0000000..0d392b8 --- /dev/null +++ b/src/render/mesh.rs @@ -0,0 +1,51 @@ +use crate::world::AABB; + +#[repr(C)] +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +pub struct TangentVertex { + pub(crate) position: [f32; 3], + pub(crate) normal: [f32; 3], + pub(crate) tex_coord: [f32; 2], + pub(crate) tangent: [f32; 3], + pub(crate) bitangent: [f32; 3], +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +pub struct Vertex { + position: [f32; 3], + normal: [f32; 3], + tex_coord: [f32; 3], +} + +impl TangentVertex { + pub(crate) 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 Mesh { + pub(crate) indices: Option<(wgpu::Buffer, u32)>, + pub(crate) vertices: (wgpu::Buffer, u32), + pub(crate) aabb: AABB, +} \ No newline at end of file diff --git a/src/render/mod.rs b/src/render/mod.rs index e6da105..b15cbfa 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,10 +1,14 @@ pub mod eye; +pub mod texture; +pub mod mesh; +pub mod instance; +pub mod loader; use crate::render::eye::Eye; use crate::world::{Shape, ColliderData, ObjectData, World, AABB}; use bytemuck::{Pod, Zeroable}; use glam::prelude::*; -use gltf::Semantic; +use gltf::{Semantic}; use image::EncodableLayout; use std::cell::RefCell; use std::collections::HashMap; @@ -15,10 +19,12 @@ use std::ops::Range; use std::rc::Rc; use std::sync::Arc; use wgpu::naga::{FastHashMap, FastHashSet}; -use wgpu::util::DeviceExt; use wgpu::{Device, Queue}; use winit::window::Window; use crate::list::{FreeList, SingleId, Id, RefId}; +use crate::render::instance::{Instances, Program}; +use crate::render::mesh::{Mesh, TangentVertex}; +use crate::render::texture::{Texture, TextureProperties}; use crate::world; const DEFAULT_VERTICES: [TangentVertex; 0] = []; @@ -27,294 +33,25 @@ pub struct Screen { } -#[repr(C)] -#[derive(Pod, Zeroable, Copy, Clone)] -pub struct ModelInstance { - 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 struct Textures { + list: FreeList, + default: Texture, } -#[repr(C)] -#[derive(Pod, Copy, Clone, Zeroable)] -pub struct LightInstance { - pub location: Vec4, - pub rotation: Vec4, - pub color: Vec4, -} - -#[derive(Clone)] -pub struct LightData { - pub instance: LightInstance, - pub transform: Affine3, -} - -#[derive(Clone)] -pub struct ModelData { - pub instance: ModelInstance, - pub material: Id, - pub mesh: Id, -} - -pub struct Instances { - light_count: usize, - light_buffer: wgpu::Buffer, - instance_count: usize, - instance_buffer: wgpu::Buffer, - models_flag: bool, - program: Program, - models: FastHashMap, FastHashMap, FastHashMap,bool>>>, -} - -enum Code { - Material(Material), - Mesh(Mesh), - Draw(Range), -} - -struct Program(Vec); - -impl Program { - fn push(&mut self, item: Code) { - self.0.push(item) - } - fn new() -> Program { - Program(Vec::new()) - } - fn render(self, pass: &mut wgpu::RenderPass) { - let mut count = 0; - let mut indexed = false; - for code in self.0 { - match code { - Code::Material(material) => pass.set_bind_group( - Renderer::SIMPLE_RENDER_TEXTURE_GROUP_POSITION, - &material.group, - &[], - ), - Code::Mesh(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; - } - } - Code::Draw(instances) => { - if indexed { - pass.draw_indexed(0..count, 0, instances) - } else { - pass.draw(0..count, instances); - } - } - } - } - } -} - -impl Instances { - const MIN_SIZE: u64 = 64; - pub fn register_object(&mut self, mut object: RefId) { - let model = object.model.as_mut().unwrap(); - self.instance_count += 1; - self.models - .entry(model.mesh.clone()).or_default() - .entry(model.material.clone()).or_default() - .insert(object.into(),self.models_flag); - } - /*pub fn register_light(&mut self, light: Id) { - self.light_count += 1; - self.lights.insert(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 > Instances::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(); - */ - Instances::reallocate_buffer( - device, - &mut self.light_buffer, - self.light_count, - size_of::(), +impl Textures { + fn new(device: &Device, queue: &Queue) -> Textures { + let default = Texture::load( + &device, + &queue, + [0, 0, 255, 255], + TextureProperties { + width: 1, + height: 1, + }, ); - 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, - mesh_list: &mut FreeList, - material_list: &mut FreeList, - objects_list: &mut FreeList, - device: &wgpu::Device, - queue: &wgpu::Queue, - ) { - Instances::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.models.iter_mut() { - self.program.push(Code::Mesh(mesh_list.get(mesh).clone())); - for (material, objects) in materials.iter_mut() { - self.program.push(Code::Material(material_list.get(material).clone())); - let before = index; - objects.retain(|object,flag| { - let object = objects_list.get(object); - if *flag == self.models_flag { - let begin = index as usize * stride; - buffer.slice(begin..begin + stride).copy_from_slice(bytemuck::cast_slice(&[object.model.as_mut().unwrap().instance])); - index += 1; - true - } else { - false - } - }); - self.program.push(Code::Draw(before..index)); - } - } - //println!("objects: {}",index); - self.models_flag = !self.models_flag; - } - - 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) -> Instances { - let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Instance Buffer"), - size: (size_of::() * Instances::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::() * Instances::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, - });*/ - Instances { - light_count: 0, - //light_ref_last: 0, - instance_count: 0, - light_buffer, - instance_buffer, - //light_ref_buffer, - //lights: Default::default(), - models_flag: false, - program: Program(Vec::new()), - models: Default::default(), + Textures { + list: Default::default(), + default, } } } @@ -324,555 +61,25 @@ pub struct Renderer { config: wgpu::SurfaceConfiguration, device: wgpu::Device, queue: wgpu::Queue, - materials: FreeList, - textures: FreeList, + textures: Textures, meshes: FreeList, - light_instances: FreeList, - model_instances: FreeList, + depth: Texture, pub(crate) eye: eye::Eye, - material_layout: wgpu::BindGroupLayout, - default_texture: Texture, - default_material: Material, - sky_pipeline: wgpu::RenderPipeline, - instance_pipeline: wgpu::RenderPipeline, - depth_texture: Texture, - pub(crate) instances: Instances, + pub(crate) instances: instance::Instances, } -#[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] -struct TangentVertex { - position: [f32; 3], - normal: [f32; 3], - tex_coord: [f32; 2], - tangent: [f32; 3], - bitangent: [f32; 3], -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] -struct Vertex { - position: [f32; 3], - normal: [f32; 3], - tex_coord: [f32; 3], -} - -impl TangentVertex { - 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 Mesh { - indices: Option<(wgpu::Buffer, u32)>, - vertices: (wgpu::Buffer, u32), - aabb: AABB, -} -#[derive(Debug, Clone, Hash, Eq, PartialEq)] -pub struct Texture { - texture: wgpu::Texture, - view: wgpu::TextureView, -} -pub struct TextureProperties { - width: u32, - height: u32, -} -// todo: use texture compression! -impl Texture { - pub fn depth(config: &wgpu::SurfaceConfiguration, device: &Device) -> Texture { - 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()); - Texture { - texture: _depth_texture, - view: depth_view, - } - } - pub fn load( - device: &Device, - queue: &Queue, - slice: impl AsRef<[u8]>, - properties: TextureProperties, - ) -> Texture { - 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, - ); - Texture { - texture: diffuse_texture, - view: diffuse_texture_view, - } - } -} - -#[derive(Clone)] -pub struct Material { - group: wgpu::BindGroup, -} - -struct MaterialProperties { - 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 Material { - fn new( - device: &wgpu::Device, - layout: &wgpu::BindGroupLayout, - base: &Texture, - normal: &Texture, - reflect: &Texture, - config: MaterialProperties, - ) -> Material { - 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"), - }); - Material { group } - } -} - -#[derive(Debug)] -struct PoolError(String); - -impl Display for PoolError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Error for PoolError {} - -pub struct TreeNode { - object: Option, - children: Vec, - name: String, -} - -impl TreeNode { - 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 - } - } -} - -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 write_instances(&mut self, objects: &mut FreeList) { - self.instances.write_instances(&mut self.meshes, &mut self.materials, objects, &self.device, &self.queue); - } - pub fn load_texture_from_bytes( - &mut self, - slice: impl AsRef<[u8]>, - format: Option, - ) -> Texture { - let image = if let Some(format) = format { - image::load_from_memory_with_format(slice.as_ref(), format) - } else { - image::load_from_memory(slice.as_ref()) - }; - if let Ok(data) = image { - let data = data.into_rgba8(); - Texture::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: &Texture, - normal: &Texture, - reflect: &Texture, - ) -> Id { - self.materials.make(Material::new( - &self.device, - &self.material_layout, - base, - normal, - reflect, - MaterialProperties { - edge: wgpu::AddressMode::Repeat, - filter: wgpu::FilterMode::Linear, - }, - )).into() - } - pub fn new_texture_from_gltf( - &mut self, - info: &gltf::texture::Texture, - images: &Vec, - ) -> Texture { - 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(), - } - Texture::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], - ) -> RefId { - let position_index = primitive - .get(&Semantic::Positions) - .and_then(|it| it.view()).map(|it| it.buffer().index()); - let normals_index = primitive - .get(&Semantic::Normals) - .and_then(|it| it.view()).map(|it| it.buffer().index()); - let tex_coords_index = primitive - .get(&Semantic::TexCoords(0)) - .and_then(|it| it.view()).map(|it| it.buffer().index()); - let indices_index = primitive - .indices() - .and_then(|it| it.view()).map(|it| it.buffer().index()); - let tangent_index = primitive - .get(&Semantic::Tangents) - .and_then(|it| it.view()).map(|it| it.buffer().index()); - let key = ( - position_index, - normals_index, - tex_coords_index, - indices_index, - ); - self.meshes.make( - meshes - .entry(key) - .or_insert_with(|| { - let mut tangents = false; - let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); - let mut vertex_data: Vec; - let mut aabb = AABB(Vec3::ZERO,Vec3::ZERO); - 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 { - aabb = aabb.extend_to(Vec3::from(position)); - vertex_data.push(TangentVertex { - 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 - }; - Mesh { - indices: indices_result, - vertices: vertices_result, - aabb, - } - }) - .clone(), - ) - } - pub fn load_node_from_gltf( - &mut self, - node: gltf::Node, - meshes: &mut HashMap, - textures: &mut HashMap, - images: &Vec, - buffers: &Vec, - ) -> TreeNode { - let mut tree_node = TreeNode { - 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 aabb = mesh.aabb; - let object = ObjectData { - model: Some(ModelData { - instance: ModelInstance { - transform: Mat4::default(), - color: Vec4::from_array(color), - lights: [0; 16], - point_lights: 0, - spot_lights: 0, - metal, - rough, - }, - material, - mesh: mesh.into(), - }), - collider: ColliderData { shape: Shape::Sphere(aabb.1.distance(aabb.0)) }, - affine: Default::default(), - asleep: false, - }; - if len == 1 { - tree_node.object = Some(object); - } else { - tree_node.children.push(TreeNode { - object: Some(object), - 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]>) -> TreeNode { - let mut root = TreeNode { - 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 = TreeNode { - 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) - } - } - root + self.instances.write_instances(&self.device, &self.queue, &mut self.meshes, objects); } pub fn resize(&mut self, width: u32, height: u32) { println!("resize"); self.config.width = width; self.config.height = height; - self.depth_texture = Texture::depth(&self.config,&self.device); + self.depth = Texture::depth(&self.config, &self.device); self.surface.configure(&self.device, &self.config); self.eye.resize(&self.queue, width, height); } @@ -954,190 +161,31 @@ impl Renderer { view_formats: vec![], desired_maximum_frame_latency: 2, }; - - let shader = - device.create_shader_module(wgpu::include_wgsl!("../assets/SimpleShader.wgsl")); - - let default_texture = Texture::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: &[TangentVertex::desc(), Instances::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 instances = Instances::new(&device); + + let textures = Textures::new(&device, &queue); + + let eye = Eye::new(&device, &config, width, height, textures.default.clone()); + + let instances = Instances::new(&device, &eye, &config, textures.default.clone()); let depth_texture = Texture::depth(&config,&device); - let default_material = Material::new( - &device, - &texture_bind_group_layout, - &default_texture, - &default_texture, - &default_texture, - MaterialProperties::default(), - ); - Ok(Renderer { - material_layout: texture_bind_group_layout, - default_texture, - default_material, - depth_texture, + depth: depth_texture, instances, - sky_pipeline, - instance_pipeline, surface, config, device, queue, - materials: Default::default(), - textures: Default::default(), + textures, meshes: Default::default(), - light_instances: Default::default(), - model_instances: Default::default(), eye, }) } pub(crate) fn render(&mut self, window: &Arc, objects: &mut FreeList) -> anyhow::Result<()> { let surface_texture = match self.surface.get_current_texture() { wgpu::CurrentSurfaceTexture::Success(surface_texture) => surface_texture, - wgpu::CurrentSurfaceTexture::Suboptimal(surface_texture) => { + wgpu::CurrentSurfaceTexture::Suboptimal(_surface_texture) => { println!("suboptimal"); self.surface.configure(&self.device, &self.config); return Ok(()); @@ -1171,7 +219,7 @@ impl Renderer { }); self.eye.write(&self.queue); - self.instances.write_instances(&mut self.meshes, &mut self.materials, objects, &self.device, &self.queue); + self.instances.write_instances(&self.device, &self.queue, &mut self.meshes, objects); { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -1191,7 +239,7 @@ impl Renderer { }, })], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &self.depth_texture.view, + view: &self.depth.view, depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(1.0), store: wgpu::StoreOp::Store, @@ -1203,15 +251,13 @@ impl Renderer { multiview_mask: None, }); - pass.set_pipeline(&self.instance_pipeline); + pass.set_pipeline(&self.eye.sky_pipeline); pass.set_bind_group(0, &self.eye.group, &[]); - pass.set_vertex_buffer(1, self.instances.instance_buffer.slice(..)); - std::mem::replace(&mut self.instances.program,Program(Vec::new())).render(&mut pass); // todo: improve - - pass.set_bind_group(1, &self.default_material.group, &[]); - - pass.set_pipeline(&self.sky_pipeline); pass.draw(0..6, 0..1); + + pass.set_pipeline(&self.instances.pipeline); + pass.set_vertex_buffer(1, self.instances.model_buffer.slice(..)); + std::mem::replace(&mut self.instances.program,Program(Vec::new())).render(&mut pass); // todo: improve } self.queue.submit(Some(encoder.finish())); diff --git a/src/render/sky.wgsl b/src/render/sky.wgsl new file mode 100644 index 0000000..fa563d0 --- /dev/null +++ b/src/render/sky.wgsl @@ -0,0 +1,72 @@ +struct Environment { + ambient: vec4, + light: vec4, + dir: vec4, +} + +struct Eye { + // from camera to screen + proj: mat4x4, + // from screen to camera + inv: mat4x4, + // world to camera + view: mat4x4, + // camera transform + frame: mat4x4, +} + +@group(0) @binding(0) +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; + +// written on the 60th sleepless hour +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 + 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); +} + +struct SkyOutput { + @builtin(position) position: vec4, + @location(0) pos: vec4 // unadulterated by WGSL +} + +const 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), +); + +fn rotation(it: mat4x4) -> mat3x3 { + return mat3x3( + it[0].xyz, + it[1].xyz, + it[2].xyz, + ); +} + +@vertex +fn vs_sky(@builtin(vertex_index) index: u32) -> SkyOutput { + var out: SkyOutput; + out.position = 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/render/texture.rs b/src/render/texture.rs new file mode 100644 index 0000000..131d52f --- /dev/null +++ b/src/render/texture.rs @@ -0,0 +1,115 @@ +use image::EncodableLayout; +use wgpu::{Device, Queue}; +use crate::render::Renderer; + +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct Texture { + texture: wgpu::Texture, + pub(crate) view: wgpu::TextureView, +} +pub struct TextureProperties { + pub(crate) width: u32, + pub(crate) height: u32, +} +// todo: use texture compression! +impl Texture { + pub fn depth(config: &wgpu::SurfaceConfiguration, device: &Device) -> Texture { + 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()); + Texture { + texture: _depth_texture, + view: depth_view, + } + } + pub fn load( + device: &Device, + queue: &Queue, + slice: impl AsRef<[u8]>, + properties: TextureProperties, + ) -> Texture { + 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, + ); + Texture { + texture: diffuse_texture, + view: diffuse_texture_view, + } + } + fn _load_from_file_bytes( + device: &Device, + queue: &Queue, + slice: impl AsRef<[u8]>, + format: Option, + ) -> Option { + let image = if let Some(format) = format { + image::load_from_memory_with_format(slice.as_ref(), format) + } else { + image::load_from_memory(slice.as_ref()) + }; + if let Ok(data) = image { + let data = data.into_rgba8(); + Some(Texture::load( + device, + queue, + data.as_bytes(), + TextureProperties { + width: data.width(), + height: data.height(), + }, + )) + } else { + println!("failed to load texture! error: {:?}", image.unwrap_err()); + None + } + } + + pub fn load_from_file_bytes(renderer: &mut Renderer, slice: impl AsRef<[u8]>, format: Option) -> Option { + Texture::_load_from_file_bytes(&renderer.device,&renderer.queue,slice,format) + } +} \ No newline at end of file diff --git a/src/state.rs b/src/state.rs index d9049fb..1ef7a2a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -4,7 +4,7 @@ use crate::{render, world}; #[derive(Default)] pub struct State { pub objects: FreeList, - pub lights: FreeList, + pub lights: FreeList, pub worlds: FreeList, pub renderer: Option, pub screens: FreeList diff --git a/src/world/debug.rs b/src/world/debug.rs new file mode 100644 index 0000000..ce65244 --- /dev/null +++ b/src/world/debug.rs @@ -0,0 +1,100 @@ +use glam::{IVec3, Mat4, Quat, Vec3}; +use wgpu::naga::FastHashMap; +use crate::list::{FreeList, Id}; +use crate::render; +use crate::world::{ColliderData, ObjectData, OctreeBlock, Shape, World}; + +pub struct OctreeDebug { + map: FastHashMap> +} + +pub enum OctreeDebugOperation { + Add(IVec3,i32), + Remove(IVec3), +} + +impl Default for OctreeDebug { + fn default() -> Self { + Self::new() + } +} + +impl OctreeDebug { + pub fn new() -> OctreeDebug { + OctreeDebug { + map: FastHashMap::default() + } + } + fn set_block( + &mut self, + blocks: &mut FreeList, + block: Id, + objects: &mut FreeList, + debug: &Option, + pos: IVec3, + mut size: i32 + ) { + let entry = self.map.get(&pos); + if let Some(debug) = debug { + if entry.is_none() { + let mut model = debug.clone(); + model.instance.transform = Mat4::from_scale_rotation_translation(Vec3::splat(size as f32),Quat::IDENTITY,pos.as_vec3()); + self.map.insert(pos, objects.make(ObjectData { + model: Some(model), + collider: ColliderData { + shape: Shape::Sphere(size as f32), + }, + affine: Default::default(), + asleep: false, + }).id); + println!("pos {} size {}",pos,size) + } + } else { + if entry.is_some() { + objects.remove(self.map.remove(&pos).unwrap()); + } + } + size /= 2; + if let Some(block) = blocks.get(&block).blocks[0].exists() { + self.set_block(blocks,block,objects,debug, pos - (IVec3::new(-1, -1, -1) * size / 2), size); + } + if let Some(block) = blocks.get(&block).blocks[1].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,-1) * size / 2), size); + } + if let Some(block) = blocks.get(&block).blocks[2].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,-1) * size / 2), size); + } + if let Some(block) = blocks.get(&block).blocks[3].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,-1) * size / 2), size); + } + if let Some(block) = blocks.get(&block).blocks[4].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,-1,1) * size / 2), size); + } + if let Some(block) = blocks.get(&block).blocks[5].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,1) * size / 2), size); + } + if let Some(block) = blocks.get(&block).blocks[6].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,1) * size / 2), size); + } + if let Some(block) = blocks.get(&block).blocks[7].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,1) * size / 2), size); + } + } + pub fn set(&mut self, objects: &mut FreeList, world: &mut World, debug: Option) { + for (index,block) in world.matter.map.iter_mut() { + self.set_block( + &mut world.matter.blocks, + block.clone(), + objects, + &debug, + index * OctreeBlock::CHUNK_SIZE, + OctreeBlock::CHUNK_SIZE, + ) + } + } + pub fn register(&mut self, objects: &mut FreeList, renderer: &mut render::Renderer) { + for (pos,block) in self.map.iter() { + renderer.instances.register_object(objects.get_ref(block.clone())); + } + } +} \ No newline at end of file diff --git a/src/world/mod.rs b/src/world/mod.rs index 34c74d0..a44fad9 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -1,3 +1,6 @@ +pub mod debug; + +use crate::render::instance::ModelData; use glam::{Affine3, IVec3, Mat4, Quat, Vec3}; use std::cell::RefCell; use std::hash::{BuildHasherDefault, Hash, Hasher}; @@ -43,15 +46,22 @@ impl ColliderData { #[derive(Clone)] pub struct ObjectData { - pub model: Option, + pub model: Option, pub collider: ColliderData, pub affine: Affine3, pub asleep: bool, } +impl ObjectData { + pub fn set_affine(&mut self, affine: Affine3) { + self.affine = affine; + self.asleep = false; + } +} + #[derive(Clone, Eq, PartialEq)] pub enum InterestType { - Light(Id), + Light(Id), Object(Id) } @@ -119,100 +129,6 @@ impl Default for World { } } -pub struct OctreeDebug { - map: FastHashMap> -} - -pub enum OctreeDebugOperation { - Add(IVec3,i32), - Remove(IVec3), -} - -impl Default for OctreeDebug { - fn default() -> Self { - Self::new() - } -} - -impl OctreeDebug { - pub fn new() -> OctreeDebug { - OctreeDebug { - map: FastHashMap::default() - } - } - fn set_block( - &mut self, - blocks: &mut FreeList, - block: Id, - objects: &mut FreeList, - debug: &Option, - pos: IVec3, - size: i32 - ) { - let entry = self.map.get(&pos); - if let Some(debug) = debug { - if entry.is_none() { - let mut model = debug.clone(); - model.instance.transform = Mat4::from_scale_rotation_translation(Vec3::splat(size as f32),Quat::IDENTITY,pos.as_vec3()); - self.map.insert(pos, objects.make(ObjectData { - model: Some(model), - collider: ColliderData { - shape: Shape::Sphere(size as f32), - }, - affine: Default::default(), - asleep: false, - }).id); - println!("pos {} size {}",pos,size) - } - } else { - if entry.is_some() { - objects.remove(self.map.remove(&pos).unwrap()); - } - } - if let Some(block) = blocks.get(&block).blocks[0].exists() { - self.set_block(blocks,block,objects,debug, pos - (IVec3::new(-1, -1, -1) * size / 4), size / 2); - } - if let Some(block) = blocks.get(&block).blocks[1].exists() { - self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,-1) * size / 4), size / 2); - } - if let Some(block) = blocks.get(&block).blocks[2].exists() { - self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,-1) * size / 4), size / 2); - } - if let Some(block) = blocks.get(&block).blocks[3].exists() { - self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,-1) * size / 4), size / 2); - } - if let Some(block) = blocks.get(&block).blocks[4].exists() { - self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,-1,1) * size / 4), size / 2); - } - if let Some(block) = blocks.get(&block).blocks[5].exists() { - self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,1) * size / 4), size / 2); - } - if let Some(block) = blocks.get(&block).blocks[6].exists() { - self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,1) * size / 4), size / 2); - } - if let Some(block) = blocks.get(&block).blocks[7].exists() { - self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,1) * size / 4), size / 2); - } - } - pub fn set(&mut self, objects: &mut FreeList, world: &mut World, debug: Option) { - for (index,block) in world.matter.map.iter_mut() { - self.set_block( - &mut world.matter.blocks, - block.clone(), - objects, - &debug, - index * OctreeBlock::CHUNK_SIZE, - OctreeBlock::CHUNK_SIZE, - ) - } - } - pub fn register(&mut self, objects: &mut FreeList, renderer: &mut render::Renderer) { - for (pos,block) in self.map.iter() { - renderer.instances.register_object(objects.get_ref(block.clone())); - } - } -} - impl World { pub fn new() -> World { World { @@ -228,15 +144,15 @@ impl World { object.id } pub fn remove_object(&mut self, object: Id) { - + } pub fn step( &mut self, maybe_renderer: &mut Option, objects: &mut FreeList, - _lights: &mut FreeList, + _lights: &mut FreeList, ) { - /*fn reinsert( + fn reinsert( block_id: &Id, blocks: &mut FreeList, interests: &mut FreeList, @@ -247,10 +163,6 @@ impl World { let interest = interests.get(&interest_id); match interest.interest.it { InterestType::Object(ref object_id) => { - let mut object = objects.get_ref(object_id.clone()); - let transform = Mat4::from_mat3_translation(object.affine.matrix3, object.affine.translation); - object.model.as_mut().unwrap().instance.transform = transform; - instances.register_object(object) } _ => {} } @@ -263,8 +175,8 @@ impl World { } } for (_pos,id) in self.matter.map.iter() { - reinsert(id, &mut self.matter.blocks, &mut self.matter.interests, objects) - }*/ + reinsert(id, &mut self.matter.blocks, &mut self.matter.nodes, objects) + } if let Some(renderer) = maybe_renderer { fn consider( block_id: &Id, @@ -294,7 +206,7 @@ impl World { } } for (_pos,id) in self.matter.map.iter() { - consider(id, renderer, &mut self.matter.blocks, &mut self.matter.interests, objects) + consider(id, renderer, &mut self.matter.blocks, &mut self.matter.nodes, objects) } } } @@ -340,14 +252,14 @@ struct OctreeBlock { } impl OctreeBlock { - fn push(&mut self, interests: &mut FreeList, interest: Interest) { + fn push_interest(&mut self, interests: &mut FreeList, interest: Interest) { self.first = interests.make(InterestNode { interest: interest.clone(), next: self.first.clone(), }).id.maybe(); self.interests += 1; } - fn remove(&mut self, interests: &mut FreeList, needle: Interest) { + fn remove_interest(&mut self, interests: &mut FreeList, needle: Interest) { // todo: needed? let mut maybe_last = MaybeId::NULL; let mut maybe_this = self.first.clone(); while let Some(this) = maybe_this.exists() { @@ -376,7 +288,7 @@ impl OctreeBlock { size: i32, ) { if pos == IVec3::ZERO || blocks.get(&it).interests < OctreeBlock::MAX_IDEAL_INTEREST as u32 { - blocks.get(&it).push(interests,interest) + blocks.get(&it).push_interest(interests, interest) } else { let mut index = 0; let x = if pos.x > 0 { @@ -413,8 +325,9 @@ impl OctreeBlock { type Blocks = [MaybeId; 8]; -struct OctreeMap { - interests: FreeList, +pub struct OctreeMap { + interests: FreeList, + nodes: FreeList, blocks: FreeList, map: FastHashMap>, } @@ -446,7 +359,7 @@ impl OctreeMap { OctreeBlock::place( block.clone(), &mut self.blocks, - &mut self.interests, + &mut self.nodes, interest.clone(), pos, OctreeBlock::CHUNK_SIZE, @@ -458,6 +371,7 @@ impl OctreeMap { fn new() -> OctreeMap { OctreeMap { interests: Default::default(), + nodes: Default::default(), blocks: Default::default(), map: FastHashMap::with_hasher(BuildHasherDefault::default()), }