TransformMatrixComponent and Scaling?

I was messing around with the new ECS and I couldn’t figure out how to change the scale of the TransformMatrixComponent. It doesn’t look like Unity.Transforms has any Components for scale, all I saw was the RotationComponent and the PositionComponent. Am I missing something or is it just because it hasn’t been implemented yet?

It hasn’t been implemented yet AFAIK. If you in need a transform system which supports scaling (without hierarchy), I can post some code here.

1 Like

i have used the transform matrix component for the instance renderer, but have only used it indirectly through position, I guess you. CAn just mess with it directly?

1 Like

If you want to post some code here I would greatly appreciate it :slight_smile:

Yeah you can really only mess with the Position and Rotation so far it seems like, hopefully as the ECS package keeps getting updated, Unity will put the Scaling component in! :wink:

That’s not what I meant. I think you should be able to set the value of the transform matrix component directly (without using unity’s build in systems for position and rotation) - this would give you access to scale

Oh I see what you mean, I started tinkering around with it and came up with this!

using Unity.Collections.LowLevel.Unsafe;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Jobs;
using UnityEngine;

public class ScaleComponent : ComponentDataWrapper<Scale> { }

[System.Serializable]
public struct Scale : IComponentData
{
    public float3 Value;
}

[UnityEngine.ExecuteInEditMode]
public class TransformSystem : JobComponentSystem
{
    struct TransformGroup
    {
        [ReadOnly]
        public ComponentDataArray<Position> Positions;

        [ReadOnly]
        public ComponentDataArray<LocalRotation> Rotations;

        [ReadOnly]
        public ComponentDataArray<Scale> Scales;

        public ComponentDataArray<TransformMatrix> Transforms;
        public int Length;
    }

    [Inject]
    TransformGroup transformGroup;

    [ComputeJobOptimization]
    struct TransformGroupJob : IJobParallelFor
    {
        [ReadOnly]
        public ComponentDataArray<Position> Positions;

        [ReadOnly]
        public ComponentDataArray<LocalRotation> Rotations;

        [ReadOnly]
        public ComponentDataArray<Scale> Scales;

        public ComponentDataArray<TransformMatrix> Transforms;

        public void Execute(int i)
        {
            Transforms[i] = new TransformMatrix
            {
                Value = math.mul(math.rottrans(Quaternion.Euler(Rotations[i].Value.value.xyz), Positions[i].Value), math.scale(Scales[i].Value))
            };
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var transformJob = new TransformGroupJob
        {
            Transforms = transformGroup.Transforms,
            Positions = transformGroup.Positions,
            Rotations = transformGroup.Rotations,
            Scales = transformGroup.Scales,
        };

        return transformJob.Schedule(transformGroup.Length, 64, inputDeps);
    }
}

Probably not the most fastest running code but it does let me tinker with Entities until Unity decides to add a Scale Component to the ECS. Another annoying thing that I noticed with the Entity debugger was that when you disable a system, after hitting play, that system would re-enable itself. Also I didn’t actually know how to reorder the systems so that my system happens before Unity’s own TransformSystem, since they create a new matrix completely invalidating the one I made in this new system.

I have not looked into your implementation, but I think that if you want to avoid interference with Unity’s Transform System, you should avoid their built-in position, rotation components and roll your own ones.

This way their Transform System should not kick-in at all and you can deal with your own “MyPosition”, “MyRotation”, “MyScale” components in your own system, i.e. setting the TransformMatrix.

1 Like

Here is the gist as promised. I fiddled a little with the code as I removed my namespaces and put all the components into one file. Let me know if it works. Please also note that you now need an “Active” component on each object you want to render.
https://gist.github.com/JoeCoo7/f497af9b1ba2ab5babae3060635a9c6a

Looks pretty clean!

One minor question / comment - I think it’s safe to pass the injection group struct to a job? Saves having to type out all the arrays / permissions twice!

1 Like

Yes, that is a good suggestion.

I had the same idea and all worked fine at first but then I had memory overflow and whatnot when I tried to spawn more entities. Be cautious, do tests!

This doesn’t sounds to be a good idea, since when you write the permissions on a Job and then mark it as a [BrustCompile] (previous [JobComputeOptimization]) the Brust Compile will take a look on those permissions, create some context, do some optimizations and safety handling based on that. If you just pass the entire group you are basically using it as a simple Job, without much of the advantages of using brust compiler.

Remember that on of the main goals of Brust Compiler is to use SIMD instructions, which needs to be heavy tied with linear memory layout. That’s also the same reason why they are working with special types like StructOfArrays, which I didn’t fully understood yet…

Also wanted to post my solution, which modifies the TransformMatrix from the TransformSystem. So it will work with all combinations of Position, Heading, Rotation, etc.

using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;

[System.Serializable]
public struct Scale : IComponentData
{
    public float3 Value;
}
public class ScaleComponent : ComponentDataWrapper<Scale> { }

[UpdateAfter(typeof(TransformSystem))]
public class ScaleSystem : ComponentSystem
{
    public struct ScaleGroup
    {
        public readonly int Length;
        [ReadOnly] public ComponentDataArray<Scale> Scale;
        public ComponentDataArray<TransformMatrix> TransformMatrix;
        public SubtractiveComponent<VoidSystem<TransformSystem>> VoidSystemTransformSystem;
        public SubtractiveComponent<VoidSystem<ScaleSystem>> VoidSystemScaleSystem;
    }
    [Inject] public ScaleGroup Scale;

