Skip to content
Snippets Groups Projects
export_fbx.py 118 KiB
Newer Older
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

# Script copyright (C) Campbell Barton

"""
This script is an exporter to the FBX file format.

http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx
"""

import os
import time
import math  # math.pi
import shutil  # for file copying

import bpy
from mathutils import Vector, Euler, Matrix

# XXX not used anymore, images are copied one at a time
def copy_images(dest_dir, textures):
    import shutil
    if not dest_dir.endswith(os.sep):
        dest_dir += os.sep

    image_paths = set()
    for tex in textures:
        image_paths.add(bpy.path.abspath(tex.filepath))

    # Now copy images
    copyCount = 0
    for image_path in image_paths:
        if Blender.sys.exists(image_path):
            # Make a name for the target path.
            dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1]
            if not Blender.sys.exists(dest_image_path):  # Image isnt already there
                print("\tCopying %r > %r" % (image_path, dest_image_path))
                try:
                    shutil.copy(image_path, dest_image_path)
                except:
                    print("\t\tWarning, file failed to copy, skipping.")

    print('\tCopied %d images' % copyCount)

# I guess FBX uses degrees instead of radians (Arystan).
# Call this function just before writing to FBX.
# 180 / math.pi == 57.295779513
def tuple_rad_to_deg(eul):
    return eul[0] * 57.295779513, eul[1] * 57.295779513, eul[2] * 57.295779513

# def strip_path(p):
# 	return p.split('\\')[-1].split('/')[-1]

# Used to add the scene name into the filepath without using odd chars
sane_name_mapping_ob = {}
sane_name_mapping_mat = {}
sane_name_mapping_tex = {}
sane_name_mapping_take = {}
sane_name_mapping_group = {}

# Make sure reserved names are not used
sane_name_mapping_ob['Scene'] = 'Scene_'
sane_name_mapping_ob['blend_root'] = 'blend_root_'

def increment_string(t):
    name = t
    num = ''
    while name and name[-1].isdigit():
        num = name[-1] + num
        name = name[:-1]
    if num:
        return '%s%d' % (name, int(num) + 1)
    else:
        return name + '_0'


# todo - Disallow the name 'Scene' and 'blend_root' - it will bugger things up.
def sane_name(data, dct):
    #if not data: return None

    if type(data) == tuple:  # materials are paired up with images
        data, other = data
        use_other = True
    else:
        other = None
        use_other = False

    name = data.name if data else None
    orig_name = name

    if other:
        orig_name_other = other.name
        name = '%s #%s' % (name, orig_name_other)
    else:
        orig_name_other = None

    # dont cache, only ever call once for each data type now,
    # so as to avoid namespace collision between types - like with objects <-> bones
    #try:		return dct[name]
    #except:		pass

    if not name:
        name = 'unnamed'  # blank string, ASKING FOR TROUBLE!
        name = bpy.path.clean_name(name)  # use our own
    while name in iter(dct.values()):
        name = increment_string(name)
    if use_other:  # even if other is None - orig_name_other will be a string or None
        dct[orig_name, orig_name_other] = name
    else:
        dct[orig_name] = name

    return name


def sane_obname(data):
    return sane_name(data, sane_name_mapping_ob)


def sane_matname(data):
    return sane_name(data, sane_name_mapping_mat)


def sane_texname(data):
    return sane_name(data, sane_name_mapping_tex)


def sane_takename(data):
    return sane_name(data, sane_name_mapping_take)


def sane_groupname(data):
    return sane_name(data, sane_name_mapping_group)

# def derived_paths(fname_orig, basepath, FORCE_CWD=False):
# 	'''
# 	fname_orig - blender path, can be relative
# 	basepath - fname_rel will be relative to this
# 	FORCE_CWD - dont use the basepath, just add a ./ to the filepath.
# 		use when we know the file will be in the basepath.
# 	'''
# 	fname = bpy.path.abspath(fname_orig)
# # 	fname = Blender.sys.expandpath(fname_orig)
# 	fname_strip = os.path.basename(fname)
# # 	fname_strip = strip_path(fname)
# 	if FORCE_CWD:
# 		fname_rel = '.' + os.sep + fname_strip
# 	else:
# 		fname_rel = bpy.path.relpath(fname, basepath)
# # 		fname_rel = Blender.sys.relpath(fname, basepath)
# 	if fname_rel.startswith('//'): fname_rel = '.' + os.sep + fname_rel[2:]
# 	return fname, fname_strip, fname_rel


def mat4x4str(mat):
    return '%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f' % tuple([f for v in mat for f in v])


# XXX not used
# duplicated in OBJ exporter
def getVertsFromGroup(me, group_index):
    ret = []

    for i, v in enumerate(me.vertices):
        for g in v.groups:
            if g.group == group_index:
                ret.append((i, g.weight))

        return ret

# ob must be OB_MESH
def BPyMesh_meshWeight2List(ob, me):
    ''' Takes a mesh and return its group names and a list of lists, one list per vertex.
    aligning the each vert list with the group names, each list contains float value for the weight.
Loading
Loading full blame...