// // 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 TriArray; typedef Ps::Array IntArray; // PT: using private inheritance to control access, and make sure allocations are SIMD friendly class TriArray : private Ps::Array { public: PX_FORCE_INLINE PxTriangle* reserve(PxU32 nbTris) { // PT: customized version of "reserveContainerMemory" const PxU32 maxNbEntries = Ps::Array::capacity(); const PxU32 realRequiredSize = Ps::Array::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::reserve(newSize); } PxTriangle* buf = Ps::Array::end(); // ...but we still want the size to reflect the correct number Ps::Array::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::size(); } PX_FORCE_INLINE const PxTriangle* begin() const { return Ps::Array::begin(); } PX_FORCE_INLINE void clear() { Ps::Array::clear(); } PX_FORCE_INLINE void forceSize_Unsafe(PxU32 size) { Ps::Array::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 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 mTouchedShape; // Shape on which the CCT is standing TouchedObject 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* 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