work happening

This commit is contained in:
Quaternions 2024-02-08 19:10:59 -08:00
parent 64baa64dd3
commit 7403ec8bed

View File

@ -1,16 +1,16 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use crate::model_physics::{self,PhysicsMesh,PhysicsMeshTransform,TransformedMesh,MeshQuery,PhysicsMeshId,PhysicsSubmeshId}; use crate::model_physics::{self,PhysicsMesh,PhysicsMeshTransform,TransformedMesh,MeshQuery,PhysicsMeshId,PhysicsSubmeshId};
use strafesnet_common::bvh; use strafesnet_common::bvh;
use strafesnet_common::map; use strafesnet_common::map;
use strafesnet_common::aabb; use strafesnet_common::aabb;
use strafesnet_common::gameplay_modes; use strafesnet_common::gameplay_modes::{self,StageId};
use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId}; use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId};
use strafesnet_common::model::ModelId; use strafesnet_common::model::ModelId;
use strafesnet_common::gameplay_style::{self,StyleModifiers}; use strafesnet_common::gameplay_style::{self,StyleModifiers};
use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,TimedInstruction}; use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,TimedInstruction};
use strafesnet_common::integer::{self,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2}; use strafesnet_common::integer::{self,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2};
use gameplay::ModeState;
#[derive(Debug)] #[derive(Debug)]
pub enum PhysicsInstruction { pub enum PhysicsInstruction {
@ -182,19 +182,6 @@ impl PhysicsModels{
fn attr(&self,model_id:PhysicsModelId)->&PhysicsCollisionAttributes{ fn attr(&self,model_id:PhysicsModelId)->&PhysicsCollisionAttributes{
&self.attributes[&self.models[&model_id].attr_id] &self.attributes[&self.models[&model_id].attr_id]
} }
fn push_mesh(&mut self,mesh:PhysicsMesh){
self.meshes.push(mesh);
}
fn push_model(&mut self,model:PhysicsModel)->PhysicsModelId{
let model_id=PhysicsModelId::new(self.models.len() as u32);
self.models.push(model);
model_id
}
fn push_attr(&mut self,attr:PhysicsCollisionAttributes)->PhysicsAttributesId{
let attr_id=PhysicsAttributesId::new(self.attributes.len() as u32);
self.attributes.push(attr);
attr_id
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -213,7 +200,7 @@ pub struct PhysicsCamera{
//yaw_limit:AngleLimit, //yaw_limit:AngleLimit,
} }
impl PhysicsCamera { impl PhysicsCamera{
const ANGLE_PITCH_LOWER_LIMIT:Angle32=-Angle32::FRAC_PI_2; const ANGLE_PITCH_LOWER_LIMIT:Angle32=-Angle32::FRAC_PI_2;
const ANGLE_PITCH_UPPER_LIMIT:Angle32=Angle32::FRAC_PI_2; const ANGLE_PITCH_UPPER_LIMIT:Angle32=Angle32::FRAC_PI_2;
pub fn move_mouse(&mut self,mouse_pos:glam::IVec2){ pub fn move_mouse(&mut self,mouse_pos:glam::IVec2){
@ -255,22 +242,73 @@ impl std::default::Default for PhysicsCamera{
} }
} }
} }
mod gameplay{
pub struct ModeState{ use super::{gameplay_modes,HashSet,HashMap,ModelId};
mode_id:gameplay_modes::ModeId, pub struct ModeState{
stage_id:gameplay_modes::StageId, mode_id:gameplay_modes::ModeId,
next_ordered_checkpoint_id:gameplay_modes::CheckpointId,//which OrderedCheckpoint model_id you must pass next (if 0 you haven't passed OrderedCheckpoint0) stage_id:gameplay_modes::StageId,
unordered_checkpoints:HashSet<ModelId>, next_ordered_checkpoint_id:gameplay_modes::CheckpointId,//which OrderedCheckpoint model_id you must pass next (if 0 you haven't passed OrderedCheckpoint0)
jump_counts:HashMap<ModelId,u32>,//model_id -> jump count unordered_checkpoints:HashSet<ModelId>,
} jump_counts:HashMap<ModelId,u32>,//model_id -> jump count
impl std::default::Default for ModeState{ }
fn default()->Self{ impl ModeState{
Self{ pub const fn get_mode_id(&self)->gameplay_modes::ModeId{
mode_id:gameplay_modes::ModeId::new(0), self.mode_id
stage_id:gameplay_modes::StageId::new(0), }
next_ordered_checkpoint_id:gameplay_modes::CheckpointId::new(0), pub const fn get_stage_id(&self)->gameplay_modes::StageId{
unordered_checkpoints:HashSet::new(), self.stage_id
jump_counts:HashMap::new(), }
pub const fn get_next_ordered_checkpoint_id(&self)->gameplay_modes::CheckpointId{
self.next_ordered_checkpoint_id
}
pub const fn get_jump_count(&self,model_id:ModelId)->Option<u32>{
self.jump_counts.get(&model_id).copied()
}
pub const fn ordered_checkpoint_count(&self)->u32{
self.next_ordered_checkpoint_id.get()
}
pub const fn unordered_checkpoint_count(&self)->u32{
self.unordered_checkpoints.len() as u32
}
pub fn set_mode_id(&mut self,mode_id:gameplay_modes::ModeId){
self.clear();
self.mode_id=mode_id;
}
pub fn set_stage_id(&mut self,stage_id:gameplay_modes::StageId){
self.clear_checkpoints();
self.stage_id=stage_id;
}
pub const fn accumulate_ordered_checkpoint(&mut self,stage:&gameplay_modes::Stage,model_id:ModelId){
if stage.is_next_ordered_checkpoint(self.get_next_ordered_checkpoint_id(),model_id){
self.next_ordered_checkpoint_id=gameplay_modes::CheckpointId::new(self.next_ordered_checkpoint_id.get()+1);
}
}
pub fn accumulate_unordered_checkpoint(&mut self,stage:&gameplay_modes::Stage,model_id:ModelId){
if stage.is_unordered_checkpoint(model_id){
self.unordered_checkpoints.insert(model_id);
}
}
pub fn clear(&mut self){
self.clear_jump_counts();
self.clear_checkpoints();
}
pub fn clear_jump_counts(&mut self){
self.jump_counts.clear();
}
pub fn clear_checkpoints(&mut self){
self.next_ordered_checkpoint_id=gameplay_modes::CheckpointId::FIRST;
self.unordered_checkpoints.clear();
}
}
impl std::default::Default for ModeState{
fn default()->Self{
Self{
mode_id:gameplay_modes::ModeId::MAIN,
stage_id:gameplay_modes::StageId::FIRST,
next_ordered_checkpoint_id:gameplay_modes::CheckpointId::FIRST,
unordered_checkpoints:HashSet::new(),
jump_counts:HashMap::new(),
}
} }
} }
} }
@ -297,7 +335,7 @@ impl HitboxMesh{
} }
#[inline] #[inline]
fn transformed_mesh(&self)->TransformedMesh{ fn transformed_mesh(&self)->TransformedMesh{
TransformedMesh::new(&self.mesh.complete_mesh_view(),&self.transform) TransformedMesh::new(self.mesh.complete_mesh_view(),&self.transform)
} }
} }
@ -792,22 +830,26 @@ pub struct PhysicsState{
time:Time, time:Time,
body:Body, body:Body,
world:WorldState,//currently there is only one state the world can be in world:WorldState,//currently there is only one state the world can be in
mode_state:ModeState,
style:StyleModifiers,//mode style with custom style updates applied
touching:TouchingState, touching:TouchingState,
//camera must exist in state because wormholes modify the camera, also camera punch //camera must exist in state because wormholes modify the camera, also camera punch
camera:PhysicsCamera, camera:PhysicsCamera,
//input_state
pub next_mouse:MouseState,//Where is the mouse headed next pub next_mouse:MouseState,//Where is the mouse headed next
controls:u32,//TODO this should be a struct controls:u32,//TODO this should be a struct
//style
style:StyleModifiers,//mode style with custom style updates applied
//gameplay_state
mode_state:ModeState,
move_state:MoveState, move_state:MoveState,
} }
//random collection of contextual data that doesn't belong in PhysicsState //random collection of contextual data that doesn't belong in PhysicsState
pub struct PhysicsData{ pub struct PhysicsData{
//permanent map data //permanent map data
bvh:bvh::BvhNode<ConvexMeshId>, bvh:bvh::BvhNode<ConvexMeshId>,
modes:gameplay_modes::Modes, //transient map/environment data (open world loads/unloads parts of this data)
//transient map/environment data (open world may load/unload)
models:PhysicsModels, models:PhysicsModels,
//semi-transient data
modes:gameplay_modes::Modes,
//cached calculations //cached calculations
hitbox_mesh:HitboxMesh, hitbox_mesh:HitboxMesh,
} }
@ -927,7 +969,6 @@ impl instruction::InstructionEmitter<PhysicsInstruction> for PhysicsContext{
} }
impl PhysicsContext{ impl PhysicsContext{
pub fn spawn(&mut self){ pub fn spawn(&mut self){
self.state.mode_state.stage_id=gameplay_modes::StageId::new(0);
self.process_instruction(instruction::TimedInstruction{ self.process_instruction(instruction::TimedInstruction{
time:self.state.time, time:self.state.time,
instruction: PhysicsInstruction::Input(PhysicsInputInstruction::Reset), instruction: PhysicsInstruction::Input(PhysicsInputInstruction::Reset),
@ -975,33 +1016,33 @@ impl PhysicsContext{
//write hash lol //write hash lol
} }
} }
}
//TODO get rid of this trash //TODO get rid of this trash
fn refresh_walk_target(&mut self)->Planar64Vec3{ fn refresh_walk_target(s:&mut PhysicsState,data:&PhysicsData)->Planar64Vec3{
match &mut self.state.move_state{ match &mut s.move_state{
MoveState::Air|MoveState::Water=>self.state.touching.base_acceleration(&self.data.models,&self.state.style,&self.state.camera,self.state.controls,&self.state.next_mouse,self.state.time), MoveState::Air|MoveState::Water=>s.touching.base_acceleration(&data.models,&s.style,&s.camera,s.controls,&s.next_mouse,s.time),
MoveState::Walk(WalkState{state,contact,jump_direction:_})=>{ MoveState::Walk(WalkState{state,contact,jump_direction:_})=>{
let n=contact_normal(&self.data.models,&self.data.hitbox_mesh,contact); let n=contact_normal(&data.models,&data.hitbox_mesh,contact);
let gravity=self.state.touching.base_acceleration(&self.data.models,&self.state.style,&self.state.camera,self.state.controls,&self.state.next_mouse,self.state.time); let gravity=s.touching.base_acceleration(&data.models,&s.style,&s.camera,s.controls,&s.next_mouse,s.time);
let mut a; let mut a;
let mut v=self.state.style.get_walk_target_velocity(&self.state.camera,self.state.controls,&self.state.next_mouse,self.state.time,&n); let mut v=s.style.get_walk_target_velocity(&s.camera,s.controls,&s.next_mouse,s.time,&n);
self.state.touching.constrain_velocity(&self.data.models,&self.data.hitbox_mesh,&mut v); s.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v);
let normal_accel=-n.dot(gravity)/n.length(); let normal_accel=-n.dot(gravity)/n.length();
(*state,a)=WalkEnum::with_target_velocity(&self.state.body,&self.state.style,v,&n,self.state.style.walk_speed,normal_accel); (*state,a)=WalkEnum::with_target_velocity(&s.body,&s.style,v,&n,s.style.walk_speed,normal_accel);
a a
}, },
MoveState::Ladder(WalkState{state,contact,jump_direction:_})=>{ MoveState::Ladder(WalkState{state,contact,jump_direction:_})=>{
let n=contact_normal(&self.data.models,&self.data.hitbox_mesh,contact); let n=contact_normal(&data.models,&data.hitbox_mesh,contact);
let gravity=self.state.touching.base_acceleration(&self.data.models,&self.state.style,&self.state.camera,self.state.controls,&self.state.next_mouse,self.state.time); let gravity=s.touching.base_acceleration(&data.models,&s.style,&s.camera,s.controls,&s.next_mouse,s.time);
let mut a; let mut a;
let mut v=self.state.style.get_ladder_target_velocity(&self.state.camera,self.state.controls,&self.state.next_mouse,self.state.time,&n); let mut v=s.style.get_ladder_target_velocity(&s.camera,s.controls,&s.next_mouse,s.time,&n);
self.state.touching.constrain_velocity(&self.data.models,&self.data.hitbox_mesh,&mut v); s.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v);
(*state,a)=WalkEnum::with_target_velocity(&self.state.body,&self.state.style,v,&n,self.state.style.ladder_speed,self.state.style.ladder_accel); (*state,a)=WalkEnum::with_target_velocity(&s.body,&s.style,v,&n,s.style.ladder_speed,s.style.ladder_accel);
a a
}, },
} }
} }
}
fn literally_next_instruction_but_with_context(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<PhysicsInstruction>>{ fn literally_next_instruction_but_with_context(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<PhysicsInstruction>>{
//JUST POLLING!!! NO MUTATION //JUST POLLING!!! NO MUTATION
@ -1118,46 +1159,53 @@ fn teleport_to_spawn(body:&mut Body,touching:&mut TouchingState,style:&StyleModi
Some(teleport(body,touching,models,style,hitbox_mesh,point)) Some(teleport(body,touching,models,style,hitbox_mesh,point))
} }
fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&mut ModeState,models:&PhysicsModels,mode:&gameplay_modes::Mode,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,touching:&mut TouchingState,body:&mut Body,convex_mesh_id:ConvexMeshId)->Option<MoveState>{ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,mode_state:&mut ModeState,models:&PhysicsModels,mode:&gameplay_modes::Mode,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,touching:&mut TouchingState,body:&mut Body,convex_mesh_id:ConvexMeshId)->Option<MoveState>{
//TODO: jump count and checkpoints are always reset on teleport. //TODO: jump count and checkpoints are always reset on teleport.
//Map makers are expected to use tools to prevent //Map makers are expected to use tools to prevent
//multi-boosting on JumpLimit boosters such as spawning into a SetVelocity //multi-boosting on JumpLimit boosters such as spawning into a SetVelocity
if let Some(stage_element)=mode.get_element(convex_mesh_id.model_id.into()){ if let Some(stage_element)=mode.get_element(convex_mesh_id.model_id.into()){
let stage=mode.get_stage(stage_element.stage_id)?; if let Some(stage)=mode.get_stage(stage_element.stage_id()){
if stage_element.force||game.stage_id<stage_element.stage_id{ if mode_state.get_stage_id()<stage_element.stage_id(){
//TODO: check if all checkpoints are complete up to destination stage id, otherwise set to checkpoint completion stage it
game.stage_id=stage_element.stage_id;
}
match &stage_element.behaviour{
gameplay_modes::StageElementBehaviour::SpawnAt=>(),
gameplay_modes::StageElementBehaviour::Trigger
|gameplay_modes::StageElementBehaviour::Teleport=>{
//I guess this is correct behaviour when trying to teleport to a non-existent spawn but it's still weird
return teleport_to_spawn(body,touching,style,hitbox_mesh,mode,models,game.stage_id);
},
gameplay_modes::StageElementBehaviour::Platform=>(),
&gameplay_modes::StageElementBehaviour::Checkpoint=>{
//checkpoint check //checkpoint check
//TODO: need to check all stages //check if current stage is complete
if stage.ordered_checkpoint_id.map_or(true,|id|id<game.next_ordered_checkpoint_id) if let Some(current_stage)=mode.get_stage(mode_state.get_stage_id()){
&&stage.unordered_checkpoint_count<=game.unordered_checkpoints.len() as u32{ if !current_stage.is_complete(mode_state.ordered_checkpoint_count(),mode_state.unordered_checkpoint_count()){
//pass //do the stage checkpoints have to be reset?
}else{ return teleport_to_spawn(body,touching,style,hitbox_mesh,mode,models,mode_state.get_stage_id());
//fail }
return teleport_to_spawn(body,touching,style,hitbox_mesh,mode,models,game.stage_id);
} }
}, //check if all between stages have no checkpoints required to pass them
} for stage_id in mode_state.get_stage_id().get()+1..stage_element.stage_id().get(){
if let Some(next_checkpoint)=stage.ordered_checkpoints.get(&game.next_ordered_checkpoint_id){ let stage_id=StageId::new(stage_id);
if convex_mesh_id==next_checkpoint{ //check if none of the between stages has checkpoints, if they do teleport back to that stage
//if you hit the next number in a sequence of ordered checkpoints if !mode.get_stage(stage_id)?.is_empty(){
//increment the current checkpoint id mode_state.set_stage_id(stage_id);
game.next_ordered_checkpoint_id=gameplay_modes::CheckpointId::new(game.next_ordered_checkpoint_id.get()+1); return teleport_to_spawn(body,touching,style,hitbox_mesh,mode,models,stage_id);
}
}
//notably you do not get teleported for touching ordered checkpoints in the wrong order within the same stage.
mode_state.set_stage_id(stage_element.stage_id());
}else if stage_element.force(){
//forced stage_element will set the stage_id even if the stage has already been passed
mode_state.set_stage_id(stage_element.stage_id());
}
match stage_element.behaviour(){
gameplay_modes::StageElementBehaviour::SpawnAt=>(),
gameplay_modes::StageElementBehaviour::Trigger
|gameplay_modes::StageElementBehaviour::Teleport=>{
//I guess this is correct behaviour when trying to teleport to a non-existent spawn but it's still weird
return teleport_to_spawn(body,touching,style,hitbox_mesh,mode,models,mode_state.get_stage_id());
},
gameplay_modes::StageElementBehaviour::Platform=>(),
gameplay_modes::StageElementBehaviour::Check=>(),//this is to run the checkpoint check behaviour without any other side effects
gameplay_modes::StageElementBehaviour::Checkpoint=>{
//each of these checks if the model is actually a valid respective checkpoint object
//accumulate sequential ordered checkpoints
mode_state.accumulate_ordered_checkpoint(&stage,convex_mesh_id.model_id.into());
//insert model id in accumulated unordered checkpoints
mode_state.accumulate_unordered_checkpoint(&stage,convex_mesh_id.model_id.into());
},
} }
}
if stage.unordered_checkpoints.contains(convex_mesh_id.model_id.into()){
//count model id in accumulated unordered checkpoints
game.unordered_checkpoints.insert(convex_mesh_id.model_id.into());
} }
} }
match wormhole{ match wormhole{
@ -1230,7 +1278,7 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
//check ground //check ground
state.touching.insert(c); state.touching.insert(c);
//I love making functions with 10 arguments to dodge the borrow checker //I love making functions with 10 arguments to dodge the borrow checker
run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.mode_id).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,convex_mesh_id); run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.get_mode_id()).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,convex_mesh_id);
//flatten v //flatten v
state.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v); state.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v);
match &general.booster{ match &general.booster{
@ -1268,13 +1316,13 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
if calc_move||Planar64::ZERO<normal.dot(v){ if calc_move||Planar64::ZERO<normal.dot(v){
(state.move_state,state.body.acceleration)=state.touching.get_move_state(&state.body,&data.models,&state.style,&data.hitbox_mesh,&state.camera,state.controls,&state.next_mouse,state.time); (state.move_state,state.body.acceleration)=state.touching.get_move_state(&state.body,&data.models,&state.style,&data.hitbox_mesh,&state.camera,state.controls,&state.next_mouse,state.time);
} }
let a=state.refresh_walk_target(); let a=refresh_walk_target(state,data);
set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,a); set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,a);
}, },
(PhysicsCollisionAttributes::Intersect{intersecting: _,general},Collision::Intersect(intersect))=>{ (PhysicsCollisionAttributes::Intersect{intersecting: _,general},Collision::Intersect(intersect))=>{
//I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop //I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
state.touching.insert(c); state.touching.insert(c);
run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.mode_id).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,convex_mesh_id); run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.get_mode_id()).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,convex_mesh_id);
}, },
_=>panic!("invalid pair"), _=>panic!("invalid pair"),
} }
@ -1360,8 +1408,10 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
}, },
PhysicsInputInstruction::Reset => { PhysicsInputInstruction::Reset => {
//it matters which of these runs first, but I have not thought it through yet as it doesn't matter yet //it matters which of these runs first, but I have not thought it through yet as it doesn't matter yet
state.mode_state.clear();
state.mode_state.set_stage_id(gameplay_modes::StageId::FIRST);
let spawn_point={ let spawn_point={
let mode=data.modes.get_mode(state.mode_state.mode_id).unwrap(); let mode=data.modes.get_mode(state.mode_state.get_mode_id()).unwrap();
let stage=mode.get_stage(gameplay_modes::StageId::FIRST).unwrap(); let stage=mode.get_stage(gameplay_modes::StageId::FIRST).unwrap();
data.models.model(stage.spawn().into()).transform.vertex.translation data.models.model(stage.spawn().into()).transform.vertex.translation
}; };
@ -1373,7 +1423,7 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
PhysicsInputInstruction::Idle => {refresh_walk_target=false;},//literally idle! PhysicsInputInstruction::Idle => {refresh_walk_target=false;},//literally idle!
} }
if refresh_walk_target{ if refresh_walk_target{
let a=state.refresh_walk_target(); let a=refresh_walk_target(state,data);
if set_acceleration_cull(&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,a){ if set_acceleration_cull(&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,a){
(state.move_state,state.body.acceleration)=state.touching.get_move_state(&state.body,&data.models,&state.style,&data.hitbox_mesh,&state.camera,state.controls,&state.next_mouse,state.time); (state.move_state,state.body.acceleration)=state.touching.get_move_state(&state.body,&data.models,&state.style,&data.hitbox_mesh,&state.camera,state.controls,&state.next_mouse,state.time);
} }
@ -1382,217 +1432,218 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
} }
} }
#[allow(dead_code)] #[cfg(test)]
fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){ mod test{
let h0=HitboxMesh::from_mesh_scale(PhysicsMesh::unit_cube(),Planar64Vec3::int(5,1,5)/2); use super::*;
let h1=HitboxMesh::roblox(); fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){
let hitbox_mesh=h1.transformed_mesh(); let h0=HitboxMesh::from_mesh_scale(PhysicsMesh::unit_cube(),Planar64Vec3::int(5,1,5)/2);
let platform_mesh=h0.transformed_mesh(); let h1=HitboxMesh::roblox();
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&platform_mesh,&hitbox_mesh); let hitbox_mesh=h1.transformed_mesh();
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX); let platform_mesh=h0.transformed_mesh();
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision"); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&platform_mesh,&hitbox_mesh);
} let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
#[allow(dead_code)] assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision");
fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){ }
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(), fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){
integer::Planar64Affine3::new( let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),
integer::Planar64Mat3::from_cols( integer::Planar64Affine3::new(
Planar64Vec3::int(5,0,1)/2, integer::Planar64Mat3::from_cols(
Planar64Vec3::int(0,1,0)/2, Planar64Vec3::int(5,0,1)/2,
Planar64Vec3::int(-1,0,5)/2, Planar64Vec3::int(0,1,0)/2,
), Planar64Vec3::int(-1,0,5)/2,
),
Planar64Vec3::ZERO,
)
);
let h1=HitboxMesh::roblox();
let hitbox_mesh=h1.transformed_mesh();
let platform_mesh=h0.transformed_mesh();
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision");
}
fn test_collision(relative_body:Body,expected_collision_time:Option<Time>){
test_collision_axis_aligned(relative_body.clone(),expected_collision_time);
test_collision_rotated(relative_body,expected_collision_time);
}
#[test]
fn test_collision_degenerate(){
test_collision(Body::new(
Planar64Vec3::int(0,5,0),
Planar64Vec3::int(0,-1,0),
Planar64Vec3::ZERO, Planar64Vec3::ZERO,
) Time::ZERO
); ),Some(Time::from_secs(2)));
let h1=HitboxMesh::roblox(); }
let hitbox_mesh=h1.transformed_mesh(); #[test]
let platform_mesh=h0.transformed_mesh(); fn test_collision_degenerate_east(){
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh); test_collision(Body::new(
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX); Planar64Vec3::int(3,5,0),
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision"); Planar64Vec3::int(0,-1,0),
} Planar64Vec3::ZERO,
#[allow(dead_code)] Time::ZERO
fn test_collision(relative_body:Body,expected_collision_time:Option<Time>){ ),Some(Time::from_secs(2)));
test_collision_axis_aligned(relative_body.clone(),expected_collision_time); }
test_collision_rotated(relative_body,expected_collision_time); #[test]
} fn test_collision_degenerate_south(){
#[test] test_collision(Body::new(
fn test_collision_degenerate(){ Planar64Vec3::int(0,5,3),
test_collision(Body::new( Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(0,5,0), Planar64Vec3::ZERO,
Planar64Vec3::int(0,-1,0), Time::ZERO
Planar64Vec3::ZERO, ),Some(Time::from_secs(2)));
Time::ZERO }
),Some(Time::from_secs(2))); #[test]
} fn test_collision_degenerate_west(){
#[test] test_collision(Body::new(
fn test_collision_degenerate_east(){ Planar64Vec3::int(-3,5,0),
test_collision(Body::new( Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(3,5,0), Planar64Vec3::ZERO,
Planar64Vec3::int(0,-1,0), Time::ZERO
Planar64Vec3::ZERO, ),Some(Time::from_secs(2)));
Time::ZERO }
),Some(Time::from_secs(2))); #[test]
} fn test_collision_degenerate_north(){
#[test] test_collision(Body::new(
fn test_collision_degenerate_south(){ Planar64Vec3::int(0,5,-3),
test_collision(Body::new( Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(0,5,3), Planar64Vec3::ZERO,
Planar64Vec3::int(0,-1,0), Time::ZERO
Planar64Vec3::ZERO, ),Some(Time::from_secs(2)));
Time::ZERO }
),Some(Time::from_secs(2))); #[test]
} fn test_collision_parabola_edge_east_from_west(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_degenerate_west(){ Planar64Vec3::int(3,3,0),
test_collision(Body::new( Planar64Vec3::int(100,-1,0),
Planar64Vec3::int(-3,5,0), Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(0,-1,0), Time::ZERO
Planar64Vec3::ZERO, )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
),Some(Time::from_secs(2))); #[test]
} fn test_collision_parabola_edge_south_from_north(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_degenerate_north(){ Planar64Vec3::int(0,3,3),
test_collision(Body::new( Planar64Vec3::int(0,-1,100),
Planar64Vec3::int(0,5,-3), Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(0,-1,0), Time::ZERO
Planar64Vec3::ZERO, )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
),Some(Time::from_secs(2))); #[test]
} fn test_collision_parabola_edge_west_from_east(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_parabola_edge_east_from_west(){ Planar64Vec3::int(-3,3,0),
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(-100,-1,0),
Planar64Vec3::int(3,3,0), Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(100,-1,0), Time::ZERO
Planar64Vec3::int(0,-1,0), )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn test_collision_parabola_edge_north_from_south(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_parabola_edge_south_from_north(){ Planar64Vec3::int(0,3,-3),
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(0,-1,-100),
Planar64Vec3::int(0,3,3), Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(0,-1,100), Time::ZERO
Planar64Vec3::int(0,-1,0), )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn test_collision_parabola_edge_north_from_ne(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_parabola_edge_west_from_east(){ Planar64Vec3::int(0,6,-7)/2,
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(-10,-1,1),
Planar64Vec3::int(-3,3,0), Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(-100,-1,0), Time::ZERO
Planar64Vec3::int(0,-1,0), )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn test_collision_parabola_edge_north_from_nw(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_parabola_edge_north_from_south(){ Planar64Vec3::int(0,6,-7)/2,
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(10,-1,1),
Planar64Vec3::int(0,3,-3), Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(0,-1,-100), Time::ZERO
Planar64Vec3::int(0,-1,0), )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn test_collision_parabola_edge_east_from_se(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_parabola_edge_north_from_ne(){ Planar64Vec3::int(7,6,0)/2,
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(-1,-1,-10),
Planar64Vec3::int(0,6,-7)/2, Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(-10,-1,1), Time::ZERO
Planar64Vec3::int(0,-1,0), )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn test_collision_parabola_edge_east_from_ne(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_parabola_edge_north_from_nw(){ Planar64Vec3::int(7,6,0)/2,
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(-1,-1,10),
Planar64Vec3::int(0,6,-7)/2, Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(10,-1,1), Time::ZERO
Planar64Vec3::int(0,-1,0), )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn test_collision_parabola_edge_south_from_se(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_parabola_edge_east_from_se(){ Planar64Vec3::int(0,6,7)/2,
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(-10,-1,-1),
Planar64Vec3::int(7,6,0)/2, Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(-1,-1,-10), Time::ZERO
Planar64Vec3::int(0,-1,0), )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn test_collision_parabola_edge_south_from_sw(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_parabola_edge_east_from_ne(){ Planar64Vec3::int(0,6,7)/2,
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(10,-1,-1),
Planar64Vec3::int(7,6,0)/2, Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(-1,-1,10), Time::ZERO
Planar64Vec3::int(0,-1,0), )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn test_collision_parabola_edge_west_from_se(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_parabola_edge_south_from_se(){ Planar64Vec3::int(-7,6,0)/2,
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(1,-1,-10),
Planar64Vec3::int(0,6,7)/2, Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(-10,-1,-1), Time::ZERO
Planar64Vec3::int(0,-1,0), )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn test_collision_parabola_edge_west_from_ne(){
#[test] test_collision(VirtualBody::relative(&Body::default(),&Body::new(
fn test_collision_parabola_edge_south_from_sw(){ Planar64Vec3::int(-7,6,0)/2,
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(1,-1,10),
Planar64Vec3::int(0,6,7)/2, Planar64Vec3::int(0,-1,0),
Planar64Vec3::int(10,-1,-1), Time::ZERO
Planar64Vec3::int(0,-1,0), )).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn test_collision_oblique(){
#[test] test_collision(Body::new(
fn test_collision_parabola_edge_west_from_se(){ Planar64Vec3::int(0,5,0),
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(1,-64,2)/64,
Planar64Vec3::int(-7,6,0)/2, Planar64Vec3::ZERO,
Planar64Vec3::int(1,-1,-10), Time::ZERO
Planar64Vec3::int(0,-1,0), ),Some(Time::from_secs(2)));
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn zoom_hit_nothing(){
#[test] test_collision(Body::new(
fn test_collision_parabola_edge_west_from_ne(){ Planar64Vec3::int(0,10,0),
test_collision(VirtualBody::relative(&Body::default(),&Body::new( Planar64Vec3::int(1,0,0),
Planar64Vec3::int(-7,6,0)/2, Planar64Vec3::int(0,1,0),
Planar64Vec3::int(1,-1,10), Time::ZERO
Planar64Vec3::int(0,-1,0), ),None);
Time::ZERO }
)).body(Time::from_secs(-1)),Some(Time::from_secs(0))); #[test]
} fn already_inside_hit_nothing(){
#[test] test_collision(Body::new(
fn test_collision_oblique(){ Planar64Vec3::ZERO,
test_collision(Body::new( Planar64Vec3::int(1,0,0),
Planar64Vec3::int(0,5,0), Planar64Vec3::int(0,1,0),
Planar64Vec3::int(1,-64,2)/64, Time::ZERO
Planar64Vec3::ZERO, ),None);
Time::ZERO }
),Some(Time::from_secs(2)));
}
#[test]
fn zoom_hit_nothing(){
test_collision(Body::new(
Planar64Vec3::int(0,10,0),
Planar64Vec3::int(1,0,0),
Planar64Vec3::int(0,1,0),
Time::ZERO
),None);
}
#[test]
fn already_inside_hit_nothing(){
test_collision(Body::new(
Planar64Vec3::ZERO,
Planar64Vec3::int(1,0,0),
Planar64Vec3::int(0,1,0),
Time::ZERO
),None);
} }