feat(physics): wire physx sdk into build

This commit is contained in:
2026-04-15 12:22:15 +08:00
parent 5bf258df6d
commit 31f40e2cbb
2044 changed files with 752623 additions and 1 deletions

View File

@@ -0,0 +1,197 @@
// 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-2025 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 "geometry/PxBoxGeometry.h"
#include "characterkinematic/PxController.h"
#include "PxRigidDynamic.h"
#include "PxShape.h"
#include "CctBoxController.h"
#include "CctCharacterControllerManager.h"
using namespace physx;
using namespace Cct;
static PX_FORCE_INLINE PxVec3 CCTtoProxyExtents(PxF32 halfHeight, PxF32 halfSideExtent, PxF32 halfForwardExtent, PxF32 coeff)
{
// PT: because we now orient the box CCT using the same quat as for capsules...
// i.e. the identity quat corresponds to a up dir = 1,0,0 (which is like the worst choice we could have made, of course)
return PxVec3(halfHeight * coeff, halfSideExtent * coeff, halfForwardExtent * coeff);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BoxController::BoxController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* s) : Controller(desc, s)
{
mType = PxControllerShapeType::eBOX;
const PxBoxControllerDesc& bc = static_cast<const PxBoxControllerDesc&>(desc);
mHalfHeight = bc.halfHeight;
mHalfSideExtent = bc.halfSideExtent;
mHalfForwardExtent = bc.halfForwardExtent;
// Create kinematic actor under the hood
PxBoxGeometry boxGeom;
boxGeom.halfExtents = CCTtoProxyExtents(bc.halfHeight, bc.halfSideExtent, bc.halfForwardExtent, mProxyScaleCoeff);
createProxyActor(sdk, boxGeom, *desc.material, desc.clientID);
}
BoxController::~BoxController()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void BoxController::invalidateCache()
{
if(mManager->mLockingEnabled)
mWriteLock.lock();
mCctModule.voidTestCache();
if(mManager->mLockingEnabled)
mWriteLock.unlock();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool BoxController::getWorldBox(PxExtendedBounds3& box) const
{
setCenterExtents(box, mPosition, PxVec3(mHalfHeight, mHalfSideExtent, mHalfForwardExtent));
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PxF32 BoxController::getHalfHeight() const
{
return mHalfHeight;
}
PxF32 BoxController::getHalfSideExtent() const
{
return mHalfSideExtent;
}
PxF32 BoxController::getHalfForwardExtent() const
{
return mHalfForwardExtent;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool BoxController::updateKinematicProxy()
{
// Set extents for kinematic proxy
if(mKineActor)
{
PxShape* shape = getKineShape();
const PxGeometry& geom = shape->getGeometry();
PX_ASSERT(geom.getType() == PxGeometryType::eBOX);
PxBoxGeometry bg = static_cast<const PxBoxGeometry&>(geom);
bg.halfExtents = CCTtoProxyExtents(mHalfHeight, mHalfSideExtent, mHalfForwardExtent, mProxyScaleCoeff);
shape->setGeometry(bg);
}
return true;
}
bool BoxController::setHalfHeight(PxF32 halfHeight)
{
if(halfHeight<=0.0f)
return false;
mHalfHeight = halfHeight;
return updateKinematicProxy();
}
bool BoxController::setHalfSideExtent(PxF32 halfSideExtent)
{
if(halfSideExtent<=0.0f)
return false;
mHalfSideExtent = halfSideExtent;
return updateKinematicProxy();
}
bool BoxController::setHalfForwardExtent(PxF32 halfForwardExtent)
{
if(halfForwardExtent<=0.0f)
return false;
mHalfForwardExtent = halfForwardExtent;
return updateKinematicProxy();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PxExtendedVec3 BoxController::getFootPosition() const
{
PxExtendedVec3 groundPosition = mPosition; // Middle of the CCT
sub(groundPosition, mUserParams.mUpDirection * (mHalfHeight + mUserParams.mContactOffset)); // Ground
return groundPosition;
}
bool BoxController::setFootPosition(const PxExtendedVec3& position)
{
PxExtendedVec3 centerPosition = position;
add(centerPosition, mUserParams.mUpDirection * (mHalfHeight + mUserParams.mContactOffset));
return setPosition(centerPosition);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void BoxController::getOBB(PxExtendedBox& obb) const
{
// PT: TODO: optimize this
PxExtendedBounds3 worldBox;
getWorldBox(worldBox);
getCenter(worldBox, obb.center);
getExtents(worldBox, obb.extents);
obb.rot = mUserParams.mQuatFromUp;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void BoxController::resize(PxReal height)
{
const float oldHeight = getHalfHeight();
setHalfHeight(height);
const float delta = height - oldHeight;
PxExtendedVec3 pos = getPosition();
add(pos, mUserParams.mUpDirection * delta);
setPosition(pos);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,112 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_BOX_CONTROLLER
#define CCT_BOX_CONTROLLER
/* Exclude from documentation */
/** \cond */
#include "characterkinematic/PxBoxController.h"
#include "CctController.h"
namespace physx
{
class PxPhysics;
namespace Cct
{
class BoxController : public PxBoxController, public Controller
{
public:
BoxController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* scene);
virtual ~BoxController();
// Controller
virtual PxF32 getHalfHeightInternal() const PX_OVERRIDE PX_FINAL { return mHalfHeight; }
virtual bool getWorldBox(PxExtendedBounds3& box) const PX_OVERRIDE PX_FINAL;
virtual PxController* getPxController() PX_OVERRIDE PX_FINAL { return this; }
//~Controller
// PxController
virtual PxControllerShapeType::Enum getType() const PX_OVERRIDE PX_FINAL { return mType; }
virtual void release() PX_OVERRIDE PX_FINAL { releaseInternal(); }
virtual PxControllerCollisionFlags move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles) PX_OVERRIDE PX_FINAL;
virtual bool setPosition(const PxExtendedVec3& position) PX_OVERRIDE PX_FINAL { return setPos(position); }
virtual const PxExtendedVec3& getPosition() const PX_OVERRIDE PX_FINAL { return mPosition; }
virtual bool setFootPosition(const PxExtendedVec3& position) PX_OVERRIDE PX_FINAL;
virtual PxExtendedVec3 getFootPosition() const PX_OVERRIDE PX_FINAL;
virtual PxRigidDynamic* getActor() const PX_OVERRIDE PX_FINAL { return mKineActor; }
virtual void setStepOffset(const float offset) PX_OVERRIDE PX_FINAL { if(offset>=0.0f)
mUserParams.mStepOffset = offset; }
virtual PxF32 getStepOffset() const PX_OVERRIDE PX_FINAL { return mUserParams.mStepOffset; }
virtual void setNonWalkableMode(PxControllerNonWalkableMode::Enum flag) PX_OVERRIDE PX_FINAL { mUserParams.mNonWalkableMode = flag; }
virtual PxControllerNonWalkableMode::Enum getNonWalkableMode() const PX_OVERRIDE PX_FINAL { return mUserParams.mNonWalkableMode; }
virtual PxF32 getContactOffset() const PX_OVERRIDE PX_FINAL { return mUserParams.mContactOffset; }
virtual void setContactOffset(PxF32 offset) PX_OVERRIDE PX_FINAL { if(offset>0.0f)
mUserParams.mContactOffset = offset;}
virtual PxVec3 getUpDirection() const PX_OVERRIDE PX_FINAL { return mUserParams.mUpDirection; }
virtual void setUpDirection(const PxVec3& up) PX_OVERRIDE PX_FINAL { setUpDirectionInternal(up); }
virtual PxF32 getSlopeLimit() const PX_OVERRIDE PX_FINAL { return mUserParams.mSlopeLimit; }
virtual void setSlopeLimit(PxF32 slopeLimit) PX_OVERRIDE PX_FINAL { if(slopeLimit>0.0f)
mUserParams.mSlopeLimit = slopeLimit;}
virtual void invalidateCache();
virtual PxScene* getScene() PX_OVERRIDE PX_FINAL { return mScene; }
virtual void* getUserData() const PX_OVERRIDE PX_FINAL { return mUserData; }
virtual void setUserData(void* userData) PX_OVERRIDE PX_FINAL { mUserData = userData; }
virtual void getState(PxControllerState& state) const PX_OVERRIDE PX_FINAL { return getInternalState(state); }
virtual void getStats(PxControllerStats& stats) const PX_OVERRIDE PX_FINAL { return getInternalStats(stats); }
virtual void resize(PxReal height);
//~PxController
// PxBoxController
virtual PxF32 getHalfHeight() const PX_OVERRIDE PX_FINAL;
virtual PxF32 getHalfSideExtent() const PX_OVERRIDE PX_FINAL;
virtual PxF32 getHalfForwardExtent() const PX_OVERRIDE PX_FINAL;
virtual bool setHalfHeight(PxF32 halfHeight) PX_OVERRIDE PX_FINAL;
virtual bool setHalfSideExtent(PxF32 halfSideExtent) PX_OVERRIDE PX_FINAL;
virtual bool setHalfForwardExtent(PxF32 halfForwardExtent) PX_OVERRIDE PX_FINAL;
//~ PxBoxController
PxF32 mHalfHeight;
PxF32 mHalfSideExtent;
PxF32 mHalfForwardExtent;
bool updateKinematicProxy();
void getOBB(PxExtendedBox& obb) const;
};
} // namespace Cct
}
/** \endcond */
#endif

View File

@@ -0,0 +1,191 @@
// 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-2025 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 "geometry/PxCapsuleGeometry.h"
#include "PxRigidDynamic.h"
#include "PxShape.h"
#include "characterkinematic/PxController.h"
#include "CctCapsuleController.h"
#include "CctCharacterControllerManager.h"
using namespace physx;
using namespace Cct;
static PX_FORCE_INLINE float CCTtoProxyRadius(float r, PxF32 coeff) { return r * coeff; }
static PX_FORCE_INLINE float CCTtoProxyHeight(float h, PxF32 coeff) { return 0.5f * h * coeff; }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CapsuleController::CapsuleController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* s) : Controller(desc, s)
{
mType = PxControllerShapeType::eCAPSULE;
const PxCapsuleControllerDesc& cc = static_cast<const PxCapsuleControllerDesc&>(desc);
mRadius = cc.radius;
mHeight = cc.height;
mClimbingMode = cc.climbingMode;
// Create kinematic actor under the hood
PxCapsuleGeometry capsGeom;
capsGeom.radius = CCTtoProxyRadius(cc.radius, mProxyScaleCoeff);
capsGeom.halfHeight = CCTtoProxyHeight(cc.height, mProxyScaleCoeff);
createProxyActor(sdk, capsGeom, *desc.material, desc.clientID);
}
CapsuleController::~CapsuleController()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CapsuleController::invalidateCache()
{
if(mManager->mLockingEnabled)
mWriteLock.lock();
mCctModule.voidTestCache();
if(mManager->mLockingEnabled)
mWriteLock.unlock();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CapsuleController::getWorldBox(PxExtendedBounds3& box) const
{
setCenterExtents(box, mPosition, PxVec3(mRadius, mRadius+mHeight*0.5f, mRadius));
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CapsuleController::setRadius(PxF32 r)
{
// Set radius for CCT volume
mRadius = r;
// Set radius for kinematic proxy
if(mKineActor)
{
PxShape* shape = getKineShape();
const PxGeometry& geom = shape->getGeometry();
PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE);
PxCapsuleGeometry cg = static_cast<const PxCapsuleGeometry&>(geom);
cg.radius = CCTtoProxyRadius(r, mProxyScaleCoeff);
shape->setGeometry(cg);
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CapsuleController::setHeight(PxF32 h)
{
// Set height for CCT volume
mHeight = h;
// Set height for kinematic proxy
if(mKineActor)
{
PxShape* shape = getKineShape();
const PxGeometry& geom = shape->getGeometry();
PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE);
PxCapsuleGeometry cg = static_cast<const PxCapsuleGeometry&>(geom);
cg.halfHeight = CCTtoProxyHeight(h, mProxyScaleCoeff);
shape->setGeometry(cg);
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PxCapsuleClimbingMode::Enum CapsuleController::getClimbingMode() const
{
return mClimbingMode;
}
bool CapsuleController::setClimbingMode(PxCapsuleClimbingMode::Enum mode)
{
if(mode>=PxCapsuleClimbingMode::eLAST)
return false;
mClimbingMode = mode;
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PxExtendedVec3 CapsuleController::getFootPosition() const
{
PxExtendedVec3 groundPosition = mPosition; // Middle of the CCT
sub(groundPosition, mUserParams.mUpDirection * (mUserParams.mContactOffset+mRadius+mHeight*0.5f)); // Ground
return groundPosition;
}
bool CapsuleController::setFootPosition(const PxExtendedVec3& position)
{
PxExtendedVec3 centerPosition = position;
add(centerPosition, mUserParams.mUpDirection * (mUserParams.mContactOffset+mRadius+mHeight*0.5f));
return setPosition(centerPosition);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CapsuleController::getCapsule(PxExtendedCapsule& capsule) const
{
// PT: TODO: optimize this
PxExtendedVec3 p0 = mPosition;
PxExtendedVec3 p1 = mPosition;
const PxVec3 extents = mUserParams.mUpDirection*mHeight*0.5f;
sub(p0, extents);
add(p1, extents);
capsule.p0 = p0;
capsule.p1 = p1;
capsule.radius = mRadius;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CapsuleController::resize(PxReal height)
{
const float oldHeight = getHeight();
setHeight(height);
const float delta = height - oldHeight;
PxExtendedVec3 pos = getPosition();
add(pos, mUserParams.mUpDirection * delta * 0.5f);
setPosition(pos);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,109 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_CAPSULE_CONTROLLER
#define CCT_CAPSULE_CONTROLLER
/* Exclude from documentation */
/** \cond */
#include "characterkinematic/PxCapsuleController.h"
#include "CctController.h"
namespace physx
{
class PxPhysics;
namespace Cct
{
class CapsuleController : public PxCapsuleController, public Controller
{
public:
CapsuleController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* scene);
virtual ~CapsuleController();
// Controller
virtual PxF32 getHalfHeightInternal() const PX_OVERRIDE PX_FINAL { return mRadius+mHeight*0.5f; }
virtual bool getWorldBox(PxExtendedBounds3& box) const PX_OVERRIDE PX_FINAL;
virtual PxController* getPxController() PX_OVERRIDE PX_FINAL { return this; }
//~Controller
// PxController
virtual PxControllerShapeType::Enum getType() const PX_OVERRIDE PX_FINAL { return mType; }
virtual void release() PX_OVERRIDE PX_FINAL { releaseInternal(); }
virtual PxControllerCollisionFlags move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles) PX_OVERRIDE PX_FINAL;
virtual bool setPosition(const PxExtendedVec3& position) PX_OVERRIDE PX_FINAL { return setPos(position); }
virtual const PxExtendedVec3& getPosition() const PX_OVERRIDE PX_FINAL { return mPosition; }
virtual bool setFootPosition(const PxExtendedVec3& position) PX_OVERRIDE PX_FINAL;
virtual PxExtendedVec3 getFootPosition() const PX_OVERRIDE PX_FINAL;
virtual PxRigidDynamic* getActor() const PX_OVERRIDE PX_FINAL { return mKineActor; }
virtual void setStepOffset(const float offset) PX_OVERRIDE PX_FINAL { if(offset>=0.0f)
mUserParams.mStepOffset = offset; }
virtual PxF32 getStepOffset() const PX_OVERRIDE PX_FINAL { return mUserParams.mStepOffset; }
virtual void setNonWalkableMode(PxControllerNonWalkableMode::Enum flag) PX_OVERRIDE PX_FINAL { mUserParams.mNonWalkableMode = flag; }
virtual PxControllerNonWalkableMode::Enum getNonWalkableMode() const PX_OVERRIDE PX_FINAL { return mUserParams.mNonWalkableMode; }
virtual PxF32 getContactOffset() const PX_OVERRIDE PX_FINAL { return mUserParams.mContactOffset; }
virtual void setContactOffset(PxF32 offset) PX_OVERRIDE PX_FINAL { if(offset>0.0f)
mUserParams.mContactOffset = offset;}
virtual PxVec3 getUpDirection() const PX_OVERRIDE PX_FINAL { return mUserParams.mUpDirection; }
virtual void setUpDirection(const PxVec3& up) PX_OVERRIDE PX_FINAL { setUpDirectionInternal(up); }
virtual PxF32 getSlopeLimit() const PX_OVERRIDE PX_FINAL { return mUserParams.mSlopeLimit; }
virtual void setSlopeLimit(PxF32 slopeLimit) PX_OVERRIDE PX_FINAL { if(slopeLimit>0.0f)
mUserParams.mSlopeLimit = slopeLimit;}
virtual void invalidateCache() PX_OVERRIDE PX_FINAL;
virtual PxScene* getScene() PX_OVERRIDE PX_FINAL { return mScene; }
virtual void* getUserData() const PX_OVERRIDE PX_FINAL { return mUserData; }
virtual void setUserData(void* userData) PX_OVERRIDE PX_FINAL { mUserData = userData; }
virtual void getState(PxControllerState& state) const PX_OVERRIDE PX_FINAL { return getInternalState(state); }
virtual void getStats(PxControllerStats& stats) const PX_OVERRIDE PX_FINAL { return getInternalStats(stats); }
virtual void resize(PxReal height) PX_OVERRIDE PX_FINAL;
//~PxController
// PxCapsuleController
virtual PxF32 getRadius() const PX_OVERRIDE PX_FINAL { return mRadius; }
virtual PxF32 getHeight() const PX_OVERRIDE PX_FINAL { return mHeight; }
virtual PxCapsuleClimbingMode::Enum getClimbingMode() const PX_OVERRIDE PX_FINAL;
virtual bool setRadius(PxF32 radius) PX_OVERRIDE PX_FINAL;
virtual bool setHeight(PxF32 height) PX_OVERRIDE PX_FINAL;
virtual bool setClimbingMode(PxCapsuleClimbingMode::Enum) PX_OVERRIDE PX_FINAL;
//~ PxCapsuleController
void getCapsule(PxExtendedCapsule& capsule) const;
PxF32 mRadius;
PxF32 mHeight;
PxCapsuleClimbingMode::Enum mClimbingMode;
};
} // namespace Cct
}
/** \endcond */
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,474 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_CHARACTER_CONTROLLER
#define CCT_CHARACTER_CONTROLLER
//#define USE_CONTACT_NORMAL_FOR_SLOPE_TEST
#include "geometry/PxTriangle.h"
#include "foundation/PxHashSet.h"
#include "characterkinematic/PxController.h"
#include "characterkinematic/PxControllerObstacles.h"
#include "CctCharacterControllerManager.h"
#include "CctUtils.h"
#include "foundation/PxArray.h"
namespace physx
{
struct PxFilterData;
class PxQueryFilterCallback;
class PxObstacle;
class RenderBuffer;
namespace Cct
{
struct CCTParams
{
CCTParams();
PxControllerNonWalkableMode::Enum mNonWalkableMode;
PxQuat mQuatFromUp;
PxVec3 mUpDirection;
PxF32 mSlopeLimit;
PxF32 mContactOffset;
PxF32 mStepOffset;
PxF32 mInvisibleWallHeight;
PxF32 mMaxJumpHeight;
PxF32 mMaxEdgeLength2;
bool mTessellation;
bool mHandleSlope; // True to handle walkable parts according to slope
bool mOverlapRecovery;
bool mPreciseSweeps;
bool mPreventVerticalSlidingAgainstCeiling;
};
// typedef PxArray<PxTriangle> TriArray;
typedef PxArray<PxU32> IntArray;
// PT: using private inheritance to control access, and make sure allocations are SIMD friendly
class TriArray : private PxArray<PxTriangle>
{
public:
PX_FORCE_INLINE PxTriangle* reserve(PxU32 nbTris)
{
// PT: customized version of "reserveContainerMemory"
const PxU32 maxNbEntries = PxArray<PxTriangle>::capacity();
const PxU32 realRequiredSize = PxArray<PxTriangle>::size() + nbTris;
// PT: allocate one more tri to make sure we can safely V4Load the last one...
const PxU32 requiredSize = realRequiredSize + 1;
if(requiredSize>maxNbEntries)
{
// PT: ok so the commented out growing policy was introduced by PX-837 but it produces
// large memory usage regressions (see PX-881) while not actually making things run
// faster. Our benchmarks show no performance difference, but up to +38% more memory
// used with this "standard" growing policy. So for now we just go back to the initial
// growing policy. It should be fine since PX-837 was not actually reported by a customer,
// it was just a concern that appeared while looking at the code. Ideally we'd use a pool
// with fixed-size slabs to get the best of both worlds but it would make iterating over
// triangles more complicated and would need more refactoring. So for now we don't bother,
// but we'll keep this note here for the next time this problem shows up.
// PT: new August 2018: turns out PX-837 was correct. Not doing this produces very large
// performance problems (like: the app freezes!) in SampleCCT. We didn't see it because
// it's an internal sample that it rarely used these days...
const PxU32 naturalGrowthSize = maxNbEntries ? maxNbEntries*2 : 2;
const PxU32 newSize = PxMax(requiredSize, naturalGrowthSize);
// const PxU32 newSize = requiredSize;
PxArray<PxTriangle>::reserve(newSize);
}
PxTriangle* buf = PxArray<PxTriangle>::end();
// ...but we still want the size to reflect the correct number
PxArray<PxTriangle>::forceSize_Unsafe(realRequiredSize);
return buf;
}
PX_FORCE_INLINE void pushBack(const PxTriangle& tri)
{
PxTriangle* memory = reserve(1);
memory->verts[0] = tri.verts[0];
memory->verts[1] = tri.verts[1];
memory->verts[2] = tri.verts[2];
}
PX_FORCE_INLINE PxU32 size() const
{
return PxArray<PxTriangle>::size();
}
PX_FORCE_INLINE const PxTriangle* begin() const
{
return PxArray<PxTriangle>::begin();
}
PX_FORCE_INLINE void clear()
{
PxArray<PxTriangle>::clear();
}
PX_FORCE_INLINE void forceSize_Unsafe(PxU32 size)
{
PxArray<PxTriangle>::forceSize_Unsafe(size);
}
PX_FORCE_INLINE const PxTriangle& getTriangle(PxU32 index) const
{
return (*this)[index];
}
};
/* Exclude from documentation */
/** \cond */
struct TouchedGeomType
{
enum Enum
{
eUSER_BOX,
eUSER_CAPSULE,
eMESH,
eBOX,
eSPHERE,
eCAPSULE,
eCUSTOM,
eLAST,
eFORCE_DWORD = 0x7fffffff
};
};
class SweptVolume;
struct TouchedGeom
{
TouchedGeomType::Enum mType;
const void* mTGUserData; // PxController or PxShape pointer
const PxRigidActor* mActor; // PxActor for PxShape pointers (mandatory with shared shapes)
PxExtendedVec3 mOffset; // Local origin, typically the center of the world bounds around the character. We translate both
// touched shapes & the character so that they are nearby this PxVec3, then add the offset back to
// computed "world" impacts.
protected:
~TouchedGeom(){}
};
struct TouchedUserBox : public TouchedGeom
{
PxExtendedBox mBox;
};
PX_COMPILE_TIME_ASSERT(sizeof(TouchedUserBox)==sizeof(TouchedGeom)+sizeof(PxExtendedBox));
struct TouchedUserCapsule : public TouchedGeom
{
PxExtendedCapsule mCapsule;
};
PX_COMPILE_TIME_ASSERT(sizeof(TouchedUserCapsule)==sizeof(TouchedGeom)+sizeof(PxExtendedCapsule));
struct TouchedMesh : public TouchedGeom
{
PxU32 mNbTris;
PxU32 mIndexWorldTriangles;
};
struct TouchedBox : public TouchedGeom
{
PxVec3 mCenter;
PxVec3 mExtents;
PxQuat mRot;
};
struct TouchedSphere : public TouchedGeom
{
PxVec3 mCenter; //!< Sphere's center
PxF32 mRadius; //!< Sphere's radius
};
struct TouchedCustom : public TouchedGeom
{
PxVec3 mCenter; //!< Custom geometry's center
PxCustomGeometry::Callbacks* mCustomCallbacks; //!< Custom geometry's callback object
};
struct TouchedCapsule : public TouchedGeom
{
PxVec3 mP0; //!< Start of segment
PxVec3 mP1; //!< End of segment
PxF32 mRadius; //!< Capsule's radius
};
struct SweptContact
{
PxExtendedVec3 mWorldPos; // Contact position in world space
PxVec3 mWorldNormal; // Contact normal in world space
PxF32 mDistance; // Contact distance
PxU32 mInternalIndex; // Reserved for internal usage
PxU32 mTriangleIndex; // Triangle index for meshes/heightfields
TouchedGeom* mGeom;
PX_FORCE_INLINE void setWorldPos(const PxVec3& localImpact, const PxExtendedVec3& offset)
{
mWorldPos.x = PxExtended(localImpact.x) + offset.x;
mWorldPos.y = PxExtended(localImpact.y) + offset.y;
mWorldPos.z = PxExtended(localImpact.z) + offset.z;
}
};
// PT: user-defined obstacles. Note that "user" is from the SweepTest class' point of view,
// i.e. the PhysX CCT module is the user in this case. This is to limit coupling between the
// core CCT module and the PhysX classes.
struct UserObstacles// : PxObstacleContext
{
PxU32 mNbBoxes;
const PxExtendedBox* mBoxes;
const void** mBoxUserData;
PxU32 mNbCapsules;
const PxExtendedCapsule* mCapsules;
const void** mCapsuleUserData;
};
struct InternalCBData_OnHit{};
struct InternalCBData_FindTouchedGeom{};
enum SweepTestFlag
{
STF_HIT_NON_WALKABLE = (1<<0),
STF_WALK_EXPERIMENT = (1<<1),
STF_VALIDATE_TRIANGLE_DOWN = (1<<2), // Validate touched triangle data (down pass)
STF_VALIDATE_TRIANGLE_SIDE = (1<<3), // Validate touched triangle data (side pass)
STF_TOUCH_OTHER_CCT = (1<<4), // Are we standing on another CCT or not? (only updated for down pass)
STF_TOUCH_OBSTACLE = (1<<5), // Are we standing on an obstacle or not? (only updated for down pass)
STF_NORMALIZE_RESPONSE = (1<<6),
STF_FIRST_UPDATE = (1<<7),
STF_IS_MOVING_UP = (1<<8)
};
enum SweepPass
{
SWEEP_PASS_UP,
SWEEP_PASS_SIDE,
SWEEP_PASS_DOWN,
SWEEP_PASS_SENSOR
};
class Controller;
template<class T>
struct TouchedObject
{
TouchedObject(bool regDl)
: mTouchedObject(NULL), mRegisterDeletionListener(regDl), mCctManager(NULL)
{
}
PX_FORCE_INLINE const T* operator->() const { return mTouchedObject; }
PX_FORCE_INLINE bool operator==(const TouchedObject& otherObject) { return mTouchedObject == otherObject.mTouchedObject; }
PX_FORCE_INLINE bool operator==(const T* otherObject) { return mTouchedObject == otherObject; }
PX_FORCE_INLINE bool operator==(const PxBase* otherObject) { return mTouchedObject == otherObject; }
PX_FORCE_INLINE operator bool() const { return mTouchedObject != NULL; }
PX_FORCE_INLINE TouchedObject& operator=(const T* assignedObject)
{
if(mRegisterDeletionListener && (mTouchedObject != assignedObject))
{
if(mTouchedObject)
mCctManager->unregisterObservedObject(mTouchedObject);
if(assignedObject)
mCctManager->registerObservedObject(assignedObject);
}
mTouchedObject = assignedObject;
return *this;
}
const T* get() const { return mTouchedObject; }
void setCctManager(CharacterControllerManager* cm) { mCctManager = cm; }
private:
TouchedObject& operator=(const TouchedObject&);
const T* mTouchedObject;
bool mRegisterDeletionListener;
CharacterControllerManager* mCctManager;
};
class SweepTest
{
PX_NOCOPY(SweepTest)
public:
SweepTest(bool registerDeletionListener);
~SweepTest();
PxControllerCollisionFlags moveCharacter( const InternalCBData_FindTouchedGeom* userData,
InternalCBData_OnHit* user_data2,
SweptVolume& volume,
const PxVec3& direction,
const UserObstacles& userObstacles,
PxF32 min_dist,
const PxControllerFilters& filters,
bool constrainedClimbingMode,
bool standingOnMoving,
const PxRigidActor*& touchedActor,
const PxShape*& touchedShape,
PxU64 contextID);
bool doSweepTest(const InternalCBData_FindTouchedGeom* userDataTouchedGeom,
InternalCBData_OnHit* userDataOnHit,
const UserObstacles& userObstacles,
SweptVolume& swept_volume,
const PxVec3& direction, const PxVec3& sideVector, PxU32 max_iter,
PxU32* nb_collisions, PxF32 min_dist, const PxControllerFilters& filters, SweepPass sweepPass,
const PxRigidActor*& touchedActor, const PxShape*& touchedShape, PxU64 contextID);
void findTouchedObstacles(const UserObstacles& userObstacles, const PxExtendedBounds3& world_box);
void voidTestCache();
void onRelease(const PxBase& observed);
void updateCachedShapesRegistration(PxU32 startIndex, bool unregister);
// observer notifications
void onObstacleRemoved(PxObstacleHandle index);
void onObstacleUpdated(PxObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, PxReal distance);
void onObstacleAdded(PxObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, PxReal distance);
void onOriginShift(const PxVec3& shift);
PxRenderBuffer* mRenderBuffer;
PxU32 mRenderFlags;
TriArray mWorldTriangles;
IntArray mTriangleIndices;
IntArray mGeomStream;
PxExtendedBounds3 mCacheBounds;
PxU32 mCachedTriIndexIndex;
mutable PxU32 mCachedTriIndex[3];
PxU32 mNbCachedStatic;
PxU32 mNbCachedT;
public:
#ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST
PxVec3 mContactNormalDownPass;
#else
PxVec3 mContactNormalDownPass;
PxVec3 mContactNormalSidePass;
float mTouchedTriMin;
float mTouchedTriMax;
//PxTriangle mTouchedTriangle;
#endif
//
TouchedObject<PxShape> mTouchedShape; // Shape on which the CCT is standing
TouchedObject<PxRigidActor> mTouchedActor; // Actor from touched shape
PxObstacleHandle mTouchedObstacleHandle; // Obstacle on which the CCT is standing
PxVec3 mTouchedPos; // Last known position of mTouchedShape/mTouchedObstacle
// PT: TODO: union those
PxVec3 mTouchedPosShape_Local;
PxVec3 mTouchedPosShape_World;
PxVec3 mTouchedPosObstacle_Local;
PxVec3 mTouchedPosObstacle_World;
//
CCTParams mUserParams;
PxF32 mVolumeGrowth; // Must be >1.0f and not too big
PxF32 mContactPointHeight; // UBI
PxU32 mSQTimeStamp;
PxU16 mNbFullUpdates;
PxU16 mNbPartialUpdates;
PxU16 mNbTessellation;
PxU16 mNbIterations;
PxU32 mFlags;
bool mRegisterDeletionListener;
PX_FORCE_INLINE void resetStats()
{
mNbFullUpdates = 0;
mNbPartialUpdates = 0;
mNbTessellation = 0;
mNbIterations = 0;
}
void setCctManager(CharacterControllerManager* cm)
{
mCctManager = cm;
mTouchedActor.setCctManager(cm);
mTouchedShape.setCctManager(cm);
}
private:
void updateTouchedGeoms( const InternalCBData_FindTouchedGeom* userData, const UserObstacles& userObstacles,
const PxExtendedBounds3& worldBox, const PxControllerFilters& filters, const PxVec3& sideVector);
CharacterControllerManager* mCctManager;
};
class CCTFilter // PT: internal filter data, could be replaced with PxControllerFilters eventually
{
public:
PX_FORCE_INLINE CCTFilter() :
mFilterData (NULL),
mFilterCallback (NULL),
mStaticShapes (false),
mDynamicShapes (false),
mPreFilter (false),
mPostFilter (false),
mCCTShapes (NULL)
{
}
const PxFilterData* mFilterData;
PxQueryFilterCallback* mFilterCallback;
bool mStaticShapes;
bool mDynamicShapes;
bool mPreFilter;
bool mPostFilter;
PxHashSet<PxShape>* mCCTShapes;
};
PxU32 getSceneTimestamp(const InternalCBData_FindTouchedGeom* userData);
void findTouchedGeometry(const InternalCBData_FindTouchedGeom* userData,
const PxExtendedBounds3& world_aabb,
TriArray& world_triangles,
IntArray& triIndicesArray,
IntArray& geomStream,
const CCTFilter& filter,
const CCTParams& params,
PxU16& nbTessellation);
PxU32 shapeHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, PxF32 length);
PxU32 userHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, PxF32 length);
} // namespace Cct
}
/** \endcond */
#endif

View File

@@ -0,0 +1,715 @@
// 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-2025 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 "CctCharacterControllerManager.h"
#include "CctBoxController.h"
#include "CctCapsuleController.h"
#include "CctObstacleContext.h"
#include "GuDistanceSegmentSegment.h"
#include "GuDistanceSegmentBox.h"
#include "foundation/PxUtilities.h"
#include "PxRigidDynamic.h"
#include "PxScene.h"
#include "PxPhysics.h"
#include "CmRenderBuffer.h"
#include "CmRadixSort.h"
using namespace physx;
using namespace Cct;
static const PxF32 gMaxOverlapRecover = 4.0f; // PT: TODO: expose this
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CharacterControllerManager::CharacterControllerManager(PxScene& scene, bool lockingEnabled) :
mScene (scene),
mRenderBuffer (NULL),
mDebugRenderingFlags (0),
mMaxEdgeLength (1.0f),
mTessellation (false),
mOverlapRecovery (true),
mPreciseSweeps (true),
mPreventVerticalSlidingAgainstCeiling (false),
mLockingEnabled (lockingEnabled)
{
// PT: register ourself as a deletion listener, to be called by the SDK whenever an object is deleted
PxPhysics& physics = scene.getPhysics();
physics.registerDeletionListener(*this, PxDeletionEventFlag::eUSER_RELEASE);
}
CharacterControllerManager::~CharacterControllerManager()
{
PX_DELETE(mRenderBuffer);
}
static PxArray<CharacterControllerManager*>* gControllerManagers = NULL;
void CharacterControllerManager::release()
{
if(gControllerManagers)
{
const bool found = gControllerManagers->findAndReplaceWithLast(this);
PX_ASSERT(found);
PX_UNUSED(found);
if(!gControllerManagers->size())
{
PX_DELETE(gControllerManagers);
}
}
// PT: TODO: use non virtual calls & move to dtor
while(getNbControllers()!= 0)
releaseController(*getController(0));
while(getNbObstacleContexts()!= 0)
mObstacleContexts[0]->release();
PxPhysics& physics = mScene.getPhysics();
physics.unregisterDeletionListener(*this);
PX_DELETE_THIS;
PxDecFoundationRefCount();
}
PX_C_EXPORT PxControllerManager* PX_CALL_CONV PxCreateControllerManager(PxScene& scene, bool lockingEnabled)
{
if(gControllerManagers)
{
// PT: make sure we cannot create two controller managers for the same scene
const PxU32 nbManagers = gControllerManagers->size();
for(PxU32 i=0;i<nbManagers;i++)
{
if(&scene==&(*gControllerManagers)[i]->getScene())
return NULL;
}
}
PxIncFoundationRefCount();
CharacterControllerManager* controllerManager = PX_NEW(CharacterControllerManager)(scene, lockingEnabled);
if(!gControllerManagers)
gControllerManagers = new PxArray<CharacterControllerManager*>;
gControllerManagers->pushBack(controllerManager);
return controllerManager;
}
PxScene& CharacterControllerManager::getScene() const
{
return mScene;
}
PxRenderBuffer& CharacterControllerManager::getRenderBuffer()
{
if(!mRenderBuffer)
mRenderBuffer = PX_NEW(Cm::RenderBuffer);
return *mRenderBuffer;
}
void CharacterControllerManager::setDebugRenderingFlags(PxControllerDebugRenderFlags flags)
{
mDebugRenderingFlags = flags;
if(!flags)
{
PX_DELETE(mRenderBuffer);
}
}
PxU32 CharacterControllerManager::getNbControllers() const
{
return mControllers.size();
}
Controller** CharacterControllerManager::getControllers()
{
return mControllers.begin();
}
PxController* CharacterControllerManager::getController(PxU32 index)
{
if(index>=mControllers.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "PxControllerManager::getController(): out-of-range index");
return NULL;
}
PX_ASSERT(mControllers[index]);
return mControllers[index]->getPxController();
}
PxController* CharacterControllerManager::createController(const PxControllerDesc& desc)
{
if(!desc.isValid())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "PxControllerManager::createController(): desc.isValid() fails.");
return NULL;
}
Controller* newController = NULL;
PxController* N = NULL;
if(desc.getType()==PxControllerShapeType::eBOX)
{
BoxController* boxController = PX_NEW(BoxController)(desc, mScene.getPhysics(), &mScene);
newController = boxController;
N = boxController;
}
else if(desc.getType()==PxControllerShapeType::eCAPSULE)
{
CapsuleController* capsuleController = PX_NEW(CapsuleController)(desc, mScene.getPhysics(), &mScene);
newController = capsuleController;
N = capsuleController;
}
else PX_ALWAYS_ASSERT_MESSAGE( "INTERNAL ERROR - invalid CCT type, should have been caught by isValid().");
if(newController)
{
mControllers.pushBack(newController);
newController->setCctManager(this);
PxShape* shape = NULL;
PxU32 nb = N->getActor()->getShapes(&shape, 1);
PX_ASSERT(nb==1);
PX_UNUSED(nb);
mCCTShapes.insert(shape);
}
return N;
}
void CharacterControllerManager::releaseController(PxController& controller)
{
for(PxU32 i = 0; i<mControllers.size(); i++)
{
if(mControllers[i]->getPxController() == &controller)
{
mControllers.replaceWithLast(i);
break;
}
}
PxShape* shape = NULL;
PxU32 nb = controller.getActor()->getShapes(&shape, 1);
PX_ASSERT(nb==1);
PX_UNUSED(nb);
mCCTShapes.erase(shape);
if(controller.getType() == PxControllerShapeType::eCAPSULE)
{
CapsuleController* cc = static_cast<CapsuleController*>(&controller);
PX_DELETE(cc);
}
else if(controller.getType() == PxControllerShapeType::eBOX)
{
BoxController* bc = static_cast<BoxController*>(&controller);
PX_DELETE(bc);
}
else PX_ASSERT(0);
}
void CharacterControllerManager::purgeControllers()
{
while(mControllers.size())
releaseController(*mControllers[0]->getPxController());
}
void CharacterControllerManager::onRelease(const PxBase* observed, void* , PxDeletionEventFlag::Enum deletionEvent)
{
PX_ASSERT(deletionEvent == PxDeletionEventFlag::eUSER_RELEASE); // the only type we registered for
PX_UNUSED(deletionEvent);
const PxType type = observed->getConcreteType();
if(type!=PxConcreteType:: eRIGID_DYNAMIC && type!=PxConcreteType:: eRIGID_STATIC && type!=PxConcreteType::eSHAPE && type!=PxConcreteType::eARTICULATION_LINK)
return;
// check if object was registered
if(mLockingEnabled)
mWriteLock.lock();
const ObservedRefCountMap::Entry* releaseEntry = mObservedRefCountMap.find(observed);
if(mLockingEnabled)
mWriteLock.unlock();
if(releaseEntry)
{
const PxU32 size = mControllers.size();
for(PxU32 i=0; i<size; i++)
{
Controller* controller = mControllers[i];
if(mLockingEnabled)
controller->mWriteLock.lock();
controller->onRelease(*observed);
if(mLockingEnabled)
controller->mWriteLock.unlock();
}
}
}
void CharacterControllerManager::registerObservedObject(const PxBase* obj)
{
if(mLockingEnabled)
mWriteLock.lock();
mObservedRefCountMap[obj].refCount++;
if(mLockingEnabled)
mWriteLock.unlock();
}
void CharacterControllerManager::unregisterObservedObject(const PxBase* obj)
{
if(mLockingEnabled)
mWriteLock.lock();
ObservedRefCounter& refCounter = mObservedRefCountMap[obj];
PX_ASSERT(refCounter.refCount);
refCounter.refCount--;
if(!refCounter.refCount)
mObservedRefCountMap.erase(obj);
if(mLockingEnabled)
mWriteLock.unlock();
}
PxU32 CharacterControllerManager::getNbObstacleContexts() const
{
return mObstacleContexts.size();
}
PxObstacleContext* CharacterControllerManager::getObstacleContext(PxU32 index)
{
if(index>=mObstacleContexts.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "PxControllerManager::getObstacleContext(): out-of-range index");
return NULL;
}
PX_ASSERT(mObstacleContexts[index]);
return mObstacleContexts[index];
}
PxObstacleContext* CharacterControllerManager::createObstacleContext()
{
ObstacleContext* oc = PX_NEW(ObstacleContext)(*this);
mObstacleContexts.pushBack(oc);
return oc;
}
void CharacterControllerManager::releaseObstacleContext(ObstacleContext& oc)
{
PX_ASSERT(mObstacleContexts.find(&oc) != mObstacleContexts.end());
mObstacleContexts.findAndReplaceWithLast(&oc);
ObstacleContext* ptr = &oc;
PX_DELETE(ptr);
}
void CharacterControllerManager::onObstacleRemoved(PxObstacleHandle index) const
{
for(PxU32 i = 0; i<mControllers.size(); i++)
{
mControllers[i]->mCctModule.onObstacleRemoved(index);
}
}
void CharacterControllerManager::onObstacleUpdated(PxObstacleHandle index, const PxObstacleContext* context) const
{
for(PxU32 i = 0; i<mControllers.size(); i++)
{
mControllers[i]->mCctModule.onObstacleUpdated(index,context, toVec3(mControllers[i]->mPosition), -mControllers[i]->mUserParams.mUpDirection, mControllers[i]->getHalfHeightInternal());
}
}
void CharacterControllerManager::onObstacleAdded(PxObstacleHandle index, const PxObstacleContext* context) const
{
for(PxU32 i = 0; i<mControllers.size(); i++)
{
mControllers[i]->mCctModule.onObstacleAdded(index,context, toVec3(mControllers[i]->mPosition), -mControllers[i]->mUserParams.mUpDirection, mControllers[i]->getHalfHeightInternal());
}
}
void CharacterControllerManager::resetObstaclesBuffers()
{
mBoxUserData.resetOrClear();
mBoxes.resetOrClear();
mCapsuleUserData.resetOrClear();
mCapsules.resetOrClear();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CharacterControllerManager::setTessellation(bool flag, float maxEdgeLength)
{
mTessellation = flag;
mMaxEdgeLength = maxEdgeLength;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CharacterControllerManager::setOverlapRecoveryModule(bool flag)
{
mOverlapRecovery = flag;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CharacterControllerManager::setPreciseSweeps(bool flag)
{
mPreciseSweeps = flag;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CharacterControllerManager::setPreventVerticalSlidingAgainstCeiling(bool flag)
{
mPreventVerticalSlidingAgainstCeiling = flag;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CharacterControllerManager::shiftOrigin(const PxVec3& shift)
{
for(PxU32 i=0; i < mControllers.size(); i++)
{
mControllers[i]->onOriginShift(shift);
}
for(PxU32 i=0; i < mObstacleContexts.size(); i++)
{
mObstacleContexts[i]->onOriginShift(shift);
}
if(mRenderBuffer)
mRenderBuffer->shift(-shift);
// assumption is that these are just used for temporary stuff
PX_ASSERT(!mBoxes.size());
PX_ASSERT(!mCapsules.size());
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static bool computeMTD(PxVec3& mtd, PxF32& depth, const PxVec3& e0, const PxVec3& c0, const PxMat33& r0, const PxVec3& e1, const PxVec3& c1, const PxMat33& r1)
{
// Translation, in parent frame
const PxVec3 v = c1 - c0;
// Translation, in A's frame
const PxVec3 T(v.dot(r0[0]), v.dot(r0[1]), v.dot(r0[2]));
// B's basis with respect to A's local frame
PxReal R[3][3];
PxReal FR[3][3];
PxReal ra, rb, t, d;
PxReal overlap[15];
// Calculate rotation matrix
for(PxU32 i=0;i<3;i++)
{
for(PxU32 k=0;k<3;k++)
{
R[i][k] = r0[i].dot(r1[k]);
FR[i][k] = 1e-6f + PxAbs(R[i][k]); // Precompute fabs matrix
}
}
// A's basis vectors
for(PxU32 i=0;i<3;i++)
{
ra = e0[i];
rb = e1[0]*FR[i][0] + e1[1]*FR[i][1] + e1[2]*FR[i][2];
t = PxAbs(T[i]);
d = ra + rb - t;
if(d<0.0f)
return false;
overlap[i] = d;
}
// B's basis vectors
for(PxU32 k=0;k<3;k++)
{
ra = e0[0]*FR[0][k] + e0[1]*FR[1][k] + e0[2]*FR[2][k];
rb = e1[k];
t = PxAbs(T[0]*R[0][k] + T[1]*R[1][k] + T[2]*R[2][k]);
d = ra + rb - t;
if(d<0.0f)
return false;
overlap[k+3] = d;
}
// PT: edge-edge tests are skipped, by design
PxU32 minIndex=0;
PxReal minD = overlap[0];
for(PxU32 i=1;i<6;i++)
{
if(overlap[i]<minD)
{
minD = overlap[i];
minIndex = i;
}
}
depth = minD;
switch(minIndex)
{
case 0: mtd = r0.column0; break;
case 1: mtd = r0.column1; break;
case 2: mtd = r0.column2; break;
case 3: mtd = r1.column0; break;
case 4: mtd = r1.column1; break;
case 5: mtd = r1.column2; break;
default: PX_ASSERT(0); break;
};
return true;
}
static PxVec3 fixDir(const PxVec3& dir, const PxVec3& up)
{
PxVec3 normalCompo, tangentCompo;
decomposeVector(normalCompo, tangentCompo, dir, up);
return tangentCompo.getNormalized();
}
static void InteractionCharacterCharacter(Controller* entity0, Controller* entity1, PxF32 elapsedTime)
{
PX_ASSERT(entity0);
PX_ASSERT(entity1);
PxF32 overlap=0.0f;
PxVec3 dir(0.0f);
if(entity0->mType>entity1->mType)
PxSwap(entity0, entity1);
if(entity0->mType==PxControllerShapeType::eCAPSULE && entity1->mType==PxControllerShapeType::eCAPSULE)
{
CapsuleController* cc0 = static_cast<CapsuleController*>(entity0);
CapsuleController* cc1 = static_cast<CapsuleController*>(entity1);
PxExtendedCapsule capsule0;
cc0->getCapsule(capsule0);
PxExtendedCapsule capsule1;
cc1->getCapsule(capsule1);
const PxF32 r = capsule0.radius + capsule1.radius;
const PxVec3 p00 = toVec3(capsule0.p0);
const PxVec3 p01 = toVec3(capsule0.p1);
const PxVec3 p10 = toVec3(capsule1.p0);
const PxVec3 p11 = toVec3(capsule1.p1);
PxF32 s,t;
const PxF32 d = sqrtf(Gu::distanceSegmentSegmentSquared(p00, p01 - p00, p10, p11 - p10, &s, &t));
if(d<r)
{
const PxVec3 center0 = s * p00 + (1.0f - s) * p01;
const PxVec3 center1 = t * p10 + (1.0f - t) * p11;
const PxVec3 up = entity0->mCctModule.mUserParams.mUpDirection;
dir = fixDir(center0 - center1, up);
overlap = r - d;
}
}
else if(entity0->mType==PxControllerShapeType::eBOX && entity1->mType==PxControllerShapeType::eCAPSULE)
{
BoxController* cc0 = static_cast<BoxController*>(entity0);
CapsuleController* cc1 = static_cast<CapsuleController*>(entity1);
PxExtendedBox obb;
cc0->getOBB(obb);
PxExtendedCapsule capsule;
cc1->getCapsule(capsule);
const PxVec3 p0 = toVec3(capsule.p0);
const PxVec3 p1 = toVec3(capsule.p1);
PxF32 t;
PxVec3 p;
const PxMat33 M(obb.rot);
const PxVec3 boxCenter = toVec3(obb.center);
const PxF32 d = sqrtf(Gu::distanceSegmentBoxSquared(p0, p1, boxCenter, obb.extents, M, &t, &p));
if(d<capsule.radius)
{
// const PxVec3 center0 = M.transform(p) + boxCenter;
// const PxVec3 center1 = t * p0 + (1.0f - t) * p1;
const PxVec3 center0 = boxCenter;
const PxVec3 center1 = (p0 + p1)*0.5f;
const PxVec3 up = entity0->mCctModule.mUserParams.mUpDirection;
dir = fixDir(center0 - center1, up);
overlap = capsule.radius - d;
}
}
else
{
PX_ASSERT(entity0->mType==PxControllerShapeType::eBOX);
PX_ASSERT(entity1->mType==PxControllerShapeType::eBOX);
BoxController* cc0 = static_cast<BoxController*>(entity0);
BoxController* cc1 = static_cast<BoxController*>(entity1);
PxExtendedBox obb0;
cc0->getOBB(obb0);
PxExtendedBox obb1;
cc1->getOBB(obb1);
PxVec3 mtd;
PxF32 depth;
if(computeMTD( mtd, depth,
obb0.extents, toVec3(obb0.center), PxMat33(obb0.rot),
obb1.extents, toVec3(obb1.center), PxMat33(obb1.rot)))
{
const PxVec3 center0 = toVec3(obb0.center);
const PxVec3 center1 = toVec3(obb1.center);
const PxVec3 witness = center0 - center1;
if(mtd.dot(witness)<0.0f)
dir = -mtd;
else
dir = mtd;
const PxVec3 up = entity0->mCctModule.mUserParams.mUpDirection;
dir = fixDir(dir, up);
overlap = depth;
}
}
if(overlap!=0.0f)
{
// We want to limit this to some reasonable amount, to avoid obvious "popping".
const PxF32 maxOverlap = gMaxOverlapRecover * elapsedTime;
if(overlap>maxOverlap)
overlap=maxOverlap;
const PxVec3 sep = dir * overlap * 0.5f;
entity0->mOverlapRecover += sep;
entity1->mOverlapRecover -= sep;
}
}
// PT: TODO: this is the very old version, revisit with newer one
static void completeBoxPruning(const PxBounds3* bounds, PxU32 nb, PxArray<PxU32>& pairs)
{
if(!nb)
return;
pairs.clear();
float* PosList = PX_ALLOCATE(float, nb, "completeBoxPruning");
for(PxU32 i=0;i<nb;i++)
PosList[i] = bounds[i].minimum.x;
/*static*/ Cm::RadixSortBuffered RS; // Static for coherence
const PxU32* Sorted = RS.Sort(PosList, nb).GetRanks();
const PxU32* const LastSorted = &Sorted[nb];
const PxU32* RunningAddress = Sorted;
PxU32 Index0, Index1;
while(RunningAddress<LastSorted && Sorted<LastSorted)
{
Index0 = *Sorted++;
while(RunningAddress<LastSorted && PosList[*RunningAddress++]<PosList[Index0]);
const PxU32* RunningAddress2 = RunningAddress;
while(RunningAddress2<LastSorted && PosList[Index1 = *RunningAddress2++]<=bounds[Index0].maximum.x)
{
if(Index0!=Index1)
{
if(bounds[Index0].intersects(bounds[Index1]))
{
pairs.pushBack(Index0);
pairs.pushBack(Index1);
}
}
}
}
PX_FREE(PosList);
}
void CharacterControllerManager::computeInteractions(PxF32 elapsedTime, PxControllerFilterCallback* cctFilterCb)
{
PxU32 nbControllers = mControllers.size();
Controller** controllers = mControllers.begin();
PxBounds3* boxes = PX_ALLOCATE(PxBounds3, nbControllers, "CharacterControllerManager::computeInteractions"); // PT: TODO: get rid of alloc
PxBounds3* runningBoxes = boxes;
while(nbControllers--)
{
Controller* current = *controllers++;
PxExtendedBounds3 extBox;
current->getWorldBox(extBox);
*runningBoxes++ = PxBounds3(toVec3(extBox.minimum), toVec3(extBox.maximum)); // ### LOSS OF ACCURACY
}
//
const PxU32 nbEntities = PxU32(runningBoxes - boxes);
PxArray<PxU32> pairs; // PT: TODO: get rid of alloc
completeBoxPruning(boxes, nbEntities, pairs);
PxU32 nbPairs = pairs.size()>>1;
const PxU32* indices = pairs.begin();
while(nbPairs--)
{
const PxU32 index0 = *indices++;
const PxU32 index1 = *indices++;
Controller* ctrl0 = mControllers[index0];
Controller* ctrl1 = mControllers[index1];
bool keep=true;
if(cctFilterCb)
keep = cctFilterCb->filter(*ctrl0->getPxController(), *ctrl1->getPxController());
if(keep)
InteractionCharacterCharacter(ctrl0, ctrl1, elapsedTime);
}
PX_FREE(boxes);
}

View File

@@ -0,0 +1,146 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_CHARACTER_CONTROLLER_MANAGER
#define CCT_CHARACTER_CONTROLLER_MANAGER
//Exclude file from docs
/** \cond */
#include "geometry/PxMeshQuery.h"
#include "common/PxRenderBuffer.h"
#include "foundation/PxHashSet.h"
#include "foundation/PxHashMap.h"
#include "characterkinematic/PxControllerManager.h"
#include "characterkinematic/PxControllerObstacles.h"
#include "PxDeletionListener.h"
#include "CctUtils.h"
#include "foundation/PxMutex.h"
#include "foundation/PxArray.h"
#include "foundation/PxUserAllocated.h"
namespace physx
{
namespace Cct
{
class Controller;
class ObstacleContext;
struct ObservedRefCounter
{
ObservedRefCounter(): refCount(0)
{
}
PxU32 refCount;
};
typedef PxHashMap<const PxBase*, ObservedRefCounter> ObservedRefCountMap;
//Implements the PxControllerManager interface, this class used to be called ControllerManager
class CharacterControllerManager : public PxControllerManager, public PxUserAllocated, public PxDeletionListener
{
PX_NOCOPY(CharacterControllerManager)
public:
CharacterControllerManager(PxScene& scene, bool lockingEnabled = false);
private:
virtual ~CharacterControllerManager();
public:
// PxControllerManager
virtual void release() PX_OVERRIDE PX_FINAL;
virtual PxScene& getScene() const PX_OVERRIDE PX_FINAL;
virtual PxU32 getNbControllers() const PX_OVERRIDE PX_FINAL;
virtual PxController* getController(PxU32 index) PX_OVERRIDE PX_FINAL;
virtual PxController* createController(const PxControllerDesc& desc) PX_OVERRIDE PX_FINAL;
virtual void purgeControllers() PX_OVERRIDE PX_FINAL;
virtual PxRenderBuffer& getRenderBuffer() PX_OVERRIDE PX_FINAL;
virtual void setDebugRenderingFlags(PxControllerDebugRenderFlags flags) PX_OVERRIDE PX_FINAL;
virtual PxU32 getNbObstacleContexts() const PX_OVERRIDE PX_FINAL;
virtual PxObstacleContext* getObstacleContext(PxU32 index) PX_OVERRIDE PX_FINAL;
virtual PxObstacleContext* createObstacleContext() PX_OVERRIDE PX_FINAL;
virtual void computeInteractions(PxF32 elapsedTime, PxControllerFilterCallback* cctFilterCb) PX_OVERRIDE PX_FINAL;
virtual void setTessellation(bool flag, float maxEdgeLength) PX_OVERRIDE PX_FINAL;
virtual void setOverlapRecoveryModule(bool flag) PX_OVERRIDE PX_FINAL;
virtual void setPreciseSweeps(bool flag) PX_OVERRIDE PX_FINAL;
virtual void setPreventVerticalSlidingAgainstCeiling(bool flag) PX_OVERRIDE PX_FINAL;
virtual void shiftOrigin(const PxVec3& shift) PX_OVERRIDE PX_FINAL;
//~PxControllerManager
// PxDeletionListener
virtual void onRelease(const PxBase* observed, void* userData, PxDeletionEventFlag::Enum deletionEvent) PX_OVERRIDE PX_FINAL;
//~PxDeletionListener
void registerObservedObject(const PxBase* obj);
void unregisterObservedObject(const PxBase* obj);
// ObstacleContextNotifications
void onObstacleRemoved(PxObstacleHandle index) const;
void onObstacleUpdated(PxObstacleHandle index, const PxObstacleContext* ) const;
void onObstacleAdded(PxObstacleHandle index, const PxObstacleContext*) const;
void releaseController(PxController& controller);
Controller** getControllers();
void releaseObstacleContext(ObstacleContext& oc);
void resetObstaclesBuffers();
PxScene& mScene;
PxRenderBuffer* mRenderBuffer;
PxControllerDebugRenderFlags mDebugRenderingFlags;
// Shared buffers for obstacles
PxArray<const void*> mBoxUserData;
PxArray<PxExtendedBox> mBoxes;
PxArray<const void*> mCapsuleUserData;
PxArray<PxExtendedCapsule> mCapsules;
PxArray<Controller*> mControllers;
PxHashSet<PxShape*> mCCTShapes;
PxArray<ObstacleContext*> mObstacleContexts;
float mMaxEdgeLength;
bool mTessellation;
bool mOverlapRecovery;
bool mPreciseSweeps;
bool mPreventVerticalSlidingAgainstCeiling;
bool mLockingEnabled;
private:
ObservedRefCountMap mObservedRefCountMap;
mutable PxMutex mWriteLock; // Lock used for guarding pointers in observedrefcountmap
};
} // namespace Cct
}
/** \endcond */
#endif //CCT_CHARACTER_CONTROLLER_MANAGER

View File

@@ -0,0 +1,238 @@
// 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-2025 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 "foundation/PxMathUtils.h"
#include "extensions/PxRigidBodyExt.h"
#include "characterkinematic/PxController.h"
#include "PxPhysics.h"
#include "PxScene.h"
#include "PxRigidDynamic.h"
#include "PxShape.h"
#include "CctController.h"
#include "CctBoxController.h"
#include "CctCharacterControllerManager.h"
#include "foundation/PxUtilities.h"
using namespace physx;
using namespace Cct;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Controller::Controller(const PxControllerDesc& desc, PxScene* s) :
mCctModule (desc.registerDeletionListener),
mScene (s),
mPreviousSceneTimestamp (0xffffffff),
mGlobalTime (0.0),
mPreviousGlobalTime (0.0),
mProxyDensity (0.0f),
mProxyScaleCoeff (0.0f),
mCollisionFlags (0),
mCachedStandingOnMoving (false),
mManager (NULL)
{
mType = PxControllerShapeType::eFORCE_DWORD;
mUserParams.mNonWalkableMode = desc.nonWalkableMode;
mUserParams.mSlopeLimit = desc.slopeLimit;
mUserParams.mContactOffset = desc.contactOffset;
mUserParams.mStepOffset = desc.stepOffset;
mUserParams.mInvisibleWallHeight = desc.invisibleWallHeight;
mUserParams.mMaxJumpHeight = desc.maxJumpHeight;
mUserParams.mHandleSlope = desc.slopeLimit!=0.0f;
mReportCallback = desc.reportCallback;
mBehaviorCallback = desc.behaviorCallback;
mUserData = desc.userData;
mKineActor = NULL;
mPosition = desc.position;
mProxyDensity = desc.density;
mProxyScaleCoeff = desc.scaleCoeff;
mCctModule.mVolumeGrowth = desc.volumeGrowth;
mRegisterDeletionListener = desc.registerDeletionListener;
mDeltaXP = PxVec3(0);
mOverlapRecover = PxVec3(0);
mUserParams.mUpDirection = PxVec3(0.0f);
setUpDirectionInternal(desc.upDirection);
}
Controller::~Controller()
{
if(mScene)
{
if(mKineActor)
mKineActor->release();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Controller::onRelease(const PxBase& observed)
{
mCctModule.onRelease(observed);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Controller::onOriginShift(const PxVec3& shift)
{
sub(mPosition, shift);
if(mManager && mManager->mLockingEnabled)
mWriteLock.lock();
mCctModule.onOriginShift(shift);
if(mManager && mManager->mLockingEnabled)
mWriteLock.unlock();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Controller::setUpDirectionInternal(const PxVec3& up)
{
PX_CHECK_MSG(up.isNormalized(), "CCT: up direction must be normalized");
if(mUserParams.mUpDirection==up)
return;
const PxQuat q = PxShortestRotation(PxVec3(1.0f, 0.0f, 0.0f), up);
mUserParams.mQuatFromUp = q;
mUserParams.mUpDirection = up;
// Update kinematic actor
/*if(mKineActor)
{
PxTransform pose = mKineActor->getGlobalPose();
pose.q = q;
mKineActor->setGlobalPose(pose);
}*/
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Controller::releaseInternal()
{
mManager->releaseController(*getPxController());
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Controller::getInternalState(PxControllerState& state) const
{
if(mManager->mLockingEnabled)
mWriteLock.lock();
state.deltaXP = mDeltaXP;
state.touchedShape = const_cast<PxShape*>(mCctModule.mTouchedShape.get());
state.touchedActor = const_cast<PxRigidActor*>(mCctModule.mTouchedActor.get());
state.touchedObstacleHandle = mCctModule.mTouchedObstacleHandle;
state.standOnAnotherCCT = (mCctModule.mFlags & STF_TOUCH_OTHER_CCT)!=0;
state.standOnObstacle = (mCctModule.mFlags & STF_TOUCH_OBSTACLE)!=0;
state.isMovingUp = (mCctModule.mFlags & STF_IS_MOVING_UP)!=0;
state.collisionFlags = mCollisionFlags;
if(mManager->mLockingEnabled)
mWriteLock.unlock();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Controller::getInternalStats(PxControllerStats& stats) const
{
stats.nbFullUpdates = mCctModule.mNbFullUpdates;
stats.nbPartialUpdates = mCctModule.mNbPartialUpdates;
stats.nbIterations = mCctModule.mNbIterations;
stats.nbTessellation = mCctModule.mNbTessellation;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Controller::setPos(const PxExtendedVec3& pos)
{
mPosition = pos;
// Update kinematic actor
if(mKineActor)
{
PxTransform targetPose = mKineActor->getGlobalPose();
targetPose.p = toVec3(mPosition); // LOSS OF ACCURACY
targetPose.q = mUserParams.mQuatFromUp;
mKineActor->setKinematicTarget(targetPose);
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Controller::createProxyActor(PxPhysics& sdk, const PxGeometry& geometry, const PxMaterial& material, PxClientID clientID)
{
// PT: we don't disable raycasting or CD because:
// - raycasting is needed for visibility queries (the SDK otherwise doesn't know about the CCTS)
// - collision is needed because the only reason we create actors there is to handle collisions with dynamic shapes
// So it's actually wrong to disable any of those.
PxTransform globalPose;
globalPose.p = toVec3(mPosition); // LOSS OF ACCURACY
globalPose.q = mUserParams.mQuatFromUp;
mKineActor = sdk.createRigidDynamic(globalPose);
if(!mKineActor)
return false;
PxShape* shape = sdk.createShape(geometry, material, true);
mKineActor->attachShape(*shape);
shape->release();
mKineActor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);
PxRigidBodyExt::updateMassAndInertia(*mKineActor, mProxyDensity);
if(clientID!=PX_DEFAULT_CLIENT)
mKineActor->setOwnerClient(clientID);
mScene->addActor(*mKineActor);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PxShape* Controller::getKineShape() const
{
// PT: TODO: cache this and avoid the virtual call
PxShape* shape = NULL;
PxU32 nb = mKineActor->getShapes(&shape, 1);
PX_ASSERT(nb==1);
PX_UNUSED(nb);
return shape;
}

View File

@@ -0,0 +1,129 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_CONTROLLER
#define CCT_CONTROLLER
/* Exclude from documentation */
/** \cond */
#include "CctCharacterController.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxMutex.h"
namespace physx
{
class PxPhysics;
class PxScene;
class PxRigidDynamic;
class PxGeometry;
class PxMaterial;
namespace Cct
{
class CharacterControllerManager;
class Controller : public PxUserAllocated
{
PX_NOCOPY(Controller)
public:
Controller(const PxControllerDesc& desc, PxScene* scene);
virtual ~Controller();
void releaseInternal();
void getInternalState(PxControllerState& state) const;
void getInternalStats(PxControllerStats& stats) const;
virtual PxF32 getHalfHeightInternal() const = 0;
virtual bool getWorldBox(PxExtendedBounds3& box) const = 0;
virtual PxController* getPxController() = 0;
void onOriginShift(const PxVec3& shift);
void onRelease(const PxBase& observed);
void setCctManager(CharacterControllerManager* cm)
{
mManager = cm;
mCctModule.setCctManager(cm);
}
PX_FORCE_INLINE CharacterControllerManager* getCctManager() { return mManager; }
PX_FORCE_INLINE PxU64 getContextId() const { return PxU64(mScene); }
PxControllerShapeType::Enum mType;
// User params
CCTParams mUserParams;
PxUserControllerHitReport* mReportCallback;
PxControllerBehaviorCallback* mBehaviorCallback;
void* mUserData;
// Internal data
SweepTest mCctModule; // Internal CCT object. Optim test for Ubi.
PxRigidDynamic* mKineActor; // Associated kinematic actor
PxExtendedVec3 mPosition; // Current position
PxVec3 mDeltaXP;
PxVec3 mOverlapRecover;
PxScene* mScene; // Handy scene owner
PxU32 mPreviousSceneTimestamp;
PxF64 mGlobalTime;
PxF64 mPreviousGlobalTime;
PxF32 mProxyDensity; // Density for proxy actor
PxF32 mProxyScaleCoeff; // Scale coeff for proxy actor
PxControllerCollisionFlags mCollisionFlags; // Last known collision flags (PxControllerCollisionFlag)
bool mCachedStandingOnMoving;
bool mRegisterDeletionListener;
mutable PxMutex mWriteLock; // Lock used for guarding touched pointers and cache data from overwriting
// during onRelease call.
protected:
// Internal methods
void setUpDirectionInternal(const PxVec3& up);
PxShape* getKineShape() const;
bool createProxyActor(PxPhysics& sdk, const PxGeometry& geometry, const PxMaterial& material, PxClientID clientID);
bool setPos(const PxExtendedVec3& pos);
void findTouchedObject(const PxControllerFilters& filters, const PxObstacleContext* obstacleContext, const PxVec3& upDirection);
bool rideOnTouchedObject(SweptVolume& volume, const PxVec3& upDirection, PxVec3& disp, const PxObstacleContext* obstacleContext);
PxControllerCollisionFlags move(SweptVolume& volume, const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles, bool constrainedClimbingMode);
bool filterTouchedShape(const PxControllerFilters& filters);
PX_FORCE_INLINE float computeTimeCoeff()
{
const float elapsedTime = float(mGlobalTime - mPreviousGlobalTime);
mPreviousGlobalTime = mGlobalTime;
return 1.0f / elapsedTime;
}
CharacterControllerManager* mManager; // Owner manager
};
} // namespace Cct
}
/** \endcond */
#endif

View File

@@ -0,0 +1,84 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_INTERNAL_STRUCTS_H
#define CCT_INTERNAL_STRUCTS_H
#include "CctController.h"
namespace physx
{
class PxObstacle; // (*)
namespace Cct
{
class ObstacleContext;
enum UserObjectType
{
USER_OBJECT_CCT = 0,
USER_OBJECT_BOX_OBSTACLE = 1,
USER_OBJECT_CAPSULE_OBSTACLE = 2
};
PX_FORCE_INLINE PxU32 encodeUserObject(PxU32 index, UserObjectType type)
{
PX_ASSERT(index<=0xffff);
PX_ASSERT(PxU32(type)<=0xffff);
return (PxU16(index)<<16)|PxU32(type);
}
PX_FORCE_INLINE UserObjectType decodeType(PxU32 code)
{
return UserObjectType(code & 0xffff);
}
PX_FORCE_INLINE PxU32 decodeIndex(PxU32 code)
{
return code>>16;
}
struct PxInternalCBData_OnHit : InternalCBData_OnHit
{
Controller* controller;
const ObstacleContext* obstacles;
const PxObstacle* touchedObstacle;
PxObstacleHandle touchedObstacleHandle;
};
struct PxInternalCBData_FindTouchedGeom : InternalCBData_FindTouchedGeom
{
PxScene* scene;
PxRenderBuffer* renderBuffer; // Render buffer from controller manager, not the one from the scene
PxHashSet<PxShape*>* cctShapeHashSet;
};
}
}
#endif

View File

@@ -0,0 +1,536 @@
// 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-2025 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 "foundation/PxMemory.h"
#include "CctObstacleContext.h"
#include "CctCharacterControllerManager.h"
#include "foundation/PxUtilities.h"
using namespace physx;
using namespace Cct;
//! Initial list size
#define DEFAULT_HANDLEMANAGER_SIZE 2
HandleManager::HandleManager() : mCurrentNbObjects(0), mNbFreeIndices(0)
{
mMaxNbObjects = DEFAULT_HANDLEMANAGER_SIZE;
mObjects = PX_ALLOCATE(void*, mMaxNbObjects, "HandleManager");
mOutToIn = PX_ALLOCATE(PxU16, mMaxNbObjects, "HandleManager");
mInToOut = PX_ALLOCATE(PxU16, mMaxNbObjects, "HandleManager");
mStamps = PX_ALLOCATE(PxU16, mMaxNbObjects, "HandleManager");
PxMemSet(mOutToIn, 0xff, mMaxNbObjects*sizeof(PxU16));
PxMemSet(mInToOut, 0xff, mMaxNbObjects*sizeof(PxU16));
PxMemZero(mStamps, mMaxNbObjects*sizeof(PxU16));
}
HandleManager::~HandleManager()
{
SetupLists();
}
bool HandleManager::SetupLists(void** objects, PxU16* oti, PxU16* ito, PxU16* stamps)
{
// Release old data
PX_FREE(mStamps);
PX_FREE(mInToOut);
PX_FREE(mOutToIn);
PX_FREE(mObjects);
// Assign new data
mObjects = objects;
mOutToIn = oti;
mInToOut = ito;
mStamps = stamps;
return true;
}
Handle HandleManager::Add(void* object)
{
// Are there any free indices I should recycle?
if(mNbFreeIndices)
{
const PxU16 FreeIndex = mInToOut[mCurrentNbObjects];// Get the recycled virtual index
mObjects[mCurrentNbObjects] = object; // The physical location is always at the end of the list (it never has holes).
mOutToIn[FreeIndex] = PxTo16(mCurrentNbObjects++); // Update virtual-to-physical remapping table.
mNbFreeIndices--;
return Handle((mStamps[FreeIndex]<<16)|FreeIndex); // Return virtual index (handle) to the client app
}
else
{
PX_ASSERT_WITH_MESSAGE(mCurrentNbObjects<0xffff ,"Internal error - 64K objects in HandleManager!");
// Is the array large enough for another entry?
if(mCurrentNbObjects==mMaxNbObjects)
{
// Nope! Resize all arrays (could be avoided with linked lists... one day)
mMaxNbObjects<<=1; // The more you eat, the more you're given
if(mMaxNbObjects>0xffff) mMaxNbObjects = 0xffff; // Clamp to 64K
void** NewList = PX_ALLOCATE(void*, mMaxNbObjects, "HandleManager"); // New physical list
PxU16* NewOTI = PX_ALLOCATE(PxU16, mMaxNbObjects, "HandleManager"); // New remapping table
PxU16* NewITO = PX_ALLOCATE(PxU16, mMaxNbObjects, "HandleManager"); // New remapping table
PxU16* NewStamps = PX_ALLOCATE(PxU16, mMaxNbObjects, "HandleManager"); // New stamps
PxMemCopy(NewList, mObjects, mCurrentNbObjects*sizeof(void*)); // Copy old data
PxMemCopy(NewOTI, mOutToIn, mCurrentNbObjects*sizeof(PxU16)); // Copy old data
PxMemCopy(NewITO, mInToOut, mCurrentNbObjects*sizeof(PxU16)); // Copy old data
PxMemCopy(NewStamps, mStamps, mCurrentNbObjects*sizeof(PxU16)); // Copy old data
PxMemSet(NewOTI+mCurrentNbObjects, 0xff, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16));
PxMemSet(NewITO+mCurrentNbObjects, 0xff, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16));
PxMemZero(NewStamps+mCurrentNbObjects, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16));
SetupLists(NewList, NewOTI, NewITO, NewStamps);
}
mObjects[mCurrentNbObjects] = object; // Store object at mCurrentNbObjects = physical index = virtual index
mOutToIn[mCurrentNbObjects] = PxTo16(mCurrentNbObjects); // Update virtual-to-physical remapping table.
mInToOut[mCurrentNbObjects] = PxTo16(mCurrentNbObjects); // Update physical-to-virtual remapping table.
PxU32 tmp = mCurrentNbObjects++;
return (mStamps[tmp]<<16)|tmp; // Return virtual index (handle) to the client app
}
}
void HandleManager::Remove(Handle handle)
{
const PxU16 VirtualIndex = PxU16(handle);
if(VirtualIndex>=mMaxNbObjects) return; // Invalid handle
const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get the physical index
if(PhysicalIndex==0xffff) return; // Value has already been deleted
if(PhysicalIndex>=mMaxNbObjects) return; // Invalid index
// There must be at least one valid entry.
if(mCurrentNbObjects)
{
if(mStamps[VirtualIndex]!=handle>>16) return; // Stamp mismatch => index has been recycled
// Update list so that there's no hole
mObjects[PhysicalIndex] = mObjects[--mCurrentNbObjects];// Move the real object so that the array has no holes.
mOutToIn[mInToOut[mCurrentNbObjects]] = PhysicalIndex; // Update virtual-to-physical remapping table.
mInToOut[PhysicalIndex] = mInToOut[mCurrentNbObjects]; // Update physical-to-virtual remapping table.
// Keep track of the recyclable virtual index
mInToOut[mCurrentNbObjects] = VirtualIndex; // Store the free virtual index/handle at the end of mInToOut
mOutToIn[VirtualIndex] = 0xffff; // Invalidate the entry
mNbFreeIndices++; // One more free index
mStamps[VirtualIndex]++; // Update stamp
}
}
void* HandleManager::GetObject(Handle handle) const
{
const PxU16 VirtualIndex = PxU16(handle);
if(VirtualIndex>=mMaxNbObjects) return NULL; // Invalid handle
const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get physical index
if(PhysicalIndex==0xffff) return NULL; // Object has been deleted
if(PhysicalIndex>=mMaxNbObjects) return NULL; // Index is invalid
if(mStamps[VirtualIndex]!=handle>>16) return NULL; // Index has been recycled
return mObjects[PhysicalIndex]; // Returns stored pointer
}
bool HandleManager::UpdateObject(Handle handle, void* object)
{
const PxU16 VirtualIndex = PxU16(handle);
if(VirtualIndex>=mMaxNbObjects) return false; // Invalid handle
const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get physical index
if(PhysicalIndex==0xffff) return false; // Object has been deleted
if(PhysicalIndex>=mMaxNbObjects) return false; // Index is invalid
if(mStamps[VirtualIndex]!=handle>>16) return false; // Index has been recycled
mObjects[PhysicalIndex] = object; // Updates stored pointer
return true;
}
// PT: please do not expose these functions outside of this class,
// as we want to retain the ability to easily modify the handle format if necessary.
#define NEW_ENCODING
#ifdef NEW_ENCODING
static PX_FORCE_INLINE void* encodeInternalHandle(PxU32 index, PxGeometryType::Enum type)
{
PX_ASSERT(index<=0xffff);
PX_ASSERT(PxU32(type)<=0xffff);
// PT: we do type+1 to ban internal handles with a 0 value, since it's reserved for NULL pointers
return reinterpret_cast<void*>((size_t(index)<<16)|(size_t(type)+1));
}
static PX_FORCE_INLINE PxGeometryType::Enum decodeInternalType(void* handle)
{
return PxGeometryType::Enum((PxU32(size_t(handle)) & 0xffff)-1);
}
static PX_FORCE_INLINE PxU32 decodeInternalIndex(void* handle)
{
return (PxU32(size_t(handle)))>>16;
}
#else
static PX_FORCE_INLINE ObstacleHandle encodeHandle(PxU32 index, PxGeometryType::Enum type)
{
PX_ASSERT(index<=0xffff);
PX_ASSERT(type<=0xffff);
return (PxU16(index)<<16)|PxU32(type);
}
static PX_FORCE_INLINE PxGeometryType::Enum decodeType(ObstacleHandle handle)
{
return PxGeometryType::Enum(handle & 0xffff);
}
static PX_FORCE_INLINE PxU32 decodeIndex(ObstacleHandle handle)
{
return handle>>16;
}
#endif
ObstacleContext::ObstacleContext(CharacterControllerManager& cctMan)
: mCCTManager(cctMan)
{
}
ObstacleContext::~ObstacleContext()
{
}
void ObstacleContext::release()
{
mCCTManager.releaseObstacleContext(*this);
}
PxControllerManager& ObstacleContext::getControllerManager() const
{
return mCCTManager;
}
PxObstacleHandle ObstacleContext::addObstacle(const PxObstacle& obstacle)
{
const PxGeometryType::Enum type = obstacle.getType();
if(type==PxGeometryType::eBOX)
{
const PxU32 index = mBoxObstacles.size();
#ifdef NEW_ENCODING
const PxObstacleHandle handle = mHandleManager.Add(encodeInternalHandle(index, type));
#else
const PxObstacleHandle handle = encodeHandle(index, type);
#endif
mBoxObstacles.pushBack(InternalBoxObstacle(handle, static_cast<const PxBoxObstacle&>(obstacle)));
mCCTManager.onObstacleAdded(handle, this);
return handle;
}
else if(type==PxGeometryType::eCAPSULE)
{
const PxU32 index = mCapsuleObstacles.size();
#ifdef NEW_ENCODING
const PxObstacleHandle handle = mHandleManager.Add(encodeInternalHandle(index, type));
#else
const PxObstacleHandle handle = encodeHandle(index, type);
#endif
mCapsuleObstacles.pushBack(InternalCapsuleObstacle(handle, static_cast<const PxCapsuleObstacle&>(obstacle)));
mCCTManager.onObstacleAdded(handle, this);
return handle;
}
else return PX_INVALID_OBSTACLE_HANDLE;
}
#ifdef NEW_ENCODING
template<class T>
static PX_FORCE_INLINE void remove(HandleManager& manager, void* object, PxObstacleHandle handle, PxU32 index, PxU32 size, const PxArray<T>& obstacles)
{
manager.Remove(handle);
if(index!=size-1)
{
bool status = manager.UpdateObject(obstacles[size-1].mHandle, object);
PX_ASSERT(status);
PX_UNUSED(status);
}
}
#endif
bool ObstacleContext::removeObstacle(PxObstacleHandle handle)
{
#ifdef NEW_ENCODING
void* object = mHandleManager.GetObject(handle);
if(!object)
return false;
const PxGeometryType::Enum type = decodeInternalType(object);
const PxU32 index = decodeInternalIndex(object);
#else
const PxGeometryType::Enum type = decodeType(handle);
const PxU32 index = decodeIndex(handle);
#endif
if(type==PxGeometryType::eBOX)
{
const PxU32 size = mBoxObstacles.size();
PX_ASSERT(index<size);
if(index>=size)
return false;
#ifdef NEW_ENCODING
remove<InternalBoxObstacle>(mHandleManager, object, handle, index, size, mBoxObstacles);
#endif
mBoxObstacles.replaceWithLast(index);
#ifdef NEW_ENCODING
mCCTManager.onObstacleRemoved(handle);
#else
mCCTManager.onObstacleRemoved(handle, encodeHandle(size-1, type));
#endif
return true;
}
else if(type==PxGeometryType::eCAPSULE)
{
const PxU32 size = mCapsuleObstacles.size();
PX_ASSERT(index<size);
if(index>=size)
return false;
#ifdef NEW_ENCODING
remove<InternalCapsuleObstacle>(mHandleManager, object, handle, index, size, mCapsuleObstacles);
#endif
mCapsuleObstacles.replaceWithLast(index);
#ifdef NEW_ENCODING
mCCTManager.onObstacleRemoved(handle);
#else
mCCTManager.onObstacleRemoved(handle, encodeHandle(size-1, type));
#endif
return true;
}
else return false;
}
bool ObstacleContext::updateObstacle(PxObstacleHandle handle, const PxObstacle& obstacle)
{
#ifdef NEW_ENCODING
void* object = mHandleManager.GetObject(handle);
if(!object)
return false;
const PxGeometryType::Enum type = decodeInternalType(object);
PX_ASSERT(type==obstacle.getType());
if(type!=obstacle.getType())
return false;
const PxU32 index = decodeInternalIndex(object);
#else
const PxGeometryType::Enum type = decodeType(handle);
PX_ASSERT(type==obstacle.getType());
if(type!=obstacle.getType())
return false;
const PxU32 index = decodeIndex(handle);
#endif
if(type==PxGeometryType::eBOX)
{
const PxU32 size = mBoxObstacles.size();
PX_ASSERT(index<size);
if(index>=size)
return false;
mBoxObstacles[index].mData = static_cast<const PxBoxObstacle&>(obstacle);
mCCTManager.onObstacleUpdated(handle,this);
return true;
}
else if(type==PxGeometryType::eCAPSULE)
{
const PxU32 size = mCapsuleObstacles.size();
PX_ASSERT(index<size);
if(index>=size)
return false;
mCapsuleObstacles[index].mData = static_cast<const PxCapsuleObstacle&>(obstacle);
mCCTManager.onObstacleUpdated(handle,this);
return true;
}
else return false;
}
PxU32 ObstacleContext::getNbObstacles() const
{
return mBoxObstacles.size() + mCapsuleObstacles.size();
}
const PxObstacle* ObstacleContext::getObstacle(PxU32 i) const
{
const PxU32 nbBoxes = mBoxObstacles.size();
if(i<nbBoxes)
return &mBoxObstacles[i].mData;
i -= nbBoxes;
const PxU32 nbCapsules = mCapsuleObstacles.size();
if(i<nbCapsules)
return &mCapsuleObstacles[i].mData;
return NULL;
}
const PxObstacle* ObstacleContext::getObstacleByHandle(PxObstacleHandle handle) const
{
#ifdef NEW_ENCODING
void* object = mHandleManager.GetObject(handle);
if(!object)
return NULL;
const PxGeometryType::Enum type = decodeInternalType(object);
const PxU32 index = decodeInternalIndex(object);
#else
const PxGeometryType::Enum type = decodeType(handle);
const PxU32 index = decodeIndex(handle);
#endif
if(type == PxGeometryType::eBOX)
{
const PxU32 size = mBoxObstacles.size();
if(index>=size)
return NULL;
PX_ASSERT(mBoxObstacles[index].mHandle==handle);
return &mBoxObstacles[index].mData;
}
else if(type==PxGeometryType::eCAPSULE)
{
const PxU32 size = mCapsuleObstacles.size();
if(index>=size)
return NULL;
PX_ASSERT(mCapsuleObstacles[index].mHandle==handle);
return &mCapsuleObstacles[index].mData;
}
else return NULL;
}
#include "GuRaycastTests.h"
#include "geometry/PxBoxGeometry.h"
#include "geometry/PxCapsuleGeometry.h"
using namespace Gu;
const PxObstacle* ObstacleContext::raycastSingle(PxGeomRaycastHit& hit, const PxVec3& origin, const PxVec3& unitDir, PxReal distance, PxObstacleHandle& obstacleHandle) const
{
PxGeomRaycastHit localHit;
PxF32 t = FLT_MAX;
const PxObstacle* touchedObstacle = NULL;
const PxHitFlags hitFlags = PxHitFlags(0);
{
const RaycastFunc raycastFunc = Gu::getRaycastFuncTable()[PxGeometryType::eBOX];
PX_ASSERT(raycastFunc);
const PxU32 nbExtraBoxes = mBoxObstacles.size();
for(PxU32 i=0;i<nbExtraBoxes;i++)
{
const PxBoxObstacle& userBoxObstacle = mBoxObstacles[i].mData;
PxU32 status = raycastFunc( PxBoxGeometry(userBoxObstacle.mHalfExtents),
PxTransform(toVec3(userBoxObstacle.mPos), userBoxObstacle.mRot),
origin, unitDir, distance,
hitFlags,
1, &localHit, sizeof(PxGeomRaycastHit), UNUSED_RAYCAST_THREAD_CONTEXT);
if(status && localHit.distance<t)
{
t = localHit.distance;
hit = localHit;
obstacleHandle = mBoxObstacles[i].mHandle;
touchedObstacle = &userBoxObstacle;
}
}
}
{
const RaycastFunc raycastFunc = Gu::getRaycastFuncTable()[PxGeometryType::eCAPSULE];
PX_ASSERT(raycastFunc);
const PxU32 nbExtraCapsules = mCapsuleObstacles.size();
for(PxU32 i=0;i<nbExtraCapsules;i++)
{
const PxCapsuleObstacle& userCapsuleObstacle = mCapsuleObstacles[i].mData;
PxU32 status = raycastFunc( PxCapsuleGeometry(userCapsuleObstacle.mRadius, userCapsuleObstacle.mHalfHeight),
PxTransform(toVec3(userCapsuleObstacle.mPos), userCapsuleObstacle.mRot),
origin, unitDir, distance,
hitFlags,
1, &localHit, sizeof(PxGeomRaycastHit), UNUSED_RAYCAST_THREAD_CONTEXT);
if(status && localHit.distance<t)
{
t = localHit.distance;
hit = localHit;
obstacleHandle = mCapsuleObstacles[i].mHandle;
touchedObstacle = &userCapsuleObstacle;
}
}
}
return touchedObstacle;
}
const PxObstacle* ObstacleContext::raycastSingle(PxGeomRaycastHit& hit, const PxObstacleHandle& obstacleHandle, const PxVec3& origin, const PxVec3& unitDir, PxReal distance) const
{
const PxHitFlags hitFlags = PxHitFlags(0);
#ifdef NEW_ENCODING
void* object = mHandleManager.GetObject(obstacleHandle);
if(!object)
return NULL;
const PxGeometryType::Enum geomType = decodeInternalType(object);
const PxU32 index = decodeInternalIndex(object);
#else
const PxGeometryType::Enum geomType = decodeType(obstacleHandle);
const PxU32 index = decodeIndex(obstacleHandle);
#endif
if(geomType == PxGeometryType::eBOX)
{
const PxBoxObstacle& userBoxObstacle = mBoxObstacles[index].mData;
PxU32 status = Gu::getRaycastFuncTable()[PxGeometryType::eBOX](
PxBoxGeometry(userBoxObstacle.mHalfExtents),
PxTransform(toVec3(userBoxObstacle.mPos), userBoxObstacle.mRot),
origin, unitDir, distance,
hitFlags,
1, &hit, sizeof(PxGeomRaycastHit), UNUSED_RAYCAST_THREAD_CONTEXT);
if(status)
{
return &userBoxObstacle;
}
}
else
{
PX_ASSERT(geomType == PxGeometryType::eCAPSULE);
const PxCapsuleObstacle& userCapsuleObstacle = mCapsuleObstacles[index].mData;
PxU32 status = Gu::getRaycastFuncTable()[PxGeometryType::eCAPSULE](
PxCapsuleGeometry(userCapsuleObstacle.mRadius, userCapsuleObstacle.mHalfHeight),
PxTransform(toVec3(userCapsuleObstacle.mPos), userCapsuleObstacle.mRot),
origin, unitDir, distance,
hitFlags,
1, &hit, sizeof(PxGeomRaycastHit), UNUSED_RAYCAST_THREAD_CONTEXT);
if(status)
{
return &userCapsuleObstacle;
}
}
return NULL;
}
void ObstacleContext::onOriginShift(const PxVec3& shift)
{
for(PxU32 i=0; i < mBoxObstacles.size(); i++)
sub(mBoxObstacles[i].mData.mPos, shift);
for(PxU32 i=0; i < mCapsuleObstacles.size(); i++)
sub(mCapsuleObstacles[i].mData.mPos, shift);
}

View File

@@ -0,0 +1,134 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_OBSTACLE_CONTEXT
#define CCT_OBSTACLE_CONTEXT
/* Exclude from documentation */
/** \cond */
#include "characterkinematic/PxControllerObstacles.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxArray.h"
namespace physx
{
struct PxGeomRaycastHit;
namespace Cct
{
class CharacterControllerManager;
typedef PxU32 Handle;
class HandleManager : public PxUserAllocated
{
public:
HandleManager();
~HandleManager();
Handle Add(void* object);
void Remove(Handle handle);
void* GetObject(Handle handle) const; // Returns object according to handle.
bool UpdateObject(Handle handle, void* object);
PX_FORCE_INLINE PxU32 GetMaxNbObjects() const { return mMaxNbObjects; } //!< Returns max number of objects
PX_FORCE_INLINE PxU32 GetNbObjects() const { return mCurrentNbObjects; } //!< Returns current number of objects
PX_FORCE_INLINE void** GetObjects() const { return mObjects; } //!< Gets the complete list of objects
PX_FORCE_INLINE void* PickObject(Handle handle) const { return mObjects[mOutToIn[PxU16(handle)]]; }
private:
// Physical list
void** mObjects; //!< Physical list, with no holes but unsorted.
PxU32 mCurrentNbObjects; //!< Current number of objects in the physical list.
PxU32 mMaxNbObjects; //!< Maximum possible number of objects in the physical list.
// Cross-references
PxU16* mOutToIn; //!< Maps virtual indices (handles) to real ones.
PxU16* mInToOut; //!< Maps real indices to virtual ones (handles).
PxU16* mStamps;
// Recycled locations
PxU32 mNbFreeIndices; //!< Current number of free indices
// Internal methods
bool SetupLists(void** objects=NULL, PxU16* oti=NULL, PxU16* ito=NULL, PxU16* stamps=NULL);
};
class ObstacleContext : public PxObstacleContext, public PxUserAllocated
{
PX_NOCOPY(ObstacleContext)
public:
ObstacleContext(CharacterControllerManager& );
virtual ~ObstacleContext();
// PxObstacleContext
virtual void release() PX_OVERRIDE PX_FINAL;
virtual PxControllerManager& getControllerManager() const PX_OVERRIDE PX_FINAL;
virtual PxObstacleHandle addObstacle(const PxObstacle& obstacle) PX_OVERRIDE PX_FINAL;
virtual bool removeObstacle(PxObstacleHandle handle) PX_OVERRIDE PX_FINAL;
virtual bool updateObstacle(PxObstacleHandle handle, const PxObstacle& obstacle) PX_OVERRIDE PX_FINAL;
virtual PxU32 getNbObstacles() const PX_OVERRIDE PX_FINAL;
virtual const PxObstacle* getObstacle(PxU32 i) const PX_OVERRIDE PX_FINAL;
virtual const PxObstacle* getObstacleByHandle(PxObstacleHandle handle) const PX_OVERRIDE PX_FINAL;
//~PxObstacleContext
const PxObstacle* raycastSingle(PxGeomRaycastHit& hit, const PxVec3& origin, const PxVec3& unitDir, PxReal distance, PxObstacleHandle& obstacleHandle) const;
const PxObstacle* raycastSingle(PxGeomRaycastHit& hit, const PxObstacleHandle& obstacleHandle, const PxVec3& origin, const PxVec3& unitDir, PxReal distance) const; // raycast just one obstacle handle
void onOriginShift(const PxVec3& shift);
struct InternalBoxObstacle
{
InternalBoxObstacle(PxObstacleHandle handle, const PxBoxObstacle& data) : mHandle(handle), mData(data) {}
PxObstacleHandle mHandle;
PxBoxObstacle mData;
};
PxArray<InternalBoxObstacle> mBoxObstacles;
struct InternalCapsuleObstacle
{
InternalCapsuleObstacle(PxObstacleHandle handle, const PxCapsuleObstacle& data) : mHandle(handle), mData(data) {}
PxObstacleHandle mHandle;
PxCapsuleObstacle mData;
};
PxArray<InternalCapsuleObstacle> mCapsuleObstacles;
private:
HandleManager mHandleManager;
CharacterControllerManager& mCCTManager;
};
} // namespace Cct
}
/** \endcond */
#endif

View File

@@ -0,0 +1,49 @@
// 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-2025 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 "CctSweptBox.h"
#include "CctCharacterController.h"
using namespace physx;
using namespace Cct;
SweptBox::SweptBox()
{
mType = SweptVolumeType::eBOX;
}
SweptBox::~SweptBox()
{
}
void SweptBox::computeTemporalBox(const SweepTest& test, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const
{
const float radius = PxMax(mExtents.y, mExtents.z);
const float height = 2.0f * mExtents.x;
Cct::computeTemporalBox(box, radius, height, test.mUserParams.mContactOffset, test.mUserParams.mMaxJumpHeight, test.mUserParams.mUpDirection, center, direction);
}

View File

@@ -0,0 +1,53 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_SWEPT_BOX
#define CCT_SWEPT_BOX
#include "CctSweptVolume.h"
namespace physx
{
namespace Cct
{
class SweptBox : public SweptVolume
{
public:
SweptBox();
virtual ~SweptBox();
virtual void computeTemporalBox(const SweepTest&, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const PX_OVERRIDE PX_FINAL;
PxVec3 mExtents;
};
} // namespace Cct
}
#endif

View File

@@ -0,0 +1,47 @@
// 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-2025 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 "CctSweptCapsule.h"
#include "CctCharacterController.h"
using namespace physx;
using namespace Cct;
SweptCapsule::SweptCapsule()
{
mType = SweptVolumeType::eCAPSULE;
}
SweptCapsule::~SweptCapsule()
{
}
void SweptCapsule::computeTemporalBox(const SweepTest& test, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const
{
Cct::computeTemporalBox(box, mRadius, mHeight, test.mUserParams.mContactOffset, test.mUserParams.mMaxJumpHeight, test.mUserParams.mUpDirection, center, direction);
}

View File

@@ -0,0 +1,54 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_SWEPT_CAPSULE
#define CCT_SWEPT_CAPSULE
#include "CctSweptVolume.h"
namespace physx
{
namespace Cct
{
class SweptCapsule : public SweptVolume
{
public:
SweptCapsule();
virtual ~SweptCapsule();
virtual void computeTemporalBox(const SweepTest&, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const PX_OVERRIDE PX_FINAL;
PxF32 mRadius;
PxF32 mHeight;
};
} // namespace Cct
}
#endif

View File

@@ -0,0 +1,73 @@
// 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-2025 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 "CctSweptVolume.h"
using namespace physx;
using namespace Cct;
SweptVolume::SweptVolume()
{
mType = SweptVolumeType::eLAST;
}
SweptVolume::~SweptVolume()
{
}
void Cct::computeTemporalBox(PxExtendedBounds3& _box, float radius, float height, float contactOffset, float maxJumpHeight, const PxVec3& upDirection, const PxExtendedVec3& center, const PxVec3& direction)
{
const float r = radius + contactOffset;
PxVec3 extents(r);
const float halfHeight = height*0.5f;
extents.x += fabsf(upDirection.x)*halfHeight;
extents.y += fabsf(upDirection.y)*halfHeight;
extents.z += fabsf(upDirection.z)*halfHeight;
PxExtendedBounds3 box;
setCenterExtents(box, center, extents);
{
PxExtendedBounds3 destBox;
PxExtendedVec3 tmp = center;
add(tmp, direction);
setCenterExtents(destBox, tmp, extents);
add(box, destBox);
}
if(maxJumpHeight!=0.0f)
{
PxExtendedBounds3 destBox;
PxExtendedVec3 tmp = center;
sub(tmp, upDirection * maxJumpHeight);
setCenterExtents(destBox, tmp, extents);
add(box, destBox);
}
_box = box;
}

View File

@@ -0,0 +1,74 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_SWEPT_VOLUME
#define CCT_SWEPT_VOLUME
#include "CctUtils.h"
namespace physx
{
namespace Cct
{
struct SweptVolumeType
{
enum Enum
{
eBOX,
eCAPSULE,
eLAST
};
};
class SweepTest;
class SweptVolume
{
public:
SweptVolume();
virtual ~SweptVolume();
virtual void computeTemporalBox(const SweepTest& test, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const = 0;
PX_FORCE_INLINE SweptVolumeType::Enum getType() const { return mType; }
PxExtendedVec3 mCenter;
PxF32 mHalfHeight; // UBI
protected:
SweptVolumeType::Enum mType;
};
void computeTemporalBox(PxExtendedBounds3& _box, float radius, float height, float contactOffset, float maxJumpHeight, const PxVec3& upDirection, const PxExtendedVec3& center, const PxVec3& direction);
} // namespace Cct
}
#endif

View File

@@ -0,0 +1,236 @@
// 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-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef CCT_UTILS
#define CCT_UTILS
#include "extensions/PxShapeExt.h"
#include "characterkinematic/PxExtended.h"
namespace physx
{
PX_FORCE_INLINE bool testSlope(const PxVec3& normal, const PxVec3& upDirection, PxF32 slopeLimit)
{
const float dp = normal.dot(upDirection);
return dp>=0.0f && dp<slopeLimit;
}
PX_INLINE PxTransform getShapeGlobalPose(const PxShape& shape, const PxRigidActor& actor)
{
return PxShapeExt::getGlobalPose(shape, actor);
}
PX_INLINE void decomposeVector(PxVec3& normalCompo, PxVec3& tangentCompo, const PxVec3& outwardDir,
const PxVec3& outwardNormal)
{
normalCompo = outwardNormal * (outwardDir.dot(outwardNormal));
tangentCompo = outwardDir - normalCompo;
}
PX_FORCE_INLINE bool isAlmostZero(const PxVec3& v)
{
if (PxAbs(v.x) > 1e-6f || PxAbs(v.y) > 1e-6f || PxAbs(v.z) > 1e-6f)
return false;
return true;
}
#ifdef PX_BIG_WORLDS
PX_INLINE void add(PxExtendedVec3& p, const PxVec3& e)
{
p += PxExtendedVec3(PxExtended(e.x), PxExtended(e.y), PxExtended(e.z));
}
PX_INLINE void sub(PxExtendedVec3& p, const PxVec3& e)
{
p -= PxExtendedVec3(PxExtended(e.x), PxExtended(e.y), PxExtended(e.z));
}
PX_INLINE PxExtended dot(const PxExtendedVec3& p, const PxVec3& e)
{
return p.dot(PxExtendedVec3(PxExtended(e.x), PxExtended(e.y), PxExtended(e.z)));
}
class PxExtendedBox
{
public:
PX_INLINE PxExtendedBox() {}
PX_INLINE PxExtendedBox(const PxExtendedVec3& _center, const PxVec3& _extents, const PxQuat& _rot) : center(_center), extents(_extents), rot(_rot){}
PX_INLINE ~PxExtendedBox() {}
PxExtendedVec3 center;
PxVec3 extents;
PxQuat rot;
};
class PxExtendedSphere
{
public:
PX_INLINE PxExtendedSphere() {}
PX_INLINE ~PxExtendedSphere() {}
PX_INLINE PxExtendedSphere(const PxExtendedVec3& _center, PxF32 _radius) : center(_center), radius(_radius) {}
PX_INLINE PxExtendedSphere(const PxExtendedSphere& sphere) : center(sphere.center), radius(sphere.radius) {}
PxExtendedVec3 center; //!< Sphere's center
PxF32 radius; //!< Sphere's radius
};
struct PxExtendedSegment
{
PX_INLINE const PxExtendedVec3& getOrigin() const
{
return p0;
}
PX_INLINE void computeDirection(PxVec3& dir) const
{
dir = diff(p1, p0);
}
PX_INLINE void computePoint(PxExtendedVec3& pt, PxExtended t) const
{
pt.x = p0.x + t * (p1.x - p0.x);
pt.y = p0.y + t * (p1.y - p0.y);
pt.z = p0.z + t * (p1.z - p0.z);
}
PxExtendedVec3 p0; //!< Start of segment
PxExtendedVec3 p1; //!< End of segment
};
struct PxExtendedCapsule : public PxExtendedSegment
{
PxReal radius;
};
struct PxExtendedBounds3
{
PX_INLINE PxExtendedBounds3()
{
}
PX_INLINE void setEmpty()
{
// We now use this particular pattern for empty boxes
set(PX_MAX_EXTENDED, PX_MAX_EXTENDED, PX_MAX_EXTENDED,
-PX_MAX_EXTENDED, -PX_MAX_EXTENDED, -PX_MAX_EXTENDED);
}
PX_INLINE void set(PxExtended minx, PxExtended miny, PxExtended minz, PxExtended maxx, PxExtended maxy, PxExtended maxz)
{
minimum = PxExtendedVec3(minx, miny, minz);
maximum = PxExtendedVec3(maxx, maxy, maxz);
}
PX_INLINE bool isInside(const PxExtendedBounds3& box) const
{
if(box.minimum.x > minimum.x) return false;
if(box.minimum.y > minimum.y) return false;
if(box.minimum.z > minimum.z) return false;
if(box.maximum.x < maximum.x) return false;
if(box.maximum.y < maximum.y) return false;
if(box.maximum.z < maximum.z) return false;
return true;
}
PxExtendedVec3 minimum, maximum;
};
PX_INLINE void getCenter(const PxExtendedBounds3& b, PxExtendedVec3& center)
{
center = b.minimum + b.maximum;
center *= 0.5;
}
PX_INLINE void getExtents(const PxExtendedBounds3& b, PxVec3& extents)
{
extents = diff(b.maximum, b.minimum);
extents *= 0.5f;
}
PX_INLINE void setCenterExtents(PxExtendedBounds3& b, const PxExtendedVec3& c, const PxVec3& e)
{
const PxExtendedVec3 eExt(PxExtended(e.x), PxExtended(e.y), PxExtended(e.z));
b.minimum = c - eExt;
b.maximum = c + eExt;
}
PX_INLINE void add(PxExtendedBounds3& b, const PxExtendedBounds3& b2)
{
// - if we're empty, minimum = MAX,MAX,MAX => minimum will be b2 in all cases => it will copy b2, ok
// - if b2 is empty, the opposite happens => keep us unchanged => ok
// => same behaviour as before, automatically
b.minimum = b.minimum.minimum(b2.minimum);
b.maximum = b.maximum.maximum(b2.maximum);
}
#else
#include "foundation/PxBounds3.h"
#include "GuBox.h"
#include "GuCapsule.h"
#include "GuPlane.h"
typedef Gu::Box PxExtendedBox;
typedef Gu::Sphere PxExtendedSphere;
typedef Gu::Segment PxExtendedSegment;
typedef Gu::Capsule PxExtendedCapsule;
typedef PxBounds3 PxExtendedBounds3;
PX_INLINE void getCenter(const PxBounds3& b, PxVec3& center)
{
center = b.minimum + b.maximum;
center *= 0.5;
}
PX_INLINE void getExtents(const PxBounds3& b, PxVec3& extents)
{
extents = b.maximum - b.minimum;
extents *= 0.5f;
}
PX_INLINE void setCenterExtents(PxBounds3& b, const PxVec3& center, const PxVec3& extents)
{
b.minimum = center - extents;
b.maximum = center + extents;
}
PX_INLINE void add(PxBounds3& b, const PxBounds3& b2)
{
// - if we're empty, minimum = MAX,MAX,MAX => minimum will be b2 in all cases => it will copy b2, ok
// - if b2 is empty, the opposite happens => keep us unchanged => ok
// => same behaviour as before, automatically
b.minimum = b.minimum.minimum(b2.minimum);
b.maximum = b.maximum.maximum(b2.maximum);
}
#endif
}
#endif