|
Advertisement |
GAMING WITH jMONKEY ENGINE PART I: GET UP AND RUNNING
Posted On August 2, 2010 by Sneha Philipose filed under
HTML clipboard
Developing cross platform games requires the purchase of costly libraries to support three dimensional graphics or to work with an inconvinient C language interface. Whereas, jMonkey Engine (jME) is a free gaming engine developed in Java, which provides support for three dimensional graphics using OpenGL library. This part introduces the jMonkey Engine, configuration of the engine and a few samples programs to run on that engine.
GAME DEVELOPMENT LIBRARIES
Gaming libraries are bound to create and render three dimensional scenes. Dealing with three dimensional rendering demands highest performance from the CPU. Some of the most popular gaming libraries are OpenGL[1] (cross platform) and DirectX for MS platforms. The OpenGL library is a mature library written in C language. The power of OpenGL is widely acknowledged.
But, using these powerful graphics libraries requires coding to be done in C language. Wrappers have been written for OpenGL in several languages. Java has also enjoyed wrapper implementations, namely, Java3D, Java binding for OpenGL (JOGL), Light Weight Java Graphics Library (LWJGL) and a lot more unfamiliar names.
The jMonkey Engine[2] is one of the powerful alternatives available for Java programmers. Since it arrived much after all the major Java wrappers, jMonkey started off from a higher level – resulting in easy interface. jMonkey works on top of LWJGL and tries to provide full support for code written in JOGL. The JOGL and LWJGL are very thin layers of wrapping around the native OpenGL libraries. It is convenient for an OpenGL programmer in C to migrate himself to Java. But, a person who does not know the nuts and bolts of OpenGL requires higher level API for comfortable working, Using jMonkey provides better abstraction from the ground level basics of OpenGL.
Apart from these, several free game development libraries are available, viz., Allegro, Chilkat (DirectX based), Crystal Space 3D, Delta3D, Demeter Terrain Engine, FreeSG, G3D C++ Library, Irrlicht Engine, Ogre3D, OpenSceneGraph, SGL (Scene Graph Library), Panda3D, QuakeForge and the list goes on. Most of these libraries are in C++[3].
THE jMONKEY ENGINE FOR GAMING
jMonkey Engine is distributed in both source and binary forms from the project home page located at http://www.jmonkeyengine.com/. If you download jMonkey sources, use ANT build system from APACHE for compiling binaries.
The jMonkey Engine comes with two important types of libraries: i) Java libraries packed into jar files, ii) platform dependent shared libraries for providing OpenGL backbone (.so files for Linux and Solaris, .dylib & .jnilib files on MacOS X and .dll files for Windows). The jar files contain Java interfacing necessary to access the native libraries and getting the work done from OpenGL.
CONFIGURING THE jMONKEY ENGINE
Configuring the jMonkey libraries is a two step process. First step is to include all the jar file names in the CLASSPATH variable. There are about 28 jar files within the lib directory of jMonkey installation. Setting the CLASSPATH variable under Windows and Linux operating systems is described here.
If you are working on Windows, right click on My Computer and select Properties. This opens the properties dialog with many tabs at top. Select the Advanced tab. There is a button names Environment Variables at the bottom of the box, on clicking which a smaller dialog opens with a list of all environment variables for current user and the machine as a whole. In list of variables, look out for a variable named CLASSPATH. If it is already defined, double click on it to edit the variable. If the variable is not defined, press the New button to create a CLASSPATH variable. The variable editor has a top input which takes the name of the variable and a bottom input which takes the values of the variable. The top box should have CLASSPATH in it and the bottom box should have the full path of each jar file separated by a semi-colon (e.g. c:\jme\lib\jme-scene.jar;c:\jme\lib\jme-audio.jar: etc).
On Linux machines, the CLASSPATH variable may be added by editing either /etc/profile file (if you have root privilege) or ~/.bash_profile file at home directory. A line looking like CLASSPATH=$CLASSPATH:/opt/jme/lib/jme-scene.jar:/opt/jme/lib/jme-audio.jar: etc. should be added, with all the jar files of jMonkey Engine. Then the line export CLASSPATH should be added. If you do not wish to edit the configuration files, each terminal session can be initialized using the command export CLASSPTH=$CLASSPATH:/opt/jme/lib/jme-scene.jar:/opt/jme/lib/jme-audio.jar: etc.
I found it difficult to manually append all the jar file names. To overcome the boredom of repeated typing of the jar file names, frequent switching between file browser & terminal and to avoid the strain of manually searching several subdirectories, I have written a Java Program, which also had 28 lines of code. I preferred to type the Java program (known devil) and avoid typing those jar file names (unknown angels). The code is presented here:
//BuildClassPath.java
1. import java.io.*;
2. import javax.swing.JFileChooser;
3.
4. class BuildClassPath implements FileFilter
5. {
6. String sep = System.getProperty("path.separator");
7. public BuildClassPath(File f) {
8. search(f);
9. System.out.println();
10. }
11. private void search(File f) {
12. File fl[] = f.listFiles(this);
13. for(int i=0; i<fl.length; i++) {
14. if(fl[i].isDirectory()) search(fl[i]);
15. else
16. System.out.printf("%s%s",fl[i].getAbsolutePath(),sep);
17. }
18. }
19. public boolean accept(File fl) {
20. return fl.isDirectory()||fl.getName().toLowerCase().endsWith(".jar");
21. }
22. public static void main(String arg[]) {
23. JFileChooser fc = new JFileChooser();
24. fc.setFileSelectionMode(fc.DIRECTORIES_ONLY);
25. if(fc.showOpenDialog(null) != fc.APPROVE_OPTION) return;
26. new BuildClassPath(fc.getSelectedFile().getAbsolutePath());
27. }
28. }
Compiling and running this file will show a file chooser, using which the lib directory of jMonkey Engine should be selected. The jar files are listed out, ready for appending to the CLASSPATH variable.
The second requirement for running jMonkey programs is to set the environment variable LD_LIBRARY_PATH. Using the same procedure that was adopted for setting CLASSPATH, the variable LD_LIBRARY_PATH can be set to the folder <jMonkey Installation>/lib/lib/lwjgl/native/linux on linux and <jMonkey Installation>\lib\lib\lwjgl\native\windows on Windows. The name of the last directory changes according to the operating system.
THE FRAMEWORK OF jMONKEY ENGINE
The root of the package hierarchy of jMonkey Engine is com.jme, within which packages named animation, app, bounding, curve, entity, image, input, intersection, light, math, renderer, scene, system and util are the first level containers for class files. Out of these, com.jme.app package contains the most elementary class required for developing a game, named SimpleGame.
Table 1 shows the some of the important packages and the classes contained within them. More information on each field or method within a class is available from the Javadoc documentation provided at the home page of jMonkey Engine[2].
|
Sl. No. |
Package |
Member Classes |
|
1. |
com.jme.animation |
AnimationEvent, AnimationEventManager, AnimationProperties,Bone, BoneAnimation, BoneChangeEvent, BoneChangeListener, BoneInfluence BoneTransform, HardpointCollection, Keyframe, SkinNode, SkinTransferNode, SpatialTransformer, SpatialTransformer$PointInTime, TextureAnimationController, TextureKeyframeController |
|
2. |
com.jme.app |
AbstractGame, BaseGame, BaseHeadlessApp, BaseSimpleGame, FixedFramerateGame, FixedLogicrateGame, SimpleGame, SimpleHeadlessApp, SimplePassGame, VariableTimestepGame |
|
3. |
com.jme.bounding |
BoundingBox, BoundingCapsule, BoundingSphere, BoundingVolume, BoundingVolume$Type, CollisionTree, CollisionTreeController, CollisionTreeManager, OrientedBoundingBox, TreeComparator, UsageTreeController. |
|
4. |
com.jme.curve |
BezierCurve, CatmullRomCurve, Curve, CurveController, PolylineCurve. |
|
5. |
com.jme.image |
BitmapHeader, Image, Texture, Texture1D, Texture2D, Texture3D, TextureCubeMap. |
|
6. |
com.jme.image.util |
BMPLoader, ColorMipMapGenerator, DDSLoader, GIFLoader, JPGLoader, PNGLoader, TGALoader. |
|
7. |
com.jme.input |
MovementPermitter, ThirdPersonBackwardAction, ThirdPersonForwardAction, ThirdPersonJoystickPlugin, ThirdPersonLeftAction, ThirdPersonMouseLook, ThirdPersonRightAction, ThirdPersonStrafeLeftAction. ThirdPersonStrafeRightAction |
|
8. |
com.jme.renderer |
AbstractCamera, Camera, ColorRGBA, RenderContext, RenderQueue, Renderer, TextureRenderer, |
|
9. |
com.jme.scene |
BezierMesh, BezierPatch, BillboardNode, CameraNode, Circle, ConnectionPoint, Controller, DistanceSwitchModel, Geometry, ImposterNode, Line, MorphInfluencesMapProvider, MorphingGeometry, MorphingTriMesh, Node, PassNode, PassNodeState, Point, QuadMesh, SharedMesh, SharedNode, Skybox, Spatial, SwitchModel, SwitchNode, TexCoords, Text, TriMesh, UserDataManager, VBOInfo. |
Above all, one should know how the coordinate system is organized for the game. The origin is located at the bottom left corner of the screen. Values in X-axis increase towards right, values in Y-axis increase when moving up. The Z-axis increases when moving away from the plane of the screen towards the user. Fig.1 shows the default setting of the coordinate system.

