Skip to content
Snippets Groups Projects
export_fbx.py 116 KiB
Newer Older
  • Learn to ignore specific revisions
  •                 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}')
    
        # 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 = io_utils.path_reference(tex.filepath, base_src, base_dst, path_mode, "", copy_set)
                fname_strip = os.path.basename(fname_rel)
    
                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)
    
            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 = io_utils.path_reference(tex.filepath, base_src, base_dst, path_mode, "", copy_set)
                fname_strip = os.path.basename(fname_rel)
    
                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.inverted() * 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.inverted() * my_bone.fbxArm.matrixWorld.copy() * my_bone.restMatrix) * mtx4_z90
    
    
            #m = mtx4_z90 * my_bone.restMatrix
            matstr = mat4x4str(m)
    
            matstr_i = mat4x4str(m.inverted())
    
            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
    
            LayerElementSmoothing: 0 {
                Version: 102
                Name: ""
                MappingInformationType: "ByPolygon"
                ReferenceInformationType: "Direct"
                Smoothing: ''')
    
    
                i = -1
                for f in me_faces:
                    if i == -1:
                        file.write('%i' % f.use_smooth)
    
                    else:
                        if i == 54:
                            file.write('\n\t\t\t ')
                            i = 0
                        file.write(',%i' % f.use_smooth)
                    i += 1
    
            elif mesh_smooth_type == 'EDGE':
                # Write Edge Smoothing
                file.write('''
    
            LayerElementSmoothing: 0 {
                Version: 101
                Name: ""
                MappingInformationType: "ByEdge"
                ReferenceInformationType: "Direct"
                Smoothing: ''')
    
    
                i = -1
                for ed in me_edges:
                    if i == -1:
                        file.write('%i' % (ed.use_edge_sharp))
    
                    else:
                        if i == 54:
                            file.write('\n\t\t\t ')
                            i = 0
                        file.write(',%i' % (ed.use_edge_sharp))
                    i += 1
    
                file.write('\n\t\t}')
            elif mesh_smooth_type == 'OFF':
                pass
            else:
                raise Exception("invalid mesh_smooth_type: %r" % mesh_smooth_type)
    
    
            # 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
                }''')
    
    
            # Smoothing info
            if mesh_smooth_type != 'OFF':
                file.write('''
                LayerElement:  {
                    Type: "LayerElementSmoothing"
                    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
    
    Campbell Barton's avatar
    Campbell Barton committed
    ## XXX
    
            # 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_tag()
    
    
                # This causes the makeDisplayList command to effect the mesh
                scene.frame_set(scene.frame_current)
    
    
        for ob_base in context_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.dupli_list_create(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':
    
                        ob_cameras.append(my_object_generic(ob, mtx))
                elif tmp_ob_type == 'LAMP':
    
                        ob_lights.append(my_object_generic(ob, mtx))
                elif tmp_ob_type == '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':
    
                        ob_null.append(my_object_generic(ob, mtx))
    
                    origData = True
                    if tmp_ob_type != 'MESH':
    
                            me = ob.to_mesh(scene, True, 'PREVIEW')
    
                            mats = me.materials
                            origData = False
                    else:
                        # Mesh Type!
    
                            me = ob.to_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 mesh_apply_modifiers 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):
                                    tex = uf.image
                                    textures[tex] = texture_mapping_local[tex] = None
    
    
                                    try:
                                        mat = mats[f.material_index]
                                    except:
                                        mat = None
    
                                    materials[mat, tex] = material_mapping_local[mat, tex] = None  # should use sets, wait for blender 2.5
    
    
                        else:
                            for mat in mats:
                                # 2.44 use mat.lib too for uniqueness
                                materials[mat, None] = material_mapping_local[mat, None] = None
                            else:
                                materials[None, None] = None
    
    
                            armob = ob.find_armature()
                            blenParentBoneName = None
    
                            # parent bone - special case
                            if (not armob) and ob.parent and ob.parent.type == 'ARMATURE' and \
                                    ob.parent_type == 'BONE':
                                armob = ob.parent
                                blenParentBoneName = ob.parent_bone
    
                            if armob and armob not in ob_arms:
                                ob_arms.append(armob)
    
                            # Warning for scaled, mesh objects with armatures
                            if abs(ob.scale[0] - 1.0) > 0.05 or abs(ob.scale[1] - 1.0) > 0.05 or abs(ob.scale[1] - 1.0) > 0.05:
                                operator.report('WARNING', "Object '%s' has a scale of (%.3f, %.3f, %.3f), Armature deformation will not work as expected!, Apply Scale to fix." % ((ob.name,) + tuple(ob.scale)))
    
                        else:
                            blenParentBoneName = armob = None
    
                        my_mesh = my_object_generic(ob, mtx)
    
                        my_mesh.blenData = me
                        my_mesh.origData = origData
                        my_mesh.blenMaterials = list(material_mapping_local.keys())
    
                        my_mesh.blenTextures = list(texture_mapping_local.keys())
    
                        # sort the name so we get predictable output, some items may be NULL
    
                        my_mesh.blenMaterials.sort(key=lambda m: (getattr(m[0], "name", ""), getattr(m[1], "name", "")))
                        my_mesh.blenTextures.sort(key=lambda m: getattr(m, "name", ""))
    
    
                        # if only 1 null texture then empty the list
                        if len(my_mesh.blenTextures) == 1 and my_mesh.blenTextures[0] is None:
                            my_mesh.blenTextures = []
    
    
                        my_mesh.fbxArm = armob  # replace with my_object_generic armature instance later
                        my_mesh.fbxBoneParent = blenParentBoneName  # replace with my_bone instance later
    
                ob_base.dupli_list_clear()
    
            # now we have the meshes, restore the rest arm position
            for i, arm in enumerate(bpy.data.armatures):
                arm.pose_position = ob_arms_orig_rest[i]
    
            if ob_arms_orig_rest:
                for ob_base in bpy.data.objects:
                    if ob_base.type == 'ARMATURE':
    
                        ob_base.update_tag()
    
                # This causes the makeDisplayList command to effect the mesh
                scene.frame_set(scene.frame_current)
    
    
        del tmp_ob_type, context_objects
    
    
        # now we have collected all armatures, add bones
        for i, ob in enumerate(ob_arms):
    
            ob_arms[i] = my_arm = my_object_generic(ob)
    
    
            my_arm.fbxBones = []
            my_arm.blenData = ob.data
    
                my_arm.blenAction = ob.animation_data.action
    
            else:
                my_arm.blenAction = None
    # 		my_arm.blenAction =		ob.action
    
    
            # fbxName, blenderObject, my_bones, blenderActions
            #ob_arms[i] = fbxArmObName, ob, arm_my_bones, (ob.action, [])
    
            for bone in my_arm.blenData.bones:
                my_bone = my_bone_class(bone, my_arm)
    
                my_arm.fbxBones.append(my_bone)
                ob_bones.append(my_bone)
    
    
        # add the meshes to the bones and replace the meshes armature with own armature class
        #for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
        for my_mesh in ob_meshes:
            # Replace
            # ...this could be sped up with dictionary mapping but its unlikely for
            # it ever to be a bottleneck - (would need 100+ meshes using armatures)
            if my_mesh.fbxArm:
                for my_arm in ob_arms:
                    if my_arm.blenObject == my_mesh.fbxArm:
                        my_mesh.fbxArm = my_arm
                        break
    
            for my_bone in ob_bones:
    
                # The mesh uses this bones armature!
                if my_bone.fbxArm == my_mesh.fbxArm:
                    if my_bone.blenBone.use_deform:
                        my_bone.blenMeshes[my_mesh.fbxName] = me
    
                    # parent bone: replace bone names with our class instances
                    # my_mesh.fbxBoneParent is None or a blender bone name initialy, replacing if the names match.
                    if my_mesh.fbxBoneParent == my_bone.blenName:
                        my_mesh.fbxBoneParent = my_bone
    
    
        bone_deformer_count = 0  # count how many bones deform a mesh
    
        my_bone_blenParent = None
        for my_bone in ob_bones:
            my_bone_blenParent = my_bone.blenBone.parent
            if my_bone_blenParent:
                for my_bone_parent in ob_bones: