forked from StrafesNET/strafe-client
point hitbox work
This commit is contained in:
parent
769f88eb9f
commit
d71f143d68
@ -1,5 +1,5 @@
|
||||
//integer units
|
||||
#[derive(Clone,Copy,Hash,PartialEq,PartialOrd,Debug)]
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
|
||||
pub struct Time(i64);
|
||||
impl Time{
|
||||
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]
|
||||
#[derive(Clone,Copy)]
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
|
||||
pub struct Planar64Mat3{
|
||||
x_axis:Planar64Vec3,
|
||||
y_axis:Planar64Vec3,
|
||||
@ -916,7 +916,7 @@ impl std::ops::Div<i64> for Planar64Mat3{
|
||||
}
|
||||
|
||||
///[-1.0,1.0] = [-2^32,2^32]
|
||||
#[derive(Clone,Copy,Default)]
|
||||
#[derive(Clone,Copy,Default,Hash,Eq,PartialEq)]
|
||||
pub struct Planar64Affine3{
|
||||
pub matrix3:Planar64Mat3,//includes scale above 1
|
||||
pub translation:Planar64Vec3,
|
||||
|
34
src/model.rs
34
src/model.rs
@ -83,11 +83,11 @@ pub enum TempIndexedAttributes{
|
||||
}
|
||||
|
||||
//you have this effect while in contact
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub struct ContactingLadder{
|
||||
pub sticky:bool
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub enum ContactingBehaviour{
|
||||
Surf,
|
||||
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
|
||||
}
|
||||
//you have this effect while intersecting
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub struct IntersectingWater{
|
||||
pub viscosity:Planar64,
|
||||
pub density:Planar64,
|
||||
pub current:Planar64Vec3,
|
||||
}
|
||||
//All models can be given these attributes
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub struct GameMechanicAccelerator{
|
||||
pub acceleration:Planar64Vec3
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub enum GameMechanicBooster{
|
||||
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
|
||||
Energy{direction:Planar64Vec3,energy:Planar64},//increase energy in direction
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub enum GameMechanicCheckpoint{
|
||||
Ordered{
|
||||
mode_id:u32,
|
||||
@ -122,12 +122,12 @@ pub enum GameMechanicCheckpoint{
|
||||
mode_id:u32,
|
||||
},
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub enum TrajectoryChoice{
|
||||
HighArcLongDuration,//underhand lob at target: less horizontal speed and more air time
|
||||
LowArcShortDuration,//overhand throw at target: more horizontal speed and less air time
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub enum GameMechanicSetTrajectory{
|
||||
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
|
||||
@ -143,14 +143,14 @@ pub enum GameMechanicSetTrajectory{
|
||||
Velocity(Planar64Vec3),//SetVelocity
|
||||
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{
|
||||
//Start is indexed
|
||||
//Checkpoints are indexed
|
||||
Finish,
|
||||
Anitcheat,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub struct GameMechanicZone{
|
||||
pub mode_id:u32,
|
||||
pub behaviour:ZoneBehaviour,
|
||||
@ -161,7 +161,7 @@ pub struct GameMechanicZone{
|
||||
// InRange(Planar64,Planar64),
|
||||
// OutsideRange(Planar64,Planar64),
|
||||
// }
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub enum StageElementBehaviour{
|
||||
//Spawn,//The behaviour of stepping on a spawn setting the spawnid
|
||||
SpawnAt,
|
||||
@ -178,14 +178,14 @@ pub enum StageElementBehaviour{
|
||||
JumpLimit(u32),
|
||||
//Speedtrap(TrapCondition),//Acts as a trigger with a speed condition
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub struct GameMechanicStageElement{
|
||||
pub mode_id:u32,
|
||||
pub stage_id:u32,//which spawn to send to
|
||||
pub force:bool,//allow setting to lower spawn id i.e. 7->3
|
||||
pub behaviour:StageElementBehaviour
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub struct GameMechanicWormhole{
|
||||
//destination does not need to be another wormhole
|
||||
//this defines a one way portal to a destination model transform
|
||||
@ -193,13 +193,13 @@ pub struct GameMechanicWormhole{
|
||||
pub destination_model_id:u32,
|
||||
//(position,angles)*=origin.transform.inverse()*destination.transform
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
pub enum TeleportBehaviour{
|
||||
StageElement(GameMechanicStageElement),
|
||||
Wormhole(GameMechanicWormhole),
|
||||
}
|
||||
//attributes listed in order of handling
|
||||
#[derive(Default,Clone)]
|
||||
#[derive(Default,Clone,Hash,Eq,PartialEq)]
|
||||
pub struct GameMechanicAttributes{
|
||||
pub zone:Option<GameMechanicZone>,
|
||||
pub booster:Option<GameMechanicBooster>,
|
||||
@ -218,7 +218,7 @@ impl GameMechanicAttributes{
|
||||
||self.accelerator.is_some()
|
||||
}
|
||||
}
|
||||
#[derive(Default,Clone)]
|
||||
#[derive(Default,Clone,Hash,Eq,PartialEq)]
|
||||
pub struct ContactingAttributes{
|
||||
//friction?
|
||||
pub contact_behaviour:Option<ContactingBehaviour>,
|
||||
@ -228,7 +228,7 @@ impl ContactingAttributes{
|
||||
self.contact_behaviour.is_some()
|
||||
}
|
||||
}
|
||||
#[derive(Default,Clone)]
|
||||
#[derive(Default,Clone,Hash,Eq,PartialEq)]
|
||||
pub struct IntersectingAttributes{
|
||||
pub water:Option<IntersectingWater>,
|
||||
}
|
||||
|
@ -20,6 +20,11 @@ struct Face{
|
||||
normal:Planar64Vec3,
|
||||
dot:Planar64,
|
||||
}
|
||||
impl Face{
|
||||
fn nd(&self)->(Planar64Vec3,Planar64){
|
||||
(self.normal,self.dot)
|
||||
}
|
||||
}
|
||||
struct Vert(Planar64Vec3);
|
||||
struct FaceRefs{
|
||||
edges:Vec<(EdgeId,FaceId)>,
|
||||
@ -171,22 +176,23 @@ impl PhysicsMesh{
|
||||
pub fn verts<'a>(&'a self)->impl Iterator<Item=Planar64Vec3>+'a{
|
||||
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
|
||||
let mut best_time=time_limit;
|
||||
let mut best_face=None;
|
||||
for (i,f) in self.face_topology.iter().enumerate(){
|
||||
let (n,d)=self.face_nd(FaceId(i));
|
||||
for (i,face) in self.faces.iter().enumerate(){
|
||||
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)){
|
||||
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 f.edges.iter().all(|&(_,face_id)|{
|
||||
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(FaceId(i));
|
||||
best_face=Some(face_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -264,6 +270,70 @@ pub struct TransformedMesh<'a>{
|
||||
normal_transform:&'a crate::integer::Planar64Mat3,
|
||||
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<'_>{
|
||||
fn closest_fev(&self,point:Planar64Vec3)->FEV<FaceId,EdgeId,VertId>{
|
||||
//TODO: put some genius code right here
|
||||
@ -405,6 +475,7 @@ impl MeshQuery<MinkowskiFace,MinkowskiEdge,MinkowskiVert> for MinkowskiMesh<'_>{
|
||||
}).collect())
|
||||
},
|
||||
MinkowskiFace::EdgeEdge(e0,e1)=>{
|
||||
/*
|
||||
let e0v=self.mesh0.edge_verts(e0);
|
||||
let e1v=self.mesh1.edge_verts(e1);
|
||||
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))
|
||||
});
|
||||
Cow::Owned(vec![r0,r1,r2,r3])
|
||||
*/
|
||||
todo!()
|
||||
},
|
||||
MinkowskiFace::VertFace(v0,f1)=>{
|
||||
Cow::Owned(self.mesh1.face_edges(f1).iter().map(|&(edge_id1,face_id1)|{
|
||||
|
196
src/physics.rs
196
src/physics.rs
@ -85,15 +85,12 @@ struct WalkState{
|
||||
impl WalkEnum{
|
||||
//args going crazy
|
||||
//(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){
|
||||
touching.constrain_velocity(models,&mut velocity);
|
||||
fn with_target_velocity(body:&Body,style:&StyleModifiers,models:&PhysicsModels,velocity:Planar64Vec3,normal:&Planar64Vec3)->(WalkEnum,Planar64Vec3){
|
||||
let mut target_diff=velocity-body.velocity;
|
||||
//remove normal component
|
||||
target_diff-=normal.clone()*(normal.dot(target_diff)/normal.dot(normal.clone()));
|
||||
if target_diff==Planar64Vec3::ZERO{
|
||||
let mut a=Planar64Vec3::ZERO;
|
||||
touching.constrain_acceleration(models,&mut a);
|
||||
(WalkEnum::Reached,a)
|
||||
(WalkEnum::Reached,Planar64Vec3::ZERO)
|
||||
}else{
|
||||
//normal friction acceleration is clippedAcceleration.dot(normal)*friction
|
||||
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 time_delta=diff_len/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)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WalkState{
|
||||
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{
|
||||
state:walk_enum,
|
||||
normal:Planar64Vec3::Y,
|
||||
},a)
|
||||
}
|
||||
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{
|
||||
state:walk_enum,
|
||||
normal:normal.clone(),
|
||||
@ -208,8 +204,10 @@ impl PhysicsModels{
|
||||
self.models.push(model);
|
||||
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);
|
||||
attr_id
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,6 +337,7 @@ struct StyleModifiers{
|
||||
swim_speed:Planar64,
|
||||
mass:Planar64,
|
||||
mv:Planar64,
|
||||
surf_slope:Option<Planar64>,
|
||||
rocket_force:Option<Planar64>,
|
||||
gravity:Planar64Vec3,
|
||||
hitbox_halfsize:Planar64Vec3,
|
||||
@ -386,6 +385,7 @@ impl StyleModifiers{
|
||||
ladder_accel:Planar64::int(160),
|
||||
ladder_dot:(Planar64::int(1)/2).sqrt(),
|
||||
swim_speed:Planar64::int(12),
|
||||
surf_slope:Some(Planar64::raw(7)/8),
|
||||
hitbox_halfsize:Planar64Vec3::int(2,5,2)/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_dot:(Planar64::int(1)/2).sqrt(),
|
||||
swim_speed:Planar64::int(12),
|
||||
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
|
||||
hitbox_halfsize:Planar64Vec3::int(2,5,2)/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_dot:(Planar64::int(1)/2).sqrt(),
|
||||
swim_speed:Planar64::int(12),
|
||||
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
|
||||
hitbox_halfsize:Planar64Vec3::int(2,5,2)/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_dot:(Planar64::int(1)/2).sqrt(),//?
|
||||
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,
|
||||
camera_offset:Planar64Vec3::raw(0,(64<<28)-(73<<27),0),
|
||||
}
|
||||
@ -496,6 +499,7 @@ impl StyleModifiers{
|
||||
ladder_accel:Planar64::int(180),//?
|
||||
ladder_dot:(Planar64::int(1)/2).sqrt(),//?
|
||||
swim_speed:Planar64::int(12),//?
|
||||
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
|
||||
hitbox_halfsize:Planar64Vec3::raw(33<<28,73<<28,33<<28)/2,
|
||||
camera_offset:Planar64Vec3::raw(0,(64<<28)-(73<<27),0),
|
||||
}
|
||||
@ -519,6 +523,7 @@ impl StyleModifiers{
|
||||
ladder_accel:Planar64::int(180),
|
||||
ladder_dot:(Planar64::int(1)/2).sqrt(),
|
||||
swim_speed:Planar64::int(12),
|
||||
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
|
||||
hitbox_halfsize:Planar64Vec3::int(2,5,2)/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{
|
||||
Contact{//track whether you are contacting the object
|
||||
contacting:crate::model::ContactingAttributes,
|
||||
@ -652,6 +658,17 @@ enum PhysicsCollisionAttributes{
|
||||
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{
|
||||
//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{
|
||||
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();
|
||||
Self{
|
||||
mesh_id,
|
||||
@ -674,18 +691,12 @@ impl PhysicsModel{
|
||||
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)]
|
||||
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
|
||||
}
|
||||
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
|
||||
@ -697,6 +708,14 @@ enum Collision{
|
||||
Contact(ContactCollision),
|
||||
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)]
|
||||
struct TouchingState{
|
||||
contacts:std::collections::HashSet::<ContactCollision>,
|
||||
@ -719,23 +738,56 @@ impl TouchingState{
|
||||
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){
|
||||
//TODO: trey push solve
|
||||
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){
|
||||
//TODO: trey push solve
|
||||
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){
|
||||
let mut starts=Vec::new();
|
||||
let mut spawns=Vec::new();
|
||||
let mut attr_hash=std::collections::HashMap::new();
|
||||
for model in &indexed_models.models{
|
||||
let mesh_id=self.meshes.len();
|
||||
let mut make_mesh=false;
|
||||
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;
|
||||
//TODO: index attributes
|
||||
let model_id=self.models.push_model(model_physics);
|
||||
for attr in &model_instance.temp_indexing{
|
||||
match attr{
|
||||
@ -918,6 +978,13 @@ impl PhysicsState {
|
||||
fn set_control(&mut self,control:u32,state:bool){
|
||||
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){
|
||||
match &self.move_state{
|
||||
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>{
|
||||
@ -976,13 +1043,13 @@ impl PhysicsState {
|
||||
MoveState::Walk(WalkState{normal,state})=>{
|
||||
let n=normal;
|
||||
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)
|
||||
},
|
||||
MoveState::Ladder(WalkState{normal,state})=>{
|
||||
let n=normal;
|
||||
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)
|
||||
},
|
||||
}
|
||||
@ -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.
|
||||
fn next_instruction(&self,time_limit:Time) -> Option<TimedInstruction<PhysicsInstruction>> {
|
||||
fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<PhysicsInstruction>>{
|
||||
//JUST POLLING!!! NO MUTATION
|
||||
let mut collector = crate::instruction::InstructionCollector::new(time_limit);
|
||||
|
||||
collector.collect(self.next_move_instruction());
|
||||
|
||||
//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
|
||||
let mut aabb=crate::aabb::Aabb::default();
|
||||
aabb.grow(self.body.extrapolated_position(self.time));
|
||||
aabb.grow(self.body.extrapolated_position(collector.time()));
|
||||
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|{
|
||||
//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 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)|{
|
||||
//TODO: match model attribute and generate Collision::{Contact,Intersect}
|
||||
TimedInstruction{time,instruction:PhysicsInstruction::CollisionStart(Collision::Contact(ContactCollision{model_id:id,face_id:face}))}
|
||||
//let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(self.style.mesh,&self.models.mesh(id));
|
||||
//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)|{
|
||||
TimedInstruction{time,instruction:PhysicsInstruction::CollisionStart(match self.models.attr(id){
|
||||
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()
|
||||
@ -1066,7 +1137,7 @@ fn run_teleport_behaviour(teleport_behaviour:&Option<crate::model::TeleportBehav
|
||||
None
|
||||
},
|
||||
&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{
|
||||
//pass
|
||||
None
|
||||
@ -1107,16 +1178,18 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
|
||||
}
|
||||
match ins.instruction{
|
||||
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}=>{
|
||||
let mut v=self.body.velocity;
|
||||
match &contacting.contact_behaviour{
|
||||
Some(crate::model::ContactingBehaviour::Surf)=>println!("I'm surfing!"),
|
||||
Some(crate::model::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
|
||||
&Some(crate::model::ContactingBehaviour::Elastic(elasticity))=>{
|
||||
let n=c.normal(&self.models);
|
||||
let d=n.dot(v)*Planar64::raw(-1-elasticity as i64);
|
||||
v-=n*(d/n.dot(n));
|
||||
// let n=c.normal(&self.models);
|
||||
// let d=n.dot(v)*Planar64::raw(-1-elasticity as i64);
|
||||
// v-=n*(d/n.dot(n));
|
||||
todo!()
|
||||
},
|
||||
Some(crate::model::ContactingBehaviour::Ladder(contacting_ladder))=>{
|
||||
if contacting_ladder.sticky{
|
||||
@ -1124,19 +1197,22 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
|
||||
v=Planar64Vec3::ZERO;//model.velocity
|
||||
}
|
||||
//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.touching.constrain_acceleration(&self.models,&mut a);
|
||||
self.body.acceleration=a;
|
||||
}
|
||||
None=>match &c.face {
|
||||
TreyMeshFace::Top => {
|
||||
None=>if self.style.surf_slope.map_or(true,|s|s<c.normal(&self.models).slope(up)){
|
||||
//ground
|
||||
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));
|
||||
let mut target_velocity=self.style.get_walk_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::ground(&self.touching,&self.body,&self.style,&self.models,target_velocity);
|
||||
self.move_state=MoveState::Walk(walk_state);
|
||||
self.touching.constrain_acceleration(&self.models,&mut a);
|
||||
self.body.acceleration=a;
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
//check ground
|
||||
self.touching.insert(c);
|
||||
@ -1173,7 +1249,8 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
|
||||
if self.style.get_control(StyleModifiers::CONTROL_JUMP,self.controls){
|
||||
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;
|
||||
}
|
||||
},
|
||||
@ -1186,18 +1263,12 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
|
||||
},
|
||||
PhysicsInstruction::CollisionEnd(c) => {
|
||||
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
|
||||
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
|
||||
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);
|
||||
},
|
||||
}
|
||||
@ -1271,7 +1342,8 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
|
||||
PhysicsInputInstruction::Idle => {refresh_walk_target=false;},//literally idle!
|
||||
}
|
||||
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;
|
||||
}else if let Some(rocket_force)=self.style.rocket_force{
|
||||
let mut a=self.style.gravity;
|
||||
|
Loading…
Reference in New Issue
Block a user