Starter Tutorial 3 - Hello TriMesh


« Previous: Starter Tutorial 2 - Hello Node
Next: Starter Tutorial 4 - Hello States »


(Tip: Up-to-date source files for the tutorials are always in the repository)


This program introduces the TriMesh (jME API) class and how to make your own custom objects from scratch. We will make a flat rectangle.

Sample Code

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.TexCoords;
import com.jme.scene.TriMesh;
import com.jme.util.geom.BufferUtils;
 
/**
 * Started Date: Jul 20, 2004<br>
 * <br>
 * 
 * Demonstrates making a new TriMesh object from scratch.
 * 
 * @author Jack Lindamood
 */
public class HelloTriMesh extends SimpleGame {
	public static void main(String[] args) {
		HelloTriMesh app = new HelloTriMesh();
		app.setConfigShowMode(ConfigShowMode.AlwaysShow);
		app.start();
	}
 
	protected void simpleInitGame() {
		// TriMesh is what most of what is drawn in jME actually is
		TriMesh m = new TriMesh("My Mesh");
 
		// Vertex positions for the mesh
		Vector3f[] vertexes = {
			new Vector3f(0, 0, 0),
			new Vector3f(1, 0, 0),
			new Vector3f(0, 1, 0),
			new Vector3f(1, 1, 0)
		};
 
		// Normal directions for each vertex position
		Vector3f[] normals = {
			new Vector3f(0, 0, 1),
			new Vector3f(0, 0, 1),
			new Vector3f(0, 0, 1),
			new Vector3f(0, 0, 1)
		};
 
		// Color for each vertex position
		ColorRGBA[] colors = {
			new ColorRGBA(1, 0, 0, 1),
			new ColorRGBA(1, 0, 0, 1),
			new ColorRGBA(0, 1, 0, 1),
			new ColorRGBA(0, 1, 0, 1)
		};
 
		// Texture Coordinates for each position
		Vector2f[] texCoords = {
			new Vector2f(0, 0),
			new Vector2f(1, 0),
			new Vector2f(0, 1),
			new Vector2f(1, 1)
		};
 
		// The indexes of Vertex/Normal/Color/TexCoord sets. Every 3 makes a
		// triangle.
		int[] indexes = { 0, 1, 2, 1, 2, 3 };
 
		// Feed the information to the TriMesh
		m.reconstruct(BufferUtils.createFloatBuffer(vertexes), BufferUtils
				.createFloatBuffer(normals), BufferUtils
				.createFloatBuffer(colors), TexCoords.makeNew(texCoords),
				BufferUtils.createIntBuffer(indexes));
 
		// Create a bounds
		m.setModelBound(new BoundingBox());
		m.updateModelBound();
 
		// Attach the mesh to my scene graph
		rootNode.attachChild(m);
 
		// Let us see the per vertex colors
		lightState.setEnabled(false);
	}
}

The TriMesh

The first new thing here is:

// TriMesh is what most of what is drawn in jME actually is
TriMesh m = new TriMesh("My Mesh");

Tip: Do not ever use the constructor new TriMesh() without any arguments. Always use new TriMesh(“String name”). The empty constructor is for internal use only and will not render correctly. The same goes for Node, Box, Sphere, and anything else that extends com.jme.scene.Spatial (jME API).


If you were to look at the class header for the Box and Sphere class we were using, you’d see public class Box extends TriMesh and public class Sphere extends TriMesh. TriMesh is the parent class of both Box and Sphere. They are made from it. You’ll notice that most things drawn in jME are made up of TriMeshes.

Vertex and Vector

The custom object we want to create in this example is a flat rectangle. A rectangle has four corner points (vertices). We describe the four points by four 3-D vectors. The following array stores the positions of the corners of the rectangle:

// Vertex positions for the mesh
Vector3f[] vertexes = {
	new Vector3f(0, 0, 0),
	new Vector3f(1, 0, 0),
	new Vector3f(0, 1, 0),
	new Vector3f(1, 1, 0)
};

See notes regarding redundant vertex construction required for accurate vertex normals in the vertex normals section below.

Vertex Normals

Now I assign a vertex normal to each vertex. Normals are a very common 3D graphics concept. For more information on how normals work with triangles, please visit jME’s math links. Basicly, they point in a direction where the light is its brightest (but note we don’t use lighting at all in this example). It also helps jME to tell the inside from the outside of a TriMesh object.

// Normal directions for each vertex position
Vector3f[] normals = {
	new Vector3f(0, 0, 1),
	new Vector3f(0, 0, 1),
	new Vector3f(0, 0, 1),
	new Vector3f(0, 0, 1)
};

In this array, the normal for vertex[0] is stored in normal[0], and the normal for vertex[i] is stored in normal[i].



NOTE jME supports only one vertex normal per vertex. This can mean that on polyhedra that have volume, such as a Box, you will need to duplicate edge vertices in order to support a normal for each face adjacent to that vertex. For example, every vertex of a cube is at a corner. That means there are three adjacent faces, and each of these needs to receive light differently due to it's orientation. So each corner in a Box in fact consists of three vertices to support these three normals – check the API docs for the cube class to see how it is constructed in this way. Also see this thread for more.

