diff --git a/Cargo.lock b/Cargo.lock index 44c9085..3f8d249 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1838,7 +1838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3732,6 +3732,7 @@ dependencies = [ "id", "linear_ops", "ratio_ops", + "vbsp", ] [[package]] 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/physics/src/physics.rs b/engine/physics/src/physics.rs index 8644cb3..e71a79b 100644 --- a/engine/physics/src/physics.rs +++ b/engine/physics/src/physics.rs @@ -1,4 +1,5 @@ use std::collections::{HashMap,HashSet}; +use crate::model::DirectedEdge; use crate::model::{self as model_physics,PhysicsMesh,PhysicsMeshTransform,TransformedMesh,MeshQuery,PhysicsMeshId,PhysicsSubmeshId}; use strafesnet_common::bvh; use strafesnet_common::map; @@ -280,7 +281,8 @@ impl PhysicsCamera{ .clamp(Self::ANGLE_PITCH_LOWER_LIMIT,Self::ANGLE_PITCH_UPPER_LIMIT); mat3::from_rotation_yx(ax,ay) } - fn rotation(&self)->Planar64Mat3{ + #[inline] + pub fn rotation(&self)->Planar64Mat3{ self.get_rotation(self.clamped_mouse_pos) } fn simulate_move_rotation(&self,mouse_delta:glam::IVec2)->Planar64Mat3{ @@ -931,6 +933,7 @@ pub struct PhysicsData{ modes:gameplay_modes::Modes, //cached calculations hitbox_mesh:HitboxMesh, + pub le_models:Vec<strafesnet_common::model::Model>, } impl Default for PhysicsData{ fn default()->Self{ @@ -939,6 +942,7 @@ impl Default for PhysicsData{ models:Default::default(), modes:Default::default(), hitbox_mesh:StyleModifiers::default().calculate_mesh(), + le_models:Default::default(), } } } @@ -980,6 +984,34 @@ impl PhysicsContext<'_>{ } } impl PhysicsData{ + pub fn trace_ray(&self,ray:strafesnet_common::ray::Ray)->Option<ModelId>{ + let (_time,convex_mesh_id)=self.bvh.sample_ray(&ray,Time::ZERO,Time::MAX/4,|&model,ray|{ + let mesh=self.models.mesh(model); + // brute force trace every face + mesh.faces().filter_map(|face_id|{ + let (n,d)=mesh.face_nd(face_id); + // trace ray onto face + // n.(o+d*t)==n.p + // n.o + n.d * t == n.p + // t == (n.p - n.o)/n.d + let nd=n.dot(ray.direction); + if nd.is_zero(){ + return None; + } + let t=(d-n.dot(ray.origin))/nd; + // check if point of intersection is behind face edges + // *2 because average of 2 vertices + let p=ray.extrapolate(t)*2; + mesh.face_edges(face_id).iter().all(|&directed_edge_id|{ + let edge_n=mesh.directed_edge_n(directed_edge_id); + let cross_n=edge_n.cross(n); + let &[vert0,vert1]=mesh.edge_verts(directed_edge_id.as_undirected()).as_ref(); + cross_n.dot(p)<cross_n.dot(mesh.vert(vert0)+mesh.vert(vert1)) + }).then(||t) + }).min().map(Into::into) + })?; + Some(convex_mesh_id.model_id.into()) + } /// use with caution, this is the only non-instruction way to mess with physics pub fn generate_models(&mut self,map:&map::CompleteMap){ let mut modes=map.modes.clone().denormalize(); @@ -1112,6 +1144,7 @@ impl PhysicsData{ self.bvh=bvh; self.models=models; self.modes=modes; + self.le_models=map.models.clone(); //hitbox_mesh is unchanged println!("Physics Objects: {}",model_count); } diff --git a/engine/session/src/session.rs b/engine/session/src/session.rs index e7959b3..b782078 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, } } } @@ -161,6 +164,7 @@ pub struct Session{ recording:Recording, //players:HashMap<PlayerId,Simulation>, replays:HashMap<BotId,Replay>, + last_ray_hit:Option<strafesnet_common::model::ModelId>, } impl Session{ pub fn new( @@ -177,6 +181,7 @@ impl Session{ view_state:ViewState::Play, recording:Default::default(), replays:HashMap::new(), + last_ray_hit:None, } } fn clear_recording(&mut self){ @@ -188,12 +193,30 @@ 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) ), } } + pub fn debug_raycast_print_model_id_if_changed(&mut self,time:SessionTime){ + if let Some(frame_state)=self.get_frame_state(time){ + let ray=strafesnet_common::ray::Ray{ + origin:frame_state.body.extrapolated_position(self.simulation.timer.time(time)), + direction:-frame_state.camera.rotation().z_axis, + }; + let model_id=self.geometry_shared.trace_ray(ray); + if model_id!=self.last_ray_hit{ + println!("hit={model_id:?}"); + self.last_ray_hit=model_id; + if let Some(model_id)=model_id{ + if let Some(model)=self.geometry_shared.le_models.get(model_id.get() as usize){ + println!("{}",model.debug_info); + } + } + } + } + } pub fn user_settings(&self)->&UserSettings{ &self.user_settings } diff --git a/lib/bsp_loader/src/bsp.rs b/lib/bsp_loader/src/bsp.rs index e27c2c0..2669550 100644 --- a/lib/bsp_loader/src/bsp.rs +++ b/lib/bsp_loader/src/bsp.rs @@ -50,6 +50,7 @@ fn add_brush<'a>( origin:vbsp::Vector, rendercolor:vbsp::Color, attributes:attr::CollisionAttributesId, + debug_info:model::DebugInfo, )->Option<AddBrush>{ let transform=integer::Planar64Affine3::from_translation( valve_transform(origin.into()) @@ -67,7 +68,7 @@ fn add_brush<'a>( let mesh=model::MeshId::new(mesh_id); let model_id=model::ModelId::new(world_models.len() as u32); world_models.push( - model::Model{mesh,attributes,transform,color} + model::Model{mesh,attributes,transform,color,debug_info} ); Some(AddBrush::Available(model_id)) }, @@ -80,7 +81,7 @@ fn add_brush<'a>( let mesh=mesh_deferred_loader.acquire_mesh_id(model); let model_id=model::ModelId::new(prop_models.len() as u32); prop_models.push( - model::Model{mesh,attributes,transform,color} + model::Model{mesh,attributes,transform,color,debug_info} ); Some(AddBrush::Deferred(model_id)) } @@ -150,6 +151,7 @@ pub fn convert<'a>( valve_transform(prop.origin.into()), ), color:glam::Vec4::ONE, + debug_info:model::DebugInfo::Prop, } }).collect(); @@ -222,6 +224,7 @@ pub fn convert<'a>( attributes:ATTRIBUTE_DECORATION, transform:integer::Planar64Affine3::IDENTITY, color:glam::Vec4::W, + debug_info:model::DebugInfo::World, }); // THE CUBE OF DESTINY @@ -235,18 +238,25 @@ pub fn convert<'a>( const ENTITY_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=ATTRIBUTE_CONTACT_DEFAULT; const ENTITY_TRIGGER_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=ATTRIBUTE_INTERSECT_DEFAULT; for raw_ent in &bsp.entities{ + let debug_info=match model::EntityInfo::new(raw_ent.properties()){ + Ok(entity_info)=>model::DebugInfo::Entity(entity_info), + Err(_)=>{ + println!("EntityInfoError"); + model::DebugInfo::World + }, + }; macro_rules! ent_brush_default{ ($entity:ident)=>{ - {add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,$entity.rendercolor,ENTITY_ATTRIBUTE);} + {add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,$entity.rendercolor,ENTITY_ATTRIBUTE,debug_info);} }; } macro_rules! ent_brush_prop{ ($entity:ident)=>{ - {add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,WHITE,ENTITY_ATTRIBUTE);} + {add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,WHITE,ENTITY_ATTRIBUTE,debug_info);} }; } macro_rules! ent_brush_trigger{ ($entity:ident)=>{ - {add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE);} + {add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE,debug_info);} }; } match raw_ent.parse(){ @@ -305,7 +315,7 @@ pub fn convert<'a>( Ok(Entity::FuncFishPool(_func_fish_pool))=>(), Ok(Entity::FuncFootstepControl(_func_footstep_control))=>(), Ok(Entity::FuncHostageRescue(_func_hostage_rescue))=>(), - Ok(Entity::FuncIllusionary(brush))=>{add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION);}, + Ok(Entity::FuncIllusionary(brush))=>{add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION,debug_info);}, Ok(Entity::FuncLod(_func_lod))=>(), Ok(Entity::FuncMonitor(brush))=>ent_brush_default!(brush), Ok(Entity::FuncMovelinear(brush))=>ent_brush_default!(brush), @@ -318,9 +328,9 @@ pub fn convert<'a>( Ok(Entity::FuncSmokevolume(_func_smokevolume))=>(), Ok(Entity::FuncTracktrain(brush))=>ent_brush_default!(brush), Ok(Entity::FuncTrain(brush))=>ent_brush_default!(brush), - Ok(Entity::FuncWall(brush))=>{add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ENTITY_ATTRIBUTE);}, - Ok(Entity::FuncWallToggle(brush))=>{add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ENTITY_ATTRIBUTE);}, - Ok(Entity::FuncWaterAnalog(brush))=>{add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor.unwrap_or(WHITE),ENTITY_ATTRIBUTE);}, + Ok(Entity::FuncWall(brush))=>{add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ENTITY_ATTRIBUTE,debug_info);}, + Ok(Entity::FuncWallToggle(brush))=>{add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ENTITY_ATTRIBUTE,debug_info);}, + Ok(Entity::FuncWaterAnalog(brush))=>{add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor.unwrap_or(WHITE),ENTITY_ATTRIBUTE,debug_info);}, Ok(Entity::GamePlayerEquip(_game_player_equip))=>(), Ok(Entity::GameText(_game_text))=>(), Ok(Entity::GameUi(_game_ui))=>(), @@ -399,7 +409,7 @@ pub fn convert<'a>( Ok(Entity::TriggerGravity(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerHurt(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerLook(brush))=>ent_brush_trigger!(brush), - Ok(Entity::TriggerMultiple(brush))=>{add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE);}, + Ok(Entity::TriggerMultiple(brush))=>{add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE,debug_info);}, Ok(Entity::TriggerOnce(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerProximity(brush))=>ent_brush_trigger!(brush), // TriggerPush is booster @@ -407,7 +417,7 @@ pub fn convert<'a>( Ok(Entity::TriggerSoundscape(brush))=>ent_brush_trigger!(brush), // TriggerTeleport is Trigger# Ok(Entity::TriggerTeleport(brush))=>{ - if let (Some(model_id),Some(target))=(add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE),brush.target){ + if let (Some(model_id),Some(target))=(add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE,debug_info),brush.target){ teleports.insert(model_id,target); } }, @@ -474,6 +484,13 @@ pub fn convert<'a>( Ok(mesh)=>{ let mesh_id=model::MeshId::new(world_meshes.len() as u32); world_meshes.push(mesh); + let sides={ + let brush_start_idx=brush.brush_side as usize; + let sides_range=brush_start_idx..brush_start_idx+brush.num_brush_sides as usize; + bsp.brush_sides[sides_range].iter().filter_map(|side|bsp.texture_info(side.texture_info as usize)).map(|texture_info|{ + texture_info.flags + }).collect() + }; world_models.push(model::Model{ mesh:mesh_id, attributes, @@ -482,6 +499,7 @@ pub fn convert<'a>( integer::vec3::ZERO, ), color:glam::Vec4::ONE, + debug_info:model::DebugInfo::Brush(model::BrushInfo{flags:brush.flags,sides}), }); }, Err(e)=>println!("Brush mesh error: {e}"), @@ -496,6 +514,7 @@ pub fn convert<'a>( attributes:ATTRIBUTE_INTERSECT_DEFAULT, transform:integer::Planar64Affine3::from_translation(valve_transform(spawn_point.into())), color:glam::Vec4::W, + debug_info:model::DebugInfo::World, }); Stage::empty(model_id) diff --git a/lib/common/Cargo.toml b/lib/common/Cargo.toml index c4ed365..0f16aab 100644 --- a/lib/common/Cargo.toml +++ b/lib/common/Cargo.toml @@ -17,3 +17,4 @@ linear_ops = { version = "0.1.0", path = "../linear_ops", registry = "strafesnet ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet" } glam = "0.30.0" id = { version = "0.1.0", registry = "strafesnet" } +vbsp = "0.8.0" diff --git a/lib/common/src/model.rs b/lib/common/src/model.rs index 41d981e..3c6306f 100644 --- a/lib/common/src/model.rs +++ b/lib/common/src/model.rs @@ -208,9 +208,87 @@ impl MeshBuilder{ #[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)] pub struct ModelId(u32); +#[derive(Clone)] pub struct Model{ pub mesh:MeshId, pub attributes:gameplay_attributes::CollisionAttributesId, pub color:Color4,//transparency is in here pub transform:Planar64Affine3, + pub debug_info:DebugInfo, +} + +#[derive(Debug,Clone)] +pub enum DebugInfo{ + World, + Prop, + Brush(BrushInfo), + Entity(EntityInfo), +} +impl std::fmt::Display for DebugInfo{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + match self{ + DebugInfo::World=>write!(f,"World"), + DebugInfo::Prop=>write!(f,"Prop"), + DebugInfo::Brush(brush_info)=>brush_info.fmt(f), + DebugInfo::Entity(entity_info)=>entity_info.fmt(f), + } + } +} + +#[derive(Debug,Clone)] +pub struct BrushInfo{ + pub flags:vbsp::BrushFlags, + pub sides:Vec<vbsp::TextureFlags>, +} +impl std::fmt::Display for BrushInfo{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + let noice=self.flags.iter_names().filter_map(|(name,flags)|{ + (flags.bits()!=0).then(||name) + }).collect::<Vec<&str>>().join("|"); + writeln!(f,"brush_info.flags={noice}")?; + for (i,side) in self.sides.iter().enumerate(){ + let noice_string=side.iter_names().filter_map(|(name,flags)|{ + (flags.bits()!=0).then(||name) + }).collect::<Vec<&str>>().join("|"); + writeln!(f,"brush_info.sides[{i}]={noice_string}")?; + } + Ok(()) + } +} + +#[derive(Debug,Clone)] +pub struct EntityInfo{ + pub classname:Box<str>, + pub properties:Vec<(Box<str>,Box<str>)>, +} +pub enum EntityInfoError{ + MissingClassname, +} +impl EntityInfo{ + pub fn new<'a>(iter:impl IntoIterator<Item=(&'a str,&'a str)>)->Result<Self,EntityInfoError>{ + let mut classname:Option<Box<str>>=None; + let mut properties:Vec<(Box<str>,Box<str>)>=Vec::new(); + for (name,value) in iter{ + match name{ + "classname"=>classname=Some(value.into()), + "hammerid"=>(), + _=>properties.push((name.into(),value.into())), + } + } + properties.sort_by(|(n0,_),(n1,_)|n0.cmp(n1)); + let Some(classname)=classname else{ + return Err(EntityInfoError::MissingClassname); + }; + Ok(EntityInfo{classname,properties}) + } +} +impl std::fmt::Display for EntityInfo{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + writeln!(f,"struct {}{{",self.classname)?; + for (name,value) in &self.properties{ + writeln!(f,"\t{name}:{value},")?; + } + write!(f,"}}")?; + Ok(()) + } } diff --git a/lib/snf/src/newtypes/model.rs b/lib/snf/src/newtypes/model.rs index 6d3ce51..c290d47 100644 --- a/lib/snf/src/newtypes/model.rs +++ b/lib/snf/src/newtypes/model.rs @@ -250,6 +250,7 @@ impl Into<strafesnet_common::model::Model> for Model{ ]), strafesnet_common::integer::vec3::raw_xyz(_9,_a,_b) ), + debug_info:strafesnet_common::model::DebugInfo::World, } } } diff --git a/strafe-client/src/physics_worker.rs b/strafe-client/src/physics_worker.rs index 63ce68f..e6c1c92 100644 --- a/strafe-client/src/physics_worker.rs +++ b/strafe-client/src/physics_worker.rs @@ -77,5 +77,8 @@ pub fn new<'a>( run_session_instruction!(ins.time,SessionInstruction::LoadReplay(bot)); } } + + //whatever just do it + session.debug_raycast_print_model_id_if_changed(ins.time); }) } 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; +}