[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [11853] trunk/blender/release/scripts/ export_fbx.py: Fixes

Campbell Barton cbarton at metavr.com
Tue Aug 28 04:11:51 CEST 2007


Revision: 11853
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=11853
Author:   campbellbarton
Date:     2007-08-28 04:11:49 +0200 (Tue, 28 Aug 2007)

Log Message:
-----------
Fixes
* material indicies were incorrectly exported
* export object materials using colbits
* scaled armatures would scale bones twice
* scenes with no world would raise an error

Additions
* added options for global scaling and rotating, defaults will load correctly in MotionBuilder
* modifiers can be applied to skinned meshes, the armatures are set to their rest positions for python to get the un-deformed mesh. This makes exporting a mesh with subsurf and armature applied work as expected.
* meshes with parent bones are exported as weighted meshes.
* use lamp modes for cast light and cast shadow FBX settings.
* batch exporter, scenes or groups can be exporter to a file each.
* help button to load the wiki exporter page.

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-27 21:45:18 UTC (rev 11852)
+++ trunk/blender/release/scripts/export_fbx.py	2007-08-28 02:11:49 UTC (rev 11853)
@@ -55,13 +55,22 @@
 	except:
 		set = None # so it complains you dont have a !
 
+try:
+	import os
+except:
+	os = None
 
+
+
+
+
 import Blender
 import bpy
 from Blender.Mathutils import Matrix, Vector, Euler, RotationMatrix, TranslationMatrix
 
 import BPyObject
 import BPyMesh
+reload(BPyMesh)
 import BPySys
 import BPyMessages
 
@@ -152,6 +161,7 @@
 	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
@@ -179,97 +189,8 @@
 def sane_takename(data):	return sane_name(data, sane_name_mapping_take)
 
 
-# storage classes
-class my_bone_class:
-	__slots__ =(\
-	  'blenName',\
-	  'blenBone',\
-	  'blenMeshes',\
-	  'restMatrix',\
-	  'parent',\
-	  'blenName',\
-	  'fbxName',\
-	  'fbxArm',\
-	  '__pose_bone',\
-	  '__anim_poselist')
-	
-	def __init__(self, blenBone, fbxArm):
-		
-		# This is so 2 armatures dont have naming conflicts since FBX bones use object namespace
-		self.fbxName = sane_obname(blenBone)
-		
-		self.blenName =			blenBone.name
-		self.blenBone =			blenBone
-		self.blenMeshes =		{}					# fbxMeshObName : mesh
-		self.fbxArm =			fbxArm
-		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 = fbxArm.blenObject.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.fbxArm.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()
 
 
-class my_object_generic:
-	# Other settings can be applied for each type - mesh, armature etc.
-	def __init__(self, ob):
-		self.fbxName = sane_obname(ob)
-		self.blenObject = ob
-		self.matrixWorld = ob.matrixWorld
-
-
 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 ])
 
@@ -312,48 +233,225 @@
 		EXP_CAMERA =				True,
 		EXP_EMPTY =					True,
 		EXP_IMAGE_COPY =			False,
+		GLOBAL_MATRIX =				Matrix(),
 		ANIM_ENABLE =				True,
 		ANIM_OPTIMIZE =				True,
 		ANIM_OPTIMIZE_PRECISSION =	6,
-		ANIM_ACTION_ALL =			True,
+		ANIM_ACTION_ALL =			False,
 		BATCH_ENABLE =				False,
 		BATCH_GROUP =				True,
 		BATCH_SCENE =				False,
 		BATCH_FILE_PREFIX =			'',
 		BATCH_OWN_DIR =				False
 	):
+
 	
