Skip to content

Commit 2d6da62

Browse files
committed
- fixed up cone functions and added better tests with more cases, modified ray vs capsule with still an outstanding test case, fix lh perspective matrix create function
1 parent 59b13da commit 2d6da62

File tree

3 files changed

+733
-371
lines changed

3 files changed

+733
-371
lines changed

src/lib.rs

Lines changed: 152 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ pub fn closest_point_on_line_segment<T: Float, V: VecFloatOps<T> + VecN<T>>(p: V
490490

491491
/// returns the closest point on the plane to point p wher the plane is defined by point on plane x and normal n
492492
pub fn closest_point_on_plane<T: SignedNumber, V: VecN<T> + SignedVecN<T>>(p: V, x: V, n: V) -> V {
493-
p - n * (V::dot(p, n) - V::dot(x, n))
493+
p + n * -(V::dot(p, n) - V::dot(x, n))
494494
}
495495

496496
/// returns the closest point on the aabb defined by aabb_min and aabb_max to point p, if the point is inside the aabb it will return p
@@ -569,36 +569,22 @@ pub fn closest_point_on_triangle<T: Float + FloatOps<T> + NumberOps<T>, V: VecN<
569569

570570
/// returns the closest point to p on the cone defined by cp position, with direction cv height h an radius r
571571
pub fn closest_point_on_cone<T: Float, V: VecN<T> + VecFloatOps<T>>(p: V, cp: V, cv: V, h: T, r: T) -> V {
572+
// centre point of the cones base (where radius is largest)
572573
let l2 = cp + cv * h;
573-
let dh = distance_on_line(p, cp, l2) / h;
574-
let x0 = closest_point_on_line_segment(p, cp, l2);
575-
let d = dist(x0, p);
576-
if dh >= T::one() {
577-
// clamp to the tip
578-
l2
579-
}
580-
else if dh <= T::zero() {
581-
// clamp to the base
582-
// base plane
583-
let pp = closest_point_on_plane(p, cp, cv);
584-
let vv = pp - x0;
585-
let m = mag(pp - x0);
586-
if m < r {
587-
pp
588-
}
589-
else {
590-
let v = vv / m;
591-
x0 + v * r
592-
}
593-
}
594-
else if d < dh * r {
595-
// inside the code
596-
p
574+
575+
// find point onbase plane and clamp to the extent
576+
let cplane = closest_point_on_plane(p, l2, cv);
577+
let extent = l2 + normalize(cplane - l2) * r;
578+
579+
// test closest point on line with the axis along the side and bottom of the cone
580+
let e1 = closest_point_on_line_segment(p, cp, extent);
581+
let e2 = closest_point_on_line_segment(p, l2, extent);
582+
583+
if dist2(p, e1) < dist2(p, e2) {
584+
e1
597585
}
598586
else {
599-
let v = normalize(p - x0);
600-
// clamp to the radius
601-
x0 + (v * dh * r)
587+
e2
602588
}
603589
}
604590

@@ -705,6 +691,21 @@ pub fn point_inside_frustum<T: Number>(p: Vec3<T>, planes: &[Vec4<T>; 6]) -> boo
705691
}
706692
true
707693
}
694+
695+
/// returns the classification of point p vs the plane defined by point on plane x and normal n
696+
pub fn point_vs_plane<T: SignedNumber + SignedNumberOps<T>>(p: Vec3<T>, x: Vec3<T>, n: Vec3<T>) -> Classification {
697+
let pd = plane_distance(x, n);
698+
let d = dot(n, p) + pd;
699+
if d < T::zero() {
700+
Classification::Behind
701+
}
702+
else if d > T::zero() {
703+
Classification::Infront
704+
}
705+
else {
706+
Classification::Intersects
707+
}
708+
}
708709

