Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2007-08-21 03:38:39 +0400
committerCampbell Barton <ideasman42@gmail.com>2007-08-21 03:38:39 +0400
commit798001556aed3dfec4b76c9525543560c7fbbbe0 (patch)
treeca93719cafd6517a25e373a825e722684bdf3670 /release/scripts/export_fbx.py
parent8a46c006d6b8c0ea567e8a79a729c9b15fe2a744 (diff)
FBX armature + mesh + weights works now.
So it can be used to export walk cycles etc. Animated armatures also work (import BVH and export as FBX for instance) Pose data is transformation is key'd on every frame at the moment, so IK's and constraints are applied but blenders keyframes are not used. at the moment one armature applied multiple meshes wont work properly and armatures cant have transformation.
Diffstat (limited to 'release/scripts/export_fbx.py')
-rw-r--r--release/scripts/export_fbx.py435
1 files changed, 205 insertions, 230 deletions
diff --git a/release/scripts/export_fbx.py b/release/scripts/export_fbx.py
index 5637610b034..9f5fa6abc92 100644
--- a/release/scripts/export_fbx.py
+++ b/release/scripts/export_fbx.py
@@ -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 @@ Select the objects you wish to export and run this script from "File->Export" me
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)
# --------------------------------------------------------------------------
@@ -50,7 +49,6 @@ import time
from math import degrees, atan, pi
from Blender.Mathutils import Matrix, Vector, Euler, RotationMatrix, TranslationMatrix
-
mtx_z90 = RotationMatrix(90, 3, 'z')
mtx_x90 = RotationMatrix(90, 3, 'x')
@@ -77,12 +75,32 @@ YVECN = Vector(0, -1, 0)
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 = {}
+# 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]
@@ -139,7 +157,7 @@ class my_bone_class:
# get pose from frame.
def getPoseMatrix(self, f):
- #return mtx4_z90 * self.__pose_bone.poseMatrix
+ #return mtx4_z90 * self.__pose_bone.poseMatrix.copy()
#return self.__pose_bone.poseMatrix.copy()
return self.__anim_poselist[f][0].copy()
def getPoseHead(self, f):
@@ -170,106 +188,58 @@ class my_bone_class:
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.getPoseMatrix(frame).invert()
+ matrix= matrix * self.parent.getPoseMatrixTip(frame).invert()
rest_matrix= rest_matrix * self.parent.restMatrixInv
- rest_matrix = mtx4_x90n * rest_matrix
+ rest_matrix = mtx4_x90 * rest_matrix
else:
- rest_matrix = mtx4_z90 * rest_matrix
+ rest_matrix = mtx4_z90n * rest_matrix
- # print rest_matrix.toEuler()
return matrix * rest_matrix.invert()
- def getSomeMatrix(self, frame):
- return self.restMatrixLocal * self.getPoseMatrixLocal(frame).invert()
-
- def getSomeMatrix2(self, frame):
- return self.getPoseMatrixLocal(frame) * self.restMatrixLocal.copy()
-
- def getAnimMatrix(self, frame):
-
- # return mtx4_z90 * self.getPoseMatrix(frame)
+ def getPoseMatrix_RestRelative(self, frame):
+ return self.getPoseMatrix(frame) * self.restMatrix.copy().invert()
- #if not self.parent:
- # return mtx4_z90 * self.getPoseMatrix()
+ 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()
- #return (mtx4_z90n * self.getPoseMatrixLocal()).invert()
- #return mtx4_z90n * self.getPoseMatrixLocal_RestRelative()
- #mod = Euler(90,0,90).toMatrix().resize4x4()
+ v = Vector(0,0,1) * self.restMatrix.copy().invert().rotationPart()
+ rest_z90 = RotationMatrix(90, 4, 'r', v)
- # Verry good except initial rotations are wrong.
- # attempt to seperate the rotation and translation
- #return mtx4_z90 * (mtx4_x90 * (mtx4_y90 * self.getPoseMatrixLocal_RestRelative(frame)))
- #return mtx4_z90 * (mtx4_y90 * self.getPoseMatrixLocal_RestRelative())
- #return mtx4_x90 * (mtx4_y90 * ( mtx4_z90 * self.getPoseMatrixLocal_RestRelative(frame)))
- return mtx4_z90 * self.getPoseMatrixLocal_RestRelative(frame)
+ return (mtx4_z90 * self.getPoseMatrix(frame)) * (rest_z90 * self.restMatrix.copy()).invert()
- '''
- mat = self.getPoseMatrixLocal_RestRelative()
- tx = (mtx4_z90 * (mtx4_x90 * (mtx4_y90 * mat))).translationPart()
- mat = TranslationMatrix(tx) * mat.rotationPart().resize4x4()
- return mat
- '''
- def getAnimMatrixRot(self, frame):
- #return self.getPoseMatrixLocal_RestRelative()
- return self.getAnimMatrix(frame)
- #return Matrix()
-
-
-
-# Change the order rotation is applied.
-'''
-ROT_ORDER = [\
-(0,1,2),\
-(1,2,0),\
-(2,0,1),\
-(2,1,0),\
-(1,0,2),\
-(0,2,1),\
-]
-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 getSomeMatrix2(self, frame):
+ return self.getPoseMatrixLocal(frame) * self.restMatrixLocal.copy()
-def eulerMat(mat, rot_order):
- x,y,z = tuple(mat.toEuler())
- # 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))
-'''
-
-
+ 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 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 ])
-
-# 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
-"""
header_comment = \
'''; FBX 6.1.0 project file
; Created by Blender FBX Exporter
-; for support mail cbarton@metavr.com
+; for support mail: ideasman42@gmail.com
; ----------------------------------------------------
'''
@@ -302,9 +272,10 @@ def write_header(file):
file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version'))
-
-
-def write_scene(file, sce, world):
+def write_scene(file, sce, world,\
+ EXPORT_APPLY_MODIFIERS=False,\
+ EXPORT_ANIMATION=True,\
+ EXPORT_NORMALS_HQ=False):
def object_tx(ob, loc, matrix, matrix_mod = None):
'''
@@ -379,14 +350,14 @@ def write_scene(file, sce, world):
loc, rot, scale, matrix, matrix_rot = write_object_tx(ob, loc, matrix, matrix_mod)
- # Rotation order
- # eEULER_XYZ
+ # Rotation order, note, for FBX files Iv loaded normal order is 1
+ # setting to zero.
+ # eEULER_XYZ = 0
# eEULER_XZY
# eEULER_YZX
# eEULER_YXZ
# eEULER_ZXY
- # eEULER_ZYX
-
+ # eEULER_ZYX
file.write('''
Property: "RotationOffset", "Vector3D", "",0,0,0
@@ -402,7 +373,7 @@ def write_scene(file, sce, world):
Property: "TranslationMaxX", "bool", "",0
Property: "TranslationMaxY", "bool", "",0
Property: "TranslationMaxZ", "bool", "",0
- Property: "RotationOrder", "enum", "",1
+ Property: "RotationOrder", "enum", "",0
Property: "RotationSpaceForLimitOnly", "bool", "",0
Property: "AxisLen", "double", "",10
Property: "PreRotation", "Vector3D", "",0,0,0
@@ -463,15 +434,15 @@ def write_scene(file, sce, world):
# -------------------------------------------- Armatures
def write_bone(bone, name, matrix_mod):
- file.write('\n\tModel: "Model::%s", "LimbNode" {' % name)
+ file.write('\n\tModel: "Model::%s", "Limb" {' % name)
file.write('\n\t\tVersion: 232')
write_object_props(bone, None, None, matrix_mod)
- file.write('\n\t\t\tProperty: "Size", "double", "",%.6f' % ((bone.head['ARMATURESPACE']-bone.tail['ARMATURESPACE']) * matrix_mod).length)
- #file.write('\n\t\t\tProperty: "Size", "double", "",1')
- #file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' % (bone.head['ARMATURESPACE']-bone.tail['ARMATURESPACE']).length)
- file.write('\n\t\t\tProperty: "LimbLength", "double", "",1')
+ #file.write('\n\t\t\tProperty: "Size", "double", "",%.6f' % ((bone.head['ARMATURESPACE']-bone.tail['ARMATURESPACE']) * matrix_mod).length)
+ file.write('\n\t\t\tProperty: "Size", "double", "",1')
+ file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' % (bone.head['ARMATURESPACE']-bone.tail['ARMATURESPACE']).length)
+ #file.write('\n\t\t\tProperty: "LimbLength", "double", "",1')
file.write('\n\t\t\tProperty: "Color", "ColorRGB", "",0.8,0.8,0.8')
file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8')
file.write('\n\t\t}')
@@ -849,11 +820,11 @@ def write_scene(file, sce, world):
file.write('\n\t\t\tProperty: "ShininessExponent", "double", "",80.0')
file.write('\n\t\t\tProperty: "ReflectionColor", "ColorRGB", "",0,0,0')
file.write('\n\t\t\tProperty: "ReflectionFactor", "double", "",1')
- file.write('\n\t\t\tProperty: "Emissive", "Vector3D", "",0,0,0')
- file.write('\n\t\t\tProperty: "Ambient", "Vector3D", "",%.1f,%.1f,%.1f' % mat_colamb)
- file.write('\n\t\t\tProperty: "Diffuse", "Vector3D", "",%.1f,%.1f,%.1f' % mat_cold)
+ file.write('\n\t\t\tProperty: "Emissive", "ColorRGB", "",0,0,0')
+ file.write('\n\t\t\tProperty: "Ambient", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_colamb)
+ file.write('\n\t\t\tProperty: "Diffuse", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cold)
if not mat_shadeless:
- file.write('\n\t\t\tProperty: "Specular", "Vector3D", "",%.1f,%.1f,%.1f' % mat_cols)
+ file.write('\n\t\t\tProperty: "Specular", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cols)
file.write('\n\t\t\tProperty: "Shininess", "double", "",%.1f' % mat_hard)
file.write('\n\t\t\tProperty: "Opacity", "double", "",%.1f' % mat_alpha)
if not mat_shadeless:
@@ -947,7 +918,7 @@ def write_scene(file, sce, world):
def write_deformer_skin(obname):
file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {' % obname)
file.write('''
- Version: 100
+ Version: 100
MultiLayer: 0
Type: "Skin"
Properties60: {
@@ -1000,10 +971,12 @@ def write_scene(file, sce, world):
file.write(',%.8f' % vg[1])
i+=1
+
m = mtx4_z90 * (matrix_mod * bone.matrix['ARMATURESPACE'])
matstr = mat4x4str(m)
matstr_i = mat4x4str(m.invert())
- #matstr = mat4x4str(Matrix())
+
+ # --- try more here
# It seems fine to have these matricies the same! - worldspace bone or pose locations?
file.write('\n\t\tTransform: %s' % matstr_i) # THIS IS __NOT__ THE GLOBAL MATRIX AS DOCUMENTED :/
@@ -1012,7 +985,7 @@ def write_scene(file, sce, world):
def write_mesh(obname, ob, mtx, me, mats, arm, armname):
- file.write('\n\tModel: "Model::%s", "Mesh" {' % sane_obname(ob))
+ file.write('\n\tModel: "Model::%s", "Mesh" {' % obname)
file.write('\n\t\tVersion: 232') # newline is added in write_object_props
write_object_props(ob, None, mtx)
file.write('\n\t\t}')
@@ -1356,8 +1329,10 @@ def write_scene(file, sce, world):
file.write('\n\t\t}')
file.write('\n\t}')
+ # add meshes here to clear because they are not used anywhere.
+ meshes_to_clear = []
- ob_meshes = []
+ ob_meshes = []
ob_lights = []
ob_cameras = []
# in fbx we export bones as children of the mesh
@@ -1384,10 +1359,26 @@ def write_scene(file, sce, world):
elif ob_type == 'Empty':
ob_null.append((sane_obname(ob), ob))
else:
- if ob_type == 'Mesh': me = ob.getData(mesh=1)
- else: me = BPyMesh.getMeshFromObject(ob)
+
+ if EXPORT_APPLY_MODIFIERS:
+ me = BPyMesh.getMeshFromObject(ob)
+ if me:
+ meshes_to_clear.append( me )
+ else:
+ if ob_type == 'Mesh':
+ me = ob.getData(mesh=1)
+ else:
+ me = BPyMesh.getMeshFromObject(ob)
+ meshes_to_clear.append( me )
if me:
+
+ # This WILL modify meshes in blender if EXPORT_APPLY_MODIFIERS is disabled.
+ # so strictly this is bad. but only in rare cases would it have negative results
+ # say with dupliverts the objects would rotate a bit differently
+ if EXPORT_NORMALS_HQ:
+ BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines.
+
mats = me.materials
for mat in mats:
# 2.44 use mat.lib too for uniqueness
@@ -1420,9 +1411,6 @@ def write_scene(file, sce, world):
else:
armname = None
- #### me.transform(ob.matrixWorld) # Export real ob coords.
- #### High Quality, not realy needed for now.
- #BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines.
ob_meshes.append( (obname, ob, mtx, me, mats, armob, armname) )
del ob_type
@@ -1442,19 +1430,27 @@ def write_scene(file, sce, world):
for bone in ob.data.bones.values():
#ob_bones.append( (sane_obname(bone), bone, name, None, ob) )
- ob_bones.append( my_bone_class(bone, ob, None, None) )
+ ob_bones.append( my_bone_class(bone, ob, None, name) )
+
+
+ bone_deformer_count = 0 # count how many bones deform a mesh
for mybone in ob_bones:
if mybone.blenBoneParent:
- print "print 'has parent'"
+ # print "print 'has parent'"
for mybone_parent in ob_bones:
#print mybone.blenBoneParent, mybone_parent.blenBone
+
+ # Note 2.45rc2 you can compare bones normally
if mybone.blenBoneParent.name == mybone_parent.blenBone.name and mybone.blenArmature == mybone_parent.blenArmature:
mybone.parent = mybone_parent
#print "FOUND BODE"
break
mybone.calcRestMatrixLocal()
+
+ if mybone.blenMesh:
+ bone_deformer_count += 1
materials = [(sane_matname(mat), mat) for mat in materials.itervalues()]
@@ -1496,9 +1492,12 @@ Definitions: {
len(ob_arms)+\
len(ob_null)+\
len(ob_bones)+\
+ bone_deformer_count+\
len(materials)+\
(len(textures)*2))) # add 1 for the root model 1 for global settings
+ del bone_deformer_count
+
file.write('''
ObjectType: "Model" {
Count: %i
@@ -1618,7 +1617,7 @@ Objects: {''')
for mybone in ob_bones:
if mybone.blenMesh == me:
#write_sub_deformer_skin(obname, bonename, bone, me, armob.matrixWorld)
- write_sub_deformer_skin(obname, mybone.blenName, mybone.blenBone, mybone.blenMesh, mybone.blenArmature.matrixWorld)
+ write_sub_deformer_skin(obname, mybone.fbxName, mybone.blenBone, mybone.blenMesh, mybone.blenArmature.matrixWorld)
# Write pose's really weired, only needed when an armature and mesh are used together
# each by themselves dont need pose data. for now only pose meshes and bones
@@ -1743,19 +1742,24 @@ Relations: {''')
;------------------------------------------------------------------
Connections: {''')
-
- #ob_bones.reverse()
- #print ob_bones[0]
+
+ # NOTE - The FBX SDK dosnt care about the order but some importers DO!
+ # for instance, defining the material->mesh connection
+ # before the mesh->blend_root crashes cinema4d
+
# write the fake root node
file.write('\n\tConnect: "OO", "Model::blend_root", "Model::Scene"')
for obname, ob in ob_null:
if ob.parent:
- file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (obname, sane_matname(ob.parent)))
+ file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (obname, sane_obname(ob.parent)))
else:
file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname)
+ for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
+ file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname)
+
for obname, ob in ob_arms:
file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname)
@@ -1771,7 +1775,7 @@ Connections: {''')
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
# Connect all materials to all objects, not good form but ok for now.
for mat in mats:
- file.write('\n\tConnect: "OO", "Material::%s", "Model::%s"' % (sane_matname(mat), obname))
+ file.write('\n\tConnect: "OO", "Material::%s", "Model::%s"' % (sane_obname(mat), obname))
if textures:
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
@@ -1799,42 +1803,35 @@ Connections: {''')
file.write('\n\tConnect: "OO", "Model::%s", "SubDeformer::Cluster %s"' % (mybone.fbxName, mybone.fbxName))
-
-
#for bonename, bone, obname, me, armob in ob_bones:
for mybone in ob_bones:
if mybone.blenBone.parent:
- file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (mybone.fbxName, sane_obname(mybone.blenBone.parent)) )
+ file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (mybone.fbxName, mybone.parent.fbxName) )
else:
# NOTE, when 'me' is None, the obname is the armature-object,
# the armature object is written as an empty and all root level bones connect to it
file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (mybone.fbxName, mybone.fbxObName) )
- #for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
- for mybone in ob_bones:
- file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % mybone.fbxObName)
-
file.write('\n}')
- #del bone
- #del armob
-
-
- if 1:
+ if EXPORT_ANIMATION:
# WRITE ANIMATION
- #define KTIME_ONE_SECOND KTime (K_LONGLONG(46186158000))
-
- sec = 46186158000 # weired FBX standard
render = sce.render
+
+
+ #define KTIME_ONE_SECOND KTime (K_LONGLONG(46186158000))
+ # 0.5 + val is the same as rounding.
fps = float(render.fps)
+ def fbx_time(t):
+ return int(0.5 + ((t/fps) * 46186158000))
+
# set pose data for all bones
start = render.sFrame
end = render.eFrame
-
i = start
while i <= end:
Blender.Set('curframe', i)
@@ -1842,10 +1839,6 @@ Connections: {''')
mybone.setPoseFrame(i)
i+=1
-
- # 0.5 + val is the same as rounding.
- def fbx_time(t): return int(0.5 + ((t/fps) * sec))
-
file.write('''
;Takes and animation section
;----------------------------------------------------
@@ -1872,95 +1865,45 @@ Takes: {''')
#for bonename, bone, obname, me, armob in ob_bones:
for mybone in ob_bones:
-
file.write('\n\t\tModel: "Model::%s" {' % mybone.fbxName) # ??? - not sure why this is needed
file.write('\n\t\t\tVersion: 1.1')
file.write('\n\t\t\tChannel: "Transform" {')
# ----------------
-
- file.write('\n\t\t\t\tChannel: "T" {') # translation
- matrix = mybone.getAnimMatrix(start)
- for i in xrange(3):
- #Blender.Set('curframe', start)
- #matrix = mybone.getAnimMatrix()
- #matrix = mybone.getPoseMatrix()
+ for TX_LAYER, TX_CHAN in enumerate('TRS'): # transform, rotate, scale
- file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation
- file.write('\n\t\t\t\t\t\tDefault: %.15f' % matrix.translationPart()[i] )
- file.write('\n\t\t\t\t\t\tKeyVer: 4004')
- file.write('\n\t\t\t\t\t\tKeyCount: %i' % (1 + end - start))
- file.write('\n\t\t\t\t\t\tKey: ')
- frame = start
- while frame <= end:
-
- if frame!=start:
- file.write(',')
-
- #Blender.Set('curframe', frame)
- #Blender.Window.RedrawAll()
- matrix = mybone.getAnimMatrix(frame)
- file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n' % (fbx_time(frame), matrix.translationPart()[i] ))
- frame+=1
+ file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation
- if i==0: file.write('\n\t\t\t\t\t\tColor: 1,0,0')
- elif i==1: file.write('\n\t\t\t\t\t\tColor: 0,1,0')
- elif i==2: file.write('\n\t\t\t\t\t\tColor: 0,0,1')
+ for i in xrange(3):
- file.write('\n\t\t\t\t\t}')
- file.write('\n\t\t\t\t}')
- # ---------------
-
-
-
-
- # ----------------
- file.write('\n\t\t\t\tChannel: "R" {') # translation
- matrix = mybone.getAnimMatrixRot(start)
- for i in xrange(3):
- #Blender.Set('curframe', start)
- # matrix = mybone.getAnimMatrixRot()
- #matrix = mybone.getPoseMatrixLocal()
- #matrix = mybone.getPoseMatrix()
-
- file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation
- file.write('\n\t\t\t\t\t\tDefault: %.15f' % matrix.toEuler()[i] )
- file.write('\n\t\t\t\t\t\tKeyVer: 4004')
- file.write('\n\t\t\t\t\t\tKeyCount: %i' % (1 + end - start))
- file.write('\n\t\t\t\t\t\tKey: ')
- frame = start
- while frame <= end:
+ def get_vec(f):
+ matrix = mybone.getAnimMatrix(f)
+ if TX_CHAN=='T': return matrix.translationPart()
+ elif TX_CHAN=='R': return matrix.toEuler()
+ else: return matrix.scalePart()
- if frame!=start:
- file.write(',')
+ file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation
+ file.write('\n\t\t\t\t\t\tDefault: %.15f' % get_vec(start)[i] )
+ file.write('\n\t\t\t\t\t\tKeyVer: 4004')
+ file.write('\n\t\t\t\t\t\tKeyCount: %i' % (1 + end - start))
+ file.write('\n\t\t\t\t\t\tKey: ')
+ frame = start
+ while frame <= end:
+ if frame!=start:
+ file.write(',')
+
+ file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n' % (fbx_time(frame), get_vec(frame)[i] ))
+ frame+=1
- # Blender.Set('curframe', frame)
- #Blender.Window.RedrawAll()
- matrix = mybone.getAnimMatrixRot(frame)
- file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n' % (fbx_time(frame), matrix.toEuler()[i] ))
- frame+=1
-
- if i==0: file.write('\n\t\t\t\t\t\tColor: 1,0,0')
- elif i==1: file.write('\n\t\t\t\t\t\tColor: 0,1,0')
- elif i==2: file.write('\n\t\t\t\t\t\tColor: 0,0,1')
+ if i==0: file.write('\n\t\t\t\t\t\tColor: 1,0,0')
+ elif i==1: file.write('\n\t\t\t\t\t\tColor: 0,1,0')
+ elif i==2: file.write('\n\t\t\t\t\t\tColor: 0,0,1')
- file.write('\n\t\t\t\t\t}')
- file.write('\n\t\t\t\t}')
- # ---------------
-
-
-
-
+ file.write('\n\t\t\t\t\t}')
+ file.write('\n\t\t\t\t\tLayerType: %i' % (TX_LAYER+1) )
+ file.write('\n\t\t\t\t}')
-
-
- '''
- file.write('\n\t\t\t\t}')
- file.write('\n\t\t\t\tChannel: "R" {') # translation
- file.write('\n\t\t\t\t}')
- file.write('\n\t\t\t\tChannel: "S" {') # translation
- file.write('\n\t\t\t\t}')
- '''
+ # ---------------
file.write('\n\t\t\t}')
file.write('\n\t\t}')
@@ -1983,13 +1926,9 @@ Takes: {''')
# Clear mesh data Only when writing with modifiers applied
- #for obname, ob, me, mats, arm, armname in objects:
- # me.verts = None
-
-
-
-
-
+ for me in meshes_to_clear:
+ me.verts = None
+
def write_footer(file, sce, world):
@@ -2002,6 +1941,9 @@ def write_footer(file, sce, world):
render = sce.render
+ fps = float(render.fps)
+ def fbx_time(t): return int(0.5 + ((t/fps) * 46186158000))
+
file.write('\n;Version 5 settings')
file.write('\n;------------------------------------------------------------------')
file.write('\n')
@@ -2019,12 +1961,12 @@ def write_footer(file, sce, world):
file.write('\n\t\tFogColor: %.1f,%.1f,%.1f,1' % tuple(world.hor))
file.write('\n\t}')
file.write('\n\tSettings: {')
- file.write('\n\t\tFrameRate: "%i"' % render.fps)
+ file.write('\n\t\tFrameRate: "%i"' % int(fps))
file.write('\n\t\tTimeFormat: 1')
file.write('\n\t\tSnapOnFrames: 0')
file.write('\n\t\tReferenceTimeIndex: -1')
- file.write('\n\t\tTimeLineStartTime: %i' % render.sFrame)
- file.write('\n\t\tTimeLineStopTime: %i' % render.eFrame)
+ file.write('\n\t\tTimeLineStartTime: %i' % fbx_time(render.sFrame))
+ file.write('\n\t\tTimeLineStopTime: %i' % fbx_time(render.eFrame))
file.write('\n\t}')
file.write('\n\tRendererSetting: {')
file.write('\n\t\tDefaultCamera: "Producer Perspective"')
@@ -2039,22 +1981,55 @@ def write_footer(file, sce, world):
sane_name_mapping_tex.clear()
import bpy
-def write_ui(filename):
- if not filename.lower().endswith('.fbx'):
- filename += '.fbx'
+
+def write(filename,
+ EXPORT_APPLY_MODIFIERS=False,
+ EXPORT_ANIMATION=True,
+ EXPORT_NORMALS_HQ=False):
- #if not BPyMessages.Warning_SaveOver(filename):
- # return
sce = bpy.data.scenes.active
world = sce.world
- Blender.Window.WaitCursor(1)
file = open(filename, 'w')
write_header(file)
- write_scene(file, sce, world)
- write_footer(file, sce, world)
+ write_scene(file, sce, world,
+ EXPORT_APPLY_MODIFIERS, EXPORT_ANIMATION, EXPORT_NORMALS_HQ)
+ write_footer(file, sce, world)
+
+
+def write_ui(filename):
+ if not filename.lower().endswith('.fbx'):
+ filename += '.fbx'
+
+ if not BPyMessages.Warning_SaveOver(filename):
+ return
+
+ # ui -------------
+ EXPORT_APPLY_MODIFIERS = Blender.Draw.Create(0)
+ EXPORT_NORMALS_HQ = Blender.Draw.Create(0)
+ EXPORT_ANIMATION = Blender.Draw.Create(1)
+
+ pup_block = [\
+ ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object. May break vert order for morph targets.'),\
+ ('High Quality Normals', EXPORT_NORMALS_HQ, 'Calculate high quality normals for rendering.'),\
+ ('Animation', EXPORT_ANIMATION, 'Export armature animation between the start and end frames'),\
+ ]
+
+ if not Blender.Draw.PupBlock('Export FBX...', pup_block):
+ return
+
+ Blender.Window.EditMode(0)
+ Blender.Window.WaitCursor(1)
+
+ EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val
+ EXPORT_NORMALS_HQ = EXPORT_NORMALS_HQ.val
+ EXPORT_ANIMATION = EXPORT_ANIMATION.val
+ # ------- end UI
+
+ Blender.Window.WaitCursor(1)
+ write(filename, EXPORT_APPLY_MODIFIERS, EXPORT_ANIMATION, EXPORT_NORMALS_HQ)
Blender.Window.WaitCursor(0)
if __name__ == '__main__':
- #Blender.Window.FileSelector(write_ui, 'Export FBX', Blender.sys.makename(ext='.fbx'))
- write_ui('/scratch/test.fbx') \ No newline at end of file
+ Blender.Window.FileSelector(write_ui, 'Export FBX', Blender.sys.makename(ext='.fbx'))
+ #write('/scratch/test.fbx')