## Colored 3D Cube

Tutorials concerning the OpenGL® ES cross-platform API for full-function 2D and 3D graphics on the Google-Android platform.

### Colored 3D Cube

Colored 3D Cube

What is this: This tutorial shows how to create colored 3D Objects using the OpenGL® ES cross-platform API.

What you learn: You will learn how easy it is, to create a Colored 3D Cube, using OpenGL® ES.

Problems/Questions: post here

Difficulty: 1.5 of 5

What it will look like:

Introduction:
Lets quote wikipedia first:
OpenGL ES (OpenGL for Embedded Systems) is a subset of the OpenGL 3D graphics API designed for embedded devices such as mobile phones, PDAs, and video game consoles. It is defined and promoted by the Khronos Group, a graphics hardware and software industry consortium interested in open APIs for graphics and multimedia.

Description:

What we will do is, create a custom view and using OpenGL ES in it to draw a colored cube.
The Main steps are:
1. Setup the view and create a cube
(1.1. Start/Stop the animation if we are (not) viewing it)
2. Do some trigonometry (rotation)
3. Make the Cube paint itself

Most interesting:
What the heck do those values in the Cube-Constructor mean...
Using java Syntax Highlighting
1.         int one = 0x10000;
2.         /* Every vertex got 3 values, for
3.          * x / y / z position in the kartesian space.
4.          */
5.         int vertices[] = {
6.                -one, -one, -one, // Vertex Zero
7.                 one, -one, -one, // Vertex Two
8.                 one,  one, -one, // Vertex ...
9.                -one,  one, -one,
10.                -one, -one,  one,
11.                 one, -one,  one,
12.                 one,  one,  one,
13.                -one,  one,  one, // Vertex Seven
14.             };
Parsed in 0.012 seconds, using GeSHi 1.0.8.4

That is pretty easy, each row stands for a single vertex, consisting of three values (x,y,z) which simply result in a point in the cartesian space.
So if you think of each codeline as one vertex you get something like this:
(Note: We only created the vertices, not the edges. I just added them, that the cube-structure becomes better visible.)
(Note2: You will see that the blue coordinate-system is located right in the middle of the cube)

Using java Syntax Highlighting
1.         /* Every vertex has got its own color, described by 4 values
2.          * R(ed)
3.          * G(green)
4.          * B(blue)
5.          * A(lpha) <-- Opticacy
6.          */
7.         int colors[] = {
8.                   0,    0,    0,  one,
9.                 one,    0,    0,  one,
10.                 one,  one,    0,  one,
11.                   0,  one,    0,  one,
12.                   0,    0,  one,  one,
13.                 one,    0,  one,  one,
14.                 one,  one,  one,  one,
15.                   0,  one,  one,  one,
16.             };
Parsed in 0.011 seconds, using GeSHi 1.0.8.4

In this code-block the color of all the 8 vertices are described, as '4' each: R(ed) G(reen) B(lue) A(lpha). (Alpha means Opticacy)
OpenGL SE will create the color-flows automatically!

Using java Syntax Highlighting
1.         /* The last thing is that we need to describe some Triangles.
2.          * A triangle got 3 vertices.
3.                  * The confusing thing is, that it is important in which order
4.                  * the vertices of each triangle are described.
5.                  * So describing a triangle through the vertices: "0, 4, 5"
6.                  * will not result in the same triangle as: "0, 5, 4"
7.                  * You probably ask: Why the hell isn't that the same ???
8.                  * The reason for that is the call of: "gl.glFrontFace(gl.GL_CW);"
9.                  * which means, that we have to describe the "visible" side of the
10.                  * triangles by naming its vertices in a ClockWise order!
11.                  * From the other side, the triangle will be 100% lookthru!
12.                  * You can create a kind of magic mirror with that <img src="http://www.anddev.org/images/smilies/wink.png" alt=";)" title="Wink" />.
13.          */
14.         byte indices[] = {
15.                 0, 4, 5,
16.                 0, 5, 1,
17.                 1, 5, 6,
18.                 1, 6, 2,
19.                 2, 6, 7,
20.                 2, 7, 3,
21.                 3, 7, 4,
22.                 3, 4, 0,
23.                 4, 7, 6,
24.                 4, 6, 5,
25.                 3, 0, 1,
26.                 3, 1, 2
27.         };
Parsed in 0.011 seconds, using GeSHi 1.0.8.4

This is the tricky part. I think that I described it good enough in them comment.
Lets take a look at two example triangles:

