# jMonkeyEngine 3 Tutorial (3) - Hello Assets

Previous: Hello Node, Next: Hello Update Loop

In this tutorial we will learn to load 3D models and text into the scene graph, using the jME Asset Manager. You will also learn how to determine the correct paths, and which file formats to use.

Trouble finding the files to run this sample? To get the assets (3D models) used in this example, add the included `jme3-test-data.jar` to your classpath. In project created with the jMonkeyEngine SDK (recommended), simply right-click your project, choose "Properties", go to "Libraries", press "Add Library" and add the preconfigured "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 box = new 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

By game assets we mean all multi-media files, such as models, materials, textures, whole scenes, custom shaders, music and sound files, and custom fonts. JME3 comes with a handy AssetManager object that helps you access your assets. The AssetManager can load files from:

- the current classpath (the top level of your project directory),
- the `assets` directory of your project, and
- optionally, custom paths that you register.

The following is the recommended directory structure for storing assets in your project directoy:

    MyGame/assets/Interface/
    MyGame/assets/MatDefs/
    MyGame/assets/Materials/
    MyGame/assets/Models/
    MyGame/assets/Scenes/
    MyGame/assets/Shaders/
    MyGame/assets/Sounds/
    MyGame/assets/Textures/
    MyGame/build.xml            <-- Ant build script
    MyGame/src/...              <-- Java sources go here
    MyGame/...

This is just a suggested best practice, and it's what you get by default when creating a new Java project in the jMokeyEngine SDK. You can create an `assets` directory and technically name the subdirectories whatever you like.

### Loading Textures

Place your 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 and loads a simple wall model:

    // Create a wall with a simple texture from test_data
    Box box = new 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 this case, you create your own Material and apply it to a Geometry. You base Materials on default material descriptions (such as "Unshaded.j3md"), as shown in this example.

### Loading Text and Fonts

This example displays the text "Hello World" in the default font at the bottom edge of the window. You attach text to the `guiNode` ??? this is a special node for flat (orthogonal) display elements. You display text to show the game score, player health, etc. 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);

Tip: Clear existing text in the guiNode by detaching all its children.

### Loading a Model

Export your 3D 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 directional light to make the model visible!
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f).normalizeLocal());
    rootNode.addLight(sun);

Note that you do not need to create a Material if you exported the model with a material. Remember to add a light source, as shown, otherwise the material (and the whole model) is not visible!

### Loading Assets From Custom Paths

What if your game relies on user supplied model files, that are not included in the distribution? If a file is not located in the default location (e.g. assets directory), 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);
        Spatial scene = assetManager.loadModel("main.scene");
        rootNode.attachChild(scene);

Here is a HttpZipLocator that can download zipped models and load them:

        assetManager.registerLocator(
          "http://jmonkeyengine.googlecode.com/files/wildhouse.zip",
          HttpZipLocator.class);
        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 with an OgreXML Exporter plugin. For example, you can
. You use the SDK to load models, convert models and create scenes from them.

If you use Blender, export your models as Ogre XML meshes with materials as follows:

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 texture 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.

### Model File Formats

JME3 can load Ogre XML models + materials, Ogre DotScenes, as well as Wavefront OBJ+MTL models. The loadModel() code works with these files when you run the code directly from the jMonkeyEngine SDK.

If you build the executables using the default build script, then the original model files (XML, OBJ, etc) are not included. When you run the executable, you get an error message if you try to load any models directly:

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

Loading the XML/OBJ files directly is only acceptable during the development phase. If your graphic designer pushes updated files to the asset directory, you can quickly review the latest version in your development environment.

For testing and for the final release build, you use .j3o files exclusively. J3o is an optimized binary format for jME3 applications, and .j3o files are automatically included in the distributable JAR file by the build script. When you do QA test builds or are ready to release, use the SDK to convert all .obj/.scene/.xml/.blend files to .j3o files, and only load the .j3o versions.

Open your JME3 Project in the jMonkeyEngine SDK.

1. Right-click a .Blend, .OBJ, or .mesh.xml file in the Projects window, and choose "convert to JME3 binary".
2. The .j3o file appears next to the .mesh.xml file and has the same name.
3. Change all your loadModel() lines accordingly. For example:

       Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.j3o");

If your executable gets 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 manager's `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 give it a material to make it visible.

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

Load a scene

You load scenes just like you load models:

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

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

## Excercise - How to Load Assets

As an exercise, let's try different ways of loading a scene. You will learn how to load the scene directly, or from a zip file.

1. 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. Verify `town.zip` is in the project directory.
2. Register a zip file locator to the project directory: Add the following code under `simpleInitApp() {`

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

   The loadModel() method now searches this zip directly for the files to load.
   (This means, do not write `loadModel(town.zip/main.scene)` or similar!)

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

Tip: If you register new locators, make sure you do not get any file name conflicts: Don't name all scenes `main.scene` but 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 that you added for 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);

   Note that the path is relative to the `assets/???` directory.

4. 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 SDK and open the project that contains the HelloAsset 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);

   Again, note that the path is relative to the `assets/???` directory.

6. Clean, Build and run the project.
   Again, you should see the Ninja+wall+teapot standing in a town.

## 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 executable JARs etc.

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

See also:

- The definitive Blender import tutorial
- 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, intro, documentation, lightnode, material, model, node, gui, hud, texture

view online version