[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [11797] trunk/blender/release/scripts/ export_fbx.py: FBX export update
Campbell Barton
cbarton at metavr.com
Thu Aug 23 18:34:15 CEST 2007
Revision: 11797
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=11797
Author: campbellbarton
Date: 2007-08-23 18:34:15 +0200 (Thu, 23 Aug 2007)
Log Message:
-----------
FBX export update
* added a user interface
* added support for exporting multiple actions as FBX's 'Takes'
* added support for one armature effecting multiple meshes
* added support for multiple armatures (bone names wont have namespace collisions)
Modified Paths:
--------------
trunk/blender/release/scripts/export_fbx.py
Modified: trunk/blender/release/scripts/export_fbx.py
===================================================================
--- trunk/blender/release/scripts/export_fbx.py 2007-08-23 13:43:09 UTC (rev 11796)
+++ trunk/blender/release/scripts/export_fbx.py 2007-08-23 16:34:15 UTC (rev 11797)
@@ -40,15 +40,57 @@
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
+
+from math import degrees, atan, pi
+import time
+import os # only needed for batch export
+
import Blender
+import bpy
+from Blender.Mathutils import Matrix, Vector, Euler, RotationMatrix, TranslationMatrix
+
import BPyObject
import BPyMesh
import BPySys
import BPyMessages
-import time
-from math import degrees, atan, pi
-from Blender.Mathutils import Matrix, Vector, Euler, RotationMatrix, TranslationMatrix
+
+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')
@@ -75,32 +117,13 @@
ZVEC = Vector(0, 0, 1)
ZVECN = Vector(0, 0, -1)
-ROT_ORDER = [\
-(0,1,2),\
-(1,2,0),\
-(2,0,1),\
-(2,1,0),\
-(1,0,2),\
-(0,2,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 = {}
-# Change the order rotation is applied.
-MATRIX_IDENTITY_3x3 = Matrix([1,0,0],[0,1,0],[0,0,1])
-MATRIX_IDENTITY_4x4 = Matrix([1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1])
-
-def eulerRotate(x,y,z, rot_order):
- # Clamp all values between 0 and 360, values outside this raise an error.
- mats=[RotationMatrix(x%360,3,'x'), RotationMatrix(y%360,3,'y'), RotationMatrix(z%360,3,'z')]
- # print rot_order
- # Standard BVH multiplication order, apply the rotation in the order Z,X,Y
- return (mats[rot_order[2]]*(mats[rot_order[1]]* (mats[rot_order[0]]* MATRIX_IDENTITY_3x3))).toEuler()
-
def strip_path(p):
return p.split('\\')[-1].split('/')[-1]
@@ -112,124 +135,122 @@
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)
+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:
- def __init__(self, blenBone, blenArmature, blenMesh, fbxObName):
+ __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.blenBoneParent = blenBone.parent
- self.blenMesh = blenMesh
+ self.blenMeshes = {} # fbxMeshObName : mesh
self.blenArmature = blenArmature
self.restMatrix = blenBone.matrix['ARMATURESPACE']
- self.restMatrixInv = self.restMatrix.copy().invert()
- self.restMatrixLocal = None # set later, need parent matrix
+
+ # not used yet
+ # self.restMatrixInv = self.restMatrix.copy().invert()
+ # self.restMatrixLocal = None # set later, need parent matrix
+
self.parent = None
- self.fbxName = sane_obname(blenBone)
- self.fbxObName = fbxObName
# not public
pose = blenArmature.getPose()
self.__pose_bone = pose.bones[self.blenName]
- self.__bone_parent = blenBone.parent
- self.__anim_poselist = {} # store a list if matricies here, (poseMatrix, head, tail)
+
+ # 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 mtx4_z90 * self.__pose_bone.poseMatrix.copy()
- #return self.__pose_bone.poseMatrix.copy()
- return self.__anim_poselist[f][0].copy()
+ 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 getPoseMatrixLocal(self, frame):
- if self.parent:
- return self.getPoseMatrix(frame) * self.parent.getPoseMatrix(frame).invert()
- else:
- return self.getPoseMatrix(frame)
- #return mtx4_z90 * mat
- def getPoseMatrixLocalTip(self):
- #print "ASAS"
- if self.parent:
-
- vec = self.parent.getPoseTail(frame) - self.parent.getPoseHead(frame)
- #vec = self.parent.getPoseHead(frame) - self.parent.getPoseTail(frame)
- mat = TranslationMatrix(vec) * self.parent.getPoseMatrix(frame)
- #mat = self.parent.getPoseMatrix() * TranslationMatrix(vec).invert()
- #print " ASAS"
- return mat * self.getPoseMatrix(frame)
- else:
- return self.getPoseMatrix(frame)
-
- def getPoseMatrixTip(self, frame):
- vec = self.getPoseHead(frame) - self.getPoseTail(frame)
- return TranslationMatrix(vec) * self.getPoseMatrix(frame)
-
- # This works in 1 test but not for all
-
- def getPoseMatrixLocal_RestRelative(self, frame):
-
- matrix= self.getPoseMatrix(frame)
- rest_matrix = self.restMatrix.copy()
-
- if self.parent:
- #matrix= matrix * self.parent.getPoseMatrix(frame).invert()
- matrix= matrix * self.parent.getPoseMatrixTip(frame).invert()
- rest_matrix= rest_matrix * self.parent.restMatrixInv
- rest_matrix = mtx4_x90 * rest_matrix
- else:
- rest_matrix = mtx4_z90n * rest_matrix
-
- return matrix * rest_matrix.invert()
-
- def getPoseMatrix_RestRelative(self, frame):
- return self.getPoseMatrix(frame) * self.restMatrix.copy().invert()
-
- def getPoseMatrix_RestRelative_ZROT(self, frame):
- # Works for rest bones with no loc/size/rot
- # but failes othwrwise.
- return (mtx4_z90 * self.getPoseMatrix(frame)) * (mtx4_z90 * self.restMatrix.copy()).invert()
-
- v = Vector(0,0,1) * self.restMatrix.copy().invert().rotationPart()
- rest_z90 = RotationMatrix(90, 4, 'r', v)
-
- return (mtx4_z90 * self.getPoseMatrix(frame)) * (rest_z90 * self.restMatrix.copy()).invert()
-
- def getSomeMatrix2(self, frame):
- return self.getPoseMatrixLocal(frame) * self.restMatrixLocal.copy()
-
def getAnimMatrix(self, frame):
if not self.parent:
return mtx4_z90 * self.getPoseMatrix(frame)
else:
return (mtx4_z90 * self.getPoseMatrix(frame)) * (mtx4_z90 * self.parent.getPoseMatrix(frame)).invert()
+
+ def flushAnimData(self):
+ self.__anim_poselist.clear()
def mat4x4str(mat):
@@ -244,7 +265,67 @@
'''
-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]
#
@@ -270,13 +351,10 @@
file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime)
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list