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
#[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,

View File

@ -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>,
}

View File

@ -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)|{

View File

@ -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)
},
}
@ -1012,19 +1079,23 @@ impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState
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;
}
},
@ -1188,14 +1265,8 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
match self.models.attr(c.model_id()){
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:_}=>{
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;