While Monster Couch slowly picks up velocity I’m still finishing up a side-gig for a 3D mobile game port. On my journey I was excited to use the cloud build to automate the process (and so that I could use the mac mini less :p). I happily set everything up but in he first cloud build something strange happened – all of my Blender models were missing.

I went on to search for the cause only to find out in the Support FAQ that:

“Unity Cloud Build supports “Exported 3d files”. We don’t support “Proprietary 3D files” at this time. A list of which types of files are which can be found here: HOWTO-importObject“

That’s a real pain but the rationale is quite clear: on local machine it’s simple: the user is responsible for providing the licensed software to work with the proprietary formats. It gets slightly more complicated with the hosted build service. It’s doable with Blender since it’s open source and free – the script I’ll be showing you in a minute could be integrated into the service. I guess they will work it out sometime in the future but in the meantime…

Ouch, so how do I export over 150 models without destroying my prefabs?

Luckily Unity uses a python script to import .blend files and turns them internally into .fbx files. If you want to check out the script it can be found at *\Unity\Editor\Data\Tools\Unity-BlenderToFBX.py .

We can use that as a base for our batch exporter – we will be sure that nothing weird happens to our models. Here’s the script updated to accept command line arguments for input and output files. Originally it also depended on the Blender file being already opened so I’ve added a line to make sure it loads the contents of the .blender file.

Export .fbx from .blend

blender249 = True import sys try: import Blender except: blender249 = False import bpy if blender249: try: import export_fbx except: print('error: export_fbx not found.') Blender.Quit() else: try: import io_scene_fbx.export_fbx except: print('error: io_scene_fbx.export_fbx not found.') # This might need to be bpy.Quit() raise # Accept command line arguments and load the contents of the .blend file infile = sys.argv[5] bpy.ops.wm.open_mainfile(filepath=infile) outfile = sys.argv[6] # Do the conversion print("Starting blender to FBX conversion " + outfile) if blender249: mtx4_x90n = Blender.Mathutils.RotationMatrix(-90, 4, 'x') export_fbx.write(outfile, EXP_OBS_SELECTED=False, EXP_MESH=True, EXP_MESH_APPLY_MOD=True, EXP_MESH_HQ_NORMALS=True, EXP_ARMATURE=True, EXP_LAMP=True, EXP_CAMERA=True, EXP_EMPTY=True, EXP_IMAGE_COPY=False, ANIM_ENABLE=True, ANIM_OPTIMIZE=False, ANIM_ACTION_ALL=True, GLOBAL_MATRIX=mtx4_x90n) else: # blender 2.58 or newer import math from mathutils import Matrix # -90 degrees mtx4_x90n = Matrix.Rotation(-math.pi / 2.0, 4, 'X') print("moo") class FakeOp: def report(self, tp, msg): print("%s: %s" % (tp, msg)) exportObjects = ['ARMATURE', 'EMPTY', 'MESH'] minorVersion = bpy.app.version[1]; if minorVersion <= 58: # 2.58 io_scene_fbx.export_fbx.save(FakeOp(), bpy.context, filepath=outfile, global_matrix=mtx4_x90n, use_selection=False, object_types=exportObjects, mesh_apply_modifiers=True, ANIM_ENABLE=True, ANIM_OPTIMIZE=False, ANIM_OPTIMIZE_PRECISSION=6, ANIM_ACTION_ALL=True, batch_mode='OFF', BATCH_OWN_DIR=False) else: # 2.59 and later kwargs = io_scene_fbx.export_fbx.defaults_unity3d() io_scene_fbx.export_fbx.save(FakeOp(), bpy.context, filepath=outfile, **kwargs) # HQ normals are not supported in the current exporter print("Finished blender to FBX conversion " + outfile) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 blender249 = True import sys try : import Blender except : blender249 = False import bpy if blender249 : try : import export_fbx except : print ( 'error: export_fbx not found.' ) Blender . Quit ( ) else : try : import io_scene_fbx . export_fbx except : print ( 'error: io_scene_fbx.export_fbx not found.' ) # This might need to be bpy.Quit() raise # Accept command line arguments and load the contents of the .blend file infile = sys . argv [ 5 ] bpy . ops . wm . open_mainfile ( filepath = infile ) outfile = sys . argv [ 6 ] # Do the conversion print ( "Starting blender to FBX conversion " + outfile ) if blender249 : mtx4_x90n = Blender . Mathutils . RotationMatrix ( - 90 , 4 , 'x' ) export_fbx . write ( outfile , EXP_OBS_SELECTED = False , EXP_MESH = True , EXP_MESH_APPLY_MOD = True , EXP_MESH_HQ_NORMALS = True , EXP_ARMATURE = True , EXP_LAMP = True , EXP_CAMERA = True , EXP_EMPTY = True , EXP_IMAGE_COPY = False , ANIM_ENABLE = True , ANIM_OPTIMIZE = False , ANIM_ACTION_ALL = True , GLOBAL_MATRIX = mtx4_x90n ) else : # blender 2.58 or newer import math from mathutils import Matrix # -90 degrees mtx4_x90n = Matrix . Rotation ( - math . pi / 2.0 , 4 , 'X' ) print ( "moo" ) class FakeOp : def report ( self , tp , msg ) : print ( "%s: %s" % ( tp , msg ) ) exportObjects = [ 'ARMATURE' , 'EMPTY' , 'MESH' ] minorVersion = bpy . app . version [ 1 ] ; if minorVersion <= 58 : # 2.58 io_scene_fbx . export_fbx . save ( FakeOp ( ) , bpy . context , filepath = outfile , global_matrix = mtx4_x90n , use_selection = False , object_types = exportObjects , mesh_apply_modifiers = True , ANIM_ENABLE = True , ANIM_OPTIMIZE = False , ANIM_OPTIMIZE_PRECISSION = 6 , ANIM_ACTION_ALL = True , batch_mode = 'OFF' , BATCH_OWN_DIR = False ) else : # 2.59 and later kwargs = io_scene_fbx . export_fbx . defaults_unity3d ( ) io_scene_fbx . export_fbx . save ( FakeOp ( ) , bpy . context , filepath = outfile , * * kwargs ) # HQ normals are not supported in the current exporter print ( "Finished blender to FBX conversion " + outfile )

