extracting UV coordinates for each vertex on export script

Scripting in Blender with Python, and working on the API

Moderators: jesterKing, stiv

saldavonschwartz
Posts: 3
Joined: Sat Nov 10, 2012 2:33 am

extracting UV coordinates for each vertex on export script

Postby saldavonschwartz » Sat Nov 10, 2012 2:46 am

I'm trying to write an export script for laying out a mesh in a OpenGL VBO-friendly format.

My approach so far is to do: (where mesh = bpy.context.scene.objects[someObject])


Code: Select all

# VBO size
    vNum = len(mesh.data.vertices)
    tNum = len(mesh.data.uv_layers)
    VBOSize = vNum * (6 + (tNum * 2))
    file.write('VBO Size: {:d}\n'.format(VBOSize))

    # VBO
    file.write('VBO Encoding: 3 3' + ' 2' * tNum + '\n')
    i = 0
    for vertex in mesh.data.vertices:
        file.write('{:.2f} {:.2f} {:.2f} {:.2f} {:.2f} {:.2f}'.format(vertex.co[0],
                                                                      vertex.co[1],
                                                                      vertex.co[2],
                                                                      vertex.normal[0],
                                                                      vertex.normal[1],
                                                                      vertex.normal[2]))
        # uv coordinates
        for uv_layer in mesh.data.uv_layers:
            file.write(' {:.2f} {:.2f}\n'.format(uv_layer.data[i].uv[0], uv_layer.data[i].uv[1]))
        i += 1


The idea is that I export to a txt file where the data is already laid out as vertex attributes in array of structs format. That is, vvvnnncc, meaning vertex + normal + 1 texture, for instance.

But it seems I'm incorrectly accesing uv coordinates because when I render the resulting VBO + Texture in my engine of sorts, the vertices and normals are fine (shading is coherent) but the texture mapping is definitely messed up.

So I went ahead and looked a bit closer on the uv_layers in my scene and what throws me off is that, my mesh is made up of 48 vertices and normals, reused among 70 faces (a total of 210 indices in terms of a GL index buffer if you will) but the length of mesh.data.uv_layers[0].data (this particular mesh has a single material with a single UV texture) is 210.

Shouldn't it be at most 48? That is, at most, every single distinct vertex in the mesh would map to one uv coordinate. How come I have 210 uv coordinates?

Regardless of the above observation, what would be the way to get appropriate UV coordinate for each vertex?

CoDEmanX
Posts: 894
Joined: Sun Apr 05, 2009 7:42 pm
Location: Germany

Postby CoDEmanX » Sat Nov 10, 2012 11:08 am

Blender haves correct, you need to know that UV coordinates is something that is stored per loop (=per-face vertices). There's a simple explanation for it: if you had UV coordinates stored per vertex, every vert could have just one UV. So the faces that use the shared vertex couldn't have "sharp texture edges". But since Blender allows you to texture every face individually, it's required to store UVs, Vertex Colors etc. per face-vertices (these are loops, basically not shared vertices).

Maybe this helps a little to understand:
http://img6.imagebanana.com/img/xvd1nvm2/cubeloops.png

So you may have to split shared vertices first, to map a UV coord to one vertex.
I'm sitting, waiting, wishing, building Blender in superstition...

saldavonschwartz
Posts: 3
Joined: Sat Nov 10, 2012 2:33 am

Postby saldavonschwartz » Sun Nov 11, 2012 12:00 am

CoDEmanX wrote:Blender haves correct, you need to know that UV coordinates is something that is stored per loop (=per-face vertices). There's a simple explanation for it: if you had UV coordinates stored per vertex, every vert could have just one UV. So the faces that use the shared vertex couldn't have "sharp texture edges". But since Blender allows you to texture every face individually, it's required to store UVs, Vertex Colors etc. per face-vertices (these are loops, basically not shared vertices).

Maybe this helps a little to understand:
http://img6.imagebanana.com/img/xvd1nvm2/cubeloops.png

So you may have to split shared vertices first, to map a UV coord to one vertex.