The Code
Original Source:

Modified by: Nicolas 'plusminus' Gramlich
Using java Syntax Highlighting
1. /*
3.  *
5.  * you may not use this file except in compliance with the License.
6.  * You may obtain a copy of the License at
7.  *
9.  *
10.  * Unless required by applicable law or agreed to in writing, software
12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13.  * See the License for the specific language governing permissions and
14.  * limitations under the License.
15.  */
16.
18.
19. import android.app.Activity;
20. import android.content.Context;
21. import android.graphics.Canvas;
22. import android.graphics.OpenGLContext;
23. import android.os.Bundle;
24. import android.os.Handler;
25. import android.os.Message;
26. import android.os.SystemClock;
27. import android.view.View;
28.
29. import java.nio.ByteBuffer;
30. import java.nio.ByteOrder;
31. import java.nio.IntBuffer;
32.
33. import javax.microedition.khronos.opengles.GL10;
34.
35.
36. /**
37.  * Example of how to use OpenGL|ES in a custom view
38.  *
39.  */
40.
41. public class GLView1 extends Activity {
42.
43.     @Override
44.         protected void onCreate(Bundle icicle)
45.     {
46.         super.onCreate(icicle);
47.         setContentView(new GLView( getApplication() ));
48.     }
49.
50.     @Override
51.         protected void onResume()
52.     {
53.         super.onResume();
54.         //android.os.Debug.startMethodTracing("/tmp/trace/GLView1.dmtrace",
55.         //  8 * 1024 * 1024);
56.     }
57.
58.     @Override
59.         protected void onStop()
60.     {
61.         super.onStop();
62.         //android.os.Debug.stopMethodTracing();
63.     }
64. }
65.
66. class GLView extends View
67. {
68.     /**
69.      * The View constructor is a good place to allocate our OpenGL context
70.      */
71.     public GLView(Context context)
72.     {
73.         super(context);
74.
75.         /*
76.          * Create an OpenGL|ES context. This must be done only once, an
77.          * OpenGL contex is a somewhat heavy object.
78.          */
79.         mGLContext = new OpenGLContext(0);
80.         mCube = new Cube();
81.         mAnimate = false;
82.     }
83.
84.     /*
85.      * Start the animation only once we're attached to a window
86.      * @see android.view.View#onAttachedToWindow()
87.      */
88.     @Override
89.     protected void onAttachedToWindow() {
90.         mAnimate = true;
91.         Message msg = mHandler.obtainMessage(INVALIDATE);
92.         mNextTime = SystemClock.uptimeMillis();
93.         mHandler.sendMessageAtTime(msg, mNextTime);
94.         super.onAttachedToWindow();
95.     }
96.
97.     /*
98.      * Make sure to stop the animation when we're no longer on screen,
99.      * failing to do so will cause most of the view hierarchy to be
100.      * leaked until the current process dies.
101.      * @see android.view.View#onDetachedFromWindow()
102.      */
103.     @Override
104.     protected void onDetachedFromWindow() {
105.         mAnimate = false;
106.         super.onDetachedFromWindow();
107.     }
108.
109.     /**
110.      * Draw the view content
111.      *
112.      * @see android.view.View#onDraw(android.graphics.Canvas)
113.      */
114.     @Override
115.     protected void onDraw(Canvas canvas) {
116.         if (true) {
117.         /*
118.          * First, we need to get to the appropriate GL interface.
119.          * This is simply done by casting the GL context to either
120.          * GL10 or GL11.
121.          */
122.         GL10 gl = (GL10)(mGLContext.getGL());
123.
124.         /*
125.          * Before we can issue GL commands, we need to make sure all
126.          * native drawing commands are completed. Simply call
127.          * waitNative() to accomplish this. Once this is done, no native
128.          * calls should be issued.
129.          */
130.         mGLContext.waitNative(canvas, this);
131.
132.             int w = getWidth();
133.             int h = getHeight();
134.
135.             /*
136.              * Set the viewport. This doesn't have to be done each time
137.              * draw() is called. Typically this is called when the view
138.              * is resized.
139.              */
140.
141.
142.             gl.glViewport(0, 0, w, h);
143.
144.             /*
145.              * Set our projection matrix. This doesn't have to be done
146.              * each time we draw, but usualy a new projection needs to be set
147.              * when the viewport is resized.
148.              */
149.
150.             float ratio = (float)w / h;
151.             gl.glMatrixMode(gl.GL_PROJECTION);
153.             gl.glFrustumf(-ratio, ratio, -1, 1, 2, 12);
154.
155.             /*
156.              * dithering is enabled by default in OpenGL, unfortunattely
157.              * it has a significant impact on performace in software
158.              * implementation. Often, it's better to just turn it off.
159.              */
160.              gl.glDisable(gl.GL_DITHER);
161.
162.             /*
163.              * Usually, the first thing one might want to do is to clear
164.              * the screen. The most efficient way of doing this is to use
165.              * glClear(). However we must make sure to set the scissor
166.              * correctly first. The scissor is always specified in window
167.              * coordinates:
168.              */
169.
170.             gl.glClearColor(1,1,1,1);
171.             gl.glEnable(gl.GL_SCISSOR_TEST);
172.             gl.glScissor(0, 0, w, h);
173.             gl.glClear(gl.GL_COLOR_BUFFER_BIT);
174.
175.
176.             /*
177.              * Now we're ready to draw some 3D object
178.              */
179.
180.             gl.glMatrixMode(gl.GL_MODELVIEW);
182.             gl.glTranslatef(0, 0, -3.0f);
183.             gl.glScalef(0.5f, 0.5f, 0.5f);
184.             gl.glRotatef(mAngle,        0, 1, 0);
185.             gl.glRotatef(mAngle*0.25f,  1, 0, 0);
186.
187.             gl.glColor4f(0.7f, 0.7f, 0.7f, 1.0f);
188.             gl.glEnableClientState(gl.GL_VERTEX_ARRAY);
189.             gl.glEnableClientState(gl.GL_COLOR_ARRAY);
190.             gl.glEnable(gl.GL_CULL_FACE);
191.
192.             mCube.draw(gl);
193.
194.             mAngle += 1.2f;
195.
196.         /*
197.          * Once we're done with GL, we need to flush all GL commands and
198.          * make sure they complete before we can issue more native
199.          * drawing commands. This is done by calling waitGL().
200.          */
201.         mGLContext.waitGL();
202.         }
203.     }
204.
205.
206.     // ------------------------------------------------------------------------
207.
208.     private static final int INVALIDATE = 1;
209.
210.     private final Handler mHandler = new Handler() {
211.         @Override
212.                 public void handleMessage(Message msg) {
213.             if (mAnimate && msg.what == INVALIDATE) {
214.                 invalidate();
215.                 msg = obtainMessage(INVALIDATE);
216.                 long current = SystemClock.uptimeMillis();
217.                 if (mNextTime < current) {
218.                     mNextTime = current + 20;
219.                 }
220.                 sendMessageAtTime(msg, mNextTime);
221.                 mNextTime += 20;
222.             }
223.         }
224.     };
225.
226.     private OpenGLContext   mGLContext;
227.     private Cube            mCube;
228.     private float           mAngle;
229.     private long            mNextTime;
230.     private boolean         mAnimate;
231. }
232.
233.
234. class Cube
235. {
236.     public Cube()
237.     {
238.         int one = 0x10000;
239.         /* Every vertex got 3 values, for
240.          * x / y / z position in the kartesian space.
241.          */
242.         int vertices[] = {
243.                -one, -one, -one,
244.                 one, -one, -one,
245.                 one,  one, -one,
246.                -one,  one, -one,
247.                -one, -one,  one,
248.                 one, -one,  one,
249.                 one,  one,  one,
250.                -one,  one,  one,
251.             };
252.
253.         /* Every vertex has got its own color, described by 4 values
254.          * R(ed)
255.          * G(green)
256.          * B(blue)
257.          * A(lpha) <-- Opticacy
258.          */
259.         int colors[] = {
260.                   0,    0,    0,  one,
261.                 one,    0,    0,  one,
262.                 one,  one,    0,  one,
263.                   0,  one,    0,  one,
264.                   0,    0,  one,  one,
265.                 one,    0,  one,  one,
266.                 one,  one,  one,  one,
267.                   0,  one,  one,  one,
268.             };
269.
270.         /* The last thing is that we need to describe some Triangles.
271.          * A triangle got 3 vertices.
272.                  * The confusing thing is, that it is important in which order
273.                  * the vertices of each triangle are described.
274.                  * So describing a triangle through the vertices: "0, 4, 5"
275.                  * will not result in the same triangle as: "0, 5, 4"
276.                  * You probably ask: Why the hell isn't that the same ???
277.                  * The reason for that is the call of: "gl.glFrontFace(gl.GL_CW);"
278.                  * which means, that we have to describe the "visible" side of the
279.                  * triangles by naming its vertices in a ClockWise order!
280.                  * From the other side, the triangle will be 100% lookthru!
281.                  * You can create a kind of magic mirror with that <img src="http://www.anddev.org/images/smilies/wink.png" alt=";)" title="Wink" />.
282.          */
283.         byte indices[] = {
284.                 0, 4, 5,
285.                 0, 5, 1,
286.                 1, 5, 6,
287.                 1, 6, 2,
288.                 2, 6, 7,
289.                 2, 7, 3,
290.                 3, 7, 4,
291.                 3, 4, 0,
292.                 4, 7, 6,
293.                 4, 6, 5,
294.                 3, 0, 1,
295.                 3, 1, 2
296.         };
297.
298.         // Buffers to be passed to gl*Pointer() functions
299.         // must be direct, i.e., they must be placed on the
300.         // native heap where the garbage collector cannot
301.         // move them.
302.     //
303.     // Buffers with multi-byte datatypes (e.g., short, int, float)
304.     // must have their byte order set to native order
305.
306.     ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
307.     vbb.order(ByteOrder.nativeOrder());
308.     mVertexBuffer = vbb.asIntBuffer();
309.         mVertexBuffer.put(vertices);
310.         mVertexBuffer.position(0);
311.
312.     ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
313.     cbb.order(ByteOrder.nativeOrder());
314.         mColorBuffer = cbb.asIntBuffer();
315.         mColorBuffer.put(colors);
316.         mColorBuffer.position(0);
317.
318.         mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
319.         mIndexBuffer.put(indices);
320.         mIndexBuffer.position(0);
321.     }
322.
323.     public void draw(GL10 gl)
324.     {
325.         gl.glFrontFace(gl.GL_CW);
326.         gl.glVertexPointer(3, gl.GL_FIXED, 0, mVertexBuffer);
327.         gl.glColorPointer(4, gl.GL_FIXED, 0, mColorBuffer);
328.         gl.glDrawElements(gl.GL_TRIANGLES, 36, gl.GL_UNSIGNED_BYTE, mIndexBuffer);
329.     }
330.
331.     private IntBuffer   mVertexBuffer;
332.     private IntBuffer   mColorBuffer;
333.     private ByteBuffer  mIndexBuffer;
334. }
Parsed in 0.023 seconds, using GeSHi 1.0.8.4

