# JME 3 Tutorial (3) - Hello Assets

Previous: Hello Node, Next: Hello Update Loop

In this tutorial we will learn to load 3-D models and text into the scene graph, using the jME asset manager. You also learn how to arrive at the correct paths, and which file formats to use.

To use the example assets in a new jMonkeyPlatform project, right-click your project, select "Properties", go to "Libraries", press "Add Library" and add the "jme3-test-data" library.

## Code Sample

    package jme3test.helloworld;

    import com.jme3.app.SimpleApplication;
    import com.jme3.font.BitmapText;
    import com.jme3.light.DirectionalLight;
    import com.jme3.material.Material;
    import com.jme3.math.Vector3f;
    import com.jme3.scene.Geometry;
    import com.jme3.scene.Spatial;
    import com.jme3.scene.shape.Box;

    /** Sample 3 - how to load an OBJ model, and OgreXML model,
     * a material/texture, or text. */
    public class HelloAssets extends SimpleApplication {

        public static void main(String[] args) {
            HelloAssets app = new HelloAssets();
            app.start();
        }

        @Override
        public void simpleInitApp() {

            Spatial teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
            Material mat_default = new Material(
                assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
            teapot.setMaterial(mat_default);
            rootNode.attachChild(teapot);

            // Create a wall with a simple texture from test_data
            Box(Vector3f.ZERO, 2.5f,2.5f,1.0f);
            Spatial wall = new Geometry("Box", box );
            Material mat_brick = new Material(
                assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            mat_brick.setTexture("ColorMap",
                assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"));
            wall.setMaterial(mat_brick);
            wall.setLocalTranslation(2.0f,-2.5f,0.0f);
            rootNode.attachChild(wall);

            // Display a line of text with a default font
            guiNode.detachAllChildren();
            guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
            BitmapText helloText = new BitmapText(guiFont, false);
            helloText.setSize(guiFont.getCharSet().getRenderedSize());
            helloText.setText("Hello World");
            helloText.setLocalTranslation(300, helloText.getLineHeight(), 0);
            guiNode.attachChild(helloText);

            // Load a model from test_data (OgreXML + material + texture)
            Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
            ninja.scale(0.05f, 0.05f, 0.05f);
            ninja.rotate(0.0f, -3.0f, 0.0f);
            ninja.setLocalTranslation(0.0f, -5.0f, -2.0f);
            rootNode.attachChild(ninja);
            // You must add a light to make the model visible
            DirectionalLight sun = new DirectionalLight();
            sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f));
            rootNode.addLight(sun);

        }
    }

Build and run the code sample. You should see a green Ninja with a colorful teapot standing behind a wall. The text on the screen should say "Hello World".

## The Asset Manager

JME3 comes with a handy asset manager that helps you keep your assets structured. Project assets are media files such as models, materials, textures, scenes, shaders, sounds, and fonts. The asset manager maintains a root that contains the classpath, so it can load any file from the current classpath (the top level of your project directory).

Additionally, the assetManager can be configured to add any path to the assets root, so assets can be loaded from directories you specify. In a jMonkeyPlatform project, jME3 seaches for models in the `assets` directory of your project. This is our recommended directory structure for storing assets:

    assets/Interface/
    assets/MatDefs/
    assets/Materials/
    assets/Models/
    assets/Scenes/
    assets/Shaders/
    assets/Sounds/
    assets/Textures/
    build.xml
    src/...
    dist/...

These are just the most common examples, you can name the directories inside the assets directory how you like.

### Loading Textures

Place the textures in a subdirectory of `assets/Textures/`. Load the texture into the material before you set the Material. The following code sample is from the `simpleInitApp()` method:

            // Create a wall with a simple texture from test_data
            Box(Vector3f.ZERO, 2.5f,2.5f,1.0f);
            Spatial wall = new Geometry("Box", box );
            Material mat_brick = new Material(
                assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            mat_brick.setTexture("ColorMap",
                assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"));
            wall.setMaterial(mat_brick);
            wall.setLocalTranslation(2.0f,-2.5f,0.0f);
            rootNode.attachChild(wall);

In most cases, you use default material descriptions such as "SimpleTextured.j3md", as we do in this example. Advanced users can create custom Materials.

### Loading Text and Fonts

This example displays "Hello Text" in the default font at the bottom edge of the window. You attach text to the `guiNode`, a special node for flat (orthogonal) display elements. You can clear existing text in the guiNode by detaching all its children. The following code sample goes into the `simpleInitApp()` method.

            // Display a line of text with a default font
            guiNode.detachAllChildren();
            guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
            BitmapText helloText = new BitmapText(guiFont, false);
            helloText.setSize(guiFont.getCharSet().getRenderedSize());
            helloText.setText("Hello World");
            helloText.setLocalTranslation(300, helloText.getLineHeight(), 0);
            guiNode.attachChild(helloText);

### Loading an Ogre XML Model

Export your model in OgreXML format (.mesh.xml, .scene, .material, .skeleton.xml) and place it in a subdirectory of `assets/Models/`. The following code sample goes into the `simpleInitApp()` method.

            // Load a model from test_data (OgreXML + material + texture)
            Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
            ninja.scale(0.05f, 0.05f, 0.05f);
            ninja.rotate(0.0f, -3.0f, 0.0f);
            ninja.setLocalTranslation(0.0f, -5.0f, -2.0f);
            rootNode.attachChild(ninja);
            // You must add a light to make the model visible
            DirectionalLight sun = new DirectionalLight();
            sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f));
            rootNode.addLight(sun);

If you use the build script created by the jMonkeyPlatform then, by default, the original OgreXML files will not be included in your distributed game. You will get an error message when trying to load them.

    com.jme3.asset.DesktopAssetManager loadAsset
    WARNING: Cannot locate resource: Scenes/town/main.scene
    com.jme3.app.Application handleError
    SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
    java.lang.NullPointerException

For the release build, you should work with .j3o files only. Use the jMonkeyPlatform's context menu action to convert OgreXML models to .j3o format.

### Loading Assets From Custom Paths

What if your game relies on user supplied model files, that will not be included in your distribution? If a file is not located in the default location, you can register a custom Locator and load it from any path.

Here is a usage example of a ZipLocator that is registered to a file `town.zip` in the top level of your project directory:

        assetManager.registerLocator("town.zip", ZipLocator.class.getName());
        Spatial scene = assetManager.loadModel("main.scene");
        rootNode.attachChild(scene);

Here is a HttpZipLocator that can download zipped models:

        assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/wildhouse.zip",
                                     HttpZipLocator.class.getName());
        Spatial scene = assetManager.loadModel("main.scene");
        rootNode.attachChild(scene);

