# Question What is the principle of transform?

Discussion in 'Scripting' started by NeverMore_, Nov 29, 2023.

1. ### NeverMore_

Joined:
Apr 16, 2021
Posts:
7
I have a requirement to simulate the hierarchical relationship of transform in a thread. Finally, the gameobject will be generated in the ui thread based on the simulated data.

In unity, Transform has localposition, localrotation, and localscale attributes. When setting parent to Transform, localposition, localrotation, and localscale will change accordingly. What is this change?

My guess is to convert the local information of the current Transform into a matrix, and then use its original parent's localtoworldmatrix to act on the matrix to get its information in the world. Then use the new parent's worldtolocalmatrix to act on the world matrix to get the final matrix, from which localposition, localrotation and localscale are extracted.

But currently I have implemented a Transform logic based on this idea. It is ok when there are only two layers in the parent-child hierarchy. After three layers, there is a big deviation from the one created with gameobject.

This is my demo, what is the problem?

#### Attached Files:

• ###### virtual-transform-master.zip
File size:
24.3 KB
Views:
20
Last edited: Nov 29, 2023
2. ### Kurt-Dekker

Joined:
Mar 16, 2013
Posts:
38,287
Nothing like THAT is gonna be happening!!!

For two, 100% of EVERY GameObject will always be created on the main thread where your script runs.

Transform hierarchal relationships are simply a cascading series of matrix transforms.

https://www.gamedev.net/tutorials/p...s/making-a-game-engine-transformations-r3566/

3. ### Bunny83

Joined:
Oct 18, 2010
Posts:
3,845
I think that your "Scale" extension method has a flaw. It should only consider the top left 3x3 matrix and not the full 4d columns to extract the scale. A single TRS matrix has the bottom row all zeros (except for the last one which is 1). However when you have combined matrices those values usually are not 0 as they get influenced by the translation of the parent(s). Though those should not be considered when extracting the scale. Rotation and scale only happens in the 3x3 part of the matrix. The 4th dimension is just added for the homogeneous coordinates in order to do translations as well.

@Kurt-Dekker I think with UI thread he actually means the main thread. In most systems the UI thread is the main thread of the application and a lot of systems have the same restriction as Unity. That's why there's usually some kind of "runOnUiThread" mechanism. Unity doesn't really have that out of the box but you can roll your own "runOnMainThread" quite easily.

angrypenguin likes this.
4. ### NeverMore_

Joined:
Apr 16, 2021
Posts:
7
The ui thread I'm talking about is the main thread. I also understand that the change of transformation is the change of matrix. It's just that when I implemented it through this set of theories, the results were not consistent with the unified one. So I'm not sure which stage the problem is.

