Custom collision response

I have created game objects where i represent the geometry with signed distance functions (SDF) (the SDFs are voxelized/discretized, unimportant for this question). But I would also like to use these SDF for the physics part.
I use these SDFs to find intersections between other SDFs and also regular colliders, resulting in intersection points and normals.
Mass and inertia tensors are also calculated from the SDFs. (I convert the inertia tensor to inertia vector and rotation for the Rigidbody component)
Together with rigidbody velocity, these are all the variables needed to calculate an impulse force (equation 5 from Collision response - Wikipedia)

The problem is however that the equation uses inertia tensor matrices (3x3).
I have that for my SDFs game objects, but for regular colliders I only have the inertia vector and rotation which as far as I can find, no one knows how to convert back to a tensor.
The only solution I can think of is to manually calculate inertia tensor matrices for all game objects with regular colliders which I hope to avoid.

Have I misunderstood something? Is calculating an impulse force the way to go? How can I then achieve that?
Sorry for the vague questions…

Some more details
All Game objects have rigidbodies (with proper mass and inertia).
Some game objects have a set of regular colliders (as usual)
Some game objects instead have a set of SDF, each SDF is bounded by a box collider (trigger). In the trigger callbacks I query the SDF for the actual intersection data. Intersection data for each game object collision pair is accumulated and saved, then in FixedUpdate an impulse force is applied on both rigid bodies involved in the collision.

Found a paper which is kind of related

In chapter 5.2 the response force is calculated as just the “amount” of overlap, scaling the force with larger overlap. I cant see how this will work since it would break conservation of energy and momentum.

1 Like

To answer my own question about how to calculate the impulse collision force in case I can help others.
The key is that the inertia vector and rotation can be used in this way:
http://answers.unity.com/answers/49388/view.html

This is my code at the moment, still some work to do but it seems to be working as expected.

//Applies impulse force to a collision pair, the position and normal are in rb coordinate system
        private static void AddImpulseForce(float3 position, float3 normal, Rigidbody rb, Rigidbody otherRb,
            PhysicMaterial material, PhysicMaterial otherMaterial)
        {
            Vector3 collisionPointWorld = rb.transform.TransformPoint(position);
            Vector3 normalWorld = rb.transform.TransformDirection(normal);
            //DebugExtension.DebugPoint(collisionPointWorld, Color.red, 4, 1);

            Vector3 v1 = rb.GetPointVelocity(collisionPointWorld);
            Vector3 v2 = otherRb.GetPointVelocity(collisionPointWorld);
            Vector3 r1 = collisionPointWorld - rb.worldCenterOfMass;
            Vector3 r2 = collisionPointWorld - otherRb.worldCenterOfMass;
            Vector3 deltaV = v2 - v1;
            float e = material.bounciness; //TODO handle other material combination, otherMaterial.bounceCombine
            float friction = material.dynamicFriction; //TODO handle other material combination, otherMaterial.frictionCombine
         
            //Normal force
            float jNormal = GetImpulseMagnitude(rb, otherRb, normalWorld, r1, r2, e, deltaV);
            if (jNormal < 0)
                return;
            Vector3 normalForce = normalWorld * jNormal;
            rb.AddForceAtPosition(-normalForce, collisionPointWorld, ForceMode.Impulse);
            otherRb.AddForceAtPosition(normalForce, collisionPointWorld, ForceMode.Impulse);

            //Debug.DrawLine(collisionPointWorld, collisionPointWorld + normalForce, Color.magenta, 2.0f);
            //Debug.Log(normalForce.magnitude);

            //Friction force
            Vector3 tangent = Vector3.Normalize(deltaV - normalWorld * Vector3.Dot(deltaV, normalWorld));
            float jTangent = GetImpulseMagnitude(rb, otherRb, tangent, r1, r2, e, deltaV);
            jTangent = math.clamp(jTangent, -friction * jNormal, friction * jNormal);
            Vector3 frictionForce = tangent * jTangent;
            rb.AddForceAtPosition(-frictionForce, collisionPointWorld, ForceMode.Impulse);
            otherRb.AddForceAtPosition(frictionForce, collisionPointWorld, ForceMode.Impulse);

            //Debug.DrawLine(collisionPointWorld, collisionPointWorld + frictionForce, Color.cyan, 2.0f);
            //Debug.Log(frictionForce.magnitude);
        }

        private static float GetImpulseMagnitude(Rigidbody rb, Rigidbody otherRb, Vector3 dir, Vector3 r1, Vector3 r2,
            float e, Vector3 deltaV)
        {
            float m1Inv = rb.isKinematic ? 0 : 1 / rb.mass;
            float m2Inv = otherRb.isKinematic ? 0 : 1 / otherRb.mass;
            Vector3 w1 = Vector3.Cross(r1, dir);
            Vector3 w2 = Vector3.Cross(r2, dir);
            Quaternion q1 = rb.rotation * rb.inertiaTensorRotation;
            Quaternion q2 = otherRb.rotation * otherRb.inertiaTensorRotation;
            Vector3 t1 = q1 * Vector3.Scale(rb.isKinematic ? Vector3.zero : rb.inertiaTensor.Invert(),
                (Quaternion.Inverse(q1) * w1));
            Vector3 t2 = q2 * Vector3.Scale(otherRb.isKinematic ? Vector3.zero : otherRb.inertiaTensor.Invert(),
                (Quaternion.Inverse(q2) * w2));
            float j = -(1 + e) * Vector3.Dot(deltaV, dir) /
                      (m1Inv + m2Inv + Vector3.Dot(Vector3.Cross(t1, r1) + Vector3.Cross(t2, r2), dir));
            return j;
        }

edit:
The Vector3 inverse is just element wise inverse:

public static Vector3 Invert(this Vector3 vec)
{
    return new Vector3(1 / vec.x, 1 / vec.y, 1 / vec.z);
}
2 Likes

This is extremely interesting, thank you for sharing!