Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question Custom collision response

Discussion in 'Physics' started by felbj694, Apr 8, 2021.

  1. felbj694

    felbj694

    Joined:
    Oct 23, 2016
    Posts:
    35
    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 https://en.wikipedia.org/wiki/Collision_response)

    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
    https://core.ac.uk/download/pdf/295592184.pdf
    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.
     
    seboverflow likes this.
  2. felbj694

    felbj694

    Joined:
    Oct 23, 2016
    Posts:
    35
    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.
    Code (CSharp):
    1. //Applies impulse force to a collision pair, the position and normal are in rb coordinate system
    2.         private static void AddImpulseForce(float3 position, float3 normal, Rigidbody rb, Rigidbody otherRb,
    3.             PhysicMaterial material, PhysicMaterial otherMaterial)
    4.         {
    5.             Vector3 collisionPointWorld = rb.transform.TransformPoint(position);
    6.             Vector3 normalWorld = rb.transform.TransformDirection(normal);
    7.             //DebugExtension.DebugPoint(collisionPointWorld, Color.red, 4, 1);
    8.  
    9.             Vector3 v1 = rb.GetPointVelocity(collisionPointWorld);
    10.             Vector3 v2 = otherRb.GetPointVelocity(collisionPointWorld);
    11.             Vector3 r1 = collisionPointWorld - rb.worldCenterOfMass;
    12.             Vector3 r2 = collisionPointWorld - otherRb.worldCenterOfMass;
    13.             Vector3 deltaV = v2 - v1;
    14.             float e = material.bounciness; //TODO handle other material combination, otherMaterial.bounceCombine
    15.             float friction = material.dynamicFriction; //TODO handle other material combination, otherMaterial.frictionCombine
    16.          
    17.             //Normal force
    18.             float jNormal = GetImpulseMagnitude(rb, otherRb, normalWorld, r1, r2, e, deltaV);
    19.             if (jNormal < 0)
    20.                 return;
    21.             Vector3 normalForce = normalWorld * jNormal;
    22.             rb.AddForceAtPosition(-normalForce, collisionPointWorld, ForceMode.Impulse);
    23.             otherRb.AddForceAtPosition(normalForce, collisionPointWorld, ForceMode.Impulse);
    24.  
    25.             //Debug.DrawLine(collisionPointWorld, collisionPointWorld + normalForce, Color.magenta, 2.0f);
    26.             //Debug.Log(normalForce.magnitude);
    27.  
    28.             //Friction force
    29.             Vector3 tangent = Vector3.Normalize(deltaV - normalWorld * Vector3.Dot(deltaV, normalWorld));
    30.             float jTangent = GetImpulseMagnitude(rb, otherRb, tangent, r1, r2, e, deltaV);
    31.             jTangent = math.clamp(jTangent, -friction * jNormal, friction * jNormal);
    32.             Vector3 frictionForce = tangent * jTangent;
    33.             rb.AddForceAtPosition(-frictionForce, collisionPointWorld, ForceMode.Impulse);
    34.             otherRb.AddForceAtPosition(frictionForce, collisionPointWorld, ForceMode.Impulse);
    35.  
    36.             //Debug.DrawLine(collisionPointWorld, collisionPointWorld + frictionForce, Color.cyan, 2.0f);
    37.             //Debug.Log(frictionForce.magnitude);
    38.         }
    39.  
    40.         private static float GetImpulseMagnitude(Rigidbody rb, Rigidbody otherRb, Vector3 dir, Vector3 r1, Vector3 r2,
    41.             float e, Vector3 deltaV)
    42.         {
    43.             float m1Inv = rb.isKinematic ? 0 : 1 / rb.mass;
    44.             float m2Inv = otherRb.isKinematic ? 0 : 1 / otherRb.mass;
    45.             Vector3 w1 = Vector3.Cross(r1, dir);
    46.             Vector3 w2 = Vector3.Cross(r2, dir);
    47.             Quaternion q1 = rb.rotation * rb.inertiaTensorRotation;
    48.             Quaternion q2 = otherRb.rotation * otherRb.inertiaTensorRotation;
    49.             Vector3 t1 = q1 * Vector3.Scale(rb.isKinematic ? Vector3.zero : rb.inertiaTensor.Invert(),
    50.                 (Quaternion.Inverse(q1) * w1));
    51.             Vector3 t2 = q2 * Vector3.Scale(otherRb.isKinematic ? Vector3.zero : otherRb.inertiaTensor.Invert(),
    52.                 (Quaternion.Inverse(q2) * w2));
    53.             float j = -(1 + e) * Vector3.Dot(deltaV, dir) /
    54.                       (m1Inv + m2Inv + Vector3.Dot(Vector3.Cross(t1, r1) + Vector3.Cross(t2, r2), dir));
    55.             return j;
    56.         }
    edit:
    The Vector3 inverse is just element wise inverse:
    Code (CSharp):
    1. public static Vector3 Invert(this Vector3 vec)
    2. {
    3.     return new Vector3(1 / vec.x, 1 / vec.y, 1 / vec.z);
    4. }
     
    Last edited: Apr 21, 2021
    BrightBit and seboverflow like this.
  3. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,541
    This is extremely interesting, thank you for sharing!