Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Resolved Changing local Mesh Vertices into World Space Coordinates

Discussion in 'Scripting' started by SannoGroup, Aug 24, 2021.

  1. SannoGroup

    SannoGroup

    Joined:
    Jun 24, 2021
    Posts:
    2
    Hello
    I have a Problem, for a Project I need to find the World Space Coordinates of a mesh.
    I'm importing .fbx models into Unity consisting of a ParentObject and three Childobjects with a Mesh attached to each of them.
    The .fbx Model should be exchangable, so for User friendlyness I only want to attach one script to the Parent Object.
    The endgoal would be to find the corners of the Model so I could measure it's dimensions. So I need to find all Points on the three Meshes, which have the same World Space Coordinates.

    So I wrote my script, getting the Meshes from the MeshFilter Component of the Child Objects, turn the Local Vetices into World Coordinates using transform.TransformPoint() and store them in a new Array.

    The Program doesn't give any Errors but the World Coordinates don't seem quite right.

    To find the Bug I build a Cube Parent Object with three Quads for front, right side and top.
    The Parent Object has the world Position (1,-1,5) and Rotaion (0,0,0)
    Quad 1 has the local Position (0,0,-0.5) and Rotation (0,0,0)
    Quad 2 has the local Position (0,0.5,0) and Rotation (90,0,0)
    Quad 3 has the local Position (0.5,0,0) and Rotation (0,-90,0)

    The local Coordinates for the four Points of each Mesh are:
    P1 (-0.5,-0.5,0)
    P2 (0.5,-0.5,0)
    P3 (-0.5,0.5,0)
    P4 (0.5,0.5,0)

    Running my script I get the same World Coordinates for all three Meshes:
    P1(0.5,-1.5,5)
    P2(1.5,-1.5,5)
    P3(0.5,-0.5,5)
    P4(1.5,-0.5,5)

    The real World Position of P1 should be
    Mesh1 (0.5,-1.5,4.5)
    Mesh2 (0.5,-0.5,4.5)
    Mesh3 (1.5,-1.5,4.5)

    Seems like transform.TransformPoint() simply adds the World Position of the ParentObject to the local Vertex Position of the Mesh without considering their Position or Rotation.

    Is there a better Way to transform into World space or a better way to access the Mesh Data.

    I'm still quite new to scripting, thanks for the help.


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class LocalToWorld : MonoBehaviour
    6. {
    7.     private Mesh Mesh1;
    8.     private Mesh Mesh2;
    9.     private Mesh Mesh3;
    10.  
    11.     int a;  
    12.  
    13.     // Start is called before the first frame update
    14.     void Start()
    15.     {
    16.         //Get Meshes from Child Objects
    17.         Mesh1 = gameObject.transform.GetChild(0).GetComponent<MeshFilter>().sharedMesh;
    18.         Mesh2 = gameObject.transform.GetChild(1).GetComponent<MeshFilter>().sharedMesh;
    19.         Mesh3 = gameObject.transform.GetChild(2).GetComponent<MeshFilter>().sharedMesh;
    20.  
    21.         //Create new Arrays for World Coordinates
    22.         a = Mesh1.vertexCount;
    23.         Vector3[] WorldVert1 = new Vector3[a];
    24.  
    25.         a = Mesh1.vertexCount;
    26.         Vector3[] WorldVert2 = new Vector3[a];
    27.  
    28.         a = Mesh1.vertexCount;
    29.         Vector3[] WorldVert3 = new Vector3[a];
    30.  
    31.         //Transfer Local Vertex Coordinates into World Coordinates
    32.         for (int i = 0; i < Mesh1.vertexCount; i++)
    33.         {
    34.             WorldVert1[i] = gameObject.transform.TransformPoint(Mesh1.vertices[i]);          
    35.         }
    36.  
    37.         for (int i = 0; i < Mesh2.vertexCount; i++)
    38.         {
    39.             WorldVert2[i] = gameObject.transform.TransformPoint(Mesh2.vertices[i]);          
    40.         }
    41.  
    42.         for (int i = 0; i < Mesh3.vertexCount; i++)
    43.         {
    44.             WorldVert3[i] = gameObject.transform.TransformPoint(Mesh3.vertices[i]);          
    45.         }
    46.  
    47.         Debug.Log("Mesh1 Vertex0" + "\nLocal" + Mesh1.vertices[0] + " World" + WorldVert1[0]);
    48.         Debug.Log("Mesh2 Vertex0" + "\nLocal" + Mesh2.vertices[0] + " World" + WorldVert2[0]);
    49.         Debug.Log("Mesh3 Vertex0" + "\nLocal" + Mesh3.vertices[0] + " World" + WorldVert3[0]);
    50.     }
    51.  
    52.     // Update is called once per frame
    53.     void Update()
    54.     {
    55.        
    56.     }
    57. }
     

    Attached Files:

  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,153
    Code (csharp):
    1. public Vector3 LocalPointToWorld(Vector3 p) => transform.localToWorldMatrix.MultiplyPoint3x4(p);
    or simpler
    Code (csharp):
    1. public Vector3 LocalPointToWorld(Vector3 p) => transform.TransformPoint(p);
    transform
    refers to a transform where the MeshFilter is.
     
  3. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,086
    Yes and no. A Transform will transform any position from its local space to world space (including rotation and scale), so you need to make sure you're using the right Transform that matches the coordinate space of the mesh.

    You're always using
    gameObject.transform.TransformPoint
    , which transforms the vertex positions from the parent Transform's space to the world space, but the meshes are in the child transforms' coordinate spaces. For each mesh, you need to use the mesh's transform to do the conversion properly (e.g.
    gameObject.transform.GetChild(0)
    etc).

    Note 1: It seems like you're just calculating the mesh bounds. Using Mesh.bounds (local space) or MeshRenderer.bounds (already world-space) might be much easier.

    Note 2: No need for
    gameObject.transform
    , just
    transform
    works inside scripts.
     
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,153
    Consider the following facts when it comes to transforms.

    Unity keeps these three information as separate entities: position (Translation), Rotation (or better: orientation), Scale (and euler angles, but that's a different topic).

    Additionally it composes a 4x4 matrix that is a mathematical representation of TRS, which is normally used for fast transformations and matrix multiplication (order of which is represented by the hierarchy).

    When you are inside a child of a hierarchy, you have to integrate the matrices back to the root, and so the parent will multiply the local matrix, in a cascade. You can get this cascade by using
    localToWorldMatrix
    .

    worldToLocalMatrix
    is a matrix inverse of that.

    For your convenience, Unity transforms also provide shortcut methods:
    TransformPoint
    ,
    TransformDirection
    ,
    TransformVector
    .

    TransformPoint
    will TRS your point, and thus is the most "expensive" (it's not really that much expensive compared to TransformVector, only on paper)
    TransformDirection
    will only R your vector, because it assumes it's just a vector (no translation), and of unit length (no scale)
    TransformVector
    will RS your vector, so no translation, but will scale it
    You also have access to inversions of this, i.e.
    InverseTransformPoint
    transforms a world point back to local space.

    You can always extract TRS values back from the matrix, however due to the math involved, it can be very sloppy and slow, at least when it comes to R and S (and this is why the world S is called lossyScale). This is known as matrix decomposition. To work around this, Unity keeps the original localRotation (as a Quaternion) and original localScale (as a Vector3). Translation is the only one that you can cleanly extract from the last column of any matrix.

    Thus,
    TransformDirection(p)
    is basically the same thing as
    rotation * p

    All in all, a plethora of ways to get properly confused.

    And as far as I can tell,
    TransformPoint
    does exactly what
    localToWorldMatrix.MultiplyPoint3x4
    does, which is a faster variant of the regular
    MultiplyPoint
    because it ignores the eventuality of an affine transformation, a non-standard transformation that numerically surpasses the common usage or simply moving, rotating and scaling something.

    Just FYI
     
    HumStuff likes this.
  5. SannoGroup

    SannoGroup

    Joined:
    Jun 24, 2021
    Posts:
    2
    That did the trick

    Thank you everyone for the quick and detailed answers.