Search the project for every .blend model

We will also need a script that traverses our project recursively and runs the above script for every .blend model. We will also need to rename the .blend.meta files so that we don’t loose any references and finally clean up the .blend files since we no longer need them for our prefabs and scenes. Backup them somewhere before you proceed!

# # blendToFbxExporter.py # # Created by Krzysztof Żarczyński (@iamsed) on 16.11.15. # http://blog.kzarczynski.com/ # Copyright (c) 2015 Krzysztof Żarczyński. All rights reserved. # # # The script exports multiple files from Blender into fbx models. # It searches the path recursively and creates .fbx files next to the .blend files, # then it renames corresponding Unity *.blend.meta files to *.fbx.meta to keep editor references, # !!! then it deletes the .blend files !!! # You need Blender to be installed on your machine. # You need to specify the following command line arguments: # Blender's directory, path to args-Unity-BlenderToFBX.py, input directory (with blend files) # import os import os.path import sys import glob import bpy import fnmatch import subprocess if len(sys.argv) != 7: print("

usage: --background --python -- ") else: pathToBlender = sys.argv[0] pathToBlenderToFBX = sys.argv[5] path = sys.argv[6] alreadyHaveFBX = [] processedFiles = [] configfiles = [os.path.join(dirpath, f) for dirpath, dirnames, files in os.walk(path) for f in fnmatch.filter(files, '*.blend')] for infile in configfiles: outfilename = infile.replace('blend', 'fbx') if os.path.isfile(outfilename): alreadyHaveFBX.append(infile) else: status = subprocess.call(pathToBlender + ' --background --python ' + pathToBlenderToFBX + ' -- "' + infile + '" "' + outfilename + '"'); os.remove(infile) processedFiles.append(infile) configfiles = [os.path.join(dirpath, f) for dirpath, dirnames, files in os.walk(path) for f in fnmatch.filter(files, '*.blend.meta')] for infile in configfiles: outfilename = infile.replace('blend', 'fbx') if not os.path.isfile(outfilename): os.rename(infile, outfilename) print("Processed files:") print(len(processedFiles)) print("Skipped files (.fbx already exists at the locaton):") print(len(alreadyHaveFBX)) for infile in alreadyHaveFBX: print(infile) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 # # blendToFbxExporter.py # # Created by Krzysztof Żarczyński (@iamsed) on 16.11.15. # http://blog.kzarczynski.com/ # Copyright (c) 2015 Krzysztof Żarczyński. All rights reserved. # # # The script exports multiple files from Blender into fbx models. # It searches the path recursively and creates .fbx files next to the .blend files, # then it renames corresponding Unity *.blend.meta files to *.fbx.meta to keep editor references, # !!! then it deletes the .blend files !!! # You need Blender to be installed on your machine. # You need to specify the following command line arguments: # Blender's directory, path to args-Unity-BlenderToFBX.py, input directory (with blend files) # import os import os.path import sys import glob import bpy import fnmatch import subprocess if len ( sys . argv ) != 7 : print ( "