709710
/// returns the classification of the 3D aabb defined as aabb_min to aabb_max vs the plane defined by point on plane x and normal n
710711
pub fn aabb_vs_plane<T: SignedNumber + SignedNumberOps<T>>(aabb_min: Vec3<T>, aabb_max: Vec3<T>, x: Vec3<T>, n: Vec3<T>) -> Classification {
@@ -777,16 +778,16 @@ pub fn capsule_vs_plane<T: Float + FloatOps<T> + SignedNumber + SignedNumberOps<
777778

778779
/// return the classification of cone defined by position cp, direction cv with height h and radius at the base of r. vs the plane defined by point x and normal n
779780
pub fn cone_vs_plane<T: Float + SignedNumberOps<T>, V: VecN<T> + Cross<T> + Dot<T> + SignedVecN<T> + VecFloatOps<T>>(cp: V, cv: V, h: T, r: T, x: V, n: V) -> Classification {
780-
let tip = cp + cv * h;
781+
let l2 = cp + cv * h;
781782
let pd = plane_distance(x, n);
782783
// check if the tip and cones extent are on different sides of the plane
783-
let d1 = dot(n, tip) + pd;
784+
let d1 = dot(n, cp) + pd;
784785
// extent from the tip is at the base centre point perp of cv at the radius edge... we need to choose the side toward the plane
785-
let perp = normalize(cross(cross(n, -cv), -cv));
786-
let extent = cp + perp * r;
787-
let extent2 = cp + perp * -r;
788-
let d2 = dot(n, extent);
789-
let d3 = dot(n, extent2);
786+
let perp = normalize(cross(cross(n, cv), cv));
787+
let extent = l2 + perp * r;
788+
let extent2 = l2 + perp * -r;
789+
let d2 = dot(n, extent) + pd;
790+
let d3 = dot(n, extent2) + pd;
790791
if d1 < T::zero() && d2 < T::zero() && d3 < T::zero() {
791792
Classification::Behind
792793
}
@@ -974,11 +975,85 @@ pub fn ray_vs_triangle<T: Float>(r0: Vec3<T>, rv: Vec3<T>, t0: Vec3<T>, t1: Vec3
974975
}
975976

976977
/// returns the intersection point of ray wih origin r0 and direction rv against the capsule with line c0 - c1 and radius cr
977-
pub fn ray_vs_capsule<T: Float + FloatOps<T> + NumberOps<T> + SignedNumberOps<T>, V: VecN<T> + VecFloatOps<T> + SignedNumberOps<T> + FloatOps<T>>(r0: V, rv: V, c0: V, c1: V, cr: T) -> Option<V> {
978-
let sl = shortest_line_segment_between_lines(r0, rv, c0, c1);
979-
if let Some(line) = sl {
980-
if dist2(line.0, line.1) < sqr(cr) {
981-
Some(line.0)
978+
pub fn ray_vs_capsule<T: Float + FloatOps<T> + NumberOps<T> + SignedNumberOps<T>, V: VecN<T> + Cross<T> + VecFloatOps<T> + SignedNumberOps<T> + FloatOps<T>>(r0: V, rv: V, c0: V, c1: V, cr: T) -> Option<V> {
979+
// shortest line seg within radius will indicate we intersect an infinite cylinder about an axis
980+
let seg = shortest_line_segment_between_line_and_line_segment(r0, r0 + rv, c0, c1);
981+
if let Some((l0, l1)) = seg {
982+
// check we intesect the cylinder
983+
let mut ipc = V::max_value();
984+
let mut bc = false;
985+
if dist2(l0, l1) < sqr(cr) {
986+
// intesection of ray and infinite cylinder about axis
987+
// https://stackoverflow.com/questions/4078401/trying-to-optimize-line-vs-cylinder-intersection
988+
let a = c0;
989+
let b = c1;
990+
let v = rv;
991+
let r = cr;
992+
993+
let ab = b - a;
994+
let ao = r0 - a;
995+
let aoxab = cross(ao, ab);
996+
let vxab = cross(v, ab);
997+
let ab2 = dot(ab, ab);
998+
999+
let aa = dot(vxab, vxab);
1000+
let bb = T::two() * dot(vxab, aoxab);
1001+
let cc = dot(aoxab, aoxab) - (r*r * ab2);
1002+
let dd = bb * bb - T::four() * aa * cc;
1003+
1004+
if dd >= T::zero() {
1005+
let t = (-bb - sqrt(dd)) / (T::two() * aa);
1006+
if t >= T::zero() {
1007+
// intersection point
1008+
ipc = r0 + rv * t;
1009+
// clamps to finite cylinder extents
1010+
let ipd = distance_on_line(ipc, a, b);
1011+
if ipd >= T::zero() && ipd <= dist(a, b) {
1012+
bc = true;
1013+
}
1014+
}
1015+
}
1016+
}
1017+
1018+
// if our line doesnt intersect the cylinder, we might still intersect the top / bottom sphere
1019+
// test intersections with the end spheres
1020+
let bs1 = ray_vs_sphere(r0, rv, c0, cr);
1021+
let bs2 = ray_vs_sphere(r0, rv, c1, cr);
1022+
1023+
// get optional intersection points
1024+
let ips1 = if let Some(ip) = bs1 {
1025+
ip
1026+
}
1027+
else {
1028+
V::max_value()
1029+
};
1030+
1031+
let ips2 = if let Some(ip) = bs2 {
1032+
ip
1033+
}
1034+
else {
1035+
V::max_value()
1036+
};
1037+
1038+
// we need to choose the closes intersection if we have multiple
1039+
let ips : [V; 3] = [ips1, ips2, ipc];
1040+
let bips : [bool; 3] = [bs1.is_some(), bs2.is_some(), bc];
1041+
1042+
let mut iclosest = -1;
1043+
let mut dclosest = T::max_value();
1044+
for i in 0..3 {
1045+
if bips[i] {
1046+
let dd = distance_on_line(ips[i], r0, r0 + rv);
1047+
if dd < dclosest {
1048+
iclosest = i as i32;
1049+
dclosest = dd;
1050+
}
1051+
}
1052+
}
1053+
1054+
// if we have a valid closest point
1055+
if iclosest != -1 {
1056+
Some(ips[iclosest as usize])
9821057
}
9831058
else {
9841059
None
@@ -1160,6 +1235,43 @@ pub fn shortest_line_segment_between_lines<T: Float + SignedNumberOps<T> + Float
11601235
))
11611236
}
11621237

1238+
/// returns the shortest line segment between 2 line segments (p1-p2) and (p3-p4) as an option tuple where .0 is the point on line segment 1 and .1 is the point on line segment 2
1239+
pub fn shortest_line_segment_between_line_and_line_segment<T: Float + SignedNumberOps<T> + FloatOps<T>, V: VecN<T> + SignedNumberOps<T> + FloatOps<T> + Dot<T>>(p1: V, p2: V, p3: V, p4: V) -> Option<(V, V)> {
1240+
// https://web.archive.org/web/20120404121511/http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline3d/lineline.c
1241+
1242+
let p13 = p1 - p3;
1243+
let p43 = p4 - p3;
1244+
1245+
if approx(abs(p43), V::zero(), T::small_epsilon()) {
1246+
return None;
1247+
}
1248+
1249+
let p21 = p2 - p1;
1250+
if approx(abs(p21), V::zero(), T::small_epsilon()) {
1251+
return None;
1252+
}
1253+
1254+
let d1343 = dot(p13, p43);
1255+
let d4321 = dot(p43, p21);
1256+
let d1321 = dot(p13, p21);
1257+
let d4343 = dot(p43, p43);
1258+
let d2121 = dot(p21, p21);
1259+
1260+
let denom = d2121 * d4343 - d4321 * d4321;
1261+
if abs(denom) < T::small_epsilon() {
1262+
return None;
1263+
}
1264+
1265+
let numer = d1343 * d4321 - d1321 * d4343;
1266+
let mua = numer / denom;
1267+
let mub = saturate((d1343 + d4321 * mua) / d4343);
1268+
1269+
Some((
1270+
p1 + (p21 * mua),
1271+
p3 + (p43 * mub)
1272+
))
1273+
}
1274+
11631275
/// returns soft clipping (in a cubic fashion) of x; let m be the threshold (anything above m stays unchanged), and n the value things will take when the signal is zero
11641276
pub fn almost_identity<T: Number + Float>(x: T, m: T, n: T) -> T {
11651277
// <https://iquilezles.org/articles/functions/>

src/mat.rs

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,53 +1272,6 @@ impl<T> MatInverse<T> for Mat4<T> where T: SignedNumber {
12721272
}
12731273
}
12741274

1275-
/*
1276-
pub fn create_ortho_matrix(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Mat4f {
1277-
Mat4f::from((
1278-
Vec4f::new(2.0 / (right - left), 0.0, 0.0, 0.0),
1279-
Vec4f::new(0.0, 2.0 / (top - bottom), 0.0, 0.0),
1280-
Vec4f::new(0.0, 0.0, 1.0 / (near - far), 0.0),
1281-
Vec4f::new((right + left) / (left - right), (top + bottom) / (bottom - top), near / (near - far), 1.0),
1282-
))
1283-
}
1284-
1285-
fn create_perspective_matrix_internal_lh(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Mat4f {
1286-
Mat4f::from((
1287-
Vec4f::new((2.0 * near) / (right - left), 0.0, (right + left) / (right - left), 0.0),
1288-
Vec4f::new(0.0, (2.0 * near) / (top - bottom), (top + bottom) / (top - bottom), 0.0),
1289-
Vec4f::new(0.0, 0.0, (-far - near) / (far - near), ((2.0 * near) * far) / (far - near)),
1290-
Vec4f::new(0.0, 0.0, -1.0, 0.0)
1291-
))
1292-
}
1293-
1294-
fn create_perspective_matrix_internal_rh(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Mat4f {
1295-
Mat4f::from((
1296-
Vec4f::new((2.0 * near) / (right - left), 0.0, (right + left) / (right - left), 0.0),
1297-
Vec4f::new(0.0, (2.0 * near) / (top - bottom), (top + bottom) / (top - bottom), 0.0),
1298-
Vec4f::new(0.0, 0.0, (-far - near) / (far - near), (-(2.0 * near) * far) / (far - near)),
1299-
Vec4f::new(0.0, 0.0, 1.0, 0.0)
1300-
))
1301-
}
1302-
1303-
pub fn create_perspective_projection_lh_yup(fov: f32, aspect: f32, near: f32, far: f32) -> Mat4f {
1304-
let tfov = f32::tan(fov * 0.5);
1305-
let right = tfov * aspect * near;
1306-
let left = -right;
1307-
let top = tfov * near;
1308-
let bottom = -top;
1309-
create_perspective_matrix_internal_lh(left, right, bottom, top, near, far)
1310-
}
1311-
1312-
pub fn create_perspective_projection_rh_yup(fov: f32, aspect: f32, near: f32, far: f32) -> Mat4f {
1313-
let tfov = f32::tan(fov * 0.5);
1314-
let right = tfov * aspect * near;
1315-
let left = -right;
1316-
let top = tfov * near;
1317-
let bottom = -top;
1318-
create_perspective_matrix_internal_rh(left, right, bottom, top, near, far)
1319-
}
1320-
*/
1321-
13221275
/// trait for 4x4 projection matrices
13231276
pub trait MatProjection<T> {
13241277
/// returns 6 frustum planes as Vec4's in the form .xyz = normal, .w = plane distance
@@ -1346,7 +1299,7 @@ fn create_perspective_matrix_internal_lh<T: Float + FloatOps<T>>(left: T, right:
13461299
Mat4::from((
13471300
Vec4::new((T::two() * near) / (right - left), T::zero(), (right + left) / (right - left), T::zero()),
13481301
Vec4::new(T::zero(), (T::two() * near) / (top - bottom), (top + bottom) / (top - bottom), T::zero()),
1349-
Vec4::new(T::zero(), T::zero(), (-far - near) / (far - near), ((T::two() * near) * far) / (far - near)),
1302+
Vec4::new(T::zero(), T::zero(), (-far - near) / (far - near), (-(T::two() * near) * far) / (far - near)),
13501303
Vec4::new(T::zero(), T::zero(), T::minus_one(), T::zero())
13511304
))
13521305
}

0 commit comments

Comments
 (0)