forked from StrafesNET/strafe-client
wip: physics work
This commit is contained in:
parent
89f7a2b9b9
commit
053bab9e24
@ -109,10 +109,10 @@ pub fn predict_collision<F:Copy,E:Copy,V:Copy>(mesh:&impl MeshQuery<F,E,V>,relat
|
||||
}
|
||||
}
|
||||
|
||||
pub fn predict_collision_end<F:Copy,E:Copy,V:Copy>(mesh:&impl MeshQuery<F,E,V>,relative_body:&Body,time_limit:Time,c:&crate::physics::RelativeCollision)->Option<(F,Time)>{
|
||||
pub fn predict_collision_end<F:Copy,E:Copy,V:Copy>(mesh:&impl MeshQuery<F,E,V>,relative_body:&Body,time_limit:Time,c:&crate::physics::ContactCollision)->Option<(F,Time)>{
|
||||
//imagine the mesh without the collision face
|
||||
//no algorithm needed, there is only one state and three cases (Face,Edge,None)
|
||||
//determine when it passes an edge ("sliding off" case) or if it leaves the surface directly
|
||||
//the state can be constructed from the RelativeCollision directly
|
||||
//the state can be constructed from the ContactCollision directly
|
||||
None
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
use crate::integer::{Planar64,Planar64Vec3};
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||
pub struct VertId(usize);
|
||||
#[derive(Clone,Copy)]
|
||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||
pub struct EdgeId(usize);
|
||||
#[derive(Clone,Copy)]
|
||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||
pub struct FaceId(usize);
|
||||
|
||||
//Vertex <-> Edge <-> Face -> Collide
|
||||
@ -115,6 +115,42 @@ impl MeshQuery<FaceId,EdgeId,VertId> for PhysicsMesh{
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VirtualMesh<'a>{
|
||||
mesh:&'a PhysicsMesh,
|
||||
transform:&'a crate::integer::Planar64Affine3,
|
||||
normal_transform:&'a crate::integer::Planar64Mat3,
|
||||
normal_determinant:Planar64,
|
||||
}
|
||||
impl MeshQuery<FaceId,EdgeId,VertId> for VirtualMesh<'_>{
|
||||
fn closest_fev(&self,point:Planar64Vec3)->FEV<FaceId,EdgeId,VertId>{
|
||||
//put some genius code right here
|
||||
todo!()
|
||||
}
|
||||
fn face_nd(&self,face_id:FaceId)->(Planar64Vec3,Planar64){
|
||||
let (n,d)=self.mesh.face_nd(face_id);
|
||||
(self.normal_transform*n,self.normal_determinant*d)
|
||||
}
|
||||
fn vert(&self,vert_id:VertId)->Planar64Vec3{
|
||||
self.transform.transform_point3(self.mesh.vert(vert_id))
|
||||
}
|
||||
#[inline]
|
||||
fn face_edges(&self,face_id:FaceId)->Cow<Vec<(EdgeId,FaceId)>>{
|
||||
self.mesh.face_edges(face_id)
|
||||
}
|
||||
#[inline]
|
||||
fn edge_faces(&self,edge_id:EdgeId)->Cow<[FaceId;2]>{
|
||||
self.mesh.edge_faces(edge_id)
|
||||
}
|
||||
#[inline]
|
||||
fn edge_verts(&self,edge_id:EdgeId)->Cow<[(VertId,FaceId);2]>{
|
||||
self.mesh.edge_verts(edge_id)
|
||||
}
|
||||
#[inline]
|
||||
fn vert_edges(&self,vert_id:VertId)->Cow<Vec<(EdgeId,FaceId)>>{
|
||||
self.mesh.vert_edges(vert_id)
|
||||
}
|
||||
}
|
||||
|
||||
//Note that a face on a minkowski mesh refers to a pair of fevs on the meshes it's summed from
|
||||
//(face,vertex)
|
||||
//(edge,edge)
|
||||
@ -128,20 +164,20 @@ enum MinkowskiEdge{
|
||||
VertEdge(VertId,EdgeId),
|
||||
EdgeVert(EdgeId,VertId),
|
||||
}
|
||||
#[derive(Clone,Copy)]
|
||||
enum MinkowskiFace{
|
||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||
pub enum MinkowskiFace{
|
||||
FaceVert(FaceId,VertId),
|
||||
EdgeEdge(EdgeId,EdgeId),
|
||||
VertFace(VertId,FaceId),
|
||||
}
|
||||
|
||||
pub struct MinkowskiMesh<'a>{
|
||||
mesh0:&'a PhysicsMesh,
|
||||
mesh1:&'a PhysicsMesh,
|
||||
mesh0:&'a VirtualMesh<'a>,
|
||||
mesh1:&'a VirtualMesh<'a>,
|
||||
}
|
||||
|
||||
impl MinkowskiMesh<'_>{
|
||||
pub fn minkowski_sum<'a>(mesh0:&'a PhysicsMesh,mesh1:&'a PhysicsMesh)->MinkowskiMesh<'a>{
|
||||
pub fn minkowski_sum<'a>(mesh0:&'a VirtualMesh,mesh1:&'a VirtualMesh)->MinkowskiMesh<'a>{
|
||||
MinkowskiMesh{
|
||||
mesh0,
|
||||
mesh1,
|
||||
|
169
src/physics.rs
169
src/physics.rs
@ -1,11 +1,12 @@
|
||||
use crate::{instruction::{InstructionEmitter, InstructionConsumer, TimedInstruction}, zeroes::zeroes2};
|
||||
|
||||
use crate::zeroes::zeroes2;
|
||||
use crate::instruction::{InstructionEmitter,InstructionConsumer,TimedInstruction};
|
||||
use crate::integer::{Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64,Ratio64Vec2};
|
||||
use crate::model_physics::{PhysicsMesh,VirtualMesh,MinkowskiMesh};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PhysicsInstruction {
|
||||
CollisionStart(RelativeCollision),
|
||||
CollisionEnd(RelativeCollision),
|
||||
CollisionStart(Collision),
|
||||
CollisionEnd(Collision),
|
||||
StrafeTick,
|
||||
ReachWalkTargetVelocity,
|
||||
// Water,
|
||||
@ -154,6 +155,7 @@ impl Default for Modes{
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PhysicsModels{
|
||||
models:Vec<PhysicsModel>,
|
||||
model_id_from_wormhole_id:std::collections::HashMap::<u32,usize>,
|
||||
@ -163,8 +165,8 @@ impl PhysicsModels{
|
||||
self.models.clear();
|
||||
self.model_id_from_wormhole_id.clear();
|
||||
}
|
||||
fn get(&self,i:usize)->Option<&PhysicsModel>{
|
||||
self.models.get(i)
|
||||
fn get(&self,model_id:usize)->Option<&PhysicsModel>{
|
||||
self.models.get(model_id)
|
||||
}
|
||||
fn get_wormhole_model(&self,wormhole_id:u32)->Option<&PhysicsModel>{
|
||||
self.models.get(*self.model_id_from_wormhole_id.get(&wormhole_id)?)
|
||||
@ -175,14 +177,6 @@ impl PhysicsModels{
|
||||
model_id
|
||||
}
|
||||
}
|
||||
impl Default for PhysicsModels{
|
||||
fn default() -> Self {
|
||||
Self{
|
||||
models:Vec::new(),
|
||||
model_id_from_wormhole_id:std::collections::HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PhysicsCamera{
|
||||
@ -592,7 +586,7 @@ pub struct PhysicsState{
|
||||
pub next_mouse:MouseState,//Where is the mouse headed next
|
||||
controls:u32,
|
||||
move_state:MoveState,
|
||||
//all models
|
||||
meshes:Vec<PhysicsMesh>,
|
||||
models:PhysicsModels,
|
||||
bvh:crate::bvh::BvhNode,
|
||||
|
||||
@ -613,10 +607,6 @@ impl PhysicsOutputState{
|
||||
}
|
||||
}
|
||||
|
||||
//pretend to be using what we want to eventually do
|
||||
type TreyMeshFace = crate::aabb::AabbFace;
|
||||
type TreyMesh = crate::aabb::Aabb;
|
||||
|
||||
enum PhysicsCollisionAttributes{
|
||||
Contact{//track whether you are contacting the object
|
||||
contacting:crate::model::ContactingAttributes,
|
||||
@ -631,110 +621,91 @@ enum PhysicsCollisionAttributes{
|
||||
pub struct PhysicsModel{
|
||||
//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.
|
||||
mesh: TreyMesh,
|
||||
transform:crate::integer::Planar64Affine3,
|
||||
mesh_id:usize,
|
||||
attributes:PhysicsCollisionAttributes,
|
||||
transform:crate::integer::Planar64Affine3,
|
||||
normal_transform:crate::integer::Planar64Mat3,
|
||||
}
|
||||
|
||||
impl PhysicsModel{
|
||||
fn from_model_transform_attributes(model:&crate::model::IndexedModel,transform:&crate::integer::Planar64Affine3,attributes:PhysicsCollisionAttributes)->Self{
|
||||
let mut aabb=TreyMesh::default();
|
||||
for indexed_vertex in &model.unique_vertices {
|
||||
aabb.grow(transform.transform_point3(model.unique_pos[indexed_vertex.pos as usize]));
|
||||
}
|
||||
fn new(mesh_id:usize,transform:crate::integer::Planar64Affine3,attributes:PhysicsCollisionAttributes)->Self{
|
||||
Self{
|
||||
mesh:aabb,
|
||||
mesh_id,
|
||||
attributes,
|
||||
transform:transform.clone(),
|
||||
transform,
|
||||
normal_transform:transform.matrix3.inverse().transpose(),
|
||||
}
|
||||
}
|
||||
pub fn from_model(model:&crate::model::IndexedModel,instance:&crate::model::ModelInstance) -> Option<Self> {
|
||||
pub fn from_model(mesh_id:usize,instance:&crate::model::ModelInstance) -> Option<Self> {
|
||||
match &instance.attributes{
|
||||
crate::model::CollisionAttributes::Contact{contacting,general}=>Some(PhysicsModel::from_model_transform_attributes(model,&instance.transform,PhysicsCollisionAttributes::Contact{contacting:contacting.clone(),general:general.clone()})),
|
||||
crate::model::CollisionAttributes::Intersect{intersecting,general}=>Some(PhysicsModel::from_model_transform_attributes(model,&instance.transform,PhysicsCollisionAttributes::Intersect{intersecting:intersecting.clone(),general:general.clone()})),
|
||||
crate::model::CollisionAttributes::Contact{contacting,general}=>Some(PhysicsModel::new(mesh_id,instance.transform.clone(),PhysicsCollisionAttributes::Contact{contacting:contacting.clone(),general:general.clone()})),
|
||||
crate::model::CollisionAttributes::Intersect{intersecting,general}=>Some(PhysicsModel::new(mesh_id,instance.transform.clone(),PhysicsCollisionAttributes::Intersect{intersecting:intersecting.clone(),general:general.clone()})),
|
||||
crate::model::CollisionAttributes::Decoration=>None,
|
||||
}
|
||||
}
|
||||
pub fn unit_vertices(&self) -> [Planar64Vec3;8] {
|
||||
TreyMesh::unit_vertices()
|
||||
pub fn mesh<'a>(&self,meshes:&Vec<PhysicsMesh>)->VirtualMesh<'a>{
|
||||
VirtualMesh{
|
||||
mesh:&meshes[self.mesh_id],
|
||||
transform:&self.transform,
|
||||
normal_transform:&self.normal_transform,
|
||||
normal_determinant:self.normal_transform.determinant(),
|
||||
}
|
||||
pub fn mesh(&self) -> &TreyMesh {
|
||||
return &self.mesh;
|
||||
}
|
||||
// pub fn face_mesh(&self,face:TreyMeshFace)->TreyMesh{
|
||||
// self.mesh.face(face)
|
||||
// }
|
||||
pub fn face_normal(&self,face:TreyMeshFace) -> Planar64Vec3 {
|
||||
TreyMesh::normal(face)//this is wrong for scale
|
||||
}
|
||||
}
|
||||
|
||||
//need non-face (full model) variant for CanCollide false objects
|
||||
//OR have a separate list from contacts for model intersection
|
||||
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
|
||||
pub struct RelativeCollision {
|
||||
face:TreyMeshFace,//just an id
|
||||
model:usize,//using id to avoid lifetimes
|
||||
struct ContactCollision{
|
||||
face_id:crate::model_physics::MinkowskiFace,
|
||||
model_id:usize,//using id to avoid lifetimes
|
||||
}
|
||||
|
||||
impl RelativeCollision {
|
||||
fn model<'a>(&self,models:&'a PhysicsModels)->Option<&'a PhysicsModel>{
|
||||
models.get(self.model)
|
||||
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
|
||||
struct IntersectCollision{
|
||||
model_id:usize,
|
||||
}
|
||||
// pub fn mesh(&self,models:&Vec<PhysicsModel>) -> TreyMesh {
|
||||
// return self.model(models).unwrap().face_mesh(self.face).clone()
|
||||
// }
|
||||
fn normal(&self,models:&PhysicsModels) -> Planar64Vec3 {
|
||||
return self.model(models).unwrap().face_normal(self.face)
|
||||
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
|
||||
enum Collision{
|
||||
Contact(ContactCollision),
|
||||
Intersect(IntersectCollision),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TouchingState{
|
||||
contacts:std::collections::HashMap::<usize,RelativeCollision>,
|
||||
intersects:std::collections::HashMap::<usize,RelativeCollision>,
|
||||
contacts:std::collections::HashSet::<ContactCollision>,
|
||||
intersects:std::collections::HashSet::<IntersectCollision>,
|
||||
}
|
||||
impl TouchingState{
|
||||
fn clear(&mut self){
|
||||
self.contacts.clear();
|
||||
self.intersects.clear();
|
||||
}
|
||||
fn insert_contact(&mut self,model_id:usize,collision:RelativeCollision)->Option<RelativeCollision>{
|
||||
self.contacts.insert(model_id,collision)
|
||||
}
|
||||
fn remove_contact(&mut self,model_id:usize)->Option<RelativeCollision>{
|
||||
self.contacts.remove(&model_id)
|
||||
}
|
||||
fn insert_intersect(&mut self,model_id:usize,collision:RelativeCollision)->Option<RelativeCollision>{
|
||||
self.intersects.insert(model_id,collision)
|
||||
}
|
||||
fn remove_intersect(&mut self,model_id:usize)->Option<RelativeCollision>{
|
||||
self.intersects.remove(&model_id)
|
||||
}
|
||||
fn constrain_velocity(&self,models:&PhysicsModels,velocity:&mut Planar64Vec3){
|
||||
for (_,contact) in &self.contacts {
|
||||
let n=contact.normal(models);
|
||||
let d=velocity.dot(n);
|
||||
if d<Planar64::ZERO{
|
||||
(*velocity)-=n*(d/n.dot(n));
|
||||
fn insert(&mut self,collision:Collision)->bool{
|
||||
match collision{
|
||||
Collision::Contact(collision)=>self.contacts.insert(collision),
|
||||
Collision::Intersect(collision)=>self.intersects.insert(collision),
|
||||
}
|
||||
}
|
||||
fn remove(&mut self,collision:&Collision)->bool{
|
||||
match collision{
|
||||
Collision::Contact(collision)=>self.contacts.remove(collision),
|
||||
Collision::Intersect(collision)=>self.intersects.remove(collision),
|
||||
}
|
||||
}
|
||||
fn get_acceleration(&self,gravity:Planar64Vec3)->Planar64Vec3{
|
||||
//accelerators
|
||||
//water
|
||||
//contact constrain?
|
||||
todo!()
|
||||
}
|
||||
fn constrain_velocity(&self,meshes:&Vec<PhysicsModel>,models:&PhysicsModels,velocity:&mut Planar64Vec3){
|
||||
for contact in &self.contacts{
|
||||
//trey push solve
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
fn constrain_acceleration(&self,models:&PhysicsModels,acceleration:&mut Planar64Vec3){
|
||||
for (_,contact) in &self.contacts {
|
||||
let n=contact.normal(models);
|
||||
let d=acceleration.dot(n);
|
||||
if d<Planar64::ZERO{
|
||||
(*acceleration)-=n*(d/n.dot(n));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Default for TouchingState{
|
||||
fn default() -> Self {
|
||||
Self{
|
||||
contacts: std::collections::HashMap::new(),
|
||||
intersects: std::collections::HashMap::new(),
|
||||
for contact in &self.contacts{
|
||||
//trey push solve
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@ -803,6 +774,7 @@ impl Default for PhysicsState{
|
||||
style:StyleModifiers::default(),
|
||||
touching:TouchingState::default(),
|
||||
models:PhysicsModels::default(),
|
||||
meshes:Vec::new(),
|
||||
bvh:crate::bvh::BvhNode::default(),
|
||||
move_state: MoveState::Air,
|
||||
camera:PhysicsCamera::default(),
|
||||
@ -844,9 +816,11 @@ impl PhysicsState {
|
||||
let mut starts=Vec::new();
|
||||
let mut spawns=Vec::new();
|
||||
for model in &indexed_models.models{
|
||||
//make aabb and run vertices to get realistic bounds
|
||||
let mesh_id=self.meshes.len();
|
||||
let mut make_mesh=false;
|
||||
for model_instance in &model.instances{
|
||||
if let Some(model_physics)=PhysicsModel::from_model(model,model_instance){
|
||||
if let Some(model_physics)=PhysicsModel::from_model(mesh_id,model_instance){
|
||||
make_mesh=true;
|
||||
let model_id=self.models.push(model_physics);
|
||||
for attr in &model_instance.temp_indexing{
|
||||
match attr{
|
||||
@ -857,8 +831,11 @@ impl PhysicsState {
|
||||
}
|
||||
}
|
||||
}
|
||||
if make_mesh{
|
||||
self.meshes.push(PhysicsMesh::from_model(model));
|
||||
}
|
||||
self.bvh=crate::bvh::generate_bvh(self.models.models.iter().map(|m|m.mesh().clone()).collect());
|
||||
}
|
||||
self.bvh=crate::bvh::generate_bvh(self.models.aabb_list(&self.meshes));
|
||||
//I don't wanna write structs for temporary structures
|
||||
//this code builds ModeDescriptions from the unsorted lists at the top of the function
|
||||
starts.sort_by_key(|tup|tup.1.mode_id);
|
||||
@ -1000,7 +977,7 @@ impl PhysicsState {
|
||||
}
|
||||
aabb
|
||||
}
|
||||
fn predict_collision_end(&self,time:Time,time_limit:Time,collision_data:&RelativeCollision) -> Option<TimedInstruction<PhysicsInstruction>> {
|
||||
fn predict_collision_end(&self,time:Time,time_limit:Time,collision_data:&ContactCollision) -> Option<TimedInstruction<PhysicsInstruction>> {
|
||||
//must treat cancollide false objects differently: you may not exit through the same face you entered.
|
||||
//RelativeCollsion must reference the full model instead of a particular face
|
||||
//this is Ctrl+C Ctrl+V of predict_collision_start but with v=-v before the calc and t=-t after the calc
|
||||
@ -1264,7 +1241,7 @@ impl PhysicsState {
|
||||
if let Some(face)=best_face{
|
||||
return Some(TimedInstruction{
|
||||
time: best_time,
|
||||
instruction:PhysicsInstruction::CollisionStart(RelativeCollision{
|
||||
instruction:PhysicsInstruction::CollisionStart(ContactCollision{
|
||||
face,
|
||||
model:model_id
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user