I hope you succeeded and understood this tutorial.

See you soon

| Android Development Community / Tutorials

plusminus

Posts: 2688
Joined: Wed Nov 14, 2007 8:37 pm
Location: Schriesheim, Germany

Woho this is nice, it grabs one whole cpu-core (got 2), but runs very smoothly.

What was the slowing-factor of emulators ?
Like x0.1 even worse I assume.

Not bad for a phone, though.
It even runs QUAKE =D
mrocket
Junior Developer

Posts: 10
Joined: Thu Nov 15, 2007 7:28 pm

great job! it's usefull and easy to understand
sumet
Freshman

Posts: 5
Joined: Sat Nov 17, 2007 6:02 pm

mrocket wrote:What was the slowing-factor of emulators ?

I think that yes, it's from emulator.

tum0rc0re
Senior Developer

Posts: 158
Joined: Sun Nov 25, 2007 7:15 am
Location: Moscow, Russia

tum0rc0re wrote:
mrocket wrote:What was the slowing-factor of emulators ?

I think that yes, it's from emulator.

The issue was how much slower is the emulator (on average pc) than an average android device

Regards,
plusminus

| Android Development Community / Tutorials

plusminus

Posts: 2688
Joined: Wed Nov 14, 2007 8:37 pm
Location: Schriesheim, Germany

