Code sample: Drawing a 3-D cube

You can use the GL10 interface to draw a 3-D cube.

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL10;

import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.container.FullScreen;
import net.rim.device.api.opengles.GLUtils;

class ThreeDCube extends FullScreen implements Runnable
{
    private EGL11 _egl;
    private EGLDisplay _eglDisplay;
    private EGLConfig _eglConfig;
    private EGLSurface _eglSurface;
    private EGLContext _eglContext;
    private GL10 _gl;

    private Bitmap _offscreenBitmap;
    private Graphics _offscreenGraphics;


    private boolean _running;
    private boolean _paused;
    private FloatBuffer _cubeVertices, _cubeNormals, _cubeColors;

    float _angle = 45f;
    
    private int _vertexCount;

    private static final float[] _vertices =
    {
        // front
        -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
        0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f,
        
        // right
        0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, -0.5f,
        0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f,
        
        // back
        0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f,
        -0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
        
        // left
        -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, 0.5f,
        -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f,
        
        // top
        -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f,
        0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
        
        // bottom
        -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f,
        0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f
    };
    
    private static final float[] _normals =
    {
        /* front */ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
        /* right */ 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
        /* back */ 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
        /* left */ -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,
        /* top */ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
        /* bottom */ 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0
    };
    
    private static final float[] _colors =
    {
        /* front  – white  */  1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
        /* right  – red    */  1,0,0,1, 1,0,0,1, 1,0,0,1, 1,0,0,1, 1,0,0,1, 1,0,0,1,
        /* back   – green  */  0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1,
        /* left   – blue   */  0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1,
        /* top    - yellow */  1,1,0,1, 1,1,0,1, 1,1,0,1, 1,1,0,1, 1,1,0,1, 1,1,0,1,
        /* bottom - magenta*/  1,0,1,1, 1,0,1,1, 1,0,1,1, 1,0,1,1, 1,0,1,1, 1,0,1,1
    };

    
    ThreeDCube()
    {
        super(FullScreen.DEFAULT_MENU | FullScreen.DEFAULT_CLOSE);
    }

    private void initialize()
    {
        // Get EGL interface
        _egl = (EGL11)EGLContext.getEGL();

        // Get the EGL display
        _eglDisplay = _egl.eglGetDisplay(EGL11.EGL_DEFAULT_DISPLAY);

        // Initialize the display for EGL, pass null as the second argument
        // because we don't need the version.
        _egl.eglInitialize(_eglDisplay, null);

        // Choose an EGL config
        EGLConfig[] configs = new EGLConfig[1];
        int[] numConfigs = new int[1];
        int[] attrs =
        {
            EGL11.EGL_RED_SIZE,   5,
            EGL11.EGL_GREEN_SIZE, 6,
            EGL11.EGL_BLUE_SIZE,  5,
            EGL11.EGL_NONE
        };
        _egl.eglChooseConfig(_eglDisplay, attrs, configs, 1, numConfigs);
        _eglConfig = configs[0];

        // Create an EGL window surface
        _eglSurface = _egl.eglCreateWindowSurface
            (_eglDisplay, _eglConfig, this, null);

        // Create an EGL context
        createEGLContext();

        _cubeVertices = createVertexBuffer();
        _cubeNormals = createNormalBuffer();
        _cubeColors = createColorBuffer();
        
        _vertexCount = _vertices.length / 3;
    }

    private FloatBuffer createVertexBuffer()
    {
        FloatBuffer buffer = 
          ByteBuffer.allocateDirect(_vertices.length * 4).asFloatBuffer();
        buffer.put(_vertices);
        buffer.rewind();
        return buffer;
    }
    
    private FloatBuffer createNormalBuffer()
    {
        FloatBuffer buffer = 
          ByteBuffer.allocateDirect(_normals.length * 4).asFloatBuffer();
        buffer.put(_normals);
        buffer.rewind();
        return buffer;
    }
    
    private FloatBuffer createColorBuffer()
    {
        FloatBuffer buffer = 
          ByteBuffer.allocateDirect(_colors.length * 4).asFloatBuffer();
        buffer.put(_colors);
        buffer.rewind();
        return buffer;
    }
    
    
    private void createEGLContext()
    {
        // Create an EGL context
        _eglContext = _egl.eglCreateContext
            (_eglDisplay, _eglConfig, EGL10.EGL_NO_CONTEXT, null);

        // Get the GL interface for our new context
        _gl = (GL10)_eglContext.getGL();

        // Make our new context current
        _egl.eglMakeCurrent
           (_eglDisplay, _eglSurface, _eglSurface, _eglContext);
    }

