diff --git a/engine/graphics/src/graphics.rs b/engine/graphics/src/graphics.rs index a7340df..cb7f3a7 100644 --- a/engine/graphics/src/graphics.rs +++ b/engine/graphics/src/graphics.rs @@ -5,7 +5,7 @@ use strafesnet_settings::settings; use strafesnet_session::session; use strafesnet_common::model::{self, ColorId, NormalId, PolygonIter, PositionId, RenderConfigId, TextureCoordinateId, VertexId}; use wgpu::{util::DeviceExt,AstcBlock,AstcChannel}; -use crate::model::{self as model_graphics,IndexedGraphicsMeshOwnedRenderConfig,IndexedGraphicsMeshOwnedRenderConfigId,GraphicsMeshOwnedRenderConfig,GraphicsModelColor4,GraphicsModelOwned,GraphicsVertex}; +use crate::model::{self as model_graphics,IndexedGraphicsMeshOwnedRenderConfig,IndexedGraphicsMeshOwnedRenderConfigId,GraphicsMeshOwnedRenderConfig,GraphicsModelColor4,GraphicsModelOwned,GraphicsVertex,DebugGraphicsVertex}; pub fn required_limits()->wgpu::Limits{ wgpu::Limits::default() @@ -36,12 +36,22 @@ struct GraphicsModel{ instance_count:u32, } +struct DebugGraphicsMesh{ + indices:Indices, + vertex_buf:wgpu::Buffer, +} +struct DebugGraphicsModel{ + debug_mesh_id:u32, + bind_group:wgpu::BindGroup, +} + struct GraphicsSamplers{ repeat:wgpu::Sampler, } struct GraphicsBindGroupLayouts{ model:wgpu::BindGroupLayout, + debug_model:wgpu::BindGroupLayout, } struct GraphicsBindGroups{ @@ -52,6 +62,7 @@ struct GraphicsBindGroups{ struct GraphicsPipelines{ skybox:wgpu::RenderPipeline, model:wgpu::RenderPipeline, + debug:wgpu::RenderPipeline, } struct GraphicsCamera{ @@ -112,6 +123,8 @@ pub struct GraphicsState{ camera_buf:wgpu::Buffer, temp_squid_texture_view:wgpu::TextureView, models:Vec<GraphicsModel>, + debug_meshes:Vec<DebugGraphicsMesh>, + debug_models:Vec<DebugGraphicsModel>, depth_view:wgpu::TextureView, staging_belt:wgpu::util::StagingBelt, } @@ -146,6 +159,76 @@ impl GraphicsState{ self.camera.fov=user_settings.calculate_fov(1.0,&self.camera.screen_size).as_vec2(); } pub fn generate_models(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,map:&map::CompleteMap){ + //generate debug meshes, each debug model refers to one + self.debug_meshes=map.meshes.iter().map(|mesh|{ + let vertices:Vec<DebugGraphicsVertex>=mesh.unique_vertices.iter().map(|vertex|{ + DebugGraphicsVertex{ + pos:mesh.unique_pos[vertex.pos.get() as usize].to_array().map(Into::into), + normal:mesh.unique_normal[vertex.normal.get() as usize].to_array().map(Into::into), + } + }).collect(); + let vertex_buf=device.create_buffer_init(&wgpu::util::BufferInitDescriptor{ + label:Some("Vertex"), + contents:bytemuck::cast_slice(&vertices), + usage:wgpu::BufferUsages::VERTEX, + }); + + let mut indices=Vec::new(); + for physics_group in &mesh.physics_groups{ + for polygon_group_id in &physics_group.groups{ + for poly in mesh.polygon_groups[polygon_group_id.get() as usize].polys(){ + // triangulate + let mut poly_vertices=poly.into_iter().copied(); + if let (Some(a),Some(mut b))=(poly_vertices.next(),poly_vertices.next()){ + for c in poly_vertices{ + indices.extend([a,b,c]); + b=c; + } + } + } + } + } + + DebugGraphicsMesh{ + indices:if (u32::MAX as usize)<vertices.len(){ + panic!("Model has too many vertices!") + }else if (u16::MAX as usize)<vertices.len(){ + Indices::new(device,&indices.into_iter().map(|vertex_idx|vertex_idx.get() as u32).collect(),wgpu::IndexFormat::Uint32) + }else{ + Indices::new(device,&indices.into_iter().map(|vertex_idx|vertex_idx.get() as u16).collect(),wgpu::IndexFormat::Uint16) + }, + vertex_buf, + } + }).collect(); + + //generate debug models, only one will be rendered at a time + self.debug_models=map.models.iter().enumerate().map(|(model_id,model)|{ + let model_uniforms=get_instance_buffer_data(&GraphicsModelOwned{ + transform:model.transform.into(), + normal_transform:glam::Mat3::from_cols_array_2d(&model.transform.matrix3.to_array().map(|row|row.map(Into::into))).inverse().transpose(), + color:GraphicsModelColor4::new(glam::vec4(1.0,0.0,0.0,0.2)), + }); + let model_buf=device.create_buffer_init(&wgpu::util::BufferInitDescriptor{ + label:Some(format!("Debug Model{} Buf",model_id).as_str()), + contents:bytemuck::cast_slice(&model_uniforms), + usage:wgpu::BufferUsages::UNIFORM|wgpu::BufferUsages::COPY_DST, + }); + let bind_group=device.create_bind_group(&wgpu::BindGroupDescriptor{ + layout:&self.bind_group_layouts.debug_model, + entries:&[ + wgpu::BindGroupEntry{ + binding:0, + resource:model_buf.as_entire_binding(), + }, + ], + label:Some(format!("Debug Model{} Bind Group",model_id).as_str()), + }); + DebugGraphicsModel{ + debug_mesh_id:model.mesh.get(), + bind_group, + } + }).collect(); + //generate texture view per texture let texture_views:HashMap<strafesnet_common::model::TextureId,wgpu::TextureView>=map.textures.iter().enumerate().filter_map(|(texture_id,texture_data)|{ let texture_id=model::TextureId::new(texture_id as u32); @@ -588,6 +671,21 @@ impl GraphicsState{ }, ], }); + let debug_model_bind_group_layout=device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor{ + label:Some("Debug Model Bind Group Layout"), + entries:&[ + wgpu::BindGroupLayoutEntry{ + binding:0, + visibility:wgpu::ShaderStages::VERTEX_FRAGMENT, + ty:wgpu::BindingType::Buffer{ + ty:wgpu::BufferBindingType::Uniform, + has_dynamic_offset:false, + min_binding_size:None, + }, + count:None, + }, + ], + }); let clamp_sampler=device.create_sampler(&wgpu::SamplerDescriptor{ label:Some("Clamp Sampler"), @@ -736,6 +834,14 @@ impl GraphicsState{ ], push_constant_ranges:&[], }); + let debug_model_pipeline_layout=device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor{ + label:None, + bind_group_layouts:&[ + &camera_bind_group_layout, + &debug_model_bind_group_layout, + ], + push_constant_ranges:&[], + }); let sky_pipeline_layout=device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor{ label:None, bind_group_layouts:&[ @@ -811,6 +917,45 @@ impl GraphicsState{ multiview:None, cache:None, }); + let debug_model_pipeline=device.create_render_pipeline(&wgpu::RenderPipelineDescriptor{ + label:Some("Debug Model Pipeline"), + layout:Some(&debug_model_pipeline_layout), + vertex:wgpu::VertexState{ + module:&shader, + entry_point:Some("vs_debug"), + buffers:&[wgpu::VertexBufferLayout{ + array_stride:std::mem::size_of::<DebugGraphicsVertex>() as wgpu::BufferAddress, + step_mode:wgpu::VertexStepMode::Vertex, + attributes:&wgpu::vertex_attr_array![0=>Float32x3,1=>Float32x3], + }], + compilation_options:wgpu::PipelineCompilationOptions::default(), + }, + fragment:Some(wgpu::FragmentState{ + module:&shader, + entry_point:Some("fs_debug"), + targets:&[Some(wgpu::ColorTargetState{ + format:config.view_formats[0], + blend:Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask:wgpu::ColorWrites::default(), + })], + compilation_options:wgpu::PipelineCompilationOptions::default(), + }), + primitive:wgpu::PrimitiveState{ + front_face:wgpu::FrontFace::Cw, + cull_mode:Some(wgpu::Face::Front), + ..Default::default() + }, + depth_stencil:Some(wgpu::DepthStencilState{ + format:Self::DEPTH_FORMAT, + depth_write_enabled:true, + depth_compare:wgpu::CompareFunction::Always, + stencil:wgpu::StencilState::default(), + bias:wgpu::DepthBiasState::default(), + }), + multisample:wgpu::MultisampleState::default(), + multiview:None, + cache:None, + }); let camera=GraphicsCamera::default(); let camera_uniforms=camera.to_uniform_data(glam::Vec3::ZERO,glam::Vec2::ZERO); @@ -850,7 +995,8 @@ impl GraphicsState{ Self{ pipelines:GraphicsPipelines{ skybox:sky_pipeline, - model:model_pipeline + model:model_pipeline, + debug:debug_model_pipeline, }, bind_groups:GraphicsBindGroups{ camera:camera_bind_group, @@ -859,9 +1005,14 @@ impl GraphicsState{ camera, camera_buf, models:Vec::new(), + debug_meshes:Vec::new(), + debug_models:Vec::new(), depth_view, staging_belt:wgpu::util::StagingBelt::new(0x100), - bind_group_layouts:GraphicsBindGroupLayouts{model:model_bind_group_layout}, + bind_group_layouts:GraphicsBindGroupLayouts{ + model:model_bind_group_layout, + debug_model:debug_model_bind_group_layout, + }, samplers:GraphicsSamplers{repeat:repeat_sampler}, temp_squid_texture_view:squid_texture_view, } @@ -949,6 +1100,7 @@ impl GraphicsState{ rpass.set_bind_group(0,&self.bind_groups.camera,&[]); rpass.set_bind_group(1,&self.bind_groups.skybox_texture,&[]); + // Draw all models. rpass.set_pipeline(&self.pipelines.model); for model in &self.models{ rpass.set_bind_group(2,&model.bind_group,&[]); @@ -960,6 +1112,19 @@ impl GraphicsState{ rpass.set_pipeline(&self.pipelines.skybox); rpass.draw(0..3,0..1); + + // render a single debug_model in red + if let Some(model_id)=frame_state.debug_model{ + if let Some(model)=self.debug_models.get(model_id.get() as usize){ + let mesh=&self.debug_meshes[model.debug_mesh_id as usize]; + rpass.set_pipeline(&self.pipelines.debug); + rpass.set_bind_group(1,&model.bind_group,&[]); + rpass.set_vertex_buffer(0,mesh.vertex_buf.slice(..)); + rpass.set_index_buffer(mesh.indices.buf.slice(..),mesh.indices.format); + //TODO: loop over triangle strips + rpass.draw_indexed(0..mesh.indices.count,0,0..1); + } + } } queue.submit(std::iter::once(encoder.finish())); @@ -968,21 +1133,23 @@ impl GraphicsState{ } } const MODEL_BUFFER_SIZE:usize=4*4 + 12 + 4;//let size=std::mem::size_of::<ModelInstance>(); -const MODEL_BUFFER_SIZE_BYTES:usize=MODEL_BUFFER_SIZE*4; +const MODEL_BUFFER_SIZE_BYTES:usize=MODEL_BUFFER_SIZE*core::mem::size_of::<f32>(); +fn get_instance_buffer_data(instance:&GraphicsModelOwned)->[f32;MODEL_BUFFER_SIZE]{ + let mut out=[0.0;MODEL_BUFFER_SIZE]; + out[0..16].copy_from_slice(instance.transform.as_ref()); + out[16..19].copy_from_slice(instance.normal_transform.x_axis.as_ref()); + // out[20]=0.0; + out[20..23].copy_from_slice(instance.normal_transform.y_axis.as_ref()); + // out[24]=0.0; + out[24..27].copy_from_slice(instance.normal_transform.z_axis.as_ref()); + // out[28]=0.0; + out[28..32].copy_from_slice(instance.color.get().as_ref()); + out +} fn get_instances_buffer_data(instances:&[GraphicsModelOwned])->Vec<f32>{ let mut raw=Vec::with_capacity(MODEL_BUFFER_SIZE*instances.len()); for mi in instances{ - //model transform - raw.extend_from_slice(&AsRef::<[f32; 4*4]>::as_ref(&mi.transform)[..]); - //normal transform - raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.x_axis)); - raw.extend_from_slice(&[0.0]); - raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.y_axis)); - raw.extend_from_slice(&[0.0]); - raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.z_axis)); - raw.extend_from_slice(&[0.0]); - //color - raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color.get())); + raw.extend_from_slice(&get_instance_buffer_data(mi)); } raw } diff --git a/engine/graphics/src/model.rs b/engine/graphics/src/model.rs index 2468cda..775b7c6 100644 --- a/engine/graphics/src/model.rs +++ b/engine/graphics/src/model.rs @@ -8,6 +8,12 @@ pub struct GraphicsVertex{ pub normal:[f32;3], pub color:[f32;4], } +#[derive(Clone,Copy,Pod,Zeroable)] +#[repr(C)] +pub struct DebugGraphicsVertex{ + pub pos:[f32;3], + pub normal:[f32;3], +} #[derive(Clone,Copy,id::Id)] pub struct IndexedGraphicsMeshOwnedRenderConfigId(u32); pub struct IndexedGraphicsMeshOwnedRenderConfig{ diff --git a/engine/session/src/session.rs b/engine/session/src/session.rs index 55bf47b..5b453c8 100644 --- a/engine/session/src/session.rs +++ b/engine/session/src/session.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use strafesnet_common::gameplay_modes::{ModeId,StageId}; use strafesnet_common::instruction::{InstructionConsumer,InstructionEmitter,InstructionFeedback,TimedInstruction}; +use strafesnet_common::model::ModelId; // session represents the non-hardware state of the client. // Ideally it is a deterministic state which is atomically updated by instructions, same as the simulation state. use strafesnet_common::physics::{ @@ -61,6 +62,7 @@ pub struct FrameState{ pub body:physics::Body, pub camera:physics::PhysicsCamera, pub time:PhysicsTime, + pub debug_model:Option<strafesnet_common::model::ModelId>, } pub struct Simulation{ @@ -77,11 +79,12 @@ impl Simulation{ physics, } } - pub fn get_frame_state(&self,time:SessionTime)->FrameState{ + pub fn get_frame_state(&self,time:SessionTime,debug_model:Option<ModelId>)->FrameState{ FrameState{ body:self.physics.camera_body(), camera:self.physics.camera(), time:self.timer.time(time), + debug_model, } } } @@ -190,9 +193,9 @@ impl Session{ } pub fn get_frame_state(&self,time:SessionTime)->Option<FrameState>{ match &self.view_state{ - ViewState::Play=>Some(self.simulation.get_frame_state(time)), + ViewState::Play=>Some(self.simulation.get_frame_state(time,self.last_ray_hit)), ViewState::Replay(bot_id)=>self.replays.get(bot_id).map(|replay| - replay.simulation.get_frame_state(time) + replay.simulation.get_frame_state(time,None) ), } } diff --git a/strafe-client/src/shader.wgsl b/strafe-client/src/shader.wgsl index 4298a03..a9bb9b6 100644 --- a/strafe-client/src/shader.wgsl +++ b/strafe-client/src/shader.wgsl @@ -86,6 +86,29 @@ fn vs_entity_texture( return result; } +@group(1) +@binding(0) +var<uniform> model_instance: ModelInstance; + +struct DebugEntityOutput { + @builtin(position) position: vec4<f32>, + @location(1) normal: vec3<f32>, + @location(2) view: vec3<f32>, +}; + +@vertex +fn vs_debug( + @location(0) pos: vec3<f32>, + @location(1) normal: vec3<f32>, +) -> DebugEntityOutput { + var position: vec4<f32> = model_instance.transform * vec4<f32>(pos, 1.0); + var result: DebugEntityOutput; + result.normal = model_instance.normal_transform * normal; + result.view = position.xyz - camera.view_inv[3].xyz;//col(3) + result.position = camera.proj * camera.view * position; + return result; +} + //group 2 is the skybox texture @group(1) @binding(0) @@ -110,3 +133,8 @@ fn fs_entity_texture(vertex: EntityOutputTexture) -> @location(0) vec4<f32> { let reflected_color = textureSample(cube_texture, cube_sampler, reflected).rgb; return mix(vec4<f32>(vec3<f32>(0.05) + 0.2 * reflected_color,1.0),mix(vertex.model_color,vec4<f32>(fragment_color.rgb,1.0),fragment_color.a),0.5+0.5*abs(d)); } + +@fragment +fn fs_debug(vertex: DebugEntityOutput) -> @location(0) vec4<f32> { + return model_instance.color; +}