Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

PostSolveJacobians results are ignored by Havok physics?

Discussion in 'Physics for ECS' started by WildMaN, Jul 28, 2020.

  1. WildMaN

    WildMaN

    Joined:
    Jan 24, 2013
    Posts:
    128
    Hey,

    I'm modifying MotionVelocities array in PostSolveJacobians, however it seems to have no effect with Havok physics. Is it intended?
     
  2. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Can you share what your intentions are with it?

    There are limitations, mostly the fact that Havok works with its own internal data and modifying MotionVelocities during Havok step can't affect the behavior. You can only modify physics runtime data (MotionData and MotionVelocity) after the step and before export.

    Also, at the moment PostSolveJacobians when using Havok actually means post integrate, since we weren't able to expose the exact point before integration.

    But please share what the use case is so that I can try to help out with more advice.
     
  3. WildMaN

    WildMaN

    Joined:
    Jan 24, 2013
    Posts:
    128
    Hey petarmHavok, I'm prototyping a game about destroying castles brick by brick. Tall constructs made of tens of thousands of stacked blocks. PhysX version was quite uninspiring, so been tinkering with DOTS for a while. There are obviously two issues - stacking stability and amount of collisions. Latter can be dealt with in multiple ways, of which I want to mention composites which then shatter into smaller parts, and my own heuristic for sleeping - thus I was really happy to discover the PostSolveJacobians way and it really helps to cut off unnecessary simulations.

    Stacking stability, however, is painful. Both Havok and Unity Physics produce massive jelly with true physical simulation of a brick wall, even with the recently added ContactSolverStabilization. It was the case for PhysX as well, so I did a custom activation event propagation system and turned on physics only for the bricks which are moving or about to move; it also involved faking on the render side - mesh didn't move visually until the related collider moved far enough. So for stacks I detect the collisions and then for colliding bricks I'm getting their velocities to determine whether the brick should move at all (clamping) and whether applied momentum is large enough to shatter it.

    PostSolveJacobians really helps in many ways. But Havok is better for my case for solving some other artifacts of Unity Physics.
     
    CristianOG likes this.
  4. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    So my advice (when using Havok) is to wait for StepPhysicsWorld to end, and then (before ExportPhysicsWorld), do your sleeping logic. It won't stop the bodies from moving in this frame, but it will help stability for sure.

    Also, for Unity Physics, try increasing the aggressiveness of the contact solver stabilization. It works on some default values that can be increased, just don't go too crazy. :)
     
  5. WildMaN

    WildMaN

    Joined:
    Jan 24, 2013
    Posts:
    128
    But would it mean that I can extract MotionVelocities after the StepPhysicsWorld and then manually move back some/all entities, effectively replicating the Unity Physics behavior of clipping MotionVelocities in PostSolveJacobians?

    Thanks for the heads up! It doesn't solve the stack stability issue, but it provides some nice results with the residue jitter.
     
  6. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Yes, unfortunately that's the only way to replicate similar behavior to what you can achieve with Unity Physics.

    You can also try a few more things, like increasing number of solver iterations, linear and angular damping, increasing inertia of bodies, friction, decreasing gravity...
     
  7. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    For completeness, here is the ClipMotionAuthoring behavior from the demo in the Unite Now video.
    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Collections;
    3. using Unity.Entities;
    4. using Unity.Jobs;
    5. using Unity.Mathematics;
    6. using Unity.Physics;
    7. using Unity.Physics.Systems;
    8. using UnityEngine;
    9.  
    10. public class ClipMotionAuthoring : MonoBehaviour
    11. {
    12.     //public bool EnableSystem = false;
    13.     public float LinearThreshold = 1E-4f;
    14.     public float AngularThreshold = 1E-6f;
    15.     public bool MassInvariant = false;
    16.  
    17.     ClipMotionAuthoring() { }
    18.  
    19.     private void OnEnable() { OnValidate(); }
    20.  
    21.     void OnValidate()
    22.     {
    23.         var world = BasePhysicsDemo.DefaultWorld;
    24.         if (world != null)
    25.         {
    26.             var system = BasePhysicsDemo.DefaultWorld.GetOrCreateSystem<ClipMotionSystem>();
    27.             if (system != null)
    28.             {
    29.                 system.Enabled = this.enabled;
    30.                 system.LinearThreshold = LinearThreshold;
    31.                 system.AngularThreshold = AngularThreshold;
    32.                 system.MassInvariant = MassInvariant;
    33.             }
    34.         }
    35.     }
    36. }
    37.  
    38.  
    39. [AlwaysUpdateSystem]
    40. [UpdateBefore(typeof(StepPhysicsWorld))]
    41. public class ClipMotionSystem : JobComponentSystem
    42. {
    43.     public float LinearThreshold = 1E-4f;
    44.     public float AngularThreshold = 1E-6f;
    45.     public bool MassInvariant = false;
    46.  
    47.     StepPhysicsWorld m_stepPhysicsWorld;
    48.  
    49.     protected override void OnCreate()
    50.     {
    51.         Enabled = false;
    52.         m_stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    53.     }
    54.  
    55.     [BurstCompile]
    56.     struct ClipMotionDataJob : IJobParallelFor
    57.     {
    58.         public float linearThreshold;
    59.         public float angularThreshold;
    60.         public bool massInvariant;
    61.         public float3 gravityDir;
    62.  
    63.         public NativeSlice<MotionVelocity> MotionVelocities;
    64.  
    65.         public void Execute(int index)
    66.         {
    67.             var pv = MotionVelocities[index];
    68.          
    69.             float3 linearEnergy = pv.LinearVelocity * pv.LinearVelocity;
    70.             float3 angularEnergy = pv.AngularVelocity* pv.AngularVelocity;
    71.             if(!massInvariant)
    72.             {
    73.                 linearEnergy *= math.rcp(pv.InverseMass);
    74.                 angularEnergy *= math.rcp(pv.InverseInertia);
    75.             }
    76.  
    77.             if (math.lengthsq(linearEnergy) < linearThreshold)
    78.             {
    79.                 var gravityComponent = math.dot(pv.LinearVelocity, gravityDir) * gravityDir;
    80.                 pv.LinearVelocity = gravityComponent;
    81.                 if (math.lengthsq(angularEnergy) < angularThreshold)
    82.                 {
    83.                     pv.AngularVelocity = float3.zero;
    84.                 }
    85.             }
    86.  
    87.             MotionVelocities[index] = pv;
    88.         }
    89.     }
    90.  
    91.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    92.     {
    93.         if (Enabled)
    94.         {
    95.             SimulationCallbacks.Callback clipMotionsCallback = (ref ISimulation simulation, ref PhysicsWorld world, JobHandle inDeps) =>
    96.             {
    97.                 return new ClipMotionDataJob
    98.                 {
    99.                     gravityDir = math.normalizesafe(GetSingleton<PhysicsStep>().Gravity),
    100.                     linearThreshold = LinearThreshold,
    101.                     angularThreshold = AngularThreshold,
    102.                     massInvariant = MassInvariant,
    103.                     MotionVelocities = world.MotionVelocities,
    104.                 }.Schedule(world.NumDynamicBodies, 64, inDeps);
    105.             };
    106.             m_stepPhysicsWorld.EnqueueCallback(SimulationCallbacks.Phase.PostSolveJacobians, clipMotionsCallback, inputDeps);
    107.         }
    108.  
    109.         return inputDeps;
    110.     }
    111. }
    112.  
     
    cultureulterior and WildMaN like this.
  8. WildMaN

    WildMaN

    Joined:
    Jan 24, 2013
    Posts:
    128
    Thank you @petarmHavok, @steveeHavok, got it working perfectly.



    Here's the profiler screen while using UnityPhysics:
    Unity_TfGnkodXTq.png

    Key bottleneck is the BodyPairsJob. Basically I'm doing my own heuristics in order to improve framerate, and part of that involves dealing with colliding entities. The only ways I found to achieve it are either IBodyPairsJob or ICollisionEventsJob.

    Problems with the IBodyPairsJob:
    - single threaded
    - does not expose the nativearray of colliding bodypairs
    - it blocks all parallel threads, being called from a SimulationCallbacks.Callback. Though I'm not modifying pairs and just do my own non-blocking stuff.

    Problems with the ICollisionEventsJob:
    - single threaded
    - does not expose the nativearray of colliding shapes
    - considerably slow; it takes ~1ms to spin up a 1000 of Execute methods. With mere 30.000 colliders it means 30ms spent only on ICollisionEventsJob.Execute calls internally, saying goodbye to 60fps.

    There are also some quite sparse areas on the profiler screen, mainly after the Jacobians job. Not sure whether I can do anything to speed it up.

    I am really missing the CollisionsExclude tag, in line with the PhysicsExclude. Right now I'm just removing PhysicsCollider component, but restoring collider would be painful if required.

    Also I think I've seen somewhere that there were plans to expose a slider "accuracy vs performance"; it would be great for a game such as mine. I don't need pixel-perfect collisions but I'd like to reduce the time spent in that dreaded narrow phase - up to 50% on Android.

    I tinkered a bit with HavokPhysics as well. The good thing is that it's even faster on PC, but being the black box, it's quite hard to do custom heuristics. Key issue was that entities put to sleep stopped generating any events and I found no way to detect such sleeping entities. Also it seems that multithreading is not that good with Havok, which is an issue on Android - when the cores are relatively slow and utilizing all threads is a must.
     
    Piefayth and florianhanke like this.
  9. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Just to add on the CollisionsExclude tag - you actually have this, just set CollisionResponse.None on the material of the collider and that's it. :)
     
  10. WildMaN

    WildMaN

    Joined:
    Jan 24, 2013
    Posts:
    128
    Tried that - no performance gain, unfortunately. Only hard removal of PhysicsCollider provided a boost.
     
  11. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Yeah, I understand that hard removal is the most efficient since it effectively removes the body from the world, but this should also give you a solid boost since collision detection and solving are skipped. I'm surprised you got no boost there...
     
  12. WildMaN

    WildMaN

    Joined:
    Jan 24, 2013
    Posts:
    128
    I didn't explore it thoroughly as removing the collider entirely worked like a charm, but AFAIR in order to set CollisionResponse.None per entity I also had to mark all colliders as unique, probably that's what negated the effect. So per-entity CollisionsExclude tag still sounds like the best option.
     
    petarmHavok likes this.