[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [11806] branches/2-44-stable/blender/ release/scripts/export_fbx.py: updated from trunk, fixes a user intterface and support for exporting animated characters
Campbell Barton
cbarton at metavr.com
Fri Aug 24 14:32:57 CEST 2007
Revision: 11806
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=11806
Author: campbellbarton
Date: 2007-08-24 14:32:57 +0200 (Fri, 24 Aug 2007)
Log Message:
-----------
updated from trunk, fixes a user intterface and support for exporting animated characters
Modified Paths:
--------------
branches/2-44-stable/blender/release/scripts/export_fbx.py
Modified: branches/2-44-stable/blender/release/scripts/export_fbx.py
===================================================================
--- branches/2-44-stable/blender/release/scripts/export_fbx.py 2007-08-24 12:13:34 UTC (rev 11805)
+++ branches/2-44-stable/blender/release/scripts/export_fbx.py 2007-08-24 12:32:57 UTC (rev 11806)
@@ -1,7 +1,7 @@
#!BPY
"""
Name: 'Autodesk FBX (.fbx)...'
-Blender: 243
+Blender: 244
Group: 'Export'
Tooltip: 'Selection to an ASCII Autodesk FBX '
"""
@@ -18,7 +18,6 @@
All objects that can be represented as a mesh (mesh, curve, metaball, surface, text3d)
will be exported as mesh data.
"""
-
# --------------------------------------------------------------------------
# FBX Export v0.1 by Campbell Barton (AKA Ideasman)
# --------------------------------------------------------------------------
@@ -41,22 +40,106 @@
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
+try:
+ import time
+ # import os # only needed for batch export, nbot used yet
+except:
+ time = None # use this to check if they have python modules installed
+
+# for python 2.3 support
+try:
+ set()
+except:
+ try:
+ from sets import Set as set
+ except:
+ set = None # so it complains you dont have a !
+
+
import Blender
+import bpy
+from Blender.Mathutils import Matrix, Vector, Euler, RotationMatrix, TranslationMatrix
+
import BPyObject
+reload(BPyObject)
import BPyMesh
import BPySys
import BPyMessages
-import time
-from math import degrees, atan, pi
+
+
+def copy_file(source, dest):
+ file = open(source, 'rb')
+ data = file.read()
+ file.close()
+
+ file = open(dest, 'wb')
+ file.write(data)
+ file.close()
+
+
+def copy_images(dest_dir, textures):
+ if not dest_dir.endswith(Blender.sys.sep):
+ dest_dir += sys.sep
+
+ image_paths = {} # use set() later
+ for img in textures:
+ image_paths[Blender.sys.expendpath(img.filename)] = None
+
+
+ # Now copy images
+ copyCount = 0
+ for image_path in image_paths.itervalues():
+ 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 alredy there
+ print '\tCopying "%s" > "%s"' % (image_path, dest_image_path)
+ try:
+ copy_file(image_path, dest_image_path)
+ copyCount+=1
+ except:
+ print '\t\tWarning, file failed to copy, skipping.'
+
+ print '\tCopied %d images' % copyCount
+
+
+mtx_z90 = RotationMatrix(90, 3, 'z')
+mtx_x90 = RotationMatrix(90, 3, 'x')
+
+# testing
+mtx_x90 = RotationMatrix( 90, 3, 'x')
+mtx_x90n = RotationMatrix(-90, 3, 'x')
+mtx_y90 = RotationMatrix( 90, 3, 'y')
+mtx_y90n = RotationMatrix(-90, 3, 'y')
+mtx_z90 = RotationMatrix( 90, 3, 'z')
+mtx_z90n = RotationMatrix(-90, 3, 'z')
+
+
+mtx4_x90 = RotationMatrix( 90, 4, 'x')
+mtx4_x90n = RotationMatrix(-90, 4, 'x')
+mtx4_y90 = RotationMatrix( 90, 4, 'y')
+mtx4_y90n = RotationMatrix(-90, 4, 'y')
+mtx4_z90 = RotationMatrix( 90, 4, 'z')
+mtx4_z90n = RotationMatrix(-90, 4, 'z')
+
+XVEC = Vector(1, 0, 0)
+XVECN = Vector(-1, 0, 0)
+YVEC = Vector(0, 1, 0)
+YVECN = Vector(0, -1, 0)
+ZVEC = Vector(0, 0, 1)
+ZVECN = Vector(0, 0, -1)
+
# Used to add the scene name into the filename without using odd chars
sane_name_mapping_ob = {}
sane_name_mapping_mat = {}
sane_name_mapping_tex = {}
+sane_name_mapping_take = {}
def strip_path(p):
return p.split('\\')[-1].split('/')[-1]
+# todo - Disallow the name 'Scene' and 'blend_root' - it will bugger things up.
def sane_name(data, dct):
if not data: return None
name = data.name
@@ -64,34 +147,221 @@
except: pass
orig_name = name
- name = BPySys.cleanName(name)
+ if not name:
+ name = 'unnamed' # blank string, ASKING FOR TROUBLE!
+ else:
+ name = BPySys.cleanName(name)
+
+ # Unlikely but make sure reserved names arnt used
+ if name == 'Scene': name = 'Scene_'
+ elif name == 'blend_root': name = 'blend_root_'
+
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)
-# May use this later
-"""
-# Auto class, use for datastorage only, a like a dictionary but with limited slots
-def auto_class(slots):
- exec('class container_class(object): __slots__=%s' % slots)
- return container_class
-"""
+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'
+# storage classes
+class my_bone_class:
+ __slots__ =(\
+ 'blenName',\
+ 'blenBone',\
+ 'blenMeshes',\
+ 'blenArmature',\
+ 'restMatrix',\
+ 'parent',\
+ 'blenName',\
+ 'fbxName',\
+ 'fbxArmObName',\
+ '__pose_bone',\
+ '__anim_poselist')
+
+ unique_names = set()
+
+ def __init__(self, blenBone, blenArmature, fbxArmObName):
+
+ # This is so 2 armatures dont have naming conflicts since FBX bones use object namespace
+ fbxName = sane_obname(blenBone)
+ while fbxName in my_bone_class.unique_names: fbxName = increment_string(fbxName)
+ self.fbxName = fbxName
+ my_bone_class.unique_names.add(fbxName)
+
+ self.fbxArmObName = fbxArmObName
+
+ self.blenName = blenBone.name
+ self.blenBone = blenBone
+ self.blenMeshes = {} # fbxMeshObName : mesh
+ self.blenArmature = blenArmature
+ self.restMatrix = blenBone.matrix['ARMATURESPACE']
+
+ # not used yet
+ # self.restMatrixInv = self.restMatrix.copy().invert()
+ # self.restMatrixLocal = None # set later, need parent matrix
+
+ self.parent = None
+
+ # not public
+ pose = blenArmature.getPose()
+ self.__pose_bone = pose.bones[self.blenName]
+
+ # store a list if matricies here, (poseMatrix, head, tail)
+ # {frame:posematrix, frame:posematrix, ...}
+ self.__anim_poselist = {}
+
+ '''
+ def calcRestMatrixLocal(self):
+ if self.parent:
+ self.restMatrixLocal = self.restMatrix * self.parent.restMatrix.copy().invert()
+ else:
+ self.restMatrixLocal = self.restMatrix.copy()
+ '''
+ def setPoseFrame(self, f):
+ # cache pose info here, frame must be set beforehand
+
+ # Didnt end up needing head or tail, if we do - here it is.
+ '''
+ self.__anim_poselist[f] = (\
+ self.__pose_bone.poseMatrix.copy(),\
+ self.__pose_bone.head.copy(),\
+ self.__pose_bone.tail.copy() )
+ '''
+
+ self.__anim_poselist[f] = self.__pose_bone.poseMatrix.copy()
+
+ # get pose from frame.
+ def getPoseMatrix(self, f):
+ return self.__anim_poselist[f]
+ '''
+ def getPoseHead(self, f):
+ #return self.__pose_bone.head.copy()
+ return self.__anim_poselist[f][1].copy()
+ def getPoseTail(self, f):
+ #return self.__pose_bone.tail.copy()
+ return self.__anim_poselist[f][2].copy()
+ '''
+ # end
+
+ def getAnimMatrix(self, frame):
+ arm_mat = self.blenArmature.matrixWorld
+ if not self.parent:
+ return mtx4_z90 * (self.getPoseMatrix(frame) * arm_mat)
+ else:
+ return (mtx4_z90 * ((self.getPoseMatrix(frame) * arm_mat))) * (mtx4_z90 * (self.parent.getPoseMatrix(frame) * arm_mat)).invert()
+
+ def flushAnimData(self):
+ self.__anim_poselist.clear()
+
+
+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 ])
+
+def meshNormalizedWeights(me):
+ try: # account for old bad BPyMesh
+ groupNames, vWeightList = BPyMesh.meshWeight2List(me)
+ except:
+ return [],[]
+
+ if not groupNames:
+ return [],[]
+
+ for i, vWeights in enumerate(vWeightList):
+ tot = 0.0
+ for w in vWeights:
+ tot+=w
+
+ if tot:
+ for j, w in enumerate(vWeights):
+ vWeights[j] = w/tot
+
+ return groupNames, vWeightList
+
header_comment = \
'''; FBX 6.1.0 project file
; Created by Blender FBX Exporter
-; for support mail cbarton at metavr.com
+; for support mail: ideasman42 at gmail.com
; ----------------------------------------------------
'''
-def write_header(file):
+# This func can be called with just the filename
+def write(filename, batch_objects = None, \
+ EXP_OBS_SELECTED = True,
+ EXP_MESH = True,
+ EXP_MESH_APPLY_MOD = True,
+ EXP_MESH_HQ_NORMALS = False,
+ EXP_ARMATURE = True,
+ EXP_LAMP = True,
+ EXP_CAMERA = True,
+ EXP_EMPTY = True,
+ EXP_IMAGE_COPY = False,
+ ANIM_ENABLE = True,
+ ANIM_OPTIMIZE = True,
+ ANIM_OPTIMIZE_PRECISSION = 6,
+ ANIM_ACTION_ALL = True,
+ BATCH_ENABLE = False,
+ BATCH_GROUP = True,
+ BATCH_SCENE = False,
+ BATCH_FILE_PREFIX = '',
+ BATCH_OWN_DIR = False
+ ):
+
+ """
+ # ----------------- Batch support!
+ if BATCH_ENABLE:
+ # get the path component of filename
+
+
+
+ # call this function within a loop with BATCH_ENABLE == False
+ if BATCH_GROUP:
+ data_seq = bpy.data.groups
+ else: # must be scene
+ data_seq = bpy.data.scenes
+
+
+
+
+
+ for data in data_seq: # scene or group
+ newname = BATCH_FILE_PREFIX + BPySys.cleanName(data)
+
+ if BATCH_OWN_DIR:
+ # make dir
+
+ return
+
+ # end batch support
+ """
+
+
+
+
+ print '\nFBX export starting...', filename
+ start_time = Blender.sys.time()
+ file = open(filename, 'w')
+ sce = bpy.data.scenes.active
+ world = sce.world
+
+
+ # ---------------------------- Write the header first
file.write(header_comment)
- curtime = time.localtime()[0:6]
+ if time:
+ curtime = time.localtime()[0:6]
+ else:
+ curtime = [0,0,0,0,0,0]
#
file.write(\
'''FBXHeaderExtension: {
@@ -115,49 +385,72 @@
file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime)
file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version'))
-
-
-
-
-def write_scene(file, sce, world):
- def write_object_tx(ob, loc, matrix):
+
+ # --------------- funcs for exporting
+ def object_tx(ob, loc, matrix, matrix_mod = None):
'''
- We have loc to set the location if non blender objects that have a location
+ Matrix mod is so armature objects can modify their bone matricies
'''
-
- if ob and not matrix: matrix = ob.matrixWorld
- matrix_rot = matrix
- #if matrix:
- # matrix = matrix_scale * matrix
-
- if matrix:
- loc = tuple(matrix.translationPart())
- scale = tuple(matrix.scalePart())
+ if isinstance(ob, Blender.Types.BoneType):
- matrix_rot = matrix.rotationPart()
- # Lamps need to be rotated
- if ob and ob.type =='Lamp':
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list