Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
a92526407c
|
|||
|
47239b10a8
|
|||
|
f5588989e9
|
|||
|
979d46e42a
|
|||
|
f858fa86e6
|
|||
|
8e6d598ea3
|
|||
|
78c0cab05a
|
|||
|
dd972c91ee
|
|||
|
8a897ca377
|
|||
|
65f29fd395
|
|||
|
a673d62ffd
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3997,6 +3997,7 @@ dependencies = [
|
|||||||
"arrayvec",
|
"arrayvec",
|
||||||
"glam",
|
"glam",
|
||||||
"id",
|
"id",
|
||||||
|
"mlua",
|
||||||
"strafesnet_common",
|
"strafesnet_common",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ edition = "2024"
|
|||||||
arrayvec = "0.7.6"
|
arrayvec = "0.7.6"
|
||||||
glam.workspace = true
|
glam.workspace = true
|
||||||
id = { version = "0.1.0", registry = "strafesnet" }
|
id = { version = "0.1.0", registry = "strafesnet" }
|
||||||
|
mlua = { version = "0.11.5", features = ["luau"] }
|
||||||
strafesnet_common.workspace = true
|
strafesnet_common.workspace = true
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ mod minkowski;
|
|||||||
mod model;
|
mod model;
|
||||||
mod push_solve;
|
mod push_solve;
|
||||||
mod minimum_difference;
|
mod minimum_difference;
|
||||||
|
mod minimum_difference_lua;
|
||||||
|
|
||||||
pub mod physics;
|
pub mod physics;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::minkowski::{MinkowskiMesh,MinkowskiVert};
|
|||||||
// written by Trey Reynolds in 2021
|
// written by Trey Reynolds in 2021
|
||||||
|
|
||||||
type Simplex<const N:usize,Vert>=[Vert;N];
|
type Simplex<const N:usize,Vert>=[Vert;N];
|
||||||
#[derive(Clone,Copy)]
|
#[derive(Clone,Copy,Debug)]
|
||||||
enum Simplex1_3<Vert>{
|
enum Simplex1_3<Vert>{
|
||||||
Simplex1(Simplex<1,Vert>),
|
Simplex1(Simplex<1,Vert>),
|
||||||
Simplex2(Simplex<2,Vert>),
|
Simplex2(Simplex<2,Vert>),
|
||||||
@@ -139,12 +139,15 @@ fn reduce1<M:MeshQuery<Position=Planar64Vec3>>(
|
|||||||
)->Reduced<M::Vert>
|
)->Reduced<M::Vert>
|
||||||
where M::Vert:Copy,
|
where M::Vert:Copy,
|
||||||
{
|
{
|
||||||
|
println!("reduce1");
|
||||||
// --debug.profilebegin("reduceSimplex0")
|
// --debug.profilebegin("reduceSimplex0")
|
||||||
// local a = a1 - a0
|
// local a = a1 - a0
|
||||||
let p0=mesh.vert(v0);
|
let p0=mesh.vert(v0);
|
||||||
|
|
||||||
|
println!("p0={p0}");
|
||||||
// local p = -a
|
// local p = -a
|
||||||
let p=-(p0+point);
|
let p=-(p0+point);
|
||||||
|
println!("p={p}");
|
||||||
|
|
||||||
// local direction = p
|
// local direction = p
|
||||||
let mut dir=p;
|
let mut dir=p;
|
||||||
@@ -171,6 +174,7 @@ fn reduce2<M:MeshQuery<Position=Planar64Vec3>>(
|
|||||||
where
|
where
|
||||||
M::Vert:Copy
|
M::Vert:Copy
|
||||||
{
|
{
|
||||||
|
println!("reduce2");
|
||||||
// --debug.profilebegin("reduceSimplex1")
|
// --debug.profilebegin("reduceSimplex1")
|
||||||
// local a = a1 - a0
|
// local a = a1 - a0
|
||||||
// local b = b1 - b0
|
// local b = b1 - b0
|
||||||
@@ -231,6 +235,7 @@ fn reduce3<M:MeshQuery<Position=Planar64Vec3>>(
|
|||||||
where
|
where
|
||||||
M::Vert:Copy
|
M::Vert:Copy
|
||||||
{
|
{
|
||||||
|
println!("reduce3");
|
||||||
// --debug.profilebegin("reduceSimplex2")
|
// --debug.profilebegin("reduceSimplex2")
|
||||||
// local a = a1 - a0
|
// local a = a1 - a0
|
||||||
// local b = b1 - b0
|
// local b = b1 - b0
|
||||||
@@ -343,6 +348,7 @@ fn reduce4<M:MeshQuery<Position=Planar64Vec3>>(
|
|||||||
where
|
where
|
||||||
M::Vert:Copy
|
M::Vert:Copy
|
||||||
{
|
{
|
||||||
|
println!("reduce4");
|
||||||
// --debug.profilebegin("reduceSimplex3")
|
// --debug.profilebegin("reduceSimplex3")
|
||||||
// local a = a1 - a0
|
// local a = a1 - a0
|
||||||
// local b = b1 - b0
|
// local b = b1 - b0
|
||||||
@@ -734,6 +740,7 @@ fn crawl_to_closest_fev<'a>(mesh:&MinkowskiMesh<'a>,simplex:Simplex<3,MinkowskiV
|
|||||||
//if test point is behind face, the face is invalid
|
//if test point is behind face, the face is invalid
|
||||||
// TODO: find out why I thought of this backwards
|
// TODO: find out why I thought of this backwards
|
||||||
if !(face_n.dot(point)-d).is_positive(){
|
if !(face_n.dot(point)-d).is_positive(){
|
||||||
|
println!("behind");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//edge-face boundary nd, n facing out of the face towards the edge
|
//edge-face boundary nd, n facing out of the face towards the edge
|
||||||
@@ -743,6 +750,8 @@ fn crawl_to_closest_fev<'a>(mesh:&MinkowskiMesh<'a>,simplex:Simplex<3,MinkowskiV
|
|||||||
if !boundary_d.is_positive(){
|
if !boundary_d.is_positive(){
|
||||||
//both faces cannot pass this condition, return early if one does.
|
//both faces cannot pass this condition, return early if one does.
|
||||||
return FEV::Face(face_id);
|
return FEV::Face(face_id);
|
||||||
|
}else{
|
||||||
|
println!("boundary_d is positive");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FEV::Edge(edge_id)
|
FEV::Edge(edge_id)
|
||||||
@@ -751,11 +760,21 @@ fn crawl_to_closest_fev<'a>(mesh:&MinkowskiMesh<'a>,simplex:Simplex<3,MinkowskiV
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn closest_fev_not_inside<'a>(mesh:&MinkowskiMesh<'a>,point:Planar64Vec3)->Option<FEV<MinkowskiMesh<'a>>>{
|
pub fn closest_fev_not_inside<'a>(mesh:&MinkowskiMesh<'a>,point:Planar64Vec3)->Option<FEV<MinkowskiMesh<'a>>>{
|
||||||
|
println!("=== LUA ===");
|
||||||
|
let (hits,_details)=crate::minimum_difference_lua::minimum_difference_details(mesh,point).unwrap();
|
||||||
|
println!("=== RUST ===");
|
||||||
|
let closest_fev_not_inside=closest_fev_not_inside_inner(mesh,point);
|
||||||
|
assert_eq!(hits,closest_fev_not_inside.is_none(),"algorithms disagree");
|
||||||
|
closest_fev_not_inside
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn closest_fev_not_inside_inner<'a>(mesh:&MinkowskiMesh<'a>,point:Planar64Vec3)->Option<FEV<MinkowskiMesh<'a>>>{
|
||||||
const ENABLE_FAST_FAIL:bool=false;
|
const ENABLE_FAST_FAIL:bool=false;
|
||||||
// TODO: remove mesh negation
|
// TODO: remove mesh negation
|
||||||
minimum_difference::<ENABLE_FAST_FAIL,_,_>(&-mesh,point,
|
minimum_difference::<ENABLE_FAST_FAIL,_,_>(&-mesh,point,
|
||||||
// on_exact
|
// on_exact
|
||||||
|is_intersecting,simplex|{
|
|is_intersecting,simplex|{
|
||||||
|
println!("on_exact simplex={simplex:?}");
|
||||||
if is_intersecting{
|
if is_intersecting{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -766,7 +785,11 @@ pub fn closest_fev_not_inside<'a>(mesh:&MinkowskiMesh<'a>,point:Planar64Vec3)->O
|
|||||||
Simplex1_3::Simplex2([v0,v1])=>{
|
Simplex1_3::Simplex2([v0,v1])=>{
|
||||||
// invert
|
// invert
|
||||||
let (v0,v1)=(-v0,-v1);
|
let (v0,v1)=(-v0,-v1);
|
||||||
crawl_to_closest_ev(mesh,[v0,v1],point).into()
|
let ev=crawl_to_closest_ev(mesh,[v0,v1],point);
|
||||||
|
if !matches!(ev,EV::Edge(_)){
|
||||||
|
println!("I can't believe it's not an edge!");
|
||||||
|
}
|
||||||
|
ev.into()
|
||||||
},
|
},
|
||||||
Simplex1_3::Simplex3([v0,v1,v2])=>{
|
Simplex1_3::Simplex3([v0,v1,v2])=>{
|
||||||
// invert
|
// invert
|
||||||
@@ -774,7 +797,11 @@ pub fn closest_fev_not_inside<'a>(mesh:&MinkowskiMesh<'a>,point:Planar64Vec3)->O
|
|||||||
// Shimmy to the side until you find a face that contains the closest point
|
// Shimmy to the side until you find a face that contains the closest point
|
||||||
// it's ALWAYS representable as a face, but this algorithm may
|
// it's ALWAYS representable as a face, but this algorithm may
|
||||||
// return E or V in edge cases but I don't think that will break the face crawler
|
// return E or V in edge cases but I don't think that will break the face crawler
|
||||||
crawl_to_closest_fev(mesh,[v0,v1,v2],point)
|
let fev=crawl_to_closest_fev(mesh,[v0,v1,v2],point);
|
||||||
|
if !matches!(fev,FEV::Face(_)){
|
||||||
|
println!("I can't believe it's not a face!");
|
||||||
|
}
|
||||||
|
fev
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -832,6 +859,7 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery<Position=Planar6
|
|||||||
if initial_axis==vec3::zero(){
|
if initial_axis==vec3::zero(){
|
||||||
initial_axis=choose_any_direction();
|
initial_axis=choose_any_direction();
|
||||||
}
|
}
|
||||||
|
println!("initial_axis={initial_axis}");
|
||||||
let last_point=mesh.farthest_vert(-initial_axis);
|
let last_point=mesh.farthest_vert(-initial_axis);
|
||||||
// this represents the 'a' value in the commented code
|
// this represents the 'a' value in the commented code
|
||||||
let mut last_pos=mesh.vert(last_point);
|
let mut last_pos=mesh.vert(last_point);
|
||||||
@@ -840,6 +868,8 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery<Position=Planar6
|
|||||||
// exitRadius = testIntersection and 0 or exitRadius or 1/0
|
// exitRadius = testIntersection and 0 or exitRadius or 1/0
|
||||||
// for _ = 1, 100 do
|
// for _ = 1, 100 do
|
||||||
loop{
|
loop{
|
||||||
|
println!("direction={direction}");
|
||||||
|
|
||||||
// new_point_p = queryP(-direction)
|
// new_point_p = queryP(-direction)
|
||||||
// new_point_q = queryQ(direction)
|
// new_point_q = queryQ(direction)
|
||||||
// local next_point = new_point_q - new_point_p
|
// local next_point = new_point_q - new_point_p
|
||||||
@@ -847,7 +877,11 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery<Position=Planar6
|
|||||||
let next_pos=mesh.vert(next_point);
|
let next_pos=mesh.vert(next_point);
|
||||||
|
|
||||||
// if -direction:Dot(next_point) > (exitRadius + radiusP + radiusQ)*direction.magnitude then
|
// if -direction:Dot(next_point) > (exitRadius + radiusP + radiusQ)*direction.magnitude then
|
||||||
if ENABLE_FAST_FAIL&&direction.dot(next_pos+point).is_negative(){
|
let d=direction.dot(next_pos+point);
|
||||||
|
let fast_fail=d.is_negative();
|
||||||
|
println!("ENABLE_FAST_FAIL={ENABLE_FAST_FAIL} fast_fail={fast_fail} next_point={} dot={d}",next_pos+point);
|
||||||
|
if ENABLE_FAST_FAIL&&fast_fail{
|
||||||
|
println!("on_fast_fail");
|
||||||
return on_fast_fail();
|
return on_fast_fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,8 +890,11 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery<Position=Planar6
|
|||||||
// if
|
// if
|
||||||
// direction:Dot(next_point - a) <= 0 or
|
// direction:Dot(next_point - a) <= 0 or
|
||||||
// absDet(next_point, a, b, c) < 1e-6
|
// absDet(next_point, a, b, c) < 1e-6
|
||||||
if !direction.dot(next_pos-last_pos).is_positive()
|
let d1=direction.dot(next_pos-last_pos);
|
||||||
||simplex_big.det_is_zero(mesh){
|
let cond2=simplex_big.det_is_zero(mesh);
|
||||||
|
println!("d1={d1:?} cond2={cond2}");
|
||||||
|
if !d1.is_positive()||cond2{
|
||||||
|
println!("on_exact");
|
||||||
// Found enough information to compute the exact closest point.
|
// Found enough information to compute the exact closest point.
|
||||||
// local norm = direction.unit
|
// local norm = direction.unit
|
||||||
// local dist = a:Dot(norm)
|
// local dist = a:Dot(norm)
|
||||||
@@ -870,6 +907,7 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery<Position=Planar6
|
|||||||
match simplex_big.reduce(mesh,point){
|
match simplex_big.reduce(mesh,point){
|
||||||
// if a and b and c and d then
|
// if a and b and c and d then
|
||||||
Reduce::Escape(simplex)=>{
|
Reduce::Escape(simplex)=>{
|
||||||
|
println!("on_escape");
|
||||||
// Enough information to conclude that the meshes are intersecting.
|
// Enough information to conclude that the meshes are intersecting.
|
||||||
// Topology information is computed if needed.
|
// Topology information is computed if needed.
|
||||||
return on_escape(simplex);
|
return on_escape(simplex);
|
||||||
|
|||||||
174
engine/physics/src/minimum_difference_lua.rs
Normal file
174
engine/physics/src/minimum_difference_lua.rs
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
use mlua::{Lua,FromLuaMulti,IntoLuaMulti,Function,Result as LuaResult,Vector};
|
||||||
|
use strafesnet_common::integer::{Planar64,Planar64Vec3,FixedFromFloatError};
|
||||||
|
|
||||||
|
use crate::mesh_query::MeshQuery;
|
||||||
|
use crate::minkowski::MinkowskiMesh;
|
||||||
|
|
||||||
|
pub fn contains_point(
|
||||||
|
mesh:&MinkowskiMesh,
|
||||||
|
point:Planar64Vec3,
|
||||||
|
)->LuaResult<bool>{
|
||||||
|
Ok(minimum_difference(mesh,point,true)?.hits)
|
||||||
|
}
|
||||||
|
pub fn minimum_difference_details(
|
||||||
|
mesh:&MinkowskiMesh,
|
||||||
|
point:Planar64Vec3,
|
||||||
|
)->LuaResult<(bool,Option<Details>)>{
|
||||||
|
let md=minimum_difference(mesh,point,false)?;
|
||||||
|
Ok((md.hits,md.details))
|
||||||
|
}
|
||||||
|
fn p64v3(v:Vector)->Result<Planar64Vec3,FixedFromFloatError>{
|
||||||
|
Ok(Planar64Vec3::new([
|
||||||
|
v.x().try_into()?,
|
||||||
|
v.y().try_into()?,
|
||||||
|
v.z().try_into()?,
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
fn vec(v:Planar64Vec3)->Vector{
|
||||||
|
Vector::new(v.x.into(),v.y.into(),v.z.into())
|
||||||
|
}
|
||||||
|
struct MinimumDifference{
|
||||||
|
hits:bool,
|
||||||
|
details:Option<Details>
|
||||||
|
}
|
||||||
|
pub struct Details{
|
||||||
|
pub distance:Planar64,
|
||||||
|
pub p_pos:Planar64Vec3,
|
||||||
|
pub p_norm:Planar64Vec3,
|
||||||
|
pub q_pos:Planar64Vec3,
|
||||||
|
pub q_norm:Planar64Vec3,
|
||||||
|
}
|
||||||
|
impl FromLuaMulti for MinimumDifference{
|
||||||
|
fn from_lua_multi(mut values:mlua::MultiValue,_lua:&Lua)->LuaResult<Self>{
|
||||||
|
match values.make_contiguous(){
|
||||||
|
&mut [
|
||||||
|
mlua::Value::Boolean(hits),
|
||||||
|
mlua::Value::Nil,
|
||||||
|
mlua::Value::Nil,
|
||||||
|
mlua::Value::Nil,
|
||||||
|
mlua::Value::Nil,
|
||||||
|
mlua::Value::Nil,
|
||||||
|
]=>Ok(Self{hits,details:None}),
|
||||||
|
&mut [
|
||||||
|
mlua::Value::Boolean(hits),
|
||||||
|
mlua::Value::Number(distance),
|
||||||
|
mlua::Value::Vector(p_pos),
|
||||||
|
mlua::Value::Vector(p_norm),
|
||||||
|
mlua::Value::Vector(q_pos),
|
||||||
|
mlua::Value::Vector(q_norm),
|
||||||
|
]=>Ok(Self{
|
||||||
|
hits,
|
||||||
|
details:Some(Details{
|
||||||
|
distance:distance.try_into().unwrap(),
|
||||||
|
p_pos:p64v3(p_pos).unwrap(),
|
||||||
|
p_norm:p64v3(p_norm).unwrap(),
|
||||||
|
q_pos:p64v3(q_pos).unwrap(),
|
||||||
|
q_norm:p64v3(q_norm).unwrap(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
&mut [
|
||||||
|
mlua::Value::Boolean(hits),
|
||||||
|
mlua::Value::Integer(distance),
|
||||||
|
mlua::Value::Vector(p_pos),
|
||||||
|
mlua::Value::Vector(p_norm),
|
||||||
|
mlua::Value::Vector(q_pos),
|
||||||
|
mlua::Value::Vector(q_norm),
|
||||||
|
]=>Ok(Self{
|
||||||
|
hits,
|
||||||
|
details:Some(Details{
|
||||||
|
distance:distance.into(),
|
||||||
|
p_pos:p64v3(p_pos).unwrap(),
|
||||||
|
p_norm:p64v3(p_norm).unwrap(),
|
||||||
|
q_pos:p64v3(q_pos).unwrap(),
|
||||||
|
q_norm:p64v3(q_norm).unwrap(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
values=>Err(mlua::Error::runtime(format!("Invalid return values: {values:?}"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Args{
|
||||||
|
query_p:Function,
|
||||||
|
radius_p:f64,
|
||||||
|
query_q:Function,
|
||||||
|
radius_q:f64,
|
||||||
|
test_intersection:bool,
|
||||||
|
}
|
||||||
|
impl Args{
|
||||||
|
fn new(
|
||||||
|
lua:&Lua,
|
||||||
|
mesh:&'static MinkowskiMesh<'static>,
|
||||||
|
point:Planar64Vec3,
|
||||||
|
test_intersection:bool,
|
||||||
|
)->LuaResult<Self>{
|
||||||
|
let radius_p=0.0;
|
||||||
|
let radius_q=0.0;
|
||||||
|
// Query the farthest point on the mesh in the given direction.
|
||||||
|
let query_p=lua.create_function(move|_,dir:Option<Vector>|{
|
||||||
|
let Some(dir)=dir else{
|
||||||
|
return Ok(vec(mesh.mesh0.hint_point()));
|
||||||
|
};
|
||||||
|
let dir=p64v3(dir).unwrap();
|
||||||
|
let vert_id=mesh.mesh0.farthest_vert(dir);
|
||||||
|
let dir=mesh.mesh0.vert(vert_id);
|
||||||
|
Ok(vec(dir))
|
||||||
|
})?;
|
||||||
|
// query_q is different since it includes the test point offset.
|
||||||
|
let query_q=lua.create_function(move|_,dir:Option<Vector>|{
|
||||||
|
let Some(dir)=dir else{
|
||||||
|
return Ok(vec(mesh.mesh1.hint_point()+point));
|
||||||
|
};
|
||||||
|
let dir=p64v3(dir).unwrap();
|
||||||
|
let vert_id=mesh.mesh1.farthest_vert(dir);
|
||||||
|
let dir=mesh.mesh1.vert(vert_id)+point;
|
||||||
|
Ok(vec(dir))
|
||||||
|
})?;
|
||||||
|
Ok(Args{
|
||||||
|
query_p,
|
||||||
|
radius_p,
|
||||||
|
query_q,
|
||||||
|
radius_q,
|
||||||
|
test_intersection,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IntoLuaMulti for Args{
|
||||||
|
fn into_lua_multi(self,lua:&Lua)->LuaResult<mlua::MultiValue>{
|
||||||
|
use mlua::IntoLua;
|
||||||
|
Ok(mlua::MultiValue::from_vec(vec![
|
||||||
|
self.query_p.into_lua(lua)?,
|
||||||
|
self.radius_p.into_lua(lua)?,
|
||||||
|
self.query_q.into_lua(lua)?,
|
||||||
|
self.radius_q.into_lua(lua)?,
|
||||||
|
mlua::Value::Nil,
|
||||||
|
self.test_intersection.into_lua(lua)?,
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn minimum_difference(
|
||||||
|
mesh:&MinkowskiMesh,
|
||||||
|
point:Planar64Vec3,
|
||||||
|
test_intersection:bool,
|
||||||
|
)->LuaResult<MinimumDifference>{
|
||||||
|
let ctx=init_lua()?;
|
||||||
|
// SAFETY: mesh lifetime must outlive args usages
|
||||||
|
let mesh=unsafe{core::mem::transmute(mesh)};
|
||||||
|
let args=Args::new(&ctx.lua,mesh,point,test_intersection)?;
|
||||||
|
ctx.f.call(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Ctx{
|
||||||
|
lua:Lua,
|
||||||
|
f:Function,
|
||||||
|
}
|
||||||
|
fn init_lua()->LuaResult<Ctx>{
|
||||||
|
static SOURCE:std::sync::LazyLock<String>=std::sync::LazyLock::new(||std::fs::read_to_string("../../Trey-MinimumDifference.lua").unwrap());
|
||||||
|
let lua=Lua::new();
|
||||||
|
lua.sandbox(true)?;
|
||||||
|
let lib_f=lua.load(SOURCE.as_str()).set_name("Trey-MinimumDifference").into_function()?;
|
||||||
|
let lib:mlua::Table=lib_f.call(())?;
|
||||||
|
let f=lib.raw_get("difference")?;
|
||||||
|
Ok(Ctx{lua,f})
|
||||||
|
}
|
||||||
@@ -80,8 +80,8 @@ pub enum MinkowskiFace{
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MinkowskiMesh<'a>{
|
pub struct MinkowskiMesh<'a>{
|
||||||
mesh0:TransformedMesh<'a>,
|
pub mesh0:TransformedMesh<'a>,
|
||||||
mesh1:TransformedMesh<'a>,
|
pub mesh1:TransformedMesh<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this
|
// TODO: remove this
|
||||||
@@ -99,6 +99,9 @@ impl MinkowskiMesh<'_>{
|
|||||||
mesh1,
|
mesh1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn closest_point(&self,point:Planar64Vec3)->Option<crate::mesh_query::FEV<Self>>{
|
||||||
|
crate::minimum_difference::closest_fev_not_inside(self,point)
|
||||||
|
}
|
||||||
pub fn predict_collision_in(&self,trajectory:&Trajectory,range:impl RangeBounds<Time>)->Option<(MinkowskiFace,GigaTime)>{
|
pub fn predict_collision_in(&self,trajectory:&Trajectory,range:impl RangeBounds<Time>)->Option<(MinkowskiFace,GigaTime)>{
|
||||||
let start_position=match range.start_bound(){
|
let start_position=match range.start_bound(){
|
||||||
Bound::Included(time)=>trajectory.extrapolated_position(*time),
|
Bound::Included(time)=>trajectory.extrapolated_position(*time),
|
||||||
|
|||||||
@@ -478,7 +478,7 @@ impl PhysicsMeshTransform{
|
|||||||
#[derive(Debug,Clone,Copy)]
|
#[derive(Debug,Clone,Copy)]
|
||||||
pub struct TransformedMesh<'a>{
|
pub struct TransformedMesh<'a>{
|
||||||
view:PhysicsMeshView<'a>,
|
view:PhysicsMeshView<'a>,
|
||||||
transform:&'a PhysicsMeshTransform,
|
pub transform:&'a PhysicsMeshTransform,
|
||||||
}
|
}
|
||||||
impl TransformedMesh<'_>{
|
impl TransformedMesh<'_>{
|
||||||
pub const fn new<'a>(
|
pub const fn new<'a>(
|
||||||
|
|||||||
@@ -1003,6 +1003,12 @@ impl PhysicsData{
|
|||||||
hitbox_mesh:StyleModifiers::default().calculate_mesh(),
|
hitbox_mesh:StyleModifiers::default().calculate_mesh(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn closest_point(&self,mesh_id:u32,point:Planar64Vec3)->Option<crate::mesh_query::FEV<MinkowskiMesh<'_>>>{
|
||||||
|
let model_mesh=self.models.mesh(ConvexMeshId{model_id:PhysicsModelId::Contact(ContactModelId(mesh_id)),submesh_id:PhysicsSubmeshId::new(0)});
|
||||||
|
println!("transform={:?}",model_mesh.transform.vertex.matrix3);
|
||||||
|
let minkowski=MinkowskiMesh::minkowski_sum(model_mesh,self.hitbox_mesh.transformed_mesh());
|
||||||
|
minkowski.closest_point(point)
|
||||||
|
}
|
||||||
pub fn new(map:&map::CompleteMap)->Self{
|
pub fn new(map:&map::CompleteMap)->Self{
|
||||||
let modes=map.modes.clone().denormalize();
|
let modes=map.modes.clone().denormalize();
|
||||||
let mut used_contact_attributes=Vec::new();
|
let mut used_contact_attributes=Vec::new();
|
||||||
|
|||||||
@@ -76,3 +76,21 @@ fn physics_bug_3()->Result<(),ReplayError>{
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn physics_bug_26()->Result<(),ReplayError>{
|
||||||
|
println!("loading map file..");
|
||||||
|
let data=read_entire_file("../tools/bhop_maps/5692124338.snfm")?;
|
||||||
|
let map=strafesnet_snf::read_map(data)?.into_complete_map()?;
|
||||||
|
|
||||||
|
// create recording
|
||||||
|
println!("generating models..");
|
||||||
|
let physics_data=PhysicsData::new(&map);
|
||||||
|
println!("reproducing bug...");
|
||||||
|
|
||||||
|
//teleport to bug
|
||||||
|
let fev=physics_data.closest_point(1020,strafesnet_common::integer::vec3::try_from_f32_array([76.889,363.188,-309.263]).unwrap()).unwrap();
|
||||||
|
println!("{fev:?}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user