JME3 offers ClasspathLocator, ZipLocator, FileLocator, HttpZipLocator, and UrlLocator (see `com.jme3.asset.plugins`).

## Creating Models and Scenes

To create 3D models and scenes, you need a 3D Mesh Editor such as Blender, with an OgreXML Exporter plugin. Create your models with UV textures. You can use the jMonkeyPlatform to load models and create scenes from them.

Export your models as Ogre XML meshes with materials.

1. Open the menu File > Export > OgreXML Exporter to open the exporter dialog.
2. In the Export Materials field: Give the material the same name as the model. For example, the model `something.mesh.xml` goes with `something.material`, plus (optionally) `something.skeleton.xml` and some JPG files.
3. In the Export Meshes field: Select a subdirectory of your `assets/Models/` directory. E.g. `assets/Models/something/`.
4. Activate the following exporter settings:
   - Copy Textures: YES
   - Rendering Materials: YES
   - Flip Axis: YES
   - Require Materials: YES
   - Skeleton name follows mesh: YES
5. Click export.

File Format: JME 3 can load Ogre XML models, materials, and scenes, as well as Wavefront OBJ+MTL models. For the game release, you should optimize model loading by converting all models to JME3's .j3o format. We recommend creating your project in the jMonkeyPlatform, it contains an integrated .j3o converter.

1. Open your JME3 Project in the jMonkeyplatform.
2. Right-click a .mesh.xml file in the Projects (or Favorites) window, and choose "convert to JME3 binary".
3. The .j3o file appears next to the .mesh.xml file with the same name.
4. If you use the build script generated by the jMonkeyPlatform, mesh.xml files and obj files will be excluded from the executable JAR. If you get a runtime exception, make sure you have converted all models to .j3o.

### Loading Models and Scenes

Task? | Solution!
--- | ---

Load a model with materials

Use the asset managers `loadModel()` method and attach the Spatial to the rootNode.

    Spatial elephant = assetManager.loadModel("Models/Elephant/Elephant.mesh.xml");
    rootNode.attachChild(elephant);

    Spatial elephant = assetManager.loadModel("Models/Elephant/Elephant.j3o");
    rootNode.attachChild(elephant);

