[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