Skip to main content

If you don't have an IBM ID and password, register here.

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

3D graphics for Java mobile devices, Part 1: M3G's immediate mode

Create 3D scenes with JSR 184


Return to article


/*
 * Sample code for M3G article on IBM developerWorks.
 * http://www.ibm.com/developerworks/
 */

package m3gsamples1;

import javax.microedition.lcdui.*;
import javax.microedition.m3g.*;

/**
 * Sample demonstrating depth buffer and projection.
 *
 * @author Claus Hoefele
 */
public class DepthBufferProjectionSample extends Canvas
    implements Sample
{
  /** The cube"s vertex positions (x, y, z). */
  private static final byte[] VERTEX_POSITIONS = {
    -1, -1,  1,    1, -1,  1,   -1,  1,  1,    1,  1,  1,
    -1, -1, -1,    1, -1, -1,   -1,  1, -1,    1,  1, -1
  };

  /** Indices that define how to connect the vertices to build 
   * triangles. */
  private static int[] TRIANGLE_INDICES = {
    0, 1, 2, 3, 7, 1, 5, 4, 7, 6, 2, 4, 0, 1
  };

  /** The cube"s vertex data. */
  private VertexBuffer _cubeVertexData;

  /** The cube"s triangles defined as triangle strips. */
  private TriangleStripArray _cubeTriangles;

  /** Graphics singleton used for rendering. */
  private Graphics3D _graphics3d;

  /** Camera with perspective projection. */
  private Camera _cameraPerspective;

  /** The camera"s transformation. */
  private Transform _cameraTransform;

  /** Camera with parallel projection. */
  private Camera _cameraParallel;

  /** Flag whether perspective or parallel projection is used. */
  private boolean _isPerspective;

  /** Flag whether depth buffer is enabled. */
  private boolean _isDepthBufferEnabled;

  /**
   * Called when this sample is displayed.
   */
  public void showNotify()
  {
    init();
  }

  /**
   * Initializes the sample.
   */
  protected void init()
  {
    // Get the singleton for 3D rendering.
    _graphics3d = Graphics3D.getInstance();

    // Create vertex data.
    _cubeVertexData = new VertexBuffer();

    VertexArray vertexPositions =
        new VertexArray(VERTEX_POSITIONS.length/3, 3, 1);
    vertexPositions.set(0, VERTEX_POSITIONS.length/3, VERTEX_POSITIONS);
    _cubeVertexData.setPositions(vertexPositions, 1.0f, null);

    // Create the triangles that define the cube; the indices point to
    // vertices in VERTEX_POSITIONS.
    _cubeTriangles = new TriangleStripArray(TRIANGLE_INDICES,
        new int[] {TRIANGLE_INDICES.length});

    // Create parallel and perspective cameras.
    _cameraPerspective = new Camera();
    float aspect = (float) getWidth() / (float) getHeight();
    _cameraPerspective.setPerspective(30.0f, aspect, 1.0f, 1000.0f);
    _cameraTransform = new Transform();
    _cameraTransform.postTranslate(0.0f, 0.0f, 10.0f);

    _cameraParallel = new Camera();
    _cameraParallel.setParallel(5.0f, aspect, 1.0f, 1000.0f);

    _graphics3d.setCamera(_cameraPerspective, _cameraTransform);
    _isPerspective = true;

    // Enable depth buffer.
    _isDepthBufferEnabled = true;
  }

  /**
   * Renders the sample on the screen.
   *
   * @param graphics the graphics object to draw on.
   */
  protected void paint(Graphics graphics)
  {
    // Create transformation objects for the cubes.
    Transform origin = new Transform();
    Transform behindOrigin = new Transform(origin);
    behindOrigin.postTranslate(-1.0f, 0.0f, -1.0f);
    Transform inFrontOfOrigin = new Transform(origin);
    inFrontOfOrigin.postTranslate(1.0f, 0.0f, 1.0f);

    // Disable or enable depth buffering when target is bound.
    _graphics3d.bindTarget(graphics, _isDepthBufferEnabled, 0);
    _graphics3d.clear(null);

    // Draw cubes front to back. If the depth buffer is enabled, 
    // they will be drawn according to their z coordinate. Otherwise,
    // according to the order of rendering.
    _cubeVertexData.setDefaultColor(0x00FF0000);
    _graphics3d.render(_cubeVertexData, _cubeTriangles,
        new Appearance(), inFrontOfOrigin);
    _cubeVertexData.setDefaultColor(0x0000FF00);
    _graphics3d.render(_cubeVertexData, _cubeTriangles,
        new Appearance(), origin);
    _cubeVertexData.setDefaultColor(0x000000FF);
    _graphics3d.render(_cubeVertexData, _cubeTriangles,
        new Appearance(), behindOrigin);

    _graphics3d.releaseTarget();

    drawMenu(graphics);
  }

  /**
   * Draws a menu on the screen.
   *
   * @param graphics graphics context.
   */
  protected void drawMenu(Graphics graphics)
  {
    graphics.setColor(0xFFFFFFFF);
    int fontHeight = graphics.getFont().getHeight();

    // Upper half menu.
    int height = 0;
    graphics.drawString(getKeyName(getKeyCode(FIRE)) + ": Reset",
        0, height, 0);

    // Lower half menu.
    height = getHeight() - fontHeight;
    if (_isDepthBufferEnabled)
    {
      graphics.drawString(getKeyName(getKeyCode(GAME_B)) +
          ": Depth Buffer: on", 0, height, 0);
    }
    else
    {
      graphics.drawString(getKeyName(getKeyCode(GAME_B)) +
          ": Depth Buffer: off", 0, height, 0);
    }

    height -= fontHeight;
    if (_isPerspective)
    {
      graphics.drawString(getKeyName(getKeyCode(GAME_A)) +
          ": Projection: perspective", 0, height, 0);
    }
    else
    {
      graphics.drawString(getKeyName(getKeyCode(GAME_A)) +
          ": Projection: parallel", 0, height, 0);
    }
  }

  /**
   * Handles key presses.
   *
   * @param keyCode key code.
   */
  protected void keyPressed(int keyCode)
  {
    switch (getGameAction(keyCode))
    {
      case GAME_A:
        _isPerspective = !_isPerspective;
        if (_isPerspective)
        {
          _graphics3d.setCamera(_cameraPerspective, _cameraTransform);
        }
        else
        {
          _graphics3d.setCamera(_cameraParallel, _cameraTransform);
        }
        break;

      case GAME_B:
        _isDepthBufferEnabled = !_isDepthBufferEnabled;
        break;

      case FIRE:
        init();
        break;

      // no default
    }

    repaint();
  }

  /**
   * Returns the <code>Displayable</code> used to display this sample.
   *
   * @return display
   */
  public Displayable getDisplayable()
  {
    return this;
  }

  /**
   * Returns the display name of this sample.
   *
   * @return name
   */
  public String getName()
  {
    return "Depth Buffer and View Projection";
  }
}

Return to article