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
//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
}

View File

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

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::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,
@ -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
//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]));
}
impl PhysicsModel{
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)
}
// 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)]
struct IntersectCollision{
model_id:usize,
}
#[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
})