////////////////////////////////////////////////////////////////////// // // Crytek Source code // Copyright (c) Crytek 2001-2004 // // File:Cry_Camera.h // Description: Common Camera class implementation // // History: // -Feb 08,2001: Created by Marco Corbetta // -Jan 20,2003: Taken over by Ivo Herzeg // ////////////////////////////////////////////////////////////////////// #ifndef CAMERA_H #define CAMERA_H #if _MSC_VER > 1000 # pragma once #endif //DOC-IGNORE-BEGIN #include "Cry_Math.h" #include "Cry_Geo.h" //DOC-IGNORE-END #ifdef WIN64 #include "Cry_XOptimise.h" // workaround for Amd64 compiler #endif ////////////////////////////////////////////////////////////////////// #define DEFAULT_ZMAX 1024.0f #define DEFAULT_ZMIN 0.25f #define DEFAULT_FOV gf_PI/2 ////////////////////////////////////////////////////////////////////// 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 }; #define YAW (0) #define PITCH (1) #define ROLL (2) //inline Matrix44 ViewMatrix(const Ang3 &angle); inline Matrix33 CryViewMatrixYPR(const Ang3 &angle); inline Matrix33 CryViewMatrix(const Ang3 &angle); inline Ang3 ConvertToRad( const Ang3& v ); /////////////////////////////////////////////////////////////////////////////// // * CCamera * // // Implements essential operations like calculation of a view-matrix and // frustum-culling with simple geometric primitives (Point, Sphere, AABB, COBB). // 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.

// //## z-axis //## ^ //## | //## | y-axis //## | / //## | / //## |/ //## +----------------> x-axis // // // This same system is also used in 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 // 90 about the X-Axis. This conversion will be moved into renderer.

