1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
|
//
// 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-2018 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;
};
// 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
|