Browse Source

SDK:
- add model import via blender
- add support dor 3ds, dae to SDK

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10237 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

nor..67 12 years ago
parent
commit
6d16b4ec93

+ 1 - 0
jme3-blender/src/com/jme3/gde/blender/BlenderAssetManagerConfigurator.java

@@ -16,5 +16,6 @@ public class BlenderAssetManagerConfigurator implements AssetManagerConfigurator
 
     public void prepareManager(AssetManager manager) {
         manager.registerLoader(com.jme3.scene.plugins.blender.BlenderModelLoader.class, "blend");
+        manager.registerLoader(com.jme3.scene.plugins.blender.BlenderModelLoader.class, BlenderTool.TEMP_SUFFIX);
     }
 }

+ 84 - 12
jme3-blender/src/com/jme3/gde/blender/BlenderTool.java

@@ -116,6 +116,7 @@
  */
 package com.jme3.gde.blender;
 
+import com.jme3.gde.blender.scripts.Scripts;
 import com.jme3.math.Vector3f;
 import java.awt.Frame;
 import java.awt.Window;
@@ -133,15 +134,18 @@ import org.openide.util.Utilities;
 import org.openide.windows.WindowManager;
 import java.awt.event.WindowEvent;
 import java.awt.event.WindowFocusListener;
