Compare commits
1 Commits
bsp-gameme
...
to-unsigne
Author | SHA1 | Date | |
---|---|---|---|
b2820ac05a |
Cargo.lock
engine/physics/src
integration-testing/src
lib
bsp_loader
common
fixed_wide
ratio_ops
src
rbx_loader
snf
src
map-tool
498
Cargo.lock
generated
498
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -982,7 +982,10 @@ impl PhysicsContext<'_>{
|
||||
impl PhysicsData{
|
||||
/// 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();
|
||||
let mut modes=map.modes.clone();
|
||||
for mode in &mut modes.modes{
|
||||
mode.denormalize_data();
|
||||
}
|
||||
let mut used_contact_attributes=Vec::new();
|
||||
let mut used_intersect_attributes=Vec::new();
|
||||
|
||||
@ -1811,18 +1814,14 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
},
|
||||
Instruction::Mode(ModeInstruction::Restart(mode_id))=>{
|
||||
//teleport to mode start zone
|
||||
let mut spawn_point=vec3::ZERO;
|
||||
let mode=data.modes.get_mode(mode_id);
|
||||
if let Some(mode)=mode{
|
||||
// set style
|
||||
state.style=mode.get_style().clone();
|
||||
let spawn_point=mode.and_then(|mode|
|
||||
//TODO: spawn at the bottom of the start zone plus the hitbox size
|
||||
//TODO: set camera angles to face the same way as the start zone
|
||||
if let Some(transform)=data.models.get_model_transform(mode.get_start()){
|
||||
// NOTE: this value may be 0,0,0 on source maps
|
||||
spawn_point=transform.vertex.translation;
|
||||
}
|
||||
}
|
||||
//TODO: set camera andles to face the same way as the start zone
|
||||
data.models.get_model_transform(mode.get_start().into()).map(|transform|
|
||||
transform.vertex.translation
|
||||
)
|
||||
).unwrap_or(vec3::ZERO);
|
||||
set_position(spawn_point,&mut state.move_state,&mut state.body,&mut state.touching,&mut state.run,&mut state.mode_state,mode,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,state.time);
|
||||
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::ZERO);
|
||||
state.set_move_state(data,MoveState::Air);
|
||||
@ -1832,9 +1831,6 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
Instruction::Mode(ModeInstruction::Spawn(mode_id,stage_id))=>{
|
||||
//spawn at a particular stage
|
||||
if let Some(mode)=data.modes.get_mode(mode_id){
|
||||
// set style
|
||||
state.style=mode.get_style().clone();
|
||||
// teleport to stage in mode
|
||||
if let Some(stage)=mode.get_stage(stage_id){
|
||||
let _=teleport_to_spawn(
|
||||
stage.spawn(),
|
||||
|
@ -1,7 +1,5 @@
|
||||
|
||||
use std::io::Cursor;
|
||||
use std::path::Path;
|
||||
use std::time::Instant;
|
||||
use std::{io::{Cursor,Read},path::Path};
|
||||
|
||||
use strafesnet_physics::physics::{PhysicsData,PhysicsState,PhysicsContext};
|
||||
|
||||
@ -39,7 +37,9 @@ impl From<strafesnet_snf::bot::Error> for ReplayError{
|
||||
}
|
||||
|
||||
fn read_entire_file(path:impl AsRef<Path>)->Result<Cursor<Vec<u8>>,std::io::Error>{
|
||||
let data=std::fs::read(path)?;
|
||||
let mut file=std::fs::File::open(path)?;
|
||||
let mut data=Vec::new();
|
||||
file.read_to_end(&mut data)?;
|
||||
Ok(Cursor::new(data))
|
||||
}
|
||||
|
||||
@ -72,12 +72,7 @@ enum DeterminismResult{
|
||||
Deterministic,
|
||||
NonDeterministic,
|
||||
}
|
||||
struct SegmentResult{
|
||||
determinism:DeterminismResult,
|
||||
ticks:u64,
|
||||
nanoseconds:u64,
|
||||
}
|
||||
fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsData)->SegmentResult{
|
||||
fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsData)->DeterminismResult{
|
||||
// create default physics state
|
||||
let mut physics_deterministic=PhysicsState::default();
|
||||
// create a second physics state
|
||||
@ -87,9 +82,6 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
|
||||
println!("simulating...");
|
||||
|
||||
let mut non_idle_count=0;
|
||||
let instruction_count=bot.instructions.len();
|
||||
|
||||
let start=Instant::now();
|
||||
|
||||
for (i,ins) in bot.instructions.into_iter().enumerate(){
|
||||
let state_deterministic=physics_deterministic.clone();
|
||||
@ -105,7 +97,6 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
|
||||
let b0=physics_deterministic.camera_body();
|
||||
let b1=physics_filtered.camera_body();
|
||||
if b0.position!=b1.position{
|
||||
let nanoseconds=start.elapsed().as_nanos() as u64;
|
||||
println!("desync at instruction #{}",i);
|
||||
println!("non idle instructions completed={non_idle_count}");
|
||||
println!("instruction #{i}={:?}",other);
|
||||
@ -113,16 +104,11 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
|
||||
println!("filtered state0:\n{state_filtered:?}");
|
||||
println!("deterministic state1:\n{:?}",physics_deterministic);
|
||||
println!("filtered state1:\n{:?}",physics_filtered);
|
||||
return SegmentResult{
|
||||
determinism:DeterminismResult::NonDeterministic,
|
||||
ticks:1+i as u64+non_idle_count,
|
||||
nanoseconds,
|
||||
};
|
||||
return DeterminismResult::NonDeterministic;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
let nanoseconds=start.elapsed().as_nanos() as u64;
|
||||
match physics_deterministic.get_finish_time(){
|
||||
Some(time)=>println!("[with idle] finish time:{}",time),
|
||||
None=>println!("[with idle] simulation did not end in finished state"),
|
||||
@ -131,14 +117,9 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
|
||||
Some(time)=>println!("[filtered] finish time:{}",time),
|
||||
None=>println!("[filtered] simulation did not end in finished state"),
|
||||
}
|
||||
SegmentResult{
|
||||
determinism:DeterminismResult::Deterministic,
|
||||
ticks:instruction_count as u64+non_idle_count,
|
||||
nanoseconds,
|
||||
}
|
||||
|
||||
DeterminismResult::Deterministic
|
||||
}
|
||||
type ThreadResult=Result<Option<SegmentResult>,ReplayError>;
|
||||
type ThreadResult=Result<Option<DeterminismResult>,ReplayError>;
|
||||
fn read_and_run(file_path:std::path::PathBuf,physics_data:&PhysicsData)->ThreadResult{
|
||||
let data=read_entire_file(file_path.as_path())?;
|
||||
let bot=strafesnet_snf::read_bot(data)?.read_all()?;
|
||||
@ -217,18 +198,10 @@ fn test_determinism()->Result<(),ReplayError>{
|
||||
invalid:u32,
|
||||
error:u32,
|
||||
}
|
||||
let mut nanoseconds=0;
|
||||
let mut ticks=0;
|
||||
let Totals{deterministic,nondeterministic,invalid,error}=thread_results.into_iter().fold(Totals::default(),|mut totals,result|{
|
||||
match result{
|
||||
Ok(Some(segment_result))=>{
|
||||
ticks+=segment_result.ticks;
|
||||
nanoseconds+=segment_result.nanoseconds;
|
||||
match segment_result.determinism{
|
||||
DeterminismResult::Deterministic=>totals.deterministic+=1,
|
||||
DeterminismResult::NonDeterministic=>totals.nondeterministic+=1,
|
||||
}
|
||||
},
|
||||
Ok(Some(DeterminismResult::Deterministic))=>totals.deterministic+=1,
|
||||
Ok(Some(DeterminismResult::NonDeterministic))=>totals.nondeterministic+=1,
|
||||
Ok(None)=>totals.invalid+=1,
|
||||
Err(_)=>totals.error+=1,
|
||||
}
|
||||
@ -239,7 +212,6 @@ fn test_determinism()->Result<(),ReplayError>{
|
||||
println!("nondeterministic={nondeterministic}");
|
||||
println!("invalid={invalid}");
|
||||
println!("error={error}");
|
||||
println!("average ticks/s per core: {}",ticks*1_000_000_000/nanoseconds);
|
||||
|
||||
assert!(nondeterministic==0);
|
||||
assert!(invalid==0);
|
||||
|
@ -13,7 +13,6 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
||||
glam = "0.30.0"
|
||||
strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" }
|
||||
strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader", registry = "strafesnet" }
|
||||
vbsp = "0.8.0"
|
||||
vbsp-entities-css = "0.6.0"
|
||||
vbsp = "0.6.0"
|
||||
vmdl = "0.2.0"
|
||||
vpk = "0.3.0"
|
||||
vpk = "0.2.0"
|
||||
|
@ -1,342 +0,0 @@
|
||||
use strafesnet_common::integer::Planar64;
|
||||
use strafesnet_common::{model,integer};
|
||||
use strafesnet_common::integer::{vec3::Vector3,Fixed,Ratio};
|
||||
|
||||
use crate::{valve_transform_normal,valve_transform_dist};
|
||||
|
||||
#[derive(Hash,Eq,PartialEq)]
|
||||
struct Face{
|
||||
normal:integer::Planar64Vec3,
|
||||
dot:integer::Planar64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Faces{
|
||||
faces:Vec<Vec<integer::Planar64Vec3>>,
|
||||
}
|
||||
|
||||
fn solve3(c0:&Face,c1:&Face,c2:&Face)->Option<Ratio<Vector3<Fixed<3,96>>,Fixed<3,96>>>{
|
||||
let n0_n1=c0.normal.cross(c1.normal);
|
||||
let det=c2.normal.dot(n0_n1);
|
||||
if det.abs().is_zero(){
|
||||
return None;
|
||||
}
|
||||
Some((
|
||||
c1.normal.cross(c2.normal)*c0.dot
|
||||
+c2.normal.cross(c0.normal)*c1.dot
|
||||
+c0.normal.cross(c1.normal)*c2.dot
|
||||
)/det)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PlanesToFacesError{
|
||||
InitFace1,
|
||||
InitFace2,
|
||||
InitIntersection,
|
||||
FindNewIntersection,
|
||||
EmptyFaces,
|
||||
InfiniteLoop1,
|
||||
InfiniteLoop2,
|
||||
}
|
||||
impl std::fmt::Display for PlanesToFacesError{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
write!(f,"{self:?}")
|
||||
}
|
||||
}
|
||||
impl core::error::Error for PlanesToFacesError{}
|
||||
|
||||
fn planes_to_faces(face_list:std::collections::HashSet<Face>)->Result<Faces,PlanesToFacesError>{
|
||||
let mut faces=Vec::new();
|
||||
// for each face, determine one edge at a time until you complete the face
|
||||
'face: for face0 in &face_list{
|
||||
// 1. find first edge
|
||||
// 2. follow edges around face
|
||||
|
||||
// === finding first edge ===
|
||||
// 1. pick the most perpendicular set of 3 faces
|
||||
// 2. check if any faces occlude the intersection
|
||||
// 3. use this test to replace left and right alternating until they are not occluded
|
||||
|
||||
// find the most perpendicular face to face0
|
||||
let mut face1=face_list.iter().min_by_key(|&p|{
|
||||
face0.normal.dot(p.normal).abs()
|
||||
}).ok_or(PlanesToFacesError::InitFace1)?;
|
||||
|
||||
// direction of edge formed by face0 x face1
|
||||
let edge_dir=face0.normal.cross(face1.normal);
|
||||
|
||||
// find the most perpendicular face to both face0 and face1
|
||||
let mut face2=face_list.iter().max_by_key(|&p|{
|
||||
// find the best *oriented* face (no .abs())
|
||||
edge_dir.dot(p.normal)
|
||||
}).ok_or(PlanesToFacesError::InitFace2)?;
|
||||
|
||||
let mut detect_loop=200u8;
|
||||
|
||||
let mut intersection=solve3(face0,face1,face2).ok_or(PlanesToFacesError::InitIntersection)?;
|
||||
|
||||
// repeatedly update face1, face2 until all faces form part of the convex solid
|
||||
'find: loop{
|
||||
detect_loop=detect_loop.checked_sub(1).ok_or(PlanesToFacesError::InfiniteLoop1)?;
|
||||
// test if any *other* faces occlude the intersection
|
||||
for new_face in &face_list{
|
||||
// new face occludes intersection point
|
||||
if (new_face.dot.fix_2()/Planar64::ONE).lt_ratio(new_face.normal.dot(intersection.num)/intersection.den){
|
||||
// replace one of the faces with the new face
|
||||
// dont' try to replace face0 because we are exploring that face in particular
|
||||
if let Some(new_intersection)=solve3(face0,new_face,face2){
|
||||
// face1 does not occlude (or intersect) the new intersection
|
||||
if (face1.dot.fix_2()/Planar64::ONE).gt_ratio(face1.normal.dot(new_intersection.num)/new_intersection.den){
|
||||
face1=new_face;
|
||||
intersection=new_intersection;
|
||||
continue 'find;
|
||||
}
|
||||
}
|
||||
if let Some(new_intersection)=solve3(face0,face1,new_face){
|
||||
// face2 does not occlude (or intersect) the new intersection
|
||||
if (face2.dot.fix_2()/Planar64::ONE).gt_ratio(face2.normal.dot(new_intersection.num)/new_intersection.den){
|
||||
face2=new_face;
|
||||
intersection=new_intersection;
|
||||
continue 'find;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we have found a set of faces for which the intersection is on the convex solid
|
||||
break 'find;
|
||||
}
|
||||
|
||||
// check if face0 must go, meaning it is a degenerate face and does not contribute anything to the convex solid
|
||||
for new_face in &face_list{
|
||||
if core::ptr::eq(face0,new_face){
|
||||
continue;
|
||||
}
|
||||
if core::ptr::eq(face1,new_face){
|
||||
continue;
|
||||
}
|
||||
if core::ptr::eq(face2,new_face){
|
||||
continue;
|
||||
}
|
||||
// new_face occludes intersection meaning intersection is not on convex solid and face0 is degenrate
|
||||
if (new_face.dot.fix_2()/Planar64::ONE).lt_ratio(new_face.normal.dot(intersection.num)/intersection.den){
|
||||
// abort! reject face0 entirely
|
||||
continue 'face;
|
||||
}
|
||||
}
|
||||
|
||||
// === follow edges around face ===
|
||||
// Note that we chose face2 such that the 3 faces create a particular winding order.
|
||||
// If we choose a consistent face to follow (face1, face2) it will always wind with a consistent chirality
|
||||
|
||||
let mut detect_loop=200u8;
|
||||
|
||||
// keep looping until we meet this face again
|
||||
let face1=face1;
|
||||
let mut face=Vec::new();
|
||||
loop{
|
||||
// push point onto vertices
|
||||
// problem: this may push a vertex that does not fit in the fixed point range and is thus meaningless
|
||||
face.push(intersection.divide().fix_1());
|
||||
|
||||
// we looped back around to face1, we're done!
|
||||
if core::ptr::eq(face1,face2){
|
||||
break;
|
||||
}
|
||||
|
||||
// the measure
|
||||
let edge_dir=face0.normal.cross(face2.normal);
|
||||
|
||||
// the dot product to beat
|
||||
let d_intersection=edge_dir.dot(intersection.num)/intersection.den;
|
||||
|
||||
// find the next face moving clockwise around face0
|
||||
let (new_face,new_intersection,_)=face_list.iter().filter_map(|new_face|{
|
||||
// ignore faces that are part of the current edge
|
||||
if core::ptr::eq(face0,new_face)
|
||||
|core::ptr::eq(face2,new_face){
|
||||
return None;
|
||||
}
|
||||
let new_intersection=solve3(face0,face2,new_face)?;
|
||||
|
||||
// the d value must be larger
|
||||
let d_new_intersection=edge_dir.dot(new_intersection.num)/new_intersection.den;
|
||||
if d_new_intersection.le_ratio(d_intersection){
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((new_face,new_intersection,d_new_intersection))
|
||||
}).min_by_key(|&(_,_,d)|d).ok_or(PlanesToFacesError::FindNewIntersection)?;
|
||||
|
||||
face2=new_face;
|
||||
intersection=new_intersection;
|
||||
|
||||
detect_loop=detect_loop.checked_sub(1).ok_or(PlanesToFacesError::InfiniteLoop2)?;
|
||||
}
|
||||
|
||||
faces.push(face);
|
||||
}
|
||||
|
||||
if faces.is_empty(){
|
||||
Err(PlanesToFacesError::EmptyFaces)
|
||||
}else{
|
||||
Ok(Faces{
|
||||
faces,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub enum BrushToMeshError{
|
||||
SliceBrushSides,
|
||||
MissingPlane,
|
||||
InvalidFaceCount{
|
||||
count:usize,
|
||||
},
|
||||
InvalidPlanes(PlanesToFacesError),
|
||||
}
|
||||
impl std::fmt::Display for BrushToMeshError{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
write!(f,"{self:?}")
|
||||
}
|
||||
}
|
||||
impl core::error::Error for BrushToMeshError{}
|
||||
|
||||
pub fn faces_to_mesh(faces:Vec<Vec<integer::Planar64Vec3>>)->model::Mesh{
|
||||
// generate the mesh
|
||||
let mut mb=model::MeshBuilder::new();
|
||||
let color=mb.acquire_color_id(glam::Vec4::ONE);
|
||||
let tex=mb.acquire_tex_id(glam::Vec2::ZERO);
|
||||
// normals are ignored by physics
|
||||
let normal=mb.acquire_normal_id(integer::vec3::ZERO);
|
||||
|
||||
let polygon_list=faces.into_iter().map(|face|{
|
||||
face.into_iter().map(|pos|{
|
||||
let pos=mb.acquire_pos_id(pos);
|
||||
mb.acquire_vertex_id(model::IndexedVertex{
|
||||
pos,
|
||||
tex,
|
||||
normal,
|
||||
color,
|
||||
})
|
||||
}).collect()
|
||||
}).collect();
|
||||
|
||||
let polygon_groups=vec![model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))];
|
||||
let physics_groups=vec![model::IndexedPhysicsGroup{
|
||||
groups:vec![model::PolygonGroupId::new(0)],
|
||||
}];
|
||||
let graphics_groups=vec![];
|
||||
|
||||
mb.build(polygon_groups,graphics_groups,physics_groups)
|
||||
}
|
||||
|
||||
pub fn brush_to_mesh(bsp:&vbsp::Bsp,brush:&vbsp::Brush)->Result<model::Mesh,BrushToMeshError>{
|
||||
let brush_start_idx=brush.brush_side as usize;
|
||||
let sides_range=brush_start_idx..brush_start_idx+brush.num_brush_sides as usize;
|
||||
let sides=bsp.brush_sides.get(sides_range).ok_or(BrushToMeshError::SliceBrushSides)?;
|
||||
let face_list=sides.iter().map(|side|{
|
||||
// The so-called tumor brushes have TRIGGER bit set
|
||||
// but also ignore visleaf hint and skip sides
|
||||
const TUMOR:vbsp::TextureFlags=vbsp::TextureFlags::HINT.union(vbsp::TextureFlags::SKIP).union(vbsp::TextureFlags::TRIGGER);
|
||||
if let Some(texture_info)=bsp.texture_info(side.texture_info as usize){
|
||||
if texture_info.flags.intersects(TUMOR){
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let plane=bsp.plane(side.plane as usize)?;
|
||||
Some(Face{
|
||||
normal:valve_transform_normal(plane.normal.into()),
|
||||
dot:valve_transform_dist(plane.dist.into()),
|
||||
})
|
||||
}).collect::<Option<std::collections::HashSet<_>>>().ok_or(BrushToMeshError::MissingPlane)?;
|
||||
|
||||
if face_list.len()<4{
|
||||
return Err(BrushToMeshError::InvalidFaceCount{count:face_list.len()});
|
||||
}
|
||||
|
||||
let faces=planes_to_faces(face_list).map_err(BrushToMeshError::InvalidPlanes)?;
|
||||
|
||||
let mesh=faces_to_mesh(faces.faces);
|
||||
|
||||
Ok(mesh)
|
||||
}
|
||||
|
||||
pub fn unit_cube()->model::Mesh{
|
||||
let face_list=[
|
||||
Face{normal:integer::vec3::X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Z,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Z,dot:Planar64::ONE},
|
||||
].into_iter().collect();
|
||||
let faces=planes_to_faces(face_list).unwrap();
|
||||
let mesh=faces_to_mesh(faces.faces);
|
||||
mesh
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_cube(){
|
||||
let face_list=[
|
||||
Face{normal:integer::vec3::X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Z,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Z,dot:Planar64::ONE},
|
||||
].into_iter().collect();
|
||||
let faces=planes_to_faces(face_list).unwrap();
|
||||
assert_eq!(faces.faces.len(),6);
|
||||
dbg!(faces);
|
||||
}
|
||||
#[test]
|
||||
fn test_cube_with_degernate_face(){
|
||||
let face_list=[
|
||||
Face{normal:integer::vec3::X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Z,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Z,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Z,dot:Planar64::EPSILON},
|
||||
].into_iter().collect();
|
||||
let faces=planes_to_faces(face_list).unwrap();
|
||||
assert_eq!(faces.faces.len(),6);
|
||||
dbg!(faces);
|
||||
}
|
||||
#[test]
|
||||
fn test_cube_with_degernate_face2(){
|
||||
let face_list=[
|
||||
Face{normal:integer::vec3::X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Z,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Z,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_X+integer::vec3::NEG_Z,dot:-Planar64::EPSILON},
|
||||
].into_iter().collect();
|
||||
let faces=planes_to_faces(face_list).unwrap();
|
||||
assert_eq!(faces.faces.len(),5);
|
||||
dbg!(faces);
|
||||
}
|
||||
#[test]
|
||||
fn test_cube_with_degernate_face3(){
|
||||
let face_list=[
|
||||
Face{normal:integer::vec3::X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::Z,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_X,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Y,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_Z,dot:Planar64::ONE},
|
||||
Face{normal:integer::vec3::NEG_X+integer::vec3::NEG_Z,dot:Planar64::EPSILON},
|
||||
].into_iter().collect();
|
||||
let faces=planes_to_faces(face_list).unwrap();
|
||||
assert_eq!(faces.faces.len(),7);
|
||||
dbg!(faces);
|
||||
}
|
||||
}
|
@ -1,13 +1,9 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use vbsp_entities_css::Entity;
|
||||
|
||||
use strafesnet_common::{map,model,integer,gameplay_attributes as attr};
|
||||
use strafesnet_common::{map,model,integer,gameplay_attributes};
|
||||
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::{self as modes,NormalizedMode,NormalizedModes,Mode,Stage};
|
||||
|
||||
use crate::valve_transform;
|
||||
|
||||
@ -36,118 +32,32 @@ fn ingest_vertex(
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||
enum AddBrush{
|
||||
Available(model::ModelId),
|
||||
Deferred(model::ModelId),
|
||||
}
|
||||
|
||||
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,
|
||||
)->Option<AddBrush>{
|
||||
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);
|
||||
let model_id=model::ModelId::new(world_models.len() as u32);
|
||||
world_models.push(
|
||||
model::Model{mesh,attributes,transform,color}
|
||||
);
|
||||
Some(AddBrush::Available(model_id))
|
||||
},
|
||||
Err(e)=>{
|
||||
println!("Brush model int parse error: {e} model={model}");
|
||||
None
|
||||
},
|
||||
},
|
||||
_=>{
|
||||
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}
|
||||
);
|
||||
Some(AddBrush::Deferred(model_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<'a>{
|
||||
)->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);
|
||||
let mut unique_attributes=Vec::new();
|
||||
unique_attributes.push(gameplay_attributes::CollisionAttributes::Decoration);
|
||||
const TEMP_TOUCH_ME_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(0);
|
||||
|
||||
//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;
|
||||
let prop_models=bsp.static_props().map(|prop|{
|
||||
//get or create mesh_id
|
||||
let mesh_id=mesh_deferred_loader.acquire_mesh_id(prop.model());
|
||||
let placement=prop.as_prop_placement();
|
||||
model::Model{
|
||||
mesh:mesh_id,
|
||||
attributes:ATTRIBUTE_DECORATION,
|
||||
attributes:TEMP_TOUCH_ME_ATTRIBUTE,
|
||||
transform:integer::Planar64Affine3::new(
|
||||
integer::mat3::try_from_f32_array_2d(
|
||||
integer::mat3::try_from_f32_array_2d((
|
||||
glam::Mat3A::from_diagonal(glam::Vec3::splat(placement.scale))
|
||||
//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()),
|
||||
*glam::Mat3A::from_quat(glam::Quat::from_array(placement.rotation.into()))
|
||||
).to_cols_array_2d()).unwrap(),
|
||||
valve_transform(placement.origin.into()),
|
||||
),
|
||||
color:glam::Vec4::ONE,
|
||||
}
|
||||
@ -157,12 +67,12 @@ pub fn convert<'a>(
|
||||
|
||||
//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 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=HashMap::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();
|
||||
@ -208,344 +118,84 @@ pub fn convert<'a>(
|
||||
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,
|
||||
});
|
||||
|
||||
// THE CUBE OF DESTINY
|
||||
let destination_mesh_id=model::MeshId::new(world_meshes.len() as u32);
|
||||
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 ENTITY_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=ATTRIBUTE_DECORATION;
|
||||
const ENTITY_TRIGGER_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=ATTRIBUTE_INTERSECT_DEFAULT;
|
||||
for raw_ent in &bsp.entities{
|
||||
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);}
|
||||
};
|
||||
} 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);}
|
||||
};
|
||||
let world_models:Vec<model::Model>=
|
||||
//one instance of the main world mesh
|
||||
std::iter::once((
|
||||
//world_model
|
||||
model::MeshId::new(0),
|
||||
//model_origin
|
||||
vbsp::Vector::from([0.0,0.0,0.0]),
|
||||
//model_color
|
||||
vbsp::Color{r:255,g:255,b:255},
|
||||
)).chain(
|
||||
//entities sprinkle instances of the other meshes around
|
||||
bsp.entities.iter()
|
||||
.flat_map(|ent|ent.parse())//ignore entity parsing errors
|
||||
.filter_map(|ent|match ent{
|
||||
vbsp::Entity::Brush(brush)=>Some(brush),
|
||||
vbsp::Entity::BrushIllusionary(brush)=>Some(brush),
|
||||
vbsp::Entity::BrushWall(brush)=>Some(brush),
|
||||
vbsp::Entity::BrushWallToggle(brush)=>Some(brush),
|
||||
_=>None,
|
||||
}).flat_map(|brush|
|
||||
//The first character of brush.model is '*'
|
||||
brush.model[1..].parse().map(|mesh_id|//ignore parse int errors
|
||||
(model::MeshId::new(mesh_id),brush.origin,brush.color)
|
||||
)
|
||||
)
|
||||
).map(|(mesh_id,model_origin,vbsp::Color{r,g,b})|{
|
||||
model::Model{
|
||||
mesh:mesh_id,
|
||||
attributes:TEMP_TOUCH_ME_ATTRIBUTE,
|
||||
transform:integer::Planar64Affine3::new(
|
||||
integer::mat3::identity(),
|
||||
valve_transform(model_origin.into())
|
||||
),
|
||||
color:(glam::Vec3::from_array([r as f32,g as f32,b as f32])/255.0).extend(1.0),
|
||||
}
|
||||
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);}
|
||||
};
|
||||
}
|
||||
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);},
|
||||
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);},
|
||||
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::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))=>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::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);},
|
||||
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))=>{
|
||||
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::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);
|
||||
world_models.push(model::Model{
|
||||
mesh:mesh_id,
|
||||
attributes,
|
||||
transform:integer::Planar64Affine3::new(
|
||||
integer::mat3::identity(),
|
||||
integer::vec3::ZERO,
|
||||
),
|
||||
color:glam::Vec4::ONE,
|
||||
});
|
||||
},
|
||||
Err(e)=>println!("Brush mesh error: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
let first_stage=found_spawn.map(|spawn_point|{
|
||||
// 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,
|
||||
});
|
||||
|
||||
Stage::empty(model_id)
|
||||
});
|
||||
}).collect();
|
||||
|
||||
PartialMap1{
|
||||
attributes:unique_attributes,
|
||||
world_meshes,
|
||||
prop_models,
|
||||
world_models,
|
||||
first_stage,
|
||||
teleports,
|
||||
teleport_destinations,
|
||||
modes:strafesnet_common::gameplay_modes::Modes::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
//partially constructed map types
|
||||
pub struct PartialMap1<'a>{
|
||||
attributes:Vec<attr::CollisionAttributes>,
|
||||
pub struct PartialMap1{
|
||||
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
|
||||
prop_models:Vec<model::Model>,
|
||||
world_meshes:Vec<model::Mesh>,
|
||||
world_models:Vec<model::Model>,
|
||||
first_stage:Option<Stage>,
|
||||
teleports:HashMap<AddBrush,&'a str>,
|
||||
teleport_destinations:HashMap<&'a str,model::ModelId>,
|
||||
modes:strafesnet_common::gameplay_modes::Modes,
|
||||
}
|
||||
impl<'a> PartialMap1<'a>{
|
||||
pub fn add_prop_meshes(
|
||||
impl PartialMap1{
|
||||
pub fn add_prop_meshes<'a>(
|
||||
self,
|
||||
prop_meshes:Meshes,
|
||||
)->PartialMap2<'a>{
|
||||
)->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,
|
||||
first_stage:self.first_stage,
|
||||
teleports:self.teleports,
|
||||
teleport_destinations:self.teleport_destinations,
|
||||
modes:self.modes,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct PartialMap2<'a>{
|
||||
attributes:Vec<attr::CollisionAttributes>,
|
||||
pub struct PartialMap2{
|
||||
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
|
||||
prop_meshes:Vec<(model::MeshId,model::Mesh)>,
|
||||
prop_models:Vec<model::Model>,
|
||||
world_meshes:Vec<model::Mesh>,
|
||||
world_models:Vec<model::Model>,
|
||||
first_stage:Option<Stage>,
|
||||
teleports:HashMap<AddBrush,&'a str>,
|
||||
teleport_destinations:HashMap<&'a str,model::ModelId>,
|
||||
modes:strafesnet_common::gameplay_modes::Modes,
|
||||
}
|
||||
impl PartialMap2<'_>{
|
||||
impl PartialMap2{
|
||||
pub fn add_render_configs_and_textures(
|
||||
mut self,
|
||||
render_configs:RenderConfigs,
|
||||
@ -553,70 +203,22 @@ impl PartialMap2<'_>{
|
||||
//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>,HashMap<model::MeshId,model::MeshId>)
|
||||
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);
|
||||
|
||||
// cull models with a missing mesh
|
||||
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)|
|
||||
//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;
|
||||
(prop_model_id,model)
|
||||
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 modes=NormalizedModes::new(vec![main_mode]);
|
||||
|
||||
//let mut models=Vec::new();
|
||||
let (textures,render_configs)=render_configs.consume();
|
||||
let (textures,texture_id_map):(Vec<Vec<u8>>,HashMap<model::TextureId,model::TextureId>)
|
||||
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)))|{
|
||||
@ -630,7 +232,7 @@ impl PartialMap2<'_>{
|
||||
render_config
|
||||
}).collect();
|
||||
map::CompleteMap{
|
||||
modes,
|
||||
modes:self.modes,
|
||||
attributes:self.attributes,
|
||||
meshes:self.world_meshes,
|
||||
models:self.world_models,
|
||||
|
@ -2,7 +2,6 @@ use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLo
|
||||
|
||||
mod bsp;
|
||||
mod mesh;
|
||||
mod brush;
|
||||
pub mod loader;
|
||||
|
||||
const VALVE_SCALE:f32=1.0/16.0;
|
||||
|
@ -7,57 +7,46 @@ pub struct Aabb{
|
||||
}
|
||||
|
||||
impl Default for Aabb{
|
||||
#[inline]
|
||||
fn default()->Self{
|
||||
Self{min:vec3::MAX,max:vec3::MIN}
|
||||
}
|
||||
}
|
||||
|
||||
impl Aabb{
|
||||
#[inline]
|
||||
pub const fn new(min:Planar64Vec3,max:Planar64Vec3)->Self{
|
||||
Self{min,max}
|
||||
}
|
||||
#[inline]
|
||||
pub const fn max(&self)->Planar64Vec3{
|
||||
self.max
|
||||
}
|
||||
#[inline]
|
||||
pub const fn min(&self)->Planar64Vec3{
|
||||
self.min
|
||||
}
|
||||
#[inline]
|
||||
pub fn grow(&mut self,point:Planar64Vec3){
|
||||
self.min=self.min.min(point);
|
||||
self.max=self.max.max(point);
|
||||
}
|
||||
#[inline]
|
||||
pub fn join(&mut self,aabb:&Aabb){
|
||||
self.min=self.min.min(aabb.min);
|
||||
self.max=self.max.max(aabb.max);
|
||||
}
|
||||
#[inline]
|
||||
pub fn inflate(&mut self,hs:Planar64Vec3){
|
||||
self.min-=hs;
|
||||
self.max+=hs;
|
||||
}
|
||||
#[inline]
|
||||
pub fn contains(&self,point:Planar64Vec3)->bool{
|
||||
let bvec=self.min.lt(point)&point.lt(self.max);
|
||||
bvec.all()
|
||||
}
|
||||
#[inline]
|
||||
pub fn intersects(&self,aabb:&Aabb)->bool{
|
||||
let bvec=self.min.lt(aabb.max)&aabb.min.lt(self.max);
|
||||
bvec.all()
|
||||
}
|
||||
#[inline]
|
||||
pub fn size(&self)->Planar64Vec3{
|
||||
self.max-self.min
|
||||
}
|
||||
#[inline]
|
||||
pub fn center(&self)->Planar64Vec3{
|
||||
self.min.map_zip(self.max,|(min,max)|min.midpoint(max))
|
||||
self.min+(self.max-self.min)>>1
|
||||
}
|
||||
//probably use floats for area & volume because we don't care about precision
|
||||
// pub fn area_weight(&self)->f32{
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::collections::{HashSet,HashMap};
|
||||
use crate::model::ModelId;
|
||||
use crate::gameplay_style;
|
||||
use crate::updatable::Updatable;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StageElement{
|
||||
@ -127,6 +128,18 @@ impl Stage{
|
||||
self.unordered_checkpoints.contains(&model_id)
|
||||
}
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct StageUpdate{
|
||||
//other behaviour models of this stage can have
|
||||
ordered_checkpoints:HashMap<CheckpointId,ModelId>,
|
||||
unordered_checkpoints:HashSet<ModelId>,
|
||||
}
|
||||
impl Updatable<StageUpdate> for Stage{
|
||||
fn update(&mut self,update:StageUpdate){
|
||||
self.ordered_checkpoints.extend(update.ordered_checkpoints);
|
||||
self.unordered_checkpoints.extend(update.unordered_checkpoints);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
|
||||
pub enum Zone{
|
||||
@ -198,47 +211,34 @@ impl Mode{
|
||||
pub fn push_stage(&mut self,stage:Stage){
|
||||
self.stages.push(stage)
|
||||
}
|
||||
pub fn get_stage_mut(&mut self,StageId(stage_id):StageId)->Option<&mut Stage>{
|
||||
self.stages.get_mut(stage_id as usize)
|
||||
pub fn get_stage_mut(&mut self,stage:StageId)->Option<&mut Stage>{
|
||||
self.stages.get_mut(stage.0 as usize)
|
||||
}
|
||||
pub fn get_spawn_model_id(&self,StageId(stage_id):StageId)->Option<ModelId>{
|
||||
self.stages.get(stage_id as usize).map(|s|s.spawn)
|
||||
pub fn get_spawn_model_id(&self,stage:StageId)->Option<ModelId>{
|
||||
self.stages.get(stage.0 as usize).map(|s|s.spawn)
|
||||
}
|
||||
pub fn get_zone(&self,model_id:ModelId)->Option<&Zone>{
|
||||
self.zones.get(&model_id)
|
||||
}
|
||||
pub fn get_stage(&self,StageId(stage_id):StageId)->Option<&Stage>{
|
||||
self.stages.get(stage_id as usize)
|
||||
pub fn get_stage(&self,stage_id:StageId)->Option<&Stage>{
|
||||
self.stages.get(stage_id.0 as usize)
|
||||
}
|
||||
pub fn get_element(&self,model_id:ModelId)->Option<&StageElement>{
|
||||
self.elements.get(&model_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NormalizedMode(Mode);
|
||||
impl NormalizedMode{
|
||||
pub fn new(mode:Mode)->Self{
|
||||
Self(mode)
|
||||
}
|
||||
pub fn into_inner(self)->Mode{
|
||||
let Self(mode)=self;
|
||||
mode
|
||||
}
|
||||
//TODO: put this in the SNF
|
||||
pub fn denormalize(self)->Mode{
|
||||
let NormalizedMode(mut mode)=self;
|
||||
pub fn denormalize_data(&mut self){
|
||||
//expand and index normalized data
|
||||
mode.zones.insert(mode.start,Zone::Start);
|
||||
for (stage_id,stage) in mode.stages.iter().enumerate(){
|
||||
mode.elements.insert(stage.spawn,StageElement{
|
||||
self.zones.insert(self.start,Zone::Start);
|
||||
for (stage_id,stage) in self.stages.iter().enumerate(){
|
||||
self.elements.insert(stage.spawn,StageElement{
|
||||
stage_id:StageId(stage_id as u32),
|
||||
force:false,
|
||||
behaviour:StageElementBehaviour::SpawnAt,
|
||||
jump_limit:None,
|
||||
});
|
||||
for (_,&model) in &stage.ordered_checkpoints{
|
||||
mode.elements.insert(model,StageElement{
|
||||
self.elements.insert(model,StageElement{
|
||||
stage_id:StageId(stage_id as u32),
|
||||
force:false,
|
||||
behaviour:StageElementBehaviour::Checkpoint,
|
||||
@ -246,7 +246,7 @@ impl NormalizedMode{
|
||||
});
|
||||
}
|
||||
for &model in &stage.unordered_checkpoints{
|
||||
mode.elements.insert(model,StageElement{
|
||||
self.elements.insert(model,StageElement{
|
||||
stage_id:StageId(stage_id as u32),
|
||||
force:false,
|
||||
behaviour:StageElementBehaviour::Checkpoint,
|
||||
@ -254,13 +254,53 @@ impl NormalizedMode{
|
||||
});
|
||||
}
|
||||
}
|
||||
mode
|
||||
}
|
||||
}
|
||||
//this would be nice as a macro
|
||||
#[derive(Default)]
|
||||
pub struct ModeUpdate{
|
||||
zones:HashMap<ModelId,Zone>,
|
||||
stages:HashMap<StageId,StageUpdate>,
|
||||
//mutually exlusive stage element behaviour
|
||||
elements:HashMap<ModelId,StageElement>,
|
||||
}
|
||||
impl Updatable<ModeUpdate> for Mode{
|
||||
fn update(&mut self,update:ModeUpdate){
|
||||
self.zones.extend(update.zones);
|
||||
for (stage,stage_update) in update.stages{
|
||||
if let Some(stage)=self.stages.get_mut(stage.0 as usize){
|
||||
stage.update(stage_update);
|
||||
}
|
||||
}
|
||||
self.elements.extend(update.elements);
|
||||
}
|
||||
}
|
||||
impl ModeUpdate{
|
||||
pub fn zone(model_id:ModelId,zone:Zone)->Self{
|
||||
let mut mu=Self::default();
|
||||
mu.zones.insert(model_id,zone);
|
||||
mu
|
||||
}
|
||||
pub fn stage(stage_id:StageId,stage_update:StageUpdate)->Self{
|
||||
let mut mu=Self::default();
|
||||
mu.stages.insert(stage_id,stage_update);
|
||||
mu
|
||||
}
|
||||
pub fn element(model_id:ModelId,element:StageElement)->Self{
|
||||
let mut mu=Self::default();
|
||||
mu.elements.insert(model_id,element);
|
||||
mu
|
||||
}
|
||||
pub fn map_stage_element_ids<F:Fn(StageId)->StageId>(&mut self,f:F){
|
||||
for (_,stage_element) in self.elements.iter_mut(){
|
||||
stage_element.stage_id=f(stage_element.stage_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default,Clone)]
|
||||
pub struct Modes{
|
||||
modes:Vec<Mode>,
|
||||
pub modes:Vec<Mode>,
|
||||
}
|
||||
impl Modes{
|
||||
pub const fn new(modes:Vec<Mode>)->Self{
|
||||
@ -274,182 +314,19 @@ impl Modes{
|
||||
pub fn push_mode(&mut self,mode:Mode){
|
||||
self.modes.push(mode)
|
||||
}
|
||||
pub fn get_mode(&self,ModeId(mode_id):ModeId)->Option<&Mode>{
|
||||
self.modes.get(mode_id as usize)
|
||||
pub fn get_mode(&self,mode:ModeId)->Option<&Mode>{
|
||||
self.modes.get(mode.0 as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NormalizedModes{
|
||||
modes:Vec<NormalizedMode>,
|
||||
pub struct ModesUpdate{
|
||||
modes:HashMap<ModeId,ModeUpdate>,
|
||||
}
|
||||
impl NormalizedModes{
|
||||
pub fn new(modes:Vec<NormalizedMode>)->Self{
|
||||
Self{modes}
|
||||
}
|
||||
pub fn len(&self)->usize{
|
||||
self.modes.len()
|
||||
}
|
||||
pub fn denormalize(self)->Modes{
|
||||
Modes{
|
||||
modes:self.modes.into_iter().map(NormalizedMode::denormalize).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl IntoIterator for NormalizedModes{
|
||||
type Item=<Vec<NormalizedMode> as IntoIterator>::Item;
|
||||
type IntoIter=<Vec<NormalizedMode> as IntoIterator>::IntoIter;
|
||||
fn into_iter(self)->Self::IntoIter{
|
||||
self.modes.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StageUpdate{
|
||||
//other behaviour models of this stage can have
|
||||
ordered_checkpoints:HashMap<CheckpointId,ModelId>,
|
||||
unordered_checkpoints:HashSet<ModelId>,
|
||||
}
|
||||
impl StageUpdate{
|
||||
fn apply_to(self,stage:&mut Stage){
|
||||
stage.ordered_checkpoints.extend(self.ordered_checkpoints);
|
||||
stage.unordered_checkpoints.extend(self.unordered_checkpoints);
|
||||
}
|
||||
}
|
||||
|
||||
//this would be nice as a macro
|
||||
#[derive(Default)]
|
||||
pub struct ModeUpdate{
|
||||
zones:Option<(ModelId,Zone)>,
|
||||
stages:Option<(StageId,StageUpdate)>,
|
||||
//mutually exlusive stage element behaviour
|
||||
elements:Option<(ModelId,StageElement)>,
|
||||
}
|
||||
impl ModeUpdate{
|
||||
fn apply_to(self,mode:&mut Mode){
|
||||
mode.zones.extend(self.zones);
|
||||
if let Some((StageId(stage_id),stage_update))=self.stages{
|
||||
if let Some(stage)=mode.stages.get_mut(stage_id as usize){
|
||||
stage_update.apply_to(stage);
|
||||
impl Updatable<ModesUpdate> for Modes{
|
||||
fn update(&mut self,update:ModesUpdate){
|
||||
for (mode,mode_update) in update.modes{
|
||||
if let Some(mode)=self.modes.get_mut(mode.0 as usize){
|
||||
mode.update(mode_update);
|
||||
}
|
||||
}
|
||||
mode.elements.extend(self.elements);
|
||||
}
|
||||
}
|
||||
impl ModeUpdate{
|
||||
pub fn zone(model_id:ModelId,zone:Zone)->Self{
|
||||
Self{
|
||||
zones:Some((model_id,zone)),
|
||||
stages:None,
|
||||
elements:None,
|
||||
}
|
||||
}
|
||||
pub fn stage(stage_id:StageId,stage_update:StageUpdate)->Self{
|
||||
Self{
|
||||
zones:None,
|
||||
stages:Some((stage_id,stage_update)),
|
||||
elements:None,
|
||||
}
|
||||
}
|
||||
pub fn element(model_id:ModelId,element:StageElement)->Self{
|
||||
Self{
|
||||
zones:None,
|
||||
stages:None,
|
||||
elements:Some((model_id,element)),
|
||||
}
|
||||
}
|
||||
pub fn map_stage_element_ids<F:Fn(StageId)->StageId>(&mut self,f:F){
|
||||
for (_,stage_element) in self.elements.iter_mut(){
|
||||
stage_element.stage_id=f(stage_element.stage_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ModeBuilder{
|
||||
mode:Mode,
|
||||
stage_id_map:HashMap<StageId,StageId>,
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct ModesBuilder{
|
||||
modes:HashMap<ModeId,Mode>,
|
||||
stages:HashMap<ModeId,HashMap<StageId,Stage>>,
|
||||
mode_updates:Vec<(ModeId,ModeUpdate)>,
|
||||
stage_updates:Vec<(ModeId,StageId,StageUpdate)>,
|
||||
}
|
||||
impl ModesBuilder{
|
||||
pub fn build_normalized(mut self)->NormalizedModes{
|
||||
//collect modes and stages into contiguous arrays
|
||||
let mut unique_modes:Vec<(ModeId,Mode)>
|
||||
=self.modes.into_iter().collect();
|
||||
unique_modes.sort_by_key(|&(mode_id,_)|mode_id);
|
||||
let (mut modes,mode_id_map):(Vec<ModeBuilder>,HashMap<ModeId,ModeId>)
|
||||
=unique_modes.into_iter().enumerate()
|
||||
.map(|(final_mode_id,(builder_mode_id,mut mode))|{
|
||||
(
|
||||
ModeBuilder{
|
||||
stage_id_map:self.stages.remove(&builder_mode_id).map_or_else(||HashMap::new(),|stages|{
|
||||
let mut unique_stages:Vec<(StageId,Stage)>
|
||||
=stages.into_iter().collect();
|
||||
unique_stages.sort_by_key(|&(StageId(stage_id),_)|stage_id);
|
||||
unique_stages.into_iter().enumerate()
|
||||
.map(|(final_stage_id,(builder_stage_id,stage))|{
|
||||
mode.push_stage(stage);
|
||||
(builder_stage_id,StageId::new(final_stage_id as u32))
|
||||
}).collect()
|
||||
}),
|
||||
mode,
|
||||
},
|
||||
(
|
||||
builder_mode_id,
|
||||
ModeId::new(final_mode_id as u32)
|
||||
)
|
||||
)
|
||||
}).unzip();
|
||||
//TODO: failure messages or errors or something
|
||||
//push stage updates
|
||||
for (builder_mode_id,builder_stage_id,stage_update) in self.stage_updates{
|
||||
if let Some(final_mode_id)=mode_id_map.get(&builder_mode_id){
|
||||
if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){
|
||||
if let Some(&final_stage_id)=mode.stage_id_map.get(&builder_stage_id){
|
||||
if let Some(stage)=mode.mode.get_stage_mut(final_stage_id){
|
||||
stage_update.apply_to(stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//push mode updates
|
||||
for (builder_mode_id,mut mode_update) in self.mode_updates{
|
||||
if let Some(final_mode_id)=mode_id_map.get(&builder_mode_id){
|
||||
if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){
|
||||
//map stage id on stage elements
|
||||
mode_update.map_stage_element_ids(|stage_id|
|
||||
//walk down one stage id at a time until a stage is found
|
||||
//TODO use better logic like BTreeMap::upper_bound instead of walking
|
||||
// final_stage_id_from_builder_stage_id.upper_bound(Bound::Included(&stage_id))
|
||||
// .value().copied().unwrap_or(StageId::FIRST)
|
||||
(0..=stage_id.get()).rev().find_map(|builder_stage_id|
|
||||
//map the stage element to that stage
|
||||
mode.stage_id_map.get(&StageId::new(builder_stage_id)).copied()
|
||||
).unwrap_or(StageId::FIRST)
|
||||
);
|
||||
mode_update.apply_to(&mut mode.mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
NormalizedModes::new(modes.into_iter().map(|mode_builder|NormalizedMode(mode_builder.mode)).collect())
|
||||
}
|
||||
pub fn insert_mode(&mut self,mode_id:ModeId,mode:Mode){
|
||||
assert!(self.modes.insert(mode_id,mode).is_none(),"Cannot replace existing mode");
|
||||
}
|
||||
pub fn insert_stage(&mut self,mode_id:ModeId,stage_id:StageId,stage:Stage){
|
||||
assert!(self.stages.entry(mode_id).or_insert(HashMap::new()).insert(stage_id,stage).is_none(),"Cannot replace existing stage");
|
||||
}
|
||||
pub fn push_mode_update(&mut self,mode_id:ModeId,mode_update:ModeUpdate){
|
||||
self.mode_updates.push((mode_id,mode_update));
|
||||
}
|
||||
// fn push_stage_update(&mut self,mode_id:ModeId,stage_id:StageId,stage_update:StageUpdate){
|
||||
// self.stage_updates.push((mode_id,stage_id,stage_update));
|
||||
// }
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ impl JumpImpulse{
|
||||
velocity:Planar64Vec3,
|
||||
jump_dir:Planar64Vec3,
|
||||
gravity:&Planar64Vec3,
|
||||
_mass:Planar64,
|
||||
mass:Planar64,
|
||||
)->Planar64Vec3{
|
||||
match self{
|
||||
&JumpImpulse::Time(time)=>velocity-(*gravity*time).map(|t|t.divide().fix_1()),
|
||||
@ -78,7 +78,7 @@ impl JumpImpulse{
|
||||
velocity-(*gravity*(radicand.sqrt().fix_2()+v_g)/gg).divide().fix_1()
|
||||
},
|
||||
&JumpImpulse::Linear(jump_speed)=>velocity+(jump_dir*jump_speed/jump_dir.length()).divide().fix_1(),
|
||||
&JumpImpulse::Energy(_energy)=>{
|
||||
&JumpImpulse::Energy(energy)=>{
|
||||
//calculate energy
|
||||
//let e=gravity.dot(velocity);
|
||||
//add
|
||||
@ -355,7 +355,7 @@ pub struct LadderSettings{
|
||||
pub dot:Planar64,
|
||||
}
|
||||
impl LadderSettings{
|
||||
pub const fn accel(&self,_target_diff:Planar64Vec3,_gravity:Planar64Vec3)->Planar64{
|
||||
pub const fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
|
||||
//TODO: fallible ladder accel
|
||||
self.accelerate.accel
|
||||
}
|
||||
@ -529,12 +529,12 @@ impl StyleModifiers{
|
||||
|
||||
pub fn source_bhop()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::all(),
|
||||
controls_mask:Controls::all()-Controls::MoveUp-Controls::MoveDown,
|
||||
controls_mask_state:Controls::all(),
|
||||
strafe:Some(StrafeSettings{
|
||||
enable:ControlsActivation::full_2d(),
|
||||
air_accel_limit:Some(Planar64::raw((150<<28)*100)),
|
||||
mv:Planar64::raw(30<<28),
|
||||
air_accel_limit:Some(Planar64::raw(150<<28)*100),
|
||||
mv:(Planar64::raw(30)*VALVE_SCALE).fix_1(),
|
||||
tick_rate:Ratio64::new(100,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(),
|
||||
}),
|
||||
jump:Some(JumpSettings{
|
||||
@ -570,12 +570,12 @@ impl StyleModifiers{
|
||||
}
|
||||
pub fn source_surf()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::all(),
|
||||
controls_mask:Controls::all()-Controls::MoveUp-Controls::MoveDown,
|
||||
controls_mask_state:Controls::all(),
|
||||
strafe:Some(StrafeSettings{
|
||||
enable:ControlsActivation::full_2d(),
|
||||
air_accel_limit:Some((int(150)*66*VALVE_SCALE).fix_1()),
|
||||
mv:Planar64::raw(30<<28),
|
||||
mv:(int(30)*VALVE_SCALE).fix_1(),
|
||||
tick_rate:Ratio64::new(66,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(),
|
||||
}),
|
||||
jump:Some(JumpSettings{
|
||||
|
@ -9,6 +9,7 @@ pub mod timer;
|
||||
pub mod integer;
|
||||
pub mod physics;
|
||||
pub mod session;
|
||||
pub mod updatable;
|
||||
pub mod instruction;
|
||||
pub mod gameplay_attributes;
|
||||
pub mod gameplay_modes;
|
||||
|
@ -4,7 +4,7 @@ use crate::gameplay_attributes;
|
||||
//this is a temporary struct to try to get the code running again
|
||||
//TODO: use snf::map::Region to update the data in physics and graphics instead of this
|
||||
pub struct CompleteMap{
|
||||
pub modes:gameplay_modes::NormalizedModes,
|
||||
pub modes:gameplay_modes::Modes,
|
||||
pub attributes:Vec<gameplay_attributes::CollisionAttributes>,
|
||||
pub meshes:Vec<model::Mesh>,
|
||||
pub models:Vec<model::Model>,
|
||||
|
57
lib/common/src/updatable.rs
Normal file
57
lib/common/src/updatable.rs
Normal file
@ -0,0 +1,57 @@
|
||||
// This whole thing should be a drive macro
|
||||
|
||||
pub trait Updatable<Updater>{
|
||||
fn update(&mut self,update:Updater);
|
||||
}
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
|
||||
struct InnerId(u32);
|
||||
#[derive(Clone)]
|
||||
struct Inner{
|
||||
id:InnerId,
|
||||
enabled:bool,
|
||||
}
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
|
||||
struct OuterId(u32);
|
||||
struct Outer{
|
||||
id:OuterId,
|
||||
inners:std::collections::HashMap<InnerId,Inner>,
|
||||
}
|
||||
|
||||
enum Update<I,U>{
|
||||
Insert(I),
|
||||
Update(U),
|
||||
Remove
|
||||
}
|
||||
|
||||
struct InnerUpdate{
|
||||
//#[updatable(Update)]
|
||||
enabled:Option<bool>,
|
||||
}
|
||||
struct OuterUpdate{
|
||||
//#[updatable(Insert,Update,Remove)]
|
||||
inners:std::collections::HashMap<InnerId,Update<Inner,InnerUpdate>>,
|
||||
//#[updatable(Update)]
|
||||
//inners:std::collections::HashMap<InnerId,InnerUpdate>,
|
||||
}
|
||||
impl Updatable<InnerUpdate> for Inner{
|
||||
fn update(&mut self,update:InnerUpdate){
|
||||
if let Some(enabled)=update.enabled{
|
||||
self.enabled=enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Updatable<OuterUpdate> for Outer{
|
||||
fn update(&mut self,update:OuterUpdate){
|
||||
for (id,up) in update.inners{
|
||||
match up{
|
||||
Update::Insert(new_inner)=>self.inners.insert(id,new_inner),
|
||||
Update::Update(inner_update)=>self.inners.get_mut(&id).map(|inner|{
|
||||
let old=inner.clone();
|
||||
inner.update(inner_update);
|
||||
old
|
||||
}),
|
||||
Update::Remove=>self.inners.remove(&id),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ wide-mul=[]
|
||||
zeroes=["dep:arrayvec"]
|
||||
|
||||
[dependencies]
|
||||
bnum = "0.13.0"
|
||||
bnum = "0.12.0"
|
||||
arrayvec = { version = "0.7.6", optional = true }
|
||||
paste = "1.0.15"
|
||||
ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet", optional = true }
|
||||
|
@ -33,14 +33,6 @@ impl<const N:usize,const F:usize> Fixed<N,F>{
|
||||
self.bits
|
||||
}
|
||||
#[inline]
|
||||
pub const fn as_bits(&self)->&BInt<N>{
|
||||
&self.bits
|
||||
}
|
||||
#[inline]
|
||||
pub const fn as_bits_mut(&mut self)->&mut BInt<N>{
|
||||
&mut self.bits
|
||||
}
|
||||
#[inline]
|
||||
pub const fn raw_digit(value:i64)->Self{
|
||||
let mut digits=[0u64;N];
|
||||
digits[0]=value.abs() as u64;
|
||||
@ -64,10 +56,6 @@ impl<const N:usize,const F:usize> Fixed<N,F>{
|
||||
pub const fn abs(self)->Self{
|
||||
Self::from_bits(self.bits.abs())
|
||||
}
|
||||
#[inline]
|
||||
pub const fn midpoint(self,other:Self)->Self{
|
||||
Self::from_bits(self.bits.midpoint(other.bits))
|
||||
}
|
||||
}
|
||||
impl<const F:usize> Fixed<1,F>{
|
||||
/// My old code called this function everywhere so let's provide it
|
||||
@ -800,10 +788,13 @@ macro_rules! impl_not_const_generic{
|
||||
let wide_self=self.[<fix_ $_2n>]();
|
||||
//descend down the bits and check if flipping each bit would push the square over the input value
|
||||
for shift in (0..=max_shift).rev(){
|
||||
result.as_bits_mut().as_bits_mut().set_bit(shift,true);
|
||||
if wide_self<result.[<wide_mul_ $n _ $n>](result){
|
||||
// put it back lol
|
||||
result.as_bits_mut().as_bits_mut().set_bit(shift,false);
|
||||
let new_result={
|
||||
let mut bits=result.to_bits().to_bits();
|
||||
bits.set_bit(shift,true);
|
||||
Self::from_bits(BInt::from_bits(bits))
|
||||
};
|
||||
if new_result.[<wide_mul_ $n _ $n>](new_result)<=wide_self{
|
||||
result=new_result;
|
||||
}
|
||||
}
|
||||
result
|
||||
|
@ -68,54 +68,57 @@ impl_ratio_method!(Add,add,add_ratio);
|
||||
impl_ratio_method!(Sub,sub,sub_ratio);
|
||||
impl_ratio_method!(Rem,rem,rem_ratio);
|
||||
|
||||
/// Comparing two ratios needs to know the parity of the denominators
|
||||
/// For signed integers this can be implemented with is_negative()
|
||||
pub trait Parity{
|
||||
fn parity(&self)->bool;
|
||||
/// The denominator cannot be negative
|
||||
/// otherwise cross-multiplying changes the comparison
|
||||
pub trait ToUnsigned{
|
||||
type Unsigned;
|
||||
fn to_unsigned(self)->(bool,Self::Unsigned);
|
||||
}
|
||||
macro_rules! impl_parity_unsigned{
|
||||
macro_rules! impl_to_unsigned_unsigned{
|
||||
($($type:ty),*)=>{
|
||||
$(
|
||||
impl Parity for $type{
|
||||
fn parity(&self)->bool{
|
||||
false
|
||||
impl ToUnsigned for $type{
|
||||
type Unsigned=Self;
|
||||
fn to_unsigned(self)->(bool,Self::Unsigned){
|
||||
(false,self)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
macro_rules! impl_parity_signed{
|
||||
($($type:ty),*)=>{
|
||||
macro_rules! impl_to_unsigned_signed{
|
||||
($(($type:ty,$unsigned:ty)),*)=>{
|
||||
$(
|
||||
impl Parity for $type{
|
||||
fn parity(&self)->bool{
|
||||
self.is_negative()
|
||||
impl ToUnsigned for $type{
|
||||
type Unsigned=$unsigned;
|
||||
fn to_unsigned(self)->(bool,Self::Unsigned){
|
||||
(self.is_negative(),self.unsigned_abs())
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
macro_rules! impl_parity_float{
|
||||
macro_rules! impl_to_unsigned_float{
|
||||
($($type:ty),*)=>{
|
||||
$(
|
||||
impl Parity for $type{
|
||||
fn parity(&self)->bool{
|
||||
self.is_sign_negative()
|
||||
impl ToUnsigned for $type{
|
||||
type Unsigned=Self;
|
||||
fn to_unsigned(self)->(bool,Self::Unsigned){
|
||||
(self.is_sign_negative(),-self)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_parity_unsigned!(u8,u16,u32,u64,u128,usize);
|
||||
impl_parity_signed!(i8,i16,i32,i64,i128,isize);
|
||||
impl_parity_float!(f32,f64);
|
||||
impl_to_unsigned_unsigned!(u8,u16,u32,u64,u128,usize);
|
||||
impl_to_unsigned_signed!((i8,u8),(i16,u16),(i32,u32),(i64,u64),(i128,u128),(isize,usize));
|
||||
impl_to_unsigned_float!(f32,f64);
|
||||
|
||||
macro_rules! impl_ratio_ord_method{
|
||||
($method:ident, $ratio_method:ident, $output:ty)=>{
|
||||
impl<LhsNum,LhsDen:Parity> Ratio<LhsNum,LhsDen>{
|
||||
impl<LhsNum,LhsDen:ToUnsigned> Ratio<LhsNum,LhsDen>{
|
||||
#[inline]
|
||||
pub fn $ratio_method<RhsNum,RhsDen:Parity,T>(self,rhs:Ratio<RhsNum,RhsDen>)->$output
|
||||
pub fn $ratio_method<RhsNum,RhsDen:ToUnsigned,T>(self,rhs:Ratio<RhsNum,RhsDen>)->$output
|
||||
where
|
||||
LhsNum:core::ops::Mul<RhsDen,Output=T>,
|
||||
LhsDen:core::ops::Mul<RhsNum,Output=T>,
|
||||
@ -273,9 +276,9 @@ impl<Num,Den> Eq for Ratio<Num,Den> where Self:PartialEq{}
|
||||
impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialOrd<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
|
||||
where
|
||||
LhsNum:Copy,
|
||||
LhsDen:Copy+Parity,
|
||||
LhsDen:Copy+ToUnsigned,
|
||||
RhsNum:Copy,
|
||||
RhsDen:Copy+Parity,
|
||||
RhsDen:Copy+ToUnsigned,
|
||||
LhsNum:core::ops::Mul<RhsDen,Output=T>,
|
||||
LhsDen:core::ops::Mul<RhsNum,Output=T>,
|
||||
RhsNum:core::ops::Mul<LhsDen,Output=U>,
|
||||
@ -290,7 +293,7 @@ impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialOrd<Ratio<RhsNum,RhsDen>> for Ratio
|
||||
impl<Num,Den,T> Ord for Ratio<Num,Den>
|
||||
where
|
||||
Num:Copy,
|
||||
Den:Copy+Parity,
|
||||
Den:Copy+ToUnsigned,
|
||||
Num:core::ops::Mul<Den,Output=T>,
|
||||
Den:core::ops::Mul<Num,Output=T>,
|
||||
T:Ord,
|
||||
|
@ -84,13 +84,13 @@ where
|
||||
fn ingest_faces2_lods3(
|
||||
polygon_groups:&mut Vec<PolygonGroup>,
|
||||
vertex_id_map:&HashMap<rbx_mesh::mesh::VertexId2,VertexId>,
|
||||
faces:&[rbx_mesh::mesh::Face2],
|
||||
lods:&[rbx_mesh::mesh::Lod3],
|
||||
faces:&Vec<rbx_mesh::mesh::Face2>,
|
||||
lods:&Vec<rbx_mesh::mesh::Lod3>
|
||||
){
|
||||
//faces have to be split into polygon groups based on lod
|
||||
polygon_groups.extend(lods.windows(2).map(|lod_pair|
|
||||
PolygonGroup::PolygonList(PolygonList::new(faces[lod_pair[0].0 as usize..lod_pair[1].0 as usize].iter().map(|rbx_mesh::mesh::Face2(v0,v1,v2)|
|
||||
vec![vertex_id_map[&v0],vertex_id_map[&v1],vertex_id_map[&v2]]
|
||||
PolygonGroup::PolygonList(PolygonList::new(faces[lod_pair[0].0 as usize..lod_pair[1].0 as usize].iter().map(|face|
|
||||
vec![vertex_id_map[&face.0],vertex_id_map[&face.1],vertex_id_map[&face.2]]
|
||||
).collect()))
|
||||
))
|
||||
}
|
||||
|
@ -4,11 +4,12 @@ use crate::primitives;
|
||||
use strafesnet_common::aabb::Aabb;
|
||||
use strafesnet_common::map;
|
||||
use strafesnet_common::model;
|
||||
use strafesnet_common::gameplay_modes::{NormalizedModes,Mode,ModeId,ModeUpdate,ModesBuilder,Stage,StageElement,StageElementBehaviour,StageId,Zone};
|
||||
use strafesnet_common::gameplay_modes;
|
||||
use strafesnet_common::gameplay_style;
|
||||
use strafesnet_common::gameplay_attributes as attr;
|
||||
use strafesnet_common::integer::{self,vec3,Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3};
|
||||
use strafesnet_common::model::RenderConfigId;
|
||||
use strafesnet_common::updatable::Updatable;
|
||||
use strafesnet_deferred_loader::deferred_loader::{RenderConfigDeferredLoader,MeshDeferredLoader};
|
||||
use strafesnet_deferred_loader::mesh::Meshes;
|
||||
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
|
||||
@ -51,7 +52,93 @@ fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_we
|
||||
vec3::try_from_f32_array([cf.position.x,cf.position.y,cf.position.z]).unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
struct ModeBuilder{
|
||||
mode:gameplay_modes::Mode,
|
||||
final_stage_id_from_builder_stage_id:HashMap<gameplay_modes::StageId,gameplay_modes::StageId>,
|
||||
}
|
||||
#[derive(Default)]
|
||||
struct ModesBuilder{
|
||||
modes:HashMap<gameplay_modes::ModeId,gameplay_modes::Mode>,
|
||||
stages:HashMap<gameplay_modes::ModeId,HashMap<gameplay_modes::StageId,gameplay_modes::Stage>>,
|
||||
mode_updates:Vec<(gameplay_modes::ModeId,gameplay_modes::ModeUpdate)>,
|
||||
stage_updates:Vec<(gameplay_modes::ModeId,gameplay_modes::StageId,gameplay_modes::StageUpdate)>,
|
||||
}
|
||||
impl ModesBuilder{
|
||||
fn build(mut self)->gameplay_modes::Modes{
|
||||
//collect modes and stages into contiguous arrays
|
||||
let mut unique_modes:Vec<(gameplay_modes::ModeId,gameplay_modes::Mode)>
|
||||
=self.modes.into_iter().collect();
|
||||
unique_modes.sort_by_key(|&(mode_id,_)|mode_id);
|
||||
let (mut modes,final_mode_id_from_builder_mode_id):(Vec<ModeBuilder>,HashMap<gameplay_modes::ModeId,gameplay_modes::ModeId>)
|
||||
=unique_modes.into_iter().enumerate()
|
||||
.map(|(final_mode_id,(builder_mode_id,mut mode))|{
|
||||
(
|
||||
ModeBuilder{
|
||||
final_stage_id_from_builder_stage_id:self.stages.remove(&builder_mode_id).map_or_else(||HashMap::new(),|stages|{
|
||||
let mut unique_stages:Vec<(gameplay_modes::StageId,gameplay_modes::Stage)>
|
||||
=stages.into_iter().collect();
|
||||
unique_stages.sort_by(|a,b|a.0.cmp(&b.0));
|
||||
unique_stages.into_iter().enumerate()
|
||||
.map(|(final_stage_id,(builder_stage_id,stage))|{
|
||||
mode.push_stage(stage);
|
||||
(builder_stage_id,gameplay_modes::StageId::new(final_stage_id as u32))
|
||||
}).collect()
|
||||
}),
|
||||
mode,
|
||||
},
|
||||
(
|
||||
builder_mode_id,
|
||||
gameplay_modes::ModeId::new(final_mode_id as u32)
|
||||
)
|
||||
)
|
||||
}).unzip();
|
||||
//TODO: failure messages or errors or something
|
||||
//push stage updates
|
||||
for (builder_mode_id,builder_stage_id,stage_update) in self.stage_updates{
|
||||
if let Some(final_mode_id)=final_mode_id_from_builder_mode_id.get(&builder_mode_id){
|
||||
if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){
|
||||
if let Some(&final_stage_id)=mode.final_stage_id_from_builder_stage_id.get(&builder_stage_id){
|
||||
if let Some(stage)=mode.mode.get_stage_mut(final_stage_id){
|
||||
stage.update(stage_update);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//push mode updates
|
||||
for (builder_mode_id,mut mode_update) in self.mode_updates{
|
||||
if let Some(final_mode_id)=final_mode_id_from_builder_mode_id.get(&builder_mode_id){
|
||||
if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){
|
||||
//map stage id on stage elements
|
||||
mode_update.map_stage_element_ids(|stage_id|
|
||||
//walk down one stage id at a time until a stage is found
|
||||
//TODO use better logic like BTreeMap::upper_bound instead of walking
|
||||
// final_stage_id_from_builder_stage_id.upper_bound(Bound::Included(&stage_id))
|
||||
// .value().copied().unwrap_or(gameplay_modes::StageId::FIRST)
|
||||
(0..=stage_id.get()).rev().find_map(|builder_stage_id|
|
||||
//map the stage element to that stage
|
||||
mode.final_stage_id_from_builder_stage_id.get(&gameplay_modes::StageId::new(builder_stage_id)).copied()
|
||||
).unwrap_or(gameplay_modes::StageId::FIRST)
|
||||
);
|
||||
mode.mode.update(mode_update);
|
||||
}
|
||||
}
|
||||
}
|
||||
gameplay_modes::Modes::new(modes.into_iter().map(|mode_builder|mode_builder.mode).collect())
|
||||
}
|
||||
fn insert_mode(&mut self,mode_id:gameplay_modes::ModeId,mode:gameplay_modes::Mode){
|
||||
assert!(self.modes.insert(mode_id,mode).is_none(),"Cannot replace existing mode");
|
||||
}
|
||||
fn insert_stage(&mut self,mode_id:gameplay_modes::ModeId,stage_id:gameplay_modes::StageId,stage:gameplay_modes::Stage){
|
||||
assert!(self.stages.entry(mode_id).or_insert(HashMap::new()).insert(stage_id,stage).is_none(),"Cannot replace existing stage");
|
||||
}
|
||||
fn push_mode_update(&mut self,mode_id:gameplay_modes::ModeId,mode_update:gameplay_modes::ModeUpdate){
|
||||
self.mode_updates.push((mode_id,mode_update));
|
||||
}
|
||||
// fn push_stage_update(&mut self,mode_id:gameplay_modes::ModeId,stage_id:gameplay_modes::StageId,stage_update:gameplay_modes::StageUpdate){
|
||||
// self.stage_updates.push((mode_id,stage_id,stage_update));
|
||||
// }
|
||||
}
|
||||
fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:model::ModelId,modes_builder:&mut ModesBuilder,wormhole_in_model_to_id:&mut HashMap<model::ModelId,u32>,wormhole_id_to_out_model:&mut HashMap<u32,model::ModelId>)->attr::CollisionAttributes{
|
||||
let mut general=attr::GeneralAttributes::default();
|
||||
let mut intersecting=attr::IntersectingAttributes::default();
|
||||
@ -80,8 +167,8 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
force_can_collide=false;
|
||||
force_intersecting=true;
|
||||
modes_builder.insert_mode(
|
||||
ModeId::MAIN,
|
||||
Mode::empty(
|
||||
gameplay_modes::ModeId::MAIN,
|
||||
gameplay_modes::Mode::empty(
|
||||
gameplay_style::StyleModifiers::roblox_bhop(),
|
||||
model_id
|
||||
)
|
||||
@ -91,10 +178,10 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
force_can_collide=false;
|
||||
force_intersecting=true;
|
||||
modes_builder.push_mode_update(
|
||||
ModeId::MAIN,
|
||||
ModeUpdate::zone(
|
||||
gameplay_modes::ModeId::MAIN,
|
||||
gameplay_modes::ModeUpdate::zone(
|
||||
model_id,
|
||||
Zone::Finish,
|
||||
gameplay_modes::Zone::Finish,
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -102,19 +189,19 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
force_can_collide=false;
|
||||
force_intersecting=true;
|
||||
modes_builder.push_mode_update(
|
||||
ModeId::MAIN,
|
||||
ModeUpdate::zone(
|
||||
gameplay_modes::ModeId::MAIN,
|
||||
gameplay_modes::ModeUpdate::zone(
|
||||
model_id,
|
||||
Zone::Anticheat,
|
||||
gameplay_modes::Zone::Anticheat,
|
||||
),
|
||||
);
|
||||
},
|
||||
"Platform"=>{
|
||||
modes_builder.push_mode_update(
|
||||
ModeId::MAIN,
|
||||
ModeUpdate::element(
|
||||
gameplay_modes::ModeId::MAIN,
|
||||
gameplay_modes::ModeUpdate::element(
|
||||
model_id,
|
||||
StageElement::new(StageId::FIRST,false,StageElementBehaviour::Platform,None),//roblox does not know which stage the platform belongs to
|
||||
gameplay_modes::StageElement::new(gameplay_modes::StageId::FIRST,false,gameplay_modes::StageElementBehaviour::Platform,None),//roblox does not know which stage the platform belongs to
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -126,8 +213,8 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
force_can_collide=false;
|
||||
force_intersecting=true;
|
||||
modes_builder.insert_mode(
|
||||
ModeId::new(captures[2].parse::<u32>().unwrap()),
|
||||
Mode::empty(
|
||||
gameplay_modes::ModeId::new(captures[2].parse::<u32>().unwrap()),
|
||||
gameplay_modes::Mode::empty(
|
||||
gameplay_style::StyleModifiers::roblox_bhop(),
|
||||
model_id
|
||||
)
|
||||
@ -144,8 +231,8 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
}else if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$")
|
||||
.captures(other){
|
||||
force_intersecting=true;
|
||||
let stage_id=StageId::new(captures[3].parse::<u32>().unwrap());
|
||||
let stage_element=StageElement::new(
|
||||
let stage_id=gameplay_modes::StageId::new(captures[3].parse::<u32>().unwrap());
|
||||
let stage_element=gameplay_modes::StageElement::new(
|
||||
//stage_id:
|
||||
stage_id,
|
||||
//force:
|
||||
@ -157,26 +244,26 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
match &captures[2]{
|
||||
"Spawn"=>{
|
||||
modes_builder.insert_stage(
|
||||
ModeId::MAIN,
|
||||
gameplay_modes::ModeId::MAIN,
|
||||
stage_id,
|
||||
Stage::empty(model_id),
|
||||
gameplay_modes::Stage::empty(model_id),
|
||||
);
|
||||
//TODO: let denormalize handle this
|
||||
StageElementBehaviour::SpawnAt
|
||||
gameplay_modes::StageElementBehaviour::SpawnAt
|
||||
},
|
||||
"SpawnAt"=>StageElementBehaviour::SpawnAt,
|
||||
"SpawnAt"=>gameplay_modes::StageElementBehaviour::SpawnAt,
|
||||
//cancollide false so you don't hit the side
|
||||
//NOT a decoration
|
||||
"Trigger"=>{force_can_collide=false;StageElementBehaviour::Trigger},
|
||||
"Teleport"=>{force_can_collide=false;StageElementBehaviour::Teleport},
|
||||
"Platform"=>StageElementBehaviour::Platform,
|
||||
"Trigger"=>{force_can_collide=false;gameplay_modes::StageElementBehaviour::Trigger},
|
||||
"Teleport"=>{force_can_collide=false;gameplay_modes::StageElementBehaviour::Teleport},
|
||||
"Platform"=>gameplay_modes::StageElementBehaviour::Platform,
|
||||
_=>panic!("regex1[2] messed up bad"),
|
||||
},
|
||||
None
|
||||
);
|
||||
modes_builder.push_mode_update(
|
||||
ModeId::MAIN,
|
||||
ModeUpdate::element(
|
||||
gameplay_modes::ModeId::MAIN,
|
||||
gameplay_modes::ModeUpdate::element(
|
||||
model_id,
|
||||
stage_element,
|
||||
),
|
||||
@ -185,14 +272,14 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
.captures(other){
|
||||
match &captures[1]{
|
||||
"Jump"=>modes_builder.push_mode_update(
|
||||
ModeId::MAIN,
|
||||
ModeUpdate::element(
|
||||
gameplay_modes::ModeId::MAIN,
|
||||
gameplay_modes::ModeUpdate::element(
|
||||
model_id,
|
||||
//jump_limit:
|
||||
StageElement::new(
|
||||
StageId::FIRST,
|
||||
gameplay_modes::StageElement::new(
|
||||
gameplay_modes::StageId::FIRST,
|
||||
false,
|
||||
StageElementBehaviour::Check,
|
||||
gameplay_modes::StageElementBehaviour::Check,
|
||||
Some(captures[2].parse::<u8>().unwrap())
|
||||
)
|
||||
),
|
||||
@ -209,13 +296,13 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
force_can_collide=false;
|
||||
force_intersecting=true;
|
||||
modes_builder.push_mode_update(
|
||||
ModeId::new(captures[2].parse::<u32>().unwrap()),
|
||||
ModeUpdate::zone(
|
||||
gameplay_modes::ModeId::new(captures[2].parse::<u32>().unwrap()),
|
||||
gameplay_modes::ModeUpdate::zone(
|
||||
model_id,
|
||||
//zone:
|
||||
match &captures[1]{
|
||||
"Finish"=>Zone::Finish,
|
||||
"Anticheat"=>Zone::Anticheat,
|
||||
"Finish"=>gameplay_modes::Zone::Finish,
|
||||
"Anticheat"=>gameplay_modes::Zone::Anticheat,
|
||||
_=>panic!("regex3[1] messed up bad"),
|
||||
},
|
||||
),
|
||||
@ -225,9 +312,9 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
// .captures(other){
|
||||
// match &captures[1]{
|
||||
// "OrderedCheckpoint"=>modes_builder.push_stage_update(
|
||||
// ModeId::MAIN,
|
||||
// StageId::new(0),
|
||||
// StageUpdate::ordered_checkpoint(captures[2].parse::<u32>().unwrap()),
|
||||
// gameplay_modes::ModeId::MAIN,
|
||||
// gameplay_modes::StageId::new(0),
|
||||
// gameplay_modes::StageUpdate::ordered_checkpoint(captures[2].parse::<u32>().unwrap()),
|
||||
// ),
|
||||
// _=>panic!("regex3[1] messed up bad"),
|
||||
// }
|
||||
@ -922,7 +1009,7 @@ impl PartialMap1<'_>{
|
||||
PartialMap2{
|
||||
meshes:self.primitive_meshes,
|
||||
models,
|
||||
modes:modes_builder.build_normalized(),
|
||||
modes:modes_builder.build(),
|
||||
attributes:unique_attributes,
|
||||
}
|
||||
}
|
||||
@ -931,7 +1018,7 @@ impl PartialMap1<'_>{
|
||||
pub struct PartialMap2{
|
||||
meshes:Vec<model::Mesh>,
|
||||
models:Vec<model::Model>,
|
||||
modes:NormalizedModes,
|
||||
modes:gameplay_modes::Modes,
|
||||
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
|
||||
}
|
||||
impl PartialMap2{
|
||||
|
@ -138,7 +138,7 @@ struct MapHeader{
|
||||
//#[br(count=num_resources_external)]
|
||||
//external_resources:Vec<ResourceExternalHeader>,
|
||||
#[br(count=num_modes)]
|
||||
modes:Vec<newtypes::gameplay_modes::NormalizedMode>,
|
||||
modes:Vec<newtypes::gameplay_modes::Mode>,
|
||||
#[br(count=num_attributes)]
|
||||
attributes:Vec<newtypes::gameplay_attributes::CollisionAttributes>,
|
||||
#[br(count=num_render_configs)]
|
||||
@ -181,7 +181,7 @@ fn read_texture<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)
|
||||
pub struct StreamableMap<R:BinReaderExt>{
|
||||
file:crate::file::File<R>,
|
||||
//this includes every platform... move the unconstrained datas to their appropriate data block?
|
||||
modes:gameplay_modes::NormalizedModes,
|
||||
modes:gameplay_modes::Modes,
|
||||
//this is every possible attribute... need some sort of streaming system
|
||||
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
|
||||
//this is every possible render configuration... shaders and such... need streaming
|
||||
@ -223,7 +223,7 @@ impl<R:BinReaderExt> StreamableMap<R>{
|
||||
}
|
||||
Ok(Self{
|
||||
file,
|
||||
modes:strafesnet_common::gameplay_modes::NormalizedModes::new(modes),
|
||||
modes:strafesnet_common::gameplay_modes::Modes::new(modes),
|
||||
attributes,
|
||||
render_configs,
|
||||
bvh:strafesnet_common::bvh::generate_bvh(bvh),
|
||||
@ -430,13 +430,13 @@ pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::Comple
|
||||
num_spacial_blocks:spacial_blocks.len() as u32,
|
||||
num_resource_blocks:resource_blocks.len() as u32,
|
||||
//num_resources_external:0,
|
||||
num_modes:map.modes.len() as u32,
|
||||
num_modes:map.modes.modes.len() as u32,
|
||||
num_attributes:map.attributes.len() as u32,
|
||||
num_render_configs:map.render_configs.len() as u32,
|
||||
spacial_blocks,
|
||||
resource_blocks,
|
||||
//external_resources:Vec::new(),
|
||||
modes:map.modes.into_iter().map(Into::into).collect(),
|
||||
modes:map.modes.modes.into_iter().map(Into::into).collect(),
|
||||
attributes:map.attributes.into_iter().map(Into::into).collect(),
|
||||
render_configs:map.render_configs.into_iter().map(Into::into).collect(),
|
||||
};
|
||||
|
@ -157,7 +157,7 @@ pub struct ModeHeader{
|
||||
}
|
||||
#[binrw::binrw]
|
||||
#[brw(little)]
|
||||
pub struct NormalizedMode{
|
||||
pub struct Mode{
|
||||
pub header:ModeHeader,
|
||||
pub style:super::gameplay_style::StyleModifiers,
|
||||
pub start:u32,
|
||||
@ -179,10 +179,10 @@ impl std::fmt::Display for ModeError{
|
||||
}
|
||||
}
|
||||
impl std::error::Error for ModeError{}
|
||||
impl TryInto<strafesnet_common::gameplay_modes::NormalizedMode> for NormalizedMode{
|
||||
impl TryInto<strafesnet_common::gameplay_modes::Mode> for Mode{
|
||||
type Error=ModeError;
|
||||
fn try_into(self)->Result<strafesnet_common::gameplay_modes::NormalizedMode,Self::Error>{
|
||||
Ok(strafesnet_common::gameplay_modes::NormalizedMode::new(strafesnet_common::gameplay_modes::Mode::new(
|
||||
fn try_into(self)->Result<strafesnet_common::gameplay_modes::Mode,Self::Error>{
|
||||
Ok(strafesnet_common::gameplay_modes::Mode::new(
|
||||
self.style.try_into().map_err(ModeError::StyleModifier)?,
|
||||
strafesnet_common::model::ModelId::new(self.start),
|
||||
self.zones.into_iter().map(|(model_id,zone)|
|
||||
@ -192,12 +192,12 @@ impl TryInto<strafesnet_common::gameplay_modes::NormalizedMode> for NormalizedMo
|
||||
self.elements.into_iter().map(|(model_id,stage_element)|
|
||||
Ok((strafesnet_common::model::ModelId::new(model_id),stage_element.try_into()?))
|
||||
).collect::<Result<_,_>>().map_err(ModeError::StageElement)?,
|
||||
)))
|
||||
))
|
||||
}
|
||||
}
|
||||
impl From<strafesnet_common::gameplay_modes::NormalizedMode> for NormalizedMode{
|
||||
fn from(value:strafesnet_common::gameplay_modes::NormalizedMode)->Self{
|
||||
let (style,start,zones,stages,elements)=value.into_inner().into_inner();
|
||||
impl From<strafesnet_common::gameplay_modes::Mode> for Mode{
|
||||
fn from(value:strafesnet_common::gameplay_modes::Mode)->Self{
|
||||
let (style,start,zones,stages,elements)=value.into_inner();
|
||||
Self{
|
||||
header:ModeHeader{
|
||||
zones:zones.len() as u32,
|
||||
|
@ -25,11 +25,10 @@ strafesnet_rbx_loader = { version = "0.6.0", path = "../lib/rbx_loader", registr
|
||||
strafesnet_snf = { version = "0.3.0", path = "../lib/snf", registry = "strafesnet" }
|
||||
thiserror = "2.0.11"
|
||||
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "fs"] }
|
||||
vbsp = "0.8.0"
|
||||
vbsp-entities-css = "0.6.0"
|
||||
vbsp = "0.6.0"
|
||||
vmdl = "0.2.0"
|
||||
vmt-parser = "0.2.0"
|
||||
vpk = "0.3.0"
|
||||
vpk = "0.2.0"
|
||||
vtf = "0.3.0"
|
||||
|
||||
#[profile.release]
|
||||
|
@ -6,7 +6,6 @@ use futures::StreamExt;
|
||||
use strafesnet_bsp_loader::loader::BspFinder;
|
||||
use strafesnet_deferred_loader::loader::Loader;
|
||||
use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLoader,RenderConfigDeferredLoader};
|
||||
use vbsp_entities_css::Entity;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Commands{
|
||||
@ -245,57 +244,8 @@ async fn gimme_them_textures(path:&Path,vpk_list:&[strafesnet_bsp_loader::Vpk],s
|
||||
}
|
||||
|
||||
let mut mesh_deferred_loader=MeshDeferredLoader::new();
|
||||
for name in &bsp.static_props.dict.name{
|
||||
mesh_deferred_loader.acquire_mesh_id(name.as_str());
|
||||
}
|
||||
|
||||
for raw_ent in &bsp.entities{
|
||||
let model=match raw_ent.parse(){
|
||||
Ok(Entity::Cycler(brush))=>brush.model,
|
||||
Ok(Entity::EnvSprite(brush))=>brush.model,
|
||||
Ok(Entity::FuncBreakable(brush))=>brush.model,
|
||||
Ok(Entity::FuncBrush(brush))=>brush.model,
|
||||
Ok(Entity::FuncButton(brush))=>brush.model,
|
||||
Ok(Entity::FuncDoor(brush))=>brush.model,
|
||||
Ok(Entity::FuncDoorRotating(brush))=>brush.model,
|
||||
Ok(Entity::FuncIllusionary(brush))=>brush.model,
|
||||
Ok(Entity::FuncMonitor(brush))=>brush.model,
|
||||
Ok(Entity::FuncMovelinear(brush))=>brush.model,
|
||||
Ok(Entity::FuncPhysbox(brush))=>brush.model,
|
||||
Ok(Entity::FuncPhysboxMultiplayer(brush))=>brush.model,
|
||||
Ok(Entity::FuncRotButton(brush))=>brush.model,
|
||||
Ok(Entity::FuncRotating(brush))=>brush.model,
|
||||
Ok(Entity::FuncTracktrain(brush))=>brush.model,
|
||||
Ok(Entity::FuncTrain(brush))=>brush.model,
|
||||
Ok(Entity::FuncWall(brush))=>brush.model,
|
||||
Ok(Entity::FuncWallToggle(brush))=>brush.model,
|
||||
Ok(Entity::FuncWaterAnalog(brush))=>brush.model,
|
||||
Ok(Entity::PropDoorRotating(brush))=>brush.model,
|
||||
Ok(Entity::PropDynamic(brush))=>brush.model,
|
||||
Ok(Entity::PropDynamicOverride(brush))=>brush.model,
|
||||
Ok(Entity::PropPhysics(brush))=>brush.model,
|
||||
Ok(Entity::PropPhysicsMultiplayer(brush))=>brush.model,
|
||||
Ok(Entity::PropPhysicsOverride(brush))=>brush.model,
|
||||
Ok(Entity::PropRagdoll(brush))=>brush.model,
|
||||
Ok(Entity::TriggerGravity(brush))=>brush.model,
|
||||
Ok(Entity::TriggerHurt(brush))=>brush.model,
|
||||
Ok(Entity::TriggerLook(brush))=>brush.model,
|
||||
Ok(Entity::TriggerMultiple(brush))=>brush.model.unwrap_or_default(),
|
||||
Ok(Entity::TriggerOnce(brush))=>brush.model,
|
||||
Ok(Entity::TriggerProximity(brush))=>brush.model,
|
||||
Ok(Entity::TriggerPush(brush))=>brush.model,
|
||||
Ok(Entity::TriggerSoundscape(brush))=>brush.model,
|
||||
Ok(Entity::TriggerTeleport(brush))=>brush.model.unwrap_or_default(),
|
||||
Ok(Entity::TriggerVphysicsMotion(brush))=>brush.model,
|
||||
Ok(Entity::TriggerWind(brush))=>brush.model,
|
||||
_=>continue,
|
||||
};
|
||||
match model.chars().next(){
|
||||
Some('*')=>(),
|
||||
_=>{
|
||||
mesh_deferred_loader.acquire_mesh_id(model);
|
||||
},
|
||||
}
|
||||
for prop in bsp.static_props(){
|
||||
mesh_deferred_loader.acquire_mesh_id(prop.model());
|
||||
}
|
||||
|
||||
let finder=BspFinder{
|
||||
|
Reference in New Issue
Block a user