java left logo
java middle logo
java right logo
 

Home arrow Other API Tips
 
 
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 spot lights in Java3D E-mail
User Rating: / 0
PoorBest 

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

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.SpotLight;
import javax.media.j3d.Switch;
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 ExSpotLight extends Java3DFrame {
  //--------------------------------------------------------------
  //  SCENE CONTENT
  //--------------------------------------------------------------

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

  //
  //  Build scene
  //
  public Group buildScene() {
    // Get the current color, position, attenuation,
    // spread angle, and concentration
    Color3f color = (Color3fcolors[currentColor].value;
    Point3f pos = (Point3fpositions[currentPosition].value;
    Vector3f dir = (Vector3fdirections[currentDirection].value;
    Point3f atten = (Point3fattenuations[currentAttenuation].value;
    float spread = ((Doublespreads[currentSpread].value).floatValue();
    float concen = ((Doubleconcentrations[currentConcentration].value)
        .floatValue();

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

    // Build the scene root
    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 SpotLight();
    light.setEnable(lightOnOff);
    light.setColor(color);
    light.setPosition(pos);
    light.setAttenuation(atten);
    light.setDirection(dir);
    light.setSpreadAngle(spread);
    light.setConcentration(concen);
    light.setCapability(SpotLight.ALLOW_STATE_WRITE);
    light.setCapability(SpotLight.ALLOW_COLOR_WRITE);
    light.setCapability(SpotLight.ALLOW_POSITION_WRITE);
    light.setCapability(SpotLight.ALLOW_ATTENUATION_WRITE);
    light.setCapability(SpotLight.ALLOW_DIRECTION_WRITE);
    light.setCapability(SpotLight.ALLOW_SPREAD_ANGLE_WRITE);
    light.setCapability(SpotLight.ALLOW_CONCENTRATION_WRITE);
    light.setInfluencingBounds(worldBounds);
    scene.addChild(light);
    // END EXAMPLE TOPIC

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

    // Add annotation arrows in a fan to show light ray directions,
    // positions, and the spread angle
    scene.addChild(buildArrows());

    return scene;
  }

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

  //
  //  Create a set of fans of annotation arrows initially pointing in
  //  the +X direction. Each fan in the set illustrates a different
  //  light spread angle listed in the user interface light control
  //  menu. Next, build two TransformGroups, one nested within
  //  the other, and place the fan within the innermost TransformGroup.
  //  The outer TransformGroup will be used to position the fan,
  //  and the inner one will be used to rotate the fan. The position
  //  and orientation are both selected via menu items. To do the
  //  position and orientation change, we compute a bunch of Transform3Ds
  //  for each menu choice position or orientation and save them.
  //  Later, when the user selects a new light direction or position,
  //  we poke the corresponding Transform3D into the appropriate
  //  TransformGroup, causing the arrows to change position or direction.
  //
  private TransformGroup arrowDirectionTransformGroup = null;

  private Transform3D[] arrowDirectionTransforms = null;

  private TransformGroup arrowPositionTransformGroup = null;

  private Transform3D[] arrowPositionTransforms = null;

  private Switch arrowSpreadAngleSwitch = null;

  private Group buildArrows() {
    // Create a switch group to hold the different arrow fan
    // spread angle choices. Enable child choice writing.
    arrowSpreadAngleSwitch = new Switch();
    arrowSpreadAngleSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);

    // Create a set of arrow fans, one per spread angle
    // shown on the menu.
    AnnotationArrowFan af = null;
    float spread = 0.0f;
    for (int i = 0; i < spreads.length; i++) {
      spread = ((Doublespreads[i].value).floatValue();
      af = new AnnotationArrowFan(0.0f0.0f0.0f// center position
          2.5f// arrow length
          -spread, // start angle
          spread, // end angle
          5)// number of arrows
      arrowSpreadAngleSwitch.addChild(af);
    }

    // Select the current fan.
    arrowSpreadAngleSwitch.setWhichChild(currentSpread);

    // Create an outer transform group used to change the fan
    // position. Enable writing of its transform.
    arrowPositionTransformGroup = new TransformGroup();
    arrowPositionTransformGroup
        .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

    // Create a set of Transform3Ds for the different arrow positions.
    arrowPositionTransforms = new Transform3D[positions.length];
    Point3f pos;
    Vector3f v = new Vector3f();
    for (int i = 0; i < positions.length; i++) {
      // Create a Transform3D, setting its translation.
      arrowPositionTransforms[inew Transform3D();
      pos = (Point3fpositions[i].value;
      v.set(pos);
      arrowPositionTransforms[i].setTranslation(v);
    }

    // Set the initial transform to be the current position
    arrowPositionTransformGroup
        .setTransform(arrowPositionTransforms[currentPosition]);

    // Create an inner transform group surrounding the arrows,
    // used to set the aim direction. Enable writing of its transform.
    arrowDirectionTransformGroup = new TransformGroup();
    arrowDirectionTransformGroup
        .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

    // Add the switch group to the direction-change transform group,
    // and add the direction-change transform group to the
    // position-change transform gorup.
    arrowDirectionTransformGroup.addChild(arrowSpreadAngleSwitch);
    arrowPositionTransformGroup.addChild(arrowDirectionTransformGroup);

    // 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 arrowPositionTransformGroup;
  }

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

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

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

  private CheckboxMenuItem lightOnOffMenu;

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

  //  Position menu choices
  private NameValue[] positions = new NameValue("Origin", Origin),
      new NameValue("+X", PlusX)new NameValue("-X", MinusX),
      new NameValue("+Y", PlusY)new NameValue("-Y", MinusY),
      new NameValue("+Z", PlusZ)new NameValue("-Z", MinusZ)};

  private int currentPosition = 0;

  private CheckboxMenu positionMenu = null;

  //  Attenuation menu choices
  private NameValue[] attenuations = {
      new NameValue("Constant"new Point3f(1.0f0.0f0.0f)),
      new NameValue("Linear"new Point3f(0.0f1.0f0.0f)),
      new NameValue("Quadratic"new Point3f(0.0f0.0f1.0f))};

  private int currentAttenuation = 0;

  private CheckboxMenu attenuationMenu = 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;

  //  Spread angle choices
  private NameValue[] spreads = {
      new NameValue("22.5 degrees"new Double(Math.PI / 8.0)),
      new NameValue("45.0 degrees"new Double(Math.PI / 4.0)),
      new NameValue("90.0 degrees"new Double(Math.PI / 2.0))};

  private int currentSpread = 1;

  private CheckboxMenu spreadMenu = null;

  //  Concentration choices
  private NameValue[] concentrations = {
      new NameValue("0.0"new Double(0.0)),
      new NameValue("5.0"new Double(5.0)),
      new NameValue("10.0"new Double(10.0)),
      new NameValue("50.0"new Double(50.0)),
      new NameValue("100.0"new Double(100.0))};

  private int currentConcentration = 0;

  private CheckboxMenu concentrationMenu = null;

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

    //
    //  Add a menubar menu to change node parameters
    //    Light on/off
    //    Color -->
    //    Position -->
    //    Direction -->
    //    Attenuation -->
    //    Spread Angle -->
    //    Concentration -->
    //

    Menu m = new Menu("SpotLight");

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

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

    positionMenu = new CheckboxMenu("Position", positions, currentPosition,
        this);
    m.add(positionMenu);

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

    attenuationMenu = new CheckboxMenu("Attenuation", attenuations,
        currentAttenuation, this);
    m.add(attenuationMenu);

    spreadMenu = new CheckboxMenu("Spread Angle", spreads, currentSpread,
        this);
    m.add(spreadMenu);

    concentrationMenu = new CheckboxMenu("Concentration", concentrations,
        currentConcentration, this);
    m.add(concentrationMenu);

    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 == positionMenu) {
      // Change the light position
      currentPosition = check;
      Point3f pos = (Point3fpositions[check].value;
      light.setPosition(pos);

      // Change the arrow group position
      arrowPositionTransformGroup
          .setTransform(arrowPositionTransforms[check]);
      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;
    }
    if (menu == attenuationMenu) {
      // Change the light attenuation
      currentAttenuation = check;
      Point3f atten = (Point3fattenuations[check].value;
      light.setAttenuation(atten);
      return;
    }
    if (menu == spreadMenu) {
      // Change the light spread angle
      currentSpread = check;
      arrowSpreadAngleSwitch.setWhichChild(check);
      float spread = ((Doublespreads[check].value).floatValue();
      light.setSpreadAngle(spread);
      return;
    }
    if (menu == concentrationMenu) {
      // Change the light concentration
      currentConcentration = check;
      float concen = ((Doubleconcentrations[check].value).floatValue();
      light.setConcentration(concen);
      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
//AnnotationArrowFan - A group of arrows in a fan
//
//DESCRIPTION
//This class creates one or more 3D, unlighted arrows arranged in a
//fan around the xyz position. Such arrow fans can be used to indicate
//point light directions, and so forth.
//
//The arrow fan is drawn in the XY plane, pointing right (middle arrow).
//The fan origin, arrow length, start and end angles, and number of
//arrows all may be controlled.
//
//SEE ALSO
//AnnotationArrow
//AnnotationArrowGroup
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
//

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

  //  Constructors
  public AnnotationArrowFan() {
    //    xyz length start/end angles count
    this(0.0f0.0f0.0f1.0f1.571f, -1.571f5);
  }

  public AnnotationArrowFan(float x, float y, float z, float length,
      float startAngle, float endAngle, int count) {
    arrows = new AnnotationArrow[count];
    float x2, y2;
    float angle = startAngle;
    float deltaAngle = (endAngle - startAngle(float) (count - 1);

    for (int i = 0; i < count; i++) {
      x2 = (float) (length * Math.cos(angle));
      y2 = (float) (length * Math.sin(angle));
      arrows[inew AnnotationArrow(x, y, z, x2, y2, z);
      addChild(arrows[i]);
      angle += deltaAngle;
    }
  }
}

//
//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(