+
 /**
  *
  * @author normenhansen
  */
 public class BlenderTool {
 
+    public static final String TEMP_SUFFIX = "blend";
     private static final String mainFolderName = "blender";
     private static final String configFolderName = mainFolderName + "/config";
     private static final String scriptsFolderName = mainFolderName + "/scripts";
+    private static final String jmeScriptsFolderName = mainFolderName + "/jmescripts";
     private static final String userScriptsFolderName = mainFolderName + "/userscripts";
     private static final String tempFolderName = mainFolderName + "/temp";
     private static final Logger logger = Logger.getLogger(BlenderTool.class.getName());
@@ -171,32 +175,42 @@ public class BlenderTool {
             FileObject configFileObject = fileObject.getFileObject(configFolderName);
             //TODO: using installed blender scripts folder, make more flexible by moving
             //to updateable folder
-//            FileObject scriptsFileObject = fileObject.getFileObject(scriptsFolderName);
+            FileObject scriptsFileObject = fileObject.getFileObject(scriptsFolderName);
+            FileObject jmeScriptsFileObject = fileObject.getFileObject(jmeScriptsFolderName);
             FileObject userScriptsFileObject = fileObject.getFileObject(userScriptsFolderName);
             if (configFileObject == null) {
                 try {
-                    FileUtil.createFolder(fileObject, configFolderName);
+                    configFileObject = FileUtil.createFolder(fileObject, configFolderName);
+                } catch (IOException ex) {
+                    Exceptions.printStackTrace(ex);
+                    return false;
+                }
+            }
+            if (scriptsFileObject == null) {
+                try {
+                    scriptsFileObject = FileUtil.createFolder(fileObject, scriptsFolderName);
+                } catch (IOException ex) {
+                    Exceptions.printStackTrace(ex);
+                    return false;
+                }
+            }
+            if (jmeScriptsFileObject == null) {
+                try {
+                    jmeScriptsFileObject = FileUtil.createFolder(fileObject, jmeScriptsFolderName);
                 } catch (IOException ex) {
                     Exceptions.printStackTrace(ex);
                     return false;
                 }
             }
-//            if (scriptsFileObject == null) {
-//                try {
-//                    FileUtil.createFolder(fileObject, scriptsFolderName);
-//                } catch (IOException ex) {
-//                    Exceptions.printStackTrace(ex);
-//                    return false;
-//                }
-//            }
             if (userScriptsFileObject == null) {
                 try {
-                    FileUtil.createFolder(fileObject, userScriptsFolderName);
+                    userScriptsFileObject = FileUtil.createFolder(fileObject, userScriptsFolderName);
                 } catch (IOException ex) {
                     Exceptions.printStackTrace(ex);
                     return false;
                 }
             }
+            Scripts.copyToFolder(jmeScriptsFileObject);
         } else {
             logger.log(Level.SEVERE, "No global settings folder found!");
             return false;
@@ -224,6 +238,18 @@ public class BlenderTool {
         return ret;
     }
 
+    private static String getJmeUserScriptPath(String scriptName) {
+        String ret = System.getProperty("netbeans.user") + "/" + jmeScriptsFolderName + "/" + scriptName;
+        ret = ret.replace("/", File.separator);
+        return ret;
+    }
+
+    private static String getImportScriptPath(String scriptName) {
+        String ret = System.getProperty("netbeans.user") + "/" + jmeScriptsFolderName + "/" + "import_" + scriptName + ".py";
+        ret = ret.replace("/", File.separator);
+        return ret;
+    }
+
     private static File getBlenderExecutable() {
         File blender = InstalledFileLocator.getDefault().locate(getBlenderOsPath() + "/" + getBlenderExeName(), null, false);
         if (blender == null) {
@@ -234,7 +260,7 @@ public class BlenderTool {
     }
 
     private static File getBlenderSettingsFolder() {
-        File blender = InstalledFileLocator.getDefault().locate(getBlenderOsPath() + "/2.64", null, false);
+        File blender = InstalledFileLocator.getDefault().locate(getBlenderOsPath() + "/2.65", null, false);
         if (blender == null) {
             DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("Error finding Blender settings"));
             logger.log(Level.SEVERE, "Error finding Blender settings");
@@ -255,6 +281,52 @@ public class BlenderTool {
         blenderWindow = win;
     }
 
+    public static boolean runConversionScript(String type, FileObject input) {
+        if (!checkBlenderFolders()) {
+            logger.log(Level.SEVERE, "Could not create blender settings folders!");
+        }
+        final File exe = getBlenderExecutable();
+        if (exe == null) {
+            logger.log(Level.SEVERE, "Could not find blender executable!");
+            return false;
+        }
+        logger.log(Level.INFO, "Try running blender as converter for file {0}", input.getPath());
+        String scriptPath = getImportScriptPath(type);
+        String inputPath = input.getPath().replace("/", File.separator);
+        String inputFolder = input.getParent().getPath().replace("/", File.separator) + File.separator;
+        String outputPath = inputFolder + input.getName() + "." + TEMP_SUFFIX;
+        try {
+            String command = exe.getAbsolutePath();
+            ProcessBuilder buildr = new ProcessBuilder(command, "-b",
+                    "--factory-startup",
+                    "-P", scriptPath,
+                    "--",
+                    "-i", inputPath,
+                    "-o", outputPath);
+            buildr.directory(getBlenderRootFolder());
+            buildr.environment().put("BLENDER_USER_CONFIG", getConfigEnv());
+            buildr.environment().put("BLENDER_SYSTEM_SCRIPTS", getScriptsEnv());
+            buildr.environment().put("BLENDER_USER_SCRIPTS", getUserScriptsEnv());
+            Process proc = buildr.start();
+            OutputReader outReader = new OutputReader(proc.getInputStream());
+            OutputReader errReader = new OutputReader(proc.getErrorStream());
+            outReader.start();
+            errReader.start();
+            try {
+                proc.waitFor();
+            } catch (InterruptedException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+            if (proc.exitValue() != 0) {
+                logger.log(Level.SEVERE, "Error running blender!");
+                return false;
+            }
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        return true;
+    }
+
     private static boolean runBlender(final String options, boolean async) {
         if (!checkBlenderFolders()) {
             logger.log(Level.SEVERE, "Could not create blender settings folders!");

+ 91 - 0
jme3-blender/src/com/jme3/gde/blender/filetypes/AbstractBlenderAssetDataObject.java

@@ -0,0 +1,91 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.blender.filetypes;
+
+import com.jme3.gde.blender.BlenderTool;
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.core.assets.SpatialAssetDataObject;
+import com.jme3.scene.Spatial;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.filesystems.FileLock;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataObjectExistsException;
+import org.openide.loaders.MultiFileLoader;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author normenhansen
+ */
+public abstract class AbstractBlenderAssetDataObject extends SpatialAssetDataObject {
+
+    protected String SUFFIX;
+
+    public AbstractBlenderAssetDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
+        super(pf, loader);
+    }
+
+    @Override
+    public Spatial loadAsset() {
+        if (SUFFIX == null) {
+            throw new IllegalStateException("Suffix for blender filetype is null! Set SUFFIX = \"sfx\" in constructor!");
+        }
+        ProjectAssetManager mgr = getLookup().lookup(ProjectAssetManager.class);
+        if (mgr == null) {
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("File is not part of a project!\nCannot load without ProjectAssetManager."));
+            return null;
+        }
+        FileObject mainFile = getPrimaryFile();
+        BlenderTool.runConversionScript(SUFFIX, mainFile);
+        FileObject outFile = FileUtil.findBrother(mainFile, BlenderTool.TEMP_SUFFIX);
+        if (outFile == null) {
+            logger.log(Level.SEVERE, "Failed to create model, blend file cannot be found");
+            return null;
+        }
+        String assetKey = mgr.getRelativeAssetPath(outFile.getPath());
+        FileLock lock = null;
+        try {
+            lock = getPrimaryFile().lock();
+            listListener.start();
+            Spatial spatial = mgr.loadModel(assetKey);
+            replaceFiles();
+            listListener.stop();
+            savable = spatial;
+            return spatial;
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+        } finally {
+            if (lock != null) {
+                lock.releaseLock();
+            }
+            try {
+                outFile.delete();
+            } catch (IOException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+        return null;
+    }
+
+    protected void replaceFiles() {
+        for (int i = 0; i < assetList.size(); i++) {
+            FileObject fileObject = assetList.get(i);
+            if (fileObject.hasExt(BlenderTool.TEMP_SUFFIX)) {
+                assetList.remove(i);
+                assetKeyList.remove(i);
+                assetList.add(i, getPrimaryFile());
+                assetKeyList.add(getAssetKey());
+                return;
+            }
+        }
+    }
+}

+ 89 - 0
jme3-blender/src/com/jme3/gde/blender/filetypes/Blender3dsDataObject.java

@@ -0,0 +1,89 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.blender.filetypes;
+
+import com.jme3.gde.core.assets.SpatialAssetDataObject;
+import java.io.IOException;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionReferences;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.MIMEResolver;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectExistsException;
+import org.openide.loaders.MultiFileLoader;
+import org.openide.util.NbBundle.Messages;
+
+@Messages({
+    "LBL_Blender3ds_LOADER=3DS Files (via Blender)"
+})
[email protected](
+    displayName = "#LBL_Blender3ds_LOADER",
+mimeType = "application/x-3ds",
+extension = {"3ds", "3DS"})
[email protected](
+    mimeType = "application/x-3ds",
+iconBase = "com/jme3/gde/blender/blender.png",
+displayName = "#LBL_Blender3ds_LOADER",
+position = 300)
+@ActionReferences({
+    @ActionReference(
+        path = "Loaders/application/x-3ds/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.OpenAction"),
+    position = 100,
+    separatorAfter = 200),
+    @ActionReference(
+        path = "Loaders/application/x-3ds/Actions",
+    id =
+    @ActionID(category = "Edit", id = "org.openide.actions.CutAction"),
+    position = 300),
+    @ActionReference(
+        path = "Loaders/application/x-3ds/Actions",
+    id =
+    @ActionID(category = "Edit", id = "org.openide.actions.CopyAction"),
+    position = 400,
+    separatorAfter = 500),
+    @ActionReference(
+        path = "Loaders/application/x-3ds/Actions",
+    id =
+    @ActionID(category = "Edit", id = "org.openide.actions.DeleteAction"),
+    position = 600),
+    @ActionReference(
+        path = "Loaders/application/x-3ds/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.RenameAction"),
+    position = 700,
+    separatorAfter = 800),
+    @ActionReference(
+        path = "Loaders/application/x-3ds/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.SaveAsTemplateAction"),
+    position = 900,
+    separatorAfter = 1000),
+    @ActionReference(
+        path = "Loaders/application/x-3ds/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.FileSystemAction"),
+    position = 1100,
+    separatorAfter = 1200),
+    @ActionReference(
+        path = "Loaders/application/x-3ds/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.ToolsAction"),
+    position = 1300),
+    @ActionReference(
+        path = "Loaders/application/x-3ds/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"),
+    position = 1400)
+})
+public class Blender3dsDataObject extends AbstractBlenderAssetDataObject {
+
+    public Blender3dsDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
+        super(pf, loader);
+        SUFFIX = "3ds";
+    }
+}

+ 92 - 0
jme3-blender/src/com/jme3/gde/blender/filetypes/BlenderDaeDataObject.java

@@ -0,0 +1,92 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.blender.filetypes;
+
+import java.io.IOException;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionReferences;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.MIMEResolver;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectExistsException;
+import org.openide.loaders.MultiFileLoader;
+import org.openide.util.NbBundle.Messages;
+
+/**
+ *
+ * @author normenhansen
+ */
+@Messages({
+    "LBL_BlenderDae_LOADER=Collada Files (via Blender)"
+})
[email protected](
+    displayName = "#LBL_BlenderDae_LOADER",
+mimeType = "model/vnd.collada+xml",
+extension = {"dae", "DAE"})
[email protected](
+    mimeType = "model/vnd.collada+xml",
+iconBase = "com/jme3/gde/blender/blender.png",
+displayName = "#LBL_BlenderDae_LOADER",
+position = 300)
+@ActionReferences({
+    @ActionReference(
+        path = "Loaders/model/vnd.collada+xml/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.OpenAction"),
+    position = 100,
+    separatorAfter = 200),
+    @ActionReference(
+        path = "Loaders/model/vnd.collada+xml/Actions",
+    id =
+    @ActionID(category = "Edit", id = "org.openide.actions.CutAction"),
+    position = 300),
+    @ActionReference(
+        path = "Loaders/model/vnd.collada+xml/Actions",
+    id =
+    @ActionID(category = "Edit", id = "org.openide.actions.CopyAction"),
+    position = 400,
+    separatorAfter = 500),
+    @ActionReference(
+        path = "Loaders/model/vnd.collada+xml/Actions",
+    id =
+    @ActionID(category = "Edit", id = "org.openide.actions.DeleteAction"),
+    position = 600),
+    @ActionReference(
+        path = "Loaders/model/vnd.collada+xml/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.RenameAction"),
+    position = 700,
+    separatorAfter = 800),
+    @ActionReference(
+        path = "Loaders/model/vnd.collada+xml/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.SaveAsTemplateAction"),
+    position = 900,
+    separatorAfter = 1000),
+    @ActionReference(
+        path = "Loaders/model/vnd.collada+xml/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.FileSystemAction"),
+    position = 1100,
+    separatorAfter = 1200),
+    @ActionReference(
+        path = "Loaders/model/vnd.collada+xml/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.ToolsAction"),
+    position = 1300),
+    @ActionReference(
+        path = "Loaders/model/vnd.collada+xml/Actions",
+    id =
+    @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"),
+    position = 1400)
+})
+public class BlenderDaeDataObject extends AbstractBlenderAssetDataObject {
+
+    public BlenderDaeDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
+        super(pf, loader);
+        SUFFIX = "dae";
+    }
+}

