Compare commits
13 Commits
data-struc
...
updatable
Author | SHA1 | Date | |
---|---|---|---|
165a59be6d | |||
2c926efa0d | |||
74acfbf206 | |||
a9f3e61f2b | |||
db142d125b | |||
ef922135e6 | |||
9aec0e1b52 | |||
b065f83faf | |||
39b202176f | |||
093a54c527 | |||
45dc9e4bd3 | |||
f2635be0fb | |||
47cdea0c8a |
54
Cargo.lock
generated
54
Cargo.lock
generated
@ -2,15 +2,69 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
|
||||
|
||||
[[package]]
|
||||
name = "id"
|
||||
version = "0.1.0"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "2337e7a6c273082b672e377e159d7a168fb51438461b7c4033c79a515dd7a25a"
|
||||
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 = [
|
||||
"bitflags",
|
||||
"glam",
|
||||
"id",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||
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"
|
||||
|
@ -6,4 +6,6 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2.4.2"
|
||||
glam = "0.25.0"
|
||||
id = { version = "0.1.0", registry = "strafesnet" }
|
||||
|
46
src/bvh.rs
46
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<BvhNode>),
|
||||
Leaf(usize),
|
||||
|
||||
enum BvhNodeContent<T>{
|
||||
Branch(Vec<BvhNode<T>>),
|
||||
Leaf(T),
|
||||
}
|
||||
impl Default for BvhNodeContent{
|
||||
impl<T> Default for BvhNodeContent<T>{
|
||||
fn default()->Self{
|
||||
Self::Branch(Vec::new())
|
||||
}
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct BvhNode{
|
||||
content:BvhNodeContent,
|
||||
pub struct BvhNode<T>{
|
||||
content:BvhNodeContent<T>,
|
||||
aabb:Aabb,
|
||||
}
|
||||
|
||||
impl BvhNode{
|
||||
pub fn the_tester<F:FnMut(usize)>(&self,aabb:&Aabb,f:&mut F){
|
||||
impl<T:Copy+Eq+std::hash::Hash> BvhNode<T>{
|
||||
pub fn the_tester<F:FnMut(T)>(&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<Aabb>)->BvhNode{
|
||||
generate_bvh_node(boxen.into_iter().enumerate().collect())
|
||||
pub fn generate_bvh<T:Copy+Eq+std::hash::Hash>(boxen:Vec<(T,Aabb)>)->BvhNode<T>{
|
||||
generate_bvh_node(boxen,false)
|
||||
}
|
||||
|
||||
fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{
|
||||
fn generate_bvh_node<T:Copy+Eq+std::hash::Hash>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
src/controls_bitflag.rs
Normal file
30
src/controls_bitflag.rs
Normal file
@ -0,0 +1,30 @@
|
||||
bitflags::bitflags!{
|
||||
#[derive(Clone,Copy,Debug,Default)]
|
||||
pub struct Controls:u32{
|
||||
const MoveForward=1<<0;
|
||||
const MoveLeft=1<<1;
|
||||
const MoveBackward=1<<2;
|
||||
const MoveRight=1<<3;
|
||||
const MoveUp=1<<4;
|
||||
const MoveDown=1<<5;
|
||||
const LookUp=1<<6;
|
||||
const LookLeft=1<<7;
|
||||
const LookDown=1<<8;
|
||||
const LookRight=1<<9;
|
||||
const Jump=1<<10;
|
||||
const Crouch=1<<11;
|
||||
const Sprint=1<<12;
|
||||
const Zoom=1<<13;
|
||||
const Use=1<<14;//Interact with object
|
||||
const PrimaryAction=1<<15;//LBM/Shoot/Melee
|
||||
const SecondaryAction=1<<16;//RMB/ADS/Block
|
||||
}
|
||||
}
|
||||
impl Controls{
|
||||
pub const fn wasd()->Self{
|
||||
Self::MoveForward.union(Self::MoveLeft).union(Self::MoveBackward).union(Self::MoveRight)
|
||||
}
|
||||
pub const fn wasdqe()->Self{
|
||||
Self::MoveForward.union(Self::MoveLeft).union(Self::MoveBackward).union(Self::MoveRight).union(Self::MoveUp).union(Self::MoveDown)
|
||||
}
|
||||
}
|
@ -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<Accelerator>,
|
||||
}
|
||||
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<ContactingBehaviour>,
|
||||
}
|
||||
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<IntersectingWater>,
|
||||
}
|
||||
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
|
||||
@ -142,8 +146,8 @@ pub enum CollisionAttributes{
|
||||
general:GeneralAttributes,
|
||||
},
|
||||
}
|
||||
impl std::default::Default for CollisionAttributes{
|
||||
fn default()->Self{
|
||||
impl CollisionAttributes{
|
||||
pub fn contact_default()->Self{
|
||||
Self::Contact{
|
||||
contacting:ContactingAttributes::default(),
|
||||
general:GeneralAttributes::default()
|
||||
|
@ -1,14 +1,44 @@
|
||||
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
|
||||
behaviour:StageElementBehaviour,
|
||||
jump_limit:Option<u8>,
|
||||
}
|
||||
impl StageElement{
|
||||
#[inline]
|
||||
pub const fn new(stage_id:StageId,force:bool,behaviour:StageElementBehaviour,jump_limit:Option<u8>)->Self{
|
||||
Self{
|
||||
stage_id,
|
||||
force,
|
||||
behaviour,
|
||||
jump_limit,
|
||||
}
|
||||
}
|
||||
#[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
|
||||
}
|
||||
#[inline]
|
||||
pub const fn jump_limit(&self)->Option<u8>{
|
||||
self.jump_limit
|
||||
}
|
||||
}
|
||||
|
||||
#[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,71 +50,223 @@ 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,Debug,Hash,id::Id,Eq,PartialEq)]
|
||||
pub struct CheckpointId(u32);
|
||||
impl CheckpointId{
|
||||
pub const FIRST:Self=Self(0);
|
||||
}
|
||||
#[derive(Clone,Copy,Debug,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<ModelId>,
|
||||
//open world support lol
|
||||
ordered_checkpoints_count:u32,
|
||||
unordered_checkpoints_count:u32,
|
||||
//currently loaded checkpoint models
|
||||
ordered_checkpoints:HashMap<CheckpointId,ModelId>,
|
||||
unordered_checkpoints:HashSet<ModelId>,
|
||||
}
|
||||
|
||||
#[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<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{
|
||||
Start,
|
||||
Finish,
|
||||
Anticheat,
|
||||
}
|
||||
#[derive(Clone,Copy,Debug,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<ModelId,ZoneBehaviour>,
|
||||
stages:Vec<Stage>,
|
||||
start:ModelId,//when you press reset you go here
|
||||
zones:HashMap<ModelId,Zone>,
|
||||
stages:Vec<Stage>,//when you load the map you go to stages[0].spawn
|
||||
//mutually exlusive stage element behaviour
|
||||
elements:HashMap<ModelId,StageElement>,
|
||||
jump_limit:HashMap<ModelId,u32>,
|
||||
}
|
||||
impl Mode{
|
||||
pub fn new(style:gameplay_style::StyleModifiers,start:ModelId)->Self{
|
||||
Self{
|
||||
style,
|
||||
start,
|
||||
zones:HashMap::new(),
|
||||
stages:Vec::new(),
|
||||
elements: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<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,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)
|
||||
}
|
||||
//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,
|
||||
jump_limit:None,
|
||||
});
|
||||
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,
|
||||
jump_limit:None,
|
||||
});
|
||||
}
|
||||
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,
|
||||
jump_limit:None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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>,
|
||||
}
|
||||
impl Modes{
|
||||
pub fn clear(&mut self){
|
||||
self.modes.clear();
|
||||
pub fn new(modes:Vec<Mode>)->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<ModeId,ModeUpdate>,
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,223 +1,51 @@
|
||||
const VALVE_SCALE:i64=16;
|
||||
const VALVE_SCALE:Planar64=Planar64::raw(1<<28);// 1/16
|
||||
|
||||
use crate::integer::{Time,Ratio64,Planar64,Planar64Vec3};
|
||||
use crate::controls_bitflag::Controls;
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
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<StrafeSettings>,
|
||||
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<Planar64>,
|
||||
rocket_force:Option<Planar64>,
|
||||
gravity:Planar64Vec3,
|
||||
hitbox:Hitbox,
|
||||
camera_offset:Planar64Vec3,
|
||||
//controls which are allowed to pass into gameplay (usually all)
|
||||
pub controls_mask:Controls,
|
||||
//controls which are masked from control state (e.g. !jump in scroll style)
|
||||
pub controls_mask_state:Controls,
|
||||
//strafing
|
||||
pub strafe:Option<StrafeSettings>,
|
||||
//player gets a controllable rocket force
|
||||
pub rocket:Option<PropulsionSettings>,
|
||||
//flying
|
||||
//jumping is allowed
|
||||
pub jump:Option<JumpSettings>,
|
||||
//standing & walking is allowed
|
||||
pub walk:Option<WalkSettings>,
|
||||
//laddering is allowed
|
||||
pub ladder:Option<LadderSettings>,
|
||||
//water propulsion
|
||||
pub swim:Option<PropulsionSettings>,
|
||||
//maximum slope before sloped surfaces become frictionless
|
||||
pub gravity:Planar64Vec3,
|
||||
//hitbox
|
||||
pub hitbox:Hitbox,
|
||||
//camera location relative to the center (0,0,0) of the hitbox
|
||||
pub camera_offset:Planar64Vec3,
|
||||
//unused
|
||||
pub mass:Planar64,
|
||||
}
|
||||
impl std::default::Default for StyleModifiers{
|
||||
fn default()->Self{
|
||||
Self::roblox_bhop()
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
const RIGHT_DIR:Planar64Vec3=Planar64Vec3::X;
|
||||
const UP_DIR:Planar64Vec3=Planar64Vec3::Y;
|
||||
const FORWARD_DIR:Planar64Vec3=Planar64Vec3::NEG_Z;
|
||||
|
||||
fn neo()->Self{
|
||||
Self{
|
||||
controls_used:!0,
|
||||
controls_mask:!0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN),
|
||||
strafe:Some(StrafeSettings{
|
||||
enable:EnableStrafe::Always,
|
||||
air_accel_limit:None,
|
||||
tick_rate:Ratio64::new(64,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump_impulse:JumpImpulse::FromEnergy(Planar64::int(512)),
|
||||
jump_calculation:JumpCalculation::Energy,
|
||||
gravity:Planar64Vec3::int(0,-80,0),
|
||||
static_friction:Planar64::int(2),
|
||||
kinetic_friction:Planar64::int(3),//unrealistic: kinetic friction is typically lower than static
|
||||
mass:Planar64::int(1),
|
||||
mv:Planar64::int(3),
|
||||
rocket_force:None,
|
||||
walk_speed:Planar64::int(16),
|
||||
walk_accel:Planar64::int(80),
|
||||
ladder_speed:Planar64::int(16),
|
||||
ladder_accel:Planar64::int(160),
|
||||
ladder_dot:(Planar64::int(1)/2).sqrt(),
|
||||
swim_speed:Planar64::int(12),
|
||||
surf_slope:Some(Planar64::raw(7)/8),
|
||||
hitbox:Hitbox::roblox(),
|
||||
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
|
||||
}
|
||||
}
|
||||
|
||||
fn roblox_bhop()->Self{
|
||||
Self{
|
||||
controls_used:!0,
|
||||
controls_mask:!0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN),
|
||||
strafe:Some(StrafeSettings{
|
||||
enable:EnableStrafe::Always,
|
||||
air_accel_limit:None,
|
||||
tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump_impulse:JumpImpulse::FromTime(Time::from_micros(715_588)),
|
||||
jump_calculation:JumpCalculation::Capped,
|
||||
gravity:Planar64Vec3::int(0,-100,0),
|
||||
static_friction:Planar64::int(2),
|
||||
kinetic_friction:Planar64::int(3),//unrealistic: kinetic friction is typically lower than static
|
||||
mass:Planar64::int(1),
|
||||
mv:Planar64::int(27)/10,
|
||||
rocket_force:None,
|
||||
walk_speed:Planar64::int(18),
|
||||
walk_accel:Planar64::int(90),
|
||||
ladder_speed:Planar64::int(18),
|
||||
ladder_accel:Planar64::int(180),
|
||||
ladder_dot:(Planar64::int(1)/2).sqrt(),
|
||||
swim_speed:Planar64::int(12),
|
||||
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
|
||||
hitbox:Hitbox::roblox(),
|
||||
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
|
||||
}
|
||||
}
|
||||
fn roblox_surf()->Self{
|
||||
Self{
|
||||
controls_used:!0,
|
||||
controls_mask:!0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN),
|
||||
strafe:Some(StrafeSettings{
|
||||
enable:EnableStrafe::Always,
|
||||
air_accel_limit:None,
|
||||
tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump_impulse:JumpImpulse::FromTime(Time::from_micros(715_588)),
|
||||
jump_calculation:JumpCalculation::Capped,
|
||||
gravity:Planar64Vec3::int(0,-50,0),
|
||||
static_friction:Planar64::int(2),
|
||||
kinetic_friction:Planar64::int(3),//unrealistic: kinetic friction is typically lower than static
|
||||
mass:Planar64::int(1),
|
||||
mv:Planar64::int(27)/10,
|
||||
rocket_force:None,
|
||||
walk_speed:Planar64::int(18),
|
||||
walk_accel:Planar64::int(90),
|
||||
ladder_speed:Planar64::int(18),
|
||||
ladder_accel:Planar64::int(180),
|
||||
ladder_dot:(Planar64::int(1)/2).sqrt(),
|
||||
swim_speed:Planar64::int(12),
|
||||
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
|
||||
hitbox:Hitbox::roblox(),
|
||||
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
|
||||
}
|
||||
}
|
||||
|
||||
fn source_bhop()->Self{
|
||||
Self{
|
||||
controls_used:!0,
|
||||
controls_mask:!0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN),
|
||||
strafe:Some(StrafeSettings{
|
||||
enable:EnableStrafe::Always,
|
||||
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_calculation:JumpCalculation::Linear,
|
||||
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,
|
||||
rocket_force:None,
|
||||
walk_speed:Planar64::int(18),//?
|
||||
walk_accel:Planar64::int(90),//?
|
||||
ladder_speed:Planar64::int(18),//?
|
||||
ladder_accel:Planar64::int(180),//?
|
||||
ladder_dot:(Planar64::int(1)/2).sqrt(),//?
|
||||
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,
|
||||
}
|
||||
}
|
||||
fn source_surf()->Self{
|
||||
Self{
|
||||
controls_used:!0,
|
||||
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),
|
||||
tick_rate:Ratio64::new(66,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump_impulse:JumpImpulse::FromHeight(Planar64::int(52)/VALVE_SCALE),
|
||||
jump_calculation:JumpCalculation::Linear,
|
||||
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,
|
||||
rocket_force:None,
|
||||
walk_speed:Planar64::int(18),//?
|
||||
walk_accel:Planar64::int(90),//?
|
||||
ladder_speed:Planar64::int(18),//?
|
||||
ladder_accel:Planar64::int(180),//?
|
||||
ladder_dot:(Planar64::int(1)/2).sqrt(),//?
|
||||
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,
|
||||
}
|
||||
}
|
||||
fn roblox_rocket()->Self{
|
||||
Self{
|
||||
controls_used:!0,
|
||||
controls_mask:!0,
|
||||
strafe:None,
|
||||
jump_impulse:JumpImpulse::FromTime(Time::from_micros(715_588)),
|
||||
jump_calculation:JumpCalculation::Capped,
|
||||
gravity:Planar64Vec3::int(0,-100,0),
|
||||
static_friction:Planar64::int(2),
|
||||
kinetic_friction:Planar64::int(3),//unrealistic: kinetic friction is typically lower than static
|
||||
mass:Planar64::int(1),
|
||||
mv:Planar64::int(27)/10,
|
||||
rocket_force:Some(Planar64::int(200)),
|
||||
walk_speed:Planar64::int(18),
|
||||
walk_accel:Planar64::int(90),
|
||||
ladder_speed:Planar64::int(18),
|
||||
ladder_accel:Planar64::int(180),
|
||||
ladder_dot:(Planar64::int(1)/2).sqrt(),
|
||||
swim_speed:Planar64::int(12),
|
||||
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
|
||||
hitbox:Hitbox::roblox(),
|
||||
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum JumpCalculation{
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum JumpCalculation{
|
||||
Capped,//roblox
|
||||
Energy,//new
|
||||
Linear,//source
|
||||
}
|
||||
|
||||
enum JumpImpulse{
|
||||
#[derive(Clone,Debug)]
|
||||
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
|
||||
@ -227,21 +55,261 @@ enum JumpImpulse{
|
||||
//Capped means it increases the dot to the cap
|
||||
//Energy means it adds energy
|
||||
//Linear means it linearly adds on
|
||||
|
||||
enum EnableStrafe{
|
||||
Always,
|
||||
MaskAny(u32),//hsw, shsw
|
||||
MaskAll(u32),
|
||||
//Function(Box<dyn Fn(u32)->bool>),
|
||||
impl JumpImpulse{
|
||||
//fn get_jump_time(&self)->Planar64
|
||||
//fn get_jump_height(&self)->Planar64
|
||||
//fn get_jump_energy(&self)->Planar64
|
||||
pub fn get_jump_deltav(&self,gravity:&Planar64Vec3,mass:Planar64)->Planar64{
|
||||
//gravity.length() is actually the proper calculation because the jump is always opposite the gravity direction
|
||||
match self{
|
||||
&JumpImpulse::FromTime(time)=>gravity.length()*(time/2),
|
||||
&JumpImpulse::FromHeight(height)=>(gravity.length()*height*2).sqrt(),
|
||||
&JumpImpulse::FromDeltaV(deltav)=>deltav,
|
||||
&JumpImpulse::FromEnergy(energy)=>(energy*2/mass).sqrt(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StrafeSettings{
|
||||
enable:EnableStrafe,
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ControlsActivation{
|
||||
//allowed keys
|
||||
controls_mask:Controls,
|
||||
//allow strafing only if any of the masked controls are held, eg W|S for shsw
|
||||
controls_intersects:Controls,
|
||||
//allow strafing only if all of the masked controls are held, eg W for hsw, w-only
|
||||
controls_contains:Controls,
|
||||
//Function(Box<dyn Fn(u32)->bool>),
|
||||
}
|
||||
impl ControlsActivation{
|
||||
pub const fn new(
|
||||
controls_mask:Controls,
|
||||
controls_intersects:Controls,
|
||||
controls_contains:Controls,
|
||||
)->Self{
|
||||
Self{
|
||||
controls_mask,
|
||||
controls_intersects,
|
||||
controls_contains,
|
||||
}
|
||||
}
|
||||
pub const fn mask(&self,controls:Controls)->Controls{
|
||||
controls.intersection(self.controls_mask)
|
||||
}
|
||||
pub const fn activates(&self,controls:Controls)->bool{
|
||||
(self.controls_intersects.is_empty()||controls.intersects(self.controls_intersects))
|
||||
&&controls.contains(self.controls_contains)
|
||||
}
|
||||
pub const fn full_3d()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::wasdqe(),
|
||||
controls_intersects:Controls::wasdqe(),
|
||||
controls_contains:Controls::empty(),
|
||||
}
|
||||
}
|
||||
//classical styles
|
||||
//Normal
|
||||
pub const fn full_2d()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::wasd(),
|
||||
controls_intersects:Controls::wasd(),
|
||||
controls_contains:Controls::empty(),
|
||||
}
|
||||
}
|
||||
//Sideways
|
||||
pub const fn sideways()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::MoveForward.union(Controls::MoveBackward),
|
||||
controls_intersects:Controls::MoveForward.union(Controls::MoveBackward),
|
||||
controls_contains:Controls::empty(),
|
||||
}
|
||||
}
|
||||
//Half-Sideways
|
||||
pub const fn half_sideways()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::MoveForward.union(Controls::MoveLeft).union(Controls::MoveRight),
|
||||
controls_intersects:Controls::MoveLeft.union(Controls::MoveRight),
|
||||
controls_contains:Controls::MoveForward,
|
||||
}
|
||||
}
|
||||
//Surf Half-Sideways
|
||||
pub const fn surf_half_sideways()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::MoveForward.union(Controls::MoveBackward).union(Controls::MoveLeft).union(Controls::MoveRight),
|
||||
controls_intersects:Controls::MoveForward.union(Controls::MoveBackward),
|
||||
controls_contains:Controls::empty(),
|
||||
}
|
||||
}
|
||||
//W-Only
|
||||
pub const fn w_only()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::MoveForward,
|
||||
controls_intersects:Controls::empty(),
|
||||
controls_contains:Controls::MoveForward,
|
||||
}
|
||||
}
|
||||
//A-Only
|
||||
pub const fn a_only()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::MoveLeft,
|
||||
controls_intersects:Controls::empty(),
|
||||
controls_contains:Controls::MoveLeft,
|
||||
}
|
||||
}
|
||||
//Backwards
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct StrafeSettings{
|
||||
enable:ControlsActivation,
|
||||
mv:Planar64,
|
||||
air_accel_limit:Option<Planar64>,
|
||||
tick_rate:Ratio64,
|
||||
}
|
||||
impl StrafeSettings{
|
||||
pub fn tick_velocity(&self,velocity:Planar64Vec3,control_dir:Planar64Vec3)->Option<Planar64Vec3>{
|
||||
let d=velocity.dot(control_dir);
|
||||
match d<self.mv{
|
||||
true=>Some(velocity+control_dir*self.air_accel_limit.map_or(self.mv-d,|limit|limit.min(self.mv-d))),
|
||||
false=>None,
|
||||
}
|
||||
}
|
||||
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 const fn activates(&self,controls:Controls)->bool{
|
||||
self.enable.activates(controls)
|
||||
}
|
||||
pub const fn mask(&self,controls:Controls)->Controls{
|
||||
self.enable.mask(controls)
|
||||
}
|
||||
}
|
||||
|
||||
enum HitboxMesh{
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct PropulsionSettings{
|
||||
magnitude:Planar64,
|
||||
}
|
||||
impl PropulsionSettings{
|
||||
pub fn acceleration(&self,control_dir:Planar64Vec3)->Planar64Vec3{
|
||||
control_dir*self.magnitude
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct JumpSettings{
|
||||
//information used to calculate jump power
|
||||
impulse:JumpImpulse,
|
||||
//information used to calculate jump behaviour
|
||||
calculation:JumpCalculation,
|
||||
}
|
||||
impl JumpSettings{
|
||||
pub fn jumped_velocity(&self,style:&StyleModifiers,jump_dir:Planar64Vec3,velocity:Planar64Vec3)->Planar64Vec3{
|
||||
match self.calculation{
|
||||
//roblox style
|
||||
JumpCalculation::Capped=>todo!(),
|
||||
//something different
|
||||
JumpCalculation::Energy=>todo!(),
|
||||
//source style
|
||||
JumpCalculation::Linear=>velocity+jump_dir*(self.impulse.get_jump_deltav(&style.gravity,style.mass)/jump_dir.length()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct AccelerateSettings{
|
||||
accel:Planar64,
|
||||
topspeed:Planar64,
|
||||
}
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct WalkSettings{
|
||||
accelerate:AccelerateSettings,
|
||||
static_friction:Planar64,
|
||||
kinetic_friction:Planar64,
|
||||
//if a surf slope angle does not exist, then everything is slippery and walking is impossible
|
||||
surf_dot:Planar64,//surf_dot<n.dot(up)/n.length()
|
||||
}
|
||||
impl WalkSettings{
|
||||
pub fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
|
||||
//TODO: fallible walk accel
|
||||
let diff_len=target_diff.length();
|
||||
let friction=if diff_len<self.accelerate.topspeed{
|
||||
self.static_friction
|
||||
}else{
|
||||
self.kinetic_friction
|
||||
};
|
||||
self.accelerate.accel.min(-Planar64Vec3::Y.dot(gravity)*friction)
|
||||
}
|
||||
pub fn get_walk_target_velocity(&self,control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{
|
||||
if control_dir==Planar64Vec3::ZERO{
|
||||
return control_dir;
|
||||
}
|
||||
let n=normal.length();
|
||||
let m=control_dir.length();
|
||||
let d=normal.dot(control_dir)/m;
|
||||
if d<n{
|
||||
let cr=normal.cross(control_dir);
|
||||
if cr==Planar64Vec3::ZERO{
|
||||
Planar64Vec3::ZERO
|
||||
}else{
|
||||
cr.cross(normal)*(self.accelerate.topspeed/(n*(n*n-d*d).sqrt()*m))
|
||||
}
|
||||
}else{
|
||||
Planar64Vec3::ZERO
|
||||
}
|
||||
}
|
||||
pub fn is_slope_walkable(&self,normal:Planar64Vec3,up:Planar64Vec3)->bool{
|
||||
//normal is not guaranteed to be unit length
|
||||
let ny=normal.dot(up);
|
||||
let h=normal.length();
|
||||
//remember this is a normal vector
|
||||
Planar64::ZERO<ny&&h*self.surf_dot<ny
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct LadderSettings{
|
||||
accelerate:AccelerateSettings,
|
||||
//how close to pushing directly into/out of the ladder normal
|
||||
//does your input need to be to redirect straight up/down the ladder
|
||||
dot:Planar64,
|
||||
}
|
||||
impl LadderSettings{
|
||||
pub fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
|
||||
//TODO: fallible ladder accel
|
||||
self.accelerate.accel
|
||||
}
|
||||
pub fn get_ladder_target_velocity(&self,mut control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{
|
||||
if control_dir==Planar64Vec3::ZERO{
|
||||
return control_dir;
|
||||
}
|
||||
let n=normal.length();
|
||||
let m=control_dir.length();
|
||||
let mut d=normal.dot(control_dir)/m;
|
||||
if d< -self.dot*n{
|
||||
control_dir=Planar64Vec3::Y*m;
|
||||
d=normal.y();
|
||||
}else if self.dot*n<d{
|
||||
control_dir=Planar64Vec3::NEG_Y*m;
|
||||
d=-normal.y();
|
||||
}
|
||||
//n=d if you are standing on top of a ladder and press E.
|
||||
//two fixes:
|
||||
//- ladder movement is not allowed on walkable surfaces
|
||||
//- fix the underlying issue
|
||||
if d.get().unsigned_abs()<n.get().unsigned_abs(){
|
||||
let cr=normal.cross(control_dir);
|
||||
if cr==Planar64Vec3::ZERO{
|
||||
Planar64Vec3::ZERO
|
||||
}else{
|
||||
cr.cross(normal)*(self.accelerate.topspeed/(n*(n*n-d*d).sqrt()))
|
||||
}
|
||||
}else{
|
||||
Planar64Vec3::ZERO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum HitboxMesh{
|
||||
Box,//source
|
||||
Cylinder,//roblox
|
||||
//Sphere,//roblox old physics
|
||||
@ -250,21 +318,206 @@ enum HitboxMesh{
|
||||
//DualCone,
|
||||
}
|
||||
|
||||
struct Hitbox{
|
||||
halfsize:Planar64Vec3,
|
||||
mesh:HitboxMesh,
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct Hitbox{
|
||||
pub halfsize:Planar64Vec3,
|
||||
pub mesh:HitboxMesh,
|
||||
}
|
||||
impl Hitbox{
|
||||
fn roblox()->Self{
|
||||
pub fn roblox()->Self{
|
||||
Self{
|
||||
halfsize:Planar64Vec3::int(2,5,2)/2,
|
||||
mesh:HitboxMesh::Cylinder,
|
||||
}
|
||||
}
|
||||
fn source()->Self{
|
||||
pub 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleModifiers{
|
||||
pub const RIGHT_DIR:Planar64Vec3=Planar64Vec3::X;
|
||||
pub const UP_DIR:Planar64Vec3=Planar64Vec3::Y;
|
||||
pub const FORWARD_DIR:Planar64Vec3=Planar64Vec3::NEG_Z;
|
||||
|
||||
pub fn neo()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::all(),
|
||||
controls_mask_state:Controls::all(),
|
||||
strafe:Some(StrafeSettings{
|
||||
enable:ControlsActivation::full_2d(),
|
||||
air_accel_limit:None,
|
||||
mv:Planar64::int(3),
|
||||
tick_rate:Ratio64::new(64,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump:Some(JumpSettings{
|
||||
impulse:JumpImpulse::FromEnergy(Planar64::int(512)),
|
||||
calculation:JumpCalculation::Energy,
|
||||
}),
|
||||
gravity:Planar64Vec3::int(0,-80,0),
|
||||
mass:Planar64::int(1),
|
||||
rocket:None,
|
||||
walk:Some(WalkSettings{
|
||||
accelerate:AccelerateSettings{
|
||||
topspeed:Planar64::int(16),
|
||||
accel:Planar64::int(80),
|
||||
},
|
||||
static_friction:Planar64::int(2),
|
||||
kinetic_friction:Planar64::int(3),//unrealistic: kinetic friction is typically lower than static
|
||||
surf_dot:Planar64::int(3)/4,
|
||||
}),
|
||||
ladder:Some(LadderSettings{
|
||||
accelerate:AccelerateSettings{
|
||||
topspeed:Planar64::int(16),
|
||||
accel:Planar64::int(160),
|
||||
},
|
||||
dot:(Planar64::int(1)/2).sqrt(),
|
||||
}),
|
||||
swim:Some(PropulsionSettings{
|
||||
magnitude:Planar64::int(12),
|
||||
}),
|
||||
hitbox:Hitbox::roblox(),
|
||||
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
|
||||
}
|
||||
}
|
||||
|
||||
pub fn roblox_bhop()->Self{
|
||||
Self{
|
||||
controls_mask:Controls::all(),
|
||||
controls_mask_state:Controls::all(),
|
||||
strafe:Some(StrafeSettings{
|
||||
enable:ControlsActivation::full_2d(),
|
||||
air_accel_limit:None,
|
||||
mv:Planar64::int(27)/10,
|
||||
tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump:Some(JumpSettings{
|
||||
impulse:JumpImpulse::FromTime(Time::from_micros(715_588)),
|
||||
calculation:JumpCalculation::Linear,//Should be capped
|
||||
}),
|
||||
gravity:Planar64Vec3::int(0,-100,0),
|
||||
mass:Planar64::int(1),
|
||||
rocket:None,
|
||||
walk:Some(WalkSettings{
|
||||
accelerate:AccelerateSettings{
|
||||
topspeed:Planar64::int(18),
|
||||
accel:Planar64::int(90),
|
||||
},
|
||||
static_friction:Planar64::int(2),
|
||||
kinetic_friction:Planar64::int(3),//unrealistic: kinetic friction is typically lower than static
|
||||
surf_dot:Planar64::int(3)/4,// normal.y=0.75
|
||||
}),
|
||||
ladder:Some(LadderSettings{
|
||||
accelerate:AccelerateSettings{
|
||||
topspeed:Planar64::int(18),
|
||||
accel:Planar64::int(180),
|
||||
},
|
||||
dot:(Planar64::int(1)/2).sqrt(),
|
||||
}),
|
||||
swim:Some(PropulsionSettings{
|
||||
magnitude:Planar64::int(12),
|
||||
}),
|
||||
hitbox:Hitbox::roblox(),
|
||||
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
|
||||
}
|
||||
}
|
||||
pub fn roblox_surf()->Self{
|
||||
Self{
|
||||
gravity:Planar64Vec3::int(0,-50,0),
|
||||
..Self::roblox_bhop()
|
||||
}
|
||||
}
|
||||
pub fn roblox_rocket()->Self{
|
||||
Self{
|
||||
strafe:None,
|
||||
rocket:Some(PropulsionSettings{
|
||||
magnitude:Planar64::int(200),
|
||||
}),
|
||||
..Self::roblox_bhop()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_bhop()->Self{
|
||||
Self{
|
||||
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)*VALVE_SCALE,
|
||||
tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump:Some(JumpSettings{
|
||||
impulse:JumpImpulse::FromHeight(Planar64::int(52)*VALVE_SCALE),
|
||||
calculation:JumpCalculation::Linear,
|
||||
}),
|
||||
gravity:Planar64Vec3::int(0,-800,0)*VALVE_SCALE,
|
||||
mass:Planar64::int(1),
|
||||
rocket:None,
|
||||
walk:Some(WalkSettings{
|
||||
accelerate:AccelerateSettings{
|
||||
topspeed:Planar64::int(18),//?
|
||||
accel:Planar64::int(90),//?
|
||||
},
|
||||
static_friction:Planar64::int(2),//?
|
||||
kinetic_friction:Planar64::int(3),//?
|
||||
surf_dot:Planar64::int(3)/4,// normal.y=0.75
|
||||
}),
|
||||
ladder:Some(LadderSettings{
|
||||
accelerate:AccelerateSettings{
|
||||
topspeed:Planar64::int(18),//?
|
||||
accel:Planar64::int(180),//?
|
||||
},
|
||||
dot:(Planar64::int(1)/2).sqrt(),//?
|
||||
}),
|
||||
swim:Some(PropulsionSettings{
|
||||
magnitude:Planar64::int(12),//?
|
||||
}),
|
||||
hitbox:Hitbox::source(),
|
||||
camera_offset:(Planar64Vec3::int(0,64,0)-Planar64Vec3::int(0,73,0)/2)*VALVE_SCALE,
|
||||
}
|
||||
}
|
||||
pub fn source_surf()->Self{
|
||||
Self{
|
||||
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::int(150)*66*VALVE_SCALE),
|
||||
mv:Planar64::int(30)*VALVE_SCALE,
|
||||
tick_rate:Ratio64::new(66,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump:Some(JumpSettings{
|
||||
impulse:JumpImpulse::FromHeight(Planar64::int(52)*VALVE_SCALE),
|
||||
calculation:JumpCalculation::Linear,
|
||||
}),
|
||||
gravity:Planar64Vec3::int(0,-800,0)*VALVE_SCALE,
|
||||
mass:Planar64::int(1),
|
||||
rocket:None,
|
||||
walk:Some(WalkSettings{
|
||||
accelerate:AccelerateSettings{
|
||||
topspeed:Planar64::int(18),//?
|
||||
accel:Planar64::int(90),//?
|
||||
},
|
||||
static_friction:Planar64::int(2),//?
|
||||
kinetic_friction:Planar64::int(3),//?
|
||||
surf_dot:Planar64::int(3)/4,// normal.y=0.75
|
||||
}),
|
||||
ladder:Some(LadderSettings{
|
||||
accelerate:AccelerateSettings{
|
||||
topspeed:Planar64::int(18),//?
|
||||
accel:Planar64::int(180),//?
|
||||
},
|
||||
dot:(Planar64::int(1)/2).sqrt(),//?
|
||||
}),
|
||||
swim:Some(PropulsionSettings{
|
||||
magnitude:Planar64::int(12),//?
|
||||
}),
|
||||
hitbox:Hitbox::source(),
|
||||
camera_offset:(Planar64Vec3::int(0,64,0)-Planar64Vec3::int(0,73,0)/2)*VALVE_SCALE,
|
||||
}
|
||||
}
|
||||
}
|
@ -10,24 +10,24 @@ impl Time{
|
||||
pub const ONE_MICROSECOND:Self=Self(1_000);
|
||||
pub const ONE_NANOSECOND:Self=Self(1);
|
||||
#[inline]
|
||||
pub fn from_secs(num:i64)->Self{
|
||||
pub const fn from_secs(num:i64)->Self{
|
||||
Self(Self::ONE_SECOND.0*num)
|
||||
}
|
||||
#[inline]
|
||||
pub fn from_millis(num:i64)->Self{
|
||||
pub const fn from_millis(num:i64)->Self{
|
||||
Self(Self::ONE_MILLISECOND.0*num)
|
||||
}
|
||||
#[inline]
|
||||
pub fn from_micros(num:i64)->Self{
|
||||
pub const fn from_micros(num:i64)->Self{
|
||||
Self(Self::ONE_MICROSECOND.0*num)
|
||||
}
|
||||
#[inline]
|
||||
pub fn from_nanos(num:i64)->Self{
|
||||
pub const fn from_nanos(num:i64)->Self{
|
||||
Self(Self::ONE_NANOSECOND.0*num)
|
||||
}
|
||||
//should I have checked subtraction? force all time variables to be positive?
|
||||
#[inline]
|
||||
pub fn nanos(&self)->i64{
|
||||
pub const fn nanos(self)->i64{
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@ -91,7 +91,7 @@ const fn gcd(mut a:u64,mut b:u64)->u64{
|
||||
};
|
||||
a
|
||||
}
|
||||
#[derive(Clone,Hash)]
|
||||
#[derive(Clone,Copy,Debug,Hash)]
|
||||
pub struct Ratio64{
|
||||
num:i64,
|
||||
den:u64,
|
||||
@ -109,15 +109,15 @@ impl Ratio64{
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn mul_int(&self,rhs:i64)->i64{
|
||||
pub const fn mul_int(&self,rhs:i64)->i64{
|
||||
rhs*self.num/(self.den as i64)
|
||||
}
|
||||
#[inline]
|
||||
pub fn rhs_div_int(&self,rhs:i64)->i64{
|
||||
pub const fn rhs_div_int(&self,rhs:i64)->i64{
|
||||
rhs*(self.den as i64)/self.num
|
||||
}
|
||||
#[inline]
|
||||
pub fn mul_ref(&self,rhs:&Ratio64)->Ratio64{
|
||||
pub const fn mul_ref(&self,rhs:&Ratio64)->Ratio64{
|
||||
let (num,den)=(self.num*rhs.num,self.den*rhs.den);
|
||||
let d=gcd(num.unsigned_abs(),den);
|
||||
Self{
|
||||
@ -259,7 +259,7 @@ impl std::ops::Div<u64> for Ratio64{
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone,Hash)]
|
||||
#[derive(Clone,Copy,Debug,Hash)]
|
||||
pub struct Ratio64Vec2{
|
||||
pub x:Ratio64,
|
||||
pub y:Ratio64,
|
||||
@ -267,11 +267,11 @@ pub struct Ratio64Vec2{
|
||||
impl Ratio64Vec2{
|
||||
pub const ONE:Self=Self{x:Ratio64::ONE,y:Ratio64::ONE};
|
||||
#[inline]
|
||||
pub fn new(x:Ratio64,y:Ratio64)->Self{
|
||||
pub const fn new(x:Ratio64,y:Ratio64)->Self{
|
||||
Self{x,y}
|
||||
}
|
||||
#[inline]
|
||||
pub fn mul_int(&self,rhs:glam::I64Vec2)->glam::I64Vec2{
|
||||
pub const fn mul_int(&self,rhs:glam::I64Vec2)->glam::I64Vec2{
|
||||
glam::i64vec2(
|
||||
self.x.mul_int(rhs.x),
|
||||
self.y.mul_int(rhs.y),
|
||||
@ -294,9 +294,10 @@ impl std::ops::Mul<i64> 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<Planar64> 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);
|
||||
@ -605,15 +606,15 @@ impl Planar64Vec3{
|
||||
Self(glam::i64vec3(x,y,z))
|
||||
}
|
||||
#[inline]
|
||||
pub fn x(&self)->Planar64{
|
||||
pub const fn x(&self)->Planar64{
|
||||
Planar64(self.0.x)
|
||||
}
|
||||
#[inline]
|
||||
pub fn y(&self)->Planar64{
|
||||
pub const fn y(&self)->Planar64{
|
||||
Planar64(self.0.y)
|
||||
}
|
||||
#[inline]
|
||||
pub fn z(&self)->Planar64{
|
||||
pub const fn z(&self)->Planar64{
|
||||
Planar64(self.0.z)
|
||||
}
|
||||
#[inline]
|
||||
@ -641,7 +642,7 @@ impl Planar64Vec3{
|
||||
self.0.cmplt(rhs.0)
|
||||
}
|
||||
#[inline]
|
||||
pub fn dot(&self,rhs:Self)->Planar64{
|
||||
pub const fn dot(&self,rhs:Self)->Planar64{
|
||||
Planar64(((
|
||||
(self.0.x as i128)*(rhs.0.x as i128)+
|
||||
(self.0.y as i128)*(rhs.0.y as i128)+
|
||||
@ -649,13 +650,13 @@ impl Planar64Vec3{
|
||||
)>>32) as i64)
|
||||
}
|
||||
#[inline]
|
||||
pub fn dot128(&self,rhs:Self)->i128{
|
||||
pub const fn dot128(&self,rhs:Self)->i128{
|
||||
(self.0.x as i128)*(rhs.0.x as i128)+
|
||||
(self.0.y as i128)*(rhs.0.y as i128)+
|
||||
(self.0.z as i128)*(rhs.0.z as i128)
|
||||
}
|
||||
#[inline]
|
||||
pub fn cross(&self,rhs:Self)->Planar64Vec3{
|
||||
pub const fn cross(&self,rhs:Self)->Planar64Vec3{
|
||||
Planar64Vec3(glam::i64vec3(
|
||||
(((self.0.y as i128)*(rhs.0.z as i128)-(self.0.z as i128)*(rhs.0.y as i128))>>32) as i64,
|
||||
(((self.0.z as i128)*(rhs.0.x as i128)-(self.0.x as i128)*(rhs.0.z as i128))>>32) as i64,
|
||||
@ -663,12 +664,6 @@ impl Planar64Vec3{
|
||||
))
|
||||
}
|
||||
#[inline]
|
||||
pub fn walkable(&self,slope:Planar64,up:Self)->bool{
|
||||
let y=self.dot(up);
|
||||
let x=self.cross(up).length();
|
||||
x*slope<y
|
||||
}
|
||||
#[inline]
|
||||
pub fn length(&self)->Planar64{
|
||||
let radicand=(self.0.x as i128)*(self.0.x as i128)+(self.0.y as i128)*(self.0.y as i128)+(self.0.z as i128)*(self.0.z as i128);
|
||||
Planar64(unsafe{(radicand as f64).sqrt().to_int_unchecked()})
|
||||
@ -829,9 +824,9 @@ impl std::ops::Div<i64> for Planar64Vec3{
|
||||
///[-1.0,1.0] = [-2^32,2^32]
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
|
||||
pub struct Planar64Mat3{
|
||||
x_axis:Planar64Vec3,
|
||||
y_axis:Planar64Vec3,
|
||||
z_axis:Planar64Vec3,
|
||||
pub x_axis:Planar64Vec3,
|
||||
pub y_axis:Planar64Vec3,
|
||||
pub z_axis:Planar64Vec3,
|
||||
}
|
||||
impl Default for Planar64Mat3{
|
||||
#[inline]
|
||||
@ -845,7 +840,7 @@ impl Default for Planar64Mat3{
|
||||
}
|
||||
impl Planar64Mat3{
|
||||
#[inline]
|
||||
pub fn from_cols(x_axis:Planar64Vec3,y_axis:Planar64Vec3,z_axis:Planar64Vec3)->Self{
|
||||
pub const fn from_cols(x_axis:Planar64Vec3,y_axis:Planar64Vec3,z_axis:Planar64Vec3)->Self{
|
||||
Self{
|
||||
x_axis,
|
||||
y_axis,
|
||||
|
@ -1,9 +1,12 @@
|
||||
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_attributes;
|
||||
pub mod gameplay_modes;
|
||||
pub mod gameplay_style;
|
||||
pub mod gameplay_attributes;
|
||||
pub mod controls_bitflag;
|
14
src/map.rs
Normal file
14
src/map.rs
Normal file
@ -0,0 +1,14 @@
|
||||
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<gameplay_attributes::CollisionAttributes>,
|
||||
pub meshes:Vec<model::Mesh>,
|
||||
pub models:Vec<model::Model>,
|
||||
//RenderPattern
|
||||
pub textures:Vec<Vec<u8>>,
|
||||
pub render_configs:Vec<model::RenderConfig>,
|
||||
}
|
121
src/model.rs
121
src/model.rs
@ -3,51 +3,130 @@ 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<VertexId>,
|
||||
pub type IndexedVertexList=Vec<VertexId>;
|
||||
pub trait PolygonIter{
|
||||
fn polys(&self)->impl Iterator<Item=&[VertexId]>;
|
||||
}
|
||||
pub struct GroupId(u32);
|
||||
pub enum IndexedGroup{
|
||||
PolygonList(Vec<IndexedVertexList>),
|
||||
//TriangleStrip(Vec<IndexedVertexList>),
|
||||
pub trait MapVertexId{
|
||||
fn map_vertex_id<F:Fn(VertexId)->VertexId>(self,f:F)->Self;
|
||||
}
|
||||
pub struct RenderId(u32);
|
||||
#[derive(Clone)]
|
||||
pub struct PolygonList(Vec<IndexedVertexList>);
|
||||
impl PolygonList{
|
||||
pub const fn new(list:Vec<IndexedVertexList>)->Self{
|
||||
Self(list)
|
||||
}
|
||||
pub fn extend<T:IntoIterator<Item=IndexedVertexList>>(&mut self,iter:T){
|
||||
self.0.extend(iter);
|
||||
}
|
||||
}
|
||||
impl PolygonIter for PolygonList{
|
||||
fn polys(&self)->impl Iterator<Item=&[VertexId]>{
|
||||
self.0.iter().map(|poly|poly.as_slice())
|
||||
}
|
||||
}
|
||||
impl MapVertexId for PolygonList{
|
||||
fn map_vertex_id<F:Fn(VertexId)->VertexId>(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<Item=&[VertexId]>{
|
||||
// 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);
|
||||
#[derive(Clone)]
|
||||
pub enum PolygonGroup{
|
||||
PolygonList(PolygonList),
|
||||
//TriangleStrip(TriangleStrip),
|
||||
}
|
||||
impl PolygonIter for PolygonGroup{
|
||||
fn polys(&self)->impl Iterator<Item=&[VertexId]>{
|
||||
match self{
|
||||
PolygonGroup::PolygonList(list)=>list.polys(),
|
||||
//PolygonGroup::TriangleStrip(strip)=>strip.polys(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MapVertexId for PolygonGroup{
|
||||
fn map_vertex_id<F:Fn(VertexId)->VertexId>(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(Clone,Copy,Default)]
|
||||
pub struct RenderConfig{
|
||||
pub texture:Option<TextureId>,
|
||||
}
|
||||
impl RenderConfig{
|
||||
pub const fn texture(texture:TextureId)->Self{
|
||||
Self{
|
||||
texture:Some(texture),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct IndexedGraphicsGroup{
|
||||
//Render pattern material/texture/shader/flat color
|
||||
pub render:RenderId,
|
||||
pub groups:Vec<GroupId>,
|
||||
pub render:RenderConfigId,
|
||||
pub groups:Vec<PolygonGroupId>,
|
||||
}
|
||||
#[derive(Clone,Default)]
|
||||
pub struct IndexedPhysicsGroup{
|
||||
//the polygons in this group are guaranteed to make a closed convex shape
|
||||
pub groups:Vec<GroupId>,
|
||||
pub groups:Vec<PolygonGroupId>,
|
||||
}
|
||||
//This is a superset of PhysicsModel and GraphicsModel
|
||||
pub struct IndexedModel{
|
||||
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
|
||||
pub struct MeshId(u32);
|
||||
#[derive(Clone)]
|
||||
pub struct Mesh{
|
||||
pub unique_pos:Vec<Planar64Vec3>,//Unit32Vec3
|
||||
pub unique_normal:Vec<Planar64Vec3>,//Unit32Vec3
|
||||
pub unique_tex:Vec<TextureCoordinate>,
|
||||
pub unique_color:Vec<Color4>,
|
||||
pub unique_vertices:Vec<IndexedVertex>,
|
||||
//groups are constant texture AND convexity slices
|
||||
pub groups:Vec<IndexedGroup>,
|
||||
//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<PolygonGroup>,
|
||||
//graphics indexed (by texture)
|
||||
pub graphics_sets:Vec<IndexedGraphicsGroup>,
|
||||
pub graphics_groups:Vec<IndexedGraphicsGroup>,
|
||||
//physics indexed (by convexity)
|
||||
pub physics_sets:Vec<IndexedPhysicsGroup>,
|
||||
pub physics_groups:Vec<IndexedPhysicsGroup>,
|
||||
}
|
||||
|
||||
#[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,
|
||||
|
56
src/updatable.rs
Normal file
56
src/updatable.rs
Normal file
@ -0,0 +1,56 @@
|
||||
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),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
//*/
|
Reference in New Issue
Block a user