Gameobject Converted Hierarchy Transformation

I am having great difficulty to understand where the logic of the hierarchical transformation of an entity converted from a gameobject is.

I am currently setting the position and rotation on a parent entity, but the position and rotation of the children are not updating and I can’t figure out why.

Check this, maybe helpful: New SubScene & ConvertToEntity workflows page-3#post-4481629

1 Like

https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/transform_system.html

Position is in local space. LocalToWorld is GlobalSpace.

1 Like

Thanks, for the root position in Local space should be the equivalent of the world space, am I right? I wrote an engine to operate only on the roots of the hierarchies, but either using the Translation and Rotation and using the LocalToWorld, I don’t see the children being translated by the parent offset.
It works only for entities without hierarchy. Currently I am using the LocalToWorld as it is actually better for my flow and I remove the Translation and Rotation components from the parents to avoid to the matrix be overwritten. Still the children remain unaffected. Help?

Not. It’s position relative to your parent entity. If your parent on (0,1,0) and your child in center of your parant your child has (0,0,0) coords in local space, and (0,1,0) in world space from local to world matrix. And if you moving parent, for example, to (0,5,0) you child still on (0,0,0) in local space. in world space (from local to world matrix) they have same (0,5,0) position but if you move child relative to parent, for example to (0,1,0) in this case your parent have (0,5,0) and child (0,6,0) in world space.

Thanks, a root doesn’t have a parent though. In any case I am trying the local to world route and still failing to see the children being transformed by the parent root transformation

On root of course local and world positions the same

1 Like

Please check my code:

https://hastebin.com/ohovuqaqak.cpp

This is my workflow:

The artists create the prefabs as they used to (more or less), the code converts these prefabs in entity prefabs, using the code:

if (renderWorld != null)
            {
                prefabEntityID=
                    GameObjectConversionUtility.ConvertGameObjectHierarchy(prototypeGameObject.Result, renderWorld);
           
                renderWorld.EntityManager.AddSharedComponentData(prefabEntityID, new UECSResourceGroup(prefabID));
                renderWorld.EntityManager.AddComponent(prefabEntityID, ComponentType.ReadWrite<SveltoEGID>());
       
                renderWorld.EntityManager.RemoveComponent<Translation>(prefabEntityID);
                renderWorld.EntityManager.RemoveComponent<Rotation>(prefabEntityID);
                renderWorld.EntityManager.RemoveComponent<NonUniformScale>(prefabEntityID);
            }

Anytime I need a new entity, I then instantiate the prefab using this code:

var entity = manager.Instantiate(prefabEntityID);

In the JobComponentSystem, I iterate all the entities with the following components:

typeof(LocalToWorld), typeof(SveltoEGID), typeof(UECSResourceGroup)

SveltoEGID is currently used in this context only to mark the root prefabs, as I want to iterate only the root (currently I didn’t find another way to identify roots of gameobject hierarchies).

I remove the Translation,Rotation and NonUniformScale from the prefab for the purpose to disable all the systems that write in the LocalToWorld matrix, as deducted from Joachim advices.

I store in the _instanceData system field, all the entities grouped by prefabID (resourceStructResourceId). If there entities, then I schedule a job to apply the transformation matrix to this set of entities. I set a group filter to be sure that I am going to iterate only the entities with that resourceID.

var uecsResourceGroup = new UECSResourceGroup(resourceStructResourceId); is a SCD that I use only to group the UECS entities by resource ID

As result I expect that all the roots of the instances generated by the prefabID gameobjects will be iterated and the LocalToWorld set.

I also expect that the children of these roots are transformed by this matrix, but this is NOT happening and it’s my current problem I seed advice of.

as side note “DO_THIS_ONCE_THE_SCD_WORKS” is currently disabled because of the problem that for some specific gameobjects the SCD is not working, generating a chunk for each entity as opposite of a chunk for each prefabid

Note: please check the way I am scheduling the jobs too. I am not sure it’s the optimal way. These jobs should be scheduled in parallel and not in serial, plus the entity debugger shows the entities touched by the last job scheduled and not all of them

UECSResourceGroup being a shared component doesn’t seem like a reasonable approach.

