Changing local Mesh Vertices into World Space Coordinates

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.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LocalToWorld : MonoBehaviour
{
    private Mesh Mesh1;
    private Mesh Mesh2;
    private Mesh Mesh3;

    int a;   

    // Start is called before the first frame update
    void Start()
    {
        //Get Meshes from Child Objects
        Mesh1 = gameObject.transform.GetChild(0).GetComponent<MeshFilter>().sharedMesh;
        Mesh2 = gameObject.transform.GetChild(1).GetComponent<MeshFilter>().sharedMesh;
        Mesh3 = gameObject.transform.GetChild(2).GetComponent<MeshFilter>().sharedMesh;

        //Create new Arrays for World Coordinates
        a = Mesh1.vertexCount;
        Vector3[] WorldVert1 = new Vector3[a];

        a = Mesh1.vertexCount;
        Vector3[] WorldVert2 = new Vector3[a];

        a = Mesh1.vertexCount;
        Vector3[] WorldVert3 = new Vector3[a];

        //Transfer Local Vertex Coordinates into World Coordinates
        for (int i = 0; i < Mesh1.vertexCount; i++)
        {
            WorldVert1[i] = gameObject.transform.TransformPoint(Mesh1.vertices[i]);           
        }

        for (int i = 0; i < Mesh2.vertexCount; i++)
        {
            WorldVert2[i] = gameObject.transform.TransformPoint(Mesh2.vertices[i]);           
        }

        for (int i = 0; i < Mesh3.vertexCount; i++)
        {
            WorldVert3[i] = gameObject.transform.TransformPoint(Mesh3.vertices[i]);          
        }

        Debug.Log("Mesh1 Vertex0" + "\nLocal" + Mesh1.vertices[0] + " World" + WorldVert1[0]);
        Debug.Log("Mesh2 Vertex0" + "\nLocal" + Mesh2.vertices[0] + " World" + WorldVert2[0]);
        Debug.Log("Mesh3 Vertex0" + "\nLocal" + Mesh3.vertices[0] + " World" + WorldVert3[0]);
    }

    // Update is called once per frame
    void Update()
    {
       
    }
}


public Vector3 LocalPointToWorld(Vector3 p) => transform.localToWorldMatrix.MultiplyPoint3x4(p);

or simpler

public Vector3 LocalPointToWorld(Vector3 p) => transform.TransformPoint(p);

transform refers to a transform where the MeshFilter is.

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.

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

1 Like

That did the trick

Thank you everyone for the quick and detailed answers.