RavEngine
Loading...
Searching...
No Matches
DyBodyCoreIntegrator.h
1// Redistribution and use in source and binary forms, with or without
2// modification, are permitted provided that the following conditions
3// are met:
4// * Redistributions of source code must retain the above copyright
5// notice, this list of conditions and the following disclaimer.
6// * Redistributions in binary form must reproduce the above copyright
7// notice, this list of conditions and the following disclaimer in the
8// documentation and/or other materials provided with the distribution.
9// * Neither the name of NVIDIA CORPORATION nor the names of its
10// contributors may be used to endorse or promote products derived
11// from this software without specific prior written permission.
12//
13// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
14// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24//
25// Copyright (c) 2008-2022 NVIDIA Corporation. All rights reserved.
26// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
27// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
28
29#ifndef DY_BODYCORE_INTEGRATOR_H
30#define DY_BODYCORE_INTEGRATOR_H
31
32#include "PxvDynamics.h"
33#include "PxsRigidBody.h"
34#include "DySolverBody.h"
35#include "DySleepingConfigulation.h"
36#include "PxsIslandSim.h"
37
38namespace physx
39{
40
41namespace Dy
42{
43
44PX_FORCE_INLINE void bodyCoreComputeUnconstrainedVelocity
45(const PxVec3& gravity, const PxReal dt, const PxReal linearDamping, const PxReal angularDamping, const PxReal accelScale,
46const PxReal maxLinearVelocitySq, const PxReal maxAngularVelocitySq, PxVec3& inOutLinearVelocity, PxVec3& inOutAngularVelocity,
47bool disableGravity)
48{
49
50 //Multiply everything that needs multiplied by dt to improve code generation.
51
52 PxVec3 linearVelocity = inOutLinearVelocity;
53 PxVec3 angularVelocity = inOutAngularVelocity;
54
55 const PxReal linearDampingTimesDT=linearDamping*dt;
56 const PxReal angularDampingTimesDT=angularDamping*dt;
57 const PxReal oneMinusLinearDampingTimesDT=1.0f-linearDampingTimesDT;
58 const PxReal oneMinusAngularDampingTimesDT=1.0f-angularDampingTimesDT;
59
60 //TODO context-global gravity
61 if (!disableGravity)
62 {
63 const PxVec3 linearAccelTimesDT = gravity*dt *accelScale;
64 linearVelocity += linearAccelTimesDT;
65 }
66
67 //Apply damping.
68 const PxReal linVelMultiplier = physx::intrinsics::fsel(oneMinusLinearDampingTimesDT, oneMinusLinearDampingTimesDT, 0.0f);
69 const PxReal angVelMultiplier = physx::intrinsics::fsel(oneMinusAngularDampingTimesDT, oneMinusAngularDampingTimesDT, 0.0f);
70 linearVelocity*=linVelMultiplier;
71 angularVelocity*=angVelMultiplier;
72
73 // Clamp velocity
74 const PxReal linVelSq = linearVelocity.magnitudeSquared();
75 if(linVelSq > maxLinearVelocitySq)
76 {
77 linearVelocity *= PxSqrt(maxLinearVelocitySq / linVelSq);
78 }
79 const PxReal angVelSq = angularVelocity.magnitudeSquared();
80 if(angVelSq > maxAngularVelocitySq)
81 {
82 angularVelocity *= PxSqrt(maxAngularVelocitySq / angVelSq);
83 }
84
85 inOutLinearVelocity = linearVelocity;
86 inOutAngularVelocity = angularVelocity;
87}
88
89
90PX_FORCE_INLINE void integrateCore(PxVec3& motionLinearVelocity, PxVec3& motionAngularVelocity,
91 PxSolverBody& solverBody, PxSolverBodyData& solverBodyData, const PxF32 dt, const PxU32 lockFlags)
92{
93 if (lockFlags)
94 {
95 if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X)
96 {
97 motionLinearVelocity.x = 0.f;
98 solverBody.linearVelocity.x = 0.f;
99 }
100 if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y)
101 {
102 motionLinearVelocity.y = 0.f;
103 solverBody.linearVelocity.y = 0.f;
104 }
105 if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z)
106 {
107 motionLinearVelocity.z = 0.f;
108 solverBody.linearVelocity.z = 0.f;
109 }
110
111 //The angular velocity should be 0 because it is now impossible to make it rotate around that axis!
112 if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X)
113 {
114 motionAngularVelocity.x = 0.f;
115 solverBody.angularState.x = 0.f;
116 }
117 if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y)
118 {
119 motionAngularVelocity.y = 0.f;
120 solverBody.angularState.y = 0.f;
121 }
122 if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z)
123 {
124 motionAngularVelocity.z = 0.f;
125 solverBody.angularState.z = 0.f;
126 }
127 }
128
129 // Integrate linear part
130 PxVec3 linearMotionVel = solverBodyData.linearVelocity + motionLinearVelocity;
131 PxVec3 delta = linearMotionVel * dt;
132 PxVec3 angularMotionVel = solverBodyData.angularVelocity + solverBodyData.sqrtInvInertia * motionAngularVelocity;
133 PxReal w = angularMotionVel.magnitudeSquared();
134 solverBodyData.body2World.p += delta;
135 PX_ASSERT(solverBodyData.body2World.p.isFinite());
136
137 //Store back the linear and angular velocities
138 //core.linearVelocity += solverBody.linearVelocity * solverBodyData.sqrtInvMass;
139 solverBodyData.linearVelocity += solverBody.linearVelocity;
140 solverBodyData.angularVelocity += solverBodyData.sqrtInvInertia * solverBody.angularState;
141
142 // Integrate the rotation using closed form quaternion integrator
143 if (w != 0.0f)
144 {
145 w = PxSqrt(w);
146 // Perform a post-solver clamping
147 // TODO(dsequeira): ignore this for the moment
148 //just clamp motionVel to half float-range
149 const PxReal maxW = 1e+7f; //Should be about sqrt(PX_MAX_REAL/2) or smaller
150 if (w > maxW)
151 {
152 angularMotionVel = angularMotionVel.getNormalized() * maxW;
153 w = maxW;
154 }
155 const PxReal v = dt * w * 0.5f;
156 PxReal s, q;
157 PxSinCos(v, s, q);
158 s /= w;
159
160 const PxVec3 pqr = angularMotionVel * s;
161 const PxQuat quatVel(pqr.x, pqr.y, pqr.z, 0);
162 PxQuat result = quatVel * solverBodyData.body2World.q;
163
164 result += solverBodyData.body2World.q * q;
165
166 solverBodyData.body2World.q = result.getNormalized();
167 PX_ASSERT(solverBodyData.body2World.q.isSane());
168 PX_ASSERT(solverBodyData.body2World.q.isFinite());
169 }
170
171 motionLinearVelocity = linearMotionVel;
172 motionAngularVelocity = angularMotionVel;
173}
174
175
176// PT: TODO: why do we force-inline this?
177PX_FORCE_INLINE PxReal _updateWakeCounter(PxsRigidBody* originalBody, PxReal dt, PxReal /*invDt*/, const bool enableStabilization, const Cm::SpatialVector& motionVelocity,
178 bool hasStaticTouch)
179{
180 PxsBodyCore& bodyCore = originalBody->getCore();
181
182 // update the body's sleep state and
183 PxReal wakeCounterResetTime = 20.0f*0.02f;
184
185 PxReal wc = bodyCore.wakeCounter;
186
187 {
188 if (enableStabilization)
189 {
190 bool freeze = false;
191 const PxTransform& body2World = bodyCore.body2World;
192
193 // calculate normalized energy: kinetic energy divided by mass
194
195 const PxVec3& t = bodyCore.inverseInertia;
196 const PxVec3 inertia(t.x > 0.f ? 1.0f / t.x : 1.f, t.y > 0.f ? 1.0f / t.y : 1.f, t.z > 0.f ? 1.0f / t.z : 1.f);
197
198 const PxVec3& sleepLinVelAcc = motionVelocity.linear;
199 const PxVec3 sleepAngVelAcc = body2World.q.rotateInv(motionVelocity.angular);
200
201 // scale threshold by cluster factor (more contacts => higher sleep threshold)
202 //const PxReal clusterFactor = PxReal(1u + getNumUniqueInteractions());
203
204 PxReal invMass = bodyCore.inverseMass;
205 if (invMass == 0.f)
206 invMass = 1.f;
207
208 const PxReal angular = sleepAngVelAcc.multiply(sleepAngVelAcc).dot(inertia) * invMass;
209 const PxReal linear = sleepLinVelAcc.magnitudeSquared();
210 const PxReal frameNormalizedEnergy = 0.5f * (angular + linear);
211
212 const PxReal cf = hasStaticTouch ? PxReal(PxMin(10u, bodyCore.numCountedInteractions)) : 0.f;
213 const PxReal freezeThresh = cf*bodyCore.freezeThreshold;
214
215 originalBody->freezeCount = PxMax(originalBody->freezeCount - dt, 0.0f);
216 bool settled = true;
217
218 PxReal accelScale = PxMin(1.f, originalBody->accelScale + dt);
219
220 if (frameNormalizedEnergy >= freezeThresh)
221 {
222 settled = false;
223 originalBody->freezeCount = PXD_FREEZE_INTERVAL;
224 }
225
226 if (!hasStaticTouch)
227 {
228 accelScale = 1.f;
229 settled = false;
230 }
231
232
233 if (settled)
234 {
235 //Dampen bodies that are just about to go to sleep
236 if (cf > 1.f)
237 {
238 const PxReal sleepDamping = PXD_SLEEP_DAMPING;
239 const PxReal sleepDampingTimesDT = sleepDamping*dt;
240 const PxReal d = 1.0f - sleepDampingTimesDT;
241 bodyCore.linearVelocity = bodyCore.linearVelocity * d;
242 bodyCore.angularVelocity = bodyCore.angularVelocity * d;
243 accelScale = accelScale * 0.75f + 0.25f*PXD_FREEZE_SCALE;
244 }
245 freeze = originalBody->freezeCount == 0.f && frameNormalizedEnergy < (bodyCore.freezeThreshold * PXD_FREEZE_TOLERANCE);
246 }
247
248 originalBody->accelScale = accelScale;
249
250 const PxU32 wasFrozen = originalBody->mInternalFlags & PxsRigidBody::eFROZEN;
251 PxU16 flags;
252 if(freeze)
253 {
254 //current flag isn't frozen but freeze flag raise so we need to raise the frozen flag in this frame
255 flags = PxU16(PxsRigidBody::eFROZEN);
256 if(!wasFrozen)
257 flags |= PxsRigidBody::eFREEZE_THIS_FRAME;
258 bodyCore.body2World = originalBody->getLastCCDTransform();
259 }
260 else
261 {
262 flags = 0;
263 if(wasFrozen)
264 flags |= PxsRigidBody::eUNFREEZE_THIS_FRAME;
265 }
266 originalBody->mInternalFlags = flags;
267
268 /*KS: New algorithm for sleeping when using stabilization:
269 * Energy *this frame* must be higher than sleep threshold and accumulated energy over previous frames
270 * must be higher than clusterFactor*energyThreshold.
271 */
272 if (wc < wakeCounterResetTime * 0.5f || wc < dt)
273 {
274 //Accumulate energy
275 originalBody->sleepLinVelAcc += sleepLinVelAcc;
276 originalBody->sleepAngVelAcc += sleepAngVelAcc;
277
278 //If energy this frame is high
279 if (frameNormalizedEnergy >= bodyCore.sleepThreshold)
280 {
281 //Compute energy over sleep preparation time
282 const PxReal sleepAngular = originalBody->sleepAngVelAcc.multiply(originalBody->sleepAngVelAcc).dot(inertia) * invMass;
283 const PxReal sleepLinear = originalBody->sleepLinVelAcc.magnitudeSquared();
284 const PxReal normalizedEnergy = 0.5f * (sleepAngular + sleepLinear);
285 const PxReal sleepClusterFactor = PxReal(1u + bodyCore.numCountedInteractions);
286 // scale threshold by cluster factor (more contacts => higher sleep threshold)
287 const PxReal threshold = sleepClusterFactor*bodyCore.sleepThreshold;
288
289 //If energy over sleep preparation time is high
290 if (normalizedEnergy >= threshold)
291 {
292 //Wake up
293 //PX_ASSERT(isActive());
294 originalBody->sleepAngVelAcc = PxVec3(0);
295 originalBody->sleepLinVelAcc = PxVec3(0);
296
297 const float factor = bodyCore.sleepThreshold == 0.f ? 2.0f : PxMin(normalizedEnergy / threshold, 2.0f);
298 PxReal oldWc = wc;
299 wc = factor * 0.5f * wakeCounterResetTime + dt * (sleepClusterFactor - 1.0f);
300 bodyCore.solverWakeCounter = wc;
301 //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
302 // notifyNotReadyForSleeping(bodyCore.nodeIndex);
303
304 if (oldWc == 0.0f)
305 originalBody->mInternalFlags |= PxsRigidBody::eACTIVATE_THIS_FRAME;
306
307 return wc;
308 }
309 }
310 }
311
312 }
313 else
314 {
315 if (wc < wakeCounterResetTime * 0.5f || wc < dt)
316 {
317 const PxTransform& body2World = bodyCore.body2World;
318
319 // calculate normalized energy: kinetic energy divided by mass
320 const PxVec3& t = bodyCore.inverseInertia;
321 const PxVec3 inertia(t.x > 0.f ? 1.0f / t.x : 1.f, t.y > 0.f ? 1.0f / t.y : 1.f, t.z > 0.f ? 1.0f / t.z : 1.f);
322
323 const PxVec3& sleepLinVelAcc = motionVelocity.linear;
324 const PxVec3 sleepAngVelAcc = body2World.q.rotateInv(motionVelocity.angular);
325
326 originalBody->sleepLinVelAcc += sleepLinVelAcc;
327 originalBody->sleepAngVelAcc += sleepAngVelAcc;
328
329 PxReal invMass = bodyCore.inverseMass;
330 if (invMass == 0.f)
331 invMass = 1.f;
332
333 const PxReal angular = originalBody->sleepAngVelAcc.multiply(originalBody->sleepAngVelAcc).dot(inertia) * invMass;
334 const PxReal linear = originalBody->sleepLinVelAcc.magnitudeSquared();
335 const PxReal normalizedEnergy = 0.5f * (angular + linear);
336
337 // scale threshold by cluster factor (more contacts => higher sleep threshold)
338 const PxReal clusterFactor = PxReal(1 + bodyCore.numCountedInteractions);
339 const PxReal threshold = clusterFactor*bodyCore.sleepThreshold;
340
341 if (normalizedEnergy >= threshold)
342 {
343 //PX_ASSERT(isActive());
344 originalBody->sleepLinVelAcc = PxVec3(0);
345 originalBody->sleepAngVelAcc = PxVec3(0);
346 const float factor = threshold == 0.f ? 2.0f : PxMin(normalizedEnergy / threshold, 2.0f);
347 PxReal oldWc = wc;
348 wc = factor * 0.5f * wakeCounterResetTime + dt * (clusterFactor - 1.0f);
349 bodyCore.solverWakeCounter = wc;
350 PxU16 flags = 0;
351 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
352 {
353 flags |= PxsRigidBody::eACTIVATE_THIS_FRAME;
354 //notifyNotReadyForSleeping(bodyCore.nodeIndex);
355 }
356
357 originalBody->mInternalFlags = flags;
358
359 return wc;
360 }
361 }
362 }
363 }
364
365 wc = PxMax(wc - dt, 0.0f);
366 bodyCore.solverWakeCounter = wc;
367 return wc;
368}
369
370PX_FORCE_INLINE void sleepCheck(PxsRigidBody* originalBody, const PxReal dt, const PxReal intDt, const bool enableStabilization, const Cm::SpatialVector& motionVelocity,
371 bool hasStaticTouch)
372{
373 const PxReal wc = _updateWakeCounter(originalBody, dt, intDt, enableStabilization, motionVelocity, hasStaticTouch);
374 const bool wakeCounterZero = (wc == 0.0f);
375
376 if (wakeCounterZero)
377 {
378 //PxsBodyCore& bodyCore = originalBody->getCore();
379 originalBody->mInternalFlags |= PxsRigidBody::eDEACTIVATE_THIS_FRAME;
380 // notifyReadyForSleeping(bodyCore.nodeIndex);
381 originalBody->sleepLinVelAcc = PxVec3(0);
382 originalBody->sleepAngVelAcc = PxVec3(0);
383 }
384}
385
386}
387
388}
389
390#endif
#define PX_FORCE_INLINE
Definition PxPreprocessor.h:335
Sorts an array of objects in ascending order, assuming that the predicate implements the < operator:
Definition PxBoxController.h:39
PX_CUDA_CALLABLE PX_FORCE_INLINE float PxSqrt(float a)
Square root.
Definition PxMath.h:146
PX_CUDA_CALLABLE PX_FORCE_INLINE T PxMax(T a, T b)
The return value is the greater of the two specified values.
Definition PxMath.h:72
PX_CUDA_CALLABLE PX_FORCE_INLINE void PxSinCos(const PxF32 a, PxF32 &sin, PxF32 &cos)
compute sine and cosine at the same time
Definition PxMath.h:202
PX_CUDA_CALLABLE PX_FORCE_INLINE T PxMin(T a, T b)
The return value is the lesser of the two specified values.
Definition PxMath.h:88