Using IComponentData on the root and then scan to find the right one is better.

LinkedEntityGroup already exists and makes Instantiate / Destroy perform those operations in whole groups (Remapping internal references). So LinkedEntityGroup already stores references to all entities in the same group.
This is automatically added to any referenced prefabs that are being converted.

1 Like
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Rendering;
using Unity.Transforms;
using UnityEngine;
using UnityEngine.Rendering;

public struct MyRoot : IComponentData
{
   
}

public class TestSystem : MonoBehaviour
{
    public GameObject prefab;

    private void Start()
    {
        var w = World.Active;
        var em = w.EntityManager;
        var prefabEntityID =
            GameObjectConversionUtility.ConvertGameObjectHierarchy(prefab, w);
          
        em.AddComponent(prefabEntityID, ComponentType.ReadWrite<MyRoot>());
      
        em.RemoveComponent<Translation>(prefabEntityID);
        em.RemoveComponent<Rotation>(prefabEntityID);
        em.RemoveComponent<NonUniformScale>(prefabEntityID);

        em.Instantiate(prefabEntityID);
    }
}

public class RootRotateSystem: JobComponentSystem
{
    private EntityQuery query;
   
    protected override void OnCreate()
    {
        query = GetEntityQuery(typeof(Rotation), typeof(MyRoot));
    }

    [BurstCompile]
    [RequireComponentTag(typeof(MyRoot))]
    public struct MoveRootJob : IJobForEachWithEntity<LocalToWorld>
    {
        public float time;
       
        public void Execute(Entity entity, int index, ref LocalToWorld m)
        {
            var pos = m.Value.c3;
            pos.y = math.sin(time) * 2f;
            m.Value.c3 = pos;
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        return new MoveRootJob()
        {
            time = Time.time
        }.Schedule(this, inputDeps);
    }
}
1 Like

Just FYI.
Don’t forget you remove it’s only from prefab root, not from childs :slight_smile:

1 Like

Root has LocalToWorld but hasn’t LocalToParent, just querying LocalToWorld with exclude LocalToParent return all roots for us, isn’t it?

1 Like

Hey thank you a lot for helping me figuring this out.

I assumed that removing the components from the prefabs would be enough. Was I wrong? If so I where does the instance process take the components from (I guess the original archetype?). I am going to try this!

I am not sure I get this. In my case I am grouping the blocks by block type (cube, motor, battery and so on). I do not know beforehand the types of blocks the game is going to have as it’s data driven, therefore I cannot attach a CubeComponent, BatteryComponent and so on the various entities. I do have IDs though (the resource ID), therefore I use a SCD (and it’s hashing uniqueness) to group the blocks of type 1, 2, 3 and so on.

I will research about LinkedEntityGroup, I assumed it was the list of children of the root, but from your words it seems to be something else that I am not getting yet.

Edit:
Rewrite this answer for better understanding.
Prefab it’s just one entity, when you delete\add components to this it’s of course not affect childs. When you instantiate prefab (which has LinkedEntityGroup which has child entities and root byself) it’s instantiate all entities from LinkedEntityGroup
4489051--413530--upload_2019-5-1_14-35-45.png

It’s buffer with root and all childs, it’s group of entities which should be deleted (as example) when you destroy this root entity, or which slould be instantiated when you instantiate this root. It’s in very simple words of course.

if I understood correctly, children do not have translation/rotation/scaling by default

edit: I was wrong, they have it, going to think about it

They have

indeed

OK Rotation and Translation removed from the children and unluckily the behavior is still the same.

I can see that the System EndFrameParentSystem is called as expected and the right entities are processed.

I can see that the roots have the right LocalToWorld matrices set.
4489438--413623--upload_2019-5-1_14-26-59.png

However Children LocalToWorld are unaffected:

4489438--413626--upload_2019-5-1_14-27-25.png

systems running:

Edit: I forced the LocalToParent matrix to be identity, no changes

Side question: can I combine the same job multiple times?
if I do
previousJob = JobHandle.CombineDependencies(previousJob, currentjob.Schedule()); although a computational waste, will the result be the same than passing the array of jobs?