Search Unity

How to set nested Translation to world position

Discussion in 'Entity Component System' started by Leonidas85, Oct 6, 2019.

  1. Leonidas85

    Leonidas85

    Joined:
    Mar 11, 2015
    Posts:
    14
    For a given Translation component that could be in a hierarchy with arbitrary other rotations, scales and translations on its parents, how do I set the Translation.Value so LocalToWorld.Position will be at a given world position?
    Multiplying by the inverse of the localToWorld matrix doesn't compile.

    Code (CSharp):
    1. float3 worldPosition = new float3(1, 2, 3);
    2. float4x4 worldToLocal = math.inverse(localToWorld.Value);
    3. translation.Value = math.mul(worldPosition, worldToLocal); // this will not compile, what is the correct notation or solution?
     
    Last edited: Oct 6, 2019
    krooninator and bb8_1 like this.
  2. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    It should be float4, use new float4(pos, 1)
     
    bb8_1 and Leonidas85 like this.
  3. Leonidas85

    Leonidas85

    Joined:
    Mar 11, 2015
    Posts:
    14
    While that does seem to compile it does not seem to provide a valid local position that can be applied to Translation to effectively set it to a given world position =/
     
  4. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Cause you doing it wrong :) You mul to LTW and setting Translation directly, but Translation is in parent relation space. You should add your new local pos to current translation value. Example:
    Code (CSharp):
    1. public class TransformSystemTest : JobComponentSystem
    2. {
    3.     private EntityQuery _query;
    4.  
    5.     protected override void OnCreate()
    6.     {
    7.         _query = GetEntityQuery(typeof(LocalToWorld), typeof(Translation));
    8.     }
    9.  
    10.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    11.     {
    12.         if (Input.GetKeyDown(KeyCode.Space))
    13.         {
    14.             Debug.Log("Move last child to (1,1,1) in world space");
    15.  
    16.             var ltw = _query.ToComponentDataArray<LocalToWorld>(Allocator.TempJob);
    17.             var t = _query.ToComponentDataArray<Translation>(Allocator.TempJob);
    18.          
    19.             float4 newWorldPosition = new float4(1, 1, 1, 1);
    20.             float4x4 worldToLocal = math.inverse(ltw[2].Value);
    21.             float3 newLocalPos = t[2].Value + math.mul(worldToLocal, newWorldPosition).xyz;
    22.  
    23.             var tmp = t[2];
    24.             tmp.Value = newLocalPos;
    25.             t[2] = tmp;
    26.  
    27.             _query.CopyFromComponentDataArray(t);
    28.          
    29.             ltw.Dispose();
    30.             t.Dispose();
    31.         }
    32.         return inputDeps;
    33.     }
    34. }
     
  5. Leonidas85

    Leonidas85

    Joined:
    Mar 11, 2015
    Posts:
    14
    You, sir, are a lifesaver. Thanks so much for taking the time to write that out :)
    I suppose that for the less mathematically inclined it would be nice to have some utility methods.

    I'll leave the one for position here for posterity:
    Code (CSharp):
    1. public static void SetTranslationToWorldPosition(ref Translation translation, [ReadOnly] LocalToWorld localToWorld, [ReadOnly] float3 worldPosition)
    2. {
    3.     translation.Value = translation.Value + math.mul(math.inverse(localToWorld.Value), new float4(worldPosition, 1)).xyz;
    4. }
    I don't suppose I could bother you for a similar method to set the world rotation? I've been at it for a while but keep getting cubes jumping around my screen.
     
    Last edited: Oct 8, 2019
    bb8_1 and florianhanke like this.
  6. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Rotation is not so easy like that. This is why Unity have complex rotation stuff in current system. Look at Mike (If I remember right it was his talk) talk about transform system and how they work with rotation, composite rotation etc. And read this docs for better understanding https://docs.unity3d.com/Packages/com.unity.entities@0.1/manual/transform_system.html
     
    Last edited: Oct 9, 2019
    bb8_1 and Leonidas85 like this.
  7. Leonidas85

    Leonidas85

    Joined:
    Mar 11, 2015
    Posts:
    14
    Just an FYI for people who come here looking for a way to sync a non-ECS transform position / rotation to an entity; there is a much easier way of doing it:
    Code (CSharp):
    1. EntityManager.AddComponentObject(entity, gameObject.transform);
    2. EntityManager.AddComponentData(entity, new CopyTransformFromGameObject { });
    3. // OR
    4. EntityManager.AddComponentData(entity, new CopyTransformToGameObject { });
    There are 3 things to note with this method:
    - You can remove the transform using RemoveComponent.
    - It is possible to assign another non-null transform by calling AddComponentObject() again.
    - The syncing of the transform from the gameObject to the entity seems to be applied directly to the LocalToWorld matrix and the Rotation value is not set as you would expect. Could be a bug they'll fix but for now it may lead to confusion. You should use the LocalToWorld matrix to get the world position and world rotation from the entity if needed (just use Quaternion.LookRotation(localToWorld.Forward, localToWorld.Up) to get the world rotation)
     
    Studiomaurer, bb8_1 and NotaNaN like this.
  8. Leonidas85

    Leonidas85

    Joined:
    Mar 11, 2015
    Posts:
    14
    Apparently setting the rotation to a global rotation for a nested entity also requires you to update the LocalToWorld and LocalToParent, but it's actually pretty straight forward.

    I've created a solution that seems to work. Simply add a FollowComponent to an entity and define the entity to follow and the following entity's position / rotation will be set to follow.

    Disclaimer: Use at your own peril, have yet to extensively test this. It does not include anything to handle scaling in the hierarchy.

    Code (CSharp):
    1. using Unity.Collections;
    2. using Unity.Entities;
    3. using Unity.Mathematics;
    4. using Unity.Transforms;
    5. using UnityEngine;
    6.  
    7. public struct FollowComponent : IComponentData
    8. {
    9.     public Entity followedEntity;
    10. }
    11.  
    12. [UpdateInGroup(typeof(LateSimulationSystemGroup))]
    13. public class FollowSystem : ComponentSystem
    14. {
    15.     protected override void OnUpdate()
    16.     {
    17.         ComponentDataFromEntity<LocalToWorld> ltws = GetComponentDataFromEntity<LocalToWorld>(false);
    18.         ComponentDataFromEntity<LocalToParent> ltps = GetComponentDataFromEntity<LocalToParent>(true);
    19.  
    20.         Entities.WithNone<Parent>().ForEach((
    21.             ref FollowComponent followComponent,
    22.             ref LocalToWorld localToWorld,
    23.             ref Translation translation,
    24.             ref Rotation rotation) =>
    25.         {
    26.             LocalToWorld followedLTW = ltws[followComponent.followedEntity];
    27.  
    28.             float3 targetWorldPosition = followedLTW.Position;
    29.             quaternion targetWorldRotation = Quaternion.LookRotation(followedLTW.Forward, followedLTW.Up);
    30.  
    31.             translation.Value = targetWorldPosition;
    32.             rotation.Value = targetWorldRotation;
    33.  
    34.             localToWorld.Value = new float4x4(targetWorldRotation, targetWorldPosition);
    35.         });
    36.  
    37.         Entities.ForEach((
    38.             Entity entity,
    39.             ref FollowComponent followComponent,
    40.             ref Parent parent,
    41.             ref LocalToWorld localToWorld,
    42.             ref LocalToParent localToParent,
    43.             ref Translation translation,
    44.             ref Rotation rotation) =>
    45.         {
    46.             LocalToWorld followedLTW = ltws[followComponent.followedEntity];
    47.  
    48.             float3 targetWorldPosition = followedLTW.Position;
    49.             quaternion targetWorldRotation = Quaternion.LookRotation(followedLTW.Forward, followedLTW.Up);
    50.  
    51.             LocalToWorld parentLTW = ltws[parent.Value];
    52.             quaternion parentWorldRotation = Quaternion.LookRotation(parentLTW.Forward, parentLTW.Up);
    53.             float3 parentWorldPosition = parentLTW.Position;
    54.  
    55.             quaternion localRotation = math.mul(targetWorldRotation, math.inverse(parentWorldRotation));
    56.             float3 localPosition = math.mul(math.inverse(parentWorldRotation), targetWorldPosition - parentWorldPosition);
    57.  
    58.             translation.Value = localPosition;
    59.             rotation.Value = localRotation;
    60.  
    61.             localToParent.Value = new float4x4(rotation.Value.value, translation.Value);
    62.             localToWorld.Value = new float4x4(targetWorldRotation, targetWorldPosition);
    63.             UpdateChildLocalToWorldsRecursively(entity, localToWorld, ref ltws, ref ltps);
    64.         });
    65.     }
    66.  
    67.     private void UpdateChildLocalToWorldsRecursively(Entity parentEntity,
    68.         LocalToWorld parentLTW,
    69.         [ReadOnly] ref ComponentDataFromEntity<LocalToWorld> ltws,
    70.         ref ComponentDataFromEntity<LocalToParent> ltps)
    71.     {
    72.         if (!EntityManager.HasComponent<Child>(parentEntity))
    73.         {
    74.             return;
    75.         }
    76.  
    77.         DynamicBuffer<Child> children = EntityManager.GetBuffer<Child>(parentEntity);
    78.         foreach (Child child in children)
    79.         {
    80.             Entity childEntity = child.Value;
    81.             LocalToParent childLTP = ltps[childEntity];
    82.             LocalToWorld childLTW = ltws[childEntity];
    83.             childLTW.Value = math.mul(parentLTW.Value, childLTP.Value);
    84.             ltws[childEntity] = childLTW;
    85.  
    86.             UpdateChildLocalToWorldsRecursively(childEntity, childLTW, ref ltws, ref ltps);
    87.         }
    88.     }
    89. }
     
    Last edited: Oct 12, 2019
  9. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    Thanks for the example, you rock!
     
    bb8_1 likes this.
  10. Skymorphis

    Skymorphis

    Joined:
    May 4, 2017
    Posts:
    17
    this should be in the docs, thanks much dude!
     
    jasonlawless and bb8_1 like this.
  11. jasonlawless

    jasonlawless

    Joined:
    Sep 21, 2019
    Posts:
    3
    What about one for scale? Say I have entity (A) and (B), and I want to parent (B) to (A), but (A) and/or it's parents might have non-uniform and non-default scales, which automatically skews (B) the moment its parented. Is there a way to use LTW Values to compensate for scale? So that if (B) was 1, 2, 1 in world scale, it would remain so after parented but with new local to parent scale?