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 2: M3G's retained mode

Easily manage 3D objects in scene graphs with JSR 184


Return to article


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

package m3gsamples2;

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

/**
 * Alignment of nodes and picking.
 *
 * @author Claus Hoefele
 */
public class AlignmentPickingSample extends Canvas implements Sample
{
  /** Name of the M3G file. */
  private static final String M3G_FILE_NAME = "/blender.m3g";
  
  /** User ID to identify the text mesh (1).*/
  private static final int USER_ID_TEXT = 1;
  
  /** Crosshair image file name (off).*/
  private static String CROSS_HAIR_OFF = "/crosshair_off.png";
  /** Crosshair image file name (on).*/
  private static String CROSS_HAIR_ON  = "/crosshair_on.png";
  
  /** State of crosshair.*/
  private boolean _crossHairOn;
  
  /** Image object for crosshair (on).*/
  private Image _crossHairImageOn;
  /** Image object for crosshair (off).*/
  private Image _crossHairImageOff;
  
  /** Object that represents the 3D world. */
  private World _world;
  
  /** 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();

    try
    {
      // Load World from M3G binary file.
      Object3D[] objects = Loader.load(M3G_FILE_NAME);
      _world = (World) objects[0];
      
      // Change the camera's properties to match the current device.
      Camera camera = _world.getActiveCamera();
      float aspect = (float) getWidth() / (float) getHeight();
      camera.setPerspective(60.0f, aspect, 1.0f, 1000.0f);

      // Align the text with the camera.
      Mesh text = (Mesh) _world.find(USER_ID_TEXT);
      text.setAlignment(camera, Node.Y_AXIS, null, Node.NONE);
      text.align(null);
      
      // Load crosshair images.
      try
      {
        _crossHairImageOn = Image.createImage(CROSS_HAIR_ON);
        _crossHairImageOff = Image.createImage(CROSS_HAIR_OFF);
      }
      catch (Exception e) {}
    }
    catch (Exception e)
    {
      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.render(_world);
    _graphics3d.releaseTarget();
    
    // Draw crosshair.
    if ((_crossHairImageOff != null) && (_crossHairImageOn != null))
    {
      graphics.drawImage(_crossHairOn ? _crossHairImageOn : _crossHairImageOff, 
              getWidth()/2, getHeight()/2, Graphics.VCENTER | Graphics.HCENTER);
    }

    drawMenu(graphics);
  }
  
  /**
   * Draws a menu on the screen.
   *
   * @param graphics graphics context.
   */
  private void drawMenu(Graphics graphics)
  {
    graphics.setColor(0xFFFFFFFF);
    int fontHeight = graphics.getFont().getHeight();
    int height = getHeight() - fontHeight;
    graphics.drawString(getKeyName(getKeyCode(LEFT)) + ": Rotate left", 0, 
            height, 0);
    height -= fontHeight;
    graphics.drawString(getKeyName(getKeyCode(RIGHT)) + ": Rotate right", 0, 
            height, 0);
    height -= fontHeight;
    graphics.drawString(getKeyName(getKeyCode(FIRE)) + ": Reset", 0, 
            height, 0);
  }
  
  /**
   * 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);
  }

  /**
   * Handles key presses.
   *
   * @param keyCode key code.
   */
  protected void keyPressed(int keyCode)
  {
    Mesh text = (Mesh) _world.find(USER_ID_TEXT);
    
    switch (getGameAction(keyCode))
    {
      case LEFT:
        text.postRotate(2.0f, 0.0f, 0.0f, 1.0f);
        break;

      case RIGHT:
        text.postRotate(-2.0f, 0.0f, 0.0f, 1.0f);
        break;

      case FIRE:
        init();
        break;

      // no default
    }

    // Check whether ray cast by crosshair intersects with the text.
    _crossHairOn = isHit();
    
    repaint();
  }
  
  /**
   * Checks whether a ray that orginates in the middle of the viewport and has 
   * the same direction as the active camera intersects with the text.
   * 
   * @return true if there's an intersection.
   */
  private boolean isHit()
  {
    boolean isHit = false;
    RayIntersection rayIntersection = new RayIntersection();
    Mesh mesh = (Mesh) _world.find(USER_ID_TEXT);
    
    if (_world.pick(-1, 0.5f, 0.5f, _world.getActiveCamera(), rayIntersection))
    {
      if (rayIntersection.getIntersected() == mesh)
      {
        isHit = true;
      }
    }
    
    return isHit;
  }
  
  /**
   * 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 "Alignment and Picking";
  }
}


Return to article