aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/PhysXCharacterKinematic/src
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/PhysXCharacterKinematic/src
downloadphysx-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')
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctBoxController.cpp197
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctBoxController.h112
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCapsuleController.cpp192
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCapsuleController.h111
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterController.cpp2536
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterController.h459
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerCallbacks.cpp1100
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerManager.cpp661
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctCharacterControllerManager.h147
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctController.cpp235
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctController.h129
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctInternalStructs.h86
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctObstacleContext.cpp538
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctObstacleContext.h136
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptBox.cpp50
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptBox.h55
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptCapsule.cpp48
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptCapsule.h56
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptVolume.cpp74
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctSweptVolume.h76
-rw-r--r--PhysX_3.4/Source/PhysXCharacterKinematic/src/CctUtils.h214
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