- 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
3.0
nor..67 12 years ago
parent f158717981
commit 74eccc7c8f
  1. 1
      sdk/jme3-blender/src/com/jme3/gde/blender/BlenderAssetManagerConfigurator.java
  2. 96
      sdk/jme3-blender/src/com/jme3/gde/blender/BlenderTool.java
  3. 91
      sdk/jme3-blender/src/com/jme3/gde/blender/filetypes/AbstractBlenderAssetDataObject.java
  4. 89
      sdk/jme3-blender/src/com/jme3/gde/blender/filetypes/Blender3dsDataObject.java
  5. 92
      sdk/jme3-blender/src/com/jme3/gde/blender/filetypes/BlenderDaeDataObject.java
  6. 64
      sdk/jme3-blender/src/com/jme3/gde/blender/scripts/Scripts.java
  7. 86
      sdk/jme3-blender/src/com/jme3/gde/blender/scripts/import_3ds.py
  8. 86
      sdk/jme3-blender/src/com/jme3/gde/blender/scripts/import_dae.py

@ -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);
}
}

@ -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!");

@ -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;
}
}
}
}

@ -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)"
})
@MIMEResolver.ExtensionRegistration(
displayName = "#LBL_Blender3ds_LOADER",
mimeType = "application/x-3ds",
extension = {"3ds", "3DS"})
@DataObject.Registration(
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";
}
}

@ -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)"
})
@MIMEResolver.ExtensionRegistration(
displayName = "#LBL_BlenderDae_LOADER",
mimeType = "model/vnd.collada+xml",
extension = {"dae", "DAE"})
@DataObject.Registration(
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";
}
}

@ -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);
}
}
}
}

@ -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()

@ -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()
Loading…
Cancel
Save