    private void destroyEGL()
    {
        _egl.eglMakeCurrent(_eglDisplay, EGL10.EGL_NO_SURFACE,
            EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        _egl.eglDestroyContext(_eglDisplay, _eglContext);
        _egl.eglDestroySurface(_eglDisplay, _eglSurface);
    }

    private void handleContextLost()
    {
        // Destroy our EGL context
        _egl.eglMakeCurrent(_eglDisplay, EGL10.EGL_NO_SURFACE,
            EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        _egl.eglDestroyContext(_eglDisplay, _eglContext);
        _eglContext = EGL10.EGL_NO_CONTEXT;

        // Re-create our EGL context
        createEGLContext();
    }

    /***************************************************************************
     * Main render loop.
     ***************************************************************************/
    public void run()
    {
        initialize();
        int throttle = 0;

        while (_running)
        {
        	
        	throttle = (int)System.currentTimeMillis();
        	
            // Idle if in the background
            if (_paused)
            {
                synchronized (this)
                {
                    try
                    {
                        wait();
                    }
                    catch (InterruptedException x) { }
                }
            }

            updateBackBuffer();
            renderFrame();
            ++_angle;
            
            if (_angle >= 360f)
            {
            	_angle = 0f;
            }
            
            //Determine how long the frame took to render
            throttle = (int)System.currentTimeMillis() - throttle;
            
            //Throttle to 30 FPS to control CPU usage.
            throttle = 33 - throttle;
            
            if (throttle > 0)
            {
	            // Throttle cpu usage
	            try
	            {
	                Thread.sleep(throttle);
	            }
	            catch (InterruptedException x) { }
            }
        }

        destroyEGL();
    }

    private void renderFrame()
    {
        // Make our context and surface current and check for EGL_CONTEXT_LOST
        if (!_egl.eglMakeCurrent(_eglDisplay, _eglSurface, _eglSurface, _eglContext))
        {
            if (_egl.eglGetError() == EGL11.EGL_CONTEXT_LOST)
                handleContextLost();
        }

        // Signal that we are about to begin OpenGL rendering
        _egl.eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, _offscreenGraphics);

        render(_gl);

        // Signal that OpenGL ES rendering is complete
        _egl.eglWaitGL();

        // Swap the window surface to the display
        _egl.eglSwapBuffers(_eglDisplay, _eglSurface);
    }

    private void render(GL10 gl)
    {
    	// Set our GL viewport
        gl.glViewport(0, 0, getWidth(), getHeight());

        // Clear the surface
        gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        GLUtils.gluPerspective(gl, 45.0f, (float)getWidth()/(float)getHeight(),
          0.15f, 100.0f);

        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();

        // Setup drawing state
        gl.glEnable(GL10.GL_DEPTH_TEST);
        gl.glEnable(GL10.GL_LIGHTING); 
        gl.glEnable(GL10.GL_LIGHT0);
        gl.glEnable(GL10.GL_COLOR_MATERIAL);

        // Draw our cube
        gl.glTranslatef(0, 0, -3.5f);
        gl.glRotatef(_angle, 1.0f, 1.0f, 0.0f);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _cubeVertices);
        gl.glNormalPointer(GL10.GL_FLOAT, 0, _cubeNormals);
        gl.glColorPointer(4, GL10.GL_FLOAT, 0, _cubeColors);
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, _vertexCount);
    }

    /***************************************************************************
     * Called by the UI system to paint the screen.
     ***************************************************************************/
    protected void paint(Graphics g)
    {
        if (_offscreenBitmap != null)
            g.drawBitmap(0, 0, _offscreenBitmap.getWidth(),
                _offscreenBitmap.getHeight(), _offscreenBitmap, 0, 0);
    }

    /***************************************************************************
     * Called when the visibility of our screen changes.
     *
     * The visible argument is true if our screen is being made visible,
     * false if it's being hidden
     ***************************************************************************/
    protected void onVisibilityChange(boolean visible)
    {
        if (visible)
        {
            resume();
        }
        else
        {
            pause();
        }
    }

    /***************************************************************************
     * Called when the screen is closing.
     ***************************************************************************/
    public void close()
    {
        _running = false;
        synchronized (this) { notifyAll(); }

        super.close();
    }

    /***************************************************************************
     * Keeps the back buffer in sync with the screen size.
     ***************************************************************************/
    private void updateBackBuffer()
    {
        if (_offscreenBitmap != null)
        {
            if (_offscreenBitmap.getWidth() == getWidth() &&
                _offscreenBitmap.getHeight() == getHeight())
                return; // no change needed
        }

        _offscreenBitmap = new Bitmap(getWidth(), getHeight());
        _offscreenGraphics = Graphics.create(_offscreenBitmap);
    }

    private void pause()
    {
        _paused = true;
    }

    private void resume()
    {
        if (_running)
        {
            // Pause the render loop
            _paused = false;
            synchronized (this) { notifyAll(); }
        }
        else
        {
            // Start the render thread.
            _running = true;
            new Thread(this).start();
        }
    }
}

Was this information helpful? Send us your comments.