Converting Mesh coordinate system

Hi, I have a trial licence of Aspose.3D, we’re looking at using it to improve some of our processes.

Our requirement is to transform the coordinate system of FBX/OBJ files.

The input FBX/OBJ files are in a coordinate system localed in say the UK (OSGB), and they need to be go through a number of conversions. OSGB -> Lat/Long, Lat/Long -> Local Eeast North Up grid.

We’ve succesffuly implemented this in the past using the Assimp library, but this comes with its limitations which I won’t go into, so we’re looking at Aspose.

I’m trying to understand the documentation for Meshes and figure out what would be the most efficient way to move / copy a mesh to export a new FBX/OBJ file.

Would looping over each mesh within the root node, then looping through all of the control points, assigning the transformation to each control point, clearing the mesh control points then assigning the new transformed control points do the trick? Or are their other vertices which need to be transformed? If control points aren’t the only points assigned to a mesh - how do I get them all?

Thanks,
Tom

Would the most appropriate way be to create a new tri mesh, assign that to a new scene and then export? FYI we don’t need any other metadata, only the geometry/texture/materials that was within the original FBX/OBJ.

This thread:

mentions that we can edit the mesh directly within the mesh class - do you have an example of updating the coordinates for a mesh so we can re-export?

Is there also a way to remove any 2D geometry before export? (lines/curves)

Thanks

We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

  Issue ID(s): THREEDNET-1324

You can obtain Paid Support services if you need support on a priority basis, along with the direct access to our Paid Support management team.

@asad.ali, thanks, but I don’t think that there is an issue or a fix required - it’s more for advice

@TPovey2

Actually, that was an automated generated response when we attached ticket ID with this forum. The ticket is an investigation ticket logged to analyze your requirements further and share our feedback with you once it is closed.

Understood, thanks @asad.ali

Any news on this?
I’ve managed to convert the coordinates to a degree, but the output scene is rotated by 180 degrees. How do we rotate the scene? Or just rotate the mesh - as this is all I am interested in.
Thanks

@TPovey2

We are afraid that the earlier logged ticket has not been yet resolved. However, we have recorded your concerns under the ticket and will surely inform you as soon as we make some progress towards its resolution. Please be patient and spare us some time.

We are sorry for the inconvenience.

1 Like

FYI this can be closed - figured out what I needed to do.

@TPovey2

You can use Node.Accept to pass a node visitor to access all nodes inside a scene, and multiply the global transform matrix to all control points of the geometries, then reset the node’s transform:

            //first step, apply node's transform into mesh
            input.RootNode.Accept((node) =>
            {
                var globalTransform = node.EvaluateGlobalTransform(true);
                foreach (var entity in node.Entities)
                {
                    if(entity is Geometry geom)
                    {
                        for(int i = 0; i < geom.ControlPoints.Count; i++)
                        {
                            geom.ControlPoints[i] = globalTransform * geom.ControlPoints[i];
                        }
                    }
                }
                return true;
            });
            //second step, reset node's transform, because we've done the transform inside the geometry's control point
            input.RootNode.Accept((node) =>
            {
                node.Transform.TransformMatrix = Matrix4.Identity;
                return true;
            });
            input.Save("output.fbx");

If you want to change the coordinate system, you can construct a matrix to do this by multiplying it to the globalTransform matrix in the above code:

// (X,Y,Z) -> (X, -Z, Y):
            var xnzy = new Matrix4(
                1, 0, 0, 0,
                0, 0, 1, 0,
                0, -1, 0, 0,
                0, 0, 0, 1);
// or (X,Y,Z) -> (X, Z, Y):
            var xzy = new Matrix4(
                1, 0, 0, 0,
                0, 0, 1, 0,
                0, 1, 0, 0,
                0, 0, 0, 1); 

To exclude object from exporting, you can set Node.Exclude or Entity.Exclude to true, or remove them from the scene tree.

Thanks for the info. I’m having some issues when the nodes transformation matrix isn’t equal to the Idendity Matrix. Everything works as expected when the nodes identity is equal to the identity.

        protected override void VisitNodeCore(Node node)
        {
            var globalTransform = node.EvaluateGlobalTransform(true);
            for (int i = 0; i < node.Entities.Count; i++)
            {
                var entity = node.Entities[i];
                if (entity is Aspose.ThreeD.Entities.Geometry geom)
                {
                    for (int j = 0; j < geom.ControlPoints.Count; j++)
                    {
                        var v = globalTransform * geom.ControlPoints[j];
                        var transformed = VectorToECEF(v);
                        geom.ControlPoints[j] = transformed;
                        Console.WriteLine(transformed.ToString());
                    }
                }
                else
                {
                    entity.Excluded = true;
                }
            }
        }

        // then...
        input.RootNode.Accept((node) =>
        {
            node.Transform.TransformMatrix = Matrix4.Identity;
            return true;
        });

