Skip to main content

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

The first time you sign into developerWorks, a profile is created for you. 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.

  • Close [x]

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.

  • Close [x]

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 displaying a lit cube with material properties.
 *
 * @author Claus Hoefele
 */
public class LightingMaterialsSample 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
  };

  /** The cube"s normals. */
  private static final byte[] VERTEX_NORMALS = {
    0, 0,  127,   0, 0,  127,   0, 0,  127,   0, 0,  127, // front
    0, 0, -128,   0, 0, -128,   0, 0, -128,   0, 0, -128, // back
     127, 0, 0,    127, 0, 0,    127, 0, 0,    127, 0, 0, // right
    -128, 0, 0,   -128, 0, 0,   -128, 0, 0,   -128, 0, 0, // left
    0,  127, 0,   0,  127, 0,   0,  127, 0,   0,  127, 0, // top
    0, -128, 0,   0, -128, 0,   0, -128, 0,   0, -128, 0, // 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
  };

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

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

  /** Light for the scene. */
  private Light _light;

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

  /** Light mode ambient (0). */
  private static final int LIGHT_AMBIENT      = 0;
  /** Light mode directional (1). */
  private static final int LIGHT_DIRECTIONAL  = 1;
  /** Light mode omni (2). */
  private static final int LIGHT_OMNI         = 2;
  /** Light mode spot (3). */
  private static final int LIGHT_SPOT         = 3;

  /** Current light mode. */
  private int _lightMode;

  /** Color target default (0). */
  private static final int COLOR_DEFAULT      = 0;
  /** Color target ambient (1). */
  private static final int COLOR_AMBIENT      = 1;
  /** Color target diffuse (2). */
  private static final int COLOR_DIFFUSE      = 2;
  /** Color target emissive (3). */
  private static final int COLOR_EMISSIVE     = 3;
  /** Color target specular (4). */
  private static final int COLOR_SPECULAR     = 4;

  /** Current color target. */
  private int _colorTarget;

  /**
   * 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 vertexNormals =
        new VertexArray(VERTEX_NORMALS.length/3, 3, 1);
    vertexNormals.set(0, VERTEX_NORMALS.length/3, VERTEX_NORMALS);
    _cubeVertexData.setNormals(vertexNormals);

    // 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 3 sides.
    _cubeTransform = new Transform();
    _cubeTransform.postRotate(45.0f, 1.0f, 0.0f, 0.0f);
    _cubeTransform.postRotate(45.0f, 0.0f, 1.0f, 0.0f);

    // Create appearance and the material.
    _cubeAppearance = new Appearance();
    _colorTarget = COLOR_DEFAULT;
    setMaterial(_cubeAppearance, _colorTarget);

    // Create light.
    _light = new Light();
    _lightMode = LIGHT_OMNI;
    setLightMode(_light, _lightMode);
    Transform lightTransform = new Transform();
    lightTransform.postTranslate(0.0f, 0.0f, 3.0f);
    _graphics3d.resetLights();
    _graphics3d.addLight(_light, lightTransform);
  }

  /**
   * Sets the light mode.
   *
   * @param light light to be modified.
   * @param mode light mode.
   */
  protected void setLightMode(Light light, int mode)
  {
    switch (mode)
    {
      case LIGHT_AMBIENT:
        light.setMode(Light.AMBIENT);
        light.setIntensity(2.0f);
        break;

      case LIGHT_DIRECTIONAL:
        light.setMode(Light.DIRECTIONAL);
        light.setIntensity(1.0f);
        break;

      case LIGHT_OMNI:
        light.setMode(Light.OMNI);
        light.setIntensity(2.0f);
        break;

      case LIGHT_SPOT:
        light.setMode(Light.SPOT);
        light.setSpotAngle(20.0f);
        light.setIntensity(2.0f);
        break;

      // no default
    }
  }

  /**
   * Sets the material according to the given target.
   *
   * @param appearance appearance to be modified.
   * @param colorTarget target color.
   */
  protected void setMaterial(Appearance appearance, int colorTarget)
  {
    Material material = new Material();

    switch (colorTarget)
    {
      case COLOR_DEFAULT:
        break;

      case COLOR_AMBIENT:
        material.setColor(Material.AMBIENT, 0x00FF0000);
        break;

      case COLOR_DIFFUSE:
        material.setColor(Material.DIFFUSE, 0x00FF0000);
        break;

      case COLOR_EMISSIVE:
        material.setColor(Material.EMISSIVE, 0x00FF0000);
        break;

      case COLOR_SPECULAR:
        material.setColor(Material.SPECULAR, 0x00FF0000);
        material.setShininess(2);
        break;

      // no default
    }

    appearance.setMaterial(material);
  }

  /**
   * 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;
    switch (_light.getMode())
    {
      case Light.AMBIENT:
        graphics.drawString(getKeyName(getKeyCode(GAME_B)) +
            ": Light: ambient", 0, height, 0);
        break;

      case Light.DIRECTIONAL:
        graphics.drawString(getKeyName(getKeyCode(GAME_B)) +
            ": Light: directional", 0, height, 0);
        break;

      case Light.OMNI:
        graphics.drawString(getKeyName(getKeyCode(GAME_B)) +
            ": Light: omni", 0, height, 0);
        break;

      case Light.SPOT:
        graphics.drawString(getKeyName(getKeyCode(GAME_B)) +
            ": Light: spot", 0, height, 0);
        break;

      // no default
    }

    height -= fontHeight;
    switch (_colorTarget)
    {
      case COLOR_AMBIENT:
        graphics.drawString(getKeyName(getKeyCode(GAME_A)) +
            ": Color: ambient", 0, height, 0);
        break;

      case COLOR_DEFAULT:
        graphics.drawString(getKeyName(getKeyCode(GAME_A)) +
            ": Color: default", 0, height, 0);
        break;

      case COLOR_DIFFUSE:
        graphics.drawString(getKeyName(getKeyCode(GAME_A)) +
            ": Color: diffuse", 0, height, 0);
        break;

      case COLOR_EMISSIVE:
        graphics.drawString(getKeyName(getKeyCode(GAME_A)) +
            ": Color: emissive", 0, height, 0);
        break;

      case COLOR_SPECULAR:
        graphics.drawString(getKeyName(getKeyCode(GAME_A)) +
            ": Color: specular", 0, height, 0);
        break;

      // no default
    }
  }

  /**
   * 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:
        _colorTarget++;
        _colorTarget %= 5;
        setMaterial(_cubeAppearance, _colorTarget);
        break;

      case GAME_B:
        _lightMode++;
        _lightMode %= 4;
        setLightMode(_light, _lightMode);
        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 "Lighting and Materials";
  }
}

Return to article