Code (CSharp):
1. using System;
2. using System.Collections.Generic;
3. using System.Linq;
4. using System.Text;
6. using UnityEngine;
7.
8.
9. public class VirtualTransform
10. {
11.     private VirtualTransform _parent;
12.     private Vector3 _localPosition = Vector3.zero;
13.     private Quaternion _localRotation = Quaternion.identity;
14.     private Vector3 _localScale = Vector3.one;
15.
16.     public List<VirtualTransform> Children { get; } = new List<VirtualTransform>();
17.
18.     public VirtualTransform Parent
19.     {
20.         get => _parent; set
21.         {
22.             if (_parent == value) return;
23.             if (_parent != null)
24.                 _parent.Children.Remove(this);
25.             if (value == null)
26.             {
27.                 ApplyMatrix(_parent.LocalToWorldMatrix * Matrix);
28.                 _parent = null;
29.                 return;
30.             }
31.
32.             var worldMatrix = Matrix;
33.             if (_parent != null)
34.                 worldMatrix = _parent.LocalToWorldMatrix * worldMatrix;
35.             _parent = value;
37.             var matrix = _parent.WorldToLocalMatrix * worldMatrix;
38.             ApplyMatrix(matrix);
39.         }
40.     }
41.
42.     void ApplyMatrix(Matrix4x4 matrix)
43.     {
44.         _localPosition = matrix.Position();
45.         _localRotation = matrix.Rotation();
46.         _localScale = matrix.Scale();
47.     }
48.
49.     public Vector3 LocalPosition { get => _localPosition; set => _localPosition = value; }
50.     public Quaternion LocalRotation { get => _localRotation; set => _localRotation = value; }
51.     public Vector3 LocalScale { get => _localScale; set => _localScale = value; }
52.
53.     public Vector3 Position
54.     {
55.         get
56.         {
57.             if (_parent == null)
58.                 return _localPosition;
59.             return _parent.LocalToWorldMatrix.MultiplyPoint(_localPosition);
60.         }
61.         set
62.         {
63.             if (_parent == null)
64.                 _localPosition = value;
65.             else
66.                 _localPosition = _parent.WorldToLocalMatrix.MultiplyPoint(value);
67.         }
68.     }
69.     public Quaternion Rotation
70.     {
71.         get
72.         {
73.             if (_parent == null)
74.                 return _localRotation;
75.             return (_parent.LocalToWorldMatrix * Matrix4x4.Rotate(_localRotation)).Rotation();
76.         }
77.         set
78.         {
79.             if (_parent == null)
80.                 _localRotation = value;
81.             else
82.                 _localRotation = (_parent.WorldToLocalMatrix * Matrix4x4.Rotate(value)).Rotation();
83.         }
84.     }
85.     public Vector3 Scale
86.     {
87.         get
88.         {
89.             if (_parent == null)
90.                 return _localScale;
91.             return (_parent.LocalToWorldMatrix * Matrix4x4.Scale(_localScale)).Scale();
92.         }
93.     }
94.
95.     Matrix4x4 Matrix => Matrix4x4.TRS(LocalPosition, LocalRotation, LocalScale);
96.
97.     public Matrix4x4 LocalToWorldMatrix
98.     {
99.         get
100.         {
101.
102.             var parent = Parent;
103.             var matrix = Matrix;
104.             while (parent != null)
105.             {
106.                 matrix = parent.Matrix * matrix;
107.                 parent = parent.Parent;
108.             }
109.             return matrix;
110.         }
111.     }
112.     public Matrix4x4 WorldToLocalMatrix => LocalToWorldMatrix.inverse;
113.
114.
115.     #region create unity gameobject
116.     public void Create()
117.     {
118.         Create(null, this);
119.     }
120.
121.     static GameObject CreateBasic()
122.     {
123.         var obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
124.         obj.name = "Virtual_" + obj.name;
125.         //var obj = new GameObject();
128.         return obj;
129.     }
130.
131.     static void Create(Transform root, VirtualTransform @object)
132.     {
133.         var obj = CreateBasic();
134.         obj.transform.parent = root;
135.         obj.transform.localPosition = @object.LocalPosition;
136.         obj.transform.localRotation = @object.LocalRotation;
137.         obj.transform.localScale = @object.LocalScale;
138.
139.         for (int i = 0; i < @object.Children.Count; i++)
140.         {
141.             var child = @object.Children[i];
142.             Create(obj.transform, child);
143.         }
144.     }
145.     #endregion
146. }
147.
148.
Code (CSharp):
1. using System;
2. using System.Collections.Generic;
3. using System.Linq;
4. using System.Text;
5. using UnityEngine;
6.
7.
8. public static class MatrixExtension
9. {
10.     public static Vector3 Position(this Matrix4x4 matrix)
11.     {
12.         return new Vector3(matrix.m03, matrix.m13, matrix.m23);
13.     }
14.
15.     public static Vector3 Scale(this Matrix4x4 matrix)
16.     {
17.         Vector3 scale;
18.         scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
19.         scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
20.         scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
21.         return scale;
22.     }
23.
24.     public static Quaternion Rotation(this Matrix4x4 matrix)
25.     {
26.         return Quaternion.LookRotation(matrix.GetColumn(2), matrix.GetColumn(1));
27.     }
28.
29. }
30.
31.

5. ### halley

Joined:
Aug 26, 2013
Posts:
2,279
I don't see why you're simulating all this, rather than just constructing the hierarchy. Probably some academia nonsense.

Each matrix for all objects represents the local position, rotation, scale. You can generate that matrix with the Matrix4x4.TRS() method. When you want to know the world space position, rotation, and scale, Transform will multiply them in the same sequence they appear in the hierarchy branch. When Unity is traversing through the entire hierarchy, it does this and caches the results, so you always have access to the world space; and conversely when you set the world space values, it calculates the inverse matrix, so it can update the local space matrix too.

As Bunny points out, once you try to combine non-uniform scale and any rotation, the matrix can end up having muddy values outside the classical TRS matrix. This is one reason the Transform calls the function to get the approximate world scale "lossy scale."

DragonCoder likes this.
6. ### NeverMore_

Joined:
Apr 16, 2021
Posts:
7
We are making a game with UGC. Players may create a lot of things, there may be many levels, and the mesh will be very large. Loading the user's works again will become extremely time-consuming, causing the game to freeze. Of course, coroutines can solve some problems, but when we load user works, there will still be intensive operations, so consider directly putting them into sub-threads to perform operations, get the final results, and then create gameobjects in the main thread, which will solve the lag problem.

7. ### zulo3d

Joined:
Feb 18, 2023
Posts:
758
I've had experience in user created worlds and any freeze you experience when loading the worlds may be a result of instantiating lots of objects with colliders that need to be added to the physics engine's internal data structures. My workaround was to instantiate the objects with their colliders previously disabled and then gradually enabling them over several frames. Doing this meant that I could load the worlds instantly but the downside was that the player was briefly a ghost.

8. ### NeverMore_

Joined:
Apr 16, 2021
Posts:
7
Our current main lag is caused by the huge amount of mesh and hierarchical structures. We mainly do construction production, and there is no demand for collisions and the like for the time being.

9. ### DragonCoder

Joined:
Jul 3, 2015
Posts:
1,635
You should have started the thread with this info

Rolling out your own threading is extremely rarely a good idea in Unity.
Have you heard of the Job System and Burst compiler? Unity has an extremely performanct and very easily parallelizable system for mass processing data.
That should be the go-to for processing massive stuff at runtime or during loading steps.

Kurt-Dekker likes this.