From 46ae55e7400e1fbe4b3231be743b76926090f1f6 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Wed, 4 Nov 2020 19:57:52 +0100 Subject: [PATCH 01/42] Added project(pos, pos) --- src/typed-geometry/functions/objects/project.hh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/typed-geometry/functions/objects/project.hh b/src/typed-geometry/functions/objects/project.hh index 5cd838e..a2f7857 100644 --- a/src/typed-geometry/functions/objects/project.hh +++ b/src/typed-geometry/functions/objects/project.hh @@ -60,6 +60,15 @@ template } +// ============== project to point ============== + +template +[[nodiscard]] constexpr pos project(pos const& p, pos const& q) +{ + return q; +} + + // ============== project to line / ray / segment ============== template -- GitLab From 693adea14919b8e60828ed92f8d0bc8fe6206735 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Wed, 4 Nov 2020 20:14:17 +0100 Subject: [PATCH 02/42] Fixed unused warning --- src/typed-geometry/functions/objects/project.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typed-geometry/functions/objects/project.hh b/src/typed-geometry/functions/objects/project.hh index a2f7857..26747e8 100644 --- a/src/typed-geometry/functions/objects/project.hh +++ b/src/typed-geometry/functions/objects/project.hh @@ -63,7 +63,7 @@ template // ============== project to point ============== template -[[nodiscard]] constexpr pos project(pos const& p, pos const& q) +[[nodiscard]] constexpr pos project([[maybe_unused]] pos const& p, pos const& q) { return q; } -- GitLab From 1f9e750a97130832148258738e93d706cc341c4f Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Fri, 6 Nov 2020 00:51:29 +0100 Subject: [PATCH 03/42] Reordered and grouped intersection functions --- .../functions/objects/intersection.hh | 225 +++++++++--------- 1 file changed, 116 insertions(+), 109 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 98425f5..5904783 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -385,6 +385,111 @@ template return t; } +// ray - triangle +template +[[nodiscard]] constexpr optional intersection_parameter(ray<3, ScalarT> const& r, + triangle<3, ScalarT> const& t, + dont_deduce eps = 100 * tg::epsilon) +{ + auto e1 = t.pos1 - t.pos0; + auto e2 = t.pos2 - t.pos0; + + auto pvec = tg::cross(r.dir, e2); + auto det = dot(pvec, e1); + + if (det < ScalarT(0)) + { + std::swap(e1, e2); + pvec = tg::cross(r.dir, e2); + det = -det; + } + + if (det < eps) + return {}; + + auto tvec = r.origin - t.pos0; + auto u = dot(tvec, pvec); + if (u < ScalarT(0) || u > det) + return {}; + + auto qvec = cross(tvec, e1); + auto v = dot(r.dir, qvec); + if (v < ScalarT(0) || v + u > det) + return {}; + + auto lambda = (ScalarT(1) / det) * dot(e2, qvec); + return (lambda > ScalarT(0)) ? lambda : tg::optional(); +} + +// ray - box +template +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, box<3, ScalarT> const& b) +{ + // see https://github.com/gszauer/GamePhysicsCookbook/blob/master/Code/Geometry3D.cpp + + auto const p = b.center - r.origin; + + // TODO: without normalize + auto const X = normalize(b.half_extents[0]); + auto const Y = normalize(b.half_extents[1]); + auto const Z = normalize(b.half_extents[2]); + + auto f = tg::vec3(dot(X, r.dir), // + dot(Y, r.dir), // + dot(Z, r.dir)); + + auto const e = tg::vec3(dot(X, p), // + dot(Y, p), // + dot(Z, p)); + + auto const size = tg::vec3(length(b.half_extents[0]), // + length(b.half_extents[1]), // + length(b.half_extents[2])); + + float t[6] = {0, 0, 0, 0, 0, 0}; + for (int i = 0; i < 3; ++i) + { + if (abs(f[i]) < tg::epsilon / 1000) + { + if (-e[i] - size[i] > 0 || -e[i] + size[i] < 0) + return {}; + + f[i] = tg::epsilon; // Avoid div by 0! + } + + t[i * 2 + 0] = (e[i] + size[i]) / f[i]; // tmin[x, y, z] + t[i * 2 + 1] = (e[i] - size[i]) / f[i]; // tmax[x, y, z] + } + + auto const tmin = tg::max(tg::max(tg::min(t[0], t[1]), tg::min(t[2], t[3])), tg::min(t[4], t[5])); + auto const tmax = tg::min(tg::min(tg::max(t[0], t[1]), tg::max(t[2], t[3])), tg::max(t[4], t[5])); + + // if tmax < 0, ray is intersecting AABB + // but entire AABB is behing it's origin + if (tmax < 0) + return {}; + + // if tmin > tmax, ray doesn't intersect AABB + if (tmin > tmax) + return {}; + + ScalarT hits[2]; + + if (tmin >= 0) // two valid intersections + { + hits[0] = tmin; + hits[1] = tmax; + return {hits, 2}; + } + else // one valid intersection + { + hits[0] = tmax; + return {hits, 1}; + } +} + + +// ====================================== Object - Object Intersections ====================================== // returns intersection circle of sphere and sphere (normal points from a to b) // for now does not work if spheres are identical (result would be a sphere3 again) @@ -613,106 +718,23 @@ template return t; } -template -[[nodiscard]] constexpr optional intersection_parameter(ray<3, ScalarT> const& r, - triangle<3, ScalarT> const& t, - dont_deduce eps = 100 * tg::epsilon) +template +[[nodiscard]] constexpr optional> intersection(segment const& a, sphere const& b) { - auto e1 = t.pos1 - t.pos0; - auto e2 = t.pos2 - t.pos0; - - auto pvec = tg::cross(r.dir, e2); - auto det = dot(pvec, e1); - - if (det < ScalarT(0)) - { - std::swap(e1, e2); - pvec = tg::cross(r.dir, e2); - det = -det; - } - - if (det < eps) - return {}; - - auto tvec = r.origin - t.pos0; - auto u = dot(tvec, pvec); - if (u < ScalarT(0) || u > det) - return {}; - - auto qvec = cross(tvec, e1); - auto v = dot(r.dir, qvec); - if (v < ScalarT(0) || v + u > det) - return {}; - - auto lambda = (ScalarT(1) / det) * dot(e2, qvec); - return (lambda > ScalarT(0)) ? lambda : tg::optional(); + static_assert(always_false, "not implemented"); + (void)a; + (void)b; + return {}; // TODO } -template -[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, box<3, ScalarT> const& b) +template +[[nodiscard]] constexpr optional> intersection(sphere const& b, segment const& a) { - // see https://github.com/gszauer/GamePhysicsCookbook/blob/master/Code/Geometry3D.cpp - - auto const p = b.center - r.origin; - - // TODO: without normalize - auto const X = normalize(b.half_extents[0]); - auto const Y = normalize(b.half_extents[1]); - auto const Z = normalize(b.half_extents[2]); - - auto f = tg::vec3(dot(X, r.dir), // - dot(Y, r.dir), // - dot(Z, r.dir)); - - auto const e = tg::vec3(dot(X, p), // - dot(Y, p), // - dot(Z, p)); - - auto const size = tg::vec3(length(b.half_extents[0]), // - length(b.half_extents[1]), // - length(b.half_extents[2])); - - float t[6] = {0, 0, 0, 0, 0, 0}; - for (int i = 0; i < 3; ++i) - { - if (abs(f[i]) < tg::epsilon / 1000) - { - if (-e[i] - size[i] > 0 || -e[i] + size[i] < 0) - return {}; - - f[i] = tg::epsilon; // Avoid div by 0! - } - - t[i * 2 + 0] = (e[i] + size[i]) / f[i]; // tmin[x, y, z] - t[i * 2 + 1] = (e[i] - size[i]) / f[i]; // tmax[x, y, z] - } - - auto const tmin = tg::max(tg::max(tg::min(t[0], t[1]), tg::min(t[2], t[3])), tg::min(t[4], t[5])); - auto const tmax = tg::min(tg::min(tg::max(t[0], t[1]), tg::max(t[2], t[3])), tg::max(t[4], t[5])); - - // if tmax < 0, ray is intersecting AABB - // but entire AABB is behing it's origin - if (tmax < 0) - return {}; + return intersection(b, a); +} - // if tmin > tmax, ray doesn't intersect AABB - if (tmin > tmax) - return {}; - ScalarT hits[2]; - - if (tmin >= 0) // two valid intersections - { - hits[0] = tmin; - hits[1] = tmax; - return {hits, 2}; - } - else // one valid intersection - { - hits[0] = tmax; - return {hits, 1}; - } -} +// ====================================== Checks if Object Intersects aabb ====================================== template [[nodiscard]] constexpr bool intersects(sphere const& a, aabb const& b) @@ -759,21 +781,6 @@ template return intersects(b, a); } -template -[[nodiscard]] constexpr optional> intersection(segment const& a, sphere const& b) -{ - static_assert(always_false, "not implemented"); - (void)a; - (void)b; - return {}; // TODO -} - -template -[[nodiscard]] constexpr optional> intersection(sphere const& b, segment const& a) -{ - return intersection(b, a); -} - template [[nodiscard]] constexpr bool intersects(triangle<2, ScalarT> const& a, aabb<2, ScalarT> const& b) { -- GitLab From 089bef8dadb82bb3f1bfd424b03fcb47d7882959 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Fri, 6 Nov 2020 01:22:02 +0100 Subject: [PATCH 04/42] Some slight code simplifications --- .../functions/objects/intersection.hh | 37 +++++++------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 5904783..a19a5f9 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -191,7 +191,7 @@ template // = p.dis // t = (p.dis - ) / - auto t = (p.dis - dot(p.normal, vec(r.origin))) / dotND; + auto t = (p.dis - dot(p.normal, r.origin)) / dotND; // check whether plane lies behind ray if (t < 0) @@ -364,10 +364,10 @@ template template [[nodiscard]] constexpr optional closest_intersection_parameter(ray<3, ScalarT> const& r, cylinder<3, ScalarT> const& c) { - auto const dir = direction(c); - auto const t_cyl = closest_intersection_parameter(r, cylinder_boundary_no_caps<3, ScalarT>(c.axis, c.radius)); - auto const t_cap0 = intersection_parameter(r, sphere<2, ScalarT, 3>(c.axis.pos0, c.radius, dir)); - auto const t_cap1 = intersection_parameter(r, sphere<2, ScalarT, 3>(c.axis.pos1, c.radius, dir)); + const auto caps = caps_of(c); + const auto t_cap0 = intersection_parameter(r, caps[0]); + const auto t_cap1 = intersection_parameter(r, caps[1]); + const auto t_cyl = closest_intersection_parameter(r, boundary_no_caps_of(c)); optional t; @@ -696,7 +696,7 @@ template template [[nodiscard]] constexpr ScalarT intersection_parameter(line<3, ScalarT> const& l, plane<3, ScalarT> const& p) { - return (p.dis - dot(l.pos - pos<3, ScalarT>::zero, p.normal)) / dot(l.dir, p.normal); + return (p.dis - dot(l.pos, p.normal)) / dot(l.dir, p.normal); } template @@ -712,7 +712,7 @@ template if (denom == ScalarT(0)) return {}; - auto t = (p.dis - dot(p.normal, a.pos0 - tg::pos::zero)) / denom; + auto t = (p.dis - dot(p.normal, a.pos0)) / denom; if (t < 0 || t > 1) return {}; return t; @@ -784,23 +784,16 @@ template template [[nodiscard]] constexpr bool intersects(triangle<2, ScalarT> const& a, aabb<2, ScalarT> const& b) { + if (!intersects(aabb_of(a), b)) + return false; + auto p0 = a.pos0; auto p1 = a.pos1; auto p2 = a.pos2; - - auto bb_t = aabb_of(p0, p1, p2); - if (!intersects(bb_t, b)) - return false; - if (contains(b, p0) || contains(b, p1) || contains(b, p2)) return true; - pos<2, ScalarT> aabb_pts[] = { - {b.min.x, b.min.y}, // - {b.min.x, b.max.y}, // - {b.max.x, b.min.y}, // - {b.max.x, b.max.y}, // - }; + auto aabb_pts = vertices_of(b); auto const is_separate = [&](pos<2, ScalarT> pa, vec<2, ScalarT> n, pos<2, ScalarT> pb) { auto da = dot(n, pa); @@ -847,7 +840,7 @@ template using pos_t = pos<3, ScalarT>; using vec_t = vec<3, ScalarT>; - auto const center = (bb_in.max + bb_in.min) / ScalarT(2); + auto const center = centroid_of(bb_in); auto const amin = pos_t(bb_in.min - center); auto const amax = pos_t(bb_in.max - center); auto const bb = aabb<3, ScalarT>(amin, amax); @@ -868,12 +861,8 @@ template b.min.z < p.z && p.z < b.max.z; }; - auto const contains_p0 = proper_contains(bb, p0); - auto const contains_p1 = proper_contains(bb, p1); - auto const contains_p2 = proper_contains(bb, p2); - // early in: tri points vs AABB - if (contains_p0 || contains_p1 || contains_p2) + if (proper_contains(bb, p0) || proper_contains(bb, p1) || proper_contains(bb, p2)) return true; // get adjusted tri base plane -- GitLab From 82ee1b49a081c6b62cb6f63a4e7d6d4edbcadc3e Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Fri, 6 Nov 2020 13:52:14 +0100 Subject: [PATCH 05/42] Moved cylinder_boundary_no_caps intersection next to cylinder intersection --- .../functions/objects/intersection.hh | 100 +++++++++--------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index a19a5f9..6fa73ac 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -252,56 +252,6 @@ template return {}; } -// ray - tube -template -[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, cylinder_boundary_no_caps<3, ScalarT> const& c) -{ - auto cdir = direction(c); - auto cosA = dot(cdir, r.dir); - auto sinA_sqr = 1 - cosA * cosA; - - if (sinA_sqr <= 0) - return {}; - - // compute closest points of the two lines - auto origDiff = r.origin - c.axis.pos0; - auto fRay = dot(r.dir, origDiff); - auto fLine = dot(cdir, origDiff); - auto tRay = (cosA * fLine - fRay) / sinA_sqr; - auto tLine = (fLine - cosA * fRay) / sinA_sqr; - - auto closest_on_ray = r.origin + tRay * r.dir; - auto closest_on_line = c.axis.pos0 + tLine * cdir; - auto line_ray_dist_sqr = distance_sqr(closest_on_ray, closest_on_line); - auto cyl_radius_sqr = c.radius * c.radius; - - if (line_ray_dist_sqr > cyl_radius_sqr) - return {}; - - // radius in 2D slice - auto r_2D = sqrt(cyl_radius_sqr - line_ray_dist_sqr); - - // infinite tube intersection - auto s = r_2D / sqrt(sinA_sqr); - auto cyl_intersection_0 = closest_on_ray - s * r.dir; - auto cyl_intersection_1 = closest_on_ray + s * r.dir; - - // project onto line segment - auto line_length = length(c.axis); - auto lambda_0 = dot(cyl_intersection_0 - c.axis.pos0, cdir); - auto lambda_1 = dot(cyl_intersection_1 - c.axis.pos0, cdir); - - ScalarT hits[2]; - int hit_cnt = 0; - - if (tRay - s >= 0 && 0 <= lambda_0 && lambda_0 < line_length) - hits[hit_cnt++] = tRay - s; - if (tRay + s >= 0 && 0 <= lambda_1 && lambda_1 < line_length) - hits[hit_cnt++] = tRay + s; - - return {hits, hit_cnt}; -} - // ray - disk template [[nodiscard]] constexpr optional intersection_parameter(ray<3, ScalarT> const& r, sphere<2, ScalarT, 3> const& d) @@ -385,6 +335,56 @@ template return t; } +// ray - tube +template +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, cylinder_boundary_no_caps<3, ScalarT> const& c) +{ + auto cdir = direction(c); + auto cosA = dot(cdir, r.dir); + auto sinA_sqr = 1 - cosA * cosA; + + if (sinA_sqr <= 0) + return {}; + + // compute closest points of the two lines + auto origDiff = r.origin - c.axis.pos0; + auto fRay = dot(r.dir, origDiff); + auto fLine = dot(cdir, origDiff); + auto tRay = (cosA * fLine - fRay) / sinA_sqr; + auto tLine = (fLine - cosA * fRay) / sinA_sqr; + + auto closest_on_ray = r.origin + tRay * r.dir; + auto closest_on_line = c.axis.pos0 + tLine * cdir; + auto line_ray_dist_sqr = distance_sqr(closest_on_ray, closest_on_line); + auto cyl_radius_sqr = c.radius * c.radius; + + if (line_ray_dist_sqr > cyl_radius_sqr) + return {}; + + // radius in 2D slice + auto r_2D = sqrt(cyl_radius_sqr - line_ray_dist_sqr); + + // infinite tube intersection + auto s = r_2D / sqrt(sinA_sqr); + auto cyl_intersection_0 = closest_on_ray - s * r.dir; + auto cyl_intersection_1 = closest_on_ray + s * r.dir; + + // project onto line segment + auto line_length = length(c.axis); + auto lambda_0 = dot(cyl_intersection_0 - c.axis.pos0, cdir); + auto lambda_1 = dot(cyl_intersection_1 - c.axis.pos0, cdir); + + ScalarT hits[2]; + int hit_cnt = 0; + + if (tRay - s >= 0 && 0 <= lambda_0 && lambda_0 < line_length) + hits[hit_cnt++] = tRay - s; + if (tRay + s >= 0 && 0 <= lambda_1 && lambda_1 < line_length) + hits[hit_cnt++] = tRay + s; + + return {hits, hit_cnt}; +} + // ray - triangle template [[nodiscard]] constexpr optional intersection_parameter(ray<3, ScalarT> const& r, -- GitLab From b8342f1998704bec1e13a4902228493560b19c88 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Sat, 7 Nov 2020 15:06:50 +0100 Subject: [PATCH 06/42] Added variadic constructor to ray_hits; Changed all intersection_parameter(ray, obj) to return ray_hits --- .../functions/objects/intersection.hh | 104 +++++++++--------- 1 file changed, 49 insertions(+), 55 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 6fa73ac..aac22b2 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -27,16 +27,16 @@ // intersects(a, b) -> bool // intersection(a, b) -> ??? // intersection_safe(a, b) -> optional -// intersection_parameter(a, b) -> coords? +// intersection_parameter(a, b) -> coords? (for a ray: ray_hits) // intersection_parameters(a, b) -> pair? // intersection_exact(a, b) -> variant -// closest_intersection(a, b) -> ??? -// closest_intersection_parameter(a, b) -> coords? +// closest_intersection(a, b) -> position/object (for a ray: optional) +// closest_intersection_parameter(a, b) -> coords (for a ray: optional) // "intersects" returns true iff any point lies in a and in b // "intersection" returns an object describing the intersection (NOTE: does NOT handle degenerate cases) // "intersection_safe" is the same as "intersection" but returns nullopt for degenerate cases -// "intersection_parameter" returns a coordinates for the first object such that a[coords] == intersection(a, b) +// "intersection_parameter" returns coordinates for the first object such that a[coords] == intersection(a, b) // "intersection_parameters" returns coordinates for both objects // "intersection_exact" returns a variant type describing all possible intersections, including degenerate cases // the "closest_" variants only return the closest intersection for objects where that concept is applicable (e.g. for rays) @@ -46,6 +46,11 @@ // - intersection_safe is currently unsupported // - for more elaborate ray-tracing, a future ray_cast function will exist (which also returns the intersection normal) +// Implementation guidelines: +// explicit intersection_parameter(ray, obj), which gives closest_intersection_parameter and intersection position +// explicit closest_intersection_parameter(ray, obj) if this is faster than computing all intersections +// explicit intersects(obj, aabb), which gives intersects(aabb, obj) + namespace tg { @@ -65,17 +70,22 @@ struct ray_hits template using as_ray_hits = ray_hits; - int size() const { return _size; } - bool any() const { return _size > 0; } + [[nodiscard]] int size() const { return _size; } + [[nodiscard]] bool any() const { return _size > 0; } - HitT const& operator[](int idx) + HitT const& operator[](int idx) const { TG_ASSERT(0 <= idx && idx < _size); return _hit[idx]; } + [[nodiscard]] HitT const& first() const + { + TG_ASSERT(_size > 0); + return _hit[0]; + } - HitT const* begin() const { return _hit; } - HitT const* end() const { return _hit + _size; } + [[nodiscard]] HitT const* begin() const { return _hit; } + [[nodiscard]] HitT const* end() const { return _hit + _size; } ray_hits() = default; ray_hits(HitT* hits, int size) : _size(size) @@ -83,6 +93,10 @@ struct ray_hits for (auto i = 0; i < size; ++i) _hit[i] = hits[i]; } + template + ray_hits(HitTs... hits) : _size(sizeof...(HitTs)), _hit{hits...} + { + } private: int _size = 0; @@ -100,6 +114,13 @@ template return intersection(a, b).has_value(); } +// if ray_hits intersection parameter is available and applicable, use that +template +[[nodiscard]] constexpr auto intersects(ray const& r, Obj const& obj) -> decltype(intersection_parameter(r, obj).any()) +{ + return intersection_parameter(r, obj).any(); +} + // if a value-typed intersection parameter is available and applicable, use that template [[nodiscard]] constexpr auto intersection(A const& a, B const& b) -> decltype(a[intersection_parameter(a, b)]) @@ -151,7 +172,7 @@ template { auto hits = intersection_parameter(r, obj); if (hits.any()) - return hits[0]; + return hits.first(); return {}; } @@ -178,7 +199,7 @@ constexpr optional> intersection(Obj const& obj, pos // ray - plane template -[[nodiscard]] constexpr optional intersection_parameter(ray const& r, plane const& p) +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray const& r, plane const& p) { // if plane normal and ray direction are parallel there is no intersection auto dotND = dot(p.normal, r.dir); @@ -194,7 +215,7 @@ template auto t = (p.dis - dot(p.normal, r.origin)) / dotND; // check whether plane lies behind ray - if (t < 0) + if (t < ScalarT(0)) return {}; return t; @@ -234,33 +255,24 @@ template auto dt = sqrt(r_sqr - d_sqr); - ScalarT hits[2]; - if (t - dt >= 0) - { - hits[0] = t - dt; - hits[1] = t + dt; - return {hits, 2}; - } + return {t - dt, t + dt}; if (t + dt >= 0) - { - hits[0] = t + dt; - return {hits, 1}; - } + return {t + dt}; return {}; } // ray - disk template -[[nodiscard]] constexpr optional intersection_parameter(ray<3, ScalarT> const& r, sphere<2, ScalarT, 3> const& d) +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, sphere<2, ScalarT, 3> const& d) { auto const t = intersection_parameter(r, plane<3, ScalarT>(d.normal, d.center)); - if (!t.has_value()) + if (!t.any()) return {}; - auto const p = r[t.value()]; + auto const p = r[t.first()]; if (distance_sqr(p, d.center) > d.radius * d.radius) return {}; @@ -292,27 +304,18 @@ template auto tMin = min(t1, t2); auto tMax = max(t1, t2); - ScalarT hits[2]; - if (tMin >= ScalarT(0)) - { - hits[0] = tMin; - hits[1] = tMax; - return {hits, 2}; - } + return {tMin, tMax}; if (tMax >= ScalarT(0)) - { - hits[0] = tMax; - return {hits, 1}; - } + return tMax; return {}; } // ray - cylinder template -[[nodiscard]] constexpr optional closest_intersection_parameter(ray<3, ScalarT> const& r, cylinder<3, ScalarT> const& c) +[[nodiscard]] constexpr optional closest_intersection_parameter(ray<3, ScalarT> const& r, cylinder_boundary<3, ScalarT> const& c) { const auto caps = caps_of(c); const auto t_cap0 = intersection_parameter(r, caps[0]); @@ -324,11 +327,11 @@ template if (t_cyl.has_value()) t = t_cyl.value(); - if (t_cap0.has_value() && (!t.has_value() || t.value() > t_cap0.value())) - t = t_cap0; + if (t_cap0.any() && (!t.has_value() || t.value() > t_cap0.first())) + t = t_cap0.first(); - if (t_cap1.has_value() && (!t.has_value() || t.value() > t_cap1.value())) - t = t_cap1; + if (t_cap1.any() && (!t.has_value() || t.value() > t_cap1.first())) + t = t_cap1.first(); TG_INTERNAL_ASSERT(!t.has_value() || t.value() >= 0); @@ -387,7 +390,7 @@ template // ray - triangle template -[[nodiscard]] constexpr optional intersection_parameter(ray<3, ScalarT> const& r, +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, triangle<3, ScalarT> const& t, dont_deduce eps = 100 * tg::epsilon) { @@ -418,7 +421,7 @@ template return {}; auto lambda = (ScalarT(1) / det) * dot(e2, qvec); - return (lambda > ScalarT(0)) ? lambda : tg::optional(); + return (lambda > ScalarT(0)) ? lambda : ray_hits<1, ScalarT>(); } // ray - box @@ -473,19 +476,10 @@ template if (tmin > tmax) return {}; - ScalarT hits[2]; - if (tmin >= 0) // two valid intersections - { - hits[0] = tmin; - hits[1] = tmax; - return {hits, 2}; - } + return {tmin, tmax}; else // one valid intersection - { - hits[0] = tmax; - return {hits, 1}; - } + return tmax; } -- GitLab From 2d80567b1f6538d1f8e93e2e75dc7716a2984fe6 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Sat, 7 Nov 2020 15:16:37 +0100 Subject: [PATCH 07/42] Added ray2 - ray2 intersection --- src/typed-geometry/functions/objects/intersection.hh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index aac22b2..82c3c73 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -197,6 +197,17 @@ constexpr optional> intersection(Obj const& obj, pos // ====================================== Ray - Object Intersections ====================================== +// ray - ray +template +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<2, ScalarT> const& r0, ray<2, ScalarT> const& r1) +{ + auto M = mat<2, 2, ScalarT>::from_cols(r0.dir, -r1.dir); + auto t = inverse(M) * (r1.origin - r0.origin); + if (t.x < ScalarT(0) || t.y < ScalarT(0)) + return {}; + return t.x; +} + // ray - plane template [[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray const& r, plane const& p) -- GitLab From 1c62c5d3f03d23aead25bdbc35d5e46f38cc8f6d Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Sat, 7 Nov 2020 15:48:21 +0100 Subject: [PATCH 08/42] Added ray2 - line2 and ray2 - segment2 intersections --- .../functions/objects/intersection.hh | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 82c3c73..1fd6855 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -197,6 +197,17 @@ constexpr optional> intersection(Obj const& obj, pos // ====================================== Ray - Object Intersections ====================================== +// ray - line +template +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<2, ScalarT> const& r, line<2, ScalarT> const& l) +{ + auto M = mat<2, 2, ScalarT>::from_cols(r.dir, -l.dir); + auto t = inverse(M) * (l.pos - r.origin); + if (t.x < ScalarT(0)) + return {}; + return t.x; +} + // ray - ray template [[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<2, ScalarT> const& r0, ray<2, ScalarT> const& r1) @@ -208,6 +219,17 @@ template return t.x; } +// ray - segment +template +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<2, ScalarT> const& r, segment<2, ScalarT> const& s) +{ + auto M = mat<2, 2, ScalarT>::from_cols(r.dir, s.pos0 - s.pos1); + auto t = inverse(M) * (s.pos0 - r.origin); + if (t.x < ScalarT(0) || t.y < ScalarT(0) || t.y > ScalarT(1)) + return {}; + return t.x; +} + // ray - plane template [[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray const& r, plane const& p) -- GitLab From c93c3f4435187d8a012d5217f6342cc9205663de Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Sun, 8 Nov 2020 22:58:17 +0100 Subject: [PATCH 09/42] Default intersects(aabb, obj) implementation with reversed parameter order --- .../functions/objects/intersection.hh | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 1fd6855..c8e0544 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -47,7 +47,7 @@ // - for more elaborate ray-tracing, a future ray_cast function will exist (which also returns the intersection normal) // Implementation guidelines: -// explicit intersection_parameter(ray, obj), which gives closest_intersection_parameter and intersection position +// explicit intersection_parameter(ray, obj), which gives closest_intersection_parameter and intersection position(s) // explicit closest_intersection_parameter(ray, obj) if this is faster than computing all intersections // explicit intersects(obj, aabb), which gives intersects(aabb, obj) @@ -121,6 +121,19 @@ template return intersection_parameter(r, obj).any(); } +// parameters for intersects with aabb can switch order +template +[[nodiscard]] constexpr bool intersects(aabb const& b, Obj const& obj) +{ + return intersects(obj, b); +} +// Explicit intersects aabb aabb to prevent infinite recursion +template +[[nodiscard]] constexpr bool intersects(aabb const& a, aabb const& b) +{ + return intersection(a, b).has_value(); +} + // if a value-typed intersection parameter is available and applicable, use that template [[nodiscard]] constexpr auto intersection(A const& a, B const& b) -> decltype(a[intersection_parameter(a, b)]) @@ -802,11 +815,6 @@ template return d_min <= a.radius * a.radius; } -template -[[nodiscard]] constexpr bool intersects(aabb const& a, sphere const& b) -{ - return intersects(b, a); -} template [[nodiscard]] constexpr bool intersects(triangle<2, ScalarT> const& a, aabb<2, ScalarT> const& b) @@ -854,11 +862,6 @@ template return true; } -template -[[nodiscard]] constexpr bool intersects(aabb<2, ScalarT> const& a, triangle<2, ScalarT> const& b) -{ - return intersects(b, a); -} // NOTE: does NOT work for integer objects template @@ -960,10 +963,5 @@ template // found no separating axis? -> intersection return true; } -template -[[nodiscard]] constexpr bool intersects(aabb<3, ScalarT> const& a, triangle<3, ScalarT> const& b) -{ - return intersects(b, a); -} } // namespace tg -- GitLab From 93b920c4117cc40a0d5f9518748b0ba3284f5a91 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Sun, 8 Nov 2020 23:33:15 +0100 Subject: [PATCH 10/42] Added ray - halfspace intersection --- .../functions/objects/intersection.hh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index c8e0544..c0cb20d 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -214,6 +214,7 @@ constexpr optional> intersection(Obj const& obj, pos template [[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<2, ScalarT> const& r, line<2, ScalarT> const& l) { + // r.origin + r.dir * t.x == l.pos + l.dir * t.y <=> (r.dir | -l.dir) * (t.x | t.y)^T == l.pos - r.origin auto M = mat<2, 2, ScalarT>::from_cols(r.dir, -l.dir); auto t = inverse(M) * (l.pos - r.origin); if (t.x < ScalarT(0)) @@ -267,6 +268,23 @@ template return t; } +// ray - halfspace +template +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray const& r, halfspace const& h) +{ + // check if ray origin is already contained in the halfspace + const auto dist = signed_distance(r.origin, h); + if (dist <= ScalarT(0)) + return ScalarT(0); + + // if ray points away from the halfspace there is no intersection + auto dotND = dot(h.normal, r.dir); + if (dotND >= ScalarT(0)) + return {}; + + return - dist / dotND; +} + // returns closest intersection point(s) of ray and sphere template [[nodiscard]] constexpr optional closest_intersection_parameter(ray const& r, sphere const& s) -- GitLab From 4b859ba49ce3b1f3667aa541563bec55e37c54cf Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 9 Nov 2020 13:48:22 +0100 Subject: [PATCH 11/42] Added ray2 - triangle2 intersection --- .../functions/objects/intersection.hh | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index c0cb20d..4411f81 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -452,7 +452,34 @@ template return {hits, hit_cnt}; } -// ray - triangle +// ray - triangle2 +template +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<2, ScalarT> const& r, triangle<2, ScalarT> const& t) +{ + ScalarT closestIntersection = tg::max(); + auto numIntersections = 0; + for (const auto& edge : edges_of(t)) + { + const auto inter = intersection_parameter(r, edge); + if (inter.any()) + { + numIntersections++; + closestIntersection = min(closestIntersection, inter.first()); + } + } + + if (numIntersections == 0) + return {}; // No intersection + if (numIntersections == 1) + { + // ray started within the triangle + TG_ASSERT(contains(t, r.origin)); + return ScalarT(0); + } + return closestIntersection; +} + +// ray - triangle3 template [[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, triangle<3, ScalarT> const& t, -- GitLab From e7bfa13fcb36690637a923a9d4928898eab97b7c Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 9 Nov 2020 23:44:58 +0100 Subject: [PATCH 12/42] Fixed contains(halfspace3) shortcut selection --- src/typed-geometry/functions/objects/contains.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typed-geometry/functions/objects/contains.hh b/src/typed-geometry/functions/objects/contains.hh index 123d9ef..46c1878 100644 --- a/src/typed-geometry/functions/objects/contains.hh +++ b/src/typed-geometry/functions/objects/contains.hh @@ -260,7 +260,7 @@ template } template -[[nodiscard]] constexpr bool contains(halfspace const& h, pos<2, ScalarT> const& p, dont_deduce eps = ScalarT(0)) +[[nodiscard]] constexpr bool contains(halfspace const& h, pos const& p, dont_deduce eps = ScalarT(0)) { return signed_distance(p, h) <= eps; } -- GitLab From f80f3eeb06fa32865e283c080640f5d450062f7e Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 9 Nov 2020 23:55:14 +0100 Subject: [PATCH 13/42] Introduced ray_interval return type for solid intersections; Added ray aabb intersection --- .../functions/objects/intersection.hh | 134 ++++++++++++++---- src/typed-geometry/types/objects/aabb.hh | 1 + 2 files changed, 107 insertions(+), 28 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 4411f81..6bb4693 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -27,7 +27,7 @@ // intersects(a, b) -> bool // intersection(a, b) -> ??? // intersection_safe(a, b) -> optional -// intersection_parameter(a, b) -> coords? (for a ray: ray_hits) +// intersection_parameter(a, b) -> coords? (for a ray: ray_hits or optional (when b is solid)) // intersection_parameters(a, b) -> pair? // intersection_exact(a, b) -> variant // closest_intersection(a, b) -> position/object (for a ray: optional) @@ -48,6 +48,7 @@ // Implementation guidelines: // explicit intersection_parameter(ray, obj), which gives closest_intersection_parameter and intersection position(s) +// intersection_parameter(ray, obj_boundary) already gives intersection_parameter(ray, obj) when boundary_of(obj) is defined and object is convex // explicit closest_intersection_parameter(ray, obj) if this is faster than computing all intersections // explicit intersects(obj, aabb), which gives intersects(aabb, obj) @@ -103,6 +104,16 @@ private: HitT _hit[MaxHits]; }; +/// describes a continuous interval on a ray between start and end +template +struct ray_interval +{ + ScalarT start; + ScalarT end; + + [[nodiscard]] constexpr bool is_unbounded() const { return end == tg::max(); } +}; + // ====================================== Default Implementations ====================================== // TODO: intersection_parameter from intersection_parameters @@ -114,11 +125,11 @@ template return intersection(a, b).has_value(); } -// if ray_hits intersection parameter is available and applicable, use that +// if closest intersection parameter is available and applicable, use that template -[[nodiscard]] constexpr auto intersects(ray const& r, Obj const& obj) -> decltype(intersection_parameter(r, obj).any()) +[[nodiscard]] constexpr auto intersects(ray const& r, Obj const& obj) -> decltype(closest_intersection_parameter(r, obj).has_value()) { - return intersection_parameter(r, obj).any(); + return closest_intersection_parameter(r, obj).has_value(); } // parameters for intersects with aabb can switch order @@ -178,6 +189,17 @@ template return {hits, ts.size()}; } +// if an optional ray_interval intersection parameter is available, use that +template >(), std::declval())), optional>>>> +[[nodiscard]] constexpr optional> intersection(ray const& r, Obj const& obj) +{ + auto ts = intersection_parameter(r, obj); + if (ts.has_value()) + return {r[ts.value().start], r[ts.value().end]}; + return {}; +} + // if ray_hits intersection parameter is available, use that template [[nodiscard]] constexpr auto closest_intersection_parameter(ray const& r, Obj const& obj) @@ -189,6 +211,37 @@ template return {}; } +// if ray_interval intersection parameter is available, use that +template +[[nodiscard]] constexpr auto closest_intersection_parameter(ray const& r, Obj const& obj) + -> enable_if>>, optional> +{ + auto hits = intersection_parameter(r, obj); + if (hits.has_value()) + return hits.value().start; + return {}; +} + +// if boundary_of a solid object returns ray_hits, use this to construct the ray_interval result of the solid intersection +template +[[nodiscard]] constexpr auto intersection_parameter(ray const& r, Obj const& obj) + -> enable_if>, optional>> +{ + const auto inter = intersection_parameter(r, boundary_of(obj)); + + if (inter.size() == 0) + return {}; + + if (inter.size() == 1) + { + TG_ASSERT(contains(obj, r.origin)); + return {{typename Obj::scalar_t(0), inter.first()}}; + } + + TG_ASSERT(inter.size() == 2); + return {{inter[0], inter[1]}}; +} + // intersection between point and obj is same as contains template >(), std::declval()))>> constexpr optional> intersection(pos const& p, Obj const& obj) @@ -248,8 +301,8 @@ template template [[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray const& r, plane const& p) { - // if plane normal and ray direction are parallel there is no intersection - auto dotND = dot(p.normal, r.dir); + // if plane normal and ray direction are orthogonal there is no intersection + const auto dotND = dot(p.normal, r.dir); if (dotND == ScalarT(0)) return {}; @@ -259,7 +312,7 @@ template // = p.dis // t = (p.dis - ) / - auto t = (p.dis - dot(p.normal, r.origin)) / dotND; + const auto t = (p.dis - dot(p.normal, r.origin)) / dotND; // check whether plane lies behind ray if (t < ScalarT(0)) @@ -270,7 +323,26 @@ template // ray - halfspace template -[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray const& r, halfspace const& h) +[[nodiscard]] constexpr optional> intersection_parameter(ray const& r, halfspace const& h) +{ + // check if ray origin is already contained in the halfspace + const auto dotND = dot(h.normal, r.dir); + const auto dist = signed_distance(r.origin, h); + if (dist <= ScalarT(0)) + { + if (dotND <= ScalarT(0)) // if ray point away from the surface, the entire ray intersects + return {{ScalarT(0), tg::max()}}; + return {{ScalarT(0), -dist / dotND}}; // otherwise the segment from ray origin until surface intersection is contained + } + + // if ray points away from the halfspace there is no intersection + if (dotND >= ScalarT(0)) + return {}; + + return {{-dist / dotND, tg::max()}}; +} +template +[[nodiscard]] constexpr optional closest_intersection_parameter(ray const& r, halfspace const& h) { // check if ray origin is already contained in the halfspace const auto dist = signed_distance(r.origin, h); @@ -278,37 +350,41 @@ template return ScalarT(0); // if ray points away from the halfspace there is no intersection - auto dotND = dot(h.normal, r.dir); + const auto dotND = dot(h.normal, r.dir); if (dotND >= ScalarT(0)) return {}; - return - dist / dotND; + return -dist / dotND; } -// returns closest intersection point(s) of ray and sphere +// ray - aabb template -[[nodiscard]] constexpr optional closest_intersection_parameter(ray const& r, sphere const& s) +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray const& r, aabb_boundary const& b) { - auto t = dot(s.center - r.origin, r.dir); + const auto tMin = (b.min - r.origin) / comp(r.dir); + const auto tMax = (b.max - r.origin) / comp(r.dir); + auto tFirst = max(min(tMin.x, tMax.x), min(tMin.y, tMax.y)); + auto tSecond = min(max(tMin.x, tMax.x), max(tMin.y, tMax.y)); - auto d_sqr = distance_sqr(r[t], s.center); - auto r_sqr = s.radius * s.radius; - if (d_sqr > r_sqr) - return {}; + if constexpr (D >= 3) + { + tFirst = max(tFirst, min(tMin.z, tMax.z)); + tSecond = min(tSecond, max(tMin.z, tMax.z)); + } - auto dt = sqrt(r_sqr - d_sqr); + // No intersection if ray starts behind aabb or if it misses the aabb + if (tSecond < ScalarT(0) || tFirst > tSecond) + return {}; - if (t - dt >= 0) - return t - dt; - if (t + dt >= 0) - return t + dt; + if (tFirst < ScalarT(0)) + return tSecond; - return {}; + return {tFirst, tSecond}; } // returns intersection point(s) of ray and sphere template -[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray const& r, sphere const& s) +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray const& r, sphere_boundary const& s) { auto t = dot(s.center - r.origin, r.dir); @@ -454,9 +530,10 @@ template // ray - triangle2 template -[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<2, ScalarT> const& r, triangle<2, ScalarT> const& t) +[[nodiscard]] constexpr optional> intersection_parameter(ray<2, ScalarT> const& r, triangle<2, ScalarT> const& t) { ScalarT closestIntersection = tg::max(); + ScalarT furtherIntersection = tg::min(); auto numIntersections = 0; for (const auto& edge : edges_of(t)) { @@ -465,6 +542,7 @@ template { numIntersections++; closestIntersection = min(closestIntersection, inter.first()); + furtherIntersection = max(furtherIntersection, inter.first()); } } @@ -474,9 +552,9 @@ template { // ray started within the triangle TG_ASSERT(contains(t, r.origin)); - return ScalarT(0); + return {{ScalarT(0), furtherIntersection}}; } - return closestIntersection; + return {{closestIntersection, furtherIntersection}}; } // ray - triangle3 @@ -517,7 +595,7 @@ template // ray - box template -[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, box<3, ScalarT> const& b) +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, box_boundary<3, ScalarT> const& b) { // see https://github.com/gszauer/GamePhysicsCookbook/blob/master/Code/Geometry3D.cpp diff --git a/src/typed-geometry/types/objects/aabb.hh b/src/typed-geometry/types/objects/aabb.hh index b0e9121..5afc17d 100644 --- a/src/typed-geometry/types/objects/aabb.hh +++ b/src/typed-geometry/types/objects/aabb.hh @@ -48,6 +48,7 @@ using aabb_boundary = aabb; template struct aabb { + using scalar_t = ScalarT; using vec_t = vec; using pos_t = pos; -- GitLab From f0fd7928906fffa2e12742e2fc5254cd74b91107 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Tue, 10 Nov 2020 00:43:31 +0100 Subject: [PATCH 14/42] Improved ray - aabb intersection, now for all dimensions --- .../functions/objects/intersection.hh | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 6bb4693..ed8a6eb 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -4,8 +4,10 @@ #include #include -#include +#include #include +#include +#include #include #include #include @@ -17,7 +19,6 @@ #include #include "aabb.hh" -#include "closest_points.hh" #include "contains.hh" #include "direction.hh" #include "normal.hh" @@ -361,15 +362,17 @@ template template [[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray const& r, aabb_boundary const& b) { - const auto tMin = (b.min - r.origin) / comp(r.dir); - const auto tMax = (b.max - r.origin) / comp(r.dir); - auto tFirst = max(min(tMin.x, tMax.x), min(tMin.y, tMax.y)); - auto tSecond = min(max(tMin.x, tMax.x), max(tMin.y, tMax.y)); - - if constexpr (D >= 3) + auto tFirst = tg::min(); + auto tSecond = tg::max(); + for (auto i = 0; i < D; ++i) { - tFirst = max(tFirst, min(tMin.z, tMax.z)); - tSecond = min(tSecond, max(tMin.z, tMax.z)); + if (abs(r.dir[i]) > ScalarT(100) * tg::epsilon) + { + const auto tMin = (b.min[i] - r.origin[i]) / r.dir[i]; + const auto tMax = (b.max[i] - r.origin[i]) / r.dir[i]; + tFirst = max(tFirst, min(tMin, tMax)); + tSecond = min(tSecond, max(tMin, tMax)); + } } // No intersection if ray starts behind aabb or if it misses the aabb -- GitLab From f4de456f3306de8f769c989cf861a20fa7d742bf Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Tue, 10 Nov 2020 01:33:29 +0100 Subject: [PATCH 15/42] Fixed ray - aabb intersection for axis aligned rays --- src/typed-geometry/functions/objects/intersection.hh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index ed8a6eb..14f8880 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -373,9 +373,11 @@ template tFirst = max(tFirst, min(tMin, tMax)); tSecond = min(tSecond, max(tMin, tMax)); } + else if (r.origin[i] < b.min[i] || r.origin[i] > b.max[i]) + return {}; // ray parallel to this axis and outside of aabb } - // No intersection if ray starts behind aabb or if it misses the aabb + // no intersection if ray starts behind aabb or if it misses the aabb if (tSecond < ScalarT(0) || tFirst > tSecond) return {}; -- GitLab From 3a9f8048463c4ce8197fae53a12d2c4e0f151454 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Tue, 10 Nov 2020 03:09:30 +0100 Subject: [PATCH 16/42] Reimplemented ray - box intersection --- .../functions/objects/intersection.hh | 95 +++++++------------ 1 file changed, 35 insertions(+), 60 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 14f8880..17d983c 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -374,10 +375,42 @@ template tSecond = min(tSecond, max(tMin, tMax)); } else if (r.origin[i] < b.min[i] || r.origin[i] > b.max[i]) - return {}; // ray parallel to this axis and outside of aabb + return {}; // ray parallel to this axis and outside of the aabb } - // no intersection if ray starts behind aabb or if it misses the aabb + // no intersection if ray starts behind the aabb or if it misses the aabb + if (tSecond < ScalarT(0) || tFirst > tSecond) + return {}; + + if (tFirst < ScalarT(0)) + return tSecond; + + return {tFirst, tSecond}; +} + +// ray - box +template +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray const& r, box_boundary const& b) +{ + const auto bMin = b[comp(-1)] - r.origin; + const auto bMax = b[comp(1)] - r.origin; + auto tFirst = tg::min(); + auto tSecond = tg::max(); + for (auto i = 0; i < D; ++i) + { + const auto rDir = dot(r.dir, b.half_extents[i]); + if (abs(rDir) > ScalarT(100) * tg::epsilon) + { + const auto tMin = dot(bMin, b.half_extents[i]) / rDir; + const auto tMax = dot(bMax, b.half_extents[i]) / rDir; + tFirst = max(tFirst, min(tMin, tMax)); + tSecond = min(tSecond, max(tMin, tMax)); + } + else if (dot(bMin, b.half_extents[i]) > ScalarT(0) || dot(bMax, b.half_extents[i]) < ScalarT(0)) + return {}; // ray parallel to this half_extents axis and outside of the box + } + + // no intersection if ray starts behind the box or if it misses the box if (tSecond < ScalarT(0) || tFirst > tSecond) return {}; @@ -598,64 +631,6 @@ template return (lambda > ScalarT(0)) ? lambda : ray_hits<1, ScalarT>(); } -// ray - box -template -[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, box_boundary<3, ScalarT> const& b) -{ - // see https://github.com/gszauer/GamePhysicsCookbook/blob/master/Code/Geometry3D.cpp - - auto const p = b.center - r.origin; - - // TODO: without normalize - auto const X = normalize(b.half_extents[0]); - auto const Y = normalize(b.half_extents[1]); - auto const Z = normalize(b.half_extents[2]); - - auto f = tg::vec3(dot(X, r.dir), // - dot(Y, r.dir), // - dot(Z, r.dir)); - - auto const e = tg::vec3(dot(X, p), // - dot(Y, p), // - dot(Z, p)); - - auto const size = tg::vec3(length(b.half_extents[0]), // - length(b.half_extents[1]), // - length(b.half_extents[2])); - - float t[6] = {0, 0, 0, 0, 0, 0}; - for (int i = 0; i < 3; ++i) - { - if (abs(f[i]) < tg::epsilon / 1000) - { - if (-e[i] - size[i] > 0 || -e[i] + size[i] < 0) - return {}; - - f[i] = tg::epsilon; // Avoid div by 0! - } - - t[i * 2 + 0] = (e[i] + size[i]) / f[i]; // tmin[x, y, z] - t[i * 2 + 1] = (e[i] - size[i]) / f[i]; // tmax[x, y, z] - } - - auto const tmin = tg::max(tg::max(tg::min(t[0], t[1]), tg::min(t[2], t[3])), tg::min(t[4], t[5])); - auto const tmax = tg::min(tg::min(tg::max(t[0], t[1]), tg::max(t[2], t[3])), tg::max(t[4], t[5])); - - // if tmax < 0, ray is intersecting AABB - // but entire AABB is behing it's origin - if (tmax < 0) - return {}; - - // if tmin > tmax, ray doesn't intersect AABB - if (tmin > tmax) - return {}; - - if (tmin >= 0) // two valid intersections - return {tmin, tmax}; - else // one valid intersection - return tmax; -} - // ====================================== Object - Object Intersections ====================================== -- GitLab From 12ebf69c1c78bd298a5abf9cd71204142bc5dfbd Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Wed, 11 Nov 2020 00:34:58 +0100 Subject: [PATCH 17/42] Added helper functions to ease implementation of intersections with compound objects --- .../functions/objects/intersection.hh | 72 ++++++++++++++----- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 17d983c..ca1ae81 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -53,6 +53,10 @@ // intersection_parameter(ray, obj_boundary) already gives intersection_parameter(ray, obj) when boundary_of(obj) is defined and object is convex // explicit closest_intersection_parameter(ray, obj) if this is faster than computing all intersections // explicit intersects(obj, aabb), which gives intersects(aabb, obj) +// +// for convex compound objects (like cylinder or pyramids), decompose the object into primitive shapes and pass them to a helper function: +// - call merge_hits(ray, objPart1, objPart2, ...) in the implementation of intersection_parameter(ray, obj_boundary) +// - call intersects_any(ray, objPart1, objPart2, ...) in the implementation of intersects(ray, obj), which can shortcut and be faster than the default namespace tg @@ -263,6 +267,42 @@ constexpr optional> intersection(Obj const& obj, pos } +// ====================================== Helper functions ====================================== + +namespace detail +{ +// intersects the given ray with all given objects and returns the concatenated intersections. A maximal number of 2 intersections is assumed. +template +[[nodiscard]] constexpr ray_hits<2, ScalarT> merge_hits(ray const& ray, ObjsT const&... objs) +{ + ScalarT hits[2]; + hits[0] = tg::max(); + hits[1] = tg::min(); + auto numHits = 0; + + const auto find_hits = [&](const auto& obj) { + const auto inters = intersection_parameter(ray, obj); + for (const auto& inter : inters) + { + hits[0] = tg::min(hits[0], inter); + hits[1] = tg::max(hits[1], inter); + numHits++; + } + }; + (find_hits(objs), ...); + + TG_ASSERT(numHits <= 2); + return {hits, numHits}; +} + +// returns true, iff the given ray intersects any of the given objects (with short-circuiting after the first intersection) +template +[[nodiscard]] constexpr bool intersects_any(ray const& ray, ObjsT const&... objs) +{ + return (intersects(ray, objs) || ...); +} +} + // ====================================== Ray - Object Intersections ====================================== // ray - line @@ -493,27 +533,21 @@ template // ray - cylinder template -[[nodiscard]] constexpr optional closest_intersection_parameter(ray<3, ScalarT> const& r, cylinder_boundary<3, ScalarT> const& c) +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, cylinder_boundary<3, ScalarT> const& c) { const auto caps = caps_of(c); - const auto t_cap0 = intersection_parameter(r, caps[0]); - const auto t_cap1 = intersection_parameter(r, caps[1]); - const auto t_cyl = closest_intersection_parameter(r, boundary_no_caps_of(c)); - - optional t; - - if (t_cyl.has_value()) - t = t_cyl.value(); - - if (t_cap0.any() && (!t.has_value() || t.value() > t_cap0.first())) - t = t_cap0.first(); - - if (t_cap1.any() && (!t.has_value() || t.value() > t_cap1.first())) - t = t_cap1.first(); - - TG_INTERNAL_ASSERT(!t.has_value() || t.value() >= 0); - - return t; + return detail::merge_hits(r, caps[0], caps[1], boundary_no_caps_of(c)); +} +template +[[nodiscard]] constexpr bool intersects(ray<3, ScalarT> const& r, cylinder<3, ScalarT, TraitsT> const& c) +{ + if constexpr (std::is_same_v) + return intersection_parameter(r, c).any(); + else + { + const auto caps = caps_of(c); + return detail::intersects_any(r, boundary_no_caps_of(c), caps[0], caps[1]); + } } // ray - tube -- GitLab From 938952c7b81af3e7f820a24e05edd5e46448a0aa Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Wed, 11 Nov 2020 00:35:52 +0100 Subject: [PATCH 18/42] Added contains(capsule), which is faster than the default implementation --- src/typed-geometry/functions/objects/contains.hh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/typed-geometry/functions/objects/contains.hh b/src/typed-geometry/functions/objects/contains.hh index 46c1878..25cecc8 100644 --- a/src/typed-geometry/functions/objects/contains.hh +++ b/src/typed-geometry/functions/objects/contains.hh @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -221,6 +222,19 @@ template return x2 / pow2(a + eps) + y2 / pow2(b + eps) + z2 / pow2(c + eps) <= ScalarT(1); } +template +[[nodiscard]] constexpr bool contains(capsule<3, ScalarT> const& c, pos<3, ScalarT> const& p, dont_deduce eps = ScalarT(0)) +{ + auto r = c.radius + eps; + return distance_sqr(c.axis, p) <= pow2(r); +} +template +[[nodiscard]] constexpr bool contains(capsule_boundary<3, ScalarT> const& c, pos<3, ScalarT> const& p, dont_deduce eps = ScalarT(0)) +{ + auto d2 = distance_sqr(c.axis, p); + return pow2(max(ScalarT(0), c.radius - eps)) <= d2 && d2 <= pow2(c.radius + eps); +} + template [[nodiscard]] constexpr bool contains(sphere const& s, pos const& p, dont_deduce eps = ScalarT(0)) { -- GitLab From 834104049d6de589530b0c4fc05c33279b6a9a05 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Wed, 11 Nov 2020 02:11:40 +0100 Subject: [PATCH 19/42] Implemented ray intersections for hemisphere and capsule --- .../functions/objects/intersection.hh | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index ca1ae81..20e8c55 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -6,8 +6,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -21,6 +23,7 @@ #include "aabb.hh" #include "contains.hh" +#include "coordinates.hh" #include "direction.hh" #include "normal.hh" @@ -305,6 +308,16 @@ template // ====================================== Ray - Object Intersections ====================================== +// ray - point +template +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<1, ScalarT> const& r, pos<1, ScalarT> const& p) +{ + const auto t = coordinates(r, p); + if (t >= ScalarT(0)) + return t; + return {}; +} + // ray - line template [[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<2, ScalarT> const& r, line<2, ScalarT> const& l) @@ -497,6 +510,34 @@ template return t; } +// ray - hemisphere +template +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray const& r, hemisphere_boundary const& h) +{ + return detail::merge_hits(r, caps_of(h), boundary_no_caps_of(h)); +} +template +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray const& r, hemisphere_boundary_no_caps const& h) +{ + ScalarT hits[2]; + auto numHits = 0; + const auto sphereHits = intersection_parameter(r, sphere_boundary(h.center, h.radius)); + const auto halfSpace = halfspace(-h.normal, h.center); // the intersection of this halfspace and the sphere is exactly the hemisphere + for (const auto& hit : sphereHits) + if (contains(halfSpace, r[hit])) + hits[numHits++] = hit; + + return {hits, numHits}; +} +template +[[nodiscard]] constexpr bool intersects(ray const& r, hemisphere const& h) +{ + if constexpr (std::is_same_v) + return intersection_parameter(r, h).any(); + else + return detail::intersects_any(r, caps_of(h), boundary_no_caps_of(h)); +} + // ray - quadric (as an isosurface, not error quadric) template [[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, quadric<3, ScalarT> const& q) @@ -531,6 +572,23 @@ template return {}; } +// ray - capsule +template +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, capsule_boundary<3, ScalarT> const& c) +{ + using caps_t = hemisphere_boundary_no_caps<3, ScalarT>; + const auto n = direction(c); + return detail::merge_hits(r, caps_t(c.axis.pos0, c.radius, -n), caps_t(c.axis.pos1, c.radius, n), + cylinder_boundary_no_caps<3, ScalarT>(c.axis, c.radius)); +} +template +[[nodiscard]] constexpr bool intersects(ray<3, ScalarT> const& r, capsule<3, ScalarT, TraitsT> const& c) +{ + using caps_t = sphere_boundary<3, ScalarT>; // spheres are faster than hemispheres and equivalent for the yes/no decision + return detail::intersects_any(r, caps_t(c.axis.pos0, c.radius), caps_t(c.axis.pos1, c.radius), + cylinder_boundary_no_caps<3, ScalarT>(c.axis, c.radius)); +} + // ray - cylinder template [[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, cylinder_boundary<3, ScalarT> const& c) @@ -546,7 +604,7 @@ template else { const auto caps = caps_of(c); - return detail::intersects_any(r, boundary_no_caps_of(c), caps[0], caps[1]); + return detail::intersects_any(r, caps[0], caps[1], boundary_no_caps_of(c)); } } -- GitLab From 5153d9f86bac6054ab2097aa7183ada7ee891a03 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Wed, 11 Nov 2020 02:19:44 +0100 Subject: [PATCH 20/42] Moved ray - quadric intersection code --- .../functions/objects/intersection.hh | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 20e8c55..04a6d61 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -538,40 +538,6 @@ template return detail::intersects_any(r, caps_of(h), boundary_no_caps_of(h)); } -// ray - quadric (as an isosurface, not error quadric) -template -[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, quadric<3, ScalarT> const& q) -{ - const auto Ad = q.A() * r.dir; - const auto p = r.origin; - - // Substituting x in Quadric equation x^TAx + 2b^Tx + c = 0 by ray equation x = t * dir + p yields - // d^TAd t^2 + (2p^TAd + 2bd) t + p^TAp + 2bp + c = 0 - const auto a = dot(r.dir, Ad); - const auto b = ScalarT(2) * (dot(p, Ad) + dot(q.b(), r.dir)); - const auto c = dot(p, q.A() * vec3(p)) + ScalarT(2) * dot(q.b(), p) + q.c; - - // Solve the quadratic equation ax^2 + bx + c = 0 - const auto discriminant = b * b - ScalarT(4) * a * c; - if (discriminant < ScalarT(0)) - return {}; // No solution - - const auto sqrtD = sqrt(discriminant); - const auto t1 = (-b - sqrtD) / (ScalarT(2) * a); - const auto t2 = (-b + sqrtD) / (ScalarT(2) * a); - - auto tMin = min(t1, t2); - auto tMax = max(t1, t2); - - if (tMin >= ScalarT(0)) - return {tMin, tMax}; - - if (tMax >= ScalarT(0)) - return tMax; - - return {}; -} - // ray - capsule template [[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, capsule_boundary<3, ScalarT> const& c) @@ -723,6 +689,40 @@ template return (lambda > ScalarT(0)) ? lambda : ray_hits<1, ScalarT>(); } +// ray - quadric_boundary (as an isosurface, not error quadric) +template +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, quadric<3, ScalarT> const& q) +{ + const auto Ad = q.A() * r.dir; + const auto p = r.origin; + + // Substituting x in Quadric equation x^TAx + 2b^Tx + c = 0 by ray equation x = t * dir + p yields + // d^TAd t^2 + (2p^TAd + 2bd) t + p^TAp + 2bp + c = 0 + const auto a = dot(r.dir, Ad); + const auto b = ScalarT(2) * (dot(p, Ad) + dot(q.b(), r.dir)); + const auto c = dot(p, q.A() * vec3(p)) + ScalarT(2) * dot(q.b(), p) + q.c; + + // Solve the quadratic equation ax^2 + bx + c = 0 + const auto discriminant = b * b - ScalarT(4) * a * c; + if (discriminant < ScalarT(0)) + return {}; // No solution + + const auto sqrtD = sqrt(discriminant); + const auto t1 = (-b - sqrtD) / (ScalarT(2) * a); + const auto t2 = (-b + sqrtD) / (ScalarT(2) * a); + + auto tMin = min(t1, t2); + auto tMax = max(t1, t2); + + if (tMin >= ScalarT(0)) + return {tMin, tMax}; + + if (tMax >= ScalarT(0)) + return tMax; + + return {}; +} + // ====================================== Object - Object Intersections ====================================== -- GitLab From 554dc0264512b88aaa64c3215bc3bcd9d8f90492 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 16 Nov 2020 10:45:29 +0100 Subject: [PATCH 21/42] Trying to fix template instantiation --- src/typed-geometry/functions/objects/intersection.hh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 04a6d61..64cb693 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -53,7 +53,8 @@ // Implementation guidelines: // explicit intersection_parameter(ray, obj), which gives closest_intersection_parameter and intersection position(s) -// intersection_parameter(ray, obj_boundary) already gives intersection_parameter(ray, obj) when boundary_of(obj) is defined and object is convex +// intersection_parameter(ray, obj_boundary) already gives intersection_parameter(ray, obj) when boundary_of(obj) is defined and object is convex and finite +// for infinite objects, a separate solid version is needed when the boundary version does not distinguish between completely contained and completely outside // explicit closest_intersection_parameter(ray, obj) if this is faster than computing all intersections // explicit intersects(obj, aabb), which gives intersects(aabb, obj) // @@ -234,7 +235,7 @@ template // if boundary_of a solid object returns ray_hits, use this to construct the ray_interval result of the solid intersection template [[nodiscard]] constexpr auto intersection_parameter(ray const& r, Obj const& obj) - -> enable_if>, optional>> + -> enable_if>, std::is_same>>, optional>> { const auto inter = intersection_parameter(r, boundary_of(obj)); -- GitLab From 39b80b3765f73c79a5673a6a1e01fd62f4bc34a4 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 16 Nov 2020 12:32:27 +0100 Subject: [PATCH 22/42] Fixed template recursion --- src/typed-geometry/functions/objects/intersection.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 64cb693..7440307 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -235,7 +235,7 @@ template // if boundary_of a solid object returns ray_hits, use this to construct the ray_interval result of the solid intersection template [[nodiscard]] constexpr auto intersection_parameter(ray const& r, Obj const& obj) - -> enable_if>, std::is_same>>, optional>> + -> enable_if, optional>> { const auto inter = intersection_parameter(r, boundary_of(obj)); -- GitLab From d0f7c669e29e5d8a2de973c614563eb9a352a6ee Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 16 Nov 2020 12:56:24 +0100 Subject: [PATCH 23/42] Fixed initializer list --- src/typed-geometry/functions/objects/intersection.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index 7440307..d6b08aa 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -206,7 +206,7 @@ template [[nodiscard]] constexpr auto intersection_parameter(ray const& r, Obj const& obj) -> enable_if, optional>> { - const auto inter = intersection_parameter(r, boundary_of(obj)); + const ray_hits<2, ScalarT> inter = intersection_parameter(r, boundary_of(obj)); if (inter.size() == 0) return {}; -- GitLab From 1a09d9b1c877e111a044535ff42228dcdd6f1f73 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 16 Nov 2020 13:30:28 +0100 Subject: [PATCH 24/42] Test for another CI problem --- src/typed-geometry/detail/optional.hh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/typed-geometry/detail/optional.hh b/src/typed-geometry/detail/optional.hh index f7f3fd7..b8b8965 100644 --- a/src/typed-geometry/detail/optional.hh +++ b/src/typed-geometry/detail/optional.hh @@ -47,6 +47,9 @@ public: template auto operator<<(Stream& os, optional const& o) -> decltype(os << o.value(), os << "", os) { - return o.has_value() ? (os << o.value()) : (os << "[empty]"); + if (o.has_value()) + return (os << o.value()); + else + return (os << "[empty]"); } } -- GitLab From 148405e7bfc301ae3fe5b7fe7e65edbde956c3f8 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 16 Nov 2020 14:10:36 +0100 Subject: [PATCH 25/42] Workaround for gcc bug --- src/typed-geometry/detail/optional.hh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/typed-geometry/detail/optional.hh b/src/typed-geometry/detail/optional.hh index b8b8965..dba24b1 100644 --- a/src/typed-geometry/detail/optional.hh +++ b/src/typed-geometry/detail/optional.hh @@ -45,11 +45,8 @@ public: }; template -auto operator<<(Stream& os, optional const& o) -> decltype(os << o.value(), os << "", os) +auto operator<<(Stream& os, optional const& o) -> decltype(os << o.value(), os << std::declval(), os) { - if (o.has_value()) - return (os << o.value()); - else - return (os << "[empty]"); + return o.has_value() ? (os << o.value()) : (os << "[empty]"); } } -- GitLab From 207fc1b1aaae8a6e63f4c0557b17e0ea974b262f Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 16 Nov 2020 15:27:29 +0100 Subject: [PATCH 26/42] Added ray - box2in3 intersection --- .../functions/objects/intersection.hh | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index d6b08aa..fdd0163 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -473,8 +473,36 @@ template return {tFirst, tSecond}; } +template +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, box<2, ScalarT, 3> const& b) +{ + const auto t = intersection_parameter(r, plane<3, ScalarT>(normal_of(b), b.center)); + if (!t.any()) + return {}; + + const auto p = r[t.first()] - b.center; + if (abs(dot(b.half_extents[0], p)) > length_sqr(b.half_extents[0]) || abs(dot(b.half_extents[1], p)) > length_sqr(b.half_extents[1])) + return {}; + + return t; +} -// returns intersection point(s) of ray and sphere +// ray - disk +template +[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, sphere<2, ScalarT, 3> const& d) +{ + const auto t = intersection_parameter(r, plane<3, ScalarT>(d.normal, d.center)); + if (!t.any()) + return {}; + + const auto p = r[t.first()]; + if (distance_sqr(p, d.center) > d.radius * d.radius) + return {}; + + return t; +} + +// ray - sphere template [[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray const& r, sphere_boundary const& s) { @@ -496,21 +524,6 @@ template return {}; } -// ray - disk -template -[[nodiscard]] constexpr ray_hits<1, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, sphere<2, ScalarT, 3> const& d) -{ - auto const t = intersection_parameter(r, plane<3, ScalarT>(d.normal, d.center)); - if (!t.any()) - return {}; - - auto const p = r[t.first()]; - if (distance_sqr(p, d.center) > d.radius * d.radius) - return {}; - - return t; -} - // ray - hemisphere template [[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray const& r, hemisphere_boundary const& h) -- GitLab From 85411dd35f064a74ec65a84bd630e9a2c07e6f0e Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 16 Nov 2020 15:28:54 +0100 Subject: [PATCH 27/42] Fixed inf_cylinder description; Added inf_tube shorthands --- src/typed-geometry/types/objects/inf_cylinder.hh | 8 ++++---- src/typed-geometry/types/objects/tube.hh | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/typed-geometry/types/objects/inf_cylinder.hh b/src/typed-geometry/types/objects/inf_cylinder.hh index 8d1aa96..2a97962 100644 --- a/src/typed-geometry/types/objects/inf_cylinder.hh +++ b/src/typed-geometry/types/objects/inf_cylinder.hh @@ -8,16 +8,16 @@ namespace tg { /** - * infinite tube + * infinite cylinder * - * An infinite tube is defined by a position and an axis + * An infinite cylinder is defined by an axis and a radius */ template struct inf_cylinder; -// Common infinte tube types +// Common infinite cylinder types using inf_cylinder3 = inf_cylinder<3, f32>; -using finttube3 = inf_cylinder<3, f32>; +using finf_cylinder3 = inf_cylinder<3, f32>; using dinf_cylinder3 = inf_cylinder<3, f64>; using iinf_cylinder3 = inf_cylinder<3, i32>; using uinf_cylinder3 = inf_cylinder<3, u32>; diff --git a/src/typed-geometry/types/objects/tube.hh b/src/typed-geometry/types/objects/tube.hh index bc90acd..16e1f54 100644 --- a/src/typed-geometry/types/objects/tube.hh +++ b/src/typed-geometry/types/objects/tube.hh @@ -1,6 +1,7 @@ #pragma once #include "cylinder.hh" +#include "inf_cylinder.hh" namespace tg { @@ -13,4 +14,15 @@ using ftube3 = tube<3, f32>; using dtube3 = tube<3, f64>; using itube3 = tube<3, i32>; using utube3 = tube<3, u32>; + + +template +using inf_tube = inf_cylinder_boundary; + +// Common inf_tube types +using inf_tube3 = inf_tube<3, f32>; +using finf_tube3 = inf_tube<3, f32>; +using dinf_tube3 = inf_tube<3, f64>; +using iinf_tube3 = inf_tube<3, i32>; +using uinf_tube3 = inf_tube<3, u32>; } -- GitLab From 8a4c563907bb83df99054881f48002c789472629 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Mon, 16 Nov 2020 23:48:00 +0100 Subject: [PATCH 28/42] Added minmax; Added ray - inf_cylinder2_boundary intersection --- .../functions/basic/scalar_math.hh | 8 ++++++ .../functions/objects/intersection.hh | 26 ++++++++++++++++--- .../types/objects/inf_cylinder.hh | 1 + 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/typed-geometry/functions/basic/scalar_math.hh b/src/typed-geometry/functions/basic/scalar_math.hh index 59f4d24..ef841c5 100644 --- a/src/typed-geometry/functions/basic/scalar_math.hh +++ b/src/typed-geometry/functions/basic/scalar_math.hh @@ -144,6 +144,14 @@ template return a < b ? b : a; } +template +[[nodiscard]] constexpr pair minmax(T const& a, T const& b) +{ + if (b < a) + return {b, a}; + return {a, b}; +} + template [[nodiscard]] constexpr auto clamp(A const& a, B const& min_value, C const& max_value) -> decltype(min(max(a, min_value), max_value)) { diff --git a/src/typed-geometry/functions/objects/intersection.hh b/src/typed-geometry/functions/objects/intersection.hh index fdd0163..dd25a67 100644 --- a/src/typed-geometry/functions/objects/intersection.hh +++ b/src/typed-geometry/functions/objects/intersection.hh @@ -587,8 +587,6 @@ template return detail::intersects_any(r, caps[0], caps[1], boundary_no_caps_of(c)); } } - -// ray - tube template [[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<3, ScalarT> const& r, cylinder_boundary_no_caps<3, ScalarT> const& c) { @@ -638,6 +636,27 @@ template return {hits, hit_cnt}; } +// ray - inf_cylinder +template +[[nodiscard]] constexpr ray_hits<2, ScalarT> intersection_parameter(ray<2, ScalarT> const& r, inf_cylinder_boundary<2, ScalarT> const& c) +{ + const auto n = perpendicular(c.axis.dir); + const auto d = dot(r.dir, n); + if (d == ScalarT(0)) // ray parallel to inf_cylinder + return {}; + + const auto dist = dot(c.axis.pos - r.origin, n); + const auto [tMin, tMax] = minmax((dist - c.radius) / d, (dist + c.radius) / d); + + if (tMin >= ScalarT(0)) + return {tMin, tMax}; + + if (tMax >= ScalarT(0)) + return tMax; + + return {}; +} + // ray - triangle2 template [[nodiscard]] constexpr optional> intersection_parameter(ray<2, ScalarT> const& r, triangle<2, ScalarT> const& t) @@ -725,8 +744,7 @@ template const auto t1 = (-b - sqrtD) / (ScalarT(2) * a); const auto t2 = (-b + sqrtD) / (ScalarT(2) * a); - auto tMin = min(t1, t2); - auto tMax = max(t1, t2); + const auto [tMin, tMax] = minmax(t1, t2); if (tMin >= ScalarT(0)) return {tMin, tMax}; diff --git a/src/typed-geometry/types/objects/inf_cylinder.hh b/src/typed-geometry/types/objects/inf_cylinder.hh index 2a97962..f4c1a16 100644 --- a/src/typed-geometry/types/objects/inf_cylinder.hh +++ b/src/typed-geometry/types/objects/inf_cylinder.hh @@ -29,6 +29,7 @@ using inf_cylinder_boundary = inf_cylinder; template struct inf_cylinder { + using scalar_t = ScalarT; using pos_t = pos; using dir_t = dir; using line_t = line; -- GitLab From 49b66510f1807a1457dce468f8e63a4ba6a451ee Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Tue, 17 Nov 2020 22:12:06 +0100 Subject: [PATCH 29/42] Added ::scalar_t to line and ray --- src/typed-geometry/types/objects/line.hh | 6 +++--- src/typed-geometry/types/objects/ray.hh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/typed-geometry/types/objects/line.hh b/src/typed-geometry/types/objects/line.hh index d0f84b5..02d2891 100644 --- a/src/typed-geometry/types/objects/line.hh +++ b/src/typed-geometry/types/objects/line.hh @@ -3,7 +3,6 @@ #include #include "../dir.hh" #include "../pos.hh" -#include "../size.hh" #include "traits.hh" // A line has a direction and a point lying on it @@ -46,8 +45,9 @@ using uline4 = line<4, u32>; template struct line { - using dir_t = tg::dir; - using pos_t = tg::pos; + using scalar_t = ScalarT; + using dir_t = dir; + using pos_t = pos; pos_t pos; dir_t dir; diff --git a/src/typed-geometry/types/objects/ray.hh b/src/typed-geometry/types/objects/ray.hh index 402f3d0..a376315 100644 --- a/src/typed-geometry/types/objects/ray.hh +++ b/src/typed-geometry/types/objects/ray.hh @@ -3,7 +3,6 @@ #include #include "../dir.hh" #include "../pos.hh" -#include "../size.hh" #include "traits.hh" namespace tg @@ -44,8 +43,9 @@ using uray4 = ray<4, u32>; template struct ray { - using dir_t = tg::dir; - using pos_t = tg::pos; + using scalar_t = ScalarT; + using dir_t = dir; + using pos_t = pos; pos_t origin; dir_t dir; -- GitLab From 1b11ecffb58cda9dd6ac80da1dae8da63e104809 Mon Sep 17 00:00:00 2001 From: Aaron Grabowy Date: Wed, 18 Nov 2020 22:30:24 +0100 Subject: [PATCH 30/42] Changed all explicit intersections from ray to line intersections and added default conversions from line to ray --- src/typed-geometry/detail/utility.hh | 12 + .../functions/objects/intersection.hh | 550 +++++++++--------- 2 files changed, 292 insertions(+), 270 deletions(-) diff --git a/src/typed-geometry/detail/utility.hh b/src/typed-geometry/detail/utility.hh index 497813f..2f0f579 100644 --- a/src/typed-geometry/detail/utility.hh +++ b/src/typed-geometry/detail/utility.hh @@ -35,7 +35,19 @@ template <> struct priority_tag<0> { }; + +// see https://stackoverflow.com/questions/44395169/why-is-sfinae-on-if-constexpr-not-allowed +template