There are 2 issues here:

1) time of impact

2) tunneling (fast moving sphere passing through triangles)

I will not go into #1, in many practical situations you can ignore it. Collision response will be less accurate, but hardly noticeable in most situations. I also have no idea how to implement swept sphere collision correctly, some else might explain it.

There is a workaround for #2 however, usually sufficient for most games; after you've integrated the sphere position, do a ray-test from the old sphere position to the new position. Then, whenever the sphere center has passed the triangle plane, you can project an approximate contact point onto the triangle, and move the sphere back so it no longer intersects. Unless your trimesh has lots of concave features such as 'v-cliffs' or other tight spots, this should work fine. If it does, you can do another ray-test when correcting the sphere position (recursively; every time you apply a correction). It should never be able to pass through triangles, but remember to add a max recursion or you may get stuck in an infinite loop (3-5 is usually ok) in tight situations.

One thing I would suggest though, is to collect all sphere-triangle intersection points, before correcting the sphere position. Not just the deepest contact intersection, but every single sphere-triangle intersection point. Store them in an array, something like this:

struct contact

{

float3 position; // intersection point on triangle surface

float3 normal; // triangle normal

float depth; // penetration depth

int faceIndex; // index of trimesh face (useful to look up surface info such as bounce, friction etc)

};

Some raw trimesh-sphere col-det code that may give you some ideas:

// collides collider with sphere

void CCollider::SphereTest(const float3 &pos, float radius, CContactGroup &contacts) const

{

// quick bounds test

if (!bounds.QuickSphereTest(pos, radius)) return;

// test faces

for (int i=0; i<facenum; i++)

{

const float3 &v1 = vert[ face[i].v1 ];

const float3 &v2 = vert[ face[i].v2 ];

const float3 &v3 = vert[ face[i].v3 ];

const float3 &fn = face[i].n;

// backface culling

if (DotProduct(fn, pos - v1 ) < 0.0) continue;

// get closest point on triangle

const float3 cp = ClosestPointTriangle(pos, v1, v2, v3);

// dist to point

const float depth = Distance(pos,cp) - radius;

// no penetration

if (depth > 0.0) continue;

// create sphere contact

contact c;

c.pos = cp;

c.norm = Normalize(pos - cp);

c.depth = -depth;

c.collider = this;

c.face = i;

c.id = face[i].id;

contacts.Add( c );

}

}

Then once you've collided the sphere against every trimesh and other spheres in your world, you can filter out double/similar contacts. Loop through the contacts, and compute the average of contacts that lie on the same plane/position (don't forget to re-normalize the contact normal).

For example when a sphere rests on the center of flat quad (consisting of two triangles), the sphere will intersect both edges, resulting into two contacts. Both contacts will have the exact same position, but contact normal (and depth) may be diverging. If you merge these, it will result into a single contact with a normal facing straight up.

Once your contacts are all cleaned up, you can correct (solve) the sphere intersections all at once.

And this code shows how the contacts are solved. 'pos' is the position of the sphere, and 'vel' is it's motion vector.

void Constrain(const float3 &plane, float3 &force)

{

const float dp = MAX( DotProduct(-plane,force) ,0.0);

return force += plane * dp;

}

// solves constraints

void CContactGroup::Solve(float3 &pos, float3 &vel) const

{

for (int i=0; i<contacts.size(); i++)

{

const contact &c = contacts[i];

// correct the sphere position

pos += c.norm * c.depth;

// correct the motion vector as well

Constrain( c.norm, vel );

}

}

Notice that, as well as the sphere's position, you need to 'correct' the velocity vector. Otherwise gravity force applied will increase the velocity each timestep, even though the sphere is not moving. Perhaps this is related to the problem you mentioned.

In the code above, the motion vector is simply 'zeroed out' in the contact normal direction, but you can easily add friction and 'bounce' effect.

Hope that helps.