The above re-assigns the control points to a point which is located around 0,0,0. These are all the new Control points:

(-8.366897 -2.087051 -0.000002 1)
(6.689811 -5.441429 -0.000002 1)
(-6.689813 5.441432 -0.000001 1)
(8.366895 2.087054 -0.000002 1)
(-8.366897 -2.087051 0.492125 1)
(-6.689813 5.441432 0.492125 1)
(6.689811 -5.44143 0.492124 1)
(8.366895 2.087054 0.492125 1)
(8.224164 1.446331 0.492125 1)
(8.366895 2.087054 0.492125 1)
(7.583453 1.589071 0.492125 1)
(-6.191833 4.657971 0.492125 1)
(-6.689813 5.441432 0.492125 1)
(-6.832544 4.80071 0.492125 1)
(-6.832547 4.800712 10.564299 1)
(-6.689816 5.441434 10.564299 1)
(-6.191836 4.657972 10.564299 1)
(7.583457 1.589071 10.564299 1)
(8.366899 2.087054 10.564299 1)
(8.224168 1.446332 10.564299 1)
(6.049099 -5.298694 0.492125 1)
(6.19183 -4.657968 0.492125 1)
(6.049102 -5.298697 10.564299 1)
(6.191833 -4.657971 10.564299 1)
(7.583457 1.589071 10.564299 1)
(7.583453 1.589071 0.492125 1)
(8.224164 1.446331 0.492125 1)
(6.689811 -5.44143 0.492124 1)
(6.689814 -5.441433 10.564298 1)
(8.224168 1.446332 10.564299 1)
(6.19183 -4.657968 0.492125 1)
(6.191833 -4.657971 10.564299 1)
(-7.583456 -1.589068 0.492125 1)
(-7.583459 -1.58907 10.564299 1)
(-8.224167 -1.446329 0.492125 1)
(-8.224171 -1.44633 10.564299 1)
(-8.366901 -2.087052 10.564299 1)
(6.049102 -5.298697 10.564299 1)
(-8.366897 -2.087051 0.492125 1)
(6.049099 -5.298694 0.492125 1)
(-6.191836 4.657972 10.564299 1)
(-6.191833 4.657971 0.492125 1)
(-7.583459 -1.58907 10.564299 1)
(-7.583456 -1.589068 0.492125 1)
(-6.832547 4.800712 10.564299 1)
(-8.224171 -1.44633 10.564299 1)
(-6.832544 4.80071 0.492125 1)
(-8.224167 -1.446329 0.492125 1)

Since each point is now very close to the origin, and I have set the TransformMatrix of every node to the identity matrix I would expect the model to be located in the right place.

When I open it up in a programme such as navisworks, I expect these points to be similar to the ones posted above. Even if the units are incorrect, it shouldn’t be this far away from the origin. Is there something I’m missing?

image.png (20.3 KB)

Just prior to saving the model, this is where I set the transformation matrix of each node:

                rootNode.Accept((node) =>
                {
                    //var globalTransform = node.EvaluateGlobalTransform(false);
                    node.Transform.TransformMatrix = Matrix4.Identity;
                    for (int i = 0; i < node.Entities.Count; i++)
                    {
                        var entity = node.Entities[i];
                        if (entity is Aspose.ThreeD.Entities.Geometry geom)
                        {
                            Console.WriteLine(node.Transform.TransformMatrix.ToString());

                            for (int j = 0; j < geom.ControlPoints.Count; j++)
                            {
                                Console.WriteLine(geom.ControlPoints[j].ToString());
                            }
                        }
                        else
                        {
                            entity.Excluded = true;
                        }
                    }

                    return true;
                });

Printing the matrix/points here shows that we have the identity matrix for the nodes and each vector is very close to the origin:

[ 1.0000000 0.0000000 0.0000000 0.0000000;0.0000000 1.0000000 0.0000000 0.0000000;0.0000000 0.0000000 1.0000000 0.0000000;0.0000000 0.0000000 0.0000000 1.0000000]
(-8.366897 -2.087051 -0.000002 1)
(6.689811 -5.441429 -0.000002 1)
(-6.689813 5.441432 -0.000001 1)
(8.366895 2.087054 -0.000002 1)
(-8.366897 -2.087051 0.492125 1)
(-6.689813 5.441432 0.492125 1)
(6.689811 -5.44143 0.492124 1)
(8.366895 2.087054 0.492125 1)
[ 1.0000000 0.0000000 0.0000000 0.0000000;0.0000000 1.0000000 0.0000000 0.0000000;0.0000000 0.0000000 1.0000000 0.0000000;0.0000000 0.0000000 0.0000000 1.0000000]
(8.224164 1.446331 0.492125 1)
(8.366895 2.087054 0.492125 1)
(7.583453 1.589071 0.492125 1)
(-6.191833 4.657971 0.492125 1)
(-6.689813 5.441432 0.492125 1)
(-6.832544 4.80071 0.492125 1)
(-6.832547 4.800712 10.564299 1)
(-6.689816 5.441434 10.564299 1)
(-6.191836 4.657972 10.564299 1)
(7.583457 1.589071 10.564299 1)
(8.366899 2.087054 10.564299 1)
(8.224168 1.446332 10.564299 1)
[ 1.0000000 0.0000000 0.0000000 0.0000000;0.0000000 1.0000000 0.0000000 0.0000000;0.0000000 0.0000000 1.0000000 0.0000000;0.0000000 0.0000000 0.0000000 1.0000000]
(6.049099 -5.298694 0.492125 1)
(6.19183 -4.657968 0.492125 1)
(6.049102 -5.298697 10.564299 1)
(6.191833 -4.657971 10.564299 1)
(7.583457 1.589071 10.564299 1)
(7.583453 1.589071 0.492125 1)
(8.224164 1.446331 0.492125 1)
(6.689811 -5.44143 0.492124 1)
(6.689814 -5.441433 10.564298 1)
(8.224168 1.446332 10.564299 1)
[ 1.0000000 0.0000000 0.0000000 0.0000000;0.0000000 1.0000000 0.0000000 0.0000000;0.0000000 0.0000000 1.0000000 0.0000000;0.0000000 0.0000000 0.0000000 1.0000000]
(6.19183 -4.657968 0.492125 1)
(6.191833 -4.657971 10.564299 1)
(-7.583456 -1.589068 0.492125 1)
(-7.583459 -1.58907 10.564299 1)
(-8.224167 -1.446329 0.492125 1)
(-8.224171 -1.44633 10.564299 1)
(-8.366901 -2.087052 10.564299 1)
(6.049102 -5.298697 10.564299 1)
(-8.366897 -2.087051 0.492125 1)
(6.049099 -5.298694 0.492125 1)
[ 1.0000000 0.0000000 0.0000000 0.0000000;0.0000000 1.0000000 0.0000000 0.0000000;0.0000000 0.0000000 1.0000000 0.0000000;0.0000000 0.0000000 0.0000000 1.0000000]
(-6.191836 4.657972 10.564299 1)
(-6.191833 4.657971 0.492125 1)
(-7.583459 -1.58907 10.564299 1)
(-7.583456 -1.589068 0.492125 1)
(-6.832547 4.800712 10.564299 1)
(-8.224171 -1.44633 10.564299 1)
(-6.832544 4.80071 0.492125 1)
(-8.224167 -1.446329 0.492125 1)

Exporting this to ASCII format, I can see that the GeometricTranslation hasn’t been edited after saving the nodes transformation to Matrix.Identity:

		Properties70:  {
			P: "ScalingMax", "Vector3D", "Vector", "",0,0,0
			P: "GeometricTranslation", "Vector3D", "Vector", "",1327029.5782806172501296,1379847.34093758929520844,0
			P: "GeometricRotation", "Vector3D", "Vector", "",0,0,-10.06947212446205953
			P: "DefaultAttributeIndex", "int", "Integer", "",0
		}

@TPovey2

We have updated the ticket information accordingly for further analysis. We will get back to you soon with a feedback.

Here are some sample files:

_test.zip (33.0 KB)

Both of the FBX files within the zip folder are located in the same place, the exception is one has no to geometric transforms/rotation, and the other one does.

Eevrything works as expected with the file that does not have any transforms applied.

Looking into the file which does have transforms further, all the nodes which contain geometry have the same node transforms as shown below:

var globalTransform = node.EvaluateGlobalTransform(true);
var globalRotation = node.Transform.GeometricRotation;

Console.WriteLine("Global Transform: " + globalTransform);
Console.WriteLine("Geometric Rotation: " + globalRotation);

globalTransform.Decompose(out Vector3 translation, out Vector3 scaling, out Quaternion rotation);
Console.WriteLine("Translation: " + translation);
Console.WriteLine("Scaling: " + scaling);
Console.WriteLine("Quaternion Rotation: " + rotation);
Console.WriteLine("Quaternion From Geometric Rotation Euler: " + Quaternion.FromEulerAngle(globalRotation));