Load a model without materials

If you have a model without materials, you have to add a default material to make it visible.

    Spatial teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
    Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
    teapot.setMaterial(mat);
    rootNode.attachChild(teapot);

Load a scene

You load scenes just like you load models:

    Spatial scene = assetManager.loadModel("Scenes/house/main.scene");
    rootNode.attachChild(scene);

### Excercise - How to Load Assets

As an exercise, let's try different ways of loading a scene. You can load a scene directly, or from a zip file:

1. Download the town.zip sample scene.
2. (Optional:) Unzip the town.zip to see the structure of the contained Ogre dotScene: You'll get a directory named `town`. It contains XML and texture files, and file called main.scene. (This is just for your information, you do not need to do anything with it.)
3. Place the town.zip file in the top level directory of your JME3 project, like so:

       jMonkeyProjects/MyGameProject/assets/
       jMonkeyProjects/MyGameProject/build.xml
       jMonkeyProjects/MyGameProject/src/
       jMonkeyProjects/MyGameProject/town.zip
       ...

Use the following method to load models from a zip file:

1. Make sure `town.zip` is in the project directory.
2. We register a zip file locator to the project directory. The loadModel() method now searches this zip directly for the files to load.
   (That is, do not write `town.zip/main.scene` or similar.)
3. Add the following code under `simpleInitApp() {`

           assetManager.registerLocator("town.zip", ZipLocator.class.getName());
           Spatial gameLevel = assetManager.loadModel("main.scene");
           gameLevel.setLocalTranslation(0, -5.2f, 0);
           gameLevel.setLocalScale(2);
           rootNode.attachChild(gameLevel);

4. Clean, build and run the project. You should see the Ninja+wall+teapot standing in a town.

If you register new locators, make sure you do not get any file name conflicts: Give each scene a unique name.

Earlier in this tutorial, you loaded scenes and models from the asset directory. This is the most common way you will be loading scenes and models. Here is the typical procedure:

1. Remove the code from the previous exercise.
2. Move the unzipped `town/` directory into the `assets/Scenes/` directory of your project.
3. Add the following code under `simpleInitApp() {`

           Spatial gameLevel = assetManager.loadModel("Scenes/town/main.scene");
           gameLevel.setLocalTranslation(0, -5.2f, 0);
           gameLevel.setLocalScale(2);
           rootNode.attachChild(gameLevel);

4. Note that the path starts relative to the `assets/…` directory.
5. Clean, build and run the project. Again, you should see the Ninja+wall+teapot standing in a town.

Here is a third method you must know. Loading a scene/model from a .j3o file:

1. Remove the code from the previous exercise.
2. If you haven't already, open the jMonkeyPlatform and open the project that contains the Hello Asset class.
3. In the projects window, browse to the `assets/Scenes/town` directory.
4. Right-click the `main.scene` and convert the scene to binary: The jMoneyPlatform generates a main.j3o file.
5. Add the following code under `simpleInitApp() {`

           Spatial gameLevel = assetManager.loadModel("Scenes/town/main.j3o");
           gameLevel.setLocalTranslation(0, -5.2f, 0);
           gameLevel.setLocalScale(2);
           rootNode.attachChild(gameLevel);

6. Again, note that the path starts relative to the `assets/…` directory.
7. Clean, Build and run the project. Again, you should see the Ninja+wall+teapot standing in a town.

What is the difference between loading `Scenes/town/main.j3o` and `Scenes/town/main.scene`?

- You can work with *.scene and .xml files during the development phase: When your designer pushes updated files to the asset directory, you can quickly review the latest version in your development environment.
- Create and load *.j3o files for QA and release builds: .j3o is a binary format for jME3 applications, and .j3o files will be automatically included in the distributable JAR file. When you do QA test builds or are ready to release, use the jMonkeyPlatform to convert all .scene and .xml files to .j3o files, and load these.

## Conclusion

Now you know how to populate the scenegraph with static shapes and models, and how to build scenes. You have learned how to load assets using the `assetManager` and you have seen that the paths start relative to your project directory. Another important thing you have learned is to convert models to .j3o format for the JAR builds.

Let's add some action to the scene and continue with the Update Loop.

See also:

- Exporting OgreXML scenes from Blender to jME
- Screenshots of a great loaded model
- If you want to learn how to load sounds, see Hello Audio
- If you want to learn more about loading textures and materials, see Hello Material

beginner, beginner,, intro, documentation, lightnode, material, model, node, gui, texture

view online version