Skip to content
Snippets Groups Projects
export_fbx.py 114 KiB
Newer Older
  • Learn to ignore specific revisions
  •         file.write('\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0')
            file.write('\n\t\t\tProperty: "FrameSamplingCount", "int", "",7')
    
            file.write('\n\t\t}')
            file.write('\n\t\tMultiLayer: 0')
            file.write('\n\t\tMultiTake: 0')
            file.write('\n\t\tShading: Y')
            file.write('\n\t\tCulling: "CullingOff"')
            file.write('\n\t\tTypeFlags: "Camera"')
            file.write('\n\t\tGeometryVersion: 124')
            file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc)
            file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Vector((0.0, 1.0, 0.0)) * matrix_rot))
            file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Vector((0.0, 0.0, -1.0)) * matrix_rot))
    
            #file.write('\n\t\tUp: 0,0,0' )
            #file.write('\n\t\tLookAt: 0,0,0' )
    
            file.write('\n\t\tShowInfoOnMoving: 1')
            file.write('\n\t\tShowAudio: 0')
            file.write('\n\t\tAudioColor: 0,1,0')
            file.write('\n\t\tCameraOrthoZoom: 1')
            file.write('\n\t}')
    
        def write_light(my_light):
            light = my_light.blenObject.data
            file.write('\n\tModel: "Model::%s", "Light" {' % my_light.fbxName)
            file.write('\n\t\tVersion: 232')
    
            write_object_props(my_light.blenObject, None, my_light.parRelMatrix())
    
            # Why are these values here twice?????? - oh well, follow the holy sdk's output
    
            # Blender light types match FBX's, funny coincidence, we just need to
            # be sure that all unsupported types are made into a point light
            #ePOINT,
            #eDIRECTIONAL
            #eSPOT
            light_type_items = {'POINT': 0, 'SUN': 1, 'SPOT': 2, 'HEMI': 3, 'AREA': 4}
            light_type = light_type_items[light.type]
    
    
            if light_type > 2:
                light_type = 1  # hemi and area lights become directional
    
    
    # 		mode = light.mode
            if light.shadow_method == 'RAY_SHADOW' or light.shadow_method == 'BUFFER_SHADOW':
    # 		if mode & Blender.Lamp.Modes.RayShadow or mode & Blender.Lamp.Modes.Shadows:
                do_shadow = 1
            else:
                do_shadow = 0
    
            if light.use_only_shadow or (not light.use_diffuse and not light.use_specular):
    # 		if mode & Blender.Lamp.Modes.OnlyShadow or (mode & Blender.Lamp.Modes.NoDiffuse and mode & Blender.Lamp.Modes.NoSpecular):
                do_light = 0
            else:
                do_light = 1
    
    
            scale = abs(GLOBAL_MATRIX.scale_part()[0])  # scale is always uniform in this case
    
    
            file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type)
            file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",1')
            file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1')
            file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1')
            file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0')
            file.write('\n\t\t\tProperty: "GoboProperty", "object", ""')
            file.write('\n\t\t\tProperty: "Color", "Color", "A+",1,1,1')
    
            file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy * 100.0, 200.0)))  # clamp below 200
    
            if light.type == 'SPOT':
                file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % math.degrees(light.spot_size))
            file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50')
            file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.color))
    
    
            file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy * 100.0, 200.0)))  # clamp below 200
    
    
            file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50')
            file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type)
            file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",%i' % do_light)
            file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1')
            file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0')
            file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1')
            file.write('\n\t\t\tProperty: "GoboProperty", "object", ""')
            file.write('\n\t\t\tProperty: "DecayType", "enum", "",0')
            file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.distance)
            file.write('\n\t\t\tProperty: "EnableNearAttenuation", "bool", "",0')
            file.write('\n\t\t\tProperty: "NearAttenuationStart", "double", "",0')
            file.write('\n\t\t\tProperty: "NearAttenuationEnd", "double", "",0')
            file.write('\n\t\t\tProperty: "EnableFarAttenuation", "bool", "",0')
            file.write('\n\t\t\tProperty: "FarAttenuationStart", "double", "",0')
            file.write('\n\t\t\tProperty: "FarAttenuationEnd", "double", "",0')
            file.write('\n\t\t\tProperty: "CastShadows", "bool", "",%i' % do_shadow)
            file.write('\n\t\t\tProperty: "ShadowColor", "ColorRGBA", "",0,0,0,1')
            file.write('\n\t\t}')
            file.write('\n\t\tMultiLayer: 0')
            file.write('\n\t\tMultiTake: 0')
            file.write('\n\t\tShading: Y')
            file.write('\n\t\tCulling: "CullingOff"')
            file.write('\n\t\tTypeFlags: "Light"')
            file.write('\n\t\tGeometryVersion: 124')
            file.write('\n\t}')
    
        # matrixOnly is not used at the moment
    
        def write_null(my_null=None, fbxName=None, matrixOnly=None):
    
            if not fbxName:
                fbxName = my_null.fbxName
    
    
            file.write('\n\tModel: "Model::%s", "Null" {' % fbxName)
            file.write('\n\t\tVersion: 232')
    
            # only use this for the root matrix at the moment
            if matrixOnly:
                poseMatrix = write_object_props(None, None, matrixOnly)[3]
    
    
            else:  # all other Null's
                if my_null:
                    poseMatrix = write_object_props(my_null.blenObject, None, my_null.parRelMatrix())[3]
                else:
                    poseMatrix = write_object_props()[3]
    
    
            pose_items.append((fbxName, poseMatrix))
    
            file.write('''
            }
            MultiLayer: 0
            MultiTake: 1
            Shading: Y
            Culling: "CullingOff"
            TypeFlags: "Null"
        }''')
    
        # Material Settings
    
        if world:
            world_amb = world.ambient_color[:]
        else:
            world_amb = 0.0, 0.0, 0.0  # default value
    
    
        def write_material(matname, mat):
            file.write('\n\tMaterial: "Material::%s", "" {' % matname)
    
            # Todo, add more material Properties.
            if mat:
                mat_cold = tuple(mat.diffuse_color)
                mat_cols = tuple(mat.specular_color)
                #mat_colm = tuple(mat.mirCol) # we wont use the mirror color
                mat_colamb = world_amb
    
                mat_dif = mat.diffuse_intensity
                mat_amb = mat.ambient
    
                mat_hard = (float(mat.specular_hardness) - 1.0) / 5.10
                mat_spec = mat.specular_intensity / 2.0
    
                mat_alpha = mat.alpha
                mat_emit = mat.emit
                mat_shadeless = mat.use_shadeless
                if mat_shadeless:
                    mat_shader = 'Lambert'
                else:
                    if mat.diffuse_shader == 'LAMBERT':
                        mat_shader = 'Lambert'
                    else:
                        mat_shader = 'Phong'
            else:
                mat_cols = mat_cold = 0.8, 0.8, 0.8
    
                # mat_colm
                mat_dif = 1.0
                mat_amb = 0.5
                mat_hard = 20.0
                mat_spec = 0.2
                mat_alpha = 1.0
                mat_emit = 0.0
                mat_shadeless = False
                mat_shader = 'Phong'
    
            file.write('\n\t\tVersion: 102')
            file.write('\n\t\tShadingModel: "%s"' % mat_shader.lower())
            file.write('\n\t\tMultiLayer: 0')
    
            file.write('\n\t\tProperties60:  {')
            file.write('\n\t\t\tProperty: "ShadingModel", "KString", "", "%s"' % mat_shader)
            file.write('\n\t\t\tProperty: "MultiLayer", "bool", "",0')
    
            file.write('\n\t\t\tProperty: "EmissiveColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cold)  # emit and diffuse color are he same in blender
    
            file.write('\n\t\t\tProperty: "EmissiveFactor", "double", "",%.4f' % mat_emit)
    
            file.write('\n\t\t\tProperty: "AmbientColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_colamb)
            file.write('\n\t\t\tProperty: "AmbientFactor", "double", "",%.4f' % mat_amb)
            file.write('\n\t\t\tProperty: "DiffuseColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cold)
            file.write('\n\t\t\tProperty: "DiffuseFactor", "double", "",%.4f' % mat_dif)
            file.write('\n\t\t\tProperty: "Bump", "Vector3D", "",0,0,0')
            file.write('\n\t\t\tProperty: "TransparentColor", "ColorRGB", "",1,1,1')
            file.write('\n\t\t\tProperty: "TransparencyFactor", "double", "",%.4f' % (1.0 - mat_alpha))
            if not mat_shadeless:
                file.write('\n\t\t\tProperty: "SpecularColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cols)
                file.write('\n\t\t\tProperty: "SpecularFactor", "double", "",%.4f' % mat_spec)
                file.write('\n\t\t\tProperty: "ShininessExponent", "double", "",80.0')
                file.write('\n\t\t\tProperty: "ReflectionColor", "ColorRGB", "",0,0,0')
                file.write('\n\t\t\tProperty: "ReflectionFactor", "double", "",1')
            file.write('\n\t\t\tProperty: "Emissive", "ColorRGB", "",0,0,0')
            file.write('\n\t\t\tProperty: "Ambient", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_colamb)
            file.write('\n\t\t\tProperty: "Diffuse", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cold)
            if not mat_shadeless:
                file.write('\n\t\t\tProperty: "Specular", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cols)
                file.write('\n\t\t\tProperty: "Shininess", "double", "",%.1f' % mat_hard)
            file.write('\n\t\t\tProperty: "Opacity", "double", "",%.1f' % mat_alpha)
            if not mat_shadeless:
                file.write('\n\t\t\tProperty: "Reflectivity", "double", "",0')
    
            file.write('\n\t\t}')
            file.write('\n\t}')
    
        def copy_image(image):
            fn = bpy.path.abspath(image.filepath)
            fn_strip = os.path.basename(fn)
    
            if EXP_IMAGE_COPY:
                rel = fn_strip
                fn_abs_dest = os.path.join(basepath, fn_strip)
                if not os.path.exists(fn_abs_dest):
                    shutil.copy(fn, fn_abs_dest)
            elif bpy.path.is_subdir(fn, basepath):
                rel = os.path.relpath(fn, basepath)
            else:
                rel = fn
    
            return (rel, fn_strip)
    
        # tex is an Image (Arystan)
        def write_video(texname, tex):
            # Same as texture really!
            file.write('\n\tVideo: "Video::%s", "Clip" {' % texname)
    
            file.write('''
            Type: "Clip"
            Properties60:  {
                Property: "FrameRate", "double", "",0
                Property: "LastFrame", "int", "",0
                Property: "Width", "int", "",0
                Property: "Height", "int", "",0''')
            if tex:
                fname_rel, fname_strip = copy_image(tex)
    # 			fname, fname_strip, fname_rel = derived_paths(tex.filepath, basepath, EXP_IMAGE_COPY)
            else:
                fname = fname_strip = fname_rel = ''
    
            file.write('\n\t\t\tProperty: "Path", "charptr", "", "%s"' % fname_strip)
    
            file.write('''
                Property: "StartFrame", "int", "",0
                Property: "StopFrame", "int", "",0
                Property: "PlaySpeed", "double", "",1
                Property: "Offset", "KTime", "",0
                Property: "InterlaceMode", "enum", "",0
                Property: "FreeRunning", "bool", "",0
                Property: "Loop", "bool", "",0
                Property: "AccessMode", "enum", "",0
            }
            UseMipMap: 0''')
    
            file.write('\n\t\tFilename: "%s"' % fname_strip)
    
            if fname_strip:
                fname_strip = '/' + fname_strip
            file.write('\n\t\tRelativeFilename: "%s"' % fname_rel)  # make relative
    
            file.write('\n\t}')
    
        def write_texture(texname, tex, num):
            # if tex is None then this is a dummy tex
            file.write('\n\tTexture: "Texture::%s", "TextureVideoClip" {' % texname)
            file.write('\n\t\tType: "TextureVideoClip"')
            file.write('\n\t\tVersion: 202')
            # TODO, rare case _empty_ exists as a name.
            file.write('\n\t\tTextureName: "Texture::%s"' % texname)
    
            file.write('''
            Properties60:  {
                Property: "Translation", "Vector", "A+",0,0,0
                Property: "Rotation", "Vector", "A+",0,0,0
                Property: "Scaling", "Vector", "A+",1,1,1''')
            file.write('\n\t\t\tProperty: "Texture alpha", "Number", "A+",%i' % num)
    
            # WrapModeU/V 0==rep, 1==clamp, TODO add support
            file.write('''
                Property: "TextureTypeUse", "enum", "",0
                Property: "CurrentTextureBlendMode", "enum", "",1
                Property: "UseMaterial", "bool", "",0
                Property: "UseMipMap", "bool", "",0
                Property: "CurrentMappingType", "enum", "",0
                Property: "UVSwap", "bool", "",0''')
    
            file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.use_clamp_x)
            file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.use_clamp_y)
    
            file.write('''
                Property: "TextureRotationPivot", "Vector3D", "",0,0,0
                Property: "TextureScalingPivot", "Vector3D", "",0,0,0
                Property: "VideoProperty", "object", ""
            }''')
    
            file.write('\n\t\tMedia: "Video::%s"' % texname)
    
            if tex:
                fname_rel, fname_strip = copy_image(tex)
    # 			fname, fname_strip, fname_rel = derived_paths(tex.filepath, basepath, EXP_IMAGE_COPY)
            else:
                fname = fname_strip = fname_rel = ''
    
            file.write('\n\t\tFileName: "%s"' % fname_strip)
    
            file.write('\n\t\tRelativeFilename: "%s"' % fname_rel)  # need some make relative command
    
    
            file.write('''
            ModelUVTranslation: 0,0
            ModelUVScaling: 1,1
            Texture_Alpha_Source: "None"
            Cropping: 0,0,0,0
        }''')
    
        def write_deformer_skin(obname):
            '''
            Each mesh has its own deformer
            '''
            file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {' % obname)
            file.write('''
            Version: 100
            MultiLayer: 0
            Type: "Skin"
            Properties60:  {
            }
            Link_DeformAcuracy: 50
        }''')
    
        # in the example was 'Bip01 L Thigh_2'
        def write_sub_deformer_skin(my_mesh, my_bone, weights):
    
            '''
            Each subdeformer is spesific to a mesh, but the bone it links to can be used by many sub-deformers
            So the SubDeformer needs the mesh-object name as a prefix to make it unique
    
            Its possible that there is no matching vgroup in this mesh, in that case no verts are in the subdeformer,
            a but silly but dosnt really matter
            '''
            file.write('\n\tDeformer: "SubDeformer::Cluster %s %s", "Cluster" {' % (my_mesh.fbxName, my_bone.fbxName))
    
            file.write('''
            Version: 100
            MultiLayer: 0
            Type: "Cluster"
            Properties60:  {
                Property: "SrcModel", "object", ""
                Property: "SrcModelReference", "object", ""
            }
            UserData: "", ""''')
    
            # Support for bone parents
            if my_mesh.fbxBoneParent:
                if my_mesh.fbxBoneParent == my_bone:
                    # TODO - this is a bit lazy, we could have a simple write loop
                    # for this case because all weights are 1.0 but for now this is ok
                    # Parent Bones arent used all that much anyway.
                    vgroup_data = [(j, 1.0) for j in range(len(my_mesh.blenData.vertices))]
                else:
                    # This bone is not a parent of this mesh object, no weights
                    vgroup_data = []
    
            else:
                # Normal weight painted mesh
                if my_bone.blenName in weights[0]:
                    # Before we used normalized wright list
                    #vgroup_data = me.getVertsFromGroup(bone.name, 1)
                    group_index = weights[0].index(my_bone.blenName)
                    vgroup_data = [(j, weight[group_index]) for j, weight in enumerate(weights[1]) if weight[group_index]]
                else:
                    vgroup_data = []
    
            file.write('\n\t\tIndexes: ')
    
            i = -1
            for vg in vgroup_data:
                if i == -1:
    
    
            file.write('\n\t\tWeights: ')
            i = -1
            for vg in vgroup_data:
                if i == -1:
    
    
            if my_mesh.fbxParent:
                # TODO FIXME, this case is broken in some cases. skinned meshes just shouldnt have parents where possible!
                m = (my_mesh.matrixWorld.copy().invert() * my_bone.fbxArm.matrixWorld.copy() * my_bone.restMatrix) * mtx4_z90
            else:
                # Yes! this is it...  - but dosnt work when the mesh is a.
                m = (my_mesh.matrixWorld.copy().invert() * my_bone.fbxArm.matrixWorld.copy() * my_bone.restMatrix) * mtx4_z90
    
            #m = mtx4_z90 * my_bone.restMatrix
            matstr = mat4x4str(m)
            matstr_i = mat4x4str(m.invert())
    
    
            file.write('\n\t\tTransform: %s' % matstr_i)  # THIS IS __NOT__ THE GLOBAL MATRIX AS DOCUMENTED :/
    
            file.write('\n\t\tTransformLink: %s' % matstr)
            file.write('\n\t}')
    
        def write_mesh(my_mesh):
    
            me = my_mesh.blenData
    
            # if there are non NULL materials on this mesh
            do_materials = bool(my_mesh.blenMaterials)
            do_textures = bool(my_mesh.blenTextures)
            do_uvs = bool(me.uv_textures)
    
            file.write('\n\tModel: "Model::%s", "Mesh" {' % my_mesh.fbxName)
    
            file.write('\n\t\tVersion: 232')  # newline is added in write_object_props
    
    
            # convert into lists once.
            me_vertices = me.vertices[:]
            me_edges = me.edges[:]
            me_faces = me.faces[:]
    
            poseMatrix = write_object_props(my_mesh.blenObject, None, my_mesh.parRelMatrix())[3]
            pose_items.append((my_mesh.fbxName, poseMatrix))
    
            file.write('\n\t\t}')
            file.write('\n\t\tMultiLayer: 0')
            file.write('\n\t\tMultiTake: 1')
            file.write('\n\t\tShading: Y')
            file.write('\n\t\tCulling: "CullingOff"')
    
            # Write the Real Mesh data here
            file.write('\n\t\tVertices: ')
    
    
            for v in me_vertices:
                if i == -1:
                    file.write('%.6f,%.6f,%.6f' % v.co[:])
                    i = 0
                else:
                    if i == 7:
                        file.write('\n\t\t')
                        i = 0
    
                    file.write(',%.6f,%.6f,%.6f' % v.co[:])
                i += 1
    
    
            file.write('\n\t\tPolygonVertexIndex: ')
    
            for f in me_faces:
                fi = f.vertices[:]
    
                # last index XORd w. -1 indicates end of face
                if i == -1:
                    if len(fi) == 3:
                        file.write('%i,%i,%i' % (fi[0], fi[1], fi[2] ^ -1))
                    else:
                        file.write('%i,%i,%i,%i' % (fi[0], fi[1], fi[2], fi[3] ^ -1))
                    i = 0
                else:
                    if i == 13:
                        file.write('\n\t\t')
                        i = 0
                    if len(fi) == 3:
                        file.write(',%i,%i,%i' % (fi[0], fi[1], fi[2] ^ -1))
                    else:
                        file.write(',%i,%i,%i,%i' % (fi[0], fi[1], fi[2], fi[3] ^ -1))
                i += 1
    
            # write loose edges as faces.
            for ed in me_edges:
                if ed.is_loose:
                    ed_val = ed.vertices[:]
                    ed_val = ed_val[0], ed_val[-1] ^ -1
    
    
                        file.write('%i,%i' % (ed.vertices[0], ed.vertices[1]))
    
                        file.write(',%i,%i' % (ed.vertices[0], ed.vertices[1]))
    
    
            file.write('\n\t\tGeometryVersion: 124')
    
            file.write('''
            LayerElementNormal: 0 {
                Version: 101
                Name: ""
                MappingInformationType: "ByVertice"
                ReferenceInformationType: "Direct"
                Normals: ''')
    
    
                if i == -1:
                    file.write('%.15f,%.15f,%.15f' % v.normal[:])
                    i = 0
    
                    file.write(',%.15f,%.15f,%.15f' % v.normal[:])
    
            file.write('\n\t\t}')
    
            # Write Face Smoothing
            file.write('''
            LayerElementSmoothing: 0 {
                Version: 102
                Name: ""
                MappingInformationType: "ByPolygon"
                ReferenceInformationType: "Direct"
                Smoothing: ''')
    
    
                if i == -1:
                    file.write('%i' % f.use_smooth)
                    i = 0
    
                    file.write(',%i' % f.use_smooth)
    
    
            file.write('\n\t\t}')
    
            # Write Edge Smoothing
            file.write('''
            LayerElementSmoothing: 0 {
                Version: 101
                Name: ""
                MappingInformationType: "ByEdge"
                ReferenceInformationType: "Direct"
                Smoothing: ''')
    
    
                if i == -1:
                    file.write('%i' % (ed.use_edge_sharp))
                    i = 0
    
                    file.write(',%i' % (ed.use_edge_sharp))
    
    
            file.write('\n\t\t}')
    
            # Write VertexColor Layers
            # note, no programs seem to use this info :/
            collayers = []
            if len(me.vertex_colors):
                collayers = me.vertex_colors
                for colindex, collayer in enumerate(collayers):
                    file.write('\n\t\tLayerElementColor: %i {' % colindex)
                    file.write('\n\t\t\tVersion: 101')
                    file.write('\n\t\t\tName: "%s"' % collayer.name)
    
                    file.write('''
                MappingInformationType: "ByPolygonVertex"
                ReferenceInformationType: "IndexToDirect"
                Colors: ''')
    
                    i = -1
    
                    ii = 0  # Count how many Colors we write
    
    
                    for fi, cf in enumerate(collayer.data):
                        if len(me_faces[fi].vertices) == 4:
                            colors = cf.color1[:], cf.color2[:], cf.color3[:], cf.color4[:]
                        else:
                            colors = cf.color1[:], cf.color2[:], cf.color3[:]
    
                        for col in colors:
    
                                file.write('%.4f,%.4f,%.4f,1' % col)
    
                                file.write(',%.4f,%.4f,%.4f,1' % col)
    
    
                    file.write('\n\t\t\tColorIndex: ')
                    i = -1
                    for j in range(ii):
                        if i == -1:
                            file.write('%i' % j)
    
    
                    file.write('\n\t\t}')
    
            # Write UV and texture layers.
            uvlayers = []
            if do_uvs:
                uvlayers = me.uv_textures
                uvlayer_orig = me.uv_textures.active
                for uvindex, uvlayer in enumerate(me.uv_textures):
                    file.write('\n\t\tLayerElementUV: %i {' % uvindex)
                    file.write('\n\t\t\tVersion: 101')
                    file.write('\n\t\t\tName: "%s"' % uvlayer.name)
    
                    file.write('''
                MappingInformationType: "ByPolygonVertex"
                ReferenceInformationType: "IndexToDirect"
                UV: ''')
    
                    i = -1
    
                    ii = 0  # Count how many UVs we write
    
    
                    for uf in uvlayer.data:
                        # workaround, since uf.uv iteration is wrong atm
                        for uv in uf.uv:
    
                                file.write(',%.6f,%.6f' % uv[:])
    
    
                    file.write('\n\t\t\tUVIndex: ')
                    i = -1
                    for j in range(ii):
                        if i == -1:
    
    
                    file.write('\n\t\t}')
    
                    if do_textures:
                        file.write('\n\t\tLayerElementTexture: %i {' % uvindex)
                        file.write('\n\t\t\tVersion: 101')
                        file.write('\n\t\t\tName: "%s"' % uvlayer.name)
    
                        if len(my_mesh.blenTextures) == 1:
                            file.write('\n\t\t\tMappingInformationType: "AllSame"')
                        else:
                            file.write('\n\t\t\tMappingInformationType: "ByPolygon"')
    
                        file.write('\n\t\t\tReferenceInformationType: "IndexToDirect"')
                        file.write('\n\t\t\tBlendMode: "Translucent"')
                        file.write('\n\t\t\tTextureAlpha: 1')
                        file.write('\n\t\t\tTextureId: ')
    
                        if len(my_mesh.blenTextures) == 1:
                            file.write('0')
                        else:
    
                            texture_mapping_local = {None: -1}
    
                            for tex in my_mesh.blenTextures:
    
                                if i == -1:
                                    i = 0
                                    file.write('%s' % texture_mapping_local[img_key])
    
    
                                    file.write(',%s' % texture_mapping_local[img_key])
    
    
                    else:
                        file.write('''
            LayerElementTexture: 0 {
                Version: 101
                Name: ""
                MappingInformationType: "NoMappingInformation"
                ReferenceInformationType: "IndexToDirect"
                BlendMode: "Translucent"
                TextureAlpha: 1
                TextureId: ''')
                    file.write('\n\t\t}')
    
            # Done with UV/textures.
            if do_materials:
                file.write('\n\t\tLayerElementMaterial: 0 {')
                file.write('\n\t\t\tVersion: 101')
                file.write('\n\t\t\tName: ""')
    
                if len(my_mesh.blenMaterials) == 1:
                    file.write('\n\t\t\tMappingInformationType: "AllSame"')
                else:
                    file.write('\n\t\t\tMappingInformationType: "ByPolygon"')
    
                file.write('\n\t\t\tReferenceInformationType: "IndexToDirect"')
                file.write('\n\t\t\tMaterials: ')
    
                if len(my_mesh.blenMaterials) == 1:
                    file.write('0')
                else:
                    # Build a material mapping for this
    
                    material_mapping_local = {}  # local-mat & tex : global index.
    
    
                    for j, mat_tex_pair in enumerate(my_mesh.blenMaterials):
                        material_mapping_local[mat_tex_pair] = j
    
                    len_material_mapping_local = len(material_mapping_local)
    
                    mats = my_mesh.blenMaterialList
    
                    if me.uv_textures.active:
                        uv_faces = me.uv_textures.active.data
                    else:
                        uv_faces = [None] * len(me_faces)
    
    
                    for f, uf in zip(me_faces, uv_faces):
    # 				for f in me_faces:
    
                        try:
                            mat = mats[f.material_index]
                        except:
                            mat = None
    
                        if do_uvs:
                            tex = uf.image  # WARNING - MULTI UV LAYER IMAGES NOT SUPPORTED :/
                        else:
                            tex = None
    
                        if i == -1:
                            i = 0
                            file.write('%s' % (material_mapping_local[mat, tex]))  # None for mat or tex is ok
    
    
                            file.write(',%s' % (material_mapping_local[mat, tex]))
    
    
                file.write('\n\t\t}')
    
            file.write('''
            Layer: 0 {
                Version: 100
                LayerElement:  {
                    Type: "LayerElementNormal"
                    TypedIndex: 0
                }''')
    
            if do_materials:
                file.write('''
                LayerElement:  {
                    Type: "LayerElementMaterial"
                    TypedIndex: 0
                }''')
    
            # Always write this
            if do_textures:
                file.write('''
                LayerElement:  {
                    Type: "LayerElementTexture"
                    TypedIndex: 0
                }''')
    
            if me.vertex_colors:
                file.write('''
                LayerElement:  {
                    Type: "LayerElementColor"
                    TypedIndex: 0
                }''')
    
    
                file.write('''
                LayerElement:  {
                    Type: "LayerElementUV"
                    TypedIndex: 0
                }''')
    
            file.write('\n\t\t}')
    
            if len(uvlayers) > 1:
                for i in range(1, len(uvlayers)):
    
                    file.write('\n\t\tLayer: %i {' % i)
                    file.write('\n\t\t\tVersion: 100')
    
                    file.write('''
                LayerElement:  {
                    Type: "LayerElementUV"''')
    
                    file.write('\n\t\t\t\tTypedIndex: %i' % i)
                    file.write('\n\t\t\t}')
    
                    if do_textures:
    
                        file.write('''
                LayerElement:  {
                    Type: "LayerElementTexture"''')
    
                        file.write('\n\t\t\t\tTypedIndex: %i' % i)
                        file.write('\n\t\t\t}')
    
                    file.write('\n\t\t}')
    
            if len(collayers) > 1:
                # Take into account any UV layers
                layer_offset = 0
    
                if uvlayers:
                    layer_offset = len(uvlayers) - 1
    
                for i in range(layer_offset, len(collayers) + layer_offset):
    
                    file.write('\n\t\tLayer: %i {' % i)
                    file.write('\n\t\t\tVersion: 100')
    
                    file.write('''
                LayerElement:  {
                    Type: "LayerElementColor"''')
    
                    file.write('\n\t\t\t\tTypedIndex: %i' % i)
                    file.write('\n\t\t\t}')
                    file.write('\n\t\t}')
            file.write('\n\t}')
    
        def write_group(name):
            file.write('\n\tGroupSelection: "GroupSelection::%s", "Default" {' % name)
    
            file.write('''
            Properties60:  {
                Property: "MultiLayer", "bool", "",0
                Property: "Pickable", "bool", "",1
                Property: "Transformable", "bool", "",1
                Property: "Show", "bool", "",1
            }
            MultiLayer: 0
        }''')
    
        # add meshes here to clear because they are not used anywhere.
        meshes_to_clear = []
    
        ob_meshes = []
        ob_lights = []
        ob_cameras = []
        # in fbx we export bones as children of the mesh
        # armatures not a part of a mesh, will be added to ob_arms
        ob_bones = []
        ob_arms = []
    
    
        # List of types that have blender objects (not bones)
        ob_all_typegroups = [ob_meshes, ob_lights, ob_cameras, ob_arms, ob_null]
    
    
        groups = []  # blender groups, only add ones that have objects in the selections
        materials = {}  # (mat, image) keys, should be a set()
        textures = {}  # should be a set()
    
        tmp_ob_type = ob_type = None  # incase no objects are exported, so as not to raise an error
    
    
        # if EXP_OBS_SELECTED is false, use sceens objects
        if not batch_objects:
    
            if EXP_OBS_SELECTED:
                tmp_objects = context.selected_objects
            else:
                tmp_objects = scene.objects
    
        else:
            tmp_objects = batch_objects
    
        if EXP_ARMATURE:
            # This is needed so applying modifiers dosnt apply the armature deformation, its also needed
            # ...so mesh objects return their rest worldspace matrix when bone-parents are exported as weighted meshes.
            # set every armature to its rest, backup the original values so we done mess up the scene
            ob_arms_orig_rest = [arm.pose_position for arm in bpy.data.armatures]
    
            for arm in bpy.data.armatures:
                arm.pose_position = 'REST'
    
            if ob_arms_orig_rest:
                for ob_base in bpy.data.objects:
                    if ob_base.type == 'ARMATURE':
                        ob_base.update()
    
                # This causes the makeDisplayList command to effect the mesh
                scene.frame_set(scene.frame_current)
    
        for ob_base in tmp_objects:
    
            # ignore dupli children
            if ob_base.parent and ob_base.parent.dupli_type != 'NONE':
                continue
    
            obs = [(ob_base, ob_base.matrix_world)]
            if ob_base.dupli_type != 'NONE':
                ob_base.create_dupli_list(scene)
                obs = [(dob.object, dob.matrix) for dob in ob_base.dupli_list]
    
            for ob, mtx in obs:
    # 		for ob, mtx in BPyObject.getDerivedObjects(ob_base):
                tmp_ob_type = ob.type
                if tmp_ob_type == 'CAMERA':
                    if EXP_CAMERA:
                        ob_cameras.append(my_object_generic(ob, mtx))
                elif tmp_ob_type == 'LAMP':
                    if EXP_LAMP:
                        ob_lights.append(my_object_generic(ob, mtx))
                elif tmp_ob_type == 'ARMATURE':
                    if EXP_ARMATURE:
                        # TODO - armatures dont work in dupligroups!
    
                        if ob not in ob_arms:
                            ob_arms.append(ob)
    
                        # ob_arms.append(ob) # replace later. was "ob_arms.append(sane_obname(ob), ob)"
                elif tmp_ob_type == 'EMPTY':
                    if EXP_EMPTY:
                        ob_null.append(my_object_generic(ob, mtx))
                elif EXP_MESH:
                    origData = True
                    if tmp_ob_type != 'MESH':
    
                        try:
                            me = ob.create_mesh(scene, True, 'PREVIEW')
                        except:
                            me = None
    
    
                            mats = me.materials
                            origData = False
                    else:
                        # Mesh Type!
                        if EXP_MESH_APPLY_MOD:
                            me = ob.create_mesh(scene, True, 'PREVIEW')
    
                            # print ob, me, me.getVertGroupNames()
    
                            origData = False
                            mats = me.materials
                        else:
                            me = ob.data
                            mats = me.materials
    
    # 						# Support object colors
    # 						tmp_colbits = ob.colbits
    # 						if tmp_colbits:
    # 							tmp_ob_mats = ob.getMaterials(1) # 1 so we get None's too.
    # 							for i in xrange(16):
    # 								if tmp_colbits & (1<<i):
    # 									mats[i] = tmp_ob_mats[i]
    # 							del tmp_ob_mats
    # 						del tmp_colbits
    
                    if me:
    # 					# This WILL modify meshes in blender if EXP_MESH_APPLY_MOD is disabled.
    # 					# so strictly this is bad. but only in rare cases would it have negative results
    # 					# say with dupliverts the objects would rotate a bit differently
    # 					if EXP_MESH_HQ_NORMALS:
    # 						BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines.
    
                        texture_mapping_local = {}
                        material_mapping_local = {}
                        if me.uv_textures:
                            for uvlayer in me.uv_textures:
                                for f, uf in zip(me.faces, uvlayer.data):