DEVELOPING GAME APPLICATIONS USING jME
Developing a game application requires that the class should extend com.jme.app.SimpleGame. The most basic game could be the creation of a geometric solid and adding it to the rootNode of SimpleGame class. To add objects, the simpleInitGame method of SimpleGame class should be overriden.
The following program creates a dome having radius of 8 units and displays it on screen. The constructor of Dome takes name of the object (Dome-1), number of slices (the experts call the planes) along the z-axis, number of planes along the radial direction (i.e. along the curved surface) and the radius of dome (8 units). The creation of dome takes place on line number 7.
The dome is attached to the game using attachChild method and passing the dome reference (d) as argument. This simple is the creation of a SimpleGame. Now, the usual main method instantiates the game (line number 11).
The program might specify how to obtain display configurations for the game. The method setConfigShowMode takes a ConfigShowMode (an inner class of SimpleGame) object. The predefined configuration modes are: i) AlwaysShow – show the configuration whenever the games is opened, ii) NeverShow – does not display configuration settings dialog at all -uses the default values and iii) ShowIfNoConfig – shows the configuration if and only if previous configuration data is not available. The display settings obtained from the user are written into a file named properties.cfg located at the directory from which the game was loaded.
Fig.2 shows the display settings dialogue, Fig.3 shows the dome we described above and Fig.4 shows inverted view of the dome, which can be obtained by moving the mouse in a loop. During looping, the object might move out of display. Left, right, up and down arrows turn the camera appropriately (Pressing left arrow moves the object to the right). I have removed the black background from all the figures to avoid wastage of ink during printing.



