diff --git a/Cargo.lock b/Cargo.lock index 5be6aee..c407d0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,9 +8,55 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" +[[package]] +name = "id" +version = "0.1.0" +source = "git+https://git.itzana.me/Quaternions/id?rev=1f710976cc786c8853dab73d6e1cee53158deeb0#1f710976cc786c8853dab73d6e1cee53158deeb0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + [[package]] name = "strafesnet_common" version = "0.1.0" dependencies = [ "glam", + "id", ] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml index a3628d1..d671ba1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] glam = "0.25.0" +id = { git = "https://git.itzana.me/Quaternions/id", rev = "1f710976cc786c8853dab73d6e1cee53158deeb0" } diff --git a/src/bvh.rs b/src/bvh.rs index f4c9bc6..5235790 100644 --- a/src/bvh.rs +++ b/src/bvh.rs @@ -9,23 +9,24 @@ use crate::aabb::Aabb; //start with bisection into octrees because a bad bvh is still 1000x better than no bvh //sort the centerpoints on each axis (3 lists) //bv is put into octant based on whether it is upper or lower in each list -enum BvhNodeContent{ - Branch(Vec), - Leaf(usize), + +enum BvhNodeContent{ + Branch(Vec>), + Leaf(T), } -impl Default for BvhNodeContent{ +impl Default for BvhNodeContent{ fn default()->Self{ Self::Branch(Vec::new()) } } #[derive(Default)] -pub struct BvhNode{ - content:BvhNodeContent, +pub struct BvhNode{ + content:BvhNodeContent, aabb:Aabb, } -impl BvhNode{ - pub fn the_tester(&self,aabb:&Aabb,f:&mut F){ +impl BvhNode{ + pub fn the_tester(&self,aabb:&Aabb,f:&mut F){ match &self.content{ &BvhNodeContent::Leaf(model)=>f(model), BvhNodeContent::Branch(children)=>for child in children{ @@ -41,13 +42,13 @@ impl BvhNode{ } } -pub fn generate_bvh(boxen:Vec)->BvhNode{ - generate_bvh_node(boxen.into_iter().enumerate().collect()) +pub fn generate_bvh(boxen:Vec<(T,Aabb)>)->BvhNode{ + generate_bvh_node(boxen,false) } -fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{ +fn generate_bvh_node(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode{ let n=boxen.len(); - if n<20{ + if force||n<20{ let mut aabb=Aabb::default(); let nodes=boxen.into_iter().map(|b|{ aabb.join(&b.1); @@ -110,14 +111,19 @@ fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{ list_list[list_id].push((i,aabb)); } let mut aabb=Aabb::default(); - let children=list_list.into_iter().map(|b|{ - let node=generate_bvh_node(b); - aabb.join(&node.aabb); - node - }).collect(); - BvhNode{ - content:BvhNodeContent::Branch(children), - aabb, + if list_list.len()==1{ + generate_bvh_node(list_list.remove(0),true) + }else{ + BvhNode{ + content:BvhNodeContent::Branch( + list_list.into_iter().map(|b|{ + let node=generate_bvh_node(b,false); + aabb.join(&node.aabb); + node + }).collect() + ), + aabb, + } } } } diff --git a/src/gameplay_attributes.rs b/src/gameplay_attributes.rs index 6756e45..1e919f9 100644 --- a/src/gameplay_attributes.rs +++ b/src/gameplay_attributes.rs @@ -1,3 +1,4 @@ +use crate::model; use crate::integer::{Time,Planar64,Planar64Vec3}; //you have this effect while in contact @@ -8,8 +9,9 @@ pub struct ContactingLadder{ #[derive(Clone,Hash,Eq,PartialEq)] pub enum ContactingBehaviour{ Surf, - Cling,//usable as a zipline, or other weird and wonderful things Ladder(ContactingLadder), + NoJump, + Cling,//usable as a zipline, or other weird and wonderful things Elastic(u32),//[1/2^32,1] 0=None (elasticity+1)/2^32 } //you have this effect while intersecting @@ -54,7 +56,7 @@ pub enum SetTrajectory{ Velocity(Planar64Vec3),//SetVelocity } impl SetTrajectory{ - fn is_velocity(&self)->bool{ + pub const fn is_absolute(&self)->bool{ match self{ SetTrajectory::AirTime(_) |SetTrajectory::Height(_) @@ -76,7 +78,7 @@ pub struct Wormhole{ //destination does not need to be another wormhole //this defines a one way portal to a destination model transform //two of these can create a two way wormhole - pub destination_model_id:u32, + pub destination_model:model::ModelId, //(position,angles)*=origin.transform.inverse()*destination.transform } //attributes listed in order of handling @@ -88,14 +90,14 @@ pub struct GeneralAttributes{ pub accelerator:Option, } impl GeneralAttributes{ - pub fn any(&self)->bool{ + pub const fn any(&self)->bool{ self.booster.is_some() ||self.trajectory.is_some() ||self.wormhole.is_some() ||self.accelerator.is_some() } pub fn is_wrcp(&self)->bool{ - self.trajectory.as_ref().map_or(false,|t|t.is_velocity()) + self.trajectory.as_ref().map_or(false,|t|t.is_absolute()) /* &&match &self.teleport_behaviour{ Some(TeleportBehaviour::StageElement( @@ -117,7 +119,7 @@ pub struct ContactingAttributes{ pub contact_behaviour:Option, } impl ContactingAttributes{ - pub fn any(&self)->bool{ + pub const fn any(&self)->bool{ self.contact_behaviour.is_some() } } @@ -126,11 +128,13 @@ pub struct IntersectingAttributes{ pub water:Option, } impl IntersectingAttributes{ - pub fn any(&self)->bool{ + pub const fn any(&self)->bool{ self.water.is_some() } } +#[derive(Clone,Copy,id::Id,Hash,Eq,PartialEq)] pub struct CollisionAttributesId(u32); +#[derive(Clone,Hash,Eq,PartialEq)] pub enum CollisionAttributes{ Decoration,//visual only Contact{//track whether you are contacting the object diff --git a/src/gameplay_modes.rs b/src/gameplay_modes.rs index a06fbb2..7b4ebb9 100644 --- a/src/gameplay_modes.rs +++ b/src/gameplay_modes.rs @@ -1,14 +1,38 @@ use std::collections::{HashSet,HashMap}; use crate::model::ModelId; use crate::gameplay_style; +use crate::updatable::Updatable; +#[derive(Clone)] pub struct StageElement{ - stage:StageId,//which stage spawn to send to + stage_id:StageId,//which stage spawn to send to force:bool,//allow setting to lower spawn id i.e. 7->3 behaviour:StageElementBehaviour } +impl StageElement{ + #[inline] + pub const fn new(stage_id:StageId,force:bool,behaviour:StageElementBehaviour)->Self{ + Self{ + stage_id, + force, + behaviour, + } + } + #[inline] + pub const fn stage_id(&self)->StageId{ + self.stage_id + } + #[inline] + pub const fn force(&self)->bool{ + self.force + } + #[inline] + pub const fn behaviour(&self)->StageElementBehaviour{ + self.behaviour + } +} -#[derive(Clone,Hash,Eq,PartialEq)] +#[derive(Clone,Copy,Hash,Eq,PartialEq)] pub enum StageElementBehaviour{ SpawnAt,//must be standing on top to get effect. except cancollide false Trigger, @@ -20,51 +44,150 @@ pub enum StageElementBehaviour{ Checkpoint,//this is a combined behaviour for Ordered & Unordered in case a model is used multiple times or for both. } +#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)] +pub struct CheckpointId(u32); +impl CheckpointId{ + pub const FIRST:Self=Self(0); +} +#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq,Ord,PartialOrd)] pub struct StageId(u32); +impl StageId{ + pub const FIRST:Self=Self(0); +} +#[derive(Clone)] pub struct Stage{ spawn:ModelId, - //other behaviour models of this stage can have - ordered_checkpoints:Vec, + //open world support lol + ordered_checkpoints_count:u32, + unordered_checkpoints_count:u32, + //currently loaded checkpoint models + ordered_checkpoints:HashMap, unordered_checkpoints:HashSet, } - -#[derive(Clone,Hash,Eq,PartialEq)] -pub enum ZoneBehaviour{ - Finish, - Anitcheat, +impl Stage{ + pub fn new(spawn:ModelId)->Self{ + Self{ + spawn, + ordered_checkpoints_count:0, + unordered_checkpoints_count:0, + ordered_checkpoints:HashMap::new(), + unordered_checkpoints:HashSet::new(), + } + } + #[inline] + pub const fn spawn(&self)->ModelId{ + self.spawn + } + #[inline] + pub const fn is_empty(&self)->bool{ + self.is_complete(0,0) + } + #[inline] + pub const fn is_complete(&self,ordered_checkpoints_count:u32,unordered_checkpoints_count:u32)->bool{ + self.ordered_checkpoints_count==ordered_checkpoints_count&&self.unordered_checkpoints_count==unordered_checkpoints_count + } + #[inline] + pub fn is_next_ordered_checkpoint(&self,next_ordered_checkpoint_id:CheckpointId,model_id:ModelId)->bool{ + self.ordered_checkpoints.get(&next_ordered_checkpoint_id).is_some_and(|&next_checkpoint|model_id==next_checkpoint) + } + #[inline] + pub fn is_unordered_checkpoint(&self,model_id:ModelId)->bool{ + self.unordered_checkpoints.contains(&model_id) + } } +#[derive(Default)] +pub struct StageUpdate{ + //other behaviour models of this stage can have + ordered_checkpoints:HashMap, + unordered_checkpoints:HashSet, +} +impl Updatable 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{ + Start, + Finish, + Anticheat, +} +#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq,Ord,PartialOrd)] pub struct ModeId(u32); +impl ModeId{ + pub const MAIN:Self=Self(0); + pub const BONUS:Self=Self(1); +} +#[derive(Clone)] pub struct Mode{ style:gameplay_style::StyleModifiers, - start:ModelId, - zones:HashMap, - stages:Vec, + start:ModelId,//when you press reset you go here + zones:HashMap, + stages:Vec,//when you load the map you go to stages[0].spawn //mutually exlusive stage element behaviour elements:HashMap, jump_limit:HashMap, } impl Mode{ + pub fn new(style:gameplay_style::StyleModifiers,start:ModelId)->Self{ + Self{ + style, + start, + zones:HashMap::new(), + stages:Vec::new(), + elements:HashMap::new(), + jump_limit:HashMap::new(), + } + } + pub const fn get_start(&self)->ModelId{ + self.start + } + pub const fn get_style(&self)->&gameplay_style::StyleModifiers{ + &self.style + } + pub fn push_stage(&mut self,stage:Stage){ + self.stages.push(stage) + } + 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,stage:StageId)->Option{ 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,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) + } + pub fn get_jump_limit(&self,model_id:ModelId)->Option{ + self.jump_limit.get(&model_id).copied() + } + //TODO: put this in the SNF pub fn denormalize_data(&mut self){ //expand and index normalized data + self.zones.insert(self.start,Zone::Start); for (stage_id,stage) in self.stages.iter().enumerate(){ self.elements.insert(stage.spawn,StageElement{ - stage:StageId(stage_id as u32), + stage_id:StageId(stage_id as u32), force:false, behaviour:StageElementBehaviour::SpawnAt, }); - for &model_id in &stage.ordered_checkpoints{ - self.elements.insert(model_id,StageElement{ - stage:StageId(stage_id as u32), + for (_,&model) in &stage.ordered_checkpoints{ + self.elements.insert(model,StageElement{ + stage_id:StageId(stage_id as u32), force:false, behaviour:StageElementBehaviour::Checkpoint, }); } - for &model_id in &stage.unordered_checkpoints{ - self.elements.insert(model_id,StageElement{ - stage:StageId(stage_id as u32), + for &model in &stage.unordered_checkpoints{ + self.elements.insert(model,StageElement{ + stage_id:StageId(stage_id as u32), force:false, behaviour:StageElementBehaviour::Checkpoint, }); @@ -72,19 +195,81 @@ impl Mode{ } } } - +//this would be nice as a macro #[derive(Default)] +pub struct ModeUpdate{ + zones:HashMap, + stages:HashMap, + //mutually exlusive stage element behaviour + elements:HashMap, + jump_limit:HashMap, +} +impl Updatable 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); + self.jump_limit.extend(update.jump_limit); + } +} +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 jump_limit(model_id:ModelId,jump_limit:u32)->Self{ + let mut mu=Self::default(); + mu.jump_limit.insert(model_id,jump_limit); + mu + } + pub fn map_stage_element_idsStageId>(&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, } impl Modes{ - pub fn clear(&mut self){ - self.modes.clear(); + pub fn new(modes:Vec)->Self{ + Self{ + modes, + } + } + pub fn push_mode(&mut self,mode:Mode){ + self.modes.push(mode) } pub fn get_mode(&self,mode:ModeId)->Option<&Mode>{ self.modes.get(mode.0 as usize) } - pub fn insert(&mut self,mode:Mode){ - self.modes.push(mode); +} +pub struct ModesUpdate{ + modes:HashMap, +} +impl Updatable 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); + } + } } } \ No newline at end of file diff --git a/src/gameplay_style.rs b/src/gameplay_style.rs index 318fb10..4a6e179 100644 --- a/src/gameplay_style.rs +++ b/src/gameplay_style.rs @@ -1,28 +1,29 @@ -const VALVE_SCALE:i64=16; +const VALVE_SCALE:Planar64=Planar64::raw(1<<28);// 1/16 use crate::integer::{Time,Ratio64,Planar64,Planar64Vec3}; +#[derive(Clone)] pub struct StyleModifiers{ - controls_used:u32,//controls which are allowed to pass into gameplay - controls_mask:u32,//controls which are masked from control state (e.g. jump in scroll style) - strafe:Option, - jump_impulse:JumpImpulse, - jump_calculation:JumpCalculation, - static_friction:Planar64, - kinetic_friction:Planar64, - walk_speed:Planar64, - walk_accel:Planar64, - ladder_speed:Planar64, - ladder_accel:Planar64, - ladder_dot:Planar64, - swim_speed:Planar64, - mass:Planar64, - mv:Planar64, - surf_slope:Option, - rocket_force:Option, - gravity:Planar64Vec3, - hitbox:Hitbox, - camera_offset:Planar64Vec3, + pub controls_used:u32,//controls which are allowed to pass into gameplay + pub controls_mask:u32,//controls which are masked from control state (e.g. jump in scroll style) + pub strafe:Option, + pub jump_impulse:JumpImpulse, + pub jump_calculation:JumpCalculation, + pub static_friction:Planar64, + pub kinetic_friction:Planar64, + pub walk_speed:Planar64, + pub walk_accel:Planar64, + pub ladder_speed:Planar64, + pub ladder_accel:Planar64, + pub ladder_dot:Planar64, + pub swim_speed:Planar64, + pub mass:Planar64, + pub mv:Planar64, + pub surf_slope:Option, + pub rocket_force:Option, + pub gravity:Planar64Vec3, + pub hitbox:Hitbox, + pub camera_offset:Planar64Vec3, } impl std::default::Default for StyleModifiers{ fn default()->Self{ @@ -30,18 +31,18 @@ impl std::default::Default for StyleModifiers{ } } impl StyleModifiers{ - const CONTROL_MOVEFORWARD:u32=0b00000001; - const CONTROL_MOVEBACK:u32=0b00000010; - const CONTROL_MOVERIGHT:u32=0b00000100; - const CONTROL_MOVELEFT:u32=0b00001000; - const CONTROL_MOVEUP:u32=0b00010000; - const CONTROL_MOVEDOWN:u32=0b00100000; - const CONTROL_JUMP:u32=0b01000000; - const CONTROL_ZOOM:u32=0b10000000; + pub const CONTROL_MOVEFORWARD:u32=0b00000001; + pub const CONTROL_MOVEBACK:u32=0b00000010; + pub const CONTROL_MOVERIGHT:u32=0b00000100; + pub const CONTROL_MOVELEFT:u32=0b00001000; + pub const CONTROL_MOVEUP:u32=0b00010000; + pub const CONTROL_MOVEDOWN:u32=0b00100000; + pub const CONTROL_JUMP:u32=0b01000000; + pub const CONTROL_ZOOM:u32=0b10000000; - const RIGHT_DIR:Planar64Vec3=Planar64Vec3::X; - const UP_DIR:Planar64Vec3=Planar64Vec3::Y; - const FORWARD_DIR:Planar64Vec3=Planar64Vec3::NEG_Z; + pub const RIGHT_DIR:Planar64Vec3=Planar64Vec3::X; + pub const UP_DIR:Planar64Vec3=Planar64Vec3::Y; + pub const FORWARD_DIR:Planar64Vec3=Planar64Vec3::NEG_Z; fn neo()->Self{ Self{ @@ -72,7 +73,7 @@ impl StyleModifiers{ } } - fn roblox_bhop()->Self{ + pub fn roblox_bhop()->Self{ Self{ controls_used:!0, controls_mask:!0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN), @@ -138,13 +139,13 @@ impl StyleModifiers{ air_accel_limit:Some(Planar64::raw(150<<28)*100), tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(), }), - jump_impulse:JumpImpulse::FromHeight(Planar64::int(52)/VALVE_SCALE), + jump_impulse:JumpImpulse::FromHeight(Planar64::int(52)*VALVE_SCALE), jump_calculation:JumpCalculation::Linear, - gravity:Planar64Vec3::int(0,-800,0)/VALVE_SCALE, + gravity:Planar64Vec3::int(0,-800,0)*VALVE_SCALE, static_friction:Planar64::int(2),//? kinetic_friction:Planar64::int(3),//? mass:Planar64::int(1), - mv:Planar64::raw(30)/VALVE_SCALE, + mv:Planar64::raw(30)*VALVE_SCALE, rocket_force:None, walk_speed:Planar64::int(18),//? walk_accel:Planar64::int(90),//? @@ -154,7 +155,7 @@ impl StyleModifiers{ swim_speed:Planar64::int(12),//? surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75 hitbox:Hitbox::source(), - camera_offset:(Planar64Vec3::int(0,64,0)-Planar64Vec3::int(0,73,0)/2)/VALVE_SCALE, + camera_offset:(Planar64Vec3::int(0,64,0)-Planar64Vec3::int(0,73,0)/2)*VALVE_SCALE, } } fn source_surf()->Self{ @@ -163,16 +164,16 @@ impl StyleModifiers{ controls_mask:!0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN), strafe:Some(StrafeSettings{ enable:EnableStrafe::Always, - air_accel_limit:Some(Planar64::int(150)*66/VALVE_SCALE), + air_accel_limit:Some(Planar64::int(150)*66*VALVE_SCALE), tick_rate:Ratio64::new(66,Time::ONE_SECOND.nanos() as u64).unwrap(), }), - jump_impulse:JumpImpulse::FromHeight(Planar64::int(52)/VALVE_SCALE), + jump_impulse:JumpImpulse::FromHeight(Planar64::int(52)*VALVE_SCALE), jump_calculation:JumpCalculation::Linear, - gravity:Planar64Vec3::int(0,-800,0)/VALVE_SCALE, + gravity:Planar64Vec3::int(0,-800,0)*VALVE_SCALE, static_friction:Planar64::int(2),//? kinetic_friction:Planar64::int(3),//? mass:Planar64::int(1), - mv:Planar64::int(30)/VALVE_SCALE, + mv:Planar64::int(30)*VALVE_SCALE, rocket_force:None, walk_speed:Planar64::int(18),//? walk_accel:Planar64::int(90),//? @@ -182,7 +183,7 @@ impl StyleModifiers{ swim_speed:Planar64::int(12),//? surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75 hitbox:Hitbox::source(), - camera_offset:(Planar64Vec3::int(0,64,0)-Planar64Vec3::int(0,73,0)/2)/VALVE_SCALE, + camera_offset:(Planar64Vec3::int(0,64,0)-Planar64Vec3::int(0,73,0)/2)*VALVE_SCALE, } } fn roblox_rocket()->Self{ @@ -211,13 +212,15 @@ impl StyleModifiers{ } } -enum JumpCalculation{ +#[derive(Clone)] +pub enum JumpCalculation{ Capped,//roblox Energy,//new Linear,//source } -enum JumpImpulse{ +#[derive(Clone)] +pub enum JumpImpulse{ FromTime(Time),//jump time is invariant across mass and gravity changes FromHeight(Planar64),//jump height is invariant across mass and gravity changes FromDeltaV(Planar64),//jump velocity is invariant across mass and gravity changes @@ -228,20 +231,35 @@ enum JumpImpulse{ //Energy means it adds energy //Linear means it linearly adds on -enum EnableStrafe{ +#[derive(Clone)] +pub enum EnableStrafe{ Always, MaskAny(u32),//hsw, shsw MaskAll(u32), //Function(Boxbool>), } -struct StrafeSettings{ +#[derive(Clone)] +pub struct StrafeSettings{ enable:EnableStrafe, air_accel_limit:Option, tick_rate:Ratio64, } +impl StrafeSettings{ + pub fn next_tick(&self,time:Time)->Time{ + Time::from_nanos(self.tick_rate.rhs_div_int(self.tick_rate.mul_int(time.nanos())+1)) + } + pub fn mask(&self,controls:u32)->bool{ + match self.enable{ + EnableStrafe::Always=>true, + EnableStrafe::MaskAny(mask)=>mask&controls!=0, + EnableStrafe::MaskAll(mask)=>mask&controls==mask, + } + } +} -enum HitboxMesh{ +#[derive(Clone)] +pub enum HitboxMesh{ Box,//source Cylinder,//roblox //Sphere,//roblox old physics @@ -250,9 +268,10 @@ enum HitboxMesh{ //DualCone, } -struct Hitbox{ - halfsize:Planar64Vec3, - mesh:HitboxMesh, +#[derive(Clone)] +pub struct Hitbox{ + pub halfsize:Planar64Vec3, + pub mesh:HitboxMesh, } impl Hitbox{ fn roblox()->Self{ @@ -263,7 +282,7 @@ impl Hitbox{ } fn source()->Self{ Self{ - halfsize:Planar64Vec3::raw(33,73,33)/2/VALVE_SCALE, + halfsize:Planar64Vec3::raw(33,73,33)/2*VALVE_SCALE, mesh:HitboxMesh::Box, } } diff --git a/src/integer.rs b/src/integer.rs index b04376a..844cf4e 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -294,9 +294,10 @@ impl std::ops::Mul for Ratio64Vec2{ pub struct Angle32(i32); impl Angle32{ pub const FRAC_PI_2:Self=Self(1<<30); + pub const NEG_FRAC_PI_2:Self=Self(-1<<30); pub const PI:Self=Self(-1<<31); #[inline] - pub fn wrap_from_i64(theta:i64)->Self{ + pub const fn wrap_from_i64(theta:i64)->Self{ //take lower bits //note: this was checked on compiler explorer and compiles to 1 instruction! Self(i32::from_ne_bytes(((theta&((1<<32)-1)) as u32).to_ne_bytes())) @@ -308,7 +309,7 @@ impl Angle32{ Self(theta.clamp(i32::MIN as i64,i32::MAX as i64) as i32) } #[inline] - pub fn get(&self)->i32{ + pub const fn get(&self)->i32{ self.0 } /// Clamps the value towards the midpoint of the range. @@ -411,7 +412,7 @@ impl TryFrom<[f32;3]> for Unit32Vec3{ */ ///[-1.0,1.0] = [-2^32,2^32] -#[derive(Clone,Copy,Hash,Eq,Ord,PartialEq,PartialOrd)] +#[derive(Clone,Copy,Debug,Hash,Eq,Ord,PartialEq,PartialOrd)] pub struct Planar64(i64); impl Planar64{ pub const ZERO:Self=Self(0); @@ -579,7 +580,7 @@ impl std::ops::Div for Planar64{ ///[-1.0,1.0] = [-2^32,2^32] -#[derive(Clone,Copy,Default,Hash,Eq,PartialEq)] +#[derive(Clone,Copy,Debug,Default,Hash,Eq,PartialEq)] pub struct Planar64Vec3(glam::I64Vec3); impl Planar64Vec3{ pub const ZERO:Self=Planar64Vec3(glam::I64Vec3::ZERO); diff --git a/src/lib.rs b/src/lib.rs index 99b060b..b567481 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,10 @@ pub mod bvh; +pub mod map; pub mod aabb; pub mod model; pub mod zeroes; pub mod integer; +pub mod updatable; pub mod instruction; pub mod gameplay_modes; pub mod gameplay_style; diff --git a/src/map.rs b/src/map.rs new file mode 100644 index 0000000..d567b8f --- /dev/null +++ b/src/map.rs @@ -0,0 +1,13 @@ +use crate::model; +use crate::gameplay_modes; +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::Modes, + pub attributes:Vec, + pub meshes:Vec, + pub models:Vec, + //RenderPattern + pub render_configs:Vec, +} diff --git a/src/model.rs b/src/model.rs index cbf7370..30d5361 100644 --- a/src/model.rs +++ b/src/model.rs @@ -3,51 +3,126 @@ use crate::gameplay_attributes; pub type TextureCoordinate=glam::Vec2; pub type Color4=glam::Vec4; +#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)] +pub struct PositionId(u32); +#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)] +pub struct TextureCoordinateId(u32); +#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)] +pub struct NormalId(u32); +#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)] +pub struct ColorId(u32); #[derive(Clone,Hash,PartialEq,Eq)] pub struct IndexedVertex{ - pub pos:u32, - pub tex:u32, - pub normal:u32, - pub color:u32, + pub pos:PositionId, + pub tex:TextureCoordinateId, + pub normal:NormalId, + pub color:ColorId, } +#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)] pub struct VertexId(u32); -pub struct IndexedVertexList{ - pub vertices:Vec, +pub type IndexedVertexList=Vec; +pub trait PolygonIter{ + fn polys(&self)->impl Iterator; } -pub struct GroupId(u32); -pub enum IndexedGroup{ - PolygonList(Vec), - //TriangleStrip(Vec), +pub trait MapVertexId{ + fn map_vertex_idVertexId>(self,f:F)->Self; +} +pub struct PolygonList(Vec); +impl PolygonList{ + pub const fn new(list:Vec)->Self{ + Self(list) + } + pub fn extend>(&mut self,iter:T){ + self.0.extend(iter); + } +} +impl PolygonIter for PolygonList{ + fn polys(&self)->impl Iterator{ + self.0.iter().map(|poly|poly.as_slice()) + } +} +impl MapVertexId for PolygonList{ + fn map_vertex_idVertexId>(self,f:F)->Self{ + Self(self.0.into_iter().map(|ivl|ivl.into_iter().map(&f).collect()).collect()) + } +} +// pub struct TriangleStrip(IndexedVertexList); +// impl PolygonIter for TriangleStrip{ +// fn polys(&self)->impl Iterator{ +// self.0.vertices.windows(3).enumerate().map(|(i,s)|if i&0!=0{return s.iter().rev()}else{return s.iter()}) +// } +// } +#[derive(Clone,Copy,Hash,id::Id,PartialEq,Eq)] +pub struct PolygonGroupId(u32); +pub enum PolygonGroup{ + PolygonList(PolygonList), + //TriangleStrip(TriangleStrip), +} +impl PolygonIter for PolygonGroup{ + fn polys(&self)->impl Iterator{ + match self{ + PolygonGroup::PolygonList(list)=>list.polys(), + //PolygonGroup::TriangleStrip(strip)=>strip.polys(), + } + } +} +impl MapVertexId for PolygonGroup{ + fn map_vertex_idVertexId>(self,f:F)->Self{ + match self{ + PolygonGroup::PolygonList(polys)=>Self::PolygonList(polys.map_vertex_id(f)), + } + } +} +/// Ah yes, a group of things to render at the same time +#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)] +pub struct TextureId(u32); +#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)] +pub struct RenderConfigId(u32); +#[derive(Default)] +pub struct RenderConfig{ + pub texture:Option, +} +impl RenderConfig{ + pub const fn texture(texture:TextureId)->Self{ + Self{ + texture:Some(texture), + } + } } -pub struct RenderId(u32); pub struct IndexedGraphicsGroup{ //Render pattern material/texture/shader/flat color - pub render:RenderId, - pub groups:Vec, + pub render:RenderConfigId, + pub groups:Vec, } +#[derive(Default)] pub struct IndexedPhysicsGroup{ //the polygons in this group are guaranteed to make a closed convex shape - pub groups:Vec, + pub groups:Vec, } //This is a superset of PhysicsModel and GraphicsModel -pub struct IndexedModel{ +#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)] +pub struct MeshId(u32); +pub struct Mesh{ pub unique_pos:Vec,//Unit32Vec3 pub unique_normal:Vec,//Unit32Vec3 pub unique_tex:Vec, pub unique_color:Vec, pub unique_vertices:Vec, - //groups are constant texture AND convexity slices - pub groups:Vec, + //polygon groups are constant texture AND convexity slices + //note that this may need to be changed to be a list of individual faces + //for submeshes to work since face ids need to be consistent across submeshes + //so face == polygon_groups[face_id] + pub polygon_groups:Vec, //graphics indexed (by texture) - pub graphics_sets:Vec, + pub graphics_groups:Vec, //physics indexed (by convexity) - pub physics_sets:Vec, + pub physics_groups:Vec, } -#[derive(Clone,Copy,Hash,Eq,PartialEq)] +#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)] pub struct ModelId(u32); pub struct Model{ - pub model:ModelId, + pub mesh:MeshId, pub attributes:gameplay_attributes::CollisionAttributesId, pub color:Color4,//transparency is in here pub transform:Planar64Affine3, diff --git a/src/updatable.rs b/src/updatable.rs new file mode 100644 index 0000000..13b6dd2 --- /dev/null +++ b/src/updatable.rs @@ -0,0 +1,56 @@ +pub trait Updatable{ + 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, +} + +enum Update{ + Insert(I), + Update(U), + Remove +} + +struct InnerUpdate{ + //#[updatable(Update)] + enabled:Option, +} +struct OuterUpdate{ + //#[updatable(Insert,Update,Remove)] + inners:std::collections::HashMap>, + //#[updatable(Update)] + //inners:std::collections::HashMap, +} +impl Updatable for Inner{ + fn update(&mut self,update:InnerUpdate){ + if let Some(enabled)=update.enabled{ + self.enabled=enabled; + } + } +} +impl Updatable 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), + }; + } + } +} +//*/ \ No newline at end of file