Compare commits
12 Commits
master
...
physics-bu
Author | SHA1 | Date | |
---|---|---|---|
af86be029b | |||
7f20e73588 | |||
9308cc8a5c | |||
2f574b297f | |||
7c306da7b0 | |||
6e9d38604e | |||
a52d46b7cc | |||
9ecb494748 | |||
1f73a8b5c6 | |||
dbae80b1d2 | |||
8cc79304dc | |||
8e4792269d |
engine
integration-testing/src
lib
@ -11,6 +11,15 @@ pub fn required_limits()->wgpu::Limits{
|
|||||||
wgpu::Limits::default()
|
wgpu::Limits::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// physics debug view:
|
||||||
|
// render two meshes adjacent according to Minkowski FEV
|
||||||
|
// render additional mesh at path point
|
||||||
|
// render path parabola
|
||||||
|
// highlight meshes according to Minkowski FEV
|
||||||
|
//
|
||||||
|
// debug process:
|
||||||
|
// press a button to step physics by one transition
|
||||||
|
|
||||||
struct Indices{
|
struct Indices{
|
||||||
count:u32,
|
count:u32,
|
||||||
buf:wgpu::Buffer,
|
buf:wgpu::Buffer,
|
||||||
|
@ -2,6 +2,7 @@ use crate::model::{GigaTime,FEV,MeshQuery,DirectedEdge};
|
|||||||
use strafesnet_common::integer::{Fixed,Ratio,vec3::Vector3};
|
use strafesnet_common::integer::{Fixed,Ratio,vec3::Vector3};
|
||||||
use crate::physics::{Time,Body};
|
use crate::physics::{Time,Body};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum Transition<M:MeshQuery>{
|
enum Transition<M:MeshQuery>{
|
||||||
Miss,
|
Miss,
|
||||||
Next(FEV<M>,GigaTime),
|
Next(FEV<M>,GigaTime),
|
||||||
@ -27,17 +28,18 @@ impl<M:MeshQuery> CrawlResult<M>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>+std::fmt::Debug> FEV<M>
|
||||||
where
|
where
|
||||||
// This is hardcoded for MinkowskiMesh lol
|
// This is hardcoded for MinkowskiMesh lol
|
||||||
M::Face:Copy,
|
M::Face:Copy,
|
||||||
M::Edge:Copy,
|
M::Edge:Copy,
|
||||||
M::Vert:Copy,
|
M::Vert:Copy,
|
||||||
F:core::ops::Mul<Fixed<1,32>,Output=Fixed<4,128>>,
|
F:core::ops::Mul<Fixed<1,32>,Output=Fixed<4,128>>+std::fmt::Debug+std::fmt::Display,
|
||||||
<F as core::ops::Mul<Fixed<1,32>>>::Output:core::iter::Sum,
|
<F as core::ops::Mul<Fixed<1,32>>>::Output:core::iter::Sum,
|
||||||
<M as MeshQuery>::Offset:core::ops::Sub<<F as std::ops::Mul<Fixed<1,32>>>::Output>,
|
<M as MeshQuery>::Offset:core::ops::Sub<<F as std::ops::Mul<Fixed<1,32>>>::Output>,
|
||||||
{
|
{
|
||||||
fn next_transition(&self,body_time:GigaTime,mesh:&M,body:&Body,mut best_time:GigaTime)->Transition<M>{
|
fn next_transition(&self,body_time:GigaTime,mesh:&M,body:&Body,mut best_time:GigaTime)->Transition<M>{
|
||||||
|
println!("next_transition fev={self:?}");
|
||||||
//conflicting derivative means it crosses in the wrong direction.
|
//conflicting derivative means it crosses in the wrong direction.
|
||||||
//if the transition time is equal to an already tested transition, do not replace the current best.
|
//if the transition time is equal to an already tested transition, do not replace the current best.
|
||||||
let mut best_transition=Transition::Miss;
|
let mut best_transition=Transition::Miss;
|
||||||
@ -75,6 +77,7 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
|||||||
//if none:
|
//if none:
|
||||||
},
|
},
|
||||||
&FEV::Edge(edge_id)=>{
|
&FEV::Edge(edge_id)=>{
|
||||||
|
println!("test edge={edge_id:?}");
|
||||||
//test each face collision time, ignoring roots with zero or conflicting derivative
|
//test each face collision time, ignoring roots with zero or conflicting derivative
|
||||||
let edge_n=mesh.edge_n(edge_id);
|
let edge_n=mesh.edge_n(edge_id);
|
||||||
let edge_verts=mesh.edge_verts(edge_id);
|
let edge_verts=mesh.edge_verts(edge_id);
|
||||||
@ -84,6 +87,7 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
|||||||
let face_n=mesh.face_nd(edge_face_id).0;
|
let face_n=mesh.face_nd(edge_face_id).0;
|
||||||
//edge_n gets parity from the order of edge_faces
|
//edge_n gets parity from the order of edge_faces
|
||||||
let n=face_n.cross(edge_n)*((i as i64)*2-1);
|
let n=face_n.cross(edge_n)*((i as i64)*2-1);
|
||||||
|
println!("edge_face={edge_face_id:?} face_n={face_n} n={n}");
|
||||||
//WARNING yada yada d *2
|
//WARNING yada yada d *2
|
||||||
//wrap for speed
|
//wrap for speed
|
||||||
for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).wrap_4(),n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){
|
for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).wrap_4(),n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){
|
||||||
@ -138,7 +142,9 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
|||||||
Ratio::new(r.num.widen_4(),r.den.widen_4())
|
Ratio::new(r.num.widen_4(),r.den.widen_4())
|
||||||
};
|
};
|
||||||
for _ in 0..20{
|
for _ in 0..20{
|
||||||
match self.next_transition(body_time,mesh,relative_body,time_limit){
|
let transition=self.next_transition(body_time,mesh,relative_body,time_limit);
|
||||||
|
println!("transition={transition:?}");
|
||||||
|
match transition{
|
||||||
Transition::Miss=>return CrawlResult::Miss(self),
|
Transition::Miss=>return CrawlResult::Miss(self),
|
||||||
Transition::Next(next_fev,next_time)=>(self,body_time)=(next_fev,next_time),
|
Transition::Next(next_fev,next_time)=>(self,body_time)=(next_fev,next_time),
|
||||||
Transition::Hit(face,time)=>return CrawlResult::Hit(face,time),
|
Transition::Hit(face,time)=>return CrawlResult::Hit(face,time),
|
||||||
|
@ -68,16 +68,17 @@ pub enum FEV<M:MeshQuery>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//use Unit32 #[repr(C)] for map files
|
//use Unit32 #[repr(C)] for map files
|
||||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
#[derive(Clone,Debug,Hash,Eq,PartialEq)]
|
||||||
struct Face{
|
struct Face{
|
||||||
normal:Planar64Vec3,
|
normal:Planar64Vec3,
|
||||||
dot:Planar64,
|
dot:Planar64,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
struct Vert(Planar64Vec3);
|
struct Vert(Planar64Vec3);
|
||||||
pub trait MeshQuery{
|
pub trait MeshQuery{
|
||||||
type Face:Copy;
|
type Face:Copy+std::fmt::Debug;
|
||||||
type Edge:Copy+DirectedEdge;
|
type Edge:Copy+DirectedEdge+std::fmt::Debug;
|
||||||
type Vert:Copy;
|
type Vert:Copy+std::fmt::Debug;
|
||||||
// Vertex must be Planar64Vec3 because it represents an actual position
|
// Vertex must be Planar64Vec3 because it represents an actual position
|
||||||
type Normal;
|
type Normal;
|
||||||
type Offset;
|
type Offset;
|
||||||
@ -97,18 +98,22 @@ pub trait MeshQuery{
|
|||||||
fn vert_edges(&self,vert_id:Self::Vert)->impl AsRef<[Self::Edge]>;
|
fn vert_edges(&self,vert_id:Self::Vert)->impl AsRef<[Self::Edge]>;
|
||||||
fn vert_faces(&self,vert_id:Self::Vert)->impl AsRef<[Self::Face]>;
|
fn vert_faces(&self,vert_id:Self::Vert)->impl AsRef<[Self::Face]>;
|
||||||
}
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
struct FaceRefs{
|
struct FaceRefs{
|
||||||
edges:Vec<SubmeshDirectedEdgeId>,
|
edges:Vec<SubmeshDirectedEdgeId>,
|
||||||
//verts:Vec<VertId>,
|
//verts:Vec<VertId>,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
struct EdgeRefs{
|
struct EdgeRefs{
|
||||||
faces:[SubmeshFaceId;2],//left, right
|
faces:[SubmeshFaceId;2],//left, right
|
||||||
verts:[SubmeshVertId;2],//bottom, top
|
verts:[SubmeshVertId;2],//bottom, top
|
||||||
}
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
struct VertRefs{
|
struct VertRefs{
|
||||||
faces:Vec<SubmeshFaceId>,
|
faces:Vec<SubmeshFaceId>,
|
||||||
edges:Vec<SubmeshDirectedEdgeId>,
|
edges:Vec<SubmeshDirectedEdgeId>,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct PhysicsMeshData{
|
pub struct PhysicsMeshData{
|
||||||
//this contains all real and virtual faces used in both the complete mesh and convex submeshes
|
//this contains all real and virtual faces used in both the complete mesh and convex submeshes
|
||||||
//faces are sorted such that all faces that belong to the complete mesh appear first, and then
|
//faces are sorted such that all faces that belong to the complete mesh appear first, and then
|
||||||
@ -118,6 +123,7 @@ pub struct PhysicsMeshData{
|
|||||||
faces:Vec<Face>,//MeshFaceId indexes this list
|
faces:Vec<Face>,//MeshFaceId indexes this list
|
||||||
verts:Vec<Vert>,//MeshVertId indexes this list
|
verts:Vec<Vert>,//MeshVertId indexes this list
|
||||||
}
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct PhysicsMeshTopology{
|
pub struct PhysicsMeshTopology{
|
||||||
//mapping of local ids to PhysicsMeshData ids
|
//mapping of local ids to PhysicsMeshData ids
|
||||||
faces:Vec<MeshFaceId>,//SubmeshFaceId indexes this list
|
faces:Vec<MeshFaceId>,//SubmeshFaceId indexes this list
|
||||||
@ -314,6 +320,9 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
|
|||||||
return Err(PhysicsMeshError::ZeroVertices);
|
return Err(PhysicsMeshError::ZeroVertices);
|
||||||
}
|
}
|
||||||
let verts=mesh.unique_pos.iter().copied().map(Vert).collect();
|
let verts=mesh.unique_pos.iter().copied().map(Vert).collect();
|
||||||
|
// TODO: do not hash faces to get face id
|
||||||
|
// meshes can have multiple identical nd representations while still being distinct faces,
|
||||||
|
// especially when the complete mesh is a non-convex mesh.
|
||||||
//TODO: fix submeshes
|
//TODO: fix submeshes
|
||||||
//flat map mesh.physics_groups[$1].groups.polys()[$2] as face_id
|
//flat map mesh.physics_groups[$1].groups.polys()[$2] as face_id
|
||||||
//lower face_id points to upper face_id
|
//lower face_id points to upper face_id
|
||||||
@ -422,6 +431,7 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct PhysicsMeshView<'a>{
|
pub struct PhysicsMeshView<'a>{
|
||||||
data:&'a PhysicsMeshData,
|
data:&'a PhysicsMeshData,
|
||||||
topology:&'a PhysicsMeshTopology,
|
topology:&'a PhysicsMeshTopology,
|
||||||
@ -458,6 +468,7 @@ impl MeshQuery for PhysicsMeshView<'_>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct PhysicsMeshTransform{
|
pub struct PhysicsMeshTransform{
|
||||||
pub vertex:integer::Planar64Affine3,
|
pub vertex:integer::Planar64Affine3,
|
||||||
pub normal:integer::mat3::Matrix3<Fixed<2,64>>,
|
pub normal:integer::mat3::Matrix3<Fixed<2,64>>,
|
||||||
@ -473,6 +484,7 @@ impl PhysicsMeshTransform{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct TransformedMesh<'a>{
|
pub struct TransformedMesh<'a>{
|
||||||
view:PhysicsMeshView<'a>,
|
view:PhysicsMeshView<'a>,
|
||||||
transform:&'a PhysicsMeshTransform,
|
transform:&'a PhysicsMeshTransform,
|
||||||
@ -598,6 +610,7 @@ pub enum MinkowskiFace{
|
|||||||
//FaceFace
|
//FaceFace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MinkowskiMesh<'a>{
|
pub struct MinkowskiMesh<'a>{
|
||||||
mesh0:TransformedMesh<'a>,
|
mesh0:TransformedMesh<'a>,
|
||||||
mesh1:TransformedMesh<'a>,
|
mesh1:TransformedMesh<'a>,
|
||||||
@ -742,7 +755,9 @@ impl MinkowskiMesh<'_>{
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn predict_collision_in(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>)->Option<(MinkowskiFace,GigaTime)>{
|
pub fn predict_collision_in(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>)->Option<(MinkowskiFace,GigaTime)>{
|
||||||
|
println!("=== physics setup ===");
|
||||||
self.closest_fev_not_inside(relative_body.clone(),start_time).and_then(|fev|{
|
self.closest_fev_not_inside(relative_body.clone(),start_time).and_then(|fev|{
|
||||||
|
println!("=== physics crawl ===");
|
||||||
//continue forwards along the body parabola
|
//continue forwards along the body parabola
|
||||||
fev.crawl(self,relative_body,start_time,time_limit).hit()
|
fev.crawl(self,relative_body,start_time,time_limit).hit()
|
||||||
})
|
})
|
||||||
@ -899,6 +914,7 @@ impl MeshQuery for MinkowskiMesh<'_>{
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
MinkowskiEdge::EdgeVert(e0,v1)=>{
|
MinkowskiEdge::EdgeVert(e0,v1)=>{
|
||||||
|
println!("MinkowskiEdge::EdgeVert({e0:?},{v1:?})");
|
||||||
//tracking index with an external variable because .enumerate() is not available
|
//tracking index with an external variable because .enumerate() is not available
|
||||||
let v1e=self.mesh1.vert_edges(v1);
|
let v1e=self.mesh1.vert_edges(v1);
|
||||||
let &[e0f0,e0f1]=self.mesh0.edge_faces(e0).as_ref();
|
let &[e0f0,e0f1]=self.mesh0.edge_faces(e0).as_ref();
|
||||||
@ -906,14 +922,18 @@ impl MeshQuery for MinkowskiMesh<'_>{
|
|||||||
let mut best_edge=None;
|
let mut best_edge=None;
|
||||||
let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
|
let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
|
||||||
let edge_face0_n=self.mesh0.face_nd(edge_face_id0).0;
|
let edge_face0_n=self.mesh0.face_nd(edge_face_id0).0;
|
||||||
|
println!("edge_face0_n={edge_face0_n}");
|
||||||
let edge_face0_nn=edge_face0_n.dot(edge_face0_n);
|
let edge_face0_nn=edge_face0_n.dot(edge_face0_n);
|
||||||
for &directed_edge_id1 in v1e.as_ref(){
|
for &directed_edge_id1 in v1e.as_ref(){
|
||||||
let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1);
|
let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1);
|
||||||
|
println!("edge1_n={edge1_n}");
|
||||||
let d=edge_face0_n.dot(edge1_n);
|
let d=edge_face0_n.dot(edge1_n);
|
||||||
|
println!("d={d} {d:?}");
|
||||||
if d.is_negative(){
|
if d.is_negative(){
|
||||||
let edge1_nn=edge1_n.dot(edge1_n);
|
let edge1_nn=edge1_n.dot(edge1_n);
|
||||||
let dd=(d*d)/(edge_face0_nn*edge1_nn);
|
let dd=(d*d)/(edge_face0_nn*edge1_nn);
|
||||||
if best_d<dd{
|
if !dd.den.is_zero()&&best_d<dd{
|
||||||
|
println!("dd={dd:?}");
|
||||||
best_d=dd;
|
best_d=dd;
|
||||||
best_edge=Some(directed_edge_id1);
|
best_edge=Some(directed_edge_id1);
|
||||||
}
|
}
|
||||||
|
@ -857,6 +857,12 @@ impl Default for PhysicsState{
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PhysicsState{
|
impl PhysicsState{
|
||||||
|
pub fn new_at_position(position:Planar64Vec3)->Self{
|
||||||
|
Self{
|
||||||
|
body:Body::new(position,vec3::int(0,0,0),vec3::int(0,-100,0),Time::ZERO),
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn camera_body(&self)->Body{
|
pub fn camera_body(&self)->Body{
|
||||||
Body{
|
Body{
|
||||||
position:self.body.position+self.style.camera_offset,
|
position:self.body.position+self.style.camera_offset,
|
||||||
@ -1122,7 +1128,7 @@ impl PhysicsData{
|
|||||||
//JUST POLLING!!! NO MUTATION
|
//JUST POLLING!!! NO MUTATION
|
||||||
let mut collector=instruction::InstructionCollector::new(time_limit);
|
let mut collector=instruction::InstructionCollector::new(time_limit);
|
||||||
|
|
||||||
collector.collect(state.next_move_instruction());
|
// collector.collect(state.next_move_instruction());
|
||||||
|
|
||||||
//check for collision ends
|
//check for collision ends
|
||||||
state.touching.predict_collision_end(&mut collector,&data.models,&data.hitbox_mesh,&state.body,state.time);
|
state.touching.predict_collision_end(&mut collector,&data.models,&data.hitbox_mesh,&state.body,state.time);
|
||||||
@ -1134,6 +1140,7 @@ impl PhysicsData{
|
|||||||
//let relative_body=state.body.relative_to(&Body::ZERO);
|
//let relative_body=state.body.relative_to(&Body::ZERO);
|
||||||
let relative_body=&state.body;
|
let relative_body=&state.body;
|
||||||
data.bvh.sample_aabb(&aabb,&mut |&convex_mesh_id|{
|
data.bvh.sample_aabb(&aabb,&mut |&convex_mesh_id|{
|
||||||
|
println!("sample model={:?}",convex_mesh_id.model_id);
|
||||||
//no checks are needed because of the time limits.
|
//no checks are needed because of the time limits.
|
||||||
let model_mesh=data.models.mesh(convex_mesh_id);
|
let model_mesh=data.models.mesh(convex_mesh_id);
|
||||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
|
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
|
||||||
@ -1665,6 +1672,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
|||||||
}
|
}
|
||||||
match ins.instruction{
|
match ins.instruction{
|
||||||
InternalInstruction::CollisionStart(collision,_)=>{
|
InternalInstruction::CollisionStart(collision,_)=>{
|
||||||
|
println!("yeaahhh!");
|
||||||
let mode=data.modes.get_mode(state.mode_state.get_mode_id());
|
let mode=data.modes.get_mode(state.mode_state.get_mode_id());
|
||||||
match collision{
|
match collision{
|
||||||
Collision::Contact(contact)=>collision_start_contact(
|
Collision::Contact(contact)=>collision_start_contact(
|
||||||
|
@ -9,10 +9,37 @@ fn main(){
|
|||||||
match arg.as_deref(){
|
match arg.as_deref(){
|
||||||
Some("determinism")|None=>test_determinism().unwrap(),
|
Some("determinism")|None=>test_determinism().unwrap(),
|
||||||
Some("replay")=>run_replay().unwrap(),
|
Some("replay")=>run_replay().unwrap(),
|
||||||
|
Some("bug2")=>physics_bug_2().unwrap(),
|
||||||
_=>println!("invalid argument"),
|
_=>println!("invalid argument"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn physics_bug_2()->Result<(),ReplayError>{
|
||||||
|
println!("loading map file..");
|
||||||
|
let data=read_entire_file("bhop_monster_jam.snfm")?;
|
||||||
|
let map=strafesnet_snf::read_map(data)?.into_complete_map()?;
|
||||||
|
|
||||||
|
// create recording
|
||||||
|
let mut physics_data=PhysicsData::default();
|
||||||
|
println!("generating models..");
|
||||||
|
physics_data.generate_models(&map);
|
||||||
|
println!("simulating...");
|
||||||
|
|
||||||
|
//teleport to bug
|
||||||
|
// body pos = Vector { array: [Fixed { bits: 554895163352 }, Fixed { bits: 1485633089990 }, Fixed { bits: 1279601007173 }] }
|
||||||
|
// after the fix it's still happening, possibly for a different reason, new position to evince:
|
||||||
|
// body pos = Vector { array: [Fixed { bits: 555690659654 }, Fixed { bits: 1490485868773 }, Fixed { bits: 1277783839382 }] }
|
||||||
|
let mut physics=PhysicsState::new_at_position(strafesnet_common::integer::vec3::raw_xyz(555690659654,1490485868773,1277783839382));
|
||||||
|
// wait one second to activate the bug
|
||||||
|
// hit=Some(ModelId(2262))
|
||||||
|
PhysicsContext::run_input_instruction(&mut physics,&physics_data,strafesnet_common::instruction::TimedInstruction{
|
||||||
|
time:strafesnet_common::integer::Time::from_millis(500),
|
||||||
|
instruction:strafesnet_common::physics::Instruction::Idle,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ReplayError{
|
enum ReplayError{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use strafesnet_common::integer::Planar64;
|
use strafesnet_common::integer::{self,Planar64,Planar64Vec3};
|
||||||
use strafesnet_common::{model,integer};
|
use strafesnet_common::model::{self,VertexId};
|
||||||
use strafesnet_common::integer::{vec3::Vector3,Fixed,Ratio};
|
use strafesnet_common::integer::{vec3::Vector3,Fixed,Ratio};
|
||||||
|
|
||||||
use crate::{valve_transform_normal,valve_transform_dist};
|
use crate::{valve_transform_normal,valve_transform_dist};
|
||||||
@ -138,7 +138,15 @@ fn planes_to_faces(face_list:std::collections::HashSet<Face>)->Result<Faces,Plan
|
|||||||
loop{
|
loop{
|
||||||
// push point onto vertices
|
// push point onto vertices
|
||||||
// problem: this may push a vertex that does not fit in the fixed point range and is thus meaningless
|
// problem: this may push a vertex that does not fit in the fixed point range and is thus meaningless
|
||||||
face.push(intersection.divide().narrow_1().unwrap());
|
//
|
||||||
|
// physics bug 2 originates from vertices being imprecise?
|
||||||
|
//
|
||||||
|
// Mask off the most precise 16 bits so that
|
||||||
|
// when face normals are calculated from
|
||||||
|
// the remaining 16 fractional bits
|
||||||
|
// they never exceed 32 bits of precision.
|
||||||
|
const MASK:Planar64=Planar64::raw(!((1<<16)-1));
|
||||||
|
face.push(intersection.divide().narrow_1().unwrap().map(|c|c&MASK));
|
||||||
|
|
||||||
// we looped back around to face1, we're done!
|
// we looped back around to face1, we're done!
|
||||||
if core::ptr::eq(face1,face2){
|
if core::ptr::eq(face1,face2){
|
||||||
@ -204,6 +212,33 @@ impl std::fmt::Display for BrushToMeshError{
|
|||||||
}
|
}
|
||||||
impl core::error::Error for BrushToMeshError{}
|
impl core::error::Error for BrushToMeshError{}
|
||||||
|
|
||||||
|
fn subdivide_max_area(tris:&mut Vec<Vec<VertexId>>,cw_verts:&[(VertexId,Planar64Vec3)],i0:usize,i2:usize,id0:VertexId,id2:VertexId,v0:Planar64Vec3,v2:Planar64Vec3){
|
||||||
|
if i0+1==i2{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut best_i1=i0+1;
|
||||||
|
if i0+2<i2{
|
||||||
|
let mut best_area={
|
||||||
|
let (_,v1)=cw_verts[best_i1.rem_euclid(cw_verts.len())];
|
||||||
|
(v2-v0).cross(v1-v0).length_squared()
|
||||||
|
};
|
||||||
|
for i1 in i0+2..=i2-1{
|
||||||
|
let (_,v1)=cw_verts[i1.rem_euclid(cw_verts.len())];
|
||||||
|
let area=(v2-v0).cross(v1-v0).length_squared();
|
||||||
|
if best_area<area{
|
||||||
|
best_i1=i1;
|
||||||
|
best_area=area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let i1=best_i1;
|
||||||
|
let (id1,v1)=cw_verts[i1.rem_euclid(cw_verts.len())];
|
||||||
|
// draw max area first
|
||||||
|
tris.push(vec![id0,id1,id2]);
|
||||||
|
subdivide_max_area(tris,cw_verts,i0,i1,id0,id1,v0,v1);
|
||||||
|
subdivide_max_area(tris,cw_verts,i1,i2,id1,id2,v1,v2);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn faces_to_mesh(faces:Vec<Vec<integer::Planar64Vec3>>)->model::Mesh{
|
pub fn faces_to_mesh(faces:Vec<Vec<integer::Planar64Vec3>>)->model::Mesh{
|
||||||
// generate the mesh
|
// generate the mesh
|
||||||
let mut mb=model::MeshBuilder::new();
|
let mut mb=model::MeshBuilder::new();
|
||||||
@ -212,16 +247,34 @@ pub fn faces_to_mesh(faces:Vec<Vec<integer::Planar64Vec3>>)->model::Mesh{
|
|||||||
// normals are ignored by physics
|
// normals are ignored by physics
|
||||||
let normal=mb.acquire_normal_id(integer::vec3::ZERO);
|
let normal=mb.acquire_normal_id(integer::vec3::ZERO);
|
||||||
|
|
||||||
let polygon_list=faces.into_iter().map(|face|{
|
let polygon_list=faces.into_iter().flat_map(|face|{
|
||||||
face.into_iter().map(|pos|{
|
let cw_verts=face.into_iter().map(|position|{
|
||||||
let pos=mb.acquire_pos_id(pos);
|
let pos=mb.acquire_pos_id(position);
|
||||||
mb.acquire_vertex_id(model::IndexedVertex{
|
(mb.acquire_vertex_id(model::IndexedVertex{
|
||||||
pos,
|
pos,
|
||||||
tex,
|
tex,
|
||||||
normal,
|
normal,
|
||||||
color,
|
color,
|
||||||
})
|
}),position)
|
||||||
}).collect()
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// scan and select maximum area triangle O(n^3)
|
||||||
|
let len=cw_verts.len();
|
||||||
|
let cw_verts=cw_verts.as_slice();
|
||||||
|
let ((i0,i1,i2),(v0,v1,v2))=cw_verts[..len-2].iter().enumerate().flat_map(|(i0,&(_,v0))|
|
||||||
|
cw_verts[i0+1..len-1].iter().enumerate().flat_map(move|(i1,&(_,v1))|
|
||||||
|
cw_verts[i0+i1+2..].iter().enumerate().map(move|(i2,&(_,v2))|((i0,i0+i1+1,i0+i1+i2+2),(v0,v1,v2)))
|
||||||
|
)
|
||||||
|
).max_by_key(|&(_,(v0,v1,v2))|(v2-v0).cross(v1-v0).length_squared()).unwrap();
|
||||||
|
// scan and select more maximum area triangles n * O(n)
|
||||||
|
let mut tris=Vec::with_capacity(len-2);
|
||||||
|
// da big one
|
||||||
|
let (id0,id1,id2)=(cw_verts[i0].0,cw_verts[i1].0,cw_verts[i2].0);
|
||||||
|
tris.push(vec![id0,id1,id2]);
|
||||||
|
subdivide_max_area(&mut tris,cw_verts,i0,i1,id0,id1,v0,v1);
|
||||||
|
subdivide_max_area(&mut tris,cw_verts,i1,i2,id1,id2,v1,v2);
|
||||||
|
subdivide_max_area(&mut tris,cw_verts,i2,i0+len,id2,id0,v2,v0);
|
||||||
|
tris
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
let polygon_groups=vec![model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))];
|
let polygon_groups=vec![model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))];
|
||||||
|
@ -648,7 +648,7 @@ pub mod mat3{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy,Default,Hash,Eq,PartialEq)]
|
#[derive(Clone,Copy,Debug,Default,Hash,Eq,PartialEq)]
|
||||||
pub struct Planar64Affine3{
|
pub struct Planar64Affine3{
|
||||||
pub matrix3:Planar64Mat3,//includes scale above 1
|
pub matrix3:Planar64Mat3,//includes scale above 1
|
||||||
pub translation:Planar64Vec3,
|
pub translation:Planar64Vec3,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user