JME 3 Tutorial (2) - Hello Node

Previous: Hello SimpleApplication, Next: Hello Assets.

In this tutorial we will have a look at the creation of a 3D scene.

When creating a 3D game

  1. You create some scene objects like players, buildings, etc.
  2. You add the objects to the scene.
  3. You move, resize, rotate, color, and animate them.

You will learn that the scene graph represents the 3D world, and why the rootNode is important. You will learn how to create simple objects, and how to "transform" them by moving, scaling, and rotating. You will understand the difference between the two types of "Spatials" in the scene graph: Nodes and Geometries.

Code Sample

package jme3test.helloworld;
 
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Node;
 
/** Sample 2 - How to use nodes as handles to manipulate objects in the scene.
 * You can rotate, translate, and scale objects by manipulating their parent nodes.
 * The Root Node is special: Only what is attached to the Root Node appears in the scene. */
public class HelloNode extends SimpleApplication {
 
    public static void main(String[] args){
        HelloNode app = new HelloNode();
        app.start();
    }
 
    @Override
    public void simpleInitApp() {
 
        /** create a blue box at coordinates (1,-1,1) */
        Box( new Vector3f(1,-1,1), 1,1,1);
        Geometry blue = new Geometry("Box", box1);
        Material mat1 = new Material(assetManager, 
                "Common/MatDefs/Misc/Unshaded.j3md");
        mat1.setColor("Color", ColorRGBA.Blue);
        blue.setMaterial(mat1);
 
        /** create a red box straight above the blue one at (1,3,1) */
        Box( new Vector3f(1,3,1), 1,1,1);
        Geometry red = new Geometry("Box", box2);
        Material mat2 = new Material(assetManager, 
                "Common/MatDefs/Misc/Unshaded.j3md");
        mat2.setColor("Color", ColorRGBA.Red);
        red.setMaterial(mat2);
 
        /** Create a pivot node at (0,0,0) and attach it to the root node */
        Node pivot = new Node("pivot");
        rootNode.attachChild(pivot); // put this node in the scene
 
        /** Attach the two boxes to the *pivot* node. */
        pivot.attachChild(blue);
        pivot.attachChild(red);
        /** Rotate the pivot node: Note that both boxes have rotated! */
        pivot.rotate(.4f,.4f,0f);
    }
}

Build and run the code sample. You should see two colored boxes tilted at the same angle.

Understanding the Terminology

In this tutorial, you learn some new terms:

What you want to doHow you say it in JME3 terminology
Structure the 3D sceneDefine the scene graph
Create scene objectsCreate Spatials
Make an object appear in the sceneAttach a Spatial to the rootNode
Make an object disappear from the sceneDetach the Spatial from the rootNode

Every JME3 application has a rootNode: Your game automatically inherits the rootNode object from SimpleApplication. Everything attached to the rootNode is part of the scene graph. The elements of the scene graph are Spatials.

Geometry Node
Visibility: A Geometry is a visible scene object. A Node is an invisible "handle" for scene objects.
Purpose: A Geometry stores an object's looks. A Node groups Geometries and other Nodes together.
Examples: A box, a sphere, a player, a building, a piece of terrain, a vehicle, missiles, NPCs, etc… The rootNode, a floor node grouping several terrains, a custom vehicle-with-passengers node, a player-with-weapon node, an audio node, etc…

Understanding the Code

What happens in the code snippet? You use the simpleInitApp() method that was introduced in the first tutorial to initialize the scene.

  1. You create the first box Geometry.
    • Create a Box shape with a radius of (1,1,1), that makes the box 2x2x2 world units big.
    • Position the box at (1,-1,1).
    • Wrap the Box shape into a Geometry.
    • Create a blue material.
    • Apply the blue material to the Box Geometry.
          Box( new Vector3f(1,-1,1), 1,1,1);
          Geometry blue = new Geometry("Box", box1);
          Material mat1 = new Material(assetManager,
            "Common/MatDefs/Misc/Unshaded.j3md");
          mat1.setColor("Color", ColorRGBA.Blue);
          blue.setMaterial(mat1);
  2. You create a second box Geometry.
    • Create a second Box shape with the same size.
    • Position the second box at (1,3,1). This is straight above the first box, with a gap of 2 world units inbetween.
    • Wrap the Box shape into a Geometry.
    • Create a red material.
    • Apply the red material to the Box Geometry.
          Box( new Vector3f(1,3,1), 1,1,1);
          Geometry red = new Geometry("Box", box2);
          Material mat2 = new Material(assetManager,
            "Common/MatDefs/Misc/Unshaded.j3md");
          mat2.setColor("Color", ColorRGBA.Red);
          red.setMaterial(mat2);
  3. You create a Node.
    • Name the Node "pivot".
    • By default the Node is positioned at (0,0,0).
    • Attach the Node to the rootNode.
    • The Node has no visible appearance in the scene.
          Node pivot = new Node("pivot");
          rootNode.attachChild(pivot);

      If you run the application with only the code up to here, the scene appears empty. This is because the Node is invisible and you have not yet attached any visible Geometries to the rootNode.

  4. Attach the two boxes to the node.
            pivot.attachChild(blue);
            pivot.attachChild(red);

    If you run the app with only the code up to here, you see two cubes: A red cube straight above a blue cube.

  5. Rotate the pivot node.
            pivot.rotate( 0.4f , 0.4f , 0.0f );

    If you run the app now, you see two boxes on top of each other – both tilted at the same angle.

