2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #if !defined(INCLUDED_MATH_FRUSTUM_H)
23 #define INCLUDED_MATH_FRUSTUM_H
26 /// \brief View-frustum data types and related operations.
28 #include "generic/enumeration.h"
29 #include "math/matrix.h"
30 #include "math/plane.h"
31 #include "math/aabb.h"
32 #include "math/line.h"
34 inline Matrix4 matrix4_frustum(float left, float right, float bottom, float top, float nearval, float farval)
37 static_cast<float>( (2*nearval) / (right-left) ),
42 static_cast<float>( (2*nearval) / (top-bottom) ),
45 static_cast<float>( (right+left) / (right-left) ),
46 static_cast<float>( (top+bottom) / (top-bottom) ),
47 static_cast<float>( -(farval+nearval) / (farval-nearval) ),
51 static_cast<float>( -(2*farval*nearval) / (farval-nearval) ),
58 typedef unsigned char ClipResult;
59 const ClipResult c_CLIP_PASS = 0x00; // 000000
60 const ClipResult c_CLIP_LT_X = 0x01; // 000001
61 const ClipResult c_CLIP_GT_X = 0x02; // 000010
62 const ClipResult c_CLIP_LT_Y = 0x04; // 000100
63 const ClipResult c_CLIP_GT_Y = 0x08; // 001000
64 const ClipResult c_CLIP_LT_Z = 0x10; // 010000
65 const ClipResult c_CLIP_GT_Z = 0x20; // 100000
66 const ClipResult c_CLIP_FAIL = 0x3F; // 111111
68 template<typename Index>
72 static bool compare(const Vector4& self)
74 return self[Index::VALUE] < self[3];
76 static double scale(const Vector4& self, const Vector4& other)
78 return (self[Index::VALUE] - self[3]) / (other[3] - other[Index::VALUE]);
82 template<typename Index>
86 static bool compare(const Vector4& self)
88 return self[Index::VALUE] > -self[3];
90 static double scale(const Vector4& self, const Vector4& other)
92 return (self[Index::VALUE] + self[3]) / (-other[3] - other[Index::VALUE]);
96 template<typename ClipPlane>
97 class Vector4ClipPolygon
100 typedef Vector4* iterator;
101 typedef const Vector4* const_iterator;
103 static std::size_t apply(const_iterator first, const_iterator last, iterator out)
105 const_iterator next = first, i = last - 1;
107 bool b0 = ClipPlane::compare(*i);
110 bool b1 = ClipPlane::compare(*next);
113 *out = vector4_subtracted(*next, *i);
115 double scale = ClipPlane::scale(*i, *out);
117 (*out)[0] = static_cast<float>((*i)[0] + scale*((*out)[0]));
118 (*out)[1] = static_cast<float>((*i)[1] + scale*((*out)[1]));
119 (*out)[2] = static_cast<float>((*i)[2] + scale*((*out)[2]));
120 (*out)[3] = static_cast<float>((*i)[3] + scale*((*out)[3]));
140 #define CLIP_X_LT_W(p) (Vector4ClipLT< IntegralConstant<0> >::compare(p))
141 #define CLIP_X_GT_W(p) (Vector4ClipGT< IntegralConstant<0> >::compare(p))
142 #define CLIP_Y_LT_W(p) (Vector4ClipLT< IntegralConstant<1> >::compare(p))
143 #define CLIP_Y_GT_W(p) (Vector4ClipGT< IntegralConstant<1> >::compare(p))
144 #define CLIP_Z_LT_W(p) (Vector4ClipLT< IntegralConstant<2> >::compare(p))
145 #define CLIP_Z_GT_W(p) (Vector4ClipGT< IntegralConstant<2> >::compare(p))
147 inline ClipResult homogenous_clip_point(const Vector4& clipped)
149 ClipResult result = c_CLIP_FAIL;
150 if(CLIP_X_LT_W(clipped)) result &= ~c_CLIP_LT_X; // X < W
151 if(CLIP_X_GT_W(clipped)) result &= ~c_CLIP_GT_X; // X > -W
152 if(CLIP_Y_LT_W(clipped)) result &= ~c_CLIP_LT_Y; // Y < W
153 if(CLIP_Y_GT_W(clipped)) result &= ~c_CLIP_GT_Y; // Y > -W
154 if(CLIP_Z_LT_W(clipped)) result &= ~c_CLIP_LT_Z; // Z < W
155 if(CLIP_Z_GT_W(clipped)) result &= ~c_CLIP_GT_Z; // Z > -W
159 /// \brief Clips \p point by canonical matrix \p self.
160 /// Stores the result in \p clipped.
161 /// Returns a bitmask indicating which clip-planes the point was outside.
162 inline ClipResult matrix4_clip_point(const Matrix4& self, const Vector3& point, Vector4& clipped)
164 clipped[0] = point[0];
165 clipped[1] = point[1];
166 clipped[2] = point[2];
168 matrix4_transform_vector4(self, clipped);
169 return homogenous_clip_point(clipped);
173 inline std::size_t homogenous_clip_triangle(Vector4 clipped[9])
176 std::size_t count = 3;
177 count = Vector4ClipPolygon< Vector4ClipLT< IntegralConstant<0> > >::apply(clipped, clipped + count, buffer);
178 count = Vector4ClipPolygon< Vector4ClipGT< IntegralConstant<0> > >::apply(buffer, buffer + count, clipped);
179 count = Vector4ClipPolygon< Vector4ClipLT< IntegralConstant<1> > >::apply(clipped, clipped + count, buffer);
180 count = Vector4ClipPolygon< Vector4ClipGT< IntegralConstant<1> > >::apply(buffer, buffer + count, clipped);
181 count = Vector4ClipPolygon< Vector4ClipLT< IntegralConstant<2> > >::apply(clipped, clipped + count, buffer);
182 return Vector4ClipPolygon< Vector4ClipGT< IntegralConstant<2> > >::apply(buffer, buffer + count, clipped);
185 /// \brief Transforms and clips the triangle formed by \p p0, \p p1, \p p2 by the canonical matrix \p self.
186 /// Stores the resulting polygon in \p clipped.
187 /// Returns the number of points in the resulting polygon.
188 inline std::size_t matrix4_clip_triangle(const Matrix4& self, const Vector3& p0, const Vector3& p1, const Vector3& p2, Vector4 clipped[9])
190 clipped[0][0] = p0[0];
191 clipped[0][1] = p0[1];
192 clipped[0][2] = p0[2];
194 clipped[1][0] = p1[0];
195 clipped[1][1] = p1[1];
196 clipped[1][2] = p1[2];
198 clipped[2][0] = p2[0];
199 clipped[2][1] = p2[1];
200 clipped[2][2] = p2[2];
203 matrix4_transform_vector4(self, clipped[0]);
204 matrix4_transform_vector4(self, clipped[1]);
205 matrix4_transform_vector4(self, clipped[2]);
207 return homogenous_clip_triangle(clipped);
210 inline std::size_t homogenous_clip_line(Vector4 clipped[2])
212 const Vector4& p0 = clipped[0];
213 const Vector4& p1 = clipped[1];
217 ClipResult mask0 = homogenous_clip_point(clipped[0]);
218 ClipResult mask1 = homogenous_clip_point(clipped[1]);
220 if((mask0 | mask1) == c_CLIP_PASS) // both points passed all planes
223 if(mask0 & mask1) // both points failed any one plane
228 const bool index = CLIP_X_LT_W(p0);
229 if(index ^ CLIP_X_LT_W(p1))
231 Vector4 clip(vector4_subtracted(p1, p0));
233 double scale = (p0[0] - p0[3]) / (clip[3] - clip[0]);
235 clip[0] = static_cast<float>(p0[0] + scale * clip[0]);
236 clip[1] = static_cast<float>(p0[1] + scale * clip[1]);
237 clip[2] = static_cast<float>(p0[2] + scale * clip[2]);
238 clip[3] = static_cast<float>(p0[3] + scale * clip[3]);
240 clipped[index] = clip;
247 const bool index = CLIP_X_GT_W(p0);
248 if(index ^ CLIP_X_GT_W(p1))
250 Vector4 clip(vector4_subtracted(p1, p0));
252 double scale = (p0[0] + p0[3]) / (-clip[3] - clip[0]);
254 clip[0] = static_cast<float>(p0[0] + scale * clip[0]);
255 clip[1] = static_cast<float>(p0[1] + scale * clip[1]);
256 clip[2] = static_cast<float>(p0[2] + scale * clip[2]);
257 clip[3] = static_cast<float>(p0[3] + scale * clip[3]);
259 clipped[index] = clip;
266 const bool index = CLIP_Y_LT_W(p0);
267 if(index ^ CLIP_Y_LT_W(p1))
269 Vector4 clip(vector4_subtracted(p1, p0));
271 double scale = (p0[1] - p0[3]) / (clip[3] - clip[1]);
273 clip[0] = static_cast<float>(p0[0] + scale * clip[0]);
274 clip[1] = static_cast<float>(p0[1] + scale * clip[1]);
275 clip[2] = static_cast<float>(p0[2] + scale * clip[2]);
276 clip[3] = static_cast<float>(p0[3] + scale * clip[3]);
278 clipped[index] = clip;
285 const bool index = CLIP_Y_GT_W(p0);
286 if(index ^ CLIP_Y_GT_W(p1))
288 Vector4 clip(vector4_subtracted(p1, p0));
290 double scale = (p0[1] + p0[3]) / (-clip[3] - clip[1]);
292 clip[0] = static_cast<float>(p0[0] + scale * clip[0]);
293 clip[1] = static_cast<float>(p0[1] + scale * clip[1]);
294 clip[2] = static_cast<float>(p0[2] + scale * clip[2]);
295 clip[3] = static_cast<float>(p0[3] + scale * clip[3]);
297 clipped[index] = clip;
304 const bool index = CLIP_Z_LT_W(p0);
305 if(index ^ CLIP_Z_LT_W(p1))
307 Vector4 clip(vector4_subtracted(p1, p0));
309 double scale = (p0[2] - p0[3]) / (clip[3] - clip[2]);
311 clip[0] = static_cast<float>(p0[0] + scale * clip[0]);
312 clip[1] = static_cast<float>(p0[1] + scale * clip[1]);
313 clip[2] = static_cast<float>(p0[2] + scale * clip[2]);
314 clip[3] = static_cast<float>(p0[3] + scale * clip[3]);
316 clipped[index] = clip;
323 const bool index = CLIP_Z_GT_W(p0);
324 if(index ^ CLIP_Z_GT_W(p1))
326 Vector4 clip(vector4_subtracted(p1, p0));
328 double scale = (p0[2] + p0[3]) / (-clip[3] - clip[2]);
330 clip[0] = static_cast<float>(p0[0] + scale * clip[0]);
331 clip[1] = static_cast<float>(p0[1] + scale * clip[1]);
332 clip[2] = static_cast<float>(p0[2] + scale * clip[2]);
333 clip[3] = static_cast<float>(p0[3] + scale * clip[3]);
335 clipped[index] = clip;
344 /// \brief Transforms and clips the line formed by \p p0, \p p1 by the canonical matrix \p self.
345 /// Stores the resulting line in \p clipped.
346 /// Returns the number of points in the resulting line.
347 inline std::size_t matrix4_clip_line(const Matrix4& self, const Vector3& p0, const Vector3& p1, Vector4 clipped[2])
349 clipped[0][0] = p0[0];
350 clipped[0][1] = p0[1];
351 clipped[0][2] = p0[2];
353 clipped[1][0] = p1[0];
354 clipped[1][1] = p1[1];
355 clipped[1][2] = p1[2];
358 matrix4_transform_vector4(self, clipped[0]);
359 matrix4_transform_vector4(self, clipped[1]);
361 return homogenous_clip_line(clipped);
369 Plane3 right, left, bottom, top, back, front;
374 Frustum(const Plane3& _right,
376 const Plane3& _bottom,
379 const Plane3& _front)
380 : right(_right), left(_left), bottom(_bottom), top(_top), back(_back), front(_front)
385 inline Frustum frustum_transformed(const Frustum& frustum, const Matrix4& transform)
388 plane3_transformed(frustum.right, transform),
389 plane3_transformed(frustum.left, transform),
390 plane3_transformed(frustum.bottom, transform),
391 plane3_transformed(frustum.top, transform),
392 plane3_transformed(frustum.back, transform),
393 plane3_transformed(frustum.front, transform)
397 inline Frustum frustum_inverse_transformed(const Frustum& frustum, const Matrix4& transform)
400 plane3_inverse_transformed(frustum.right, transform),
401 plane3_inverse_transformed(frustum.left, transform),
402 plane3_inverse_transformed(frustum.bottom, transform),
403 plane3_inverse_transformed(frustum.top, transform),
404 plane3_inverse_transformed(frustum.back, transform),
405 plane3_inverse_transformed(frustum.front, transform)
409 inline bool viewproj_test_point(const Matrix4& viewproj, const Vector3& point)
411 Vector4 hpoint(matrix4_transformed_vector4(viewproj, Vector4(point, 1.0f)));
412 if(fabs(hpoint[0]) < fabs(hpoint[3])
413 && fabs(hpoint[1]) < fabs(hpoint[3])
414 && fabs(hpoint[2]) < fabs(hpoint[3]))
419 inline bool viewproj_test_transformed_point(const Matrix4& viewproj, const Vector3& point, const Matrix4& localToWorld)
421 return viewproj_test_point(viewproj, matrix4_transformed_point(localToWorld, point));
424 inline Frustum frustum_from_viewproj(const Matrix4& viewproj)
428 plane3_normalised(Plane3(viewproj[ 3] - viewproj[ 0], viewproj[ 7] - viewproj[ 4], viewproj[11] - viewproj[ 8], viewproj[15] - viewproj[12])),
429 plane3_normalised(Plane3(viewproj[ 3] + viewproj[ 0], viewproj[ 7] + viewproj[ 4], viewproj[11] + viewproj[ 8], viewproj[15] + viewproj[12])),
430 plane3_normalised(Plane3(viewproj[ 3] + viewproj[ 1], viewproj[ 7] + viewproj[ 5], viewproj[11] + viewproj[ 9], viewproj[15] + viewproj[13])),
431 plane3_normalised(Plane3(viewproj[ 3] - viewproj[ 1], viewproj[ 7] - viewproj[ 5], viewproj[11] - viewproj[ 9], viewproj[15] - viewproj[13])),
432 plane3_normalised(Plane3(viewproj[ 3] - viewproj[ 2], viewproj[ 7] - viewproj[ 6], viewproj[11] - viewproj[10], viewproj[15] - viewproj[14])),
433 plane3_normalised(Plane3(viewproj[ 3] + viewproj[ 2], viewproj[ 7] + viewproj[ 6], viewproj[11] + viewproj[10], viewproj[15] + viewproj[14]))
437 struct VolumeIntersection
447 typedef EnumeratedValue<VolumeIntersection> VolumeIntersectionValue;
449 const VolumeIntersectionValue c_volumeOutside(VolumeIntersectionValue::OUTSIDE);
450 const VolumeIntersectionValue c_volumeInside(VolumeIntersectionValue::INSIDE);
451 const VolumeIntersectionValue c_volumePartial(VolumeIntersectionValue::PARTIAL);
453 inline VolumeIntersectionValue frustum_test_aabb(const Frustum& frustum, const AABB& aabb)
455 VolumeIntersectionValue result = c_volumeInside;
457 switch(aabb_classify_plane(aabb, frustum.right))
460 return c_volumeOutside;
462 result = c_volumePartial;
465 switch(aabb_classify_plane(aabb, frustum.left))
468 return c_volumeOutside;
470 result = c_volumePartial;
473 switch(aabb_classify_plane(aabb, frustum.bottom))
476 return c_volumeOutside;
478 result = c_volumePartial;
481 switch(aabb_classify_plane(aabb, frustum.top))
484 return c_volumeOutside;
486 result = c_volumePartial;
489 switch(aabb_classify_plane(aabb, frustum.back))
492 return c_volumeOutside;
494 result = c_volumePartial;
497 switch(aabb_classify_plane(aabb, frustum.front))
500 return c_volumeOutside;
502 result = c_volumePartial;
508 inline double plane_distance_to_point(const Plane3& plane, const Vector3& point)
510 return vector3_dot(plane.normal(), point) + plane.d;
513 inline double plane_distance_to_oriented_extents(const Plane3& plane, const Vector3& extents, const Matrix4& orientation)
515 return fabs(extents[0] * vector3_dot(plane.normal(), vector4_to_vector3(orientation.x())))
516 + fabs(extents[1] * vector3_dot(plane.normal(), vector4_to_vector3(orientation.y())))
517 + fabs(extents[2] * vector3_dot(plane.normal(), vector4_to_vector3(orientation.z())));
520 /// \brief Return false if \p aabb with \p orientation is partially or completely outside \p plane.
521 inline bool plane_contains_oriented_aabb(const Plane3& plane, const AABB& aabb, const Matrix4& orientation)
523 double dot = plane_distance_to_point(plane, aabb.origin);
524 return !(dot > 0 || -dot < plane_distance_to_oriented_extents(plane, aabb.extents, orientation));
527 inline VolumeIntersectionValue frustum_intersects_transformed_aabb(const Frustum& frustum, const AABB& aabb, const Matrix4& localToWorld)
529 AABB aabb_world(aabb);
530 matrix4_transform_point(localToWorld, aabb_world.origin);
532 if(plane_contains_oriented_aabb(frustum.right, aabb_world, localToWorld)
533 || plane_contains_oriented_aabb(frustum.left, aabb_world, localToWorld)
534 || plane_contains_oriented_aabb(frustum.bottom, aabb_world, localToWorld)
535 || plane_contains_oriented_aabb(frustum.top, aabb_world, localToWorld)
536 || plane_contains_oriented_aabb(frustum.back, aabb_world, localToWorld)
537 || plane_contains_oriented_aabb(frustum.front, aabb_world, localToWorld))
538 return c_volumeOutside;
539 return c_volumeInside;
542 inline bool plane3_test_point(const Plane3& plane, const Vector3& point)
544 return vector3_dot(point, plane.normal()) + plane.dist() <= 0;
547 inline bool plane3_test_line(const Plane3& plane, const Segment& segment)
549 return segment_classify_plane(segment, plane) == 2;
552 inline bool frustum_test_point(const Frustum& frustum, const Vector3& point)
554 return !plane3_test_point(frustum.right, point)
555 && !plane3_test_point(frustum.left, point)
556 && !plane3_test_point(frustum.bottom, point)
557 && !plane3_test_point(frustum.top, point)
558 && !plane3_test_point(frustum.back, point)
559 && !plane3_test_point(frustum.front, point);
562 inline bool frustum_test_line(const Frustum& frustum, const Segment& segment)
564 return !plane3_test_line(frustum.right, segment)
565 && !plane3_test_line(frustum.left, segment)
566 && !plane3_test_line(frustum.bottom, segment)
567 && !plane3_test_line(frustum.top, segment)
568 && !plane3_test_line(frustum.back, segment)
569 && !plane3_test_line(frustum.front, segment);
572 inline bool viewer_test_plane(const Vector4& viewer, const Plane3& plane)
574 return ((plane.a * viewer[0])
575 + (plane.b * viewer[1])
576 + (plane.c * viewer[2])
577 + (plane.d * viewer[3])) > 0;
580 inline Vector3 triangle_cross(const Vector3& p0, const Vector3& p1, const Vector3& p2)
582 return vector3_cross(vector3_subtracted(p1, p0), vector3_subtracted(p1, p2));
585 inline bool viewer_test_triangle(const Vector4& viewer, const Vector3& p0, const Vector3& p1, const Vector3& p2)
587 Vector3 cross(triangle_cross(p0, p1, p2));
588 return ((viewer[0] * cross[0])
589 + (viewer[1] * cross[1])
590 + (viewer[2] * cross[2])
591 + (viewer[3] * 0)) > 0;
594 inline Vector4 viewer_from_transformed_viewer(const Vector4& viewer, const Matrix4& transform)
598 return Vector4(matrix4_transformed_direction(transform, vector4_to_vector3(viewer)), 0);
602 return Vector4(matrix4_transformed_point(transform, vector4_to_vector3(viewer)), viewer[3]);
606 inline bool viewer_test_transformed_plane(const Vector4& viewer, const Plane3& plane, const Matrix4& localToWorld)
609 return viewer_test_plane(viewer_from_transformed_viewer(viewer, matrix4_affine_inverse(localToWorld)), plane);
611 return viewer_test_plane(viewer, plane3_transformed(plane, localToWorld));
615 inline Vector4 viewer_from_viewproj(const Matrix4& viewproj)
617 // get viewer pos in object coords
618 Vector4 viewer(matrix4_transformed_vector4(matrix4_full_inverse(viewproj), Vector4(0, 0, -1, 0)));
619 if(viewer[3] != 0) // non-affine matrix
621 viewer[0] /= viewer[3];
622 viewer[1] /= viewer[3];
623 viewer[2] /= viewer[3];
624 viewer[3] /= viewer[3];