This commit is contained in:
2025-11-28 23:13:44 +05:30
commit a3a8e79709
7360 changed files with 1156074 additions and 0 deletions

View File

@ -0,0 +1,481 @@
//
// 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<<plane)
p++; // advance to next plane
}
out_clip_mask = TmpOutClipMask; // copy output value (temp used to resolve aliasing!)
return true; // indicate that AABB intersects frustum
}
PlaneAABBCode Camera::cull(const PxBounds3& aabb) const
{
const PxU32 nbFrustumPlanes = 6; // PT: can sometimes be 5 with infinite far clip plane
const PxU32 frustumPlanesMask = (1<<nbFrustumPlanes)-1;
PxU32 outClipMask;
if(!planesAABBOverlap(aabb, mPlanes, outClipMask, frustumPlanesMask))
return PLANEAABB_EXCLUSION;
if(outClipMask)
return PLANEAABB_INTERSECT;
return PLANEAABB_INCLUSION;
}
void Camera::drawDebug(RenderPhysX3Debug* debug)
{
if(mDrawDebugData)
{
/* for(PxU32 i=0;i<8;i++)
{
debug->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);
}
}