wip: physics work

This commit is contained in:
Quaternions 2023-10-30 22:11:54 -07:00
parent 89f7a2b9b9
commit 053bab9e24
3 changed files with 124 additions and 111 deletions

View File

@ -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 //imagine the mesh without the collision face
//no algorithm needed, there is only one state and three cases (Face,Edge,None) //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 //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 None
} }

View File

@ -1,11 +1,11 @@
use crate::integer::{Planar64,Planar64Vec3}; use crate::integer::{Planar64,Planar64Vec3};
use std::borrow::Cow; use std::borrow::Cow;
#[derive(Clone,Copy)] #[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub struct VertId(usize); pub struct VertId(usize);
#[derive(Clone,Copy)] #[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub struct EdgeId(usize); pub struct EdgeId(usize);
#[derive(Clone,Copy)] #[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub struct FaceId(usize); pub struct FaceId(usize);
//Vertex <-> Edge <-> Face -> Collide //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 //Note that a face on a minkowski mesh refers to a pair of fevs on the meshes it's summed from
//(face,vertex) //(face,vertex)
//(edge,edge) //(edge,edge)
@ -128,20 +164,20 @@ enum MinkowskiEdge{
VertEdge(VertId,EdgeId), VertEdge(VertId,EdgeId),
EdgeVert(EdgeId,VertId), EdgeVert(EdgeId,VertId),
} }
#[derive(Clone,Copy)] #[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
enum MinkowskiFace{ pub enum MinkowskiFace{
FaceVert(FaceId,VertId), FaceVert(FaceId,VertId),
EdgeEdge(EdgeId,EdgeId), EdgeEdge(EdgeId,EdgeId),
VertFace(VertId,FaceId), VertFace(VertId,FaceId),
} }
pub struct MinkowskiMesh<'a>{ pub struct MinkowskiMesh<'a>{
mesh0:&'a PhysicsMesh, mesh0:&'a VirtualMesh<'a>,
mesh1:&'a PhysicsMesh, mesh1:&'a VirtualMesh<'a>,
} }
impl MinkowskiMesh<'_>{ 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{ MinkowskiMesh{
mesh0, mesh0,
mesh1, mesh1,

View File

