// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2021 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "SampleCamera.h" #include "SampleUtils.h" #include "RenderPhysX3Debug.h" #include "RendererColor.h" using namespace SampleRenderer; // PT: the base camera code should be the same for all cameras, regardless of how // the camera is controlled. For example this should deal with VFC, etc. Camera::Camera() : mProjMatrix (degtorad(45.0f), 1.0f, 1.0f, 100.0f), mFOV (0.0f), mNearPlane (0.0f), mFarPlane (0.0f), mDirtyProj (true), mDirtyView (true) { mViewMatrix = PxTransform(PxIdentity); mPos = PxVec3(0); mRot = PxVec3(0); mDrawDebugData = false; mFreezeFrustum = false; mPerformVFC = true; } Camera::~Camera() { } // PT: TODO: copied from SampleApplication. Refactor. static PxMat33 EulerToMat33(const PxVec3 &e) { float c1 = cosf(e.z); float s1 = sinf(e.z); float c2 = cosf(e.y); float s2 = sinf(e.y); float c3 = cosf(e.x); float s3 = sinf(e.x); PxMat33 m(PxVec3(c1*c2, -s1*c2, s2), PxVec3((s1*c3)+(c1*s2*s3), (c1*c3)-(s1*s2*s3),-c2*s3), PxVec3((s1*s3)-(c1*s2*c3), (c1*s3)+(s1*s2*c3), c2*c3)); return m; } void Camera::updateInternals() { if(mDirtyProj) { mDirtyProj = false; mProjMatrix = RendererProjection(degtorad(mFOV), mViewport.computeRatio(), mNearPlane, mFarPlane); } if(mDirtyView) { mDirtyView = false; mViewMatrix.q = PxQuat(EulerToMat33(mRot)); mViewMatrix.p = mPos; } } PxVec3 Camera::getViewDir() const { const PxTransform& camPose = getViewMatrix(); PxVec3 forward = PxMat33(camPose.q)[2]; return -forward; } void Camera::lookAt(const PxVec3& position, const PxVec3& target) { PxVec3 dir, right, up; Ps::computeBasis(position, target, dir, right, up); PxTransform view; view.p = position; view.q = PxQuat(PxMat33(-right, up, -dir)); setView(view); } enum FrustumPlaneIndex { FRUSTUM_PLANE_LEFT = 0, //!< Left clipping plane FRUSTUM_PLANE_RIGHT = 1, //!< Right clipping plane FRUSTUM_PLANE_TOP = 2, //!< Top clipping plane FRUSTUM_PLANE_BOTTOM = 3, //!< Bottom clipping plane FRUSTUM_PLANE_NEAR = 4, //!< Near clipping plane FRUSTUM_PLANE_FAR = 5, //!< Far clipping plane (must be last for infinite far clip) FRUSTUM_PLANE_FORCE_DWORD = 0x7fffffff }; static PxMat44 convertViewMatrix(const PxTransform& eye) { PxTransform viewMatrix = eye.getInverse(); PxMat44 mat44 = PxMat44(viewMatrix).getTranspose(); float m[16]; memcpy(m, mat44.front(), sizeof m); PxMat44 view44; view44.column0.x = m[0]; view44.column0.y = m[1]; view44.column0.z = m[2]; view44.column0.w = m[3]; view44.column1.x = m[4]; view44.column1.y = m[5]; view44.column1.z = m[6]; view44.column1.w = m[7]; view44.column2.x = m[8]; view44.column2.y = m[9]; view44.column2.z = m[10]; view44.column2.w = m[11]; view44.column3.x = m[12]; view44.column3.y = m[13]; view44.column3.z = m[14]; view44.column3.w = m[15]; PxMat44 tmpmat = view44.getTranspose(); view44 = tmpmat; return view44; } static PxMat44 convertProjMatrix(const RendererProjection& proj) { float renderProjMatrix[16]; proj.getColumnMajor44(renderProjMatrix); PxMat44 proj44; proj44.column0.x = renderProjMatrix[0]; proj44.column0.y = renderProjMatrix[1]; proj44.column0.z = renderProjMatrix[2]; proj44.column0.w = renderProjMatrix[3]; proj44.column1.x = renderProjMatrix[4]; proj44.column1.y = renderProjMatrix[5]; proj44.column1.z = renderProjMatrix[6]; proj44.column1.w = renderProjMatrix[7]; proj44.column2.x = renderProjMatrix[8]; proj44.column2.y = renderProjMatrix[9]; proj44.column2.z = renderProjMatrix[10]; proj44.column2.w = renderProjMatrix[11]; proj44.column3.x = renderProjMatrix[12]; proj44.column3.y = renderProjMatrix[13]; proj44.column3.z = renderProjMatrix[14]; proj44.column3.w = renderProjMatrix[15]; //PxMat44 tmpmat = proj44.getTranspose(); proj44 = tmpmat; return proj44; } void Camera::BuildFrustum() { if(mFreezeFrustum) return; // PT: a better way is to extract the planes from the view-proj matrix but it has some subtle differences with D3D/GL. // Building the frustum explicitly is just easier here (although not as efficient) const PxReal ratio = mViewport.computeRatio(); const PxReal Tan = tanf(degtorad(0.5f * mFOV)) / ratio; const PxReal nearCoeff = mNearPlane * Tan; const PxReal farCoeff = mFarPlane * Tan; const PxReal rightCoeff = ratio; const PxReal upCoeff = 1.0f; const PxTransform& view = getViewMatrix(); PxMat33 mat33(view.q); PxVec3 right = mat33[0]; PxVec3 up = mat33[1]; PxVec3 forward =-mat33[2]; mFrustum[0] = mPos + forward*mNearPlane - right*nearCoeff*rightCoeff + up*nearCoeff*upCoeff; mFrustum[1] = mPos + forward*mNearPlane - right*nearCoeff*rightCoeff - up*nearCoeff*upCoeff; mFrustum[2] = mPos + forward*mNearPlane + right*nearCoeff*rightCoeff - up*nearCoeff*upCoeff; mFrustum[3] = mPos + forward*mNearPlane + right*nearCoeff*rightCoeff + up*nearCoeff*upCoeff; mFrustum[4] = mPos + forward*mFarPlane - right*farCoeff*rightCoeff + up*farCoeff*upCoeff; mFrustum[5] = mPos + forward*mFarPlane - right*farCoeff*rightCoeff - up*farCoeff*upCoeff; mFrustum[6] = mPos + forward*mFarPlane + right*farCoeff*rightCoeff - up*farCoeff*upCoeff; mFrustum[7] = mPos + forward*mFarPlane + right*farCoeff*rightCoeff + up*farCoeff*upCoeff; if(1) { mPlanes[0] = PxPlane(mFrustum[4], mFrustum[1], mFrustum[5]); mPlanes[1] = PxPlane(mFrustum[6], mFrustum[3], mFrustum[7]); mPlanes[2] = PxPlane(mFrustum[4], mFrustum[7], mFrustum[3]); mPlanes[3] = PxPlane(mFrustum[1], mFrustum[6], mFrustum[5]); mPlanes[4] = PxPlane(mFrustum[0], mFrustum[2], mFrustum[1]); mPlanes[5] = PxPlane(mFrustum[5], mFrustum[7], mFrustum[4]); { for(int i=0;i<6;i++) { mPlanes[i].n = -mPlanes[i].n; mPlanes[i].d = -mPlanes[i].d; } } } if(0) { // const PxVec3 axisX(1.0f, 0.0f, 0.0f); const PxVec3 axisY(0.0f, 1.0f, 0.0f); const PxVec3 axisZ(0.0f, 0.0f, 1.0f); PxQuat RotX(degtorad(0.5f * mFOV), axisX); PxQuat RotY(degtorad(0.5f * mFOV), axisY); PxQuat RotZ(degtorad(0.5f * mFOV), axisZ); PxVec3 tmp1 = RotY.rotate(-axisX); PxVec3 tmp11 = view.q.rotate(tmp1); // Plane0 mPlanes[0].n = tmp11; mPlanes[0].d = - mPos.dot(mPlanes[0].n); // RotY = PxQuat(-degtorad(0.5f * mFOV), axisY); PxVec3 tmpy = RotY.rotate(axisX); PxVec3 tmpyy = view.q.rotate(tmpy); // Plane1 mPlanes[1].n = tmpyy; mPlanes[1].d = - mPos.dot(mPlanes[1].n); // RotX = PxQuat(degtorad(0.5f * mFOV)/ratio, axisX); PxVec3 tmpx = RotX.rotate(axisY); PxVec3 tmpxx = view.q.rotate(tmpx); // Plane2? mPlanes[2].n = tmpxx; mPlanes[2].d = - mPos.dot(mPlanes[2].n); // RotX = PxQuat(-degtorad(0.5f * mFOV)/ratio, axisX); tmpx = RotX.rotate(axisY); tmpxx = view.q.rotate(tmpx); // -Plane3? mPlanes[3].n = -tmpxx; mPlanes[3].d = - mPos.dot(mPlanes[3].n); // mPlanes[4].n = -forward; mPlanes[4].d = - (mPos.dot(mPlanes[4].n) + forward.dot(mPlanes[4].n)*mNearPlane); mPlanes[5].n = forward; mPlanes[5].d = - (mPos.dot(mPlanes[5].n) + forward.dot(mPlanes[5].n)*mFarPlane); } if(0) { PxMat44 proj44 = convertProjMatrix(mProjMatrix); PxMat44 view44 = convertViewMatrix(view); // PxMat44 combo44 = view44 * proj44; PxMat44 combo44 = proj44 * view44; PxReal combo[4][4]; PxReal* dst = &combo[0][0]; memcpy(dst, &combo44, sizeof(PxReal)*16); // D3D: // -w' < x' < w' // -w' < y' < w' // 0 < z' < w' // // GL: // -w' < x' < w' // -w' < y' < w' // -w' < z' < w' // Left clipping plane mPlanes[FRUSTUM_PLANE_LEFT].n.x = -(combo[0][3] + combo[0][0]); mPlanes[FRUSTUM_PLANE_LEFT].n.y = -(combo[1][3] + combo[1][0]); mPlanes[FRUSTUM_PLANE_LEFT].n.z = -(combo[2][3] + combo[2][0]); mPlanes[FRUSTUM_PLANE_LEFT].d = -(combo[3][3] + combo[3][0]); // Right clipping plane mPlanes[FRUSTUM_PLANE_RIGHT].n.x = -(combo[0][3] - combo[0][0]); mPlanes[FRUSTUM_PLANE_RIGHT].n.y = -(combo[1][3] - combo[1][0]); mPlanes[FRUSTUM_PLANE_RIGHT].n.z = -(combo[2][3] - combo[2][0]); mPlanes[FRUSTUM_PLANE_RIGHT].d = -(combo[3][3] - combo[3][0]); // Top clipping plane mPlanes[FRUSTUM_PLANE_TOP].n.x = -(combo[0][3] - combo[0][1]); mPlanes[FRUSTUM_PLANE_TOP].n.y = -(combo[1][3] - combo[1][1]); mPlanes[FRUSTUM_PLANE_TOP].n.z = -(combo[2][3] - combo[2][1]); mPlanes[FRUSTUM_PLANE_TOP].d = -(combo[3][3] - combo[3][1]); // Bottom clipping plane mPlanes[FRUSTUM_PLANE_BOTTOM].n.x = -(combo[0][3] + combo[0][1]); mPlanes[FRUSTUM_PLANE_BOTTOM].n.y = -(combo[1][3] + combo[1][1]); mPlanes[FRUSTUM_PLANE_BOTTOM].n.z = -(combo[2][3] + combo[2][1]); mPlanes[FRUSTUM_PLANE_BOTTOM].d = -(combo[3][3] + combo[3][1]); // Near clipping plane if(1) { // OpenGL path mPlanes[FRUSTUM_PLANE_NEAR].n.x = -(combo[0][3] + combo[0][2]); mPlanes[FRUSTUM_PLANE_NEAR].n.y = -(combo[1][3] + combo[1][2]); mPlanes[FRUSTUM_PLANE_NEAR].n.z = -(combo[2][3] + combo[2][2]); mPlanes[FRUSTUM_PLANE_NEAR].d = -(combo[3][3] + combo[3][2]); } else { // D3D path mPlanes[FRUSTUM_PLANE_NEAR].n.x = - combo[0][2]; mPlanes[FRUSTUM_PLANE_NEAR].n.y = - combo[1][2]; mPlanes[FRUSTUM_PLANE_NEAR].n.z = - combo[2][2]; mPlanes[FRUSTUM_PLANE_NEAR].d = - combo[3][2]; } // Far clipping plane (must be last for infinite far clip) mPlanes[FRUSTUM_PLANE_FAR].n.x = -(combo[0][3] - combo[0][2]); mPlanes[FRUSTUM_PLANE_FAR].n.y = -(combo[1][3] - combo[1][2]); mPlanes[FRUSTUM_PLANE_FAR].n.z = -(combo[2][3] - combo[2][2]); mPlanes[FRUSTUM_PLANE_FAR].d = -(combo[3][3] - combo[3][2]); // Normalize if needed for(PxU32 i=0;i<6;i++) { // mPlanes[i].normalize(); mPlanes[i].n.normalize(); // mPlanes[i].normal = -mPlanes[i].normal; // mPlanes[i].d = -mPlanes[i].d; mPlanes[i].d *= 0.5f; } } } // Following code from Umbra/dPVS. //------------------------------------------------------------------------ // // Function: DPVS::intersectAABBFrustum() // // Description: Determines whether an AABB intersects a frustum // // Parameters: a = reference to AABB (defined by minimum & maximum vectors) // p = array of pre-normalized clipping planes // outClipMask = output clip mask (if function returns 'true') // inClipMask = input clip mask (indicates which planes are active) // // Returns: true if AABB intersects the frustum, false otherwise // // Intersection of AABB and a frustum. The frustum may // contain 0-32 planes (active planes are defined by inClipMask). // If AABB intersects the frustum, an output clip mask is returned // as well (indicating which planes are crossed by the AABB). This // information can be used to optimize testing of child nodes or // objects inside the nodes (pass value as 'inClipMask' next time). // // This is a variant of the classic "fast" AABB/frustum // intersection tester. AABBs that are not culled away by any single // plane are classified as "intersecting" even though the AABB may // actually be outside the convex volume formed by the planes. //------------------------------------------------------------------------ static PX_FORCE_INLINE bool planesAABBOverlap(const PxBounds3& a, const PxPlane* p, PxU32& out_clip_mask, PxU32 in_clip_mask) { //------------------------------------------------------------------------ // Convert the AABB from (minimum,maximum) form into (center,half-diagonal). // Note that we could get rid of these six subtractions and three // multiplications if the AABB was originally expressed in (center, // half-diagonal) form. //------------------------------------------------------------------------ PxVec3 m = a.getCenter(); // get center of AABB ((minimum+maximum)*0.5f) PxVec3 d = a.maximum; d-=m; // get positive half-diagonal (maximum - center) //------------------------------------------------------------------------ // Evaluate through all active frustum planes. We determine the relation // between the AABB and a plane by using the concept of "near" and "far" // vertices originally described by Zhang (and later by Moeller). Our // variant here uses 3 fabs ops, 6 muls, 7 adds and two floating point // comparisons per plane. The routine early-exits if the AABB is found // to be outside any of the planes. The loop also constructs a new output // clip mask. Most FPUs have a native single-cycle fabsf() operation. //------------------------------------------------------------------------ PxU32 Mask = 1; // current mask index (1,2,4,8,..) PxU32 TmpOutClipMask = 0; // initialize output clip mask into empty. while(Mask<=in_clip_mask) // keep looping while we have active planes left... { if(in_clip_mask & Mask) // if clip plane is active, process it.. { const float NP = d.x*PxAbs(p->n.x) + d.y*PxAbs(p->n.y) + d.z*PxAbs(p->n.z); const float MP = m.x*p->n.x + m.y*p->n.y + m.z*p->n.z + p->d; if(NP < MP) // near vertex behind the clip plane... return false; // .. so there is no intersection.. if((-NP) < MP) // near and far vertices on different sides of plane.. TmpOutClipMask |= Mask; // .. so update the clip mask... } Mask+=Mask; // mk = (1<addLine(mFrustum[i], mFrustum[i]+PxVec3(1,0,0), RendererColor(255,0,0)); debug->addLine(mFrustum[i], mFrustum[i]+PxVec3(0,1,0), RendererColor(0, 255,0)); debug->addLine(mFrustum[i], mFrustum[i]+PxVec3(0,0,1), RendererColor(0, 0, 255)); }*/ const RendererColor lineColor(255, 255, 0); debug->addLine(mFrustum[0], mFrustum[1], lineColor); debug->addLine(mFrustum[1], mFrustum[2], lineColor); debug->addLine(mFrustum[2], mFrustum[3], lineColor); debug->addLine(mFrustum[3], mFrustum[0], lineColor); debug->addLine(mFrustum[4], mFrustum[5], lineColor); debug->addLine(mFrustum[5], mFrustum[6], lineColor); debug->addLine(mFrustum[6], mFrustum[7], lineColor); debug->addLine(mFrustum[7], mFrustum[4], lineColor); debug->addLine(mFrustum[0], mFrustum[4], lineColor); debug->addLine(mFrustum[3], mFrustum[7], lineColor); debug->addLine(mFrustum[1], mFrustum[5], lineColor); debug->addLine(mFrustum[6], mFrustum[2], lineColor); } }