aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/LowLevelCloth/src/IterationState.h
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/LowLevelCloth/src/IterationState.h
downloadphysx-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/LowLevelCloth/src/IterationState.h')
-rw-r--r--PhysX_3.4/Source/LowLevelCloth/src/IterationState.h403
1 files changed, 403 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/LowLevelCloth/src/IterationState.h b/PhysX_3.4/Source/LowLevelCloth/src/IterationState.h
new file mode 100644
index 00000000..c9ad9293
--- /dev/null
+++ b/PhysX_3.4/Source/LowLevelCloth/src/IterationState.h
@@ -0,0 +1,403 @@
+// 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.
+
+#pragma once
+
+#include "foundation/PxTransform.h"
+#include "foundation/PxMat44.h"
+#include "Types.h"
+#include "Array.h"
+#include "Simd.h"
+#include "PsMathUtils.h"
+
+namespace physx
+{
+
+/* function object to perform solver iterations on one cloth */
+
+// todo: performance optimization: cache this object and test if velocity/iterDt has changed
+// c'tor takes about 5% of the iteration time of a 20x20 cloth
+
+namespace cloth
+{
+
+/* helper functions */
+
+template <typename T>
+T sqr(const T& x)
+{
+ return x * x;
+}
+
+inline PxVec3 log(const PxQuat& q)
+{
+ float theta = q.getImaginaryPart().magnitude();
+ float scale = theta > PX_EPS_REAL ? PxAsin(theta) / theta : 1.0f;
+ scale = intrinsics::fsel(q.w, scale, -scale);
+ return PxVec3(q.x * scale, q.y * scale, q.z * scale);
+}
+
+inline PxQuat exp(const PxVec3& v)
+{
+ float theta = v.magnitude();
+ float scale = theta > PX_EPS_REAL ? PxSin(theta) / theta : 1.0f;
+ return PxQuat(v.x * scale, v.y * scale, v.z * scale, PxCos(theta));
+}
+
+template <typename Simd4f, uint32_t N>
+inline void assign(Simd4f (&columns)[N], const PxMat44& matrix)
+{
+ for(uint32_t i = 0; i < N; ++i)
+ columns[i] = load(array(matrix[i]));
+}
+
+template <typename Simd4f>
+inline Simd4f transform(const Simd4f (&columns)[3], const Simd4f& vec)
+{
+ return splat<0>(vec) * columns[0] + splat<1>(vec) * columns[1] + splat<2>(vec) * columns[2];
+}
+
+template <typename Simd4f>
+inline Simd4f transform(const Simd4f (&columns)[3], const Simd4f& translate, const Simd4f& vec)
+{
+ return translate + splat<0>(vec) * columns[0] + splat<1>(vec) * columns[1] + splat<2>(vec) * columns[2];
+}
+
+template <typename>
+struct IterationState; // forward declaration
+
+struct IterationStateFactory
+{
+ template <typename MyCloth>
+ IterationStateFactory(MyCloth& cloth, float frameDt);
+
+ template <typename Simd4f, typename MyCloth>
+ IterationState<Simd4f> create(MyCloth const& cloth) const;
+
+ template <typename Simd4f>
+ static Simd4f lengthSqr(Simd4f const& v)
+ {
+ return dot3(v, v);
+ }
+
+ template <typename Simd4f>
+ static PxVec3 castToPxVec3(const Simd4f& v)
+ {
+ return *reinterpret_cast<const PxVec3*>(reinterpret_cast<const char*>(&v));
+ }
+
+ int mNumIterations;
+ float mInvNumIterations;
+ float mIterDt, mIterDtRatio, mIterDtAverage;
+ PxQuat mCurrentRotation;
+ PxVec3 mPrevLinearVelocity;
+ PxVec3 mPrevAngularVelocity;
+};
+
+/* solver iterations helper functor */
+template <typename Simd4f>
+struct IterationState
+{
+ // call after each iteration
+ void update();
+
+ inline float getCurrentAlpha() const;
+ inline float getPreviousAlpha() const;
+
+ public:
+ Simd4f mRotationMatrix[3]; // should rename to 'mRotation'
+
+ Simd4f mCurBias; // in local space
+ Simd4f mPrevBias; // in local space
+ Simd4f mWind; // delta position per iteration
+
+ Simd4f mPrevMatrix[3];
+ Simd4f mCurMatrix[3];
+ Simd4f mDampScaleUpdate;
+
+ // iteration counter
+ uint32_t mRemainingIterations;
+
+ // reciprocal total number of iterations
+ float mInvNumIterations;
+
+ // time step size per iteration
+ float mIterDt;
+
+ bool mIsTurning; // if false, mPositionScale = mPrevMatrix[0]
+};
+
+} // namespace cloth
+
+template <typename Simd4f>
+inline float cloth::IterationState<Simd4f>::getCurrentAlpha() const
+{
+ return getPreviousAlpha() + mInvNumIterations;
+}
+
+template <typename Simd4f>
+inline float cloth::IterationState<Simd4f>::getPreviousAlpha() const
+{
+ return 1.0f - mRemainingIterations * mInvNumIterations;
+}
+
+template <typename MyCloth>
+cloth::IterationStateFactory::IterationStateFactory(MyCloth& cloth, float frameDt)
+{
+ mNumIterations = PxMax(1, int(frameDt * cloth.mSolverFrequency + 0.5f));
+ mInvNumIterations = 1.0f / mNumIterations;
+ mIterDt = frameDt * mInvNumIterations;
+
+ mIterDtRatio = cloth.mPrevIterDt ? mIterDt / cloth.mPrevIterDt : 1.0f;
+ mIterDtAverage = cloth.mIterDtAvg.empty() ? mIterDt : cloth.mIterDtAvg.average();
+
+ mCurrentRotation = cloth.mCurrentMotion.q;
+ mPrevLinearVelocity = cloth.mLinearVelocity;
+ mPrevAngularVelocity = cloth.mAngularVelocity;
+
+ // update cloth
+ float invFrameDt = 1.0f / frameDt;
+ cloth.mLinearVelocity = invFrameDt * (cloth.mTargetMotion.p - cloth.mCurrentMotion.p);
+ PxQuat dq = cloth.mTargetMotion.q * cloth.mCurrentMotion.q.getConjugate();
+ cloth.mAngularVelocity = log(dq) * invFrameDt;
+
+ cloth.mPrevIterDt = mIterDt;
+ cloth.mIterDtAvg.push(static_cast<uint32_t>(mNumIterations), mIterDt);
+ cloth.mCurrentMotion = cloth.mTargetMotion;
+}
+
+/*
+momentum conservation:
+m2*x2 - m1*x1 = m1*x1 - m0*x0 + g*dt2, m = r+t
+r2*x2+t2 = 2(r1*x1+t1) - (r0*x0+t0) + g*dt2
+r2*x2 = r1*x1 + r1*x1 - r0*x0 - (t2-2t1+t0) + g*dt2
+substitue r1*x1 - r0*x0 = r1*(x1-x0) + (r1-r0)*x0
+and r1*x1 = r2*x1 - (r2-r1)*x1
+
+x2 = x1 + r2'*g*dt2
+ + r2'r1*(x1-x0) //< damp
+ + (r2'r1-r2'r0)*x0 - (1-r2'r1)*x1 - r2'*(t2-2t1+t0) //< inertia
+ + (1-r2'r1)x1 + t2-t1 //< drag (not momentum conserving)
+
+x2 = x0 + a0*x0 + a1*x1 + b with
+a0 = (inertia-damp)*r2'r1 - inertia*r2'r0 - eye
+a1 = (1-inertia-drag)*eye + (damp+inertia+drag)*r2'r1
+b = r2'*(g*dt2 - (inertia+drag)*(t2-t1) + inertia*(t1-t0))
+
+Velocities are used to deal with multiple iterations and varying dt. Only b needs
+to updated from one iteration to the next. Specifically, it is multiplied
+by (r2'r1)^1/numIterations. a0 and a1 are unaffected by that multiplication.
+
+The centrifugal and coriolis forces of non-inertial (turning) reference frame are
+not generally captured in these formulas. The 'inertia' term above contains radial
+acceleration plus centrifugal and coriolis force for a single iteration.
+For multiple iterations, or when the centrifugal forces are scaled differently
+than angular inertia, we need to add explicit centrifugal and coriolis forces.
+We only use them to correct the above formula because their discretization is
+not accurate.
+
+Possible improvements: multiply coriolis and centrifugal matrix by curInvRotation
+from the left. Do the alpha trick of linearInertia also for angularInertia, write
+prevParticle after multiplying it with matrix.
+
+If you change anything in this function, make sure that ClothCustomFloating and
+ClothInertia haven't regressed for any choice of solver frequency.
+*/
+
+template <typename Simd4f, typename MyCloth>
+cloth::IterationState<Simd4f> cloth::IterationStateFactory::create(MyCloth const& cloth) const
+{
+ IterationState<Simd4f> result;
+
+ result.mRemainingIterations = static_cast<uint32_t>(mNumIterations);
+ result.mInvNumIterations = mInvNumIterations;
+ result.mIterDt = mIterDt;
+
+ Simd4f curLinearVelocity = load(array(cloth.mLinearVelocity));
+ Simd4f prevLinearVelocity = load(array(mPrevLinearVelocity));
+
+ Simd4f iterDt = simd4f(mIterDt);
+ Simd4f dampExponent = simd4f(cloth.mStiffnessFrequency) * iterDt;
+
+ Simd4f translation = iterDt * curLinearVelocity;
+
+ // gravity delta per iteration
+ Simd4f gravity = load(array(cloth.mGravity)) * static_cast<Simd4f>(simd4f(sqr(mIterDtAverage)));
+
+ // scale of local particle velocity per iteration
+ Simd4f dampScale = exp2(load(array(cloth.mLogDamping)) * dampExponent);
+ // adjust for the change in time step during the first iteration
+ Simd4f firstDampScale = dampScale * simd4f(mIterDtRatio);
+
+ // portion of negative frame velocity to transfer to particle
+ Simd4f linearDrag = (gSimd4fOne - exp2(load(array(cloth.mLinearLogDrag)) * dampExponent)) * translation;
+
+ // portion of frame acceleration to transfer to particle
+ Simd4f linearInertia = load(array(cloth.mLinearInertia)) * iterDt * (prevLinearVelocity - curLinearVelocity);
+
+ // for inertia, we want to violate newton physics to
+ // match velocity and position as given by the user, which means:
+ // vt = v0 + a*t and xt = x0 + v0*t + (!) a*t^2
+ // this is achieved by applying a different portion to cur and prev
+ // position, compared to the normal +0.5 and -0.5 for '... 1/2 a*t^2'.
+ // specifically, the portion is alpha=(n+1)/2n and 1-alpha.
+
+ float linearAlpha = (mNumIterations + 1) * 0.5f * mInvNumIterations;
+ Simd4f curLinearInertia = linearInertia * simd4f(linearAlpha);
+
+ // rotate to local space (use mRotationMatrix temporarily to hold matrix)
+ PxMat44 invRotation(mCurrentRotation.getConjugate());
+ assign(result.mRotationMatrix, invRotation);
+
+ Simd4f maskXYZ = simd4f(simd4i(~0, ~0, ~0, 0));
+
+ // Previously, we split the bias between previous and current position to
+ // get correct disretized position and velocity. However, this made a
+ // hanging cloth experience a downward velocity, which is problematic
+ // when scaled by the iterDt ratio and results in jitter under variable
+ // timesteps. Instead, we now apply the entire bias to current position
+ // and accept a less noticeable error for a free falling cloth.
+
+ Simd4f bias = gravity - linearDrag;
+ result.mCurBias = transform(result.mRotationMatrix, curLinearInertia + bias) & maskXYZ;
+ result.mPrevBias = transform(result.mRotationMatrix, linearInertia - curLinearInertia) & maskXYZ;
+
+ Simd4f wind = load(array(cloth.mWind)) * iterDt;
+ result.mWind = transform(result.mRotationMatrix, translation - wind) & maskXYZ;
+
+ result.mIsTurning = mPrevAngularVelocity.magnitudeSquared() + cloth.mAngularVelocity.magnitudeSquared() > 0.0f;
+
+ if(result.mIsTurning)
+ {
+ Simd4f curAngularVelocity = load(array(invRotation.rotate(cloth.mAngularVelocity)));
+ Simd4f prevAngularVelocity = load(array(invRotation.rotate(mPrevAngularVelocity)));
+
+ // rotation for one iteration in local space
+ Simd4f curInvAngle = -iterDt * curAngularVelocity;
+ Simd4f prevInvAngle = -iterDt * prevAngularVelocity;
+
+ PxQuat curInvRotation = exp(castToPxVec3(curInvAngle));
+ PxQuat prevInvRotation = exp(castToPxVec3(prevInvAngle));
+
+ PxMat44 curMatrix(curInvRotation);
+ PxMat44 prevMatrix(prevInvRotation * curInvRotation);
+
+ assign(result.mRotationMatrix, curMatrix);
+
+ Simd4f angularDrag = gSimd4fOne - exp2(load(array(cloth.mAngularLogDrag)) * dampExponent);
+ Simd4f centrifugalInertia = load(array(cloth.mCentrifugalInertia));
+ Simd4f angularInertia = load(array(cloth.mAngularInertia));
+ Simd4f angularAcceleration = curAngularVelocity - prevAngularVelocity;
+
+ Simd4f epsilon = simd4f(PxSqrt(FLT_MIN)); // requirement: sqr(epsilon) > 0
+ Simd4f velocityLengthSqr = lengthSqr(curAngularVelocity) + epsilon;
+ Simd4f dragLengthSqr = lengthSqr(Simd4f(curAngularVelocity * angularDrag)) + epsilon;
+ Simd4f centrifugalLengthSqr = lengthSqr(Simd4f(curAngularVelocity * centrifugalInertia)) + epsilon;
+ Simd4f accelerationLengthSqr = lengthSqr(angularAcceleration) + epsilon;
+ Simd4f inertiaLengthSqr = lengthSqr(Simd4f(angularAcceleration * angularInertia)) + epsilon;
+
+ float dragScale = array(rsqrt(velocityLengthSqr * dragLengthSqr) * dragLengthSqr)[0];
+ float inertiaScale =
+ mInvNumIterations * array(rsqrt(accelerationLengthSqr * inertiaLengthSqr) * inertiaLengthSqr)[0];
+
+ // magic factor found by comparing to global space simulation:
+ // some centrifugal force is in inertia part, remainder is 2*(n-1)/n
+ // after scaling the inertia part, we get for centrifugal:
+ float centrifugalAlpha = (2 * mNumIterations - 1) * mInvNumIterations;
+ float centrifugalScale =
+ centrifugalAlpha * array(rsqrt(velocityLengthSqr * centrifugalLengthSqr) * centrifugalLengthSqr)[0] -
+ inertiaScale;
+
+ // slightly better in ClothCustomFloating than curInvAngle alone
+ Simd4f centrifugalVelocity = (prevInvAngle + curInvAngle) * simd4f(0.5f);
+ const Simd4f data = lengthSqr(centrifugalVelocity);
+ float centrifugalSqrLength = array(data)[0] * centrifugalScale;
+
+ Simd4f coriolisVelocity = centrifugalVelocity * simd4f(centrifugalScale);
+ PxMat33 coriolisMatrix = shdfnd::star(castToPxVec3(coriolisVelocity));
+
+ const float* dampScalePtr = array(firstDampScale);
+ const float* centrifugalPtr = array(centrifugalVelocity);
+
+ for(unsigned int j = 0; j < 3; ++j)
+ {
+ float centrifugalJ = -centrifugalPtr[j] * centrifugalScale;
+ for(unsigned int i = 0; i < 3; ++i)
+ {
+ float damping = dampScalePtr[j];
+ float coriolis = coriolisMatrix(i, j);
+ float centrifugal = centrifugalPtr[i] * centrifugalJ;
+
+ prevMatrix(i, j) = centrifugal - coriolis + curMatrix(i, j) * (inertiaScale - damping) -
+ prevMatrix(i, j) * inertiaScale;
+ curMatrix(i, j) = centrifugal + coriolis + curMatrix(i, j) * (inertiaScale + damping + dragScale);
+ }
+ curMatrix(j, j) += centrifugalSqrLength - inertiaScale - dragScale;
+ prevMatrix(j, j) += centrifugalSqrLength;
+ }
+
+ assign(result.mPrevMatrix, prevMatrix);
+ assign(result.mCurMatrix, curMatrix);
+ }
+ else
+ {
+ Simd4f minusOne = -static_cast<Simd4f>(gSimd4fOne);
+ result.mRotationMatrix[0] = minusOne;
+ result.mPrevMatrix[0] = select(maskXYZ, firstDampScale, minusOne);
+ }
+
+ // difference of damp scale between first and other iterations
+ result.mDampScaleUpdate = (dampScale - firstDampScale) & maskXYZ;
+
+ return result;
+}
+
+template <typename Simd4f>
+void cloth::IterationState<Simd4f>::update()
+{
+ if(mIsTurning)
+ {
+ // only need to turn bias, matrix is unaffected (todo: verify)
+ mCurBias = transform(mRotationMatrix, mCurBias);
+ mPrevBias = transform(mRotationMatrix, mPrevBias);
+ mWind = transform(mRotationMatrix, mWind);
+ }
+
+ // remove time step ratio in damp scale after first iteration
+ for(uint32_t i = 0; i < 3; ++i)
+ {
+ mPrevMatrix[i] = mPrevMatrix[i] - mRotationMatrix[i] * mDampScaleUpdate;
+ mCurMatrix[i] = mCurMatrix[i] + mRotationMatrix[i] * mDampScaleUpdate;
+ }
+ mDampScaleUpdate = gSimd4fZero; // only once
+
+ --mRemainingIterations;
+}
+
+} // namespace physx