Sorry about the red box, but we really need you to update your browser. Read this excellent article if you're wondering why we are no longer supporting this browser version. Go to Browse Happy for browser suggestions and how to update.

OpenGL ES

OpenGL is a cross-platform graphics API that is designed to make optimal use of graphics hardware to produce high-quality, real-time graphics. OpenGL is specified by The Khronos Group, which is a not-for-profit industry consortium.

OpenGL ES is a simplified version of OpenGL that is designed to run on less powerful, more resource-constrained devices, such as smartphones. OpenGL ES is specified as a list of differences from standard OpenGL. The primary difference between the two versions is that OpenGL ES offers you fewer capabilites. However, OpenGL ES does include some extensions that are not available in standard OpenGL.

OpenGL ES takes low-level descriptions of modeled objects, textures to apply to those objects, and the viewing perspective, and turns all of that input into a 2-D image to display on the screen. OpenGL ES accomplishes this transformation by using a series of steps that is sometimes called the OpenGL pipeline.

OpenGL ES provides low-level mechanisms for describing the geometry of 3-D objects. It is not designed to help you create models of objects and does not have an associated model file format. You can manually specify data to describe very simple models, such as a cube or pyramid, but that approach is impractical for more complex models.

You can use a 3-D modeling program to create more complex models. Different modeling programs store model information in different file formats. To use the models in an OpenGL ES application, you must convert them to the low-level array-based format that OpenGL ES uses.

BlackBerry Java SDK 6.1 supports version 1.1 and version 2.0 of OpenGL ES. These two versions of the library are very different. OpenGL ES version 1.1 is designed to work with fixed-function graphics hardware and OpenGL ES version 2.0 is designed to work with graphics hardware that supports shader programs.

GLField

The GLField class enables you to use the OpenGL ES library by including code that handles EGL setup. GLField also includes built-in support for refreshing the display at a specified target frame rate. It is a convenient choice for 3-D gaming and animation applications.

GLField is an abstract class. You must extend it to create a UI component that you can add to a screen. Your subclass must include a constructor and override layout(), initialize(), and render().

The constructor can call the parent class constructor by invoking super()and passing in the version of OpenGL ES that you want to use. In initialize(), you write code to perform setup tasks, such as setting the color to use to clear the screen. In layout(), you set the size of the field.

You can use setTargetFrameRate()to create a rendering thread that calls render() at the frame rate you specify. You do not have to override update(), but if you do, it is also called at the target frame rate. The rendering started by setTargetFrameRate() runs on the event thread that you create when you call enterEventDispatcher(). You should set a target frame rate using setTargetFrameRate() instead of manually throttling rendering yourself. Thirty frames per second is generally the lower bound for smooth real-time graphics. Sixty frames per second is the upper limit because of the LCD refresh rate. A higher frame rate consumes more battery power. The frame rate can be set dynamically.

If you do not specify a target frame rate, it defaults to 0 and paint(), render(), and update() are called from the standard UI event thread in response to invalidate(). Note that initialize() is called when the EGL context is lost and when the field is removed and added back to the screen.

Code sample: Minimal GLField implementation

The following code sample provides simple implementations for the required methods. You can create a MyGLField class and add it to a screen.

import net.rim.device.api.opengles.GLField;


public class MyGLField extends GLField
 {
     public MyGLField(int ver)
     {
         super(ver);
     }

     protected void layout(int width, int height)
     {
         // Set field dimensions
         setExtent(100, 100);
     }
     
     protected void initialize(GL gl)
     {
         GL20 gl20 = (GL20)gl;
         gl20.glClearColor(0, 0, 0, 1);
     }
     
     protected void render(GL gl)
     {
         GL20 gl20 = (GL20)gl;
         gl20.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
     }
 }

Using GLField with the Animation API

If you use the Animation API with a GLField, you must update your Animator at the same rate as the GLField.

Both the GLField and Animator classes enable you to set a target frame rate. But when you use them together, you should synchronize the update rates by handling the Animator update from within the GLField update() method. Doing so will ensure that the Animator and GLField are updated at the same frame rate.

The following code snippet demonstrates overriding the GLField update() method to have it update the Animator. This ensures that the Animator is synchronized with the GLField.

private Animator _animator;

