Merge branch 'bsp-gamemechanics' into debug-merge

This commit is contained in:
Quaternions 2025-03-11 17:46:04 -07:00
commit e230eeccb7

@ -1,4 +1,5 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap;
use vbsp_entities_css::Entity; use vbsp_entities_css::Entity;
@ -6,7 +7,7 @@ use strafesnet_common::{map,model,integer,gameplay_attributes as attr};
use strafesnet_deferred_loader::deferred_loader::{MeshDeferredLoader,RenderConfigDeferredLoader}; use strafesnet_deferred_loader::deferred_loader::{MeshDeferredLoader,RenderConfigDeferredLoader};
use strafesnet_deferred_loader::mesh::Meshes; use strafesnet_deferred_loader::mesh::Meshes;
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture}; use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
use strafesnet_common::gameplay_modes::{NormalizedMode,NormalizedModes,Mode,Stage}; use strafesnet_common::gameplay_modes::{self as modes,NormalizedMode,NormalizedModes,Mode,Stage};
use crate::valve_transform; use crate::valve_transform;
@ -35,6 +36,12 @@ fn ingest_vertex(
}) })
} }
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
enum AddBrush{
Available(model::ModelId),
Deferred(model::ModelId),
}
fn add_brush<'a>( fn add_brush<'a>(
mesh_deferred_loader:&mut MeshDeferredLoader<&'a str>, mesh_deferred_loader:&mut MeshDeferredLoader<&'a str>,
world_models:&mut Vec<model::Model>, world_models:&mut Vec<model::Model>,
@ -43,7 +50,7 @@ fn add_brush<'a>(
origin:vbsp::Vector, origin:vbsp::Vector,
rendercolor:vbsp::Color, rendercolor:vbsp::Color,
attributes:attr::CollisionAttributesId, attributes:attr::CollisionAttributesId,
){ )->Option<AddBrush>{
let transform=integer::Planar64Affine3::from_translation( let transform=integer::Planar64Affine3::from_translation(
valve_transform(origin.into()) valve_transform(origin.into())
); );
@ -58,20 +65,24 @@ fn add_brush<'a>(
("*",id_str)=>match id_str.parse(){ ("*",id_str)=>match id_str.parse(){
Ok(mesh_id)=>{ Ok(mesh_id)=>{
let mesh=model::MeshId::new(mesh_id); let mesh=model::MeshId::new(mesh_id);
let model_id=model::ModelId::new(world_models.len() as u32);
world_models.push( world_models.push(
model::Model{mesh,attributes,transform,color} model::Model{mesh,attributes,transform,color}
); );
Some(AddBrush::Available(model_id))
}, },
Err(e)=>{ Err(e)=>{
println!("Brush model int parse error: {e} model={model}"); println!("Brush model int parse error: {e} model={model}");
return; None
}, },
}, },
_=>{ _=>{
let mesh=mesh_deferred_loader.acquire_mesh_id(model); let mesh=mesh_deferred_loader.acquire_mesh_id(model);
let model_id=model::ModelId::new(prop_models.len() as u32);
prop_models.push( prop_models.push(
model::Model{mesh,attributes,transform,color} model::Model{mesh,attributes,transform,color}
); );
Some(AddBrush::Deferred(model_id))
} }
} }
} }
@ -80,7 +91,7 @@ pub fn convert<'a>(
bsp:&'a crate::Bsp, bsp:&'a crate::Bsp,
render_config_deferred_loader:&mut RenderConfigDeferredLoader<Cow<'a,str>>, render_config_deferred_loader:&mut RenderConfigDeferredLoader<Cow<'a,str>>,
mesh_deferred_loader:&mut MeshDeferredLoader<&'a str>, mesh_deferred_loader:&mut MeshDeferredLoader<&'a str>,
)->PartialMap1{ )->PartialMap1<'a>{
let bsp=bsp.as_ref(); let bsp=bsp.as_ref();
//figure out real attributes later //figure out real attributes later
let unique_attributes=vec![ let unique_attributes=vec![
@ -151,7 +162,7 @@ pub fn convert<'a>(
let color=mb.acquire_color_id(glam::Vec4::ONE); let color=mb.acquire_color_id(glam::Vec4::ONE);
let mut graphics_groups=Vec::new(); let mut graphics_groups=Vec::new();
let mut render_id_to_graphics_group_id=std::collections::HashMap::new(); let mut render_id_to_graphics_group_id=HashMap::new();
let polygon_groups=world_model.faces().enumerate().map(|(polygon_group_id,face)|{ let polygon_groups=world_model.faces().enumerate().map(|(polygon_group_id,face)|{
let polygon_group_id=model::PolygonGroupId::new(polygon_group_id as u32); let polygon_group_id=model::PolygonGroupId::new(polygon_group_id as u32);
let face_texture=face.texture(); let face_texture=face.texture();
@ -213,22 +224,25 @@ pub fn convert<'a>(
let destination_mesh_id=model::MeshId::new(world_meshes.len() as u32); let destination_mesh_id=model::MeshId::new(world_meshes.len() as u32);
world_meshes.push(crate::brush::unit_cube()); world_meshes.push(crate::brush::unit_cube());
let mut teleports=HashMap::new();
let mut teleport_destinations=HashMap::new();
const WHITE:vbsp::Color=vbsp::Color{r:255,g:255,b:255}; const WHITE:vbsp::Color=vbsp::Color{r:255,g:255,b:255};
const ENTITY_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=ATTRIBUTE_DECORATION; const ENTITY_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=ATTRIBUTE_DECORATION;
const ENTITY_TRIGGER_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=ATTRIBUTE_INTERSECT_DEFAULT; const ENTITY_TRIGGER_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=ATTRIBUTE_INTERSECT_DEFAULT;
for raw_ent in &bsp.entities{ for raw_ent in &bsp.entities{
macro_rules! ent_brush_default{ macro_rules! ent_brush_default{
($entity:ident)=>{ ($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);}
}; };
} macro_rules! ent_brush_prop{ } macro_rules! ent_brush_prop{
($entity:ident)=>{ ($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);}
}; };
} }
macro_rules! ent_brush_trigger{ macro_rules! ent_brush_trigger{
($entity:ident)=>{ ($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);}
}; };
} }
match raw_ent.parse(){ match raw_ent.parse(){
@ -287,7 +301,7 @@ pub fn convert<'a>(
Ok(Entity::FuncFishPool(_func_fish_pool))=>(), Ok(Entity::FuncFishPool(_func_fish_pool))=>(),
Ok(Entity::FuncFootstepControl(_func_footstep_control))=>(), Ok(Entity::FuncFootstepControl(_func_footstep_control))=>(),
Ok(Entity::FuncHostageRescue(_func_hostage_rescue))=>(), 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);},
Ok(Entity::FuncLod(_func_lod))=>(), Ok(Entity::FuncLod(_func_lod))=>(),
Ok(Entity::FuncMonitor(brush))=>ent_brush_default!(brush), Ok(Entity::FuncMonitor(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncMovelinear(brush))=>ent_brush_default!(brush), Ok(Entity::FuncMovelinear(brush))=>ent_brush_default!(brush),
@ -300,9 +314,9 @@ pub fn convert<'a>(
Ok(Entity::FuncSmokevolume(_func_smokevolume))=>(), Ok(Entity::FuncSmokevolume(_func_smokevolume))=>(),
Ok(Entity::FuncTracktrain(brush))=>ent_brush_default!(brush), Ok(Entity::FuncTracktrain(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncTrain(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::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::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::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::GamePlayerEquip(_game_player_equip))=>(), Ok(Entity::GamePlayerEquip(_game_player_equip))=>(),
Ok(Entity::GameText(_game_text))=>(), Ok(Entity::GameText(_game_text))=>(),
Ok(Entity::GameUi(_game_ui))=>(), Ok(Entity::GameUi(_game_ui))=>(),
@ -321,7 +335,17 @@ pub fn convert<'a>(
Ok(Entity::InfoPlayerTerrorist(spawn))=>found_spawn=Some(spawn.origin), Ok(Entity::InfoPlayerTerrorist(spawn))=>found_spawn=Some(spawn.origin),
Ok(Entity::InfoTarget(_info_target))=>(), Ok(Entity::InfoTarget(_info_target))=>(),
// InfoTeleportDestination is Spawn# // InfoTeleportDestination is Spawn#
Ok(Entity::InfoTeleportDestination(_info_teleport_destination))=>(), Ok(Entity::InfoTeleportDestination(info_teleport_destination))=>if let Some(target)=info_teleport_destination.targetname{
// create a new model
let model_id=model::ModelId::new(world_models.len() as u32);
world_models.push(model::Model{
mesh:destination_mesh_id,
attributes:ATTRIBUTE_INTERSECT_DEFAULT,
transform:integer::Planar64Affine3::from_translation(valve_transform(info_teleport_destination.origin.into())),
color:glam::Vec4::W,
});
teleport_destinations.insert(target,model_id);
},
Ok(Entity::Infodecal(_infodecal))=>(), Ok(Entity::Infodecal(_infodecal))=>(),
Ok(Entity::KeyframeRope(_keyframe_rope))=>(), Ok(Entity::KeyframeRope(_keyframe_rope))=>(),
Ok(Entity::Light(_light))=>(), Ok(Entity::Light(_light))=>(),
@ -371,14 +395,18 @@ pub fn convert<'a>(
Ok(Entity::TriggerGravity(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerGravity(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerHurt(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerHurt(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerLook(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);},
Ok(Entity::TriggerOnce(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerOnce(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerProximity(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerProximity(brush))=>ent_brush_trigger!(brush),
// TriggerPush is booster // TriggerPush is booster
Ok(Entity::TriggerPush(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerPush(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerSoundscape(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerSoundscape(brush))=>ent_brush_trigger!(brush),
// TriggerTeleport is Trigger# // TriggerTeleport is Trigger#
Ok(Entity::TriggerTeleport(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::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){
teleports.insert(model_id,target);
}
},
Ok(Entity::TriggerVphysicsMotion(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerVphysicsMotion(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerWind(brush))=>ent_brush_trigger!(brush), Ok(Entity::TriggerWind(brush))=>ent_brush_trigger!(brush),
Ok(Entity::WaterLodControl(_water_lod_control))=>(), Ok(Entity::WaterLodControl(_water_lod_control))=>(),
@ -456,8 +484,7 @@ pub fn convert<'a>(
} }
} }
let mut modes_list=Vec::new(); let first_stage=found_spawn.map(|spawn_point|{
if let Some(spawn_point)=found_spawn{
// create a new model // create a new model
let model_id=model::ModelId::new(world_models.len() as u32); let model_id=model::ModelId::new(world_models.len() as u32);
world_models.push(model::Model{ world_models.push(model::Model{
@ -467,58 +494,58 @@ pub fn convert<'a>(
color:glam::Vec4::W, color:glam::Vec4::W,
}); });
let first_stage=Stage::empty(model_id); Stage::empty(model_id)
let main_mode=Mode::new( });
strafesnet_common::gameplay_style::StyleModifiers::source_bhop(),
model_id,
std::collections::HashMap::new(),
vec![first_stage],
std::collections::HashMap::new(),
);
modes_list.push(NormalizedMode::new(main_mode));
}
PartialMap1{ PartialMap1{
attributes:unique_attributes, attributes:unique_attributes,
world_meshes, world_meshes,
prop_models, prop_models,
world_models, world_models,
modes:NormalizedModes::new(modes_list), first_stage,
teleports,
teleport_destinations,
} }
} }
//partially constructed map types //partially constructed map types
pub struct PartialMap1{ pub struct PartialMap1<'a>{
attributes:Vec<attr::CollisionAttributes>, attributes:Vec<attr::CollisionAttributes>,
prop_models:Vec<model::Model>, prop_models:Vec<model::Model>,
world_meshes:Vec<model::Mesh>, world_meshes:Vec<model::Mesh>,
world_models:Vec<model::Model>, world_models:Vec<model::Model>,
modes:NormalizedModes, first_stage:Option<Stage>,
teleports:HashMap<AddBrush,&'a str>,
teleport_destinations:HashMap<&'a str,model::ModelId>,
} }
impl PartialMap1{ impl<'a> PartialMap1<'a>{
pub fn add_prop_meshes<'a>( pub fn add_prop_meshes(
self, self,
prop_meshes:Meshes, prop_meshes:Meshes,
)->PartialMap2{ )->PartialMap2<'a>{
PartialMap2{ PartialMap2{
attributes:self.attributes, attributes:self.attributes,
prop_meshes:prop_meshes.consume().collect(), prop_meshes:prop_meshes.consume().collect(),
prop_models:self.prop_models, prop_models:self.prop_models,
world_meshes:self.world_meshes, world_meshes:self.world_meshes,
world_models:self.world_models, world_models:self.world_models,
modes:self.modes, first_stage:self.first_stage,
teleports:self.teleports,
teleport_destinations:self.teleport_destinations,
} }
} }
} }
pub struct PartialMap2{ pub struct PartialMap2<'a>{
attributes:Vec<attr::CollisionAttributes>, attributes:Vec<attr::CollisionAttributes>,
prop_meshes:Vec<(model::MeshId,model::Mesh)>, prop_meshes:Vec<(model::MeshId,model::Mesh)>,
prop_models:Vec<model::Model>, prop_models:Vec<model::Model>,
world_meshes:Vec<model::Mesh>, world_meshes:Vec<model::Mesh>,
world_models:Vec<model::Model>, world_models:Vec<model::Model>,
modes:NormalizedModes, first_stage:Option<Stage>,
teleports:HashMap<AddBrush,&'a str>,
teleport_destinations:HashMap<&'a str,model::ModelId>,
} }
impl PartialMap2{ impl PartialMap2<'_>{
pub fn add_render_configs_and_textures( pub fn add_render_configs_and_textures(
mut self, mut self,
render_configs:RenderConfigs, render_configs:RenderConfigs,
@ -526,22 +553,70 @@ impl PartialMap2{
//merge mesh and model lists, flatten and remap all ids //merge mesh and model lists, flatten and remap all ids
let mesh_id_offset=self.world_meshes.len(); let mesh_id_offset=self.world_meshes.len();
println!("prop_meshes.len()={}",self.prop_meshes.len()); println!("prop_meshes.len()={}",self.prop_meshes.len());
let (mut prop_meshes,prop_mesh_id_map):(Vec<model::Mesh>,std::collections::HashMap<model::MeshId,model::MeshId>) let (mut prop_meshes,prop_mesh_id_map):(Vec<model::Mesh>,HashMap<model::MeshId,model::MeshId>)
=self.prop_meshes.into_iter().enumerate().map(|(new_mesh_id,(old_mesh_id,mesh))|{ =self.prop_meshes.into_iter().enumerate().map(|(new_mesh_id,(old_mesh_id,mesh))|{
(mesh,(old_mesh_id,model::MeshId::new((mesh_id_offset+new_mesh_id) as u32))) (mesh,(old_mesh_id,model::MeshId::new((mesh_id_offset+new_mesh_id) as u32)))
}).unzip(); }).unzip();
self.world_meshes.append(&mut prop_meshes); self.world_meshes.append(&mut prop_meshes);
//there is no modes or runtime behaviour with references to the model ids currently
//so just relentlessly cull them if the mesh is missing // cull models with a missing mesh
self.world_models.extend(self.prop_models.into_iter().filter_map(|mut model| let model_id_offset=self.world_models.len();
let mut prop_model_id_to_final_model_id=HashMap::new();
self.world_models.extend(self.prop_models.into_iter().enumerate().filter_map(|(prop_model_id,mut model)|
prop_mesh_id_map.get(&model.mesh).map(|&new_mesh_id|{ prop_mesh_id_map.get(&model.mesh).map(|&new_mesh_id|{
model.mesh=new_mesh_id; model.mesh=new_mesh_id;
model (prop_model_id,model)
}) })
).enumerate().map(|(model_id,(prop_model_id,model))|{
prop_model_id_to_final_model_id.insert(
model::ModelId::new(prop_model_id as u32),
model::ModelId::new((model_id_offset+model_id) as u32),
);
model
}));
//calculate teleports
let first_stage_spawn_model_id=self.first_stage.as_ref().unwrap().spawn();
let mut teleport_destinations=HashMap::new();
let stages={
let mut stages=self.teleport_destinations.iter().map(|(&target,&model_id)|(target,model_id)).collect::<Vec<_>>();
stages.sort_by_key(|&(target,_)|target);
self.first_stage.into_iter().chain(
stages.into_iter().enumerate().map(|(stage_id,(target,model_id))|{
let stage_id=modes::StageId::new(1+stage_id as u32);
teleport_destinations.insert(target,stage_id);
Stage::empty(model_id)
})
).collect()
};
let mut elements=HashMap::new();
for (teleport_model,target) in self.teleports{
if let Some(&stage_id)=teleport_destinations.get(target){
let model_id=match teleport_model{
AddBrush::Available(model_id)=>Some(model_id),
AddBrush::Deferred(model_id)=>prop_model_id_to_final_model_id.get(&model_id).copied(),
};
if let Some(model_id)=model_id{
elements.insert(model_id,modes::StageElement::new(
stage_id,
true,
modes::StageElementBehaviour::Teleport,
None,
));
}
}
}
let main_mode=NormalizedMode::new(Mode::new(
strafesnet_common::gameplay_style::StyleModifiers::default(),
first_stage_spawn_model_id,
HashMap::new(),
stages,
elements,
)); ));
//let mut models=Vec::new(); let modes=NormalizedModes::new(vec![main_mode]);
let (textures,render_configs)=render_configs.consume(); let (textures,render_configs)=render_configs.consume();
let (textures,texture_id_map):(Vec<Vec<u8>>,std::collections::HashMap<model::TextureId,model::TextureId>) let (textures,texture_id_map):(Vec<Vec<u8>>,HashMap<model::TextureId,model::TextureId>)
=textures.into_iter() =textures.into_iter()
//.filter_map(f) cull unused textures //.filter_map(f) cull unused textures
.enumerate().map(|(new_texture_id,(old_texture_id,Texture::ImageDDS(texture)))|{ .enumerate().map(|(new_texture_id,(old_texture_id,Texture::ImageDDS(texture)))|{
@ -555,7 +630,7 @@ impl PartialMap2{
render_config render_config
}).collect(); }).collect();
map::CompleteMap{ map::CompleteMap{
modes:self.modes, modes,
attributes:self.attributes, attributes:self.attributes,
meshes:self.world_meshes, meshes:self.world_meshes,
models:self.world_models, models:self.world_models,