Texture Coordinates Not Exporting Correctly

Hello! I’m using Blender 2.7a and have been working on getting a custom exporter to work. Geometry and material information is exporting ok for me, but texture coordinates are coming out wrong despite me following the obj exporter pattern for the data access (as best as i can tell anyways). Script below… does anyone see what I’m doing wrong?

Thank you so much for any help you can give, I’ve been banging my head on this for several days now with no luck!

bl_info = {
    "name":         "ProjectX Exporters",
    "author":       "Alan Wolfe",
    "blender":      (2,7,0),
    "version":      (0,0,1),
    "location":     "File > Import-Export",
    "description":  "Export custom data formats for ProjectX",
    "category":     "Import-Export"
}
        
import bpy
from bpy_extras.io_utils import ExportHelper


import bpy_extras.io_utils


import os


from mathutils import Matrix, Vector


# flip Y and Z axis, since blender has +Z as up, and we have +Y as up
GLOBAL_EXPORT_MATRIX = Matrix([[1, 0, 0, 0],[0, 0, 1, 0],[0, 1, 0, 0],[0, 0, 0, 1]])


def mesh_triangulate(me):
    import bmesh
    bm = bmesh.new()
    bm.from_mesh(me)
    bmesh.ops.triangulate(bm, faces=bm.faces)
    bm.to_mesh(me)
    bm.free()


class ExportModel(bpy.types.Operator, ExportHelper):
    bl_idname       = "model.xmd";
    bl_label        = "Save";
    bl_options      = {'PRESET'};
    
    filename_ext    = ".xmd";
    
    def execute(self, context):
        out = open(self.filepath, "w");
        out.write('<?xml version="1.0" encoding="utf-8"?>
<model>
')
        for obj in bpy.data.objects:


            try:
                data = obj.to_mesh(bpy.context.scene, True, 'PREVIEW')
            except RuntimeError:
                data = None


            if data is None:
                continue


            data.transform(obj.matrix_world * GLOBAL_EXPORT_MATRIX)


            mesh_triangulate(data)
            data.calc_tessface();
            data.calc_tangents();


            out.write('	<object>
')


            source_dir = os.path.dirname(bpy.data.filepath)
            dest_dir = os.path.dirname(bpy.data.filepath)


            if len(data.materials) == 0:
                out.write('		<material/>
')


            # write materials for this object
            for mat_num, mat in enumerate(data.materials):
                out.write('		<material>
')


                # todo: EmissiveColor
                # todo: ReflectionAmount
                # todo: Absorbance


                out.write('			<DiffuseColor Value="%f,%f,%f"/>
' % (mat.diffuse_intensity * mat.diffuse_color)[:])


                # write images
                image_map = {}
                # backwards so topmost are highest priority
                for mtex in reversed(mat.texture_slots):
                    if mtex and mtex.texture and mtex.texture.type == 'IMAGE':
                        image = mtex.texture.image
                        if image:
                            # texface overrides others
                            if      (mtex.use_map_color_diffuse and
                                    (mtex.use_map_warp is False) and
                                    (mtex.texture_coords != 'REFLECTION')):
                                image_map["DiffuseTexture"] = image
                            if mtex.use_map_ambient:
                                image_map["AmbientTexture_Unused"] = image
                            # this is the Spec intensity channel but Ks stands for specular Color
                            '''
                            if mtex.use_map_specular:
                                image_map["SpecularTexture_Unused"] = image
                            '''
                            if mtex.use_map_color_spec:  # specular color
                                image_map["SpecularTexture_Unused"] = image
                            if mtex.use_map_hardness:  # specular hardness/glossiness
                                image_map["HardnessTexture_Unused"] = image
                            if mtex.use_map_alpha:
                                image_map["AlphaTexture_Unused"] = image
                            if mtex.use_map_translucency:
                                image_map["TranslucencyTexture_Unused"] = image
                            if mtex.use_map_normal and (mtex.texture.use_normal_map is True):
                                image_map["BumpTexture_Unused"] = image
                            if mtex.use_map_normal and (mtex.texture.use_normal_map is False):
                                image_map["NormalTexture"] = image                      
                            if mtex.use_map_color_diffuse and (mtex.texture_coords == 'REFLECTION'):
                                image_map["ReflectionTexture_Unused"] = image
                            if mtex.use_map_emit:
                                image_map["EmissiveTexture"] = image


                for key, image in image_map.items():
                    out.write('			<%s Value="%s"/>
' % (key, repr(image.filepath)[1:-1]))


                out.write('			<SpecularColor Value="%f,%f,%f"/>
' % (mat.specular_intensity * mat.specular_color)[:])


                # convert from blenders spec to 0 - 1000 range.
                if mat.specular_shader == 'WARDISO':
                    tspec = (0.4 - mat.specular_slope) / 0.0004
                else:
                    tspec = (mat.specular_hardness - 1) * 1.9607843137254901
                out.write('			<SpecularPower Value="%f"/>
' % tspec)


                if hasattr(mat, "ior"):
                    out.write('			<RefractionIndex Value="%f"/>
' % mat.ior)


                out.write('			<RefractionAmount Value="%f"/>
' % (1.0 - mat.alpha))


                out.write('		</material>
')


            # write polygons
            for face_num, face in enumerate(data.polygons):
                out.write('		<face>
')


                for loopIndex in (face.loop_indices):
                    vert = data.vertices[data.loops[loopIndex].vertex_index];
                    out.write('			<vert>
');
                    out.write('				<pos Value="%f,%f,%f"/>
' % (vert.co[:]));
                    out.write('				<normal Value="%f,%f,%f"/>
' % (data.loops[loopIndex].normal[:]));
                    out.write('				<tangent Value="%f,%f,%f"/>
' % (data.loops[loopIndex].tangent[:]));
                    out.write('				<bitangent Value="%f,%f,%f"/>
' % (data.loops[loopIndex].bitangent[:]));
                    if data.uv_layers.active != None:
                        out.write('				<uv Value="%f,%f"/>
' % (data.uv_layers.active.data[loopIndex].uv[:]));
                    out.write('			</vert>
');


                out.write('		</face>
')
            out.write('	</object>
')
            bpy.data.meshes.remove(data)
        out.write('</model>
')
        out.close();
        return {'FINISHED'};


def model_menu_func(self, context):
    self.layout.operator(ExportModel.bl_idname, text="ProjectX Model(.xmd)");


def register():
    bpy.utils.register_module(__name__);
    bpy.types.INFO_MT_file_export.append(model_menu_func);
    
def unregister():
    bpy.utils.unregister_module(__name__);
    bpy.types.INFO_MT_file_export.remove(model_menu_func);


if __name__ == "__main__":
    register()

Ok i figured it out thanks to another post. The issue is that im loading the texture with directx, and thus needed to flip the v coordinate for textures.

AKA where the uv coordinates are written needed to be changed to this:


					if data.uv_layers.active != None:
						out.write('				<uv Value="%f,%f"/>
' % (data.uv_layers.active.data[loopIndex].uv.x, 1.0 - data.uv_layers.active.data[loopIndex].uv.y));