protected void initialize(GL g)
{
    // ...
    _animator = new Animator(0);
    // add animations to animator
    _animator.begin(0L);
}

public void update()
{
    _animator.update();
}

Shader programs

OpenGL ES is designed to take the data that describes a 3-D scene and manipulate it so that the scene is represented as a square grid of pixels on a 2-D display. The OpenGL ES pipeline refers to the stages that the 3-D scene data goes through to achieve a 3-D to 2-D transformation.

In versions of OpenGL ES prior to version 2.0, the operations that take place at each stage of the pipeline are fixed. You can set parameters that affect these fixed operations, but you cannot define new operations. For example, you can specify if your model should rotate and by how much, but you cannot define a new type of rotation that distorts the model as it rotates.

In OpenGL ES 2.0, you can define custom pipeline operations that act on vertices and fragments. You can define these operations in the GL shader language. You must define both a vertex shader program and a fragment shader program.

You create a shader object, add source code in GLSL that specifies the shader, and then compile the shader. The GLUtils library includes a glLoadShader() convenience method that you can use to do all of this in one call.

Best practice: Working with shader programs

Best practice

Description

Load shader programs from a file

Tutorials and sample code that demonstrate how to use OpenGL ES shader programs often store the programs in string variables. Storing shader programs in string variables is useful when you are learning because it makes the code shorter and simpler. However, when you are writing production code, you should store your shader programs in files.

Check for shader program compile errors

Shader programs often have compile errors. You should always check for shader program compile errors and log any related error messages to help you debug problems. It is useful to log the messages to the event log or even to a modal dialog box.

The GLUtils.glLoadShader() method returns 0 if the shader failed to compile. The method accepts a string array parameter where any compiler error messages are stored. You should always check the return value and pass a string array to store error messages.

String[] infolog = new String[2];

int vertexShader = GLUtils.glLoadShader(gl,
                                        GL20.GL_VERTEX_SHADER,
                                        vertexShaderSource,
                                        infolog,
                                        0);

int fragShader   = GLUtils.glLoadShader(gl,
                                        GL20.GL_FRAGMENT_SHADER,
                                        fragShaderSource,
                                        infolog,
                                        1);
if (vertexShader == 0)
{
   throw new IllegalStateException("Vertex shader error: " 
                                   + infolog[0]);                             
}                 

if (fragShader == 0)
{
   throw new IllegalStateException("Fragment shader error: " 
                                   + infolog[1]);                             
}

Check shader program link status

Check the GL_LINK_STATUS of shader programs to make sure they linked without errors. If GL_LINK_STATUS == GL_FALSE, then there was a link error and you can retrieve the error message by calling GL20.glGetProgramInfoLog() before deleting the shader program.

int program = gl.glCreateProgram();
if(program == 0)
{
   throw new 
    IllegalStateException("Error creating shader program");
}

gl.glAttachShader(program,vertexShader);
gl.glAttachShader(program,fragmentShader);

gl.glLinkProgram(program);

int[] linked = new int[1];
gl.glGetProgramiv(program,GL20.GL_LINK_STATUS,linked,0);

if(linked[0] == GL20.GL_FALSE)
{
   String log = gl.glGetProgramInfoLog(program);
   gl.glDeleteProgram(program);
   throw new 
    IllegalStateException("Program link error: " + log);
}

Delete shader objects after linking a shader program

After a shader program is successfully linked, it doesn't have to remain in memory. You should delete vertex and fragment shader objects by calling GL20.glDeleteShader().

gl.glDeleteShader(vertexShader);
gl.glDeleteShader(fragShader);

JSR 239 overview

OpenGL ES is specified as a C API. The JSR 239 specification describes a Java API that is a thin layer over the C API that is described in the specification.

You can use the APIs that are described in the JSR 239 specification to develop OpenGL ES applications on BlackBerry devices.

JSR 239 packages

Package

Description

javax.microedition.khronos.egl

Provides bindings for EGL 1.0 and 1.1.

javax.microedition.khronos.opengles

Provides bindings for OpenGL ES 1.0 and 1.1, including core profile extensions and optional profile extensions.

java.nio

Provides buffer classes that are modeled after those in the java.nio.Buffer package in Java SE that you can use to store data on the native heap, which is required by some APIs that are described in the JSR 239 specification.

