2020-11-17 11:16:16 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 2001-2006, William Joseph.
|
|
|
|
All Rights Reserved.
|
|
|
|
|
|
|
|
This file is part of GtkRadiant.
|
|
|
|
|
|
|
|
GtkRadiant is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
GtkRadiant is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with GtkRadiant; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if !defined( INCLUDED_MATH_FRUSTUM_H )
|
|
|
|
#define INCLUDED_MATH_FRUSTUM_H
|
|
|
|
|
|
|
|
/// \file
|
|
|
|
/// \brief View-frustum data types and related operations.
|
|
|
|
|
|
|
|
#include "generic/enumeration.h"
|
|
|
|
#include "math/matrix.h"
|
|
|
|
#include "math/plane.h"
|
|
|
|
#include "math/aabb.h"
|
|
|
|
#include "math/line.h"
|
|
|
|
|
|
|
|
inline Matrix4 matrix4_frustum( float left, float right, float bottom, float top, float nearval, float farval ){
|
|
|
|
return Matrix4(
|
2021-08-04 20:23:18 +00:00
|
|
|
static_cast<float>( ( 2 * nearval ) / ( right - left ) ),
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
static_cast<float>( ( 2 * nearval ) / ( top - bottom ) ),
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
static_cast<float>( ( right + left ) / ( right - left ) ),
|
|
|
|
static_cast<float>( ( top + bottom ) / ( top - bottom ) ),
|
|
|
|
static_cast<float>( -( farval + nearval ) / ( farval - nearval ) ),
|
|
|
|
-1,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
static_cast<float>( -( 2 * farval * nearval ) / ( farval - nearval ) ),
|
|
|
|
0
|
|
|
|
);
|
2020-11-17 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef unsigned char ClipResult;
|
|
|
|
const ClipResult c_CLIP_PASS = 0x00; // 000000
|
|
|
|
const ClipResult c_CLIP_LT_X = 0x01; // 000001
|
|
|
|
const ClipResult c_CLIP_GT_X = 0x02; // 000010
|
|
|
|
const ClipResult c_CLIP_LT_Y = 0x04; // 000100
|
|
|
|
const ClipResult c_CLIP_GT_Y = 0x08; // 001000
|
|
|
|
const ClipResult c_CLIP_LT_Z = 0x10; // 010000
|
|
|
|
const ClipResult c_CLIP_GT_Z = 0x20; // 100000
|
|
|
|
const ClipResult c_CLIP_FAIL = 0x3F; // 111111
|
|
|
|
|
|
|
|
template<typename Index>
|
|
|
|
class Vector4ClipLT
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static bool compare( const Vector4& self ){
|
|
|
|
return self[Index::VALUE] < self[3];
|
|
|
|
}
|
|
|
|
static double scale( const Vector4& self, const Vector4& other ){
|
|
|
|
return ( self[Index::VALUE] - self[3] ) / ( other[3] - other[Index::VALUE] );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename Index>
|
|
|
|
class Vector4ClipGT
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static bool compare( const Vector4& self ){
|
|
|
|
return self[Index::VALUE] > -self[3];
|
|
|
|
}
|
|
|
|
static double scale( const Vector4& self, const Vector4& other ){
|
|
|
|
return ( self[Index::VALUE] + self[3] ) / ( -other[3] - other[Index::VALUE] );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename ClipPlane>
|
|
|
|
class Vector4ClipPolygon
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
typedef Vector4* iterator;
|
|
|
|
typedef const Vector4* const_iterator;
|
|
|
|
|
|
|
|
static std::size_t apply( const_iterator first, const_iterator last, iterator out ){
|
|
|
|
const_iterator next = first, i = last - 1;
|
|
|
|
iterator tmp( out );
|
|
|
|
bool b0 = ClipPlane::compare( *i );
|
|
|
|
while ( next != last )
|
|
|
|
{
|
|
|
|
bool b1 = ClipPlane::compare( *next );
|
|
|
|
if ( b0 ^ b1 ) {
|
|
|
|
*out = vector4_subtracted( *next, *i );
|
|
|
|
|
|
|
|
double scale = ClipPlane::scale( *i, *out );
|
|
|
|
|
|
|
|
( *out )[0] = static_cast<float>( ( *i )[0] + scale * ( ( *out )[0] ) );
|
|
|
|
( *out )[1] = static_cast<float>( ( *i )[1] + scale * ( ( *out )[1] ) );
|
|
|
|
( *out )[2] = static_cast<float>( ( *i )[2] + scale * ( ( *out )[2] ) );
|
|
|
|
( *out )[3] = static_cast<float>( ( *i )[3] + scale * ( ( *out )[3] ) );
|
|
|
|
|
|
|
|
++out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( b1 ) {
|
|
|
|
*out = *next;
|
|
|
|
++out;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = next;
|
|
|
|
++next;
|
|
|
|
b0 = b1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return out - tmp;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#define CLIP_X_LT_W( p ) ( Vector4ClipLT< IntegralConstant<0> >::compare( p ) )
|
|
|
|
#define CLIP_X_GT_W( p ) ( Vector4ClipGT< IntegralConstant<0> >::compare( p ) )
|
|
|
|
#define CLIP_Y_LT_W( p ) ( Vector4ClipLT< IntegralConstant<1> >::compare( p ) )
|
|
|
|
#define CLIP_Y_GT_W( p ) ( Vector4ClipGT< IntegralConstant<1> >::compare( p ) )
|
|
|
|
#define CLIP_Z_LT_W( p ) ( Vector4ClipLT< IntegralConstant<2> >::compare( p ) )
|
|
|
|
#define CLIP_Z_GT_W( p ) ( Vector4ClipGT< IntegralConstant<2> >::compare( p ) )
|
|
|
|
|
|
|
|
inline ClipResult homogenous_clip_point( const Vector4& clipped ){
|
|
|
|
ClipResult result = c_CLIP_FAIL;
|
|
|
|
if ( CLIP_X_LT_W( clipped ) ) {
|
|
|
|
result &= ~c_CLIP_LT_X; // X < W
|
|
|
|
}
|
|
|
|
if ( CLIP_X_GT_W( clipped ) ) {
|
|
|
|
result &= ~c_CLIP_GT_X; // X > -W
|
|
|
|
}
|
|
|
|
if ( CLIP_Y_LT_W( clipped ) ) {
|
|
|
|
result &= ~c_CLIP_LT_Y; // Y < W
|
|
|
|
}
|
|
|
|
if ( CLIP_Y_GT_W( clipped ) ) {
|
|
|
|
result &= ~c_CLIP_GT_Y; // Y > -W
|
|
|
|
}
|
|
|
|
if ( CLIP_Z_LT_W( clipped ) ) {
|
|
|
|
result &= ~c_CLIP_LT_Z; // Z < W
|
|
|
|
}
|
|
|
|
if ( CLIP_Z_GT_W( clipped ) ) {
|
|
|
|
result &= ~c_CLIP_GT_Z; // Z > -W
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Clips \p point by canonical matrix \p self.
|
|
|
|
/// Stores the result in \p clipped.
|
|
|
|
/// Returns a bitmask indicating which clip-planes the point was outside.
|
|
|
|
inline ClipResult matrix4_clip_point( const Matrix4& self, const Vector3& point, Vector4& clipped ){
|
|
|
|
clipped[0] = point[0];
|
|
|
|
clipped[1] = point[1];
|
|
|
|
clipped[2] = point[2];
|
|
|
|
clipped[3] = 1;
|
|
|
|
matrix4_transform_vector4( self, clipped );
|
|
|
|
return homogenous_clip_point( clipped );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline std::size_t homogenous_clip_triangle( Vector4 clipped[9] ){
|
|
|
|
Vector4 buffer[9];
|
|
|
|
std::size_t count = 3;
|
|
|
|
count = Vector4ClipPolygon< Vector4ClipLT< IntegralConstant<0> > >::apply( clipped, clipped + count, buffer );
|
|
|
|
count = Vector4ClipPolygon< Vector4ClipGT< IntegralConstant<0> > >::apply( buffer, buffer + count, clipped );
|
|
|
|
count = Vector4ClipPolygon< Vector4ClipLT< IntegralConstant<1> > >::apply( clipped, clipped + count, buffer );
|
|
|
|
count = Vector4ClipPolygon< Vector4ClipGT< IntegralConstant<1> > >::apply( buffer, buffer + count, clipped );
|
|
|
|
count = Vector4ClipPolygon< Vector4ClipLT< IntegralConstant<2> > >::apply( clipped, clipped + count, buffer );
|
|
|
|
return Vector4ClipPolygon< Vector4ClipGT< IntegralConstant<2> > >::apply( buffer, buffer + count, clipped );
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Transforms and clips the triangle formed by \p p0, \p p1, \p p2 by the canonical matrix \p self.
|
|
|
|
/// Stores the resulting polygon in \p clipped.
|
|
|
|
/// Returns the number of points in the resulting polygon.
|
|
|
|
inline std::size_t matrix4_clip_triangle( const Matrix4& self, const Vector3& p0, const Vector3& p1, const Vector3& p2, Vector4 clipped[9] ){
|
|
|
|
clipped[0][0] = p0[0];
|
|
|
|
clipped[0][1] = p0[1];
|
|
|
|
clipped[0][2] = p0[2];
|
|
|
|
clipped[0][3] = 1;
|
|
|
|
clipped[1][0] = p1[0];
|
|
|
|
clipped[1][1] = p1[1];
|
|
|
|
clipped[1][2] = p1[2];
|
|
|
|
clipped[1][3] = 1;
|
|
|
|
clipped[2][0] = p2[0];
|
|
|
|
clipped[2][1] = p2[1];
|
|
|
|
clipped[2][2] = p2[2];
|
|
|
|
clipped[2][3] = 1;
|
|
|
|
|
|
|
|
matrix4_transform_vector4( self, clipped[0] );
|
|
|
|
matrix4_transform_vector4( self, clipped[1] );
|
|
|
|
matrix4_transform_vector4( self, clipped[2] );
|
|
|
|
|
|
|
|
return homogenous_clip_triangle( clipped );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline std::size_t homogenous_clip_line( Vector4 clipped[2] ){
|
|
|
|
const Vector4& p0 = clipped[0];
|
|
|
|
const Vector4& p1 = clipped[1];
|
|
|
|
|
|
|
|
// early out
|
|
|
|
{
|
|
|
|
ClipResult mask0 = homogenous_clip_point( clipped[0] );
|
|
|
|
ClipResult mask1 = homogenous_clip_point( clipped[1] );
|
|
|
|
|
|
|
|
if ( ( mask0 | mask1 ) == c_CLIP_PASS ) { // both points passed all planes
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( mask0 & mask1 ) { // both points failed any one plane
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const bool index = CLIP_X_LT_W( p0 );
|
|
|
|
if ( index ^ CLIP_X_LT_W( p1 ) ) {
|
|
|
|
Vector4 clip( vector4_subtracted( p1, p0 ) );
|
|
|
|
|
|
|
|
double scale = ( p0[0] - p0[3] ) / ( clip[3] - clip[0] );
|
|
|
|
|
|
|
|
clip[0] = static_cast<float>( p0[0] + scale * clip[0] );
|
|
|
|
clip[1] = static_cast<float>( p0[1] + scale * clip[1] );
|
|
|
|
clip[2] = static_cast<float>( p0[2] + scale * clip[2] );
|
|
|
|
clip[3] = static_cast<float>( p0[3] + scale * clip[3] );
|
|
|
|
|
|
|
|
clipped[index] = clip;
|
|
|
|
}
|
|
|
|
else if ( index == 0 ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const bool index = CLIP_X_GT_W( p0 );
|
|
|
|
if ( index ^ CLIP_X_GT_W( p1 ) ) {
|
|
|
|
Vector4 clip( vector4_subtracted( p1, p0 ) );
|
|
|
|
|
|
|
|
double scale = ( p0[0] + p0[3] ) / ( -clip[3] - clip[0] );
|
|
|
|
|
|
|
|
clip[0] = static_cast<float>( p0[0] + scale * clip[0] );
|
|
|
|
clip[1] = static_cast<float>( p0[1] + scale * clip[1] );
|
|
|
|
clip[2] = static_cast<float>( p0[2] + scale * clip[2] );
|
|
|
|
clip[3] = static_cast<float>( p0[3] + scale * clip[3] );
|
|
|
|
|
|
|
|
clipped[index] = clip;
|
|
|
|
}
|
|
|
|
else if ( index == 0 ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const bool index = CLIP_Y_LT_W( p0 );
|
|
|
|
if ( index ^ CLIP_Y_LT_W( p1 ) ) {
|
|
|
|
Vector4 clip( vector4_subtracted( p1, p0 ) );
|
|
|
|
|
|
|
|
double scale = ( p0[1] - p0[3] ) / ( clip[3] - clip[1] );
|
|
|
|
|
|
|
|
clip[0] = static_cast<float>( p0[0] + scale * clip[0] );
|
|
|
|
clip[1] = static_cast<float>( p0[1] + scale * clip[1] );
|
|
|
|
clip[2] = static_cast<float>( p0[2] + scale * clip[2] );
|
|
|
|
clip[3] = static_cast<float>( p0[3] + scale * clip[3] );
|
|
|
|
|
|
|
|
clipped[index] = clip;
|
|
|
|
}
|
|
|
|
else if ( index == 0 ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const bool index = CLIP_Y_GT_W( p0 );
|
|
|
|
if ( index ^ CLIP_Y_GT_W( p1 ) ) {
|
|
|
|
Vector4 clip( vector4_subtracted( p1, p0 ) );
|
|
|
|
|
|
|
|
double scale = ( p0[1] + p0[3] ) / ( -clip[3] - clip[1] );
|
|
|
|
|
|
|
|
clip[0] = static_cast<float>( p0[0] + scale * clip[0] );
|
|
|
|
clip[1] = static_cast<float>( p0[1] + scale * clip[1] );
|
|
|
|
clip[2] = static_cast<float>( p0[2] + scale * clip[2] );
|
|
|
|
clip[3] = static_cast<float>( p0[3] + scale * clip[3] );
|
|
|
|
|
|
|
|
clipped[index] = clip;
|
|
|
|
}
|
|
|
|
else if ( index == 0 ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const bool index = CLIP_Z_LT_W( p0 );
|
|
|
|
if ( index ^ CLIP_Z_LT_W( p1 ) ) {
|
|
|
|
Vector4 clip( vector4_subtracted( p1, p0 ) );
|
|
|
|
|
|
|
|
double scale = ( p0[2] - p0[3] ) / ( clip[3] - clip[2] );
|
|
|
|
|
|
|
|
clip[0] = static_cast<float>( p0[0] + scale * clip[0] );
|
|
|
|
clip[1] = static_cast<float>( p0[1] + scale * clip[1] );
|
|
|
|
clip[2] = static_cast<float>( p0[2] + scale * clip[2] );
|
|
|
|
clip[3] = static_cast<float>( p0[3] + scale * clip[3] );
|
|
|
|
|
|
|
|
clipped[index] = clip;
|
|
|
|
}
|
|
|
|
else if ( index == 0 ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const bool index = CLIP_Z_GT_W( p0 );
|
|
|
|
if ( index ^ CLIP_Z_GT_W( p1 ) ) {
|
|
|
|
Vector4 clip( vector4_subtracted( p1, p0 ) );
|
|
|
|
|
|
|
|
double scale = ( p0[2] + p0[3] ) / ( -clip[3] - clip[2] );
|
|
|
|
|
|
|
|
clip[0] = static_cast<float>( p0[0] + scale * clip[0] );
|
|
|
|
clip[1] = static_cast<float>( p0[1] + scale * clip[1] );
|
|
|
|
clip[2] = static_cast<float>( p0[2] + scale * clip[2] );
|
|
|
|
clip[3] = static_cast<float>( p0[3] + scale * clip[3] );
|
|
|
|
|
|
|
|
clipped[index] = clip;
|
|
|
|
}
|
|
|
|
else if ( index == 0 ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Transforms and clips the line formed by \p p0, \p p1 by the canonical matrix \p self.
|
|
|
|
/// Stores the resulting line in \p clipped.
|
|
|
|
/// Returns the number of points in the resulting line.
|
|
|
|
inline std::size_t matrix4_clip_line( const Matrix4& self, const Vector3& p0, const Vector3& p1, Vector4 clipped[2] ){
|
|
|
|
clipped[0][0] = p0[0];
|
|
|
|
clipped[0][1] = p0[1];
|
|
|
|
clipped[0][2] = p0[2];
|
|
|
|
clipped[0][3] = 1;
|
|
|
|
clipped[1][0] = p1[0];
|
|
|
|
clipped[1][1] = p1[1];
|
|
|
|
clipped[1][2] = p1[2];
|
|
|
|
clipped[1][3] = 1;
|
|
|
|
|
|
|
|
matrix4_transform_vector4( self, clipped[0] );
|
|
|
|
matrix4_transform_vector4( self, clipped[1] );
|
|
|
|
|
|
|
|
return homogenous_clip_line( clipped );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Frustum
|
|
|
|
{
|
|
|
|
Plane3 right, left, bottom, top, back, front;
|
|
|
|
|
|
|
|
Frustum(){
|
|
|
|
}
|
|
|
|
Frustum( const Plane3& _right,
|
2021-08-04 20:23:18 +00:00
|
|
|
const Plane3& _left,
|
|
|
|
const Plane3& _bottom,
|
|
|
|
const Plane3& _top,
|
|
|
|
const Plane3& _back,
|
|
|
|
const Plane3& _front )
|
2020-11-17 11:16:16 +00:00
|
|
|
: right( _right ), left( _left ), bottom( _bottom ), top( _top ), back( _back ), front( _front ){
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
inline Frustum frustum_transformed( const Frustum& frustum, const Matrix4& transform ){
|
|
|
|
return Frustum(
|
2021-08-04 20:23:18 +00:00
|
|
|
plane3_transformed( frustum.right, transform ),
|
|
|
|
plane3_transformed( frustum.left, transform ),
|
|
|
|
plane3_transformed( frustum.bottom, transform ),
|
|
|
|
plane3_transformed( frustum.top, transform ),
|
|
|
|
plane3_transformed( frustum.back, transform ),
|
|
|
|
plane3_transformed( frustum.front, transform )
|
|
|
|
);
|
2020-11-17 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline Frustum frustum_inverse_transformed( const Frustum& frustum, const Matrix4& transform ){
|
|
|
|
return Frustum(
|
2021-08-04 20:23:18 +00:00
|
|
|
plane3_inverse_transformed( frustum.right, transform ),
|
|
|
|
plane3_inverse_transformed( frustum.left, transform ),
|
|
|
|
plane3_inverse_transformed( frustum.bottom, transform ),
|
|
|
|
plane3_inverse_transformed( frustum.top, transform ),
|
|
|
|
plane3_inverse_transformed( frustum.back, transform ),
|
|
|
|
plane3_inverse_transformed( frustum.front, transform )
|
|
|
|
);
|
2020-11-17 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool viewproj_test_point( const Matrix4& viewproj, const Vector3& point ){
|
|
|
|
Vector4 hpoint( matrix4_transformed_vector4( viewproj, Vector4( point, 1.0f ) ) );
|
|
|
|
if ( fabs( hpoint[0] ) < fabs( hpoint[3] )
|
2021-08-04 20:23:18 +00:00
|
|
|
&& fabs( hpoint[1] ) < fabs( hpoint[3] )
|
|
|
|
&& fabs( hpoint[2] ) < fabs( hpoint[3] ) ) {
|
2020-11-17 11:16:16 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool viewproj_test_transformed_point( const Matrix4& viewproj, const Vector3& point, const Matrix4& localToWorld ){
|
|
|
|
return viewproj_test_point( viewproj, matrix4_transformed_point( localToWorld, point ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Frustum frustum_from_viewproj( const Matrix4& viewproj ){
|
|
|
|
return Frustum
|
2021-08-04 20:23:18 +00:00
|
|
|
(
|
|
|
|
plane3_normalised( Plane3( viewproj[ 3] - viewproj[ 0], viewproj[ 7] - viewproj[ 4], viewproj[11] - viewproj[ 8], viewproj[15] - viewproj[12] ) ),
|
|
|
|
plane3_normalised( Plane3( viewproj[ 3] + viewproj[ 0], viewproj[ 7] + viewproj[ 4], viewproj[11] + viewproj[ 8], viewproj[15] + viewproj[12] ) ),
|
|
|
|
plane3_normalised( Plane3( viewproj[ 3] + viewproj[ 1], viewproj[ 7] + viewproj[ 5], viewproj[11] + viewproj[ 9], viewproj[15] + viewproj[13] ) ),
|
|
|
|
plane3_normalised( Plane3( viewproj[ 3] - viewproj[ 1], viewproj[ 7] - viewproj[ 5], viewproj[11] - viewproj[ 9], viewproj[15] - viewproj[13] ) ),
|
|
|
|
plane3_normalised( Plane3( viewproj[ 3] - viewproj[ 2], viewproj[ 7] - viewproj[ 6], viewproj[11] - viewproj[10], viewproj[15] - viewproj[14] ) ),
|
|
|
|
plane3_normalised( Plane3( viewproj[ 3] + viewproj[ 2], viewproj[ 7] + viewproj[ 6], viewproj[11] + viewproj[10], viewproj[15] + viewproj[14] ) )
|
|
|
|
);
|
2020-11-17 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct VolumeIntersection
|
|
|
|
{
|
|
|
|
enum Value
|
|
|
|
{
|
|
|
|
OUTSIDE,
|
|
|
|
INSIDE,
|
|
|
|
PARTIAL
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef EnumeratedValue<VolumeIntersection> VolumeIntersectionValue;
|
|
|
|
|
|
|
|
const VolumeIntersectionValue c_volumeOutside( VolumeIntersectionValue::OUTSIDE );
|
|
|
|
const VolumeIntersectionValue c_volumeInside( VolumeIntersectionValue::INSIDE );
|
|
|
|
const VolumeIntersectionValue c_volumePartial( VolumeIntersectionValue::PARTIAL );
|
|
|
|
|
|
|
|
inline VolumeIntersectionValue frustum_test_aabb( const Frustum& frustum, const AABB& aabb ){
|
|
|
|
VolumeIntersectionValue result = c_volumeInside;
|
|
|
|
|
|
|
|
switch ( aabb_classify_plane( aabb, frustum.right ) )
|
|
|
|
{
|
|
|
|
case 2:
|
|
|
|
return c_volumeOutside;
|
|
|
|
case 1:
|
|
|
|
result = c_volumePartial;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( aabb_classify_plane( aabb, frustum.left ) )
|
|
|
|
{
|
|
|
|
case 2:
|
|
|
|
return c_volumeOutside;
|
|
|
|
case 1:
|
|
|
|
result = c_volumePartial;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( aabb_classify_plane( aabb, frustum.bottom ) )
|
|
|
|
{
|
|
|
|
case 2:
|
|
|
|
return c_volumeOutside;
|
|
|
|
case 1:
|
|
|
|
result = c_volumePartial;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( aabb_classify_plane( aabb, frustum.top ) )
|
|
|
|
{
|
|
|
|
case 2:
|
|
|
|
return c_volumeOutside;
|
|
|
|
case 1:
|
|
|
|
result = c_volumePartial;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( aabb_classify_plane( aabb, frustum.back ) )
|
|
|
|
{
|
|
|
|
case 2:
|
|
|
|
return c_volumeOutside;
|
|
|
|
case 1:
|
|
|
|
result = c_volumePartial;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( aabb_classify_plane( aabb, frustum.front ) )
|
|
|
|
{
|
|
|
|
case 2:
|
|
|
|
return c_volumeOutside;
|
|
|
|
case 1:
|
|
|
|
result = c_volumePartial;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline double plane_distance_to_point( const Plane3& plane, const Vector3& point ){
|
|
|
|
return vector3_dot( plane.normal(), point ) + plane.d;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline double plane_distance_to_oriented_extents( const Plane3& plane, const Vector3& extents, const Matrix4& orientation ){
|
|
|
|
return fabs( extents[0] * vector3_dot( plane.normal(), vector4_to_vector3( orientation.x() ) ) )
|
2021-08-04 20:23:18 +00:00
|
|
|
+ fabs( extents[1] * vector3_dot( plane.normal(), vector4_to_vector3( orientation.y() ) ) )
|
|
|
|
+ fabs( extents[2] * vector3_dot( plane.normal(), vector4_to_vector3( orientation.z() ) ) );
|
2020-11-17 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Return false if \p aabb with \p orientation is partially or completely outside \p plane.
|
|
|
|
inline bool plane_contains_oriented_aabb( const Plane3& plane, const AABB& aabb, const Matrix4& orientation ){
|
|
|
|
double dot = plane_distance_to_point( plane, aabb.origin );
|
|
|
|
return !( dot > 0 || -dot < plane_distance_to_oriented_extents( plane, aabb.extents, orientation ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline VolumeIntersectionValue frustum_intersects_transformed_aabb( const Frustum& frustum, const AABB& aabb, const Matrix4& localToWorld ){
|
|
|
|
AABB aabb_world( aabb );
|
|
|
|
matrix4_transform_point( localToWorld, aabb_world.origin );
|
|
|
|
|
|
|
|
if ( plane_contains_oriented_aabb( frustum.right, aabb_world, localToWorld )
|
2021-08-04 20:23:18 +00:00
|
|
|
|| plane_contains_oriented_aabb( frustum.left, aabb_world, localToWorld )
|
|
|
|
|| plane_contains_oriented_aabb( frustum.bottom, aabb_world, localToWorld )
|
|
|
|
|| plane_contains_oriented_aabb( frustum.top, aabb_world, localToWorld )
|
|
|
|
|| plane_contains_oriented_aabb( frustum.back, aabb_world, localToWorld )
|
|
|
|
|| plane_contains_oriented_aabb( frustum.front, aabb_world, localToWorld ) ) {
|
2020-11-17 11:16:16 +00:00
|
|
|
return c_volumeOutside;
|
|
|
|
}
|
|
|
|
return c_volumeInside;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool plane3_test_point( const Plane3& plane, const Vector3& point ){
|
|
|
|
return vector3_dot( point, plane.normal() ) + plane.dist() <= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool plane3_test_line( const Plane3& plane, const Segment& segment ){
|
|
|
|
return segment_classify_plane( segment, plane ) == 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool frustum_test_point( const Frustum& frustum, const Vector3& point ){
|
|
|
|
return !plane3_test_point( frustum.right, point )
|
2021-08-04 20:23:18 +00:00
|
|
|
&& !plane3_test_point( frustum.left, point )
|
|
|
|
&& !plane3_test_point( frustum.bottom, point )
|
|
|
|
&& !plane3_test_point( frustum.top, point )
|
|
|
|
&& !plane3_test_point( frustum.back, point )
|
|
|
|
&& !plane3_test_point( frustum.front, point );
|
2020-11-17 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool frustum_test_line( const Frustum& frustum, const Segment& segment ){
|
|
|
|
return !plane3_test_line( frustum.right, segment )
|
2021-08-04 20:23:18 +00:00
|
|
|
&& !plane3_test_line( frustum.left, segment )
|
|
|
|
&& !plane3_test_line( frustum.bottom, segment )
|
|
|
|
&& !plane3_test_line( frustum.top, segment )
|
|
|
|
&& !plane3_test_line( frustum.back, segment )
|
|
|
|
&& !plane3_test_line( frustum.front, segment );
|
2020-11-17 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool viewer_test_plane( const Vector4& viewer, const Plane3& plane ){
|
|
|
|
return ( ( plane.a * viewer[0] )
|
2021-08-04 20:23:18 +00:00
|
|
|
+ ( plane.b * viewer[1] )
|
|
|
|
+ ( plane.c * viewer[2] )
|
|
|
|
+ ( plane.d * viewer[3] ) ) > 0;
|
2020-11-17 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline Vector3 triangle_cross( const Vector3& p0, const Vector3& p1, const Vector3& p2 ){
|
|
|
|
return vector3_cross( vector3_subtracted( p1, p0 ), vector3_subtracted( p1, p2 ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool viewer_test_triangle( const Vector4& viewer, const Vector3& p0, const Vector3& p1, const Vector3& p2 ){
|
|
|
|
Vector3 cross( triangle_cross( p0, p1, p2 ) );
|
|
|
|
return ( ( viewer[0] * cross[0] )
|
2021-08-04 20:23:18 +00:00
|
|
|
+ ( viewer[1] * cross[1] )
|
|
|
|
+ ( viewer[2] * cross[2] )
|
|
|
|
+ ( viewer[3] * 0 ) ) > 0;
|
2020-11-17 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline Vector4 viewer_from_transformed_viewer( const Vector4& viewer, const Matrix4& transform ){
|
|
|
|
if ( viewer[3] == 0 ) {
|
|
|
|
return Vector4( matrix4_transformed_direction( transform, vector4_to_vector3( viewer ) ), 0 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return Vector4( matrix4_transformed_point( transform, vector4_to_vector3( viewer ) ), viewer[3] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool viewer_test_transformed_plane( const Vector4& viewer, const Plane3& plane, const Matrix4& localToWorld ){
|
|
|
|
#if 0
|
|
|
|
return viewer_test_plane( viewer_from_transformed_viewer( viewer, matrix4_affine_inverse( localToWorld ) ), plane );
|
|
|
|
#else
|
|
|
|
return viewer_test_plane( viewer, plane3_transformed( plane, localToWorld ) );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Vector4 viewer_from_viewproj( const Matrix4& viewproj ){
|
|
|
|
// get viewer pos in object coords
|
|
|
|
Vector4 viewer( matrix4_transformed_vector4( matrix4_full_inverse( viewproj ), Vector4( 0, 0, -1, 0 ) ) );
|
|
|
|
if ( viewer[3] != 0 ) { // non-affine matrix
|
|
|
|
viewer[0] /= viewer[3];
|
|
|
|
viewer[1] /= viewer[3];
|
|
|
|
viewer[2] /= viewer[3];
|
|
|
|
viewer[3] /= viewer[3];
|
|
|
|
}
|
|
|
|
return viewer;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|