Dev 145 – JSR184 in Retained Mode – part 2 of 2

A very useful feature of “Retained Mode” is the ability to load 3D objects created with modeling programs.

Having to implement a 3D video game or even a demo you will need to create and draw different objects on the screen, but not always as simple as the primitives (cubes, pyramids, etc.). It is obvious that it would become a very stressful job, if not impossible, to specify by hand all the information necessary to define these objects.

If you have uploaded and compiled the NetBeans projects of the examples presented in the previous articles you surely noticed the amount of steps needed to define a simple cube. For greater clarity, I quote an extract.

private static final byte[] VERTICES_POSITIONS = {
    -1, -1,  1,  1, -1,  1, -1,  1,  1,  1,  1,  1, // front
     1, -1, -1, -1, -1, -1,  1,  1, -1, -1,  1, -1, // retro
     1, -1,  1,  1, -1, -1,  1,  1,  1,  1,  1, -1, // right
    -1, -1, -1, -1, -1,  1, -1,  1, -1, -1,  1,  1, // left
    -1,  1,  1,  1,  1,  1, -1,  1, -1,  1,  1, -1, // top
    -1, -1, -1,  1, -1, -1, -1, -1,  1,  1, -1,  1  // bottom
};
 
private static int[] TRIANGLE INDICES = {
    0,   1,  2,  3, // front
    4,   5,  6,  7, // back
    8,   9, 10, 11, // right
    12, 13, 14, 15, // left
    16, 17, 18, 19, // top
    20, 21, 22, 23, // bottom
};      
 
private static final byte[] VERTICES_NORMALS = {
    0, 0, 127, 0, 0, 127, 0, 0, 127, 0, 0, 127, // front
    0, 0, -128, 0, 0, -128, 0, 0, -128, 0, 0, -128, // retro
    127, 0, 0, 127, 0, 0, 127, 0, 0, 127, 0, 0, // right
    -128, 0, 0, -128, 0, 0, -128, 0, 0, -128, 0, 0, //    left
    0, 127, 0, 0, 127, 0, 0, 127, 0, 0, 127, 0, // high
    0, -128, 0, 0, -128, 0, 0, -128, 0, 0, -128, 0, // bottom           
};               
 
private static int[] TRIANGLES_LENGTHS = {
    4, 4, 4, 4, 4, 4
};
 
private static final byte[] VERTICES_COLORS = {
    0, (byte) 255, 0, 0, (byte) 255, (byte) 255,                                                                                   
    (byte) 255, 0, 0, (byte) 255, 0, (byte) 255,                                         
    (byte) 255, (byte) 255, 0, (byte) 255, (byte) 255, (byte) 255,                           
    0, 0, (byte) 128, 0, 0, (byte) 255                                         
};

In addition it is necessary to create a VertexBuffer to keep the information, several VertexArrays to load the data, one or more TriangleStrips and at least one Appearance object. In code

cubeVerticesData = new VertexBuffer();
VertexArray verticesPotisions = new VertexArray (VERTICES_POSITIONS.length / 3, 3, 1);
verticesPositions.set(0, VERTICES_POSITIONS.length / 3, VERTICES_POSITIONS);
cubeVerticesData.setPositions(verticesPotisions, 1.0f, null);
cubeTriangles = new TriangleStripArray(TRIANGLE_INDICES, new int[]{TRIANGLE_INDICES.length});           
cubeAppearance = new Appearance();           
polygonMode = new PolygonMode();
cubeAppearance.setPolygonMode(polygonMode);           
VertexArray verticesColors = new VertexArray(VERTICES_COLORS.length / 3, 3, 1);           
verticesColors.set(0, VERTICES_COLORS.length / 3, VERTICES_COLORS);           
cubeVerticesData.setColors(verticesColors); 

Imagine having to repeat these steps for each object to be displayed and you will realize that loading directly to memory objects contained in a file simplifies enormously the work.

Use m3g files

