Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
165a59be6d | |||
2c926efa0d | |||
74acfbf206 |
@ -1,2 +0,0 @@
|
||||
[registries.strafesnet]
|
||||
index = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
29
Cargo.lock
generated
29
Cargo.lock
generated
@ -2,23 +2,17 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.28.0"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94"
|
||||
checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
|
||||
|
||||
[[package]]
|
||||
name = "id"
|
||||
@ -33,27 +27,26 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strafesnet_common"
|
||||
version = "0.4.1"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
"glam",
|
||||
"id",
|
||||
@ -61,9 +54,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.72"
|
||||
version = "2.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
11
Cargo.toml
11
Cargo.toml
@ -1,16 +1,11 @@
|
||||
[package]
|
||||
name = "strafesnet_common"
|
||||
version = "0.4.1"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
repository = "https://git.itzana.me/StrafesNET/common"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Common types and helpers for Strafe Client associated projects."
|
||||
authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
arrayvec = "0.7.4"
|
||||
bitflags = "2.6.0"
|
||||
glam = "0.28.0"
|
||||
bitflags = "2.4.2"
|
||||
glam = "0.25.0"
|
||||
id = { version = "0.1.0", registry = "strafesnet" }
|
||||
|
15
src/aabb.rs
15
src/aabb.rs
@ -6,22 +6,13 @@ pub struct Aabb{
|
||||
max:Planar64Vec3,
|
||||
}
|
||||
|
||||
impl Default for Aabb{
|
||||
fn default()->Self{
|
||||
impl Default for Aabb {
|
||||
fn default()->Self {
|
||||
Self{min:Planar64Vec3::MAX,max:Planar64Vec3::MIN}
|
||||
}
|
||||
}
|
||||
|
||||
impl Aabb{
|
||||
pub const fn new(min:Planar64Vec3,max:Planar64Vec3)->Self{
|
||||
Self{min,max}
|
||||
}
|
||||
pub const fn max(&self)->Planar64Vec3{
|
||||
self.max
|
||||
}
|
||||
pub const fn min(&self)->Planar64Vec3{
|
||||
self.min
|
||||
}
|
||||
pub fn grow(&mut self,point:Planar64Vec3){
|
||||
self.min=self.min.min(point);
|
||||
self.max=self.max.max(point);
|
||||
@ -52,4 +43,4 @@ impl Aabb{
|
||||
// let d=self.max-self.min;
|
||||
// d.x*d.y*d.z
|
||||
// }
|
||||
}
|
||||
}
|
135
src/bvh.rs
135
src/bvh.rs
@ -10,38 +10,26 @@ use crate::aabb::Aabb;
|
||||
//sort the centerpoints on each axis (3 lists)
|
||||
//bv is put into octant based on whether it is upper or lower in each list
|
||||
|
||||
pub enum RecursiveContent<R,T>{
|
||||
Branch(Vec<R>),
|
||||
enum BvhNodeContent<T>{
|
||||
Branch(Vec<BvhNode<T>>),
|
||||
Leaf(T),
|
||||
}
|
||||
impl<R,T> Default for RecursiveContent<R,T>{
|
||||
impl<T> Default for BvhNodeContent<T>{
|
||||
fn default()->Self{
|
||||
Self::Branch(Vec::new())
|
||||
}
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct BvhNode<T>{
|
||||
content:RecursiveContent<BvhNode<T>,T>,
|
||||
aabb:Aabb,
|
||||
}
|
||||
impl<T> Default for BvhNode<T>{
|
||||
fn default()->Self{
|
||||
Self{
|
||||
content:Default::default(),
|
||||
aabb:Aabb::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct BvhWeightNode<W,T>{
|
||||
content:RecursiveContent<BvhWeightNode<W,T>,T>,
|
||||
weight:W,
|
||||
content:BvhNodeContent<T>,
|
||||
aabb:Aabb,
|
||||
}
|
||||
|
||||
impl<T> BvhNode<T>{
|
||||
pub fn the_tester<F:FnMut(&T)>(&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{
|
||||
RecursiveContent::Leaf(model)=>f(model),
|
||||
RecursiveContent::Branch(children)=>for child in children{
|
||||
&BvhNodeContent::Leaf(model)=>f(model),
|
||||
BvhNodeContent::Branch(children)=>for child in children{
|
||||
//this test could be moved outside the match statement
|
||||
//but that would test the root node aabb
|
||||
//you're probably not going to spend a lot of time outside the map,
|
||||
@ -52,83 +40,38 @@ impl<T> BvhNode<T>{
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn into_visitor<F:FnMut(T)>(self,f:&mut F){
|
||||
match self.content{
|
||||
RecursiveContent::Leaf(model)=>f(model),
|
||||
RecursiveContent::Branch(children)=>for child in children{
|
||||
child.into_visitor(f)
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn weigh_contents<W:Copy+std::iter::Sum<W>,F:Fn(&T)->W>(self,f:&F)->BvhWeightNode<W,T>{
|
||||
match self.content{
|
||||
RecursiveContent::Leaf(model)=>BvhWeightNode{
|
||||
weight:f(&model),
|
||||
content:RecursiveContent::Leaf(model),
|
||||
aabb:self.aabb,
|
||||
},
|
||||
RecursiveContent::Branch(children)=>{
|
||||
let branch:Vec<BvhWeightNode<W,T>>=children.into_iter().map(|child|
|
||||
child.weigh_contents(f)
|
||||
).collect();
|
||||
BvhWeightNode{
|
||||
weight:branch.iter().map(|node|node.weight).sum(),
|
||||
content:RecursiveContent::Branch(branch),
|
||||
aabb:self.aabb,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <W,T> BvhWeightNode<W,T>{
|
||||
pub const fn weight(&self)->&W{
|
||||
&self.weight
|
||||
}
|
||||
pub const fn aabb(&self)->&Aabb{
|
||||
&self.aabb
|
||||
}
|
||||
pub fn into_content(self)->RecursiveContent<BvhWeightNode<W,T>,T>{
|
||||
self.content
|
||||
}
|
||||
pub fn into_visitor<F:FnMut(T)>(self,f:&mut F){
|
||||
match self.content{
|
||||
RecursiveContent::Leaf(model)=>f(model),
|
||||
RecursiveContent::Branch(children)=>for child in children{
|
||||
child.into_visitor(f)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_bvh<T>(boxen:Vec<(T,Aabb)>)->BvhNode<T>{
|
||||
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<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
|
||||
fn generate_bvh_node<T:Copy+Eq+std::hash::Hash>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
|
||||
let n=boxen.len();
|
||||
if force||n<20{
|
||||
let mut aabb=Aabb::default();
|
||||
let nodes=boxen.into_iter().map(|b|{
|
||||
aabb.join(&b.1);
|
||||
BvhNode{
|
||||
content:RecursiveContent::Leaf(b.0),
|
||||
content:BvhNodeContent::Leaf(b.0),
|
||||
aabb:b.1,
|
||||
}
|
||||
}).collect();
|
||||
BvhNode{
|
||||
content:RecursiveContent::Branch(nodes),
|
||||
content:BvhNodeContent::Branch(nodes),
|
||||
aabb,
|
||||
}
|
||||
}else{
|
||||
let mut octant=std::collections::HashMap::with_capacity(n);//this ids which octant the boxen is put in
|
||||
let mut sort_x=Vec::with_capacity(n);
|
||||
let mut sort_y=Vec::with_capacity(n);
|
||||
let mut sort_z=Vec::with_capacity(n);
|
||||
for (i,(_,aabb)) in boxen.iter().enumerate(){
|
||||
for (i,aabb) in boxen.iter(){
|
||||
let center=aabb.center();
|
||||
sort_x.push((i,center.x()));
|
||||
sort_y.push((i,center.y()));
|
||||
sort_z.push((i,center.z()));
|
||||
octant.insert(*i,0);
|
||||
sort_x.push((*i,center.x()));
|
||||
sort_y.push((*i,center.y()));
|
||||
sort_z.push((*i,center.z()));
|
||||
}
|
||||
sort_x.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
|
||||
sort_y.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
|
||||
@ -137,34 +80,26 @@ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
|
||||
let median_x=sort_x[h].1;
|
||||
let median_y=sort_y[h].1;
|
||||
let median_z=sort_z[h].1;
|
||||
//locate a run of values equal to the median
|
||||
//partition point gives the first index for which the predicate evaluates to false
|
||||
let first_index_eq_median_x=sort_x.partition_point(|&(_,x)|x<median_x);
|
||||
let first_index_eq_median_y=sort_y.partition_point(|&(_,y)|y<median_y);
|
||||
let first_index_eq_median_z=sort_z.partition_point(|&(_,z)|z<median_z);
|
||||
let first_index_gt_median_x=sort_x.partition_point(|&(_,x)|x<=median_x);
|
||||
let first_index_gt_median_y=sort_y.partition_point(|&(_,y)|y<=median_y);
|
||||
let first_index_gt_median_z=sort_z.partition_point(|&(_,z)|z<=median_z);
|
||||
//pick which side median value copies go into such that both sides are as balanced as possible based on distance from n/2
|
||||
let partition_point_x=if n.abs_diff(2*first_index_eq_median_x)<n.abs_diff(2*first_index_gt_median_x){first_index_eq_median_x}else{first_index_gt_median_x};
|
||||
let partition_point_y=if n.abs_diff(2*first_index_eq_median_y)<n.abs_diff(2*first_index_gt_median_y){first_index_eq_median_y}else{first_index_gt_median_y};
|
||||
let partition_point_z=if n.abs_diff(2*first_index_eq_median_z)<n.abs_diff(2*first_index_gt_median_z){first_index_eq_median_z}else{first_index_gt_median_z};
|
||||
//this ids which octant the boxen is put in
|
||||
let mut octant=vec![0;n];
|
||||
for &(i,_) in &sort_x[partition_point_x..]{
|
||||
octant[i]+=1<<0;
|
||||
for (i,c) in sort_x{
|
||||
if median_x<c{
|
||||
octant.insert(i,octant[&i]+1<<0);
|
||||
}
|
||||
}
|
||||
for &(i,_) in &sort_y[partition_point_y..]{
|
||||
octant[i]+=1<<1;
|
||||
for (i,c) in sort_y{
|
||||
if median_y<c{
|
||||
octant.insert(i,octant[&i]+1<<1);
|
||||
}
|
||||
}
|
||||
for &(i,_) in &sort_z[partition_point_z..]{
|
||||
octant[i]+=1<<2;
|
||||
for (i,c) in sort_z{
|
||||
if median_z<c{
|
||||
octant.insert(i,octant[&i]+1<<2);
|
||||
}
|
||||
}
|
||||
//generate lists for unique octant values
|
||||
let mut list_list=Vec::with_capacity(8);
|
||||
let mut octant_list=Vec::with_capacity(8);
|
||||
for (i,(data,aabb)) in boxen.into_iter().enumerate(){
|
||||
let octant_id=octant[i];
|
||||
for (i,aabb) in boxen.into_iter(){
|
||||
let octant_id=octant[&i];
|
||||
let list_id=if let Some(list_id)=octant_list.iter().position(|&id|id==octant_id){
|
||||
list_id
|
||||
}else{
|
||||
@ -173,14 +108,14 @@ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
|
||||
list_list.push(Vec::new());
|
||||
list_id
|
||||
};
|
||||
list_list[list_id].push((data,aabb));
|
||||
list_list[list_id].push((i,aabb));
|
||||
}
|
||||
let mut aabb=Aabb::default();
|
||||
if list_list.len()==1{
|
||||
generate_bvh_node(list_list.remove(0),true)
|
||||
}else{
|
||||
BvhNode{
|
||||
content:RecursiveContent::Branch(
|
||||
content:BvhNodeContent::Branch(
|
||||
list_list.into_iter().map(|b|{
|
||||
let node=generate_bvh_node(b,false);
|
||||
aabb.join(&node.aabb);
|
||||
|
@ -31,22 +31,6 @@ pub enum Booster{
|
||||
//Affine(crate::integer::Planar64Affine3),//capable of SetVelocity,DotVelocity,normal booster,bouncy part,redirect velocity, and much more
|
||||
Velocity(Planar64Vec3),//straight up boost velocity adds to your current velocity
|
||||
Energy{direction:Planar64Vec3,energy:Planar64},//increase energy in direction
|
||||
AirTime(Time),//increase airtime, invariant across mass and gravity changes
|
||||
Height(Planar64),//increase height, invariant across mass and gravity changes
|
||||
}
|
||||
impl Booster{
|
||||
pub fn boost(&self,velocity:Planar64Vec3)->Planar64Vec3{
|
||||
match self{
|
||||
&Booster::Velocity(boost_velocity)=>velocity+boost_velocity,
|
||||
&Booster::Energy{direction,energy}=>{
|
||||
let d=direction.dot(velocity);
|
||||
//TODO: think about negative
|
||||
velocity+direction.with_length((d*d+energy).sqrt()-d)
|
||||
},
|
||||
Booster::AirTime(_)=>todo!(),
|
||||
Booster::Height(_)=>todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub enum TrajectoryChoice{
|
||||
@ -150,24 +134,23 @@ impl IntersectingAttributes{
|
||||
}
|
||||
#[derive(Clone,Copy,id::Id,Hash,Eq,PartialEq)]
|
||||
pub struct CollisionAttributesId(u32);
|
||||
#[derive(Clone,Default,Hash,Eq,PartialEq)]
|
||||
pub struct ContactAttributes{
|
||||
pub contacting:ContactingAttributes,
|
||||
pub general:GeneralAttributes,
|
||||
}
|
||||
#[derive(Clone,Default,Hash,Eq,PartialEq)]
|
||||
pub struct IntersectAttributes{
|
||||
pub intersecting:IntersectingAttributes,
|
||||
pub general:GeneralAttributes,
|
||||
}
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub enum CollisionAttributes{
|
||||
Decoration,//visual only
|
||||
Contact(ContactAttributes),//track whether you are contacting the object
|
||||
Intersect(IntersectAttributes),//track whether you are intersecting the object
|
||||
Contact{//track whether you are contacting the object
|
||||
contacting:ContactingAttributes,
|
||||
general:GeneralAttributes,
|
||||
},
|
||||
Intersect{//track whether you are intersecting the object
|
||||
intersecting:IntersectingAttributes,
|
||||
general:GeneralAttributes,
|
||||
},
|
||||
}
|
||||
impl CollisionAttributes{
|
||||
pub fn contact_default()->Self{
|
||||
Self::Contact(ContactAttributes::default())
|
||||
Self::Contact{
|
||||
contacting:ContactingAttributes::default(),
|
||||
general:GeneralAttributes::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,22 +71,7 @@ pub struct Stage{
|
||||
unordered_checkpoints:HashSet<ModelId>,
|
||||
}
|
||||
impl Stage{
|
||||
pub fn new(
|
||||
spawn:ModelId,
|
||||
ordered_checkpoints_count:u32,
|
||||
unordered_checkpoints_count:u32,
|
||||
ordered_checkpoints:HashMap<CheckpointId,ModelId>,
|
||||
unordered_checkpoints:HashSet<ModelId>,
|
||||
)->Self{
|
||||
Self{
|
||||
spawn,
|
||||
ordered_checkpoints_count,
|
||||
unordered_checkpoints_count,
|
||||
ordered_checkpoints,
|
||||
unordered_checkpoints,
|
||||
}
|
||||
}
|
||||
pub fn empty(spawn:ModelId)->Self{
|
||||
pub fn new(spawn:ModelId)->Self{
|
||||
Self{
|
||||
spawn,
|
||||
ordered_checkpoints_count:0,
|
||||
@ -100,17 +85,6 @@ impl Stage{
|
||||
self.spawn
|
||||
}
|
||||
#[inline]
|
||||
pub const fn ordered_checkpoints_count(&self)->u32{
|
||||
self.ordered_checkpoints_count
|
||||
}
|
||||
#[inline]
|
||||
pub const fn unordered_checkpoints_count(&self)->u32{
|
||||
self.unordered_checkpoints_count
|
||||
}
|
||||
pub fn into_inner(self)->(HashMap<CheckpointId,ModelId>,HashSet<ModelId>){
|
||||
(self.ordered_checkpoints,self.unordered_checkpoints)
|
||||
}
|
||||
#[inline]
|
||||
pub const fn is_empty(&self)->bool{
|
||||
self.is_complete(0,0)
|
||||
}
|
||||
@ -162,22 +136,7 @@ pub struct Mode{
|
||||
elements:HashMap<ModelId,StageElement>,
|
||||
}
|
||||
impl Mode{
|
||||
pub fn new(
|
||||
style:gameplay_style::StyleModifiers,
|
||||
start:ModelId,
|
||||
zones:HashMap<ModelId,Zone>,
|
||||
stages:Vec<Stage>,
|
||||
elements:HashMap<ModelId,StageElement>,
|
||||
)->Self{
|
||||
Self{
|
||||
style,
|
||||
start,
|
||||
zones,
|
||||
stages,
|
||||
elements,
|
||||
}
|
||||
}
|
||||
pub fn empty(style:gameplay_style::StyleModifiers,start:ModelId)->Self{
|
||||
pub fn new(style:gameplay_style::StyleModifiers,start:ModelId)->Self{
|
||||
Self{
|
||||
style,
|
||||
start,
|
||||
@ -186,21 +145,6 @@ impl Mode{
|
||||
elements:HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn into_inner(self)->(
|
||||
gameplay_style::StyleModifiers,
|
||||
ModelId,
|
||||
HashMap<ModelId,Zone>,
|
||||
Vec<Stage>,
|
||||
HashMap<ModelId,StageElement>,
|
||||
){
|
||||
(
|
||||
self.style,
|
||||
self.start,
|
||||
self.zones,
|
||||
self.stages,
|
||||
self.elements,
|
||||
)
|
||||
}
|
||||
pub const fn get_start(&self)->ModelId{
|
||||
self.start
|
||||
}
|
||||
@ -299,17 +243,14 @@ impl ModeUpdate{
|
||||
|
||||
#[derive(Default,Clone)]
|
||||
pub struct Modes{
|
||||
pub modes:Vec<Mode>,
|
||||
modes:Vec<Mode>,
|
||||
}
|
||||
impl Modes{
|
||||
pub const fn new(modes:Vec<Mode>)->Self{
|
||||
pub fn new(modes:Vec<Mode>)->Self{
|
||||
Self{
|
||||
modes,
|
||||
}
|
||||
}
|
||||
pub fn into_inner(self)->Vec<Mode>{
|
||||
self.modes
|
||||
}
|
||||
pub fn push_mode(&mut self,mode:Mode){
|
||||
self.modes.push(mode)
|
||||
}
|
||||
@ -328,4 +269,4 @@ impl Updatable<ModesUpdate> for Modes{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,8 +14,6 @@ pub struct StyleModifiers{
|
||||
//player gets a controllable rocket force
|
||||
pub rocket:Option<PropulsionSettings>,
|
||||
//flying
|
||||
//pub move_type:MoveType::Fly(FlySettings)
|
||||
//MoveType::Physics(PhysicsSettings) -> PhysicsSettings (strafe,rocket,jump,walk,ladder,swim,gravity)
|
||||
//jumping is allowed
|
||||
pub jump:Option<JumpSettings>,
|
||||
//standing & walking is allowed
|
||||
@ -41,135 +39,33 @@ impl std::default::Default for StyleModifiers{
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum JumpCalculation{
|
||||
Max,//Roblox: jumped_speed=max(velocity.boost(),velocity.jump())
|
||||
BoostThenJump,//jumped_speed=velocity.boost().jump()
|
||||
JumpThenBoost,//jumped_speed=velocity.jump().boost()
|
||||
Capped,//roblox
|
||||
Energy,//new
|
||||
Linear,//source
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum JumpImpulse{
|
||||
Time(Time),//jump time is invariant across mass and gravity changes
|
||||
Height(Planar64),//jump height is invariant across mass and gravity changes
|
||||
Linear(Planar64),//jump velocity is invariant across mass and gravity changes
|
||||
Energy(Planar64),// :)
|
||||
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
|
||||
FromEnergy(Planar64),// :)
|
||||
}
|
||||
//Jumping acts on dot(walks_state.normal,body.velocity)
|
||||
//Capped means it increases the dot to the cap
|
||||
//Energy means it adds energy
|
||||
//Linear means it linearly adds on
|
||||
impl JumpImpulse{
|
||||
pub fn jump(
|
||||
&self,
|
||||
velocity:Planar64Vec3,
|
||||
jump_dir:Planar64Vec3,
|
||||
gravity:&Planar64Vec3,
|
||||
mass:Planar64,
|
||||
)->Planar64Vec3{
|
||||
match self{
|
||||
&JumpImpulse::Time(time)=>velocity-*gravity*time,
|
||||
&JumpImpulse::Height(height)=>{
|
||||
//height==-v.y*v.y/(2*g.y);
|
||||
//use energy to determine max height
|
||||
let g=gravity.length();
|
||||
let v_g=gravity.dot(velocity)/g;
|
||||
//do it backwards
|
||||
velocity-gravity.with_length((v_g*v_g+height*g*2).sqrt()+v_g)
|
||||
},
|
||||
&JumpImpulse::Linear(jump_speed)=>velocity+jump_dir.with_length(jump_speed),
|
||||
&JumpImpulse::Energy(energy)=>{
|
||||
//calculate energy
|
||||
let e=gravity.dot(velocity);
|
||||
//add
|
||||
//you get the idea
|
||||
todo!()
|
||||
},
|
||||
}
|
||||
}
|
||||
//TODO: remove this and implement JumpCalculation properly
|
||||
//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::Time(time)=>gravity.length()*(time/2),
|
||||
&JumpImpulse::Height(height)=>(gravity.length()*height*2).sqrt(),
|
||||
&JumpImpulse::Linear(deltav)=>deltav,
|
||||
&JumpImpulse::Energy(energy)=>(energy*2/mass).sqrt(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct JumpSettings{
|
||||
//information used to calculate jump power
|
||||
pub impulse:JumpImpulse,
|
||||
//information used to calculate jump behaviour
|
||||
pub calculation:JumpCalculation,
|
||||
//limit the minimum jump power when combined with downwards momentum
|
||||
//This is true in both roblox and source
|
||||
pub limit_minimum:bool,
|
||||
}
|
||||
impl JumpSettings{
|
||||
pub fn jumped_velocity(
|
||||
&self,
|
||||
style:&StyleModifiers,
|
||||
jump_dir:Planar64Vec3,
|
||||
rel_velocity:Planar64Vec3,
|
||||
booster:Option<&crate::gameplay_attributes::Booster>,
|
||||
)->Planar64Vec3{
|
||||
let jump_speed=self.impulse.get_jump_deltav(&style.gravity,style.mass);
|
||||
match (self.limit_minimum,&self.calculation){
|
||||
(true,JumpCalculation::Max)=>{
|
||||
//the roblox calculation
|
||||
let boost_vel=match booster{
|
||||
Some(booster)=>booster.boost(rel_velocity),
|
||||
None=>rel_velocity,
|
||||
};
|
||||
let j=boost_vel.dot(jump_dir);
|
||||
if j<jump_speed{
|
||||
//weak booster: just do a regular jump
|
||||
boost_vel+jump_dir.with_length(jump_speed-j)
|
||||
}else{
|
||||
//activate booster normally, jump does nothing
|
||||
boost_vel
|
||||
}
|
||||
},
|
||||
(true,_)=>{
|
||||
//the source calculation (?)
|
||||
let boost_vel=match booster{
|
||||
Some(booster)=>booster.boost(rel_velocity),
|
||||
None=>rel_velocity,
|
||||
};
|
||||
let j=boost_vel.dot(jump_dir);
|
||||
if j<jump_speed{
|
||||
//speed in direction of jump cannot be lower than amount
|
||||
boost_vel+jump_dir.with_length(jump_speed-j)
|
||||
}else{
|
||||
//boost and jump add together
|
||||
boost_vel+jump_dir.with_length(jump_speed)
|
||||
}
|
||||
}
|
||||
(false,JumpCalculation::Max)=>{
|
||||
//??? calculation
|
||||
//max(boost_vel,jump_vel)
|
||||
let boost_vel=match booster{
|
||||
Some(booster)=>booster.boost(rel_velocity),
|
||||
None=>rel_velocity,
|
||||
};
|
||||
let boost_dot=boost_vel.dot(jump_dir);
|
||||
if boost_dot<jump_speed{
|
||||
//weak boost is extended to jump speed
|
||||
boost_vel+jump_dir.with_length(jump_speed-boost_dot)
|
||||
}else{
|
||||
//activate booster normally, jump does nothing
|
||||
boost_vel
|
||||
}
|
||||
},
|
||||
//the strafe client calculation
|
||||
(false,_)=>{
|
||||
let boost_vel=match booster{
|
||||
Some(booster)=>booster.boost(rel_velocity),
|
||||
None=>rel_velocity,
|
||||
};
|
||||
boost_vel+jump_dir.with_length(jump_speed)
|
||||
},
|
||||
&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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,14 +73,25 @@ impl JumpSettings{
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ControlsActivation{
|
||||
//allowed keys
|
||||
pub controls_mask:Controls,
|
||||
controls_mask:Controls,
|
||||
//allow strafing only if any of the masked controls are held, eg W|S for shsw
|
||||
pub controls_intersects:Controls,
|
||||
controls_intersects:Controls,
|
||||
//allow strafing only if all of the masked controls are held, eg W for hsw, w-only
|
||||
pub controls_contains:Controls,
|
||||
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)
|
||||
}
|
||||
@ -253,10 +160,10 @@ impl ControlsActivation{
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct StrafeSettings{
|
||||
pub enable:ControlsActivation,
|
||||
pub mv:Planar64,
|
||||
pub air_accel_limit:Option<Planar64>,
|
||||
pub tick_rate:Ratio64,
|
||||
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>{
|
||||
@ -279,7 +186,7 @@ impl StrafeSettings{
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct PropulsionSettings{
|
||||
pub magnitude:Planar64,
|
||||
magnitude:Planar64,
|
||||
}
|
||||
impl PropulsionSettings{
|
||||
pub fn acceleration(&self,control_dir:Planar64Vec3)->Planar64Vec3{
|
||||
@ -287,18 +194,38 @@ impl PropulsionSettings{
|
||||
}
|
||||
}
|
||||
|
||||
#[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{
|
||||
pub accel:Planar64,
|
||||
pub topspeed:Planar64,
|
||||
accel:Planar64,
|
||||
topspeed:Planar64,
|
||||
}
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct WalkSettings{
|
||||
pub accelerate:AccelerateSettings,
|
||||
pub static_friction:Planar64,
|
||||
pub kinetic_friction:Planar64,
|
||||
accelerate:AccelerateSettings,
|
||||
static_friction:Planar64,
|
||||
kinetic_friction:Planar64,
|
||||
//if a surf slope angle does not exist, then everything is slippery and walking is impossible
|
||||
pub surf_dot:Planar64,//surf_dot<n.dot(up)/n.length()
|
||||
surf_dot:Planar64,//surf_dot<n.dot(up)/n.length()
|
||||
}
|
||||
impl WalkSettings{
|
||||
pub fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
|
||||
@ -340,13 +267,13 @@ impl WalkSettings{
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct LadderSettings{
|
||||
pub accelerate:AccelerateSettings,
|
||||
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
|
||||
pub dot:Planar64,
|
||||
dot:Planar64,
|
||||
}
|
||||
impl LadderSettings{
|
||||
pub const fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
|
||||
pub fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
|
||||
//TODO: fallible ladder accel
|
||||
self.accelerate.accel
|
||||
}
|
||||
@ -405,7 +332,7 @@ impl Hitbox{
|
||||
}
|
||||
pub fn source()->Self{
|
||||
Self{
|
||||
halfsize:Planar64Vec3::int(33,73,33)/2*VALVE_SCALE,
|
||||
halfsize:Planar64Vec3::raw(33,73,33)/2*VALVE_SCALE,
|
||||
mesh:HitboxMesh::Box,
|
||||
}
|
||||
}
|
||||
@ -427,9 +354,8 @@ impl StyleModifiers{
|
||||
tick_rate:Ratio64::new(64,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump:Some(JumpSettings{
|
||||
impulse:JumpImpulse::Energy(Planar64::int(512)),
|
||||
calculation:JumpCalculation::JumpThenBoost,
|
||||
limit_minimum:false,
|
||||
impulse:JumpImpulse::FromEnergy(Planar64::int(512)),
|
||||
calculation:JumpCalculation::Energy,
|
||||
}),
|
||||
gravity:Planar64Vec3::int(0,-80,0),
|
||||
mass:Planar64::int(1),
|
||||
@ -469,9 +395,8 @@ impl StyleModifiers{
|
||||
tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump:Some(JumpSettings{
|
||||
impulse:JumpImpulse::Time(Time::from_micros(715_588)),
|
||||
calculation:JumpCalculation::Max,
|
||||
limit_minimum:true,
|
||||
impulse:JumpImpulse::FromTime(Time::from_micros(715_588)),
|
||||
calculation:JumpCalculation::Linear,//Should be capped
|
||||
}),
|
||||
gravity:Planar64Vec3::int(0,-100,0),
|
||||
mass:Planar64::int(1),
|
||||
@ -526,9 +451,8 @@ impl StyleModifiers{
|
||||
tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump:Some(JumpSettings{
|
||||
impulse:JumpImpulse::Height(Planar64::int(52)*VALVE_SCALE),
|
||||
calculation:JumpCalculation::JumpThenBoost,
|
||||
limit_minimum:true,
|
||||
impulse:JumpImpulse::FromHeight(Planar64::int(52)*VALVE_SCALE),
|
||||
calculation:JumpCalculation::Linear,
|
||||
}),
|
||||
gravity:Planar64Vec3::int(0,-800,0)*VALVE_SCALE,
|
||||
mass:Planar64::int(1),
|
||||
@ -567,9 +491,8 @@ impl StyleModifiers{
|
||||
tick_rate:Ratio64::new(66,Time::ONE_SECOND.nanos() as u64).unwrap(),
|
||||
}),
|
||||
jump:Some(JumpSettings{
|
||||
impulse:JumpImpulse::Height(Planar64::int(52)*VALVE_SCALE),
|
||||
calculation:JumpCalculation::JumpThenBoost,
|
||||
limit_minimum:true,
|
||||
impulse:JumpImpulse::FromHeight(Planar64::int(52)*VALVE_SCALE),
|
||||
calculation:JumpCalculation::Linear,
|
||||
}),
|
||||
gravity:Planar64Vec3::int(0,-800,0)*VALVE_SCALE,
|
||||
mass:Planar64::int(1),
|
||||
@ -597,4 +520,4 @@ impl StyleModifiers{
|
||||
camera_offset:(Planar64Vec3::int(0,64,0)-Planar64Vec3::int(0,73,0)/2)*VALVE_SCALE,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -19,14 +19,14 @@ pub struct InstructionCollector<I>{
|
||||
instruction:Option<I>,
|
||||
}
|
||||
impl<I> InstructionCollector<I>{
|
||||
pub const fn new(time:Time)->Self{
|
||||
pub fn new(time:Time)->Self{
|
||||
Self{
|
||||
time,
|
||||
instruction:None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub const fn time(&self)->Time{
|
||||
pub fn time(&self)->Time{
|
||||
self.time
|
||||
}
|
||||
pub fn collect(&mut self,instruction:Option<TimedInstruction<I>>){
|
||||
@ -50,4 +50,4 @@ impl<I> InstructionCollector<I>{
|
||||
None=>None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,14 +10,6 @@ impl Time{
|
||||
pub const ONE_MICROSECOND:Self=Self(1_000);
|
||||
pub const ONE_NANOSECOND:Self=Self(1);
|
||||
#[inline]
|
||||
pub const fn raw(num:i64)->Self{
|
||||
Self(num)
|
||||
}
|
||||
#[inline]
|
||||
pub const fn get(self)->i64{
|
||||
self.0
|
||||
}
|
||||
#[inline]
|
||||
pub const fn from_secs(num:i64)->Self{
|
||||
Self(Self::ONE_SECOND.0*num)
|
||||
}
|
||||
@ -117,14 +109,6 @@ impl Ratio64{
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub const fn num(self)->i64{
|
||||
self.num
|
||||
}
|
||||
#[inline]
|
||||
pub const fn den(self)->u64{
|
||||
self.den
|
||||
}
|
||||
#[inline]
|
||||
pub const fn mul_int(&self,rhs:i64)->i64{
|
||||
rhs*self.num/(self.den as i64)
|
||||
}
|
||||
@ -448,25 +432,8 @@ impl Planar64{
|
||||
self.0
|
||||
}
|
||||
#[inline]
|
||||
pub const fn abs(self)->Self{
|
||||
Self(self.0.abs())
|
||||
}
|
||||
#[inline]
|
||||
pub fn sqrt(&self)->Self{
|
||||
const BITS:i32=64;
|
||||
const FRAC:i32=32;
|
||||
let pow=(((BITS-FRAC-(self.0.leading_zeros() as i32)+1)>>1)+FRAC)-1;
|
||||
let mut result=Self::ZERO;
|
||||
let wide_self=(self.0 as i128)<<FRAC;
|
||||
for i in (0..=pow).rev(){
|
||||
let new_result=Self::raw(result.0|1<<i);
|
||||
match wide_self.cmp(&((new_result.0 as i128)*(new_result.0 as i128))){
|
||||
core::cmp::Ordering::Less=>(),
|
||||
core::cmp::Ordering::Equal=>return new_result,
|
||||
core::cmp::Ordering::Greater=>result=new_result,
|
||||
}
|
||||
}
|
||||
result
|
||||
Planar64(unsafe{(((self.0 as i128)<<32) as f64).sqrt().to_int_unchecked()})
|
||||
}
|
||||
#[inline]
|
||||
pub const fn signum_i64(&self)->i64{
|
||||
@ -631,26 +598,14 @@ impl Planar64Vec3{
|
||||
Self(glam::i64vec3(x.0,y.0,z.0))
|
||||
}
|
||||
#[inline]
|
||||
pub const fn get(self)->glam::I64Vec3{
|
||||
self.0
|
||||
}
|
||||
#[inline]
|
||||
pub const fn int(x:i32,y:i32,z:i32)->Self{
|
||||
Self(glam::i64vec3((x as i64)<<32,(y as i64)<<32,(z as i64)<<32))
|
||||
}
|
||||
#[inline]
|
||||
pub const fn raw_xyz(x:i64,y:i64,z:i64)->Self{
|
||||
pub const fn raw(x:i64,y:i64,z:i64)->Self{
|
||||
Self(glam::i64vec3(x,y,z))
|
||||
}
|
||||
#[inline]
|
||||
pub const fn raw_array(xyz:[i64;3])->Self{
|
||||
Self(glam::I64Vec3::from_array(xyz))
|
||||
}
|
||||
#[inline]
|
||||
pub const fn raw(xyz:glam::I64Vec3)->Self{
|
||||
Self(xyz)
|
||||
}
|
||||
#[inline]
|
||||
pub const fn x(&self)->Planar64{
|
||||
Planar64(self.0.x)
|
||||
}
|
||||
@ -902,9 +857,9 @@ impl Planar64Mat3{
|
||||
#[inline]
|
||||
pub const fn from_diagonal(diagonal:Planar64Vec3)->Self{
|
||||
Self{
|
||||
x_axis:Planar64Vec3::raw_xyz(diagonal.0.x,0,0),
|
||||
y_axis:Planar64Vec3::raw_xyz(0,diagonal.0.y,0),
|
||||
z_axis:Planar64Vec3::raw_xyz(0,0,diagonal.0.z),
|
||||
x_axis:Planar64Vec3::raw(diagonal.0.x,0,0),
|
||||
y_axis:Planar64Vec3::raw(0,diagonal.0.y,0),
|
||||
z_axis:Planar64Vec3::raw(0,0,diagonal.0.z),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
@ -948,25 +903,25 @@ impl Planar64Mat3{
|
||||
+self.x_axis.0.x as i128*self.y_axis.0.y as i128*self.z_axis.0.z as i128
|
||||
)>>32;
|
||||
Self{
|
||||
x_axis:Planar64Vec3::raw_xyz((((-(self.y_axis.0.z as i128*self.z_axis.0.y as i128)+self.y_axis.0.y as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((self.x_axis.0.z as i128*self.z_axis.0.y as i128-self.x_axis.0.y as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((-(self.x_axis.0.z as i128*self.y_axis.0.y as i128)+self.x_axis.0.y as i128*self.y_axis.0.z as i128)<<32)/det) as i64),
|
||||
y_axis:Planar64Vec3::raw_xyz((((self.y_axis.0.z as i128*self.z_axis.0.x as i128-self.y_axis.0.x as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((-(self.x_axis.0.z as i128*self.z_axis.0.x as i128)+self.x_axis.0.x as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((self.x_axis.0.z as i128*self.y_axis.0.x as i128-self.x_axis.0.x as i128*self.y_axis.0.z as i128)<<32)/det) as i64),
|
||||
z_axis:Planar64Vec3::raw_xyz((((-(self.y_axis.0.y as i128*self.z_axis.0.x as i128)+self.y_axis.0.x as i128*self.z_axis.0.y as i128)<<32)/det) as i64,(((self.x_axis.0.y as i128*self.z_axis.0.x as i128-self.x_axis.0.x as i128*self.z_axis.0.y as i128)<<32)/det) as i64,(((-(self.x_axis.0.y as i128*self.y_axis.0.x as i128)+self.x_axis.0.x as i128*self.y_axis.0.y as i128)<<32)/det) as i64),
|
||||
x_axis:Planar64Vec3::raw((((-(self.y_axis.0.z as i128*self.z_axis.0.y as i128)+self.y_axis.0.y as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((self.x_axis.0.z as i128*self.z_axis.0.y as i128-self.x_axis.0.y as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((-(self.x_axis.0.z as i128*self.y_axis.0.y as i128)+self.x_axis.0.y as i128*self.y_axis.0.z as i128)<<32)/det) as i64),
|
||||
y_axis:Planar64Vec3::raw((((self.y_axis.0.z as i128*self.z_axis.0.x as i128-self.y_axis.0.x as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((-(self.x_axis.0.z as i128*self.z_axis.0.x as i128)+self.x_axis.0.x as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((self.x_axis.0.z as i128*self.y_axis.0.x as i128-self.x_axis.0.x as i128*self.y_axis.0.z as i128)<<32)/det) as i64),
|
||||
z_axis:Planar64Vec3::raw((((-(self.y_axis.0.y as i128*self.z_axis.0.x as i128)+self.y_axis.0.x as i128*self.z_axis.0.y as i128)<<32)/det) as i64,(((self.x_axis.0.y as i128*self.z_axis.0.x as i128-self.x_axis.0.x as i128*self.z_axis.0.y as i128)<<32)/det) as i64,(((-(self.x_axis.0.y as i128*self.y_axis.0.x as i128)+self.x_axis.0.x as i128*self.y_axis.0.y as i128)<<32)/det) as i64),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub const fn inverse_times_det(&self)->Self{
|
||||
Self{
|
||||
x_axis:Planar64Vec3::raw_xyz(((-(self.y_axis.0.z as i128*self.z_axis.0.y as i128)+self.y_axis.0.y as i128*self.z_axis.0.z as i128)>>32) as i64,((self.x_axis.0.z as i128*self.z_axis.0.y as i128-self.x_axis.0.y as i128*self.z_axis.0.z as i128)>>32) as i64,((-(self.x_axis.0.z as i128*self.y_axis.0.y as i128)+self.x_axis.0.y as i128*self.y_axis.0.z as i128)>>32) as i64),
|
||||
y_axis:Planar64Vec3::raw_xyz(((self.y_axis.0.z as i128*self.z_axis.0.x as i128-self.y_axis.0.x as i128*self.z_axis.0.z as i128)>>32) as i64,((-(self.x_axis.0.z as i128*self.z_axis.0.x as i128)+self.x_axis.0.x as i128*self.z_axis.0.z as i128)>>32) as i64,((self.x_axis.0.z as i128*self.y_axis.0.x as i128-self.x_axis.0.x as i128*self.y_axis.0.z as i128)>>32) as i64),
|
||||
z_axis:Planar64Vec3::raw_xyz(((-(self.y_axis.0.y as i128*self.z_axis.0.x as i128)+self.y_axis.0.x as i128*self.z_axis.0.y as i128)>>32) as i64,((self.x_axis.0.y as i128*self.z_axis.0.x as i128-self.x_axis.0.x as i128*self.z_axis.0.y as i128)>>32) as i64,((-(self.x_axis.0.y as i128*self.y_axis.0.x as i128)+self.x_axis.0.x as i128*self.y_axis.0.y as i128)>>32) as i64),
|
||||
x_axis:Planar64Vec3::raw(((-(self.y_axis.0.z as i128*self.z_axis.0.y as i128)+self.y_axis.0.y as i128*self.z_axis.0.z as i128)>>32) as i64,((self.x_axis.0.z as i128*self.z_axis.0.y as i128-self.x_axis.0.y as i128*self.z_axis.0.z as i128)>>32) as i64,((-(self.x_axis.0.z as i128*self.y_axis.0.y as i128)+self.x_axis.0.y as i128*self.y_axis.0.z as i128)>>32) as i64),
|
||||
y_axis:Planar64Vec3::raw(((self.y_axis.0.z as i128*self.z_axis.0.x as i128-self.y_axis.0.x as i128*self.z_axis.0.z as i128)>>32) as i64,((-(self.x_axis.0.z as i128*self.z_axis.0.x as i128)+self.x_axis.0.x as i128*self.z_axis.0.z as i128)>>32) as i64,((self.x_axis.0.z as i128*self.y_axis.0.x as i128-self.x_axis.0.x as i128*self.y_axis.0.z as i128)>>32) as i64),
|
||||
z_axis:Planar64Vec3::raw(((-(self.y_axis.0.y as i128*self.z_axis.0.x as i128)+self.y_axis.0.x as i128*self.z_axis.0.y as i128)>>32) as i64,((self.x_axis.0.y as i128*self.z_axis.0.x as i128-self.x_axis.0.x as i128*self.z_axis.0.y as i128)>>32) as i64,((-(self.x_axis.0.y as i128*self.y_axis.0.x as i128)+self.x_axis.0.x as i128*self.y_axis.0.y as i128)>>32) as i64),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub const fn transpose(&self)->Self{
|
||||
Self{
|
||||
x_axis:Planar64Vec3::raw_xyz(self.x_axis.0.x,self.y_axis.0.x,self.z_axis.0.x),
|
||||
y_axis:Planar64Vec3::raw_xyz(self.x_axis.0.y,self.y_axis.0.y,self.z_axis.0.y),
|
||||
z_axis:Planar64Vec3::raw_xyz(self.x_axis.0.z,self.y_axis.0.z,self.z_axis.0.z),
|
||||
x_axis:Planar64Vec3::raw(self.x_axis.0.x,self.y_axis.0.x,self.z_axis.0.x),
|
||||
y_axis:Planar64Vec3::raw(self.x_axis.0.y,self.y_axis.0.y,self.z_axis.0.y),
|
||||
z_axis:Planar64Vec3::raw(self.x_axis.0.z,self.y_axis.0.z,self.z_axis.0.z),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
@ -1091,4 +1046,4 @@ fn test_sqrt(){
|
||||
assert_eq!(1717986918400,r.get());
|
||||
let s=r.sqrt();
|
||||
assert_eq!(85899345920,s.get());
|
||||
}
|
||||
}
|
@ -1,16 +1,12 @@
|
||||
pub mod bvh;
|
||||
pub mod map;
|
||||
pub mod run;
|
||||
pub mod aabb;
|
||||
pub mod model;
|
||||
pub mod mouse;
|
||||
pub mod timer;
|
||||
pub mod zeroes;
|
||||
pub mod integer;
|
||||
pub mod physics;
|
||||
pub mod updatable;
|
||||
pub mod instruction;
|
||||
pub mod gameplay_attributes;
|
||||
pub mod gameplay_modes;
|
||||
pub mod gameplay_style;
|
||||
pub mod controls_bitflag;
|
||||
pub mod controls_bitflag;
|
@ -76,7 +76,7 @@ impl MapVertexId for PolygonGroup{
|
||||
}
|
||||
}
|
||||
/// Ah yes, a group of things to render at the same time
|
||||
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,PartialEq)]
|
||||
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
|
||||
pub struct TextureId(u32);
|
||||
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
|
||||
pub struct RenderConfigId(u32);
|
||||
@ -103,7 +103,7 @@ pub struct IndexedPhysicsGroup{
|
||||
pub groups:Vec<PolygonGroupId>,
|
||||
}
|
||||
//This is a superset of PhysicsModel and GraphicsModel
|
||||
#[derive(Clone,Copy,Debug,Hash,id::Id,Eq,PartialEq)]
|
||||
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
|
||||
pub struct MeshId(u32);
|
||||
#[derive(Clone)]
|
||||
pub struct Mesh{
|
||||
|
26
src/mouse.rs
26
src/mouse.rs
@ -1,26 +0,0 @@
|
||||
use crate::integer::Time;
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct MouseState{
|
||||
pub pos:glam::IVec2,
|
||||
pub time:Time,
|
||||
}
|
||||
impl Default for MouseState{
|
||||
fn default()->Self{
|
||||
Self{
|
||||
time:Time::ZERO,
|
||||
pos:glam::IVec2::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MouseState{
|
||||
pub fn lerp(&self,target:&MouseState,time:Time)->glam::IVec2{
|
||||
let m0=self.pos.as_i64vec2();
|
||||
let m1=target.pos.as_i64vec2();
|
||||
//these are deltas
|
||||
let t1t=(target.time-time).nanos();
|
||||
let tt0=(time-self.time).nanos();
|
||||
let dt=(target.time-self.time).nanos();
|
||||
((m0*t1t+m1*tt0)/dt).as_ivec2()
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum Instruction{
|
||||
ReplaceMouse(crate::mouse::MouseState,crate::mouse::MouseState),
|
||||
SetNextMouse(crate::mouse::MouseState),
|
||||
SetMoveRight(bool),
|
||||
SetMoveUp(bool),
|
||||
SetMoveBack(bool),
|
||||
SetMoveLeft(bool),
|
||||
SetMoveDown(bool),
|
||||
SetMoveForward(bool),
|
||||
SetJump(bool),
|
||||
SetZoom(bool),
|
||||
/// Reset: fully replace the physics state.
|
||||
/// This forgets all inputs and settings which need to be reapplied.
|
||||
Reset,
|
||||
/// Restart: Teleport to the start zone.
|
||||
Restart,
|
||||
/// Spawn: Teleport to a specific mode's spawn
|
||||
/// Sets current mode & spawn
|
||||
Spawn(crate::gameplay_modes::ModeId,crate::gameplay_modes::StageId),
|
||||
Idle,
|
||||
//Idle: there were no input events, but the simulation is safe to advance to this timestep
|
||||
//for interpolation / networking / playback reasons, most playback heads will always want
|
||||
//to be 1 instruction ahead to generate the next state for interpolation.
|
||||
PracticeFly,
|
||||
SetSensitivity(crate::integer::Ratio64Vec2),
|
||||
}
|
103
src/run.rs
103
src/run.rs
@ -1,103 +0,0 @@
|
||||
use crate::timer::{TimerFixed,Realtime,Paused,Unpaused};
|
||||
use crate::integer::Time;
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub enum FlagReason{
|
||||
Anticheat,
|
||||
StyleChange,
|
||||
Clock,
|
||||
Pause,
|
||||
Flying,
|
||||
Gravity,
|
||||
Timescale,
|
||||
TimeTravel,
|
||||
Teleport,
|
||||
}
|
||||
impl ToString for FlagReason{
|
||||
fn to_string(&self)->String{
|
||||
match self{
|
||||
FlagReason::Anticheat=>"Passed through anticheat zone.",
|
||||
FlagReason::StyleChange=>"Changed style.",
|
||||
FlagReason::Clock=>"Incorrect clock. (This can be caused by internet hiccups)",
|
||||
FlagReason::Pause=>"Pausing is not allowed in this style.",
|
||||
FlagReason::Flying=>"Flying is not allowed in this style.",
|
||||
FlagReason::Gravity=>"Gravity modification is not allowed in this style.",
|
||||
FlagReason::Timescale=>"Timescale is not allowed in this style.",
|
||||
FlagReason::TimeTravel=>"Time travel is not allowed in this style.",
|
||||
FlagReason::Teleport=>"Illegal teleport.",
|
||||
}.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error{
|
||||
NotStarted,
|
||||
AlreadyStarted,
|
||||
AlreadyFinished,
|
||||
}
|
||||
impl std::fmt::Display for Error{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
write!(f,"{self:?}")
|
||||
}
|
||||
}
|
||||
impl std::error::Error for Error{}
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
enum RunState{
|
||||
Created,
|
||||
Started{timer:TimerFixed<Realtime,Unpaused>},
|
||||
Finished{timer:TimerFixed<Realtime,Paused>},
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct Run{
|
||||
state:RunState,
|
||||
flagged:Option<FlagReason>,
|
||||
}
|
||||
|
||||
impl Run{
|
||||
pub fn new()->Self{
|
||||
Self{
|
||||
state:RunState::Created,
|
||||
flagged:None,
|
||||
}
|
||||
}
|
||||
pub fn time(&self,time:Time)->Time{
|
||||
match &self.state{
|
||||
RunState::Created=>Time::ZERO,
|
||||
RunState::Started{timer}=>timer.time(time),
|
||||
RunState::Finished{timer}=>timer.time(time),
|
||||
}
|
||||
}
|
||||
pub fn start(&mut self,time:Time)->Result<(),Error>{
|
||||
match &self.state{
|
||||
RunState::Created=>{
|
||||
self.state=RunState::Started{
|
||||
timer:TimerFixed::new(time,Time::ZERO),
|
||||
};
|
||||
Ok(())
|
||||
},
|
||||
RunState::Started{..}=>Err(Error::AlreadyStarted),
|
||||
RunState::Finished{..}=>Err(Error::AlreadyFinished),
|
||||
}
|
||||
}
|
||||
pub fn finish(&mut self,time:Time)->Result<(),Error>{
|
||||
//this uses Copy
|
||||
match &self.state{
|
||||
RunState::Created=>Err(Error::NotStarted),
|
||||
RunState::Started{timer}=>{
|
||||
self.state=RunState::Finished{
|
||||
timer:timer.into_paused(time),
|
||||
};
|
||||
Ok(())
|
||||
},
|
||||
RunState::Finished{..}=>Err(Error::AlreadyFinished),
|
||||
}
|
||||
}
|
||||
pub fn flag(&mut self,flag_reason:FlagReason){
|
||||
//don't replace the first reason the run was flagged
|
||||
if self.flagged.is_none(){
|
||||
self.flagged=Some(flag_reason);
|
||||
}
|
||||
}
|
||||
}
|
319
src/timer.rs
319
src/timer.rs
@ -1,319 +0,0 @@
|
||||
use crate::integer::{Time,Ratio64};
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct Paused;
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct Unpaused;
|
||||
|
||||
pub trait PauseState:Copy+std::fmt::Debug{
|
||||
const IS_PAUSED:bool;
|
||||
fn new()->Self;
|
||||
}
|
||||
impl PauseState for Paused{
|
||||
const IS_PAUSED:bool=true;
|
||||
fn new()->Self{
|
||||
Self
|
||||
}
|
||||
}
|
||||
impl PauseState for Unpaused{
|
||||
const IS_PAUSED:bool=false;
|
||||
fn new()->Self{
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct Realtime{
|
||||
offset:Time,
|
||||
}
|
||||
impl Realtime{
|
||||
pub const fn new(offset:Time)->Self{
|
||||
Self{offset}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct Scaled{
|
||||
scale:Ratio64,
|
||||
offset:Time,
|
||||
}
|
||||
impl Scaled{
|
||||
pub const fn new(scale:Ratio64,offset:Time)->Self{
|
||||
Self{scale,offset}
|
||||
}
|
||||
const fn with_scale(scale:Ratio64)->Self{
|
||||
Self{scale,offset:Time::ZERO}
|
||||
}
|
||||
const fn scale(&self,time:Time)->Time{
|
||||
Time::raw(self.scale.mul_int(time.get()))
|
||||
}
|
||||
const fn get_scale(&self)->Ratio64{
|
||||
self.scale
|
||||
}
|
||||
fn set_scale(&mut self,time:Time,new_scale:Ratio64){
|
||||
let new_time=self.get_time(time);
|
||||
self.scale=new_scale;
|
||||
self.set_time(time,new_time);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TimerState:Copy+std::fmt::Debug{
|
||||
fn identity()->Self;
|
||||
fn get_time(&self,time:Time)->Time;
|
||||
fn set_time(&mut self,time:Time,new_time:Time);
|
||||
fn get_offset(&self)->Time;
|
||||
fn set_offset(&mut self,offset:Time);
|
||||
}
|
||||
impl TimerState for Realtime{
|
||||
fn identity()->Self{
|
||||
Self{offset:Time::ZERO}
|
||||
}
|
||||
fn get_time(&self,time:Time)->Time{
|
||||
time+self.offset
|
||||
}
|
||||
fn set_time(&mut self,time:Time,new_time:Time){
|
||||
self.offset=new_time-time;
|
||||
}
|
||||
fn get_offset(&self)->Time{
|
||||
self.offset
|
||||
}
|
||||
fn set_offset(&mut self,offset:Time){
|
||||
self.offset=offset;
|
||||
}
|
||||
}
|
||||
impl TimerState for Scaled{
|
||||
fn identity()->Self{
|
||||
Self{scale:Ratio64::ONE,offset:Time::ZERO}
|
||||
}
|
||||
fn get_time(&self,time:Time)->Time{
|
||||
self.scale(time)+self.offset
|
||||
}
|
||||
fn set_time(&mut self,time:Time,new_time:Time){
|
||||
self.offset=new_time-self.scale(time);
|
||||
}
|
||||
fn get_offset(&self)->Time{
|
||||
self.offset
|
||||
}
|
||||
fn set_offset(&mut self,offset:Time){
|
||||
self.offset=offset;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct TimerFixed<T:TimerState,P:PauseState>{
|
||||
state:T,
|
||||
_paused:P,
|
||||
}
|
||||
|
||||
//scaled timer methods are generic across PauseState
|
||||
impl<P:PauseState> TimerFixed<Scaled,P>{
|
||||
pub fn scaled(time:Time,new_time:Time,scale:Ratio64)->Self{
|
||||
let mut timer=Self{
|
||||
state:Scaled::with_scale(scale),
|
||||
_paused:P::new(),
|
||||
};
|
||||
timer.set_time(time,new_time);
|
||||
timer
|
||||
}
|
||||
pub const fn get_scale(&self)->Ratio64{
|
||||
self.state.get_scale()
|
||||
}
|
||||
pub fn set_scale(&mut self,time:Time,new_scale:Ratio64){
|
||||
self.state.set_scale(time,new_scale)
|
||||
}
|
||||
}
|
||||
|
||||
//pause and unpause is generic across TimerState
|
||||
impl<T:TimerState> TimerFixed<T,Paused>{
|
||||
pub fn into_unpaused(self,time:Time)->TimerFixed<T,Unpaused>{
|
||||
let new_time=self.time(time);
|
||||
let mut timer=TimerFixed{
|
||||
state:self.state,
|
||||
_paused:Unpaused,
|
||||
};
|
||||
timer.set_time(time,new_time);
|
||||
timer
|
||||
}
|
||||
}
|
||||
impl<T:TimerState> TimerFixed<T,Unpaused>{
|
||||
pub fn into_paused(self,time:Time)->TimerFixed<T,Paused>{
|
||||
let new_time=self.time(time);
|
||||
let mut timer=TimerFixed{
|
||||
state:self.state,
|
||||
_paused:Paused,
|
||||
};
|
||||
timer.set_time(time,new_time);
|
||||
timer
|
||||
}
|
||||
}
|
||||
|
||||
//the new constructor and time queries are generic across both
|
||||
impl<T:TimerState,P:PauseState> TimerFixed<T,P>{
|
||||
pub fn new(time:Time,new_time:Time)->Self{
|
||||
let mut timer=Self{
|
||||
state:T::identity(),
|
||||
_paused:P::new(),
|
||||
};
|
||||
timer.set_time(time,new_time);
|
||||
timer
|
||||
}
|
||||
pub fn from_state(state:T)->Self{
|
||||
Self{
|
||||
state,
|
||||
_paused:P::new(),
|
||||
}
|
||||
}
|
||||
pub fn into_state(self)->T{
|
||||
self.state
|
||||
}
|
||||
pub fn time(&self,time:Time)->Time{
|
||||
match P::IS_PAUSED{
|
||||
true=>self.state.get_offset(),
|
||||
false=>self.state.get_time(time),
|
||||
}
|
||||
}
|
||||
pub fn set_time(&mut self,time:Time,new_time:Time){
|
||||
match P::IS_PAUSED{
|
||||
true=>self.state.set_offset(new_time),
|
||||
false=>self.state.set_time(time,new_time),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error{
|
||||
AlreadyPaused,
|
||||
AlreadyUnpaused,
|
||||
}
|
||||
impl std::fmt::Display for Error{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
write!(f,"{self:?}")
|
||||
}
|
||||
}
|
||||
impl std::error::Error for Error{}
|
||||
|
||||
//wrapper type which holds type state internally
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum Timer<T:TimerState>{
|
||||
Paused(TimerFixed<T,Paused>),
|
||||
Unpaused(TimerFixed<T,Unpaused>),
|
||||
}
|
||||
impl<T:TimerState> Timer<T>{
|
||||
pub fn from_state(state:T,paused:bool)->Self{
|
||||
match paused{
|
||||
true=>Self::Paused(TimerFixed::from_state(state)),
|
||||
false=>Self::Unpaused(TimerFixed::from_state(state)),
|
||||
}
|
||||
}
|
||||
pub fn into_state(self)->(T,bool){
|
||||
match self{
|
||||
Self::Paused(timer)=>(timer.into_state(),true),
|
||||
Self::Unpaused(timer)=>(timer.into_state(),false),
|
||||
}
|
||||
}
|
||||
pub fn paused(time:Time,new_time:Time)->Self{
|
||||
Self::Paused(TimerFixed::new(time,new_time))
|
||||
}
|
||||
pub fn unpaused(time:Time,new_time:Time)->Self{
|
||||
Self::Unpaused(TimerFixed::new(time,new_time))
|
||||
}
|
||||
pub fn time(&self,time:Time)->Time{
|
||||
match self{
|
||||
Self::Paused(timer)=>timer.time(time),
|
||||
Self::Unpaused(timer)=>timer.time(time),
|
||||
}
|
||||
}
|
||||
pub fn set_time(&mut self,time:Time,new_time:Time){
|
||||
match self{
|
||||
Self::Paused(timer)=>timer.set_time(time,new_time),
|
||||
Self::Unpaused(timer)=>timer.set_time(time,new_time),
|
||||
}
|
||||
}
|
||||
pub fn pause(&mut self,time:Time)->Result<(),Error>{
|
||||
*self=match *self{
|
||||
Self::Paused(_)=>return Err(Error::AlreadyPaused),
|
||||
Self::Unpaused(timer)=>Self::Paused(timer.into_paused(time)),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn unpause(&mut self,time:Time)->Result<(),Error>{
|
||||
*self=match *self{
|
||||
Self::Paused(timer)=>Self::Unpaused(timer.into_unpaused(time)),
|
||||
Self::Unpaused(_)=>return Err(Error::AlreadyUnpaused),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn is_paused(&self)->bool{
|
||||
match self{
|
||||
Self::Paused(_)=>true,
|
||||
Self::Unpaused(_)=>false,
|
||||
}
|
||||
}
|
||||
pub fn set_paused(&mut self,time:Time,paused:bool)->Result<(),Error>{
|
||||
match paused{
|
||||
true=>self.pause(time),
|
||||
false=>self.unpause(time),
|
||||
}
|
||||
}
|
||||
}
|
||||
//scaled timer methods are generic across PauseState
|
||||
impl Timer<Scaled>{
|
||||
pub const fn get_scale(&self)->Ratio64{
|
||||
match self{
|
||||
Self::Paused(timer)=>timer.get_scale(),
|
||||
Self::Unpaused(timer)=>timer.get_scale(),
|
||||
}
|
||||
}
|
||||
pub fn set_scale(&mut self,time:Time,new_scale:Ratio64){
|
||||
match self{
|
||||
Self::Paused(timer)=>timer.set_scale(time,new_scale),
|
||||
Self::Unpaused(timer)=>timer.set_scale(time,new_scale),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use super::*;
|
||||
macro_rules! sec {
|
||||
($s: expr) => {
|
||||
Time::from_secs($s)
|
||||
};
|
||||
}
|
||||
#[test]
|
||||
fn test_timerfixed_scaled(){
|
||||
//create a paused timer that reads 0s
|
||||
let timer=TimerFixed::<Scaled,Paused>::new(sec!(0),sec!(0));
|
||||
//the paused timer at 1 second should read 0s
|
||||
assert_eq!(timer.time(sec!(1)),sec!(0));
|
||||
|
||||
//unpause it after one second
|
||||
let timer=timer.into_unpaused(sec!(1));
|
||||
//the timer at 6 seconds should read 5s
|
||||
assert_eq!(timer.time(sec!(6)),sec!(5));
|
||||
|
||||
//pause the timer after 11 seconds
|
||||
let timer=timer.into_paused(sec!(11));
|
||||
//the paused timer at 20 seconds should read 10s
|
||||
assert_eq!(timer.time(sec!(20)),sec!(10));
|
||||
}
|
||||
#[test]
|
||||
fn test_timer()->Result<(),Error>{
|
||||
//create a paused timer that reads 0s
|
||||
let mut timer=Timer::<Realtime>::paused(sec!(0),sec!(0));
|
||||
//the paused timer at 1 second should read 0s
|
||||
assert_eq!(timer.time(sec!(1)),sec!(0));
|
||||
|
||||
//unpause it after one second
|
||||
timer.unpause(sec!(1))?;
|
||||
//the timer at 6 seconds should read 5s
|
||||
assert_eq!(timer.time(sec!(6)),sec!(5));
|
||||
|
||||
//pause the timer after 11 seconds
|
||||
timer.pause(sec!(11))?;
|
||||
//the paused timer at 20 seconds should read 10s
|
||||
assert_eq!(timer.time(sec!(20)),sec!(10));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,41 +1,40 @@
|
||||
//find roots of polynomials
|
||||
use arrayvec::ArrayVec;
|
||||
use crate::integer::Planar64;
|
||||
|
||||
#[inline]
|
||||
pub fn zeroes2(a0:Planar64,a1:Planar64,a2:Planar64)->ArrayVec<Planar64,2>{
|
||||
pub fn zeroes2(a0:Planar64,a1:Planar64,a2:Planar64) -> Vec<Planar64>{
|
||||
if a2==Planar64::ZERO{
|
||||
return zeroes1(a0,a1);
|
||||
return zeroes1(a0, a1);
|
||||
}
|
||||
let radicand=a1.get() as i128*a1.get() as i128-a2.get() as i128*a0.get() as i128*4;
|
||||
if 0<radicand{
|
||||
if 0<radicand {
|
||||
//start with f64 sqrt
|
||||
//failure case: 2^63 < sqrt(2^127)
|
||||
let planar_radicand=Planar64::raw(unsafe{(radicand as f64).sqrt().to_int_unchecked()});
|
||||
//TODO: one or two newtons
|
||||
//sort roots ascending and avoid taking the difference of large numbers
|
||||
match (Planar64::ZERO<a2,Planar64::ZERO<a1){
|
||||
(true, true )=>[(-a1-planar_radicand)/(a2*2),(a0*2)/(-a1-planar_radicand)].into(),
|
||||
(true, false)=>[(a0*2)/(-a1+planar_radicand),(-a1+planar_radicand)/(a2*2)].into(),
|
||||
(false,true )=>[(a0*2)/(-a1-planar_radicand),(-a1-planar_radicand)/(a2*2)].into(),
|
||||
(false,false)=>[(-a1+planar_radicand)/(a2*2),(a0*2)/(-a1+planar_radicand)].into(),
|
||||
(true, true )=>vec![(-a1-planar_radicand)/(a2*2),(a0*2)/(-a1-planar_radicand)],
|
||||
(true, false)=>vec![(a0*2)/(-a1+planar_radicand),(-a1+planar_radicand)/(a2*2)],
|
||||
(false,true )=>vec![(a0*2)/(-a1-planar_radicand),(-a1-planar_radicand)/(a2*2)],
|
||||
(false,false)=>vec![(-a1+planar_radicand)/(a2*2),(a0*2)/(-a1+planar_radicand)],
|
||||
}
|
||||
}else if radicand==0{
|
||||
return ArrayVec::from_iter([a1/(a2*-2)]);
|
||||
}else{
|
||||
return ArrayVec::new_const();
|
||||
} else if radicand==0 {
|
||||
return vec![a1/(a2*-2)];
|
||||
} else {
|
||||
return vec![];
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn zeroes1(a0:Planar64,a1:Planar64)->ArrayVec<Planar64,2>{
|
||||
pub fn zeroes1(a0:Planar64,a1:Planar64) -> Vec<Planar64> {
|
||||
if a1==Planar64::ZERO{
|
||||
return ArrayVec::new_const();
|
||||
return vec![];
|
||||
}else{
|
||||
let q=((-a0.get() as i128)<<32)/(a1.get() as i128);
|
||||
if i64::MIN as i128<=q&&q<=i64::MAX as i128{
|
||||
return ArrayVec::from_iter([Planar64::raw(q as i64)]);
|
||||
return vec![Planar64::raw(q as i64)];
|
||||
}else{
|
||||
return ArrayVec::new_const();
|
||||
return vec![];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user