Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Update scale of object without temporarily re-parenting...

Discussion in 'Scripting' started by numberkruncher, Feb 17, 2013.

  1. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    953
    Hey guys

    I am using the following technique to apply a specific scale to a game object which is working flawlessly. The benefit with the following approach is that I am able to modify the "local" scale of the object without causing any distortion to occur.

    Object Structure:
    Code (csharp):
    1.  
    2. Outer Container
    3. ---> Chunk
    4. --------> An Object
    5. ---------------> An Attachment
    6.  
    The above object structure is very important and cannot be changed at all :)

    I am using the following logic (which is working fine):
    Code (csharp):
    1.  
    2. // Temporarily un-parent attachment
    3. attachmentTransform.parent = chunkTransform;
    4. // Set scale of attachment object (could be any value):
    5. attachmentTransform.localScale = Vector3.one;
    6. // Re-parent attachment to its object
    7. attachmentTransform.parent = anObjectTransform;
    8.  
    The process of re-parenting the attachment object automatically recomputes its position, rotation and scale so that it is local to the new parent object. In the above I have utilised that characteristic to achieve what I need.

    Whilst I have something that works fine, the process of re-parenting objects is relatively slow. If possible I would like to re-implement the above using some maths to replicate the behaviour that occurs when objects are re-parented. The aim here is to improve performance by avoiding the need to re-parent.

    The new scale "Vector3.one" is local to the space of the "Chunk" object.

    I have taken many stabs at improving this with many alternations of the following broken implementation. The following implementation does not work at all. For some reason the object becomes distorted and does not resemble an equivalent to the above.
    Code (csharp):
    1.  
    2. Matrix4x4 LocalSpaceToLocalSpace(Transform from, Transform to) {
    3.     return from.localToWorldMatrix * to.worldToLocalMatrix;
    4. }
    5.  
    6. Matrix4x4 convert = LocalSpaceToLocalSpace(chunkTransform, anObjectTransform);
    7. attachmentTransform.localScale = convert.MultiplyPoint3x4(Vector3.one);
    8.  
    I understand that object distortion occurs due to the way in which matrices are applied, though this must be possible because re-parenting works! Ideally I want the mathematical effect of re-parenting without the performance overhead that occurs (processing static colliders and other complicated stuff that I don't know about...)
     
  2. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    953
    Ah that's interesting, if I use an empty "dummy" object for the re-parenting then the performance is significantly better. Obviously a mathematical non-hack approach would still be preferred, though it looks like I have a workable improvement.

    Executing the first script from above with 100,000 samples:
    Total execution time: 19.63 seconds
    Average time per sample: 0.0001963 seconds

    Executing the following script with 100,000 samples:
    Total execution time: 0.0004224 seconds
    Average time per sample: 4.224e-09 seconds

    Clearly an improvement!

    I should clarify that the attachment in question can be described as follows:
    Code (csharp):
    1.  
    2. "Attachment Object" [ Transform, SphereCollider, CustomMonoBehaviour ]
    3.      |---> "mesh" [ Transform, MeshFilter, MeshRenderer ]
    4.  
    The new version of the script:
    Code (csharp):
    1.  
    2. // Create and cache a temporary object
    3. if (temp == null) {
    4.     GameObject tempGO = new GameObject();
    5.     tempGO.hideFlags = HideFlags.HideAndDontSave;
    6.     temp = tempGO.transform;
    7. }
    8.  
    9. // Place temporary object within space of chunk
    10. temp.parent = chunkTransform;
    11. // Set scale of attachment object (could be any value):
    12. temp.localScale = Vector3.one;
    13. // Re-parent temporary object into object
    14. temp.parent = anObjectTransform;
    15. // Copy scale from temporary object to attachment object
    16. attachmentTransform.localScale = temp.localScale;
    17.  
    18. // Move temporary object to safety!
    19. temp.parent = null;
    20.  
     
    Last edited: Feb 18, 2013
  3. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    953
    Okay, I have figured out how to achieve this purely with matrices. Essentially I can perform all of the calculations using matrices like I wanted. The matrix must be decomposed and assigned to the transform component to avoid the distortion effect that otherwise occurs. According to the Unity documentation it seems that scale can only be accurately represented using a 3x3 matrix (which makes a lot of sense).

    So, here is how I decomposed the matrix:
    Code (csharp):
    1.  
    2. Matrix4x4 worldMatrix = CalculateTransformMatrixInWorldSpace();
    3. Matrix4x4 m = chunkTransform.worldToLocalMatrix * worldMatrix;
    4.  
    5. // Extract new local position
    6. attachmentTransform.localPosition = m.GetColumn(3);
    7.  
    8. // Extract new local rotation
    9. attachmentTransform.localRotation = Quaternion.LookRotation(m.GetColumn(2), m.GetColumn(1));
    10.  
    11. // Extract new local scale
    12. attachmentTransform.localScale = new Vector3(
    13.     m.GetColumn(0).magnitude,
    14.     m.GetColumn(1).magnitude,
    15.     m.GetColumn(2).magnitude
    16. );
    17.  
    And this works perfectly for me and is a much better solution than re-parenting!
     
    Last edited: Feb 19, 2013
  4. trzmiel

    trzmiel

    Joined:
    Nov 5, 2013
    Posts:
    28
    What is CalculateTransformMatrixInWorldSpace() ?
     
  5. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    953
    Some custom function which creates a transformation matrix in world space.
     
  6. trzmiel

    trzmiel

    Joined:
    Nov 5, 2013
    Posts:
    28
    Could you provide a code, please :)
     
  7. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    953
    This was merely pseudo-code representing a function which produces a matrix in world space for the purpose of the above example. In my scenario this is a series of more complicated calculations. This will almost certainly be different to suit your use case.

    You may find one of the following simple examples useful:
    Code (csharp):
    1.  
    2. // Version 1 - Transform matrix from a game object:
    3. Matrix4x4 CalculateTransformMatrixInWorldSpace() {
    4.     return someGameObject.transform.localToWorldMatrix;
    5. }
    6.  
    7. // Version 2 - Some calculated matrix:
    8. Matrix4x4 CalculateTransformMatrixInWorldSpace() {
    9.     Vector3 position = CalculateYourPosition();
    10.     Quaternion rotation = CalculateYourRotation();
    11.     Vector3 scale = CalculateYourScale();
    12.  
    13.     return Matrix4x4.TRS(position, rotation, scale);
    14. }
    15.  
     
  8. trzmiel

    trzmiel

    Joined:
    Nov 5, 2013
    Posts:
    28
    Thank you very much. I spent all day finding a sollution.
    You are genial!
     
  9. wrobel221

    wrobel221

    Joined:
    Jan 21, 2013
    Posts:
    5
    Hi, Does anyone know if using localToWorldMatrix/worldToLocalMatrix multiple times is cached internally? I sometimes have a deep Transform tree and I converted to using only local matrices obtained from localPosition/localRotation/localScale. Then I multiply them to get localToLocal as there's not much distance in tree between two locals.
    So question again. What's better/more efficient to use in a deep Transform tree?
    • localToWorld*worldToLocal or,
    • manually obtained localToLocalMatrix between 2-3 transforms.