java left logo
java middle logo
java right logo
 

Home arrow Other API Tips arrow Java3D arrow How to use directional lights in Java3D
 
 
Main Menu
Home
Java Tutorials
Book Reviews
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Sitemap
Java Network
Java Forums
Java Tips Blog




Most Visited Tips
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Book Reviews
Top Rated Tips
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Book Reviews


Statistics
Registered Users: 769
Java SE Tips: 614
Java ME Tips: 201
Java EE Tips: 184
Other API Tips: 779
Java Applications: 298
Java Libraries: 209
Java Games: 16
Book Reviews:
 
 
 
How to use directional lights in Java3D E-mail
User Rating: / 1
PoorBest 

This Java tip illustrates the use of directional lights in Java 3D scenes.


Image

import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.CheckboxMenuItem;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Frame;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.util.Enumeration;
import java.util.EventListener;

import javax.media.j3d.Appearance;
import javax.media.j3d.Behavior;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.Group;
import javax.media.j3d.Light;
import javax.media.j3d.LineArray;
import javax.media.j3d.LineAttributes;
import javax.media.j3d.Material;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.media.j3d.WakeupOnElapsedFrames;
import javax.media.j3d.WakeupOr;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Color3f;
import javax.vecmath.Matrix4d;
import javax.vecmath.Matrix4f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.geometry.Cone;
import com.sun.j3d.utils.geometry.Primitive;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.universe.PlatformGeometry;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.Viewer;
import com.sun.j3d.utils.universe.ViewingPlatform;

public class ExDirectionalLight extends Java3DFrame {
  //--------------------------------------------------------------
  //  SCENE CONTENT
  //--------------------------------------------------------------

  //
  //  Nodes (updated via menu)
  //
  private DirectionalLight light = null;

  //
  //  Build scene
  //
  public Group buildScene() {
    // Get the current color and direction
    Color3f color = (Color3fcolors[currentColor].value;
    Vector3f dir = (Vector3fdirections[currentDirection].value;

    // Turn off the example headlight
    setHeadlightEnable(false);

    // Build the scene group
    Group scene = new Group();

    // BEGIN EXAMPLE TOPIC
    // Create influencing bounds
    BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.00.0,
        0.0)// Center
        1000.0)// Extent

    // Set the light color and its influencing bounds
    light = new DirectionalLight();
    light.setEnable(lightOnOff);
    light.setColor(color);
    light.setDirection(dir);
    light.setCapability(DirectionalLight.ALLOW_STATE_WRITE);
    light.setCapability(DirectionalLight.ALLOW_COLOR_WRITE);
    light.setCapability(DirectionalLight.ALLOW_DIRECTION_WRITE);
    light.setInfluencingBounds(worldBounds);
    scene.addChild(light);
    // END EXAMPLE TOPIC

    // Build foreground geometry
    scene.addChild(new SphereGroup());

    // Add anotation arrows pointing in +-X, +-Y, +-Z to
    // illustrate aim direction
    scene.addChild(buildArrows());

    return scene;
  }

  //--------------------------------------------------------------
  //  FOREGROUND AND ANNOTATION CONTENT
  //--------------------------------------------------------------

  //
  //  Create a set of annotation arrows initially pointing in
  //  the +X direciton. Next, build an array of Transform3D's,
  //  one for each of the aim directions shown on the directions
  //  menu. Save these Transform3Ds and a top-level TransformGroup
  //  surrounding the arrows. Later, when the user selects a new
  //  light direction, we poke the corresponding Transform3D into
  //  the TransformGroup to cause the arrows to change direction.
  //
  private TransformGroup arrowDirectionTransformGroup = null;

  private Transform3D[] arrowDirectionTransforms = null;

