// 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-2025 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "DySleep.h" using namespace physx; // PT: TODO: refactor this, parts of the two codepaths are very similar static PX_FORCE_INLINE PxReal updateWakeCounter(PxsRigidBody* originalBody, PxReal dt, bool enableStabilization, const Cm::SpatialVector& motionVelocity, PxIntBool hasStaticTouch) { PxsBodyCore& bodyCore = originalBody->getCore(); // update the body's sleep state and const PxReal wakeCounterResetTime = 20.0f*0.02f; PxReal wc = bodyCore.wakeCounter; if (enableStabilization) { const PxTransform& body2World = bodyCore.body2World; // calculate normalized energy: kinetic energy divided by mass const PxVec3& t = bodyCore.inverseInertia; const PxVec3 inertia( t.x > 0.0f ? 1.0f / t.x : 1.0f, t.y > 0.0f ? 1.0f / t.y : 1.0f, t.z > 0.0f ? 1.0f / t.z : 1.0f); const PxVec3& sleepLinVelAcc = motionVelocity.linear; const PxVec3 sleepAngVelAcc = body2World.q.rotateInv(motionVelocity.angular); // scale threshold by cluster factor (more contacts => higher sleep threshold) //const PxReal clusterFactor = PxReal(1u + getNumUniqueInteractions()); PxReal invMass = bodyCore.inverseMass; if (invMass == 0.0f) invMass = 1.0f; const PxReal angular = sleepAngVelAcc.multiply(sleepAngVelAcc).dot(inertia) * invMass; const PxReal linear = sleepLinVelAcc.magnitudeSquared(); const PxReal frameNormalizedEnergy = 0.5f * (angular + linear); const PxReal cf = hasStaticTouch ? PxReal(PxMin(10u, bodyCore.numCountedInteractions)) : 0.0f; const PxReal freezeThresh = cf*bodyCore.freezeThreshold; originalBody->mFreezeCount = PxMax(originalBody->mFreezeCount - dt, 0.0f); bool settled = true; PxReal accelScale = PxMin(1.0f, originalBody->mAccelScale + dt); if (frameNormalizedEnergy >= freezeThresh) { settled = false; originalBody->mFreezeCount = PXD_FREEZE_INTERVAL; } if (!hasStaticTouch) { accelScale = 1.0f; settled = false; } bool freeze = false; if (settled) { //Dampen bodies that are just about to go to sleep if (cf > 1.0f) { const PxReal sleepDamping = PXD_SLEEP_DAMPING; const PxReal sleepDampingTimesDT = sleepDamping*dt; const PxReal d = 1.0f - sleepDampingTimesDT; bodyCore.linearVelocity = bodyCore.linearVelocity * d; bodyCore.angularVelocity = bodyCore.angularVelocity * d; accelScale = accelScale * 0.75f + 0.25f*PXD_FREEZE_SCALE; } freeze = originalBody->mFreezeCount == 0.0f && frameNormalizedEnergy < (bodyCore.freezeThreshold * PXD_FREEZE_TOLERANCE); } originalBody->mAccelScale = accelScale; const PxU32 wasFrozen = originalBody->mInternalFlags & PxsRigidBody::eFROZEN; PxU16 flags; if(freeze) { //current flag isn't frozen but freeze flag raise so we need to raise the frozen flag in this frame flags = PxU16(PxsRigidBody::eFROZEN); if(!wasFrozen) flags |= PxsRigidBody::eFREEZE_THIS_FRAME; bodyCore.body2World = originalBody->getLastCCDTransform(); } else { flags = 0; if(wasFrozen) flags |= PxsRigidBody::eUNFREEZE_THIS_FRAME; } originalBody->mInternalFlags = flags; /*KS: New algorithm for sleeping when using stabilization: * Energy *this frame* must be higher than sleep threshold and accumulated energy over previous frames * must be higher than clusterFactor*energyThreshold. */ if (wc < wakeCounterResetTime * 0.5f || wc < dt) { //Accumulate energy originalBody->mSleepLinVelAcc += sleepLinVelAcc; originalBody->mSleepAngVelAcc += sleepAngVelAcc; //If energy this frame is high if (frameNormalizedEnergy >= bodyCore.sleepThreshold) { //Compute energy over sleep preparation time const PxReal sleepAngular = originalBody->mSleepAngVelAcc.multiply(originalBody->mSleepAngVelAcc).dot(inertia) * invMass; const PxReal sleepLinear = originalBody->mSleepLinVelAcc.magnitudeSquared(); const PxReal normalizedEnergy = 0.5f * (sleepAngular + sleepLinear); const PxReal sleepClusterFactor = PxReal(1u + bodyCore.numCountedInteractions); // scale threshold by cluster factor (more contacts => higher sleep threshold) const PxReal threshold = sleepClusterFactor*bodyCore.sleepThreshold; //If energy over sleep preparation time is high if (normalizedEnergy >= threshold) { //Wake up //PX_ASSERT(isActive()); originalBody->resetSleepFilter(); const float factor = bodyCore.sleepThreshold == 0.0f ? 2.0f : PxMin(normalizedEnergy / threshold, 2.0f); PxReal oldWc = wc; wc = factor * 0.5f * wakeCounterResetTime + dt * (sleepClusterFactor - 1.0f); bodyCore.solverWakeCounter = wc; //if (oldWc == 0.0f) // for the case where a sleeping body got activated by the system (not the user) AND got processed by the solver as well // notifyNotReadyForSleeping(bodyCore.nodeIndex); if (oldWc == 0.0f) originalBody->mInternalFlags |= PxsRigidBody::eACTIVATE_THIS_FRAME; return wc; } } } } else { if (wc < wakeCounterResetTime * 0.5f || wc < dt) { const PxTransform& body2World = bodyCore.body2World; // calculate normalized energy: kinetic energy divided by mass const PxVec3& t = bodyCore.inverseInertia; const PxVec3 inertia( t.x > 0.0f ? 1.0f / t.x : 1.0f, t.y > 0.0f ? 1.0f / t.y : 1.0f, t.z > 0.0f ? 1.0f / t.z : 1.0f); const PxVec3& sleepLinVelAcc = motionVelocity.linear; const PxVec3 sleepAngVelAcc = body2World.q.rotateInv(motionVelocity.angular); originalBody->mSleepLinVelAcc += sleepLinVelAcc; originalBody->mSleepAngVelAcc += sleepAngVelAcc; PxReal invMass = bodyCore.inverseMass; if (invMass == 0.0f) invMass = 1.0f; const PxReal angular = originalBody->mSleepAngVelAcc.multiply(originalBody->mSleepAngVelAcc).dot(inertia) * invMass; const PxReal linear = originalBody->mSleepLinVelAcc.magnitudeSquared(); const PxReal normalizedEnergy = 0.5f * (angular + linear); // scale threshold by cluster factor (more contacts => higher sleep threshold) const PxReal clusterFactor = PxReal(1 + bodyCore.numCountedInteractions); const PxReal threshold = clusterFactor*bodyCore.sleepThreshold; if (normalizedEnergy >= threshold) { //PX_ASSERT(isActive()); originalBody->resetSleepFilter(); const float factor = threshold == 0.0f ? 2.0f : PxMin(normalizedEnergy / threshold, 2.0f); PxReal oldWc = wc; wc = factor * 0.5f * wakeCounterResetTime + dt * (clusterFactor - 1.0f); bodyCore.solverWakeCounter = wc; PxU16 flags = 0; if (oldWc == 0.0f) // for the case where a sleeping body got activated by the system (not the user) AND got processed by the solver as well { flags |= PxsRigidBody::eACTIVATE_THIS_FRAME; //notifyNotReadyForSleeping(bodyCore.nodeIndex); } originalBody->mInternalFlags = flags; return wc; } } } wc = PxMax(wc - dt, 0.0f); bodyCore.solverWakeCounter = wc; return wc; } void Dy::sleepCheck(PxsRigidBody* originalBody, PxReal dt, bool enableStabilization, const Cm::SpatialVector& motionVelocity, PxIntBool hasStaticTouch) { const PxReal wc = updateWakeCounter(originalBody, dt, enableStabilization, motionVelocity, hasStaticTouch); if(wc == 0.0f) { //PxsBodyCore& bodyCore = originalBody->getCore(); originalBody->mInternalFlags |= PxsRigidBody::eDEACTIVATE_THIS_FRAME; // notifyReadyForSleeping(bodyCore.nodeIndex); originalBody->resetSleepFilter(); } }