1 | // |
2 | // Redistribution and use in source and binary forms, with or without |
3 | // modification, are permitted provided that the following conditions |
4 | // are met: |
5 | // * Redistributions of source code must retain the above copyright |
6 | // notice, this list of conditions and the following disclaimer. |
7 | // * Redistributions in binary form must reproduce the above copyright |
8 | // notice, this list of conditions and the following disclaimer in the |
9 | // documentation and/or other materials provided with the distribution. |
10 | // * Neither the name of NVIDIA CORPORATION nor the names of its |
11 | // contributors may be used to endorse or promote products derived |
12 | // from this software without specific prior written permission. |
13 | // |
14 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY |
15 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
18 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
24 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | // |
26 | // Copyright (c) 2008-2021 NVIDIA Corporation. All rights reserved. |
27 | // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. |
28 | // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. |
29 | |
30 | |
31 | #ifndef PXDV_ARTICULATION_H |
32 | #define PXDV_ARTICULATION_H |
33 | |
34 | #include "foundation/PxVec3.h" |
35 | #include "foundation/PxQuat.h" |
36 | #include "foundation/PxTransform.h" |
37 | #include "PsVecMath.h" |
38 | #include "PsUtilities.h" |
39 | #include "CmUtils.h" |
40 | #include "CmSpatialVector.h" |
41 | #include "PxArticulationJoint.h" |
42 | #include "PxArticulation.h" |
43 | #include "foundation/PxMemory.h" |
44 | #include "DyArticulationCore.h" |
45 | #include "DyArticulationJointCore.h" |
46 | |
47 | namespace physx |
48 | { |
49 | struct PxsBodyCore; |
50 | class PxsConstraintBlockManager; |
51 | class PxsContactManagerOutputIterator; |
52 | struct PxSolverConstraintDesc; |
53 | struct PxSolverBodyData; |
54 | class PxContactJoint; |
55 | struct PxTGSSolverBodyData; |
56 | struct PxTGSSolverBodyTxInertia; |
57 | struct PxSolverConstraintDesc; |
58 | |
59 | namespace Dy |
60 | { |
61 | |
62 | struct SpatialSubspaceMatrix; |
63 | |
64 | struct ConstraintWriteback; |
65 | class ThreadContext; |
66 | |
67 | static const size_t DY_ARTICULATION_MAX_SIZE = 64; |
68 | class ArticulationJointCoreData; |
69 | struct Constraint; |
70 | class Context; |
71 | |
72 | struct ArticulationJointCore : public ArticulationJointCoreBase |
73 | { |
74 | //= ATTENTION! ===================================================================================== |
75 | // Changing the data layout of this class breaks the binary serialization format. See comments for |
76 | // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData |
77 | // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION |
78 | // accordingly. |
79 | //================================================================================================== |
80 | |
81 | // drive model |
82 | PxQuat targetPosition; |
83 | PxVec3 targetVelocity; |
84 | |
85 | PxReal spring;//old |
86 | PxReal damping;//old |
87 | |
88 | PxReal internalCompliance;//old |
89 | PxReal externalCompliance;//old |
90 | |
91 | // limit model |
92 | |
93 | PxReal swingLimitContactDistance;//old |
94 | |
95 | PxReal tangentialStiffness;//old |
96 | PxReal tangentialDamping;//old |
97 | |
98 | bool swingLimited;//old |
99 | bool twistLimited;//old |
100 | |
101 | PxU8 driveType; //both |
102 | |
103 | PxReal twistLimitContactDistance; //old |
104 | |
105 | PxReal tanQSwingY;//old |
106 | PxReal tanQSwingZ;//old |
107 | PxReal tanQSwingPad;//old |
108 | PxReal tanQTwistHigh;//old |
109 | PxReal tanQTwistLow;//old |
110 | PxReal tanQTwistPad;//old |
111 | |
112 | ArticulationJointCore() |
113 | { |
114 | //Cm::markSerializedMem(this, sizeof(ArticulationJointCore)); |
115 | parentPose = PxTransform(PxIdentity); |
116 | childPose = PxTransform(PxIdentity); |
117 | internalCompliance = 0.0f; |
118 | externalCompliance = 0.0f; |
119 | swingLimitContactDistance = 0.05f; |
120 | twistLimitContactDistance = 0.05f; |
121 | |
122 | driveType = PxArticulationJointDriveType::eTARGET; |
123 | |
124 | jointType = PxArticulationJointType::eFIX; |
125 | |
126 | for(PxU32 i=0; i<PxArticulationAxis::eCOUNT; i++) |
127 | motion[i] = PxArticulationMotion::eLOCKED; |
128 | |
129 | dirtyFlag = ArticulationJointCoreDirtyFlag::eMOTION; |
130 | |
131 | jointOffset = 0; |
132 | } |
133 | |
134 | ArticulationJointCore(const PxTransform& parentFrame, const PxTransform& childFrame) |
135 | { |
136 | parentPose = parentFrame; |
137 | childPose = childFrame; |
138 | |
139 | //the old articulation is eTARGET |
140 | driveType = PxArticulationJointDriveType::eTARGET; |
141 | |
142 | spring = 0.0f; |
143 | damping = 0.0f; |
144 | |
145 | internalCompliance = 1.0f; |
146 | externalCompliance = 1.0f; |
147 | |
148 | for(PxU32 i=0; i<PxArticulationAxis::eCOUNT; i++) |
149 | { |
150 | limits[i].low = 0.f; |
151 | limits[i].high = 0.f; |
152 | drives[i].stiffness = 0.f; |
153 | drives[i].damping = 0.f; |
154 | drives[i].maxForce = 0.f; |
155 | targetP[i] = 0.f; |
156 | targetV[i] = 0.f; |
157 | motion[i] = PxArticulationMotion::eLOCKED; |
158 | } |
159 | |
160 | const PxReal swingYLimit = PxPi / 4.0f; |
161 | const PxReal swingZLimit = PxPi / 4.0f; |
162 | |
163 | limits[PxArticulationAxis::eSWING1].low = swingYLimit; |
164 | limits[PxArticulationAxis::eSWING2].low = swingZLimit; |
165 | |
166 | swingLimitContactDistance = 0.05f; |
167 | swingLimited = false; |
168 | tangentialStiffness = 0.0f; |
169 | tangentialDamping = 0.0f; |
170 | |
171 | const PxReal twistLimitLow = -PxPi / 4.0f; |
172 | const PxReal twistLimitHigh = PxPi / 4.0f; |
173 | limits[PxArticulationAxis::eTWIST].low = twistLimitLow; |
174 | limits[PxArticulationAxis::eTWIST].high = twistLimitHigh; |
175 | twistLimitContactDistance = 0.05f; |
176 | twistLimited = false; |
177 | |
178 | tanQSwingY = PxTan(a: swingYLimit / 4.0f); |
179 | tanQSwingZ = PxTan(a: swingZLimit / 4.0f); |
180 | tanQSwingPad = PxTan(a: swingLimitContactDistance / 4.0f); |
181 | |
182 | tanQTwistHigh = PxTan(a: twistLimitHigh / 4.0f); |
183 | tanQTwistLow = PxTan(a: twistLimitLow / 4.0f); |
184 | tanQTwistPad = PxTan(a: twistLimitContactDistance / 4.0f); |
185 | |
186 | frictionCoefficient = 0.f; |
187 | |
188 | dirtyFlag = ArticulationJointCoreDirtyFlag::eMOTION; |
189 | |
190 | jointOffset = 0; |
191 | } |
192 | |
193 | void setJointPose(ArticulationJointCoreData& jointDatum, SpatialSubspaceMatrix& motionMatrix, bool forceUpdate, |
194 | PxQuat& relativeRot); |
195 | |
196 | // PX_SERIALIZATION |
197 | ArticulationJointCore(const PxEMPTY&) : ArticulationJointCoreBase(PxEmpty) {} |
198 | //~PX_SERIALIZATION |
199 | }; |
200 | |
201 | struct ArticulationLoopConstraint |
202 | { |
203 | public: |
204 | PxU32 linkIndex0; |
205 | PxU32 linkIndex1; |
206 | Dy::Constraint* constraint; |
207 | }; |
208 | |
209 | #define DY_ARTICULATION_LINK_NONE 0xffffffff |
210 | |
211 | typedef PxU64 ArticulationBitField; |
212 | |
213 | struct ArticulationLink |
214 | { |
215 | ArticulationBitField children; // child bitmap |
216 | ArticulationBitField pathToRoot; // path to root, including link and root |
217 | PxsBodyCore* bodyCore; |
218 | ArticulationJointCore* inboundJoint; |
219 | PxU32 parent; |
220 | }; |
221 | |
222 | typedef size_t ArticulationLinkHandle; |
223 | |
224 | class ArticulationV; |
225 | |
226 | struct ArticulationSolverDesc |
227 | { |
228 | void initData(const ArticulationCore* core_, const PxArticulationFlags* flags_) |
229 | { |
230 | articulation = NULL; |
231 | links = NULL; |
232 | motionVelocity = NULL; |
233 | acceleration = NULL; |
234 | poses = NULL; |
235 | deltaQ = NULL; |
236 | externalLoads = NULL; |
237 | internalLoads = NULL; |
238 | core = core_; |
239 | flags = flags_; |
240 | scratchMemory = NULL; |
241 | totalDataSize = 0; |
242 | solverDataSize = 0; |
243 | linkCount = 0; |
244 | numInternalConstraints = 0; |
245 | scratchMemorySize = 0; |
246 | } |
247 | |
248 | ArticulationV* articulation; |
249 | ArticulationLink* links; |
250 | Cm::SpatialVectorV* motionVelocity; |
251 | Cm::SpatialVector* acceleration; |
252 | PxTransform* poses; |
253 | PxQuat* deltaQ; |
254 | physx::shdfnd::aos::Mat33V* externalLoads; |
255 | physx::shdfnd::aos::Mat33V* internalLoads; |
256 | const ArticulationCore* core; |
257 | const PxArticulationFlags* flags; // PT: PX-1399 |
258 | char* scratchMemory; |
259 | PxU16 totalDataSize; |
260 | PxU16 solverDataSize; |
261 | PxU8 linkCount; |
262 | PxU8 numInternalConstraints; |
263 | PxU16 scratchMemorySize; |
264 | }; |
265 | |
266 | struct PxcFsScratchAllocator |
267 | { |
268 | char* base; |
269 | size_t size; |
270 | size_t taken; |
271 | PxcFsScratchAllocator(char* p, size_t s) : base(p), size(s), taken(0) {} |
272 | |
273 | template<typename T> |
274 | static size_t sizeof16() |
275 | { |
276 | return (sizeof(T) + 15)&~15; |
277 | } |
278 | |
279 | template<class T> T* alloc(PxU32 count) |
280 | { |
281 | size_t s = sizeof16<T>(); |
282 | PX_ASSERT(taken + s*count <= size); |
283 | T* result = reinterpret_cast<T*>(base + taken); |
284 | taken += s*count; |
285 | return result; |
286 | } |
287 | }; |
288 | |
289 | static const size_t DY_ARTICULATION_IDMASK = DY_ARTICULATION_MAX_SIZE - 1; |
290 | |
291 | #if PX_VC |
292 | #pragma warning(push) |
293 | #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. |
294 | #endif |
295 | PX_ALIGN_PREFIX(64) |
296 | class ArticulationV |
297 | { |
298 | PX_NOCOPY(ArticulationV) |
299 | public: |
300 | |
301 | enum Enum |
302 | { |
303 | eReducedCoordinate = 0, |
304 | eMaximumCoordinate = 1 |
305 | }; |
306 | |
307 | ArticulationV(void* userData, Enum type) : |
308 | mUserData (userData), |
309 | mContext (NULL), |
310 | mType (type), |
311 | mUpdateSolverData (true), |
312 | mDirty (false), |
313 | mMaxDepth (0) |
314 | {} |
315 | |
316 | virtual ~ArticulationV() {} |
317 | |
318 | virtual void onUpdateSolverDesc() {} |
319 | |
320 | virtual bool resize(const PxU32 linkCount); |
321 | |
322 | virtual void addBody() |
323 | { |
324 | mAcceleration.pushBack(a: Cm::SpatialVector(PxVec3(0.f), PxVec3(0.f))); |
325 | mUpdateSolverData = true; |
326 | } |
327 | |
328 | virtual void removeBody() |
329 | { |
330 | mUpdateSolverData = true; |
331 | } |
332 | |
333 | PX_FORCE_INLINE bool updateSolverData() { return mUpdateSolverData; } |
334 | |
335 | PX_FORCE_INLINE void setDirty(const bool dirty) { mDirty = dirty; } |
336 | PX_FORCE_INLINE bool getDirty() const { return mDirty; } |
337 | |
338 | PX_FORCE_INLINE PxU32 getMaxDepth() const { return mMaxDepth; } |
339 | PX_FORCE_INLINE void setMaxDepth(const PxU32 depth) { mMaxDepth = depth; } |
340 | |
341 | // solver methods |
342 | PX_FORCE_INLINE PxU32 getBodyCount() const { return mSolverDesc.linkCount; } |
343 | PX_FORCE_INLINE PxU32 getSolverDataSize() const { return mSolverDesc.solverDataSize; } |
344 | PX_FORCE_INLINE PxU32 getTotalDataSize() const { return mSolverDesc.totalDataSize; } |
345 | PX_FORCE_INLINE void getSolverDesc(ArticulationSolverDesc& d) const { d = mSolverDesc; } |
346 | PX_FORCE_INLINE ArticulationSolverDesc& getSolverDesc() { return mSolverDesc; } |
347 | |
348 | PX_FORCE_INLINE const ArticulationCore* getCore() const { return mSolverDesc.core; } |
349 | PX_FORCE_INLINE PxU16 getIterationCounts() const { return mSolverDesc.core->solverIterationCounts; } |
350 | |
351 | PX_FORCE_INLINE void* getUserData() const { return mUserData; } |
352 | |
353 | PX_FORCE_INLINE PxU32 getType() const { return mType; } |
354 | |
355 | PX_FORCE_INLINE void setDyContext(Dy::Context* context) { mContext = context; } |
356 | |
357 | // get data sizes for allocation at higher levels |
358 | virtual void getDataSizes(PxU32 linkCount, PxU32& solverDataSize, PxU32& totalSize, PxU32& scratchSize) = 0; |
359 | |
360 | virtual PxU32 getDofs() { return 0; } |
361 | |
362 | virtual PxU32 getDof(const PxU32 /*linkID*/) { return 0; } |
363 | |
364 | virtual bool applyCache(PxArticulationCache& /*cache*/, const PxArticulationCacheFlags /*flag*/) {return false;} |
365 | |
366 | virtual void copyInternalStateToCache(PxArticulationCache&/* cache*/, const PxArticulationCacheFlags /*flag*/) {} |
367 | |
368 | virtual void packJointData(const PxReal* /*maximum*/, PxReal* /*reduced*/) {} |
369 | |
370 | virtual void unpackJointData(const PxReal* /*reduced*/, PxReal* /*maximum*/) {} |
371 | |
372 | virtual void initializeCommonData() {} |
373 | |
374 | virtual void getGeneralizedGravityForce(const PxVec3& /*gravity*/, PxArticulationCache& /*cache*/) {} |
375 | |
376 | virtual void getCoriolisAndCentrifugalForce(PxArticulationCache& /*cache*/) {} |
377 | |
378 | virtual void getGeneralizedExternalForce(PxArticulationCache& /*cache*/) {} |
379 | |
380 | virtual void getJointAcceleration(const PxVec3& /*gravity*/, PxArticulationCache& /*cache*/){} |
381 | |
382 | virtual void getJointForce(PxArticulationCache& /*cache*/){} |
383 | |
384 | virtual void getCoefficientMatrix(const PxReal /*dt*/, const PxU32 /*linkID*/, const PxContactJoint* /*joints*/, const PxU32 /*nbContacts*/, PxArticulationCache& /*cache*/){} |
385 | |
386 | virtual void getDenseJacobian(PxArticulationCache& /*cache*/, PxU32&, PxU32&) {} |
387 | |
388 | virtual void getCoefficientMatrixWithLoopJoints(ArticulationLoopConstraint* /*lConstraints*/, const PxU32 /*nbJoints*/, PxArticulationCache& /*cache*/) {} |
389 | |
390 | virtual bool getLambda( ArticulationLoopConstraint* /*lConstraints*/, const PxU32 /*nbJoints*/, |
391 | PxArticulationCache& /*cache*/, PxArticulationCache& /*initialState*/, const PxReal* /*jointTorque*/, |
392 | const PxVec3& /*gravity*/, const PxU32 /*maxIter*/) { return false; } |
393 | |
394 | virtual void getGeneralizedMassMatrix(PxArticulationCache& /*cache*/){} |
395 | virtual void getGeneralizedMassMatrixCRB(PxArticulationCache& /*cache*/){} |
396 | |
397 | virtual void teleportRootLink(){} |
398 | |
399 | virtual void getImpulseResponse( PxU32 linkID, |
400 | Cm::SpatialVectorF* Z, |
401 | const Cm::SpatialVector& impulse, |
402 | Cm::SpatialVector& deltaV) const = 0; |
403 | |
404 | virtual void getImpulseResponse( |
405 | PxU32 linkID, |
406 | Cm::SpatialVectorV* Z, |
407 | const Cm::SpatialVectorV& impulse, |
408 | Cm::SpatialVectorV& deltaV) const = 0; |
409 | |
410 | virtual void getImpulseSelfResponse( |
411 | PxU32 linkID0, |
412 | PxU32 linkID1, |
413 | Cm::SpatialVectorF* Z, |
414 | const Cm::SpatialVector& impulse0, |
415 | const Cm::SpatialVector& impulse1, |
416 | Cm::SpatialVector& deltaV0, |
417 | Cm::SpatialVector& deltaV1) const = 0; |
418 | |
419 | virtual Cm::SpatialVectorV getLinkVelocity(const PxU32 linkID) const = 0; |
420 | |
421 | virtual Cm::SpatialVectorV getLinkMotionVector(const PxU32 linkID) const = 0; |
422 | |
423 | virtual PxReal getLinkMaxPenBias(const PxU32 linkID) const = 0; |
424 | |
425 | virtual void pxcFsApplyImpulse( PxU32 linkID, Ps::aos::Vec3V linear, |
426 | Ps::aos::Vec3V angular, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) = 0; |
427 | |
428 | virtual void pxcFsApplyImpulses( PxU32 linkID, const Ps::aos::Vec3V& linear, |
429 | const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, |
430 | const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) = 0; |
431 | |
432 | virtual void solveInternalConstraints(const PxReal dt, const PxReal invDt, Cm::SpatialVectorF* impulses, Cm::SpatialVectorF* DeltaV, |
433 | bool velIteration, bool isTGS, const PxReal elapsedTime) = 0; |
434 | |
435 | virtual void writebackInternalConstraints(bool isTGS) = 0; |
436 | |
437 | virtual void concludeInternalConstraints(bool isTGS) = 0; |
438 | |
439 | virtual void prepareStaticConstraints(const PxReal /*dt*/, const PxReal /*invDt*/, PxsContactManagerOutputIterator& /*outputs*/, |
440 | ThreadContext& /*threadContext*/, PxReal /*correlationDist*/, PxReal /*bounceThreshold*/, PxReal /*frictionOffsetThreshold*/, PxReal /*solverOffsetSlop*/, |
441 | PxReal /*ccdMaxSeparation*/, PxSolverBodyData* /*solverBodyData*/, PxsConstraintBlockManager& /*blockManager*/, |
442 | Dy::ConstraintWriteback* /*constraintWritebackPool*/) {} |
443 | |
444 | virtual void prepareStaticConstraintsTGS(const PxReal /*stepDt*/, const PxReal /*totalDt*/, const PxReal /*invStepDt*/, const PxReal /*invTotalDt*/, |
445 | PxsContactManagerOutputIterator& /*outputs*/, ThreadContext& /*threadContext*/, PxReal /*correlationDist*/, PxReal /*bounceThreshold*/, |
446 | PxReal /*frictionOffsetThreshold*/, PxTGSSolverBodyData* /*solverBodyData*/, |
447 | PxTGSSolverBodyTxInertia* /*txInertia*/, PxsConstraintBlockManager& /*blockManager*/, Dy::ConstraintWriteback* /*constraintWritebackPool*/, |
448 | const PxU32 /*nbSubsteps*/, const PxReal /*lengthScale*/) {} |
449 | |
450 | virtual void pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1) = 0; |
451 | |
452 | virtual Cm::SpatialVectorV pxcFsGetVelocity(PxU32 linkID) = 0; |
453 | |
454 | virtual Cm::SpatialVectorV pxcFsGetVelocityTGS(PxU32 linkID) = 0; |
455 | |
456 | virtual const PxTransform& getCurrentTransform(PxU32 linkID) const= 0; |
457 | |
458 | virtual const PxQuat& getDeltaQ(PxU32 linkID) const = 0; |
459 | |
460 | virtual bool storeStaticConstraint(const PxSolverConstraintDesc& /*desc*/) { return false; } |
461 | |
462 | virtual bool willStoreStaticConstraint() { return false; } |
463 | |
464 | //this is called by island gen to determine whether the articulation should be awake or sleep |
465 | virtual Cm::SpatialVector getMotionVelocity(const PxU32 linkID) const = 0; |
466 | |
467 | virtual Cm::SpatialVector getMotionAcceleration(const PxU32 linkID) const = 0; |
468 | |
469 | void setupLinks(PxU32 nbLinks, Dy::ArticulationLink* links) |
470 | { |
471 | //if this is needed, we need to re-allocated the link data |
472 | resize(linkCount: nbLinks); |
473 | |
474 | getSolverDesc().links = links; |
475 | getSolverDesc().linkCount = Ps::to8(value: nbLinks); |
476 | |
477 | //if this is needed, we need to re-allocated the joint data |
478 | onUpdateSolverDesc(); |
479 | } |
480 | virtual void fillIndexedManager(const PxU32 linkId, Dy::ArticulationLinkHandle& handle, PxU8& indexType) = 0; |
481 | |
482 | //These variables are used in the constraint partition |
483 | PxU16 maxSolverFrictionProgress; |
484 | PxU16 maxSolverNormalProgress; |
485 | PxU32 solverProgress; |
486 | PxU8 numTotalConstraints; |
487 | protected: |
488 | void* mUserData; |
489 | Dy::Context* mContext; |
490 | PxU32 mType; |
491 | ArticulationSolverDesc mSolverDesc; |
492 | |
493 | Ps::Array<Cm::SpatialVector> mAcceleration; // supplied by Sc-layer to feed into articulations |
494 | |
495 | bool mUpdateSolverData; |
496 | bool mDirty; //any of links update configulations, the boolean will be set to true |
497 | PxU32 mMaxDepth; |
498 | |
499 | } PX_ALIGN_SUFFIX(64); |
500 | |
501 | #if PX_VC |
502 | #pragma warning(pop) |
503 | #endif |
504 | |
505 | PX_FORCE_INLINE ArticulationV* getArticulation(ArticulationLinkHandle handle) |
506 | { |
507 | return reinterpret_cast<ArticulationV*>(handle & ~DY_ARTICULATION_IDMASK); |
508 | } |
509 | |
510 | PX_FORCE_INLINE bool isArticulationRootLink(ArticulationLinkHandle handle) |
511 | { |
512 | return !(handle & DY_ARTICULATION_IDMASK); |
513 | } |
514 | |
515 | PX_FORCE_INLINE PxU32 getLinkIndex(ArticulationLinkHandle handle) |
516 | { |
517 | return PxU32(handle&DY_ARTICULATION_IDMASK); |
518 | } |
519 | } |
520 | |
521 | } |
522 | |
523 | #endif |
524 | |