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 java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.m3g.*;

/**
 * Sample displaying a lit cube with material properties.
 *
 * @author Claus Hoefele
 */
public class TexturesSample 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, // front
     1, -1, -1,   -1, -1, -1,    1,  1, -1,   -1,  1, -1, // back
     1, -1,  1,    1, -1, -1,    1,  1,  1,    1,  1, -1, // right
    -1, -1, -1,   -1, -1,  1,   -1,  1, -1,   -1,  1,  1, // left
    -1,  1,  1,    1,  1,  1,   -1,  1, -1,    1,  1, -1, // top
    -1, -1, -1,    1, -1, -1,   -1, -1,  1,    1, -1,  1  // bottom
  };

  /** Indices that define how to connect the vertices to build 
   * triangles. */
  private static final int[] TRIANGLE_INDICES = {
     0,  1,  2,  3,   // front
     4,  5,  6,  7,   // back
     8,  9, 10, 11,   // right
    12, 13, 14, 15,   // left
    16, 17, 18, 19,   // top
    20, 21, 22, 23,   // bottom
  };

  /** Lengths of triangle strips in TRIANGLE_INDICES. */
  private static int[] TRIANGLE_LENGTHS = {
    4, 4, 4, 4, 4, 4
  };

  /** File name of the texture. */
  private static final String TEXTURE_FILE = "/texture.png";

  /** The texture coordinates (s, t) that define how to map the 
   * texture to the cube. */
  private static final byte[] VERTEX_TEXTURE_COORDINATES = {
    0, 1,   1, 1,   0, 0,   1, 0,   // front
    0, 1,   1, 1,   0, 0,   1, 0,   // back
    0, 1,   1, 1,   0, 0,   1, 0,   // right
    0, 1,   1, 1,   0, 0,   1, 0,   // left
    0, 1,   1, 1,   0, 0,   1, 0,   // top
    0, 1,   1, 1,   0, 0,   1, 0,   // bottom
  };

  /** First color for blending. */
  private static final int COLOR_0 = 0x000000FF;

  /** Second color for blending. */
  private static final int COLOR_1 = 0x0000FF00;

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

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

  /** The cube"s transformation. */
  private Transform _cubeTransform;

  /** Our cube"s appearance. */
  private Appearance _cubeAppearance;

  /** Polygon mode component of appearance that encapsulates per-face
   * attributes. */
  private PolygonMode _polygonMode;

  /** Stores whether perspective correction is enabled or not. */
  private boolean _isPerspectiveCorrectionEnabled;

  /** The cube"s texture. */
  private Texture2D _cubeTexture;

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

  /**
   * 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);

    VertexArray vertexTextureCoordinates =
        new VertexArray(VERTEX_TEXTURE_COORDINATES.length/2, 2, 1);
    vertexTextureCoordinates.set(0,
        VERTEX_TEXTURE_COORDINATES.length/2, VERTEX_TEXTURE_COORDINATES);
    _cubeVertexData.setTexCoords(0, vertexTextureCoordinates, 2.0f, null);

    // Set default color for cube.
    _cubeVertexData.setDefaultColor(COLOR_0);

    // Create the triangles that define the cube; the indices point to
    // vertices in VERTEX_POSITIONS.
    _cubeTriangles = new TriangleStripArray(TRIANGLE_INDICES,
        TRIANGLE_LENGTHS);

    // Create a camera with perspective projection.
    Camera camera = new Camera();
    float aspect = (float) getWidth() / (float) getHeight();
    camera.setPerspective(30.0f, aspect, 1.0f, 1000.0f);
    Transform cameraTransform = new Transform();
    cameraTransform.postTranslate(0.0f, 0.0f, 10.0f);
    _graphics3d.setCamera(camera, cameraTransform);

    // Rotate the cube so we can see three sides.
    _cubeTransform = new Transform();
    _cubeTransform.postRotate(20.0f, 1.0f, 0.0f, 0.0f);
    _cubeTransform.postRotate(45.0f, 0.0f, 1.0f, 0.0f);

    // Define an appearance object and set the polygon mode.
    _cubeAppearance = new Appearance();
    _polygonMode = new PolygonMode();
    _isPerspectiveCorrectionEnabled = false;
    _cubeAppearance.setPolygonMode(_polygonMode);

    try
    {
      // Load image for texture and assign it to the appearance. The
      // default values are: WRAP_REPEAT, FILTER_BASE_LEVEL/
      // FILTER_NEAREST, and FUNC_MODULATE.
      Image2D image2D = (Image2D) Loader.load(TEXTURE_FILE)[0];
      _cubeTexture = new Texture2D(image2D);
      _cubeTexture.setBlending(Texture2D.FUNC_DECAL);

      // Index 0 is used because we have only one texture.
      _cubeAppearance.setTexture(0, _cubeTexture);
    }
    catch (Exception e)
    {
      System.out.println("Error loading image " + TEXTURE_FILE);
      e.printStackTrace();
    }
  }

  /**
   * Renders the sample on the screen.
   *
   * @param graphics the graphics object to draw on.
   */
  protected void paint(Graphics graphics)
  {
    _graphics3d.bindTarget(graphics);
    _graphics3d.clear(null);
    _graphics3d.render(_cubeVertexData, _cubeTriangles,
        _cubeAppearance, _cubeTransform);
    _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);
    height += fontHeight;
    graphics.drawString(
        getKeyName(getKeyCode(LEFT)) + "/" +
        getKeyName(getKeyCode(RIGHT)) + ": Rotate y ", 0, height, 0);

    // Lower half menu.
    height = getHeight() - fontHeight;
    if (_cubeVertexData.getDefaultColor() == COLOR_0)
    {
      graphics.drawString(getKeyName(getKeyCode(GAME_C)) +
          ": Color: blue", 0, height, 0);
    }
    else
    {
      graphics.drawString(getKeyName(getKeyCode(GAME_C)) +
          ": Color: green", 0, height, 0);
    }

    height -= fontHeight;
    if (_cubeTexture.getWrappingS() == Texture2D.WRAP_CLAMP)
    {
      graphics.drawString(getKeyName(getKeyCode(GAME_B)) +
          ": Wrapping: clamp", 0, height, 0);
    }
    else
    {
      graphics.drawString(getKeyName(getKeyCode(GAME_B)) +
          ": Wrapping: repeat", 0, height, 0);
    }

    height -= fontHeight;
    if (isPerspectiveCorrectionSupported())
    {
      if (_isPerspectiveCorrectionEnabled)
      {
        graphics.drawString(getKeyName(getKeyCode(GAME_A)) +
            ": Persp. correction: on", 0, height, 0);
      }
      else
      {
        graphics.drawString(getKeyName(getKeyCode(GAME_A)) +
            ": Persp. correction: off", 0, height, 0);
      }
    }
    else
    {
      graphics.drawString(getKeyName(getKeyCode(GAME_A)) +
          ": Persp. correction: not supported", 0, height, 0);
    }
  }

  /**
   * Checks whether perspective correction is supported.
   *
   * @return true if persepctive correction is supported, false otherwise.
   */
  protected boolean isPerspectiveCorrectionSupported()
  {
    Hashtable properties = Graphics3D.getProperties();
    Boolean supportPerspectiveCorrection =
        (Boolean) properties.get("supportPerspectiveCorrection");

    return supportPerspectiveCorrection.booleanValue();
  }

  /**
   * Handles key presses.
   *
   * @param keyCode key code.
   */
  protected void keyPressed(int keyCode)
  {
    switch (getGameAction(keyCode))
    {
      case LEFT:
        _cubeTransform.postRotate(-10.0f, 0.0f, 1.0f, 0.0f);
        break;

      case RIGHT:
        _cubeTransform.postRotate(10.0f, 0.0f, 1.0f, 0.0f);
        break;

      case FIRE:
        init();
        break;

      case GAME_A:
        if (isPerspectiveCorrectionSupported())
        {
          _isPerspectiveCorrectionEnabled = !_isPerspectiveCorrectionEnabled;
          _polygonMode.setPerspectiveCorrectionEnable(
              _isPerspectiveCorrectionEnabled);
        }
        break;

      case GAME_B:
        if (_cubeTexture.getWrappingS() == Texture2D.WRAP_CLAMP)
        {
          _cubeTexture.setWrapping(Texture2D.WRAP_REPEAT,
              Texture2D.WRAP_REPEAT);
        }
        else
        {
          _cubeTexture.setWrapping(Texture2D.WRAP_CLAMP,
              Texture2D.WRAP_CLAMP);
        }
        break;

      case GAME_C:
        if (_cubeVertexData.getDefaultColor() == COLOR_0)
        {
          _cubeVertexData.setDefaultColor(COLOR_1);
        }
        else
        {
          _cubeVertexData.setDefaultColor(COLOR_0);
        }
        break;

      // no default
    }

    repaint();
  }

  /**
   * Handles key repeat events for phones that support them. Redirects
   * all key input to <code>keyPressed()</code>.
   *
   * @param keyCode key code.
   */
  protected void keyRepeated(int keyCode)
  {
    keyPressed(keyCode);
  }

  /**
   * 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 "Textures";
  }
}

Return to article