EGL API - Native Platform Graphics Interface

You can use the javax.microedition.khronos.egl package to provide bindings for EGL 1.0 and 1.1 for the OpenGL ES API. The rendering contexts that are supported provide a container for the OpenGL ES rendering state.

The rendering surfaces that are supported include the following:

  • Window surfaces: onscreen rendering
  • Pbuffer surfaces: offscreen rendering of pixel buffers
  • Pixmap surfaces: offscreen rendering to client buffers

The most commonly used feature of this package is the EGL10 interface. This interface contains the programming language bindings for EGL 1.0.

All OpenGL ES drawing except to Pbuffer surfaces must be preceded by a call to eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, target), where target is an object that describes the rendering target. This must be followed by a call to eglWaitGL(). When drawing to an image, the results are not guaranteed to appear in the image pixels until eglWaitGL() has returned.

Pbuffer surfaces are created using EGL10.eglCreatePbufferSurface() and are accessible only from EGL-based APIs.

Code sample: Rendering a single frame using EGL10

private void render()
{
    if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext))
    {
        int error = egl.eglGetError();

        if ( error == EGL11.EGL_CONTEXT_LOST )
            handleContextLost();
        else
            handleError("eglMakeCurrent failed: " + error);
    }
        
    // Signal that we're about to begin GL rendering.
    egl.eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, graphics);
        
    // Check for a viewport change.
    if (vpWidth != getWidth() || vpHeight != getHeight())
    {
        vpWidth = getWidth();
        vpHeight = getHeight();
        renderer.sizeChanged(gl, vpWidth, vpHeight);
    }
        
    renderer.render(gl);
        
    // Signal that we're finished GL rendering.
    egl.eglWaitGL();
}

Set up EGL

Get an object that implements the EGL 1.1 interface.

EGL11 egl = (EGL11)EGLContext.getEGL();

Get and initialize the default EGL display.

EGLDisplay eglDisplay = egl.eglGetDisplay(EGL11.EGL_DEFAULT_DISPLAY);
egl.eglInitialize(eglDisplay, null);

Specify the configuration attributes that you want.

int[] configAttributes = { 
EGL11.EGL_RED_SIZE, 5,
EGL11.EGL_GREEN_SIZE, 6,
EGL11.EGL_BLUE_SIZE, 5,
EGL11.EGL_ALPHA_SIZE, EGL11.EGL_DONT_CARE,
EGL11.EGL_DEPTH_SIZE, EGL11.EGL_DONT_CARE,
EGL11.EGL_STENCIL_SIZE, EGL11.EGL_DONT_CARE,
EGL11.EGL_NONE}

Create an integer array with one element to hold the return value that indicates the number of EGL configurations that matched the attributes specified by the configAttributes array. Create an EGLConfig array with one element to store the first EGL configuration that matches the attributes. Invoke eglChooseConfig() and provide, as arguments, the EGLDisplay object that you initialized in step 2, the array that specifies the configuration attributes to match, a placeholder for the first matching EGLConfig object, the size of the EGLConfig placeholder (1), and the num_configs array to store the number of configurations that matched. Store the single configuration from the eglConfigs array in the EGLConfig variable eglConfig.

int[] num_configs = new int[1];
EGLConfig[] eglConfigs = new EGLConfigs[1];
egl.eglChooseConfig(eglDisplay,configAttributes,eglConfigs,1, num_configs);
EGLConfig eglConfig = eglConfigs[0];

Invoke eglCreateWindowSurface() to create an EGL surface and provide, as arguments, eglDisplay and eglConfig, which are the instances of EGLDisplay and EGLConfig that you set up in steps 2 and 4. In the following code sample, eglCreateWindowSurface() is invoked from a class that is derived from the Screen class, and the this argument binds the EGLSurface object to the current screen.

EGLSurface eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, this, null);

Invoke eglCreateContext() to create an EGL context.

EGLContext eglContext = egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, null);

Invoke eglMakeCurrent() to bind the EGL context to the EGL surface and EGL display.

egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);

You can now use the statement gl = (GL11)eglContext.getGL(); to get an object that implements the GL11 interface, which lets you start working with OpenGL ES 1.1.

Native I/O

