aboutsummaryrefslogtreecommitdiff
path: root/NvCloth/src/SwCollision.cpp
diff options
context:
space:
mode:
authorMarijn Tamis <[email protected]>2017-10-20 14:30:56 +0200
committerMarijn Tamis <[email protected]>2017-10-20 14:36:12 +0200
commitfabb251458f4a2d6d4f87dd36038fac2774b378c (patch)
tree68a4a0ecd940dc949e0477d521d8c159968cfcd5 /NvCloth/src/SwCollision.cpp
parentNvCloth 1.1.2 Release. (22576033) (diff)
downloadnvcloth-1.1.3.tar.xz
nvcloth-1.1.3.zip
NvCloth 1.1.3 Release. (23014067)v1.1.3
Diffstat (limited to 'NvCloth/src/SwCollision.cpp')
-rw-r--r--NvCloth/src/SwCollision.cpp85
1 files changed, 64 insertions, 21 deletions
diff --git a/NvCloth/src/SwCollision.cpp b/NvCloth/src/SwCollision.cpp
index 89df8a5..0aa196d 100644
--- a/NvCloth/src/SwCollision.cpp
+++ b/NvCloth/src/SwCollision.cpp
@@ -40,6 +40,7 @@
using namespace nv;
using namespace physx;
+using namespace cloth;
// the particle trajectory needs to penetrate more than 0.2 * radius to trigger continuous collision
template <typename T4f>
@@ -160,31 +161,41 @@ void generateCones(cloth::ConeData* dst, const cloth::SphereData* sourceSpheres,
cloth::ConeData* cIt = dst;
for (const cloth::IndexPair* iIt = capsuleIndices, *iEnd = iIt + numCones; iIt != iEnd; ++iIt, ++cIt)
{
+ // w element contains sphere radii
PxVec4 first = reinterpret_cast<const PxVec4&>(sourceSpheres[iIt->first]);
PxVec4 second = reinterpret_cast<const PxVec4&>(sourceSpheres[iIt->second]);
PxVec4 center = (second + first) * 0.5f;
- PxVec4 axis = (second - first) * 0.5f;
+ PxVec4 axis = (second - first) * 0.5f; //half axis
+ //axiw.w = half of radii difference
- float sqrAxisLength = axis.x * axis.x + axis.y * axis.y + axis.z * axis.z;
- float sqrConeLength = sqrAxisLength - cloth::sqr(axis.w);
+ // |Axis|^2
+ float sqrAxisHalfLength = axis.x * axis.x + axis.y * axis.y + axis.z * axis.z;
- float invAxisLength = 1 / sqrtf(sqrAxisLength);
- float invConeLength = 1 / sqrtf(sqrConeLength);
+ // http://jwilson.coe.uga.edu/emt669/Student.Folders/Kertscher.Jeff/Essay.3/Tangents.html
+ // |Axis|^2 = |Cone|^2 + (sphere2Radius-sphere1Radius)^2
+ float sqrConeHalfLength = sqrAxisHalfLength - cloth::sqr(axis.w);
- if (sqrConeLength <= 0.0f)
- invAxisLength = invConeLength = 0.0f;
+ float invAxisHalfLength = 1 / sqrtf(sqrAxisHalfLength);
+ float invConeHalfLength = 1 / sqrtf(sqrConeHalfLength);
- float axisLength = sqrAxisLength * invAxisLength;
- float slope = axis.w * invConeLength;
+ if (sqrConeHalfLength <= 0.0f)
+ invAxisHalfLength = invConeHalfLength = 0.0f;
+
+ float axisHalfLength = sqrAxisHalfLength * invAxisHalfLength;
+ float slope = axis.w * invConeHalfLength;
cIt->center = PxVec3(center.x, center.y, center.z );
- cIt->radius = (axis.w + first.w) * invConeLength * axisLength;
- cIt->axis = PxVec3(axis.x, axis.y, axis.z) * invAxisLength;
+ cIt->radius = (axis.w + first.w) * invConeHalfLength * axisHalfLength; //cone radius in the center
+ cIt->axis = PxVec3(axis.x, axis.y, axis.z) * invAxisHalfLength;
cIt->slope = slope;
- cIt->sqrCosine = 1.0f - cloth::sqr(axis.w * invAxisLength);
- cIt->halfLength = axisLength;
+ // cos()^2 = 1.0 - (radius difference / axis length)^2
+ // cos()^2 = 1.0 - (opposite/hypotenuse)^2
+ // cos()^2 = 1.0 - sin(angle between c2c1 and c2t1)^2
+ // cos()^2 = 1.0 - sin(angle between axis and c2t1)^2
+ cIt->sqrCosine = 1.0f - cloth::sqr(axis.w * invAxisHalfLength);
+ cIt->halfLength = axisHalfLength;
uint32_t firstMask = 0x1u << iIt->first;
cIt->firstMask = firstMask;
@@ -407,12 +418,14 @@ void cloth::SwCollision<T4f>::buildSphereAcceleration(const SphereData* sIt)
{
static const int maxIndex = sGridSize - 1;
+ uint32_t mask = 0x1; //single bit mask for current sphere
const SphereData* sEnd = sIt + mClothData.mNumSpheres;
- for (uint32_t mask = 0x1; sIt != sEnd; ++sIt, mask <<= 1)
+ for (; sIt != sEnd; ++sIt, mask <<= 1)
{
T4f sphere = loadAligned(array(sIt->center));
T4f radius = splat<3>(sphere);
+ //calculate the first and last cell index, for each axis, that contains the sphere
T4i first = intFloor(max((sphere - radius) * mGridScale + mGridBias, gSimd4fZero));
T4i last = intFloor(min((sphere + radius) * mGridScale + mGridBias, sGridLength));
@@ -422,11 +435,14 @@ void cloth::SwCollision<T4f>::buildSphereAcceleration(const SphereData* sIt)
uint32_t* firstIt = reinterpret_cast<uint32_t*>(mSphereGrid);
uint32_t* lastIt = firstIt + 3 * sGridSize;
+ //loop through the 3 axes
for (uint32_t i = 0; i < 3; ++i, firstIt += sGridSize, lastIt += sGridSize)
{
+ //mark the sphere and everything to the right
for (int j = firstIdx[i]; j <= maxIndex; ++j)
firstIt[j] |= mask;
+ //mark the sphere and everything to the left
for (int j = lastIdx[i]; j >= 0; --j)
lastIt[j] |= mask;
}
@@ -469,17 +485,23 @@ void cloth::SwCollision<T4f>::mergeAcceleration(uint32_t* firstIt)
template <typename T4f>
bool cloth::SwCollision<T4f>::buildAcceleration()
{
- // determine sphere bbox
+ // determine single bounding box around all spheres
BoundingBox<T4f> sphereBounds =
expandBounds(emptyBounds<T4f>(), mCurData.mSpheres, mCurData.mSpheres + mClothData.mNumSpheres);
+
+ // determine single bounding box around all particles
BoundingBox<T4f> particleBounds = loadBounds<T4f>(mClothData.mCurBounds);
+
if (mClothData.mEnableContinuousCollision)
{
+ // extend bounds to include movement from previous frame
sphereBounds = expandBounds(sphereBounds, mPrevData.mSpheres, mPrevData.mSpheres + mClothData.mNumSpheres);
particleBounds = expandBounds(particleBounds, loadBounds<T4f>(mClothData.mPrevBounds));
}
BoundingBox<T4f> bounds = intersectBounds(sphereBounds, particleBounds);
+
+ // no collision checks needed if the intersection between particle bounds and sphere bounds is empty
T4f edgeLength = (bounds.mUpper - bounds.mLower) & ~static_cast<T4f>(sMaskW);
if (!allGreaterEqual(edgeLength, gSimd4fZero))
return false;
@@ -490,6 +512,7 @@ bool cloth::SwCollision<T4f>::buildAcceleration()
const T4f expandedEdgeLength = max(expandedUpper - expandedLower, gSimd4fEpsilon);
// make grid minimal thickness and strict upper bound of spheres
+ // grid maps bounds to 0-7 space (sGridLength =~= 8)
mGridScale = sGridLength * recip<1>(expandedEdgeLength);
mGridBias = -expandedLower * mGridScale;
array(mGridBias)[3] = 1.0f; // needed for collideVirtualParticles()
@@ -655,8 +678,8 @@ struct cloth::SwCollision<T4f>::ImpulseAccumulator
mNumCollisions = mNumCollisions + (gSimd4fOne & mask);
}
- T4f mDeltaX, mDeltaY, mDeltaZ;
- T4f mVelX, mVelY, mVelZ;
+ T4f mDeltaX, mDeltaY, mDeltaZ; //depenetration delta
+ T4f mVelX, mVelY, mVelZ; //frame offset of the collision shape (velocity * dt)
T4f mNumCollisions;
};
@@ -684,12 +707,15 @@ FORCE_INLINE void cloth::SwCollision<T4f>::collideSpheres(const T4i& sphereMask,
T4f sqrDistance = gSimd4fEpsilon + deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
T4f negativeScale = gSimd4fOne - rsqrt(sqrDistance) * splat<3>(sphere);
+ // negativeScale = 1 - radius/|position-sphere|
T4f contactMask;
if (!anyGreater(gSimd4fZero, negativeScale, contactMask))
continue;
accum.subtract(deltaX, deltaY, deltaZ, negativeScale, contactMask);
+ // -= delta * negativeScale
+ // = delta - delta * radius/|position-sphere|
if (frictionEnabled)
{
@@ -730,10 +756,13 @@ cloth::SwCollision<T4f>::collideCones(const T4f* __restrict positions, ImpulseAc
T4f center = loadAligned(centerPtr, offset);
+ // offset from center of cone to particle
+ // delta = pos - center
T4f deltaX = positions[0] - splat<0>(center);
T4f deltaY = positions[1] - splat<1>(center);
T4f deltaZ = positions[2] - splat<2>(center);
+ //axis of the cone
T4f axis = loadAligned(axisPtr, offset);
T4f axisX = splat<0>(axis);
@@ -741,12 +770,16 @@ cloth::SwCollision<T4f>::collideCones(const T4f* __restrict positions, ImpulseAc
T4f axisZ = splat<2>(axis);
T4f slope = splat<3>(axis);
+ // project delta onto axis
T4f dot = deltaX * axisX + deltaY * axisY + deltaZ * axisZ;
+ // interpolate radius
T4f radius = dot * slope + splat<3>(center);
// set radius to zero if cone is culled
radius = max(radius, gSimd4fZero) & ~culled;
+ // distance to axis
+ // sqrDistance = |delta|^2 - |dot|^2
T4f sqrDistance = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ - dot * dot;
T4i auxiliary = loadAligned(auxiliaryPtr, offset);
@@ -765,6 +798,8 @@ cloth::SwCollision<T4f>::collideCones(const T4f* __restrict positions, ImpulseAc
sqrDistance = max(sqrDistance, gSimd4fEpsilon);
T4f invDistance = rsqrt(sqrDistance);
+
+ //offset base to take slope in to account
T4f base = dot + slope * sqrDistance * invDistance;
// force left/rightMask to false if not inside cone
@@ -780,6 +815,7 @@ cloth::SwCollision<T4f>::collideCones(const T4f* __restrict positions, ImpulseAc
shapeMask.mSpheres = shapeMask.mSpheres & ~(firstMask & ~leftMask);
shapeMask.mSpheres = shapeMask.mSpheres & ~(secondMask & ~rightMask);
+ //contact normal direction
deltaX = deltaX - base * axisX;
deltaY = deltaY - base * axisY;
deltaZ = deltaZ - base * axisZ;
@@ -1173,7 +1209,9 @@ PX_INLINE void calculateFrictionImpulse(const T4f& deltaX, const T4f& deltaY, co
T4f ny = deltaY * rcpDelta;
T4f nz = deltaZ * rcpDelta;
- // calculate relative velocity scaled by number of collisions
+ // calculate relative velocity
+ // velXYZ is scaled by one over the number of collisions since all collisions accumulate into
+ // that variable during collision detection
T4f rvx = curPos[0] - prevPos[0] - velX * scale;
T4f rvy = curPos[1] - prevPos[1] - velY * scale;
T4f rvz = curPos[2] - prevPos[2] - velZ * scale;
@@ -1186,7 +1224,7 @@ PX_INLINE void calculateFrictionImpulse(const T4f& deltaX, const T4f& deltaY, co
T4f rvty = rvy - rvn * ny;
T4f rvtz = rvz - rvn * nz;
- // calculate magnitude of vt
+ // calculate magnitude of relative tangential velocity
T4f rcpVt = rsqrt(rvtx * rvtx + rvty * rvty + rvtz * rvtz + gSimd4fEpsilon);
// magnitude of friction impulse (cannot be greater than -vt)
@@ -1206,7 +1244,7 @@ void cloth::SwCollision<T4f>::collideParticles()
const T4f massScale = simd4f(mClothData.mCollisionMassScale);
const bool frictionEnabled = mClothData.mFrictionScale > 0.0f;
- const T4f frictionScale = simd4f(mClothData.mFrictionScale);
+ const T4f frictionScale = simd4f(mClothData.mFrictionScale); //[arameter set by user
T4f curPos[4];
T4f prevPos[4];
@@ -1214,16 +1252,20 @@ void cloth::SwCollision<T4f>::collideParticles()
float* __restrict prevIt = mClothData.mPrevParticles;
float* __restrict pIt = mClothData.mCurParticles;
float* __restrict pEnd = pIt + mClothData.mNumParticles * 4;
+ //loop over particles 4 at a time
for (; pIt < pEnd; pIt += 16, prevIt += 16)
{
curPos[0] = loadAligned(pIt, 0);
curPos[1] = loadAligned(pIt, 16);
curPos[2] = loadAligned(pIt, 32);
curPos[3] = loadAligned(pIt, 48);
- transpose(curPos[0], curPos[1], curPos[2], curPos[3]);
+ transpose(curPos[0], curPos[1], curPos[2], curPos[3]); //group values by axis in simd structure
ImpulseAccumulator accum;
+
+ //first collide cones
T4i sphereMask = collideCones(curPos, accum);
+ //pass on hit mask to ignore sphere parts that are inside the cones
collideSpheres(sphereMask, curPos, accum);
T4f mask;
@@ -1267,6 +1309,7 @@ void cloth::SwCollision<T4f>::collideParticles()
curPos[3] = select(mask, curPos[3] * scale, curPos[3]);
}
+ //apply average de-penetration delta
curPos[0] = curPos[0] + accum.mDeltaX * invNumCollisions;
curPos[1] = curPos[1] + accum.mDeltaY * invNumCollisions;
curPos[2] = curPos[2] + accum.mDeltaZ * invNumCollisions;