Search Unity

Feedback CopyTransformFromGameObject apparently incompatible with Unity Physics?

Discussion in 'Physics for ECS' started by jashan, Aug 31, 2020.

  1. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    I have a setup where I need to synchronize the Transforms from the game objects world to Kinematic Unity Physics objects. I assumed that CopyTransformFromGameObject would do this for me.

    Unfortunately, CopyTransformFromGameObject only overwrites LocalToWorld, which apparently does all that's needed for rendering - but Unity Physics seems to rely on Rotation and Translation instead. And those are not updated by CopyTransformFromGameObject.

    Took me a while to figure that one out.

    For now, I'm using a hacky workaround: I simply copy the Transform position and rotation over to the Translation and Rotation components of the entity on FixedUpdate, Update and LateUpdate. A better solution would probably be to do this in a system that runs before the physics simulation.

    Is there a recommended way of pushing this information properly into the dots-based physics simulation (these are objects controlled by VR controllers).
     
  2. Scorr

    Scorr

    Joined:
    Jul 2, 2013
    Posts:
    73
    You could copy CopyTransformFromGameObjectSystem.cs and swap out a few lines to make a version that works with Translation/Rotation components. All it does is combine position+rotation into a LTW and then copy it to the entity, could just skip the combine step.
     
    jashan likes this.
  3. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Create a job that runs after CopyTransformFromGameObjectSystem and before EndFrameTRSToLocalToWorldSystem that populates Translation/Rotation should work.
     
    jashan likes this.
  4. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    For sake of simplicity and performance ... and at the expense of maintainability in the case of DOTS-updates, I extended CopyTransformFromGameObjectSystem to include the updates to Translation and Rotation. The reason I didn't create my own system is because I believe the costly part of CopyTransformFromGameObjectSystem is generating the TransformStashes, and if I keep CopyTransformFromGameObjectSystem and add my own system that updates Translation and Rotation, that would be kind of a waste. That may be premature optimization, especially because I'm using CopyTransformFromGameObject on only a handful of objects ... but I guess it's also a bit of practice.

    Here's the code, let me know what you think:

    Code (CSharp):
    1. using Unity.Collections;
    2. using Unity.Entities;
    3. using Unity.Jobs;
    4. using Unity.Burst;
    5. using Unity.Mathematics;
    6. using UnityEngine.Jobs;
    7.  
    8. namespace Unity.Transforms
    9. {
    10.     [UnityEngine.ExecuteAlways]
    11.     [UpdateInGroup(typeof(TransformSystemGroup))]
    12.     [UpdateBefore(typeof(EndFrameTRSToLocalToWorldSystem))]
    13.     public class CopyTransformFromGameObjectSystem : JobComponentSystem
    14.     {
    15.         struct TransformStash
    16.         {
    17.             public float3 position;
    18.             public quaternion rotation;
    19.         }
    20.  
    21.         [BurstCompile]
    22.         struct StashTransforms : IJobParallelForTransform
    23.         {
    24.             public NativeArray<TransformStash> transformStashes;
    25.  
    26.             public void Execute(int index, TransformAccess transform)
    27.             {
    28.                 transformStashes[index] = new TransformStash
    29.                 {
    30.                     rotation       = transform.rotation,
    31.                     position       = transform.position,
    32.                 };
    33.             }
    34.         }
    35.  
    36. #pragma warning disable 618
    37.         [BurstCompile]
    38.         struct CopyTranslations : IJobForEachWithEntity<Translation> {
    39.             [ReadOnly] public NativeArray<TransformStash> transformStashes;
    40.  
    41.             public void Execute(Entity entity, int index, ref Translation translation) {
    42.                 var transformStash = transformStashes[index];
    43.                 translation.Value = transformStash.position;
    44.             }
    45.         }
    46.  
    47.         [BurstCompile]
    48.         struct CopyRotations : IJobForEachWithEntity<Rotation> {
    49.             [ReadOnly] public NativeArray<TransformStash> transformStashes;
    50.  
    51.             public void Execute(Entity entity, int index, ref Rotation rotation) {
    52.                 var transformStash = transformStashes[index];
    53.                 rotation.Value = transformStash.rotation;
    54.             }
    55.         }
    56.  
    57.         [BurstCompile]
    58.         struct CopyTransforms : IJobForEachWithEntity<LocalToWorld>
    59.         {
    60.             [ReadOnly] public NativeArray<TransformStash> transformStashes;
    61.  
    62.             public void Execute(Entity entity, int index, ref LocalToWorld localToWorld)
    63.             {
    64.                 var transformStash = transformStashes[index];
    65.  
    66.                 localToWorld.Value = float4x4.TRS(
    67.                         transformStash.position,
    68.                         transformStash.rotation,
    69.                         new float3(1.0f, 1.0f, 1.0f));
    70.             }
    71.         }
    72.  
    73.         [BurstCompile]
    74.         struct DeallocateStashes : IJob
    75.         {
    76.             [DeallocateOnJobCompletion] public NativeArray<TransformStash> transformStashes;
    77.  
    78.             public void Execute() {
    79.                 // nothing to do ... probably could transformStashes.Dispose
    80.                 // ... but we have [DeallocateOnJobCompletion] ;-)
    81.             }
    82.         }
    83. #pragma warning restore 618
    84.  
    85.         EntityQuery m_TransformGroup;
    86.  
    87.         protected override void OnCreate()
    88.         {
    89.             m_TransformGroup = GetEntityQuery(
    90.                 ComponentType.ReadOnly(typeof(CopyTransformFromGameObject)),
    91.                 typeof(UnityEngine.Transform),
    92.                 ComponentType.ReadWrite<LocalToWorld>(),
    93.                 ComponentType.ReadWrite<Translation>(),
    94.                 ComponentType.ReadWrite<Rotation>());
    95.        
    96.             //@TODO this should not be required, see https://github.com/Unity-Technologies/dots/issues/1122
    97.             RequireForUpdate(m_TransformGroup);
    98.         }
    99.        
    100.         private NativeArray<JobHandle> handles = new NativeArray<JobHandle>(3, Allocator.TempJob);
    101.        
    102.         protected override JobHandle OnUpdate(JobHandle inputDeps)
    103.         {
    104.             var transforms = m_TransformGroup.GetTransformAccessArray();
    105.             var transformStashes = new NativeArray<TransformStash>(transforms.length, Allocator.TempJob);
    106.            
    107.             var stashTransformsJob = new StashTransforms { transformStashes = transformStashes };
    108.             var stashTransformsJobHandle = stashTransformsJob.Schedule(transforms, inputDeps);
    109.  
    110.             // JC (2020-09-01): Unity Physics also needs the Translation and Rotation!
    111.             // can be done parallel, deallocate stashes after all these jobs have been completed
    112.             var copyTranslationJob = new CopyTranslations {transformStashes = transformStashes};
    113.             handles[0] = copyTranslationJob.Schedule(m_TransformGroup, stashTransformsJobHandle);
    114.  
    115.             var copyRotationsJob = new CopyRotations {transformStashes = transformStashes};
    116.             handles[1] = copyRotationsJob.Schedule(m_TransformGroup, stashTransformsJobHandle);
    117.  
    118.             var copyTransformsJob = new CopyTransforms {transformStashes = transformStashes};
    119.             handles[2] = copyTransformsJob.Schedule(m_TransformGroup, stashTransformsJobHandle);
    120.  
    121.             JobHandle combined = JobHandle.CombineDependencies(handles);
    122.            
    123.             var deallocateStashesJob = new DeallocateStashes() { transformStashes = transformStashes };
    124.             return deallocateStashesJob.Schedule(combined);
    125.         }
    126.  
    127.         protected override void OnDestroy() {
    128.             handles.Dispose();
    129.         }
    130.     }
    131. }
    132.