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