JSR-184 supports a type of binary file, with the extension .m3g. The format of an m3g file is very complex as can be seen from the API documentation. For simplicity we can say that, every scene contained in it is structured in the same way seen in the first part of this series of articles.

The loading process creates an array in memory where each element represents a node of the structure. Obviously the first element of the array is nothing but the root of the tree, that is the World type node.

In the article by Maurizio Terzo, an integral part of this series of articles, the process of creating a .m3g file through 3D Studio Max is explained in detail.

Let us see a practical example and comment on the code in Listing 1

private World world;

public void Init() {                 
    
    try
    {
        // Load the m3d file
        Object3D[] objects = Loader.load("/example.m3g");
        
        // Gets the reference to the World object
        world = (World)objects[0];

        // Retrieve the reference to the active camera
        Camera camera = world.getActiveCamera();
        
        // Calculate the aspectRatio
        float aspectRatio = (float) getWidth() / (float) getHeight();
        
        // Activate the perspective correction on the camera
        camera.setPerspective(60.0f, aspectRatio, 1.0f, 1000.0f); 
    }
    catch (Exception e)
    {
        // Error
    }            
    
    initDone = true;                        
}

// Here is inserted the code that carries 
// out the periodic application management operations
void frame()
{
    // Recover the Mesh object with id 1
    Mesh obj = (Mesh) world.find(1);
    
    // Rotate on the Y and Z axis
    obj.postRotate(-2.0f, 0.0f, 1.0f, 1.0f);
}

It is extremely simple since the core of the loading process is represented by the line

Object3D[] objects = Loader.load("/example.m3g");

which takes care of opening the indicated file, loading it into memory and reconstructing the tree structure that represents the scene. In the scene proposed as an example there are only 3 objects, namely a camera, a light source and the Mesh of a car. Each object created in memory is inserted into the Object3D array[] as a consequence – as already mentioned – in the first element of this array the reference to the World object will be present.

world = (World) objects[0];

the only further operation to do is to change the settings of the active camera. This happens because the camera must adapt the perspective correction according to the display of the device on which the program is running.

For this, after retrieving the reference to the object of the active camera

Camera room = world.getActiveCamera();

we recalculate the aspect ratio. Remember that this value is the ratio between the width and the height of the rendering area and it is necessary because to maintain the same proportions of the rendering from display to display.

float aspectRatio = (float) getWidth() / (float) getHeight();
camera.setPerspective(60.0f, aspectRatio, 1.0f, 1000.0f );

Now the application is ready to be executed. The result is visible in Figure 1 .

Figure 1 – The m3g file loaded. The same camera and light source settings are maintained.

As usual it is a static demo, in fact the code deals only with executing an initialization procedure and drawing the world on screen at every step through the instruction

g3d.render(world);

To add some animation to the scene let’s rotate the object. It is sufficient to retrieve the reference to the Mesh object that we intend to rotate

Mesh obj = (Mesh) world.find(1);

– in this case the number 1 was assigned to the object – and apply the desired transformation to it with

obj.postRotate(-2.0f, 0.0f, 1.0f, 1.0f );

As you can see in Figure 2 the object will rotate on itself in the center of the scene.

Figure 2 – Applying a transformation to the main Mesh object

Conclusions

With this article we conclude the series dedicated to the games programming on mobile phones. Probably in the future there will be space for further in-depth studies. However I hope that the path we have followed has been useful to let you enter and take your first steps in this wonderful world that, according to our expectations, has not yet shown its real possibilities. We will see shortly a remarkable growth of the capacity of mobile devices that will bring them to a progressive “approach” – if not a true fusion – with the portable consoles.

For clarifications, advice or questions of any kind I remain at your disposal. Keep on contacting me.

Bibliography

  1. Sun Corporation, “Mobile 3D Graphics API Specification “, www.sun.com

Original article

Downloads

Leave a Reply

Your email address will not be published. Required fields are marked *