# Exporting UV coordinates

I'm having massive trouble exporting UV coordinates from Blender. I'm using the following code:

"""
Name: 'PMDL'
Blender: 263
Group: 'Export'
Tooltip: 'Exports to ESTL format'
"""

bl_info={
"name":             "ESTL",
"description":      "Exports to estl format",
"author":           "Doxin",
"version":          (0,2),
"blender":          (2,60,0),
"location":         "File > Import-Export",
"category":         "Import-Export"
}

import bpy
from bpy_extras.io_utils import ExportHelper
import sys
import struct

class ExportMyFormat(bpy.types.Operator, ExportHelper):
bl_idname       = "model.estl";
bl_label        = "ESTL Exporter";
bl_options      = {'PRESET'};

filename_ext    = ".estl";

def execute(self, context):
with open(self.filepath,"wb") as fid:
fid.write(b"ExtendedStl")
fid.write(struct.pack("I",0))

count=0
for ob in bpy.context.scene.objects:
try:
ob.data.polygons
except AttributeError:
continue

bpy.context.scene.objects.active=ob
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT')

count+=len(ob.data.polygons)

fid.write(struct.pack("I",count))
bpy.ops.object.mode_set(mode='OBJECT')
for ob in bpy.context.scene.objects:
try:
ob.data.polygons
except AttributeError:
continue

for face in ob.data.polygons:
for vert, loop in zip(face.vertices, face.loop_indices):
for item in ob.data.vertices[vert].normal:#normal
fid.write(struct.pack("f",item))
for item in ob.data.vertices[vert].co:#vertex
fid.write(struct.pack("f",item))
for item in (ob.data.uv_layers.active.data[loop].uv if ob.data.uv_layers.active!=None else (0,0)):#uv
fid.write(struct.pack("f",item))

return {'FINISHED'};

self.layout.operator(ExportMyFormat.bl_idname, text="ESTL (.estl)");

def register():
bpy.utils.register_module(__name__);

def unregister():
bpy.utils.unregister_module(__name__);

if __name__ == "__main__":
register()


It seems like almost every triangle gets the wrong UV coordinates, causing stretching and smearing of my textures. I've been stuck on this issue for a couple days now.

Does anyone know if I'm doing something obvious wrong?

EDIT: it seems that somehow the triangulation is messing up my uvs, if I triangulate the mesh before unwrapping the uvs seem to be mostly correct.

• first of all, are you sure all faces are triangulated? - bpy.ops.mesh.quads_convert_to_tris() depends on the selection. Suggest using: blender.org/documentation/blender_python_api_2_69_release/… Nov 11 '13 at 14:50
• Suggest you generate a file by hand that your importer can load, and validate that its working right. Then you can be sure the Blender exported file is correct or not. Nov 12 '13 at 5:01
• it being a binary format makes this a tad tricky, but I'll work on it. Nov 12 '13 at 10:39
• so I am a giant noob. the problem wasn't with blender after all. it appears openGL cares a great deal in which order your vertex attributes are specified (although I fail to see why). this lead to the uv coords being applies to the next vertex instead of the current one. swapping this around fixed the problem. thanks for all the help, and apologies for wasting all of your time. Nov 14 '13 at 12:50

This issue is that you are accessing UV's by the vertex index,

ob.data.uv_layers.active.data[vert].uv


This is incorrect, you need to iterate over the face loops and access UV's by the loop index.

This is how you could access loops.

            for face in ob.data.polygons:
for vert, loop in zip(face.vertices, face.loop_indices):
for item in ob.data.vertices[vert].normal:  # normal
fid.write(struct.pack("f",item))
for item in ob.data.vertices[vert].co:  # vertex
fid.write(struct.pack("f",item))
for item in (ob.data.uv_layers.active.data[loop].uv if ob.data.uv_layers.active is not None else (0.0, 0.0)):  # uv
fid.write(struct.pack("f",item))


Note that ob.data.uv_layers.active is not None should probably be moved outside of the loop as assigned a var has_uv for eg, its quite inefficient to check this all the time.

• for the people upvoting this: I already applied this, which makes the uvs work better, but still not good enough, still got smeared textures going on. Nov 11 '13 at 16:46
• @doxin - are you sure that its an incorrect UV? sometimes uv's use a flipped V coord. or there may be some other unrelated issue with writing the file. Nov 11 '13 at 17:35
• I'm not sure on much of anything at this point. I'm pretty sure that I'm reading from the file correctly. I tried inverting and swapping the uv coords in all the ways I could thing of, none fixed the problem. it almost seems as if the uv coords for the vertices get mixed up somehow. Nov 11 '13 at 17:48
• does the format support UV coords per loop, or only per vertex? Some formats have this limitation, and thus you need to split the mesh to retain UVs. Nov 12 '13 at 1:13
• @ideasman42 I couldn't find any exporters that a) export uvs, and b) aren't a total unreadable hackjob. how would I go about checking if the uvs are correct? Nov 12 '13 at 23:55

You could try this to match up face-verts and loops:

class EmptyUV:
uv = (0.0, 0.0)
def __getitem__(self, index): return self
# ...

ob = bpy.context.object
uv_act = ob.data.uv_layers.active
uv_layer = uv_act.data if uv_act is not None else EmptyUV()

verts = ob.data.vertices
loop_vert = {l.index: l.vertex_index for l in ob.data.loops}

for face in ob.data.polygons:
for li in face.loop_indices:
struct.pack("fff", *verts[loop_vert[li]].normal)
struct.pack("fff", *verts[loop_vert[li]].co)
struct.pack("ff", *uv_layer[li].uv)

• "Integer object has no vertex_index attribute". I suppose I need to retrieve the actual loop from bpy somehow. Nov 13 '13 at 16:14
• I tried this approach (with some modifications to resolve the errors), but it leads to the same results. I'll doublecheck once more if any of my other code is doing something weird. Nov 13 '13 at 18:41
• My bad, you need to retrieve the actual loop to get the .vertex_index property of course. Updated code above. Can you post a screenshot how it looks in the target app? Nov 13 '13 at 21:43
• Unfortunately this code is printing out redundant vertex info. For a cube it prints out all 24 vertices instead of only 8. This is the same as having each face as a separate mesh. It's not very efficient and it can triple the file size. Feb 2 '14 at 19:36
• @MarcClintDion: It is supposed to do that, and it's 24 loops, not verts. Otherwise you can't have UVs for every corner of a face (which is the case in Blender, allows for "hard edges" in texturing) Feb 2 '14 at 20:53