You can use the java.nio package to read and write data using block I/O operations. These operations let you send native, contiguous data to OpenGL ES. In contrast, arrays in Java are not contiguous and require a lot of copying. The java.nio package provides direct buffers to communicate with OpenGL pointer functions, as well as wrapped buffers to access underlying Java arrays that cannot be passed to OpenGL pointer functions. Data types that are supported by the java.nio package include byte, short, int, and float.

Defining the geometry for a 3-D cube using the java.nio package

final class Cube
{
    static FloatBuffer createVertexBuffer()
    {
        FloatBuffer buffer = ByteBuffer.allocateDirect
         (vertices.length * 4).asFloatBuffer();
        buffer.put(vertices);
        buffer.rewind();
        return buffer;
    }
    
    static FloatBuffer createNormalBuffer()
    {
        FloatBuffer buffer = ByteBuffer.allocateDirect
          (normals.length * 4).asFloatBuffer();
        buffer.put(normals);
        buffer.rewind();
        return buffer;
    }
    
    static FloatBuffer createTexCoordBuffer()
    {
        FloatBuffer buffer = ByteBuffer.allocateDirect
          (texCoords.length * 4).asFloatBuffer();
        buffer.put(texCoords);
        buffer.rewind();
        return buffer;
    }

    private static 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 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 float[] texCoords =
    {
        /* front */ 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1,
        /* right */ 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1,
        /* back */ 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1,
        /* left */ 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1,
        /* top */ 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1,
        /* bottom */ 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1
    };
}

3-D math utilities

You can use methods in the net.rim.device.api.math package to enhance the speed and versatility of BlackBerry device applications that you create.

You can use the math.Fixed32 class for a collection of fixed-point math routines that use the 16.16 convention to fix the decimal point. This convention specifies that the most significant 16 bits of a 32-bit integer are used for the fixed-point component of the value, and the remainder are used for the mantissa. Addition and subtraction operations are performed with regular integer + and - operators to help keep this library small. The numbers are signed, so negative numbers can be represented. The largest positive number in 16.16 format is just over 32767.9999, and the closest number to negative infinity is -32768.0. The smallest increment between consecutive numbers is 1/65536.

You can use the math.Matrix4f class to create a 4-by-4 matrix of floating point numbers that represents a 3-D transformation. The math.Matrix4f class is directly compatible with OpenGL because its elements are stored in memory in the same manner that is used by OpenGL.

You can use the math.Quaternion4f class to create a quaternion number that you can use to represent the orientation of an object in space. Quaternions are typically used instead of Euler angles and rotation matrices as a way to achieve smooth interpolation and avoid gimbal lock. The math.Quaternion4f class does not keep the quaternion normalized, so you must normalize the quaternion when necessary by invoking normalize().

You can use the math.Vector3f class to create a 3-element vector of floating-point numbers. When you use a vector to represent a surface normal, the vector should typically be normalized. In other applications of directional vectors, you might want to leave the magnitude of the vector intact. When you use a vector to represent a point, the elements of the vector represent a position in 3-D space.

Code sample: Using Fixed32()

import net.rim.device.api.math.*;

public class demoFixed32
{
    demoFixed32()
    {
        // Convert an integer to fixed-point.
        int n = Fixed32.toFP(7); // 7.0

        // Convert a quantity in ten-thousandths to fixed-point.
        int m = Fixed32.tenThouToFP(70625); // 7.0625

        // Convert from fixed-point, truncate fractional component.
        int trunc = Fixed32.toInt(m); // 7
 
        // Multiply by ten thousand.
        int mult = Fixed32.toIntTenThou(m); // 70625

        // Add, subtract, negate, and compare.
        int result = m - n; // 7.0625 - 7.0 == 0.0625
        result = -result; // -0.0625
        result -= n; // -0.0625 - 7.0 == -7.0625
        boolean b = (result == -m); // true
        boolean bb = (m < n); // false

        // Do not use increment and decrement operators.
        result = Fixed32.toFP(2);
        ++result; // WRONG! result will NOT be 3

        result = Fixed32.toFP(2);
        result += Fixed32.toFP(1); // Correct: result will be 3

        // Use * and / when multiplying or dividing by an integer scalar.
        // Use mul to multiply 2 fixed-point numbers.
        // Use div to divide 2 fixed-point numbers.
        m = Fixed32.tenThouToFP(12500); // 1.25
        m *= 3; // OK: 1.25 * 3 == 3.75
        m /= 2; // OK: 3.75 / 2 == 1.875
        m = Fixed32.mul(m, Fixed32.tenThouToFP(15000)); // 1.875 * 1.5000 == 2.8125
        m = Fixed32.div(m, m); // 2.8125 / 2.8125 == 1.0
 
        // mul, div, sqrt, sind, cosd, tand, atand2 all work with 16.16 fixed-point numbers.
        m = Fixed32.tenThouToFP(172500); // 17.2500
        n = Fixed32.sqrt(m); // sqrt(17.25)
        n = Fixed32.sind(m); // sine of 17.25 degrees
        n = Fixed32.cosd(m); // cosine of 17.25 degrees
        result = Fixed32.atand2(-m, -m); // -135.0 degrees in fixed-point
    }
}

