Compare commits
3 Commits
minkowksi-
...
max-area-t
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a03987d89 | |||
| 57a4cadaf3 | |||
| 881bc60ab3 |
@@ -103,26 +103,6 @@ impl std::default::Default for GraphicsCamera{
|
||||
}
|
||||
}
|
||||
|
||||
const MODEL_BUFFER_SIZE:usize=4*4 + 12 + 4;//let size=std::mem::size_of::<ModelInstance>();
|
||||
const MODEL_BUFFER_SIZE_BYTES:usize=MODEL_BUFFER_SIZE*4;
|
||||
fn get_instances_buffer_data(instances:&[GraphicsModelOwned])->Vec<f32>{
|
||||
let mut raw=Vec::with_capacity(MODEL_BUFFER_SIZE*instances.len());
|
||||
for mi in instances{
|
||||
//model transform
|
||||
raw.extend_from_slice(&AsRef::<[f32; 4*4]>::as_ref(&mi.transform)[..]);
|
||||
//normal transform
|
||||
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.x_axis));
|
||||
raw.extend_from_slice(&[0.0]);
|
||||
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.y_axis));
|
||||
raw.extend_from_slice(&[0.0]);
|
||||
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.z_axis));
|
||||
raw.extend_from_slice(&[0.0]);
|
||||
//color
|
||||
raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color.get()));
|
||||
}
|
||||
raw
|
||||
}
|
||||
|
||||
pub struct GraphicsState{
|
||||
pipelines:GraphicsPipelines,
|
||||
bind_groups:GraphicsBindGroups,
|
||||
@@ -987,3 +967,22 @@ impl GraphicsState{
|
||||
self.staging_belt.recall();
|
||||
}
|
||||
}
|
||||
const MODEL_BUFFER_SIZE:usize=4*4 + 12 + 4;//let size=std::mem::size_of::<ModelInstance>();
|
||||
const MODEL_BUFFER_SIZE_BYTES:usize=MODEL_BUFFER_SIZE*4;
|
||||
fn get_instances_buffer_data(instances:&[GraphicsModelOwned])->Vec<f32>{
|
||||
let mut raw=Vec::with_capacity(MODEL_BUFFER_SIZE*instances.len());
|
||||
for mi in instances{
|
||||
//model transform
|
||||
raw.extend_from_slice(&AsRef::<[f32; 4*4]>::as_ref(&mi.transform)[..]);
|
||||
//normal transform
|
||||
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.x_axis));
|
||||
raw.extend_from_slice(&[0.0]);
|
||||
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.y_axis));
|
||||
raw.extend_from_slice(&[0.0]);
|
||||
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.z_axis));
|
||||
raw.extend_from_slice(&[0.0]);
|
||||
//color
|
||||
raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color.get()));
|
||||
}
|
||||
raw
|
||||
}
|
||||
|
||||
@@ -314,6 +314,9 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
|
||||
return Err(PhysicsMeshError::ZeroVertices);
|
||||
}
|
||||
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
|
||||
//flat map mesh.physics_groups[$1].groups.polys()[$2] as face_id
|
||||
//lower face_id points to upper face_id
|
||||
@@ -757,7 +760,7 @@ impl MinkowskiMesh<'_>{
|
||||
.map(|(face,time)|(face,-time))
|
||||
})
|
||||
}
|
||||
pub fn predict_collision_face_out(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>,contact_face_id:MinkowskiFace)->Option<(MinkowskiDirectedEdge,GigaTime)>{
|
||||
pub fn predict_collision_face_out(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{
|
||||
//no algorithm needed, there is only one state and two cases (Edge,None)
|
||||
//determine when it passes an edge ("sliding off" case)
|
||||
let start_time={
|
||||
@@ -787,7 +790,7 @@ impl MinkowskiMesh<'_>{
|
||||
}
|
||||
}
|
||||
}
|
||||
best_edge.map(|e|(e,best_time))
|
||||
best_edge.map(|e|(e.as_undirected(),best_time))
|
||||
}
|
||||
fn infinity_in(&self,infinity_body:Body)->Option<(MinkowskiFace,GigaTime)>{
|
||||
let infinity_fev=self.infinity_fev(-infinity_body.velocity,infinity_body.position);
|
||||
|
||||
@@ -26,8 +26,6 @@ use strafesnet_common::physics::{Instruction,MouseInstruction,ModeInstruction,Mi
|
||||
#[derive(Debug)]
|
||||
pub enum InternalInstruction{
|
||||
CollisionStart(Collision,model_physics::GigaTime),
|
||||
// transfer to a flush minkowski face
|
||||
CollisionTransfer(ContactCollision,model_physics::MinkowskiFace),
|
||||
CollisionEnd(Collision,model_physics::GigaTime),
|
||||
StrafeTick,
|
||||
ReachWalkTargetVelocity,
|
||||
@@ -787,39 +785,19 @@ impl TouchingState{
|
||||
crate::push_solve::push_solve(&contacts,acceleration)
|
||||
}
|
||||
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<InternalInstruction,Time>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,start_time:Time){
|
||||
use model_physics::DirectedEdge;
|
||||
// let relative_body=body.relative_to(&Body::ZERO);
|
||||
let relative_body=body;
|
||||
for contact in &self.contacts{
|
||||
//detect face slide off
|
||||
let model_mesh=models.contact_mesh(contact);
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
||||
collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),contact.face_id).map(|(out_edge,time)|{
|
||||
// TODO: determine if this code should go inside predict_collision_face_out
|
||||
// the face across may be left or right depending on the directed edge parity
|
||||
let &[f0,f1]=minkowski.edge_faces(out_edge.as_undirected()).as_ref();
|
||||
let (f0n,_f0d)=minkowski.face_nd(f0);
|
||||
let (f1n,_f1d)=minkowski.face_nd(f1);
|
||||
// are they exactly flush
|
||||
if f0n.cross(f1n)==vec3::ZERO_6
|
||||
// implicitly don't need to check d values because faces share two vertices
|
||||
// && n0*d1==n1*d0
|
||||
{
|
||||
TimedInstruction{
|
||||
time:relative_body.time+time.into(),
|
||||
instruction:InternalInstruction::CollisionTransfer(
|
||||
*contact,
|
||||
if out_edge.parity(){f1}else{f0},
|
||||
),
|
||||
}
|
||||
}else{
|
||||
TimedInstruction{
|
||||
time:relative_body.time+time.into(),
|
||||
instruction:InternalInstruction::CollisionEnd(
|
||||
Collision::Contact(*contact),
|
||||
time
|
||||
),
|
||||
}
|
||||
collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),contact.face_id).map(|(_face,time)|{
|
||||
TimedInstruction{
|
||||
time:relative_body.time+time.into(),
|
||||
instruction:InternalInstruction::CollisionEnd(
|
||||
Collision::Contact(*contact),
|
||||
time
|
||||
),
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -1004,7 +982,7 @@ impl PhysicsContext<'_>{
|
||||
impl PhysicsData{
|
||||
/// use with caution, this is the only non-instruction way to mess with physics
|
||||
pub fn generate_models(&mut self,map:&map::CompleteMap){
|
||||
let modes=map.modes.clone().denormalize();
|
||||
let mut modes=map.modes.clone().denormalize();
|
||||
let mut used_contact_attributes=Vec::new();
|
||||
let mut used_intersect_attributes=Vec::new();
|
||||
|
||||
@@ -1678,7 +1656,6 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
||||
|InternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)),
|
||||
InternalInstruction::StrafeTick
|
||||
|InternalInstruction::ReachWalkTargetVelocity=>(true,None),
|
||||
InternalInstruction::CollisionTransfer(..)=>(false,None),
|
||||
};
|
||||
if should_advance_body{
|
||||
match goober_time{
|
||||
@@ -1723,15 +1700,6 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
||||
state.time
|
||||
),
|
||||
},
|
||||
InternalInstruction::CollisionTransfer(collision,dst_face)=>{
|
||||
// transfer to a flush surface of the same object
|
||||
state.touching.contacts.remove(&collision);
|
||||
state.touching.contacts.insert(ContactCollision{
|
||||
face_id:dst_face,
|
||||
model_id:collision.model_id,
|
||||
submesh_id:collision.submesh_id,
|
||||
});
|
||||
},
|
||||
InternalInstruction::StrafeTick=>{
|
||||
//TODO make this less huge
|
||||
if let Some(strafe_settings)=&state.style.strafe{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use strafesnet_common::integer::Planar64;
|
||||
use strafesnet_common::{model,integer};
|
||||
use strafesnet_common::integer::{self,Planar64,Planar64Vec3};
|
||||
use strafesnet_common::model::{self,VertexId};
|
||||
use strafesnet_common::integer::{vec3::Vector3,Fixed,Ratio};
|
||||
|
||||
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{
|
||||
// push point onto vertices
|
||||
// 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!
|
||||
if core::ptr::eq(face1,face2){
|
||||
@@ -204,6 +212,33 @@ impl std::fmt::Display 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{
|
||||
// generate the mesh
|
||||
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
|
||||
let normal=mb.acquire_normal_id(integer::vec3::ZERO);
|
||||
|
||||
let polygon_list=faces.into_iter().map(|face|{
|
||||
face.into_iter().map(|pos|{
|
||||
let pos=mb.acquire_pos_id(pos);
|
||||
mb.acquire_vertex_id(model::IndexedVertex{
|
||||
let polygon_list=faces.into_iter().flat_map(|face|{
|
||||
let cw_verts=face.into_iter().map(|position|{
|
||||
let pos=mb.acquire_pos_id(position);
|
||||
(mb.acquire_vertex_id(model::IndexedVertex{
|
||||
pos,
|
||||
tex,
|
||||
normal,
|
||||
color,
|
||||
})
|
||||
}).collect()
|
||||
}),position)
|
||||
}).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();
|
||||
|
||||
let polygon_groups=vec![model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))];
|
||||
|
||||
@@ -401,10 +401,6 @@ impl Angle32{
|
||||
pub const NEG_FRAC_PI_2:Self=Self(-1<<30);
|
||||
pub const PI:Self=Self(-1<<31);
|
||||
#[inline]
|
||||
pub const fn raw(num:i32)->Self{
|
||||
Self(num)
|
||||
}
|
||||
#[inline]
|
||||
pub const fn wrap_from_i64(theta:i64)->Self{
|
||||
//take lower bits
|
||||
//note: this was checked on compiler explorer and compiles to 1 instruction!
|
||||
@@ -562,10 +558,6 @@ pub mod vec3{
|
||||
pub const MAX:Planar64Vec3=Planar64Vec3::new([Planar64::MAX;3]);
|
||||
pub const ZERO:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO;3]);
|
||||
pub const ZERO_2:linear_ops::types::Vector3<Fixed::<2,64>>=linear_ops::types::Vector3::new([Fixed::<2,64>::ZERO;3]);
|
||||
pub const ZERO_3:linear_ops::types::Vector3<Fixed::<3,96>>=linear_ops::types::Vector3::new([Fixed::<3,96>::ZERO;3]);
|
||||
pub const ZERO_4:linear_ops::types::Vector3<Fixed::<4,128>>=linear_ops::types::Vector3::new([Fixed::<4,128>::ZERO;3]);
|
||||
pub const ZERO_5:linear_ops::types::Vector3<Fixed::<5,160>>=linear_ops::types::Vector3::new([Fixed::<5,160>::ZERO;3]);
|
||||
pub const ZERO_6:linear_ops::types::Vector3<Fixed::<6,192>>=linear_ops::types::Vector3::new([Fixed::<6,192>::ZERO;3]);
|
||||
pub const X:Planar64Vec3=Planar64Vec3::new([Planar64::ONE,Planar64::ZERO,Planar64::ZERO]);
|
||||
pub const Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ONE,Planar64::ZERO]);
|
||||
pub const Z:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ZERO,Planar64::ONE]);
|
||||
|
||||
Reference in New Issue
Block a user