1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29
30 // appleseed.foundation headers.
31 #include "foundation/math/aabb.h"
32 #include "foundation/math/intersection/rayaabb.h"
33 #include "foundation/math/ray.h"
34 #include "foundation/math/vector.h"
35 #include "foundation/utility/test.h"
36
37 // Standard headers.
38 #include <limits>
39
40 using namespace foundation;
41 using namespace std;
42
TEST_SUITE(Foundation_Math_Intersection_RayAABB)43 TEST_SUITE(Foundation_Math_Intersection_RayAABB)
44 {
45 #pragma warning (push)
46 #pragma warning (disable : 4723) // potential division by 0
47
48 TEST_CASE(Intersect_GivenRayNotPiercingTheAABB_ReturnsFalse)
49 {
50 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
51 const Ray3d ray(Vector3d(2.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
52
53 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
54
55 EXPECT_FALSE(hit);
56 }
57
58 TEST_CASE(Intersect_GivenRayPiercingPosXFaceInTheMiddle_ReturnsTrue)
59 {
60 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
61 const Ray3d ray(Vector3d(2.0, 0.0, 0.0), Vector3d(-1.0, 0.0, 0.0));
62
63 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
64
65 EXPECT_TRUE(hit);
66 }
67
68 TEST_CASE(Intersect_GivenRayPiercingNegXFaceInTheMiddle_ReturnsTrue)
69 {
70 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
71 const Ray3d ray(Vector3d(-2.0, 0.0, 0.0), Vector3d(1.0, 0.0, 0.0));
72
73 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
74
75 EXPECT_TRUE(hit);
76 }
77
78 TEST_CASE(Intersect_GivenRayPiercingPosYFaceInTheMiddle_ReturnsTrue)
79 {
80 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
81 const Ray3d ray(Vector3d(0.0, 2.0, 0.0), Vector3d(0.0, -1.0, 0.0));
82
83 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
84
85 EXPECT_TRUE(hit);
86 }
87
88 TEST_CASE(Intersect_GivenRayPiercingNegYFaceInTheMiddle_ReturnsTrue)
89 {
90 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
91 const Ray3d ray(Vector3d(0.0, -2.0, 0.0), Vector3d(0.0, 1.0, 0.0));
92
93 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
94
95 EXPECT_TRUE(hit);
96 }
97
98 TEST_CASE(Intersect_GivenRayPiercingPosZFaceInTheMiddle_ReturnsTrue)
99 {
100 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
101 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
102
103 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
104
105 EXPECT_TRUE(hit);
106 }
107
108 TEST_CASE(Intersect_GivenRayPiercingNegZFaceInTheMiddle_ReturnsTrue)
109 {
110 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
111 const Ray3d ray(Vector3d(0.0, 0.0, -2.0), Vector3d(0.0, 0.0, 1.0));
112
113 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
114
115 EXPECT_TRUE(hit);
116 }
117
118 TEST_CASE(Intersect_GivenRayEmbeddedInPosXFace_ReturnsTrue)
119 {
120 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
121 const Ray3d ray(Vector3d(1.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
122
123 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
124
125 EXPECT_TRUE(hit);
126 }
127
128 TEST_CASE(Intersect_GivenRayEmbeddedInNegXFace_ReturnsTrue)
129 {
130 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
131 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
132
133 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
134
135 EXPECT_TRUE(hit);
136 }
137
138 TEST_CASE(Intersect_GivenRayEmbeddedInPosYFace_ReturnsTrue)
139 {
140 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
141 const Ray3d ray(Vector3d(2.0, 1.0, 0.0), Vector3d(-1.0, 0.0, 0.0));
142
143 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
144
145 EXPECT_TRUE(hit);
146 }
147
148 TEST_CASE(Intersect_GivenRayEmbeddedInNegYFace_ReturnsTrue)
149 {
150 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
151 const Ray3d ray(Vector3d(2.0, 0.0, 0.0), Vector3d(-1.0, 0.0, 0.0));
152
153 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
154
155 EXPECT_TRUE(hit);
156 }
157
158 TEST_CASE(Intersect_GivenRayEmbeddedInPosZFace_ReturnsTrue)
159 {
160 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
161 const Ray3d ray(Vector3d(0.0, 2.0, 1.0), Vector3d(0.0, -1.0, 0.0));
162
163 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
164
165 EXPECT_TRUE(hit);
166 }
167
168 TEST_CASE(Intersect_GivenRayEmbeddedInNegZFace_ReturnsTrue)
169 {
170 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
171 const Ray3d ray(Vector3d(0.0, 2.0, 0.0), Vector3d(0.0, -1.0, 0.0));
172
173 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
174
175 EXPECT_TRUE(hit);
176 }
177
178 TEST_CASE(Intersect_GivenRayWithTMinEqualToHitDistance_ReturnsTrue)
179 {
180 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
181 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.0, 10.0);
182
183 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
184
185 EXPECT_TRUE(hit);
186 }
187
188 TEST_CASE(Intersect_GivenRayWithTMaxEqualToHitDistance_ReturnsFalse)
189 {
190 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
191 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 1.0);
192
193 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
194
195 EXPECT_FALSE(hit);
196 }
197
198 TEST_CASE(Intersect_GivenRayWithTMinLargerThanHitDistance_ReturnsFalse)
199 {
200 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
201 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.1, 10.0);
202
203 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
204
205 EXPECT_FALSE(hit);
206 }
207
208 TEST_CASE(Intersect_GivenRayWithTMaxSmallerThanHitDistance_ReturnsFalse)
209 {
210 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
211 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 0.9);
212
213 const bool hit = intersect(ray, RayInfo3d(ray), aabb);
214
215 EXPECT_FALSE(hit);
216 }
217
218 TEST_CASE(Intersect_GivenRayNotPiercingTheAABB_ReturnsFalseAndLeavesDistanceUnchanged)
219 {
220 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
221 const Ray3d ray(Vector3d(2.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
222
223 double distance = 42.0;
224 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
225
226 EXPECT_FALSE(hit);
227 EXPECT_EQ(42.0, distance);
228 }
229
230 TEST_CASE(Intersect_GivenRayEmbeddedInPosXFace_ReturnsTrueAndDistanceToHit)
231 {
232 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
233 const Ray3d ray(Vector3d(1.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
234
235 double distance = 42.0;
236 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
237
238 ASSERT_TRUE(hit);
239 EXPECT_FEQ(1.0, distance);
240 }
241
242 TEST_CASE(Intersect_GivenRayEmbeddedInNegXFace_ReturnsTrueAndDistanceToHit)
243 {
244 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
245 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
246
247 double distance = 42.0;
248 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
249
250 ASSERT_TRUE(hit);
251 EXPECT_FEQ(1.0, distance);
252 }
253
254 TEST_CASE(Intersect_GivenRayEmbeddedInPosYFace_ReturnsTrueAndDistanceToHit)
255 {
256 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
257 const Ray3d ray(Vector3d(2.0, 1.0, 0.0), Vector3d(-1.0, 0.0, 0.0));
258
259 double distance = 42.0;
260 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
261
262 ASSERT_TRUE(hit);
263 EXPECT_FEQ(1.0, distance);
264 }
265
266 TEST_CASE(Intersect_GivenRayEmbeddedInNegYFace_ReturnsTrueAndDistanceToHit)
267 {
268 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
269 const Ray3d ray(Vector3d(2.0, 0.0, 0.0), Vector3d(-1.0, 0.0, 0.0));
270
271 double distance = 42.0;
272 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
273
274 ASSERT_TRUE(hit);
275 EXPECT_FEQ(1.0, distance);
276 }
277
278 TEST_CASE(Intersect_GivenRayEmbeddedInPosZFace_ReturnsTrueAndDistanceToHit)
279 {
280 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
281 const Ray3d ray(Vector3d(0.0, 2.0, 1.0), Vector3d(0.0, -1.0, 0.0));
282
283 double distance = 42.0;
284 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
285
286 ASSERT_TRUE(hit);
287 EXPECT_FEQ(1.0, distance);
288 }
289
290 TEST_CASE(Intersect_GivenRayEmbeddedInNegZFace_ReturnsTrueAndDistanceToHit)
291 {
292 const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
293 const Ray3d ray(Vector3d(0.0, 2.0, 0.0), Vector3d(0.0, -1.0, 0.0));
294
295 double distance = 42.0;
296 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
297
298 ASSERT_TRUE(hit);
299 EXPECT_FEQ(1.0, distance);
300 }
301
302 TEST_CASE(Intersect_GivenRayWithTMinEqualToHitDistance_ReturnsTrueAndDistanceToHit)
303 {
304 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
305 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.0, 10.0);
306
307 double distance = 42.0;
308 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
309
310 EXPECT_TRUE(hit);
311 EXPECT_EQ(3.0, distance);
312 }
313
314 TEST_CASE(Intersect_GivenRayWithTMaxEqualToHitDistance_ReturnsFalseAndLeavesDistanceUnchanged)
315 {
316 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
317 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 1.0);
318
319 double distance = 42.0;
320 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
321
322 EXPECT_FALSE(hit);
323 EXPECT_EQ(42.0, distance);
324 }
325
326 TEST_CASE(Intersect_GivenRayWithTMinLargerThanHitDistance_ReturnsFalseAndLeavesDistanceUnchanged)
327 {
328 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
329 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.1, 10.0);
330
331 double distance = 42.0;
332 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
333
334 EXPECT_FALSE(hit);
335 EXPECT_EQ(42.0, distance);
336 }
337
338 TEST_CASE(Intersect_GivenRayWithTMaxSmallerThanHitDistance_ReturnsFalseAndLeavesDistanceUnchanged)
339 {
340 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
341 const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 0.9);
342
343 double distance = 42.0;
344 const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
345
346 EXPECT_FALSE(hit);
347 EXPECT_EQ(42.0, distance);
348 }
349
350 TEST_CASE(Clip_GivenRayNotPiercingTheAABB_ReturnsFalseAndLeavesTMinAndTMaxUnchanged)
351 {
352 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
353 Ray3d ray(Vector3d(2.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
354
355 const bool hit = clip(ray, RayInfo3d(ray), aabb);
356
357 EXPECT_FALSE(hit);
358 EXPECT_EQ(0.0, ray.m_tmin);
359 EXPECT_EQ(numeric_limits<double>::max(), ray.m_tmax);
360 }
361
362 TEST_CASE(Clip_GivenRayPiercingPosZFaceInTheMiddle_ReturnsTrueAndUpdatesTMinAndTMax)
363 {
364 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
365 Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
366
367 const bool hit = clip(ray, RayInfo3d(ray), aabb);
368
369 EXPECT_TRUE(hit);
370 EXPECT_FEQ(1.0, ray.m_tmin);
371 EXPECT_FEQ(3.0, ray.m_tmax);
372 }
373
374 TEST_CASE(Clip_GivenRayWithTMinEqualToHitDistance_ReturnsTrueAndUpdatesTMinAndTMax)
375 {
376 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
377 Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.0, 10.0);
378
379 const bool hit = clip(ray, RayInfo3d(ray), aabb);
380
381 EXPECT_TRUE(hit);
382 EXPECT_EQ(3.0, ray.m_tmin);
383 EXPECT_EQ(3.0, ray.m_tmax);
384 }
385
386 TEST_CASE(Clip_GivenRayWithTMaxEqualToHitDistance_ReturnsFalseAndLeavesTMinAndTMaxUnchanged)
387 {
388 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
389 Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 1.0);
390
391 const bool hit = clip(ray, RayInfo3d(ray), aabb);
392
393 EXPECT_FALSE(hit);
394 EXPECT_EQ(0.0, ray.m_tmin);
395 EXPECT_EQ(1.0, ray.m_tmax);
396 }
397
398 TEST_CASE(Clip_GivenRayWithTMinLargerThanHitDistance_ReturnsFalseAndLeavesTMinAndTMaxUnchanged)
399 {
400 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
401 Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.1, 10.0);
402
403 const bool hit = clip(ray, RayInfo3d(ray), aabb);
404
405 EXPECT_FALSE(hit);
406 EXPECT_EQ(3.1, ray.m_tmin);
407 EXPECT_EQ(10.0, ray.m_tmax);
408 }
409
410 TEST_CASE(Clip_GivenRayWithTMaxSmallerThanHitDistance_ReturnsFalseAndLeavesTMinAndTMaxUnchanged)
411 {
412 const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
413 Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 0.9);
414
415 const bool hit = clip(ray, RayInfo3d(ray), aabb);
416
417 EXPECT_FALSE(hit);
418 EXPECT_EQ(0.0, ray.m_tmin);
419 EXPECT_EQ(0.9, ray.m_tmax);
420 }
421
422 #pragma warning (pop)
423 }
424