Objects can now be animated (camera paths, lamps etc)

However meshes that use an armature cant use object animation.

faster exporting for non modifier applied, armature meshes (transform all verts using wrapped C func)
This commit is contained in:
Campbell Barton 2007-08-29 09:50:08 +00:00
parent 1d43cae37b
commit c96cde42bd

@ -118,6 +118,8 @@ def copy_images(dest_dir, textures):
print '\tCopied %d images' % copyCount print '\tCopied %d images' % copyCount
mtx4_identity = Matrix()
mtx_z90 = RotationMatrix(90, 3, 'z') mtx_z90 = RotationMatrix(90, 3, 'z')
mtx_x90 = RotationMatrix(90, 3, 'x') mtx_x90 = RotationMatrix(90, 3, 'x')
@ -439,6 +441,10 @@ def write(filename, batch_objects = None, \
else: else:
return (mtx4_z90 * ((self.getPoseMatrix(frame) * arm_mat))) * (mtx4_z90 * (self.parent.getPoseMatrix(frame) * arm_mat)).invert() return (mtx4_z90 * ((self.getPoseMatrix(frame) * arm_mat))) * (mtx4_z90 * (self.parent.getPoseMatrix(frame) * arm_mat)).invert()
# we need thes because cameras and lights modified rotations
def getAnimMatrixRot(self, frame):
return self.getAnimMatrix(frame)
def flushAnimData(self): def flushAnimData(self):
self.__anim_poselist.clear() self.__anim_poselist.clear()
@ -449,15 +455,33 @@ def write(filename, batch_objects = None, \
self.fbxName = sane_obname(ob) self.fbxName = sane_obname(ob)
self.blenObject = ob self.blenObject = ob
self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX
self.__anim_poselist = {}
def setPoseFrame(self, f):
self.__anim_poselist[f] = self.blenObject.matrixWorld.copy()
def getAnimMatrix(self, frame):
return self.__anim_poselist[frame] * GLOBAL_MATRIX
def getAnimMatrixRot(self, frame):
type = self.blenObject.type
matrix_rot = (self.__anim_poselist[frame] * GLOBAL_MATRIX).rotationPart()
# Lamps need to be rotated
if type =='Lamp':
matrix_rot = mtx_x90 * matrix_rot
elif ob and type =='Camera':
y = Vector(0,1,0) * matrix_rot
matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y)
return matrix_rot
# ---------------------------------------------- # ----------------------------------------------
print '\nFBX export starting...', filename print '\nFBX export starting...', filename
start_time = Blender.sys.time() start_time = Blender.sys.time()
file = open(filename, 'w') file = open(filename, 'w')
@ -531,7 +555,7 @@ def write(filename, batch_objects = None, \
matrix_rot = matrix.rotationPart() matrix_rot = matrix.rotationPart()
# Lamps need to be rotated # Lamps need to be rotated
if ob and ob.type =='Lamp': if ob and ob.type =='Lamp':
matrix_rot = mtx_x90 * matrix.rotationPart() matrix_rot = mtx_x90 * matrix_rot
rot = tuple(matrix_rot.toEuler()) rot = tuple(matrix_rot.toEuler())
elif ob and ob.type =='Camera': elif ob and ob.type =='Camera':
y = Vector(0,1,0) * matrix_rot y = Vector(0,1,0) * matrix_rot
@ -1275,9 +1299,10 @@ def write(filename, batch_objects = None, \
file.write('\n\t\tTransformLink: %s' % matstr) file.write('\n\t\tTransformLink: %s' % matstr)
file.write('\n\t}') file.write('\n\t}')
#def write_mesh(obname, ob, mtx, me, mats, arm, armname):
def write_mesh(my_mesh): def write_mesh(my_mesh):
me = my_mesh.blenData
# if there are non NULL materials on this mesh # if there are non NULL materials on this mesh
if [mat for mat in my_mesh.blenMaterials if mat]: do_materials = True if [mat for mat in my_mesh.blenMaterials if mat]: do_materials = True
else: do_materials = False else: do_materials = False
@ -1289,11 +1314,26 @@ def write(filename, batch_objects = None, \
file.write('\n\tModel: "Model::%s", "Mesh" {' % my_mesh.fbxName) file.write('\n\tModel: "Model::%s", "Mesh" {' % my_mesh.fbxName)
file.write('\n\t\tVersion: 232') # newline is added in write_object_props file.write('\n\t\tVersion: 232') # newline is added in write_object_props
# Apply the mesh matrix because bones arnt applied correctly if we use object transformation if my_mesh.fbxArm:
# Other then that, object matricies work well on meshes. if my_mesh.origData:
# if this can be fixd, be sure to remove matrix multiplication on the verts. do_tx_write = True
#write_object_props(ob, None, mtx) else:
write_object_props(my_mesh.blenObject, None, Matrix()) do_tx_write = False
me.transform(my_mesh.matrixWorld)
else:
do_tx_write = False
# When we have an armature...
if my_mesh.fbxArm:
# Apply the mesh matrix because bones arnt applied correctly if we use object transformation
# Other then that, object matricies work well on meshes.
# if this can be fixd, be sure to remove matrix multiplication on the verts.
write_object_props(my_mesh.blenObject, None, mtx4_identity)
else:
write_object_props(my_mesh.blenObject, None, my_mesh.matrixWorld)
file.write('\n\t\t}') file.write('\n\t\t}')
file.write('\n\t\tMultiLayer: 0') file.write('\n\t\tMultiLayer: 0')
@ -1301,21 +1341,30 @@ def write(filename, batch_objects = None, \
file.write('\n\t\tShading: Y') file.write('\n\t\tShading: Y')
file.write('\n\t\tCulling: "CullingOff"') file.write('\n\t\tCulling: "CullingOff"')
me = my_mesh.blenData
# Write the Real Mesh data here # Write the Real Mesh data here
file.write('\n\t\tVertices: ') file.write('\n\t\tVertices: ')
i=-1 i=-1
for v in me.verts:
if i==-1: if do_tx_write :# transform verts on write?
file.write('%.6f,%.6f,%.6f' % tuple(v.co * my_mesh.matrixWorld)) for v in me.verts:
i=0 if i==-1:
else: file.write('%.6f,%.6f,%.6f' % tuple(v.co * my_mesh.matrixWorld)); i=0
if i==7: else:
file.write('\n\t\t') if i==7:
i=0 file.write('\n\t\t'); i=0
file.write(',%.6f,%.6f,%.6f'% tuple(v.co * my_mesh.matrixWorld)) file.write(',%.6f,%.6f,%.6f'% tuple(v.co * my_mesh.matrixWorld))
i+=1 i+=1
else: # same as above but has alredy been transformed
for v in me.verts:
if i==-1:
file.write('%.6f,%.6f,%.6f' % tuple(v.co)); i=0
else:
if i==7:
file.write('\n\t\t'); i=0
file.write(',%.6f,%.6f,%.6f'% tuple(v.co))
i+=1
file.write('\n\t\tPolygonVertexIndex: ') file.write('\n\t\tPolygonVertexIndex: ')
i=-1 i=-1
for f in me.faces: for f in me.faces:
@ -1367,17 +1416,31 @@ def write(filename, batch_objects = None, \
mtx_rot = my_mesh.matrixWorld.rotationPart() mtx_rot = my_mesh.matrixWorld.rotationPart()
i=-1 i=-1
for v in me.verts:
if i==-1:
file.write('%.15f,%.15f,%.15f' % tuple(v.no * mtx_rot)) if do_tx_write: # transform normals on write?
i=0 for v in me.verts:
else: if i==-1:
if i==2: file.write('%.15f,%.15f,%.15f' % tuple(v.no * mtx_rot)); i=0
file.write('\n ') else:
i=0 if i==2:
file.write(',%.15f,%.15f,%.15f' % tuple(v.no * mtx_rot)) file.write('\n '); i=0
i+=1 file.write(',%.15f,%.15f,%.15f' % tuple(v.no * mtx_rot))
file.write('\n\t\t}') i+=1
file.write('\n\t\t}')
else: # same as above but has alredy been transformed
for v in me.verts:
if i==-1:
file.write('%.15f,%.15f,%.15f' % tuple(v.no)); i=0
else:
if i==2:
file.write('\n '); i=0
file.write(',%.15f,%.15f,%.15f' % tuple(v.no))
i+=1
file.write('\n\t\t}')
# Write VertexColor Layers # Write VertexColor Layers
@ -1721,6 +1784,7 @@ def write(filename, batch_objects = None, \
if EXP_EMPTY: if EXP_EMPTY:
ob_null.append(my_object_generic(ob)) ob_null.append(my_object_generic(ob))
elif EXP_MESH: elif EXP_MESH:
origData = True
if tmp_ob_type != 'Mesh': if tmp_ob_type != 'Mesh':
me = bpy.data.meshes.new() me = bpy.data.meshes.new()
try: me.getFromObject(ob) try: me.getFromObject(ob)
@ -1728,6 +1792,7 @@ def write(filename, batch_objects = None, \
if me: if me:
meshes_to_clear.append( me ) meshes_to_clear.append( me )
mats = me.materials mats = me.materials
origData = False
else: else:
# Mesh Type! # Mesh Type!
if EXP_MESH_APPLY_MOD: if EXP_MESH_APPLY_MOD:
@ -1747,6 +1812,7 @@ def write(filename, batch_objects = None, \
# print ob, me, me.getVertGroupNames() # print ob, me, me.getVertGroupNames()
meshes_to_clear.append( me ) meshes_to_clear.append( me )
origData = False
mats = me.materials mats = me.materials
else: else:
me = ob.getData(mesh=1) me = ob.getData(mesh=1)
@ -1806,10 +1872,9 @@ def write(filename, batch_objects = None, \
else: else:
blenParentBoneName = armob = None blenParentBoneName = armob = None
#ob_meshes.append( (obname, ob, mtx, me, mats, armob, armname) )
my_mesh = my_object_generic(ob) my_mesh = my_object_generic(ob)
my_mesh.blenData = me my_mesh.blenData = me
my_mesh.origData = origData
my_mesh.blenMaterials = mats my_mesh.blenMaterials = mats
my_mesh.blenTextures = texture_mapping_local.values() my_mesh.blenTextures = texture_mapping_local.values()
@ -2068,7 +2133,7 @@ Objects: {''')
file.write('\n\t\tPoseNode: {') file.write('\n\t\tPoseNode: {')
file.write('\n\t\t\tNode: "Model::%s"' % fbxName ) file.write('\n\t\t\tNode: "Model::%s"' % fbxName )
if matrix: file.write('\n\t\t\tMatrix: %s' % mat4x4str(matrix)) if matrix: file.write('\n\t\t\tMatrix: %s' % mat4x4str(matrix))
else: file.write('\n\t\t\tMatrix: %s' % mat4x4str(Matrix())) else: file.write('\n\t\t\tMatrix: %s' % mat4x4str(mtx4_identity))
file.write('\n\t\t}') file.write('\n\t\t}')
file.write('\n\t}') file.write('\n\t}')
@ -2367,88 +2432,103 @@ Takes: {''')
i = act_start i = act_start
while i <= act_end: while i <= act_end:
Blender.Set('curframe', i) Blender.Set('curframe', i)
#Blender.Window.RedrawAll() for ob_generic in (ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights):
for my_bone in ob_bones: for my_ob in ob_generic:
my_bone.setPoseFrame(i) #Blender.Window.RedrawAll()
if ob_generic == ob_meshes and my_ob.fbxArm:
# We cant animate armature meshes!
pass
else:
my_ob.setPoseFrame(i)
i+=1 i+=1
#for bonename, bone, obname, me, armob in ob_bones: #for bonename, bone, obname, me, armob in ob_bones:
for my_bone in ob_bones: for ob_generic in (ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights):
file.write('\n\t\tModel: "Model::%s" {' % my_bone.fbxName) # ??? - not sure why this is needed
file.write('\n\t\t\tVersion: 1.1')
file.write('\n\t\t\tChannel: "Transform" {')
context_bone_anim_mats = [ my_bone.getAnimMatrix(frame) for frame in xrange(act_start, act_end+1) ]
# ----------------
for TX_LAYER, TX_CHAN in enumerate('TRS'): # transform, rotate, scale
if TX_CHAN=='T': context_bone_anim_vecs = [mtx.translationPart() for mtx in context_bone_anim_mats] for my_ob in ob_generic:
elif TX_CHAN=='R': context_bone_anim_vecs = [mtx.toEuler() for mtx in context_bone_anim_mats]
else: context_bone_anim_vecs = [mtx.scalePart() for mtx in context_bone_anim_mats]
file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation
for i in xrange(3): if ob_generic == ob_meshes and my_ob.fbxArm:
# Loop on each axis of the bone # do nothing,
file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation pass
file.write('\n\t\t\t\t\t\tDefault: %.15f' % context_bone_anim_vecs[0][i] ) else:
file.write('\n\t\t\t\t\t\tKeyVer: 4005')
file.write('\n\t\tModel: "Model::%s" {' % my_ob.fbxName) # ??? - not sure why this is needed
file.write('\n\t\t\tVersion: 1.1')
file.write('\n\t\t\tChannel: "Transform" {')
if not ANIM_OPTIMIZE: context_bone_anim_mats = [ (my_ob.getAnimMatrix(frame), my_ob.getAnimMatrixRot(frame)) for frame in xrange(act_start, act_end+1) ]
# Just write all frames, simple but in-eficient
file.write('\n\t\t\t\t\t\tKeyCount: %i' % (1 + act_end - act_start)) # ----------------
file.write('\n\t\t\t\t\t\tKey: ') # ----------------
frame = act_start for TX_LAYER, TX_CHAN in enumerate('TRS'): # transform, rotate, scale
while frame <= act_end:
if frame!=act_start: if TX_CHAN=='T': context_bone_anim_vecs = [mtx[0].translationPart() for mtx in context_bone_anim_mats]
file.write(',') elif TX_CHAN=='R': context_bone_anim_vecs = [mtx[1].toEuler() for mtx in context_bone_anim_mats]
else: context_bone_anim_vecs = [mtx[0].scalePart() for mtx in context_bone_anim_mats]
file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation
for i in xrange(3):
# Loop on each axis of the bone
file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation
file.write('\n\t\t\t\t\t\tDefault: %.15f' % context_bone_anim_vecs[0][i] )
file.write('\n\t\t\t\t\t\tKeyVer: 4005')
# Curve types are if not ANIM_OPTIMIZE:
# C,n is for bezier? - linear is best for now so we can do simple keyframe removal # Just write all frames, simple but in-eficient
file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n' % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] )) file.write('\n\t\t\t\t\t\tKeyCount: %i' % (1 + act_end - act_start))
#file.write('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] )) file.write('\n\t\t\t\t\t\tKey: ')
frame+=1 frame = act_start
else: while frame <= act_end:
# remove unneeded keys, j is the frame, needed when some frames are removed. if frame!=act_start:
context_bone_anim_keys = [ (vec[i], j) for j, vec in enumerate(context_bone_anim_vecs) ] file.write(',')
# last frame to fisrt frame, missing 1 frame on either side. # Curve types are
# removeing in a backwards loop is faster # C,n is for bezier? - linear is best for now so we can do simple keyframe removal
for j in xrange( (act_end-act_start)-1, 0, -1 ): file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n' % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] ))
# Is this key reduenant? #file.write('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] ))
if abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j-1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT and\ frame+=1
abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j+1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT: else:
del context_bone_anim_keys[j] # remove unneeded keys, j is the frame, needed when some frames are removed.
context_bone_anim_keys = [ (vec[i], j) for j, vec in enumerate(context_bone_anim_vecs) ]
if len(context_bone_anim_keys) == 2 and context_bone_anim_keys[0][0] == context_bone_anim_keys[1][0]:
# This axis has no moton, its okay to skip KeyCount and Keys in this case # last frame to fisrt frame, missing 1 frame on either side.
pass # removeing in a backwards loop is faster
else: for j in xrange( (act_end-act_start)-1, 0, -1 ):
# We only need to write these if there is at least one # Is this key reduenant?
file.write('\n\t\t\t\t\t\tKeyCount: %i' % len(context_bone_anim_keys)) if abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j-1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT and\
file.write('\n\t\t\t\t\t\tKey: ') abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j+1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT:
for val, frame in context_bone_anim_keys: del context_bone_anim_keys[j]
if frame!=act_start:
file.write(',') if len(context_bone_anim_keys) == 2 and context_bone_anim_keys[0][0] == context_bone_anim_keys[1][0]:
# frame is alredy one less then blenders frame # This axis has no moton, its okay to skip KeyCount and Keys in this case
file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n' % (fbx_time(frame), val )) pass
#file.write('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(frame), val )) else:
# We only need to write these if there is at least one
file.write('\n\t\t\t\t\t\tKeyCount: %i' % len(context_bone_anim_keys))
file.write('\n\t\t\t\t\t\tKey: ')
for val, frame in context_bone_anim_keys:
if frame!=act_start:
file.write(',')
# frame is alredy one less then blenders frame
file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n' % (fbx_time(frame), val ))
#file.write('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(frame), val ))
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\tLayerType: %i' % (TX_LAYER+1) )
file.write('\n\t\t\t\t}')
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}')
file.write('\n\t\t\t\t\tLayerType: %i' % (TX_LAYER+1) ) file.write('\n\t\t}')
file.write('\n\t\t\t\t}')
# ---------------
file.write('\n\t\t\t}')
file.write('\n\t\t}')
# end the take # end the take
file.write('\n\t}') file.write('\n\t}')
@ -2629,7 +2709,7 @@ def fbx_ui_write(filename):
Blender.Window.WaitCursor(1) Blender.Window.WaitCursor(1)
# Make the matrix # Make the matrix
GLOBAL_MATRIX = Matrix() GLOBAL_MATRIX = mtx4_identity
GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = GLOBALS['_SCALE'].val GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = GLOBALS['_SCALE'].val
if GLOBALS['_XROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n if GLOBALS['_XROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n
if GLOBALS['_YROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n if GLOBALS['_YROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n