I see what you mean about blender having then more than one UV for a vertex. But my UV map will anyway map a single UV coordinate to a single vertex. Because I'm unwrapping the model to adjacent faces and never mapping one adjacent face in an unwrapped polygon to a non adjacent area in the uv map image.

So given the above, shouldn't this work?

Code: Select all

vertexAttributeList = []
    for vertex in mesh.data.vertices:
        vertexAttribute = list(vertex.co)
        vertexAttribute.extend(list(vertex.normal))
        vertexAttributeList.append(vertexAttribute)
   
    for triangle in mesh.data.polygons:
        for uv_layer in mesh.data.uv_layers:
            for i in triangle.loop_indices:
                uvCoord = uv_layer.data[i].uv
                lookupIndex = mesh.data.loops[i].vertex_index
                vertexAttributeList[lookupIndex].extend([uvCoord[0], 1 - uvCoord[1]])


I'd assume this way I am, for each polygon (they are triangles), looking up each of its 3 vertices and pairing them up with their right uv coord, by looking the right uv up in uv_layer.data since I index it with the loop_indices.
Sure, in blender it'll turn out that one triangle will have, say, vertex n with uv coord a and another triangle might have the same vertex n but with a uv coord b. But both a and b should actually have the same coordinates.

However, the above code gives me the following layout and the uv coords are still not right. The uv mapping is still wrong when I render:

Code: Select all

-1.000000 -1.000000 -1.000000 -0.707083 -0.707083  0.000000  0.076381  0.948520
-1.000000  1.000000 -1.000000 -0.707083  0.707083  0.000000  0.454183  0.948519
 1.000000  1.000000 -1.000000  0.707083  0.707083  0.000000  0.325162  0.948519
 1.000000 -1.000000 -1.000000  0.707083 -0.707083  0.000000  0.205674  0.948519
-1.000000 -1.000000  1.000000 -0.577349 -0.577349  0.577349  0.581634  0.795012
-1.000000  1.000000  1.000000 -0.577349  0.577349  0.577349  0.454183  0.795012
 1.000000  1.000000  1.000000  0.577349  0.577349  0.577349  0.325162  0.794894
 1.000000 -1.000000  1.000000  0.577349 -0.577349  0.577349  0.205674  0.794894
...


What am I missing?

saldavonschwartz
Posts: 3
Joined: Sat Nov 10, 2012 2:33 am

Postby saldavonschwartz » Sun Nov 11, 2012 1:35 am

In the above example, with the new code, I forgot to mention if I actually print say vertexAttribute 0 as a list so I can see the total number of uvs that were assigned to vertex 0, I get:

Code: Select all

[-1.0, -1.0, -1.0, -0.7070833444595337, -0.7070833444595337, 0.0, 0.07638061791658401, 0.9485195726156235, 0.5816344618797302, 0.9485194832086563, 0.07638061791658401, 0.9485195726156235]


So clearly blender did assign different uv coordinates to the same vertex.
Bu I don't see why since like I said I unwrapped the mesh into adjacent faces and I still don't quite understand how would I go about getting the right uvs in an "index" approach like the one I'm trying to get the VBO in.
Meaning, sure, one option would be to forget about using a vertex element array in opengl to index into the vertex attributes and just create a VBO where vertex attributes are ordered consecutively already. This would mean duplicating vertices since they'll be duplicated each time a face references them, but the "duplicates" will have different uvs. This approach then would probably solve the uv issue but at the expense of duplicating data, which is why I want to do the "indexed" approach.

CoDEmanX
Posts: 894
Joined: Sun Apr 05, 2009 7:42 pm
Location: Germany

Postby CoDEmanX » Tue Nov 13, 2012 8:30 am

unless VBO allows shared vertices/multiple uvs per vertex, you are forced to duplicate shared vertices. The MD5mesh exporter does this.
I'm sitting, waiting, wishing, building Blender in superstition...


Return to “Python”

Who is online

Users browsing this forum: No registered users and 1 guest