nice little tutorial

..but its "opacity" not "opticacy"
d03boy
Freshman

Posts: 5
Joined: Tue Nov 20, 2007 8:31 pm
Location: USA

plusminus wrote:The issue was how much slower is the emulator (on average pc) than an average android device

I think that much slower than real Android device, because there's program emulation ARM instructions of assembler in the emulator, as in Windows Mobile Emulator (WME works slower than WM devices too).

tum0rc0re
Senior Developer

Posts: 158
Joined: Sun Nov 25, 2007 7:15 am
Location: Moscow, Russia

I'd like to create a more complex 3D object and I hit the following problem:
When I change the mVertexBuffer from IntBuffer to DoubleBuffer, it does not work properly.
So the following is what I modified on this cube example:

1. Change the vertices to double array:

Using java Syntax Highlighting
1.         double coor = 0x10000;
2.
3.         double vertices[] = {
4.
5.                -coor, -coor, -coor,
6.
7.                coor, -coor, -coor,
8.
9.                coor,  coor, -coor,
10.
11.                -coor,  coor, -coor,
12.
13.                -coor, -coor,  coor,
14.
15.                coor, -coor,  coor,
16.
17.                coor,  coor,  coor,
18.
19.                -coor,  coor,  coor,
20.
21.             };
Parsed in 0.011 seconds, using GeSHi 1.0.8.4

