1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4 
5    This file is part of GtkRadiant.
6 
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.
11 
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.
16 
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
20  */
21 
22 #if !defined( INCLUDED_MATH_FRUSTUM_H )
23 #define INCLUDED_MATH_FRUSTUM_H
24 
25 /// \file
26 /// \brief View-frustum data types and related operations.
27 
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"
33 
matrix4_frustum(float left,float right,float bottom,float top,float nearval,float farval)34 inline Matrix4 matrix4_frustum( float left, float right, float bottom, float top, float nearval, float farval ){
35 	return Matrix4(
36 			   static_cast<float>( ( 2 * nearval ) / ( right - left ) ),
37 			   0,
38 			   0,
39 			   0,
40 			   0,
41 			   static_cast<float>( ( 2 * nearval ) / ( top - bottom ) ),
42 			   0,
43 			   0,
44 			   static_cast<float>( ( right + left ) / ( right - left ) ),
45 			   static_cast<float>( ( top + bottom ) / ( top - bottom ) ),
46 			   static_cast<float>( -( farval + nearval ) / ( farval - nearval ) ),
47 			   -1,
48 			   0,
49 			   0,
50 			   static_cast<float>( -( 2 * farval * nearval ) / ( farval - nearval ) ),
51 			   0
52 			   );
53 }
54 
55 
56 
57 typedef unsigned char ClipResult;
58 const ClipResult c_CLIP_PASS = 0x00; // 000000
59 const ClipResult c_CLIP_LT_X = 0x01; // 000001
60 const ClipResult c_CLIP_GT_X = 0x02; // 000010
61 const ClipResult c_CLIP_LT_Y = 0x04; // 000100
62 const ClipResult c_CLIP_GT_Y = 0x08; // 001000
63 const ClipResult c_CLIP_LT_Z = 0x10; // 010000
64 const ClipResult c_CLIP_GT_Z = 0x20; // 100000
65 const ClipResult c_CLIP_FAIL = 0x3F; // 111111
66 
67 template<typename Index>
68 class Vector4ClipLT
69 {
70 public:
compare(const Vector4 & self)71 static bool compare( const Vector4& self ){
72 	return self[Index::VALUE] < self[3];
73 }
scale(const Vector4 & self,const Vector4 & other)74 static double scale( const Vector4& self, const Vector4& other ){
75 	return ( self[Index::VALUE] - self[3] ) / ( other[3] - other[Index::VALUE] );
76 }
77 };
78 
79 template<typename Index>
80 class Vector4ClipGT
81 {
82 public:
compare(const Vector4 & self)83 static bool compare( const Vector4& self ){
84 	return self[Index::VALUE] > -self[3];
85 }
scale(const Vector4 & self,const Vector4 & other)86 static double scale( const Vector4& self, const Vector4& other ){
87 	return ( self[Index::VALUE] + self[3] ) / ( -other[3] - other[Index::VALUE] );
88 }
89 };
90 
91 template<typename ClipPlane>
92 class Vector4ClipPolygon
93 {
94 public:
95 typedef Vector4* iterator;
96 typedef const Vector4* const_iterator;
97 
apply(const_iterator first,const_iterator last,iterator out)98 static std::size_t apply( const_iterator first, const_iterator last, iterator out ){
99 	const_iterator next = first, i = last - 1;
100 	iterator tmp( out );
101 	bool b0 = ClipPlane::compare( *i );
102 	while ( next != last )
103 	{
104 		bool b1 = ClipPlane::compare( *next );
105 		if ( b0 ^ b1 ) {
106 			*out = vector4_subtracted( *next, *i );
107 
108 			double scale = ClipPlane::scale( *i, *out );
109 
110 			( *out )[0] = static_cast<float>( ( *i )[0] + scale * ( ( *out )[0] ) );
111 			( *out )[1] = static_cast<float>( ( *i )[1] + scale * ( ( *out )[1] ) );
112 			( *out )[2] = static_cast<float>( ( *i )[2] + scale * ( ( *out )[2] ) );
113 			( *out )[3] = static_cast<float>( ( *i )[3] + scale * ( ( *out )[3] ) );
114 
115 			++out;
116 		}
117 
118 		if ( b1 ) {
119 			*out = *next;
120 			++out;
121 		}
122 
123 		i = next;
124 		++next;
125 		b0 = b1;
126 	}
127 
128 	return out - tmp;
129 }
130 };
131 
132 #define CLIP_X_LT_W( p ) ( Vector4ClipLT< IntegralConstant<0> >::compare( p ) )
133 #define CLIP_X_GT_W( p ) ( Vector4ClipGT< IntegralConstant<0> >::compare( p ) )
134 #define CLIP_Y_LT_W( p ) ( Vector4ClipLT< IntegralConstant<1> >::compare( p ) )
135 #define CLIP_Y_GT_W( p ) ( Vector4ClipGT< IntegralConstant<1> >::compare( p ) )
136 #define CLIP_Z_LT_W( p ) ( Vector4ClipLT< IntegralConstant<2> >::compare( p ) )
137 #define CLIP_Z_GT_W( p ) ( Vector4ClipGT< IntegralConstant<2> >::compare( p ) )
138 
homogenous_clip_point(const Vector4 & clipped)139 inline ClipResult homogenous_clip_point( const Vector4& clipped ){
140 	ClipResult result = c_CLIP_FAIL;
141 	if ( CLIP_X_LT_W( clipped ) ) {
142 		result &= ~c_CLIP_LT_X;                    // X < W
143 	}
144 	if ( CLIP_X_GT_W( clipped ) ) {
145 		result &= ~c_CLIP_GT_X;                    // X > -W
146 	}
147 	if ( CLIP_Y_LT_W( clipped ) ) {
148 		result &= ~c_CLIP_LT_Y;                    // Y < W
149 	}
150 	if ( CLIP_Y_GT_W( clipped ) ) {
151 		result &= ~c_CLIP_GT_Y;                    // Y > -W
152 	}
153 	if ( CLIP_Z_LT_W( clipped ) ) {
154 		result &= ~c_CLIP_LT_Z;                    // Z < W
155 	}
156 	if ( CLIP_Z_GT_W( clipped ) ) {
157 		result &= ~c_CLIP_GT_Z;                    // Z > -W
158 	}
159 	return result;
160 }
161 
162 /// \brief Clips \p point by canonical matrix \p self.
163 /// Stores the result in \p clipped.
164 /// Returns a bitmask indicating which clip-planes the point was outside.
matrix4_clip_point(const Matrix4 & self,const Vector3 & point,Vector4 & clipped)165 inline ClipResult matrix4_clip_point( const Matrix4& self, const Vector3& point, Vector4& clipped ){
166 	clipped[0] = point[0];
167 	clipped[1] = point[1];
168 	clipped[2] = point[2];
169 	clipped[3] = 1;
170 	matrix4_transform_vector4( self, clipped );
171 	return homogenous_clip_point( clipped );
172 }
173 
174 
homogenous_clip_triangle(Vector4 clipped[9])175 inline std::size_t homogenous_clip_triangle( Vector4 clipped[9] ){
176 	Vector4 buffer[9];
177 	std::size_t count = 3;
178 	count = Vector4ClipPolygon< Vector4ClipLT< IntegralConstant<0> > >::apply( clipped, clipped + count, buffer );
179 	count = Vector4ClipPolygon< Vector4ClipGT< IntegralConstant<0> > >::apply( buffer, buffer + count, clipped );
180 	count = Vector4ClipPolygon< Vector4ClipLT< IntegralConstant<1> > >::apply( clipped, clipped + count, buffer );
181 	count = Vector4ClipPolygon< Vector4ClipGT< IntegralConstant<1> > >::apply( buffer, buffer + count, clipped );
182 	count = Vector4ClipPolygon< Vector4ClipLT< IntegralConstant<2> > >::apply( clipped, clipped + count, buffer );
183 	return Vector4ClipPolygon< Vector4ClipGT< IntegralConstant<2> > >::apply( buffer, buffer + count, clipped );
184 }
185 
186 /// \brief Transforms and clips the triangle formed by \p p0, \p p1, \p p2 by the canonical matrix \p self.
187 /// Stores the resulting polygon in \p clipped.
188 /// Returns the number of points in the resulting polygon.
matrix4_clip_triangle(const Matrix4 & self,const Vector3 & p0,const Vector3 & p1,const Vector3 & p2,Vector4 clipped[9])189 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];
193 	clipped[0][3] = 1;
194 	clipped[1][0] = p1[0];
195 	clipped[1][1] = p1[1];
196 	clipped[1][2] = p1[2];
197 	clipped[1][3] = 1;
198 	clipped[2][0] = p2[0];
199 	clipped[2][1] = p2[1];
200 	clipped[2][2] = p2[2];
201 	clipped[2][3] = 1;
202 
203 	matrix4_transform_vector4( self, clipped[0] );
204 	matrix4_transform_vector4( self, clipped[1] );
205 	matrix4_transform_vector4( self, clipped[2] );
206 
207 	return homogenous_clip_triangle( clipped );
208 }
209 
homogenous_clip_line(Vector4 clipped[2])210 inline std::size_t homogenous_clip_line( Vector4 clipped[2] ){
211 	const Vector4& p0 = clipped[0];
212 	const Vector4& p1 = clipped[1];
213 
214 	// early out
215 	{
216 		ClipResult mask0 = homogenous_clip_point( clipped[0] );
217 		ClipResult mask1 = homogenous_clip_point( clipped[1] );
218 
219 		if ( ( mask0 | mask1 ) == c_CLIP_PASS ) { // both points passed all planes
220 			return 2;
221 		}
222 
223 		if ( mask0 & mask1 ) { // both points failed any one plane
224 			return 0;
225 		}
226 	}
227 
228 	{
229 		const bool index = CLIP_X_LT_W( p0 );
230 		if ( index ^ CLIP_X_LT_W( p1 ) ) {
231 			Vector4 clip( vector4_subtracted( p1, p0 ) );
232 
233 			double scale = ( p0[0] - p0[3] ) / ( clip[3] - clip[0] );
234 
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] );
239 
240 			clipped[index] = clip;
241 		}
242 		else if ( index == 0 ) {
243 			return 0;
244 		}
245 	}
246 
247 	{
248 		const bool index = CLIP_X_GT_W( p0 );
249 		if ( index ^ CLIP_X_GT_W( p1 ) ) {
250 			Vector4 clip( vector4_subtracted( p1, p0 ) );
251 
252 			double scale = ( p0[0] + p0[3] ) / ( -clip[3] - clip[0] );
253 
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] );
258 
259 			clipped[index] = clip;
260 		}
261 		else if ( index == 0 ) {
262 			return 0;
263 		}
264 	}
265 
266 	{
267 		const bool index = CLIP_Y_LT_W( p0 );
268 		if ( index ^ CLIP_Y_LT_W( p1 ) ) {
269 			Vector4 clip( vector4_subtracted( p1, p0 ) );
270 
271 			double scale = ( p0[1] - p0[3] ) / ( clip[3] - clip[1] );
272 
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] );
277 
278 			clipped[index] = clip;
279 		}
280 		else if ( index == 0 ) {
281 			return 0;
282 		}
283 	}
284 
285 	{
286 		const bool index = CLIP_Y_GT_W( p0 );
287 		if ( index ^ CLIP_Y_GT_W( p1 ) ) {
288 			Vector4 clip( vector4_subtracted( p1, p0 ) );
289 
290 			double scale = ( p0[1] + p0[3] ) / ( -clip[3] - clip[1] );
291 
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] );
296 
297 			clipped[index] = clip;
298 		}
299 		else if ( index == 0 ) {
300 			return 0;
301 		}
302 	}
303 
304 	{
305 		const bool index = CLIP_Z_LT_W( p0 );
306 		if ( index ^ CLIP_Z_LT_W( p1 ) ) {
307 			Vector4 clip( vector4_subtracted( p1, p0 ) );
308 
309 			double scale = ( p0[2] - p0[3] ) / ( clip[3] - clip[2] );
310 
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] );
315 
316 			clipped[index] = clip;
317 		}
318 		else if ( index == 0 ) {
319 			return 0;
320 		}
321 	}
322 
323 	{
324 		const bool index = CLIP_Z_GT_W( p0 );
325 		if ( index ^ CLIP_Z_GT_W( p1 ) ) {
326 			Vector4 clip( vector4_subtracted( p1, p0 ) );
327 
328 			double scale = ( p0[2] + p0[3] ) / ( -clip[3] - clip[2] );
329 
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] );
334 
335 			clipped[index] = clip;
336 		}
337 		else if ( index == 0 ) {
338 			return 0;
339 		}
340 	}
341 
342 	return 2;
343 }
344 
345 /// \brief Transforms and clips the line formed by \p p0, \p p1 by the canonical matrix \p self.
346 /// Stores the resulting line in \p clipped.
347 /// Returns the number of points in the resulting line.
matrix4_clip_line(const Matrix4 & self,const Vector3 & p0,const Vector3 & p1,Vector4 clipped[2])348 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];
352 	clipped[0][3] = 1;
353 	clipped[1][0] = p1[0];
354 	clipped[1][1] = p1[1];
355 	clipped[1][2] = p1[2];
356 	clipped[1][3] = 1;
357 
358 	matrix4_transform_vector4( self, clipped[0] );
359 	matrix4_transform_vector4( self, clipped[1] );
360 
361 	return homogenous_clip_line( clipped );
362 }
363 
364 
365 
366 
367 struct Frustum
368 {
369 	Plane3 right, left, bottom, top, back, front;
370 
FrustumFrustum371 	Frustum(){
372 	}
FrustumFrustum373 	Frustum( const Plane3& _right,
374 			 const Plane3& _left,
375 			 const Plane3& _bottom,
376 			 const Plane3& _top,
377 			 const Plane3& _back,
378 			 const Plane3& _front )
379 		: right( _right ), left( _left ), bottom( _bottom ), top( _top ), back( _back ), front( _front ){
380 	}
381 };
382 
frustum_transformed(const Frustum & frustum,const Matrix4 & transform)383 inline Frustum frustum_transformed( const Frustum& frustum, const Matrix4& transform ){
384 	return Frustum(
385 			   plane3_transformed( frustum.right, transform ),
386 			   plane3_transformed( frustum.left, transform ),
387 			   plane3_transformed( frustum.bottom, transform ),
388 			   plane3_transformed( frustum.top, transform ),
389 			   plane3_transformed( frustum.back, transform ),
390 			   plane3_transformed( frustum.front, transform )
391 			   );
392 }
393 
frustum_inverse_transformed(const Frustum & frustum,const Matrix4 & transform)394 inline Frustum frustum_inverse_transformed( const Frustum& frustum, const Matrix4& transform ){
395 	return Frustum(
396 			   plane3_inverse_transformed( frustum.right, transform ),
397 			   plane3_inverse_transformed( frustum.left, transform ),
398 			   plane3_inverse_transformed( frustum.bottom, transform ),
399 			   plane3_inverse_transformed( frustum.top, transform ),
400 			   plane3_inverse_transformed( frustum.back, transform ),
401 			   plane3_inverse_transformed( frustum.front, transform )
402 			   );
403 }
404 
viewproj_test_point(const Matrix4 & viewproj,const Vector3 & point)405 inline bool viewproj_test_point( const Matrix4& viewproj, const Vector3& point ){
406 	Vector4 hpoint( matrix4_transformed_vector4( viewproj, Vector4( point, 1.0f ) ) );
407 	if ( fabs( hpoint[0] ) < fabs( hpoint[3] )
408 		 && fabs( hpoint[1] ) < fabs( hpoint[3] )
409 		 && fabs( hpoint[2] ) < fabs( hpoint[3] ) ) {
410 		return true;
411 	}
412 	return false;
413 }
414 
viewproj_test_transformed_point(const Matrix4 & viewproj,const Vector3 & point,const Matrix4 & localToWorld)415 inline bool viewproj_test_transformed_point( const Matrix4& viewproj, const Vector3& point, const Matrix4& localToWorld ){
416 	return viewproj_test_point( viewproj, matrix4_transformed_point( localToWorld, point ) );
417 }
418 
frustum_from_viewproj(const Matrix4 & viewproj)419 inline Frustum frustum_from_viewproj( const Matrix4& viewproj ){
420 	return Frustum
421 		   (
422 			   plane3_normalised( Plane3( viewproj[ 3] - viewproj[ 0], viewproj[ 7] - viewproj[ 4], viewproj[11] - viewproj[ 8], viewproj[15] - viewproj[12] ) ),
423 			   plane3_normalised( Plane3( viewproj[ 3] + viewproj[ 0], viewproj[ 7] + viewproj[ 4], viewproj[11] + viewproj[ 8], viewproj[15] + viewproj[12] ) ),
424 			   plane3_normalised( Plane3( viewproj[ 3] + viewproj[ 1], viewproj[ 7] + viewproj[ 5], viewproj[11] + viewproj[ 9], viewproj[15] + viewproj[13] ) ),
425 			   plane3_normalised( Plane3( viewproj[ 3] - viewproj[ 1], viewproj[ 7] - viewproj[ 5], viewproj[11] - viewproj[ 9], viewproj[15] - viewproj[13] ) ),
426 			   plane3_normalised( Plane3( viewproj[ 3] - viewproj[ 2], viewproj[ 7] - viewproj[ 6], viewproj[11] - viewproj[10], viewproj[15] - viewproj[14] ) ),
427 			   plane3_normalised( Plane3( viewproj[ 3] + viewproj[ 2], viewproj[ 7] + viewproj[ 6], viewproj[11] + viewproj[10], viewproj[15] + viewproj[14] ) )
428 		   );
429 }
430 
431 struct VolumeIntersection
432 {
433 	enum Value
434 	{
435 		OUTSIDE,
436 		INSIDE,
437 		PARTIAL
438 	};
439 };
440 
441 typedef EnumeratedValue<VolumeIntersection> VolumeIntersectionValue;
442 
443 const VolumeIntersectionValue c_volumeOutside( VolumeIntersectionValue::OUTSIDE );
444 const VolumeIntersectionValue c_volumeInside( VolumeIntersectionValue::INSIDE );
445 const VolumeIntersectionValue c_volumePartial( VolumeIntersectionValue::PARTIAL );
446 
frustum_test_aabb(const Frustum & frustum,const AABB & aabb)447 inline VolumeIntersectionValue frustum_test_aabb( const Frustum& frustum, const AABB& aabb ){
448 	VolumeIntersectionValue result = c_volumeInside;
449 
450 	switch ( aabb_classify_plane( aabb, frustum.right ) )
451 	{
452 	case 2:
453 		return c_volumeOutside;
454 	case 1:
455 		result = c_volumePartial;
456 	}
457 
458 	switch ( aabb_classify_plane( aabb, frustum.left ) )
459 	{
460 	case 2:
461 		return c_volumeOutside;
462 	case 1:
463 		result = c_volumePartial;
464 	}
465 
466 	switch ( aabb_classify_plane( aabb, frustum.bottom ) )
467 	{
468 	case 2:
469 		return c_volumeOutside;
470 	case 1:
471 		result = c_volumePartial;
472 	}
473 
474 	switch ( aabb_classify_plane( aabb, frustum.top ) )
475 	{
476 	case 2:
477 		return c_volumeOutside;
478 	case 1:
479 		result = c_volumePartial;
480 	}
481 
482 	switch ( aabb_classify_plane( aabb, frustum.back ) )
483 	{
484 	case 2:
485 		return c_volumeOutside;
486 	case 1:
487 		result = c_volumePartial;
488 	}
489 
490 	switch ( aabb_classify_plane( aabb, frustum.front ) )
491 	{
492 	case 2:
493 		return c_volumeOutside;
494 	case 1:
495 		result = c_volumePartial;
496 	}
497 
498 	return result;
499 }
500 
plane_distance_to_point(const Plane3 & plane,const Vector3 & point)501 inline double plane_distance_to_point( const Plane3& plane, const Vector3& point ){
502 	return vector3_dot( plane.normal(), point ) + plane.d;
503 }
504 
plane_distance_to_oriented_extents(const Plane3 & plane,const Vector3 & extents,const Matrix4 & orientation)505 inline double plane_distance_to_oriented_extents( const Plane3& plane, const Vector3& extents, const Matrix4& orientation ){
506 	return fabs( extents[0] * vector3_dot( plane.normal(), vector4_to_vector3( orientation.x() ) ) )
507 		   + fabs( extents[1] * vector3_dot( plane.normal(), vector4_to_vector3( orientation.y() ) ) )
508 		   + fabs( extents[2] * vector3_dot( plane.normal(), vector4_to_vector3( orientation.z() ) ) );
509 }
510 
511 /// \brief Return false if \p aabb with \p orientation is partially or completely outside \p plane.
plane_contains_oriented_aabb(const Plane3 & plane,const AABB & aabb,const Matrix4 & orientation)512 inline bool plane_contains_oriented_aabb( const Plane3& plane, const AABB& aabb, const Matrix4& orientation ){
513 	double dot = plane_distance_to_point( plane, aabb.origin );
514 	return !( dot > 0 || -dot < plane_distance_to_oriented_extents( plane, aabb.extents, orientation ) );
515 }
516 
frustum_intersects_transformed_aabb(const Frustum & frustum,const AABB & aabb,const Matrix4 & localToWorld)517 inline VolumeIntersectionValue frustum_intersects_transformed_aabb( const Frustum& frustum, const AABB& aabb, const Matrix4& localToWorld ){
518 	AABB aabb_world( aabb );
519 	matrix4_transform_point( localToWorld, aabb_world.origin );
520 
521 	if ( plane_contains_oriented_aabb( frustum.right, aabb_world, localToWorld )
522 		 || plane_contains_oriented_aabb( frustum.left, aabb_world, localToWorld )
523 		 || plane_contains_oriented_aabb( frustum.bottom, aabb_world, localToWorld )
524 		 || plane_contains_oriented_aabb( frustum.top, aabb_world, localToWorld )
525 		 || plane_contains_oriented_aabb( frustum.back, aabb_world, localToWorld )
526 		 || plane_contains_oriented_aabb( frustum.front, aabb_world, localToWorld ) ) {
527 		return c_volumeOutside;
528 	}
529 	return c_volumeInside;
530 }
531 
plane3_test_point(const Plane3 & plane,const Vector3 & point)532 inline bool plane3_test_point( const Plane3& plane, const Vector3& point ){
533 	return vector3_dot( point, plane.normal() ) + plane.dist() <= 0;
534 }
535 
plane3_test_line(const Plane3 & plane,const Segment & segment)536 inline bool plane3_test_line( const Plane3& plane, const Segment& segment ){
537 	return segment_classify_plane( segment, plane ) == 2;
538 }
539 
frustum_test_point(const Frustum & frustum,const Vector3 & point)540 inline bool frustum_test_point( const Frustum& frustum, const Vector3& point ){
541 	return !plane3_test_point( frustum.right, point )
542 		   && !plane3_test_point( frustum.left, point )
543 		   && !plane3_test_point( frustum.bottom, point )
544 		   && !plane3_test_point( frustum.top, point )
545 		   && !plane3_test_point( frustum.back, point )
546 		   && !plane3_test_point( frustum.front, point );
547 }
548 
frustum_test_line(const Frustum & frustum,const Segment & segment)549 inline bool frustum_test_line( const Frustum& frustum, const Segment& segment ){
550 	return !plane3_test_line( frustum.right, segment )
551 		   && !plane3_test_line( frustum.left, segment )
552 		   && !plane3_test_line( frustum.bottom, segment )
553 		   && !plane3_test_line( frustum.top, segment )
554 		   && !plane3_test_line( frustum.back, segment )
555 		   && !plane3_test_line( frustum.front, segment );
556 }
557 
viewer_test_plane(const Vector4 & viewer,const Plane3 & plane)558 inline bool viewer_test_plane( const Vector4& viewer, const Plane3& plane ){
559 	return ( ( plane.a * viewer[0] )
560 			 + ( plane.b * viewer[1] )
561 			 + ( plane.c * viewer[2] )
562 			 + ( plane.d * viewer[3] ) ) > 0;
563 }
564 
triangle_cross(const Vector3 & p0,const Vector3 & p1,const Vector3 & p2)565 inline Vector3 triangle_cross( const Vector3& p0, const Vector3& p1, const Vector3& p2 ){
566 	return vector3_cross( vector3_subtracted( p1, p0 ), vector3_subtracted( p1, p2 ) );
567 }
568 
viewer_test_triangle(const Vector4 & viewer,const Vector3 & p0,const Vector3 & p1,const Vector3 & p2)569 inline bool viewer_test_triangle( const Vector4& viewer, const Vector3& p0, const Vector3& p1, const Vector3& p2 ){
570 	Vector3 cross( triangle_cross( p0, p1, p2 ) );
571 	return ( ( viewer[0] * cross[0] )
572 			 + ( viewer[1] * cross[1] )
573 			 + ( viewer[2] * cross[2] )
574 			 + ( viewer[3] * 0 ) ) > 0;
575 }
576 
viewer_from_transformed_viewer(const Vector4 & viewer,const Matrix4 & transform)577 inline Vector4 viewer_from_transformed_viewer( const Vector4& viewer, const Matrix4& transform ){
578 	if ( viewer[3] == 0 ) {
579 		return Vector4( matrix4_transformed_direction( transform, vector4_to_vector3( viewer ) ), 0 );
580 	}
581 	else
582 	{
583 		return Vector4( matrix4_transformed_point( transform, vector4_to_vector3( viewer ) ), viewer[3] );
584 	}
585 }
586 
viewer_test_transformed_plane(const Vector4 & viewer,const Plane3 & plane,const Matrix4 & localToWorld)587 inline bool viewer_test_transformed_plane( const Vector4& viewer, const Plane3& plane, const Matrix4& localToWorld ){
588 #if 0
589 	return viewer_test_plane( viewer_from_transformed_viewer( viewer, matrix4_affine_inverse( localToWorld ) ), plane );
590 #else
591 	return viewer_test_plane( viewer, plane3_transformed( plane, localToWorld ) );
592 #endif
593 }
594 
viewer_from_viewproj(const Matrix4 & viewproj)595 inline Vector4 viewer_from_viewproj( const Matrix4& viewproj ){
596 	// get viewer pos in object coords
597 	Vector4 viewer( matrix4_transformed_vector4( matrix4_full_inverse( viewproj ), Vector4( 0, 0, -1, 0 ) ) );
598 	if ( viewer[3] != 0 ) { // non-affine matrix
599 		viewer[0] /= viewer[3];
600 		viewer[1] /= viewer[3];
601 		viewer[2] /= viewer[3];
602 		viewer[3] /= viewer[3];
603 	}
604 	return viewer;
605 }
606 
607 #endif
608