Euphoria
intersection.cc
Go to the documentation of this file.
1 #include "core/intersection.h"
2 
3 #include "base/ray.h"
4 #include "base/aabb.h"
5 #include "core/sphere.h"
6 #include "base/plane.h"
7 #include "base/numeric.h"
8 
9 
10 namespace eu::core
11 {
12  namespace
13  {
14  Ray3AabbResult
15  ray3_aabb_result_false()
16  {
17  auto r = Ray3AabbResult{};
18  r.intersected = false;
19  r.start = r.end = -1.0f;
20  return r;
21  }
22 
23 
24  Ray3AabbResult
25  ray3_aabb_result_true(float start, float end)
26  {
27  auto r = Ray3AabbResult{};
28  r.intersected = true;
29  r.start = start;
30  r.end = end;
31  return r;
32  }
33  }
34 
35 
36  Ray3AabbResult
38  (
39  const UnitRay3f& r,
40  const Aabb& aabb
41  )
42  {
43  // https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection
44 
45  // todo(Gustav): refactor aabb class?
46  const vec3f bounds[] {aabb.min, aabb.max};
47 
48  // todo(Gustav): move to ray class?
49  const vec3f r_invdir = 1.0f / static_cast<vec3f>(r.dir);
50  const int r_sign[3] =
51  {
52  r_invdir.x < 0 ? 1 : 0,
53  r_invdir.y < 0 ? 1 : 0,
54  r_invdir.z < 0 ? 1 : 0
55  };
56 
57  float tmin = (bounds[r_sign[0]].x - r.from.x) * r_invdir.x;
58  float tmax = (bounds[1 - r_sign[0]].x - r.from.x) * r_invdir.x;
59  const float tymin = (bounds[r_sign[1]].y - r.from.y) * r_invdir.y;
60  const float tymax = (bounds[1 - r_sign[1]].y - r.from.y) * r_invdir.y;
61 
62  if((tmin > tymax) || (tymin > tmax))
63  {
64  return ray3_aabb_result_false();
65  }
66  if(tymin > tmin)
67  {
68  tmin = tymin;
69  }
70  if(tymax < tmax)
71  {
72  tmax = tymax;
73  }
74 
75  const float tzmin = (bounds[r_sign[2]].z - r.from.z) * r_invdir.z;
76  const float tzmax = (bounds[1 - r_sign[2]].z - r.from.z) * r_invdir.z;
77 
78  if((tmin > tzmax) || (tzmin > tmax))
79  {
80  return ray3_aabb_result_false();
81  }
82 
83  if(tzmin > tmin)
84  {
85  tmin = tzmin;
86  }
87  if(tzmax < tmax)
88  {
89  tmax = tzmax;
90  }
91 
92  return ray3_aabb_result_true(tmin, tmax);
93  }
94 
95 
96  float
98  (
99  const UnitRay3f& r,
100  const Plane& p
101  )
102  {
103  return -(r.from.dot(p.normal) + p.distance) / r.dir.dot(p.normal);
104  }
105 
106 
107  namespace
108  {
109  Ray2Ray2Result
110  ray2_ray2_result_parallel()
111  {
112  return {false, true, zero2f, -1.0f, -1.0f};
113  }
114 
115  Ray2Ray2Result
116  ray2_ray2_result_no_collision()
117  {
118  return {false, false, zero2f, -1.0f, -1.0f};
119  }
120 
121  Ray2Ray2Result
122  ray2_ray2_result_collided(const vec2f& p, float a, float b)
123  {
124  return {false, true, p, a, b};
125  }
126  }
127 
128 
129  Ray2Ray2Result
131  (
132  const Ray2f& lhs,
133  const Ray2f& rhs
134  )
135  {
136  // https://stackoverflow.com/a/1968345/180307
137  const vec2f p1 = lhs.position;
138  const vec2f p2 = lhs.position + lhs.direction;
139  const vec2f p3 = rhs.position;
140  const vec2f p4 = rhs.position + rhs.direction;
141 
142  const float p0_x = p1.x;
143  const float p0_y = p1.y;
144  const float p1_x = p2.x;
145  const float p1_y = p2.y;
146  const float p2_x = p3.x;
147  const float p2_y = p3.y;
148  const float p3_x = p4.x;
149  const float p3_y = p4.y;
150 
151  const float s1_x = p1_x - p0_x;
152  const float s1_y = p1_y - p0_y;
153  const float s2_x = p3_x - p2_x;
154  const float s2_y = p3_y - p2_y;
155 
156  const float den = (-s2_x * s1_y + s1_x * s2_y);
157 
158  // todo(Gustav): implement a check for zero for float
159  if(abs(den) < 0.00001f)
160  {
161  return ray2_ray2_result_parallel();
162  }
163 
164  const float s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / den;
165  const float t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / den;
166 
167  if(s >= 0 && s <= 1 && t >= 0 && t <= 1)
168  {
169  const float x = p0_x + (t * s1_x);
170  const float y = p0_y + (t * s1_y);
171  return ray2_ray2_result_collided(vec2f(x, y), s, t);
172  }
173 
174  return ray2_ray2_result_no_collision();
175  }
176 
177  float
179  (
180  const Plane& plane,
181  const vec3f& p
182  )
183  {
184  return plane.normal.dot(p) + plane.distance;
185  }
186 
187  vec3f
189  (
190  const Plane& plane,
191  const vec3f& point
192  )
193  {
194  const auto distance = plane.normal.dot(point) - plane.distance;
195  return point - distance * plane.normal;
196  }
197 
198 
199  float
201  (
202  const UnitRay3f& ray,
203  const vec3f& point
204  )
205  {
206  const auto new_normalized = (point - ray.from).get_normalized();
207 
208  const auto d = new_normalized.dot(ray.dir);
209  return abs(1.0f - d);
210  }
211 
212  vec3f
214  (
215  const UnitRay3f& ray,
216  const vec3f& c
217  )
218  {
219  const auto& ab = ray;
220  const auto a = ray.from;
221 
222  auto t = (c - a).dot(ab.dir) / ab.dir.dot(ab.dir);
223 
224  t = max(t, 0.0f);
225 
226  const auto d = ray.get_point(t);
227  return d;
228  }
229 
230 
231  bool
233  (
234  const Sphere& lhs,
235  const vec3f& lhs_center,
236  const Sphere& rhs,
237  const vec3f& rhs_center
238  )
239  {
240  return vec3f::from_to(lhs_center, rhs_center).get_length_squared() < square(lhs.radius + rhs.radius);
241  }
242 
243 
244  bool
246  (
247  const Sphere& sphere,
248  const vec3f& sphere_center,
249  const vec3f& point
250  )
251  {
252  return vec3f::from_to(sphere_center, point).get_length_squared() < square(sphere.radius);
253  }
254 
255 
256  vec3f
258  (
259  const Sphere& sphere,
260  const vec3f& sphere_center,
261  const vec3f& point
262  )
263  {
264  return UnitRay3f::from_to(sphere_center, point).get_point(sphere.radius);
265  }
266 
267 
268  float
270  (
271  const UnitRay3f& ray,
272  const Sphere& sphere,
273  const vec3f& sphere_center
274  )
275  {
276  const vec3f oc = ray.from - sphere_center;
277  const auto a = ray.dir.dot(ray.dir);
278  const auto b = 2.0f * oc.dot(ray.dir);
279  const auto c = oc.dot(oc) - sphere.radius*sphere.radius;
280  const auto discriminant = b*b - 4*a*c;
281  if (discriminant < 0) {
282  return -1.0f;
283  }
284  else {
285  return (-b - sqrt(discriminant) ) / (2.0f*a);
286  }
287  }
288 
289 
290  bool
292  (
293  const Aabb& aabb,
294  const vec3f& point
295  )
296  {
297  ASSERT(aabb.is_valid());
298 
299  return
300  // greater than min
301  point.x >= aabb.min.x &&
302  point.y >= aabb.min.y &&
303  point.z >= aabb.min.z &&
304  // and less than max
305  point.x <= aabb.max.x &&
306  point.y <= aabb.max.y &&
307  point.z <= aabb.max.z;
308  }
309 
310 
311  vec3f
313  (
314  const Aabb& aabb,
315  const vec3f& point
316  )
317  {
318  ASSERT(aabb.is_valid());
319 
320  #define VEC(N) (point.N > aabb.max.N ? aabb.max.N : (point.N < aabb.min.N ? aabb.min.N : point.N))
321  return {VEC(x), VEC(y), VEC(z)};
322  #undef VEC
323  }
324 
325 
326  bool
328  (
329  const vec2f& a,
330  const vec2f& b,
331  const vec2f& c,
332  const vec2f& p
333  )
334  {
335  const auto s1 = c.y - a.y;
336  const auto s2 = c.x - a.x;
337  const auto s3 = b.y - a.y;
338  const auto s4 = p.y - a.y;
339 
340  const auto w1 = (a.x * s1 + s4 * s2 - p.x * s1) / (s3 * s2 - (b.x - a.x) * s1);
341  const auto w2 = (s4 - w1*s3) / s1;
342  return w1 >= 0 && w2 >= 0 && (w1 + w2) <= 1;
343  }
344 
345 
350  std::optional<float>
352  (
353  const UnitRay3f& ray,
354  const vec3f& v0, const vec3f& v1, const vec3f& v2
355  )
356  {
357  constexpr float epsilon = 0.0000001f;
358 
359  const auto edge1 = v1 - v0;
360  const auto edge2 = v2 - v0;
361  const auto h = ray.dir.cross(edge2);
362  const auto a = edge1.dot(h);
363  if (a > -epsilon && a < epsilon)
364  {
365  return std::nullopt;
366  }
367 
368  const auto f = 1.0f/a;
369  const auto s = ray.from - v0;
370  const auto u = f * s.dot(h);
371  if (u < 0.0f || u > 1.0f)
372  {
373  return std::nullopt;
374  }
375 
376  const auto q = s.cross(edge1);
377  const auto v = f * ray.dir.dot(q);
378  if (v < 0.0f || u + v > 1.0f)
379  {
380  return std::nullopt;
381  }
382 
383  const auto t = f * edge2.dot(q);
384  if (t > epsilon)
385  {
386  return t;
387  }
388  else
389  {
390  return std::nullopt;
391  }
392  }
393 }
#define ASSERT(x)
Definition: assert.h:29
#define VEC(N)
bool contains_point(const Sphere &sphere, const vec3f &sphere_center, const vec3f &point)
vec3f get_closest_point(const Plane &plane, const vec3f &point)
float get_distance_between(const Plane &plane, const vec3f &p)
bool is_point_in_triangle(const vec2f &a, const vec2f &b, const vec2f &c, const vec2f &p)
std::optional< float > get_intersection_ray_triangle(const UnitRay3f &ray, const vec3f &v0, const vec3f &v1, const vec3f &v2)
This implements the Möller–Trumbore intersection algorithm.
std::optional< float > get_intersection(const UnitRay3f &ray, const CollisionMesh &mesh)
float sqrt(float r)
Definition: numeric.cc:101
constexpr vec2f zero2f
Definition: vec2.h:68
constexpr float abs(float r)
Definition: numeric.h:8
float square(float r)
Definition: numeric.cc:94
size2f max(const size2f lhs, const size2f rhs)
Definition: size2.cc:149
float dot(const quatf &lhs, const quatf &rhs)
Definition: quat.cc:363
Definition: aabb.h:15
bool is_valid() const
Definition: aabb.cc:64
vec3f min
Definition: aabb.h:16
vec3f max
Definition: aabb.h:17
Definition: plane.h:8
float distance
Definition: plane.h:10
unit3f normal
Definition: plane.h:9
Definition: ray.h:24
vec2f position
Definition: ray.h:25
vec2f direction
Definition: ray.h:26
unit3f dir
Definition: ray.h:11
vec3f get_point(float at) const
Definition: ray.cc:31
static UnitRay3f from_to(const vec3f &from, const vec3f &to)
Definition: ray.cc:17
vec3f from
Definition: ray.h:10
float radius
Definition: sphere.h:7
Definition: vec2.h:33
float x
Definition: vec2.h:34
float y
Definition: vec2.h:35
Definition: vec3.h:48
float x
Definition: vec3.h:49
float y
Definition: vec3.h:50
constexpr float get_length_squared() const
Definition: vec3.h:79
vec3f cross(const vec3f &u) const
Definition: vec3.cc:283
static vec3f from_to(const vec3f &from, const vec3f &to)
Definition: vec3.cc:127
float z
Definition: vec3.h:51
float dot(const vec3f &rhs) const
Definition: vec3.cc:276