## Android 3D game tutorial – Part I

Posted: March 1, 2012 in Android Posts
Tags: , , , , ,

The first part of this series will give you a short introduction to the OpenGL terminology and the first step in your 3D programming.

The series itself will be about a 3D game called Vortex.
The tutorial will focus on 3D programming, stuff like menu or life cycle may be part of the code but will not be introduced.

Vertex
A vertex is a point in 3D space and is the building block for many objects. In OpenGL you can specify as few as two coordinates (X,Y) and as many as four (X,Y,Z,W). The w-axis is optional, the default value is set to 1.0. The z-axis is also optional, the default value is set to 0. In this series, we will use the three main coordinates X, Y, and Z, since the W is generally used as a placeholder. The plural of vertex is vertices (mainly important for non native speakers, because it may create confusion). All objects are drawn using vertices as their points, so a point will refer to a vertex.

Triangle
A triangle requires three points to be created. So in OpenGL, we use three vertices to create one.

Polygon
A polygon is an object which has at least three connected points. Therefor a triangle is also a polygon.

Primitives
A primitive is a simple shape, like a line, point, or polygon (including a triangle, obviously). A model with 50,000 vertices is just a model, whether it’s a primitive or not depends on whether it’s a single polygon or is composed of many polygons. (Thanks to nil for clarification)

We create a new project called Vortex, our activity will be named so, too.
Our activity will look familiar:

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16``` ```package com.droidnova.android.games.vortex; import android.app.Activity; import android.os.Bundle; public class Vortex extends Activity { private static final String LOG_TAG = Vortex.class.getSimpleName(); private VortexView _vortexView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); _vortexView = new VortexView(this); setContentView(_vortexView); } }```

As you see, we already added our own view. Lets take a look right on our VortexView class.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15``` ```package com.droidnova.android.games.vortex; import android.content.Context; import android.opengl.GLSurfaceView; public class VortexView extends GLSurfaceView { private static final String LOG_TAG = VortexView.class.getSimpleName(); private VortexRenderer _renderer; public VortexView(Context context) { super(context); _renderer = new VortexRenderer(); setRenderer(_renderer); } }```

As you see, we inherit GLSurfaceView because it will help us manage the drawing. The next thing you should see is our VortexRenderer class.
A renderer has the task to perform anything thats needed to draw a frame. Quote from references:

The renderer is responsible for making OpenGL calls to render a frame.

