Projekt_Grafika/dependencies/physx-4.1/source/physxcharacterkinematic/src/CctCharacterController.h

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