From d81eebe423fd3e00df5ff035ec24fe7fb37f2c62 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 7 Jan 2025 12:25:37 +0100 Subject: albedo texture works --- client/Cargo.toml | 1 + client/src/renderer.rs | 15 ++- client/src/scene_prepare.rs | 286 ++++++++++++++++++++++++++++++-------------- client/src/scene_render.rs | 109 +++++++++++++---- client/src/shader.wgsl | 4 +- client/src/state.rs | 3 + client/src/window.rs | 2 +- 7 files changed, 299 insertions(+), 121 deletions(-) (limited to 'client') diff --git a/client/Cargo.toml b/client/Cargo.toml index 4bcfb4c..da0f5c6 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -16,3 +16,4 @@ winit = "0.30.8" weareshared = { path = "../shared" } rand = "0.9.0-beta.1" glam = "0.29.2" +image = "0.25.5" diff --git a/client/src/renderer.rs b/client/src/renderer.rs index 3561d87..3591503 100644 --- a/client/src/renderer.rs +++ b/client/src/renderer.rs @@ -13,7 +13,7 @@ use winit::window::Window; pub struct Renderer<'a> { surface: Surface<'a>, - queue: Queue, + queue: Arc, device: Arc, surface_configuration: SurfaceConfiguration, scene_pipeline: ScenePipeline, @@ -60,10 +60,15 @@ impl<'a> Renderer<'a> { surface.configure(&device, &surface_configuration); let device = Arc::new(device); + let queue = Arc::new(queue); + + let (scene_pipeline, texture_bgl) = + ScenePipeline::new(&device, surface_configuration.format); + let scene_prepare = ScenePreparer::new(device.clone(), queue.clone(), texture_bgl); Ok(Self { - scene_pipeline: ScenePipeline::new(&device, surface_configuration.format), - scene_prepare: ScenePreparer::new(device.clone()), + scene_pipeline, + scene_prepare, surface, device, queue, @@ -77,6 +82,7 @@ impl<'a> Renderer<'a> { self.surface_configuration.height = height; self.surface .configure(&self.device, &self.surface_configuration); + self.scene_pipeline.resize(&self.device, width, height); } pub fn draw(&mut self, scene: &SceneTree) -> Result<()> { @@ -102,8 +108,7 @@ impl<'a> Renderer<'a> { &mut commands, &target_view, scene, - &self.scene_prepare.prefabs, - &mut self.scene_prepare.prefabs_needed, + &mut self.scene_prepare.prefabs, ); let i = self.queue.submit(Some(commands.finish())); diff --git a/client/src/scene_prepare.rs b/client/src/scene_prepare.rs index 85db160..f7a5bc8 100644 --- a/client/src/scene_prepare.rs +++ b/client/src/scene_prepare.rs @@ -1,31 +1,62 @@ use crate::download::Downloader; use anyhow::{Context, Result}; +use image::{ImageFormat, ImageReader}; use log::debug; use std::{ collections::{HashMap, HashSet}, + hash::Hash, + io::Cursor, sync::Arc, }; use weareshared::{ Affine3A, packets::{ReadWrite, Resource}, - resources::{Attribute, Part, Prefab}, + resources::{Part, Prefab}, }; use wgpu::{ - Buffer, BufferUsages, Device, - util::{BufferInitDescriptor, DeviceExt}, + BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindingResource, Buffer, + BufferUsages, Device, Extent3d, Queue, SamplerDescriptor, Texture, TextureDescriptor, + TextureDimension, TextureFormat, TextureUsages, TextureViewDescriptor, + util::{BufferInitDescriptor, DeviceExt, TextureDataOrder}, }; -pub struct ScenePreparer { - index_buffers: HashMap, u32)>, - index_buffers_needed: HashSet, - vertex_buffers: HashMap>, - vertex_buffers_needed: HashSet, - parts: HashMap>, - parts_needed: HashSet, - pub prefabs: HashMap, - pub prefabs_needed: HashSet, +pub struct DemandMap { + values: HashMap, + needed: HashSet, +} +impl DemandMap { + pub fn new() -> Self { + Self { + needed: HashSet::new(), + values: HashMap::new(), + } + } + pub fn insert(&mut self, key: K, value: V) { + self.needed.remove(&key); + self.values.insert(key, value); + } + pub fn try_get(&mut self, key: K) -> Option { + if let Some(k) = self.values.get(&key) { + Some(k.to_owned()) + } else { + self.needed.insert(key); + None + } + } +} +pub struct ScenePreparer { device: Arc, + queue: Arc, + texture_bgl: BindGroupLayout, + + textures: DemandMap, Arc)>, + placeholder_textures: DemandMap<(), (Arc, Arc)>, + index_buffers: DemandMap, u32)>, + vertex_buffers: DemandMap, u32)>, + placeholder_vertex_buffers: DemandMap<(u32, bool), Arc>, + parts: DemandMap>, + pub prefabs: DemandMap>, } pub struct RPrefab(pub Vec<(Affine3A, Arc)>); @@ -35,44 +66,42 @@ pub struct RPart { pub position: [Arc; 3], pub normal: [Arc; 3], pub texcoord: [Arc; 2], + pub texture: Arc, } impl ScenePreparer { - pub fn new(device: Arc) -> Self { + pub fn new(device: Arc, queue: Arc, texture_bgl: BindGroupLayout) -> Self { Self { - index_buffers: HashMap::new(), - vertex_buffers: HashMap::new(), - vertex_buffers_needed: HashSet::new(), - parts: HashMap::new(), - parts_needed: HashSet::new(), - prefabs: HashMap::new(), - prefabs_needed: HashSet::new(), - index_buffers_needed: HashSet::new(), + texture_bgl, + index_buffers: DemandMap::new(), + vertex_buffers: DemandMap::new(), + parts: DemandMap::new(), + prefabs: DemandMap::new(), + textures: DemandMap::new(), + placeholder_vertex_buffers: DemandMap::new(), + placeholder_textures: DemandMap::new(), device, + queue, } } pub fn update(&mut self, dls: &mut Downloader) -> Result<()> { - let mut done = Vec::new(); - for pres in &self.prefabs_needed { - if let Some(buf) = dls.try_get(*pres)? { + for pres in self.prefabs.needed.clone() { + if let Some(buf) = dls.try_get(pres)? { let prefab = Prefab::read(&mut buf.as_slice()).context("parsing prefab")?; let mut rprefab = RPrefab(Vec::new()); for (aff, partres) in &prefab.0 { - if let Some(part) = self.parts.get(partres) { + if let Some(part) = self.parts.try_get(*partres) { rprefab.0.push((*aff, part.clone())); - } else { - self.parts_needed.insert(*partres); } } if rprefab.0.len() == prefab.0.len() { - self.prefabs.insert(*pres, rprefab); + self.prefabs.insert(pres, Arc::new(rprefab)); debug!("prefab created ({pres})"); - done.push(*pres); } } } - for pres in &self.index_buffers_needed { - if let Some(buf) = dls.try_get(*pres)? { + for pres in self.index_buffers.needed.clone() { + if let Some(buf) = dls.try_get(pres)? { let buf = buf .into_iter() .array_chunks::<2>() @@ -86,13 +115,12 @@ impl ScenePreparer { usage: BufferUsages::INDEX | BufferUsages::COPY_DST, }); self.index_buffers - .insert(*pres, (Arc::new(buffer), buf.len() as u32 / 2)); - debug!("index buffer created (len={}) {pres}", buf.len() / 6); - done.push(*pres); + .insert(pres, (Arc::new(buffer), (buf.len() / 2) as u32)); + debug!("index buffer created (len={}) {pres}", buf.len() / 2); } } - for pres in &self.vertex_buffers_needed { - if let Some(buf) = dls.try_get(*pres)? { + for pres in self.vertex_buffers.needed.clone() { + if let Some(buf) = dls.try_get(pres)? { let buf = buf .into_iter() .array_chunks::<4>() @@ -105,94 +133,174 @@ impl ScenePreparer { label: None, usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, }); - self.vertex_buffers.insert(*pres, Arc::new(buffer)); + self.vertex_buffers + .insert(pres, (Arc::new(buffer), (buf.len() / 4) as u32)); debug!( "vertex attribute buffer created (len={}) {pres}", buf.len() / 4 ); - done.push(*pres); } } - for pres in &self.parts_needed { - if let Some(buf) = dls.try_get(*pres)? { + for pres in self.textures.needed.clone() { + if let Some(buf) = dls.try_get(pres)? { + let image = ImageReader::new(Cursor::new(buf)).with_guessed_format()?; + let image = image.decode()?; + let image = image.to_rgba8(); + let image_raw = image.to_vec(); + let tex_bg = create_texture( + &self.device, + &self.queue, + &self.texture_bgl, + &image_raw, + image.width(), + image.height(), + ); + self.textures.insert(pres, tex_bg); + } + } + for pres in self.placeholder_textures.needed.clone() { + let tex_bg = create_texture( + &self.device, + &self.queue, + &self.texture_bgl, + &[255, 255, 255, 255], + 1, + 1, + ); + self.placeholder_textures.insert(pres, tex_bg); + } + for pres in self.parts.needed.clone() { + if let Some(buf) = dls.try_get(pres)? { let part = Part::read(&mut buf.as_slice()).context("parsing part")?; - if let (Some(indexres), Some(positionres), Some(normalres), Some(texcoordres)) = ( - part.index, - part.va_position, - part.va_normal, - part.va_texcoord, - ) { - let Some((index, index_count)) = self.index_buffers.get(&indexres).cloned() - else { - self.index_buffers_needed.insert(indexres); + if let (Some(indexres), Some(positionres)) = (part.index, part.va_position) { + let Some((index, index_count)) = self.index_buffers.try_get(indexres) else { + self.index_buffers.needed.insert(indexres); continue; }; let mut position = Vec::new(); + let mut vertex_count = 0; for vr in positionres { - match vr { - Attribute::Constant(_) => todo!(), - Attribute::Vertex(resource) => { - if let Some(vertex) = self.vertex_buffers.get(&resource).cloned() { - position.push(vertex); - } else { - self.vertex_buffers_needed.insert(resource); - }; - } - Attribute::Texture(_resource, _ch) => todo!(), + if let Some((vertex, n)) = self.vertex_buffers.try_get(vr) { + vertex_count = n; + position.push(vertex); } } let mut normal = Vec::new(); - for vr in normalres { - match vr { - Attribute::Constant(_) => todo!(), - Attribute::Vertex(resource) => { - if let Some(vertex) = self.vertex_buffers.get(&resource).cloned() { - normal.push(vertex); - } else { - self.vertex_buffers_needed.insert(resource); - }; + if let Some(normalres) = part.va_normal { + for vr in normalres { + if let Some((vertex, _)) = self.vertex_buffers.try_get(vr) { + normal.push(vertex); + } + } + } else { + // TODO generate normals + for _ in 0..3 { + if let Some(buf) = self + .placeholder_vertex_buffers + .try_get((vertex_count, false)) + { + normal.push(buf); } - Attribute::Texture(_resource, _ch) => todo!(), } } let mut texcoord = Vec::new(); - for vr in texcoordres { - match vr { - Attribute::Constant(_) => todo!(), - Attribute::Vertex(resource) => { - if let Some(vertex) = self.vertex_buffers.get(&resource).cloned() { - texcoord.push(vertex); - } else { - self.vertex_buffers_needed.insert(resource); - }; + if let Some(texcoordres) = part.va_texcoord { + for vr in texcoordres { + if let Some((vertex, _)) = self.vertex_buffers.try_get(vr) { + texcoord.push(vertex); } - Attribute::Texture(_resource, _ch) => todo!(), + } + } else { + // TODO generate UVs + for _ in 0..3 { + if let Some(buf) = self + .placeholder_vertex_buffers + .try_get((vertex_count, false)) + { + texcoord.push(buf); + } + } + } + let mut texture = None; + if let Some(albedores) = part.tex_pbr_albedo { + if let Some((_tex, bg)) = self.textures.try_get(albedores) { + texture = Some(bg) + } + } else { + if let Some((_tex, bg)) = self.placeholder_textures.try_get(()) { + texture = Some(bg) } } - if texcoord.len() == 2 && normal.len() == 3 && position.len() == 3 { + + if texcoord.len() == 2 + && normal.len() == 3 + && position.len() == 3 + && texture.is_some() + { debug!("part created ({pres})"); self.parts.insert( - *pres, + pres, Arc::new(RPart { index_count, index, texcoord: texcoord.try_into().unwrap(), normal: normal.try_into().unwrap(), position: position.try_into().unwrap(), + texture: texture.unwrap(), }), ); - done.push(*pres); } } } } - - for d in done { - self.parts_needed.remove(&d); - self.prefabs_needed.remove(&d); - self.index_buffers_needed.remove(&d); - self.vertex_buffers_needed.remove(&d); - } Ok(()) } } + +fn create_texture( + device: &Device, + queue: &Queue, + bgl: &BindGroupLayout, + data: &[u8], + width: u32, + height: u32, +) -> (Arc, Arc) { + let texture = device.create_texture_with_data( + &queue, + &TextureDescriptor { + label: None, + size: Extent3d { + depth_or_array_layers: 1, + width, + height, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Rgba8UnormSrgb, + usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, + view_formats: &[], + }, + TextureDataOrder::LayerMajor, + data, + ); + let textureview = texture.create_view(&TextureViewDescriptor::default()); + let sampler = device.create_sampler(&SamplerDescriptor { + ..Default::default() + }); + let bindgroup = device.create_bind_group(&BindGroupDescriptor { + label: None, + layout: &bgl, + entries: &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView(&textureview), + }, + BindGroupEntry { + binding: 1, + resource: BindingResource::Sampler(&sampler), + }, + ], + }); + (Arc::new(texture), Arc::new(bindgroup)) +} diff --git a/client/src/scene_render.rs b/client/src/scene_render.rs index b4965e1..8703c9f 100644 --- a/client/src/scene_render.rs +++ b/client/src/scene_render.rs @@ -1,21 +1,24 @@ use glam::{EulerRot, Mat3, Mat4, Vec3, vec3}; -use std::collections::{HashMap, HashSet}; +use std::sync::Arc; use weareshared::{packets::Resource, tree::SceneTree}; use wgpu::{ - BindGroup, BindGroupDescriptor, BindGroupLayoutDescriptor, BlendState, Color, ColorTargetState, - ColorWrites, CommandEncoder, Device, FragmentState, FrontFace, IndexFormat, LoadOp, + BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState, + Color, ColorTargetState, ColorWrites, CommandEncoder, CompareFunction, DepthBiasState, + DepthStencilState, Device, Extent3d, FragmentState, FrontFace, IndexFormat, LoadOp, MultisampleState, Operations, PipelineCompilationOptions, PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, PushConstantRange, RenderPassColorAttachment, - RenderPassDescriptor, RenderPipeline, RenderPipelineDescriptor, ShaderStages, StoreOp, - TextureFormat, TextureView, VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, - VertexStepMode, include_wgsl, + RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPipeline, + RenderPipelineDescriptor, SamplerBindingType, ShaderStages, StencilState, StoreOp, + TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages, + TextureView, TextureViewDescriptor, TextureViewDimension, VertexAttribute, VertexBufferLayout, + VertexFormat, VertexState, VertexStepMode, include_wgsl, }; -use crate::scene_prepare::RPrefab; +use crate::scene_prepare::{DemandMap, RPrefab}; pub struct ScenePipeline { pipeline: RenderPipeline, - bind_group: BindGroup, + depth: TextureView, } macro_rules! v_attr { @@ -33,17 +36,45 @@ macro_rules! v_attr { } impl ScenePipeline { - pub fn new(device: &Device, format: TextureFormat) -> Self { + pub fn new(device: &Device, format: TextureFormat) -> (Self, BindGroupLayout) { let module = device.create_shader_module(include_wgsl!("shader.wgsl")); - let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { - entries: &[], + let depth = device.create_texture(&TextureDescriptor { label: None, + size: Extent3d { + height: 256, + width: 256, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Depth32Float, + usage: TextureUsages::RENDER_ATTACHMENT, + view_formats: &[], }); - let bind_group = device.create_bind_group(&BindGroupDescriptor { + let depth = depth.create_view(&TextureViewDescriptor::default()); + + let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { + entries: &[ + BindGroupLayoutEntry { + binding: 0, + count: None, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + sample_type: TextureSampleType::Float { filterable: true }, + view_dimension: TextureViewDimension::D2, + multisampled: false, + }, + }, + BindGroupLayoutEntry { + binding: 1, + count: None, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Filtering), + }, + ], label: None, - layout: &bind_group_layout, - entries: &[], }); let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { label: None, @@ -79,23 +110,45 @@ impl ScenePipeline { polygon_mode: PolygonMode::Fill, ..Default::default() }, - depth_stencil: Default::default(), + depth_stencil: Some(DepthStencilState { + depth_compare: CompareFunction::Greater, + depth_write_enabled: true, + format: TextureFormat::Depth32Float, + bias: DepthBiasState::default(), + stencil: StencilState::default(), + }), multisample: MultisampleState::default(), multiview: None, cache: None, }); - Self { - bind_group, - pipeline, - } + (Self { pipeline, depth }, bind_group_layout) + } + + pub fn resize(&mut self, device: &Device, width: u32, height: u32) { + self.depth = device + .create_texture(&TextureDescriptor { + label: None, + size: Extent3d { + height, + width, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Depth32Float, + usage: TextureUsages::RENDER_ATTACHMENT, + view_formats: &[], + }) + .create_view(&TextureViewDescriptor::default()); } + pub fn draw( &mut self, commands: &mut CommandEncoder, target: &TextureView, scene: &SceneTree, - prefabs: &HashMap, - prefabs_needed: &mut HashSet, + prefabs: &mut DemandMap>, ) { let mut rpass = commands.begin_render_pass(&RenderPassDescriptor { label: None, @@ -112,6 +165,14 @@ impl ScenePipeline { }), }, })], + depth_stencil_attachment: Some(RenderPassDepthStencilAttachment { + view: &self.depth, + depth_ops: Some(Operations { + load: LoadOp::Clear(0.), + store: StoreOp::Store, + }), + stencil_ops: None, + }), ..Default::default() }); @@ -127,14 +188,14 @@ impl ScenePipeline { ob.rot.z, )) * Mat4::from_translation(ob.pos.into()); - if let Some(prefab) = prefabs.get(&ob.res) { + if let Some(prefab) = prefabs.try_get(ob.res) { for (affine, part) in &prefab.0 { let part_projection = prefab_projection * Mat4::from_mat3a(affine.matrix3) * Mat4::from_translation(affine.translation.into()); let projection = part_projection.to_cols_array().map(|v| v.to_le_bytes()); - rpass.set_bind_group(0, &self.bind_group, &[]); + rpass.set_bind_group(0, &*part.texture, &[]); rpass.set_pipeline(&self.pipeline); rpass.set_push_constants(ShaderStages::VERTEX, 0, projection.as_flattened()); rpass.set_index_buffer(part.index.slice(..), IndexFormat::Uint16); @@ -148,8 +209,6 @@ impl ScenePipeline { rpass.set_vertex_buffer(7, part.texcoord[1].slice(..)); rpass.draw_indexed(0..part.index_count, 0, 0..1); } - } else { - prefabs_needed.insert(ob.res); } } } diff --git a/client/src/shader.wgsl b/client/src/shader.wgsl index d2b2d9f..b40bf57 100644 --- a/client/src/shader.wgsl +++ b/client/src/shader.wgsl @@ -15,6 +15,8 @@ struct VertexOut { @location(1) uv: vec2, } +@group(0) @binding(0) var tex_albedo: texture_2d; +@group(0) @binding(1) var tex_albedo_sampler: sampler; var project: mat4x4; @vertex @@ -30,5 +32,5 @@ fn vs_main(vi: VertexIn) -> VertexOut { } @fragment fn fs_main(vo: VertexOut) -> @location(0) vec4 { - return vec4(vo.normal, 1.0); + return textureSample(tex_albedo, tex_albedo_sampler, vo.uv); } diff --git a/client/src/state.rs b/client/src/state.rs index e8188d8..a370264 100644 --- a/client/src/state.rs +++ b/client/src/state.rs @@ -26,6 +26,9 @@ impl<'a> State<'a> { warn!("draw failed: {e:?}"); } } + pub fn resize(&mut self, width: u32, height: u32) { + self.renderer.resize(width, height); + } pub fn update(&mut self) -> Result<()> { for p in self.network.packet_recv.try_iter() { self.downloader.packet(&p)?; diff --git a/client/src/window.rs b/client/src/window.rs index 2430479..6ee7fa4 100644 --- a/client/src/window.rs +++ b/client/src/window.rs @@ -41,7 +41,7 @@ impl ApplicationHandler for WindowState { if let Some((win, sta)) = &mut self.window { match event { WindowEvent::Resized(size) => { - sta.renderer.resize(size.width, size.height); + sta.resize(size.width, size.height); } WindowEvent::RedrawRequested => { sta.draw(); -- cgit v1.2.3-70-g09d2