usage: --background --python -- " ) else : pathToBlender = sys . argv [ 0 ] pathToBlenderToFBX = sys . argv [ 5 ] path = sys . argv [ 6 ] alreadyHaveFBX = [ ] processedFiles = [ ] configfiles = [ os.path . join ( dirpath , f ) for dirpath , dirnames , files in os . walk ( path ) for f in fnmatch . filter ( files , '*.blend' ) ] for infile in configfiles : outfilename = infile . replace ( 'blend' , 'fbx' ) if os.path . isfile ( outfilename ) : alreadyHaveFBX . append ( infile ) else : status = subprocess . call ( pathToBlender + ' --background --python ' + pathToBlenderToFBX + ' -- "' + infile + '" "' + outfilename + '"' ) ; os . remove ( infile ) processedFiles . append ( infile ) configfiles = [ os.path . join ( dirpath , f ) for dirpath , dirnames , files in os . walk ( path ) for f in fnmatch . filter ( files , '*.blend.meta' ) ] for infile in configfiles : outfilename = infile . replace ( 'blend' , 'fbx' ) if not os.path . isfile ( outfilename ) : os . rename ( infile , outfilename ) print ( "Processed files:" ) print ( len ( processedFiles ) ) print ( "Skipped files (.fbx already exists at the locaton):" ) print ( len ( alreadyHaveFBX ) ) for infile in alreadyHaveFBX : print ( infile )

Sample usage

blender --background --python "E:\blendToFbxExporter.py" -- "E:\args-Unity-BlenderToFBX.py" "E:\NewUnityProject\Assets" 1 blender -- background -- python "E:\blendToFbxExporter.py" -- "E:\args-Unity-BlenderToFBX.py" "E:\NewUnityProject\Assets"

Summary

The script exports multiple files from Blender into fbx models. It searches the path recursively and creates .fbx files next to the .blend files, then it renames corresponding Unity *.blend.meta files to *.fbx.meta to keep editor references, then it deletes the .blend files (make your own backup somewhere outside of the project path).

This script uses custom version of Unity-BlenderToFBX.py that Unity uses internally for importing .blend files. It has been changed to allow passing arguments to it and to load the necessary .blend file. You can find the original file at [Unity path]\Editor\Data\Tools\Unity-BlenderToFBX.py

You need Blender to be installed on your machine. You need to specify the following command line arguments: Blender’s directory, path to args-Unity-BlenderToFBX.py, input directory (with blend files)

I have compiled it into a small github repo: blendToFbxExporterForUnity

Edit June 2016

Here is how you can use Blender’s command line: https://www.blender.org/manual/render/workflows/command_line.html

Thanks for reading!

If you have found the post useful please visit us at fb.com/MonsterCouchGames and leave us a like :)