What has happened? You have grouped two Geometries by attaching them to one pivot Node. You can now use the pivot Node as a handle to move the two Geometries. Rotating the pivot Node rotates both attached Geometries, in one step. Transforming a Node to transform attached Spatials is a common task. You will use this method a lot in your games when you move Spatials around, e.g. for game characters. You can also rotate Spatials around their own center – then you do not need a pivot Node.

How do I Populate the Scenegraph?

Task…? Solution!
Create a Spatial Create a shape and give it a Material. For example:
Box(Vector3f.ZERO, 1, 1, 1);
Geometry thing = new Geometry("thing", mesh);
Material mat = new Material(assetManager,
   "Common/MatDefs/Misc/ShowNormals.j3md");
thing.setMaterial(mat);
Make an object appear in the scene Attach the Spatial to the rootNode, or to any node that is attached to the rootNode.
rootNode.attachChild(thing);
Remove objects from the scene Detach the Spatial from the rootNode, and from any node that is attached to the rootNode.
rootNode.detachChild(thing);
rootNode.detachAllChildren();
Find a Spatial in the scene by the object's name or ID. Look at the node's children:
Spatial thing = rootNode.getChild("thing");
Spatial twentyThird = rootNode.getChild(22);
Specify what should be loaded at the start Everything you initialize and attach to the rootNode in the simpleInitApp() method is part of the scene at the start of the game.

How do I Transform Spatials?

There are three types of 3D transformation: Translation, Scaling, and Rotation.

Translation moves Spatials X-axis Y-axis Z-axis
Specify the new location in three dimensions: How far away is it from the origin going right-up-forward?
To move a Spatial to specific coordinates, such as (0,40.2f,-2), use:
thing.setLocalTranslation( new Vector3f( 0.0f, 40.2f, -2.0f ) );

To move a Spatial by a certain amount, e.g. higher up (y=40.2f) and further back (z=-2.0f):

thing.move( 0.0f, 40.2f, -2.0f );
+right -left+up -down+forward -backward
Scaling resizes Spatials X-axis Y-axis Z-axis
Specify the scaling factor in each dimension: length, height, width.
A value between 0.0f and 1.0f shrinks the Spatial; bigger than 1.0f stretches it; 1.0f keeps it the same.
Using the same value for each dimension scales proportionally, different values stretch it.
To scale a Spatial 10 times longer, one tenth the height, and keep the same width:
thing.scale( 10.0f, 0.1f, 1.0f );
lengthheightwidth
Rotation turns Spatials X-axis Y-axis Z-axis
3-D rotation is a bit tricky (learn details here). In short: You can rotate around three axes: Pitch, yaw, and roll. You can specify angles in degrees by multiplying the degrees value with FastMath.DEG_TO_RAD.
To roll an object 180° around the z axis:
thing.rotate( 0f , 0f , 180*FastMath.DEG_TO_RAD );

Tip: If your game idea calls for a serious amount of rotations, it is worth looking into quaternions, a data structure that can combine and store rotations efficiently.

thing.setLocalRotation( 
  new Quaternion().fromAngleAxis(180*FastMath.DEG_TO_RAD, new Vector3f(1,0,0)));
pitch = nodding your headyaw = shaking your headroll = cocking your head

How do I Troubleshoot Spatials?

If you get unexpected results, check whether you made the following common mistakes:

Problem? Solution!
A created Geometry does not appear in the scene. Have you attached it to (a node that is attached to) the rootNode?
Does it have a Material?
What is its translation (position)? Is it behind the camera or covered up by another Geometry?
Is it to tiny or too gigantic to see?
Is it too far from the camera? (Try (111111f); to see further)
A Spatial rotates in unexpected ways. Did you use radian values, and not degrees? (If you used degrees, multiply them with FastMath.DEG_TO_RAD to convert them to radians)
Did you rotate around the intended pivot node or something else?
Did you rotate around the right axis?
A Geometry has an unexpected Color or Material. Did you reuse a Material from another Geometry and have inadvertently changed its properties? (If so, consider cloning it: mat2 = mat.clone(); )

Conclusion

You have learned that your 3D scene is a scene graph made up of Spatials: Visible Geometries and invisible Nodes. You can transform Spatials, or attach them to nodes and transform the nodes.

Since standard shapes like spheres and boxes get old fast, continue with the next chapter where you learn to load assets, such as 3-D models.

beginner, rootNode, node, intro, documentation, color

view online version