Pressing the q key tilts the object bottom up (or the camera goes down, thus the user begins to see the details of bottom portion) and pressing the z key turns the object top down (the camera goes up, reversing the effect of pressing q). Pressing the key s takes the object away in the z-direction (in the z-direction, perpendicular to the screen) and pressing the key w takes the object nearer in the z-direction.
The source code for creating the above visualizations is shown below. It is really amazing that just 15 lines of code could produce some sort of three dimensional visualization.
//DomeTest.java
1. import com.jme.app.SimpleGame;
2. import com.jme.scene.shape.Dome;
3.
4. public class DomeTest extends SimpleGame
5. {
6. protected void simpleInitGame() {
7. Dome d=new Dome("Dome-1",50,100,8);
8. rootNode.attachChild(d);
9. }
10. public static void main(String[] args) {
11. DomeTest dt = new DomeTest();
12. dt.setConfigShowMode(ConfigShowMode.AlwaysShow);
13. dt.start();
14. }
15. }
WORKING WITH COLOURS
jME permits changing the colours associated with surfaces. For setting colours one has to understand the fact that three dimensional objects consist of several points on 3D space. These points are called nodes or vertices (plural for vertex) in technical parlance. These nodes are connected using thin membranes in a smooth manner. Hence, any three dimensional object displayed on screen is composed of several nodes connected using thin surfaces. Sometimes, the inner space may be filled. In any three dimensional display, we can visualize the nodes by pressing n key on the keyboard.
The that we know about nodes (or vertices), jME permits setting the colour of each vertex. The region between connected nodes is provided colour transition in a smooth manner. For setting Colours in nodes, jME provides com.jme.render.ColorRGBA. There are three ways in which colour can be applied to an object:
i) Using the method setSolidColor and providing a colour value for the object.
ii) Using setRandomColors method, which applies random colours on the object. No control from the programmer.
iii) To set the colour of each node using java.nio.FloatBuffer object. The FloatBuffer object should contain four float values (for red, green, blue and alpha) for each node. Hence, the number of float values in a FloatBuffer should be four times the number of nodes/ vertices in the object.
Out of the three techniques, first two are direct. Setting solid colour to an object makes it difficult to view the corners and contours properly. Hence, try setSolidColor only if you wish to develop a 2D game or on objects which are strictly 2 dimensional. Setting random colours is quiet easy and hence, it will be demonstrated as part of rotating teapot example in the next part of the article. The only technique to be tried in the present section is to set the colour of each node using a FloatBuffer.
Please remember that effect of colours will be visible only after calling the method lightState.setEnable(false). The protected field lightState is inherited by SimpleGame from BaseSimpleGame class in the package com.jme.app. The following example shows a three dimensional box with two red colour sides, two green colour sides and two blue colour sides (Fig.6).