@ -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::integer::{Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64,Ratio64Vec2};
use crate::model_physics::{PhysicsMesh,VirtualMesh,MinkowskiMesh};
#[derive(Debug)] #[derive(Debug)]
pub enum PhysicsInstruction { pub enum PhysicsInstruction {
CollisionStart(RelativeCollision), CollisionStart(Collision),
CollisionEnd(RelativeCollision), CollisionEnd(Collision),
StrafeTick, StrafeTick,
ReachWalkTargetVelocity, ReachWalkTargetVelocity,
// Water, // Water,
@ -154,6 +155,7 @@ impl Default for Modes{
} }
} }
#[derive(Default)]
struct PhysicsModels{ struct PhysicsModels{
models:Vec<PhysicsModel>, models:Vec<PhysicsModel>,
model_id_from_wormhole_id:std::collections::HashMap::<u32,usize>, model_id_from_wormhole_id:std::collections::HashMap::<u32,usize>,
@ -163,8 +165,8 @@ impl PhysicsModels{
self.models.clear(); self.models.clear();
self.model_id_from_wormhole_id.clear(); self.model_id_from_wormhole_id.clear();
} }
fn get(&self,i:usize)->Option<&PhysicsModel>{ fn get(&self,model_id:usize)->Option<&PhysicsModel>{
self.models.get(i) self.models.get(model_id)
} }
fn get_wormhole_model(&self,wormhole_id:u32)->Option<&PhysicsModel>{ fn get_wormhole_model(&self,wormhole_id:u32)->Option<&PhysicsModel>{
self.models.get(*self.model_id_from_wormhole_id.get(&wormhole_id)?) self.models.get(*self.model_id_from_wormhole_id.get(&wormhole_id)?)
@ -175,14 +177,6 @@ impl PhysicsModels{
model_id model_id
} }
} }
impl Default for PhysicsModels{
fn default() -> Self {
Self{
models:Vec::new(),
model_id_from_wormhole_id:std::collections::HashMap::new(),
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct PhysicsCamera{ pub struct PhysicsCamera{
@ -592,7 +586,7 @@ pub struct PhysicsState{
pub next_mouse:MouseState,//Where is the mouse headed next pub next_mouse:MouseState,//Where is the mouse headed next
controls:u32, controls:u32,
move_state:MoveState, move_state:MoveState,
//all models meshes:Vec<PhysicsMesh>,
models:PhysicsModels, models:PhysicsModels,
bvh:crate::bvh::BvhNode, 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{ enum PhysicsCollisionAttributes{
Contact{//track whether you are contacting the object Contact{//track whether you are contacting the object
contacting:crate::model::ContactingAttributes, contacting:crate::model::ContactingAttributes,
@ -628,113 +618,94 @@ enum PhysicsCollisionAttributes{
}, },
} }
pub struct PhysicsModel { pub struct PhysicsModel{
//A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es //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. //in this iteration, all it needs is extents.
mesh: TreyMesh, mesh_id:usize,
transform:crate::integer::Planar64Affine3,
attributes:PhysicsCollisionAttributes, attributes:PhysicsCollisionAttributes,
transform:crate::integer::Planar64Affine3,
normal_transform:crate::integer::Planar64Mat3,
} }
impl PhysicsModel { impl PhysicsModel{
fn from_model_transform_attributes(model:&crate::model::IndexedModel,transform:&crate::integer::Planar64Affine3,attributes:PhysicsCollisionAttributes)->Self{ fn new(mesh_id:usize,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]));
}
Self{ Self{
mesh:aabb, mesh_id,
attributes, 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{ 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::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::from_model_transform_attributes(model,&instance.transform,PhysicsCollisionAttributes::Intersect{intersecting:intersecting.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, crate::model::CollisionAttributes::Decoration=>None,
} }
} }
pub fn unit_vertices(&self) -> [Planar64Vec3;8] { pub fn mesh<'a>(&self,meshes:&Vec<PhysicsMesh>)->VirtualMesh<'a>{
TreyMesh::unit_vertices() 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)] #[derive(Debug,Clone,Eq,Hash,PartialEq)]
pub struct RelativeCollision { struct ContactCollision{
face:TreyMeshFace,//just an id face_id:crate::model_physics::MinkowskiFace,
model:usize,//using id to avoid lifetimes model_id:usize,//using id to avoid lifetimes
} }
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
impl RelativeCollision { struct IntersectCollision{
fn model<'a>(&self,models:&'a PhysicsModels)->Option<&'a PhysicsModel>{ model_id:usize,
models.get(self.model)
}
// 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{ struct TouchingState{
contacts:std::collections::HashMap::<usize,RelativeCollision>, contacts:std::collections::HashSet::<ContactCollision>,
intersects:std::collections::HashMap::<usize,RelativeCollision>, intersects:std::collections::HashSet::<IntersectCollision>,
} }
impl TouchingState{ impl TouchingState{
fn clear(&mut self){ fn clear(&mut self){
self.contacts.clear(); self.contacts.clear();
self.intersects.clear(); self.intersects.clear();
} }
fn insert_contact(&mut self,model_id:usize,collision:RelativeCollision)->Option<RelativeCollision>{ fn insert(&mut self,collision:Collision)->bool{
self.contacts.insert(model_id,collision) match collision{
} Collision::Contact(collision)=>self.contacts.insert(collision),
fn remove_contact(&mut self,model_id:usize)->Option<RelativeCollision>{ Collision::Intersect(collision)=>self.intersects.insert(collision),
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 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){ fn constrain_acceleration(&self,models:&PhysicsModels,acceleration:&mut Planar64Vec3){
for (_,contact) in &self.contacts { for contact in &self.contacts{
let n=contact.normal(models); //trey push solve
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(),
} }
todo!()
} }
} }
@ -803,6 +774,7 @@ impl Default for PhysicsState{
style:StyleModifiers::default(), style:StyleModifiers::default(),
touching:TouchingState::default(), touching:TouchingState::default(),
models:PhysicsModels::default(), models:PhysicsModels::default(),
meshes:Vec::new(),
bvh:crate::bvh::BvhNode::default(), bvh:crate::bvh::BvhNode::default(),
move_state: MoveState::Air, move_state: MoveState::Air,
camera:PhysicsCamera::default(), camera:PhysicsCamera::default(),
@ -844,9 +816,11 @@ impl PhysicsState {
let mut starts=Vec::new(); let mut starts=Vec::new();
let mut spawns=Vec::new(); let mut spawns=Vec::new();
for model in &indexed_models.models{ 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{ 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); let model_id=self.models.push(model_physics);
for attr in &model_instance.temp_indexing{ for attr in &model_instance.temp_indexing{
match attr{ 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 //I don't wanna write structs for temporary structures
//this code builds ModeDescriptions from the unsorted lists at the top of the function //this code builds ModeDescriptions from the unsorted lists at the top of the function
starts.sort_by_key(|tup|tup.1.mode_id); starts.sort_by_key(|tup|tup.1.mode_id);
@ -1000,7 +977,7 @@ impl PhysicsState {
} }
aabb 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. //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 //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 //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{ if let Some(face)=best_face{
return Some(TimedInstruction{ return Some(TimedInstruction{
time: best_time, time: best_time,
instruction:PhysicsInstruction::CollisionStart(RelativeCollision{ instruction:PhysicsInstruction::CollisionStart(ContactCollision{
face, face,
model:model_id model:model_id
}) })