This is an introduction to the concept of Spatials, the elements of the 3D scene graph. The scene graph is a data structure that manages all objects in your 3D world. For example, the scene graph keeps track of the 3D models that you load and position. When you extend a Java class from com.jme3.app.SimpleApplication, you automatically inherit the scene graph and its rootNode.
The rootNode is the central element of the scene graph. Even if the scene graph is empty, it always contains at least the rootNode. We attach Spatials to the rootNode. Attached Spatials are always in a parent-child relationship. Every time you attach a Spatial to something, it is implicitly detached from its previous parent. A Spatial can have only one parent. A Spatial can have several children.
If you think you need to understand the scene graph concept better, please read Scenegraph for dummies first.
In your Java code, a Spatial is either a com.jme3.scene.Node or a com.jme3.scene.Geometry. You use the two for different purposes:
| com.jme3.scene.Spatial | ||
|---|---|---|
| Purpose: | A Spatial is an abstract data structure that stores transformations (translation, rotation, scale) of elements of the 3D scene graph. Spatials can be saved and loaded using the Asset Manager. | |
| com.jme3.scene.Geometry | com.jme3.scene.Node | |
| Visibility: | A Geometry represents a visible 3D object in the scene graph. | A Node is an invisible "handle" for a group of Spatials in the scene graph. |
| Purpose: | Use Geometries to represent an object's looks: Every Geometry contains a polygon mesh and a material, specifying its shape, color, texture, and opacity/transparency. You can attach a Geometry to a Node. | Use Nodes to structure and group Geometries and other Nodes. Every Node is attached to one parent node, and each node can have zero or more children attached to itself. When you transform (move, rotate, etc) a parent node, all its children are transformed (moved, rotated, etc). |
| Content: | Transformations; custom user data; mesh, material; | Transformations; custom user data; no mesh, no material. |
| Examples: | Box, sphere, player, building, terrain, vehicle, missiles, NPCs, etc… | rootNode, guiNode, audioNode, a custom vehicleNode or shipNode with passengers attached, etc. |
Spatial s = new Spatial();! A Spatial is an abstract concept, like a mammal (there is no actual creature called "mammal" walking around here). You either create a Node or a Geometry object. Some methods however require a Spatial argument: This is because they are able to accept both Nodes and Geometries as arguments. In this case, you must cast a Node or Geometry to Spatial.
The polygon Mesh inside a Geometry can be one of three things:
You can include custom user data –that is, custom Java objects and methods– in Nodes and Geometries. This is very useful for maintaining information about a game element, such as health, budget, ammunition, inventory, equipment, etc for players, or landmark locations for terrains, and much more.
setUserData() method instead. Where ever the Spatial is accessible, you can easily access the object's game data and accessors this way.
The following example adds an integer field named health to the Spatil player_node, and initializes it to 100.
player_node.setUserData("health", 100);
To be able to add accessors to the player, you create a custom PlayerControl class and add it to the Spatial.
player_node.addControl(PlayerControl.class);
In PlayerControl you define methods that set and get your users data in the spatial object.
public int getHealth() { return (Integer)spatial.getUserData("health"); } public void setHealth(int h) { spatial.setUserData("health",h); }
Elsewhere in your code, you can access this data wherever you have access to the Spatial player_node.
health = player_node.getControl(PlayerControl.class).getHealth(); ... player_node.getControl(PlayerControl.class).setHealth(99);
health, inventory, equipment, etc). This is how you list all data keys that are already defined for one Spatial:
for(String key : spatial.getUserDataKeys()){ System.out.println(spatial.getName()+"'s keys: "+key); }
Often after you load a scene or model, you need to access a part of it as an individual Geometry in the scene graph. Maybe you want to swap a character's weapon, or you want to play a door-opening animation. First you need to know the unique name of the sub-mesh.
In the following example, the Node house is the loaded model. The sub-meshes in the Node are called its children. The String, here door 12, is the name of the mesh that you are searching.
Geometry submesh = (Geometry) houseScene.getChild("door 12");