From c96cde42bdc486fe28ab1ea4c9998480d5813a1e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 29 Aug 2007 09:50:08 +0000 Subject: [PATCH] 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) --- release/scripts/export_fbx.py | 294 +++++++++++++++++++++------------- 1 file changed, 187 insertions(+), 107 deletions(-) diff --git a/release/scripts/export_fbx.py b/release/scripts/export_fbx.py index 984f5d26b48..047803a898d 100644 --- a/release/scripts/export_fbx.py +++ b/release/scripts/export_fbx.py @@ -118,6 +118,8 @@ def copy_images(dest_dir, textures): print '\tCopied %d images' % copyCount +mtx4_identity = Matrix() + mtx_z90 = RotationMatrix(90, 3, 'z') mtx_x90 = RotationMatrix(90, 3, 'x') @@ -439,6 +441,10 @@ def write(filename, batch_objects = None, \ else: 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): self.__anim_poselist.clear() @@ -449,15 +455,33 @@ def write(filename, batch_objects = None, \ self.fbxName = sane_obname(ob) self.blenObject = ob 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 start_time = Blender.sys.time() file = open(filename, 'w') @@ -531,7 +555,7 @@ def write(filename, batch_objects = None, \ matrix_rot = matrix.rotationPart() # Lamps need to be rotated if ob and ob.type =='Lamp': - matrix_rot = mtx_x90 * matrix.rotationPart() + matrix_rot = mtx_x90 * matrix_rot rot = tuple(matrix_rot.toEuler()) elif ob and ob.type =='Camera': 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}') - #def write_mesh(obname, ob, mtx, me, mats, arm, armname): def write_mesh(my_mesh): + me = my_mesh.blenData + # if there are non NULL materials on this mesh if [mat for mat in my_mesh.blenMaterials if mat]: do_materials = True 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\t\tVersion: 232') # newline is added in write_object_props - # 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(ob, None, mtx) - write_object_props(my_mesh.blenObject, None, Matrix()) + if my_mesh.fbxArm: + if my_mesh.origData: + do_tx_write = True + else: + 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\tMultiLayer: 0') @@ -1301,21 +1341,30 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tShading: Y') file.write('\n\t\tCulling: "CullingOff"') - me = my_mesh.blenData # Write the Real Mesh data here file.write('\n\t\tVertices: ') i=-1 - for v in me.verts: - if i==-1: - file.write('%.6f,%.6f,%.6f' % tuple(v.co * my_mesh.matrixWorld)) - i=0 - else: - if i==7: - file.write('\n\t\t') - i=0 - file.write(',%.6f,%.6f,%.6f'% tuple(v.co * my_mesh.matrixWorld)) - i+=1 + + if do_tx_write :# transform verts on write? + for v in me.verts: + if i==-1: + file.write('%.6f,%.6f,%.6f' % tuple(v.co * my_mesh.matrixWorld)); i=0 + else: + if i==7: + file.write('\n\t\t'); i=0 + file.write(',%.6f,%.6f,%.6f'% tuple(v.co * my_mesh.matrixWorld)) + 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: ') i=-1 for f in me.faces: @@ -1367,17 +1416,31 @@ def write(filename, batch_objects = None, \ mtx_rot = my_mesh.matrixWorld.rotationPart() i=-1 - for v in me.verts: - if i==-1: - file.write('%.15f,%.15f,%.15f' % tuple(v.no * mtx_rot)) - i=0 - else: - if i==2: - file.write('\n ') - i=0 - file.write(',%.15f,%.15f,%.15f' % tuple(v.no * mtx_rot)) - i+=1 - file.write('\n\t\t}') + + + if do_tx_write: # transform normals on write? + for v in me.verts: + if i==-1: + file.write('%.15f,%.15f,%.15f' % tuple(v.no * mtx_rot)); i=0 + else: + if i==2: + file.write('\n '); i=0 + file.write(',%.15f,%.15f,%.15f' % tuple(v.no * mtx_rot)) + 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 @@ -1721,6 +1784,7 @@ def write(filename, batch_objects = None, \ if EXP_EMPTY: ob_null.append(my_object_generic(ob)) elif EXP_MESH: + origData = True if tmp_ob_type != 'Mesh': me = bpy.data.meshes.new() try: me.getFromObject(ob) @@ -1728,6 +1792,7 @@ def write(filename, batch_objects = None, \ if me: meshes_to_clear.append( me ) mats = me.materials + origData = False else: # Mesh Type! if EXP_MESH_APPLY_MOD: @@ -1747,6 +1812,7 @@ def write(filename, batch_objects = None, \ # print ob, me, me.getVertGroupNames() meshes_to_clear.append( me ) + origData = False mats = me.materials else: me = ob.getData(mesh=1) @@ -1806,10 +1872,9 @@ def write(filename, batch_objects = None, \ else: blenParentBoneName = armob = None - #ob_meshes.append( (obname, ob, mtx, me, mats, armob, armname) ) - my_mesh = my_object_generic(ob) my_mesh.blenData = me + my_mesh.origData = origData my_mesh.blenMaterials = mats my_mesh.blenTextures = texture_mapping_local.values() @@ -2068,7 +2133,7 @@ Objects: {''') file.write('\n\t\tPoseNode: {') file.write('\n\t\t\tNode: "Model::%s"' % fbxName ) 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}') @@ -2367,88 +2432,103 @@ Takes: {''') i = act_start while i <= act_end: Blender.Set('curframe', i) - #Blender.Window.RedrawAll() - for my_bone in ob_bones: - my_bone.setPoseFrame(i) + for ob_generic in (ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights): + for my_ob in ob_generic: + #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 #for bonename, bone, obname, me, armob in ob_bones: - for my_bone in ob_bones: - - 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 + for ob_generic in (ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights): - if TX_CHAN=='T': context_bone_anim_vecs = [mtx.translationPart() for mtx in context_bone_anim_mats] - 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] + for my_ob in ob_generic: - 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') + if ob_generic == ob_meshes and my_ob.fbxArm: + # do nothing, + pass + else: + + 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: - # 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 - while frame <= act_end: - if frame!=act_start: - file.write(',') + context_bone_anim_mats = [ (my_ob.getAnimMatrix(frame), my_ob.getAnimMatrixRot(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[0].translationPart() for mtx in context_bone_anim_mats] + 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 - # C,n is for bezier? - linear is best for now so we can do simple keyframe removal - 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\t\t%i,%.15f,L' % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] )) - frame+=1 - else: - # 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) ] - - # last frame to fisrt frame, missing 1 frame on either side. - # removeing in a backwards loop is faster - for j in xrange( (act_end-act_start)-1, 0, -1 ): - # Is this key reduenant? - if abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j-1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT and\ - abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j+1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT: - del context_bone_anim_keys[j] - - 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 - pass - 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 not ANIM_OPTIMIZE: + # 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 + while frame <= act_end: + if frame!=act_start: + file.write(',') + + # Curve types are + # C,n is for bezier? - linear is best for now so we can do simple keyframe removal + 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\t\t%i,%.15f,L' % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] )) + frame+=1 + else: + # 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) ] + + # last frame to fisrt frame, missing 1 frame on either side. + # removeing in a backwards loop is faster + for j in xrange( (act_end-act_start)-1, 0, -1 ): + # Is this key reduenant? + if abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j-1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT and\ + abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j+1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT: + del context_bone_anim_keys[j] + + 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 + pass + 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\t\tLayerType: %i' % (TX_LAYER+1) ) - file.write('\n\t\t\t\t}') - - # --------------- - - file.write('\n\t\t\t}') - file.write('\n\t\t}') + file.write('\n\t\t\t}') + file.write('\n\t\t}') # end the take file.write('\n\t}') @@ -2629,7 +2709,7 @@ def fbx_ui_write(filename): Blender.Window.WaitCursor(1) # 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 if GLOBALS['_XROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n if GLOBALS['_YROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n