diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/PhysXCharacterKinematic/src | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'PhysX_3.4/Source/PhysXCharacterKinematic/src')
21 files changed, 7212 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctBoxController.cpp b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctBoxController.cpp new file mode 100644 index 00000000..02122c89 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctBoxController.cpp @@ -0,0 +1,197 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PxController.h" +#include "CctBoxController.h" +#include "CctCharacterControllerManager.h" +#include "PxBoxGeometry.h" +#include "PxRigidDynamic.h" +#include "PxShape.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); +} + +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(); + + PX_ASSERT(shape->getGeometryType() == PxGeometryType::eBOX); + PxBoxGeometry bg; + shape->getBoxGeometry(bg); + + 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 + groundPosition -= mUserParams.mUpDirection * (mHalfHeight + mUserParams.mContactOffset); // Ground + return groundPosition; +} + +bool BoxController::setFootPosition(const PxExtendedVec3& position) +{ + PxExtendedVec3 centerPosition = position; + 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(); + pos += mUserParams.mUpDirection * delta; + setPosition(pos); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctBoxController.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctBoxController.h new file mode 100644 index 00000000..237b5a27 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctBoxController.h @@ -0,0 +1,112 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "CctController.h" +#include "PxBoxController.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 { return mHalfHeight; } + virtual bool getWorldBox(PxExtendedBounds3& box) const; + virtual PxController* getPxController() { return this; } + //~Controller + + // PxController + virtual PxControllerShapeType::Enum getType() const { return mType; } + virtual void release() { releaseInternal(); } + virtual PxControllerCollisionFlags move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles); + virtual bool setPosition(const PxExtendedVec3& position) { return setPos(position); } + virtual const PxExtendedVec3& getPosition() const { return mPosition; } + virtual bool setFootPosition(const PxExtendedVec3& position); + virtual PxExtendedVec3 getFootPosition() const; + virtual PxRigidDynamic* getActor() const { return mKineActor; } + virtual void setStepOffset(const float offset) { if(offset>0.0f) + mUserParams.mStepOffset = offset; } + virtual PxF32 getStepOffset() const { return mUserParams.mStepOffset; } + virtual void setNonWalkableMode(PxControllerNonWalkableMode::Enum flag) { mUserParams.mNonWalkableMode = flag; } + virtual PxControllerNonWalkableMode::Enum getNonWalkableMode() const { return mUserParams.mNonWalkableMode; } + virtual PxF32 getContactOffset() const { return mUserParams.mContactOffset; } + virtual void setContactOffset(PxF32 offset) { if(offset>0.0f) + mUserParams.mContactOffset = offset;} + virtual PxVec3 getUpDirection() const { return mUserParams.mUpDirection; } + virtual void setUpDirection(const PxVec3& up) { setUpDirectionInternal(up); } + virtual PxF32 getSlopeLimit() const { return mUserParams.mSlopeLimit; } + virtual void setSlopeLimit(PxF32 slopeLimit) { if(slopeLimit>0.0f) + mUserParams.mSlopeLimit = slopeLimit;} + virtual void invalidateCache(); + virtual PxScene* getScene() { return mScene; } + virtual void* getUserData() const { return mUserData; } + virtual void setUserData(void* userData) { mUserData = userData; } + virtual void getState(PxControllerState& state) const { return getInternalState(state); } + virtual void getStats(PxControllerStats& stats) const { return getInternalStats(stats); } + virtual void resize(PxReal height); + //~PxController + + // PxBoxController + virtual PxF32 getHalfHeight() const; + virtual PxF32 getHalfSideExtent() const; + virtual PxF32 getHalfForwardExtent() const; + virtual bool setHalfHeight(PxF32 halfHeight); + virtual bool setHalfSideExtent(PxF32 halfSideExtent); + virtual bool setHalfForwardExtent(PxF32 halfForwardExtent); + //~ PxBoxController + + PxF32 mHalfHeight; + PxF32 mHalfSideExtent; + PxF32 mHalfForwardExtent; + + bool updateKinematicProxy(); + void getOBB(PxExtendedBox& obb) const; + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCapsuleController.cpp b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCapsuleController.cpp new file mode 100644 index 00000000..6973fdf6 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCapsuleController.cpp @@ -0,0 +1,192 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PxController.h" +#include "CctCapsuleController.h" +#include "CctCharacterControllerManager.h" +#include "PxCapsuleGeometry.h" +#include "PxRigidDynamic.h" +#include "PxShape.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); +} + +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(); + + PX_ASSERT(shape->getGeometryType() == PxGeometryType::eCAPSULE); + PxCapsuleGeometry cg; + shape->getCapsuleGeometry(cg); + + 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(); + + PX_ASSERT(shape->getGeometryType() == PxGeometryType::eCAPSULE); + PxCapsuleGeometry cg; + shape->getCapsuleGeometry(cg); + + 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 + groundPosition -= mUserParams.mUpDirection * (mUserParams.mContactOffset+mRadius+mHeight*0.5f); // Ground + return groundPosition; +} + +bool CapsuleController::setFootPosition(const PxExtendedVec3& position) +{ + PxExtendedVec3 centerPosition = position; + 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; + p0 -= extents; + 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(); + pos += mUserParams.mUpDirection * delta * 0.5f; + setPosition(pos); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCapsuleController.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCapsuleController.h new file mode 100644 index 00000000..784db5a1 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCapsuleController.h @@ -0,0 +1,111 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "CctController.h" +#include "PxCapsuleController.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 { return mRadius+mHeight*0.5f; } + virtual bool getWorldBox(PxExtendedBounds3& box) const; + virtual PxController* getPxController() { return this; } + //~Controller + + // PxController + virtual PxControllerShapeType::Enum getType() const { return mType; } + virtual void release() { releaseInternal(); } + virtual PxControllerCollisionFlags move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles); + virtual bool setPosition(const PxExtendedVec3& position) { return setPos(position); } + virtual const PxExtendedVec3& getPosition() const { return mPosition; } + virtual bool setFootPosition(const PxExtendedVec3& position); + virtual PxExtendedVec3 getFootPosition() const; + virtual PxRigidDynamic* getActor() const { return mKineActor; } + virtual void setStepOffset(const float offset) { if(offset>0.0f) + mUserParams.mStepOffset = offset; } + virtual PxF32 getStepOffset() const { return mUserParams.mStepOffset; } + virtual void setNonWalkableMode(PxControllerNonWalkableMode::Enum flag) { mUserParams.mNonWalkableMode = flag; } + virtual PxControllerNonWalkableMode::Enum getNonWalkableMode() const { return mUserParams.mNonWalkableMode; } + virtual PxF32 getContactOffset() const { return mUserParams.mContactOffset; } + virtual void setContactOffset(PxF32 offset) { if(offset>0.0f) + mUserParams.mContactOffset = offset;} + virtual PxVec3 getUpDirection() const { return mUserParams.mUpDirection; } + virtual void setUpDirection(const PxVec3& up) { setUpDirectionInternal(up); } + virtual PxF32 getSlopeLimit() const { return mUserParams.mSlopeLimit; } + virtual void setSlopeLimit(PxF32 slopeLimit) { if(slopeLimit>0.0f) + mUserParams.mSlopeLimit = slopeLimit;} + virtual void invalidateCache(); + virtual PxScene* getScene() { return mScene; } + virtual void* getUserData() const { return mUserData; } + virtual void setUserData(void* userData) { mUserData = userData; } + virtual void getState(PxControllerState& state) const { return getInternalState(state); } + virtual void getStats(PxControllerStats& stats) const { return getInternalStats(stats); } + virtual void resize(PxReal height); + //~PxController + + // PxCapsuleController + virtual PxF32 getRadius() const { return mRadius; } + virtual PxF32 getHeight() const { return mHeight; } + virtual PxCapsuleClimbingMode::Enum getClimbingMode() const; + virtual bool setRadius(PxF32 radius); + virtual bool setHeight(PxF32 height); + virtual bool setClimbingMode(PxCapsuleClimbingMode::Enum); + //~ PxCapsuleController + + void getCapsule(PxExtendedCapsule& capsule) const; + + PxF32 mRadius; + PxF32 mHeight; + PxCapsuleClimbingMode::Enum mClimbingMode; + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterController.cpp b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterController.cpp new file mode 100644 index 00000000..00a49316 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterController.cpp @@ -0,0 +1,2536 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "CctCharacterController.h" +#include "CctCharacterControllerManager.h" +#include "CctSweptBox.h" +#include "CctSweptCapsule.h" +#include "CctObstacleContext.h" +#include "PxRigidDynamic.h" +#include "CmRenderOutput.h" +#include "PsMathUtils.h" +#include "GuIntersectionBoxBox.h" +#include "GuDistanceSegmentBox.h" +#include "PxMeshQuery.h" +#include "PsFPU.h" + +// PT: TODO: remove those includes.... shouldn't be allowed from here +#include "PxControllerObstacles.h" // (*) +#include "CctInternalStructs.h" // (*) +#include "PxControllerManager.h" // (*) +#include "PxControllerBehavior.h" // (*) + +//#define DEBUG_MTD +#ifdef DEBUG_MTD + #include <stdio.h> +#endif + +#define MAX_ITER 10 + +using namespace physx; +using namespace Cct; +using namespace Gu; + +static const PxU32 gObstacleDebugColor = PxU32(PxDebugColor::eARGB_CYAN); +//static const PxU32 gCCTBoxDebugColor = PxU32(PxDebugColor::eARGB_YELLOW); +static const PxU32 gTBVDebugColor = PxU32(PxDebugColor::eARGB_MAGENTA); +static const bool gUsePartialUpdates = true; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxHitFlags getSweepHitFlags(const CCTParams& params) +{ + PxHitFlags sweepHitFlags = PxHitFlag::eDEFAULT/*|PxHitFlag::eMESH_BOTH_SIDES*/; +// sweepHitFlags |= PxHitFlag::eASSUME_NO_INITIAL_OVERLAP; + if(params.mPreciseSweeps) + sweepHitFlags |= PxHitFlag::ePRECISE_SWEEP; + return sweepHitFlags; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool shouldApplyRecoveryModule(const PxRigidActor& rigidActor) +{ + // PT: we must let the dynamic objects go through the CCT for proper 2-way interactions. + // But we should still apply the recovery module for kinematics. + + const PxType type = rigidActor.getConcreteType(); + if(type==PxConcreteType::eRIGID_STATIC) + return true; + + if(type!=PxConcreteType::eRIGID_DYNAMIC) + return false; + + return static_cast<const PxRigidBody&>(rigidActor).getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static const bool gUseLocalSpace = true; +static PxVec3 worldToLocal(const PxObstacle& obstacle, const PxExtendedVec3& worldPos) +{ + const PxTransform tr(toVec3(obstacle.mPos), obstacle.mRot); + return tr.transformInv(toVec3(worldPos)); +} + +static PxVec3 localToWorld(const PxObstacle& obstacle, const PxVec3& localPos) +{ + const PxTransform tr(toVec3(obstacle.mPos), obstacle.mRot); + return tr.transform(localPos); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef PX_BIG_WORLDS + typedef PxExtendedBounds3 PxCCTBounds3; + typedef PxExtendedVec3 PxCCTVec3; +#else + typedef PxBounds3 PxCCTBounds3; + typedef PxVec3 PxCCTVec3; +#endif + +static PX_INLINE void scale(PxCCTBounds3& b, const PxVec3& scale) +{ + PxCCTVec3 center; getCenter(b, center); + PxVec3 extents; getExtents(b, extents); + extents.x *= scale.x; + extents.y *= scale.y; + extents.z *= scale.z; + setCenterExtents(b, center, extents); +} + +static PX_INLINE void computeReflexionVector(PxVec3& reflected, const PxVec3& incomingDir, const PxVec3& outwardNormal) +{ + reflected = incomingDir - outwardNormal * 2.0f * (incomingDir.dot(outwardNormal)); +} + +static PX_INLINE void collisionResponse(PxExtendedVec3& targetPosition, const PxExtendedVec3& currentPosition, const PxVec3& currentDir, const PxVec3& hitNormal, PxF32 bump, PxF32 friction, bool normalize=false) +{ + // Compute reflect direction + PxVec3 reflectDir; + computeReflexionVector(reflectDir, currentDir, hitNormal); + reflectDir.normalize(); + + // Decompose it + PxVec3 normalCompo, tangentCompo; + Ps::decomposeVector(normalCompo, tangentCompo, reflectDir, hitNormal); + + // Compute new destination position + const PxF32 amplitude = (targetPosition - currentPosition).magnitude(); + + targetPosition = currentPosition; + if(bump!=0.0f) + { + if(normalize) + normalCompo.normalize(); + targetPosition += normalCompo*bump*amplitude; + } + if(friction!=0.0f) + { + if(normalize) + tangentCompo.normalize(); + targetPosition += tangentCompo*friction*amplitude; + } +} + +static PX_INLINE void relocateBox(PxBoxGeometry& boxGeom, PxTransform& pose, const PxExtendedVec3& center, const PxVec3& extents, const PxExtendedVec3& origin, const PxQuat& quatFromUp) +{ + boxGeom.halfExtents = extents; + + pose.p.x = float(center.x - origin.x); + pose.p.y = float(center.y - origin.y); + pose.p.z = float(center.z - origin.z); + + pose.q = quatFromUp; +} + +static PX_INLINE void relocateBox(PxBoxGeometry& boxGeom, PxTransform& pose, const TouchedUserBox& userBox) +{ + relocateBox(boxGeom, pose, userBox.mBox.center, userBox.mBox.extents, userBox.mOffset, userBox.mBox.rot); +} + +static PX_INLINE void relocateBox(PxBoxGeometry& boxGeom, PxTransform& pose, const TouchedBox& box) +{ + boxGeom.halfExtents = box.mExtents; + + pose.p = box.mCenter; + pose.q = box.mRot; +} + +static PX_INLINE void relocateCapsule( + PxCapsuleGeometry& capsuleGeom, PxTransform& pose, const SweptCapsule* sc, + const PxQuat& quatFromUp, + const PxExtendedVec3& center, const PxExtendedVec3& origin) +{ + capsuleGeom.radius = sc->mRadius; + capsuleGeom.halfHeight = 0.5f * sc->mHeight; + + pose.p.x = float(center.x - origin.x); + pose.p.y = float(center.y - origin.y); + pose.p.z = float(center.z - origin.z); + + pose.q = quatFromUp; +} + +static PX_INLINE void relocateCapsule(PxCapsuleGeometry& capsuleGeom, PxTransform& pose, const PxVec3& p0, const PxVec3& p1, PxReal radius) +{ + capsuleGeom.radius = radius; + pose = PxTransformFromSegment(p0, p1, &capsuleGeom.halfHeight); +} + +static PX_INLINE void relocateCapsule(PxCapsuleGeometry& capsuleGeom, PxTransform& pose, const TouchedUserCapsule& userCapsule) +{ + PxVec3 p0, p1; + p0.x = float(userCapsule.mCapsule.p0.x - userCapsule.mOffset.x); + p0.y = float(userCapsule.mCapsule.p0.y - userCapsule.mOffset.y); + p0.z = float(userCapsule.mCapsule.p0.z - userCapsule.mOffset.z); + p1.x = float(userCapsule.mCapsule.p1.x - userCapsule.mOffset.x); + p1.y = float(userCapsule.mCapsule.p1.y - userCapsule.mOffset.y); + p1.z = float(userCapsule.mCapsule.p1.z - userCapsule.mOffset.z); + + relocateCapsule(capsuleGeom, pose, p0, p1, userCapsule.mCapsule.radius); +} + +static bool SweepBoxUserBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_BOX); + const SweptBox* SB = static_cast<const SweptBox*>(volume); + const TouchedUserBox* TC = static_cast<const TouchedUserBox*>(geom); + + PxBoxGeometry boxGeom0; + PxTransform boxPose0; + // To precompute + relocateBox(boxGeom0, boxPose0, center, SB->mExtents, TC->mOffset, test->mUserParams.mQuatFromUp); + + PxBoxGeometry boxGeom1; + PxTransform boxPose1; + relocateBox(boxGeom1, boxPose1, *TC); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom0, boxPose0, boxGeom1, boxPose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mWorldNormal = sweepHit.normal; + impact.mDistance = sweepHit.distance; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TC->mOffset); + return true; +} + +static bool SweepBoxUserCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_CAPSULE); + const SweptBox* SB = static_cast<const SweptBox*>(volume); + const TouchedUserCapsule* TC = static_cast<const TouchedUserCapsule*>(geom); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, center, SB->mExtents, TC->mOffset, test->mUserParams.mQuatFromUp); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, *TC); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom, boxPose, capsuleGeom, capsulePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + // ### check this + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(Capsule, Box0.center, Box0.extents, Box0.rot, &t, &p); + Box0.rot.multiply(p,p); + impact.mWorldPos.x = p.x + Box0.center.x + TC->mOffset.x; + impact.mWorldPos.y = p.y + Box0.center.y + TC->mOffset.y; + impact.mWorldPos.z = p.z + Box0.center.z + TC->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TC->mOffset); + } + return true; +} + +static bool sweepVolumeVsMesh( const SweepTest* sweepTest, const TouchedMesh* touchedMesh, SweptContact& impact, + const PxVec3& unitDir, const PxGeometry& geom, const PxTransform& pose, + PxU32 nbTris, const PxTriangle* triangles, + PxU32 cachedIndex) +{ + PxSweepHit sweepHit; + if(PxMeshQuery::sweep(unitDir, impact.mDistance, geom, pose, nbTris, triangles, sweepHit, getSweepHitFlags(sweepTest->mUserParams), &cachedIndex)) + { + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.setWorldPos(sweepHit.position, touchedMesh->mOffset); + + // Returned index is only between 0 and nbTris, i.e. it indexes the array of cached triangles, not the original mesh. + PX_ASSERT(sweepHit.faceIndex < nbTris); + sweepTest->mCachedTriIndex[sweepTest->mCachedTriIndexIndex] = sweepHit.faceIndex; + + // The CCT loop will use the index from the start of the cache... + impact.mInternalIndex = sweepHit.faceIndex + touchedMesh->mIndexWorldTriangles; + const PxU32* triangleIndices = &sweepTest->mTriangleIndices[touchedMesh->mIndexWorldTriangles]; + impact.mTriangleIndex = triangleIndices[sweepHit.faceIndex]; + return true; + } + return false; +} + +static bool SweepBoxMesh(const SweepTest* sweep_test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eMESH); + const SweptBox* SB = static_cast<const SweptBox*>(volume); + const TouchedMesh* TM = static_cast<const TouchedMesh*>(geom); + + PxU32 nbTris = TM->mNbTris; + if(!nbTris) + return false; + + // Fetch triangle data for current mesh (the stream may contain triangles from multiple meshes) + const PxTriangle* T = &sweep_test->mWorldTriangles.getTriangle(TM->mIndexWorldTriangles); + + // PT: this only really works when the CCT collides with a single mesh, but that's the most common case. When it doesn't, there's just no speedup but it still works. + PxU32 CachedIndex = sweep_test->mCachedTriIndex[sweep_test->mCachedTriIndexIndex]; + if(CachedIndex>=nbTris) + CachedIndex=0; + + PxBoxGeometry boxGeom; + boxGeom.halfExtents = SB->mExtents; + PxTransform boxPose(PxVec3(float(center.x - TM->mOffset.x), float(center.y - TM->mOffset.y), float(center.z - TM->mOffset.z)), sweep_test->mUserParams.mQuatFromUp); // Precompute + return sweepVolumeVsMesh(sweep_test, TM, impact, dir, boxGeom, boxPose, nbTris, T, CachedIndex); +} + +static bool SweepCapsuleMesh( + const SweepTest* sweep_test, const SweptVolume* volume, const TouchedGeom* geom, + const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eMESH); + const SweptCapsule* SC = static_cast<const SweptCapsule*>(volume); + const TouchedMesh* TM = static_cast<const TouchedMesh*>(geom); + + PxU32 nbTris = TM->mNbTris; + if(!nbTris) + return false; + + // Fetch triangle data for current mesh (the stream may contain triangles from multiple meshes) + const PxTriangle* T = &sweep_test->mWorldTriangles.getTriangle(TM->mIndexWorldTriangles); + + // PT: this only really works when the CCT collides with a single mesh, but that's the most common case. + // When it doesn't, there's just no speedup but it still works. + PxU32 CachedIndex = sweep_test->mCachedTriIndex[sweep_test->mCachedTriIndexIndex]; + if(CachedIndex>=nbTris) + CachedIndex=0; + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, sweep_test->mUserParams.mQuatFromUp, center, TM->mOffset); + + return sweepVolumeVsMesh(sweep_test, TM, impact, dir, capsuleGeom, capsulePose, nbTris, T, CachedIndex); +} + +static bool SweepBoxBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eBOX); + const SweptBox* SB = static_cast<const SweptBox*>(volume); + const TouchedBox* TB = static_cast<const TouchedBox*>(geom); + + PxBoxGeometry boxGeom0; + PxTransform boxPose0; + // To precompute + relocateBox(boxGeom0, boxPose0, center, SB->mExtents, TB->mOffset, test->mUserParams.mQuatFromUp); + + PxBoxGeometry boxGeom1; + PxTransform boxPose1; + relocateBox(boxGeom1, boxPose1, *TB); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom0, boxPose0, boxGeom1, boxPose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mWorldNormal = sweepHit.normal; + impact.mDistance = sweepHit.distance; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TB->mOffset); + return true; +} + +static bool SweepBoxSphere(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eSPHERE); + const SweptBox* SB = static_cast<const SweptBox*>(volume); + const TouchedSphere* TS = static_cast<const TouchedSphere*>(geom); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, center, SB->mExtents, TS->mOffset, test->mUserParams.mQuatFromUp); + + PxSphereGeometry sphereGeom; + sphereGeom.radius = TS->mRadius; + PxTransform spherePose; + spherePose.p = TS->mCenter; + spherePose.q = PxQuat(PxIdentity); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom, boxPose, sphereGeom, spherePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /* + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + PxVec3 NewSphereCenter = TS->mSphere.center - d * dir; + PxVec3 Closest; + gUtilLib->PxPointOBBSqrDist(NewSphereCenter, Box0.center, Box0.extents, Box0.rot, &Closest); + // Compute point on the box, after sweep + Box0.rot.multiply(Closest, Closest); + impact.mWorldPos.x = TS->mOffset.x + Closest.x + Box0.center.x + d * dir.x; + impact.mWorldPos.y = TS->mOffset.y + Closest.y + Box0.center.y + d * dir.y; + impact.mWorldPos.z = TS->mOffset.z + Closest.z + Box0.center.z + d * dir.z; + + impact.mWorldNormal = -impact.mWorldNormal; + }*/ + { + impact.setWorldPos(sweepHit.position, TS->mOffset); + } + return true; +} + +static bool SweepBoxCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eCAPSULE); + const SweptBox* SB = static_cast<const SweptBox*>(volume); + const TouchedCapsule* TC = static_cast<const TouchedCapsule*>(geom); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, center, SB->mExtents, TC->mOffset, test->mUserParams.mQuatFromUp); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, TC->mP0, TC->mP1, TC->mRadius); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom, boxPose, capsuleGeom, capsulePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(TC->mCapsule, Box0.center, Box0.extents, Box0.rot, &t, &p); + Box0.rot.multiply(p,p); + impact.mWorldPos.x = p.x + Box0.center.x + TC->mOffset.x; + impact.mWorldPos.y = p.y + Box0.center.y + TC->mOffset.y; + impact.mWorldPos.z = p.z + Box0.center.z + TC->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TC->mOffset); + } + return true; +} + +static bool SweepCapsuleBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eBOX); + const SweptCapsule* SC = static_cast<const SweptCapsule*>(volume); + const TouchedBox* TB = static_cast<const TouchedBox*>(geom); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, test->mUserParams.mQuatFromUp, center, TB->mOffset); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, *TB); + + // The box and capsule coordinates are relative to the center of the cached bounding box + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom, capsulePose, boxGeom, boxPose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(Capsule, TB->mBox.center, TB->mBox.extents, TB->mBox.rot, &t, &p); + TB->mBox.rot.multiply(p,p); + p += TB->mBox.center; + impact.mWorldPos.x = p.x + TB->mOffset.x; + impact.mWorldPos.y = p.y + TB->mOffset.y; + impact.mWorldPos.z = p.z + TB->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TB->mOffset); + } + return true; +} + +static bool SweepCapsuleSphere(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eSPHERE); + const SweptCapsule* SC = static_cast<const SweptCapsule*>(volume); + const TouchedSphere* TS = static_cast<const TouchedSphere*>(geom); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, test->mUserParams.mQuatFromUp, center, TS->mOffset); + + PxSphereGeometry sphereGeom; + sphereGeom.radius = TS->mRadius; + PxTransform spherePose; + spherePose.p = TS->mCenter; + spherePose.q = PxQuat(PxIdentity); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom, capsulePose, sphereGeom, spherePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TS->mOffset); + return true; +} + +static bool SweepCapsuleCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eCAPSULE); + const SweptCapsule* SC = static_cast<const SweptCapsule*>(volume); + const TouchedCapsule* TC = static_cast<const TouchedCapsule*>(geom); + + PxCapsuleGeometry capsuleGeom0; + PxTransform capsulePose0; + relocateCapsule(capsuleGeom0, capsulePose0, SC, test->mUserParams.mQuatFromUp, center, TC->mOffset); + + PxCapsuleGeometry capsuleGeom1; + PxTransform capsulePose1; + relocateCapsule(capsuleGeom1, capsulePose1, TC->mP0, TC->mP1, TC->mRadius); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom0, capsulePose0, capsuleGeom1, capsulePose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TC->mOffset); + return true; +} + +static bool SweepCapsuleUserCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_CAPSULE); + const SweptCapsule* SC = static_cast<const SweptCapsule*>(volume); + const TouchedUserCapsule* TC = static_cast<const TouchedUserCapsule*>(geom); + + PxCapsuleGeometry capsuleGeom0; + PxTransform capsulePose0; + relocateCapsule(capsuleGeom0, capsulePose0, SC, test->mUserParams.mQuatFromUp, center, TC->mOffset); + + PxCapsuleGeometry capsuleGeom1; + PxTransform capsulePose1; + relocateCapsule(capsuleGeom1, capsulePose1, *TC); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom0, capsulePose0, capsuleGeom1, capsulePose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TC->mOffset); + return true; +} + +static bool SweepCapsuleUserBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_BOX); + const SweptCapsule* SC = static_cast<const SweptCapsule*>(volume); + const TouchedUserBox* TB = static_cast<const TouchedUserBox*>(geom); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, test->mUserParams.mQuatFromUp, center, TB->mOffset); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + relocateBox(boxGeom, boxPose, *TB); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom, capsulePose, boxGeom, boxPose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + // ### check this + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(Capsule, Box.center, Box.extents, Box.rot, &t, &p); + p += Box.center; + impact.mWorldPos.x = p.x + TB->mOffset.x; + impact.mWorldPos.y = p.y + TB->mOffset.y; + impact.mWorldPos.z = p.z + TB->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TB->mOffset); + } + return true; +} + +typedef bool (*SweepFunc) (const SweepTest*, const SweptVolume*, const TouchedGeom*, const PxExtendedVec3&, const PxVec3&, SweptContact&); + +static SweepFunc gSweepMap[SweptVolumeType::eLAST][TouchedGeomType::eLAST] = { + // Box funcs + { + SweepBoxUserBox, + SweepBoxUserCapsule, + SweepBoxMesh, + SweepBoxBox, + SweepBoxSphere, + SweepBoxCapsule + }, + + // Capsule funcs + { + SweepCapsuleUserBox, + SweepCapsuleUserCapsule, + SweepCapsuleMesh, + SweepCapsuleBox, + SweepCapsuleSphere, + SweepCapsuleCapsule + } +}; + +PX_COMPILE_TIME_ASSERT(sizeof(gSweepMap)==SweptVolumeType::eLAST*TouchedGeomType::eLAST*sizeof(SweepFunc)); + +static const PxU32 GeomSizes[] = +{ + sizeof(TouchedUserBox), + sizeof(TouchedUserCapsule), + sizeof(TouchedMesh), + sizeof(TouchedBox), + sizeof(TouchedSphere), + sizeof(TouchedCapsule), +}; + +static const TouchedGeom* CollideGeoms( + const SweepTest* sweep_test, const SweptVolume& volume, const IntArray& geom_stream, + const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact, bool discardInitialOverlap) +{ + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.mGeom = NULL; + + const PxU32* Data = geom_stream.begin(); + const PxU32* Last = geom_stream.end(); + while(Data!=Last) + { + const TouchedGeom* CurrentGeom = reinterpret_cast<const TouchedGeom*>(Data); + + SweepFunc ST = gSweepMap[volume.getType()][CurrentGeom->mType]; + if(ST) + { + SweptContact C; + C.mDistance = impact.mDistance; // Initialize with current best distance + C.mInternalIndex = PX_INVALID_U32; + C.mTriangleIndex = PX_INVALID_U32; + if((ST)(sweep_test, &volume, CurrentGeom, center, dir, C)) + { + if(C.mDistance==0.0f) + { + if(!discardInitialOverlap) + { + if(CurrentGeom->mType==TouchedGeomType::eUSER_BOX || CurrentGeom->mType==TouchedGeomType::eUSER_CAPSULE) + { + } + else + { + const PxRigidActor* touchedActor = CurrentGeom->mActor; + PX_ASSERT(touchedActor); + + if(shouldApplyRecoveryModule(*touchedActor)) + { + impact = C; + impact.mGeom = const_cast<TouchedGeom*>(CurrentGeom); + return CurrentGeom; + } + } + } + } +/* else + if(discardInitialOverlap && C.mDistance==0.0f) + { + // PT: we previously used eINITIAL_OVERLAP without eINITIAL_OVERLAP_KEEP, i.e. initially overlapping shapes got ignored. + // So we replicate this behavior here. + }*/ + else if(C.mDistance<impact.mDistance) + { + impact = C; + impact.mGeom = const_cast<TouchedGeom*>(CurrentGeom); + if(C.mDistance <= 0.0f) // there is no point testing for closer hits + return CurrentGeom; // since we are touching a shape already + } + } + } + + const PxU8* ptr = reinterpret_cast<const PxU8*>(Data); + ptr += GeomSizes[CurrentGeom->mType]; + Data = reinterpret_cast<const PxU32*>(ptr); + } + return impact.mGeom; +} + +static PxVec3 computeMTD(const SweepTest* sweep_test, const SweptVolume& volume, const IntArray& geom_stream, const PxExtendedVec3& center, float contactOffset) +{ + PxVec3 p = toVec3(center); + +// contactOffset += 0.01f; + + const PxU32 maxIter = 4; + PxU32 nbIter = 0; + bool isValid = true; + while(isValid && nbIter<maxIter) + { + const PxU32* Data = geom_stream.begin(); + const PxU32* Last = geom_stream.end(); + while(Data!=Last) + { + const TouchedGeom* CurrentGeom = reinterpret_cast<const TouchedGeom*>(Data); + + if(CurrentGeom->mType==TouchedGeomType::eUSER_BOX || CurrentGeom->mType==TouchedGeomType::eUSER_CAPSULE) + { + } + else + { + const PxRigidActor* touchedActor = CurrentGeom->mActor; + PX_ASSERT(touchedActor); + + if(shouldApplyRecoveryModule(*touchedActor)) + { + const PxShape* touchedShape = reinterpret_cast<const PxShape*>(CurrentGeom->mTGUserData); + PX_ASSERT(touchedShape); + + const PxGeometryHolder gh = touchedShape->getGeometry(); + const PxTransform globalPose = getShapeGlobalPose(*touchedShape, *touchedActor); + + PxVec3 mtd; + PxF32 depth; + + const PxTransform volumePose(p, sweep_test->mUserParams.mQuatFromUp); + if(volume.getType()==SweptVolumeType::eCAPSULE) + { + const SweptCapsule& sc = static_cast<const SweptCapsule&>(volume); + const PxCapsuleGeometry capsuleGeom(sc.mRadius+contactOffset, sc.mHeight*0.5f); + isValid = PxGeometryQuery::computePenetration(mtd, depth, capsuleGeom, volumePose, gh.any(), globalPose); + } + else + { + PX_ASSERT(volume.getType()==SweptVolumeType::eBOX); + const SweptBox& sb = static_cast<const SweptBox&>(volume); + const PxBoxGeometry boxGeom(sb.mExtents+PxVec3(contactOffset)); + isValid = PxGeometryQuery::computePenetration(mtd, depth, boxGeom, volumePose, gh.any(), globalPose); + } + + if(isValid) + { + nbIter++; + PX_ASSERT(depth>=0.0f); + PX_ASSERT(mtd.isFinite()); + PX_ASSERT(PxIsFinite(depth)); +#ifdef DEBUG_MTD + PX_ASSERT(depth<=1.0f); + if(depth>1.0f || !mtd.isFinite() || !PxIsFinite(depth)) + { + int stop=1; + (void)stop; + } + printf("Depth: %f\n", depth); + printf("mtd: %f %f %f\n", mtd.x, mtd.y, mtd.z); +#endif + p += mtd * depth; + } + } + } + + const PxU8* ptr = reinterpret_cast<const PxU8*>(Data); + ptr += GeomSizes[CurrentGeom->mType]; + Data = reinterpret_cast<const PxU32*>(ptr); + } + } + return p; +} + + + +static bool ParseGeomStream(const void* object, const IntArray& geom_stream) +{ + const PxU32* Data = geom_stream.begin(); + const PxU32* Last = geom_stream.end(); + while(Data!=Last) + { + const TouchedGeom* CurrentGeom = reinterpret_cast<const TouchedGeom*>(Data); + if(CurrentGeom->mTGUserData==object) + return true; + + const PxU8* ptr = reinterpret_cast<const PxU8*>(Data); + ptr += GeomSizes[CurrentGeom->mType]; + Data = reinterpret_cast<const PxU32*>(ptr); + } + return false; +} + +CCTParams::CCTParams() : + mNonWalkableMode (PxControllerNonWalkableMode::ePREVENT_CLIMBING), + mQuatFromUp (PxQuat(PxIdentity)), + mUpDirection (PxVec3(0.0f)), + mSlopeLimit (0.0f), + mContactOffset (0.0f), + mStepOffset (0.0f), + mInvisibleWallHeight (0.0f), + mMaxJumpHeight (0.0f), + mMaxEdgeLength2 (0.0f), + mTessellation (false), + mHandleSlope (false), + mOverlapRecovery (false), + mPreciseSweeps (true), + mPreventVerticalSlidingAgainstCeiling (false) +{ +} + +SweepTest::SweepTest(bool registerDeletionListener) : + mRenderBuffer (NULL), + mRenderFlags (0), + mTriangleIndices (PX_DEBUG_EXP("sweepTestTriangleIndices")), + mGeomStream (PX_DEBUG_EXP("sweepTestStream")), + mTouchedShape (registerDeletionListener), + mTouchedActor (registerDeletionListener), + mSQTimeStamp (0xffffffff), + mNbFullUpdates (0), + mNbPartialUpdates (0), + mNbTessellation (0), + mNbIterations (0), + mFlags (0), + mRegisterDeletionListener(registerDeletionListener), + mCctManager (NULL) +{ + mCacheBounds.setEmpty(); + mCachedTriIndexIndex = 0; + mCachedTriIndex[0] = mCachedTriIndex[1] = mCachedTriIndex[2] = 0; + mNbCachedStatic = 0; + mNbCachedT = 0; + + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + mTouchedPos = PxVec3(0); + mTouchedPosShape_Local = PxVec3(0); + mTouchedPosShape_World = PxVec3(0); + mTouchedPosObstacle_Local = PxVec3(0); + mTouchedPosObstacle_World = PxVec3(0); + +// mVolumeGrowth = 1.2f; // Must be >1.0f and not too big + mVolumeGrowth = 1.5f; // Must be >1.0f and not too big +// mVolumeGrowth = 2.0f; // Must be >1.0f and not too big + + mContactNormalDownPass = PxVec3(0.0f); + mContactNormalSidePass = PxVec3(0.0f); + mTouchedTriMin = 0.0f; + mTouchedTriMax = 0.0f; +} + + +SweepTest::~SweepTest() +{ + // set the TouchedObject to NULL so we unregister the actor/shape + mTouchedShape = NULL; + mTouchedActor = NULL; +} + +void SweepTest::voidTestCache() +{ + mTouchedShape = NULL; + mTouchedActor = NULL; + mCacheBounds.setEmpty(); + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; +} + +void SweepTest::onRelease(const PxBase& observed) +{ + if (mTouchedActor == &observed) + { + mTouchedShape = NULL; + mTouchedActor = NULL; + return; + } + + if(ParseGeomStream(&observed, mGeomStream)) + mCacheBounds.setEmpty(); + + if (mTouchedShape == &observed) + mTouchedShape = NULL; +} + +void SweepTest::updateCachedShapesRegistration(PxU32 startIndex, bool unregister) +{ + if(!mRegisterDeletionListener) + return; + + if(!mGeomStream.size() || startIndex == mGeomStream.size()) + return; + + PX_ASSERT(startIndex <= mGeomStream.size()); + + const PxU32* data = &mGeomStream[startIndex]; + const PxU32* last = mGeomStream.end(); + while (data != last) + { + const TouchedGeom* CurrentGeom = reinterpret_cast<const TouchedGeom*>(data); + if (CurrentGeom->mActor) + { + if(unregister) + mCctManager->unregisterObservedObject(reinterpret_cast<const PxBase*>(CurrentGeom->mTGUserData)); + else + mCctManager->registerObservedObject(reinterpret_cast<const PxBase*>(CurrentGeom->mTGUserData)); + } + else + { + // we can early exit, the rest of the data are user obstacles + return; + } + + const PxU8* ptr = reinterpret_cast<const PxU8*>(data); + ptr += GeomSizes[CurrentGeom->mType]; + data = reinterpret_cast<const PxU32*>(ptr); + } +} + +void SweepTest::onObstacleAdded(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance ) +{ + if(mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE) + { + // check if new obstacle is closer + const ObstacleContext* obstContext = static_cast<const ObstacleContext*> (context); + PxRaycastHit obstacleHit; + const PxObstacle* obst = obstContext->raycastSingle(obstacleHit,index,origin,unitDir,distance); + + if(obst && (obstacleHit.position.dot(unitDir))<(mTouchedPosObstacle_World.dot(unitDir))) + { + PX_ASSERT(obstacleHit.distance<=distance); + mTouchedObstacleHandle = index; + if(!gUseLocalSpace) + { + mTouchedPos = toVec3(obst->mPos); + } + else + { + mTouchedPosObstacle_World = obstacleHit.position; + mTouchedPosObstacle_Local = worldToLocal(*obst, PxExtendedVec3(PxExtended(obstacleHit.position.x),PxExtended(obstacleHit.position.y),PxExtended(obstacleHit.position.z))); + } + } + } +} + +void SweepTest::onObstacleRemoved(ObstacleHandle index) +{ + if(index == mTouchedObstacleHandle) + { + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + } +} + +void SweepTest::onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance) +{ + if(index == mTouchedObstacleHandle) + { + // check if updated obstacle is still closest + const ObstacleContext* obstContext = static_cast<const ObstacleContext*> (context); + PxRaycastHit obstacleHit; + ObstacleHandle closestHandle = INVALID_OBSTACLE_HANDLE; + const PxObstacle* obst = obstContext->raycastSingle(obstacleHit,origin,unitDir,distance,closestHandle); + + if(mTouchedObstacleHandle == closestHandle) + return; + + if(obst) + { + PX_ASSERT(obstacleHit.distance<=distance); + mTouchedObstacleHandle = closestHandle; + if(!gUseLocalSpace) + { + mTouchedPos = toVec3(obst->mPos); + } + else + { + mTouchedPosObstacle_World = obstacleHit.position; + mTouchedPosObstacle_Local = worldToLocal(*obst, PxExtendedVec3(PxExtended(obstacleHit.position.x),PxExtended(obstacleHit.position.y),PxExtended(obstacleHit.position.z))); + } + } + } +} + +void SweepTest::onOriginShift(const PxVec3& shift) +{ + mCacheBounds.minimum -= shift; + mCacheBounds.maximum -= shift; + + if(mTouchedShape) + { + const PxRigidActor* rigidActor = mTouchedActor.get(); + if(rigidActor->getConcreteType() != PxConcreteType::eRIGID_STATIC) + { + mTouchedPosShape_World -= shift; + } + } + else if (mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE) + { + if(!gUseLocalSpace) + { + mTouchedPos -= shift; + } + else + { + mTouchedPosObstacle_World -= shift; + } + } + + // adjust cache + PxU32* data = mGeomStream.begin(); + PxU32* last = mGeomStream.end(); + while(data != last) + { + TouchedGeom* currentGeom = reinterpret_cast<TouchedGeom*>(data); + + currentGeom->mOffset -= shift; + + PxU8* ptr = reinterpret_cast<PxU8*>(data); + ptr += GeomSizes[currentGeom->mType]; + data = reinterpret_cast<PxU32*>(ptr); + } +} + +static PxBounds3 getBounds3(const PxExtendedBounds3& extended) +{ + return PxBounds3(toVec3(extended.minimum), toVec3(extended.maximum)); // LOSS OF ACCURACY +} + +// PT: finds both touched CCTs and touched user-defined obstacles +void SweepTest::findTouchedObstacles(const UserObstacles& userObstacles, const PxExtendedBounds3& worldBox) +{ + PxExtendedVec3 Origin; // Will be TouchedGeom::mOffset + getCenter(worldBox, Origin); + + { + const PxU32 nbBoxes = userObstacles.mNbBoxes; + const PxExtendedBox* boxes = userObstacles.mBoxes; + const void** boxUserData = userObstacles.mBoxUserData; + + const PxBounds3 singlePrecisionWorldBox = getBounds3(worldBox); + + // Find touched boxes, i.e. other box controllers + for(PxU32 i=0;i<nbBoxes;i++) + { + const Gu::Box obb( + toVec3(boxes[i].center), // LOSS OF ACCURACY + boxes[i].extents, + PxMat33(boxes[i].rot)); // #### PT: TODO: useless conversion here + + if(!Gu::intersectOBBAABB(obb, singlePrecisionWorldBox)) + continue; + + TouchedUserBox* UserBox = reinterpret_cast<TouchedUserBox*>(reserve(mGeomStream, sizeof(TouchedUserBox)/sizeof(PxU32))); + UserBox->mType = TouchedGeomType::eUSER_BOX; + UserBox->mTGUserData = boxUserData[i]; + UserBox->mActor = NULL; + UserBox->mOffset = Origin; + UserBox->mBox = boxes[i]; + } + } + + { + // Find touched capsules, i.e. other capsule controllers + const PxU32 nbCapsules = userObstacles.mNbCapsules; + const PxExtendedCapsule* capsules = userObstacles.mCapsules; + const void** capsuleUserData = userObstacles.mCapsuleUserData; + + PxExtendedVec3 Center; + PxVec3 Extents; + getCenter(worldBox, Center); + getExtents(worldBox, Extents); + + for(PxU32 i=0;i<nbCapsules;i++) + { + // PT: do a quick AABB check first, to avoid calling the SDK too much + const PxF32 r = capsules[i].radius; + const PxExtended capMinx = PxMin(capsules[i].p0.x, capsules[i].p1.x); + const PxExtended capMaxx = PxMax(capsules[i].p0.x, capsules[i].p1.x); + if((capMinx - PxExtended(r) > worldBox.maximum.x) || (worldBox.minimum.x > capMaxx + PxExtended(r))) continue; + + const PxExtended capMiny = PxMin(capsules[i].p0.y, capsules[i].p1.y); + const PxExtended capMaxy = PxMax(capsules[i].p0.y, capsules[i].p1.y); + if((capMiny - PxExtended(r) > worldBox.maximum.y) || (worldBox.minimum.y > capMaxy + PxExtended(r))) continue; + + const PxExtended capMinz = PxMin(capsules[i].p0.z, capsules[i].p1.z); + const PxExtended capMaxz = PxMax(capsules[i].p0.z, capsules[i].p1.z); + if((capMinz - PxExtended(r) > worldBox.maximum.z) || (worldBox.minimum.z > capMaxz + PxExtended(r))) continue; + + // PT: more accurate capsule-box test. Not strictly necessary but worth doing if available + const PxReal d2 = Gu::distanceSegmentBoxSquared(toVec3(capsules[i].p0), toVec3(capsules[i].p1), toVec3(Center), Extents, PxMat33(PxIdentity)); + if(d2>r*r) + continue; + + TouchedUserCapsule* UserCapsule = reinterpret_cast<TouchedUserCapsule*>(reserve(mGeomStream, sizeof(TouchedUserCapsule)/sizeof(PxU32))); + UserCapsule->mType = TouchedGeomType::eUSER_CAPSULE; + UserCapsule->mTGUserData = capsuleUserData[i]; + UserCapsule->mActor = NULL; + UserCapsule->mOffset = Origin; + UserCapsule->mCapsule = capsules[i]; + } + } +} + +void SweepTest::updateTouchedGeoms( const InternalCBData_FindTouchedGeom* userData, const UserObstacles& userObstacles, + const PxExtendedBounds3& worldTemporalBox, const PxControllerFilters& filters, const PxVec3& sideVector) +{ + /* + - if this is the first iteration (new frame) we have to redo the dynamic objects & the CCTs. The static objects can + be cached. + - if this is not, we can cache everything + */ + + // PT: using "worldTemporalBox" instead of "mCacheBounds" seems to produce TTP 6207 +//#define DYNAMIC_BOX worldTemporalBox +#define DYNAMIC_BOX mCacheBounds + + bool newCachedBox = false; + + CCTFilter filter; + filter.mFilterData = filters.mFilterData; + filter.mFilterCallback = filters.mFilterCallback; + filter.mPreFilter = filters.mFilterFlags & PxQueryFlag::ePREFILTER; + filter.mPostFilter = filters.mFilterFlags & PxQueryFlag::ePOSTFILTER; + + // PT: detect changes to the static pruning structure + bool sceneHasChanged = false; + { + const PxU32 currentTimestamp = getSceneTimestamp(userData); + if(currentTimestamp!=mSQTimeStamp) + { + mSQTimeStamp = currentTimestamp; + sceneHasChanged = true; + } + } + + // If the input box is inside the cached box, nothing to do + if(gUsePartialUpdates && !sceneHasChanged && worldTemporalBox.isInside(mCacheBounds)) + { + //printf("CACHEIN%d\n", mFirstUpdate); + if(mFlags & STF_FIRST_UPDATE) + { + mFlags &= ~STF_FIRST_UPDATE; + + // Only redo the dynamic + updateCachedShapesRegistration(mNbCachedStatic, true); + mGeomStream.forceSize_Unsafe(mNbCachedStatic); + mWorldTriangles.forceSize_Unsafe(mNbCachedT); + mTriangleIndices.forceSize_Unsafe(mNbCachedT); + + filter.mStaticShapes = false; + if(filters.mFilterFlags & PxQueryFlag::eDYNAMIC) + filter.mDynamicShapes = true; + findTouchedGeometry(userData, DYNAMIC_BOX, mWorldTriangles, mTriangleIndices, mGeomStream, filter, mUserParams, mNbTessellation); + updateCachedShapesRegistration(mNbCachedStatic, false); + + findTouchedObstacles(userObstacles, DYNAMIC_BOX); + + mNbPartialUpdates++; + } + } + else + { + //printf("CACHEOUTNS=%d\n", mNbCachedStatic); + newCachedBox = true; + + // Cache BV used for the query + mCacheBounds = worldTemporalBox; + + // Grow the volume a bit. The temporal box here doesn't take sliding & collision response into account. + // In bad cases it is possible to eventually touch a portion of space not covered by this volume. Just + // in case, we grow the initial volume slightly. Then, additional tests are performed within the loop + // to make sure the TBV is always correct. There's a tradeoff between the original (artificial) growth + // of the volume, and the number of TBV recomputations performed at runtime... + scale(mCacheBounds, PxVec3(mVolumeGrowth)); +// scale(mCacheBounds, PxVec3(mVolumeGrowth, 1.0f, mVolumeGrowth)); + + if(1 && !sideVector.isZero()) + { + const PxVec3 sn = sideVector.getNormalized(); + float dp0 = PxAbs((worldTemporalBox.maximum - worldTemporalBox.minimum).dot(sn)); + float dp1 = PxAbs((mCacheBounds.maximum - mCacheBounds.minimum).dot(sn)); + dp1 -= dp0; + dp1 *= 0.5f * 0.9f; + const PxVec3 offset = sn * dp1; +// printf("%f %f %f\n", offset.x, offset.y, offset.z); + mCacheBounds.minimum += offset; + mCacheBounds.maximum += offset; + add(mCacheBounds, worldTemporalBox); + PX_ASSERT(worldTemporalBox.isInside(mCacheBounds)); + } + + updateCachedShapesRegistration(0, true); + + // Gather triangles touched by this box. This covers multiple meshes. + mWorldTriangles.clear(); + mTriangleIndices.clear(); + mGeomStream.clear(); +// mWorldTriangles.reset(); +// mTriangleIndices.reset(); +// mGeomStream.reset(); + + mCachedTriIndexIndex = 0; + mCachedTriIndex[0] = mCachedTriIndex[1] = mCachedTriIndex[2] = 0; + + mNbFullUpdates++; + + if(filters.mFilterFlags & PxQueryFlag::eSTATIC) + filter.mStaticShapes = true; + filter.mDynamicShapes = false; + findTouchedGeometry(userData, mCacheBounds, mWorldTriangles, mTriangleIndices, mGeomStream, filter, mUserParams, mNbTessellation); + + mNbCachedStatic = mGeomStream.size(); + mNbCachedT = mWorldTriangles.size(); + PX_ASSERT(mTriangleIndices.size()==mNbCachedT); + + filter.mStaticShapes = false; + if(filters.mFilterFlags & PxQueryFlag::eDYNAMIC) + filter.mDynamicShapes = true; + findTouchedGeometry(userData, DYNAMIC_BOX, mWorldTriangles, mTriangleIndices, mGeomStream, filter, mUserParams, mNbTessellation); + // We can't early exit when no tris are touched since we also have to handle the boxes + updateCachedShapesRegistration(0, false); + + findTouchedObstacles(userObstacles, DYNAMIC_BOX); + + mFlags &= ~STF_FIRST_UPDATE; + //printf("CACHEOUTNSDONE=%d\n", mNbCachedStatic); + } + + if(mRenderBuffer) + { + // PT: worldTemporalBox = temporal BV for this frame + Cm::RenderOutput out(*mRenderBuffer); + + if(mRenderFlags & PxControllerDebugRenderFlag::eTEMPORAL_BV) + { + out << gTBVDebugColor; + out << Cm::DebugBox(getBounds3(worldTemporalBox)); + } + + if(mRenderFlags & PxControllerDebugRenderFlag::eCACHED_BV) + { + if(newCachedBox) + out << PxU32(PxDebugColor::eARGB_RED); + else + out << PxU32(PxDebugColor::eARGB_GREEN); + out << Cm::DebugBox(getBounds3(mCacheBounds)); + } + } +} + +// This is the generic sweep test for all swept volumes, but not character-controller specific +bool SweepTest::doSweepTest(const InternalCBData_FindTouchedGeom* userData, + InternalCBData_OnHit* userHitData, + const UserObstacles& userObstacles, + SweptVolume& swept_volume, + const PxVec3& direction, const PxVec3& sideVector, PxU32 max_iter, PxU32* nb_collisions, + float min_dist, const PxControllerFilters& filters, SweepPass sweepPass, + const PxRigidActor*& touchedActorOut, const PxShape*& touchedShapeOut) +{ + // Early exit when motion is zero. Since the motion is decomposed into several vectors + // and this function is called for each of them, it actually happens quite often. + if(direction.isZero()) + return false; + + bool hasMoved = false; + mFlags &= ~(STF_VALIDATE_TRIANGLE_DOWN|STF_TOUCH_OTHER_CCT|STF_TOUCH_OBSTACLE); + touchedShapeOut = NULL; + touchedActorOut = NULL; + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + + PxExtendedVec3 currentPosition = swept_volume.mCenter; + PxExtendedVec3 targetOrientation = swept_volume.mCenter; + targetOrientation += direction; + + PxU32 NbCollisions = 0; + while(max_iter--) + { + mNbIterations++; + // Compute current direction + PxVec3 currentDirection = targetOrientation - currentPosition; + + // Make sure the new TBV is still valid + { + // Compute temporal bounding box. We could use a capsule or an OBB instead: + // - the volume would be smaller + // - but the query would be slower + // Overall it's unclear whether it's worth it or not. + // TODO: optimize this part ? + PxExtendedBounds3 temporalBox; + swept_volume.computeTemporalBox(*this, temporalBox, currentPosition, currentDirection); + + // Gather touched geoms + updateTouchedGeoms(userData, userObstacles, temporalBox, filters, sideVector); + } + + const float Length = currentDirection.magnitude(); + if(Length<=min_dist) //Use <= to handle the case where min_dist is zero. + break; + + currentDirection /= Length; + + // From Quake2: "if velocity is against the original velocity, stop dead to avoid tiny occilations in sloping corners" + if((currentDirection.dot(direction)) <= 0.0f) + break; + + // From this point, we're going to update the position at least once + hasMoved = true; + + // Find closest collision + SweptContact C; + C.mDistance = Length + mUserParams.mContactOffset; + + if(!CollideGeoms(this, swept_volume, mGeomStream, currentPosition, currentDirection, C, !mUserParams.mOverlapRecovery)) + { + // no collision found => move to desired position + currentPosition = targetOrientation; + break; + } + + PX_ASSERT(C.mGeom); // If we reach this point, we must have touched a geom + + if(mUserParams.mOverlapRecovery && C.mDistance==0.0f) + { +/* SweptContact C; + C.mDistance = 10.0f; + CollideGeoms(this, swept_volume, mGeomStream, currentPosition, -currentDirection, C, true); + currentPosition -= currentDirection*C.mDistance; + + C.mDistance = 10.0f; + CollideGeoms(this, swept_volume, mGeomStream, currentPosition, currentDirection, C, true); + const float DynSkin = mUserParams.mContactOffset; + if(C.mDistance>DynSkin) + currentPosition += currentDirection*(C.mDistance-DynSkin);*/ + + const PxVec3 mtd = computeMTD(this, swept_volume, mGeomStream, currentPosition, mUserParams.mContactOffset); + + NbCollisions++; + + if(nb_collisions) + *nb_collisions = NbCollisions; + +#ifdef DEBUG_MTD + printf("MTD FIXUP: %f %f %f\n", mtd.x - swept_volume.mCenter.x, mtd.y - swept_volume.mCenter.y, mtd.z - swept_volume.mCenter.z); +#endif + swept_volume.mCenter.x = PxExtended(mtd.x); + swept_volume.mCenter.y = PxExtended(mtd.y); + swept_volume.mCenter.z = PxExtended(mtd.z); + return hasMoved; +// currentPosition.x = mtd.x; +// currentPosition.y = mtd.y; +// currentPosition.z = mtd.z; +// continue; + } + + bool preventVerticalMotion = false; + bool stopSliding = true; + if(C.mGeom->mType==TouchedGeomType::eUSER_BOX || C.mGeom->mType==TouchedGeomType::eUSER_CAPSULE) + { + if(sweepPass!=SWEEP_PASS_SENSOR) + { + // We touched a user object, typically another CCT, but can also be a user-defined obstacle + + // PT: TODO: technically lines marked with (*) shouldn't be here... revisit later + + const PxObstacle* touchedObstacle = NULL; // (*) + ObstacleHandle touchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + // if(mValidateCallback) + { + PxInternalCBData_OnHit* internalData = static_cast<PxInternalCBData_OnHit*>(userHitData); // (*) + internalData->touchedObstacle = NULL; // (*) + internalData->touchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + const PxU32 behaviorFlags = userHitCallback(userHitData, C, currentDirection, Length); + stopSliding = (behaviorFlags & PxControllerBehaviorFlag::eCCT_SLIDE)==0; // (*) + touchedObstacle = internalData->touchedObstacle; // (*) + touchedObstacleHandle = internalData->touchedObstacleHandle; + } + // printf("INTERNAL: %d\n", int(touchedObstacle)); + + if(sweepPass==SWEEP_PASS_DOWN) + { + // (*) + if(touchedObstacle) + { + mFlags |= STF_TOUCH_OBSTACLE; + + mTouchedObstacleHandle = touchedObstacleHandle; + if(!gUseLocalSpace) + { + mTouchedPos = toVec3(touchedObstacle->mPos); + } + else + { + mTouchedPosObstacle_World = toVec3(C.mWorldPos); + mTouchedPosObstacle_Local = worldToLocal(*touchedObstacle, C.mWorldPos); + } + } + else + { + mFlags |= STF_TOUCH_OTHER_CCT; + } + } + } + } + else + { + const PxShape* touchedShape = reinterpret_cast<const PxShape*>(C.mGeom->mTGUserData); + PX_ASSERT(touchedShape); + const PxRigidActor* touchedActor = C.mGeom->mActor; + PX_ASSERT(touchedActor); + + // We touched a normal object + if(sweepPass==SWEEP_PASS_DOWN) + { + mFlags &= ~(STF_TOUCH_OTHER_CCT|STF_TOUCH_OBSTACLE); + +#ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST + mFlags |= STF_VALIDATE_TRIANGLE_DOWN; + mContactNormalDownPass = C.mWorldNormal; +#else + + // Work out if the shape is attached to a static or dynamic actor. + // The slope limit is currently only considered when walking on static actors. + // It is ignored for shapes attached attached to dynamics and kinematics. + // TODO: 1. should we treat stationary kinematics the same as statics. + // 2. should we treat all kinematics the same as statics. + // 3. should we treat no kinematics the same as statics. + if((touchedActor->getConcreteType() == PxConcreteType::eRIGID_STATIC) && (C.mInternalIndex!=PX_INVALID_U32)) + { + mFlags |= STF_VALIDATE_TRIANGLE_DOWN; + const PxTriangle& touchedTri = mWorldTriangles.getTriangle(C.mInternalIndex); + const PxVec3& upDirection = mUserParams.mUpDirection; + const float dp0 = touchedTri.verts[0].dot(upDirection); + const float dp1 = touchedTri.verts[1].dot(upDirection); + const float dp2 = touchedTri.verts[2].dot(upDirection); + float dpmin = dp0; + dpmin = physx::intrinsics::selectMin(dpmin, dp1); + dpmin = physx::intrinsics::selectMin(dpmin, dp2); + float dpmax = dp0; + dpmax = physx::intrinsics::selectMax(dpmax, dp1); + dpmax = physx::intrinsics::selectMax(dpmax, dp2); + + PxExtendedVec3 cacheCenter; + getCenter(mCacheBounds, cacheCenter); + const float offset = upDirection.dot(toVec3(cacheCenter)); + mTouchedTriMin = dpmin + offset; + mTouchedTriMax = dpmax + offset; + + touchedTri.normal(mContactNormalDownPass); + } +#endif + // Update touched shape in down pass + touchedShapeOut = const_cast<PxShape*>(touchedShape); + touchedActorOut = touchedActor; +// mTouchedPos = getShapeGlobalPose(*touchedShape).p; + const PxTransform shapeTransform = getShapeGlobalPose(*touchedShape, *touchedActor); + const PxVec3 worldPos = toVec3(C.mWorldPos); + mTouchedPosShape_World = worldPos; + mTouchedPosShape_Local = shapeTransform.transformInv(worldPos); + } + else if(sweepPass==SWEEP_PASS_SIDE || sweepPass==SWEEP_PASS_SENSOR) + { + if((touchedActor->getConcreteType() == PxConcreteType::eRIGID_STATIC) && (C.mInternalIndex!=PX_INVALID_U32)) + { + mFlags |= STF_VALIDATE_TRIANGLE_SIDE; + const PxTriangle& touchedTri = mWorldTriangles.getTriangle(C.mInternalIndex); + touchedTri.normal(mContactNormalSidePass); +// printf("%f | %f | %f\n", mContactNormalSidePass.x, mContactNormalSidePass.y, mContactNormalSidePass.z); + if(mUserParams.mPreventVerticalSlidingAgainstCeiling && mContactNormalSidePass.dot(mUserParams.mUpDirection)<0.0f) + preventVerticalMotion = true; + } + } + + if(sweepPass!=SWEEP_PASS_SENSOR) +// if(mValidateCallback) + { + const PxU32 behaviorFlags = shapeHitCallback(userHitData, C, currentDirection, Length); + stopSliding = (behaviorFlags & PxControllerBehaviorFlag::eCCT_SLIDE)==0; // (*) + } + } + + if(sweepPass==SWEEP_PASS_DOWN && !stopSliding) + { + // Trying to solve the following problem: + // - by default, the CCT "friction" is infinite, i.e. a CCT will not slide on a slope (this is by design) + // - this produces bad results when a capsule CCT stands on top of another capsule CCT, without sliding. Visually it looks + // like the character is standing on the other character's head, it looks bad. So, here, we would like to let the CCT + // slide away, i.e. we don't want friction. + // So here we simply increase the number of iterations (== let the CCT slide) when the first down collision is with another CCT. + if(!NbCollisions) + max_iter += 9; +// max_iter += 1; + } + + NbCollisions++; +// mContactPointHeight = (float)C.mWorldPos[mUserParams.mUpDirection]; // UBI + mContactPointHeight = toVec3(C.mWorldPos).dot(mUserParams.mUpDirection); // UBI + + const float DynSkin = mUserParams.mContactOffset; + + if(C.mDistance>DynSkin/*+0.01f*/) + currentPosition += currentDirection*(C.mDistance-DynSkin); +// DE6513 +/* else if(sweepPass==SWEEP_PASS_SIDE) + { + // Might be better to do a proper sweep pass here, in the opposite direction + currentPosition += currentDirection*(C.mDistance-DynSkin); + }*/ +//~DE6513 + + PxVec3 WorldNormal = C.mWorldNormal; + if(preventVerticalMotion || ((mFlags & STF_WALK_EXPERIMENT) && (mUserParams.mNonWalkableMode!=PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING))) + { + // Make sure the auto-step doesn't bypass this ! + // PT: cancel out normal compo +// WorldNormal[mUserParams.mUpDirection]=0.0f; +// WorldNormal.normalize(); + PxVec3 normalCompo, tangentCompo; + Ps::decomposeVector(normalCompo, tangentCompo, WorldNormal, mUserParams.mUpDirection); + WorldNormal = tangentCompo; + WorldNormal.normalize(); + } + + const float Bump = 0.0f; // ### doesn't work when !=0 because of Quake2 hack! + const float Friction = 1.0f; + collisionResponse(targetOrientation, currentPosition, currentDirection, WorldNormal, Bump, Friction, (mFlags & STF_NORMALIZE_RESPONSE)!=0); + } + + if(nb_collisions) + *nb_collisions = NbCollisions; + + // Final box position that should be reflected in the graphics engine + swept_volume.mCenter = currentPosition; + + // If we didn't move, don't update the box position at all (keeping possible lazy-evaluated structures valid) + return hasMoved; +} + +// ### have a return code to tell if we really moved or not + +// Using swept code & direct position update (no physics engine) +// This function is the generic character controller logic, valid for all swept volumes +PxControllerCollisionFlags SweepTest::moveCharacter( + const InternalCBData_FindTouchedGeom* userData, + InternalCBData_OnHit* userHitData, + SweptVolume& volume, + const PxVec3& direction, + const UserObstacles& userObstacles, + float min_dist, + const PxControllerFilters& filters, + bool constrainedClimbingMode, + bool standingOnMoving, + const PxRigidActor*& touchedActor, + const PxShape*& touchedShape + ) +{ + bool standingOnMovingUp = standingOnMoving; + + mFlags &= ~STF_HIT_NON_WALKABLE; + PxControllerCollisionFlags CollisionFlags = PxControllerCollisionFlags(0); + const PxU32 maxIter = MAX_ITER; // 1 for "collide and stop" + const PxU32 maxIterSides = maxIter; + const PxU32 maxIterDown = ((mFlags & STF_WALK_EXPERIMENT) && mUserParams.mNonWalkableMode==PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING) ? maxIter : 1; +// const PxU32 maxIterDown = 1; + + // ### this causes the artificial gap on top of chars + float stepOffset = mUserParams.mStepOffset; // Default step offset can be cancelled in some cases. + + // Save initial height + const PxVec3& upDirection = mUserParams.mUpDirection; + const PxExtended originalHeight = volume.mCenter.dot(upDirection); + const PxExtended originalBottomPoint = originalHeight - PxExtended(volume.mHalfHeight); // UBI + + // TEST! Disable auto-step when flying. Not sure this is really useful. +// if(direction[upDirection]>0.0f) + const float dir_dot_up = direction.dot(upDirection); +//printf("%f\n", dir_dot_up); + if(dir_dot_up>0.0f) + { + mFlags |= STF_IS_MOVING_UP; + + // PT: this makes it fail on a platform moving up when jumping + // However if we don't do that a jump when moving up a slope doesn't work anymore! + // Not doing this also creates jittering when a capsule CCT jumps against another capsule CCT + if(!standingOnMovingUp) // PT: if we're standing on something moving up it's safer to do the up motion anyway, even though this won't work well before we add the flag in TA13542 + { +// static int count=0; printf("Cancelling step offset... %d\n", count++); + stepOffset = 0.0f; + } + } + else + { + mFlags &= ~STF_IS_MOVING_UP; + } + + // Decompose motion into 3 independent motions: up, side, down + // - if the motion is purely down (gravity only), the up part is needed to fight accuracy issues. For example if the + // character is already touching the geometry a bit, the down sweep test might have troubles. If we first move it above + // the geometry, the problems disappear. + // - if the motion is lateral (character moving forward under normal gravity) the decomposition provides the autostep feature + // - if the motion is purely up, the down part can be skipped + + PxVec3 UpVector(0.0f, 0.0f, 0.0f); + PxVec3 DownVector(0.0f, 0.0f, 0.0f); + + PxVec3 normal_compo, tangent_compo; + Ps::decomposeVector(normal_compo, tangent_compo, direction, upDirection); + +// if(direction[upDirection]<0.0f) + if(dir_dot_up<=0.0f) +// DownVector[upDirection] = direction[upDirection]; + DownVector = normal_compo; + else +// UpVector[upDirection] = direction[upDirection]; + UpVector = normal_compo; + +// PxVec3 SideVector = direction; +// SideVector[upDirection] = 0.0f; + PxVec3 SideVector = tangent_compo; + + // If the side motion is zero, i.e. if the character is not really moving, disable auto-step. + // This is important to prevent the CCT from automatically climbing on small objects that move + // against it. We should climb over those only if there's a valid side motion from the player. + const bool sideVectorIsZero = !standingOnMovingUp && Ps::isAlmostZero(SideVector); // We can't use PxVec3::isZero() safely with arbitrary up vectors + // #### however if we do this the up pass is disabled, with bad consequences when the CCT is on a dynamic object!! + // ### this line makes it possible to push other CCTs by jumping on them +// const bool sideVectorIsZero = false; +// printf("sideVectorIsZero: %d\n", sideVectorIsZero); + +// if(!SideVector.isZero()) + if(!sideVectorIsZero) +// UpVector[upDirection] += stepOffset; + UpVector += upDirection*stepOffset; +// printf("stepOffset: %f\n", stepOffset); + + // ==========[ Initial volume query ]=========================== + // PT: the main difference between this initial query and subsequent ones is that we use the + // full direction vector here, not the components along each axis. So there is a good chance + // that this initial query will contain all the motion we need, and thus subsequent queries + // will be skipped. + { + PxExtendedBounds3 temporalBox; + volume.computeTemporalBox(*this, temporalBox, volume.mCenter, direction); + + // Gather touched geoms + updateTouchedGeoms(userData, userObstacles, temporalBox, filters, SideVector); + } + + // ==========[ UP PASS ]=========================== + + mCachedTriIndexIndex = 0; + const bool performUpPass = true; + PxU32 NbCollisions=0; + + PxU32 maxIterUp; + if(mUserParams.mPreventVerticalSlidingAgainstCeiling) + maxIterUp = 1; + else + maxIterUp = Ps::isAlmostZero(SideVector) ? maxIter : 1; + + if(performUpPass) + { +// printf("%f | %f | %f\n", UpVector.x, UpVector.y, UpVector.z); + + // Prevent user callback for up motion. This up displacement is artificial, and only needed for auto-stepping. + // If we call the user for this, we might eventually apply upward forces to objects resting on top of us, even + // if we visually don't move. This produces weird-looking motions. +// mValidateCallback = false; + // PT: actually I think the previous comment is wrong. It's not only needed for auto-stepping: when the character + // jumps there's a legit up motion and the lack of callback in that case could need some object can't be pushed + // by the character's 'head' (for example). So I now think it's better to use the callback all the time, and + // let users figure out what to do using the available state (like "isMovingUp", etc). +// mValidateCallback = true; + + // In the walk-experiment we explicitly want to ban any up motions, to avoid characters climbing slopes they shouldn't climb. + // So let's bypass the whole up pass. + if(!(mFlags & STF_WALK_EXPERIMENT)) + { + // ### maxIter here seems to "solve" the V bug + if(doSweepTest(userData, userHitData, userObstacles, volume, UpVector, SideVector, maxIterUp, &NbCollisions, min_dist, filters, SWEEP_PASS_UP, touchedActor, touchedShape)) + { + if(NbCollisions) + { + CollisionFlags |= PxControllerCollisionFlag::eCOLLISION_UP; + + // Clamp step offset to make sure we don't undo more than what we did + float Delta = float(volume.mCenter.dot(upDirection) - originalHeight); + + if(Delta<stepOffset) + { + stepOffset=Delta; + } + } + } + } + } + + // ==========[ SIDE PASS ]=========================== + + mCachedTriIndexIndex = 1; +// mValidateCallback = true; + const bool PerformSidePass = true; + + mFlags &= ~STF_VALIDATE_TRIANGLE_SIDE; + if(PerformSidePass) + { + NbCollisions=0; + //printf("BS:%.2f %.2f %.2f NS=%d\n", volume.mCenter.x, volume.mCenter.y, volume.mCenter.z, mNbCachedStatic); + if(doSweepTest(userData, userHitData, userObstacles, volume, SideVector, SideVector, maxIterSides, &NbCollisions, min_dist, filters, SWEEP_PASS_SIDE, touchedActor, touchedShape)) + { + if(NbCollisions) + CollisionFlags |= PxControllerCollisionFlag::eCOLLISION_SIDES; + } + //printf("AS:%.2f %.2f %.2f NS=%d\n", volume.mCenter.x, volume.mCenter.y, volume.mCenter.z, mNbCachedStatic); + + if(1 && constrainedClimbingMode && volume.getType()==SweptVolumeType::eCAPSULE && !(mFlags & STF_VALIDATE_TRIANGLE_SIDE)) + { + const float capsuleRadius = static_cast<const SweptCapsule&>(volume).mRadius; + + const float sideM = SideVector.magnitude(); + if(sideM<capsuleRadius) + { + const PxVec3 sensor = SideVector.getNormalized() * capsuleRadius; + + mFlags &= ~STF_VALIDATE_TRIANGLE_SIDE; + NbCollisions=0; + //printf("BS:%.2f %.2f %.2f NS=%d\n", volume.mCenter.x, volume.mCenter.y, volume.mCenter.z, mNbCachedStatic); + const PxExtendedVec3 saved = volume.mCenter; + doSweepTest(userData, userHitData, userObstacles, volume, sensor, SideVector, 1, &NbCollisions, min_dist, filters, SWEEP_PASS_SENSOR, touchedActor, touchedShape); + volume.mCenter = saved; + } + } + } + + // ==========[ DOWN PASS ]=========================== + + mCachedTriIndexIndex = 2; + const bool PerformDownPass = true; + + if(PerformDownPass) + { + NbCollisions=0; + +// if(!SideVector.isZero()) // We disabled that before so we don't have to undo it in that case + if(!sideVectorIsZero) // We disabled that before so we don't have to undo it in that case +// DownVector[upDirection] -= stepOffset; // Undo our artificial up motion + DownVector -= upDirection*stepOffset; // Undo our artificial up motion + + mFlags &= ~STF_VALIDATE_TRIANGLE_DOWN; + touchedShape = NULL; + touchedActor = NULL; + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + + // min_dist actually makes a big difference :( + // AAARRRGGH: if we get culled because of min_dist here, mValidateTriangle never becomes valid! + if(doSweepTest(userData, userHitData, userObstacles, volume, DownVector, SideVector, maxIterDown, &NbCollisions, min_dist, filters, SWEEP_PASS_DOWN, touchedActor, touchedShape)) + { + if(NbCollisions) + { + if(dir_dot_up<=0.0f) // PT: fix attempt + CollisionFlags |= PxControllerCollisionFlag::eCOLLISION_DOWN; + + if(mUserParams.mHandleSlope && !(mFlags & (STF_TOUCH_OTHER_CCT|STF_TOUCH_OBSTACLE))) // PT: I think the following fix shouldn't be performed when mHandleSlope is false. + { + // PT: the following code is responsible for a weird capsule behaviour, + // when colliding against a highly tesselated terrain: + // - with a large direction vector, the capsule gets stuck against some part of the terrain + // - with a slower direction vector (but in the same direction!) the capsule manages to move + // I will keep that code nonetheless, since it seems to be useful for them. +//printf("%d\n", mFlags & STF_VALIDATE_TRIANGLE_SIDE); + // constrainedClimbingMode + if((mFlags & STF_VALIDATE_TRIANGLE_SIDE) && testSlope(mContactNormalSidePass, upDirection, mUserParams.mSlopeLimit)) + { +//printf("%d\n", mFlags & STF_VALIDATE_TRIANGLE_SIDE); + if(constrainedClimbingMode && PxExtended(mContactPointHeight) > originalBottomPoint + PxExtended(stepOffset)) + { + mFlags |= STF_HIT_NON_WALKABLE; + if(!(mFlags & STF_WALK_EXPERIMENT)) + return CollisionFlags; + // printf("Contrained\n"); + } + } + //~constrainedClimbingMode + } + } + } + //printf("AD:%.2f %.2f %.2f NS=%d\n", volume.mCenter.x, volume.mCenter.y, volume.mCenter.z, mNbCachedStatic); +// printf("%d\n", mTouchOtherCCT); + + // TEST: do another down pass if we're on a non-walkable poly + // ### kind of works but still not perfect + // ### could it be because we zero the Y impulse later? + // ### also check clamped response vectors +// if(mUserParams.mHandleSlope && mValidateTriangle && direction[upDirection]<0.0f) +// if(mUserParams.mHandleSlope && !mTouchOtherCCT && !mTouchObstacle && mValidateTriangle && dir_dot_up<0.0f) + if(mUserParams.mHandleSlope && !(mFlags & (STF_TOUCH_OTHER_CCT|STF_TOUCH_OBSTACLE)) && (mFlags & STF_VALIDATE_TRIANGLE_DOWN) && dir_dot_up<=0.0f) + { + PxVec3 Normal; + #ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST + Normal = mContactNormalDownPass; + #else + //mTouchedTriangle.normal(Normal); + Normal = mContactNormalDownPass; + #endif + + const float touchedTriHeight = float(PxExtended(mTouchedTriMax) - originalBottomPoint); + +/* if(touchedTriHeight>mUserParams.mStepOffset) + { + if(constrainedClimbingMode && mContactPointHeight > originalBottomPoint + stepOffset) + { + mFlags |= STF_HIT_NON_WALKABLE; + if(!(mFlags & STF_WALK_EXPERIMENT)) + return CollisionFlags; + } + }*/ + + if(touchedTriHeight>mUserParams.mStepOffset && testSlope(Normal, upDirection, mUserParams.mSlopeLimit)) + { + mFlags |= STF_HIT_NON_WALKABLE; + // Early exit if we're going to run this again anyway... + if(!(mFlags & STF_WALK_EXPERIMENT)) + return CollisionFlags; + /* CatchScene()->GetRenderer()->AddLine(mTouchedTriangle.mVerts[0], mTouched.mVerts[1], ARGB_YELLOW); + CatchScene()->GetRenderer()->AddLine(mTouchedTriangle.mVerts[0], mTouched.mVerts[2], ARGB_YELLOW); + CatchScene()->GetRenderer()->AddLine(mTouchedTriangle.mVerts[1], mTouched.mVerts[2], ARGB_YELLOW); + */ + + // ==========[ WALK EXPERIMENT ]=========================== + + mFlags |= STF_NORMALIZE_RESPONSE; + + const PxExtended tmp = volume.mCenter.dot(upDirection); + float Delta = tmp > originalHeight ? float(tmp - originalHeight) : 0.0f; + Delta += fabsf(direction.dot(upDirection)); + float Recover = Delta; + + NbCollisions=0; + const float MD = Recover < min_dist ? Recover/float(maxIter) : min_dist; + + PxVec3 RecoverPoint(0,0,0); + RecoverPoint = -upDirection*Recover; + + // PT: we pass "SWEEP_PASS_UP" for compatibility with previous code, but it's technically wrong (this is a 'down' pass) + if(doSweepTest(userData, userHitData, userObstacles, volume, RecoverPoint, SideVector, maxIter, &NbCollisions, MD, filters, SWEEP_PASS_UP, touchedActor, touchedShape)) + { + // if(NbCollisions) CollisionFlags |= COLLISION_Y_DOWN; + // PT: why did we do this ? Removed for now. It creates a bug (non registered event) when we land on a steep poly. + // However this might have been needed when we were sliding on those polygons, and we didn't want the land anim to + // start while we were sliding. + // if(NbCollisions) CollisionFlags &= ~PxControllerCollisionFlag::eCOLLISION_DOWN; + } + mFlags &= ~STF_NORMALIZE_RESPONSE; + } + } + } + + return CollisionFlags; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// This is an interface between NX users and the internal character controller module. + +#include "CctInternalStructs.h" +#include "CctBoxController.h" +#include "CctCapsuleController.h" +#include "CctCharacterControllerManager.h" +#include "PxActor.h" +#include "PxScene.h" +#include "PxControllerBehavior.h" +#include "CctObstacleContext.h" + + // PT: we use a local class instead of making "Controller" a PxQueryFilterCallback, since it would waste more memory. + // Ideally we'd have a C-style callback and a user-data pointer, instead of being forced to create a class. + class ControllerFilter : public PxQueryFilterCallback + { + public: + PxQueryHitType::Enum preFilter(const PxFilterData& filterData, const PxShape* shape, const PxRigidActor* actor, PxHitFlags& queryFlags) + { + // PT: ignore triggers + if(shape->getFlags() & physx::PxShapeFlag::eTRIGGER_SHAPE) + return PxQueryHitType::eNONE; + + // PT: we want to discard our own internal shapes only + if(mShapeHashSet->contains(const_cast<PxShape*>(shape))) + return PxQueryHitType::eNONE; + + // PT: otherwise we revert to the user-callback, if it exists, and if users enabled that call + if(mUserFilterCallback && (mUserFilterFlags | PxQueryFlag::ePREFILTER)) + return mUserFilterCallback->preFilter(filterData, shape, actor, queryFlags); + + return PxQueryHitType::eBLOCK; + } + + PxQueryHitType::Enum postFilter(const PxFilterData& filterData, const PxQueryHit& hit) + { + // PT: we may get called if users have asked for such a callback + if(mUserFilterCallback && (mUserFilterFlags | PxQueryFlag::ePOSTFILTER)) + return mUserFilterCallback->postFilter(filterData, hit); + + PX_ASSERT(0); // PT: otherwise we shouldn't have been called + return PxQueryHitType::eNONE; + } + + Ps::HashSet<PxShape*>* mShapeHashSet; + PxQueryFilterCallback* mUserFilterCallback; + PxQueryFlags mUserFilterFlags; + }; + + +bool Controller::filterTouchedShape(const PxControllerFilters& filters) +{ + PxQueryFlags filterFlags = PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER; + PxQueryFilterData filterData(filters.mFilterData ? *filters.mFilterData : PxFilterData(), filterFlags); + PxHitFlags hitFlags = PxHitFlag::eDISTANCE; + + if(filters.mFilterCallback && (filters.mFilterFlags | PxQueryFlag::ePREFILTER)) + { + PxQueryHitType::Enum retVal = filters.mFilterCallback->preFilter(filterData.data, mCctModule.mTouchedShape.get(), mCctModule.mTouchedActor.get(), hitFlags); + if(retVal != PxQueryHitType::eNONE) + return true; + else + return false; + } + + return true; +} + +void Controller::findTouchedObject(const PxControllerFilters& filters, const PxObstacleContext* obstacleContext, const PxVec3& upDirection) +{ + PX_ASSERT(!mCctModule.mTouchedShape && (mCctModule.mTouchedObstacleHandle == INVALID_OBSTACLE_HANDLE)); + + // PT: the CCT works perfectly on statics without this extra mechanism, so we only raycasts against dynamics. + // The pre-filter callback is used to filter out our own proxy actor shapes. We need to make sure our own filter + // doesn't disturb the user-provided filter(s). + + // PT: for starter, if user doesn't want to collide against dynamics, we can skip the whole thing + if(filters.mFilterFlags & PxQueryFlag::eDYNAMIC) + { + ControllerFilter preFilter; + preFilter.mShapeHashSet = &mManager->mCCTShapes; + preFilter.mUserFilterCallback = filters.mFilterCallback; + preFilter.mUserFilterFlags = filters.mFilterFlags; + + // PT: for our own purpose we just want dynamics & pre-filter + PxQueryFlags filterFlags = PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER; + // PT: but we may need the post-filter callback as well if users want it + if(filters.mFilterFlags & PxQueryFlag::ePOSTFILTER) + filterFlags |= PxQueryFlag::ePOSTFILTER; + + PxQueryFilterData filterData(filters.mFilterData ? *filters.mFilterData : PxFilterData(), filterFlags); + + const PxF32 probeLength = getHalfHeightInternal(); // Distance to feet + const PxF32 extra = 0.0f;//probeLength * 0.1f; + + const PxVec3 rayOrigin = toVec3(mPosition); + + PxRaycastBuffer hit; + hit.block.distance = FLT_MAX; + if(mScene->raycast(rayOrigin, -upDirection, probeLength+extra, hit, PxHitFlag::eDISTANCE, filterData, &preFilter)) + { + // copy touching hit to blocking so that the rest of the code works with .block + hit.block = hit.getAnyHit(0); + PX_ASSERT(hit.block.shape); + PX_ASSERT(hit.block.actor); + PX_ASSERT(hit.block.distance<=probeLength+extra); + mCctModule.mTouchedShape = hit.block.shape; + mCctModule.mTouchedActor = hit.block.actor; +// mCctModule.mTouchedPos = getShapeGlobalPose(*hit.shape).p - upDirection*(probeLength-hit.distance); + // PT: we only care about the up delta here + const PxTransform shapeTransform = getShapeGlobalPose(*hit.block.shape, *hit.block.actor); + mCctModule.mTouchedPosShape_World = PxVec3(0) - upDirection*(probeLength-hit.block.distance); + mCctModule.mTouchedPosShape_Local = shapeTransform.transformInv(PxVec3(0)); + + mPreviousSceneTimestamp = mScene->getTimestamp()-1; // PT: just make sure cached timestamp is different + } + + if(obstacleContext) + { + const ObstacleContext* obstacles = static_cast<const ObstacleContext*>(obstacleContext); + PxRaycastHit obstacleHit; + ObstacleHandle obstacleHandle; + const PxObstacle* touchedObstacle = obstacles->raycastSingle(obstacleHit, rayOrigin, -upDirection, probeLength+extra, obstacleHandle); +// printf("Touched raycast obstacle: %d\n", int(touchedObstacle)); + if(touchedObstacle && obstacleHit.distance<hit.block.distance) + { + PX_ASSERT(obstacleHit.distance<=probeLength+extra); + mCctModule.mTouchedObstacleHandle = obstacleHandle; + if(!gUseLocalSpace) + { + mCctModule.mTouchedPos = toVec3(touchedObstacle->mPos) - upDirection*(probeLength-obstacleHit.distance); + } + else + { + // PT: we only care about the up delta here + mCctModule.mTouchedPosObstacle_World = PxVec3(0) - upDirection*(probeLength-obstacleHit.distance); + mCctModule.mTouchedPosObstacle_Local = worldToLocal(*touchedObstacle, PxExtendedVec3(0,0,0)); + } + } + } + } +} + +bool Controller::rideOnTouchedObject(SweptVolume& volume, const PxVec3& upDirection, PxVec3& disp, const PxObstacleContext* obstacleContext) +{ + PX_ASSERT(mCctModule.mTouchedShape || (mCctModule.mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE)); + + bool standingOnMoving = false; + + bool canDoUpdate = true; // Always true on obstacles + PxU32 behaviorFlags = 0; // Default on shapes + PxVec3 delta(0); + float timeCoeff = 1.0f; + + if(mCctModule.mTouchedShape) + { + // PT: riding on a shape + + // PT: it is important to skip this stuff for static meshes, + // otherwise accuracy issues create bugs like TA14007. + const PxRigidActor& rigidActor = *mCctModule.mTouchedActor.get(); + if(rigidActor.getConcreteType()!=PxConcreteType::eRIGID_STATIC) + { + // PT: we only do the update when the timestamp has changed, otherwise "delta" will be zero + // even if the underlying shape is moving. + const PxU32 timestamp = mScene->getTimestamp(); +// printf("TimeStamp: %d\n", timestamp); + canDoUpdate = timestamp!=mPreviousSceneTimestamp; + if(canDoUpdate) + { + mPreviousSceneTimestamp = timestamp; + + timeCoeff = computeTimeCoeff(); + + if(mBehaviorCallback) + behaviorFlags = mBehaviorCallback->getBehaviorFlags(*mCctModule.mTouchedShape.get(), *mCctModule.mTouchedActor.get()); + +// delta = getShapeGlobalPose(*mCctModule.mTouchedShape).p - mCctModule.mTouchedPos; + const PxTransform shapeTransform = getShapeGlobalPose(*mCctModule.mTouchedShape.get(), rigidActor); + const PxVec3 posPreviousFrame = mCctModule.mTouchedPosShape_World; + const PxVec3 posCurrentFrame = shapeTransform.transform(mCctModule.mTouchedPosShape_Local); + delta = posCurrentFrame - posPreviousFrame; + } + } + } + else + { + // PT: riding on an obstacle + behaviorFlags = PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT; // Default on obstacles + + timeCoeff = computeTimeCoeff(); + + const PxObstacle* touchedObstacle = obstacleContext->getObstacleByHandle(mCctModule.mTouchedObstacleHandle); + PX_ASSERT(touchedObstacle); + + if(mBehaviorCallback) + behaviorFlags = mBehaviorCallback->getBehaviorFlags(*touchedObstacle); + + if(!gUseLocalSpace) + { + delta = toVec3(touchedObstacle->mPos) - mCctModule.mTouchedPos; + } + else + { + PxVec3 posPreviousFrame = mCctModule.mTouchedPosObstacle_World; + PxVec3 posCurrentFrame = localToWorld(*touchedObstacle, mCctModule.mTouchedPosObstacle_Local); + delta = posCurrentFrame - posPreviousFrame; + } + } + + if(canDoUpdate && !(behaviorFlags & PxControllerBehaviorFlag::eCCT_USER_DEFINED_RIDE)) + { + // PT: amazingly enough even isAlmostZero doesn't solve this one. + // Moving on a static mesh sometimes produces delta bigger than 1e-6f! + // This may also explain the drift on some rotating platforms. It looks + // like this delta computation is not very accurate. +// standingOnMoving = !delta.isZero(); + standingOnMoving = !Ps::isAlmostZero(delta); + mCachedStandingOnMoving = standingOnMoving; +//printf("%f %f %f\n", delta.x, delta.y, delta.z); + if(standingOnMoving) + { + const float dir_dot_up = delta.dot(upDirection); + const bool deltaMovingUp = dir_dot_up>0.0f; + + PxVec3 deltaUpDisp, deltaSideDisp; + Ps::decomposeVector(deltaUpDisp, deltaSideDisp, delta, upDirection); + + if(deltaMovingUp) + { + volume.mCenter.x += PxExtended(deltaUpDisp.x); + volume.mCenter.y += PxExtended(deltaUpDisp.y); + volume.mCenter.z += PxExtended(deltaUpDisp.z); + } + else + { + disp += deltaUpDisp; + } + + if(behaviorFlags & PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT) + disp += deltaSideDisp; + } +// printf("delta in: %f %f %f (%f)\n", delta.x, delta.y, delta.z, 1.0f/timeCoeff); + mDeltaXP = delta * timeCoeff; + } + else + { + standingOnMoving = mCachedStandingOnMoving; + } +// mDelta = delta; + + return standingOnMoving; +} + +PxControllerCollisionFlags Controller::move(SweptVolume& volume, const PxVec3& originalDisp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacleContext, bool constrainedClimbingMode) +{ + const bool lockWrite = mManager->mLockingEnabled; + if(lockWrite) + mWriteLock.lock(); + + mGlobalTime += PxF64(elapsedTime); + + // Init CCT with per-controller settings + Cm::RenderBuffer* renderBuffer = mManager->mRenderBuffer; + const PxU32 debugRenderFlags = mManager->mDebugRenderingFlags; + mCctModule.mRenderBuffer = renderBuffer; + mCctModule.mRenderFlags = debugRenderFlags; + mCctModule.mUserParams = mUserParams; + mCctModule.mFlags |= STF_FIRST_UPDATE; + mCctModule.mUserParams.mMaxEdgeLength2 = mManager->mMaxEdgeLength * mManager->mMaxEdgeLength; + mCctModule.mUserParams.mTessellation = mManager->mTessellation; + mCctModule.mUserParams.mOverlapRecovery = mManager->mOverlapRecovery; + mCctModule.mUserParams.mPreciseSweeps = mManager->mPreciseSweeps; + mCctModule.mUserParams.mPreventVerticalSlidingAgainstCeiling = mManager->mPreventVerticalSlidingAgainstCeiling; + mCctModule.resetStats(); + + const PxVec3& upDirection = mUserParams.mUpDirection; + + /////////// + + PxVec3 disp = originalDisp + mOverlapRecover; + mOverlapRecover = PxVec3(0.0f); + + bool standingOnMoving = false; // PT: whether the CCT is currently standing on a moving object + //printf("Touched shape: %d\n", int(mCctModule.mTouchedShape)); +//standingOnMoving=true; +// printf("Touched obstacle: %d\n", int(mCctModule.mTouchedObstacle)); + + if(mCctModule.mTouchedActor && mCctModule.mTouchedShape) + { + PxU32 nbShapes = mCctModule.mTouchedActor->getNbShapes(); + bool found = false; + for(PxU32 i=0;i<nbShapes;i++) + { + PxShape* shape = NULL; + mCctModule.mTouchedActor->getShapes(&shape, 1, i); + if(mCctModule.mTouchedShape==shape) + { + found = true; + break; + } + } + + if(!found) + { + mCctModule.mTouchedActor = NULL; + mCctModule.mTouchedShape = NULL; + } + else + { + // check if we are still in the same scene + if(mCctModule.mTouchedActor->getScene() != mScene) + { + mCctModule.mTouchedShape = NULL; + mCctModule.mTouchedActor = NULL; + } + else + { + // check if the shape still does have the sq flag + if(!(mCctModule.mTouchedShape->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE)) + { + mCctModule.mTouchedShape = NULL; + mCctModule.mTouchedActor = NULL; + } + else + { + // invoke the CCT filtering for the shape + if(!filterTouchedShape(filters)) + { + mCctModule.mTouchedShape = NULL; + mCctModule.mTouchedActor = NULL; + } + } + } + } + } + + if(!mCctModule.mTouchedShape && (mCctModule.mTouchedObstacleHandle == INVALID_OBSTACLE_HANDLE)) + findTouchedObject(filters, obstacleContext, upDirection); + + if(mCctModule.mTouchedShape || (mCctModule.mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE)) + { + standingOnMoving = rideOnTouchedObject(volume, upDirection, disp, obstacleContext); + } + else + { + mCachedStandingOnMoving = false; + mDeltaXP = PxVec3(0.0f); + } +// printf("standingOnMoving: %d\n", standingOnMoving); + + /////////// + Ps::Array<const void*>& boxUserData = mManager->mBoxUserData; + Ps::Array<PxExtendedBox>& boxes = mManager->mBoxes; + Ps::Array<const void*>& capsuleUserData = mManager->mCapsuleUserData; + Ps::Array<PxExtendedCapsule>& capsules = mManager->mCapsules; + PX_ASSERT(!boxUserData.size()); + PX_ASSERT(!boxes.size()); + PX_ASSERT(!capsuleUserData.size()); + PX_ASSERT(!capsules.size()); + + { + // Experiment - to do better + const PxU32 nbControllers = mManager->getNbControllers(); + Controller** controllers = mManager->getControllers(); + + for(PxU32 i=0;i<nbControllers;i++) + { + Controller* currentController = controllers[i]; + if(currentController==this) + continue; + + bool keepController = true; + if(filters.mCCTFilterCallback) + keepController = filters.mCCTFilterCallback->filter(*getPxController(), *currentController->getPxController()); + + if(keepController) + { + if(currentController->mType==PxControllerShapeType::eBOX) + { + // PT: TODO: optimize this + BoxController* BC = static_cast<BoxController*>(currentController); + PxExtendedBox obb; + BC->getOBB(obb); + + boxes.pushBack(obb); + +#ifdef REMOVED + if(renderBuffer /*&& (debugRenderFlags & PxControllerDebugRenderFlag::eOBSTACLES)*/) + { + Cm::RenderOutput out(*renderBuffer); + out << gCCTBoxDebugColor; + + out << PxTransform(toVec3(obb.center), obb.rot); + + out << Cm::DebugBox(obb.extents, true); + } +#endif + const size_t code = encodeUserObject(i, USER_OBJECT_CCT); + boxUserData.pushBack(reinterpret_cast<const void*>(code)); + } + else if(currentController->mType==PxControllerShapeType::eCAPSULE) + { + CapsuleController* CC = static_cast<CapsuleController*>(currentController); + + // PT: TODO: optimize this + PxExtendedCapsule worldCapule; + CC->getCapsule(worldCapule); + capsules.pushBack(worldCapule); + + const size_t code = encodeUserObject(i, USER_OBJECT_CCT); + capsuleUserData.pushBack(reinterpret_cast<const void*>(code)); + } + else PX_ASSERT(0); + } + } + } + + const ObstacleContext* obstacles = NULL; + if(obstacleContext) + { + obstacles = static_cast<const ObstacleContext*>(obstacleContext); + + // PT: TODO: optimize this + const PxU32 nbExtraBoxes = obstacles->mBoxObstacles.size(); + for(PxU32 i=0;i<nbExtraBoxes;i++) + { + const PxBoxObstacle& userBoxObstacle = obstacles->mBoxObstacles[i].mData; + + PxExtendedBox extraBox; + extraBox.center = userBoxObstacle.mPos; + extraBox.extents = userBoxObstacle.mHalfExtents; + extraBox.rot = userBoxObstacle.mRot; + boxes.pushBack(extraBox); + + const size_t code = encodeUserObject(i, USER_OBJECT_BOX_OBSTACLE); + boxUserData.pushBack(reinterpret_cast<const void*>(code)); + + if(renderBuffer && (debugRenderFlags & PxControllerDebugRenderFlag::eOBSTACLES)) + { + Cm::RenderOutput out(*renderBuffer); + out << gObstacleDebugColor; + + out << PxTransform(toVec3(userBoxObstacle.mPos), userBoxObstacle.mRot); + + out << Cm::DebugBox(userBoxObstacle.mHalfExtents, true); + } + } + + const PxU32 nbExtraCapsules = obstacles->mCapsuleObstacles.size(); + for(PxU32 i=0;i<nbExtraCapsules;i++) + { + const PxCapsuleObstacle& userCapsuleObstacle = obstacles->mCapsuleObstacles[i].mData; + + PxExtendedCapsule extraCapsule; + const PxVec3 capsuleAxis = userCapsuleObstacle.mRot.getBasisVector0() * userCapsuleObstacle.mHalfHeight; + extraCapsule.p0 = PxExtendedVec3( userCapsuleObstacle.mPos.x - PxExtended(capsuleAxis.x), + userCapsuleObstacle.mPos.y - PxExtended(capsuleAxis.y), + userCapsuleObstacle.mPos.z - PxExtended(capsuleAxis.z)); + extraCapsule.p1 = PxExtendedVec3( userCapsuleObstacle.mPos.x + PxExtended(capsuleAxis.x), + userCapsuleObstacle.mPos.y + PxExtended(capsuleAxis.y), + userCapsuleObstacle.mPos.z + PxExtended(capsuleAxis.z)); + + extraCapsule.radius = userCapsuleObstacle.mRadius; + capsules.pushBack(extraCapsule); + const size_t code = encodeUserObject(i, USER_OBJECT_CAPSULE_OBSTACLE); + capsuleUserData.pushBack(reinterpret_cast<const void*>(code)); + + if(renderBuffer && (debugRenderFlags & PxControllerDebugRenderFlag::eOBSTACLES)) + { + Cm::RenderOutput out(*renderBuffer); + out << gObstacleDebugColor; + out.outputCapsule(userCapsuleObstacle.mRadius, userCapsuleObstacle.mHalfHeight, PxTransform(toVec3(userCapsuleObstacle.mPos), userCapsuleObstacle.mRot)); + } + } + } + + + UserObstacles userObstacles; + + const PxU32 nbBoxes = boxes.size(); + userObstacles.mNbBoxes = nbBoxes; + userObstacles.mBoxes = nbBoxes ? boxes.begin() : NULL; + userObstacles.mBoxUserData = nbBoxes ? boxUserData.begin() : NULL; + + const PxU32 nbCapsules = capsules.size(); + userObstacles.mNbCapsules = nbCapsules; + userObstacles.mCapsules = nbCapsules ? capsules.begin() : NULL; + userObstacles.mCapsuleUserData = nbCapsules ? capsuleUserData.begin() : NULL; + + PxInternalCBData_OnHit userHitData; + userHitData.controller = this; + userHitData.obstacles = obstacles; + + /////////// + + PxControllerCollisionFlags collisionFlags = PxControllerCollisionFlags(0); + + PxInternalCBData_FindTouchedGeom findGeomData; + findGeomData.scene = mScene; + findGeomData.renderBuffer = renderBuffer; + findGeomData.cctShapeHashSet = &mManager->mCCTShapes; + + mCctModule.mFlags &= ~STF_WALK_EXPERIMENT; + + // store new touched actor/shape. Then set new actor/shape to avoid register/unregister for same objects + const PxRigidActor* touchedActor = NULL; + const PxShape* touchedShape = NULL; + PxExtendedVec3 Backup = volume.mCenter; + collisionFlags = mCctModule.moveCharacter(&findGeomData, &userHitData, volume, disp, userObstacles, minDist, filters, constrainedClimbingMode, standingOnMoving, touchedActor, touchedShape); + + if(mCctModule.mFlags & STF_HIT_NON_WALKABLE) + { + // A bit slow, but everything else I tried was less convincing... + mCctModule.mFlags |= STF_WALK_EXPERIMENT; + volume.mCenter = Backup; + + PxVec3 xpDisp; + if(mUserParams.mNonWalkableMode==PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING) + { + PxVec3 tangent_compo; + Ps::decomposeVector(xpDisp, tangent_compo, disp, upDirection); + } + else xpDisp = disp; + + collisionFlags = mCctModule.moveCharacter(&findGeomData, &userHitData, volume, xpDisp, userObstacles, minDist, filters, constrainedClimbingMode, standingOnMoving, touchedActor, touchedShape); + + mCctModule.mFlags &= ~STF_WALK_EXPERIMENT; + } + mCctModule.mTouchedActor = touchedActor; + mCctModule.mTouchedShape = touchedShape; + + mCollisionFlags = collisionFlags; + + // Copy results back + mPosition = volume.mCenter; + + // Update kinematic actor + if(mKineActor) + { + const PxVec3 delta = Backup - volume.mCenter; + const PxF32 deltaM2 = delta.magnitudeSquared(); + if(deltaM2!=0.0f) + { + PxTransform targetPose = mKineActor->getGlobalPose(); + targetPose.p = toVec3(mPosition); + targetPose.q = mUserParams.mQuatFromUp; + mKineActor->setKinematicTarget(targetPose); + } + } + + mManager->resetObstaclesBuffers(); + + if (lockWrite) + mWriteLock.unlock(); + + return collisionFlags; +} + + +PxControllerCollisionFlags BoxController::move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles) +{ + PX_SIMD_GUARD; + + // Create internal swept box + SweptBox sweptBox; + sweptBox.mCenter = mPosition; + sweptBox.mExtents = PxVec3(mHalfHeight, mHalfSideExtent, mHalfForwardExtent); + sweptBox.mHalfHeight = mHalfHeight; // UBI + return Controller::move(sweptBox, disp, minDist, elapsedTime, filters, obstacles, false); +} + +PxControllerCollisionFlags CapsuleController::move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles) +{ + PX_SIMD_GUARD; + + // Create internal swept capsule + SweptCapsule sweptCapsule; + sweptCapsule.mCenter = mPosition; + sweptCapsule.mRadius = mRadius; + sweptCapsule.mHeight = mHeight; + sweptCapsule.mHalfHeight = mHeight*0.5f + mRadius; // UBI + return Controller::move(sweptCapsule, disp, minDist, elapsedTime, filters, obstacles, mClimbingMode==PxCapsuleClimbingMode::eCONSTRAINED); +} + diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterController.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterController.h new file mode 100644 index 00000000..f87f7512 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterController.h @@ -0,0 +1,459 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PxController.h" +#include "PxControllerObstacles.h" +#include "CctCharacterControllerManager.h" +#include "CctUtils.h" +#include "PxTriangle.h" +#include "PsArray.h" +#include "PsHashSet.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +struct PxFilterData; +class PxQueryFilterCallback; +class PxObstacle; + +namespace Cm +{ + 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; + }; + + template<class T, class A> + PX_INLINE T* reserve(Ps::Array<T, A>& array, PxU32 nb) + { + const PxU32 currentSize = array.size(); + array.resizeUninitialized(array.size() + nb); + return array.begin() + currentSize; + } + +// typedef Ps::Array<PxTriangle> TriArray; + typedef Ps::Array<PxU32> IntArray; + + // PT: using private inheritance to control access, and make sure allocations are SIMD friendly + class TriArray : private Ps::Array<PxTriangle> + { + public: + + PX_FORCE_INLINE PxTriangle* reserve(PxU32 nbTris) + { + const PxU32 currentSize = Ps::Array<PxTriangle>::size(); + // PT: allocate one more tri to make sure we can safely V4Load the last one... + Ps::Array<PxTriangle>::resizeUninitialized(currentSize + nbTris + 1); + // ...but we still want the size to reflect the correct number + Ps::Array<PxTriangle>::forceSize_Unsafe(currentSize + nbTris); + return Ps::Array<PxTriangle>::begin() + currentSize; + } + + PX_FORCE_INLINE void pushBack(const PxTriangle& tri) + { +// Ps::Array<PxTriangle>::pushBack(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 Ps::Array<PxTriangle>::size(); + } + + PX_FORCE_INLINE void clear() + { + Ps::Array<PxTriangle>::clear(); + } + + PX_FORCE_INLINE void forceSize_Unsafe(PxU32 size) + { + Ps::Array<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, + + eLAST, + + eFORCE_DWORD = 0x7fffffff + }; + }; + + class SweptVolume; + + // PT: apparently .Net aligns some of them on 8-bytes boundaries for no good reason. This is bad. + // Whenever a variable points to a field of a specially aligned struct, it has to be declared with __packed (see GHS docu, Structure Packing, page 111). + // Every reference to such a field needs the __packed declaration: all function parameters and assignment operators etc. +#pragma pack(push,4) + + 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 TouchedCapsule : public TouchedGeom + { + PxVec3 mP0; //!< Start of segment + PxVec3 mP1; //!< End of segment + PxF32 mRadius; //!< Capsule's radius + }; + +#pragma pack(pop) + + 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 + { + 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 + ); + + 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); + + 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(ObstacleHandle index); + void onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance); + void onObstacleAdded(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance); + + void onOriginShift(const PxVec3& shift); + + Cm::RenderBuffer* 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 + ObstacleHandle 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; + SweepTest(const SweepTest&); + SweepTest& operator=(const SweepTest& ); + }; + + 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; + Ps::HashSet<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 diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerCallbacks.cpp b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerCallbacks.cpp new file mode 100644 index 00000000..dc59ca59 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerCallbacks.cpp @@ -0,0 +1,1100 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "CctInternalStructs.h" +#include "PxScene.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxBoxGeometry.h" +#include "PxConvexMesh.h" +#include "PxMeshQuery.h" +#include "extensions/PxTriangleMeshExt.h" +#include "PxTriangleMeshGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "CmRenderOutput.h" +#include "GuIntersectionTriangleBox.h" +#include "PsMathUtils.h" +#include "GuSIMDHelpers.h" + +static const bool gVisualizeTouchedTris = true; +static const float gDebugVisOffset = 0.01f; + +using namespace physx; +using namespace Cct; +using namespace Gu; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: HINT: disable gUsePartialUpdates for easier visualization +static void visualizeTouchedTriangles(PxU32 nbTrisToRender, PxU32 startIndex, const PxTriangle* triangles, Cm::RenderBuffer* renderBuffer, const PxVec3& offset, const PxVec3& upDirection) +{ + if(!renderBuffer) + return; + + PxVec3 yy = -offset; + yy += upDirection * gDebugVisOffset; // PT: move slightly in the up direction + + for(PxU32 i=0; i<nbTrisToRender; i++) + { + const PxTriangle& currentTriangle = triangles[i+startIndex]; + +// Cm::RenderOutput(*renderBuffer) +// << PxDebugColor::eARGB_GREEN << Cm::RenderOutput::TRIANGLES +// << currentTriangle.verts[0]+yy << currentTriangle.verts[1]+yy << currentTriangle.verts[2]+yy; + Cm::RenderOutput(*renderBuffer) + << PxU32(PxDebugColor::eARGB_GREEN) << Cm::RenderOutput::LINES + << currentTriangle.verts[0]+yy << currentTriangle.verts[1]+yy + << currentTriangle.verts[1]+yy << currentTriangle.verts[2]+yy + << currentTriangle.verts[2]+yy << currentTriangle.verts[0]+yy; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct TessParams +{ + PxU32 nbNewTris; + PxU32 index; + TriArray* worldTriangles; + IntArray* triIndicesArray; + PxVec3 cullingBoxCenter; // PT: make sure we can read 4 bytes after this one + PxVec3 cullingBoxExtents; // PT: make sure we can read 4 bytes after this one + PxF32 maxEdgeLength2; + PxU16 nbTessellation; +}; + +static void tessellateTriangleRecursive(TessParams* tp, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2) +{ + tp->nbTessellation++; + + // PT: this one is safe, v0/v1/v2 can always be V4Loaded + if(!intersectTriangleBox_Unsafe(tp->cullingBoxCenter, tp->cullingBoxExtents, v0, v1, v2)) + return; + + PxU32 code; + { + const PxVec3 edge0 = v0 - v1; + const PxVec3 edge1 = v1 - v2; + const PxVec3 edge2 = v2 - v0; + const float maxEdgeLength2 = tp->maxEdgeLength2; + const bool split0 = edge0.magnitudeSquared()>maxEdgeLength2; + const bool split1 = edge1.magnitudeSquared()>maxEdgeLength2; + const bool split2 = edge2.magnitudeSquared()>maxEdgeLength2; + code = (PxU32(split2)<<2)|(PxU32(split1)<<1)|PxU32(split0); + } + + // PT: using Vec3p to make sure we can safely V4LoadU these vertices + const Vec3p m0 = (v0 + v1)*0.5f; + const Vec3p m1 = (v1 + v2)*0.5f; + const Vec3p m2 = (v2 + v0)*0.5f; + + switch(code) + { + case 0: // 000: no split + { + tp->worldTriangles->pushBack(PxTriangle(v0, v1, v2)); + tp->triIndicesArray->pushBack(tp->index); + tp->nbNewTris++; + } + break; + case 1: // 001: split edge0 + { + tessellateTriangleRecursive(tp, v0, m0, v2); + tessellateTriangleRecursive(tp, m0, v1, v2); + } + break; + case 2: // 010: split edge1 + { + tessellateTriangleRecursive(tp, v0, v1, m1); + tessellateTriangleRecursive(tp, v0, m1, v2); + } + break; + case 3: // 011: split edge0/edge1 + { + tessellateTriangleRecursive(tp, v0, m0, m1); + tessellateTriangleRecursive(tp, v0, m1, v2); + tessellateTriangleRecursive(tp, m0, v1, m1); + } + break; + case 4: // 100: split edge2 + { + tessellateTriangleRecursive(tp, v0, v1, m2); + tessellateTriangleRecursive(tp, v1, v2, m2); + } + break; + case 5: // 101: split edge0/edge2 + { + tessellateTriangleRecursive(tp, v0, m0, m2); + tessellateTriangleRecursive(tp, m0, v1, m2); + tessellateTriangleRecursive(tp, m2, v1, v2); + } + break; + case 6: // 110: split edge1/edge2 + { + tessellateTriangleRecursive(tp, v0, v1, m1); + tessellateTriangleRecursive(tp, v0, m1, m2); + tessellateTriangleRecursive(tp, m2, m1, v2); + } + break; + case 7: // 111: split edge0/edge1/edge2 + { + tessellateTriangleRecursive(tp, v0, m0, m2); + tessellateTriangleRecursive(tp, m0, v1, m1); + tessellateTriangleRecursive(tp, m2, m1, v2); + tessellateTriangleRecursive(tp, m0, m1, m2); + } + break; + }; +} + +static void tessellateTriangle(PxU32& nbNewTris, const TrianglePadded& tr, PxU32 index, TriArray& worldTriangles, IntArray& triIndicesArray, const PxBounds3& cullingBox, const CCTParams& params, PxU16& nbTessellation) +{ + TessParams tp; + tp.nbNewTris = 0; + tp.index = index; + tp.worldTriangles = &worldTriangles; + tp.triIndicesArray = &triIndicesArray; + tp.cullingBoxCenter = cullingBox.getCenter(); + tp.cullingBoxExtents = cullingBox.getExtents(); + tp.maxEdgeLength2 = params.mMaxEdgeLength2; + tp.nbTessellation = 0; + tessellateTriangleRecursive(&tp, tr.verts[0], tr.verts[1], tr.verts[2]); + + nbNewTris += tp.nbNewTris; + nbTessellation += tp.nbTessellation; +// nbTessellation += PxU16(tp.nbNewTris); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputPlaneToStream(PxShape* planeShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, Cm::RenderBuffer* renderBuffer) +{ + PX_ASSERT(planeShape->getGeometryType() == PxGeometryType::ePLANE); + + const PxF32 length = (tmpBounds.maximum - tmpBounds.minimum).magnitude(); + PxVec3 center = toVec3(origin); + + const PxPlane plane = PxPlaneEquationFromTransform(globalPose); + + PxVec3 right, up; + Ps::computeBasis(plane.n, right, up); + right *= length; + up *= length; + + const PxVec3 p = plane.project(center); + const PxVec3 p0 = p - right + up; + const PxVec3 p1 = p - right - up; + const PxVec3 p2 = p + right - up; + const PxVec3 p3 = p + right + up; + + const PxU32 nbTouchedTris = 2; + + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + + TouchedMesh* touchedMesh = reinterpret_cast<TouchedMesh*>(reserve(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = planeShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mNbTris = nbTouchedTris; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(nbTouchedTris); + + triIndicesArray.pushBack(0); + triIndicesArray.pushBack(1); + + TouchedTriangles[0].verts[0] = p0 + offset; + TouchedTriangles[0].verts[1] = p1 + offset; + TouchedTriangles[0].verts[2] = p2 + offset; + + TouchedTriangles[1].verts[0] = p0 + offset; + TouchedTriangles[1].verts[1] = p2 + offset; + TouchedTriangles[1].verts[2] = p3 + offset; + + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, &worldTriangles.getTriangle(0), renderBuffer, offset, params.mUpDirection); +} + +static void outputSphereToStream(PxShape* sphereShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, const PxExtendedVec3& origin) +{ + PX_ASSERT(sphereShape->getGeometryType() == PxGeometryType::eSPHERE); + PxExtendedSphere WorldSphere; + { + PxSphereGeometry sg; + sphereShape->getSphereGeometry(sg); + + WorldSphere.radius = sg.radius; + WorldSphere.center.x = PxExtended(globalPose.p.x); + WorldSphere.center.y = PxExtended(globalPose.p.y); + WorldSphere.center.z = PxExtended(globalPose.p.z); + } + + TouchedSphere* PX_RESTRICT touchedSphere = reinterpret_cast<TouchedSphere*>(reserve(geomStream, sizeof(TouchedSphere)/sizeof(PxU32))); + touchedSphere->mType = TouchedGeomType::eSPHERE; + touchedSphere->mTGUserData = sphereShape; + touchedSphere->mActor = actor; + touchedSphere->mOffset = origin; + touchedSphere->mRadius = WorldSphere.radius; + touchedSphere->mCenter.x = float(WorldSphere.center.x - origin.x); + touchedSphere->mCenter.y = float(WorldSphere.center.y - origin.y); + touchedSphere->mCenter.z = float(WorldSphere.center.z - origin.z); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputCapsuleToStream(PxShape* capsuleShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, const PxExtendedVec3& origin) +{ + PX_ASSERT(capsuleShape->getGeometryType() == PxGeometryType::eCAPSULE); + PxExtendedCapsule WorldCapsule; + { + PxCapsuleGeometry cg; + capsuleShape->getCapsuleGeometry(cg); + + PxVec3 p0 = cg.halfHeight * globalPose.q.getBasisVector0(); + PxVec3 p1 = -p0; + p0 += globalPose.p; + p1 += globalPose.p; + + WorldCapsule.radius = cg.radius; + WorldCapsule.p0.x = PxExtended(p0.x); + WorldCapsule.p0.y = PxExtended(p0.y); + WorldCapsule.p0.z = PxExtended(p0.z); + WorldCapsule.p1.x = PxExtended(p1.x); + WorldCapsule.p1.y = PxExtended(p1.y); + WorldCapsule.p1.z = PxExtended(p1.z); + } + + TouchedCapsule* PX_RESTRICT touchedCapsule = reinterpret_cast<TouchedCapsule*>(reserve(geomStream, sizeof(TouchedCapsule)/sizeof(PxU32))); + touchedCapsule->mType = TouchedGeomType::eCAPSULE; + touchedCapsule->mTGUserData = capsuleShape; + touchedCapsule->mActor = actor; + touchedCapsule->mOffset = origin; + touchedCapsule->mRadius = WorldCapsule.radius; + touchedCapsule->mP0.x = float(WorldCapsule.p0.x - origin.x); + touchedCapsule->mP0.y = float(WorldCapsule.p0.y - origin.y); + touchedCapsule->mP0.z = float(WorldCapsule.p0.z - origin.z); + touchedCapsule->mP1.x = float(WorldCapsule.p1.x - origin.x); + touchedCapsule->mP1.y = float(WorldCapsule.p1.y - origin.y); + touchedCapsule->mP1.z = float(WorldCapsule.p1.z - origin.z); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputBoxToStream( PxShape* boxShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, PxU16& nbTessellation) +{ + PX_ASSERT(boxShape->getGeometryType() == PxGeometryType::eBOX); + PxBoxGeometry bg; + boxShape->getBoxGeometry(bg); + + //8 verts in local space. + const PxF32 dx = bg.halfExtents.x; + const PxF32 dy = bg.halfExtents.y; + const PxF32 dz = bg.halfExtents.z; + PxVec3 boxVerts[8]= + { + PxVec3(-dx,-dy,-dz), + PxVec3(+dx,-dy,-dz), + PxVec3(+dx,+dy,-dz), + PxVec3(-dx,+dy,-dz), + PxVec3(-dx,-dy,+dz), + PxVec3(+dx,-dy,+dz), + PxVec3(+dx,+dy,+dz), + PxVec3(-dx,+dy,+dz) + }; + //Transform verts into world space. + const PxVec3 pxOrigin = toVec3(origin); + for(PxU32 i = 0; i < 8; i++) + { + boxVerts[i] = globalPose.transform(boxVerts[i]) - pxOrigin; + } + + //Index of triangles. + const PxU32 boxTris[12][3]= + { + {0,2,1}, + {2,0,3}, //0,1,2,3 + + {3,6,2}, + {6,3,7}, //3,2,6,7 + + {7,5,6}, + {5,7,4}, //7,6,5,4 + + {4,1,5}, + {1,4,0}, //4,5,1,0 + + {0,7,3}, + {7,0,4}, //0,3,7,4 + + {2,5,1}, + {5,2,6} //2,1,5,6 + }; + + TouchedMesh* touchedMesh = reinterpret_cast<TouchedMesh*>(reserve(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = boxShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + if(params.mTessellation) + { + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + const PxBounds3 cullingBox = PxBounds3::centerExtents(tmpBounds.getCenter() + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i<12; i++) + { + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + currentTriangle.verts[0] = boxVerts[boxTris[i][0]]; + currentTriangle.verts[1] = boxVerts[boxTris[i][1]]; + currentTriangle.verts[2] = boxVerts[boxTris[i][2]]; + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, PX_INVALID_U32, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + touchedMesh->mNbTris = 12; + + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(12); + + for(PxU32 i=0; i<12; i++) + { + PxTriangle& currentTriangle = TouchedTriangles[i]; + currentTriangle.verts[0] = boxVerts[boxTris[i][0]]; + currentTriangle.verts[1] = boxVerts[boxTris[i][1]]; + currentTriangle.verts[2] = boxVerts[boxTris[i][2]]; + + triIndicesArray.pushBack(PX_INVALID_U32); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PxU32 createInvisibleWalls(const CCTParams& params, const PxTriangle& currentTriangle, TriArray& worldTriangles, IntArray& triIndicesArray) +{ + const PxF32 wallHeight = params.mInvisibleWallHeight; + if(wallHeight==0.0f) + return 0; + + PxU32 nbNewTris = 0; // Number of newly created tris + + const PxVec3& upDirection = params.mUpDirection; + + PxVec3 normal; + currentTriangle.normal(normal); + if(testSlope(normal, upDirection, params.mSlopeLimit)) + { + const PxVec3 upWall = upDirection*wallHeight; + PxVec3 v0p = currentTriangle.verts[0] + upWall; + PxVec3 v1p = currentTriangle.verts[1] + upWall; + PxVec3 v2p = currentTriangle.verts[2] + upWall; + + // Extrude edge 0-1 + PxVec3 faceNormal01; + { + // 0-1-0p + const PxTriangle tri0_1_0p(currentTriangle.verts[0], currentTriangle.verts[1], v0p); + worldTriangles.pushBack(tri0_1_0p); + + // 0p-1-1p + const PxTriangle tri0p_1_1p(v0p, currentTriangle.verts[1], v1p); + worldTriangles.pushBack(tri0p_1_1p); + + tri0p_1_1p.normal(faceNormal01); + } + + // Extrude edge 1-2 + PxVec3 faceNormal12; + { + // 1p-1-2p + const PxTriangle tri1p_1_2p(v1p, currentTriangle.verts[1], v2p); + worldTriangles.pushBack(tri1p_1_2p); + + // 2p-1-2 + const PxTriangle tri2p_1_2(v2p, currentTriangle.verts[1], currentTriangle.verts[2]); + worldTriangles.pushBack(tri2p_1_2); + + tri2p_1_2.normal(faceNormal12); + } + + // Extrude edge 2-0 + PxVec3 faceNormal20; + { + // 0p-2-0 + const PxTriangle tri0p_2_0(v0p, currentTriangle.verts[2], currentTriangle.verts[0]); + worldTriangles.pushBack(tri0p_2_0); + + // 0p-2p-2 + const PxTriangle tri0p_2p_2(v0p, v2p, currentTriangle.verts[2]); + worldTriangles.pushBack(tri0p_2p_2); + + tri0p_2p_2.normal(faceNormal20); + } + + const PxU32 triIndex = PX_INVALID_U32; + for(PxU32 i=0;i<6;i++) + triIndicesArray.pushBack(triIndex); + + nbNewTris += 6; + } + return nbNewTris; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputMeshToStream( PxShape* meshShape, const PxRigidActor* actor, const PxTransform& meshPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, Cm::RenderBuffer* renderBuffer, PxU16& nbTessellation) +{ + PX_ASSERT(meshShape->getGeometryType() == PxGeometryType::eTRIANGLEMESH); + // Do AABB-mesh query + + PxTriangleMeshGeometry triGeom; + meshShape->getTriangleMeshGeometry(triGeom); + + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxTransform boxPose(tmpBounds.getCenter(), PxQuat(PxIdentity)); + + // Collide AABB against current mesh + PxMeshOverlapUtil overlapUtil; + const PxU32 nbTouchedTris = overlapUtil.findOverlap(boxGeom, boxPose, triGeom, meshPose); + + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + + TouchedMesh* touchedMesh = reinterpret_cast<TouchedMesh*>(reserve(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = meshShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mNbTris = nbTouchedTris; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + const PxU32* PX_RESTRICT indices = overlapUtil.getResults(); + + if(params.mSlopeLimit!=0.0f) + { + if(!params.mTessellation) + { + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + const PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { + worldTriangles.pushBack(currentTriangle); + + triIndicesArray.pushBack(triangleIndex); + nbCreatedTris++; + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { +/* worldTriangles.pushBack(currentTriangle); + triIndicesArray.pushBack(triangleIndex); + nbCreatedTris++;*/ + + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; +// printf("Tesselate: %d new tris\n", nbNewTris); + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + else + { + if(!params.mTessellation) + { + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(nbTouchedTris); + + // Loop through touched triangles + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + PxTriangle& currentTriangle = *TouchedTriangles++; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + triIndicesArray.pushBack(triangleIndex); + } + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); +// printf("Tesselate: %d new tris\n", nbNewTris); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, &worldTriangles.getTriangle(0), renderBuffer, offset, params.mUpDirection); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputHeightFieldToStream( PxShape* hfShape, const PxRigidActor* actor, const PxTransform& heightfieldPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, Cm::RenderBuffer* renderBuffer, PxU16& nbTessellation) +{ + PX_ASSERT(hfShape->getGeometryType() == PxGeometryType::eHEIGHTFIELD); + // Do AABB-mesh query + + PxHeightFieldGeometry hfGeom; + hfShape->getHeightFieldGeometry(hfGeom); + + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxTransform boxPose(tmpBounds.getCenter(), PxQuat(PxIdentity)); + + // Collide AABB against current heightfield + PxMeshOverlapUtil overlapUtil; + const PxU32 nbTouchedTris = overlapUtil.findOverlap(boxGeom, boxPose, hfGeom, heightfieldPose); + + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + + TouchedMesh* touchedMesh = reinterpret_cast<TouchedMesh*>(reserve(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; // ptchernev: seems to work + touchedMesh->mTGUserData = hfShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mNbTris = nbTouchedTris; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + const PxU32* PX_RESTRICT indices = overlapUtil.getResults(); + + if(params.mSlopeLimit!=0.0f) + { + if(!params.mTessellation) + { + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + const PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { + worldTriangles.pushBack(currentTriangle); + + triIndicesArray.pushBack(triangleIndex); + nbCreatedTris++; + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; +// printf("Tesselate: %d new tris\n", nbNewTris); + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + else + { + if(!params.mTessellation) + { + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(nbTouchedTris); + + // Loop through touched triangles + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + PxTriangle& currentTriangle = *TouchedTriangles++; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + triIndicesArray.pushBack(triangleIndex); + } + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); +// printf("Tesselate: %d new tris\n", nbNewTris); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, &worldTriangles.getTriangle(0), renderBuffer, offset, params.mUpDirection); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputConvexToStream(PxShape* convexShape, const PxRigidActor* actor, const PxTransform& absPose_, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, Cm::RenderBuffer* renderBuffer, PxU16& nbTessellation) +{ + PX_ASSERT(convexShape->getGeometryType() == PxGeometryType::eCONVEXMESH); + PxConvexMeshGeometry cg; + convexShape->getConvexMeshGeometry(cg); + PX_ASSERT(cg.convexMesh); + + // Do AABB-mesh query + + PxU32* TF; + + // Collide AABB against current mesh + // The overlap function doesn't exist for convexes so let's just dump all tris + PxConvexMesh& cm = *cg.convexMesh; + + // PT: convex triangles are not exposed anymore so we need to access convex polygons & triangulate them + + // PT: TODO: this is copied from "DrawObjects", move this to a shared place. Actually a helper directly in PxConvexMesh would be useful. + PxU32 Nb = 0; + { + const PxU32 nbPolys = cm.getNbPolygons(); + const PxU8* polygons = cm.getIndexBuffer(); + + for(PxU32 i=0;i<nbPolys;i++) + { + PxHullPolygon data; + cm.getPolygonData(i, data); + Nb += data.mNbVerts - 2; + } + + // PT: revisit this code. We don't use the polygon offset? + TF = reinterpret_cast<PxU32*>(PxAlloca(sizeof(PxU32)*Nb*3)); + PxU32* t = TF; + for(PxU32 i=0;i<nbPolys;i++) + { + PxHullPolygon data; + cm.getPolygonData(i, data); + + const PxU32 nbV = data.mNbVerts; + + const PxU32 nbTris = nbV - 2; + const PxU8 vref0 = *polygons; + for(PxU32 j=0;j<nbTris;j++) + { + const PxU32 vref1 = polygons[(j+1)%nbV]; + const PxU32 vref2 = polygons[(j+2)%nbV]; + *t++ = vref0; + *t++ = vref1; + *t++ = vref2; + } + polygons += nbV; + } + } + + // PT: you can't use PxTransform with a non-uniform scaling + const PxMat33 rot = PxMat33(absPose_.q) * cg.scale.toMat33(); + const PxMat44 absPose(rot, absPose_.p); + + const PxVec3 absPosTmp = absPose.getPosition(); + const PxExtendedVec3 absPos(PxExtended(absPosTmp.x), PxExtended(absPosTmp.y), PxExtended(absPosTmp.z)); + + const PxVec3 MeshOffset(absPos - origin); // LOSS OF ACCURACY + + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + + TouchedMesh* touchedMesh = reinterpret_cast<TouchedMesh*>(reserve(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = convexShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + const PxVec3* verts = cm.getVertices(); + // Loop through touched triangles + if(params.mTessellation) + { + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxBounds3 cullingBox = PxBounds3::centerExtents(tmpBounds.getCenter() + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + while(Nb--) + { + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + + const PxU32 vref0 = *TF++; + const PxU32 vref1 = *TF++; + const PxU32 vref2 = *TF++; + + currentTriangle.verts[0] = MeshOffset + absPose.rotate(verts[vref0]); + currentTriangle.verts[1] = MeshOffset + absPose.rotate(verts[vref1]); + currentTriangle.verts[2] = MeshOffset + absPose.rotate(verts[vref2]); + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, PX_INVALID_U32, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(Nb); + + touchedMesh->mNbTris = Nb; + while(Nb--) + { + // Compute triangle in world space, add to array + PxTriangle& currentTriangle = *TouchedTriangles++; + + const PxU32 vref0 = *TF++; + const PxU32 vref1 = *TF++; + const PxU32 vref2 = *TF++; + + currentTriangle.verts[0] = MeshOffset + absPose.rotate(verts[vref0]); + currentTriangle.verts[1] = MeshOffset + absPose.rotate(verts[vref1]); + currentTriangle.verts[2] = MeshOffset + absPose.rotate(verts[vref2]); + + triIndicesArray.pushBack(PX_INVALID_U32); + } + } + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, &worldTriangles.getTriangle(0), renderBuffer, offset, params.mUpDirection); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxU32 Cct::getSceneTimestamp(const InternalCBData_FindTouchedGeom* userData) +{ + PX_ASSERT(userData); + const PxInternalCBData_FindTouchedGeom* internalData = static_cast<const PxInternalCBData_FindTouchedGeom*>(userData); + PxScene* scene = internalData->scene; + return scene->getSceneQueryStaticTimestamp(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Cct::findTouchedGeometry( + const InternalCBData_FindTouchedGeom* userData, + const PxExtendedBounds3& worldBounds, // ### we should also accept other volumes + + TriArray& worldTriangles, + IntArray& triIndicesArray, + IntArray& geomStream, + + const CCTFilter& filter, + const CCTParams& params, + PxU16& nbTessellation) +{ + PX_ASSERT(userData); + + const PxInternalCBData_FindTouchedGeom* internalData = static_cast<const PxInternalCBData_FindTouchedGeom*>(userData); + PxScene* scene = internalData->scene; + Cm::RenderBuffer* renderBuffer = internalData->renderBuffer; + + PxExtendedVec3 Origin; // Will be TouchedGeom::mOffset + getCenter(worldBounds, Origin); + + // Find touched *boxes* i.e. touched objects' AABBs in the world + // We collide against dynamic shapes too, to get back dynamic boxes/etc + // TODO: add active groups in interface! + PxQueryFlags sqFilterFlags; + if(filter.mStaticShapes) sqFilterFlags |= PxQueryFlag::eSTATIC; + if(filter.mDynamicShapes) sqFilterFlags |= PxQueryFlag::eDYNAMIC; + if(filter.mFilterCallback) + { + if(filter.mPreFilter) + sqFilterFlags |= PxQueryFlag::ePREFILTER; + if(filter.mPostFilter) + sqFilterFlags |= PxQueryFlag::ePOSTFILTER; + } + + // ### this one is dangerous + const PxBounds3 tmpBounds(toVec3(worldBounds.minimum), toVec3(worldBounds.maximum)); // LOSS OF ACCURACY + + // PT: unfortunate conversion forced by the PxGeometry API + PxVec3 center = tmpBounds.getCenter(), extents = tmpBounds.getExtents(); + + const PxU32 size = 100; + PxOverlapHit hits[size]; + + PxQueryFilterData sceneQueryFilterData = filter.mFilterData ? PxQueryFilterData(*filter.mFilterData, sqFilterFlags) : PxQueryFilterData(sqFilterFlags); + + PxOverlapBuffer hitBuffer(hits, size); + sceneQueryFilterData.flags |= PxQueryFlag::eNO_BLOCK; // fix for DE8255 + scene->overlap(PxBoxGeometry(extents), PxTransform(center), hitBuffer, sceneQueryFilterData, filter.mFilterCallback); + PxU32 numberHits = hitBuffer.getNbAnyHits(); + for(PxU32 i = 0; i < numberHits; i++) + { + const PxOverlapHit& hit = hitBuffer.getAnyHit(i); + PxShape* shape = hit.shape; + PxRigidActor* actor = hit.actor; + if(!shape || !actor) + continue; + + // Filtering + + // Discard all CCT shapes, i.e. kinematic actors we created ourselves. We don't need to collide with them since they're surrounded + // by the real CCT volume - and collisions with those are handled elsewhere. + if(internalData->cctShapeHashSet->contains(shape)) + continue; + + // Ubi (EA) : Discarding Triggers : + if(shape->getFlags() & PxShapeFlag::eTRIGGER_SHAPE) + continue; + + // PT: here you might want to disable kinematic objects. + + // Output shape to stream + const PxTransform globalPose = getShapeGlobalPose(*shape, *actor); + + const PxGeometryType::Enum type = shape->getGeometryType(); // ### VIRTUAL! + if(type==PxGeometryType::eSPHERE) outputSphereToStream (shape, actor, globalPose, geomStream, Origin); + else if(type==PxGeometryType::eCAPSULE) outputCapsuleToStream (shape, actor, globalPose, geomStream, Origin); + else if(type==PxGeometryType::eBOX) outputBoxToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, nbTessellation); + else if(type==PxGeometryType::eTRIANGLEMESH) outputMeshToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); + else if(type==PxGeometryType::eHEIGHTFIELD) outputHeightFieldToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); + else if(type==PxGeometryType::eCONVEXMESH) outputConvexToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); + else if(type==PxGeometryType::ePLANE) outputPlaneToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "CctCharacterControllerManager.h" +#include "CctObstacleContext.h" +#include "PxControllerBehavior.h" + +// #### hmmm, in the down case, isn't reported length too big ? It contains our artificial up component, +// that might confuse the user + +static void fillCCTHit(PxControllerHit& hit, const SweptContact& contact, const PxVec3& dir, float length, Controller* controller) +{ + hit.controller = controller->getPxController(); + hit.worldPos = contact.mWorldPos; + hit.worldNormal = contact.mWorldNormal; + hit.dir = dir; + hit.length = length; +} + +static const PxU32 defaultBehaviorFlags = 0; + +PxU32 Cct::shapeHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, float length) +{ + Controller* controller = static_cast<const PxInternalCBData_OnHit*>(userData)->controller; + + PxControllerShapeHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + hit.shape = const_cast<PxShape*>(reinterpret_cast<const PxShape*>(contact.mGeom->mTGUserData)); + hit.actor = const_cast<PxRigidActor*>(contact.mGeom->mActor); + hit.triangleIndex = contact.mTriangleIndex; + + if(controller->mReportCallback) + controller->mReportCallback->onShapeHit(hit); + + PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; + return behaviorCB ? behaviorCB->getBehaviorFlags(*hit.shape, *hit.actor) : defaultBehaviorFlags; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxU32 handleObstacleHit(const PxObstacle& touchedObstacle, const ObstacleHandle& obstacleHandle, PxControllerObstacleHit& hit, const PxInternalCBData_OnHit* internalData, Controller* controller) +{ + hit.userData = touchedObstacle.mUserData; + const_cast<PxInternalCBData_OnHit*>(internalData)->touchedObstacle = &touchedObstacle; // (*) PT: TODO: revisit + const_cast<PxInternalCBData_OnHit*>(internalData)->touchedObstacleHandle = obstacleHandle; + + if(controller->mReportCallback) + controller->mReportCallback->onObstacleHit(hit); + + PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; + return behaviorCB ? behaviorCB->getBehaviorFlags(touchedObstacle) : defaultBehaviorFlags; +} + +PxU32 Cct::userHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, float length) +{ + const PxInternalCBData_OnHit* internalData = static_cast<const PxInternalCBData_OnHit*>(userData); + Controller* controller = internalData->controller; + + const PxU32 objectCode = PxU32(size_t(contact.mGeom->mTGUserData)); + const UserObjectType type = decodeType(objectCode); + const PxU32 index = decodeIndex(objectCode); + + if(type==USER_OBJECT_CCT) + { + PX_ASSERT(index<controller->getCctManager()->getNbControllers()); + Controller** controllers = controller->getCctManager()->getControllers(); + Controller* other = controllers[index]; + + PxControllersHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + hit.other = other->getPxController(); + + if(controller->mReportCallback) + controller->mReportCallback->onControllerHit(hit); + + PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; + return behaviorCB ? behaviorCB->getBehaviorFlags(*hit.other) : defaultBehaviorFlags; + } + else if(type==USER_OBJECT_BOX_OBSTACLE) + { + PX_ASSERT(internalData->obstacles); + PX_ASSERT(index<internalData->obstacles->mBoxObstacles.size()); + + PxControllerObstacleHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + const ObstacleContext::InternalBoxObstacle& obstacle = internalData->obstacles->mBoxObstacles[index]; + const PxBoxObstacle& touchedObstacle = obstacle.mData; + return handleObstacleHit(touchedObstacle, obstacle.mHandle , hit, internalData, controller); + } + else if(type==USER_OBJECT_CAPSULE_OBSTACLE) + { + PX_ASSERT(internalData->obstacles); + PX_ASSERT(index<internalData->obstacles->mCapsuleObstacles.size()); + + PxControllerObstacleHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + const ObstacleContext::InternalCapsuleObstacle& obstacle = internalData->obstacles->mCapsuleObstacles[index]; + const PxCapsuleObstacle& touchedObstacle = obstacle.mData; + return handleObstacleHit(touchedObstacle, obstacle.mHandle, hit, internalData, controller); + } + else PX_ASSERT(0); + + return defaultBehaviorFlags; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerManager.cpp b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerManager.cpp new file mode 100644 index 00000000..6c435dc7 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerManager.cpp @@ -0,0 +1,661 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "CmBoxPruning.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentBox.h" +#include "PsUtilities.h" +#include "PsMathUtils.h" +#include "PxRigidDynamic.h" +#include "PxScene.h" +#include "PxPhysics.h" +#include "PsFoundation.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() +{ + if(mRenderBuffer) + { + delete mRenderBuffer; + mRenderBuffer = 0; + } +} + +void CharacterControllerManager::release() +{ + // 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); + + delete this; + + Ps::Foundation::decRefCount(); +} + +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) + { + if(mRenderBuffer) + { + delete mRenderBuffer; + mRenderBuffer = 0; + } + } +} + +PxU32 CharacterControllerManager::getNbControllers() const +{ + return mControllers.size(); +} + +Controller** CharacterControllerManager::getControllers() +{ + return mControllers.begin(); +} + +PxController* CharacterControllerManager::getController(PxU32 index) +{ + if(index>=mControllers.size()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "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()) + 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); + + if(!(observed->getConcreteType()==PxConcreteType:: eRIGID_DYNAMIC || observed->getConcreteType()==PxConcreteType:: eRIGID_STATIC || + observed->getConcreteType()==PxConcreteType::eSHAPE)) + return; + + // check if object was registered + if(mLockingEnabled) + mWriteLock.lock(); + + const ObservedRefCountMap::Entry* releaseEntry = mObservedRefCountMap.find(observed); + if(mLockingEnabled) + mWriteLock.unlock(); + + if(releaseEntry) + { + for (PxU32 i = 0; i < mControllers.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()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "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); + + PX_DELETE(&oc); +} + +void CharacterControllerManager::onObstacleRemoved(ObstacleHandle index) const +{ + for(PxU32 i = 0; i<mControllers.size(); i++) + { + mControllers[i]->mCctModule.onObstacleRemoved(index); + } +} + +void CharacterControllerManager::onObstacleUpdated(ObstacleHandle 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(ObstacleHandle 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()); + } +} + + +// PT: TODO: move to array class? +template <class T> +void resetOrClear(T& a) +{ + const PxU32 c = a.capacity(); + if(!c) + return; + const PxU32 s = a.size(); + if(s>c/2) + a.clear(); + else + a.reset(); +} + +void CharacterControllerManager::resetObstaclesBuffers() +{ + resetOrClear(mBoxUserData); + resetOrClear(mBoxes); + resetOrClear(mCapsuleUserData); + resetOrClear(mCapsules); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +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; + Ps::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) + Ps::swap(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; + } +} + +void CharacterControllerManager::computeInteractions(PxF32 elapsedTime, PxControllerFilterCallback* cctFilterCb) +{ + PxU32 nbControllers = mControllers.size(); + Controller** controllers = mControllers.begin(); + + PxBounds3* boxes = reinterpret_cast<PxBounds3*>(PX_ALLOC_TEMP(sizeof(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); + + Ps::Array<PxU32> pairs; // PT: TODO: get rid of alloc + Cm::CompleteBoxPruning(boxes, nbEntities, pairs, Gu::Axes(physx::Gu::AXES_XZY)); // PT: TODO: revisit for variable up axis + + 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); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//Public factory methods + +PX_C_EXPORT PX_PHYSX_CHARACTER_API PxControllerManager* PX_CALL_CONV PxCreateControllerManager(PxScene& scene, bool lockingEnabled) +{ + Ps::Foundation::incRefCount(); + return PX_NEW(CharacterControllerManager)(scene, lockingEnabled); +} diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerManager.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerManager.h new file mode 100644 index 00000000..ecc36636 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerManager.h @@ -0,0 +1,147 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PxControllerManager.h" +#include "PxControllerObstacles.h" +#include "PxMeshQuery.h" +#include "PxDeletionListener.h" +#include "CmRenderOutput.h" +#include "CctUtils.h" +#include "PsHashSet.h" +#include "PsHashMap.h" +#include "PsMutex.h" + +namespace physx +{ +namespace Cct +{ + class Controller; + class ObstacleContext; + + struct ObservedRefCounter + { + ObservedRefCounter(): refCount(0) + { + } + + PxU32 refCount; + }; + + typedef Ps::HashMap<const PxBase*, ObservedRefCounter> ObservedRefCountMap; + + //Implements the PxControllerManager interface, this class used to be called ControllerManager + class CharacterControllerManager : public PxControllerManager , public Ps::UserAllocated, public PxDeletionListener + { + public: + CharacterControllerManager(PxScene& scene, bool lockingEnabled = false); + virtual ~CharacterControllerManager(); + + // PxControllerManager + virtual void release(); + virtual PxScene& getScene() const; + virtual PxU32 getNbControllers() const; + virtual PxController* getController(PxU32 index); + virtual PxController* createController(const PxControllerDesc& desc); + + virtual void purgeControllers(); + virtual PxRenderBuffer& getRenderBuffer(); + virtual void setDebugRenderingFlags(PxControllerDebugRenderFlags flags); + virtual PxU32 getNbObstacleContexts() const; + virtual PxObstacleContext* getObstacleContext(PxU32 index); + virtual PxObstacleContext* createObstacleContext(); + virtual void computeInteractions(PxF32 elapsedTime, PxControllerFilterCallback* cctFilterCb); + virtual void setTessellation(bool flag, float maxEdgeLength); + virtual void setOverlapRecoveryModule(bool flag); + virtual void setPreciseSweeps(bool flag); + virtual void setPreventVerticalSlidingAgainstCeiling(bool flag); + virtual void shiftOrigin(const PxVec3& shift); + //~PxControllerManager + + // PxDeletionListener + virtual void onRelease(const PxBase* observed, void* userData, PxDeletionEventFlag::Enum deletionEvent); + //~PxDeletionListener + void registerObservedObject(const PxBase* obj); + void unregisterObservedObject(const PxBase* obj); + + // ObstacleContextNotifications + void onObstacleRemoved(ObstacleHandle index) const; + void onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* ) const; + void onObstacleAdded(ObstacleHandle index, const PxObstacleContext*) const; + + void releaseController(PxController& controller); + Controller** getControllers(); + void releaseObstacleContext(ObstacleContext& oc); + void resetObstaclesBuffers(); + + PxScene& mScene; + + Cm::RenderBuffer* mRenderBuffer; + PxControllerDebugRenderFlags mDebugRenderingFlags; + // Shared buffers for obstacles + Ps::Array<const void*> mBoxUserData; + Ps::Array<PxExtendedBox> mBoxes; + + Ps::Array<const void*> mCapsuleUserData; + Ps::Array<PxExtendedCapsule> mCapsules; + + Ps::Array<Controller*> mControllers; + Ps::HashSet<PxShape*> mCCTShapes; + + Ps::Array<ObstacleContext*> mObstacleContexts; + + float mMaxEdgeLength; + bool mTessellation; + + bool mOverlapRecovery; + bool mPreciseSweeps; + bool mPreventVerticalSlidingAgainstCeiling; + + bool mLockingEnabled; + + protected: + CharacterControllerManager &operator=(const CharacterControllerManager &); + CharacterControllerManager(const CharacterControllerManager& ); + + private: + ObservedRefCountMap mObservedRefCountMap; + mutable Ps::Mutex mWriteLock; // Lock used for guarding pointers in observedrefcountmap + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif //CCT_CHARACTER_CONTROLLER_MANAGER diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctController.cpp b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctController.cpp new file mode 100644 index 00000000..17186b79 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctController.cpp @@ -0,0 +1,235 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PxController.h" +#include "CctController.h" +#include "CctBoxController.h" +#include "CctCharacterControllerManager.h" +#include "PxScene.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" +#include "extensions/PxRigidBodyExt.h" +#include "foundation/PxMathUtils.h" +#include "PsUtilities.h" +#include "PxPhysics.h" +#include "PsFoundation.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) +{ + 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) +{ + // 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); + 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; +} diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctController.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctController.h new file mode 100644 index 00000000..beab7ee4 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctController.h @@ -0,0 +1,129 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsUserAllocated.h" +#include "PsMutex.h" + +namespace physx +{ + +class PxPhysics; +class PxScene; +class PxRigidDynamic; +class PxGeometry; +class PxMaterial; + +namespace Cct +{ + class CharacterControllerManager; + + class Controller : public Ps::UserAllocated + { + 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); + } + CharacterControllerManager* getCctManager() { return mManager; } + + + 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 Ps::Mutex 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); + 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 diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctInternalStructs.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctInternalStructs.h new file mode 100644 index 00000000..a93979b9 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctInternalStructs.h @@ -0,0 +1,86 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 PX_CHARACTER_INTERNAL_STRUCTS_H +#define PX_CHARACTER_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; + ObstacleHandle touchedObstacleHandle; + }; + + struct PxInternalCBData_FindTouchedGeom : InternalCBData_FindTouchedGeom + { + PxScene* scene; + Cm::RenderBuffer* renderBuffer; // Render buffer from controller manager, not the one from the scene + + Ps::HashSet<PxShape*>* cctShapeHashSet; + }; +} +} + +#endif diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctObstacleContext.cpp b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctObstacleContext.cpp new file mode 100644 index 00000000..b6b4b735 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctObstacleContext.cpp @@ -0,0 +1,538 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsUtilities.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 = reinterpret_cast<void**>(PX_ALLOC(sizeof(void*)*mMaxNbObjects, "HandleManager")); + mOutToIn = reinterpret_cast<PxU16*>(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); + mInToOut = reinterpret_cast<PxU16*>(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); + mStamps = reinterpret_cast<PxU16*>(PX_ALLOC(sizeof(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_AND_RESET(mStamps); + PX_FREE_AND_RESET(mInToOut); + PX_FREE_AND_RESET(mOutToIn); + PX_FREE_AND_RESET(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] = Ps::to16(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 = reinterpret_cast<void**>(PX_ALLOC(sizeof(void*)*mMaxNbObjects, "HandleManager")); // New physical list + PxU16* NewOTI = reinterpret_cast<PxU16*>(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New remapping table + PxU16* NewITO = reinterpret_cast<PxU16*>(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New remapping table + PxU16* NewStamps = reinterpret_cast<PxU16*>(PX_ALLOC(sizeof(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] = Ps::to16(mCurrentNbObjects); // Update virtual-to-physical remapping table. + mInToOut[mCurrentNbObjects] = Ps::to16(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(reinterpret_cast<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; +} + +ObstacleHandle ObstacleContext::addObstacle(const PxObstacle& obstacle) +{ + const PxGeometryType::Enum type = obstacle.getType(); + if(type==PxGeometryType::eBOX) + { + const PxU32 index = mBoxObstacles.size(); +#ifdef NEW_ENCODING + const ObstacleHandle handle = mHandleManager.Add(encodeInternalHandle(index, type)); +#else + const ObstacleHandle 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 ObstacleHandle handle = mHandleManager.Add(encodeInternalHandle(index, type)); +#else + const ObstacleHandle handle = encodeHandle(index, type); +#endif + mCapsuleObstacles.pushBack(InternalCapsuleObstacle(handle, static_cast<const PxCapsuleObstacle&>(obstacle))); + mCCTManager.onObstacleAdded(handle, this); + return handle; + } + else return INVALID_OBSTACLE_HANDLE; +} + +#ifdef NEW_ENCODING +template<class T> +static PX_FORCE_INLINE void remove(HandleManager& manager, void* object, ObstacleHandle handle, PxU32 index, PxU32 size, const Ps::Array<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(ObstacleHandle 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(ObstacleHandle 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(ObstacleHandle 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 "PxBoxGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PsMathUtils.h" +using namespace Gu; +const PxObstacle* ObstacleContext::raycastSingle(PxRaycastHit& hit, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, ObstacleHandle& obstacleHandle) const +{ + PxRaycastHit localHit; + PxF32 t = FLT_MAX; + const PxObstacle* touchedObstacle = NULL; + + const PxHitFlags hitFlags = PxHitFlag::eDISTANCE; + + { + 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); + 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); + if(status && localHit.distance<t) + { + t = localHit.distance; + hit = localHit; + obstacleHandle = mCapsuleObstacles[i].mHandle; + touchedObstacle = &userCapsuleObstacle; + } + } + } + return touchedObstacle; +} + + +const PxObstacle* ObstacleContext::raycastSingle(PxRaycastHit& hit, const ObstacleHandle& obstacleHandle, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance) const +{ + const PxHitFlags hitFlags = PxHitFlag::eDISTANCE; + +#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); + + 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); + if(status) + { + return &userCapsuleObstacle; + } + } + + return NULL; +} + +void ObstacleContext::onOriginShift(const PxVec3& shift) +{ + for(PxU32 i=0; i < mBoxObstacles.size(); i++) + mBoxObstacles[i].mData.mPos -= shift; + + for(PxU32 i=0; i < mCapsuleObstacles.size(); i++) + mCapsuleObstacles[i].mData.mPos -= shift; +} diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctObstacleContext.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctObstacleContext.h new file mode 100644 index 00000000..1028775d --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctObstacleContext.h @@ -0,0 +1,136 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsUserAllocated.h" +#include "PsArray.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + struct PxRaycastHit; + +namespace Cct +{ + class CharacterControllerManager; + + typedef PxU32 Handle; + class HandleManager : public Ps::UserAllocated + { + 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 Ps::UserAllocated + { + public: + ObstacleContext(CharacterControllerManager& ); + virtual ~ObstacleContext(); + + // PxObstacleContext + virtual void release(); + virtual PxControllerManager& getControllerManager() const; + virtual ObstacleHandle addObstacle(const PxObstacle& obstacle); + virtual bool removeObstacle(ObstacleHandle handle); + virtual bool updateObstacle(ObstacleHandle handle, const PxObstacle& obstacle); + virtual PxU32 getNbObstacles() const; + virtual const PxObstacle* getObstacle(PxU32 i) const; + virtual const PxObstacle* getObstacleByHandle(ObstacleHandle handle) const; + //~PxObstacleContext + + const PxObstacle* raycastSingle(PxRaycastHit& hit, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, ObstacleHandle& obstacleHandle) const; + const PxObstacle* raycastSingle(PxRaycastHit& hit, const ObstacleHandle& obstacleHandle, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance) const; // raycast just one obstacle handle + + void onOriginShift(const PxVec3& shift); + + struct InternalBoxObstacle + { + InternalBoxObstacle(ObstacleHandle handle, const PxBoxObstacle& data) : mHandle(handle), mData(data) {} + + ObstacleHandle mHandle; + PxBoxObstacle mData; + }; + Ps::Array<InternalBoxObstacle> mBoxObstacles; + + struct InternalCapsuleObstacle + { + InternalCapsuleObstacle(ObstacleHandle handle, const PxCapsuleObstacle& data) : mHandle(handle), mData(data) {} + + ObstacleHandle mHandle; + PxCapsuleObstacle mData; + }; + Ps::Array<InternalCapsuleObstacle> mCapsuleObstacles; + + private: + ObstacleContext& operator=(const ObstacleContext&); + HandleManager mHandleManager; + CharacterControllerManager& mCCTManager; + }; + + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptBox.cpp b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptBox.cpp new file mode 100644 index 00000000..b7ca96a3 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptBox.cpp @@ -0,0 +1,50 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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); +} diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptBox.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptBox.h new file mode 100644 index 00000000..e8adb340 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptBox.h @@ -0,0 +1,55 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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; + + PxVec3 mExtents; + }; + +} // namespace Cct + +} + +#endif diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptCapsule.cpp b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptCapsule.cpp new file mode 100644 index 00000000..ba21b417 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptCapsule.cpp @@ -0,0 +1,48 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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); +} diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptCapsule.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptCapsule.h new file mode 100644 index 00000000..47a153ba --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptCapsule.h @@ -0,0 +1,56 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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; + + PxF32 mRadius; + PxF32 mHeight; + }; + +} // namespace Cct + +} + +#endif diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptVolume.cpp b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptVolume.cpp new file mode 100644 index 00000000..1ca077ec --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptVolume.cpp @@ -0,0 +1,74 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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; + tmp += direction; + setCenterExtents(destBox, tmp, extents); + add(box, destBox); + } + + if(maxJumpHeight!=0.0f) + { + PxExtendedBounds3 destBox; + PxExtendedVec3 tmp = center; + tmp -= upDirection * maxJumpHeight; + setCenterExtents(destBox, tmp, extents); + add(box, destBox); + } + + _box = box; +} diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptVolume.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptVolume.h new file mode 100644 index 00000000..1df3fe40 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptVolume.h @@ -0,0 +1,76 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 + diff --git a/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctUtils.h b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctUtils.h new file mode 100644 index 00000000..960cd4aa --- /dev/null +++ b/PhysX_3.4/Source/PhysXCharacterKinematic/src/CctUtils.h @@ -0,0 +1,214 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PxExtended.h" +#include "PxShapeExt.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); +} + +#ifdef PX_BIG_WORLDS + + 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 = 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.set(minx, miny, minz); + maximum.set(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 = b.maximum - b.minimum; + extents *= 0.5f; + } + + PX_INLINE void setCenterExtents(PxExtendedBounds3& b, const PxExtendedVec3& c, const PxVec3& e) + { + b.minimum = c; b.minimum -= e; + b.maximum = c; b.maximum += e; + } + + 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.minimum(b2.minimum); + 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 PxExtended distance(const PxVec3& v2, const PxVec3& v) + { + const PxExtended dx = v2.x - v.x; + const PxExtended dy = v2.y - v.y; + const PxExtended dz = v2.z - v.z; + return PxSqrt(dx * dx + dy * dy + dz * dz); + } + + 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.minimum(b2.minimum); + b.maximum.maximum(b2.maximum); + } +#endif + +} + +#endif |