summaryrefslogtreecommitdiffstats
path: root/core/math/aabb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/math/aabb.cpp')
-rw-r--r--core/math/aabb.cpp68
1 files changed, 48 insertions, 20 deletions
diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp
index 76e9e74dea..7d1d7c5648 100644
--- a/core/math/aabb.cpp
+++ b/core/math/aabb.cpp
@@ -117,55 +117,75 @@ AABB AABB::intersection(const AABB &p_aabb) const {
return AABB(min, max - min);
}
-bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip, Vector3 *r_normal) const {
+// Note that this routine returns the BACKTRACKED (i.e. behind the ray origin)
+// intersection point + normal if INSIDE the AABB.
+// The caller can therefore decide when INSIDE whether to use the
+// backtracked intersection, or use p_from as the intersection, and
+// carry on progressing without e.g. reflecting against the normal.
+bool AABB::find_intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, bool &r_inside, Vector3 *r_intersection_point, Vector3 *r_normal) const {
#ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) {
ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size.");
}
#endif
- Vector3 c1, c2;
Vector3 end = position + size;
- real_t depth_near = -1e20;
- real_t depth_far = 1e20;
+ real_t tmin = -1e20;
+ real_t tmax = 1e20;
int axis = 0;
+ // Make sure r_inside is always initialized,
+ // to prevent reading uninitialized data in the client code.
+ r_inside = false;
+
for (int i = 0; i < 3; i++) {
if (p_dir[i] == 0) {
if ((p_from[i] < position[i]) || (p_from[i] > end[i])) {
return false;
}
} else { // ray not parallel to planes in this direction
- c1[i] = (position[i] - p_from[i]) / p_dir[i];
- c2[i] = (end[i] - p_from[i]) / p_dir[i];
+ real_t t1 = (position[i] - p_from[i]) / p_dir[i];
+ real_t t2 = (end[i] - p_from[i]) / p_dir[i];
- if (c1[i] > c2[i]) {
- SWAP(c1, c2);
+ if (t1 > t2) {
+ SWAP(t1, t2);
}
- if (c1[i] > depth_near) {
- depth_near = c1[i];
+ if (t1 >= tmin) {
+ tmin = t1;
axis = i;
}
- if (c2[i] < depth_far) {
- depth_far = c2[i];
+ if (t2 < tmax) {
+ if (t2 < 0) {
+ return false;
+ }
+ tmax = t2;
}
- if ((depth_near > depth_far) || (depth_far < 0)) {
+ if (tmin > tmax) {
return false;
}
}
}
- if (r_clip) {
- *r_clip = c1;
+ // Did the ray start from inside the box?
+ // In which case the intersection returned is the point of entry
+ // (behind the ray start) or the calling routine can use the ray origin as intersection point.
+ r_inside = tmin < 0;
+
+ if (r_intersection_point) {
+ *r_intersection_point = p_from + p_dir * tmin;
+
+ // Prevent float error by making sure the point is exactly
+ // on the AABB border on the relevant axis.
+ r_intersection_point->coord[axis] = (p_dir[axis] >= 0) ? position.coord[axis] : end.coord[axis];
}
if (r_normal) {
*r_normal = Vector3();
- (*r_normal)[axis] = p_dir[axis] ? -1 : 1;
+ (*r_normal)[axis] = (p_dir[axis] >= 0) ? -1 : 1;
}
return true;
}
-bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_clip, Vector3 *r_normal) const {
+bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_intersection_point, Vector3 *r_normal) const {
#ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) {
ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size.");
@@ -223,8 +243,8 @@ bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector
*r_normal = normal;
}
- if (r_clip) {
- *r_clip = p_from + rel * min;
+ if (r_intersection_point) {
+ *r_intersection_point = p_from + rel * min;
}
return true;
@@ -410,7 +430,15 @@ Variant AABB::intersects_segment_bind(const Vector3 &p_from, const Vector3 &p_to
Variant AABB::intersects_ray_bind(const Vector3 &p_from, const Vector3 &p_dir) const {
Vector3 inters;
- if (intersects_ray(p_from, p_dir, &inters)) {
+ bool inside = false;
+
+ if (find_intersects_ray(p_from, p_dir, inside, &inters)) {
+ // When inside the intersection point may be BEHIND the ray,
+ // so for general use we return the ray origin.
+ if (inside) {
+ return p_from;
+ }
+
return inters;
}
return Variant();