483 lines
16 KiB
C
483 lines
16 KiB
C
![]() |
//
|
||
|
// Redistribution and use in source and binary forms, with or without
|
||
|
// modification, are permitted provided that the following conditions
|
||
|
// are met:
|
||
|
// * Redistributions of source code must retain the above copyright
|
||
|
// notice, this list of conditions and the following disclaimer.
|
||
|
// * Redistributions in binary form must reproduce the above copyright
|
||
|
// notice, this list of conditions and the following disclaimer in the
|
||
|
// documentation and/or other materials provided with the distribution.
|
||
|
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||
|
// contributors may be used to endorse or promote products derived
|
||
|
// from this software without specific prior written permission.
|
||
|
//
|
||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||
|
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||
|
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||
|
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||
|
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
//
|
||
|
// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved.
|
||
|
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
|
||
|
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
|
||
|
|
||
|
#ifndef CCT_CHARACTER_CONTROLLER
|
||
|
#define CCT_CHARACTER_CONTROLLER
|
||
|
|
||
|
//#define USE_CONTACT_NORMAL_FOR_SLOPE_TEST
|
||
|
|
||
|
#include "geometry/PxTriangle.h"
|
||
|
#include "characterkinematic/PxController.h"
|
||
|
#include "characterkinematic/PxControllerObstacles.h"
|
||
|
|
||
|
#include "CctCharacterControllerManager.h"
|
||
|
#include "CctUtils.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;
|
||
|
};
|
||
|
|
||
|
// 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)
|
||
|
{
|
||
|
// PT: customized version of "reserveContainerMemory"
|
||
|
|
||
|
const PxU32 maxNbEntries = Ps::Array<PxTriangle>::capacity();
|
||
|
const PxU32 realRequiredSize = Ps::Array<PxTriangle>::size() + nbTris;
|
||
|
// PT: allocate one more tri to make sure we can safely V4Load the last one...
|
||
|
const PxU32 requiredSize = realRequiredSize + 1;
|
||
|
|
||
|
if(requiredSize>maxNbEntries)
|
||
|
{
|
||
|
// PT: ok so the commented out growing policy was introduced by PX-837 but it produces
|
||
|
// large memory usage regressions (see PX-881) while not actually making things run
|
||
|
// faster. Our benchmarks show no performance difference, but up to +38% more memory
|
||
|
// used with this "standard" growing policy. So for now we just go back to the initial
|
||
|
// growing policy. It should be fine since PX-837 was not actually reported by a customer,
|
||
|
// it was just a concern that appeared while looking at the code. Ideally we'd use a pool
|
||
|
// with fixed-size slabs to get the best of both worlds but it would make iterating over
|
||
|
// triangles more complicated and would need more refactoring. So for now we don't bother,
|
||
|
// but we'll keep this note here for the next time this problem shows up.
|
||
|
// PT: new August 2018: turns out PX-837 was correct. Not doing this produces very large
|
||
|
// performance problems (like: the app freezes!) in SampleCCT. We didn't see it because
|
||
|
// it's an internal sample that it rarely used these days...
|
||
|
const PxU32 naturalGrowthSize = maxNbEntries ? maxNbEntries*2 : 2;
|
||
|
const PxU32 newSize = PxMax(requiredSize, naturalGrowthSize);
|
||
|
// const PxU32 newSize = requiredSize;
|
||
|
|
||
|
Ps::Array<PxTriangle>::reserve(newSize);
|
||
|
}
|
||
|
|
||
|
PxTriangle* buf = Ps::Array<PxTriangle>::end();
|
||
|
// ...but we still want the size to reflect the correct number
|
||
|
Ps::Array<PxTriangle>::forceSize_Unsafe(realRequiredSize);
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
PX_FORCE_INLINE void pushBack(const PxTriangle& tri)
|
||
|
{
|
||
|
PxTriangle* memory = reserve(1);
|
||
|
memory->verts[0] = tri.verts[0];
|
||
|
memory->verts[1] = tri.verts[1];
|
||
|
memory->verts[2] = tri.verts[2];
|
||
|
}
|
||
|
|
||
|
PX_FORCE_INLINE PxU32 size() const
|
||
|
{
|
||
|
return Ps::Array<PxTriangle>::size();
|
||
|
}
|
||
|
|
||
|
PX_FORCE_INLINE const PxTriangle* begin() const
|
||
|
{
|
||
|
return Ps::Array<PxTriangle>::begin();
|
||
|
}
|
||
|
|
||
|
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,
|
||
|
PxU64 contextID);
|
||
|
|
||
|
bool doSweepTest(const InternalCBData_FindTouchedGeom* userDataTouchedGeom,
|
||
|
InternalCBData_OnHit* userDataOnHit,
|
||
|
const UserObstacles& userObstacles,
|
||
|
SweptVolume& swept_volume,
|
||
|
const PxVec3& direction, const PxVec3& sideVector, PxU32 max_iter,
|
||
|
PxU32* nb_collisions, PxF32 min_dist, const PxControllerFilters& filters, SweepPass sweepPass,
|
||
|
const PxRigidActor*& touchedActor, const PxShape*& touchedShape, PxU64 contextID);
|
||
|
|
||
|
void findTouchedObstacles(const UserObstacles& userObstacles, const PxExtendedBounds3& world_box);
|
||
|
|
||
|
void voidTestCache();
|
||
|
void onRelease(const PxBase& observed);
|
||
|
void updateCachedShapesRegistration(PxU32 startIndex, bool unregister);
|
||
|
|
||
|
// observer notifications
|
||
|
void onObstacleRemoved(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
|