-	"""
+	
+	
 	# ----------------- Batch support!
 	if BATCH_ENABLE:
+		if os == None:	BATCH_OWN_DIR = False
+		
+		fbxpath = filename
+		
 		# get the path component of filename
 		
+		tmp_exists = Blender.sys.exists(fbxpath)
 		
+		if tmp_exists != 2: # a file, we want a path
+			while fbxpath and fbxpath[-1] not in ('/', '\\'):
+				fbxpath = fbxpath[:-1]
+			if not filename:
+				Draw.PupMenu('Error%t|Directory does not exist!')
+				return
+			
+			tmp_exists = Blender.sys.exists(fbxpath)
 		
-		# call this function within a loop with BATCH_ENABLE == False
+		if tmp_exists != 2:
+			Draw.PupMenu('Error%t|Directory does not exist!')
+			return
+		
+		if not fbxpath.endswith(Blender.sys.sep):
+			fbxpath += Blender.sys.sep
+		del tmp_exists
+		
+		
 		if BATCH_GROUP:
 			data_seq = bpy.data.groups
-		else: # must be scene
+		else:
 			data_seq = bpy.data.scenes
 		
+		# call this function within a loop with BATCH_ENABLE == False
+		orig_sce = bpy.data.scenes.active
 		
 		
-		
-		
+		new_fbxpath = fbxpath # own dir option modifies, we need to keep an original
 		for data in data_seq: # scene or group
-			newname = BATCH_FILE_PREFIX + BPySys.cleanName(data)
+			newname = BATCH_FILE_PREFIX + BPySys.cleanName(data.name)
 			
 			if BATCH_OWN_DIR:
-				# make dir
-
-		return
+				new_fbxpath = fbxpath + newname + Blender.sys.sep
+				# path may alredy exist
+				# TODO - might exist but be a file. unlikely but should probably account for it.
+				
+				if Blender.sys.exists(new_fbxpath) == 0:
+					os.mkdir(new_fbxpath)
+				
+			
+			filename = new_fbxpath + newname + '.fbx'
+			
+			print '\nBatch exporting %s as...\n\t"%s"' % (data, filename)
+			
+			if BATCH_GROUP: #group
+				# group, so objects update properly, add a dummy scene.
+				sce = bpy.data.scenes.new()
+				sce.Layers = (1<<20) -1
+				bpy.data.scenes.active = sce
+				for ob_base in data.objects:
+					sce.objects.link(ob_base)
+				
+				sce.update(1)
+				
+				# TODO - BUMMER! Armatures not in the group wont animate the mesh
+				
+			else:# scene
+				
+				
+				data_seq.active = data
+			
+			
+			# Call self with modified args
+			# Dont pass batch options since we alredy usedt them
+			write(filename, data.objects,
+				False,
+				EXP_MESH,
+				EXP_MESH_APPLY_MOD,
+				EXP_MESH_HQ_NORMALS,
+				EXP_ARMATURE,
+				EXP_LAMP,
+				EXP_CAMERA,
+				EXP_EMPTY,
+				EXP_IMAGE_COPY,
+				GLOBAL_MATRIX,
+				ANIM_ENABLE,
+				ANIM_OPTIMIZE,
+				ANIM_OPTIMIZE_PRECISSION,
+				ANIM_ACTION_ALL
+			)
+			
+			if BATCH_GROUP:
+				# remove temp group scene
+				bpy.data.scenes.unlink(sce)
+		
+		bpy.data.scenes.active = orig_sce
+		
+		return # so the script wont run after we have batch exported.
 	
 	# end batch support
-	"""
 	
 	
 	
 	
+	
+	# ----------------------------------------------
+	# storage classes
+	class my_bone_class:
+		__slots__ =(\
+		  'blenName',\
+		  'blenBone',\
+		  'blenMeshes',\
+		  'restMatrix',\
+		  'parent',\
+		  'blenName',\
+		  'fbxName',\
+		  'fbxArm',\
+		  '__pose_bone',\
+		  '__anim_poselist')
+		
+		def __init__(self, blenBone, fbxArm):
+			
+			# This is so 2 armatures dont have naming conflicts since FBX bones use object namespace
+			self.fbxName = sane_obname(blenBone)
+			
+			self.blenName =			blenBone.name
+			self.blenBone =			blenBone
+			self.blenMeshes =		{}					# fbxMeshObName : mesh
+			self.fbxArm =			fbxArm
+			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 = fbxArm.blenObject.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.fbxArm.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()
+
+
+	class my_object_generic:
+		# Other settings can be applied for each type - mesh, armature etc.
+		def __init__(self, ob):
+			self.fbxName = sane_obname(ob)
+			self.blenObject = ob
+			self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX
+	# ----------------------------------------------
+	
+	
+	
+	
+	
+	
+	
+	
 	print '\nFBX export starting...', filename
 	start_time = Blender.sys.time()
 	file = open(filename, 'w')
@@ -391,7 +489,6 @@
 	file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime)
 	file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version'))
 	
-	
 	# --------------- funcs for exporting
 	def object_tx(ob, loc, matrix, matrix_mod = None):
 		'''
@@ -414,7 +511,7 @@
 			rot =			tuple(matrix_rot.toEuler())
 			
 		else:
-			if ob and not matrix:	matrix = ob.matrixWorld
+			if ob and not matrix:	matrix = ob.matrixWorld * GLOBAL_MATRIX
 			matrix_rot = matrix
 			#if matrix:
 			#	matrix = matrix_scale * matrix
@@ -560,9 +657,14 @@
 		
 		#((my_bone.blenData.head['ARMATURESPACE'] * my_bone.fbxArm.matrixWorld) - (my_bone.blenData.tail['ARMATURESPACE'] * my_bone.fbxArm.matrixWorld)).length)
 		
+		"""
 		file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list