    protected override void OnUpdate()
    {
        for (int i = 0; i < Scale.Length; i++)
        {
            var scale = Scale.Scale[i].Value;
            Scale.TransformMatrix[i] = new TransformMatrix { Value = math.mul(Scale.TransformMatrix[i].Value, new float4x4(scale.x, 0, 0, 0, 0, scale.y, 0, 0, 0, 0, scale.z, 0, 0, 0, 0, 1)) };
        }
    }
}

@NudelGames Thank you for this, the cleanest solution that doesn’t involve modifying the ECS source.

And for anyone looking to preset the scale value:

        var scaleComponents = new NativeArray<Scale>(scaleCount, Allocator.TempJob);
        var setScaleJob = new MemsetNativeArray<Scale>()
        {
            Source = scaleComponents,
            Value = new Scale()
            {
                Value = new float3(1f)
            }
        }.Schedule(scaleCount, 64);

Hey Caiuse,

I just started playing with Pure ECS, and I don’t really get your code.

What do you mean by preset abd how do you use it?

I’m using NudelGames Code to spawn trees that are different size

entityManager.SetComponentData(tree, new Scale { Value = new float3(ecosystemSettings.treeSX, ecosystemSettings.treeSY, ecosystemSettings.treeSZ) });

Should I use your method?

So I have been looking into scaling and noticed that TransformMatrix no longer exists. So looking around the samples found a solution around it:

using System;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine.Jobs;

public class ScaleSystem : JobComponentSystem
{
struct Resources
{
public readonly int Length;

public ComponentDataArray<Scale> scales;
public TransformAccessArray Transforms;
}
public struct ScaleGroup : IJobParallelForTransform
{
public ComponentDataArray<Scale> scales;
public void Execute(int index, TransformAccess transform)
{
transform.localScale = scales[index].scale;
}

}

[Inject]
Resources _resources;

protected override JobHandle OnUpdate(JobHandle inputDeps)
{
ScaleGroup job = new ScaleGroup
{
scales = _resources.scales
};
return job.Schedule(_resources.Transforms, inputDeps);
}
}


using System;
using Unity.Entities;
using Unity.Mathematics;

[Serializable]
public struct Scale : IComponentData {
    public float3 scale;
}

[UnityEngine.DisallowMultipleComponent]
public class ScaleComponent : ComponentDataWrapper<Scale> { }

I can see the scale properly being applied on the transform of the GameObject, but the game object itself doesn’t change size. scale 1 or scale 250. I added hard coded scales in the code above for testing purpose, the working code would be the one commented out. If anybody knows why it sets transform local scale correctly but fails to actually resize the mesh, would be greatly appreciated :slight_smile:

When im try to use this - Scale is not float3 it just float
Cannot implicitly convert type ‘Unity.Mathematics.float3’ to ‘float’

tl;dr got it to work, code below

I saw this as well. It initially seemed like NonUniformScale was the way to go but…

Likewise here, when I set the Unity.Transforms.Scale component.

I ended up changing the prefab’s scale just to see what’d happen. Luckily, ConvertToEntity then added a new component called CompositeScale. I just modified this component’s float4x4 for the appropriate scaling. To generate the float4x4, it looks like Unity.Mathematics.float4x4 has a number of nice Scale utilities. For a simple “hello world” Unity scaling demo in ECS, create a new project:

  1. add a cube prefab with a ConvertToEntity component.
  2. Change the prefab’s scale, so that ConvertToEntity creates a CompositeScale component during conversion.
  3. add the following system.
using Unity.Entities;
using Unity.NetCode;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Transforms;
using Unity.Collections;
using Unity.Physics.Systems;
using Unity.Physics.Extensions;

public class TestComponentSystem : ComponentSystem
{
    protected override void OnUpdate()
    {
        Entities.ForEach((ref CompositeScale scale) =>
        {
            scale.Value = float4x4.Scale(2, 1, 4);
        });
    }
}

Notes:

  • Steps 1 and 2 can be replaced with your favorite method of spawning an entity with a CompositeScale component. I just found the above approach (prefab → convert → instantiate prefab if necessary), used in the netcode tutorial, to be easiest.
  • Note the above will discard the original state. Maybe this is obvious to some, but just pointing out that you likely want something like math.mul(scale.Value, float4x4.Scale(…)).
2 Likes