Gives us:

Global Transform: 
[ 0.9845965 -0.1748421 0.0000000 0.0000000;
0.1748421 0.9845965 0.0000000 0.0000000;
0.0000000 0.0000000 1.0000000 0.0000000;
1327029.5782806 1379847.3409376 0.0000000 1.0000000]

Geometric Rotation: (0 0 -10.069472)
Translation: (1327029.578281 1379847.340938 0)
Scaling: (1 1 1)
Quaternion Rotation: (0.996 -0.000 -0.000 -0.088)
Quaternion From Geometric Rotation Euler: (0.317 0.000 0.000 0.948)

Using the following:

rootNode.Accept(delegate (Node node)
    {
        var globalTransform = node.EvaluateGlobalTransform(true);
        for (int i = 0; i < node.Entities.Count; i++)
        {
            if (node.Entities[i] is Aspose.ThreeD.Entities.Geometry geom)
            {
                for (int j = 0; j < geom.ControlPoints.Count; j++)
                {
                    var v = globalTransform * geom.ControlPoints[j];
                    Console.WriteLine(v);

                }
            }
    }
    return true;
});

Here are the first few vectors from the file which does not have the transform, these are the correct values:

(1327066.373712 1379783.591349 370.013123 1)
(1327081.777634 1379782.888522 370.013123 1)
(1327066.725127 1379791.29331 370.013123 1)
(1327082.129049 1379790.590483 370.013123 1)
(1327066.373712 1379783.591349 370.505249 1)

And the vectors from the file which does have the transform:

(1327054.872962 1379778.217187 370.013123 1)	
(1327069.927039 1379774.878091 370.013123 1)	
(1327056.542512 1379785.744225 370.013123 1)	
(1327071.596589 1379782.405129 370.013123 1)	
(1327054.872962 1379778.217187 370.505249 1)	

I would expect the above two files to match since we’re multiplying the points by the global transform matrix. To me it seems like a rotation issue, note the difference in euler angles above.

When I set node.Transform.GeometricRotation = Vector3.Origin; prior to evaluating the global transform var globalTransform = node.EvaluateGlobalTransform(true);, the vector values from from the file which does have the transform are now almost correct:

(1327066.56908 1379783.704513 370.013123 1)
(1327081.975085 1379783.048938 370.013123 1)
(1327066.896869 1379791.407516 370.013123 1)
(1327082.302875 1379790.751941 370.013123 1)
(1327066.56908 1379783.704513 370.505249 1)

Taking the first vector from the file I know is correct (no transforms applied to the original FBX, and the first vector from the above:

xDifference = 1327066.56908 - 1327066.373712 = 0.19536800007
yDifference = 1379783.704513 - 1379783.591349 = 0.11316399998

The above difference is in feet, converting this to meters we have:

xDifference = 0.059548166421336, or 5.95cm
yDifference = 0.034492387193904m, or 3.45cm

The above difference is small but still not acceptable for my requirements, especially since this is a small simple model and the differences will be larger on more complex models.

FYI, the reason I need the absolute correct values for each vector is because I will be converting them to latitude/longitude values and performing additional calculations on them.

As a side note, I not only have to set the transformation to the identity as you noted in the second step for exporting, I also have to set the GeometricTranslation to 0 also, and possibly the rotation:

node.Transform.TransformMatrix = Matrix4.Identity;
node.Transform.GeometricTranslation = Vector3.Origin;
node.Transform.GeometricRotation = Vector3.Origin;

Please advise how I can get the correct vector values when the input FBX file has a geometric transformation/rotation.

Note, performing the translation myself instead of using EvaluateGlobalTransform gets me the same values:

var globalRotation = node.Transform.GeometricRotation;
Matrix4 geometricMatrix = (new TransformBuilder(ComposeOrder.Append))
    .Translate(node.Transform.GeometricTranslation)
    .RotateEulerDegree(globalRotation.x, globalRotation.y, globalRotation.z)
    .Scale(node.Transform.GeometricScaling).Matrix;

Images of the two files to show they have the same coordinates:
image.png (26.0 KB)
image.jpg (434.3 KB)

Thanks for your time

@TPovey2

Thanks for sharing further details. We will definitely include it in our investigation and let you know once we have some more updates to share.

@TPovey2

We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): THREEDNET-1337

You can obtain Paid Support services if you need support on a priority basis, along with the direct access to our Paid Support management team.

any news on this?

@TPovey2

We are afraid that the earlier logged ticket has not been yet resolved. We will surely inform you once we make some progress towards its fix. We apologize for the inconvenience.