Naming conventions

The naming conventions for OpenGL ES methods are the same as those for standard OpenGL. For example:

  • javax.microedition.khronos.opengles.GL10.glClear(int mask)
  • javax.microedition.khronos.opengles.GL10.glLoadMatrixf(FloatBuffer m)

Fixed-point methods are named with an x suffix. For example:

  • javax.microedition.khronos.opengles.GL10.glLoadMatrixx(IntBuffer m)

Constants are named using the same syntax and values as standard OpenGL. For example:

  • javax.microedition.khronos.opengles.GL10.GL_TEXTURE0

Data type mappings

Table 1. Type mappings

Native data type

Java data type

All 8-bit integral types

byte

All 16-bit integral types

short

All 32-bit integral types

int

All 32-bit float types

float

Table 2. Pointer mappings

Native pointer

Java class

void*

java.nio.Buffer

int*

java.nio.IntBuffer

float*

java.nio.FloatBuffer

OpenGL ES code samples

Code sample: Determining OpenGL ES support

import net.rim.device.api.ui.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.opengles.*;
import javax.microedition.khronos.egl.*;
import javax.microedition.khronos.opengles.*;

public class OpenGLSupport extends UiApplication
{  
    public static void main( String[] args )
    {
        OpenGLSupport app = new OpenGLSupport();
        app.enterEventDispatcher();
    }
    
    public OpenGLSupport()
    {
        pushScreen(new InfoScreen());
    }
    
}

class InfoScreen extends MainScreen
{
    private LabelField labelSupportMsg;
    private LabelField labelVendor;
    private LabelField labelRenderer;
    private LabelField labelVersion;
    private LabelField labelExtensions;
    
    private EGL11 _egl;
    private EGLDisplay _eglDisplay;
    private EGLSurface _eglSurface;
    private EGLConfig _eglConfig;
    private EGLContext _eglContext;
    private GL10 _gl;

    public InfoScreen()
    {
        if(GLUtils.isSupported())
        {
            labelSupportMsg = new LabelField("OpenGL ES is supported.");
            
            _egl = (EGL11)EGLContext.getEGL();
            _eglDisplay = _egl.eglGetDisplay(EGL11.EGL_DEFAULT_DISPLAY);
            int[] versionInfo = new int[2];
            _egl.eglInitialize(_eglDisplay, versionInfo);

            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];
            
            _eglContext = _egl.eglCreateContext(_eglDisplay,
              _eglConfig, EGL10.EGL_NO_CONTEXT, null);
            
            _eglSurface = _egl.eglCreateWindowSurface(_eglDisplay,
              _eglConfig, this, null);
            
            _egl.eglMakeCurrent(_eglDisplay,_eglSurface,_eglSurface,_eglContext);
            _gl = (GL10)_eglContext.getGL();
            
            labelVendor     = new LabelField("Vendor: " + 
                                             _gl.glGetString(GL10.GL_VENDOR));
            labelRenderer   = new LabelField("Renderer: " + 
                                             _gl.glGetString(GL10.GL_RENDERER));
            labelVersion    = new LabelField("Version: " + 
                                             _gl.glGetString(GL10.GL_VERSION));
            labelExtensions = new LabelField("Extensions:\n" + 
                                             _gl.glGetString(GL10.GL_EXTENSIONS));
            
            add(labelSupportMsg);
            add(labelVendor);
            add(labelRenderer);
            add(labelVersion);
            add(labelExtensions);
        }
        else
        {
            labelSupportMsg = new LabelField("OpenGL ES is not supported.");
            add(labelSupportMsg);
        }
        
        
    }
    
}

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();
        }
    }
}

