2024-02-02 06:14:58 +00:00
use std ::collections ::HashMap ;
use std ::collections ::HashSet ;
use crate ::model_physics ::{ self , PhysicsMesh , TransformedMesh , MeshQuery } ;
2024-01-30 00:19:41 +00:00
use strafesnet_common ::bvh ;
use strafesnet_common ::aabb ;
2024-02-02 06:14:58 +00:00
use strafesnet_common ::gameplay_attributes ;
use strafesnet_common ::gameplay_modes ;
use strafesnet_common ::map ;
use strafesnet_common ::model ;
use strafesnet_common ::gameplay_style ::{ self , StyleModifiers , StrafeSettings } ;
2024-01-30 00:19:41 +00:00
use strafesnet_common ::instruction ::{ self , InstructionEmitter , InstructionConsumer , TimedInstruction } ;
2024-01-30 06:37:05 +00:00
use strafesnet_common ::integer ::{ self , Time , Planar64 , Planar64Vec3 , Planar64Mat3 , Angle32 , Ratio64Vec2 } ;
2024-02-02 06:14:58 +00:00
use strafesnet_common ::model ::ModelId ;
2023-09-27 09:12:20 +00:00
2023-09-19 01:34:48 +00:00
#[ derive(Debug) ]
2023-09-09 00:34:26 +00:00
pub enum PhysicsInstruction {
2023-11-30 09:51:17 +00:00
CollisionStart ( Collision ) ,
CollisionEnd ( Collision ) ,
2023-09-09 00:00:50 +00:00
StrafeTick ,
2023-09-09 03:14:18 +00:00
ReachWalkTargetVelocity ,
2023-09-09 03:13:30 +00:00
// Water,
// Spawn(
// Option<SpawnId>,
// bool,//true = Trigger; false = teleport
// bool,//true = Force
// )
2023-09-20 00:53:29 +00:00
//InputInstructions conditionally activate RefreshWalkTarget (by doing what SetWalkTargetVelocity used to do and then flagging it)
2023-10-05 03:04:04 +00:00
Input ( PhysicsInputInstruction ) ,
}
#[ derive(Debug) ]
pub enum PhysicsInputInstruction {
ReplaceMouse ( MouseState , MouseState ) ,
SetNextMouse ( MouseState ) ,
SetMoveRight ( bool ) ,
SetMoveUp ( bool ) ,
2023-10-10 22:33:32 +00:00
SetMoveBack ( bool ) ,
SetMoveLeft ( bool ) ,
2023-10-05 03:04:04 +00:00
SetMoveDown ( bool ) ,
2023-10-10 22:33:32 +00:00
SetMoveForward ( bool ) ,
2023-10-05 03:04:04 +00:00
SetJump ( bool ) ,
SetZoom ( bool ) ,
Reset ,
2023-09-30 07:13:26 +00:00
Idle ,
//Idle: there were no input events, but the simulation is safe to advance to this timestep
//for interpolation / networking / playback reasons, most playback heads will always want
//to be 1 instruction ahead to generate the next state for interpolation.
2023-09-09 00:00:50 +00:00
}
2023-10-19 00:17:21 +00:00
#[ derive(Clone,Hash,Default) ]
2023-11-30 09:51:17 +00:00
pub struct Body {
pub position :Planar64Vec3 , //I64 where 2^32 = 1 u
pub velocity :Planar64Vec3 , //I64 where 2^32 = 1 u/s
pub acceleration :Planar64Vec3 , //I64 where 2^32 = 1 u/s/s
pub time :Time , //nanoseconds x xxxxD!
}
impl std ::ops ::Neg for Body {
type Output = Self ;
fn neg ( self ) ->Self ::Output {
Self {
position :self . position ,
velocity :- self . velocity ,
acceleration :self . acceleration ,
time :- self . time ,
}
}
2023-09-19 04:10:07 +00:00
}
2023-09-09 03:14:18 +00:00
2023-10-03 05:45:48 +00:00
//hey dumbass just use a delta
2023-10-05 03:04:04 +00:00
#[ derive(Clone,Debug) ]
pub struct MouseState {
pub pos : glam ::IVec2 ,
2023-09-27 09:12:20 +00:00
pub time :Time ,
2023-09-10 20:24:47 +00:00
}
2023-10-05 03:04:04 +00:00
impl Default for MouseState {
fn default ( ) -> Self {
2023-09-20 00:53:29 +00:00
Self {
2023-09-27 09:12:20 +00:00
time :Time ::ZERO ,
2023-10-05 03:04:04 +00:00
pos :glam ::IVec2 ::ZERO ,
2023-09-20 00:53:29 +00:00
}
}
2023-10-05 03:04:04 +00:00
}
impl MouseState {
2023-09-27 09:12:20 +00:00
pub fn lerp ( & self , target :& MouseState , time :Time ) ->glam ::IVec2 {
2023-10-05 03:04:04 +00:00
let m0 = self . pos . as_i64vec2 ( ) ;
let m1 = target . pos . as_i64vec2 ( ) ;
//these are deltas
2023-09-27 09:12:20 +00:00
let t1t = ( target . time - time ) . nanos ( ) ;
let tt0 = ( time - self . time ) . nanos ( ) ;
let dt = ( target . time - self . time ) . nanos ( ) ;
2023-10-05 03:04:04 +00:00
( ( m0 * t1t + m1 * tt0 ) / dt ) . as_ivec2 ( )
2023-09-10 20:24:47 +00:00
}
}
2023-11-30 09:51:17 +00:00
enum JumpDirection {
Exactly ( Planar64Vec3 ) ,
FromContactNormal ,
}
2023-10-17 07:17:54 +00:00
enum WalkEnum {
2023-09-19 06:36:14 +00:00
Reached ,
2023-10-17 07:17:54 +00:00
Transient ( WalkTarget ) ,
2023-09-19 06:36:14 +00:00
}
2023-10-17 07:17:54 +00:00
struct WalkTarget {
velocity :Planar64Vec3 ,
time :Time ,
2023-09-19 04:10:07 +00:00
}
2023-10-17 07:17:54 +00:00
struct WalkState {
2023-11-30 09:51:17 +00:00
jump_direction :JumpDirection ,
contact :ContactCollision ,
2023-10-17 07:17:54 +00:00
state :WalkEnum ,
}
impl WalkEnum {
//args going crazy
//(walk_enum,body.acceleration)=with_target_velocity();
2023-11-30 09:51:17 +00:00
fn with_target_velocity ( body :& Body , style :& StyleModifiers , velocity :Planar64Vec3 , normal :& Planar64Vec3 , speed :Planar64 , normal_accel :Planar64 ) ->( WalkEnum , Planar64Vec3 ) {
2023-10-17 07:17:54 +00:00
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 {
2023-11-30 09:51:17 +00:00
( WalkEnum ::Reached , Planar64Vec3 ::ZERO )
2023-10-17 07:17:54 +00:00
} else {
//normal friction acceleration is clippedAcceleration.dot(normal)*friction
2023-10-17 23:18:55 +00:00
let diff_len = target_diff . length ( ) ;
2023-11-30 09:51:17 +00:00
let friction = if diff_len < speed {
2023-10-17 23:18:55 +00:00
style . static_friction
} else {
style . kinetic_friction
} ;
2023-11-30 09:51:17 +00:00
let accel = style . walk_accel . min ( normal_accel * friction ) ;
2023-10-17 23:18:55 +00:00
let time_delta = diff_len / accel ;
2023-11-30 09:51:17 +00:00
let a = target_diff . with_length ( accel ) ;
2023-10-17 07:17:54 +00:00
( WalkEnum ::Transient ( WalkTarget { velocity , time :body . time + Time ::from ( time_delta ) } ) , a )
2023-09-19 04:10:07 +00:00
}
}
}
2023-10-17 07:17:54 +00:00
impl WalkState {
2023-11-30 09:51:17 +00:00
fn ground ( body :& Body , style :& StyleModifiers , gravity :Planar64Vec3 , velocity :Planar64Vec3 , contact :ContactCollision , normal :& Planar64Vec3 ) ->( Self , Planar64Vec3 ) {
let ( walk_enum , a ) = WalkEnum ::with_target_velocity ( body , style , velocity , & Planar64Vec3 ::Y , style . walk_speed , - normal . dot ( gravity ) ) ;
2023-10-17 07:17:54 +00:00
( Self {
state :walk_enum ,
2023-11-30 09:51:17 +00:00
contact ,
jump_direction :JumpDirection ::Exactly ( Planar64Vec3 ::Y ) ,
2023-10-17 07:17:54 +00:00
} , a )
}
2023-11-30 09:51:17 +00:00
fn ladder ( body :& Body , style :& StyleModifiers , gravity :Planar64Vec3 , velocity :Planar64Vec3 , contact :ContactCollision , normal :& Planar64Vec3 ) ->( Self , Planar64Vec3 ) {
let ( walk_enum , a ) = WalkEnum ::with_target_velocity ( body , style , velocity , normal , style . ladder_speed , style . ladder_accel ) ;
2023-10-17 07:17:54 +00:00
( Self {
state :walk_enum ,
2023-11-30 09:51:17 +00:00
contact ,
jump_direction :JumpDirection ::FromContactNormal ,
2023-10-17 07:17:54 +00:00
} , a )
}
}
2023-09-19 04:10:07 +00:00
2023-11-30 09:51:17 +00:00
#[ derive(Default) ]
2023-10-18 23:21:06 +00:00
struct PhysicsModels {
2023-11-30 09:51:17 +00:00
meshes :Vec < PhysicsMesh > ,
2023-10-26 02:51:24 +00:00
models :Vec < PhysicsModel > ,
2023-11-30 09:51:17 +00:00
//separate models into Contacting and Intersecting?
//wrap model id with ContactingModelId and IntersectingModelId
//attributes can be split into contacting and intersecting (this also saves a bit of memory)
//can go even further and deduplicate General attributes separately, reconstructing it when queried
attributes :Vec < PhysicsCollisionAttributes > ,
2023-10-18 23:21:06 +00:00
}
impl PhysicsModels {
fn clear ( & mut self ) {
2023-11-30 09:51:17 +00:00
self . meshes . clear ( ) ;
2023-10-18 23:21:06 +00:00
self . models . clear ( ) ;
2023-11-30 09:51:17 +00:00
self . attributes . clear ( ) ;
2023-10-18 23:21:06 +00:00
}
2023-11-30 09:51:17 +00:00
//TODO: "statically" verify the maps don't refer to any nonexistant data, if they do delete the references.
//then I can make these getter functions unchecked.
2024-02-02 06:14:58 +00:00
fn mesh ( & self , model_id :ModelId ) ->TransformedMesh {
2023-11-30 09:51:17 +00:00
TransformedMesh ::new (
& self . meshes [ self . models [ model_id ] . mesh_id ] ,
& self . models [ model_id ] . transform ,
& self . models [ model_id ] . normal_transform ,
2023-12-02 04:00:23 +00:00
self . models [ model_id ] . transform_det ,
2023-11-30 09:51:17 +00:00
)
}
2024-02-02 06:14:58 +00:00
fn model ( & self , model_id :ModelId ) ->& PhysicsModel {
2023-11-30 09:51:17 +00:00
& self . models [ model_id ]
}
2024-02-02 06:14:58 +00:00
fn attr ( & self , model_id :ModelId ) ->& PhysicsCollisionAttributes {
2023-11-30 09:51:17 +00:00
& self . attributes [ self . models [ model_id ] . attr_id ]
2023-10-18 23:21:06 +00:00
}
2023-11-30 09:51:17 +00:00
fn push_mesh ( & mut self , mesh :PhysicsMesh ) {
self . meshes . push ( mesh ) ;
}
2024-02-02 06:14:58 +00:00
fn push_model ( & mut self , model :PhysicsModel ) ->ModelId {
2023-10-18 23:21:06 +00:00
let model_id = self . models . len ( ) ;
self . models . push ( model ) ;
model_id
}
2024-02-02 06:14:58 +00:00
fn push_attr ( & mut self , attr :PhysicsCollisionAttributes ) ->PhysicsAttributeId {
2023-11-30 09:51:17 +00:00
let attr_id = self . attributes . len ( ) ;
self . attributes . push ( attr ) ;
attr_id
2023-10-18 23:21:06 +00:00
}
}
2023-10-05 03:04:04 +00:00
#[ derive(Clone) ]
2023-10-31 05:25:03 +00:00
pub struct PhysicsCamera {
2023-09-27 09:12:20 +00:00
//punch: Planar64Vec3,
//punch_velocity: Planar64Vec3,
sensitivity :Ratio64Vec2 , //dots to Angle32 ratios
mouse :MouseState , //last seen absolute mouse pos
clamped_mouse_pos :glam ::IVec2 , //angles are calculated from this cumulative value
angle_pitch_lower_limit :Angle32 ,
angle_pitch_upper_limit :Angle32 ,
//angle limits could be an enum + struct that defines whether it's limited and selects clamp or wrap depending
// enum AngleLimit{
// Unlimited,
// Limited{lower:Angle32,upper:Angle32},
// }
//pitch_limit:AngleLimit,
//yaw_limit:AngleLimit,
2023-09-20 00:53:29 +00:00
}
2023-10-05 03:04:04 +00:00
impl PhysicsCamera {
2023-09-27 09:12:20 +00:00
pub fn move_mouse ( & mut self , mouse_pos :glam ::IVec2 ) {
let mut unclamped_mouse_pos = self . clamped_mouse_pos + mouse_pos - self . mouse . pos ;
unclamped_mouse_pos . y = unclamped_mouse_pos . y . clamp (
self . sensitivity . y . rhs_div_int ( self . angle_pitch_lower_limit . get ( ) as i64 ) as i32 ,
self . sensitivity . y . rhs_div_int ( self . angle_pitch_upper_limit . get ( ) as i64 ) as i32 ,
) ;
self . clamped_mouse_pos = unclamped_mouse_pos ;
2023-09-20 00:53:29 +00:00
}
2023-09-27 09:12:20 +00:00
pub fn simulate_move_angles ( & self , mouse_pos :glam ::IVec2 ) ->glam ::Vec2 {
let a = - self . sensitivity . mul_int ( ( mouse_pos - self . mouse . pos + self . clamped_mouse_pos ) . as_i64vec2 ( ) ) ;
let ax = Angle32 ::wrap_from_i64 ( a . x ) ;
let ay = Angle32 ::clamp_from_i64 ( a . y )
//clamp to actual vertical cam limit
. clamp ( self . angle_pitch_lower_limit , self . angle_pitch_upper_limit ) ;
return glam ::vec2 ( ax . into ( ) , ay . into ( ) ) ;
}
2023-10-17 06:20:38 +00:00
fn simulate_move_rotation ( & self , mouse_pos :glam ::IVec2 ) ->Planar64Mat3 {
let a = - self . sensitivity . mul_int ( ( mouse_pos - self . mouse . pos + self . clamped_mouse_pos ) . as_i64vec2 ( ) ) ;
let ax = Angle32 ::wrap_from_i64 ( a . x ) ;
let ay = Angle32 ::clamp_from_i64 ( a . y )
//clamp to actual vertical cam limit
. clamp ( self . angle_pitch_lower_limit , self . angle_pitch_upper_limit ) ;
Planar64Mat3 ::from_rotation_yx ( ax , ay )
}
2023-09-27 09:12:20 +00:00
fn simulate_move_rotation_y ( & self , mouse_pos_x :i32 ) ->Planar64Mat3 {
let ax = - self . sensitivity . x . mul_int ( ( mouse_pos_x - self . mouse . pos . x + self . clamped_mouse_pos . x ) as i64 ) ;
Planar64Mat3 ::from_rotation_y ( Angle32 ::wrap_from_i64 ( ax ) )
2023-09-20 00:53:29 +00:00
}
}
2023-10-19 00:17:21 +00:00
impl std ::default ::Default for PhysicsCamera {
fn default ( ) ->Self {
Self {
sensitivity :Ratio64Vec2 ::ONE * 200_000 ,
mouse :MouseState ::default ( ) , //t=0 does not cause divide by zero because it's immediately replaced
clamped_mouse_pos :glam ::IVec2 ::ZERO ,
angle_pitch_lower_limit :- Angle32 ::FRAC_PI_2 ,
angle_pitch_upper_limit :Angle32 ::FRAC_PI_2 ,
}
}
}
2024-02-02 06:14:58 +00:00
pub struct ModeState {
stage_id :gameplay_modes ::StageId ,
jump_counts :HashMap < ModelId , u32 > , //model_id -> jump count
next_ordered_checkpoint_id :gameplay_modes ::CheckpointId , //which OrderedCheckpoint model_id you must pass next (if 0 you haven't passed OrderedCheckpoint0)
unordered_checkpoints :HashSet < ModelId > ,
2023-10-03 05:45:20 +00:00
}
2024-02-02 06:14:58 +00:00
impl std ::default ::Default for ModeState {
2023-10-28 23:38:16 +00:00
fn default ( ) ->Self {
2023-10-03 05:45:20 +00:00
Self {
2024-02-02 06:14:58 +00:00
stage_id :gameplay_modes ::StageId ::id ( 0 ) ,
next_ordered_checkpoint_id :gameplay_modes ::CheckpointId ::id ( 0 ) ,
2023-10-28 23:38:16 +00:00
unordered_checkpoints :std ::collections ::HashSet ::new ( ) ,
jump_counts :std ::collections ::HashMap ::new ( ) ,
2023-10-03 05:45:20 +00:00
}
}
}
2023-10-17 23:18:55 +00:00
struct WorldState { }
2024-01-30 06:37:05 +00:00
trait HitboxMeshPresets {
fn roblox ( ) ->Self ;
fn source ( ) ->Self ;
2023-10-17 23:18:55 +00:00
}
2024-02-02 06:14:58 +00:00
impl HitboxMeshPresets for gameplay_style ::Hitbox {
2023-11-30 09:51:17 +00:00
fn roblox ( ) ->Self {
Self ::from_mesh_scale ( PhysicsMesh ::from ( & crate ::primitives ::unit_cylinder ( ) ) , Planar64Vec3 ::int ( 2 , 5 , 2 ) / 2 )
}
fn source ( ) ->Self {
Self ::from_mesh_scale ( PhysicsMesh ::from ( & crate ::primitives ::unit_cube ( ) ) , Planar64Vec3 ::raw ( 33 < < 28 , 73 < < 28 , 33 < < 28 ) / 2 )
}
}
2024-01-30 06:37:05 +00:00
trait StyleHelper {
fn get_control ( & self , control :u32 , controls :u32 ) ->bool ;
fn allow_strafe ( & self , controls :u32 ) ->bool ;
fn get_control_dir ( & self , controls :u32 ) ->Planar64Vec3 ;
//fn get_jump_time(&self)->Planar64;
//fn get_jump_height(&self)->Planar64;
//fn get_jump_energy(&self)->Planar64;
fn get_jump_deltav ( & self ) ->Planar64 ;
fn get_walk_target_velocity ( & self , camera :& PhysicsCamera , controls :u32 , next_mouse :& MouseState , time :Time , normal :& Planar64Vec3 ) ->Planar64Vec3 ;
fn get_ladder_target_velocity ( & self , camera :& PhysicsCamera , controls :u32 , next_mouse :& MouseState , time :Time , normal :& Planar64Vec3 ) ->Planar64Vec3 ;
fn get_propulsion_control_dir ( & self , camera :& PhysicsCamera , controls :u32 , next_mouse :& MouseState , time :Time ) ->Planar64Vec3 ;
fn mesh ( & self ) ->TransformedMesh ;
2023-10-03 05:45:20 +00:00
}
2024-01-30 06:37:05 +00:00
impl StyleHelper for StyleModifiers {
2023-10-03 23:52:45 +00:00
fn get_control ( & self , control :u32 , controls :u32 ) ->bool {
2023-10-05 03:04:04 +00:00
controls & self . controls_mask & control = = control
2023-10-03 23:52:45 +00:00
}
2023-10-31 21:11:39 +00:00
fn allow_strafe ( & self , controls :u32 ) ->bool {
//disable strafing according to strafe settings
2024-02-02 06:14:58 +00:00
self . strafe . is_some_and ( | s | s . mask ( controls ) )
2023-10-31 21:11:39 +00:00
}
2023-09-27 09:12:20 +00:00
fn get_control_dir ( & self , controls :u32 ) ->Planar64Vec3 {
2023-10-03 23:52:45 +00:00
//don't get fancy just do it
2023-09-27 09:12:20 +00:00
let mut control_dir :Planar64Vec3 = Planar64Vec3 ::ZERO ;
2023-10-03 23:52:45 +00:00
//Apply mask after held check so you can require non-allowed keys to be held for some reason
let controls = controls & self . controls_mask ;
if controls & Self ::CONTROL_MOVEFORWARD = = Self ::CONTROL_MOVEFORWARD {
control_dir + = Self ::FORWARD_DIR ;
}
if controls & Self ::CONTROL_MOVEBACK = = Self ::CONTROL_MOVEBACK {
2023-09-27 09:12:20 +00:00
control_dir - = Self ::FORWARD_DIR ;
2023-10-03 23:52:45 +00:00
}
if controls & Self ::CONTROL_MOVELEFT = = Self ::CONTROL_MOVELEFT {
2023-09-27 09:12:20 +00:00
control_dir - = Self ::RIGHT_DIR ;
2023-10-03 23:52:45 +00:00
}
if controls & Self ::CONTROL_MOVERIGHT = = Self ::CONTROL_MOVERIGHT {
control_dir + = Self ::RIGHT_DIR ;
}
if controls & Self ::CONTROL_MOVEUP = = Self ::CONTROL_MOVEUP {
control_dir + = Self ::UP_DIR ;
}
if controls & Self ::CONTROL_MOVEDOWN = = Self ::CONTROL_MOVEDOWN {
2023-09-27 09:12:20 +00:00
control_dir - = Self ::UP_DIR ;
2023-10-03 23:52:45 +00:00
}
return control_dir
}
2023-10-14 21:51:13 +00:00
2023-10-17 23:18:55 +00:00
//fn get_jump_time(&self)->Planar64
//fn get_jump_height(&self)->Planar64
//fn get_jump_energy(&self)->Planar64
fn get_jump_deltav ( & self ) ->Planar64 {
match & self . jump_impulse {
& JumpImpulse ::FromTime ( time ) = > self . gravity . length ( ) * ( time / 2 ) ,
& JumpImpulse ::FromHeight ( height ) = > ( self . gravity . length ( ) * height * 2 ) . sqrt ( ) ,
& JumpImpulse ::FromDeltaV ( deltav ) = > deltav ,
& JumpImpulse ::FromEnergy ( energy ) = > ( energy * 2 / self . mass ) . sqrt ( ) ,
}
2023-10-17 07:17:54 +00:00
}
2023-11-30 09:51:17 +00:00
fn get_walk_target_velocity ( & self , camera :& PhysicsCamera , controls :u32 , next_mouse :& MouseState , time :Time , normal :& Planar64Vec3 ) ->Planar64Vec3 {
let mut control_dir = self . get_control_dir ( controls ) ;
if control_dir = = Planar64Vec3 ::ZERO {
return control_dir ;
}
2023-10-17 07:17:54 +00:00
let camera_mat = camera . simulate_move_rotation_y ( camera . mouse . lerp ( & next_mouse , time ) . x ) ;
2023-11-30 09:51:17 +00:00
control_dir = camera_mat * control_dir ;
let n = normal . length ( ) ;
let m = control_dir . length ( ) ;
let d = normal . dot ( control_dir ) / m ;
if d < n {
let cr = normal . cross ( control_dir ) ;
if cr = = Planar64Vec3 ::ZERO {
Planar64Vec3 ::ZERO
} else {
cr . cross ( * normal ) * ( self . walk_speed / ( n * ( n * n - d * d ) . sqrt ( ) * m ) )
}
} else {
Planar64Vec3 ::ZERO
}
2023-10-17 23:18:55 +00:00
}
2023-11-30 09:51:17 +00:00
fn get_ladder_target_velocity ( & self , camera :& PhysicsCamera , controls :u32 , next_mouse :& MouseState , time :Time , normal :& Planar64Vec3 ) ->Planar64Vec3 {
let mut control_dir = self . get_control_dir ( controls ) ;
if control_dir = = Planar64Vec3 ::ZERO {
return control_dir ;
}
2023-10-17 23:18:55 +00:00
let camera_mat = camera . simulate_move_rotation ( camera . mouse . lerp ( & next_mouse , time ) ) ;
2023-11-30 09:51:17 +00:00
control_dir = camera_mat * control_dir ;
let n = normal . length ( ) ;
let m = control_dir . length ( ) ;
let mut d = normal . dot ( control_dir ) / m ;
if d < - self . ladder_dot * n {
control_dir = Planar64Vec3 ::Y * m ;
d = normal . y ( ) ;
} else if self . ladder_dot * n < d {
control_dir = Planar64Vec3 ::NEG_Y * m ;
d = - normal . y ( ) ;
}
//n=d if you are standing on top of a ladder and press E.
//two fixes:
//- ladder movement is not allowed on walkable surfaces
//- fix the underlying issue
if d . get ( ) . unsigned_abs ( ) < n . get ( ) . unsigned_abs ( ) {
let cr = normal . cross ( control_dir ) ;
if cr = = Planar64Vec3 ::ZERO {
Planar64Vec3 ::ZERO
} else {
cr . cross ( * normal ) * ( self . ladder_speed / ( n * ( n * n - d * d ) . sqrt ( ) ) )
}
} else {
Planar64Vec3 ::ZERO
}
2023-10-17 07:17:54 +00:00
}
2023-10-20 20:47:26 +00:00
fn get_propulsion_control_dir ( & self , camera :& PhysicsCamera , controls :u32 , next_mouse :& MouseState , time :Time ) ->Planar64Vec3 {
2023-10-17 07:17:54 +00:00
let camera_mat = camera . simulate_move_rotation ( camera . mouse . lerp ( & next_mouse , time ) ) ;
2023-10-20 20:47:26 +00:00
camera_mat * self . get_control_dir ( controls )
2023-10-14 21:51:13 +00:00
}
2023-11-30 09:51:17 +00:00
#[ inline ]
fn mesh ( & self ) ->TransformedMesh {
self . hitbox . transformed_mesh ( )
}
2023-10-03 23:52:45 +00:00
}
2023-10-03 05:45:20 +00:00
2023-10-13 02:44:46 +00:00
enum MoveState {
Air ,
Walk ( WalkState ) ,
Water ,
Ladder ( WalkState ) ,
}
2023-10-03 05:45:20 +00:00
pub struct PhysicsState {
2023-10-19 00:17:21 +00:00
time :Time ,
2023-10-17 01:57:37 +00:00
body :Body ,
world :WorldState , //currently there is only one state the world can be in
2024-02-02 06:14:58 +00:00
mode_state :ModeState ,
style :StyleModifiers , //mode style with custom style updates applied
2023-10-13 02:44:46 +00:00
touching :TouchingState ,
2023-09-10 20:24:47 +00:00
//camera must exist in state because wormholes modify the camera, also camera punch
2023-10-17 01:57:37 +00:00
camera :PhysicsCamera ,
2023-10-19 00:17:21 +00:00
pub next_mouse :MouseState , //Where is the mouse headed next
2024-02-02 06:14:58 +00:00
controls :u32 , //TODO this should be a struct
2023-10-17 01:57:37 +00:00
move_state :MoveState ,
2024-02-02 06:14:58 +00:00
//does not belong here
//bvh:bvh::BvhNode,
//models:PhysicsModels,
//modes:gameplay_modes::Modes,
2023-09-08 18:33:16 +00:00
}
2023-10-19 00:17:21 +00:00
#[ derive(Clone,Default) ]
2023-10-05 03:04:04 +00:00
pub struct PhysicsOutputState {
body :Body ,
2023-10-31 05:25:03 +00:00
camera :PhysicsCamera ,
camera_offset :Planar64Vec3 ,
2023-10-05 03:04:04 +00:00
}
impl PhysicsOutputState {
2023-10-19 00:17:21 +00:00
pub fn extrapolate ( & self , mouse_pos :glam ::IVec2 , time :Time ) ->( glam ::Vec3 , glam ::Vec2 ) {
2023-10-31 05:25:03 +00:00
( ( self . body . extrapolated_position ( time ) + self . camera_offset ) . into ( ) , self . camera . simulate_move_angles ( mouse_pos ) )
2023-10-05 03:04:04 +00:00
}
}
2023-09-08 18:33:16 +00:00
2023-11-30 09:51:17 +00:00
#[ derive(Clone,Hash,Eq,PartialEq) ]
2023-10-03 05:45:20 +00:00
enum PhysicsCollisionAttributes {
Contact { //track whether you are contacting the object
2024-02-02 06:14:58 +00:00
contacting :gameplay_attributes ::ContactingAttributes ,
general :gameplay_attributes ::GeneralAttributes ,
2023-10-03 05:45:20 +00:00
} ,
Intersect { //track whether you are intersecting the object
2024-02-02 06:14:58 +00:00
intersecting :gameplay_attributes ::IntersectingAttributes ,
general :gameplay_attributes ::GeneralAttributes ,
2023-10-03 05:45:20 +00:00
} ,
}
2023-11-30 09:51:17 +00:00
struct NonPhysicsError ;
2024-02-02 06:14:58 +00:00
impl TryFrom < & gameplay_attributes ::CollisionAttributes > for PhysicsCollisionAttributes {
2023-11-30 09:51:17 +00:00
type Error = NonPhysicsError ;
2024-02-02 06:14:58 +00:00
fn try_from ( value :& gameplay_attributes ::CollisionAttributes ) ->Result < Self , Self ::Error > {
2023-11-30 09:51:17 +00:00
match value {
2024-02-02 06:14:58 +00:00
gameplay_attributes ::CollisionAttributes ::Decoration = > Err ( NonPhysicsError ) ,
gameplay_attributes ::CollisionAttributes ::Contact { contacting , general } = > Ok ( Self ::Contact { contacting :contacting . clone ( ) , general :general . clone ( ) } ) ,
gameplay_attributes ::CollisionAttributes ::Intersect { intersecting , general } = > Ok ( Self ::Intersect { intersecting :intersecting . clone ( ) , general :general . clone ( ) } ) ,
2023-11-30 09:51:17 +00:00
}
}
}
2023-10-03 05:45:20 +00:00
2024-02-02 06:14:58 +00:00
struct GeneralAttributesId ( u32 ) ;
struct ContactAttributesId ( u32 ) ;
struct IntersectAttributesId ( u32 ) ;
enum PhysicsAttributesId {
Contact ( ContactAttributesId ) ,
Intersect ( IntersectAttributesId )
}
//unique physics meshes indexed by this
struct MeshId {
model_id :ModelId ,
group_id :GroupId ,
}
2023-11-30 09:51:17 +00:00
pub struct PhysicsModel {
2023-09-08 22:54:43 +00:00
//A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es
//in this iteration, all it needs is extents.
2024-02-02 06:14:58 +00:00
mesh_id :MeshId ,
//put these up on the Model (data normalization)
general_attributes :GeneralAttributesId ,
collision_attributes :PhysicsAttributesId ,
2024-01-30 00:19:41 +00:00
transform :integer ::Planar64Affine3 ,
normal_transform :integer ::Planar64Mat3 ,
2023-12-02 04:00:23 +00:00
transform_det :Planar64 ,
2023-09-08 22:54:43 +00:00
}
2023-11-30 09:51:17 +00:00
impl PhysicsModel {
2024-02-02 06:14:58 +00:00
pub fn new ( mesh_id :MeshId , attr_id :PhysicsAttributesId , transform :integer ::Planar64Affine3 ) ->Self {
2023-09-28 23:15:12 +00:00
Self {
2023-11-30 09:51:17 +00:00
mesh_id ,
attr_id ,
transform ,
2023-12-02 04:00:23 +00:00
normal_transform :transform . matrix3 . inverse_times_det ( ) . transpose ( ) ,
transform_det :transform . matrix3 . determinant ( ) ,
2023-09-28 23:15:12 +00:00
}
2023-09-08 23:14:01 +00:00
}
2023-09-08 22:54:43 +00:00
}
2023-09-19 01:34:48 +00:00
#[ derive(Debug,Clone,Eq,Hash,PartialEq) ]
2023-11-30 09:51:17 +00:00
struct ContactCollision {
2024-02-02 06:14:58 +00:00
face_id :model_physics ::MinkowskiFace ,
model_id :ModelId , //using id to avoid lifetimes
2023-09-08 22:54:22 +00:00
}
2023-11-30 09:51:17 +00:00
#[ derive(Debug,Clone,Eq,Hash,PartialEq) ]
struct IntersectCollision {
2024-02-02 06:14:58 +00:00
model_id :ModelId ,
2023-11-30 09:51:17 +00:00
}
#[ derive(Debug,Clone,Eq,Hash,PartialEq) ]
enum Collision {
Contact ( ContactCollision ) ,
Intersect ( IntersectCollision ) ,
}
impl Collision {
2024-02-02 06:14:58 +00:00
fn model_id ( & self ) ->ModelId {
2023-11-30 09:51:17 +00:00
match self {
& Collision ::Contact ( ContactCollision { model_id , face_id :_ } )
| & Collision ::Intersect ( IntersectCollision { model_id } ) = > model_id ,
}
2023-10-04 00:20:35 +00:00
}
2024-02-02 06:14:58 +00:00
fn face_id ( & self ) ->Option < model_physics ::MinkowskiFace > {
2023-11-30 09:51:17 +00:00
match self {
& Collision ::Contact ( ContactCollision { model_id :_ , face_id } ) = > Some ( face_id ) ,
& Collision ::Intersect ( IntersectCollision { model_id :_ } ) = > None ,
}
2023-09-08 22:54:22 +00:00
}
}
2023-11-30 09:51:17 +00:00
#[ derive(Default) ]
2023-10-13 02:44:46 +00:00
struct TouchingState {
2023-11-30 09:51:17 +00:00
contacts :std ::collections ::HashSet ::< ContactCollision > ,
intersects :std ::collections ::HashSet ::< IntersectCollision > ,
2023-10-13 02:44:46 +00:00
}
impl TouchingState {
fn clear ( & mut self ) {
self . contacts . clear ( ) ;
self . intersects . clear ( ) ;
}
2023-11-30 09:51:17 +00:00
fn insert ( & mut self , collision :Collision ) ->bool {
match collision {
Collision ::Contact ( collision ) = > self . contacts . insert ( collision ) ,
Collision ::Intersect ( collision ) = > self . intersects . insert ( collision ) ,
}
2023-10-13 02:44:46 +00:00
}
2023-11-30 09:51:17 +00:00
fn remove ( & mut self , collision :& Collision ) ->bool {
match collision {
Collision ::Contact ( collision ) = > self . contacts . remove ( collision ) ,
Collision ::Intersect ( collision ) = > self . intersects . remove ( collision ) ,
}
2023-10-13 02:44:46 +00:00
}
2023-11-30 09:51:17 +00:00
fn base_acceleration ( & self , models :& PhysicsModels , style :& StyleModifiers , camera :& PhysicsCamera , controls :u32 , next_mouse :& MouseState , time :Time ) ->Planar64Vec3 {
let mut a = style . gravity ;
if let Some ( rocket_force ) = style . rocket_force {
a + = style . get_propulsion_control_dir ( camera , controls , next_mouse , time ) * rocket_force ;
}
//add accelerators
for contact in & self . contacts {
match models . attr ( contact . model_id ) {
PhysicsCollisionAttributes ::Contact { contacting , general } = > {
match & general . accelerator {
Some ( accelerator ) = > a + = accelerator . acceleration ,
None = > ( ) ,
}
} ,
_ = > panic! ( " impossible touching state " ) ,
}
}
for intersect in & self . intersects {
match models . attr ( intersect . model_id ) {
PhysicsCollisionAttributes ::Intersect { intersecting , general } = > {
match & general . accelerator {
Some ( accelerator ) = > a + = accelerator . acceleration ,
None = > ( ) ,
}
} ,
_ = > panic! ( " impossible touching state " ) ,
}
}
//add water../?
a
}
fn constrain_velocity ( & self , models :& PhysicsModels , style_mesh :& TransformedMesh , velocity :& mut Planar64Vec3 ) {
//TODO: trey push solve
for contact in & self . contacts {
let n = contact_normal ( models , style_mesh , contact ) ;
let d = n . dot128 ( * velocity ) ;
if d < 0 {
* velocity - = n * Planar64 ::raw ( ( ( d < < 32 ) / n . dot128 ( n ) ) as i64 ) ;
2023-10-13 02:44:46 +00:00
}
}
}
2023-11-30 09:51:17 +00:00
fn constrain_acceleration ( & self , models :& PhysicsModels , style_mesh :& TransformedMesh , acceleration :& mut Planar64Vec3 ) {
//TODO: trey push solve
for contact in & self . contacts {
let n = contact_normal ( models , style_mesh , contact ) ;
let d = n . dot128 ( * acceleration ) ;
if d < 0 {
* acceleration - = n * Planar64 ::raw ( ( ( d < < 32 ) / n . dot128 ( n ) ) as i64 ) ;
2023-10-13 02:44:46 +00:00
}
}
}
2023-11-30 09:51:17 +00:00
fn get_move_state ( & self , body :& Body , models :& PhysicsModels , style :& StyleModifiers , camera :& PhysicsCamera , controls :u32 , next_mouse :& MouseState , time :Time ) ->( 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)
let style_mesh = style . mesh ( ) ;
let gravity = self . base_acceleration ( models , style , camera , controls , next_mouse , time ) ;
let mut move_state = MoveState ::Air ;
let mut a = gravity ;
for contact in & self . contacts {
match models . attr ( contact . model_id ) {
PhysicsCollisionAttributes ::Contact { contacting , general } = > {
let normal = contact_normal ( models , & style_mesh , contact ) ;
match & contacting . contact_behaviour {
2024-02-02 06:14:58 +00:00
Some ( model ::ContactingBehaviour ::Ladder ( _ ) ) = > {
2023-11-30 09:51:17 +00:00
//ladder walkstate
let mut target_velocity = style . get_ladder_target_velocity ( camera , controls , next_mouse , time , & normal ) ;
self . constrain_velocity ( models , & style_mesh , & mut target_velocity ) ;
let ( walk_state , mut acceleration ) = WalkState ::ladder ( body , style , gravity , target_velocity , contact . clone ( ) , & normal ) ;
move_state = MoveState ::Ladder ( walk_state ) ;
self . constrain_acceleration ( models , & style_mesh , & mut acceleration ) ;
a = acceleration ;
} ,
None = > if style . surf_slope . map_or ( true , | s | normal . walkable ( s , Planar64Vec3 ::Y ) ) {
//check ground
let mut target_velocity = style . get_walk_target_velocity ( camera , controls , next_mouse , time , & normal ) ;
self . constrain_velocity ( models , & style_mesh , & mut target_velocity ) ;
let ( walk_state , mut acceleration ) = WalkState ::ground ( body , style , gravity , target_velocity , contact . clone ( ) , & normal ) ;
move_state = MoveState ::Walk ( walk_state ) ;
self . constrain_acceleration ( models , & style_mesh , & mut acceleration ) ;
a = acceleration ;
} ,
_ = > ( ) ,
}
} ,
_ = > panic! ( " impossible touching state " ) ,
}
}
for intersect in & self . intersects {
//water
}
self . constrain_acceleration ( models , & style_mesh , & mut a ) ;
( move_state , a )
}
2024-01-30 00:19:41 +00:00
fn predict_collision_end ( & self , collector :& mut instruction ::InstructionCollector < PhysicsInstruction > , models :& PhysicsModels , style_mesh :& TransformedMesh , body :& Body , time :Time ) {
2023-11-30 09:51:17 +00:00
let relative_body = VirtualBody ::relative ( & Body ::default ( ) , body ) . body ( time ) ;
for contact in & self . contacts {
//detect face slide off
let model_mesh = models . mesh ( contact . model_id ) ;
2024-02-02 06:14:58 +00:00
let minkowski = model_physics ::MinkowskiMesh ::minkowski_sum ( & model_mesh , & style_mesh ) ;
2023-11-30 09:51:17 +00:00
collector . collect ( minkowski . predict_collision_face_out ( & relative_body , collector . time ( ) , contact . face_id ) . map ( | ( face , time ) | {
TimedInstruction {
time ,
instruction :PhysicsInstruction ::CollisionEnd (
Collision ::Contact ( ContactCollision { model_id :contact . model_id , face_id :contact . face_id } )
) ,
}
} ) ) ;
}
for intersect in & self . intersects {
//detect model collision in reverse
let model_mesh = models . mesh ( intersect . model_id ) ;
2024-02-02 06:14:58 +00:00
let minkowski = model_physics ::MinkowskiMesh ::minkowski_sum ( & model_mesh , & style_mesh ) ;
2023-11-30 09:51:17 +00:00
collector . collect ( minkowski . predict_collision_out ( & relative_body , collector . time ( ) ) . map ( | ( face , time ) | {
TimedInstruction {
time ,
instruction :PhysicsInstruction ::CollisionEnd (
Collision ::Intersect ( IntersectCollision { model_id :intersect . model_id } )
) ,
}
} ) ) ;
2023-10-13 02:44:46 +00:00
}
}
}
2023-11-30 09:51:17 +00:00
impl Body {
pub fn new ( position :Planar64Vec3 , velocity :Planar64Vec3 , acceleration :Planar64Vec3 , time :Time ) ->Self {
2023-09-09 03:14:18 +00:00
Self {
2023-09-18 23:03:27 +00:00
position ,
velocity ,
acceleration ,
2023-11-30 09:51:17 +00:00
time ,
2023-09-09 03:14:18 +00:00
}
}
2023-09-27 09:12:20 +00:00
pub fn extrapolated_position ( & self , time :Time ) ->Planar64Vec3 {
let dt = time - self . time ;
self . position + self . velocity * dt + self . acceleration * ( dt * dt / 2 )
2023-09-09 03:14:18 +00:00
}
2023-09-27 09:12:20 +00:00
pub fn extrapolated_velocity ( & self , time :Time ) ->Planar64Vec3 {
let dt = time - self . time ;
self . velocity + self . acceleration * dt
2023-09-18 23:04:18 +00:00
}
2023-09-27 09:12:20 +00:00
pub fn advance_time ( & mut self , time :Time ) {
2023-09-09 03:14:18 +00:00
self . position = self . extrapolated_position ( time ) ;
2023-09-18 23:04:18 +00:00
self . velocity = self . extrapolated_velocity ( time ) ;
2023-09-09 03:14:18 +00:00
self . time = time ;
}
2023-11-30 09:51:17 +00:00
pub fn infinity_dir ( & self ) ->Option < Planar64Vec3 > {
if self . velocity = = Planar64Vec3 ::ZERO {
if self . acceleration = = Planar64Vec3 ::ZERO {
None
} else {
Some ( self . acceleration )
}
} else {
Some ( self . velocity )
}
}
2024-01-30 00:19:41 +00:00
pub fn grow_aabb ( & self , aabb :& mut aabb ::Aabb , t0 :Time , t1 :Time ) {
2023-11-30 09:51:17 +00:00
aabb . grow ( self . extrapolated_position ( t0 ) ) ;
aabb . grow ( self . extrapolated_position ( t1 ) ) ;
//v+a*t==0
//goober code
if self . acceleration . x ( ) ! = Planar64 ::ZERO {
let t = Time ::from ( - self . velocity . x ( ) / self . acceleration . x ( ) ) ;
if t0 < t & & t < t1 {
aabb . grow ( self . extrapolated_position ( t ) ) ;
}
}
if self . acceleration . y ( ) ! = Planar64 ::ZERO {
let t = Time ::from ( - self . velocity . y ( ) / self . acceleration . y ( ) ) ;
if t0 < t & & t < t1 {
aabb . grow ( self . extrapolated_position ( t ) ) ;
}
}
if self . acceleration . z ( ) ! = Planar64 ::ZERO {
let t = Time ::from ( - self . velocity . z ( ) / self . acceleration . z ( ) ) ;
if t0 < t & & t < t1 {
aabb . grow ( self . extrapolated_position ( t ) ) ;
}
}
}
2023-09-09 03:14:18 +00:00
}
2023-10-14 22:41:59 +00:00
impl std ::fmt ::Display for Body {
fn fmt ( & self , f :& mut std ::fmt ::Formatter < '_ > ) ->std ::fmt ::Result {
write! ( f , " p({}) v({}) a({}) t({}) " , self . position , self . velocity , self . acceleration , self . time )
}
}
2023-09-09 03:14:18 +00:00
2023-11-30 09:51:17 +00:00
struct VirtualBody < ' a > {
body0 :& ' a Body ,
body1 :& ' a Body ,
}
impl VirtualBody < '_ > {
fn relative < ' a > ( body0 :& ' a Body , body1 :& ' a Body ) ->VirtualBody < ' a > {
//(p0,v0,a0,t0)
//(p1,v1,a1,t1)
VirtualBody {
body0 ,
body1 ,
}
}
fn extrapolated_position ( & self , time :Time ) ->Planar64Vec3 {
self . body1 . extrapolated_position ( time ) - self . body0 . extrapolated_position ( time )
}
fn extrapolated_velocity ( & self , time :Time ) ->Planar64Vec3 {
self . body1 . extrapolated_velocity ( time ) - self . body0 . extrapolated_velocity ( time )
}
fn acceleration ( & self ) ->Planar64Vec3 {
self . body1 . acceleration - self . body0 . acceleration
}
fn body ( & self , time :Time ) ->Body {
Body ::new ( self . extrapolated_position ( time ) , self . extrapolated_velocity ( time ) , self . acceleration ( ) , time )
}
}
2023-10-05 03:04:04 +00:00
impl Default for PhysicsState {
2023-10-31 05:25:03 +00:00
fn default ( ) ->Self {
2023-10-05 03:04:04 +00:00
Self {
2023-09-27 09:12:20 +00:00
spawn_point :Planar64Vec3 ::int ( 0 , 50 , 0 ) ,
2023-11-30 09:51:17 +00:00
body :Body ::new ( Planar64Vec3 ::int ( 0 , 50 , 0 ) , Planar64Vec3 ::int ( 0 , 0 , 0 ) , Planar64Vec3 ::int ( 0 , - 100 , 0 ) , Time ::ZERO ) ,
2023-10-31 05:25:03 +00:00
time :Time ::ZERO ,
2023-10-05 03:04:04 +00:00
style :StyleModifiers ::default ( ) ,
2023-10-13 02:44:46 +00:00
touching :TouchingState ::default ( ) ,
2023-10-18 23:21:06 +00:00
models :PhysicsModels ::default ( ) ,
2024-01-30 00:19:41 +00:00
bvh :bvh ::BvhNode ::default ( ) ,
2023-10-13 02:44:46 +00:00
move_state : MoveState ::Air ,
2023-10-31 05:25:03 +00:00
camera :PhysicsCamera ::default ( ) ,
next_mouse :MouseState ::default ( ) ,
controls :0 ,
2023-10-05 03:04:04 +00:00
world :WorldState { } ,
2024-02-02 06:14:58 +00:00
mode_state :ModeState ::default ( ) ,
2023-10-18 23:18:17 +00:00
modes :Modes ::default ( ) ,
2023-10-05 03:04:04 +00:00
}
}
}
2023-09-08 18:33:16 +00:00
impl PhysicsState {
2023-10-04 02:43:41 +00:00
pub fn clear ( & mut self ) {
self . models . clear ( ) ;
self . modes . clear ( ) ;
2023-10-13 02:44:46 +00:00
self . touching . clear ( ) ;
2024-01-30 00:19:41 +00:00
self . bvh = bvh ::BvhNode ::default ( ) ;
2023-10-05 03:04:04 +00:00
}
pub fn output ( & self ) ->PhysicsOutputState {
PhysicsOutputState {
body :self . body . clone ( ) ,
camera :self . camera . clone ( ) ,
2023-10-31 05:25:03 +00:00
camera_offset :self . style . camera_offset . clone ( ) ,
2023-10-05 03:04:04 +00:00
}
}
2023-10-19 00:17:21 +00:00
pub fn spawn ( & mut self , spawn_point :Planar64Vec3 ) {
2024-02-02 06:14:58 +00:00
self . mode_state . stage_id = 0 ;
2023-10-19 00:17:21 +00:00
self . spawn_point = spawn_point ;
2024-01-30 00:19:41 +00:00
self . process_instruction ( instruction ::TimedInstruction {
2023-10-19 00:17:21 +00:00
time :self . time ,
instruction : PhysicsInstruction ::Input ( PhysicsInputInstruction ::Reset ) ,
} ) ;
}
2024-02-02 06:14:58 +00:00
pub fn generate_models ( & mut self , indexed_models :& map ::Map ) {
2023-10-05 03:04:04 +00:00
let mut starts = Vec ::new ( ) ;
let mut spawns = Vec ::new ( ) ;
2023-11-30 09:51:17 +00:00
let mut attr_hash = std ::collections ::HashMap ::new ( ) ;
2023-10-05 03:04:04 +00:00
for model in & indexed_models . models {
2023-11-30 09:51:17 +00:00
let mesh_id = self . models . meshes . len ( ) ;
let mut make_mesh = false ;
2023-10-05 03:04:04 +00:00
for model_instance in & model . instances {
2023-11-30 09:51:17 +00:00
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 ;
2024-02-02 06:14:58 +00:00
self . models . push_model ( model_physics ) ;
2023-10-05 03:04:04 +00:00
}
}
2023-11-30 09:51:17 +00:00
if make_mesh {
self . models . push_mesh ( PhysicsMesh ::from ( model ) ) ;
}
2023-10-05 03:04:04 +00:00
}
2024-01-30 00:19:41 +00:00
self . bvh = bvh ::generate_bvh ( self . models . aabb_list ( ) ) ;
2023-10-18 23:21:06 +00:00
println! ( " Physics Objects: {} " , self . models . models . len ( ) ) ;
2023-10-05 03:04:04 +00:00
}
2023-10-10 02:44:49 +00:00
pub fn load_user_settings ( & mut self , user_settings :& crate ::settings ::UserSettings ) {
self . camera . sensitivity = user_settings . calculate_sensitivity ( ) ;
}
2023-09-09 03:14:18 +00:00
//tickless gaming
2023-09-27 09:12:20 +00:00
pub fn run ( & mut self , time_limit :Time ) {
2023-09-09 03:14:18 +00:00
//prepare is ommitted - everything is done via instructions.
2023-09-19 01:06:03 +00:00
while let Some ( instruction ) = self . next_instruction ( time_limit ) { //collect
2023-09-09 03:14:18 +00:00
//process
self . process_instruction ( instruction ) ;
//write hash lol
2023-09-08 18:33:16 +00:00
}
}
2023-09-27 09:12:20 +00:00
pub fn advance_time ( & mut self , time : Time ) {
2023-09-09 03:14:18 +00:00
self . body . advance_time ( time ) ;
self . time = time ;
2023-09-08 18:33:16 +00:00
}
2023-09-08 18:33:20 +00:00
2023-09-20 00:53:29 +00:00
fn set_control ( & mut self , control :u32 , state :bool ) {
self . controls = if state { self . controls | control } else { self . controls & ! control } ;
}
2023-10-13 02:44:46 +00:00
2023-10-31 21:11:39 +00:00
fn next_strafe_instruction ( & self ) ->Option < TimedInstruction < PhysicsInstruction > > {
self . style . strafe . as_ref ( ) . map ( | strafe | {
2023-10-20 20:46:59 +00:00
TimedInstruction {
2024-02-02 06:14:58 +00:00
time :strafe . next_tick ( self . time ) ,
2023-10-20 20:46:59 +00:00
//only poll the physics if there is a before and after mouse event
instruction :PhysicsInstruction ::StrafeTick
}
} )
2023-09-08 18:33:20 +00:00
}
2023-09-08 21:11:24 +00:00
2023-09-09 03:13:30 +00:00
//state mutated on collision:
//Accelerator
//stair step-up
//state mutated on instruction
//change fly acceleration (fly_sustain)
//change fly velocity
//generic event emmiters
//PlatformStandTime
//walk/swim/air/ladder sounds
//VState?
//falling under the map
// fn next_respawn_instruction(&self) -> Option<TimedInstruction<PhysicsInstruction>> {
// if self.body.position<self.world.min_y {
// return Some(TimedInstruction{
// time:self.time,
// instruction:PhysicsInstruction::Trigger(None)
// });
// }
// }
// fn next_water_instruction(&self) -> Option<TimedInstruction<PhysicsInstruction>> {
// return Some(TimedInstruction{
// time:(self.time*self.strafe_tick_num/self.strafe_tick_den+1)*self.strafe_tick_den/self.strafe_tick_num,
// //only poll the physics if there is a before and after mouse event
// instruction:PhysicsInstruction::Water
// });
// }
2023-11-30 09:51:17 +00:00
fn refresh_walk_target ( & mut self ) ->Planar64Vec3 {
2023-10-13 02:44:46 +00:00
match & mut self . move_state {
2023-11-30 09:51:17 +00:00
MoveState ::Air | MoveState ::Water = > self . touching . base_acceleration ( & self . models , & self . style , & self . camera , self . controls , & self . next_mouse , self . time ) ,
MoveState ::Walk ( WalkState { state , contact , jump_direction :_ } ) = > {
let style_mesh = self . style . mesh ( ) ;
let n = contact_normal ( & self . models , & style_mesh , contact ) ;
let gravity = self . touching . base_acceleration ( & self . models , & self . style , & self . camera , self . controls , & self . next_mouse , self . time ) ;
let mut a ;
let mut v = self . style . get_walk_target_velocity ( & self . camera , self . controls , & self . next_mouse , self . time , & n ) ;
self . touching . constrain_velocity ( & self . models , & style_mesh , & mut v ) ;
let normal_accel = - n . dot ( gravity ) / n . length ( ) ;
( * state , a ) = WalkEnum ::with_target_velocity ( & self . body , & self . style , v , & n , self . style . walk_speed , normal_accel ) ;
a
2023-10-13 02:44:46 +00:00
} ,
2023-11-30 09:51:17 +00:00
MoveState ::Ladder ( WalkState { state , contact , jump_direction :_ } ) = > {
let style_mesh = self . style . mesh ( ) ;
let n = contact_normal ( & self . models , & style_mesh , contact ) ;
let gravity = self . touching . base_acceleration ( & self . models , & self . style , & self . camera , self . controls , & self . next_mouse , self . time ) ;
let mut a ;
let mut v = self . style . get_ladder_target_velocity ( & self . camera , self . controls , & self . next_mouse , self . time , & n ) ;
self . touching . constrain_velocity ( & self . models , & style_mesh , & mut v ) ;
( * state , a ) = WalkEnum ::with_target_velocity ( & self . body , & self . style , v , & n , self . style . ladder_speed , self . style . ladder_accel ) ;
a
2023-10-13 02:44:46 +00:00
} ,
2023-09-20 00:53:29 +00:00
}
}
2023-10-13 02:44:46 +00:00
fn next_move_instruction ( & self ) ->Option < TimedInstruction < PhysicsInstruction > > {
2023-09-19 04:10:07 +00:00
//check if you have a valid walk state and create an instruction
2023-10-13 02:44:46 +00:00
match & self . move_state {
MoveState ::Walk ( walk_state ) | MoveState ::Ladder ( walk_state ) = > match & walk_state . state {
2023-10-17 07:17:54 +00:00
WalkEnum ::Transient ( walk_target ) = > Some ( TimedInstruction {
time :walk_target . time ,
2023-09-19 06:36:14 +00:00
instruction :PhysicsInstruction ::ReachWalkTargetVelocity
} ) ,
WalkEnum ::Reached = > None ,
}
2023-10-13 02:44:46 +00:00
MoveState ::Air = > self . next_strafe_instruction ( ) ,
MoveState ::Water = > None , //TODO
2023-09-19 04:10:07 +00:00
}
2023-09-08 21:11:24 +00:00
}
2023-09-08 18:33:20 +00:00
}
2024-01-30 00:19:41 +00:00
impl instruction ::InstructionEmitter < PhysicsInstruction > for PhysicsState {
2023-09-09 00:34:26 +00:00
//this little next instruction function can cache its return value and invalidate the cached value by watching the State.
2023-11-30 09:51:17 +00:00
fn next_instruction ( & self , time_limit :Time ) ->Option < TimedInstruction < PhysicsInstruction > > {
2023-09-08 18:33:20 +00:00
//JUST POLLING!!! NO MUTATION
2024-01-30 00:19:41 +00:00
let mut collector = instruction ::InstructionCollector ::new ( time_limit ) ;
2023-11-30 09:51:17 +00:00
collector . collect ( self . next_move_instruction ( ) ) ;
let style_mesh = self . style . mesh ( ) ;
//check for collision ends
self . touching . predict_collision_end ( & mut collector , & self . models , & style_mesh , & self . body , self . time ) ;
//check for collision starts
2024-01-30 00:19:41 +00:00
let mut aabb = aabb ::Aabb ::default ( ) ;
2023-11-30 09:51:17 +00:00
self . body . grow_aabb ( & mut aabb , self . time , collector . time ( ) ) ;
aabb . inflate ( self . style . hitbox . halfsize ) ;
//common body
let relative_body = VirtualBody ::relative ( & Body ::default ( ) , & self . body ) . body ( self . time ) ;
2023-10-06 06:52:04 +00:00
self . bvh . the_tester ( & aabb , & mut | id | {
2023-11-30 09:51:17 +00:00
//no checks are needed because of the time limits.
let model_mesh = self . models . mesh ( id ) ;
2024-02-02 06:14:58 +00:00
let minkowski = model_physics ::MinkowskiMesh ::minkowski_sum ( & model_mesh , & style_mesh ) ;
2023-11-30 09:51:17 +00:00
collector . collect ( minkowski . predict_collision_in ( & relative_body , collector . time ( ) )
//temp (?) code to avoid collision loops
. map_or ( None , | ( face , time ) | if time = = self . time { None } else { Some ( ( face , 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 } ) ,
} ) }
} ) ) ;
2023-10-06 06:52:04 +00:00
} ) ;
2023-09-09 03:12:58 +00:00
collector . instruction ( )
2023-09-08 18:33:20 +00:00
}
2023-09-08 18:33:16 +00:00
}
2023-09-09 00:15:49 +00:00
2023-11-30 09:51:17 +00:00
fn get_walk_state ( move_state :& MoveState ) ->Option < & WalkState > {
match move_state {
MoveState ::Walk ( walk_state ) | MoveState ::Ladder ( walk_state ) = > Some ( walk_state ) ,
MoveState ::Air | MoveState ::Water = > None ,
}
}
fn jumped_velocity ( models :& PhysicsModels , style :& StyleModifiers , walk_state :& WalkState , v :& mut Planar64Vec3 ) {
let jump_dir = match & walk_state . jump_direction {
JumpDirection ::FromContactNormal = > contact_normal ( models , & style . mesh ( ) , & walk_state . contact ) ,
& JumpDirection ::Exactly ( dir ) = > dir ,
} ;
* v = * v + jump_dir * ( style . get_jump_deltav ( ) / jump_dir . length ( ) ) ;
}
fn contact_normal ( models :& PhysicsModels , style_mesh :& TransformedMesh , contact :& ContactCollision ) ->Planar64Vec3 {
let model_mesh = models . mesh ( contact . model_id ) ;
2024-02-02 06:14:58 +00:00
let minkowski = model_physics ::MinkowskiMesh ::minkowski_sum ( & model_mesh , style_mesh ) ;
2023-11-30 09:51:17 +00:00
minkowski . face_nd ( contact . face_id ) . 0
}
fn set_position ( body :& mut Body , touching :& mut TouchingState , point :Planar64Vec3 ) ->Planar64Vec3 {
//test intersections at new position
//hovering above the surface 0 units is not intersecting. you will fall into it just fine
2023-10-18 23:18:17 +00:00
body . position = point ;
//manual clear //for c in contacts{process_instruction(CollisionEnd(c))}
touching . clear ( ) ;
//TODO: calculate contacts and determine the actual state
//touching.recalculate(body);
2023-11-30 09:51:17 +00:00
point
}
fn set_velocity_cull ( body :& mut Body , touching :& mut TouchingState , models :& PhysicsModels , style_mesh :& TransformedMesh , v :Planar64Vec3 ) ->bool {
//This is not correct but is better than what I have
let mut culled = false ;
touching . contacts . retain ( | contact | {
let n = contact_normal ( models , style_mesh , contact ) ;
let r = n . dot ( v ) < = Planar64 ::ZERO ;
if ! r {
culled = true ;
println! ( " set_velocity_cull contact= {:?} " , contact ) ;
}
r
} ) ;
set_velocity ( body , touching , models , style_mesh , v ) ;
culled
}
fn set_velocity ( body :& mut Body , touching :& TouchingState , models :& PhysicsModels , style_mesh :& TransformedMesh , mut v :Planar64Vec3 ) ->Planar64Vec3 {
touching . constrain_velocity ( models , style_mesh , & mut v ) ;
body . velocity = v ;
v
}
fn set_acceleration_cull ( body :& mut Body , touching :& mut TouchingState , models :& PhysicsModels , style_mesh :& TransformedMesh , a :Planar64Vec3 ) ->bool {
//This is not correct but is better than what I have
let mut culled = false ;
touching . contacts . retain ( | contact | {
let n = contact_normal ( models , style_mesh , contact ) ;
let r = n . dot ( a ) < = Planar64 ::ZERO ;
if ! r {
culled = true ;
println! ( " set_acceleration_cull contact= {:?} " , contact ) ;
}
r
} ) ;
set_acceleration ( body , touching , models , style_mesh , a ) ;
culled
}
fn set_acceleration ( body :& mut Body , touching :& TouchingState , models :& PhysicsModels , style_mesh :& TransformedMesh , mut a :Planar64Vec3 ) ->Planar64Vec3 {
touching . constrain_acceleration ( models , style_mesh , & mut a ) ;
body . acceleration = a ;
a
}
fn teleport ( body :& mut Body , touching :& mut TouchingState , models :& PhysicsModels , style :& StyleModifiers , point :Planar64Vec3 ) ->MoveState {
set_position ( body , touching , point ) ;
set_acceleration ( body , touching , models , & style . mesh ( ) , style . gravity ) ;
MoveState ::Air
2023-10-18 23:18:17 +00:00
}
2024-02-02 06:14:58 +00:00
fn teleport_to_spawn ( body :& mut Body , touching :& mut TouchingState , style :& StyleModifiers , mode :& gameplay_modes ::Mode , models :& PhysicsModels , stage_id :StageId ) ->Option < MoveState > {
let model = models . model ( mode . get_spawn_model_id ( stage_id ) ? ) ;
2023-11-30 09:51:17 +00:00
let point = model . transform . transform_point3 ( Planar64Vec3 ::Y ) + Planar64Vec3 ::Y * ( style . hitbox . halfsize . y ( ) + Planar64 ::ONE / 16 ) ;
Some ( teleport ( body , touching , models , style , point ) )
2023-10-28 23:38:16 +00:00
}
2023-10-18 23:18:17 +00:00
2024-02-02 06:14:58 +00:00
fn run_teleport_behaviour ( wormhole :& Option < gameplay_attributes ::Wormhole > , game :& mut ModeState , models :& PhysicsModels , mode :& gameplay_modes ::Mode , style :& StyleModifiers , touching :& mut TouchingState , body :& mut Body , model_id :ModelId ) ->Option < MoveState > {
2023-11-30 09:51:17 +00:00
//TODO: jump count and checkpoints are always reset on teleport.
//Map makers are expected to use tools to prevent
//multi-boosting on JumpLimit boosters such as spawning into a SetVelocity
2024-02-02 06:14:58 +00:00
mode . get_element ( model_id ) . map ( | stage_element | {
if stage_element . force | | game . 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 {
model ::StageElementBehaviour ::SpawnAt = > None ,
model ::StageElementBehaviour ::Trigger
| model ::StageElementBehaviour ::Teleport = > {
//I guess this is correct behaviour when trying to teleport to a non-existent spawn but it's still weird
teleport_to_spawn ( body , touching , style , modes . get_mode ( stage_element . mode_id ) ? , models , game . stage_id )
} ,
model ::StageElementBehaviour ::Platform = > None ,
& model ::StageElementBehaviour ::Checkpoint = > {
// let mode=modes.get_mode(stage_element.mode_id)?;
// if mode.ordered_checkpoint_id.map_or(true,|id|id<game.next_ordered_checkpoint_id)
// &&mode.unordered_checkpoint_count<=game.unordered_checkpoints.len() as u32{
// //pass
None
// }else{
// //fail
// teleport_to_spawn(body,touching,style,modes.get_mode(stage_element.mode_id)?,models,game.stage_id)
// }
} ,
& model ::StageElementBehaviour ::Ordered { checkpoint_id } = > {
if checkpoint_id < game . next_ordered_checkpoint_id {
//if you hit a checkpoint you already hit, nothing happens
2023-11-30 09:51:17 +00:00
None
2024-02-02 06:14:58 +00:00
} else if game . next_ordered_checkpoint_id = = checkpoint_id {
//if you hit the next number in a sequence of ordered checkpoints
//increment the current checkpoint id
game . next_ordered_checkpoint_id + = 1 ;
2023-11-30 09:51:17 +00:00
None
2024-02-02 06:14:58 +00:00
} else {
//If you hit an ordered checkpoint after missing a previous one
teleport_to_spawn ( body , touching , style , modes . get_mode ( stage_element . mode_id ) ? , models , game . stage_id )
}
} ,
model ::StageElementBehaviour ::Unordered = > {
//count model id in accumulated unordered checkpoints
game . unordered_checkpoints . insert ( model_id ) ;
None
} ,
& model ::StageElementBehaviour ::JumpLimit ( jump_limit ) = > {
//let count=game.jump_counts.get(&model.id);
//TODO
None
} ,
}
} ) . or_else ( | |
match wormhole {
Some ( gameplay_attributes ::Wormhole { destination_model } ) = > {
2023-11-30 09:51:17 +00:00
let origin_model = models . model ( model_id ) ;
2024-02-02 06:14:58 +00:00
let destination_model = models . get_wormhole_model ( destination_model ) ? ;
2023-10-18 23:15:42 +00:00
//ignore the transform for now
2023-11-30 09:51:17 +00:00
Some ( teleport ( body , touching , models , style , body . position - origin_model . transform . translation + destination_model . transform . translation ) )
2023-10-18 23:18:17 +00:00
}
None = > None ,
2024-02-02 06:14:58 +00:00
} )
2023-10-18 23:18:17 +00:00
}
2024-01-30 00:19:41 +00:00
impl instruction ::InstructionConsumer < PhysicsInstruction > for PhysicsState {
2023-09-09 03:14:18 +00:00
fn process_instruction ( & mut self , ins :TimedInstruction < PhysicsInstruction > ) {
2023-11-30 09:51:17 +00:00
match & ins . instruction {
2023-10-05 03:04:04 +00:00
PhysicsInstruction ::Input ( PhysicsInputInstruction ::Idle )
| PhysicsInstruction ::Input ( PhysicsInputInstruction ::SetNextMouse ( _ ) )
| PhysicsInstruction ::Input ( PhysicsInputInstruction ::ReplaceMouse ( _ , _ ) )
2023-11-30 09:51:17 +00:00
| PhysicsInstruction ::StrafeTick = > ( ) ,
2023-10-05 06:51:39 +00:00
_ = > println! ( " {} | {:?} " , ins . time , ins . instruction ) ,
2023-09-19 06:36:14 +00:00
}
//selectively update body
2023-11-30 09:51:17 +00:00
match & ins . instruction {
2023-10-19 00:17:21 +00:00
PhysicsInstruction ::Input ( PhysicsInputInstruction ::Idle ) = > self . time = ins . time , //idle simply updates time
2023-10-04 21:01:06 +00:00
PhysicsInstruction ::Input ( _ )
| PhysicsInstruction ::ReachWalkTargetVelocity
| PhysicsInstruction ::CollisionStart ( _ )
| PhysicsInstruction ::CollisionEnd ( _ )
2023-10-19 00:17:21 +00:00
| PhysicsInstruction ::StrafeTick = > self . advance_time ( ins . time ) ,
2023-09-19 06:36:14 +00:00
}
2023-11-30 09:51:17 +00:00
match ins . instruction {
PhysicsInstruction ::CollisionStart ( c ) = > {
let style_mesh = self . style . mesh ( ) ;
let model_id = c . model_id ( ) ;
match ( self . models . attr ( model_id ) , & c ) {
( PhysicsCollisionAttributes ::Contact { contacting , general } , Collision ::Contact ( contact ) ) = > {
2023-10-18 02:12:25 +00:00
let mut v = self . body . velocity ;
2023-11-30 09:51:17 +00:00
let normal = contact_normal ( & self . models , & style_mesh , contact ) ;
2023-10-17 07:17:54 +00:00
match & contacting . contact_behaviour {
2024-02-02 06:14:58 +00:00
Some ( gameplay_attributes ::ContactingBehaviour ::Surf ) = > println! ( " I'm surfing! " ) ,
Some ( gameplay_attributes ::ContactingBehaviour ::Cling ) = > println! ( " Unimplemented! " ) ,
& Some ( gameplay_attributes ::ContactingBehaviour ::Elastic ( elasticity ) ) = > {
2023-11-30 09:51:17 +00:00
//velocity and normal are facing opposite directions so this is inherently negative.
let d = normal . dot ( v ) * ( Planar64 ::ONE + Planar64 ::raw ( elasticity as i64 + 1 ) ) ;
v + = normal * ( d / normal . dot ( normal ) ) ;
2023-10-18 02:12:25 +00:00
} ,
2024-02-02 06:14:58 +00:00
Some ( gameplay_attributes ::ContactingBehaviour ::Ladder ( contacting_ladder ) ) = > {
2023-10-17 07:17:54 +00:00
if contacting_ladder . sticky {
//kill v
2023-11-30 09:51:17 +00:00
//actually you could do this with a booster attribute :thinking:
2023-10-18 02:12:25 +00:00
v = Planar64Vec3 ::ZERO ; //model.velocity
2023-10-17 07:17:54 +00:00
}
//ladder walkstate
2023-11-30 09:51:17 +00:00
let gravity = self . touching . base_acceleration ( & self . models , & self . style , & self . camera , self . controls , & self . next_mouse , self . time ) ;
let mut target_velocity = self . style . get_ladder_target_velocity ( & self . camera , self . controls , & self . next_mouse , self . time , & normal ) ;
self . touching . constrain_velocity ( & self . models , & style_mesh , & mut target_velocity ) ;
let ( walk_state , a ) = WalkState ::ladder ( & self . body , & self . style , gravity , target_velocity , contact . clone ( ) , & normal ) ;
2023-10-17 07:17:54 +00:00
self . move_state = MoveState ::Ladder ( walk_state ) ;
2023-11-30 09:51:17 +00:00
set_acceleration ( & mut self . body , & self . touching , & self . models , & style_mesh , a ) ;
2023-10-17 07:17:54 +00:00
}
2023-11-30 09:51:17 +00:00
None = > if self . style . surf_slope . map_or ( true , | s | contact_normal ( & self . models , & style_mesh , contact ) . walkable ( s , Planar64Vec3 ::Y ) ) {
//ground
let gravity = self . touching . base_acceleration ( & self . models , & self . style , & 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 , & normal ) ;
self . touching . constrain_velocity ( & self . models , & style_mesh , & mut target_velocity ) ;
let ( walk_state , a ) = WalkState ::ground ( & self . body , & self . style , gravity , target_velocity , contact . clone ( ) , & normal ) ;
self . move_state = MoveState ::Walk ( walk_state ) ;
set_acceleration ( & mut self . body , & self . touching , & self . models , & style_mesh , a ) ;
2023-10-04 21:15:04 +00:00
} ,
}
2023-10-05 03:04:04 +00:00
//check ground
2023-11-30 09:51:17 +00:00
self . touching . insert ( c ) ;
2023-10-18 23:18:17 +00:00
//I love making functions with 10 arguments to dodge the borrow checker
2024-02-02 06:14:58 +00:00
run_teleport_behaviour ( & general . teleport_behaviour , & mut self . mode_state , & self . models , & self . modes , & self . style , & mut self . touching , & mut self . body , model_id ) ;
2023-10-04 21:15:04 +00:00
//flatten v
2023-11-30 09:51:17 +00:00
self . touching . constrain_velocity ( & self . models , & style_mesh , & mut v ) ;
2023-10-05 03:04:04 +00:00
match & general . booster {
Some ( booster ) = > {
2023-11-30 09:51:17 +00:00
//DELETE THIS when boosters get converted to height machines
2023-10-18 02:12:25 +00:00
match booster {
2024-02-02 06:14:58 +00:00
& gameplay_attributes ::Booster ::Affine ( transform ) = > v = transform . transform_point3 ( v ) ,
& gameplay_attributes ::Booster ::Velocity ( velocity ) = > v + = velocity ,
& gameplay_attributes ::Booster ::Energy { direction : _ , energy : _ } = > todo! ( ) ,
2023-10-18 02:12:25 +00:00
}
} ,
None = > ( ) ,
}
2023-11-30 09:51:17 +00:00
let calc_move = if self . style . get_control ( StyleModifiers ::CONTROL_JUMP , self . controls ) {
if let Some ( walk_state ) = get_walk_state ( & self . move_state ) {
jumped_velocity ( & self . models , & self . style , walk_state , & mut v ) ;
set_velocity_cull ( & mut self . body , & mut self . touching , & self . models , & style_mesh , v )
} else { false }
} else { false } ;
2023-10-18 02:12:25 +00:00
match & general . trajectory {
Some ( trajectory ) = > {
match trajectory {
2024-02-02 06:14:58 +00:00
gameplay_attributes ::SetTrajectory ::AirTime ( _ ) = > todo! ( ) ,
gameplay_attributes ::SetTrajectory ::Height ( _ ) = > todo! ( ) ,
gameplay_attributes ::SetTrajectory ::TargetPointTime { target_point : _ , time : _ } = > todo! ( ) ,
gameplay_attributes ::SetTrajectory ::TargetPointSpeed { target_point : _ , speed : _ , trajectory_choice : _ } = > todo! ( ) ,
& gameplay_attributes ::SetTrajectory ::Velocity ( velocity ) = > v = velocity ,
gameplay_attributes ::SetTrajectory ::DotVelocity { direction : _ , dot : _ } = > todo! ( ) ,
2023-10-18 02:12:25 +00:00
}
2023-10-05 03:04:04 +00:00
} ,
None = > ( ) ,
}
2023-11-30 09:51:17 +00:00
set_velocity ( & mut self . body , & self . touching , & self . models , & style_mesh , v ) ;
//not sure if or is correct here
if calc_move | | Planar64 ::ZERO < normal . dot ( v ) {
( self . move_state , self . body . acceleration ) = self . touching . get_move_state ( & self . body , & self . models , & self . style , & self . camera , self . controls , & self . next_mouse , self . time ) ;
2023-10-20 20:47:26 +00:00
}
2023-11-30 09:51:17 +00:00
let a = self . refresh_walk_target ( ) ;
set_acceleration ( & mut self . body , & self . touching , & self . models , & self . style . mesh ( ) , a ) ;
2023-10-04 21:15:04 +00:00
} ,
2023-11-30 09:51:17 +00:00
( PhysicsCollisionAttributes ::Intersect { intersecting : _ , general } , Collision ::Intersect ( intersect ) ) = > {
2023-10-04 21:15:04 +00:00
//I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
2023-11-30 09:51:17 +00:00
self . touching . insert ( c ) ;
2024-02-02 06:14:58 +00:00
run_teleport_behaviour ( & general . teleport_behaviour , & mut self . mode_state , & self . models , & self . modes , & self . style , & mut self . touching , & mut self . body , model_id ) ;
2023-10-04 21:15:04 +00:00
} ,
2023-11-30 09:51:17 +00:00
_ = > panic! ( " invalid pair " ) ,
2023-09-20 00:53:29 +00:00
}
2023-09-18 20:20:51 +00:00
} ,
PhysicsInstruction ::CollisionEnd ( c ) = > {
2023-11-30 09:51:17 +00:00
match self . models . attr ( c . model_id ( ) ) {
PhysicsCollisionAttributes ::Contact { contacting :_ , general :_ } = > {
self . touching . remove ( & c ) ; //remove contact before calling contact_constrain_acceleration
2023-10-04 21:15:04 +00:00
//check ground
2023-11-30 09:51:17 +00:00
( self . move_state , self . body . acceleration ) = self . touching . get_move_state ( & self . body , & self . models , & self . style , & self . camera , self . controls , & self . next_mouse , self . time ) ;
2023-10-04 21:15:04 +00:00
} ,
2023-11-30 09:51:17 +00:00
PhysicsCollisionAttributes ::Intersect { intersecting :_ , general :_ } = > {
self . touching . remove ( & c ) ;
2023-10-04 21:15:04 +00:00
} ,
}
2023-09-18 20:20:51 +00:00
} ,
2023-09-09 03:14:18 +00:00
PhysicsInstruction ::StrafeTick = > {
2023-11-30 09:51:17 +00:00
let control_dir = self . style . get_control_dir ( self . controls ) ;
if control_dir ! = Planar64Vec3 ::ZERO {
let camera_mat = self . camera . simulate_move_rotation_y ( self . camera . mouse . lerp ( & self . next_mouse , self . time ) . x ) ;
let control_dir = camera_mat * control_dir ;
//normalize but careful for zero
let d = self . body . velocity . dot ( control_dir ) ;
if d < self . style . mv {
let v = self . body . velocity + control_dir * ( self . style . mv - d ) ;
//this is wrong but will work ig
//need to note which push planes activate in push solve and keep those
if set_velocity_cull ( & mut self . body , & mut self . touching , & self . models , & self . style . mesh ( ) , v ) {
( self . move_state , self . body . acceleration ) = self . touching . get_move_state ( & self . body , & self . models , & self . style , & self . camera , self . controls , & self . next_mouse , self . time ) ;
}
}
2023-09-09 03:14:18 +00:00
}
}
PhysicsInstruction ::ReachWalkTargetVelocity = > {
2023-10-13 02:44:46 +00:00
match & mut self . move_state {
MoveState ::Air | MoveState ::Water = > ( ) ,
MoveState ::Walk ( walk_state ) | MoveState ::Ladder ( walk_state ) = > {
2023-10-17 07:17:54 +00:00
match & mut walk_state . state {
WalkEnum ::Reached = > ( ) ,
WalkEnum ::Transient ( walk_target ) = > {
2023-11-30 09:51:17 +00:00
let style_mesh = self . style . mesh ( ) ;
2023-10-17 07:17:54 +00:00
//precisely set velocity
2023-11-30 09:51:17 +00:00
let a = Planar64Vec3 ::ZERO ; //ignore gravity for now.
set_acceleration ( & mut self . body , & self . touching , & self . models , & style_mesh , a ) ;
let v = walk_target . velocity ;
set_velocity ( & mut self . body , & self . touching , & self . models , & style_mesh , v ) ;
2023-10-17 07:17:54 +00:00
walk_state . state = WalkEnum ::Reached ;
} ,
}
2023-10-13 02:44:46 +00:00
}
}
2023-09-19 06:36:14 +00:00
} ,
2023-09-20 00:53:29 +00:00
PhysicsInstruction ::Input ( input_instruction ) = > {
2023-10-02 02:25:16 +00:00
let mut refresh_walk_target = true ;
2023-09-20 00:53:29 +00:00
match input_instruction {
2023-10-05 03:04:04 +00:00
PhysicsInputInstruction ::SetNextMouse ( m ) = > {
2023-09-27 09:12:20 +00:00
self . camera . move_mouse ( self . next_mouse . pos ) ;
2023-10-05 03:04:04 +00:00
( self . camera . mouse , self . next_mouse ) = ( self . next_mouse . clone ( ) , m ) ;
} ,
PhysicsInputInstruction ::ReplaceMouse ( m0 , m1 ) = > {
2023-09-27 09:12:20 +00:00
self . camera . move_mouse ( m0 . pos ) ;
2023-10-05 03:04:04 +00:00
( self . camera . mouse , self . next_mouse ) = ( m0 , m1 ) ;
2023-09-20 00:53:29 +00:00
} ,
2023-10-05 03:04:04 +00:00
PhysicsInputInstruction ::SetMoveForward ( s ) = > self . set_control ( StyleModifiers ::CONTROL_MOVEFORWARD , s ) ,
PhysicsInputInstruction ::SetMoveLeft ( s ) = > self . set_control ( StyleModifiers ::CONTROL_MOVELEFT , s ) ,
PhysicsInputInstruction ::SetMoveBack ( s ) = > self . set_control ( StyleModifiers ::CONTROL_MOVEBACK , s ) ,
PhysicsInputInstruction ::SetMoveRight ( s ) = > self . set_control ( StyleModifiers ::CONTROL_MOVERIGHT , s ) ,
PhysicsInputInstruction ::SetMoveUp ( s ) = > self . set_control ( StyleModifiers ::CONTROL_MOVEUP , s ) ,
PhysicsInputInstruction ::SetMoveDown ( s ) = > self . set_control ( StyleModifiers ::CONTROL_MOVEDOWN , s ) ,
PhysicsInputInstruction ::SetJump ( s ) = > {
2023-10-03 23:52:45 +00:00
self . set_control ( StyleModifiers ::CONTROL_JUMP , s ) ;
2023-11-30 09:51:17 +00:00
if let Some ( walk_state ) = get_walk_state ( & self . move_state ) {
let mut v = self . body . velocity ;
jumped_velocity ( & self . models , & self . style , walk_state , & mut v ) ;
if set_velocity_cull ( & mut self . body , & mut self . touching , & self . models , & self . style . mesh ( ) , v ) {
( self . move_state , self . body . acceleration ) = self . touching . get_move_state ( & self . body , & self . models , & self . style , & self . camera , self . controls , & self . next_mouse , self . time ) ;
}
}
2023-10-17 07:17:54 +00:00
refresh_walk_target = false ;
2023-09-20 00:53:29 +00:00
} ,
2023-10-05 03:04:04 +00:00
PhysicsInputInstruction ::SetZoom ( s ) = > {
2023-10-03 23:52:45 +00:00
self . set_control ( StyleModifiers ::CONTROL_ZOOM , s ) ;
2023-10-02 02:25:16 +00:00
refresh_walk_target = false ;
2023-09-20 00:53:29 +00:00
} ,
2023-10-05 03:04:04 +00:00
PhysicsInputInstruction ::Reset = > {
2023-11-30 09:51:17 +00:00
//it matters which of these runs first, but I have not thought it through yet as it doesn't matter yet
set_position ( & mut self . body , & mut self . touching , self . spawn_point ) ;
set_velocity ( & mut self . body , & self . touching , & self . models , & self . style . mesh ( ) , Planar64Vec3 ::ZERO ) ;
( self . move_state , self . body . acceleration ) = self . touching . get_move_state ( & self . body , & self . models , & self . style , & self . camera , self . controls , & self . next_mouse , self . time ) ;
2023-10-02 02:25:16 +00:00
refresh_walk_target = false ;
2023-09-20 00:53:29 +00:00
} ,
2023-10-05 03:04:04 +00:00
PhysicsInputInstruction ::Idle = > { refresh_walk_target = false ; } , //literally idle!
2023-09-20 00:53:29 +00:00
}
if refresh_walk_target {
2023-11-30 09:51:17 +00:00
let a = self . refresh_walk_target ( ) ;
if set_acceleration_cull ( & mut self . body , & mut self . touching , & self . models , & self . style . mesh ( ) , a ) {
( self . move_state , self . body . acceleration ) = self . touching . get_move_state ( & self . body , & self . models , & self . style , & self . camera , self . controls , & self . next_mouse , self . time ) ;
2023-10-20 20:47:26 +00:00
}
2023-09-19 04:10:07 +00:00
}
2023-09-19 06:36:14 +00:00
} ,
2023-09-09 03:14:18 +00:00
}
2023-09-09 00:15:49 +00:00
}
2023-09-23 02:41:27 +00:00
}
2023-11-30 09:51:17 +00:00
#[ allow(dead_code) ]
fn test_collision_axis_aligned ( relative_body :Body , expected_collision_time :Option < Time > ) {
2024-02-02 06:14:58 +00:00
let h0 = gameplay_style ::Hitbox ::from_mesh_scale ( PhysicsMesh ::from ( & crate ::primitives ::unit_cube ( ) ) , Planar64Vec3 ::int ( 5 , 1 , 5 ) / 2 ) ;
let h1 = gameplay_style ::Hitbox ::roblox ( ) ;
2023-11-30 09:51:17 +00:00
let hitbox_mesh = h1 . transformed_mesh ( ) ;
let platform_mesh = h0 . transformed_mesh ( ) ;
2024-02-02 06:14:58 +00:00
let minkowski = model_physics ::MinkowskiMesh ::minkowski_sum ( & platform_mesh , & hitbox_mesh ) ;
2023-11-30 09:51:17 +00:00
let collision = minkowski . predict_collision_in ( & relative_body , Time ::MAX ) ;
assert_eq! ( collision . map ( | tup | tup . 1 ) , expected_collision_time , " Incorrect time of collision " ) ;
}
#[ allow(dead_code) ]
fn test_collision_rotated ( relative_body :Body , expected_collision_time :Option < Time > ) {
2024-02-02 06:14:58 +00:00
let h0 = gameplay_style ::Hitbox ::new ( PhysicsMesh ::from ( & crate ::primitives ::unit_cube ( ) ) ,
2024-01-30 00:19:41 +00:00
integer ::Planar64Affine3 ::new (
integer ::Planar64Mat3 ::from_cols (
2023-11-30 09:51:17 +00:00
Planar64Vec3 ::int ( 5 , 0 , 1 ) / 2 ,
Planar64Vec3 ::int ( 0 , 1 , 0 ) / 2 ,
Planar64Vec3 ::int ( - 1 , 0 , 5 ) / 2 ,
) ,
Planar64Vec3 ::ZERO ,
)
) ;
2024-02-02 06:14:58 +00:00
let h1 = gameplay_style ::Hitbox ::roblox ( ) ;
2023-11-30 09:51:17 +00:00
let hitbox_mesh = h1 . transformed_mesh ( ) ;
let platform_mesh = h0 . transformed_mesh ( ) ;
2024-02-02 06:14:58 +00:00
let minkowski = model_physics ::MinkowskiMesh ::minkowski_sum ( & platform_mesh , & hitbox_mesh ) ;
2023-11-30 09:51:17 +00:00
let collision = minkowski . predict_collision_in ( & relative_body , Time ::MAX ) ;
assert_eq! ( collision . map ( | tup | tup . 1 ) , expected_collision_time , " Incorrect time of collision " ) ;
}
#[ allow(dead_code) ]
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 ,
Time ::ZERO
) , Some ( Time ::from_secs ( 2 ) ) ) ;
}
#[ test ]
fn test_collision_degenerate_east ( ) {
test_collision ( Body ::new (
Planar64Vec3 ::int ( 3 , 5 , 0 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Planar64Vec3 ::ZERO ,
Time ::ZERO
) , Some ( Time ::from_secs ( 2 ) ) ) ;
}
#[ test ]
fn test_collision_degenerate_south ( ) {
test_collision ( Body ::new (
Planar64Vec3 ::int ( 0 , 5 , 3 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Planar64Vec3 ::ZERO ,
Time ::ZERO
) , Some ( Time ::from_secs ( 2 ) ) ) ;
}
#[ test ]
fn test_collision_degenerate_west ( ) {
test_collision ( Body ::new (
Planar64Vec3 ::int ( - 3 , 5 , 0 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Planar64Vec3 ::ZERO ,
Time ::ZERO
) , Some ( Time ::from_secs ( 2 ) ) ) ;
}
#[ test ]
fn test_collision_degenerate_north ( ) {
test_collision ( Body ::new (
Planar64Vec3 ::int ( 0 , 5 , - 3 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Planar64Vec3 ::ZERO ,
Time ::ZERO
) , Some ( Time ::from_secs ( 2 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_east_from_west ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( 3 , 3 , 0 ) ,
Planar64Vec3 ::int ( 100 , - 1 , 0 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_south_from_north ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( 0 , 3 , 3 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 100 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_west_from_east ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( - 3 , 3 , 0 ) ,
Planar64Vec3 ::int ( - 100 , - 1 , 0 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_north_from_south ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( 0 , 3 , - 3 ) ,
Planar64Vec3 ::int ( 0 , - 1 , - 100 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_north_from_ne ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( 0 , 6 , - 7 ) / 2 ,
Planar64Vec3 ::int ( - 10 , - 1 , 1 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_north_from_nw ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( 0 , 6 , - 7 ) / 2 ,
Planar64Vec3 ::int ( 10 , - 1 , 1 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_east_from_se ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( 7 , 6 , 0 ) / 2 ,
Planar64Vec3 ::int ( - 1 , - 1 , - 10 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_east_from_ne ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( 7 , 6 , 0 ) / 2 ,
Planar64Vec3 ::int ( - 1 , - 1 , 10 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_south_from_se ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( 0 , 6 , 7 ) / 2 ,
Planar64Vec3 ::int ( - 10 , - 1 , - 1 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_south_from_sw ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( 0 , 6 , 7 ) / 2 ,
Planar64Vec3 ::int ( 10 , - 1 , - 1 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_west_from_se ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( - 7 , 6 , 0 ) / 2 ,
Planar64Vec3 ::int ( 1 , - 1 , - 10 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_parabola_edge_west_from_ne ( ) {
test_collision ( VirtualBody ::relative ( & Body ::default ( ) , & Body ::new (
Planar64Vec3 ::int ( - 7 , 6 , 0 ) / 2 ,
Planar64Vec3 ::int ( 1 , - 1 , 10 ) ,
Planar64Vec3 ::int ( 0 , - 1 , 0 ) ,
Time ::ZERO
) ) . body ( Time ::from_secs ( - 1 ) ) , Some ( Time ::from_secs ( 0 ) ) ) ;
}
#[ test ]
fn test_collision_oblique ( ) {
test_collision ( Body ::new (
Planar64Vec3 ::int ( 0 , 5 , 0 ) ,
Planar64Vec3 ::int ( 1 , - 64 , 2 ) / 64 ,
Planar64Vec3 ::ZERO ,
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 ) ;
}