+ 64 - 0
jme3-blender/src/com/jme3/gde/blender/scripts/Scripts.java

@@ -0,0 +1,64 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.blender.scripts;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class Scripts {
+
+    private static final Logger logger = Logger.getLogger(Scripts.class.getName());
+    private final static String root = "com/jme3/gde/blender/scripts/";
+
+    public static void copyToFolder(FileObject folder) {
+        if (folder == null) {
+            logger.log(Level.WARNING, "Got null folder for scripts check");
+            return;
+        }
+        checkScript(folder, "import_3ds.py");
+        checkScript(folder, "import_dae.py");
+    }
+
+    private static void checkScript(FileObject folder, String name) {
+        FileObject file = folder.getFileObject(name);
+        //TODO:check version!
+        if (file == null) {
+            try {
+                InputStream in = null;
+                OutputStream out = null;
+                try {
+                    URL url = new URL("nbres:" + root + name);
+                    file = FileUtil.createData(folder, name);
+                    in = url.openStream();
+                    out = file.getOutputStream();
+                    FileUtil.copy(in, out);
+                } catch (IOException e) {
+                    Exceptions.printStackTrace(e);
+                } finally {
+                    if (in != null) {
+                        in.close();
+                    }
+                    if (out != null) {
+                        out.close();
+                    }
+                }
+                logger.log(Level.INFO, "Extracted script {0}", file.getPath());
+            } catch (IOException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+    }
+}

+ 86 - 0
jme3-blender/src/com/jme3/gde/blender/scripts/import_3ds.py

@@ -0,0 +1,86 @@
+# This script is an example of how you can run blender from the command line
+# (in background mode with no interface) to automate tasks, in this example it
+# creates a text object, camera and light, then renders and/or saves it.
+# This example also shows how you can parse command line options to scripts.
+#
+# Example usage for this test.
+#  blender --background --factory-startup --python $HOME/background_job.py -- \
+#          --text="Hello World" \
+#          --render="/tmp/hello" \
+#          --save="/tmp/hello.blend"
+#
+# Notice:
+# '--factory-startup' is used to avoid the user default settings from
+#                     interfearing with automated scene generation.
+#
+# '--' causes blender to ignore all following arguments so python can use them.
+#
+# See blender --help for details.
+
+import bpy
+
+
+def convert_file(file_path, save_path):
+    bpy.ops.import_scene.autodesk_3ds(filepath = file_path)
+
+    scene = bpy.context.scene
+
+    try:
+        f = open(save_path, 'w')
+        f.close()
+        ok = True
+    except:
+        print("Cannot save to path %r" % save_path)
+
+        import traceback
+        traceback.print_exc()
+
+    if ok:
+        bpy.ops.wm.save_as_mainfile(filepath=save_path)
+
+def main():
+    import sys       # to get command line args
+    import argparse  # to parse options for us and print a nice help message
+
+    # get the args passed to blender after "--", all of which are ignored by
+    # blender so scripts may receive their own arguments
+    argv = sys.argv
+
+    if "--" not in argv:
+        argv = []  # as if no args are passed
+    else:
+        argv = argv[argv.index("--") + 1:]  # get all args after "--"
+
+    # When --help or no args are given, print this help
+    usage_text = \
+    "Run blender in background mode with this script:"
+    "  blender --background --factory-startup --python " + __file__ + " -- [options]"
+
+    parser = argparse.ArgumentParser(description=usage_text)
+
+    # Possible types are: string, int, long, choice, float and complex.
+    parser.add_argument("-i", "--input", dest="file_path", metavar='FILE',
+            help="Import the specified file")
+    parser.add_argument("-o", "--output", dest="save_path", metavar='FILE',
+            help="Save the generated file to the specified path")
+
+    args = parser.parse_args(argv)  # In this example we wont use the args
+
+    if not argv:
+        parser.print_help()
+        return
+
+    # Clear existing objects.
+    scene = bpy.context.scene
+    scene.camera = None
+    for obj in scene.objects:
+        scene.objects.unlink(obj)
+
+    # Run the conversion
+    convert_file(args.file_path, args.save_path)
+
+    print("batch job finished, exiting")
+
+
+if __name__ == "__main__":
+    main()

+ 86 - 0
jme3-blender/src/com/jme3/gde/blender/scripts/import_dae.py

@@ -0,0 +1,86 @@
+# This script is an example of how you can run blender from the command line
+# (in background mode with no interface) to automate tasks, in this example it
+# creates a text object, camera and light, then renders and/or saves it.
+# This example also shows how you can parse command line options to scripts.
+#
+# Example usage for this test.
+#  blender --background --factory-startup --python $HOME/background_job.py -- \
+#          --text="Hello World" \
+#          --render="/tmp/hello" \
+#          --save="/tmp/hello.blend"
+#
+# Notice:
+# '--factory-startup' is used to avoid the user default settings from
+#                     interfearing with automated scene generation.
+#
+# '--' causes blender to ignore all following arguments so python can use them.
+#
+# See blender --help for details.
+
+import bpy
+
+
+def convert_file(file_path, save_path):
+    bpy.ops.wm.collada_import(filepath = file_path)
+
+    scene = bpy.context.scene
+
+    try:
+        f = open(save_path, 'w')
+        f.close()
+        ok = True
+    except:
+        print("Cannot save to path %r" % save_path)
+
+        import traceback
+        traceback.print_exc()
+
+    if ok:
+        bpy.ops.wm.save_as_mainfile(filepath=save_path)
+
+def main():
+    import sys       # to get command line args
+    import argparse  # to parse options for us and print a nice help message
+
+    # get the args passed to blender after "--", all of which are ignored by
+    # blender so scripts may receive their own arguments
+    argv = sys.argv
+
+    if "--" not in argv:
+        argv = []  # as if no args are passed
+    else:
+        argv = argv[argv.index("--") + 1:]  # get all args after "--"
+
+    # When --help or no args are given, print this help
+    usage_text = \
+    "Run blender in background mode with this script:"
+    "  blender --background --python " + __file__ + " -- [options]"
+
+    parser = argparse.ArgumentParser(description=usage_text)
+
+    # Possible types are: string, int, long, choice, float and complex.
+    parser.add_argument("-i", "--input", dest="file_path", metavar='FILE',
+            help="Import the specified file")
+    parser.add_argument("-o", "--output", dest="save_path", metavar='FILE',
+            help="Save the generated file to the specified path")
+
+    args = parser.parse_args(argv)  # In this example we wont use the args
+
+    if not argv:
+        parser.print_help()
+        return
+
+    # Clear existing objects.
+    scene = bpy.context.scene
+    scene.camera = None
+    for obj in scene.objects:
+        scene.objects.unlink(obj)
+
+    # Run the conversion
+    convert_file(args.file_path, args.save_path)
+
+    print("batch job finished, exiting")
+
+
+if __name__ == "__main__":
+    main()