// /////////////////////////////////////////////////////////////////////////////// class CCamera { private: Vec3 m_Position,m_OldPosition; Vec3 m_GameAnglesDeg; Vec3 m_CameraAnglesRad; float m_fov; Vec3 m_Scale; float m_ViewSurfaceX; //suface width-resolution float m_ViewSurfaceZ; //suface height-resolution float m_ProjectionRatio; //ratio between width and height of view-surface 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 float m_ZMax; //this is the left/upper vertex of the far-plane Plane m_frustum [FRUSTUM_PLANES]; // Plane m_fp[FRUSTUM_PLANES]; //this are the 6 planes view-frustum in world-space Vec3 cltp,crtp,clbp,crbp; //this are the 4 vertices of the projection-plane in cam-space Matrix34 m_VMat; //this is the "pure" view-matrix. view-vector (0,1,0) = identity Matrix44 m_VCMatD3D9; //concatenation of view- and conversion matrix Vec3 m_OccPosition; //Position for calculate occlusions (needed for portals rendering) public: // access to frustum vertices, // in order to make exact frustum test work it needs to be updated from outside // if frustum planes was updated from outside of the camera void SetFrustumVertices(Vec3d * arrvVerts) { clbp = arrvVerts[0]; cltp = arrvVerts[1]; crtp = arrvVerts[2]; crbp = arrvVerts[3]; } Vec3d GetFrustumVertex(int nId) const { switch(nId) { case 0: return clbp; case 1: return cltp; case 2: return crtp; case 3: return crbp; } assert(0); return Vec3d(0,0,0); } void SetFrustumVertex(int nId, const Vec3d & vVert) { switch(nId) { case 0: clbp = vVert; return; case 1: cltp = vVert; return; case 2: crtp = vVert; return; case 3: crbp = vVert; return; } assert(0); } Vec3 m_vOffset; struct IVisArea * m_pPortal; // pointer to portal used to create this camera struct ScissorInfo{ ScissorInfo() { x1=y1=x2=y2=0; } unsigned short x1,y1,x2,y2; }; ScissorInfo m_ScissorInfo; ScissorInfo m_ScissorInfoParent; public: //! constructor/destructor CCamera() { m_Position(0,0,0); m_OldPosition(0,0,0); m_GameAnglesDeg(0,0,0); m_CameraAnglesRad(0,0,0); m_fov=DEFAULT_FOV; m_Scale = Vec3(1.0f, 1.0f, 1.0f); m_ViewSurfaceX=640.0f; //suface width-resolution m_ViewSurfaceZ=480.0f; //suface height-resolution m_VMat.SetIdentity(); // m_CMat.SetIdentity33(); m_VCMatD3D9.SetIdentity(); m_edge_nlt.y = DEFAULT_ZMIN; //this is the left/upper vertex of the near-plane m_edge_flt.y = DEFAULT_ZMAX; //this is the left/upper vertex of the far-plane m_ZMax = DEFAULT_ZMAX; //this is the left/upper vertex of the far-plane m_ProjectionRatio = m_ViewSurfaceZ/m_ViewSurfaceX; for (int k=0;k0.01f); //check if near-plane is valid assert (fZMAX>0.01f); //check if far-plane is valid assert (fZMAX>fZMIN); //check if far-plane bigger then near-plane m_ViewSurfaceX =(float)(nWidth); //suface x-resolution m_ViewSurfaceZ =(float)(nHeight); //suface z-resolution m_fov = fFova; m_edge_nlt.y = fZMIN; m_edge_flt.y = fZMAX; m_ZMax = fZMAX; // projection ratio (1.0 for square pixels) m_ProjectionRatio = m_ViewSurfaceZ/m_ViewSurfaceX; if (fProjectionRatio) m_ProjectionRatio = fProjectionRatio; } /*! * * Updates all parameters required by the render-engine: * * 3d-view-frustum and all matrices * */ inline void CCamera::Update(int nWidth=-1,int nHeight=-1) { if (nWidth != -1) m_ViewSurfaceX=(float)(nWidth); //suface x-resolution if (nHeight != -1) m_ViewSurfaceZ=(float)(nHeight); //suface z-resolution assert (m_edge_nlt.y>0.009f); //check if near-plane is valid assert (m_ZMax>0.01f); //check if far-plane is valid assert (m_ZMax>m_edge_nlt.y); //check if far-plane bigger then near-plane Ang3 ca = ConvertToRad(m_GameAnglesDeg); Matrix34 t = Matrix34::CreateTranslationMat(-m_Position); Matrix33diag diag = m_Scale; Matrix33 v = CryViewMatrix(ca); //with this approach its impossible to do a roll m_VMat = v*diag*t; //concatenate the conversion-matrix with the view-matrix m_VCMatD3D9 = Matrix33::CreateRotationX( -gf_PI/2 ) * m_VMat; m_VCMatD3D9 = GetTransposed44(m_VCMatD3D9); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 //--------------------------------------------------------------------------------------------------- //calculate the Left/Top edge of the Projection plane (relative to camerapos (0,0,0) and not rotated) m_edge_plt.x =-m_ViewSurfaceX*0.5f; m_edge_plt.y = cry_cosf(m_fov*m_ProjectionRatio*0.5f) / cry_sinf(m_fov*m_ProjectionRatio*0.5f) * m_ViewSurfaceZ*0.50f; m_edge_plt.z = m_ViewSurfaceZ*0.5f; // m_edge_plt.x +=100; // m_edge_plt.z -=100; //calculate the left/upper edge of the near-plane (=not rotated) //the depth-value is set by Init(), thus all we need is x and z m_edge_nlt.x = (m_edge_nlt.y/m_edge_plt.y)*m_edge_plt.x; m_edge_nlt.z = (m_edge_nlt.y/m_edge_plt.y)*m_edge_plt.z; //calculate the left/upper edge of the far-clip-plane (=not rotated) //the depth-value is set by Init(), thus all we need is x and z m_edge_flt.x = (m_edge_flt.y/m_edge_plt.y)*m_edge_plt.x; m_edge_flt.z = (m_edge_flt.y/m_edge_plt.y)*m_edge_plt.z; //we get the real-frustum edges, if we multiply the not-rotated edges by the inverse VMat //note: the rotated frustum-eges are always in camera-space. Matrix34 iVMat=m_VMat.GetInverted(); Vec3 ltp,rtp,lbp,rbp; //this are the 4 vertices of the projection-plane in cam-space Vec3 ltn,rtn,lbn,rbn; //this are the 4 vertices of the near-plane in cam-space Vec3 ltf,rtf,lbf,rbf; //this are the 4 vertices of the far-clip-plane in cam-space //----------------------------------------------------- //--- calculate frustum-edges of projection-plane --- //----------------------------------------------------- ltp=iVMat*Vec3(+m_edge_plt.x,+m_edge_plt.y,+m_edge_plt.z); rtp=iVMat*Vec3(-m_edge_plt.x,+m_edge_plt.y,+m_edge_plt.z); lbp=iVMat*Vec3(+m_edge_plt.x,+m_edge_plt.y,-m_edge_plt.z); rbp=iVMat*Vec3(-m_edge_plt.x,+m_edge_plt.y,-m_edge_plt.z); //----------------------------------------------------- //--- calculate frustum-edges of projection-plane --- //----------------------------------------------------- cltp=Vec3(+m_edge_plt.x,+m_edge_plt.y,+m_edge_plt.z)*Matrix33(m_VMat); crtp=Vec3(-m_edge_plt.x,+m_edge_plt.y,+m_edge_plt.z)*Matrix33(m_VMat); clbp=Vec3(+m_edge_plt.x,+m_edge_plt.y,-m_edge_plt.z)*Matrix33(m_VMat); crbp=Vec3(-m_edge_plt.x,+m_edge_plt.y,-m_edge_plt.z)*Matrix33(m_VMat); //----------------------------------------------------- //--- calculate frustum-edges of near-plane --- //----------------------------------------------------- ltn=iVMat*Vec3(+m_edge_nlt.x,+m_edge_nlt.y,+m_edge_nlt.z); rtn=iVMat*Vec3(-m_edge_nlt.x,+m_edge_nlt.y,+m_edge_nlt.z); lbn=iVMat*Vec3(+m_edge_nlt.x,+m_edge_nlt.y,-m_edge_nlt.z); rbn=iVMat*Vec3(-m_edge_nlt.x,+m_edge_nlt.y,-m_edge_nlt.z); //----------------------------------------------------- //--- calculate frustum-edges of far-clip-plane --- //----------------------------------------------------- ltf=iVMat*Vec3(+m_edge_flt.x,+m_edge_flt.y,+m_edge_flt.z); rtf=iVMat*Vec3(-m_edge_flt.x,+m_edge_flt.y,+m_edge_flt.z); lbf=iVMat*Vec3(+m_edge_flt.x,+m_edge_flt.y,-m_edge_flt.z); rbf=iVMat*Vec3(-m_edge_flt.x,+m_edge_flt.y,-m_edge_flt.z); //------------------------------------------------------------------------- //--- calculate the six frustum-planes using the rotated fustum edges --- //------------------------------------------------------------------------- m_fp[FR_PLANE_NEAR ] = GetPlane( ltn,rtn,rbn ); m_fp[FR_PLANE_RIGHT ] = GetPlane( rtf,rbf, m_Position ); m_fp[FR_PLANE_LEFT ] = GetPlane( lbf,ltf, m_Position ); m_fp[FR_PLANE_TOP ] = GetPlane( ltf,rtf, m_Position ); m_fp[FR_PLANE_BOTTOM] = GetPlane( rbf,lbf, m_Position ); m_fp[FR_PLANE_FAR ] = GetPlane( rbf,rtf,ltf ); //clip-plane //maybe this makes a problem (but I dont think so.....) //here I'm just overwriting the old frustum calculation to fix the frustum bug if (1) { m_frustum[FR_PLANE_NEAR ] = m_fp[FR_PLANE_NEAR]; m_frustum[FR_PLANE_NEAR ].d *=-1; m_frustum[FR_PLANE_LEFT ] = m_fp[FR_PLANE_LEFT]; m_frustum[FR_PLANE_LEFT ].d *=-1; m_frustum[FR_PLANE_RIGHT ] = m_fp[FR_PLANE_RIGHT]; m_frustum[FR_PLANE_RIGHT ].d *=-1; m_frustum[FR_PLANE_TOP ] = m_fp[FR_PLANE_TOP]; m_frustum[FR_PLANE_TOP ].d *=-1; m_frustum[FR_PLANE_BOTTOM] = m_fp[FR_PLANE_BOTTOM]; m_frustum[FR_PLANE_BOTTOM].d *=-1; m_frustum[FR_PLANE_FAR ] = m_fp[FR_PLANE_FAR]; m_frustum[FR_PLANE_FAR ].d *=-1; } } /*! * Check if a point lies within camera's frustum * * Example: * unsigned char 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; } /*! * Conventional method to check if a sphere and the camera-frustum intersect * The center of the sphere is assumed to be in world-space. * * Example: * unsigned char InOut=camera.IsSphereVisibleFast(sphere); * * return values: * CULL_EXCLUSION = sphere outside of frustum (very fast rejection-test) * CULL_OVERLAP = sphere and frustum intersects or sphere in completely inside frustum */ inline bool CCamera::IsSphereVisibleFast( const Sphere &s ) const { float dist; Plane *frus_ptr=(Plane *)m_frustum; for (int i=0;iDistFromPlane(s.center); if (dist>s.radius) return (false); } return (true); } /*! * Conventional method to check if a sphere and the camera-frustum overlap * The center of the sphere is assumed to be in world-space. * NOTE: even if the sphere is totally inside the frustum, this function returns CULL_INTERSECT * For hirarchical frustum-culling this function is not perfect. * * Example: * unsigned char InOut=camera.IsSphereVisibleFast(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 */ /*__forceinline bool CCamera::IsSphereVisibleFast( const Sphere &s ) const { if ((m_fp[FR_PLANE_NEAR ]|s.center) > s.radius) return CULL_EXCLUSION; if ((m_fp[FR_PLANE_LEFT ]|s.center) > s.radius) return CULL_EXCLUSION; if ((m_fp[FR_PLANE_RIGHT ]|s.center) > s.radius) return CULL_EXCLUSION; if ((m_fp[FR_PLANE_TOP ]|s.center) > s.radius) return CULL_EXCLUSION; if ((m_fp[FR_PLANE_BOTTOM]|s.center) > s.radius) return CULL_EXCLUSION; if ((m_fp[FR_PLANE_FAR ]|s.center) > s.radius) return CULL_EXCLUSION; return CULL_OVERLAP; }*/ /*! * * conventional method to check if a sphere and the camera-frustum intersect, * or if the sphere is completely inside the camera-frustum. * The center of Sphere is assumed to be in world-space. * * Example: * unsigned char InOut=camera.IsSphereVisible_hierarchical(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 */ __forceinline char CCamera::IsSphereVisible_hierarchical( const Sphere &s, bool *bAllIn=0 ) const { float _nc,_rc,_lc,_tc,_bc,_cc; if (bAllIn) *bAllIn=false; if ((_nc=m_fp[FR_PLANE_NEAR ]|s.center)>s.radius) return CULL_EXCLUSION; if ((_rc=m_fp[FR_PLANE_RIGHT ]|s.center)>s.radius) return CULL_EXCLUSION; if ((_lc=m_fp[FR_PLANE_LEFT ]|s.center)>s.radius) return CULL_EXCLUSION; if ((_tc=m_fp[FR_PLANE_TOP ]|s.center)>s.radius) return CULL_EXCLUSION; if ((_bc=m_fp[FR_PLANE_BOTTOM]|s.center)>s.radius) return CULL_EXCLUSION; if ((_cc=m_fp[FR_PLANE_FAR ]|s.center)>s.radius) return CULL_EXCLUSION; //now we have to check if it is completely in frustum bool nc = (_nc<=(-s.radius)); bool lc = (_lc<=(-s.radius)); bool rc = (_rc<=(-s.radius)); bool tc = (_tc<=(-s.radius)); bool bc = (_bc<=(-s.radius)); bool cc = (_cc<=(-s.radius)); if (tc&lc&rc&bc&nc&cc) { if (bAllIn) *bAllIn=true; return CULL_INCLUSION; } return CULL_OVERLAP; } extern char BoxSides[0x40*8]; /*! * Very fast approach to check if an AABB and the camera-frustum overlap, or if the AABB * is totally inside the camera-frustum. The bounding-box of 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 usefull if you really need hierachical-culling. * It is about 30% slower then "IsAABBVisibleFast(aabb)" * * Example: * int InOut=camera.IsAABBVisible_hierarchical(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 */ __forceinline bool CCamera::IsAABBVisibleFast( const AABB& aabb ) const { float d; const Vec3* pAABB=&aabb.min; for (int i=0;i>31)].x; d += m_frustum[i].n.y * pAABB[(*((uint32*)&m_frustum[i].n.y)>>31)].y; d += m_frustum[i].n.z * pAABB[(*((uint32*)&m_frustum[i].n.z)>>31)].z; if (d>0) return CULL_EXCLUSION; */ d=-m_frustum[i].d; if (m_frustum[i].n.x>=0) d+=m_frustum[i].n.x*aabb.min.x; else d+=m_frustum[i].n.x*aabb.max.x; if (m_frustum[i].n.y>=0) d+=m_frustum[i].n.y*aabb.min.y; else d+=m_frustum[i].n.y*aabb.max.y; if (m_frustum[i].n.z>=0) d+=m_frustum[i].n.z*aabb.min.z; else d+=m_frustum[i].n.z*aabb.max.z; if (d>0) return CULL_EXCLUSION; } return CULL_OVERLAP; } /*! * This function checks if an AABB and the camera-frustum overlap. * The bounding-box of 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: It is about 10% slower then "IsAABBVisibleFast(aabb)" * * Example: * int InOut=camera.IsAABBVisible_exact(aabb); * * return values: * CULL_EXCLUSION = AABB outside of frustum (very fast rejection-test) * CULL_OVERLAP = AABB intersects the borders of the frustum or is totally inside */ inline bool CCamera::IsAABBVisible_exact(const AABB& aabb) const { float d; const Vec3* pAABB=&aabb.min; //------------------------------------------------------------------------------ //--- loop over all 6 frustum-planes and do an early rejection the AABB is --- //--- completely on the positive side of any one of the 6 planes --- //------------------------------------------------------------------------------ for (int i=0; i>31)].x; d += m_frustum[i].n.y * pAABB[(*((uint32*)&m_frustum[i].n.y)>>31)].y; d += m_frustum[i].n.z * pAABB[(*((uint32*)&m_frustum[i].n.z)>>31)].z; if (d>0) return CULL_EXCLUSION; */ d=-m_frustum[i].d; if (m_frustum[i].n.x>=0) d+=m_frustum[i].n.x*aabb.min.x; else d+=m_frustum[i].n.x*aabb.max.x; if (m_frustum[i].n.y>=0) d+=m_frustum[i].n.y*aabb.min.y; else d+=m_frustum[i].n.y*aabb.max.y; if (m_frustum[i].n.z>=0) d+=m_frustum[i].n.z*aabb.min.z; else d+=m_frustum[i].n.z*aabb.max.z; if (d>0) return CULL_EXCLUSION; } Vec3 p = (aabb.min+aabb.max)*0.5f; if ( (m_frustum[FR_PLANE_NEAR ].DistFromPlane(p) ) < 0) { if ( (m_frustum[FR_PLANE_RIGHT ].DistFromPlane(p) ) < 0) { if ( (m_frustum[FR_PLANE_LEFT ].DistFromPlane(p) ) < 0) { if ( (m_frustum[FR_PLANE_TOP ].DistFromPlane(p) ) < 0) { if ( (m_frustum[FR_PLANE_BOTTOM].DistFromPlane(p) ) < 0) { if ( (m_frustum[FR_PLANE_FAR ].DistFromPlane(p) ) < 0) { return CULL_OVERLAP; //AABB is patially visible } } } } } } //------------------------------------------------------------------------------ //--- ADDITIONAL-TEST --- //--- 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 --- //------------------------------------------------------------------------------ //----- With this check, we make sure the AABB is really not visble ------ //------------------------------------------------------------------------------ AABB caabb(aabb.min-m_Position,aabb.max-m_Position); //caabb in camera-space unsigned long front=0; if (caabb.min.x>0.0f) front|=0x01; if (caabb.max.x<0.0f) front|=0x02; if (caabb.min.y>0.0f) front|=0x04; if (caabb.max.y<0.0f) front|=0x08; if (caabb.min.z>0.0f) front|=0x10; if (caabb.max.z<0.0f) front|=0x20; //check if camera is inside the aabb if (front==0) return CULL_OVERLAP; //AABB is patially visible Vec3 v[8]; Vec3 s0,s1,s2,s3,s4,s5; //caabb-side-normals (....but not normalized) v[0] = Vec3(caabb.min.x,caabb.min.y,caabb.min.z); v[1] = Vec3(caabb.max.x,caabb.min.y,caabb.min.z); v[2] = Vec3(caabb.min.x,caabb.max.y,caabb.min.z); v[3] = Vec3(caabb.max.x,caabb.max.y,caabb.min.z); v[4] = Vec3(caabb.min.x,caabb.min.y,caabb.max.z); v[5] = Vec3(caabb.max.x,caabb.min.y,caabb.max.z); v[6] = Vec3(caabb.min.x,caabb.max.y,caabb.max.z); v[7] = Vec3(caabb.max.x,caabb.max.y,caabb.max.z); //--------------------------------------------------------------------- //--- find the silhouette-vertices of the AABB --- //--------------------------------------------------------------------- unsigned long p0,p1,p2,p3,p4,p5,sideamount; p0 = BoxSides[(front<<3)+0]; p1 = BoxSides[(front<<3)+1]; p2 = BoxSides[(front<<3)+2]; p3 = BoxSides[(front<<3)+3]; p4 = BoxSides[(front<<3)+4]; p5 = BoxSides[(front<<3)+5]; sideamount=BoxSides[(front<<3)+7]; if(sideamount==4) { //-------------------------------------------------------------------------- //--- calculate the 4 "planes" for the AABB --- //-------------------------------------------------------------------------- s0 = v[p0] % v[p1]; s1 = v[p1] % v[p2]; s2 = v[p2] % v[p3]; s3 = v[p3] % v[p0]; //-------------------------------------------------------------------------- //--- we take the 4 vertices of projection-plane in cam-space, --- //----- and clip them against the 4 side-frustum-planes of the AABB - //-------------------------------------------------------------------------- if (!( ((s0|cltp)<=0.0f)|((s0|crtp)<=0.0f)|((s0|crbp)<=0.0f)|((s0|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s1|cltp)<=0.0f)|((s1|crtp)<=0.0f)|((s1|crbp)<=0.0f)|((s1|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s2|cltp)<=0.0f)|((s2|crtp)<=0.0f)|((s2|crbp)<=0.0f)|((s2|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s3|cltp)<=0.0f)|((s3|crtp)<=0.0f)|((s3|crbp)<=0.0f)|((s3|clbp)<=0.0f) )) return CULL_EXCLUSION; } if(sideamount==6) { //-------------------------------------------------------------------------- //--- calculate the 6 "planes" for the AABB --- //-------------------------------------------------------------------------- s0=v[p0]%v[p1]; s1=v[p1]%v[p2]; s2=v[p2]%v[p3]; s3=v[p3]%v[p4]; s4=v[p4]%v[p5]; s5=v[p5]%v[p0]; //-------------------------------------------------------------------------- //--- we take the 4 vertices of projection-plane in cam-space, --- //--- and clip them against the 6 side-frustum-planes of the AABB --- //-------------------------------------------------------------------------- if (!( ((s0|cltp)<=0.0f)|((s0|crtp)<=0.0f)|((s0|crbp)<=0.0f)|((s0|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s1|cltp)<=0.0f)|((s1|crtp)<=0.0f)|((s1|crbp)<=0.0f)|((s1|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s2|cltp)<=0.0f)|((s2|crtp)<=0.0f)|((s2|crbp)<=0.0f)|((s2|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s3|cltp)<=0.0f)|((s3|crtp)<=0.0f)|((s3|crbp)<=0.0f)|((s3|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s4|cltp)<=0.0f)|((s4|crtp)<=0.0f)|((s4|crbp)<=0.0f)|((s4|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s5|cltp)<=0.0f)|((s5|crtp)<=0.0f)|((s5|crbp)<=0.0f)|((s5|clbp)<=0.0f) )) return CULL_EXCLUSION; } return CULL_OVERLAP; //AABB is patially visible } /*! * Improved approach to check if an AABB and the camera-frustum overlap, or if the AABB * is totally inside the camera-frustum. The bounding-box of 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 usefull if you really need hierarchical-culling. * It is about 30% slower then "IsAABBVisibleFast(aabb)" * * Example: * int InOut=camera.IsAABBVisible_hierarchical(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 char CCamera::IsAABBVisible_hierarchical(const AABB& aabb, bool *bAllIn=0 ) const { /* float dot1,dot2; unsigned long notOverlap = 0x80000000; // will be reset to 0 if there's at least one overlapping const Vec3* pAABB=&aabb.min; if (bAllIn) *bAllIn=false; //------------------------------------------------------------------------------ //--- loop over all 6 frustum-planes and do an early rejection the AABB is --- //--- completely on the positive side of any one of the 6 planes --- //------------------------------------------------------------------------------ for (int i=0;i>31)].x; dot2 += m_frustum[i].n.x * pAABB[1-(*((uint32*)&m_frustum[i].n.x)>>31)].x; dot1 += m_frustum[i].n.y * pAABB[0+(*((uint32*)&m_frustum[i].n.y)>>31)].y; dot2 += m_frustum[i].n.y * pAABB[1-(*((uint32*)&m_frustum[i].n.y)>>31)].y; dot1 += m_frustum[i].n.z * pAABB[0+(*((uint32*)&m_frustum[i].n.z)>>31)].z; dot2 += m_frustum[i].n.z * pAABB[1-(*((uint32*)&m_frustum[i].n.z)>>31)].z; if ( !(((int32&)dot1)&0x80000000) ) return CULL_EXCLUSION; notOverlap &= (int32&)dot2; } if (notOverlap) { if (bAllIn) *bAllIn=true; return CULL_INCLUSION; }*/ float dot1,dot2; unsigned long overlap=false; if (bAllIn) *bAllIn=false; //------------------------------------------------------------------------------ //--- loop over all 6 frustum-planes and do an early rejection the AABB is --- //--- completely on the positive side of any one of the 6 planes --- //------------------------------------------------------------------------------ for (int i=0;i=0) { dot1+=m_frustum[i].n.x*aabb.min.x; dot2+=m_frustum[i].n.x*aabb.max.x; } else { dot1+=m_frustum[i].n.x*aabb.max.x; dot2+=m_frustum[i].n.x*aabb.min.x; } if (m_frustum[i].n.y>=0) { dot1+=m_frustum[i].n.y*aabb.min.y; dot2+=m_frustum[i].n.y*aabb.max.y; } else { dot1+=m_frustum[i].n.y*aabb.max.y; dot2+=m_frustum[i].n.y*aabb.min.y; } if (m_frustum[i].n.z>=0) { dot1+=m_frustum[i].n.z*aabb.min.z; dot2+=m_frustum[i].n.z*aabb.max.z; } else { dot1+=m_frustum[i].n.z*aabb.max.z; dot2+=m_frustum[i].n.z*aabb.min.z; } if (dot1>0) return CULL_EXCLUSION; if (dot2>0) overlap=true; } if (!overlap) { if (bAllIn) *bAllIn=true; return CULL_INCLUSION; } //------------------------------------------------------------------------------ //--- ADDITIONAL-TEST --- //--- 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 --- //------------------------------------------------------------------------------ //----- With this check, we make sure the AABB is really not visble ------ //------------------------------------------------------------------------------ AABB AABB(aabb.min-m_Position,aabb.max-m_Position); //AABB in camera-space unsigned long frontx8=0; // make the flags using the fact that the upper bit in float is its sign frontx8 |= (unsigned(-(int&)AABB.min.x)>>5)&(0x01<<26); //if (AABB.min.x>0.0f) frontx8|=0x01; frontx8 |= (unsigned( (int&)AABB.max.x)>>4)&(0x02<<26); //if (AABB.max.x<0.0f) frontx8|=0x02; frontx8 |= (unsigned(-(int&)AABB.min.y)>>3)&(0x04<<26); //if (AABB.min.y>0.0f) frontx8|=0x04; frontx8 |= (unsigned( (int&)AABB.max.y)>>2)&(0x08<<26); //if (AABB.max.y<0.0f) frontx8|=0x08; frontx8 |= (unsigned(-(int&)AABB.min.z)>>1)&(0x10<<26); // if (AABB.min.z>0.0f) frontx8|=0x10; frontx8 |= (unsigned( (int&)AABB.max.z))&(0x20<<26); // if (AABB.max.z<0.0f) frontx8|=0x20; //frontx8 |= (frontx8 + frontx8) ^ 0x2A; frontx8 = (frontx8>>23) & (0x3F << 3); //check if camera is inside the aabb if (frontx8==0) return CULL_OVERLAP; //AABB is patially visible Vec3 v[8]; Vec3 s0,s1,s2,s3,s4,s5; //AABB-side-normals (....but not normalized) v[0] = Vec3(AABB.min.x,AABB.min.y,AABB.min.z); v[1] = Vec3(AABB.max.x,AABB.min.y,AABB.min.z); v[2] = Vec3(AABB.min.x,AABB.max.y,AABB.min.z); v[3] = Vec3(AABB.max.x,AABB.max.y,AABB.min.z); v[4] = Vec3(AABB.min.x,AABB.min.y,AABB.max.z); v[5] = Vec3(AABB.max.x,AABB.min.y,AABB.max.z); v[6] = Vec3(AABB.min.x,AABB.max.y,AABB.max.z); v[7] = Vec3(AABB.max.x,AABB.max.y,AABB.max.z); //--------------------------------------------------------------------- //--- find the silhouette-vertices of the AABB --- //--------------------------------------------------------------------- unsigned long p0,p1,p2,p3,p4,p5,sideamount; p0 = BoxSides[frontx8+0]; p1 = BoxSides[frontx8+1]; p2 = BoxSides[frontx8+2]; p3 = BoxSides[frontx8+3]; p4 = BoxSides[frontx8+4]; p5 = BoxSides[frontx8+5]; sideamount=BoxSides[frontx8+7]; if(sideamount==4) { //-------------------------------------------------------------------------- //--- calculate the 4 "planes" for the AABB --- //-------------------------------------------------------------------------- s0 = v[p0] % v[p1]; s1 = v[p1] % v[p2]; s2 = v[p2] % v[p3]; s3 = v[p3] % v[p0]; //-------------------------------------------------------------------------- //--- we take the 4 vertices of projection-plane in cam-space, --- //----- and clip them against the 4 side-frustum-planes of the AABB - //-------------------------------------------------------------------------- if (!( ((s0|cltp)<=0.0f)|((s0|crtp)<=0.0f)|((s0|crbp)<=0.0f)|((s0|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s1|cltp)<=0.0f)|((s1|crtp)<=0.0f)|((s1|crbp)<=0.0f)|((s1|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s2|cltp)<=0.0f)|((s2|crtp)<=0.0f)|((s2|crbp)<=0.0f)|((s2|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s3|cltp)<=0.0f)|((s3|crtp)<=0.0f)|((s3|crbp)<=0.0f)|((s3|clbp)<=0.0f) )) return CULL_EXCLUSION; } if(sideamount==6) { //-------------------------------------------------------------------------- //--- calculate the 6 "planes" for the AABB --- //-------------------------------------------------------------------------- s0=v[p0]%v[p1]; s1=v[p1]%v[p2]; s2=v[p2]%v[p3]; s3=v[p3]%v[p4]; s4=v[p4]%v[p5]; s5=v[p5]%v[p0]; //-------------------------------------------------------------------------- //--- we take the 4 vertices of projection-plane in cam-space, --- //--- and clip them against the 6 side-frustum-planes of the AABB --- //-------------------------------------------------------------------------- if (!( ((s0|cltp)<=0.0f)|((s0|crtp)<=0.0f)|((s0|crbp)<=0.0f)|((s0|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s1|cltp)<=0.0f)|((s1|crtp)<=0.0f)|((s1|crbp)<=0.0f)|((s1|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s2|cltp)<=0.0f)|((s2|crtp)<=0.0f)|((s2|crbp)<=0.0f)|((s2|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s3|cltp)<=0.0f)|((s3|crtp)<=0.0f)|((s3|crbp)<=0.0f)|((s3|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s4|cltp)<=0.0f)|((s4|crtp)<=0.0f)|((s4|crbp)<=0.0f)|((s4|clbp)<=0.0f) )) return CULL_EXCLUSION; if (!( ((s5|cltp)<=0.0f)|((s5|crtp)<=0.0f)|((s5|crbp)<=0.0f)|((s5|clbp)<=0.0f) )) return CULL_EXCLUSION; } return CULL_OVERLAP; //AABB is patially visible } //////////////////////////////////////////////////////////////// //! convert from degrees to radians and adjust the coordinate system //////////////////////////////////////////////////////////////// inline Ang3 ConvertToRad( const Ang3& v ){ Ang3 angles; angles.x=DEG2RAD( v.z+180.0f); angles.y=DEG2RAD(-v.x+90.0f); angles.z=DEG2RAD( v.y); return angles; } //////////////////////////////////////////////////////////////// //! convert a view angle from degrees to a normalized view-vector //////////////////////////////////////////////////////////////// inline Vec3 ConvertToRadAngles( const Ang3& v ) { Vec3 vec=ConvertToRad(v); Vec3 dir; dir.x=-sin_tpl(vec.y)*sin_tpl(vec.x); dir.y= sin_tpl(vec.y)*cos_tpl(vec.x); dir.z=-cos_tpl(vec.y); return dir; } inline Matrix44 ViewMatrix(const Ang3 &angle) { Matrix33 ViewMatZ=Matrix33::CreateRotationZ(-angle.x); Matrix33 ViewMatX=Matrix33::CreateRotationX(-angle.y); Matrix33 ViewMatY=Matrix33::CreateRotationY(+angle.z); return GetTransposed44( ViewMatX*ViewMatY*ViewMatZ); } //ZXY inline Matrix33 CryViewMatrix(const Ang3 &angle) { Matrix33 ViewMatZ=Matrix33::CreateRotationZ(-angle.x); Matrix33 ViewMatX=Matrix33::CreateRotationX(-angle.y); Matrix33 ViewMatY=Matrix33::CreateRotationY(+angle.z); return Matrix33::CreateRotationX(gf_PI*0.5f)*ViewMatX*ViewMatY*ViewMatZ; } /*! convert a lenght unit vector to camera's angles: x camera axis is looking up/down y is roll z is left/right */ inline Vec3 ConvertUnitVectorToCameraAngles(const Vec3& vec) { Vec3 v=vec; float fForward; float fYaw,fPitch; if (v.y==0 && v.x==0) { //looking up/down fYaw=0; if (v.z>0) fPitch=90; else fPitch=270; } else { if (v.x!=0) { fYaw=(float)(cry_atan2f((float)(v.y),(float)(v.x))*180.0f/gf_PI); } else //lokking left/right if (v.y>0) fYaw=90; else fYaw=270; if (fYaw<0) fYaw+=360; fForward=(float)cry_sqrtf(v.x*v.x+v.y*v.y); fPitch=(float)(cry_atan2f(v.z,fForward)*180.0f/gf_PI); if (fPitch<0) fPitch+=360; } //y = -fPitch; //x = fYaw; //z = 0; v.x=-fPitch; v.y=0; v.z=fYaw+90; //clamp again if (v.x>360) v.x-=360;else if (v.x<-360) v.x+=360; if (v.z>360) v.z-=360;else if (v.z<-360) v.z+=360; return v; } /*! convert a vector to camera's angles: x camera axis is looking up/down y is roll z is left/right */ inline Vec3 ConvertVectorToCameraAngles(const Vec3& v) { return ConvertUnitVectorToCameraAngles( GetNormalized(v) ); } /*! convert a vector to camera's angles: x camera axis is looking up/down y is roll z is left/right */ inline Ang3 ConvertVectorToCameraAnglesSnap180(const Vec3& vec) { Ang3 ang=ConvertUnitVectorToCameraAngles( GetNormalized(vec) ); ang.Snap180(); return ang; } #endif