//ColorBox.java
1. import com.jme.app.SimpleGame;
2. import com.jme.scene.shape.Box;
3. import com.jme.util.geom.BufferUtils;
4.
5. public class ColorBox extends SimpleGame
6. {
7. protected void simpleInitGame() {
8. Box b=new Box("Box-1",new com.jme.math.Vector3f(0,0,0),3,5,7);
9. lightState.setEnabled(false);
10. java.nio.FloatBuffer cb = BufferUtils.createColorBuffer ( b.getVertexCount());
11. int n=cb.limit(), rl=n/3, gl = 2*n/3;
12. for(int i=0; i<n; i+=4) {
13. cb.put(i<rl?1F:0F);
14. cb.put(i>=rl && i<gl?1F:0F);
15. cb.put(i>=gl?1F:0F);
16. cb.put(1F);
17. }
18. b.setColorBuffer(cb);
19. rootNode.attachChild(b);
20. cam.setLocation(new com.jme.math.Vector3f(7, 10, 50));
21. cam.update();
22. }
23. public static void main(String[] args) {
24. ColorBox b = new ColorBox();
25. b.setConfigShowMode(ConfigShowMode.AlwaysShow);
26. b.start();
27. }
28. }
Line 8 creates a box with 6, 10, 14 units in x,y and z directions respectively, with centre point located at (0,0,0). Please note that that the dimensions given to the constructor are half the length of side in each direction, since the constructor requires the projection on either side rather than the total length.
Line 10 allots a float buffer with four float values for each vertex. Lines 11 to 17 assign red colour to first 1/3 of the vertices, green colour to the next 1/3 vertices and blue colour to the remaining vertices. Alpha value is set to 1 since transparency is not required for the vertices.
Line 20 sets the location of the camera at (7,10, 50) on the x, y, z coordinate system. This takes the viewer to a convenient location for visualizing the three different colours. Otherwise, the view would be similar to a person standing against a green wall, without any clue on what the other faces of the box look like,
APPLYING TEXTURES TO SURFACES
In most of the games, we look at objects having beautiful texture (an image wrapped around the outer surface), to make their appearance realistic. Most of the textures are created from real world photographs and hence appear natural to the eyes.
The next task is to apply a texture on a box object. Fig.7 shows the box with a texture. Curiously, I went into the box using w key (camera forward) and looked around using the arrow keys (camera swivel). The interior was as pleasing as the exterior and gave a feeling of being within a well furnished room (Fig.8).