Code sample: Drawing a multicolored triangle

import java.nio.*;
import javax.microedition.khronos.egl.*;
import javax.microedition.khronos.opengles.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
import net.rim.device.api.opengles.*;

public final class MultiColoredOpenGLTriangle extends UiApplication
{
    public MultiColoredOpenGLTriangle()
    {
        pushScreen(new DrawScreen());
    }
    
    public static void main(String[] args)
    {
        new MultiColoredOpenGLTriangle().enterEventDispatcher();
    }
}
class DrawScreen 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 FloatBuffer _vertexArray;
    private FloatBuffer _colorArray;
    private boolean _running;
    private boolean _paused;
    
    DrawScreen()
    {
        super(FullScreen.DEFAULT_MENU | FullScreen.DEFAULT_CLOSE);
    }
    
    private void initialize()
    {
        _egl = (EGL11)EGLContext.getEGL();
        // Get the EGL display
        _eglDisplay = _egl.eglGetDisplay(EGL11.EGL_DEFAULT_DISPLAY);
        // Initialize the display for EGL setting the version to null
        _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();
        float[] vertices =
        {
            -0.5f, -0.5f, 0.0f,
            0.0f, 0.5f, 0.0f,
            0.5f, -0.5f, 0.0f
        };

        float[] colors =
        {
            0.0f, 1.0f, 0.0f, 1.0f,
            1.0f, 0.0f, 0.0f, 1.0f,
            0.0f, 0.0f, 1.0f, 1.0f
        };
        _vertexArray = ByteBuffer.allocateDirect(3 * 3 * 4).asFloatBuffer();
        _vertexArray.put(vertices);
        _vertexArray.rewind();
        _colorArray = ByteBuffer.allocateDirect(4 * 3 * 4).asFloatBuffer();
        _colorArray.put(colors);
        _colorArray.rewind();
    }

    private void createEGLContext()
    {
        // Create an EGL context
        _eglContext = _egl.eglCreateContext
        (_eglDisplay, _eglConfig, EGL10.EGL_NO_CONTEXT, null);
        // Get the GL interface for the new context
        _gl = (GL10)_eglContext.getGL();
        // Make the 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 the 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 the EGL context
        createEGLContext();
    }

    /***************************************************************************
    * Main render loop
    ****************************************************************************/
    public void run()
    {
        initialize();
        while (_running)
        {
        // Idle if this thread is in the background
        if (_paused)
        {
            synchronized (this)
            {
                try
                {
                    wait();
                }
                catch (InterruptedException x) { }
            }
        }
        updateBackBuffer();
        renderFrame();
        // Throttle cpu usage
        try
        {
            Thread.sleep(20);
        }
            catch (InterruptedException x) { }
        }
        destroyEGL();
    }

    private void renderFrame()
    {
        // Make the 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 OpenGL rendering is about to begin
        _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 the 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);
        // Set the projection matrix
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        GLUtils.gluPerspective
        (gl, 45.0f, (float)getWidth()/(float)getHeight(), 0.15f, 10.0f);
        // Draw the triangle
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
        gl.glTranslatef(0.0f, 0.0f, -3.0f);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexArray);
        gl.glColorPointer(4, GL10.GL_FLOAT, 0, _colorArray);
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
    }

    /***************************************************************************
    * 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 the screen changes.
    *
    * The visible argument is true if the screen is being made visible,
    * false if 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)
        {
        _paused = false;
        synchronized (this) { notifyAll(); }
    }
    else
    {
        // Start the render thread.
        _running = true;
        new Thread(this).start();
    }
}
}

Code sample: Drawing a texture on a GLField

The following code sample requires you to add a 256-by-256 pixel .png file named bg.png to your project.

import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.opengles.*;
import javax.microedition.khronos.opengles.*;
import javax.microedition.khronos.egl.*;

public final class GLFieldDemo extends UiApplication
{
    public GLFieldDemo()
    {
        pushScreen(new HomeScreen());
    }
    
    public static void main(String[] args)
    {
        GLFieldDemo app = new GLFieldDemo();
        app.enterEventDispatcher();
    }
}

class MyGLField extends GLField
{
     protected void layout(int width, int height)
     {
         setExtent(256, 256);
     }
     
     protected void initialize(GL gl)
     {
         GL11 gl11 = (GL11)gl;
         gl11.glClearColor(0, 0, 0, 1);
         
         int textureNames[] = new int[1];
         gl11.glGenTextures(1,textureNames,0);
         gl11.glBindTexture(GL11.GL_TEXTURE_2D, textureNames[0]);

         Bitmap img = Bitmap.getBitmapResource("bg.png");
         GLUtils.glTexImage2D(gl11,0,GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, img, null);
         gl11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, 
           GL11.GL_LINEAR);
         gl11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, 
           GL11.GL_LINEAR);
         
         gl11.glEnable(GL11.GL_TEXTURE_2D);
     }
     
     protected void render(GL gl)
     {
         GL11 gl11 = (GL11)gl;
         gl11.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

         ((GL11Ext)gl11).glDrawTexiOES(0, 0, 0, 256,256);
     }
     
     public MyGLField(int ver)
     {
         super(ver);
     }
 }
 
 
class HomeScreen extends MainScreen
 {
     public HomeScreen()
     {
         add(new MyGLField(GLField.VERSION_1_1));
         add(new LabelField("OpenGL ES field"));
     }
 }

Code sample: Loading a 3-D perspective projection matrix

You can use the GLUtils class to load a 3-D perspective projection matrix.

public void sizeChanged(GL10 gl, int width, int height)
{
    // Update our viewport to reflect the new size
    gl.glViewport(0, 0, width, height);

    // Setup a perspective projection
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();
    net.rim.device.api.opengles.GLUtils.gluPerspective
        (gl, 45.0f, (float)width/(float)height, 0.15f, 100.0f);
}

Code sample: Using fixed-point arithmetic

The following code sample demonstrates how you can and cannot use the Fixed32 class for addition, subtraction, multiplication, division, value conversion, and trigonometry.

import net.rim.device.api.math.*;

public class demoFixed32
{
    demoFixed32()
    {
        // convert an integer to fixed-point
        int n = Fixed32.toFP(7); // 7.0

        // convert a quantity in ten-thousandths to fixed-point
        int m = Fixed32.tenThouToFP(70625); // 7.0625

        // convert from fixed-point, truncate fractional component
        int trunc = Fixed32.toInt(m); // 7
 
        // multiply by ten thousand
        int mult = Fixed32.toIntTenThou(m); // 70625

        // add, subtract, negate, and compare
        int result = m - n; // 7.0625 - 7.0 == 0.0625
        result = -result; // -0.0625
        result -= n; // -0.0625 - 7.0 == -7.0625
        boolean b = (result == -m); // true
        boolean bb = (m < n); // false

        // do not use increment and decrement operators
        result = Fixed32.toFP(2);
        ++result; // WRONG! result will NOT be 3

        result = Fixed32.toFP(2);
        result += Fixed32.toFP(1); // Correct: result will be 3

        // Use * and / when multiplying or dividing by an integer scalar
        // Use mul to multiply two fixed-point numbers
        // Use div to divide two fixed-point numbers
        m = Fixed32.tenThouToFP(12500); // 1.25
        m *= 3; // OK: 1.25 * 3 == 3.75
        m /= 2; // OK: 3.75 / 2 == 1.875
        m = Fixed32.mul(m, Fixed32.tenThouToFP(15000)); // 1.875 * 1.5000 == 2.8125
        m = Fixed32.div(m, m); // 2.8125 / 2.8125 == 1.0
 
        // mul, div, sqrt, sind, cosd, tand, and atand2
        // all work with 16.16 fixed-point numbers
        m = Fixed32.tenThouToFP(172500); // 17.2500
        n = Fixed32.sqrt(m); // sqrt(17.25)
        n = Fixed32.sind(m); // sine of 17.25 degrees
        n = Fixed32.cosd(m); // cosine of 17.25 degrees
        result = Fixed32.atand2(-m, -m); // -135.0 degrees in fixed-point
    }
}