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 scenegraph is empty, it always has at least its rootNode. All other Spatials are attached to the rootNode in a parent-child relationship. 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 scene graph. Spatials can be saved and loaded using the AssetManager. | |
com.jme3.scene.Geometry | com.jme3.scene.Node | |
Visibility: | A Geometry represents a visible 3-D object in the scene graph. | A Node is an invisible "handle" for a group of objects 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 a parent node, all its children are transformed as well. |
Content: | Transformations; custom user data; mesh, material; | Transformations; custom user data; no mesh, no material. |
Examples: | A box, a sphere, player, a building, a piece of terrain, a vehicle, missiles, NPCs, etc… | The rootNode, the guiNode, an audio node, a custom grouping node, 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 create a Node, or load a Geometry object. Some methods however require a Spatial argement: 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 Java objects in Nodes and Geometries. This is 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. Where ever the spatial is accessible, you can also access the object's game data.
// create and instance of your custom data class PlayerData playerData = new PlayerData("joe", 0, 100); // store custom data in Node or Geometry player.setUserData("player data", playerData); ... // Elsewhere: retrieved data from Node or Geometry... PlayerData playerData = player.getUserData("player data"); // ... set the data... playerData.setHealth("99"); // ... or get the data for tests or to display it in the HUD. health = playerData.getHealth();
You can add as many data objects to a Spatial as you need. Just make sure to label them with different Strings (player data
, player inventory
, player equipment
, etc).
You can also list all data keys that are defined for one Spatial:
for(String key : geom.getUserDataKeys()){ System.out.println(geom.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");