The source code for box with texture is shown below:
//TextureDisplay.java
1. import com.jme.app.SimpleGame;
2. import com.jme.scene.shape.Box;
3.
4. public class TextureDisplay extends SimpleGame
5. {
6. protected void simpleInitGame() {
7. Box b=new Box("Box-1",new com.jme.math.Vector3f(0,0,0),8,6,6);
8. lightState.setEnabled(false);
9. rootNode.attachChild(b);
10. com.jme.scene.state.TextureState ts = display.getRenderer(). createTextureState();
11. ts.setEnabled(true);
12. ts.setTexture(com.jme.util.TextureManager.loadTexture(
13. TextureDisplay.class.getClassLoader().getResource(
14. "3d-model.jpg"), com.jme.image.Texture. MinificationFilter.Triline ar,
15. com.jme.image.Texture.MagnificationFilter.Bilinear));
16. rootNode.setRenderState(ts);
17. }
18. public static void main(String[] args) {
19. TextureDisplay td = new TextureDisplay();
20. td.setConfigShowMode(ConfigShowMode.AlwaysShow);
21. td.start();
22. }
23. }
TextureState for the box is created and enabled on lines 10 and 11. The texture is applied using the setTexture method which takes URL of an image (to be used for the texture), type of filter to be used for image reduction (they call it minification) and the filter to be used for magnification. In the present case, trilinear filter is used for reduction and bilinear filter is used for enlargement. Now the texture becomes ready for display.
TAKING HELP FROM API DOCUMENTATION
The Java source code comes with javadoc comments which will be helpful to understand the source code. But, reading through the source file does not provide the comfort of HTML. One cannot move to a new class without extensive navigation on file browser and search using text editor.
Alternatively, I tried the javadoc provided at the jMonkey home page[4]. It was easy for navigation, but it was not possible to download the entire documentation (in a zip file). As the documentation was so helpful, I wanted to have a local copy extracted using javadoc command which comes with the JDK. Running javadoc manually on each file would require a lot of effort, since the depth of folders is quiet heavy.
Why all these deliberations? I want to show a lazy man's way out of this problem. That is, a small java program which will generate the documentation for the entire API. Running the following code would show a file dialogue. Choose the src directory of the jMonkey Engine using the dialogue and press Open button. It will begin to generate the documentation at a sub-directory named jme-doc within the parent current directory of the program.
//BuildDoc.java
1. import java.io.*;
2. import javax.swing.JFileChooser;
3.
4. class BuildDoc implements FileFilter
5. {
6. long init = System.currentTimeMillis();
7. private void createDoc(File f) {
8. File fl[] = f.listFiles(this);
9. for(int i=0; i<fl.length; i++) {
10. if(fl[i].isDirectory()) createDoc(fl[i]);
11. else {
12. try {
13. System.out.printf("%.2fs passed. Working on %s\n",((System.cur
rentTimeMillis()-init)/1000.0),fl[i].getAbsolutePath());
14. Process p = Runtime.getRuntime().exec("javadoc -d jme-doc "+fl
[i].getAbsolutePath());
15. new IOThread(p.getErrorStream(),System.err);
16. new IOThread(p.getInputStream(), System.out);
17. new IOThread(System.in, p.getOutputStream());
18. p.waitFor();
19. } catch(Exception ex) {ex.printStackTrace();}
20. }
21. }
22. }
23. class IOThread extends Thread {
24. InputStream is = null;
25. OutputStream os = null;
26. IOThread(InputStream is, OutputStream os) {
27. this.is=is; this.os = os; this.start(); }
28. public void run() {
29. try {while(is.available()>0) os.write(is.read());}
30. catch(Exception ex){}
31. }
32. }
33. public boolean accept(File fl) {
34. return fl.isDirectory()||fl.getName().toLowerCase().endsWith(".java");
35. }
36. public static void main(String arg[]) {
37. JFileChooser fc = new JFileChooser();
38. fc.setFileSelectionMode(fc.DIRECTORIES_ONLY);
39. if(fc.showOpenDialog(null) != fc.APPROVE_OPTION) return;
40. File fl = new File("jme-doc");
41. if(!fl.exists()) fl.mkdirs();
42. new BuildDoc().createDoc(fc.getSelectedFile());
43. }
44. }
READY TO WORK
It is time for you to start working on serious models. The Java API provides easy access to the underlying OpenGL libraries. The object oriented interface provided by jMonkey Engine is easier for programmers to understand and adopt, when compared to the terse procedural API provided by OpenGL in C language.
For programmers who just begin to work with jMonkey Engine, it is advisable to go through the example code provided with the jMonkey source code in the folder named src/jmetest. The java documentation for the API would be useful to getting details on specific classes and methods. The next part of this article will explain animation, collision and input handing using jMonkey Engine.
REFERENCES
1. http://www.opengl.org/
2. http://www.jmonkeyengine.com/
3. http://www.codemonsters.de/home/content.php?show=freelibraries
4. http://www.jmonkeyengine.com/doc
About Author
V. Nagaradjane is a freelance programmer. He may be contacted at nagaradjanev@rediffmail.com.

Comments
Rajesh Jain commented, on August 9, 2010 at 3:37 p.m.:
Give us more Power Builder articles. PB articles which was published in July and August issue was good.
Thanks to DeveloperIQ team.
Sandeep Kumar commented, on August 24, 2010 at 11:41 a.m.:
Hi Nagaradjane your articles are very good. Especially articles on JAVA.






Latha Nair commented, on August 6, 2010 at 8:41 p.m.:
Very Good Articel on jMONKEY. Can you provide me more articles?