586 lines
24 KiB
Rust
586 lines
24 KiB
Rust
use std::borrow::Cow;
|
|
|
|
use vbsp_entities_css::Entity;
|
|
|
|
use strafesnet_common::{map,model,integer,gameplay_attributes as attr};
|
|
use strafesnet_deferred_loader::deferred_loader::{MeshDeferredLoader,RenderConfigDeferredLoader};
|
|
use strafesnet_deferred_loader::mesh::Meshes;
|
|
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
|
|
use strafesnet_common::gameplay_modes::{NormalizedMode,NormalizedModes,Mode,Stage};
|
|
|
|
use crate::valve_transform;
|
|
|
|
fn ingest_vertex(
|
|
mb:&mut model::MeshBuilder,
|
|
world_position:vbsp::Vector,
|
|
texture_transform_u:glam::Vec4,
|
|
texture_transform_v:glam::Vec4,
|
|
normal:model::NormalId,
|
|
color:model::ColorId,
|
|
)->model::VertexId{
|
|
//world_model.origin seems to always be 0,0,0
|
|
let vertex_xyz=world_position.into();
|
|
let pos=mb.acquire_pos_id(valve_transform(vertex_xyz));
|
|
|
|
//calculate texture coordinates
|
|
let pos_4d=glam::Vec3::from_array(vertex_xyz).extend(1.0);
|
|
let tex=glam::vec2(texture_transform_u.dot(pos_4d),texture_transform_v.dot(pos_4d));
|
|
let tex=mb.acquire_tex_id(tex);
|
|
|
|
mb.acquire_vertex_id(model::IndexedVertex{
|
|
pos,
|
|
tex,
|
|
normal,
|
|
color,
|
|
})
|
|
}
|
|
|
|
fn add_brush<'a>(
|
|
mesh_deferred_loader:&mut MeshDeferredLoader<&'a str>,
|
|
world_models:&mut Vec<model::Model>,
|
|
prop_models:&mut Vec<model::Model>,
|
|
model:&'a str,
|
|
origin:vbsp::Vector,
|
|
rendercolor:vbsp::Color,
|
|
attributes:attr::CollisionAttributesId,
|
|
debug_info:model::DebugInfo,
|
|
){
|
|
let transform=integer::Planar64Affine3::from_translation(
|
|
valve_transform(origin.into())
|
|
);
|
|
let color=(glam::Vec3::from_array([
|
|
rendercolor.r as f32,
|
|
rendercolor.g as f32,
|
|
rendercolor.b as f32
|
|
])/255.0).extend(1.0);
|
|
|
|
match model.split_at(1){
|
|
// The first character of brush.model is '*'
|
|
("*",id_str)=>match id_str.parse(){
|
|
Ok(mesh_id)=>{
|
|
let mesh=model::MeshId::new(mesh_id);
|
|
world_models.push(
|
|
model::Model{mesh,attributes,transform,color,debug_info}
|
|
);
|
|
},
|
|
Err(e)=>{
|
|
println!("Brush model int parse error: {e} model={model}");
|
|
return;
|
|
},
|
|
},
|
|
_=>{
|
|
let mesh=mesh_deferred_loader.acquire_mesh_id(model);
|
|
prop_models.push(
|
|
model::Model{mesh,attributes,transform,color,debug_info}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn convert<'a>(
|
|
bsp:&'a crate::Bsp,
|
|
render_config_deferred_loader:&mut RenderConfigDeferredLoader<Cow<'a,str>>,
|
|
mesh_deferred_loader:&mut MeshDeferredLoader<&'a str>,
|
|
)->PartialMap1{
|
|
let bsp=bsp.as_ref();
|
|
//figure out real attributes later
|
|
let unique_attributes=vec![
|
|
attr::CollisionAttributes::Decoration,
|
|
attr::CollisionAttributes::contact_default(),
|
|
attr::CollisionAttributes::intersect_default(),
|
|
// ladder
|
|
attr::CollisionAttributes::Contact(
|
|
attr::ContactAttributes{
|
|
contacting:attr::ContactingAttributes{
|
|
contact_behaviour:Some(attr::ContactingBehaviour::Ladder(
|
|
attr::ContactingLadder{sticky:true}
|
|
))
|
|
},
|
|
general:attr::GeneralAttributes::default(),
|
|
}
|
|
),
|
|
// water
|
|
attr::CollisionAttributes::Intersect(
|
|
attr::IntersectAttributes{
|
|
intersecting:attr::IntersectingAttributes{
|
|
water:Some(attr::IntersectingWater{
|
|
viscosity:integer::Planar64::ONE,
|
|
density:integer::Planar64::ONE,
|
|
velocity:integer::vec3::ZERO,
|
|
}),
|
|
},
|
|
general:attr::GeneralAttributes::default(),
|
|
}
|
|
),
|
|
];
|
|
const ATTRIBUTE_DECORATION:attr::CollisionAttributesId=attr::CollisionAttributesId::new(0);
|
|
const ATTRIBUTE_CONTACT_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(1);
|
|
const ATTRIBUTE_INTERSECT_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(2);
|
|
const ATTRIBUTE_LADDER_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(3);
|
|
const ATTRIBUTE_WATER_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(4);
|
|
|
|
//declare all prop models to Loader
|
|
let mut prop_models=bsp.static_props().map(|prop|{
|
|
const DEG_TO_RAD:f32=std::f32::consts::TAU/360.0;
|
|
//get or create mesh_id
|
|
let mesh_id=mesh_deferred_loader.acquire_mesh_id(prop.model());
|
|
model::Model{
|
|
mesh:mesh_id,
|
|
attributes:ATTRIBUTE_DECORATION,
|
|
transform:integer::Planar64Affine3::new(
|
|
integer::mat3::try_from_f32_array_2d(
|
|
//TODO: figure this out
|
|
glam::Mat3A::from_euler(
|
|
glam::EulerRot::XYZ,
|
|
prop.angles.pitch*DEG_TO_RAD,
|
|
prop.angles.yaw*DEG_TO_RAD,
|
|
prop.angles.roll*DEG_TO_RAD
|
|
).to_cols_array_2d()
|
|
).unwrap(),
|
|
valve_transform(prop.origin.into()),
|
|
),
|
|
color:glam::Vec4::ONE,
|
|
debug_info:model::DebugInfo::Prop,
|
|
}
|
|
}).collect();
|
|
|
|
//TODO: make the main map one single mesh with a bunch of different physics groups and graphics groups
|
|
|
|
//the generated MeshIds in here will collide with the Loader Mesh Ids
|
|
//but I can't think of a good workaround other than just remapping one later.
|
|
let mut world_meshes:Vec<model::Mesh>=bsp.models().map(|world_model|{
|
|
let mut mb=model::MeshBuilder::new();
|
|
|
|
let color=mb.acquire_color_id(glam::Vec4::ONE);
|
|
let mut graphics_groups=Vec::new();
|
|
let mut render_id_to_graphics_group_id=std::collections::HashMap::new();
|
|
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 face_texture=face.texture();
|
|
let face_texture_data=face_texture.texture_data();
|
|
//this would be better as a 4x2 matrix
|
|
let texture_transform_u=glam::Vec4::from_array(face_texture.texture_transforms_u)/(face_texture_data.width as f32);
|
|
let texture_transform_v=glam::Vec4::from_array(face_texture.texture_transforms_v)/(face_texture_data.height as f32);
|
|
|
|
//normal
|
|
let normal=mb.acquire_normal_id(valve_transform(face.normal().into()));
|
|
let mut polygon_iter=face.vertex_positions().map(|vertex_position|
|
|
world_model.origin+vertex_position
|
|
);
|
|
let polygon_list=std::iter::from_fn(move||{
|
|
match (polygon_iter.next(),polygon_iter.next(),polygon_iter.next()){
|
|
(Some(v1),Some(v2),Some(v3))=>Some([v1,v2,v3]),
|
|
//ignore extra vertices, not sure what to do in this case, failing the whole conversion could be appropriate
|
|
_=>None,
|
|
}
|
|
}).map(|triplet|{
|
|
triplet.map(|world_position|
|
|
ingest_vertex(&mut mb,world_position,texture_transform_u,texture_transform_v,normal,color)
|
|
).to_vec()
|
|
}).collect();
|
|
if face.is_visible(){
|
|
//this automatically figures out what the texture is trying to do and creates
|
|
//a render config for it, and then returns the id to that render config
|
|
let render_id=render_config_deferred_loader.acquire_render_config_id(Some(Cow::Borrowed(face_texture_data.name())));
|
|
//deduplicate graphics groups by render id
|
|
let graphics_group_id=*render_id_to_graphics_group_id.entry(render_id).or_insert_with(||{
|
|
let graphics_group_id=graphics_groups.len();
|
|
graphics_groups.push(model::IndexedGraphicsGroup{
|
|
render:render_id,
|
|
groups:vec![],
|
|
});
|
|
graphics_group_id
|
|
});
|
|
graphics_groups[graphics_group_id].groups.push(polygon_group_id);
|
|
}
|
|
model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))
|
|
}).collect();
|
|
|
|
mb.build(polygon_groups,graphics_groups,vec![])
|
|
}).collect();
|
|
|
|
let mut found_spawn=None;
|
|
|
|
let mut world_models=Vec::new();
|
|
|
|
// the one and only world model 0
|
|
world_models.push(model::Model{
|
|
mesh:model::MeshId::new(0),
|
|
attributes:ATTRIBUTE_DECORATION,
|
|
transform:integer::Planar64Affine3::IDENTITY,
|
|
color:glam::Vec4::W,
|
|
debug_info:model::DebugInfo::World,
|
|
});
|
|
|
|
// THE CUBE OF DESTINY
|
|
let destination_mesh_id=model::MeshId::new(world_meshes.len() as u32);
|
|
world_meshes.push(crate::brush::unit_cube());
|
|
|
|
const WHITE:vbsp::Color=vbsp::Color{r:255,g:255,b:255};
|
|
const ENTITY_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=ATTRIBUTE_DECORATION;
|
|
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,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,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,debug_info)
|
|
};
|
|
}
|
|
match raw_ent.parse(){
|
|
Ok(Entity::AmbientGeneric(_ambient_generic))=>(),
|
|
Ok(Entity::Cycler(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::EnvBeam(_env_beam))=>(),
|
|
Ok(Entity::EnvBubbles(_env_bubbles))=>(),
|
|
Ok(Entity::EnvDetailController(_env_detail_controller))=>(),
|
|
Ok(Entity::EnvEmbers(_env_embers))=>(),
|
|
Ok(Entity::EnvEntityMaker(_env_entity_maker))=>(),
|
|
Ok(Entity::EnvExplosion(_env_explosion))=>(),
|
|
Ok(Entity::EnvFade(_env_fade))=>(),
|
|
Ok(Entity::EnvFire(_env_fire))=>(),
|
|
Ok(Entity::EnvFireTrail(_env_fire_trail))=>(),
|
|
Ok(Entity::EnvFiresource(_env_firesource))=>(),
|
|
Ok(Entity::EnvFogController(_env_fog_controller))=>(),
|
|
Ok(Entity::EnvHudhint(_env_hudhint))=>(),
|
|
Ok(Entity::EnvLaser(_env_laser))=>(),
|
|
Ok(Entity::EnvLightglow(_env_lightglow))=>(),
|
|
Ok(Entity::EnvPhysexplosion(_env_physexplosion))=>(),
|
|
Ok(Entity::EnvProjectedtexture(_env_projectedtexture))=>(),
|
|
Ok(Entity::EnvScreenoverlay(_env_screenoverlay))=>(),
|
|
Ok(Entity::EnvShake(_env_shake))=>(),
|
|
Ok(Entity::EnvShooter(_env_shooter))=>(),
|
|
Ok(Entity::EnvSmokestack(_env_smokestack))=>(),
|
|
Ok(Entity::EnvSoundscape(_env_soundscape))=>(),
|
|
Ok(Entity::EnvSoundscapeProxy(_env_soundscape_proxy))=>(),
|
|
Ok(Entity::EnvSoundscapeTriggerable(_env_soundscape_triggerable))=>(),
|
|
Ok(Entity::EnvSpark(_env_spark))=>(),
|
|
Ok(Entity::EnvSprite(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::EnvSpritetrail(_env_spritetrail))=>(),
|
|
Ok(Entity::EnvSteam(_env_steam))=>(),
|
|
Ok(Entity::EnvSun(_env_sun))=>(),
|
|
Ok(Entity::EnvTonemapController(_env_tonemap_controller))=>(),
|
|
Ok(Entity::EnvWind(_env_wind))=>(),
|
|
// trigger_teleport.filtername probably has to do with one of these
|
|
Ok(Entity::FilterActivatorClass(_filter_activator_class))=>(),
|
|
Ok(Entity::FilterActivatorName(_filter_activator_name))=>(),
|
|
Ok(Entity::FilterDamageType(_filter_damage_type))=>(),
|
|
Ok(Entity::FilterMulti(_filter_multi))=>(),
|
|
Ok(Entity::FuncAreaportal(_func_areaportal))=>(),
|
|
Ok(Entity::FuncAreaportalwindow(_func_areaportalwindow))=>(),
|
|
Ok(Entity::FuncBombTarget(_func_bomb_target))=>(),
|
|
Ok(Entity::FuncBreakable(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::FuncBreakableSurf(_func_breakable_surf))=>(),
|
|
Ok(Entity::FuncBrush(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::FuncButton(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::FuncBuyzone(_func_buyzone))=>(),
|
|
Ok(Entity::FuncClipVphysics(_func_clip_vphysics))=>(),
|
|
Ok(Entity::FuncConveyor(_func_conveyor))=>(),
|
|
// FuncDoor is Platform
|
|
Ok(Entity::FuncDoor(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::FuncDoorRotating(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::FuncDustcloud(_func_dustcloud))=>(),
|
|
Ok(Entity::FuncDustmotes(_func_dustmotes))=>(),
|
|
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,debug_info),
|
|
Ok(Entity::FuncLod(_func_lod))=>(),
|
|
Ok(Entity::FuncMonitor(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::FuncMovelinear(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::FuncOccluder(_func_occluder))=>(),
|
|
Ok(Entity::FuncPhysbox(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::FuncPhysboxMultiplayer(brush))=>ent_brush_default!(brush),
|
|
Ok(Entity::FuncPrecipitation(_func_precipitation))=>(),
|
|
Ok(Entity::FuncRotButton(brush))=>ent_brush_prop!(brush),
|
|
Ok(Entity::FuncRotating(brush))=>ent_brush_default!(brush),
|
|
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,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))=>(),
|
|
Ok(Entity::GameWeaponManager(_game_weapon_manager))=>(),
|
|
Ok(Entity::HostageEntity(_hostage_entity))=>(),
|
|
Ok(Entity::InfoCameraLink(_info_camera_link))=>(),
|
|
Ok(Entity::InfoLadder(_info_ladder))=>(),
|
|
Ok(Entity::InfoLightingRelative(_info_lighting_relative))=>(),
|
|
Ok(Entity::InfoMapParameters(_info_map_parameters))=>(),
|
|
Ok(Entity::InfoNode(_info_node))=>(),
|
|
Ok(Entity::InfoNodeHint(_info_node_hint))=>(),
|
|
Ok(Entity::InfoParticleSystem(_info_particle_system))=>(),
|
|
Ok(Entity::InfoPlayerCounterterrorist(spawn))=>found_spawn=Some(spawn.origin),
|
|
Ok(Entity::InfoPlayerLogo(_info_player_logo))=>(),
|
|
Ok(Entity::InfoPlayerStart(_info_player_start))=>(),
|
|
Ok(Entity::InfoPlayerTerrorist(spawn))=>found_spawn=Some(spawn.origin),
|
|
Ok(Entity::InfoTarget(_info_target))=>(),
|
|
// InfoTeleportDestination is Spawn#
|
|
Ok(Entity::InfoTeleportDestination(_info_teleport_destination))=>(),
|
|
Ok(Entity::Infodecal(_infodecal))=>(),
|
|
Ok(Entity::KeyframeRope(_keyframe_rope))=>(),
|
|
Ok(Entity::Light(_light))=>(),
|
|
Ok(Entity::LightEnvironment(_light_environment))=>(),
|
|
Ok(Entity::LightSpot(_light_spot))=>(),
|
|
Ok(Entity::LogicAuto(_logic_auto))=>(),
|
|
Ok(Entity::LogicBranch(_logic_branch))=>(),
|
|
Ok(Entity::LogicCase(_logic_case))=>(),
|
|
Ok(Entity::LogicCompare(_logic_compare))=>(),
|
|
Ok(Entity::LogicMeasureMovement(_logic_measure_movement))=>(),
|
|
Ok(Entity::LogicRelay(_logic_relay))=>(),
|
|
Ok(Entity::LogicTimer(_logic_timer))=>(),
|
|
Ok(Entity::MathCounter(_math_counter))=>(),
|
|
Ok(Entity::MoveRope(_move_rope))=>(),
|
|
Ok(Entity::PathTrack(_path_track))=>(),
|
|
Ok(Entity::PhysBallsocket(_phys_ballsocket))=>(),
|
|
Ok(Entity::PhysConstraint(_phys_constraint))=>(),
|
|
Ok(Entity::PhysConstraintsystem(_phys_constraintsystem))=>(),
|
|
Ok(Entity::PhysHinge(_phys_hinge))=>(),
|
|
Ok(Entity::PhysKeepupright(_phys_keepupright))=>(),
|
|
Ok(Entity::PhysLengthconstraint(_phys_lengthconstraint))=>(),
|
|
Ok(Entity::PhysPulleyconstraint(_phys_pulleyconstraint))=>(),
|
|
Ok(Entity::PhysRagdollconstraint(_phys_ragdollconstraint))=>(),
|
|
Ok(Entity::PhysRagdollmagnet(_phys_ragdollmagnet))=>(),
|
|
Ok(Entity::PhysThruster(_phys_thruster))=>(),
|
|
Ok(Entity::PhysTorque(_phys_torque))=>(),
|
|
Ok(Entity::PlayerSpeedmod(_player_speedmod))=>(),
|
|
Ok(Entity::PlayerWeaponstrip(_player_weaponstrip))=>(),
|
|
Ok(Entity::PointCamera(_point_camera))=>(),
|
|
Ok(Entity::PointClientcommand(_point_clientcommand))=>(),
|
|
Ok(Entity::PointDevshotCamera(_point_devshot_camera))=>(),
|
|
Ok(Entity::PointServercommand(_point_servercommand))=>(),
|
|
Ok(Entity::PointSpotlight(_point_spotlight))=>(),
|
|
Ok(Entity::PointSurroundtest(_point_surroundtest))=>(),
|
|
Ok(Entity::PointTemplate(_point_template))=>(),
|
|
Ok(Entity::PointTesla(_point_tesla))=>(),
|
|
Ok(Entity::PointViewcontrol(_point_viewcontrol))=>(),
|
|
Ok(Entity::PropDoorRotating(brush))=>ent_brush_prop!(brush),
|
|
Ok(Entity::PropDynamic(brush))=>ent_brush_prop!(brush),
|
|
Ok(Entity::PropDynamicOverride(brush))=>ent_brush_prop!(brush),
|
|
Ok(Entity::PropPhysics(brush))=>ent_brush_prop!(brush),
|
|
Ok(Entity::PropPhysicsMultiplayer(brush))=>ent_brush_prop!(brush),
|
|
Ok(Entity::PropPhysicsOverride(brush))=>ent_brush_prop!(brush),
|
|
Ok(Entity::PropRagdoll(brush))=>ent_brush_prop!(brush),
|
|
Ok(Entity::ShadowControl(_shadow_control))=>(),
|
|
Ok(Entity::SkyCamera(_sky_camera))=>(),
|
|
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,debug_info),
|
|
Ok(Entity::TriggerOnce(brush))=>ent_brush_trigger!(brush),
|
|
Ok(Entity::TriggerProximity(brush))=>ent_brush_trigger!(brush),
|
|
// TriggerPush is booster
|
|
Ok(Entity::TriggerPush(brush))=>ent_brush_trigger!(brush),
|
|
Ok(Entity::TriggerSoundscape(brush))=>ent_brush_trigger!(brush),
|
|
// 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,debug_info),
|
|
Ok(Entity::TriggerVphysicsMotion(brush))=>ent_brush_trigger!(brush),
|
|
Ok(Entity::TriggerWind(brush))=>ent_brush_trigger!(brush),
|
|
Ok(Entity::WaterLodControl(_water_lod_control))=>(),
|
|
Ok(Entity::WeaponAk47(_weapon_ak47))=>(),
|
|
Ok(Entity::WeaponAwp(_weapon_awp))=>(),
|
|
Ok(Entity::WeaponDeagle(_weapon_deagle))=>(),
|
|
Ok(Entity::WeaponElite(_weapon_elite))=>(),
|
|
Ok(Entity::WeaponFamas(_weapon_famas))=>(),
|
|
Ok(Entity::WeaponFiveseven(_weapon_fiveseven))=>(),
|
|
Ok(Entity::WeaponFlashbang(_weapon_flashbang))=>(),
|
|
Ok(Entity::WeaponG3sg1(_weapon_g3sg1))=>(),
|
|
Ok(Entity::WeaponGlock(_weapon_glock))=>(),
|
|
Ok(Entity::WeaponHegrenade(_weapon_hegrenade))=>(),
|
|
Ok(Entity::WeaponKnife(_weapon_knife))=>(),
|
|
Ok(Entity::WeaponM249(_weapon_m249))=>(),
|
|
Ok(Entity::WeaponM3(_weapon_m3))=>(),
|
|
Ok(Entity::WeaponM4a1(_weapon_m4a1))=>(),
|
|
Ok(Entity::WeaponMac10(_weapon_mac10))=>(),
|
|
Ok(Entity::WeaponP228(_weapon_p228))=>(),
|
|
Ok(Entity::WeaponP90(_weapon_p90))=>(),
|
|
Ok(Entity::WeaponScout(_weapon_scout))=>(),
|
|
Ok(Entity::WeaponSg550(_weapon_sg550))=>(),
|
|
Ok(Entity::WeaponSmokegrenade(_weapon_smokegrenade))=>(),
|
|
Ok(Entity::WeaponTmp(_weapon_tmp))=>(),
|
|
Ok(Entity::WeaponUmp45(_weapon_ump45))=>(),
|
|
Ok(Entity::WeaponUsp(_weapon_usp))=>(),
|
|
Ok(Entity::WeaponXm1014(_weapon_xm1014))=>(),
|
|
Ok(Entity::Worldspawn(_worldspawn))=>(),
|
|
Err(e)=>{
|
|
println!("Bsp Entity parse error: {e}");
|
|
},
|
|
_=>(),
|
|
}
|
|
}
|
|
|
|
// physics models
|
|
for brush in &bsp.brushes{
|
|
const RELEVANT:vbsp::BrushFlags=
|
|
vbsp::BrushFlags::SOLID
|
|
.union(vbsp::BrushFlags::PLAYERCLIP)
|
|
.union(vbsp::BrushFlags::WATER)
|
|
.union(vbsp::BrushFlags::MOVEABLE)
|
|
.union(vbsp::BrushFlags::LADDER);
|
|
if !brush.flags.intersects(RELEVANT){
|
|
continue;
|
|
}
|
|
let is_ladder=brush.flags.contains(vbsp::BrushFlags::LADDER);
|
|
let is_water=brush.flags.contains(vbsp::BrushFlags::WATER);
|
|
let attributes=match (is_ladder,is_water){
|
|
(true,false)=>ATTRIBUTE_LADDER_DEFAULT,
|
|
(false,true)=>ATTRIBUTE_WATER_DEFAULT,
|
|
(false,false)=>ATTRIBUTE_CONTACT_DEFAULT,
|
|
(true,true)=>{
|
|
// water ladder? wtf
|
|
println!("brush is a water ladder o_o defaulting to ladder");
|
|
ATTRIBUTE_LADDER_DEFAULT
|
|
}
|
|
};
|
|
let mesh_result=crate::brush::brush_to_mesh(bsp,brush);
|
|
match mesh_result{
|
|
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,
|
|
transform:integer::Planar64Affine3::new(
|
|
integer::mat3::identity(),
|
|
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}"),
|
|
}
|
|
}
|
|
|
|
let mut modes_list=Vec::new();
|
|
if let Some(spawn_point)=found_spawn{
|
|
// 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(spawn_point.into())),
|
|
color:glam::Vec4::W,
|
|
debug_info:model::DebugInfo::World,
|
|
});
|
|
|
|
let first_stage=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{
|
|
attributes:unique_attributes,
|
|
world_meshes,
|
|
prop_models,
|
|
world_models,
|
|
modes:NormalizedModes::new(modes_list),
|
|
}
|
|
}
|
|
|
|
//partially constructed map types
|
|
pub struct PartialMap1{
|
|
attributes:Vec<attr::CollisionAttributes>,
|
|
prop_models:Vec<model::Model>,
|
|
world_meshes:Vec<model::Mesh>,
|
|
world_models:Vec<model::Model>,
|
|
modes:NormalizedModes,
|
|
}
|
|
impl PartialMap1{
|
|
pub fn add_prop_meshes<'a>(
|
|
self,
|
|
prop_meshes:Meshes,
|
|
)->PartialMap2{
|
|
PartialMap2{
|
|
attributes:self.attributes,
|
|
prop_meshes:prop_meshes.consume().collect(),
|
|
prop_models:self.prop_models,
|
|
world_meshes:self.world_meshes,
|
|
world_models:self.world_models,
|
|
modes:self.modes,
|
|
}
|
|
}
|
|
}
|
|
pub struct PartialMap2{
|
|
attributes:Vec<attr::CollisionAttributes>,
|
|
prop_meshes:Vec<(model::MeshId,model::Mesh)>,
|
|
prop_models:Vec<model::Model>,
|
|
world_meshes:Vec<model::Mesh>,
|
|
world_models:Vec<model::Model>,
|
|
modes:NormalizedModes,
|
|
}
|
|
impl PartialMap2{
|
|
pub fn add_render_configs_and_textures(
|
|
mut self,
|
|
render_configs:RenderConfigs,
|
|
)->map::CompleteMap{
|
|
//merge mesh and model lists, flatten and remap all ids
|
|
let mesh_id_offset=self.world_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>)
|
|
=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)))
|
|
}).unzip();
|
|
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
|
|
self.world_models.extend(self.prop_models.into_iter().filter_map(|mut model|
|
|
prop_mesh_id_map.get(&model.mesh).map(|&new_mesh_id|{
|
|
model.mesh=new_mesh_id;
|
|
model
|
|
})
|
|
));
|
|
//let mut models=Vec::new();
|
|
let (textures,render_configs)=render_configs.consume();
|
|
let (textures,texture_id_map):(Vec<Vec<u8>>,std::collections::HashMap<model::TextureId,model::TextureId>)
|
|
=textures.into_iter()
|
|
//.filter_map(f) cull unused textures
|
|
.enumerate().map(|(new_texture_id,(old_texture_id,Texture::ImageDDS(texture)))|{
|
|
(texture,(old_texture_id,model::TextureId::new(new_texture_id as u32)))
|
|
}).unzip();
|
|
let render_configs=render_configs.into_iter().map(|(_render_config_id,mut render_config)|{
|
|
//this may generate duplicate no-texture render configs but idc
|
|
render_config.texture=render_config.texture.and_then(|texture_id|
|
|
texture_id_map.get(&texture_id).copied()
|
|
);
|
|
render_config
|
|
}).collect();
|
|
map::CompleteMap{
|
|
modes:self.modes,
|
|
attributes:self.attributes,
|
|
meshes:self.world_meshes,
|
|
models:self.world_models,
|
|
textures,
|
|
render_configs,
|
|
}
|
|
}
|
|
}
|