Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug (Case 1293499) Angular velocity is incorrectly applied on some bodies with convex shapes

Discussion in 'Physics for ECS' started by ArtyIF, Oct 22, 2020.

  1. ArtyIF

    ArtyIF

    Joined:
    Oct 13, 2014
    Posts:
    36
    The problem is that when I apply angular impulses to physics bodies using PhysicsWorld.ApplyAngularImpulse, the body adds some other angular velocity that I don't want since it may actually flip my car over. The "ground truth" implementation in regular C# and PhysX works fine. Here's the demonstration when the car is in the air without gravity and not considering if it's landed:


    Current system code is attached. Can anyone help me please?
     

    Attached Files:

  2. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    You can take a look at the implementation of the ApplyAngularImpulse method, it works in local space of the motion, therefore it needs to apply rotation to your input angular impulse in order to apply it. So the y axis you are trying to change the velocity around might not be the one you want, thus giving you rotations you don't want. Perhaps you could just apply the angular impulse on the MotionVelocity instead, that could help?
     
  3. ArtyIF

    ArtyIF

    Joined:
    Oct 13, 2014
    Posts:
    36
    I'm not sure. Thing is, when i switch the physics shape to Cube, the impulse is applied just fine. That might have something to do with center of mass mismatching somewhere.
     
  4. ArtyIF

    ArtyIF

    Joined:
    Oct 13, 2014
    Posts:
    36
    So I decided to investigate the cause. Didn't go far, but for whatever reason when for some complicated shapes (Kenney's Car Pack in this case, I'm using the sports hatchback for this case but others have the same issue) I set the shape type to Convex Hull, the parent (which has a Physics Body) gets rotated a tiny amount, which gets really noticable when adding angular velocity to it. But when I set the type to Mesh or any other, the parent doesn't rotate and works fine when adding angular velocity. So for now I'm forced to use a more expensive or a more broad method of collision detection, which sucks.

    Also why does center of mass have rotation (which by itself doesn't make sense, how can a dot be rotated) on convex mesh? Think there's a miscalculation of CoM on convex hulls for whatever reason.
     
  5. ArtyIF

    ArtyIF

    Joined:
    Oct 13, 2014
    Posts:
    36
    Aha! Found the issue.
    Here's how
    Unity.Physics.MeshCollider
    sets Transform in
    MassProperties
    's
    MassDistricbution
    (
    Unity.Physics/Collision/Colliders/Physics_MeshCollider.cs
    , line 152):

    Code (CSharp):
    1. Transform = new RigidTransform(quaternion.identity, m_Aabb.Center)
    And here's how
    Unity.Physics.ConvexCollider
    does that (
    Unity.Physics/Collision/Colliders/ConvexCollider.cs
    , lines 196-197 and 209):

    Code (CSharp):
    1. var massProperties = builder.HullMassProperties;
    2. Math.DiagonalizeSymmetricApproximation(massProperties.InertiaTensor, out float3x3 orientation, out float3 inertia);
    3.  
    4. // when setting MassDistibution...
    5.  
    6. Transform = new RigidTransform(orientation, massProperties.CenterOfMass)
    So I fixed the issue by replacing
    orientation
    in
    RigidTransform
    constructor arguments to
    quaternion.identity
    , similar to
    MeshCollider
    . This fix will probably cause some regressions (and I would like to know which), but the mesh rotates as expected now.
     
  6. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    You definitely don't want a dynamic mesh collider :) That's a performance killer.
    The center of mass when calculated from a convex hull could easily be offset and rotated from the original pivot depending on the hulls shape.

    This is a little confusing but hopefully this will clear things up.
    Your WRCarPhysicsSystem is using the
    physicsWorld.ApplyAngularImpulse
    extension function which expects world space impulses. If you directly the angular impulse via the physics
    MotionVelocity
    runtime struct, then it expects motion space impulses.
    So, from the quick look I had at your code, I believe the problem is the combination of the impulse being calculated in one space, the inertia diagonal being in motion space and being applied in world space.
     
  7. ArtyIF

    ArtyIF

    Joined:
    Oct 13, 2014
    Posts:
    36
    Still, I don't understand why would center of mass have orientation. This is kind of counter-intuitive.
     
  8. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    Its not that COM has orientation, it's that the motion space might be different from the body space.
    The Motion space origin will be at the COM and its orientation will be defined by the axes around which the body can be rotated most easily and with most difficulty (i.e. axes with the smallest and largest inertia components).
    Imagine a long body that isn't axis aligned i.e the OBB of the body is not aligned to the AABB of the body. In calculating the mass properties (including COM and Inertia), the center of mass won't be at the body pivot point and the convex hull major axis won't be aligned with the bodies canonical axes either, hence Motion space will have a rotation and position offset from Body space.
     
    petarmHavok likes this.
  9. ArtyIF

    ArtyIF

    Joined:
    Oct 13, 2014
    Posts:
    36
    Alright, let's say I use
    MotionVelocity
    to apply impulses to the body. When I use it as a parameter in
    Entities.ForEach
    , I get this error:
    parameter 'velocityMotion' has type MotionVelocity. This type is not a IComponentData / ISharedComponentData and is therefore not a supported parameter type for Entities.ForEach.
    . How do I use it then? And do I just apply local space impulses? Don't really understand what "motion space" means, and Google doesn't find anything useful
     
  10. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    Motion space is just the name for a frame of reference whose origin is at the bodies COM and whose axes line up with the mass distribution. So it's just another handy name, like World space or Local space. Local space is to generic though (it will be context dependent) so we specifically use Body space (reference frame of the pivot) and Motion space (reference frame of the mass distribution).

    Apologies if I've exacerbated the confusion between the ECS Component structs (e.g. PhysicsVelocity, PhysicsGravityFactor & PhysicsMass) which work with Entities.ForEach, and the simulation runtime structs (e.g. MotionData & MotionVelocity) which are processed by the core Physics algorithms.
    ECS data is nicely aligned for extremely fast contiguous memory access. The physics simulation really needs to bounce randomly between different parts of memory so the BuildPhysicsWorld system first converts the IComponentData structs into the simulation runtime optimized structs. StepPhysicsWorld works on the simulation optimized structs before ExportPhysicsWorld spits the data back into the ECS component data structs. (see more details in this video)

    I recommend you work at the ECS level data and use the extension functions that work on them. The problem is the ApplyAngularImpulse doesn't work in WorldSpace. We should really add something like this:

    Code (CSharp):
    1.         /// <summary>
    2.         /// Apply a world-space angular impulse to a rigid body.
    3.         /// </summary>
    4.         /// <param name="bodyVelocity">The body's <see cref="PhysicsVelocity"/> component.</param>
    5.         /// <param name="bodyMass">The body's <see cref="PhysicsMass"/> component</param>
    6.         /// <param name="bodyOrientation">The body's <see cref="Rotation"/> component</param>
    7.         /// <param name="impulse">An angular impulse in world space specifying radians per second about each axis.</param>
    8.         public static void ApplyAngularImpulseWorldSpace(ref this PhysicsVelocity bodyVelocity, in PhysicsMass bodyMass, in Rotation bodyOrientation, in float3 impulse)
    9.         {
    10.             quaternion inertiaOrientationInWorldSpace = math.mul(bodyOrientation.Value, bodyMass.InertiaOrientation);
    11.             float3 angularImpulseInertiaSpace = math.rotate(math.inverse(inertiaOrientationInWorldSpace), impulse);
    12.             bodyVelocity.Angular += angularImpulseInertiaSpace * bodyMass.InverseInertia;
    13.         }
    I hope this helps clear things up and I haven't just added more to the confusion!
     
    Occuros and Lukas_Kastern like this.