2. Change the declaration of mVertexBuffer:
Using java Syntax Highlighting
1. //    private IntBuffer   mVertexBuffer;
2.
3.     private DoubleBuffer        mVertexBuffer;
Parsed in 0.011 seconds, using GeSHi 1.0.8.4

3. Change the allocate of mVertexBudder:
Using java Syntax Highlighting
1. //    ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
2.
3. //    vbb.order(ByteOrder.nativeOrder());
4.
5. //    mVertexBuffer = vbb.asIntBuffer();
6.
7.     mVertexBuffer = DoubleBuffer.allocate(vertices.length*8 );
Parsed in 0.011 seconds, using GeSHi 1.0.8.4

The application compiles and runs with very scary looking...

Does anyone has any idea on this? Ideally glVertexPointer should accept any type of Buffer.

Thanks and Regards,
Xu
Freshman

Posts: 5
Joined: Mon Dec 24, 2007 3:27 am

Nice tutorial. But what the heck is that 0x10000 constant? Why not 1.0 or 255 or 0xFFFF? It's ok for vertex but I don't get how it can be applied for color definition? Probably, it will be narrowed to 0xFFFF but it's not a case for tutorials for beginners, so please explain.

Thanks.
freenode
Once Poster

Posts: 1
Joined: Fri Dec 26, 2008 10:31 am

galahadxu: OpenGL implementations were never perfect. Stick with floats and bytes for now. IntBuffer has a bug for streaming, so you might want to avoid it for now, unless you're using it only for constant data.
freenode: GL_FIXED is 16.16 fixed-point, put in integer. So, 1.0f = 1<<16 .

By 0xFFFF, I guess you mean to use it for color:
565 color-data is only allowed for texel-data when uploading your texture with glTexImage2D. All color parameters for API-calls are in normalized 0..1 floats or unsigned-bytes. OpenGL doesn't accept color-parameters in one whole chunk (i.e white being 0xFFFFFFFF) - unless you're doing pointer-tricks in C++ and asm. OpenGL accepts components (in order: R,G,B,A) - you specify their count (1..4) and type (GL_UNSIGNED_BYTE, GL_FIXED or GL_FLOAT) when binding the array.

Btw, the Java bytes are signed -128..127, so white will be:
byte ColorArray[];
....
ColorArray[0] = -1; ColorArray[1] = -1; ColorArray[2] = -1; ColorArray[3] = -1;
or
ColorArray[0] = (byte)255;ColorArray[1] = (byte)255;ColorArray[2] = (byte)255;ColorArray[3] = (byte)255;

I suggest you stick to common data-types: GL_UNSIGNED_BYTE for color, GL_FLOAT and GL_FIXED for coordinates. That is the only sane and usually-tested way.
Ultrano
Junior Developer

Posts: 16
Joined: Sat Dec 20, 2008 12:53 am

Is there anoy body that could fix this sample to be compiled using SDK 1.1 R1?

This sample has been writted using an older SDK and now it doesn´t compile.
SkyNet800
Junior Developer

Posts: 20
Joined: Tue Feb 24, 2009 11:44 am

Check the samples dir, there are some OpenGL demos there.

MrSnowflake
Moderator

Posts: 1439
Joined: Sat Feb 16, 2008 3:11 pm
Location: Flanders, Belgium

I´ve seen some examples, but this is very simple and clear, and is perfect for beginners.

plusminus is very clever.
SkyNet800
Junior Developer

Posts: 20
Joined: Tue Feb 24, 2009 11:44 am

### Re: Colored 3D Cube

plusminus wrote:import android.graphics.OpenGLContext;

this import "cannot be resolved"... why?

Ery86
Developer

Posts: 27
Joined: Mon Apr 20, 2009 10:09 am
Location: Italy

Unable to resolve openGLContext in new SDK.