Coloring

After having normals assigned, each vertex can have a color. I want to assign a gradient color to my rectangle that goes from red to green.

// Color for each vertex position
ColorRGBA[] colors = {
	new ColorRGBA(1, 0, 0, 1),
	new ColorRGBA(1, 0, 0, 1),
	new ColorRGBA(0, 1, 0, 1),
	new ColorRGBA(0, 1, 0, 1)
};

In this code snippet, we give the first two vertices the color red, and the last two green. Remember the order:

  • The first two vertex positions are vertexes[0] = (0,0,0) and vertexes[1] = (1,0,0) which are the lower part of our rectangle.
  • The last two are vertexes[2] = (0,1,0) and vertexes[3] = (1,1,0) which are the upper parts.

Looking at the picture you can see how the rectangle does a smooth transition from red to green from bottom to top.

Texture Coordinates

Next, we assign texture coordinates:

// Texture Coordinates for each position
Vector2f[] texCoords = {
	new Vector2f(0, 0),
	new Vector2f(1, 0),
	new Vector2f(0, 1),
	new Vector2f(1, 1)
};

If you’ve worked with any 3D graphics before, the concept of texture coordinates is the same in jME as it is anywhere. Vector2f (jME API) is just like a Vector3f, but it has 2 floats (notice the 2f in Vector2f) instead of 3 floats (notice the 3f in Vector3f). These two floats are (x,y).
We’ll go into the details of texturing later, but for now I throw this in just so you can get the pattern of how constructing a TriMesh works.


Summary: We now have four vertices. Vertex i is now stored in vertexes[i], has a normal of normals[i], a color of colors[i], and a texture coordinate of texCoords[i].

Indices

TriMesh means Triangle mesh. It is a collection of triangles. Everything in jME is made up of triangles!


Next I have to declare indices for my TriMesh. I do that in an index array. Indices describe the order in which the triangles' corners are connected and filled.


Each triangle has three corners, that is why an index array must always be a length of modulus 3 (3,6,9,12,15, etc). For eample, if I had an index array {0,1,2,1,2,3,2,3,0}, that would be a TriMesh made up of 3 triangles.

// The indexes of Vertex/Normal/Color/TexCoord sets. 
// Every 3 makes a triangle.
int[] indexes={
 0,1,2,1,2,3
 };

In this example here, I want to create a custom rectangle. I know that a rectangle is made up of two triangles (2*3), so I use an index array of length 6.

  • Let's look at the first set, it is {0,1,2}.
    • This means that my TriMesh object’s first triangle is drawn by connecting vertexes[0] → vertexes[1] → vertexes[2].
  • The second set is {1,2,3}.
    • So the second triangle is drawn by connecting vertexes[1] → vertexes[2] → vertexes[3].
  • Both triangles are right next to each other and together, they look like a rectangle.


Troubleshooting: Note that int[] indexes={ 0,1,2,1,2,4 }; would be illegal because the index array does not contain a fifth element called vertexes[4]! It starts counting by zero.

Constructing the TriMesh

Now we have prepared all our data, and we finally create the TriMesh. Then we set a bounding volume, and attach the TriMesh to the rootNode.

// Feed the information to the TriMesh
m.reconstruct(BufferUtils.createFloatBuffer(vertexes),
	BufferUtils.createFloatBuffer(normals),
	BufferUtils.createFloatBuffer(colors),
	TexCoords.makeNew(texCoords),
	BufferUtils.createIntBuffer(indexes));
 
// Create a bounds
m.setModelBound(new BoundingBox());
m.updateModelBound();
 
// Attach the mesh to my scene graph
rootNode.attachChild(m);
 
// Let us see the per vertex colors
lightState.setEnabled(false);

The last line will make more sense after you’ve completed a few more chapters. For now, just assume that it turns on the per-vertex colors we need to see the gradient effect on the box.
Note that I don’t use the texCoords really in this example (They only have meaning when you use a picture on your object). I could have used the following, shorter contructor:

m.reconstruct(BufferUtils.createFloatBuffer(vertexes),
	BufferUtils.createFloatBuffer(normals),
	BufferUtils.createFloatBuffer(colors),
	null,
	BufferUtils.createIntBuffer(indexes));

I could even have created it using the following very short constructor:

m.reconstruct(BufferUtils.createFloatBuffer(vertexes),
	null,
	null,
	null,
	BufferUtils.createIntBuffer(indexes));

Then I would have drawn a grey, boring TriMesh without any surface colors.


We're done! With a bit of practice, you can create simple custom objects out of TriMeshes. For more complex objects you should create a 3-D model and load it. But more about that later.

Scene Graph

Here is a picture of the scene graph:


rootNode
“My Mesh”

/var/www/wiki/data/pages/tutorial_3_-_learning_jme_2.txt · Last modified: 2010/03/30 08:30 by nickza  
Recent changes · Show pagesource · Login

Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki

subscribe to jME latest jme headlines


site design by bleedcrimson designs © 2008