@Tomo & z64555: Inelastic collisions with arbitrary CoR, and friction. Maybe I'll omit the friction at this stage if it seems like it's too complicated with it.

@Tomo: No, I don't think I can assume anything like that. Yes, the ground is immobile. And I plan to make it possible for things to "sleep" when they get within some threshold of stationary... eventually.

@z64555: Yes, the collisions will go on forever. When an object falls and hits the ground, it should eventually come to rest.

A little more info about the problem... here's what a contact point looks like:

`struct ContactPoint`

{

struct Part

{

RigidBody* obj;

Vec3 pos, norm; // both are world coords

Part() : obj(NULL), pos(), norm() { }

} a, b;

};

For every

`ContactPoint`, my algorithm will have to come up with a

`Vec3` impulse, which will be applied equally and oppositely to the two objects in contact (unless one of them is immobile). The impulse could hypothetically be decomposed into a component parallel to the contact point's normal vector (restitution) and a component orthogonal to it (friction).

And rather than waste more time explaining with words what my code is doing, I'll explain with code. This is the

`DoCollisionResponse` function:

`void PhysicsWorld::Imp::DoCollisionResponse(const ContactPoint& cp)`

{

RigidBody* ibody = cp.a.obj;

RigidBody* jbody = cp.b.obj;

RigidBody::Imp* iimp = ibody->imp;

RigidBody::Imp* jimp = jbody->imp;

bool j_can_move = jimp->can_move;

float m1 = iimp->mass_info.mass;

float m2 = jimp->mass_info.mass;

if(m1 + m2 > 0)

{

Vec3 i_poi = ibody->GetInvTransform().TransformVec3(cp.a.pos, 1.0f);

Vec3 j_poi = jbody->GetInvTransform().TransformVec3(cp.b.pos, 1.0f);

Vec3 i_v = iimp->GetLocalVelocity(cp.a.pos);

Vec3 j_v = jimp->GetLocalVelocity(cp.b.pos);

Vec3 dv = j_v - i_v;

const Vec3& normal = Vec3::Normalize(cp.a.norm - cp.b.norm);

float nvdot = Vec3::Dot(normal, dv);

if(nvdot < 0.0f)

{

float A, B;

GetUseMass(normal, cp, A, B);

float use_mass = 1.0f / A;

float bounciness = iimp->bounciness * jimp->bounciness;

float impulse_mag = -(1.0f + bounciness) * B * use_mass;

if(impulse_mag < 0)

{

Vec3 impulse = normal * impulse_mag;

if(impulse.ComputeMagnitudeSquared() != 0)

{

ibody->ApplyImpulse(impulse, i_poi);

if(j_can_move)

jbody->ApplyImpulse(-impulse, j_poi);

}

float sfric_coeff = iimp->friction * jimp->friction;

float kfric_coeff = 0.9f * sfric_coeff;

Vec3 t_dv = dv - normal * nvdot;

float t_dv_magsq = t_dv.ComputeMagnitudeSquared();

if(t_dv_magsq > 0.001f) // object is moving; apply kinetic friction

{

float t_dv_mag = sqrtf(t_dv_magsq);

GetUseMass(t_dv / t_dv_mag, cp, A, B);

use_mass = 1.0f / A;

Vec3 fric_impulse = t_dv * min(use_mass, fabs(impulse_mag * kfric_coeff / t_dv_mag));

ibody->ApplyImpulse(fric_impulse, i_poi);

if(j_can_move)

jbody->ApplyImpulse(-fric_impulse, j_poi);

}

else // object isn't moving; apply static friction

{

Vec3 df = jimp->applied_force - iimp->applied_force;

float nfdot = Vec3::Dot(normal, df);

Vec3 t_df = df - normal * nfdot;

float t_df_mag = t_df.ComputeMagnitude();

float fric_i_mag = min(impulse_mag * sfric_coeff, t_df_mag);

if(fric_i_mag > 0)

{

Vec3 fric_impulse = t_df * (-fric_i_mag / t_df_mag);

ibody->ApplyImpulse(fric_impulse, i_poi);

if(j_can_move)

jbody->ApplyImpulse(-fric_impulse, j_poi);

}

}

}

}

}

}

Some explanation of a couple of those other functions:

`void ApplyImpulse(Vec3 impulse, Vec3 position);` "Impulse" is the impulse to be applied, i.e. the change in linear velocity will be this vector divided by the mass. "Position" is the position where I'm applying the impulse (not necessarily on an axis passing through the CoM).

`void GetUseMass(Vec3 direction, ContactPoint cp, float& A, float& B);` Could possibly use a better name; it's for preparing the inputs to

`ApplyImpulse`. "Direction" is the direction of the impulse I want to apply, and "cp" is used for its position relative to the CoMs. After the function completes, "A" and "B" contain values such that the magnitude of the impulse I need to apply is

`-(1.0f + bounciness) * B / A`. If "bounciness" is one, the collision is perfectly ellastic. If "bounciness" is zero, it is perfectly inelastic (I think).

@Nuke: I've already come up with something to deal with the immobility of the ground... when there's a

`ContactPoint` between a mobile body and an immobile one, instead of reusing the same node for all

`ContactPoint`s that immobile

`RigidBody` has, I give that particular

`Edge` a

`NULL other_node` pointer.

`struct Edge`

{

ContactPoint* cp;

ContactPoint::Part* self;

ContactPoint::Part* other;

Node* other_node;

};

struct Node

{

RigidBody* body;

vector<Edge> edges;

};

**Edit:** @z64555: by

Done correctly, after all the impulses are applied, no contact point should have an "inward" relative local velocity.

I mean that if I were to evaluate

`nvdot` (from

`DoCollisionResponse`) again for all of the

`ContactPoint`s, it wouldn't be negative for any of them.

**Edit II:** Well that was easier to solve than I expected.

My old code:

`for(vector<ContactPoint*>::iterator iter = collision_graph.contact_points.begin(); iter != collision_graph.contact_points.end(); ++iter)`

DoCollisionResponse(**iter);

My new code:

`for(int i = 0; i < MAX_SEQUENTIAL_SOLVER_ITERATIONS; ++i)`

{

bool any = false;

for(vector<ContactPoint*>::iterator iter = collision_graph.contact_points.begin(); iter != collision_graph.contact_points.end(); ++iter)

if(DoCollisionResponse(**iter))

any = true;

if(!any)

break;

}

Also

`DoCollisionResponse` now returns a bool, true if

`nvdot` was < 0, false otherwise.

And now it works. Mostly.