Guest User

Export Blender Animation Library with Linked Armature to FBX

a guest
May 3rd, 2016
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.16 KB | None | 0 0
  1. # animation-export.py
  2. #
  3. # Purpose:
  4. #   When dealing with larger animation projects such as characters for
  5. #   video games or machinima, a lot of Actions (Blender animation clips)
  6. #   will result. It is advantageous to store these in separate files.
  7. #  
  8. #   Blender supports this scenario by allowing .blend files to "link"
  9. #   in objects from each other. However, as of now, the FBX exporter
  10. #   will not correctly export animation tracks in separate .blend files.
  11. #
  12. #   up to 2.72:   works if not using a proxy for the armature
  13. #   2.73:         nothing works at all
  14. #   2.74 to 2.77: only works with proxy but animations are detached
  15. #   2.78:         only works with proxy, animations attached to wrong object
  16. #
  17. #   In short, until the rewrite of the proxy system (may happen in 2.8),
  18. #   exporting animations on linked Armatures only works in Blender 2.72
  19. #   and without proxies, but the old FBX exporter has its own problems.
  20. #
  21. #   The only viable solution, then, is to:
  22. #     1) open the master file
  23. #     2) append all animation tracks from a separate .blend file to it
  24. #     3) export the master file
  25. #     4) exit without saving
  26. #
  27. #   This script will do just that. Since exporting the master meshes
  28. #   multiple times would be a giant waste of space, it only exports
  29. #   a single mesh (the mesh must be a part of the Armature, though).
  30. #  
  31. # Usage:
  32. #   Invoke Blender like this:
  33. #
  34. #   blender master.blend --python animation-export.py -- \
  35. #           animations.blend wildcard* target.fbx
  36. #
  37. #   - The first argument (master.blend) is the .blend file containing
  38. #     your character without animations
  39. #
  40. #   - The second argument (--python animation-export.py) runs this script
  41. #
  42. #   - The third argument (--) is a separator behind which the script
  43. #     arguments begin. You can add other Blender arguments before this.
  44. #
  45. #   - The fourth argument (animations.blend) specifies the .blend file
  46. #     containing the animation tracks you wish to export
  47. #
  48. #   - The fifth argument (wildcard*) is a wildcard string specifying
  49. #     which animations to append (this is useful if you have animations
  50. #     for multiple characters in animations.blend and want to append
  51. #     only the animations for the specific character you're exporting).
  52. #
  53. #   - The sixth argument (target.fbx) is the output file into which
  54. #     the animations will be exported
  55. #
  56. # Conventions:
  57. #   If you have multiple Armatures in your .blend file (very common if you
  58. #   use Rigify and kept the metarig), turn off interaction on them to make
  59. #   this script ignore them. Interaction can be turned off by disabling the
  60. #   little mouse cursor-like symbol in the outliner.
  61. #
  62. import bpy
  63. import sys
  64. import fnmatch
  65. import os
  66.  
  67. def append_actions_from_file(path, wildcard):
  68.     """
  69.    Builds a list of the actions present in a blend file
  70.    """
  71.     # https://www.blender.org/api/blender_python_api_2_77_0/bpy.types.BlendDataLibraries.html
  72.     with bpy.data.libraries.load(path) as (data_from, data_to):
  73.         data_to.actions = [name for name in data_from.actions if fnmatch.fnmatch(name, wildcard)]
  74.  
  75. def make_all_layers_visible():
  76.     """
  77.    Makes all layers in the scene visible
  78.    """
  79.     for i in range(len(bpy.context.scene.layers)):
  80.         bpy.context.scene.layers[i] = True
  81.  
  82. def clear_selection():
  83.     """
  84.    Unselects all selected objects in the scene
  85.    """
  86.     for ob in bpy.data.objects:
  87.         ob.select = False
  88.  
  89. def select_first_armature():
  90.     """
  91.    Selects the first object of type Armature found in the scene
  92.    """
  93.     for ob in bpy.data.objects:
  94.         if ob.type == 'ARMATURE':
  95.             if ob.hide_select == False:
  96.                 print("Selected Armature for exporting: " + ob.name)
  97.                 ob.select = True
  98.                 return ob
  99.  
  100. def create_and_select_dummy_mesh(armature):
  101.     """
  102.    Creates a dummy mesh that is parented to the specified armature
  103.    """
  104.     verts = [
  105.         (-1.0, -1.0, 0.0),
  106.         (+1.0, -1.0, 0.0),
  107.         (+1.0, +1.0, 0.0),
  108.         (-1.0, +1.0, 0.0),
  109.         (0.0, 0.0, +1.0)
  110.     ]
  111.     faces = [
  112.         (0, 1, 4),
  113.         (1, 2, 4),
  114.         (2, 3, 4),
  115.         (3, 0, 4),
  116.         (2, 1, 0),
  117.         (0, 3, 2)
  118.     ]
  119.     mesh_data = bpy.data.meshes.new("AnimationDummy.Mesh")
  120.     mesh_data.from_pydata(verts, [], faces)
  121.     mesh_data.update()
  122.    
  123.     obj = bpy.data.objects.new("AnimationDummy", mesh_data)
  124.     scene = bpy.context.scene
  125.     scene.objects.link(obj)
  126.    
  127.     # The object needs to be select to be exported
  128.     obj.select = True
  129.    
  130.     # Add the Armature modifier to the mesh
  131.     mod = obj.modifiers.new('Armature', 'ARMATURE')
  132.     mod.object = armature
  133.     mod.use_bone_envelopes = False
  134.     mod.use_vertex_groups = True
  135.    
  136.     # Create a vertex group matching the first bone of the Armature
  137.     # (assumption: an Armature with less than 1 bone is impossible)
  138.     # and assign all vertices of the mesh to this vertex group
  139.     firstbone = armature.pose.bones[0]
  140.     vgroup = obj.vertex_groups.new(firstbone.name)
  141.     vgroup.add([0, 1, 2, 3, 4], 1.0, 'REPLACE')
  142.  
  143. # ----------------------------------------------------------------------
  144.  
  145. print("animation-export.py running...")
  146.  
  147. cwd = os.getcwd()
  148. print("base path: " + cwd)
  149.  
  150. argv = sys.argv
  151. argv = argv[argv.index("--") + 1:]  # get all args after "--"
  152.  
  153. # Animation library (.blend file) we want to append from
  154. animlib = argv[0]
  155. animlib = os.path.join(cwd, animlib)
  156. print("animation library: " + animlib)
  157.  
  158. # Wildcard of the actions we need to append
  159. wildcard = argv[1]
  160. print("actions to export: " + wildcard)
  161.  
  162. # Target path
  163. fbxpath = argv[2]
  164. print("fbx target path: " + fbxpath)
  165.  
  166. # Append the actions from the animation library
  167. append_actions_from_file(animlib, wildcard)
  168.  
  169. # Select the first Armature and the export dummy mesh in the scene
  170. make_all_layers_visible() # dummy objects might be on different layers
  171. clear_selection() # don't rely on what was selected when the user saved
  172. armature = select_first_armature() # we want to export the Armature
  173. create_and_select_dummy_mesh(armature)
  174.  
  175. # Export to FBX
  176. bpy.ops.export_scene.fbx(
  177.     filepath=fbxpath,
  178.     check_existing=False,
  179.     axis_forward='-Z',
  180.     axis_up='Y',
  181.     version='BIN7400',
  182.     use_selection=True,
  183.     global_scale=1.0,
  184.     apply_unit_scale=False,
  185.     bake_space_transform=False,
  186.     object_types={'ARMATURE', 'EMPTY', 'MESH'},
  187.     use_mesh_modifiers=True,
  188.     mesh_smooth_type='OFF',
  189.     use_mesh_edges=False,
  190.     use_tspace=True,
  191.     use_custom_props=False,
  192.     add_leaf_bones=False,
  193.     primary_bone_axis='Y',
  194.     secondary_bone_axis='X',
  195.     use_armature_deform_only=True,
  196.     bake_anim=True,
  197.     bake_anim_use_all_bones=True,
  198.     bake_anim_use_nla_strips=False,
  199.     bake_anim_use_all_actions=True,
  200.     bake_anim_force_startend_keying=True,
  201.     bake_anim_step=1.0,
  202.     bake_anim_simplify_factor=0.0,
  203.     use_anim=True,
  204.     use_anim_action_all=True,
  205.     use_default_take=False,
  206.     use_anim_optimize=False,
  207.     path_mode='AUTO',
  208.     embed_textures=False,
  209.     batch_mode='OFF',
  210.     use_metadata=True
  211. )
  212.  
  213. # Quit. We do not want to risk keeping the window open,
  214. # which might end up making the user save our patchwork file
  215. #
  216. quit()
Add Comment
Please, Sign In to add comment