Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Suggestion / Idea  Capsule-based collision instead of spherical collision
#1
I've noticed that collision between ropes and other objects only occurs at the particles' centers. Between two connected particles, there is no collision.

I'm guessing the current collision force is something calculated like this:
Code:
var diffAtoB = (bPos - aPos);
var currentDistance = diffAtoB.magnitude;
var minDistance = aRadius+bRadius;

// If the two particles are too far away, do not add forces.
if(currentDistance >= minDistance) return;

var desiredDiffAtoB = diffAtoB * (minDistance / currentDistance)
var relativeVelocityChange = desiredDiffAtoB - diffAtoB;

var massRatio = bMass / (bMass + aMass);
aVel -= relativeVelocityChange * massRatio;
bVel += relativeVelocityChange * (1-massRatio);


However, the code you could use to push two connected particles away from one another is actually fairly similar.

For each pair of segments, you could perform the same math, treating the the two closest points between the two segments as imaginary particles. An imaginary particle would be linear interpolations of position and mass between the two original particles composing the segment. Then after the forces are calculated for the two imaginary particles, they'd similarly be applied to the four original particles:

Code:
// Construct imaginary particles:
var aRatio = closest point on segmentA, expressed as a ratio between segA's start and end positions;
var bRatio = closest point on segmentB, expressed as a ratio between segB's start and end positions;
var aClosest = closest point on segmentA, segA.startPos * aRatio + segA.endPos * (1-aRatio);
var bClosest = closest point on segmentB, segB.startPos * aRatio + segB.endPos * (1-bRatio);
var aMass, bMass, aRadius, bRadius = linear interpolations of other properties.

// Apply the above code to the imaginary particles.
var aVelOffset, bVelOffset = GetParticleCollisionOffsets(imaginary particles' properties listed above);

// Distribute the velocitiy offsets to the original four particles.
segA.startVel += aVelOffset * aRatio;
segA.endVel += aVelOffset * (1-aRatio);
segB.startVel += bVelOffset * bRatio;
segB.endVel += bVelOffset * (1-bRatio);

There may be some considerations about how to distribute the added momentum between the two particles of a line segment if they have different mass, and the force direction isn't perpendicular to the direction of the line segment when the start and endpoints have different radiuses. However, this seems like it'd be a reasonable approximation to start with.
Reply
#2
(22-02-2020, 02:12 AM)Hatchling Wrote: I've noticed that collision between ropes and other objects only occurs at the particles' centers. Between two connected particles, there is no collision.

I'm guessing the current collision force is something calculated like this:
Code:
var diffAtoB = (bPos - aPos);
var currentDistance = diffAtoB.magnitude;
var minDistance = aRadius+bRadius;

// If the two particles are too far away, do not add forces.
if(currentDistance >= minDistance) return;

var desiredDiffAtoB = diffAtoB * (minDistance / currentDistance)
var relativeVelocityChange = desiredDiffAtoB - diffAtoB;

var massRatio = bMass / (bMass + aMass);
aVel -= relativeVelocityChange * massRatio;
bVel += relativeVelocityChange * (1-massRatio);


However, the code you could use to push two connected particles away from one another is actually fairly similar.

For each pair of segments, you could perform the same math, treating the the two closest points between the two segments as imaginary particles. An imaginary particle would be linear interpolations of position and mass between the two original particles composing the segment. Then after the forces are calculated for the two imaginary particles, they'd similarly be applied to the four original particles:

Code:
// Construct imaginary particles:
var aRatio = closest point on segmentA, expressed as a ratio between segA's start and end positions;
var bRatio = closest point on segmentB, expressed as a ratio between segB's start and end positions;
var aClosest = closest point on segmentA, segA.startPos * aRatio + segA.endPos * (1-aRatio);
var bClosest = closest point on segmentB, segB.startPos * aRatio + segB.endPos * (1-bRatio);
var aMass, bMass, aRadius, bRadius = linear interpolations of other properties.

// Apply the above code to the imaginary particles.
var aVelOffset, bVelOffset = GetParticleCollisionOffsets(imaginary particles' properties listed above);

// Distribute the velocitiy offsets to the original four particles.
segA.startVel += aVelOffset * aRatio;
segA.endVel += aVelOffset * (1-aRatio);
segB.startVel += bVelOffset * bRatio;
segB.endVel += bVelOffset * (1-bRatio);

There may be some considerations about how to distribute the added momentum between the two particles of a line segment if they have different mass, and the force direction isn't perpendicular to the direction of the line segment when the start and endpoints have different radiuses. However, this seems like it'd be a reasonable approximation to start with.

Hi Hatchling!

Thanks for the suggestion and the detailed math. We actually had a very similar thing working at some point in development.

We started using the concept of 'simplices': edges (2 particles) and triangles (3 particles). The two main issues with the above approach are: as-is, it only works for edge-edge collisions (for edge-box, edge-sphere, edge-triangle mesh, edge - distance field, etc, you need additional tests), and it complicates the broad phase. In our broad phase we use a simple hierarchical grid. This assumes the size ratio of the objects in the grid is pretty close to 1, but this can't be assumed for edges. Long, thin edges can't be stored in a single grid cell (as it would be too conservative a broad phase, creating lots of contacts that aren't needed), and storing them in multiple cells complicates things.

So we opted for a simpler method, very similar to yours: virtual particles, that were meant to be introduced in v4.0. This basically creates additional particles during the collision detection phase, placed at linearly interpolated positions along the edge between 2 particles. However, the position and number of particles along each edge is fixed, instead of being calculated as the closest point between 2 edges. This is nice because you still only deal with particles: you can leave the broad phase untouched, you don't need additional tests for boxes, triangle meshes, etc, and it covers the case where particles have different radiuses. These virtual particles only exist during collision detection, so they don't affect the constraint projection phase. The only drawback is that you work with a discrete approximation of a capsule, so collision isn't as smooth as it would be with your method.

On paper it sounded nice, but in practice it caused a lot of issues when interpolating position corrections (in PBD we don't work with velocities or impulses, but position deltas) back to the 2 "real" particles in the edge. We ended up removing this system a few weeks before releasing 4.0. I still like the idea though, so there's a chance it will make a comeback some time later.
Reply