So lets take a look at this class:

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32``` ```package com.droidnova.android.games.vortex; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView; public class VortexRenderer implements GLSurfaceView.Renderer { private static final String LOG_TAG = VortexRenderer.class.getSimpleName(); private float _red = 0.9f; private float _green = 0.2f; private float _blue = 0.2f; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // Do nothing special. } @Override public void onSurfaceChanged(GL10 gl, int w, int h) { gl.glViewport(0, 0, w, h); } @Override public void onDrawFrame(GL10 gl) { // define the color we want to be displayed as the "clipping wall" gl.glClearColor(_red, _green, _blue, 1.0f); // clear the color buffer to show the ClearColor we called above... gl.glClear(GL10.GL_COLOR_BUFFER_BIT); } }```

Ok what do we have here?
First we implemented the interface GLSurfaceView.Renderer which forces us to implement 3 methods calles onSurfaceCreated(), onSurfaceChanged() and onDrawFrame().
These methods are easily to understand, the first one is called after the surface was created, the second if the surface changed, e.g. you switch from portrait to landscape and the last one anytime a drawing is requested.
From line 11 to 13 we have floats defining each color of the RGB color system.
On line 28 we define the the color of our “clipping wall” with the method glClearColor(). The “clipping wall” covers everything that is behind the distance we can see, so every object behind this “wall” is invisible. Imagine the wall as something like fog. Later we will set the distance to show how it works. At the moment it is absolutely sufficient that you know it exists.
To make our color changes visible, we have to call glClear() with the mask for the color buffer to clear the buffer and use the new color for our “clipping wall”.

To see that it is working, we will create a response to a MotionEvent which changes the color we set.
First lets create a set for the color in our VortexRenderer class:

 ```1 2 3 4 5``` ```public void setColor(float r, float g, float b) { _red = r; _green = g; _blue = b; }```

Now the method in our VortexView class to handle the MotionEvent:

 ```1 2 3 4 5 6 7 8``` ```public boolean onTouchEvent(final MotionEvent event) { queueEvent(new Runnable() { public void run() { _renderer.setColor(event.getX() / getWidth(), event.getY() / getHeight(), 1.0f); } }); return true; }```

We create a new anonymous object of Runnable where the run() method call the setColor() method of the renderer with a bit of calculation depending on the MotionEvent coordinates.

We now have a little app which uses OpenGL to change the background color
In Germany we say in this case “Mit Kanonen auf Spatzen schießen”, you would say “He breaks a fly on the wheel.”. Thats absolute correct but it is the minimalistic example of working with OpenGL and you are now prepared for more!

The last hint in this part is a documentation for OpenGL. The usability is nonexistent but at least it is a documentation.

Sources as Eclipse project: Vortex Part I

Screenshots:

# Android 3D game tutorial – Part II

The second part of this series will show you how to add a triangle and how to rotate it a bit.

The first thing we have to do is to initialize the triangle we want to display. We have to create a function named initTriangle() in our VortexRenderer class.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35``` ```// new object variables we need // a raw buffer to hold indices private ShortBuffer _indexBuffer; // a raw buffer to hold the vertices private FloatBuffer _vertexBuffer; private short[] _indicesArray = {0, 1, 2}; private int _nrOfVertices = 3; // code snipped private void initTriangle() { // float has 4 bytes ByteBuffer vbb = ByteBuffer.allocateDirect(_nrOfVertices * 3 * 4); vbb.order(ByteOrder.nativeOrder()); _vertexBuffer = vbb.asFloatBuffer(); // short has 2 bytes ByteBuffer ibb = ByteBuffer.allocateDirect(_nrOfVertices * 2); ibb.order(ByteOrder.nativeOrder()); _indexBuffer = ibb.asShortBuffer(); float[] coords = { -0.5f, -0.5f, 0f, // (x1, y1, z1) 0.5f, -0.5f, 0f, // (x2, y2, z2) 0f, 0.5f, 0f // (x3, y3, z3) }; _vertexBuffer.put(coords); _indexBuffer.put(_indicesArray); _vertexBuffer.position(0); _indexBuffer.position(0); }```

Lets start with the new object variables. The _vertexBuffer will be filled with the coordinates for our triangle. The _indexBuffer stores the indices. The variable _nrOfVertices defines how many vertices are required. For a triangle we have three vertices.
The method itself allocate the needed memory for both buffer (line 14 – 22). Than we define some coordinates (line 24 – 28) and the comments behind each row explains you, how you can read the coordinates.
In line 30 we fill the _vertexBuffer with the coordinates stored on the coords array. The same with the indices array and the _indexBuffer on line 31. Finally we set both buffer to position 0.

To prevent the initialization of the triangle for every frame, we just do it once in a function that will be called before onDrawFrame(). On choice could be the method onSurfaceCreated().

 ```1 2 3 4 5 6``` ```@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // preparation gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); initTriangle(); }```

glEnableClientState() set OpenGL to use vertex arrays to draw. Thats important to enable because otherwise OpenGL don’t know how to handle our data. Than we will initialize our triangle.

Why do we have to work with different buffers? Lets take a look at the new onDrawFrame() method where we have to add some new OpenGL calls.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17``` ```@Override public void onDrawFrame(GL10 gl) { // define the color we want to be displayed as the "clipping wall" gl.glClearColor(_red, _green, _blue, 1.0f); // clear the color buffer to show the ClearColor we called above... gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // set the color of our element gl.glColor4f(0.5f, 0f, 0f, 0.5f); // define the vertices we want to draw gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer); // finally draw the vertices gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer); }```

Ok now step for step:
glClearColor() and glClear() should be known by the first part of the series.
On line 10 we set the color for our triangle to a darker red using glColor4f(red, green, blue, alpha).
On line 13 we initialize the vertex pointer using glVertexPointer(). The first parameter is for the size, also known as dimension of our vertices. Are we using just x and y or also z? We use all three dimensions. The second parameter, GL_FLOAT, defines the data type used in the buffer. The third parameter is 0 because our coordinates are tightly packed in the array, no offset used. And finally the fourth parameter is the buffer in which we have our vertices stored.
The last call, glDrawElements(), will draw the elements. First parameter defines what kind of primitives have to rendered. The second element defines the number of elements and the third parameter the type of the values used for the indices. The last one is the index buffer which will be used to render the vertices.

When you finally test the application, you will see a static triangle in the middle of your screen. The change of the color of your background should still work if you touch the screen.

Lets add some rotation to the triangle. The following code must be implemented in our VortexRenderer class.

 ```1 2 3 4 5``` ```private float _angle; public void setAngle(float angle) { _angle = angle; }```

The glRotatef() method will be called in our onDrawFrame() right above glColor4f().

 ```1 2 3 4 5 6 7 8``` ```@Override public void onDrawFrame(GL10 gl) { // set rotation gl.glRotatef(_angle, 0f, 1f, 0f); gl.glColor4f(0.5f, 0f, 0f, 0.5f); // code snipped }```

We rotate at the moment just around the y-axis. If you want to change this, simply change the 0f of the glRotatef() method call. The value of this parameter are for a vector which represent the axis on which the triangle will rotate.

To make this work, we have to add a call to the onTouchEvent() method in our VortexView class.

 ```1 2 3 4 5 6 7 8 9``` ```public boolean onTouchEvent(final MotionEvent event) { queueEvent(new Runnable() { public void run() { _renderer.setColor(event.getX() / getWidth(), event.getY() / getHeight(), 1.0f); _renderer.setAngle(event.getX() / 10); } }); return true; }```

The division through 10 is to reduce the speed of angle changing.

Now compile and run the application. If you touch the screen at the most left side, you should see the triangle rotate slightly. If you move your finger to the right, the speed of the rotation should increase dramatically.

Source as Eclipse project: Vortex Part II

# Android 3D game tutorial – Part III

The third part of this series will show you how to stop the rotation of the triangle and that the rotation really just work on the triangle and not the “camera”.

We want to have more control over the rotation. To get that, we reset the matrix on every call of the onDrawFrame() method. This will reset the angle of our triangle so it always it stays rotated at the given angle on initialization.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14``` ```@Override public void onDrawFrame(GL10 gl) { // define the color we want to be displayed as the "clipping wall" gl.glClearColor(_red, _green, _blue, 1.0f); // reset the matrix - good to fix the rotation to a static angle gl.glLoadIdentity(); // clear the color buffer and the depth buffer to show the ClearColor // we called above... gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // code snipped }```

In the VortexView class you should remove the division by 10 to be able to rotate it more than a bit.

 `1` `_renderer.setAngle(event.getX());`

If you try this, you will see the rotation will only goes the way our touch motion goes. If you don’t move the finger over the screen, it doesn’t change the rotation.

The next thing: Do we rotate the triangle or the view/camera?
To check that, the easiest way is to create a second triangle which will not rotate.
The fastest but also dirtiest way to do this is to copy & paste the initTriangle() method to initStaticTriangle(), copy & paste both buffers and finally copy & paste and modify the last 4 lines of the onDrawFrame() method.
Don’t forget to change the color of the second triangle and reduce the coordinates of the second triangle, so we can see both. I changed the every 0.5f coordinate to 0.4f.
Here the full class:

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130``` ```package com.droidnova.android.games.vortex; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView; public class VortexRenderer implements GLSurfaceView.Renderer { private static final String LOG_TAG = VortexRenderer.class.getSimpleName(); private float _red = 0f; private float _green = 0f; private float _blue = 0f; // a raw buffer to hold indices allowing a reuse of points. private ShortBuffer _indexBuffer; private ShortBuffer _indexBufferStatic; // a raw buffer to hold the vertices private FloatBuffer _vertexBuffer; private FloatBuffer _vertexBufferStatic; private short[] _indicesArray = {0, 1, 2}; private int _nrOfVertices = 3; private float _angle; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // preparation gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); initTriangle(); initStaticTriangle(); } @Override public void onSurfaceChanged(GL10 gl, int w, int h) { gl.glViewport(0, 0, w, h); } public void setAngle(float angle) { _angle = angle; } @Override public void onDrawFrame(GL10 gl) { // define the color we want to be displayed as the "clipping wall" gl.glClearColor(_red, _green, _blue, 1.0f); // reset the matrix - good to fix the rotation to a static angle gl.glLoadIdentity(); // clear the color buffer to show the ClearColor we called above... gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // draw the static triangle gl.glColor4f(0f, 0.5f, 0f, 0.5f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBufferStatic); gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBufferStatic); // set rotation for the non-static triangle gl.glRotatef(_angle, 0f, 1f, 0f); gl.glColor4f(0.5f, 0f, 0f, 0.5f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer); } private void initTriangle() { // float has 4 bytes ByteBuffer vbb = ByteBuffer.allocateDirect(_nrOfVertices * 3 * 4); vbb.order(ByteOrder.nativeOrder()); _vertexBuffer = vbb.asFloatBuffer(); // short has 4 bytes ByteBuffer ibb = ByteBuffer.allocateDirect(_nrOfVertices * 2); ibb.order(ByteOrder.nativeOrder()); _indexBuffer = ibb.asShortBuffer(); float[] coords = { -0.5f, -0.5f, 0f, // (x1, y1, z1) 0.5f, -0.5f, 0f, // (x2, y2, z2) 0f, 0.5f, 0f // (x3, y3, z3) }; _vertexBuffer.put(coords); _indexBuffer.put(_indicesArray); _vertexBuffer.position(0); _indexBuffer.position(0); } private void initStaticTriangle() { // float has 4 bytes ByteBuffer vbb = ByteBuffer.allocateDirect(_nrOfVertices * 3 * 4); vbb.order(ByteOrder.nativeOrder()); _vertexBufferStatic = vbb.asFloatBuffer(); // short has 4 bytes ByteBuffer ibb = ByteBuffer.allocateDirect(_nrOfVertices * 2); ibb.order(ByteOrder.nativeOrder()); _indexBufferStatic = ibb.asShortBuffer(); float[] coords = { -0.4f, -0.4f, 0f, // (x1, y1, z1) 0.4f, -0.4f, 0f, // (x2, y2, z2) 0f, 0.4f, 0f // (x3, y3, z3) }; _vertexBufferStatic.put(coords); _indexBufferStatic.put(_indicesArray); _vertexBufferStatic.position(0); _indexBufferStatic.position(0); } public void setColor(float r, float g, float b) { _red = r; _green = g; _blue = b; } }```

If you try this, you will see only one triangle rotate. If you want to rotate both, simply change the place where the rotation will be called to right above the comment “draw the static triangle”.

Compile and run the application and you will see the green triangle rotate while the red triangle stays at the same angle.
That should be prove enough for the answer, that we really only rotate the triangle and not the whole scene.

Source as Eclipse project: Vortex Part III

# Android 3D game tutorial – Part IV

The fourth part of this series will show you how to add some colors to you triangle.

In the last part we created a second static triangle to prove the rotation of the triangle and not the entire scene. We will now get rid of this static triangle by removing the function initStaticTriangle(), removing both buffers, _indexBufferStatic and _vertexBufferStatic, used for it. We also have to remove the last 4 lines of code of our onDrawFrame() where we initialized the static triangle.
The “new” onDrawFrame() method should now look like this:

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18``` ```@Override public void onDrawFrame(GL10 gl) { // define the color we want to be displayed as the "clipping wall" gl.glClearColor(_red, _green, _blue, 1.0f); // reset the matrix - good to fix the rotation to a static angle gl.glLoadIdentity(); // clear the color buffer to show the ClearColor we called above... gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // set rotation for the non-static triangle gl.glRotatef(_angle, 0f, 1f, 0f); gl.glColor4f(0.5f, 0f, 0f, 0.5f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer); }```

Now we can create a new buffer which will keep the color informations. The _colorBuffer will be a object variable, but w need to define the colors and fill the buffer where we initialize the other buffers, in our initTriangle() method.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42``` ```// code snipped // a raw buffer to hold the colors private FloatBuffer _colorBuffer; // code snipped private void initTriangle() { // float has 4 bytes ByteBuffer vbb = ByteBuffer.allocateDirect(_nrOfVertices * 3 * 4); vbb.order(ByteOrder.nativeOrder()); _vertexBuffer = vbb.asFloatBuffer(); // short has 4 bytes ByteBuffer ibb = ByteBuffer.allocateDirect(_nrOfVertices * 2); ibb.order(ByteOrder.nativeOrder()); _indexBuffer = ibb.asShortBuffer(); // float has 4 bytes, 4 colors (RGBA) * number of vertices * 4 bytes ByteBuffer cbb = ByteBuffer.allocateDirect(4 * _nrOfVertices * 4); cbb.order(ByteOrder.nativeOrder()); _colorBuffer = cbb.asFloatBuffer(); float[] coords = { -0.5f, -0.5f, 0f, // (x1, y1, z1) 0.5f, -0.5f, 0f, // (x2, y2, z2) 0.5f, 0.5f, 0f // (x3, y3, z3) }; float[] colors = { 1f, 0f, 0f, 1f, // point 1 0f, 1f, 0f, 1f, // point 2 0f, 0f, 1f, 1f, // point 3 }; _vertexBuffer.put(coords); _indexBuffer.put(_indicesArray); _colorBuffer.put(colors); _vertexBuffer.position(0); _indexBuffer.position(0); _colorBuffer.position(0); }```

We create the object variable _colorBuffer of type FloatBuffer (line 4). In the method initTriangle() we allocate enough memory for the new color buffer (line 19-21). The we create a float array (line 23-27) with 4 values for each vertex. The structure is for RGBA (red, green, blue, alpha) so the first vertex will have the color red, the second green and the third is blue. The last two steps are equal to the _vertexBuffer. We put the color array into the buffer and set the position of the buffer to 0.

With the preparation done, we can start to tell OpenGL ES to use color arrays too. This will be done by the method glEnableClientState() and, similar to the vertexBuffer, by calling glColorPointer().

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19``` ```@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // preparation gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); initTriangle(); } // code snipped @Override public void onDrawFrame(GL10 gl) { // code snipped // gl.glColor4f(0.5f, 0f, 0f, 0.5f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer); gl.glColorPointer(4, GL10.GL_FLOAT, 0, _colorBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer); }```

On line 5 we enabled the color mode. On line 17 we set the color pointer. The parameter 4 stands for the RGBA (which are 4 values) and the rest should be familiar.
As you may notice, we commented the line 15, because when we use the color mode, we don’t need glColor4f. It will be overridden, so we can comment it out or just delete it.

Source as Eclipse project: Vortex Part IV

# Android 3D game tutorial – Part V

The fifth part of this series will show you how you can create your first full 3d object. In this case a 4 sided pyramid.

Some preparation will be needed to make our future development much easier.
We have to be more dynamic in calculating our buffers and creating arrays with the correct size.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39``` ```private int _nrOfVertices = 0; private void initTriangle() { float[] coords = { // coodinates }; _nrOfVertices = coords.length; float[] colors = { // colors }; short[] indices = new short[] { // indices }; // float has 4 bytes, coordinate * 4 bytes ByteBuffer vbb = ByteBuffer.allocateDirect(coords.length * 4); vbb.order(ByteOrder.nativeOrder()); _vertexBuffer = vbb.asFloatBuffer(); // short has 2 bytes, indices * 2 bytes ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2); ibb.order(ByteOrder.nativeOrder()); _indexBuffer = ibb.asShortBuffer(); // float has 4 bytes, colors (RGBA) * 4 bytes ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4); cbb.order(ByteOrder.nativeOrder()); _colorBuffer = cbb.asFloatBuffer(); _vertexBuffer.put(coords); _indexBuffer.put(indices); _colorBuffer.put(colors); _vertexBuffer.position(0); _indexBuffer.position(0); _colorBuffer.position(0); }```

To be more dynamically, we have to change some variables and the order we do something. Lets take a closer look:
On line 1 you see we initialized our _nrOfVertices with 0 because we will determine it depending on the size of our coordinates array on line 7.
We also changed the object variable known as _indicesArray to a local variable called indices and initialized on line 13.
The buffer creation is moved below the arrays for coordinates, colors and indices because the buffer size depends directly on the arrays. So please take a look at lines 17-18, 22-23, 27-28. In the comments I explain the math.
The main advantage is, that we can create more vertices without manually recalculate the number of vertices, sizes of arrays or buffers.

Next step: You have to understand how OpenGL draws and determines what we see.
A great disadvantage of OpenGL ES compared to OpenGL is the lack of more than just triangles as primitive types. We don’t have polygons, so every object we want to create have to be made of triangles.
As this is harder to explain for me (as a non native speaker), I want to quote from a blog post of an IPhone developer and also recommend his OpenGL ES series, too.

There are a few more things you need to know about triangles, however. In OpenGL, there is a concept known as winding, which just means that the order in which the vertices are drawn matters. Unlike objects in the real world, polygons in OpenGL do not generally have two sides to them. They have one side, which is considered the front face, and a triangle can only be seen if its front face if facing the viewer. While it is possible to configure OpenGL to treat polygons as two-sided, by default, triangles have only one visible side. By knowing which is the front or visible side of the polygon, OpenGL is able to do half the amount of calculations per polygon that it would have to do if both sides were visible.

Although there are times when a polygon will stand on its own, and you might very well want the back drawn, usually a triangle is part of a larger object, and one side of the polygon will be facing the inside of the object and will never be seen. The side that isn’t drawn is called a backface, and OpenGL determines which is the front face to be drawn and which is the backface by looking at the drawing order of the vertices. The front face is the one that would be drawn by following the vertices in counter-clockwise order (by default, it can be changed). Since OpenGL can determine easily which triangles are visible to the user, it can use a process called Backface Culling to avoid doing work for polygons that aren’t facing the front of the viewport and, therefore, can’t be seen. We’ll discuss the viewport in the next posting, but you can think of it as the virtual camera, or virtual window looking into the OpenGL world.

In the illustration above, the cyan triangle on the left is a backface and won’t be drawn because the order that the vertices would be drawn in relation to the viewer is clockwise. On the other hand, the triangle on the right is a frontface that will be drawn because the order of the vertices is counter-clockwise in relation to the viewer.

As we now want to create a colorful pyramid, we first disable the glClearColor() depending on our touch event. So we can remove the variables _red, _green, _blue and the method setColor().
We also want to change the navigation, so we will split the rotation into x and y axis.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43``` ```public class VortexRenderer implements GLSurfaceView.Renderer { private static final String LOG_TAG = VortexRenderer.class.getSimpleName(); // a raw buffer to hold indices allowing a reuse of points. private ShortBuffer _indexBuffer; // a raw buffer to hold the vertices private FloatBuffer _vertexBuffer; // a raw buffer to hold the colors private FloatBuffer _colorBuffer; private int _nrOfVertices = 0; private float _xAngle; private float _yAngle; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // code snipped } @Override public void onSurfaceChanged(GL10 gl, int w, int h) { gl.glViewport(0, 0, w, h); } public void setXAngle(float angle) { _xAngle = angle; } public float getXAngle() { return _xAngle; } public void setYAngle(float angle) { _yAngle = angle; } public float getYAngle() { return _yAngle; } // code snipped```

To be sure that you have the same object variables, I posted the top of the class, too. As you can see we have now two float variables for our angle, _xAngle and _yAngle (line 15-16) and their setter and getter (line 28-42).
Now lets implement the logic to calculate the angle which depends on our touch event. To do this, we have to change our VortexView class a bit.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26``` ```// code snipped private float _x = 0; private float _y = 0; // code snipped public boolean onTouchEvent(final MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { _x = event.getX(); _y = event.getY(); } if (event.getAction() == MotionEvent.ACTION_MOVE) { final float xdiff = (_x - event.getX()); final float ydiff = (_y - event.getY()); queueEvent(new Runnable() { public void run() { _renderer.setXAngle(_renderer.getXAngle() + ydiff); _renderer.setYAngle(_renderer.getYAngle() + xdiff); } }); _x = event.getX(); _y = event.getY(); } return true; }```

On line 3 and 4 we have two variables for our x and y values.
We set them on the ACTION_DOWN event and while we move, we calculate the difference between the old values and the current values given by the MotionEvent. Calculating the difference and adding them to the already applied angle of our object. Don’t be disturbed by the ydiff added to the x-angle and vice-versa (line 18-19). As you can imagine, if we want to spin the object while we move on the x-axis, we have to rotate it around the y-axis. Same thing on y-axis and up and down movement.
If we move our finger to the left or up, the value of xdiff/ydiff will be negative and the rotation will be backwards. So we can easily rotate on 2 axis.

Now to the very interesting part: the pyramid.
As we quote above, the winding requires some settings. Some might be default settings, but we define them anyway to be sure.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15``` ```@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // preparation // enable the differentiation of which side may be visible gl.glEnable(GL10.GL_CULL_FACE); // which is the front? the one which is drawn counter clockwise gl.glFrontFace(GL10.GL_CCW); // which one should NOT be drawn gl.glCullFace(GL10.GL_BACK); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); initTriangle(); }```

On line 5 we enable the culling face so we always the just one side. On line 7 we define which order defines the front. It is set to GL_CCW which means counter clockwise. On line 9 we finally define which side should be visible as the culling face. We set it to GL10.GL_BACK to just show the front. It might be confusing, but check what happens if you use GL_FRONT_AND_BACK… you will see nothing.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20``` ```@Override public void onDrawFrame(GL10 gl) { // define the color we want to be displayed as the "clipping wall" gl.glClearColor(0f, 0f, 0f, 1.0f); // reset the matrix - good to fix the rotation to a static angle gl.glLoadIdentity(); // clear the color buffer to show the ClearColor we called above... gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // set rotation gl.glRotatef(_xAngle, 1f, 0f, 0f); gl.glRotatef(_yAngle, 0f, 1f, 0f); //gl.glColor4f(0.5f, 0f, 0f, 0.5f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer); gl.glColorPointer(4, GL10.GL_FLOAT, 0, _colorBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer); }```

On line 4 you see that our background color will be black since we remove the dynamic color mentioned above. On line 13 and 14 you see the rotation for each angle. The rest is the same you know from the parts before.

The last thing you have to change are the arrays for color, coordinates and indices in the method initTriangle(). Our pyramid should look like that:

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25``` ```private void initTriangle() { float[] coords = { -0.5f, -0.5f, 0.5f, // 0 0.5f, -0.5f, 0.5f, // 1 0f, -0.5f, -0.5f, // 2 0f, 0.5f, 0f, // 3 }; _nrOfVertices = coords.length; float[] colors = { 1f, 0f, 0f, 1f, // point 0 red 0f, 1f, 0f, 1f, // point 1 green 0f, 0f, 1f, 1f, // point 2 blue 1f, 1f, 1f, 1f, // point 3 white }; short[] indices = new short[] { 0, 1, 3, // rwg 0, 2, 1, // rbg 0, 3, 2, // rbw 1, 2, 3, // bwg }; //code snipped }```

As you see in the image, our pyramid has 4 corners. Each corner has his own coordinates so we have 4 vertices to define. Done on line 2-7.
Each vertex has his own color, defines on line 10-15.
The main part is done with the array of our indices which define the triangle we want to have. Remember the winding, which means the triangle 0, 1, 3 doesn’t result in the triangle 0, 3, 1.
Each index points the the vertex defined in the coords array. Just check the comments to see how the references work.

Compile and play with it, check what happens if you change the order of the indices or if your are able to see your pyramid if you change GL_CCW to GL_CW.

Source as Eclipse project: Vortex Part V

# Android 3D game tutorial – Part VI

The sixth part of this series will show you how you create the correct perspective because 3D is nothing without the correct perspective.

Before we start we should discuss the two possible “views” OpenGL offers: orthographic and perspective.

Orthographic (non vanish point projection)
The orthographic view is a view that makes it impossible to see if a object is right in front of us or far away. Why? Because it doesn’t shrink in the distance. So if you will draw an object with the a specific size near the viewport and another object with the same size far behind the first one but a bit to one side (to be sure that the first object doesn’t stand in the view), you won’t be able to say which element is the first one. Because both will have the same size independent of their distance. They simply don’t shrink with the distance.

Perspective (vanish point projection)
The perspective view is the view we know from our eyes. An example: A tall guy in front of you is, of course, tall. If this guy is 100 meter away, he is not as tall as your thumb. He seems to shrink in the distance but we know of course, he stays tall. That effect is called perspective. For our example with the two objects, the second one will be much smaller so we can say in an instance, that this is far away and the other one is right in front of us.

Because my example might confuse you, I recommend again a blog post of iPhone development: OpenGL ES From the Ground Up, Part 3: Viewports in Perspective which uses railroad tracks as an example.

The first view we want to create is the one using the orthographic. The view setup is something we usually do just once or, in case we change the rotation, we do it every time the surface is created. Thats the reason why we should change a bit. So some code we have in our onDrawFrame() method will be moved to the onSurfaceCreated() method. There it should only be executed if we start the application or change the rotation.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38``` ```@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // preparation // define the color we want to be displayed as the "clipping wall" gl.glClearColor(0f, 0f, 0f, 1.0f); // enable the differentiation of which side may be visible gl.glEnable(GL10.GL_CULL_FACE); // which is the front? the one which is drawn counter clockwise gl.glFrontFace(GL10.GL_CCW); // which one should NOT be drawn gl.glCullFace(GL10.GL_BACK); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); initTriangle(); } // code snipped @Override public void onDrawFrame(GL10 gl) { gl.glLoadIdentity(); // clear the color buffer and the depth buffer to show the ClearColor // we called above... gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // set rotation gl.glRotatef(_xAngle, 1f, 0f, 0f); gl.glRotatef(_yAngle, 0f, 1f, 0f); //gl.glColor4f(0.5f, 0f, 0f, 0.5f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer); gl.glColorPointer(4, GL10.GL_FLOAT, 0, _colorBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer); }```

You see that we didn’t move the glClear() and the glLoadIdentity() method from onDrawFrame() to onSurfaceCreated(). The reason is easy: they should be called on every frame.

Because we need the screen size to calculate the ratio of our screen we introduce two object variables called _width and _height. An we need to set them in the method onSurfaceChanged() which will be called on every rotation change.

 ```1 2 3 4 5 6 7 8 9``` ```private float _width = 320f; private float _height = 480f; @Override public void onSurfaceChanged(GL10 gl, int w, int h) { _width = w; _height = h; gl.glViewport(0, 0, w, h); }```

Now we have everything we need to start with the viewport. We need to change the onSurfaceCreated() method.

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25``` ```@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glMatrixMode(GL10.GL_PROJECTION); float ratio = _width / _height; // orthographic: gl.glOrthof(-1, 1, -1 / ratio, 1 / ratio, 0.01f, 100.0f); gl.glViewport(0, 0, (int) _width, (int) _height); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glEnable(GL10.GL_DEPTH_TEST); // define the color we want to be displayed as the "clipping wall" gl.glClearColor(0f, 0f, 0f, 1.0f); // enable the differentiation of which side may be visible gl.glEnable(GL10.GL_CULL_FACE); // which is the front? the one which is drawn counter clockwise gl.glFrontFace(GL10.GL_CCW); // which one should NOT be drawn gl.glCullFace(GL10.GL_BACK); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); initTriangle(); }```

Wow this is a lot of new code! Don’t worry we will go step by step.
On line 3 we see the glMatrixMode() with GL10.GL_PROJECTION as parameter and on line 8 we see the method again but with the GL10.GL_MODELVIEW as parameter. The reasons are in the lines between line 3 and line 8. In the lines 4 till 7 we set up our viewport, so we set up our projection. On line 9 till 17 we set up our model environment. In this context both calls with different parameter should be understandable. Tip: As always simply try to get rid of some code lines and see the result. Thats the best way to understand what line of code is responsible for what.
On line 4 we calculate our screen ratio we need for the next line. On this line (line 6) we set our viewport to make a orthographic view. The parameter are for the border in the order of left, right, bottom, top, zNear, zFar.
On line 7 we set the viewport. We know this method because we already used it in onSurfaceChanged().
On line 8 we switch the MatrixMode to GL10.GL_MODELVIEW which set OpenGL to accept some calls which change the way models should be drawn.
On line 9 we call glEnable() with the parameter GL10.GL_DEPTH_TEST. That enabled that OpenGL ES check which z-order the objects have. If we don’t enable that, we will see the last drawn object always in front of all other objects. That means even if this object should be hidden by another much nearer and bigger object, we will see the first one in front of it.
The other lines of code we already know from the previous parts of this series.

The perspective view is nearly the same but instead of calling glOrthof() we will call glFrustumf().
The parameters for glFrustumf() will be a bit different from the parameters we used for glOrthof(). The reason therefor is, that we didn’t shrink the objects but we the defined frustum will be funnel-formed. Look at these pictures to see the difference between glOrthof() and glFrustumf():

Orthographic:

Perspective:

Back to the code:

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29``` ```@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { Log.i(LOG_TAG, "onSurfaceCreated()"); gl.glMatrixMode(GL10.GL_PROJECTION); float size = .01f * (float) Math.tan(Math.toRadians(45.0) / 2); float ratio = _width / _height; // perspective: gl.glFrustumf(-size, size, -size / ratio, size / ratio, 0.01f, 100.0f); // orthographic: //gl.glOrthof(-1, 1, -1 / ratio, 1 / ratio, 0.01f, 100.0f); gl.glViewport(0, 0, (int) _width, (int) _height); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glEnable(GL10.GL_DEPTH_TEST); // define the color we want to be displayed as the "clipping wall" gl.glClearColor(0f, 0f, 0f, 1.0f); // enable the differentiation of which side may be visible gl.glEnable(GL10.GL_CULL_FACE); // which is the front? the one which is drawn counter clockwise gl.glFrontFace(GL10.GL_CCW); // which one should NOT be drawn gl.glCullFace(GL10.GL_BACK); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); initTriangle(); }```

Information: The calculation of our variable size (on line 5) is something
we should take on faith, we will see why it works when we talk about matrices.
On line 8 we now have glFrustumf() instead of glOrthof(). Thats all we have to change between orthographic view and perspective view.

But hey, with just one object we won’t see any changes. Ok lets change the method onDrawFrame().

 ```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17``` ```@Override public void onDrawFrame(GL10 gl) { // clear the color buffer and the depth buffer gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer); gl.glColorPointer(4, GL10.GL_FLOAT, 0, _colorBuffer); for (int i = 1; i <= 10; i++) { gl.glLoadIdentity(); gl.glTranslatef(0.0f, -1f, -1.0f + -1.5f * i); // set rotation gl.glRotatef(_xAngle, 1f, 0f, 0f); gl.glRotatef(_yAngle, 0f, 1f, 0f); gl.glDrawElements(GL10.GL_TRIANGLES, _nrOfVertices, GL10.GL_UNSIGNED_SHORT, _indexBuffer); } }```

Ok, what have changed?
On line 3 we have now modified parameter to be sure that the depth buffer will be cleared to.
On line 9 we started a loop to create 10 objects.
On line 10 we see the missing glLoadIdentity(). It is now located here to reset the matrix. Thats have to be done because we will use glRotatef() and glTranslatef() to modify our objects. But to be sure that we only modify the object we are currently looping over, we call glLoadIdentity(). So we “reset” every glTranslatef() and glRotatef() call we made for the previous object.
On line 11 we see the new glTranslatef() method which will move our object to another location. In our case we don’t change the location on the x axis, but we change -1.0f on the y axis which means it will be near the bottom of our screen. The last calculation you see is simply to modify the position on the z axis, which means the depth, of your object. The first object will be on -2.5f, the second on -4.0f and so on. So we should see 10 objects lined up to the middle of our screen.

If you compile that using the glFrustumf() call, you should see that:

If you switch the lines glFrustumf() and glOrthof() you should see that:

Hey wait, why do we not see more than one object? Because in orthographic we don’t have any kind of perspective. So every single object has the same size and doesn’t shrink so it won’t follow any vanish point and without that, each object is exactly behind the first one.

won’t follow any vanish point and without that, each object is exactly behind the first one.

Full source: Vortex Part VI

1. Willie Dearing says:

Some genuinely fantastic content on this web site , thanks for contribution.

Like

2. elektroninen tupakka suomesta says:

Very interesting topic , thankyou for posting . “Challenge is a dragon with a gift in its mouthTame the dragon and the gift is yours.” by Noela Evans.

Like

3. certified nursing assistant exam says:

I have been browsing on-line greater than 3 hours these days, yet I by no means found any interesting article like yours. It’s lovely worth enough for me. In my opinion, if all webmasters and bloggers made just right content as you probably did, the web will probably be much more helpful than ever before.

Like

4. Ha Wolfrum says:

I like your writing style really loving this website. “Men do less than they ought, unless they do all that they can.” by Thomas Carlyle.

Like

5. Santana Ritchko says:

As I web-site possessor I believe the content material here is rattling fantastic , appreciate it for your hard work. You should keep it up forever! Good Luck.

Like

6. automotive media blasting san diego says:

Fantastic website you have here but I was wondering if you knew of any message boards that cover the same topics talked about here? I’d really love to be a part of online community where I can get suggestions from other knowledgeable people that share the same interest. If you have any suggestions, please let me know. Bless you!

Like

Oh my goodness! an remarkable write-up dude. Thank you However I am experiencing problem with ur rss . Donâ€™t know why Unable to subscribe to it. Is there anyone finding identical rss issue? Anyone who knows kindly respond. Thnkx

Like

I enjoy your writing style truly enjoying this web site. “Finance is the art of passing currency from hand to hand until it finally disappears.” by Robert W. Sarnoff.

Like

9. cheap vacation ideas says:

I have been browsing on-line greater than 3 hours today, yet I never found any fascinating article like yours. It is pretty price enough for me. Personally, if all website owners and bloggers made good content as you did, the internet can be much more useful than ever before.

Like

10. canadian cancer society lottery results says:

Hi there just wanted to give you a quick heads up and let you know a few of the images aren’t loading correctly. I’m not sure why but I think its a linking issue. I’ve tried it in two different web browsers and both show the same outcome.

Like

11. seoul backpack with laptop protection says:

I was looking through some of your content on this internet site and I think this internet site is real informative ! Keep on posting .

Like

12. kore dizileri izle says:

i cant get how you’ll be able to reveal like this incredible posts admin considerably thanks

Like

13. Fawn Maulin says:

Very interesting topic, appreciate it for putting up.

Like

14. speed dating spokane says:

Hello there! I know this is kinda off topic nevertheless I’d figured I’d ask. Would you be interested in exchanging links or maybe guest writing a blog article or vice-versa? My blog discusses a lot of the same topics as yours and I believe we could greatly benefit from each other. If you might be interested feel free to send me an e-mail. I look forward to hearing from you! Terrific blog by the way!

Like

15. accelerated nursing programs says:

wonderful issues altogether, you just won a brand new reader. What might you suggest about your publish that you simply made a few days ago? Any certain?

Like

Very interesting info!Perfect just what I was looking for!

Like

Wohh exactly what I was searching for, regards for posting.

Like

18. mario games online free says:

Keep up the excellent work, I read few content on this web site and I conceive that your web site is rattling interesting and has got lots of great info.

Like

19. Floy says:

I like this web site very much so much good info.

Like

20. forex auto money says:

F*ckin’ tremendous things here. I’m very glad to see your post. Thank you so much and i’m taking a look forward to contact you. Will you please drop me a mail?

Like

21. four micro onde says:

I have been absent for a while, but now I remember why I used to love this blog. Thank you, I’ll try and check back more often. How frequently you update your website?

Like

You’ve mentioned very interesting details ! ps nice internet site .

Like

23. Abbey says:

Wohh precisely what I was searching for, appreciate it for putting up.

Like

24. Eva Angelina says:

thank you for all your efforts that you have put in this. Very interesting info. “I’ve never known any trouble that an hour’s reading didn’t assuage.” by Charles De Secondat.

Like

25. roulette games for sale says:

Real nice design and wonderful content material, very little else we want :D.

Like

26. blackjack 2 case w/ skulls says:

I like this site it’s a master piece! Glad I detected this on google.

Like