« Previous: Starter Tutorial 5 - Hello KeyInput
Next: Starter Tutorial 7 - Hello MousePick »
(Tip: Up-to-date source files for the tutorials are always in the repository)
This example introduces BinaryImporter and FileConverter. You will learn how to load 3D object formats into a jME scene graph. Pretty short program, eh?
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; import com.jme.app.SimpleGame; import com.jme.bounding.BoundingSphere; import com.jme.scene.Node; import com.jme.util.export.binary.BinaryImporter; import com.jmex.model.converters.FormatConverter; import com.jmex.model.converters.ObjToJme; /** * Started Date: Jul 22, 2004<br> * <br> * * Demonstrates loading formats. * * @author Jack Lindamood */ public class HelloModelLoading extends SimpleGame { private static final Logger logger = Logger .getLogger(HelloModelLoading.class.getName()); public static void main(String[] args) { HelloModelLoading app = new HelloModelLoading(); app.setConfigShowMode(ConfigShowMode.AlwaysShow); // Turn the logger off so we can see the XML later on app.start(); } protected void simpleInitGame() { // Point to a URL of my model URL model = HelloModelLoading.class.getClassLoader().getResource( "jmetest/data/model/maggie.obj"); // Create something to convert .obj format to .jme FormatConverter converter = new ObjToJme(); // Point the converter to where it will find the .mtl file from converter.setProperty("mtllib", model); // This byte array will hold my .jme file ByteArrayOutputStream BO = new ByteArrayOutputStream(); try { // Use the format converter to convert .obj to .jme converter.convert(model.openStream(), BO); Node maggie = (Node) BinaryImporter.getInstance().load( new ByteArrayInputStream(BO.toByteArray())); // shrink this baby down some maggie.setLocalScale(.1f); maggie.setModelBound(new BoundingSphere()); maggie.updateModelBound(); // Put her on the scene graph rootNode.attachChild(maggie); } catch (IOException e) { // Just in case anything happens logger.logp(Level.SEVERE, this.getClass().toString(), "simpleInitGame()", "Exception", e); System.exit(0); } } }
(Screenshot taken from jME 1)
To understand file loading with jME, one must understand that jME doesn’t support loading any file format directly, other than its own binary format called “jME binary.” But there are converter classes, such as ObjToJme (jME API) or MaxToJme (jME API) or Md2ToJme (jME API), that convert from the given format to jME’s binary format. They all extend FormatConverter (jME API). Because the file we’re using in this example is an .obj format file, we use ObjToJme to convert it to binary.
// Create something to convert .obj format to .jme FormatConverter converter = new ObjToJme();
Normally, it is sufficient to simply convert without setting any properties. The .obj format is different though because it splits its information between a .obj file and a .mtl file. The .obj holds geometry and the .mtl holds material information. Because of this, we have to set a property to tell the converter which directory to find .mtl libraries in.
// Point the converter to where it will find the .mtl file from converter.setProperty("mtllib", model);
To learn which properties are needed for which formats, consult the javadoc of each format you’re using.
jME works purely with streams for converting files: InputStreams to read files and OutputStreams to send their contents. What we are doing in this example, is simply writing the model to a byte stream:
// This byte array will hold my .jme file ByteArrayOutputStream BO = new ByteArrayOutputStream();
Then we just call convert and read from the stream:
// Use the format converter to convert .obj to .jme converter.convert(model.openStream(), BO);
Convert takes an InputStream which contains the model (.obj or .3ds or .md3 or .ms3d or whatever), and sends it to an OutputStream as .jme. If anything goes wrong with converting or reading a file, an IOException is thrown, which is why I have to surround it with a try/catch. After the conversion, the BO stream now contains the model, but in jME’s binary format.
: If you abort a program, you should exit with non-zero exit value. Tthis program aborts with 0.
Now, we use the BinaryImporter (jME API) class to read the object's binary format and load it into a Node in the scene graph.
Node maggie = (Node)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
That's it! If the node is attached to the rootNode, the model should appear now.
The method used in this example works well during the development phase. You may still be modifying and adjusting your models and you want to reload them every time. But for the release you should convert all your models to jME’s binary format, and read that directly.
Tip: The best practice is to write a method that converts all model files to .jme files, and save them using FileOutputStreams. When your game starts, you load the .jme file using a FileInputStream. This way you increase your application's performance, because the user doesn't need to wait for the files to be converted.