You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Legacy/CryCommon/Cry_Camera.h

2429 lines
78 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Common Camera class implementation
#ifndef CRYINCLUDE_CRYCOMMON_CRY_CAMERA_H
#define CRYINCLUDE_CRYCOMMON_CRY_CAMERA_H
#pragma once
//DOC-IGNORE-BEGIN
#include <Cry_Math.h>
#include <Cry_Geo.h>
#include <MemoryAccess.h>
#include <Cry_XOptimise.h>
//DOC-IGNORE-END
//////////////////////////////////////////////////////////////////////
#define CAMERA_MIN_NEAR 0.001f
#define DEFAULT_NEAR 0.2f
#define DEFAULT_FAR 1024.0f
#define DEFAULT_FOV (75.0f * gf_PI / 180.0f)
#define MIN_FOV 0.0000001f
//////////////////////////////////////////////////////////////////////
enum
{
FR_PLANE_NEAR,
FR_PLANE_FAR,
FR_PLANE_RIGHT,
FR_PLANE_LEFT,
FR_PLANE_TOP,
FR_PLANE_BOTTOM,
FRUSTUM_PLANES
};
//////////////////////////////////////////////////////////////////////
enum cull
{
CULL_EXCLUSION, // The whole object is outside of frustum.
CULL_OVERLAP, // The object & frustum overlap.
CULL_INCLUSION // The whole object is inside frustum.
};
class CameraViewParameters
{
public:
CameraViewParameters();
void LookAt(const Vec3& Eye, const Vec3& ViewRefPt, const Vec3& ViewUp);
void Perspective(float Yfov, float Aspect, float Ndist, float Fdist);
void Frustum(float l, float r, float b, float t, float Ndist, float Fdist);
const Vec3& wCOP() const;
Vec3 ViewDir() const;
Vec3 ViewDirOffAxis() const;
float* GetXform_Screen2Obj(float* M, int WW, int WH) const;
float* GetXform_Obj2Screen(float* M, int WW, int WH) const;
float* GetModelviewMatrix(float* M) const;
float* GetProjectionMatrix(float* M) const;
float* GetViewportMatrix(float* M, int WW, int WH) const;
void SetModelviewMatrix(const float* M);
void GetLookAtParams(Vec3* Eye, Vec3* ViewRefPt, Vec3* ViewUp) const;
void GetPerspectiveParams(float* Yfov, float* Xfov, float* Aspect, float* Ndist, float* Fdist) const;
void GetFrustumParams(float* l, float* r, float* b, float* t, float* Ndist, float* Fdist) const;
float* GetInvModelviewMatrix(float* M) const;
float* GetInvProjectionMatrix(float* M) const;
float* GetInvViewportMatrix(float* M, int WW, int WH) const;
Vec3 WorldToCam(const Vec3& wP) const;
float WorldToCamZ(const Vec3& wP) const;
Vec3 CamToWorld(const Vec3& cP) const;
void LoadIdentityXform();
void Xform(const float M[16]);
void Translate(const Vec3& trans);
void Rotate(const float M[9]);
void GetPixelRay(float sx, float sy, int ww, int wh, Vec3* Start, Vec3* Dir) const;
void CalcVerts(Vec3* V) const;
void CalcTileVerts(Vec3* V, f32 nPosX, f32 nPosY, f32 nGridSizeX, f32 nGridSizeY) const;
void CalcRegionVerts(Vec3* V, const Vec2& vMin, const Vec2& vMax) const;
void CalcTiledRegionVerts(Vec3* V, Vec2& vMin, Vec2& vMax, f32 nPosX, f32 nPosY, f32 nGridSizeX, f32 nGridSizeY) const;
Vec3 vX, vY, vZ;
Vec3 vOrigin;
float fWL, fWR, fWB, fWT;
float fNear, fFar;
};
inline float* Frustum16fv(float* M, float l, float r, float b, float t, float n, float f)
{
M[0] = (2 * n) / (r - l);
M[4] = 0;
M[8] = (r + l) / (r - l);
M[12] = 0;
M[1] = 0;
M[5] = (2 * n) / (t - b);
M[9] = (t + b) / (t - b);
M[13] = 0;
M[2] = 0;
M[6] = 0;
M[10] = -(f + n) / (f - n);
M[14] = (-2 * f * n) / (f - n);
M[3] = 0;
M[7] = 0;
M[11] = -1;
M[15] = 0;
return M;
}
inline float* Viewing16fv(float* M, const Vec3 X, const Vec3 Y, const Vec3 Z, const Vec3 O)
{
M[0] = X.x;
M[4] = X.y;
M[8] = X.z;
M[12] = -X | O;
M[1] = Y.x;
M[5] = Y.y;
M[9] = Y.z;
M[13] = -Y | O;
M[2] = Z.x;
M[6] = Z.y;
M[10] = Z.z;
M[14] = -Z | O;
M[3] = 0;
M[7] = 0;
M[11] = 0;
M[15] = 1;
return M;
}
inline CameraViewParameters::CameraViewParameters()
{
vX.Set(1, 0, 0);
vY.Set(0, 1, 0);
vZ.Set(0, 0, 1);
vOrigin.Set(0, 0, 0);
fNear = 1.4142f;
fFar = 10;
fWL = -1;
fWR = 1;
fWT = 1;
fWB = -1;
}
inline void CameraViewParameters::LookAt(const Vec3& Eye, const Vec3& ViewRefPt, const Vec3& ViewUp)
{
vZ = Eye - ViewRefPt;
vZ.NormalizeSafe();
vX = ViewUp % vZ;
vX.NormalizeSafe();
vY = vZ % vX;
vY.NormalizeSafe();
vOrigin = Eye;
}
inline void CameraViewParameters::Perspective(float Yfov, float Aspect, float Ndist, float Fdist)
{
fNear = Ndist;
fFar = Fdist;
fWT = tanf(Yfov * 0.5f) * fNear;
fWB = -fWT;
fWR = fWT * Aspect;
fWL = -fWR;
}
inline void CameraViewParameters::Frustum(float l, float r, float b, float t, float Ndist, float Fdist)
{
fNear = Ndist;
fFar = Fdist;
fWR = r;
fWL = l;
fWB = b;
fWT = t;
}
inline void CameraViewParameters::GetLookAtParams(Vec3* Eye, Vec3* ViewRefPt, Vec3* ViewUp) const
{
*Eye = vOrigin;
*ViewRefPt = vOrigin - vZ;
*ViewUp = vY;
}
inline void CameraViewParameters::GetPerspectiveParams(float* Yfov, float* Xfov, float* Aspect, float* Ndist, float* Fdist) const
{
*Yfov = atanf(fWT / fNear) * 57.29578f * 2.0f;
*Xfov = atanf(fWR / fNear) * 57.29578f * 2.0f;
*Aspect = fWT / fWR;
*Ndist = fNear;
*Fdist = fFar;
}
inline void CameraViewParameters::GetFrustumParams(float* l, float* r, float* b, float* t, float* Ndist, float* Fdist) const
{
*l = fWL;
*r = fWR;
*b = fWB;
*t = fWT;
*Ndist = fNear;
*Fdist = fFar;
}
inline const Vec3& CameraViewParameters::wCOP() const
{
return(vOrigin);
}
inline Vec3 CameraViewParameters::ViewDir() const
{
return(-vZ);
}
inline Vec3 CameraViewParameters::ViewDirOffAxis() const
{
float fX = (fWL + fWR) * 0.5f, fY = (fWT + fWB) * 0.5f; // MIDPOINT ON VIEWPLANE WINDOW
Vec3 ViewDir = vX * fX + vY * fY - vZ * fNear;
ViewDir.Normalize();
return ViewDir;
}
inline Vec3 CameraViewParameters::WorldToCam(const Vec3& wP) const
{
Vec3 sP(wP - vOrigin);
Vec3 cP(vX | sP, vY | sP, vZ | sP);
return cP;
}
inline float CameraViewParameters::WorldToCamZ(const Vec3& wP) const
{
Vec3 sP(wP - vOrigin);
float zdist = vZ | sP;
return zdist;
}
inline Vec3 CameraViewParameters::CamToWorld(const Vec3& cP) const
{
Vec3 wP(vX * cP.x + vY * cP.y + vZ * cP.z + vOrigin);
return wP;
}
inline void CameraViewParameters::LoadIdentityXform()
{
vX.Set(1, 0, 0);
vY.Set(0, 1, 0);
vZ.Set(0, 0, 1);
vOrigin.Set(0, 0, 0);
}
inline void CameraViewParameters::Xform(const float M[16])
{
vX.Set(vX.x * M[0] + vX.y * M[4] + vX.z * M[8],
vX.x * M[1] + vX.y * M[5] + vX.z * M[9],
vX.x * M[2] + vX.y * M[6] + vX.z * M[10]);
vY.Set(vY.x * M[0] + vY.y * M[4] + vY.z * M[8],
vY.x * M[1] + vY.y * M[5] + vY.z * M[9],
vY.x * M[2] + vY.y * M[6] + vY.z * M[10]);
vZ.Set(vZ.x * M[0] + vZ.y * M[4] + vZ.z * M[8],
vZ.x * M[1] + vZ.y * M[5] + vZ.z * M[9],
vZ.x * M[2] + vZ.y * M[6] + vZ.z * M[10]);
vOrigin.Set(vOrigin.x * M[0] + vOrigin.y * M[4] + vOrigin.z * M[8] + M[12],
vOrigin.x * M[1] + vOrigin.y * M[5] + vOrigin.z * M[9] + M[13],
vOrigin.x * M[2] + vOrigin.y * M[6] + vOrigin.z * M[10] + M[14]);
float Scale = vX.GetLength();
vX /= Scale;
vY /= Scale;
vZ /= Scale;
fWL *= Scale;
fWR *= Scale;
fWB *= Scale;
fWT *= Scale;
fNear *= Scale;
fFar *= Scale;
};
inline void CameraViewParameters::Translate(const Vec3& trans)
{
vOrigin += trans;
}
inline void CameraViewParameters::Rotate(const float M[9])
{
vX.Set(vX.x * M[0] + vX.y * M[3] + vX.z * M[6],
vX.x * M[1] + vX.y * M[4] + vX.z * M[7],
vX.x * M[2] + vX.y * M[5] + vX.z * M[8]);
vY.Set(vY.x * M[0] + vY.y * M[3] + vY.z * M[6],
vY.x * M[1] + vY.y * M[4] + vY.z * M[7],
vY.x * M[2] + vY.y * M[5] + vY.z * M[8]);
vZ.Set(vZ.x * M[0] + vZ.y * M[3] + vZ.z * M[6],
vZ.x * M[1] + vZ.y * M[4] + vZ.z * M[7],
vZ.x * M[2] + vZ.y * M[5] + vZ.z * M[8]);
}
inline float* CameraViewParameters::GetModelviewMatrix(float* M) const
{
Viewing16fv(M, vX, vY, vZ, vOrigin);
return M;
}
inline float* CameraViewParameters::GetProjectionMatrix(float* M) const
{
Frustum16fv(M, fWL, fWR, fWB, fWT, fNear, fFar);
return(M);
}
inline void CameraViewParameters::GetPixelRay(float sx, float sy, int ww, int wh, Vec3* Start, Vec3* Dir) const
{
Vec3 wTL = vOrigin + (vX * fWL) + (vY * fWT) - (vZ * fNear); // FIND LOWER-LEFT
Vec3 dX = (vX * (fWR - fWL)) / (float)ww; // WORLD WIDTH OF PIXEL
Vec3 dY = (vY * (fWT - fWB)) / (float)wh; // WORLD HEIGHT OF PIXEL
wTL += (dX * sx - dY * sy); // INCR TO WORLD PIXEL
wTL += (dX * 0.5f - dY * 0.5f); // INCR TO PIXEL CNTR
*Start = vOrigin;
*Dir = wTL - vOrigin;
}
inline void CameraViewParameters::CalcVerts(Vec3* V) const
{
float NearZ = -fNear;
V[0].Set(fWR, fWT, NearZ);
V[1].Set(fWL, fWT, NearZ);
V[2].Set(fWL, fWB, NearZ);
V[3].Set(fWR, fWB, NearZ);
float FarZ = -fFar, FN = fFar / fNear;
float fwL = fWL * FN, fwR = fWR * FN, fwB = fWB * FN, fwT = fWT * FN;
V[4].Set(fwR, fwT, FarZ);
V[5].Set(fwL, fwT, FarZ);
V[6].Set(fwL, fwB, FarZ);
V[7].Set(fwR, fwB, FarZ);
for (int i = 0; i < 8; i++)
{
V[i] = CamToWorld(V[i]);
}
}
inline void CameraViewParameters::CalcTileVerts(Vec3* V, f32 nPosX, f32 nPosY, f32 nGridSizeX, f32 nGridSizeY) const
{
float NearZ = -fNear;
float TileWidth = abs(fWR - fWL) / nGridSizeX;
float TileHeight = abs(fWT - fWB) / nGridSizeY;
float TileL = fWL + TileWidth * nPosX;
float TileR = fWL + TileWidth * (nPosX + 1);
float TileB = fWB + TileHeight * nPosY;
float TileT = fWB + TileHeight * (nPosY + 1);
V[0].Set(TileR, TileT, NearZ);
V[1].Set(TileL, TileT, NearZ);
V[2].Set(TileL, TileB, NearZ);
V[3].Set(TileR, TileB, NearZ);
float FarZ = -fFar, FN = fFar / fNear;
float fwL = fWL * FN, fwR = fWR * FN, fwB = fWB * FN, fwT = fWT * FN;
float TileFarWidth = abs(fwR - fwL) / nGridSizeX;
float TileFarHeight = abs(fwT - fwB) / nGridSizeY;
float TileFarL = fwL + TileFarWidth * nPosX;
float TileFarR = fwL + TileFarWidth * (nPosX + 1);
float TileFarB = fwB + TileFarHeight * nPosY;
float TileFarT = fwB + TileFarHeight * (nPosY + 1);
V[4].Set(TileFarR, TileFarT, FarZ);
V[5].Set(TileFarL, TileFarT, FarZ);
V[6].Set(TileFarL, TileFarB, FarZ);
V[7].Set(TileFarR, TileFarB, FarZ);
for (int i = 0; i < 8; i++)
{
V[i] = CamToWorld(V[i]);
}
}
inline void CameraViewParameters::CalcTiledRegionVerts(Vec3* V, Vec2& vMin, Vec2& vMax, f32 nPosX, f32 nPosY, f32 nGridSizeX, f32 nGridSizeY) const
{
float NearZ = -fNear;
Vec2 vTileMin, vTileMax;
vMin.x = max(vMin.x, nPosX / nGridSizeX);
vMax.x = min(vMax.x, (nPosX + 1) / nGridSizeX);
vMin.y = max(vMin.y, nPosY / nGridSizeY);
vMax.y = min(vMax.y, (nPosY + 1) / nGridSizeY);
vTileMin.x = abs(fWR - fWL) * vMin.x;
vTileMin.y = abs(fWT - fWB) * vMin.y;
vTileMax.x = abs(fWR - fWL) * vMax.x;
vTileMax.y = abs(fWT - fWB) * vMax.y;
float TileL = fWL + vTileMin.x;
float TileR = fWL + vTileMax.x;
float TileB = fWB + vTileMin.y;
float TileT = fWB + vTileMax.y;
V[0].Set(TileR, TileT, NearZ);
V[1].Set(TileL, TileT, NearZ);
V[2].Set(TileL, TileB, NearZ);
V[3].Set(TileR, TileB, NearZ);
float FarZ = -fFar, FN = fFar / fNear;
float fwL = fWL * FN, fwR = fWR * FN, fwB = fWB * FN, fwT = fWT * FN;
Vec2 vTileFarMin, vTileFarMax;
vTileFarMin.x = abs(fwR - fwL) * vMin.x;
vTileFarMin.y = abs(fwT - fwB) * vMin.y;
vTileFarMax.x = abs(fwR - fwL) * vMax.x;
vTileFarMax.y = abs(fwT - fwB) * vMax.y;
float TileFarL = fwL + vTileFarMin.x;
float TileFarR = fwL + vTileFarMax.x;
float TileFarB = fwB + vTileFarMin.y;
float TileFarT = fwB + vTileFarMax.y;
V[4].Set(TileFarR, TileFarT, FarZ);
V[5].Set(TileFarL, TileFarT, FarZ);
V[6].Set(TileFarL, TileFarB, FarZ);
V[7].Set(TileFarR, TileFarB, FarZ);
for (int i = 0; i < 8; i++)
{
V[i] = CamToWorld(V[i]);
}
}
inline void CameraViewParameters::CalcRegionVerts(Vec3* V, const Vec2& vMin, const Vec2& vMax) const
{
float NearZ = -fNear;
Vec2 vTileMin, vTileMax;
vTileMin.x = abs(fWR - fWL) * vMin.x;
vTileMin.y = abs(fWT - fWB) * vMin.y;
vTileMax.x = abs(fWR - fWL) * vMax.x;
vTileMax.y = abs(fWT - fWB) * vMax.y;
float TileL = fWL + vTileMin.x;
float TileR = fWL + vTileMax.x;
float TileB = fWB + vTileMin.y;
float TileT = fWB + vTileMax.y;
V[0].Set(TileR, TileT, NearZ);
V[1].Set(TileL, TileT, NearZ);
V[2].Set(TileL, TileB, NearZ);
V[3].Set(TileR, TileB, NearZ);
float FarZ = -fFar, FN = fFar / fNear;
float fwL = fWL * FN, fwR = fWR * FN, fwB = fWB * FN, fwT = fWT * FN;
Vec2 vTileFarMin, vTileFarMax;
vTileFarMin.x = abs(fwR - fwL) * vMin.x;
vTileFarMin.y = abs(fwT - fwB) * vMin.y;
vTileFarMax.x = abs(fwR - fwL) * vMax.x;
vTileFarMax.y = abs(fwT - fwB) * vMax.y;
float TileFarL = fwL + vTileFarMin.x;
float TileFarR = fwL + vTileFarMax.x;
float TileFarB = fwB + vTileFarMin.y;
float TileFarT = fwB + vTileFarMax.y;
V[4].Set(TileFarR, TileFarT, FarZ);
V[5].Set(TileFarL, TileFarT, FarZ);
V[6].Set(TileFarL, TileFarB, FarZ);
V[7].Set(TileFarR, TileFarB, FarZ);
for (int i = 0; i < 8; i++)
{
V[i] = CamToWorld(V[i]);
}
}
///////////////////////////////////////////////////////////////////////////////
// Implements essential operations like calculation of a view-matrix and
// frustum-culling with simple geometric primitives (Point, Sphere, AABB, OBB).
// All calculation are based on the CryENGINE coordinate-system
//
// We are using a "right-handed" coordinate systems, where the positive X-Axis points
// to the right, the positive Y-Axis points away from the viewer and the positive
// Z-Axis points up. The following illustration shows our coordinate system.
//
// <PRE>
// z-axis
// ^
// |
// | y-axis
// | /
// | /
// |/
// +----------------> x-axis
// </PRE>
//
// This same system is also used in 3D-Studio-MAX. It is not unusual for 3D-APIs like D3D9 or
// OpenGL to use a different coordinate system. Currently in D3D9 we use a coordinate system
// in which the X-Axis points to the right, the Y-Axis points down and the Z-Axis points away
// from the viewer. To convert from the CryEngine system into D3D9 we are just doing a clockwise
// rotation of pi/2 about the X-Axis. This conversion happens in the renderer.
//
// The 6 DOFs (degrees-of-freedom) are stored in one single 3x4 matrix ("m_Matrix"). The 3
// orientation-DOFs are stored in the 3x3 part and the 3 position-DOFs are stored in the translation-
// vector. You can use the member-functions "GetMatrix()" or "SetMatrix(Matrix34(orientation,positon))"
// to change or access the 6 DOFs.
//
// There are helper-function in Cry_Math.h to create the orientation:
//
// This function builds a 3x3 orientation matrix using a view-direction and a radiant to rotate about Y-axis.
// Matrix33 orientation=Matrix33::CreateOrientation( Vec3(0,1,0), 0 );
//
// This function builds a 3x3 orientation matrix using Yaw-Pitch-Roll angles.
// Matrix33 orientation=CCamera::CreateOrientationYPR( Ang3(1.234f,0.342f,0) );
//
///////////////////////////////////////////////////////////////////////////////
class CCamera
{
public:
ILINE static Matrix33 CreateOrientationYPR(const Ang3& ypr);
ILINE static Ang3 CreateAnglesYPR(const Matrix33& m);
ILINE static Ang3 CreateAnglesYPR(const Vec3& vdir, f32 r = 0);
ILINE static Vec3 CreateViewdir(const Ang3& ypr);
ILINE static Vec3 CreateViewdir(const Matrix33& m) { return m.GetColumn1(); };
ILINE void SetMatrix(const Matrix34& mat) { assert(mat.IsOrthonormal()); m_Matrix = mat; UpdateFrustum(); };
ILINE void SetMatrixNoUpdate(const Matrix34& mat) { assert(mat.IsOrthonormal()); m_Matrix = mat; };
ILINE const Matrix34& GetMatrix() const { return m_Matrix; };
ILINE Vec3 GetViewdir() const { return m_Matrix.GetColumn1(); };
ILINE void SetEntityRotation(const Quat& entityRot) { m_entityRot = entityRot; }
ILINE Quat GetEntityRotation() { return m_entityRot; }
ILINE void SetEntityPos(const Vec3& entityPos) { m_entityPos = entityPos; }
ILINE Vec3 GetEntityPos() const { return m_entityPos; };
ILINE Matrix34 GetViewMatrix() const { return m_Matrix.GetInverted(); };
ILINE Vec3 GetPosition() const { return m_Matrix.GetTranslation(); }
ILINE void SetPosition(const Vec3& p) { m_Matrix.SetTranslation(p); UpdateFrustum(); }
ILINE void SetPositionNoUpdate(const Vec3& p) { m_Matrix.SetTranslation(p); }
ILINE bool Project(const Vec3& p, Vec3& result, Vec2i topLeft = Vec2i(0, 0), Vec2i widthHeight = Vec2i(0, 0)) const;
ILINE bool Unproject(const Vec3& viewportPos, Vec3& result, Vec2i topLeft = Vec2i(0, 0), Vec2i widthHeight = Vec2i(0, 0)) const;
ILINE void CalcScreenBounds(int* vOut, const AABB* pAABB, int nWidth, int nHeight) const;
ILINE Vec3 GetUp() const { return m_Matrix.GetColumn2(); }
//------------------------------------------------------------
void SetFrustum(int nWidth, int nHeight, f32 FOV = DEFAULT_FOV, f32 nearplane = DEFAULT_NEAR, f32 farplane = DEFAULT_FAR, f32 fPixelAspectRatio = 1.0f);
ILINE void SetAsymmetry(float l, float r, float b, float t) { m_asymLeft = l; m_asymRight = r; m_asymBottom = b; m_asymTop = t; UpdateFrustum(); }
ILINE int GetViewSurfaceX() const { return m_Width; }
ILINE int GetViewSurfaceZ() const { return m_Height; }
ILINE f32 GetFov() const { return m_fov; }
ILINE float GetHorizontalFov() const;
ILINE f32 GetNearPlane() const { return m_edge_nlt.y; }
ILINE f32 GetFarPlane() const { return m_edge_flt.y; }
ILINE f32 GetProjRatio() const { return(m_ProjectionRatio); }
ILINE f32 GetAngularResolution() const { return m_Height / m_fov; }
ILINE f32 GetPixelAspectRatio() const { return m_PixelAspectRatio; }
ILINE Vec3 GetEdgeP() const { return m_edge_plt; }
ILINE Vec3 GetEdgeN() const { return m_edge_nlt; }
ILINE Vec3 GetEdgeF() const { return m_edge_flt; }
ILINE f32 GetAsymL() const { return m_asymLeft; }
ILINE f32 GetAsymR() const { return m_asymRight; }
ILINE f32 GetAsymB() const { return m_asymBottom; }
ILINE f32 GetAsymT() const { return m_asymTop; }
ILINE const Vec3& GetNPVertex(int nId) const; //get near-plane vertices
ILINE const Vec3& GetFPVertex(int nId) const; //get far-plane vertices
ILINE const Vec3& GetPPVertex(int nId) const; //get projection-plane vertices
ILINE const Plane_tpl<f32>* GetFrustumPlane(int numplane) const { return &m_fp[numplane]; }
//////////////////////////////////////////////////////////////////////////
// Z-Buffer ranges.
// This values are defining near/far clipping plane, it only used to specify z-buffer range.
// Use it only when you want to override default z-buffer range.
// Valid values for are: 0 <= zrange <= 1
//////////////////////////////////////////////////////////////////////////
ILINE void SetZRange(float zmin, float zmax)
{
// Clamp to 0-1 range.
m_zrangeMin = min(1.0f, max(0.0f, zmin));
m_zrangeMax = min(1.0f, max(0.0f, zmax));
};
ILINE float GetZRangeMin() const { return m_zrangeMin; }
ILINE float GetZRangeMax() const { return m_zrangeMax; }
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------------
//-------- Frustum-Culling ----------------------------
//-----------------------------------------------------------------------------------
//Check if a point lies within camera's frustum
bool IsPointVisible(const Vec3& p) const;
//sphere-frustum test
bool IsSphereVisible_F(const ::Sphere& s) const;
uint8 IsSphereVisible_FH(const ::Sphere& s) const; //this is going to be the exact version of sphere-culling
// AABB-frustum test
// Fast
bool IsAABBVisible_F(const ::AABB& aabb) const;
uint8 IsAABBVisible_FH(const ::AABB& aabb, bool* pAllInside) const;
uint8 IsAABBVisible_FH(const ::AABB& aabb) const;
// Exact
bool IsAABBVisible_E(const ::AABB& aabb) const;
uint8 IsAABBVisible_EH(const ::AABB& aabb, bool* pAllInside) const;
uint8 IsAABBVisible_EH(const ::AABB& aabb) const;
// Multi-camera
bool IsAABBVisible_EHM(const ::AABB& aabb, bool* pAllInside) const;
bool IsAABBVisible_EM(const ::AABB& aabb) const;
bool IsAABBVisible_FM(const ::AABB& aabb) const;
//OBB-frustum test
bool IsOBBVisible_F(const Vec3& wpos, const OBB& obb) const;
uint8 IsOBBVisible_FH(const Vec3& wpos, const OBB& obb) const;
bool IsOBBVisible_E(const Vec3& wpos, const OBB& obb, f32 uscale) const;
uint8 IsOBBVisible_EH(const Vec3& wpos, const OBB& obb, f32 uscale) const;
//## constructor/destructor
CCamera()
{
m_Matrix.SetIdentity();
m_asymRight = 0;
m_asymLeft = 0;
m_asymBottom = 0;
m_asymTop = 0;
SetFrustum(640, 480);
m_zrangeMin = 0.0f;
m_zrangeMax = 1.0f;
m_pMultiCamera = NULL;
m_pPortal = NULL;
m_JustActivated = 0;
m_nPosX = m_nPosY = m_nSizeX = m_nSizeY = 0;
m_entityPos = Vec3(0, 0, 0);
m_entityRot = Quat(0, 0, 0, 1);
}
~CCamera() {}
void GetFrustumVertices(Vec3* pVerts) const;
void GetFrustumVerticesCam(Vec3* pVerts) const;
void SetJustActivated(const bool justActivated) {m_JustActivated = (int)justActivated; }
bool IsJustActivated() const {return m_JustActivated != 0; }
void SetViewPort(int nPosX, int nPosY, int nSizeX, int nSizeY)
{
m_nPosX = nPosX;
m_nPosY = nPosY;
m_nSizeX = nSizeX;
m_nSizeY = nSizeY;
}
void GetViewPort(int& nPosX, int& nPosY, int& nSizeX, int& nSizeY) const
{
nPosX = m_nPosX;
nPosY = m_nPosY;
nSizeX = m_nSizeX;
nSizeY = m_nSizeY;
}
void UpdateFrustum();
void GetMemoryUsage([[maybe_unused]] ICrySizer* pSizer) const { /*nothing*/}
CameraViewParameters m_viewParameters;
private:
bool AdditionalCheck(const AABB& aabb) const;
bool AdditionalCheck(const Vec3& wpos, const OBB& obb, f32 uscale) const;
Matrix34 m_Matrix; // world space-matrix
f32 m_fov; // vertical fov in radiants [0..1*PI[
int m_Width; // surface width-resolution
int m_Height; // surface height-resolution
f32 m_ProjectionRatio; // ratio between width and height of view-surface
f32 m_PixelAspectRatio; // accounts for aspect ratio and non-square pixels
Quat m_entityRot; //The rotation of this camera's entity (does not include HMD orientation)
Vec3 m_entityPos; //The position of this camera's entity (does not include HMD position or stereo offsets)
Vec3 m_edge_nlt; // this is the left/upper vertex of the near-plane
Vec3 m_edge_plt; // this is the left/upper vertex of the projection-plane
Vec3 m_edge_flt; // this is the left/upper vertex of the far-clip-plane
f32 m_asymLeft, m_asymRight, m_asymBottom, m_asymTop; // Shift to create asymmetric frustum (only used for GPU culling of tessellated objects)
f32 m_asymLeftProj, m_asymRightProj, m_asymBottomProj, m_asymTopProj;
f32 m_asymLeftFar, m_asymRightFar, m_asymBottomFar, m_asymTopFar;
//usually we update these values every frame (they depend on m_Matrix)
Vec3 m_cltp, m_crtp, m_clbp, m_crbp; //this are the 4 vertices of the projection-plane in cam-space
Vec3 m_cltn, m_crtn, m_clbn, m_crbn; //this are the 4 vertices of the near-plane in cam-space
Vec3 m_cltf, m_crtf, m_clbf, m_crbf; //this are the 4 vertices of the farclip-plane in cam-space
Plane_tpl<f32> m_fp [FRUSTUM_PLANES]; //
uint32 m_idx1[FRUSTUM_PLANES], m_idy1[FRUSTUM_PLANES], m_idz1[FRUSTUM_PLANES]; //
uint32 m_idx2[FRUSTUM_PLANES], m_idy2[FRUSTUM_PLANES], m_idz2[FRUSTUM_PLANES]; //
// Near Far range of the z-buffer to use for this camera.
float m_zrangeMin;
float m_zrangeMax;
int m_nPosX, m_nPosY, m_nSizeX, m_nSizeY;
//------------------------------------------------------------------------
//--- OLD STUFF
//------------------------------------------------------------------------
public:
void SetFrustumVertices(Vec3* arrvVerts)
{
m_clbp = arrvVerts[0];
m_cltp = arrvVerts[1];
m_crtp = arrvVerts[2];
m_crbp = arrvVerts[3];
}
inline void SetFrustumPlane(int i, const Plane_tpl<f32>& plane)
{
m_fp[i] = plane;
//do not break strict aliasing rules, use union instead of reinterpret_casts
union f32_u
{
float floatVal;
uint32 uintVal;
};
{
f32_u ux;
ux.floatVal = m_fp[i].n.x;
f32_u uy;
uy.floatVal = m_fp[i].n.y;
f32_u uz;
uz.floatVal = m_fp[i].n.z;
uint32 bitX = ux.uintVal >> 31;
uint32 bitY = uy.uintVal >> 31;
uint32 bitZ = uz.uintVal >> 31;
m_idx1[i] = bitX * 3 + 0;
m_idx2[i] = (1 - bitX) * 3 + 0;
m_idy1[i] = bitY * 3 + 1;
m_idy2[i] = (1 - bitY) * 3 + 1;
m_idz1[i] = bitZ * 3 + 2;
m_idz2[i] = (1 - bitZ) * 3 + 2;
}
}
inline Ang3 GetAngles() const { return CreateAnglesYPR(Matrix33(m_Matrix)); }
void SetAngles(const Ang3& angles) { SetMatrix(Matrix34::CreateRotationXYZ(angles)); }
struct IVisArea* m_pPortal; // pointer to portal used to create this camera
struct ScissorInfo
{
ScissorInfo() { x1 = y1 = x2 = y2 = 0; }
uint16 x1, y1, x2, y2;
};
ScissorInfo m_ScissorInfo;
class PodArray<CCamera>* m_pMultiCamera; // maybe used for culling instead of this camera
Vec3 m_OccPosition; //Position for calculate occlusions (needed for portals rendering)
inline const Vec3& GetOccPos() const { return(m_OccPosition); }
int m_JustActivated; //Camera activated in this frame, used for disabling motion blur effect at camera changes in movies
};
inline float CCamera::GetHorizontalFov() const
{
float fFractionVert = tanf(m_fov * 0.5f);
float fFractionHoriz = fFractionVert * GetProjRatio();
float fHorizFov = atanf(fFractionHoriz) * 2;
return fHorizFov;
}
// Description
// This function builds a 3x3 orientation matrix using YPR-angles
// Rotation order for the orientation-matrix is Z-X-Y. (Zaxis=YAW / Xaxis=PITCH / Yaxis=ROLL)
//
// <PRE>
// COORDINATE-SYSTEM
//
// z-axis
// ^
// |
// | y-axis
// | /
// | /
// |/
// +---------------> x-axis
// </PRE>
//
// Example:
// Matrix33 orientation=CCamera::CreateOrientationYPR( Ang3(1,2,3) );
inline Matrix33 CCamera::CreateOrientationYPR(const Ang3& ypr)
{
f32 sz, cz;
sincos_tpl(ypr.x, &sz, &cz); //Zaxis = YAW
f32 sx, cx;
sincos_tpl(ypr.y, &sx, &cx); //Xaxis = PITCH
f32 sy, cy;
sincos_tpl(ypr.z, &sy, &cy); //Yaxis = ROLL
Matrix33 c;
c.m00 = cy * cz - sy * sz * sx;
c.m01 = -sz * cx;
c.m02 = sy * cz + cy * sz * sx;
c.m10 = cy * sz + sy * sx * cz;
c.m11 = cz * cx;
c.m12 = sy * sz - cy * sx * cz;
c.m20 = -sy * cx;
c.m21 = sx;
c.m22 = cy * cx;
return c;
}
// Description
// <PRE>
// x-YAW
// y-PITCH (negative=looking down / positive=looking up)
// z-ROLL
// </PRE>
// Note: If we are looking along the z-axis, its not possible to specify the x and z-angle
inline Ang3 CCamera::CreateAnglesYPR(const Matrix33& m)
{
assert(m.IsOrthonormal());
float l = Vec3(m.m01, m.m11, 0.0f).GetLength();
if (l > 0.0001)
{
return Ang3(atan2f(-m.m01 / l, m.m11 / l), atan2f(m.m21, l), atan2f(-m.m20 / l, m.m22 / l));
}
else
{
return Ang3(0, atan2f(m.m21, l), 0);
}
}
// Description
// <PRE>
//x-YAW
//y-PITCH (negative=looking down / positive=looking up)
//z-ROLL (its not possile to extract a "roll" from a view-vector)
// </PRE>
// Note: if we are looking along the z-axis, its not possible to specify the rotation about the z-axis
ILINE Ang3 CCamera::CreateAnglesYPR(const Vec3& vdir, f32 r)
{
assert((fabs_tpl(1 - (vdir | vdir))) < 0.001); //check if unit-vector
f32 l = Vec3(vdir.x, vdir.y, 0.0f).GetLength(); //check if not zero
if (l > 0.0001)
{
return Ang3(atan2f(-vdir.x / l, vdir.y / l), atan2f(vdir.z, l), r);
}
else
{
return Ang3(0, atan2f(vdir.z, l), r);
}
}
// Description
// <PRE>
//x=yaw
//y=pitch
//z=roll (we ignore this element, since its not possible to convert the roll-component into a vector)
// </PRE>
ILINE Vec3 CCamera::CreateViewdir(const Ang3& ypr)
{
assert(ypr.IsInRangePI()); //all angles need to be in range between -pi and +pi
f32 sz, cz;
sincos_tpl(ypr.x, &sz, &cz); //YAW
f32 sx, cx;
sincos_tpl(ypr.y, &sx, &cx); //PITCH
return Vec3(-sz * cx, cz * cx, sx); //calculate the view-direction
}
// Description
// <PRE>
//p=world space position
//result=spreen space pos
//retval=is visible on screen
// </PRE>
ILINE bool CCamera::Project(const Vec3& p, Vec3& result, Vec2i topLeft, Vec2i widthHeight) const
{
Matrix44A mProj, mView;
Vec4 in, transformed, projected;
mathMatrixPerspectiveFov(&mProj, GetFov(), GetProjRatio(), GetNearPlane(), GetFarPlane());
mathMatrixLookAt(&mView, GetPosition(), GetPosition() + GetViewdir(), GetUp());
int pViewport[4] = {0, 0, GetViewSurfaceX(), GetViewSurfaceZ()};
if (!topLeft.IsZero() || !widthHeight.IsZero())
{
pViewport[0] = topLeft.x;
pViewport[1] = topLeft.y;
pViewport[2] = widthHeight.x;
pViewport[3] = widthHeight.y;
}
in.x = p.x;
in.y = p.y;
in.z = p.z;
in.w = 1.0f;
mathVec4Transform((f32*)&transformed, (f32*)&mView, (f32*)&in);
bool visible = transformed.z < 0.0f;
mathVec4Transform((f32*)&projected, (f32*)&mProj, (f32*)&transformed);
if (projected.w == 0.0f)
{
result = Vec3(0.f, 0.f, 0.f);
return false;
}
projected.x /= projected.w;
projected.y /= projected.w;
projected.z /= projected.w;
visible = visible && (fabs_tpl(projected.x) <= 1.0f) && (fabs_tpl(projected.y) <= 1.0f);
//output coords
result.x = pViewport[0] + (1 + projected.x) * pViewport[2] / 2;
result.y = pViewport[1] + (1 - projected.y) * pViewport[3] / 2; //flip coords for y axis
result.z = projected.z;
return visible;
}
ILINE bool CCamera::Unproject(const Vec3& viewportPos, Vec3& result, Vec2i topLeft, Vec2i widthHeight) const
{
Matrix44A mProj, mView;
mathMatrixPerspectiveFov(&mProj, GetFov(), GetProjRatio(), GetNearPlane(), GetFarPlane());
mathMatrixLookAt(&mView, GetPosition(), GetPosition() + GetViewdir(), Vec3(0, 0, 1));
int viewport[4] = {0, 0, GetViewSurfaceX(), GetViewSurfaceZ()};
if (!topLeft.IsZero() || !widthHeight.IsZero())
{
viewport[0] = topLeft.x;
viewport[1] = topLeft.y;
viewport[2] = widthHeight.x;
viewport[3] = widthHeight.y;
}
Vec4 vIn;
vIn.x = (viewportPos.x - viewport[0]) * 2 / viewport[2] - 1.0f;
vIn.y = (viewportPos.y - viewport[1]) * 2 / viewport[3] - 1.0f;
vIn.z = viewportPos.z;
vIn.w = 1.0;
Matrix44A m;
const float* proj = mProj.GetData();
const float* view = mView.GetData();
float* mdata = m.GetData();
for (int i = 0; i < 4; i++)
{
float ai0 = proj[i], ai1 = proj[4 + i], ai2 = proj[8 + i], ai3 = proj[12 + i];
mdata[i] = ai0 * view[0] + ai1 * view[1] + ai2 * view[2] + ai3 * view[3];
mdata[4 + i] = ai0 * view[4] + ai1 * view[5] + ai2 * view[6] + ai3 * view[7];
mdata[8 + i] = ai0 * view[8] + ai1 * view[9] + ai2 * view[10] + ai3 * view[11];
mdata[12 + i] = ai0 * view[12] + ai1 * view[13] + ai2 * view[14] + ai3 * view[15];
}
m.Invert();
if (!m.IsValid())
{
return false;
}
Vec4 vOut = vIn * m;
if (vOut.w == 0.0)
{
return false;
}
result = Vec3(vOut.x / vOut.w, vOut.y / vOut.w, vOut.z / vOut.w);
return true;
}
ILINE void CCamera::CalcScreenBounds(int* vOut, const AABB* pAABB, int nWidth, int nHeight) const
{
Matrix44A mProj, mView, mVP;
mathMatrixPerspectiveFov(&mProj, GetFov(), GetProjRatio(), GetNearPlane(), GetFarPlane());
mathMatrixLookAt(&mView, GetPosition(), GetPosition() + GetViewdir(), GetMatrix().GetColumn2());
mVP = mView * mProj;
Vec3 verts[8];
Vec2i topLeft = Vec2i(0, 0);
Vec2i widthHeight = Vec2i(nWidth, nHeight);
float pViewport[4] = {0.0f, 0.0f, (float)widthHeight.x, (float)widthHeight.y};
float x0 = 9999.9f, x1 = -9999.9f, y0 = 9999.9f, y1 = -9999.9f;
float fIntersect = 1.0f;
Vec3 vDir = GetViewdir();
Vec3 vPos = GetPosition();
float d = vPos.Dot(vDir);
verts[0] = Vec3(pAABB->min.x, pAABB->min.y, pAABB->min.z);
verts[1] = Vec3(pAABB->max.x, pAABB->min.y, pAABB->min.z);
verts[2] = Vec3(pAABB->min.x, pAABB->max.y, pAABB->min.z);
verts[3] = Vec3(pAABB->max.x, pAABB->max.y, pAABB->min.z);
verts[4] = Vec3(pAABB->min.x, pAABB->min.y, pAABB->max.z);
verts[5] = Vec3(pAABB->max.x, pAABB->min.y, pAABB->max.z);
verts[6] = Vec3(pAABB->min.x, pAABB->max.y, pAABB->max.z);
verts[7] = Vec3(pAABB->max.x, pAABB->max.y, pAABB->max.z);
for (int i = 0; i < 8; i++)
{
float fDist = verts[i].Dot(vDir) - d;
fDist = (float)fsel(fDist, 0.0f, -fDist);
//Project(verts[i],vertsOut[i], topLeft, widthHeight);
Vec3 result = Vec3(0.0f, 0.0f, 0.0f);
Vec4 transformed, projected, vIn;
vIn = Vec4(verts[i].x, verts[i].y, verts[i].z, 1.0f);
mathVec4Transform((f32*)&projected, (f32*)&mVP, (f32*)&vIn);
fIntersect = (float)fsel(-projected.w, 0.0f, 1.0f);
if (!fzero(fIntersect) && !fzero(projected.w))
{
projected.x /= projected.w;
projected.y /= projected.w;
projected.z /= projected.w;
//output coords
result.x = pViewport[0] + (1.0f + projected.x) * pViewport[2] / 2.0f;
result.y = pViewport[1] + (1.0f - projected.y) * pViewport[3] / 2.0f; //flip coords for y axis
result.z = projected.z;
}
else
{
vOut[0] = topLeft.x;
vOut[1] = topLeft.y;
vOut[2] = widthHeight.x;
vOut[3] = widthHeight.y;
return;
}
x0 = min(x0, result.x);
x1 = max(x1, result.x);
y0 = min(y0, result.y);
y1 = max(y1, result.y);
}
vOut[0] = (int)max(0.0f, min(pViewport[2], x0));
vOut[1] = (int)max(0.0f, min(pViewport[3], y0));
vOut[2] = (int)max(0.0f, min(pViewport[2], x1));
vOut[3] = (int)max(0.0f, min(pViewport[3], y1));
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
inline void CCamera::SetFrustum(int nWidth, int nHeight, f32 FOV, f32 nearplane, f32 farplane, f32 fPixelAspectRatio)
{
assert (nearplane >= CAMERA_MIN_NEAR); //check if near-plane is valid
assert (farplane >= 0.1f); //check if far-plane is valid
assert (farplane >= nearplane); //check if far-plane bigger then near-plane
assert (FOV >= MIN_FOV && FOV < gf_PI); //check if specified FOV is valid
m_fov = FOV;
m_Width = nWidth; //surface x-resolution
m_Height = nHeight; //surface z-resolution
f32 fWidth = (((f32)nWidth) / fPixelAspectRatio);
f32 fHeight = (f32) nHeight;
m_ProjectionRatio = fWidth / fHeight; // projection ratio (1.0 for square pixels)
m_PixelAspectRatio = fPixelAspectRatio;
//-------------------------------------------------------------------------
//--- calculate the Left/Top edge of the Projection-Plane in EYE-SPACE ---
//-------------------------------------------------------------------------
f32 projLeftTopX = -fWidth * 0.5f;
f32 projLeftTopY = static_cast<f32>((1.0f / tan_tpl(m_fov * 0.5f)) * (fHeight * 0.5f));
f32 projLeftTopZ = fHeight * 0.5f;
m_edge_plt.x = projLeftTopX;
m_edge_plt.y = projLeftTopY;
m_edge_plt.z = projLeftTopZ;
assert(fabs(acos_tpl(Vec3d(0, m_edge_plt.y, m_edge_plt.z).GetNormalized().y) * 2 - m_fov) < 0.001);
float invProjLeftTopY = 1.0f / projLeftTopY;
//Apply asym shift to the camera frustum - Necessary for properly culling tessellated objects in VR
//These are applied in UpdateFrustum to the camera space frustum planes
//Can't apply asym shift to frustum edges here. That would only apply to the top left corner
//rather than the whole frustum. It would also interfere with shadow map application
//m_asym is at the near plane, we want it at the projection plane too
m_asymLeftProj = (m_asymLeft / nearplane) * projLeftTopY;
m_asymTopProj = (m_asymTop / nearplane) * projLeftTopY;
m_asymRightProj = (m_asymRight / nearplane) * projLeftTopY;
m_asymBottomProj = (m_asymBottom / nearplane) * projLeftTopY;
//Also want m_asym at the far plane
m_asymLeftFar = m_asymLeftProj * (farplane * invProjLeftTopY);
m_asymTopFar = m_asymTopProj * (farplane * invProjLeftTopY);
m_asymRightFar = m_asymRightProj * (farplane * invProjLeftTopY);
m_asymBottomFar = m_asymBottomProj * (farplane * invProjLeftTopY);
m_edge_nlt.x = nearplane * projLeftTopX * invProjLeftTopY;
m_edge_nlt.y = nearplane;
m_edge_nlt.z = nearplane * projLeftTopZ * invProjLeftTopY;
//calculate the left/upper edge of the far-plane (=not rotated)
m_edge_flt.x = projLeftTopX * (farplane * invProjLeftTopY);
m_edge_flt.y = farplane;
m_edge_flt.z = projLeftTopZ * (farplane * invProjLeftTopY);
UpdateFrustum();
}
/*!
*
* Updates all parameters required by the render-engine:
*
* 3d-view-frustum and all matrices
*
*/
inline void CCamera::UpdateFrustum()
{
//-------------------------------------------------------------------
//--- calculate frustum-edges of projection-plane in CAMERA-SPACE ---
//-------------------------------------------------------------------
Matrix33 m33 = Matrix33(m_Matrix);
m_cltp = m33 * Vec3(+m_edge_plt.x + m_asymLeftProj, +m_edge_plt.y, +m_edge_plt.z + m_asymTopProj);
m_crtp = m33 * Vec3(-m_edge_plt.x + m_asymRightProj, +m_edge_plt.y, +m_edge_plt.z + m_asymTopProj);
m_clbp = m33 * Vec3(+m_edge_plt.x + m_asymLeftProj, +m_edge_plt.y, -m_edge_plt.z + m_asymBottomProj);
m_crbp = m33 * Vec3(-m_edge_plt.x + m_asymRightProj, +m_edge_plt.y, -m_edge_plt.z + m_asymBottomProj);
m_cltn = m33 * Vec3(+m_edge_nlt.x + m_asymLeft, +m_edge_nlt.y, +m_edge_nlt.z + m_asymTop);
m_crtn = m33 * Vec3(-m_edge_nlt.x + m_asymRight, +m_edge_nlt.y, +m_edge_nlt.z + m_asymTop);
m_clbn = m33 * Vec3(+m_edge_nlt.x + m_asymLeft, +m_edge_nlt.y, -m_edge_nlt.z + m_asymBottom);
m_crbn = m33 * Vec3(-m_edge_nlt.x + m_asymRight, +m_edge_nlt.y, -m_edge_nlt.z + m_asymBottom);
m_cltf = m33 * Vec3(+m_edge_flt.x + m_asymLeftFar, +m_edge_flt.y, +m_edge_flt.z + m_asymTopFar);
m_crtf = m33 * Vec3(-m_edge_flt.x + m_asymRightFar, +m_edge_flt.y, +m_edge_flt.z + m_asymTopFar);
m_clbf = m33 * Vec3(+m_edge_flt.x + m_asymLeftFar, +m_edge_flt.y, -m_edge_flt.z + m_asymBottomFar);
m_crbf = m33 * Vec3(-m_edge_flt.x + m_asymRightFar, +m_edge_flt.y, -m_edge_flt.z + m_asymBottomFar);
//-------------------------------------------------------------------------------
//--- calculate the six frustum-planes using the frustum edges in world-space ---
//-------------------------------------------------------------------------------
m_fp[FR_PLANE_NEAR ] = Plane_tpl<f32>::CreatePlane(m_crtn + GetPosition(), m_cltn + GetPosition(), m_crbn + GetPosition());
m_fp[FR_PLANE_RIGHT ] = Plane_tpl<f32>::CreatePlane(m_crbf + GetPosition(), m_crtf + GetPosition(), GetPosition());
m_fp[FR_PLANE_LEFT ] = Plane_tpl<f32>::CreatePlane(m_cltf + GetPosition(), m_clbf + GetPosition(), GetPosition());
m_fp[FR_PLANE_TOP ] = Plane_tpl<f32>::CreatePlane(m_crtf + GetPosition(), m_cltf + GetPosition(), GetPosition());
m_fp[FR_PLANE_BOTTOM] = Plane_tpl<f32>::CreatePlane(m_clbf + GetPosition(), m_crbf + GetPosition(), GetPosition());
m_fp[FR_PLANE_FAR ] = Plane_tpl<f32>::CreatePlane(m_crtf + GetPosition(), m_crbf + GetPosition(), m_cltf + GetPosition()); //clip-plane
uint32 rh = m_Matrix.IsOrthonormalRH();
if (rh == 0)
{
m_fp[FR_PLANE_NEAR ] = -m_fp[FR_PLANE_NEAR ];
m_fp[FR_PLANE_RIGHT ] = -m_fp[FR_PLANE_RIGHT ];
m_fp[FR_PLANE_LEFT ] = -m_fp[FR_PLANE_LEFT ];
m_fp[FR_PLANE_TOP ] = -m_fp[FR_PLANE_TOP ];
m_fp[FR_PLANE_BOTTOM] = -m_fp[FR_PLANE_BOTTOM];
m_fp[FR_PLANE_FAR ] = -m_fp[FR_PLANE_FAR ]; //clip-plane
}
union f32_u
{
float floatVal;
uint32 uintVal;
};
for (int i = 0; i < FRUSTUM_PLANES; i++)
{
f32_u ux;
ux.floatVal = m_fp[i].n.x;
f32_u uy;
uy.floatVal = m_fp[i].n.y;
f32_u uz;
uz.floatVal = m_fp[i].n.z;
uint32 bitX = ux.uintVal >> 31;
uint32 bitY = uy.uintVal >> 31;
uint32 bitZ = uz.uintVal >> 31;
m_idx1[i] = bitX * 3 + 0;
m_idx2[i] = (1 - bitX) * 3 + 0;
m_idy1[i] = bitY * 3 + 1;
m_idy2[i] = (1 - bitY) * 3 + 1;
m_idz1[i] = bitZ * 3 + 2;
m_idz2[i] = (1 - bitZ) * 3 + 2;
}
m_OccPosition = GetPosition();
}
// Summary
// Return frustum vertices in world space
// Description
// Takes pointer to array of 8 elements
inline void CCamera::GetFrustumVertices(Vec3* pVerts) const
{
Matrix33 m33 = Matrix33(m_Matrix);
/*
The frustum array contains points in the following order
frustum[0] = far left top
frustum[1] = far left bottom
frustum[2] = far right bottom
frustum[3] = far right top
frustum[4] = near left top
frustum[5] = near left bottom
frustum[6] = near right bottom
frustum[7] = near right top
*/
int i = 0;
pVerts[i++] = m33 * Vec3(+m_edge_flt.x, +m_edge_flt.y, +m_edge_flt.z) + GetPosition();
pVerts[i++] = m33 * Vec3(+m_edge_flt.x, +m_edge_flt.y, -m_edge_flt.z) + GetPosition();
pVerts[i++] = m33 * Vec3(-m_edge_flt.x, +m_edge_flt.y, -m_edge_flt.z) + GetPosition();
pVerts[i++] = m33 * Vec3(-m_edge_flt.x, +m_edge_flt.y, +m_edge_flt.z) + GetPosition();
pVerts[i++] = m33 * Vec3(+m_edge_nlt.x, +m_edge_nlt.y, +m_edge_nlt.z) + GetPosition();
pVerts[i++] = m33 * Vec3(+m_edge_nlt.x, +m_edge_nlt.y, -m_edge_nlt.z) + GetPosition();
pVerts[i++] = m33 * Vec3(-m_edge_nlt.x, +m_edge_nlt.y, -m_edge_nlt.z) + GetPosition();
pVerts[i++] = m33 * Vec3(-m_edge_nlt.x, +m_edge_nlt.y, +m_edge_nlt.z) + GetPosition();
}
inline void CCamera::GetFrustumVerticesCam(Vec3* pVerts) const
{
int i = 0;
//near plane
pVerts[i++] = Vec3(+m_edge_nlt.x, +m_edge_nlt.y, +m_edge_nlt.z);
pVerts[i++] = Vec3(+m_edge_nlt.x, +m_edge_nlt.y, -m_edge_nlt.z);
pVerts[i++] = Vec3(-m_edge_nlt.x, +m_edge_nlt.y, -m_edge_nlt.z);
pVerts[i++] = Vec3(-m_edge_nlt.x, +m_edge_nlt.y, +m_edge_nlt.z);
//far plane
pVerts[i++] = Vec3(+m_edge_flt.x, +m_edge_flt.y, +m_edge_flt.z);
pVerts[i++] = Vec3(+m_edge_flt.x, +m_edge_flt.y, -m_edge_flt.z);
pVerts[i++] = Vec3(-m_edge_flt.x, +m_edge_flt.y, -m_edge_flt.z);
pVerts[i++] = Vec3(-m_edge_flt.x, +m_edge_flt.y, +m_edge_flt.z);
}
//get near-plane vertices
ILINE const Vec3& CCamera::GetNPVertex(int nId) const
{
switch (nId)
{
case 0:
return m_clbn;
case 1:
return m_cltn;
case 2:
return m_crtn;
case 3:
return m_crbn;
}
assert(0);
return m_clbn;
}
//get far-plane vertices
ILINE const Vec3& CCamera::GetFPVertex(int nId) const
{
switch (nId)
{
case 0:
return m_clbf;
case 1:
return m_cltf;
case 2:
return m_crtf;
case 3:
return m_crbf;
}
assert(0);
return m_clbf;
}
ILINE const Vec3& CCamera::GetPPVertex(int nId) const
{
switch (nId)
{
case 0:
return m_clbp;
case 1:
return m_cltp;
case 2:
return m_crtp;
case 3:
return m_crbp;
}
assert(0);
return m_clbp;
}
// Description
// Check if a point lies within camera's frustum
//
// Example
// u8 InOut=camera.IsPointVisible(point);
//
// return values
// CULL_EXCLUSION = point outside of frustum
// CULL_INTERSECT = point inside of frustum
inline bool CCamera::IsPointVisible(const Vec3& p) const
{
if ((m_fp[FR_PLANE_NEAR ] | p) > 0)
{
return CULL_EXCLUSION;
}
if ((m_fp[FR_PLANE_RIGHT ] | p) > 0)
{
return CULL_EXCLUSION;
}
if ((m_fp[FR_PLANE_LEFT ] | p) > 0)
{
return CULL_EXCLUSION;
}
if ((m_fp[FR_PLANE_TOP ] | p) > 0)
{
return CULL_EXCLUSION;
}
if ((m_fp[FR_PLANE_BOTTOM] | p) > 0)
{
return CULL_EXCLUSION;
}
if ((m_fp[FR_PLANE_FAR ] | p) > 0)
{
return CULL_EXCLUSION;
}
return CULL_OVERLAP;
}
// Description
// Conventional method to check if a sphere and the camera-frustum overlap
// The center of the sphere is assumed to be in world-space.
//
// Example
// u8 InOut=camera.IsSphereVisible_F(sphere);
//
// return values
// CULL_EXCLUSION = sphere outside of frustum (very fast rejection-test)
// CULL_INTERSECT = sphere and frustum intersects or sphere in completely inside frustum
inline bool CCamera::IsSphereVisible_F(const ::Sphere& s) const
{
if ((m_fp[0] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
if ((m_fp[1] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
if ((m_fp[2] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
if ((m_fp[3] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
if ((m_fp[4] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
if ((m_fp[5] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
return CULL_OVERLAP;
}
// Description
// Conventional method to check if a sphere and the camera-frustum overlap, or
// if the sphere is completely inside the camera-frustum. The center of the
// sphere is assumed to be in world-space.
//
// Example
// u8 InOut=camera.IsSphereVisible_FH(sphere);
//
// return values
// CULL_EXCLUSION = sphere outside of frustum (very fast rejection-test)
// CULL_INTERSECT = sphere intersects the borders of the frustum, further checks necessary
// CULL_INCLUSION = sphere is complete inside the frustum, no further checks necessary
inline uint8 CCamera::IsSphereVisible_FH(const ::Sphere& s) const
{
f32 nc, rc, lc, tc, bc, cc;
if ((nc = m_fp[0] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
if ((rc = m_fp[1] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
if ((lc = m_fp[2] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
if ((tc = m_fp[3] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
if ((bc = m_fp[4] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
if ((cc = m_fp[5] | s.center) > s.radius)
{
return CULL_EXCLUSION;
}
//now we have to check if it is completely in frustum
f32 r = -s.radius;
if (nc > r)
{
return CULL_OVERLAP;
}
if (lc > r)
{
return CULL_OVERLAP;
}
if (rc > r)
{
return CULL_OVERLAP;
}
if (tc > r)
{
return CULL_OVERLAP;
}
if (bc > r)
{
return CULL_OVERLAP;
}
if (cc > r)
{
return CULL_OVERLAP;
}
return CULL_INCLUSION;
}
// Description
// Simple approach to check if an AABB and the camera-frustum overlap. The AABB
// is assumed to be in world-space. This is a very fast method, just one single
// dot-product is necessary to check an AABB against a plane. Actually there
// is no significant speed-different between culling a sphere or an AABB.
//
// Example
// bool InOut=camera.IsAABBVisible_F(aabb);
//
// return values
// CULL_EXCLUSION = AABB outside of frustum (very fast rejection-test)
// CULL_OVERLAP = AABB either intersects the borders of the frustum or is totally inside
inline bool CCamera::IsAABBVisible_F(const AABB& aabb) const
{
const f32* p = &aabb.min.x;
uint32 x, y, z;
x = m_idx1[0];
y = m_idy1[0];
z = m_idz1[0];
if ((m_fp[0] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_EXCLUSION;
}
x = m_idx1[1];
y = m_idy1[1];
z = m_idz1[1];
if ((m_fp[1] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_EXCLUSION;
}
x = m_idx1[2];
y = m_idy1[2];
z = m_idz1[2];
if ((m_fp[2] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_EXCLUSION;
}
x = m_idx1[3];
y = m_idy1[3];
z = m_idz1[3];
if ((m_fp[3] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_EXCLUSION;
}
x = m_idx1[4];
y = m_idy1[4];
z = m_idz1[4];
if ((m_fp[4] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_EXCLUSION;
}
x = m_idx1[5];
y = m_idy1[5];
z = m_idz1[5];
if ((m_fp[5] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_EXCLUSION;
}
return CULL_OVERLAP;
}
// Description
// Hierarchical approach to check if an AABB and the camera-frustum overlap, or if the AABB
// is totally inside the camera-frustum. The AABB is assumed to be in world-space.
//
// Example
// int InOut=camera.IsAABBVisible_FH(aabb);
//
// return values
// CULL_EXCLUSION = AABB outside of frustum (very fast rejection-test)
// CULL_OVERLAP = AABB intersects the borders of the frustum, further checks necessary
// CULL_INCLUSION = AABB is complete inside the frustum, no further checks necessary
inline uint8 CCamera::IsAABBVisible_FH(const AABB& aabb, bool* pAllInside) const
{
assert(pAllInside && *pAllInside == false);
if (IsAABBVisible_F(aabb) == CULL_EXCLUSION)
{
return CULL_EXCLUSION;
}
const f32* p = &aabb.min.x;
uint32 x, y, z;
x = m_idx2[0];
y = m_idy2[0];
z = m_idz2[0];
if ((m_fp[0] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
x = m_idx2[1];
y = m_idy2[1];
z = m_idz2[1];
if ((m_fp[1] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
x = m_idx2[2];
y = m_idy2[2];
z = m_idz2[2];
if ((m_fp[2] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
x = m_idx2[3];
y = m_idy2[3];
z = m_idz2[3];
if ((m_fp[3] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
x = m_idx2[4];
y = m_idy2[4];
z = m_idz2[4];
if ((m_fp[4] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
x = m_idx2[5];
y = m_idy2[5];
z = m_idz2[5];
if ((m_fp[5] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
*pAllInside = true;
return CULL_INCLUSION;
}
// Description
// Hierarchical approach to check if an AABB and the camera-frustum overlap, or if the AABB
// is totally inside the camera-frustum. The AABB is assumed to be in world-space.
//
// Example
// int InOut=camera.IsAABBVisible_FH(aabb);
//
// return values
// CULL_EXCLUSION = AABB outside of frustum (very fast rejection-test)
// CULL_OVERLAP = AABB intersects the borders of the frustum, further checks necessary
// CULL_INCLUSION = AABB is complete inside the frustum, no further checks necessary
inline uint8 CCamera::IsAABBVisible_FH(const AABB& aabb) const
{
if (IsAABBVisible_F(aabb) == CULL_EXCLUSION)
{
return CULL_EXCLUSION;
}
const f32* p = &aabb.min.x;
uint32 x, y, z;
x = m_idx2[0];
y = m_idy2[0];
z = m_idz2[0];
if ((m_fp[0] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
x = m_idx2[1];
y = m_idy2[1];
z = m_idz2[1];
if ((m_fp[1] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
x = m_idx2[2];
y = m_idy2[2];
z = m_idz2[2];
if ((m_fp[2] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
x = m_idx2[3];
y = m_idy2[3];
z = m_idz2[3];
if ((m_fp[3] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
x = m_idx2[4];
y = m_idy2[4];
z = m_idz2[4];
if ((m_fp[4] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
x = m_idx2[5];
y = m_idy2[5];
z = m_idz2[5];
if ((m_fp[5] | Vec3(p[x], p[y], p[z])) > 0)
{
return CULL_OVERLAP;
}
return CULL_INCLUSION;
}
// Description
// This function checks if an AABB and the camera-frustum overlap. The AABB is assumed to be in world-space.
// This test can reject even such AABBs that overlap a frustum-plane far outside the view-frustum.
// IMPORTANT:
// This function is only useful if you really need exact-culling.
// It's about 30% slower then "IsAABBVisible_F(aabb)"
//
// Example:
// int InOut=camera.IsAABBVisible_E(aabb);
//
// return values:
// CULL_EXCLUSION = AABB outside of frustum (very fast rejection-test)
// CULL_OVERLAP = AABB either intersects the borders of the frustum or is totally inside
inline bool CCamera::IsAABBVisible_E(const AABB& aabb) const
{
uint8 o = IsAABBVisible_FH(aabb);
if (o == CULL_EXCLUSION)
{
return CULL_EXCLUSION;
}
if (o == CULL_INCLUSION)
{
return CULL_OVERLAP;
}
return AdditionalCheck(aabb); //result is either "exclusion" or "overlap"
}
// Description:
// Improved approach to check if an AABB and the camera-frustum overlap, or if the AABB
// is totally inside the camera-frustum. The AABB is assumed to be in world-space. This
// test can reject even such AABBs that overlap a frustum-plane far outside the view-frustum.
// IMPORTANT:
// This function is only useful if you really need exact-culling.
// It's about 30% slower then "IsAABBVisible_FH(aabb)"
//
// Example:
// int InOut=camera.IsAABBVisible_EH(aabb);
//
// return values:
// CULL_EXCLUSION = AABB outside of frustum (very fast rejection-test)
// CULL_OVERLAP = AABB intersects the borders of the frustum, further checks necessary
// CULL_INCLUSION = AABB is complete inside the frustum, no further checks necessary
inline uint8 CCamera::IsAABBVisible_EH(const AABB& aabb, bool* pAllInside) const
{
uint8 o = IsAABBVisible_FH(aabb, pAllInside);
if (o == CULL_EXCLUSION)
{
return CULL_EXCLUSION;
}
if (o == CULL_INCLUSION)
{
return CULL_INCLUSION;
}
return AdditionalCheck(aabb); //result is either "exclusion" or "overlap"
}
// Description:
// Improved approach to check if an AABB and the camera-frustum overlap, or if the AABB
// is totally inside the camera-frustum. The AABB is assumed to be in world-space. This
// test can reject even such AABBs that overlap a frustum-plane far outside the view-frustum.
// IMPORTANT:
// This function is only useful if you really need exact-culling.
// It's about 30% slower then "IsAABBVisible_FH(aabb)"
//
// Example:
// int InOut=camera.IsAABBVisible_EH(aabb);
//
// return values:
// CULL_EXCLUSION = AABB outside of frustum (very fast rejection-test)
// CULL_OVERLAP = AABB intersects the borders of the frustum, further checks necessary
// CULL_INCLUSION = AABB is complete inside the frustum, no further checks necessary
inline uint8 CCamera::IsAABBVisible_EH(const AABB& aabb) const
{
uint8 o = IsAABBVisible_FH(aabb);
if (o == CULL_EXCLUSION)
{
return CULL_EXCLUSION;
}
if (o == CULL_INCLUSION)
{
return CULL_INCLUSION;
}
return AdditionalCheck(aabb); //result is either "exclusion" or "overlap"
}
// Description:
// Makes culling taking into account presence of m_pMultiCamera
// If m_pMultiCamera exists - object is visible if at least one of cameras see's it
//
// return values:
// true - box visible
// true - not visible
inline bool CCamera::IsAABBVisible_EHM(const AABB& aabb, bool* pAllInside) const
{
assert(pAllInside && *pAllInside == false);
if (!m_pMultiCamera) // use main camera
{
return IsAABBVisible_EH(aabb, pAllInside) != CULL_EXCLUSION;
}
bool bVisible = false;
for (int i = 0; i < m_pMultiCamera->Count(); i++)
{
bool bAllIn = false;
if (m_pMultiCamera->GetAt(i).IsAABBVisible_EH(aabb, &bAllIn))
{
bVisible = true;
// don't break here always because another camera may include bbox completely
if (bAllIn)
{
*pAllInside = true;
break;
}
}
}
return bVisible;
}
// Description:
// Makes culling taking into account presence of m_pMultiCamera
// If m_pMultiCamera exists - object is visible if at least one of cameras see's it
//
// return values:
// true - box visible
// true - not visible
ILINE bool CCamera::IsAABBVisible_EM(const AABB& aabb) const
{
if (!m_pMultiCamera) // use main camera
{
return IsAABBVisible_E(aabb) != CULL_EXCLUSION;
}
// check several parallel cameras - object is visible if at least one camera see's it
for (int i = 0; i < m_pMultiCamera->Count(); i++)
{
if (m_pMultiCamera->GetAt(i).IsAABBVisible_E(aabb))
{
return true;
}
}
return false;
}
// Description:
// Makes culling taking into account presence of m_pMultiCamera
// If m_pMultiCamera exists - object is visible if at least one of cameras see's it
//
// return values:
// true - box visible
// true - not visible
ILINE bool CCamera::IsAABBVisible_FM(const AABB& aabb) const
{
PrefetchLine(&aabb, sizeof(AABB));
if (!m_pMultiCamera) // use main camera
{
return IsAABBVisible_F(aabb) != CULL_EXCLUSION;
}
// check several parallel cameras - object is visible if at least one camera see's it
for (int i = 0; i < m_pMultiCamera->Count(); i++)
{
if (m_pMultiCamera->GetAt(i).IsAABBVisible_F(aabb))
{
return true;
}
}
return false;
}
// Description
// Fast check if an OBB and the camera-frustum overlap, using the separating-axis-theorem (SAT)
// The center of the OOBB is assumed to be in world-space.
// NOTE: even if the OBB is totally inside the frustum, this function returns CULL_OVERLAP
// For hierarchical frustum-culling this function is not perfect.
//
// Example:
// bool InOut=camera.IsOBBVisibleFast(obb);
//
// return values:
// CULL_EXCLUSION = OBB outside of frustum (very fast rejection-test)
// CULL_OVERLAP = OBB and frustum intersects or OBB in totally inside frustum
inline bool CCamera::IsOBBVisible_F(const Vec3& wpos, const OBB& obb) const
{
CRY_ASSERT(obb.m33.IsOrthonormalRH(0.001f));
//transform the obb-center into world-space
Vec3 p = obb.m33 * obb.c + wpos;
//extract the orientation-vectors from the columns of the 3x3 matrix
//and scale them by the half-lengths
Vec3 ax = obb.m33.GetColumn0() * obb.h.x;
Vec3 ay = obb.m33.GetColumn1() * obb.h.y;
Vec3 az = obb.m33.GetColumn2() * obb.h.z;
//we project the axes of the OBB onto the normal of each of the 6 planes.
//If the absolute value of the distance from the center of the OBB to the plane
//is larger then the "radius" of the OBB, then the OBB is outside the frustum.
f32 t;
if ((t = m_fp[0] | p) > 0.0f)
{
if (t > (fabsf(m_fp[0].n | ax) + fabsf(m_fp[0].n | ay) + fabsf(m_fp[0].n | az)))
{
return CULL_EXCLUSION;
}
}
if ((t = m_fp[1] | p) > 0.0f)
{
if (t > (fabsf(m_fp[1].n | ax) + fabsf(m_fp[1].n | ay) + fabsf(m_fp[1].n | az)))
{
return CULL_EXCLUSION;
}
}
if ((t = m_fp[2] | p) > 0.0f)
{
if (t > (fabsf(m_fp[2].n | ax) + fabsf(m_fp[2].n | ay) + fabsf(m_fp[2].n | az)))
{
return CULL_EXCLUSION;
}
}
if ((t = m_fp[3] | p) > 0.0f)
{
if (t > (fabsf(m_fp[3].n | ax) + fabsf(m_fp[3].n | ay) + fabsf(m_fp[3].n | az)))
{
return CULL_EXCLUSION;
}
}
if ((t = m_fp[4] | p) > 0.0f)
{
if (t > (fabsf(m_fp[4].n | ax) + fabsf(m_fp[4].n | ay) + fabsf(m_fp[4].n | az)))
{
return CULL_EXCLUSION;
}
}
if ((t = m_fp[5] | p) > 0.0f)
{
if (t > (fabsf(m_fp[5].n | ax) + fabsf(m_fp[5].n | ay) + fabsf(m_fp[5].n | az)))
{
return CULL_EXCLUSION;
}
}
//probably the OBB is visible!
//With this test we can't be sure if the OBB partially visible or totally included or
//totally outside the frustum but still intersecting one of the 6 planes (=worst case)
return CULL_OVERLAP;
}
ILINE uint8 CCamera::IsOBBVisible_FH([[maybe_unused]] const Vec3& wpos, [[maybe_unused]] const OBB& obb) const
{
//not implemented yet
return CULL_EXCLUSION;
}
// Description:
// This function checks if an OBB and the camera-frustum overlap.
// This test can reject even such OBBs that overlap a frustum-plane
// far outside the view-frustum.
// IMPORTANT: It is about 10% slower then "IsOBBVisibleFast(obb)"
//
// Example:
// int InOut=camera.IsOBBVisible_E(OBB);
//
// return values:
// CULL_EXCLUSION = OBB outside of frustum (very fast rejection-test)
// CULL_OVERLAP = OBB intersects the borders of the frustum or is totally inside
inline bool CCamera::IsOBBVisible_E(const Vec3& wpos, const OBB& obb, f32 uscale = 1.0f) const
{
assert(obb.m33.IsOrthonormalRH(0.001f));
//transform the obb-center into world-space
Vec3 p = obb.m33 * obb.c * uscale + wpos;
//extract the orientation-vectors from the columns of the 3x3 matrix
//and scale them by the half-lengths
Vec3 ax = obb.m33.GetColumn0() * obb.h.x * uscale;
Vec3 ay = obb.m33.GetColumn1() * obb.h.y * uscale;
Vec3 az = obb.m33.GetColumn2() * obb.h.z * uscale;
//we project the axes of the OBB onto the normal of each of the 6 planes.
//If the absolute value of the distance from the center of the OBB to the plane
//is larger then the "radius" of the OBB, then the OBB is outside the frustum.
f32 t0, t1, t2, t3, t4, t5;
bool mt0, mt1, mt2, mt3, mt4, mt5;
mt0 = (t0 = m_fp[0] | p) > 0.0f;
if (mt0)
{
if (t0 > (fabsf(m_fp[0].n | ax) + fabsf(m_fp[0].n | ay) + fabsf(m_fp[0].n | az)))
{
return CULL_EXCLUSION;
}
}
mt1 = (t1 = m_fp[1] | p) > 0.0f;
if (mt1)
{
if (t1 > (fabsf(m_fp[1].n | ax) + fabsf(m_fp[1].n | ay) + fabsf(m_fp[1].n | az)))
{
return CULL_EXCLUSION;
}
}
mt2 = (t2 = m_fp[2] | p) > 0.0f;
if (mt2)
{
if (t2 > (fabsf(m_fp[2].n | ax) + fabsf(m_fp[2].n | ay) + fabsf(m_fp[2].n | az)))
{
return CULL_EXCLUSION;
}
}
mt3 = (t3 = m_fp[3] | p) > 0.0f;
if (mt3)
{
if (t3 > (fabsf(m_fp[3].n | ax) + fabsf(m_fp[3].n | ay) + fabsf(m_fp[3].n | az)))
{
return CULL_EXCLUSION;
}
}
mt4 = (t4 = m_fp[4] | p) > 0.0f;
if (mt4)
{
if (t4 > (fabsf(m_fp[4].n | ax) + fabsf(m_fp[4].n | ay) + fabsf(m_fp[4].n | az)))
{
return CULL_EXCLUSION;
}
}
mt5 = (t5 = m_fp[5] | p) > 0.0f;
if (mt5)
{
if (t5 > (fabsf(m_fp[5].n | ax) + fabsf(m_fp[5].n | ay) + fabsf(m_fp[5].n | az)))
{
return CULL_EXCLUSION;
}
}
//if obb-center is in view-frustum, then stop further calculation
if (!(mt0 | mt1 | mt2 | mt3 | mt4 | mt5))
{
return CULL_OVERLAP;
}
return AdditionalCheck(wpos, obb, uscale);
}
// Description:
// Improved approach to check if an OBB and the camera-frustum intersect, or if the OBB
// is totally inside the camera-frustum. The bounding-box of the OBB is assumed to be
// in world-space. This test can reject even such OBBs that intersect a frustum-plane far
// outside the view-frustum
//
// Example:
// int InOut=camera.IsOBBVisible_EH(obb);
//
// return values:
// CULL_EXCLUSION = OBB outside of frustum (very fast rejection-test)
// CULL_OVERLAP = OBB intersects the borders of the frustum, further checks necessary
// CULL_INCLUSION = OBB is complete inside the frustum, no further checks necessary
inline uint8 CCamera::IsOBBVisible_EH(const Vec3& wpos, const OBB& obb, f32 uscale = 1.0f) const
{
assert(obb.m33.IsOrthonormalRH(0.001f));
//transform the obb-center into world-space
Vec3 p = obb.m33 * obb.c * uscale + wpos;
//extract the orientation-vectors from the columns of the 3x3 matrix
//and scale them by the half-lengths
Vec3 ax = obb.m33.GetColumn0() * obb.h.x * uscale;
Vec3 ay = obb.m33.GetColumn1() * obb.h.y * uscale;
Vec3 az = obb.m33.GetColumn2() * obb.h.z * uscale;
//we project the axes of the OBB onto the normal of each of the 6 planes.
//If the absolute value of the distance from the center of the OBB to the plane
//is larger then the "radius" of the OBB, then the OBB is outside the frustum.
f32 t0, t1, t2, t3, t4, t5;
bool mt0, mt1, mt2, mt3, mt4, mt5;
if (mt0 = (t0 = m_fp[0] | p) > 0.0f)
{
if (t0 > (fabsf(m_fp[0].n | ax) + fabsf(m_fp[0].n | ay) + fabsf(m_fp[0].n | az)))
{
return CULL_EXCLUSION;
}
}
if (mt1 = (t1 = m_fp[1] | p) > 0.0f)
{
if (t1 > (fabsf(m_fp[1].n | ax) + fabsf(m_fp[1].n | ay) + fabsf(m_fp[1].n | az)))
{
return CULL_EXCLUSION;
}
}
if (mt2 = (t2 = m_fp[2] | p) > 0.0f)
{
if (t2 > (fabsf(m_fp[2].n | ax) + fabsf(m_fp[2].n | ay) + fabsf(m_fp[2].n | az)))
{
return CULL_EXCLUSION;
}
}
if (mt3 = (t3 = m_fp[3] | p) > 0.0f)
{
if (t3 > (fabsf(m_fp[3].n | ax) + fabsf(m_fp[3].n | ay) + fabsf(m_fp[3].n | az)))
{
return CULL_EXCLUSION;
}
}
if (mt4 = (t4 = m_fp[4] | p) > 0.0f)
{
if (t4 > (fabsf(m_fp[4].n | ax) + fabsf(m_fp[4].n | ay) + fabsf(m_fp[4].n | az)))
{
return CULL_EXCLUSION;
}
}
if (mt5 = (t5 = m_fp[5] | p) > 0.0f)
{
if (t5 > (fabsf(m_fp[5].n | ax) + fabsf(m_fp[5].n | ay) + fabsf(m_fp[5].n | az)))
{
return CULL_EXCLUSION;
}
}
//check if obb-center is in view-frustum
if (!(mt0 | mt1 | mt2 | mt3 | mt4 | mt5))
{
//yes, it is!
//and now check if OBB is totally included
if (-t0 < (fabsf(m_fp[0].n | ax) + fabsf(m_fp[0].n | ay) + fabsf(m_fp[0].n | az)))
{
return CULL_OVERLAP;
}
if (-t1 < (fabsf(m_fp[1].n | ax) + fabsf(m_fp[1].n | ay) + fabsf(m_fp[1].n | az)))
{
return CULL_OVERLAP;
}
if (-t2 < (fabsf(m_fp[2].n | ax) + fabsf(m_fp[2].n | ay) + fabsf(m_fp[2].n | az)))
{
return CULL_OVERLAP;
}
if (-t3 < (fabsf(m_fp[3].n | ax) + fabsf(m_fp[3].n | ay) + fabsf(m_fp[3].n | az)))
{
return CULL_OVERLAP;
}
if (-t4 < (fabsf(m_fp[4].n | ax) + fabsf(m_fp[4].n | ay) + fabsf(m_fp[4].n | az)))
{
return CULL_OVERLAP;
}
if (-t5 < (fabsf(m_fp[5].n | ax) + fabsf(m_fp[5].n | ay) + fabsf(m_fp[5].n | az)))
{
return CULL_OVERLAP;
}
return CULL_INCLUSION;
}
return AdditionalCheck(wpos, obb, uscale);
}
//------------------------------------------------------------------------------
//--- ADDITIONAL-TEST ---
//------------------------------------------------------------------------------
extern _MS_ALIGN(64) uint32 BoxSides[];
// Description:
// A box can easily straddle one of the view-frustum planes far
// outside the view-frustum and in this case the previous test would
// return CULL_OVERLAP.
//
// Note: With this check, we make sure the AABB is really not visble
NO_INLINE_WEAK bool CCamera::AdditionalCheck(const AABB& aabb) const
{
Vec3d m = (aabb.min + aabb.max) * 0.5;
uint32 o = 1; //will be reset to 0 if center is outside
o &= isneg(m_fp[0] | m);
o &= isneg(m_fp[2] | m);
o &= isneg(m_fp[3] | m);
o &= isneg(m_fp[4] | m);
o &= isneg(m_fp[5] | m);
o &= isneg(m_fp[1] | m);
if (o)
{
return CULL_OVERLAP; //if obb-center is in view-frustum, then stop further calculation
}
Vec3d vmin(aabb.min - GetPosition()); //AABB in camera-space
Vec3d vmax(aabb.max - GetPosition()); //AABB in camera-space
uint32 frontx8 = 0; // make the flags using the fact that the upper bit in f32 is its sign
union f64_u
{
f64 floatVal;
int64 intVal;
};
f64_u uminx, uminy, uminz, umaxx, umaxy, umaxz;
uminx.floatVal = vmin.x;
uminy.floatVal = vmin.y;
uminz.floatVal = vmin.z;
umaxx.floatVal = vmax.x;
umaxy.floatVal = vmax.y;
umaxz.floatVal = vmax.z;
frontx8 |= (-uminx.intVal >> 0x3f) & 0x008; //if (AABB.min.x>0.0f) frontx8|=0x008;
frontx8 |= (umaxx.intVal >> 0x3f) & 0x010; //if (AABB.max.x<0.0f) frontx8|=0x010;
frontx8 |= (-uminy.intVal >> 0x3f) & 0x020; //if (AABB.min.y>0.0f) frontx8|=0x020;
frontx8 |= (umaxy.intVal >> 0x3f) & 0x040; //if (AABB.max.y<0.0f) frontx8|=0x040;
frontx8 |= (-uminz.intVal >> 0x3f) & 0x080; //if (AABB.min.z>0.0f) frontx8|=0x080;
frontx8 |= (umaxz.intVal >> 0x3f) & 0x100; //if (AABB.max.z<0.0f) frontx8|=0x100;
//check if camera is inside the aabb
if (frontx8 == 0)
{
return CULL_OVERLAP; //AABB is patially visible
}
Vec3d v[8] = {
Vec3d(vmin.x, vmin.y, vmin.z),
Vec3d(vmax.x, vmin.y, vmin.z),
Vec3d(vmin.x, vmax.y, vmin.z),
Vec3d(vmax.x, vmax.y, vmin.z),
Vec3d(vmin.x, vmin.y, vmax.z),
Vec3d(vmax.x, vmin.y, vmax.z),
Vec3d(vmin.x, vmax.y, vmax.z),
Vec3d(vmax.x, vmax.y, vmax.z)
};
//---------------------------------------------------------------------
//--- find the silhouette-vertices of the AABB ---
//---------------------------------------------------------------------
uint32 p0 = BoxSides[frontx8 + 0];
uint32 p1 = BoxSides[frontx8 + 1];
uint32 p2 = BoxSides[frontx8 + 2];
uint32 p3 = BoxSides[frontx8 + 3];
uint32 p4 = BoxSides[frontx8 + 4];
uint32 p5 = BoxSides[frontx8 + 5];
uint32 sideamount = BoxSides[frontx8 + 7];
if (sideamount == 4)
{
//--------------------------------------------------------------------------
//--- we take the 4 vertices of projection-plane in cam-space, ---
//----- and clip them against the 4 side-frustum-planes of the AABB -
//--------------------------------------------------------------------------
Vec3d s0 = v[p0] % v[p1];
if ((s0 | m_cltp) > 0 && (s0 | m_crtp) > 0 && (s0 | m_crbp) > 0 && (s0 | m_clbp) > 0)
{
return CULL_EXCLUSION;
}
Vec3d s1 = v[p1] % v[p2];
if ((s1 | m_cltp) > 0 && (s1 | m_crtp) > 0 && (s1 | m_crbp) > 0 && (s1 | m_clbp) > 0)
{
return CULL_EXCLUSION;
}
Vec3d s2 = v[p2] % v[p3];
if ((s2 | m_cltp) > 0 && (s2 | m_crtp) > 0 && (s2 | m_crbp) > 0 && (s2 | m_clbp) > 0)
{
return CULL_EXCLUSION;
}
Vec3d s3 = v[p3] % v[p0];
if ((s3 | m_cltp) > 0 && (s3 | m_crtp) > 0 && (s3 | m_crbp) > 0 && (s3 | m_clbp) > 0)
{
return CULL_EXCLUSION;
}
}
if (sideamount == 6)
{
//--------------------------------------------------------------------------
//--- we take the 4 vertices of projection-plane in cam-space, ---
//--- and clip them against the 6 side-frustum-planes of the AABB ---
//--------------------------------------------------------------------------
Vec3d s0 = v[p0] % v[p1];
if ((s0 | m_cltp) > 0 && (s0 | m_crtp) > 0 && (s0 | m_crbp) > 0 && (s0 | m_clbp) > 0)
{
return CULL_EXCLUSION;
}
Vec3d s1 = v[p1] % v[p2];
if ((s1 | m_cltp) > 0 && (s1 | m_crtp) > 0 && (s1 | m_crbp) > 0 && (s1 | m_clbp) > 0)
{
return CULL_EXCLUSION;
}
Vec3d s2 = v[p2] % v[p3];
if ((s2 | m_cltp) > 0 && (s2 | m_crtp) > 0 && (s2 | m_crbp) > 0 && (s2 | m_clbp) > 0)
{
return CULL_EXCLUSION;
}
Vec3d s3 = v[p3] % v[p4];
if ((s3 | m_cltp) > 0 && (s3 | m_crtp) > 0 && (s3 | m_crbp) > 0 && (s3 | m_clbp) > 0)
{
return CULL_EXCLUSION;
}
Vec3d s4 = v[p4] % v[p5];
if ((s4 | m_cltp) > 0 && (s4 | m_crtp) > 0 && (s4 | m_crbp) > 0 && (s4 | m_clbp) > 0)
{
return CULL_EXCLUSION;
}
Vec3d s5 = v[p5] % v[p0];
if ((s5 | m_cltp) > 0 && (s5 | m_crtp) > 0 && (s5 | m_crbp) > 0 && (s5 | m_clbp) > 0)
{
return CULL_EXCLUSION;
}
}
return CULL_OVERLAP; //AABB is patially visible
}
//------------------------------------------------------------------------------
//--- ADDITIONAL-TEST ---
//------------------------------------------------------------------------------
// Description:
// A box can easily straddle one of the view-frustum planes far
// outside the view-frustum and in this case the previous test would
// return CULL_OVERLAP.
// Note:
// With this check, we make sure the OBB is really not visible
NO_INLINE_WEAK bool CCamera::AdditionalCheck(const Vec3& wpos, const OBB& obb, f32 uscale) const
{
Vec3 CamInOBBSpace = wpos - GetPosition();
Vec3 iCamPos = -CamInOBBSpace * obb.m33;
uint32 front8 = 0;
AABB aabb = AABB((obb.c - obb.h) * uscale, (obb.c + obb.h) * uscale);
if (iCamPos.x < aabb.min.x)
{
front8 |= 0x008;
}
if (iCamPos.x > aabb.max.x)
{
front8 |= 0x010;
}
if (iCamPos.y < aabb.min.y)
{
front8 |= 0x020;
}
if (iCamPos.y > aabb.max.y)
{
front8 |= 0x040;
}
if (iCamPos.z < aabb.min.z)
{
front8 |= 0x080;
}
if (iCamPos.z > aabb.max.z)
{
front8 |= 0x100;
}
if (front8 == 0)
{
return CULL_OVERLAP;
}
//the transformed OBB-vertices in cam-space
Vec3 v[8] = {
obb.m33 * Vec3(aabb.min.x, aabb.min.y, aabb.min.z) + CamInOBBSpace,
obb.m33 * Vec3(aabb.max.x, aabb.min.y, aabb.min.z) + CamInOBBSpace,
obb.m33 * Vec3(aabb.min.x, aabb.max.y, aabb.min.z) + CamInOBBSpace,
obb.m33 * Vec3(aabb.max.x, aabb.max.y, aabb.min.z) + CamInOBBSpace,
obb.m33 * Vec3(aabb.min.x, aabb.min.y, aabb.max.z) + CamInOBBSpace,
obb.m33 * Vec3(aabb.max.x, aabb.min.y, aabb.max.z) + CamInOBBSpace,
obb.m33 * Vec3(aabb.min.x, aabb.max.y, aabb.max.z) + CamInOBBSpace,
obb.m33 * Vec3(aabb.max.x, aabb.max.y, aabb.max.z) + CamInOBBSpace
};
//---------------------------------------------------------------------
//--- find the silhouette-vertices of the OBB ---
//---------------------------------------------------------------------
uint32 p0 = BoxSides[front8 + 0];
uint32 p1 = BoxSides[front8 + 1];
uint32 p2 = BoxSides[front8 + 2];
uint32 p3 = BoxSides[front8 + 3];
uint32 p4 = BoxSides[front8 + 4];
uint32 p5 = BoxSides[front8 + 5];
uint32 sideamount = BoxSides[front8 + 7];
if (sideamount == 4)
{
//--------------------------------------------------------------------------
//--- we take the 4 vertices of projection-plane in cam-space, ---
//----- and clip them against the 4 side-frustum-planes of the OBB -
//--------------------------------------------------------------------------
Vec3 s0 = v[p0] % v[p1];
if (((s0 | m_cltp) >= 0) && ((s0 | m_crtp) >= 0) && ((s0 | m_crbp) >= 0) && ((s0 | m_clbp) >= 0))
{
return CULL_EXCLUSION;
}
Vec3 s1 = v[p1] % v[p2];
if (((s1 | m_cltp) >= 0) && ((s1 | m_crtp) >= 0) && ((s1 | m_crbp) >= 0) && ((s1 | m_clbp) >= 0))
{
return CULL_EXCLUSION;
}
Vec3 s2 = v[p2] % v[p3];
if (((s2 | m_cltp) >= 0) && ((s2 | m_crtp) >= 0) && ((s2 | m_crbp) >= 0) && ((s2 | m_clbp) >= 0))
{
return CULL_EXCLUSION;
}
Vec3 s3 = v[p3] % v[p0];
if (((s3 | m_cltp) >= 0) && ((s3 | m_crtp) >= 0) && ((s3 | m_crbp) >= 0) && ((s3 | m_clbp) >= 0))
{
return CULL_EXCLUSION;
}
}
if (sideamount == 6)
{
//--------------------------------------------------------------------------
//--- we take the 4 vertices of projection-plane in cam-space, ---
//--- and clip them against the 6 side-frustum-planes of the OBB ---
//--------------------------------------------------------------------------
Vec3 s0 = v[p0] % v[p1];
if (((s0 | m_cltp) >= 0) && ((s0 | m_crtp) >= 0) && ((s0 | m_crbp) >= 0) && ((s0 | m_clbp) >= 0))
{
return CULL_EXCLUSION;
}
Vec3 s1 = v[p1] % v[p2];
if (((s1 | m_cltp) >= 0) && ((s1 | m_crtp) >= 0) && ((s1 | m_crbp) >= 0) && ((s1 | m_clbp) >= 0))
{
return CULL_EXCLUSION;
}
Vec3 s2 = v[p2] % v[p3];
if (((s2 | m_cltp) >= 0) && ((s2 | m_crtp) >= 0) && ((s2 | m_crbp) >= 0) && ((s2 | m_clbp) >= 0))
{
return CULL_EXCLUSION;
}
Vec3 s3 = v[p3] % v[p4];
if (((s3 | m_cltp) >= 0) && ((s3 | m_crtp) >= 0) && ((s3 | m_crbp) >= 0) && ((s3 | m_clbp) >= 0))
{
return CULL_EXCLUSION;
}
Vec3 s4 = v[p4] % v[p5];
if (((s4 | m_cltp) >= 0) && ((s4 | m_crtp) >= 0) && ((s4 | m_crbp) >= 0) && ((s4 | m_clbp) >= 0))
{
return CULL_EXCLUSION;
}
Vec3 s5 = v[p5] % v[p0];
if (((s5 | m_cltp) >= 0) && ((s5 | m_crtp) >= 0) && ((s5 | m_crbp) >= 0) && ((s5 | m_clbp) >= 0))
{
return CULL_EXCLUSION;
}
}
//now we are 100% sure that the OBB is visible on the screen
return CULL_OVERLAP;
}
#endif // CRYINCLUDE_CRYCOMMON_CRY_CAMERA_H