diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/PhysX/src/buffering/ScbBody.h | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'PhysX_3.4/Source/PhysX/src/buffering/ScbBody.h')
| -rw-r--r-- | PhysX_3.4/Source/PhysX/src/buffering/ScbBody.h | 1068 |
1 files changed, 1068 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/PhysX/src/buffering/ScbBody.h b/PhysX_3.4/Source/PhysX/src/buffering/ScbBody.h new file mode 100644 index 00000000..00ce9f52 --- /dev/null +++ b/PhysX_3.4/Source/PhysX/src/buffering/ScbBody.h @@ -0,0 +1,1068 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 PX_PHYSICS_SCB_BODY +#define PX_PHYSICS_SCB_BODY + +#include "ScBodyCore.h" + +#include "ScbRigidObject.h" +#include "CmUtils.h" +#include "PsUtilities.h" +#include "PxRigidDynamic.h" +#include "ScbDefs.h" +#include "GuSIMDHelpers.h" + +namespace physx +{ +namespace Scb +{ +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +struct BodyBuffer : public RigidObjectBuffer //once RigidObject has its own buffered elements, derive from that instead +{ + template <PxU32 I, PxU32 Dummy> struct Fns {}; // TODO: make the base class traits visible + typedef Sc::BodyCore Core; + typedef BodyBuffer Buf; + + // regular attributes + SCB_REGULAR_ATTRIBUTE(0, PxReal, InverseMass) + SCB_REGULAR_ATTRIBUTE(1, PxVec3, InverseInertia) + SCB_REGULAR_ATTRIBUTE(2, PxReal, LinearDamping) + SCB_REGULAR_ATTRIBUTE(3, PxReal, AngularDamping) + SCB_REGULAR_ATTRIBUTE(4, PxReal, MaxAngVelSq) + SCB_REGULAR_ATTRIBUTE(5, PxReal, SleepThreshold) + SCB_REGULAR_ATTRIBUTE(6, PxReal, CCDAdvanceCoefficient) + SCB_REGULAR_ATTRIBUTE(7, PxReal, ContactReportThreshold) + SCB_REGULAR_ATTRIBUTE(8, PxU16, SolverIterationCounts) + SCB_REGULAR_ATTRIBUTE_ALIGNED(9,PxTransform, Body2Actor, 16) + SCB_REGULAR_ATTRIBUTE(10, PxReal, MaxPenetrationBias) + SCB_REGULAR_ATTRIBUTE(11, PxReal, FreezeThreshold) + SCB_REGULAR_ATTRIBUTE(12, PxReal, MaxContactImpulse) + SCB_REGULAR_ATTRIBUTE(13, PxRigidDynamicLockFlags, RigidDynamicLockFlags) + + // irregular attributes + + PX_ALIGN(16, PxTransform) mKinematicTarget; + PxVec3 mLinAcceleration; + PxVec3 mAngAcceleration; + PxVec3 mLinDeltaVelocity; + PxVec3 mAngDeltaVelocity; + + PxRigidBodyFlags mRigidBodyFlags; + PxRigidDynamicLockFlags mRigidDynamicFlags; + + enum + { + BF_RigidBodyFlags = 1<<14, + BF_KinematicTarget = 1<<15, + BF_AccelerationLinear = 1<<16, + BF_AccelerationAngular = 1<<17, + BF_Acceleration = BF_AccelerationLinear|BF_AccelerationAngular, + BF_DeltaVelocityLinear = 1<<18, + BF_DeltaVelocityAngular = 1<<19, + BF_DeltaVelocity = BF_DeltaVelocityLinear|BF_DeltaVelocityAngular, + BF_Body2World = 1<<20, + BF_Body2World_CoM = 1<<21, // the body pose was adjusted because of a center of mass change only + BF_LinearVelocity = 1<<22, + BF_AngularVelocity = 1<<23, + BF_WakeCounter = 1<<24, + BF_PutToSleep = 1<<25, + BF_WakeUp = 1<<26, + BF_ClearAccelerationLinear = 1<<27, + BF_ClearAccelerationAngular = 1<<28, + BF_ClearAcceleration = BF_ClearAccelerationLinear|BF_ClearAccelerationAngular, + BF_ClearDeltaVelocityLinear = 1<<29, + BF_ClearDeltaVelocityAngular= 1<<30, + BF_ClearDeltaVelocity = BF_ClearDeltaVelocityLinear|BF_ClearDeltaVelocityAngular + }; + + BodyBuffer(): mLinAcceleration(0), mAngAcceleration(0), mLinDeltaVelocity(0), mAngDeltaVelocity(0) {} +}; + + +#if PX_VC + #pragma warning(pop) +#endif + +class Body : public Scb::RigidObject +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef BodyBuffer Buf; + typedef Sc::BodyCore Core; + +public: +// PX_SERIALIZATION + Body(const PxEMPTY) : Scb::RigidObject(PxEmpty), mBodyCore(PxEmpty), mBufferedIsSleeping(1) { PX_ASSERT(mBodyBufferFlags == 0); } + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + PX_INLINE Body(PxActorType::Enum type, const PxTransform& bodyPose); + PX_INLINE ~Body() {} + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::BodyCore interface + //--------------------------------------------------------------------------------- + + + PX_FORCE_INLINE const PxTransform& getBody2World() const { return mBufferedBody2World; } // PT: important: keep returning an address here (else update prefetch in SceneQueryManager::addShapes) + PX_INLINE void setBody2World(const PxTransform& p, bool asPartOfBody2ActorChange); + + PX_FORCE_INLINE const PxVec3& getLinearVelocity() const { return mBufferedLinVelocity; } + PX_INLINE void setLinearVelocity(const PxVec3& v); + PX_FORCE_INLINE const PxVec3& getAngularVelocity() const { return mBufferedAngVelocity; } + PX_INLINE void setAngularVelocity(const PxVec3& v); + + PX_FORCE_INLINE void wakeUp(); + PX_FORCE_INLINE void putToSleep(); + PX_FORCE_INLINE PxReal getWakeCounter() const { return mBufferedWakeCounter; } + PX_INLINE void setWakeCounter(PxReal w); + PX_FORCE_INLINE bool isSleeping() const { return (mBufferedIsSleeping != 0); } + + PX_INLINE const PxTransform& getBody2Actor() const { return read<Buf::BF_Body2Actor>(); } + PX_INLINE void setBody2Actor(const PxTransform& m) { write<Buf::BF_Body2Actor>(m); } + + PX_INLINE PxReal getInverseMass() const { return read<Buf::BF_InverseMass>(); } + PX_INLINE void setInverseMass(PxReal m) { write<Buf::BF_InverseMass>(m); } + + PX_INLINE PxVec3 getInverseInertia() const { return read<Buf::BF_InverseInertia>(); } + PX_INLINE void setInverseInertia(const PxVec3& i) { write<Buf::BF_InverseInertia>(i); } + + PX_INLINE PxReal getLinearDamping() const { return read<Buf::BF_LinearDamping>(); } + PX_INLINE void setLinearDamping(PxReal d) { write<Buf::BF_LinearDamping>(d); } + + PX_INLINE PxReal getAngularDamping() const { return read<Buf::BF_AngularDamping>(); } + PX_INLINE void setAngularDamping(PxReal d) { write<Buf::BF_AngularDamping>(d); } + + PX_INLINE PxRigidBodyFlags getFlags() const { return (isBuffered(Buf::BF_RigidBodyFlags)) ? getBodyBuffer()->mRigidBodyFlags : mBodyCore.getFlags(); } + PX_INLINE void setFlags(PxRigidBodyFlags f); + + PX_INLINE PxRigidDynamicLockFlags getLockFlags() const { return read<Buf::BF_RigidDynamicLockFlags>(); } + PX_INLINE void setLockFlags(PxRigidDynamicLockFlags f) { write<Buf::BF_RigidDynamicLockFlags>(f); } + + PX_INLINE PxReal getSleepThreshold() const { return read<Buf::BF_SleepThreshold>(); } + PX_INLINE void setSleepThreshold(PxReal t) { write<Buf::BF_SleepThreshold>(t); } + + PX_INLINE PxReal getFreezeThreshold() const { return read<Buf::BF_FreezeThreshold>(); } + PX_INLINE void setFreezeThreshold(PxReal t) { write<Buf::BF_FreezeThreshold>(t); } + + PX_INLINE PxReal getMaxPenetrationBias() const { return read<Buf::BF_MaxPenetrationBias>(); } + PX_INLINE void setMaxPenetrationBias(PxReal t) { write<Buf::BF_MaxPenetrationBias>(t); } + + PX_INLINE PxReal getMaxAngVelSq() const { return read<Buf::BF_MaxAngVelSq>(); } + PX_INLINE void setMaxAngVelSq(PxReal v) { write<Buf::BF_MaxAngVelSq>(v); } + + PX_INLINE PxU16 getSolverIterationCounts() const { return Ps::to16(read<Buf::BF_SolverIterationCounts>()); } + PX_INLINE void setSolverIterationCounts(PxU16 c) { write<Buf::BF_SolverIterationCounts>(c); } + + PX_INLINE PxReal getContactReportThreshold() const { return read<Buf::BF_ContactReportThreshold>(); } + PX_INLINE void setContactReportThreshold(PxReal t) { write<Buf::BF_ContactReportThreshold>(t); } + + PX_INLINE PxReal getMaxContactImpulse() const { return read<Buf::BF_MaxContactImpulse>(); } + PX_INLINE void setMaxContactImpulse(PxReal t) { write<Buf::BF_MaxContactImpulse>(t); } + + PX_INLINE void addSpatialAcceleration(const PxVec3* linAcc, const PxVec3* angAcc); + PX_INLINE void clearSpatialAcceleration(bool force, bool torque); + PX_INLINE void addSpatialVelocity(const PxVec3* linVelDelta, const PxVec3* angVelDelta); + PX_INLINE void clearSpatialVelocity(bool force, bool torque); + + PX_INLINE bool getKinematicTarget(PxTransform& p) const; + PX_INLINE void setKinematicTarget(const PxTransform& p); + + PX_INLINE void setMinCCDAdvanceCoefficient(PxReal minCCDAdvanceCoefficient){write<Buf::BF_CCDAdvanceCoefficient>(minCCDAdvanceCoefficient);} + PX_INLINE PxReal getMinCCDAdvanceCoefficient() const { return read<Buf::BF_CCDAdvanceCoefficient>();} + + PX_FORCE_INLINE void onOriginShift(const PxVec3& shift); + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(); + PX_INLINE void syncCollisionWriteThroughState(); + + static size_t getScOffset() + { + return reinterpret_cast<size_t>(&reinterpret_cast<Body*>(0)->mBodyCore); + } + + /** + \brief Shadowed method of #Scb::Base::markUpdated() to store the buffered property flags in a separate location (ran out of flag space) + */ + PX_FORCE_INLINE void markUpdated(PxU32 flag); + + /** + \brief Shadowed method of #Scb::Base::isBuffered() to check the buffered property flags (ran out of flag space) + */ + PX_FORCE_INLINE Ps::IntBool isBuffered(PxU32 flag) const; + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- +public: + PX_FORCE_INLINE const Sc::BodyCore& getScBody() const { return mBodyCore; } // Only use if you know what you're doing! + PX_FORCE_INLINE Sc::BodyCore& getScBody() { return mBodyCore; } // Only use if you know what you're doing! + + PX_FORCE_INLINE static const Body& fromSc(const Core& a) { return static_cast<const Body&>(Actor::fromSc(a)); } + PX_FORCE_INLINE static Body& fromSc(Core &a) { return static_cast<Body&>(Actor::fromSc(a)); } + + PX_FORCE_INLINE bool hasKinematicTarget() const; + PX_FORCE_INLINE void clearSimStateDataForPendingInsert(); + PX_FORCE_INLINE void transitionSimStateDataForPendingInsert(); + + PX_INLINE PxMat33 getGlobalInertiaTensorInverse() const; + + PX_FORCE_INLINE bool checkSleepReadinessBesidesWakeCounter(); + PX_FORCE_INLINE void initBufferedState(); + PX_FORCE_INLINE void clearBufferedState(); + PX_FORCE_INLINE void clearBufferedSleepStateChange(); + + PX_INLINE void wakeUpInternal(PxReal wakeCounter); + PX_INLINE void putToSleepInternal(); + + PX_FORCE_INLINE void switchBodyToNoSim(); + +private: + Sc::BodyCore mBodyCore; + //--------------------------------------------------------------------------------- + // Permanently buffered data (simulation written data) + //--------------------------------------------------------------------------------- + PxTransform mBufferedBody2World; + PxVec3 mBufferedLinVelocity; + PxVec3 mBufferedAngVelocity; + PxReal mBufferedWakeCounter; + PxU32 mBufferedIsSleeping; // Note: If the object is not in a scene this value must be true, i.e., positive + // Don't need 4 bytes but otherwise there is padding here anyway. + PxU32 mBodyBufferFlags; // Stores the buffered property flags since there is not enough space in usual location in Scb::Base. + + PX_FORCE_INLINE const Buf* getBodyBuffer() const { return reinterpret_cast<const Buf*>(getStream()); } + PX_FORCE_INLINE Buf* getBodyBuffer() { return reinterpret_cast<Buf*>(getStream()); } + + PX_INLINE void accumulate(PxVec3& linear, PxVec3& angular, PxU32 linearFlag, PxU32 angularFlag, const PxVec3* linIncrement, const PxVec3* angIncrement) + { + PxU32 flag = 0; + if(linIncrement) + { + linear += *linIncrement; + flag |= linearFlag; + } + + if(angIncrement) + { + angular += *angIncrement; + flag |= angularFlag; + } + + markUpdated(flag); + } + + PX_INLINE void resetAccumulator(PxVec3& linear, PxVec3& angular, PxU32 linearFlag, PxU32 angularFlag, PxU32 raisedFlagLinear, PxU32 raisedFlagAngular, bool force, bool torque) + { + PxU32 flags = mBodyBufferFlags; + PxU32 raisedFlags = 0; + if(force) + { + linear = PxVec3(0.0f); + flags &= ~linearFlag; + raisedFlags |= raisedFlagLinear; + } + + if(torque) + { + angular = PxVec3(0.0f); + flags &= ~angularFlag; + raisedFlags |= raisedFlagAngular; + } + + //This is for the split sim logic to support write-through spatial accelerations. It is for the condition where a spatial acceleration has been applied prior to + //collide(). However, a clear spatial acceleration command is raised by the user between collide() and fetchCollision(), we need to raised this + //flag to clear the previous applied spatial acceleration in the unbuffered state so that this spatial acceleration can be cleared correctly in fetchCollision(). + flags |= raisedFlags; + mBodyBufferFlags = flags; + scheduleForUpdate(); + } + + PX_FORCE_INLINE void setBufferedParamsForAsleep() // use this in the non-buffered case to set the buffered properties + { + mBufferedIsSleeping = 1; + mBufferedWakeCounter = 0.0f; + mBufferedLinVelocity = PxVec3(0.0f); + mBufferedAngVelocity = PxVec3(0.0f); + // no need to clear forces since that will be the job of the corresponding core/sim methods + } + + PX_FORCE_INLINE void setBufferedParamsForAwake(PxReal wakeCounter) // use this in the non-buffered case to set the buffered properties + { + mBufferedIsSleeping = 0; + mBufferedWakeCounter = wakeCounter; + } + + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess<Buf, Core, Body, Body> {}; + + template<PxU32 f> PX_FORCE_INLINE typename Buf::Fns<f,0>::Arg read() const { return Access::read<Buf::Fns<f,0> >(*this, mBodyCore); } + template<PxU32 f> PX_FORCE_INLINE void write(typename Buf::Fns<f,0>::Arg v) { Access::write<Buf::Fns<f,0> >(*this, mBodyCore, v); } + template<PxU32 f> PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush<Buf::Fns<f,0> >(*this, mBodyCore, buf); } + +}; + + +PX_INLINE Body::Body(PxActorType::Enum type, const PxTransform& bodyPose) : mBodyCore(type, bodyPose) +{ + setScbType(ScbType::BODY); + + mBufferedBody2World = mBodyCore.getBody2World(); + mBufferedLinVelocity = mBodyCore.getLinearVelocity(); + mBufferedAngVelocity = mBodyCore.getAngularVelocity(); + mBufferedWakeCounter = mBodyCore.getWakeCounter(); + mBufferedIsSleeping = 1; // this is the specified value for free standing objects + mBodyBufferFlags = 0; +} + +PX_INLINE void Body::setBody2World(const PxTransform& p, bool asPartOfBody2ActorChange) +{ + mBufferedBody2World = p; + + if (!isBuffering()) + { + mBodyCore.setBody2World(p); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if (!asPartOfBody2ActorChange) + { + // call was triggered by a setGlobalPose(). This means the simulated body pose will get + // overwritten by the user value, so we do not need to adjust it. + + mBodyBufferFlags &= ~Buf::BF_Body2World_CoM; + } + else if (!(mBodyBufferFlags & Buf::BF_Body2World)) + { + // there has been no setGlobalPose() on the body yet and the center of mass changes. + // This case needs special treatment because the simulation results for such a body will be based on + // the old center of mass but need to get translated to the new center of mass. + + mBodyBufferFlags |= Buf::BF_Body2World_CoM; + } + + markUpdated(Buf::BF_Body2World); + } +} + +PX_INLINE void Body::setLinearVelocity(const PxVec3& v) +{ + mBufferedLinVelocity = v; + + if (!isBuffering()) + { + mBodyCore.setLinearVelocity(v); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + markUpdated(Buf::BF_LinearVelocity); +} + +PX_INLINE void Body::setAngularVelocity(const PxVec3& v) +{ + mBufferedAngVelocity = v; + + if (!isBuffering()) + { + mBodyCore.setAngularVelocity(v); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + markUpdated(Buf::BF_AngularVelocity); +} + + +PX_INLINE void Body::wakeUpInternal(PxReal wakeCounter) +{ + PX_ASSERT(getScbScene()); + + if (!isBuffering()) + { + setBufferedParamsForAwake(wakeCounter); + mBodyCore.wakeUp(wakeCounter); + } + else + { + mBufferedIsSleeping = 0; + mBufferedWakeCounter = wakeCounter; + markUpdated(Buf::BF_WakeUp | Buf::BF_WakeCounter); + mBodyBufferFlags &= ~Buf::BF_PutToSleep; + } +} + +PX_FORCE_INLINE void Body::wakeUp() +{ + PX_ASSERT(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)); + Scene* scene = getScbScene(); + PX_ASSERT(scene); // only allowed for an object in a scene + + wakeUpInternal(scene->getWakeCounterResetValue()); +} + + +PX_INLINE void Body::putToSleepInternal() +{ + if (!isBuffering()) + { + setBufferedParamsForAsleep(); + mBodyCore.putToSleep(); + } + else + { + mBufferedIsSleeping = 1; + mBufferedWakeCounter = 0.0f; + // it is necessary to set the velocities as a buffered operation (not just adjust the buffered velocities) because + // a putToSleep can be followed by a wakeUp in which case only the wakeUp will get processed on sync, however, the velocities + // still need to get set to 0. + setLinearVelocity(PxVec3(0.0f)); + setAngularVelocity(PxVec3(0.0f)); + mBodyBufferFlags &= ~(Buf::BF_Acceleration | Buf::BF_DeltaVelocity | Buf::BF_KinematicTarget); + + markUpdated(Buf::BF_PutToSleep | Buf::BF_WakeCounter); + mBodyBufferFlags &= ~Buf::BF_WakeUp; + } +} + + +PX_FORCE_INLINE void Body::putToSleep() +{ + PX_ASSERT(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + putToSleepInternal(); +} + + +PX_INLINE void Body::setWakeCounter(PxReal w) +{ + PX_ASSERT(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + mBufferedWakeCounter = w; + + if (!isBuffering()) + { + if (getScbScene() && (w > 0.0f)) + mBufferedIsSleeping = 0; + + mBodyCore.setWakeCounter(w); + + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if (w > 0.0f) + wakeUpInternal(w); + else + markUpdated(Buf::BF_WakeCounter); + } +} + + +PX_INLINE void Body::setFlags(PxRigidBodyFlags f) +{ + PxU32 wasKinematic = getFlags() & PxRigidBodyFlag::eKINEMATIC; + PxU32 isKinematic = f & PxRigidBodyFlag::eKINEMATIC; + bool switchToKinematic = ((!wasKinematic) && isKinematic); + bool switchToDynamic = (wasKinematic && (!isKinematic)); + + if (!isBuffering()) + { + if (switchToKinematic) + setBufferedParamsForAsleep(); + + mBodyCore.setFlags(getScbScene() ? getScbScene()->getScScene().getSimStateDataPool() : NULL, f); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if (switchToKinematic) + putToSleepInternal(); + else if (switchToDynamic) + mBodyBufferFlags &= ~Buf::BF_KinematicTarget; + + getBodyBuffer()->mRigidBodyFlags = f; + markUpdated(Buf::BF_RigidBodyFlags); + } +} + + +PX_INLINE void Body::addSpatialAcceleration(const PxVec3* linAcc, const PxVec3* angAcc) +{ + if (!isBuffering()) + { + mBodyCore.addSpatialAcceleration(getScbScene()->getScScene().getSimStateDataPool(), linAcc, angAcc); + //Spatial acceleration isn't sent to PVD. + } + else + { + Buf* b = getBodyBuffer(); + accumulate(b->mLinAcceleration, b->mAngAcceleration, Buf::BF_AccelerationLinear, Buf::BF_AccelerationAngular, linAcc, angAcc); + } +} + + +PX_INLINE void Body::clearSpatialAcceleration(bool force, bool torque) +{ + if (!isBuffering()) + { + mBodyCore.clearSpatialAcceleration(force, torque); + //Spatial acceleration isn't sent to PVD. + } + else + { + Buf* b = getBodyBuffer(); + resetAccumulator(b->mLinAcceleration, b->mAngAcceleration, Buf::BF_AccelerationLinear, Buf::BF_AccelerationAngular, Buf::BF_ClearAccelerationLinear, Buf::BF_ClearAccelerationAngular, force, torque); + } +} + + +PX_INLINE void Body::addSpatialVelocity(const PxVec3* linVelDelta, const PxVec3* angVelDelta) +{ + if (!isBuffering()) + { + mBodyCore.addSpatialVelocity(getScbScene()->getScScene().getSimStateDataPool(), linVelDelta, angVelDelta); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* b = getBodyBuffer(); + accumulate(b->mLinDeltaVelocity, b->mAngDeltaVelocity, Buf::BF_DeltaVelocityLinear, Buf::BF_DeltaVelocityAngular, linVelDelta, angVelDelta); + } +} + + +PX_INLINE void Body::clearSpatialVelocity(bool force, bool torque) +{ + if (!isBuffering()) + { + mBodyCore.clearSpatialVelocity(force, torque); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* b = getBodyBuffer(); + resetAccumulator(b->mLinDeltaVelocity, b->mAngDeltaVelocity, Buf::BF_DeltaVelocityLinear, Buf::BF_DeltaVelocityAngular, Buf::BF_ClearDeltaVelocityLinear, Buf::BF_ClearDeltaVelocityAngular, force, torque); + } +} + + +PX_INLINE bool Body::getKinematicTarget(PxTransform& p) const +{ + if (isBuffered(Buf::BF_KinematicTarget)) + { + p = getBodyBuffer()->mKinematicTarget; + return true; + } + else if (getControlState() != ControlState::eREMOVE_PENDING) + return mBodyCore.getKinematicTarget(p); + else + return false; +} + + +PX_INLINE void Body::setKinematicTarget(const PxTransform& p) +{ + Scene* scene = getScbScene(); + PX_ASSERT(scene); // only allowed for an object in a scene + PxReal wakeCounterResetValue = scene->getWakeCounterResetValue(); + + if (!isBuffering()) + { + mBodyCore.setKinematicTarget(scene->getScScene().getSimStateDataPool(), p, wakeCounterResetValue); + setBufferedParamsForAwake(wakeCounterResetValue); + + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + PX_ASSERT((mBodyBufferFlags & (Buf::BF_DeltaVelocity|Buf::BF_Acceleration)) == 0); // switching to kinematic should do that. + getBodyBuffer()->mKinematicTarget = p; + markUpdated(Buf::BF_KinematicTarget); + + wakeUpInternal(wakeCounterResetValue); + } +#if PX_SUPPORT_PVD + if(getControlState() == ControlState::eIN_SCENE) + { + scene->getScenePvdClient().updateKinematicTarget(this, p); + } +#endif +} + + +PX_FORCE_INLINE void Body::onOriginShift(const PxVec3& shift) +{ + mBufferedBody2World.p -= shift; + mBodyCore.onOriginShift(shift); +} + + + +//-------------------------------------------------------------- +// +// Miscellaneous +// +//-------------------------------------------------------------- + +PX_FORCE_INLINE bool Body::hasKinematicTarget() const +{ + return + ( + isBuffered(BodyBuffer::BF_KinematicTarget) || mBodyCore.getHasValidKinematicTarget() + ); +} + + +PX_FORCE_INLINE void Body::clearSimStateDataForPendingInsert() +{ + Sc::BodyCore& core = getScBody(); + if (insertPending()) + { + // not-so-nice-code to cover the following cases: + // - user adds a kinematic to the scene, sets a target and removes the kinematic from scene again (all while the sim is running) + // - same as above but instead of removing the kinematic it gets switched to dynamic + // - user adds a dynamic to the scene, sets a target and removes the dynamic from scene again (all while the sim is running) + + if(core.getSimStateData(true)) + core.tearDownSimStateData(getScbScene()->getScScene().getSimStateDataPool(), true); + else if(core.getSimStateData(false)) + core.tearDownSimStateData(getScbScene()->getScScene().getSimStateDataPool(), false); + } +} + + +PX_FORCE_INLINE void Body::transitionSimStateDataForPendingInsert() +{ + Sc::BodyCore& core = getScBody(); + if (insertPending()) + { + // not-so-nice-code to cover the following case: + // - user adds a dynamic, adds force, then switches to kinematic (all while the sim is running) + + if(core.getSimStateData(false)) + { + core.setupSimStateData(getScbScene()->getScScene().getSimStateDataPool(), true); // note: this won't allocate the memory twice + } + } +} + + +PX_INLINE PxMat33 Body::getGlobalInertiaTensorInverse() const +{ + PxMat33 inverseInertiaWorldSpace; + Cm::transformInertiaTensor(getInverseInertia(), Gu::PxMat33Padded(getBody2World().q), inverseInertiaWorldSpace); + return inverseInertiaWorldSpace; +} + + +PX_FORCE_INLINE bool Body::checkSleepReadinessBesidesWakeCounter() +{ + return (getLinearVelocity().isZero() && getAngularVelocity().isZero()); + // no need to test for pending force updates yet since currently this is not supported on scene insertion +} + + +PX_FORCE_INLINE void Body::initBufferedState() +{ + PX_ASSERT(mBufferedIsSleeping); // this method is only meant to get called when an object is added to the scene + + if ((getWakeCounter() == 0.0f) && checkSleepReadinessBesidesWakeCounter()) + mBufferedIsSleeping = 1; + else + mBufferedIsSleeping = 0; +} + + +PX_FORCE_INLINE void Body::clearBufferedState() +{ + if (!(getFlags() & PxRigidBodyFlag::eKINEMATIC)) + { + mBufferedIsSleeping = 1; // the expected state when an object gets removed from the scene. + mBodyBufferFlags &= ~(Buf::BF_Acceleration | Buf::BF_DeltaVelocity); + } + else + { + // make sure the buffered properties for a kinematic that is not in a scene are set according to the specification + + // necessary to use the putToSleep method because buffered re-insertion case needs to be covered as well. Currently, re-insertion + // just clears the remove operation. This would prevent the core object parameters to get updated. Thus the operations need + // to be buffered and putToSleepInternal takes care of that. + putToSleepInternal(); + } + + RigidObject::clearBufferedState(); +} + + +PX_FORCE_INLINE void Body::clearBufferedSleepStateChange() +{ + mBodyBufferFlags &= ~(Buf::BF_WakeUp | Buf::BF_PutToSleep); +} + + +PX_FORCE_INLINE void Body::switchBodyToNoSim() +{ + Scb::Scene* scene = getScbScene(); + + switchToNoSim(true); + + if ((!scene) || (!getScbScene()->isPhysicsBuffering())) + { + setBufferedParamsForAsleep(); + getScBody().putToSleep(); + } + else + putToSleepInternal(); + + if (scene) + clearSimStateDataForPendingInsert(); +} + + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_FORCE_INLINE void Body::markUpdated(PxU32 flag) +{ + scheduleForUpdate(); + mBodyBufferFlags |= flag; +} + +PX_FORCE_INLINE Ps::IntBool Body::isBuffered(PxU32 flag) const +{ + return Ps::IntBool(mBodyBufferFlags & flag); +} + +PX_INLINE void Body::syncCollisionWriteThroughState() +{ + PxU32 bufferFlags = mBodyBufferFlags; + + //---- + if ((bufferFlags & Buf::BF_LinearVelocity) == 0) + mBufferedLinVelocity = mBodyCore.getLinearVelocity(); + else + { + PX_ASSERT( (mBufferedIsSleeping && mBufferedLinVelocity.isZero()) || + (!mBufferedIsSleeping) || + (getControlState() == ControlState::eREMOVE_PENDING)); + PX_ASSERT( mBufferedLinVelocity.isZero() || + ((!mBufferedLinVelocity.isZero()) && (!mBufferedIsSleeping)) || + (getControlState() == ControlState::eREMOVE_PENDING)); + + mBodyCore.setLinearVelocity(mBufferedLinVelocity); + //clear the flag + bufferFlags &= ~Buf::BF_LinearVelocity; + } + + //---- + + if ((bufferFlags & Buf::BF_AngularVelocity) == 0) + mBufferedAngVelocity = mBodyCore.getAngularVelocity(); + else + { + PX_ASSERT( (mBufferedIsSleeping && mBufferedAngVelocity.isZero()) || + (!mBufferedIsSleeping) || + (getControlState() == ControlState::eREMOVE_PENDING)); + PX_ASSERT( mBufferedAngVelocity.isZero() || + ((!mBufferedAngVelocity.isZero()) && (!mBufferedIsSleeping)) || + (getControlState() == ControlState::eREMOVE_PENDING)); + + mBodyCore.setAngularVelocity(mBufferedAngVelocity); + //clear the flag + bufferFlags &= ~Buf::BF_AngularVelocity; + } + + //---- + + if (bufferFlags & Buf::BF_KinematicTarget) + { + //don't apply kinematic target unless the actor is kinematic already. setKinematicTarget is write-through properties for split sim but setRigidBodyFlag transition from rigid body to kinematic isn't write-through + if(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC) + { + Buf& buffer = *getBodyBuffer(); + PX_ASSERT(mBufferedWakeCounter > 0.0f); // that is the expected behavior + + mBodyCore.setKinematicTarget(getScbScene()->getScScene().getSimStateDataPool(), buffer.mKinematicTarget, mBufferedWakeCounter); + //clear the flag + bufferFlags &= ~Buf::BF_KinematicTarget; + } + } + + //---- + //in case user call addForce(), collide(), clearForce(), which we need to clear the acclearation in the low-level + if(bufferFlags & Buf::BF_ClearAcceleration) + { + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping); + mBodyCore.clearSpatialAcceleration((bufferFlags & Buf::BF_ClearAccelerationLinear)!=0, (bufferFlags & Buf::BF_ClearAccelerationAngular)!=0); + + //clear the flag, we don't clear the buffered acceleration, because the user might call addForce() again after calling clearForce() + bufferFlags &= ~Buf::BF_ClearAcceleration; + } + + //---- + + //apply addForce/clearForce, addTorque/clearTorque + if(bufferFlags & Buf::BF_Acceleration) + { + Buf& buffer = *getBodyBuffer(); + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping || (buffer.mLinAcceleration.isZero() && buffer.mAngAcceleration.isZero())); + mBodyCore.addSpatialAcceleration(getScbScene()->getScScene().getSimStateDataPool(), &buffer.mLinAcceleration, &buffer.mAngAcceleration); + + //clear the flag + bufferFlags &= ~Buf::BF_Acceleration; + buffer.mLinAcceleration = PxVec3(0.0f); + buffer.mAngAcceleration = PxVec3(0.0f); + } + + //---- + + if(bufferFlags & Buf::BF_ClearDeltaVelocity) + { + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping ); + mBodyCore.clearSpatialVelocity((bufferFlags & Buf::BF_ClearDeltaVelocityLinear)!=0, (bufferFlags & Buf::BF_ClearDeltaVelocityAngular)!=0); + + //clear the flag, we don't clear the buffered velocity, because the user might call addForce() again after calling clearForce() + bufferFlags &= ~Buf::BF_ClearDeltaVelocity; + } + + //---- + + if(bufferFlags & Buf::BF_DeltaVelocity) + { + Buf& buffer = *getBodyBuffer(); + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping || (buffer.mLinDeltaVelocity.isZero() && buffer.mAngDeltaVelocity.isZero())); + mBodyCore.addSpatialVelocity(getScbScene()->getScScene().getSimStateDataPool(), &buffer.mLinDeltaVelocity, &buffer.mAngDeltaVelocity); + + //clear the flag + bufferFlags &= ~Buf::BF_DeltaVelocity; + buffer.mLinDeltaVelocity = PxVec3(0.0f); + buffer.mAngDeltaVelocity = PxVec3(0.0f); + } + + //---- + + if ((bufferFlags & Buf::BF_WakeCounter) == 0) + mBufferedWakeCounter = mBodyCore.getWakeCounter(); + else if (!(bufferFlags & (Buf::BF_WakeUp | Buf::BF_PutToSleep))) // if there has been at least one buffered sleep state transition, then there is no use in adjusting the wake counter separately because it will + // get done in the sleep state update. + { + PX_ASSERT((getControlState() == ControlState::eREMOVE_PENDING) || (mBufferedWakeCounter == 0.0f)); // a wake counter change is always connected to a sleep state change, except if setWakeCounter(0.0f) was called or an object gets removed from the scene after it was woken up. + + mBodyCore.setWakeCounter(mBufferedWakeCounter); + + bufferFlags &= ~Buf::BF_WakeCounter; + } + else if(bufferFlags & Buf::BF_WakeUp) + { + Buf& buffer = *getBodyBuffer(); + //Because in the split sim, transition from rigid body to kinematic isn't a write through properties. However, when the user call setKinematicTarget, the SDK wake up the actor so we want to avoid waking up the + //actor if the actor is transitioning from rigid body to kinematic or vice versa. + PxRigidBodyFlags changeFlags= mBodyCore.getFlags() ^ buffer.mRigidBodyFlags; + if(!((bufferFlags & Buf::BF_RigidBodyFlags) && (changeFlags & PxRigidBodyFlag::eKINEMATIC))) + { + PX_ASSERT(bufferFlags & Buf::BF_WakeCounter); // sleep state transitions always mark the wake counter dirty + PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing an object should clear pending wakeUp/putToSleep operations since the state for a free standing object gets set according to specification. + + // The sleep state ends up with the proper result that reflects the order of the original buffered operations because... + // - every operation that affects the sleep state makes a buffered call to wakeUp/putToSleep, hence, the buffered sleep transition + // will always reflect the latest change + // - a user triggered sleep state transition (wakeUp/putToSleep) always adjusts the affected properties (velocity, force, wake counter...) through separate + // buffered calls, hence, those properties will get adjusted to the correct values in the end + // - sleep state sync runs after all calls that have side effects on the sleep state. + // + PX_ASSERT(!mBufferedIsSleeping); + PX_ASSERT(bufferFlags & Buf::BF_WakeUp); + + // can not assert for any values here since it is possible, for example, to end up waking something up with a 0 wakeCounter here (as a consequence of a buffered wakeUp() followed by a setWakeCounter(0)) + + mBodyCore.wakeUp(mBufferedWakeCounter); + + bufferFlags &= ~(Buf::BF_WakeUp | Buf::BF_WakeCounter); + } + } + + //---- + + mBodyBufferFlags = bufferFlags; +} + +PX_INLINE void Body::syncState() +{ + // if the body was removed from the scene, we expect... + // ...it to be marked as sleeping (that is the specification) + // ...no pending sleep state change (because wakeUp/putTosleep is illegal for a free standing object and we clear such operations if pending). + // Note: a sleep state change might have happened before the removal but the corresponding wake counter change is then covered through the BF_WakeCounter dirty flag. + PX_ASSERT( (getControlState() != ControlState::eREMOVE_PENDING) || + (mBufferedIsSleeping && (!isBuffered(Buf::BF_WakeUp | Buf::BF_PutToSleep))) ); + + + // + // IMPORTANT: Since we ran out of space for buffered property flags, the Scb::Body property related flags are stored in mBodyBufferFlags. + // To get the buffer flags from the base classes, use getBufferFlags() + // + const PxU32 bufferFlags = mBodyBufferFlags; + const PxU32 baseBufferFlags = getBufferFlags(); + + if ((bufferFlags & Buf::BF_Body2World) == 0) + mBufferedBody2World = mBodyCore.getBody2World(); + else if ((bufferFlags & Buf::BF_Body2World_CoM) == 0) + mBodyCore.setBody2World(mBufferedBody2World); + else + { + // IMPORTANT: Do this before adjusting body2Actor + PX_ASSERT(bufferFlags & Buf::BF_Body2Actor); + Buf& buffer = *getBodyBuffer(); + const PxTransform newBody2oldBody = mBodyCore.getBody2Actor().transformInv(buffer.mBody2Actor); + + PxTransform b2w = mBodyCore.getBody2World(); + b2w = b2w.transform(newBody2oldBody); // translate simulation result from old CoM to new CoM + + mBufferedBody2World = b2w; + mBodyCore.setBody2World(b2w); + } + + //---- + + if (baseBufferFlags & Buf::BF_ActorFlags) + syncNoSimSwitch(*getBodyBuffer(), mBodyCore, true); + + //---- + + if(bufferFlags & ~( Buf::BF_WakeCounter|Buf::BF_Body2World|Buf::BF_LinearVelocity|Buf::BF_AngularVelocity + |Buf::BF_WakeUp|Buf::BF_PutToSleep)) // Optimization to avoid all the if-statements below if possible + { + Buf& buffer = *getBodyBuffer(); + + flush<Buf::BF_InverseMass>(buffer); + flush<Buf::BF_InverseInertia>(buffer); + flush<Buf::BF_LinearDamping>(buffer); + flush<Buf::BF_AngularDamping>(buffer); + flush<Buf::BF_MaxAngVelSq>(buffer); + flush<Buf::BF_SleepThreshold>(buffer); + flush<Buf::BF_SolverIterationCounts>(buffer); + flush<Buf::BF_ContactReportThreshold>(buffer); + flush<Buf::BF_Body2Actor>(buffer); + flush<Buf::BF_FreezeThreshold>(buffer); + flush<Buf::BF_MaxPenetrationBias>(buffer); + flush<Buf::BF_MaxContactImpulse>(buffer); + if (bufferFlags & Buf::BF_RigidBodyFlags) + { + mBodyCore.setFlags(getScbScene()->getScScene().getSimStateDataPool(), buffer.mRigidBodyFlags); + } + } + + + //This method sync all the write through properties in collision and is called in fetchCollision() + syncCollisionWriteThroughState(); + + //---- + + bool isSimObjectSleeping = mBodyCore.isSleeping(); + if ((bufferFlags & (Buf::BF_PutToSleep)) == 0) + { + if (getControlState() != ControlState::eREMOVE_PENDING) // we do not want to sync the simulation sleep state if the object was removed (free standing objects have buffered state sleeping) + mBufferedIsSleeping = PxU32(isSimObjectSleeping); + else + PX_ASSERT(mBufferedIsSleeping); // this must get set immediately at remove + } + else + { + PX_ASSERT(bufferFlags & Buf::BF_WakeCounter); // sleep state transitions always mark the wake counter dirty + PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing an object should clear pending wakeUp/putToSleep operations since the state for a free standing object gets set according to specification. + + // The sleep state ends up with the proper result that reflects the order of the original buffered operations because... + // - every operation that affects the sleep state makes a buffered call to wakeUp/putToSleep, hence, the buffered sleep transition + // will always reflect the latest change + // - a user triggered sleep state transition (wakeUp/putToSleep) always adjusts the affected properties (velocity, force, wake counter...) through separate + // buffered calls, hence, those properties will get adjusted to the correct values in the end + // - sleep state sync runs after all calls that have side effects on the sleep state. + // + + PX_ASSERT(mBufferedIsSleeping); + PX_ASSERT(!(bufferFlags & Buf::BF_WakeUp)); + PX_ASSERT(mBufferedWakeCounter == 0.0f); + PX_ASSERT(mBufferedLinVelocity.isZero()); + PX_ASSERT(mBufferedAngVelocity.isZero()); + PX_ASSERT(!(bufferFlags & Buf::BF_Acceleration)); + PX_ASSERT(!(bufferFlags & Buf::BF_DeltaVelocity)); + + mBodyCore.putToSleep(); + } + + // PT: we must call this even when there's no buffered data + RigidObject::syncState(); + + // -------------- + // postconditions + // + PX_ASSERT((getControlState() != ControlState::eREMOVE_PENDING) || mBufferedIsSleeping); // nothing in this method should change this +#ifdef _DEBUG + // make sure that for a removed kinematic, the buffered params hold the values as defined in our specification + if ((mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC) && (getControlState() == ControlState::eREMOVE_PENDING)) + { + PX_ASSERT(mBufferedLinVelocity.isZero()); + PX_ASSERT(mBufferedAngVelocity.isZero()); + PX_ASSERT(mBufferedWakeCounter == 0.0f); + } +#endif + // + // postconditions + // -------------- + + postSyncState(); + mBodyBufferFlags = 0; +} + +} // namespace Scb + +} + +#endif |