point hitbox work

This commit is contained in:
Quaternions 2023-11-02 16:44:14 -07:00
parent 769f88eb9f
commit d71f143d68
4 changed files with 236 additions and 91 deletions

View File

@ -1,5 +1,5 @@
//integer units //integer units
#[derive(Clone,Copy,Hash,PartialEq,PartialOrd,Debug)] #[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
pub struct Time(i64); pub struct Time(i64);
impl Time{ impl Time{
pub const ZERO:Self=Self(0); pub const ZERO:Self=Self(0);
@ -800,7 +800,7 @@ impl std::ops::Div<i64> for Planar64Vec3{
} }
///[-1.0,1.0] = [-2^32,2^32] ///[-1.0,1.0] = [-2^32,2^32]
#[derive(Clone,Copy)] #[derive(Clone,Copy,Hash,Eq,PartialEq)]
pub struct Planar64Mat3{ pub struct Planar64Mat3{
x_axis:Planar64Vec3, x_axis:Planar64Vec3,
y_axis:Planar64Vec3, y_axis:Planar64Vec3,
@ -916,7 +916,7 @@ impl std::ops::Div<i64> for Planar64Mat3{
} }
///[-1.0,1.0] = [-2^32,2^32] ///[-1.0,1.0] = [-2^32,2^32]
#[derive(Clone,Copy,Default)] #[derive(Clone,Copy,Default,Hash,Eq,PartialEq)]
pub struct Planar64Affine3{ pub struct Planar64Affine3{
pub matrix3:Planar64Mat3,//includes scale above 1 pub matrix3:Planar64Mat3,//includes scale above 1
pub translation:Planar64Vec3, pub translation:Planar64Vec3,

View File

@ -83,11 +83,11 @@ pub enum TempIndexedAttributes{
} }
//you have this effect while in contact //you have this effect while in contact
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub struct ContactingLadder{ pub struct ContactingLadder{
pub sticky:bool pub sticky:bool
} }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub enum ContactingBehaviour{ pub enum ContactingBehaviour{
Surf, Surf,
Cling,//usable as a zipline, or other weird and wonderful things Cling,//usable as a zipline, or other weird and wonderful things
@ -95,24 +95,24 @@ pub enum ContactingBehaviour{
Elastic(u32),//[1/2^32,1] 0=None (elasticity+1)/2^32 Elastic(u32),//[1/2^32,1] 0=None (elasticity+1)/2^32
} }
//you have this effect while intersecting //you have this effect while intersecting
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub struct IntersectingWater{ pub struct IntersectingWater{
pub viscosity:Planar64, pub viscosity:Planar64,
pub density:Planar64, pub density:Planar64,
pub current:Planar64Vec3, pub current:Planar64Vec3,
} }
//All models can be given these attributes //All models can be given these attributes
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub struct GameMechanicAccelerator{ pub struct GameMechanicAccelerator{
pub acceleration:Planar64Vec3 pub acceleration:Planar64Vec3
} }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub enum GameMechanicBooster{ pub enum GameMechanicBooster{
Affine(Planar64Affine3),//capable of SetVelocity,DotVelocity,normal booster,bouncy part,redirect velocity, and much more Affine(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 Velocity(Planar64Vec3),//straight up boost velocity adds to your current velocity
Energy{direction:Planar64Vec3,energy:Planar64},//increase energy in direction Energy{direction:Planar64Vec3,energy:Planar64},//increase energy in direction
} }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub enum GameMechanicCheckpoint{ pub enum GameMechanicCheckpoint{
Ordered{ Ordered{
mode_id:u32, mode_id:u32,
@ -122,12 +122,12 @@ pub enum GameMechanicCheckpoint{
mode_id:u32, mode_id:u32,
}, },
} }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub enum TrajectoryChoice{ pub enum TrajectoryChoice{
HighArcLongDuration,//underhand lob at target: less horizontal speed and more air time HighArcLongDuration,//underhand lob at target: less horizontal speed and more air time
LowArcShortDuration,//overhand throw at target: more horizontal speed and less air time LowArcShortDuration,//overhand throw at target: more horizontal speed and less air time
} }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub enum GameMechanicSetTrajectory{ pub enum GameMechanicSetTrajectory{
AirTime(Time),//air time (relative to gravity direction) is invariant across mass and gravity changes AirTime(Time),//air time (relative to gravity direction) is invariant across mass and gravity changes
Height(Planar64),//boost height (relative to gravity direction) is invariant across mass and gravity changes Height(Planar64),//boost height (relative to gravity direction) is invariant across mass and gravity changes
@ -143,14 +143,14 @@ pub enum GameMechanicSetTrajectory{
Velocity(Planar64Vec3),//SetVelocity Velocity(Planar64Vec3),//SetVelocity
DotVelocity{direction:Planar64Vec3,dot:Planar64},//set your velocity in a specific direction without touching other directions DotVelocity{direction:Planar64Vec3,dot:Planar64},//set your velocity in a specific direction without touching other directions
} }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub enum ZoneBehaviour{ pub enum ZoneBehaviour{
//Start is indexed //Start is indexed
//Checkpoints are indexed //Checkpoints are indexed
Finish, Finish,
Anitcheat, Anitcheat,
} }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub struct GameMechanicZone{ pub struct GameMechanicZone{
pub mode_id:u32, pub mode_id:u32,
pub behaviour:ZoneBehaviour, pub behaviour:ZoneBehaviour,
@ -161,7 +161,7 @@ pub struct GameMechanicZone{
// InRange(Planar64,Planar64), // InRange(Planar64,Planar64),
// OutsideRange(Planar64,Planar64), // OutsideRange(Planar64,Planar64),
// } // }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub enum StageElementBehaviour{ pub enum StageElementBehaviour{
//Spawn,//The behaviour of stepping on a spawn setting the spawnid //Spawn,//The behaviour of stepping on a spawn setting the spawnid
SpawnAt, SpawnAt,
@ -178,14 +178,14 @@ pub enum StageElementBehaviour{
JumpLimit(u32), JumpLimit(u32),
//Speedtrap(TrapCondition),//Acts as a trigger with a speed condition //Speedtrap(TrapCondition),//Acts as a trigger with a speed condition
} }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub struct GameMechanicStageElement{ pub struct GameMechanicStageElement{
pub mode_id:u32, pub mode_id:u32,
pub stage_id:u32,//which spawn to send to pub stage_id:u32,//which spawn to send to
pub force:bool,//allow setting to lower spawn id i.e. 7->3 pub force:bool,//allow setting to lower spawn id i.e. 7->3
pub behaviour:StageElementBehaviour pub behaviour:StageElementBehaviour
} }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub struct GameMechanicWormhole{ pub struct GameMechanicWormhole{
//destination does not need to be another wormhole //destination does not need to be another wormhole
//this defines a one way portal to a destination model transform //this defines a one way portal to a destination model transform
@ -193,13 +193,13 @@ pub struct GameMechanicWormhole{
pub destination_model_id:u32, pub destination_model_id:u32,
//(position,angles)*=origin.transform.inverse()*destination.transform //(position,angles)*=origin.transform.inverse()*destination.transform
} }
#[derive(Clone)] #[derive(Clone,Hash,Eq,PartialEq)]
pub enum TeleportBehaviour{ pub enum TeleportBehaviour{
StageElement(GameMechanicStageElement), StageElement(GameMechanicStageElement),
Wormhole(GameMechanicWormhole), Wormhole(GameMechanicWormhole),
} }
//attributes listed in order of handling //attributes listed in order of handling
#[derive(Default,Clone)] #[derive(Default,Clone,Hash,Eq,PartialEq)]
pub struct GameMechanicAttributes{ pub struct GameMechanicAttributes{
pub zone:Option<GameMechanicZone>, pub zone:Option<GameMechanicZone>,
pub booster:Option<GameMechanicBooster>, pub booster:Option<GameMechanicBooster>,
@ -218,7 +218,7 @@ impl GameMechanicAttributes{
||self.accelerator.is_some() ||self.accelerator.is_some()
} }
} }
#[derive(Default,Clone)] #[derive(Default,Clone,Hash,Eq,PartialEq)]
pub struct ContactingAttributes{ pub struct ContactingAttributes{
//friction? //friction?
pub contact_behaviour:Option<ContactingBehaviour>, pub contact_behaviour:Option<ContactingBehaviour>,
@ -228,7 +228,7 @@ impl ContactingAttributes{
self.contact_behaviour.is_some() self.contact_behaviour.is_some()
} }
} }
#[derive(Default,Clone)] #[derive(Default,Clone,Hash,Eq,PartialEq)]
pub struct IntersectingAttributes{ pub struct IntersectingAttributes{
pub water:Option<IntersectingWater>, pub water:Option<IntersectingWater>,
} }

View File

@ -20,6 +20,11 @@ struct Face{
normal:Planar64Vec3, normal:Planar64Vec3,
dot:Planar64, dot:Planar64,
} }
impl Face{
fn nd(&self)->(Planar64Vec3,Planar64){
(self.normal,self.dot)
}
}
struct Vert(Planar64Vec3); struct Vert(Planar64Vec3);
struct FaceRefs{ struct FaceRefs{
edges:Vec<(EdgeId,FaceId)>, edges:Vec<(EdgeId,FaceId)>,
@ -171,22 +176,23 @@ impl PhysicsMesh{
pub fn verts<'a>(&'a self)->impl Iterator<Item=Planar64Vec3>+'a{ pub fn verts<'a>(&'a self)->impl Iterator<Item=Planar64Vec3>+'a{
self.verts.iter().map(|Vert(pos)|*pos) self.verts.iter().map(|Vert(pos)|*pos)
} }
pub fn brute_t(&self,body:&crate::physics::Body,time_limit:crate::integer::Time)->Option<(FaceId,crate::integer::Time)>{ pub fn brute(&self,body:&crate::physics::Body,time_limit:crate::integer::Time)->Option<(FaceId,crate::integer::Time)>{
//check each face //check each face
let mut best_time=time_limit; let mut best_time=time_limit;
let mut best_face=None; let mut best_face=None;
for (i,f) in self.face_topology.iter().enumerate(){ for (i,face) in self.faces.iter().enumerate(){
let (n,d)=self.face_nd(FaceId(i)); let face_id=FaceId(i);
let (n,d)=face.nd();
for t in crate::zeroes::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for t in crate::zeroes::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
let t=body.time+crate::integer::Time::from(t); let t=body.time+crate::integer::Time::from(t);
if body.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{ if body.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
let p=body.extrapolated_position(t); let p=body.extrapolated_position(t);
if f.edges.iter().all(|&(_,face_id)|{ if self.face_edges(face_id).iter().all(|&(_,face_id)|{
let (n,d)=self.face_nd(face_id); let (n,d)=self.face_nd(face_id);
n.dot(p)<=d n.dot(p)<=d
}){ }){
best_time=t; best_time=t;
best_face=Some(FaceId(i)); best_face=Some(face_id);
} }
} }
} }
@ -264,6 +270,70 @@ pub struct TransformedMesh<'a>{
normal_transform:&'a crate::integer::Planar64Mat3, normal_transform:&'a crate::integer::Planar64Mat3,
normal_determinant:Planar64, normal_determinant:Planar64,
} }
impl TransformedMesh<'_>{
pub fn brute_in(&self,body:&crate::physics::Body,time_limit:crate::integer::Time)->Option<(FaceId,crate::integer::Time)>{
//check each face
let mut best_time=time_limit;
let mut best_face=None;
for i in 0..self.mesh.faces.len(){
let face_id=FaceId(i);
let (n,d)=self.face_nd(face_id);
for t in crate::zeroes::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
let t=body.time+crate::integer::Time::from(t);
if body.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
let p=body.extrapolated_position(t);
if self.face_edges(face_id).iter().all(|&(_,face_id)|{
let (n,d)=self.face_nd(face_id);
n.dot(p)<=d
}){
best_time=t;
best_face=Some(face_id);
}
}
}
}
best_face.map(|f|(f,best_time))
}
pub fn brute_out(&self,body:&crate::physics::Body,time_limit:crate::integer::Time)->Option<(FaceId,crate::integer::Time)>{
//check each face
let mut best_time=time_limit;
let mut best_face=None;
for i in 0..self.mesh.faces.len(){
let face_id=FaceId(i);
let (n,d)=self.face_nd(face_id);
for t in crate::zeroes::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
let t=body.time+crate::integer::Time::from(t);
if body.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))>Planar64::ZERO{
let p=body.extrapolated_position(t);
if self.face_edges(face_id).iter().all(|&(_,face_id)|{
let (n,d)=self.face_nd(face_id);
n.dot(p)<=d
}){
best_time=t;
best_face=Some(face_id);
}
}
}
}
best_face.map(|f|(f,best_time))
}
pub fn brute_out_face(&self,body:&crate::physics::Body,time_limit:crate::integer::Time,face_id:FaceId)->Option<(FaceId,crate::integer::Time)>{
//check each face
let mut best_time=time_limit;
let mut best_face=None;
for &(edge_id,face_id) in self.mesh.face_edges(face_id).iter(){
let (n,d)=self.face_nd(face_id);
for t in crate::zeroes::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
let t=body.time+crate::integer::Time::from(t);
if body.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))>Planar64::ZERO{
best_time=t;
best_face=Some(face_id);
}
}
}
best_face.map(|f|(f,best_time))
}
}
impl MeshQuery<FaceId,EdgeId,VertId> for TransformedMesh<'_>{ impl MeshQuery<FaceId,EdgeId,VertId> for TransformedMesh<'_>{
fn closest_fev(&self,point:Planar64Vec3)->FEV<FaceId,EdgeId,VertId>{ fn closest_fev(&self,point:Planar64Vec3)->FEV<FaceId,EdgeId,VertId>{
//TODO: put some genius code right here //TODO: put some genius code right here
@ -405,6 +475,7 @@ impl MeshQuery<MinkowskiFace,MinkowskiEdge,MinkowskiVert> for MinkowskiMesh<'_>{
}).collect()) }).collect())
}, },
MinkowskiFace::EdgeEdge(e0,e1)=>{ MinkowskiFace::EdgeEdge(e0,e1)=>{
/*
let e0v=self.mesh0.edge_verts(e0); let e0v=self.mesh0.edge_verts(e0);
let e1v=self.mesh1.edge_verts(e1); let e1v=self.mesh1.edge_verts(e1);
let [r0,r1]=e0v.map(|vert_id0|{ let [r0,r1]=e0v.map(|vert_id0|{
@ -427,6 +498,8 @@ impl MeshQuery<MinkowskiFace,MinkowskiEdge,MinkowskiVert> for MinkowskiMesh<'_>{
(MinkowskiEdge::EdgeVert(e0,vert_id1),MinkowskiFace::VertFace(v0,face_id1)) (MinkowskiEdge::EdgeVert(e0,vert_id1),MinkowskiFace::VertFace(v0,face_id1))
}); });
Cow::Owned(vec![r0,r1,r2,r3]) Cow::Owned(vec![r0,r1,r2,r3])
*/
todo!()
}, },
MinkowskiFace::VertFace(v0,f1)=>{ MinkowskiFace::VertFace(v0,f1)=>{
Cow::Owned(self.mesh1.face_edges(f1).iter().map(|&(edge_id1,face_id1)|{ Cow::Owned(self.mesh1.face_edges(f1).iter().map(|&(edge_id1,face_id1)|{

View File

@ -85,15 +85,12 @@ struct WalkState{
impl WalkEnum{ impl WalkEnum{
//args going crazy //args going crazy
//(walk_enum,body.acceleration)=with_target_velocity(); //(walk_enum,body.acceleration)=with_target_velocity();
fn with_target_velocity(touching:&TouchingState,body:&Body,style:&StyleModifiers,models:&PhysicsModels,mut velocity:Planar64Vec3,normal:&Planar64Vec3)->(WalkEnum,Planar64Vec3){ fn with_target_velocity(body:&Body,style:&StyleModifiers,models:&PhysicsModels,velocity:Planar64Vec3,normal:&Planar64Vec3)->(WalkEnum,Planar64Vec3){
touching.constrain_velocity(models,&mut velocity);
let mut target_diff=velocity-body.velocity; let mut target_diff=velocity-body.velocity;
//remove normal component //remove normal component
target_diff-=normal.clone()*(normal.dot(target_diff)/normal.dot(normal.clone())); target_diff-=normal.clone()*(normal.dot(target_diff)/normal.dot(normal.clone()));
if target_diff==Planar64Vec3::ZERO{ if target_diff==Planar64Vec3::ZERO{
let mut a=Planar64Vec3::ZERO; (WalkEnum::Reached,Planar64Vec3::ZERO)
touching.constrain_acceleration(models,&mut a);
(WalkEnum::Reached,a)
}else{ }else{
//normal friction acceleration is clippedAcceleration.dot(normal)*friction //normal friction acceleration is clippedAcceleration.dot(normal)*friction
let diff_len=target_diff.length(); let diff_len=target_diff.length();
@ -105,21 +102,20 @@ impl WalkEnum{
let accel=style.walk_accel.min(style.gravity.dot(Planar64Vec3::NEG_Y)*friction); let accel=style.walk_accel.min(style.gravity.dot(Planar64Vec3::NEG_Y)*friction);
let time_delta=diff_len/accel; let time_delta=diff_len/accel;
let mut a=target_diff.with_length(accel); let mut a=target_diff.with_length(accel);
touching.constrain_acceleration(models,&mut a);
(WalkEnum::Transient(WalkTarget{velocity,time:body.time+Time::from(time_delta)}),a) (WalkEnum::Transient(WalkTarget{velocity,time:body.time+Time::from(time_delta)}),a)
} }
} }
} }
impl WalkState{ impl WalkState{
fn ground(touching:&TouchingState,body:&Body,style:&StyleModifiers,models:&PhysicsModels,velocity:Planar64Vec3)->(Self,Planar64Vec3){ fn ground(touching:&TouchingState,body:&Body,style:&StyleModifiers,models:&PhysicsModels,velocity:Planar64Vec3)->(Self,Planar64Vec3){
let (walk_enum,a)=WalkEnum::with_target_velocity(touching,body,style,models,velocity,&Planar64Vec3::Y); let (walk_enum,a)=WalkEnum::with_target_velocity(body,style,models,velocity,&Planar64Vec3::Y);
(Self{ (Self{
state:walk_enum, state:walk_enum,
normal:Planar64Vec3::Y, normal:Planar64Vec3::Y,
},a) },a)
} }
fn ladder(touching:&TouchingState,body:&Body,style:&StyleModifiers,models:&PhysicsModels,velocity:Planar64Vec3,normal:&Planar64Vec3)->(Self,Planar64Vec3){ fn ladder(touching:&TouchingState,body:&Body,style:&StyleModifiers,models:&PhysicsModels,velocity:Planar64Vec3,normal:&Planar64Vec3)->(Self,Planar64Vec3){
let (walk_enum,a)=WalkEnum::with_target_velocity(touching,body,style,models,velocity,normal); let (walk_enum,a)=WalkEnum::with_target_velocity(body,style,models,velocity,normal);
(Self{ (Self{
state:walk_enum, state:walk_enum,
normal:normal.clone(), normal:normal.clone(),
@ -208,8 +204,10 @@ impl PhysicsModels{
self.models.push(model); self.models.push(model);
model_id model_id
} }
fn push_attr(&mut self,attr:PhysicsCollisionAttributes){ fn push_attr(&mut self,attr:PhysicsCollisionAttributes)->usize{
let attr_id=self.attributes.len();
self.attributes.push(attr); self.attributes.push(attr);
attr_id
} }
} }
@ -339,6 +337,7 @@ struct StyleModifiers{
swim_speed:Planar64, swim_speed:Planar64,
mass:Planar64, mass:Planar64,
mv:Planar64, mv:Planar64,
surf_slope:Option<Planar64>,
rocket_force:Option<Planar64>, rocket_force:Option<Planar64>,
gravity:Planar64Vec3, gravity:Planar64Vec3,
hitbox_halfsize:Planar64Vec3, hitbox_halfsize:Planar64Vec3,
@ -386,6 +385,7 @@ impl StyleModifiers{
ladder_accel:Planar64::int(160), ladder_accel:Planar64::int(160),
ladder_dot:(Planar64::int(1)/2).sqrt(), ladder_dot:(Planar64::int(1)/2).sqrt(),
swim_speed:Planar64::int(12), swim_speed:Planar64::int(12),
surf_slope:Some(Planar64::raw(7)/8),
hitbox_halfsize:Planar64Vec3::int(2,5,2)/2, hitbox_halfsize:Planar64Vec3::int(2,5,2)/2,
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2 camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
} }
@ -414,6 +414,7 @@ impl StyleModifiers{
ladder_accel:Planar64::int(180), ladder_accel:Planar64::int(180),
ladder_dot:(Planar64::int(1)/2).sqrt(), ladder_dot:(Planar64::int(1)/2).sqrt(),
swim_speed:Planar64::int(12), swim_speed:Planar64::int(12),
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
hitbox_halfsize:Planar64Vec3::int(2,5,2)/2, hitbox_halfsize:Planar64Vec3::int(2,5,2)/2,
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2 camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
} }
@ -441,6 +442,7 @@ impl StyleModifiers{
ladder_accel:Planar64::int(180), ladder_accel:Planar64::int(180),
ladder_dot:(Planar64::int(1)/2).sqrt(), ladder_dot:(Planar64::int(1)/2).sqrt(),
swim_speed:Planar64::int(12), swim_speed:Planar64::int(12),
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
hitbox_halfsize:Planar64Vec3::int(2,5,2)/2, hitbox_halfsize:Planar64Vec3::int(2,5,2)/2,
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2 camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
} }
@ -469,6 +471,7 @@ impl StyleModifiers{
ladder_accel:Planar64::int(180),//? ladder_accel:Planar64::int(180),//?
ladder_dot:(Planar64::int(1)/2).sqrt(),//? ladder_dot:(Planar64::int(1)/2).sqrt(),//?
swim_speed:Planar64::int(12),//? swim_speed:Planar64::int(12),//?
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
hitbox_halfsize:Planar64Vec3::raw(33<<28,73<<28,33<<28)/2, hitbox_halfsize:Planar64Vec3::raw(33<<28,73<<28,33<<28)/2,
camera_offset:Planar64Vec3::raw(0,(64<<28)-(73<<27),0), camera_offset:Planar64Vec3::raw(0,(64<<28)-(73<<27),0),
} }
@ -496,6 +499,7 @@ impl StyleModifiers{
ladder_accel:Planar64::int(180),//? ladder_accel:Planar64::int(180),//?
ladder_dot:(Planar64::int(1)/2).sqrt(),//? ladder_dot:(Planar64::int(1)/2).sqrt(),//?
swim_speed:Planar64::int(12),//? swim_speed:Planar64::int(12),//?
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
hitbox_halfsize:Planar64Vec3::raw(33<<28,73<<28,33<<28)/2, hitbox_halfsize:Planar64Vec3::raw(33<<28,73<<28,33<<28)/2,
camera_offset:Planar64Vec3::raw(0,(64<<28)-(73<<27),0), camera_offset:Planar64Vec3::raw(0,(64<<28)-(73<<27),0),
} }
@ -519,6 +523,7 @@ impl StyleModifiers{
ladder_accel:Planar64::int(180), ladder_accel:Planar64::int(180),
ladder_dot:(Planar64::int(1)/2).sqrt(), ladder_dot:(Planar64::int(1)/2).sqrt(),
swim_speed:Planar64::int(12), swim_speed:Planar64::int(12),
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
hitbox_halfsize:Planar64Vec3::int(2,5,2)/2, hitbox_halfsize:Planar64Vec3::int(2,5,2)/2,
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2 camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
} }
@ -642,6 +647,7 @@ impl PhysicsOutputState{
} }
} }
#[derive(Clone,Hash,Eq,PartialEq)]
enum PhysicsCollisionAttributes{ enum PhysicsCollisionAttributes{
Contact{//track whether you are contacting the object Contact{//track whether you are contacting the object
contacting:crate::model::ContactingAttributes, contacting:crate::model::ContactingAttributes,
@ -652,6 +658,17 @@ enum PhysicsCollisionAttributes{
general:crate::model::GameMechanicAttributes, general:crate::model::GameMechanicAttributes,
}, },
} }
struct NonPhysicsError;
impl TryFrom<&crate::model::CollisionAttributes> for PhysicsCollisionAttributes{
type Error=NonPhysicsError;
fn try_from(value:&crate::model::CollisionAttributes)->Result<Self,Self::Error>{
match value{
crate::model::CollisionAttributes::Decoration=>Err(NonPhysicsError),
crate::model::CollisionAttributes::Contact{contacting,general}=>Ok(Self::Contact{contacting:contacting.clone(),general:general.clone()}),
crate::model::CollisionAttributes::Intersect{intersecting,general}=>Ok(Self::Intersect{intersecting:intersecting.clone(),general:general.clone()}),
}
}
}
pub struct PhysicsModel{ pub struct PhysicsModel{
//A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es //A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es
@ -664,7 +681,7 @@ pub struct PhysicsModel{
} }
impl PhysicsModel{ impl PhysicsModel{
fn new(mesh_id:usize,attr_id:usize,transform:crate::integer::Planar64Affine3)->Self{ pub fn new(mesh_id:usize,attr_id:usize,transform:crate::integer::Planar64Affine3)->Self{
let normal_transform=transform.matrix3.inverse().transpose(); let normal_transform=transform.matrix3.inverse().transpose();
Self{ Self{
mesh_id, mesh_id,
@ -674,18 +691,12 @@ impl PhysicsModel{
normal_determinant:normal_transform.determinant(), normal_determinant:normal_transform.determinant(),
} }
} }
pub fn from_model(mesh_id:usize,instance:&crate::model::ModelInstance)->Option<Self>{
match &instance.attributes{
crate::model::CollisionAttributes::Contact{contacting,general}=>Some(PhysicsModel::new(mesh_id,instance.transform.clone(),PhysicsCollisionAttributes::Contact{contacting:contacting.clone(),general:general.clone()})),
crate::model::CollisionAttributes::Intersect{intersecting,general}=>Some(PhysicsModel::new(mesh_id,instance.transform.clone(),PhysicsCollisionAttributes::Intersect{intersecting:intersecting.clone(),general:general.clone()})),
crate::model::CollisionAttributes::Decoration=>None,
}
}
} }
#[derive(Debug,Clone,Eq,Hash,PartialEq)] #[derive(Debug,Clone,Eq,Hash,PartialEq)]
struct ContactCollision{ struct ContactCollision{
face_id:crate::model_physics::MinkowskiFace, //face_id:crate::model_physics::MinkowskiFace,
face_id:crate::model_physics::FaceId,
model_id:usize,//using id to avoid lifetimes model_id:usize,//using id to avoid lifetimes
} }
#[derive(Debug,Clone,Eq,Hash,PartialEq)] #[derive(Debug,Clone,Eq,Hash,PartialEq)]
@ -697,6 +708,14 @@ enum Collision{
Contact(ContactCollision), Contact(ContactCollision),
Intersect(IntersectCollision), Intersect(IntersectCollision),
} }
impl Collision{
fn model_id(&self)->usize{
match self{
&Collision::Contact(ContactCollision{model_id,face_id:_})
|&Collision::Intersect(IntersectCollision{model_id})=>model_id,
}
}
}
#[derive(Default)] #[derive(Default)]
struct TouchingState{ struct TouchingState{
contacts:std::collections::HashSet::<ContactCollision>, contacts:std::collections::HashSet::<ContactCollision>,
@ -719,23 +738,56 @@ impl TouchingState{
Collision::Intersect(collision)=>self.intersects.remove(collision), Collision::Intersect(collision)=>self.intersects.remove(collision),
} }
} }
fn get_acceleration(&self,gravity:Planar64Vec3)->Planar64Vec3{
//accelerators
//water
//contact constrain?
todo!()
}
fn constrain_velocity(&self,models:&PhysicsModels,velocity:&mut Planar64Vec3){ fn constrain_velocity(&self,models:&PhysicsModels,velocity:&mut Planar64Vec3){
//TODO: trey push solve
for contact in &self.contacts{ for contact in &self.contacts{
//trey push solve let n=contact.normal(models);
velocity-=n.dot(velocity)/n.length();
} }
todo!()
} }
fn constrain_acceleration(&self,models:&PhysicsModels,acceleration:&mut Planar64Vec3){ fn constrain_acceleration(&self,models:&PhysicsModels,acceleration:&mut Planar64Vec3){
//TODO: trey push solve
for contact in &self.contacts{ for contact in &self.contacts{
//trey push solve let n=contact.normal(models);
acceleration-=n.dot(acceleration)/n.length();
}
}
fn get_move_state(&self,mut a:Planar64Vec3)->(MoveState,Planar64Vec3){
//check current move conditions and use heuristics to determine
//which ladder to climb on, which ground to walk on, etc
//collect move state affecting objects from contacts (accelerator,water,ladder,ground)
for contact in &self.contacts{
//
}
for intersect in &self.intersects{
//
}
(move_state,a)
}
fn predict_collision_end(&self,collector:&mut crate::instruction::InstructionCollector<PhysicsInstruction>,models:&PhysicsModels,body:&Body,time:Time){
for contact in &self.contacts{
//detect face slide off
collector.collect(models.mesh(contact.model_id).brute_out_face(body,collector.time(),contact.face_id).map(|(face,time)|{
TimedInstruction{
time,
instruction:PhysicsInstruction::CollisionStart(
Collision::Contact(ContactCollision{model_id:contact.model_id,face_id:face})
),
}
}));
}
let relative_body=VirtualBody::relative(&Body::default(),body).body(time);
for intersect in &self.intersects{
//detect model collision in reverse
collector.collect(models.mesh(intersect.model_id).brute_out(&relative_body,collector.time()).map(|(face,time)|{
TimedInstruction{
time,
instruction:PhysicsInstruction::CollisionStart(
Collision::Intersect(IntersectCollision{model_id:intersect.model_id})
),
}
}));
} }
todo!()
} }
} }
@ -845,13 +897,21 @@ impl PhysicsState {
pub fn generate_models(&mut self,indexed_models:&crate::model::IndexedModelInstances){ pub fn generate_models(&mut self,indexed_models:&crate::model::IndexedModelInstances){
let mut starts=Vec::new(); let mut starts=Vec::new();
let mut spawns=Vec::new(); let mut spawns=Vec::new();
let mut attr_hash=std::collections::HashMap::new();
for model in &indexed_models.models{ for model in &indexed_models.models{
let mesh_id=self.meshes.len(); let mesh_id=self.meshes.len();
let mut make_mesh=false; let mut make_mesh=false;
for model_instance in &model.instances{ for model_instance in &model.instances{
if let Some(model_physics)=PhysicsModel::from_model(mesh_id,model_instance){ if let Ok(physics_attributes)=PhysicsCollisionAttributes::try_from(&model_instance.attributes){
let attr_id=if let Some(&attr_id)=attr_hash.get(&physics_attributes){
attr_id
}else{
let attr_id=self.models.push_attr(physics_attributes.clone());
attr_hash.insert(physics_attributes,attr_id);
attr_id
};
let model_physics=PhysicsModel::new(mesh_id,attr_id,model_instance.transform);
make_mesh=true; make_mesh=true;
//TODO: index attributes
let model_id=self.models.push_model(model_physics); let model_id=self.models.push_model(model_physics);
for attr in &model_instance.temp_indexing{ for attr in &model_instance.temp_indexing{
match attr{ match attr{
@ -918,6 +978,13 @@ impl PhysicsState {
fn set_control(&mut self,control:u32,state:bool){ fn set_control(&mut self,control:u32,state:bool){
self.controls=if state{self.controls|control}else{self.controls&!control}; self.controls=if state{self.controls|control}else{self.controls&!control};
} }
fn gravity(&self)->Planar64Vec3{
let mut a=self.style.gravity;
if let Some(rocket_force)=self.style.rocket_force{
a+=self.style.get_propulsion_control_dir(&self.camera,self.controls,&self.next_mouse,self.time)*rocket_force;
}
a
}
fn jump(&mut self){ fn jump(&mut self){
match &self.move_state{ match &self.move_state{
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>{ MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>{
@ -976,13 +1043,13 @@ impl PhysicsState {
MoveState::Walk(WalkState{normal,state})=>{ MoveState::Walk(WalkState{normal,state})=>{
let n=normal; let n=normal;
let a; let a;
(*state,a)=WalkEnum::with_target_velocity(&self.touching,&self.body,&self.style,&self.models,self.style.get_walk_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time),&n); (*state,a)=WalkEnum::with_target_velocity(&self.body,&self.style,&self.models,self.style.get_walk_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time),&n);
Some(a) Some(a)
}, },
MoveState::Ladder(WalkState{normal,state})=>{ MoveState::Ladder(WalkState{normal,state})=>{
let n=normal; let n=normal;
let a; let a;
(*state,a)=WalkEnum::with_target_velocity(&self.touching,&self.body,&self.style,&self.models,self.style.get_ladder_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time),&n); (*state,a)=WalkEnum::with_target_velocity(&self.body,&self.style,&self.models,self.style.get_ladder_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time),&n);
Some(a) Some(a)
}, },
} }
@ -1003,28 +1070,32 @@ impl PhysicsState {
} }
} }
impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState { impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState{
//this little next instruction function can cache its return value and invalidate the cached value by watching the State. //this little next instruction function can cache its return value and invalidate the cached value by watching the State.
fn next_instruction(&self,time_limit:Time) -> Option<TimedInstruction<PhysicsInstruction>> { fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<PhysicsInstruction>>{
//JUST POLLING!!! NO MUTATION //JUST POLLING!!! NO MUTATION
let mut collector = crate::instruction::InstructionCollector::new(time_limit); let mut collector = crate::instruction::InstructionCollector::new(time_limit);
collector.collect(self.next_move_instruction()); collector.collect(self.next_move_instruction());
//check for collision ends //check for collision ends
self.touching.next_instruction(&mut collector,self.style.mesh,self.body,self.time,collector.time()); self.touching.predict_collision_end(&mut collector,&self.models,&self.body,self.time);
//check for collision starts //check for collision starts
let mut aabb=crate::aabb::Aabb::default(); let mut aabb=crate::aabb::Aabb::default();
aabb.grow(self.body.extrapolated_position(self.time)); aabb.grow(self.body.extrapolated_position(self.time));
aabb.grow(self.body.extrapolated_position(collector.time())); aabb.grow(self.body.extrapolated_position(collector.time()));
aabb.inflate(self.style.hitbox_halfsize); aabb.inflate(self.style.hitbox_halfsize);
//common body
let relative_body=VirtualBody::relative(&Body::default(),&self.body).body(self.time);
self.bvh.the_tester(&aabb,&mut |id|{ self.bvh.the_tester(&aabb,&mut |id|{
//no checks are needed because of the time limits. //no checks are needed because of the time limits.
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(self.style.mesh,&self.models.mesh(id)); //let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(self.style.mesh,&self.models.mesh(id));
let relative_body=VirtualBody::relative(&self.body,&Body::default()).body(self.time); //collector.collect(crate::face_crawler::predict_collision(&minkowski,&relative_body,collector.time()).map(|(face,time)|{
collector.collect(crate::face_crawler::predict_collision(&minkowski,&relative_body,collector.time()).map(|(face,time)|{ collector.collect(self.models.mesh(id).brute_in(&relative_body,collector.time()).map(|(face,time)|{
//TODO: match model attribute and generate Collision::{Contact,Intersect} TimedInstruction{time,instruction:PhysicsInstruction::CollisionStart(match self.models.attr(id){
TimedInstruction{time,instruction:PhysicsInstruction::CollisionStart(Collision::Contact(ContactCollision{model_id:id,face_id:face}))} PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>Collision::Contact(ContactCollision{model_id:id,face_id:face}),
PhysicsCollisionAttributes::Intersect{intersecting:_,general:_}=>Collision::Intersect(IntersectCollision{model_id:id}),
})}
})); }));
}); });
collector.instruction() collector.instruction()
@ -1066,7 +1137,7 @@ fn run_teleport_behaviour(teleport_behaviour:&Option<crate::model::TeleportBehav
None None
}, },
&crate::model::StageElementBehaviour::Checkpoint{ordered_checkpoint_id,unordered_checkpoint_count}=>{ &crate::model::StageElementBehaviour::Checkpoint{ordered_checkpoint_id,unordered_checkpoint_count}=>{
if (ordered_checkpoint_id.is_none()||ordered_checkpoint_id.is_some_and(|id|id<game.next_ordered_checkpoint_id)) if ordered_checkpoint_id.map_or(true,|id|id<game.next_ordered_checkpoint_id)
&&unordered_checkpoint_count<=game.unordered_checkpoints.len() as u32{ &&unordered_checkpoint_count<=game.unordered_checkpoints.len() as u32{
//pass //pass
None None
@ -1107,16 +1178,18 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
} }
match ins.instruction{ match ins.instruction{
PhysicsInstruction::CollisionStart(c)=>{ PhysicsInstruction::CollisionStart(c)=>{
match self.models.attr(c.model_id()){ let model_id=c.model_id();
match self.models.attr(model_id){
PhysicsCollisionAttributes::Contact{contacting,general}=>{ PhysicsCollisionAttributes::Contact{contacting,general}=>{
let mut v=self.body.velocity; let mut v=self.body.velocity;
match &contacting.contact_behaviour{ match &contacting.contact_behaviour{
Some(crate::model::ContactingBehaviour::Surf)=>println!("I'm surfing!"), Some(crate::model::ContactingBehaviour::Surf)=>println!("I'm surfing!"),
Some(crate::model::ContactingBehaviour::Cling)=>println!("Unimplemented!"), Some(crate::model::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
&Some(crate::model::ContactingBehaviour::Elastic(elasticity))=>{ &Some(crate::model::ContactingBehaviour::Elastic(elasticity))=>{
let n=c.normal(&self.models); // let n=c.normal(&self.models);
let d=n.dot(v)*Planar64::raw(-1-elasticity as i64); // let d=n.dot(v)*Planar64::raw(-1-elasticity as i64);
v-=n*(d/n.dot(n)); // v-=n*(d/n.dot(n));
todo!()
}, },
Some(crate::model::ContactingBehaviour::Ladder(contacting_ladder))=>{ Some(crate::model::ContactingBehaviour::Ladder(contacting_ladder))=>{
if contacting_ladder.sticky{ if contacting_ladder.sticky{
@ -1124,18 +1197,21 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
v=Planar64Vec3::ZERO;//model.velocity v=Planar64Vec3::ZERO;//model.velocity
} }
//ladder walkstate //ladder walkstate
let (walk_state,a)=WalkState::ladder(&self.touching,&self.body,&self.style,&self.models,self.style.get_ladder_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time),&c.normal(&self.models)); let mut target_velocity=self.style.get_ladder_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time);
self.touching.constrain_velocity(&self.models,&mut target_velocity);
let (walk_state,mut a)=WalkState::ladder(&self.touching,&self.body,&self.style,&self.models,target_velocity,&c.normal(&self.models));
self.move_state=MoveState::Ladder(walk_state); self.move_state=MoveState::Ladder(walk_state);
self.touching.constrain_acceleration(&self.models,&mut a);
self.body.acceleration=a; self.body.acceleration=a;
} }
None=>match &c.face { None=>if self.style.surf_slope.map_or(true,|s|s<c.normal(&self.models).slope(up)){
TreyMeshFace::Top => { //ground
//ground let mut target_velocity=self.style.get_walk_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time);
let (walk_state,a)=WalkState::ground(&self.touching,&self.body,&self.style,&self.models,self.style.get_walk_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time)); self.touching.constrain_velocity(&self.models,&mut target_velocity);
self.move_state=MoveState::Walk(walk_state); let (walk_state,mut a)=WalkState::ground(&self.touching,&self.body,&self.style,&self.models,target_velocity);
self.body.acceleration=a; self.move_state=MoveState::Walk(walk_state);
}, self.touching.constrain_acceleration(&self.models,&mut a);
_ => (), self.body.acceleration=a;
}, },
} }
//check ground //check ground
@ -1173,7 +1249,8 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
if self.style.get_control(StyleModifiers::CONTROL_JUMP,self.controls){ if self.style.get_control(StyleModifiers::CONTROL_JUMP,self.controls){
self.jump(); self.jump();
} }
if let Some(a)=self.refresh_walk_target(){ if let Some(mut a)=self.refresh_walk_target(){
self.touching.constrain_acceleration(&self.models,&mut a);
self.body.acceleration=a; self.body.acceleration=a;
} }
}, },
@ -1186,18 +1263,12 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
}, },
PhysicsInstruction::CollisionEnd(c) => { PhysicsInstruction::CollisionEnd(c) => {
match self.models.attr(c.model_id()){ match self.models.attr(c.model_id()){
PhysicsCollisionAttributes::Contact{contacting: _,general: _}=>{ PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>{
self.touching.remove(&c);//remove contact before calling contact_constrain_acceleration self.touching.remove(&c);//remove contact before calling contact_constrain_acceleration
let mut a=self.style.gravity;
if let Some(rocket_force)=self.style.rocket_force{
a+=self.style.get_propulsion_control_dir(&self.camera,self.controls,&self.next_mouse,self.time)*rocket_force;
}
self.touching.constrain_acceleration(&self.models,&mut a);
self.body.acceleration=a;
//check ground //check ground
self.move_state=self.touching.get_move_state(); (self.move_state,self.body.acceleration)=self.touching.get_move_state(self.gravity());
}, },
PhysicsCollisionAttributes::Intersect{intersecting: _,general: _}=>{ PhysicsCollisionAttributes::Intersect{intersecting:_,general:_}=>{
self.touching.remove(&c); self.touching.remove(&c);
}, },
} }
@ -1271,7 +1342,8 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
PhysicsInputInstruction::Idle => {refresh_walk_target=false;},//literally idle! PhysicsInputInstruction::Idle => {refresh_walk_target=false;},//literally idle!
} }
if refresh_walk_target{ if refresh_walk_target{
if let Some(a)=self.refresh_walk_target(){ if let Some(mut a)=self.refresh_walk_target(){
self.touching.constrain_acceleration(&self.models,&mut a);
self.body.acceleration=a; self.body.acceleration=a;
}else if let Some(rocket_force)=self.style.rocket_force{ }else if let Some(rocket_force)=self.style.rocket_force{
let mut a=self.style.gravity; let mut a=self.style.gravity;