  private Group buildArrows() {
    // Create a transform group surrounding the arrows.
    // Enable writing of its transform.
    arrowDirectionTransformGroup = new TransformGroup();
    arrowDirectionTransformGroup
        .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

    // Create a group of arrows and add the group to the
    // transform group. The arrows point in the +X direction.
    AnnotationArrowGroup ag = new AnnotationArrowGroup(-2.0f2.0f// X
        // start
        // and
        // end
        1.5f, -1.5f// Y start and end
        5)// Number of arrows
    arrowDirectionTransformGroup.addChild(ag);

    // Create a set of Transform3Ds for the different
    // arrow directions.
    arrowDirectionTransforms = new Transform3D[directions.length];
    Vector3f dir = new Vector3f();
    Vector3f positiveX = new Vector3f(1.0f0.0f0.0f);
    Vector3f axis = new Vector3f();
    float angle;
    float dot;

    for (int i = 0; i < directions.length; i++) {
      // Normalize the direction vector
      dir.normalize((Vector3fdirections[i].value);

      // Cross the direction vector with the arrow's
      // +X aim direction to get a vector orthogonal
      // to both. This is the rotation axis.
      axis.cross(positiveX, dir);
      if (axis.x == 0.0f && axis.y == 0.0f && axis.z == 0.0f) {
        // New direction is parallel to current
        // arrow direction. Default to a Y axis.
        axis.y = 1.0f;
      }

      // Compute the angle between the direction and +X
      // vectors, where:
      //
      //   cos(angle) = (dir dot positiveX)
      //                -------------------------------
      //                (positiveX.length * dir.length)
      //
      // but since positiveX is normalized (as created
      // above and dir has been normalized, both have a
      // length of 1. So, the angle between the
      // vectors is:
      //
      //   angle = arccos(dir dot positiveX)
      //
      dot = dir.dot(positiveX);
      angle = (floatMath.acos(dot);

      // Create a Transform3D, setting its rotation using
      // an AxisAngle4f, which takes an XYZ rotation vector
      // and an angle to rotate by around that vector.
      arrowDirectionTransforms[inew Transform3D();
      arrowDirectionTransforms[i].setRotation(new AxisAngle4f(axis.x,
          axis.y, axis.z, angle));
    }

    // Set the initial transform to be the current aim direction.
    arrowDirectionTransformGroup
        .setTransform(arrowDirectionTransforms[currentDirection]);

    return arrowDirectionTransformGroup;
  }

  //--------------------------------------------------------------
  //  USER INTERFACE
  //--------------------------------------------------------------

  //
  //  Main
  //
  public static void main(String[] args) {
    ExDirectionalLight ex = new ExDirectionalLight();
    ex.initialize(args);
    ex.buildUniverse();
    ex.showFrame();
  }

  //  On/off choices
  private boolean lightOnOff = true;

  private CheckboxMenuItem lightOnOffMenu = null;

  //  Color menu choices
  private NameValue[] colors = new NameValue("White", White),
      new NameValue("Gray", Gray)new NameValue("Black", Black),
      new NameValue("Red", Red)new NameValue("Yellow", Yellow),
      new NameValue("Green", Green)new NameValue("Cyan", Cyan),
      new NameValue("Blue", Blue)new NameValue("Magenta", Magenta)};

  private int currentColor = 0;

  private CheckboxMenu colorMenu = null;

  //  Direction menu choices
  private NameValue[] directions = new NameValue("Positive X", PosX),
      new NameValue("Negative X", NegX),
      new NameValue("Positive Y", PosY),
      new NameValue("Negative Y", NegY),
      new NameValue("Positive Z", PosZ),
      new NameValue("Negative Z", NegZ)};

  private int currentDirection = 0;

  private CheckboxMenu directionMenu = null;

  //
  //  Initialize the GUI (application and applet)
  //
  public void initialize(String[] args) {
    // Initialize the window, menubar, etc.
    super.initialize(args);
    exampleFrame.setTitle("Java 3D Directional Light Example");

    //
    //  Add a menubar menu to change node parameters
    //    Light on/off
    //    Color -->
    //    Direction -->
    //

    Menu m = new Menu("DirectionalLight");

    lightOnOffMenu = new CheckboxMenuItem("Light on/off", lightOnOff);
    lightOnOffMenu.addItemListener(this);
    m.add(lightOnOffMenu);

    colorMenu = new CheckboxMenu("Color", colors, currentColor, this);
    m.add(colorMenu);

    directionMenu = new CheckboxMenu("Direction", directions,
        currentDirection, this);
    m.add(directionMenu);

    exampleMenuBar.add(m);
  }

  //
  //  Handle checkboxes and menu choices
  //
  public void checkboxChanged(CheckboxMenu menu, int check) {
    if (menu == colorMenu) {
      // Change the light color
      currentColor = check;
      Color3f color = (Color3fcolors[check].value;
      light.setColor(color);
      return;
    }
    if (menu == directionMenu) {
      // Change the light direction
      currentDirection = check;
      Vector3f dir = (Vector3fdirections[check].value;
      light.setDirection(dir);

      // Change the arrow group direction
      arrowDirectionTransformGroup
          .setTransform(arrowDirectionTransforms[check]);
      return;
    }

    // Handle all other checkboxes
    super.checkboxChanged(menu, check);
  }

  public void itemStateChanged(ItemEvent event) {
    Object src = event.getSource();
    if (src == lightOnOffMenu) {
      // Turn the light on or off
      lightOnOff = lightOnOffMenu.getState();
      light.setEnable(lightOnOff);
      return;
    }

    // Handle all other checkboxes
    super.itemStateChanged(event);
  }
}

//
//CLASS
//AnnotationArrowGroup - A group of parallel arrows
//
//DESCRIPTION
//This class creates one or more parallel 3D, unlighted arrows.
//Such arrow groups can be used to indicate directional light
//directions, and so forth.
//
//The arrow group is drawn in the XY plane, pointing right.
//The X start and end values, and the Y start and end values
//can be set, along with the count of the number of arrows to
//build.
//
//SEE ALSO
//AnnotationArrow
//AnnotationArrowFan
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
//

class AnnotationArrowGroup extends Group {
  // 3D nodes
  AnnotationArrow[] arrows;

  //  Constructors
  public AnnotationArrowGroup() {
    //    xStart xEnd yStart yEnd count
    this(-1.0f1.0f1.0f, -1.0f3);
  }

  public AnnotationArrowGroup(float xStart, float xEnd, float yStart,
      float yEnd, int count) {
    arrows = new AnnotationArrow[count];
    float y = yStart;
    float deltaY = (yEnd - yStart(float) (count - 1);
    for (int i = 0; i < count; i++) {
      arrows[inew AnnotationArrow(xStart, y, 0.0f, xEnd, y, 0.0f);
      addChild(arrows[i]);
      y += deltaY;
    }
  }
}

//
//CLASS
//AnnotationArrow - 3D arrow used for annotation & diagrams
//
//DESCRIPTION
//This class creates a 3D, unlighted line between two 3D coordinates
//plus a cone-shaped arrow at the line's endpoint. The line's width
//and color can be controlled. The arrow head's width and length
//can be controlled.
//
//SEE ALSO
//AnnotationLine
//AnnotationAxes
//AnnotationArrowFan
//AnnotationArrowGroup
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//

class AnnotationArrow extends AnnotationLine {
  // Parameters
  private Color3f arrowColor = new Color3f(1.0f1.0f1.0f);

  private float arrowRadius = 0.1f;

  private float arrowLength = 0.20f;

  private float lineWidth = 3.0f;

  private int radialDivisions = 8;

  private int sideDivisions = 1;

  // 3D Nodes
  private Cone arrowHead = null;

  private Appearance arrowAppearance = null;

  private TransformGroup arrowTrans = null;

  private ColoringAttributes coloringAttributes = null;

  //
  //  Construct a straight line
  //
  public AnnotationArrow(float x2, float y2, float z2) {
    //    origin to given coordinate
    this(0.0f0.0f0.0f, x2, y2, z2);
  }

  public AnnotationArrow(float x, float y, float z, float x2, float y2,
      float z2) {
    super(x, y, z, x2, y2, z2);
    setLineWidth(lineWidth);

    // Compute the length and direction of the line
    float deltaX = x2 - x;
    float deltaY = y2 - y;
    float deltaZ = z2 - z;

    float theta = -(floatMath.atan2(deltaZ, deltaX);
    float phi = (floatMath.atan2(deltaY, deltaX);
    if (deltaX < 0.0f) {
      phi = (floatMath.PI - phi;
    }

    // Compute a matrix to rotate a cone to point in the line's
    // direction, then place the cone at the line's endpoint.
    Matrix4f mat = new Matrix4f();
    Matrix4f mat2 = new Matrix4f();
    mat.setIdentity();

    // Move to the endpoint of the line
    mat2.setIdentity();
    mat2.setTranslation(new Vector3f(x2, y2, z2));
    mat.mul(mat2);

    // Spin around Y
    mat2.setIdentity();
    mat2.rotY(theta);
    mat.mul(mat2);

    // Tilt up or down around Z
    mat2.setIdentity();
    mat2.rotZ(phi);
    mat.mul(mat2);

    // Tilt cone to point right
    mat2.setIdentity();
    mat2.rotZ(-1.571f);
    mat.mul(mat2);

    arrowTrans = new TransformGroup();
    arrowTrans.setCapability(Group.ALLOW_CHILDREN_WRITE);
    Transform3D trans = new Transform3D(mat);
    arrowTrans.setTransform(trans);

    // Create an appearance
    arrowAppearance = new Appearance();
    arrowAppearance
        .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);

    getLineColor(arrowColor);
    coloringAttributes = new ColoringAttributes();
    coloringAttributes.setColor(arrowColor);
    coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
    arrowAppearance.setColoringAttributes(coloringAttributes);

    // Build a cone for the arrow head
    arrowHead = new Cone(arrowRadius, // base radius
        arrowLength, // height
        0// don't generate normals
        radialDivisions, // divisions radially
        sideDivisions, // divisions vertically
        arrowAppearance)// appearance

    arrowTrans.addChild(arrowHead);
    addChild(arrowTrans);
  }

  //
  //  Control the arrow head size
  //
  public void setArrowHeadRadius(float radius) {
    arrowRadius = radius;

    arrowTrans.removeChild(0);
    arrowHead = new Cone(arrowRadius, // base radius
        arrowLength, // height
        0// don't generate normals
        radialDivisions, // divisions radially
        sideDivisions, // divisions vertically
        arrowAppearance)// appearance
    arrowTrans.addChild(arrowHead);
  }

  public void setArrowHeadLength(float length) {
    arrowLength = length;

    arrowTrans.removeChild(0);
    arrowHead = new Cone(arrowRadius, // base radius
        arrowLength, // height
        0// don't generate normals
        radialDivisions, // divisions radially
        sideDivisions, // divisions vertically
        arrowAppearance)// appearance
    arrowTrans.addChild(arrowHead);
  }

  public float getArrowHeadRadius() {
    return arrowRadius;
  }

  public float getArrowHeadLength() {
    return arrowLength;
  }

  //
  //  Control the line color
  //
  public void setLineColor(Color3f color) {
    super.setLineColor(color);

    getLineColor(arrowColor);
    coloringAttributes.setColor(arrowColor);
    arrowAppearance.setColoringAttributes(coloringAttributes);
    arrowHead.setAppearance(arrowAppearance);
  }

  public void setLineColor(float r, float g, float b) {
    super.setLineColor(r, g, b);

    getLineColor(arrowColor);
    coloringAttributes.setColor(arrowColor);
    arrowAppearance.setColoringAttributes(coloringAttributes);
    arrowHead.setAppearance(arrowAppearance);
  }

  public void setLineColor(float[] color) {
    super.setLineColor(color);

    getLineColor(arrowColor);
    coloringAttributes.setColor(arrowColor);
    arrowAppearance.setColoringAttributes(coloringAttributes);
    arrowHead.setAppearance(arrowAppearance);
  }

  //
  //  Control the appearance
  //
  public void setAppearance(Appearance app) {
    super.setAppearance(app);

    arrowAppearance = app;
    arrowAppearance
        .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
    arrowAppearance.setColoringAttributes(coloringAttributes);
    arrowHead.setAppearance(arrowAppearance);
  }

  //
  //  Provide info on the shape and geometry
  //
  public Shape3D getShape(int partid) {
    if (partid == Cone.BODY)
      return arrowHead.getShape(Cone.BODY);
    else if (partid == Cone.CAP)
      return arrowHead.getShape(Cone.CAP);
    else
      return super.getShape(partid);
  }

  public int getNumTriangles() {
    return arrowHead.getNumTriangles();
  }

  public int getNumVertices() {
    return arrowHead.getNumVertices() super.getNumVertices();
  }
}

//
//CLASS
//AnnotationLine - 3D line used for annotation & diagrams
//
//DESCRIPTION
//This class creates a 3D, unlighted line between two 3D coordinates.
//The line's width and color can be controlled.
//
//SEE ALSO
//AnnotationArrow
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
//

class AnnotationLine extends Primitive {
  // Parameters
  private float lineWidth = 1;

  private Color3f lineColor = new Color3f(1.0f1.0f1.0f);

  // 3D nodes
  private Shape3D shape = null;

  private LineAttributes lineAttributes = null;

  private ColoringAttributes coloringAttributes = null;

  private LineArray line = null;

  protected Appearance mainAppearance = null;

  //
  //  Construct a straight line
  //
  public AnnotationLine(float x2, float y2, float z2) {
    //    origin to given coordinate
    this(0.0f0.0f0.0f, x2, y2, z2);
  }

  public AnnotationLine(float x, float y, float z, float x2, float y2,
      float z2) {
    float[] coord = new float[3];
    float[] texcoord = new float[2];

    // Build a shape
    shape = new Shape3D();
    shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);

    // Create geometry for a 2-vertex straight line
    line = new LineArray(2, GeometryArray.COORDINATES
        | GeometryArray.TEXTURE_COORDINATE_2);
    line.setCapability(GeometryArray.ALLOW_COLOR_WRITE);

    // Starting point
    coord[0= x;
    coord[1= y;
    coord[2= z;
    texcoord[00.0f;
    texcoord[10.0f;
    line.setCoordinate(0, coord);
    line.setTextureCoordinate(0, texcoord);

    // Ending point
    coord[0= x2;
    coord[1= y2;
    coord[2= z2;
    texcoord[01.0f;
    texcoord[10.0f;
    line.setCoordinate(1, coord);
    line.setTextureCoordinate(1, texcoord);

    shape.setGeometry(line);

    // Create an appearance
    mainAppearance = new Appearance();
    mainAppearance.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
    mainAppearance
        .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);

    lineAttributes = new LineAttributes();
    lineAttributes.setLineWidth(lineWidth);
    mainAppearance.setLineAttributes(lineAttributes);

    coloringAttributes = new ColoringAttributes();
    coloringAttributes.setColor(lineColor);
    coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
    mainAppearance.setColoringAttributes(coloringAttributes);

    addChild(shape);
  }

  //
  //  Control the line width
  //
  public float getLineWidth() {
    return lineWidth;
  }

  public void setLineWidth(float width) {
    lineWidth = width;
    lineAttributes.setLineWidth(