.Obj format - why different indicies?

OK, so I’m enhancing a script for exporting in .obj format. But I noticed that Rhino doesn’t use the same index for the VerticeNormal as the Vertex itself when the mesh is Welded.
Q1: Why is that, and, does it matter if I just disregard the differing indicies and use the same index for both??

Unwelded 
------------------------------------------------
   v//vn                      (v = vn)
f 53//53 54//54 55//55


Welded
------------------------------------------------
  v//vn                      (v != vn)  !
f 5//82 1//81 3//83


// Is this OK also for unwelded meshes?
mesh.Normals[i] == mesh.Vertices[i]

Q2: IF these differing indexes matter, than how can I find those indexes when I only have the same number of Normals as Vertices (and thus indexes) after welding?

// Rolf

Hi Rolf,

Are you referring to welding on the way out through OBJ, or welding prior to export? It does make a difference.

Welding in Rhino consolidates vertexes at the same location and averages the normals and texture coordinates. In OBJ export (because the obj format allows this), when a person welds on export, it uses one of the vertex indexes (probably the first in the topological vertexes array) for all of the vertexes at the 3d location but the unique texture coordinate and normal. This is for the sake of downstream apps. If you were to roundtrip the same file if you would make additional vertexes for any vertex that had multiple TCs or normals associated with it. So, for Q1 it does matter.

This is syntactically ok, I think

mesh.Normals[i] == mesh.Vertices[i]

but it means you’re comparing a point to a normal (so maybe it isn’t ok) and I don’t know how useful that is, may be in your case it is. They are associated with each other in an unwelded mesh but they won’t often be the same sets of three numbers.

I’m not really following you on Q2. If you’re welding in Rhino, you should have a one to one correspondence between vertexes and normals that already match. Welding on OBJ output will make vertex, TC and Normal lists of differing lengths. (unless the mesh was modified in such way prior to export to prevent that).

Tim

Yes, prior to export. Welding only a copy before exporting.

I actually meant “same index” (bad way of illustrating that, sorry).

Regarding Q1 again, I have not thought of the Topological vertex array, so I will have to look into that one.

But I noticed now when I tested that even when unwelding the mesh, the numbers in the mesh.Vertices list and mesh.Normals list still match, only the mesh.TopologicalVertices would have less vertices when unwelded. So this indicates that I in fact can use the same index for mesh.Vertices[i] and mesh.Normals[i] when exporting the face indicies “f v1//vn1 …”. Which is why I felt a bit confused seeing how Rhino picks the indicies.

Gotta ponder on your answer a bit more before I grasp it…

// Rolf

Q3: Another question is about the command mesh.Faces.ConvertQuadsToTriangles() which I had executed before generating the following face indicies. It still contains quads after running the ConvertQuadsToTriangles() command. Is that a bug or am I missing something?

bild

The resulting file:

bild

However, I did notice that the quads disappear when I run the mesh.TriangulateMesh() command instead. Directly from Rhino that is, because that command doesn’t seem to be available in RhinoCommon(5).

So what result should I expect from running mesh.Faces.ConvertQuadsToTriangles() before export?

// Rolf

In RhinoCycles I use ToIntArray() on MeshFaceList (i.e. Mesh.Faces.ToIntArray(true)) just prior to reading the geometry data for Cycles - Cycles uses triangles only.

I assume the function works the same in v5 as in v6.

Interesting. But since this returns a prolonged list of indicies, it doesn’t actually change the mesh, and it will not match the number of Vertices& Normals List, it means I will have to calculate the Normals myself, right?

// Rolf

You still will have same amount of vertices and vertex normals, but you can use the resulting array with stride three to set up your exported data. It doesn’t change the mesh now, but you don’t need it to. You’d write out your vertex data, vertex normal data, then use this list for the indices. With the stride of 3 you get triangles for your data.

If you have ngon generation checked in your obj options then you’ll potentially get quads (or greater) in your output despite having triangulated the mesh prior.

I was thinking about your initial post last night. The only time you should get indexes that are not the same for vertex/TC/normal in the obj output is when you check the welded option. If you weld prior to export and don’t have welded checked they should be the same because internal to Rhino the arrays in a mesh are always parallel, either having zero or vertex count.

Yes, thanks, that is my strategy for now. It simplifies things.

// Rolf

I am trying something similar. I want to export Mesh vertices and faces via JSON and write them to an OBJ elsewhere. It seems pretty straightforward - I am only interested in vertices and faces.

  1. Convert GH_Mesh to Mesh
  2. Mesh.Faces.ConvertQuadsToTriangles();
  3. vertices = Mesh.Vertices.ToFloatArray()
  4. faces = Mesh.Faces.ToIntArray(true)

I will write an obj with this data by writing vertices 3 at a time:
v vertices[0] vertices[1] vertices[2]
v vertices[3] vertices[4] vertices[5] …

and Faces indices as
f faces[0] faces[1] faces[2]
f faces[3] faces[4] faces[5]

This worked perfectly using a cone in Grasshopper, which means there’s inherently nothing wrong with my logic, but the moment I create something a little more complex (<10kb) or even a sphere, it looks like the dog chewed on it. I compared a baked + exported OBJ using GH/Rhino to my scripts output, not a single vertex or face index set is similar. The overall shape is similar, and a few faces are correct, but noticeably the faces from the bottom seem to be connected to the top arbitrarily.

What am I doing wrong ?
Thanks.

There is no way for us to even speculate without see some source code and being able to replicate the problem with it.

– Dale

Improved gist for better understanding. Ignore the first gist

Hi @arefin.86,

Try commenting this out:

SuperMesh.Faces.ConvertQuadsToTriangles();

The reason being that you (basically do this on the next line when you pass true to MeshFaceList.ToIntArray.

Does this help?

– Dale

Thanks, but I get the same results. The reverse didn’t help either i.e.
SuperMesh.Faces.ConvertQuadsToTriangles();
MeshFaceList.ToIntArray(false);

The successful cone (in both cases) actually has it’s base frustum missing. On inspection I realized that these were formed of quads. It seems that something messes up the order of lookup (face(A B C) -> vertex) post-triangulation, using ConvertQuadsToTriangles(); or MeshFaceList.ToIntArray(false) or even a separate triangulation script.