Java 3D TM Explore


 

 

Here is an example virtual world for you to explore. Click on the headings to see the code for each of the main classes. If you want to add more rides (maybe a roller-coaster) then use these as a base.

A Ferris wheel - run with parameter ON to ride

// Copyright 2003 Resplendent Technology Ltd.
// See objectlessons.com for details of the java3d course.

// A Ferris wheel, big wheel, London Eye or similar ride

import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.geometry.Box;
import javax.media.j3d.*;
import javax.vecmath.*;
import javax.swing.Timer;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import com.sun.j3d.utils.universe.ViewingPlatform;


public class Ferris implements Ride, ActionListener, KeyListener {

	private Transform3D viewTransform = new Transform3D();
	private TransformGroup rotTG;
	private TransformGroup[] seatRotTG;
	private TransformGroup[] rotTG2;
	private Transform3D rot = new Transform3D();
	private Transform3D sRot = new Transform3D();
	private TransformGroup riderPos = new TransformGroup();
	private static Timer timer;
	private int tics = 0;
	private int numArms;
	private SimpleUniverse universe = null;
	private Transform3D turn = new Transform3D();
	private Vector3f viewVector;
	private ViewingPlatform vp = null;
	private TransformGroup viewTG = null;
	private Mover mover;

	private boolean riding = false;
	private static boolean onRide = false;
	private double upAngle = 0;
	private double turnSpeed = .045;
	public boolean pageDown = false;
	public boolean pageUp = false;
	public boolean end = false;

	private float rimLength;
	private Transform3D stillTransform = new Transform3D();
	private double rotY;
	private boolean justGotOn;

	protected static boolean mainRun = false;

	public void getOn() {
		riding = true;
		justGotOn = true;
		vp.detach();
		Virtuland.on = true;
		riderPos.addChild(vp);
	}

	public void getOff() {
		riding = false;
		Virtuland.on = false;
		vp.detach();
		((BranchGroup) (((Locale) (universe.getAllLocales().nextElement()))
				.getAllBranchGraphs().nextElement())).addChild(vp);
	}

	public boolean riding() {
		return riding;
	}

	public Ferris(Group group, SimpleUniverse universe) {
		setUp(group, universe, 12, 3.1f, .7f, Palette.BLUE);
	}

	protected void setUp(Group group, SimpleUniverse universe, int numArms,
			float radius, float width, Appearance axleColor) {
		this.universe = universe;
		this.numArms = numArms;
		double angle = Math.PI / numArms;
		rimLength = radius * (float) Math.sin(angle);
		turn.rotY((Math.PI));
		// rotate ride
		TransformGroup tg = new TransformGroup();
		rotTG = new TransformGroup();
		rotTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

		Transform3D rot7 = new Transform3D();
		rot7.rotX(Math.PI * 3.0 / 4.0);
		rotTG.setTransform(rot7);
		Vector3f vector = new Vector3f(0, radius + rimLength, 0);
		Transform3D transform = new Transform3D();
		transform.setTranslation(vector);
		tg.setTransform(transform);
		tg.addChild(rotTG);
		Box aBox = new Box(width / 2 + .2f, 0.05f, 0.05f, axleColor);
		aBox.setPickable(false);
		rotTG.addChild(aBox);

		// columns at the side of the of the wheel
		double angleOut = Math.PI / 15;
		double angleApart = Math.PI / 10;

		float height = .5f + (radius + rimLength) / 2;
		// float offset = height * (float)Math.tan(angleOut);
		// float offset2 = height * (float)Math.tan(angleApart);
		Appearance columnColor = Palette.MAGENTA;

		TransformGroup pos = Pos.at(width / 2 + .1f, -height, 0f);
		TransformGroup pos2 = Pos.at(-width / 2 - .1f, -height, 0f);
		TransformGroup posA = Pos.at(width / 2 + .1f, -height, 0f);
		TransformGroup pos2A = Pos.at(-width / 2 - .1f, -height, 0f);
		Box bBox = new Box(.06f, height, .06f, columnColor);
		Box cBox = new Box(.06f, height, .06f, columnColor);
		Box bBoxA = new Box(.06f, height, .06f, columnColor);
		Box cBoxA = new Box(.06f, height, .06f, columnColor);
		TransformGroup angle1 = new TransformGroup();
		TransformGroup angle2 = new TransformGroup();
		TransformGroup angle1A = new TransformGroup();
		TransformGroup angle2A = new TransformGroup();
		TransformGroup columnDown = Pos.at(0, -height, 0);
		TransformGroup columnUp = Pos.at(0, height, 0);
		TransformGroup columnDown2 = Pos.at(0, -height, 0);
		TransformGroup columnUp2 = Pos.at(0, height, 0);
		TransformGroup columnDownA = Pos.at(0, -height, 0);
		TransformGroup columnUpA = Pos.at(0, height, 0);
		TransformGroup columnDown2A = Pos.at(0, -height, 0);
		TransformGroup columnUp2A = Pos.at(0, height, 0);
		Transform3D columnOut = new Transform3D();
		Transform3D columnApart = new Transform3D();

		columnOut.rotZ(angleOut);
		columnApart.rotX(angleApart);
		columnOut.mul(columnApart);
		angle1.setTransform(columnOut);

		columnOut.rotZ(angleOut);
		columnApart.rotX(-angleApart);
		columnOut.mul(columnApart);
		angle1A.setTransform(columnOut);

		columnOut.rotZ(-angleOut);
		columnApart.rotX(angleApart);
		columnOut.mul(columnApart);
		angle2.setTransform(columnOut);

		columnOut.rotZ(-angleOut);
		columnApart.rotX(-angleApart);
		columnOut.mul(columnApart);
		angle2A.setTransform(columnOut);

		bBox.setPickable(false);

		pos.addChild(columnUp);
		columnUp.addChild(angle1);
		angle1.addChild(columnDown);
		columnDown.addChild(bBox);
		tg.addChild(pos);
		pos.addChild(columnUpA);
		columnUp.addChild(angle1A);
		angle1A.addChild(columnDownA);
		columnDownA.addChild(bBoxA);
		tg.addChild(posA);

		pos2.addChild(columnUp2);
		columnUp2.addChild(angle2);
		angle2.addChild(columnDown2);
		columnDown2.addChild(cBox);
		tg.addChild(pos2);

		pos2A.addChild(columnUp2A);
		columnUp2A.addChild(angle2A);
		angle2A.addChild(columnDown2A);
		columnDown2A.addChild(cBoxA);
		tg.addChild(pos2A);

		// First level

		Box[] arm = new Box[numArms];
		Transform3D[] armT = new Transform3D[numArms];
		Transform3D[] armRY = new Transform3D[numArms];
		TransformGroup[] seatTG = new TransformGroup[numArms];
		seatRotTG = new TransformGroup[numArms];
		Transform3D[] seatRotate = new Transform3D[numArms];
		TransformGroup[] armTG = new TransformGroup[numArms];
		rotTG2 = new TransformGroup[numArms];
		Transform3D armRZ = new Transform3D();

		armRZ.rotZ((float) Math.PI / 2);
		TransformGroup[] endArm = new TransformGroup[numArms];
		Transform3D endArmT = new Transform3D();
		vector = new Vector3f(radius / 2, 0f, 0f);
		endArmT.setTranslation(vector);

		// Transform3D endArmLevel = new Transform3D();
		// endArmLevel.rotZ(-armAngle);
		vector = new Vector3f(radius / 2, width / 2, 0f);
		Vector3f vector2 = new Vector3f(radius / 2, -width / 2, 0f);
		for (int i = 0; i < numArms; i++) {
			arm[i] = new Box(radius / 2, .04f, .04f, Palette.YELLOW);
			arm[i].setPickable(false);
			armT[i] = new Transform3D();
			// vector = new Vector3f(radius, width/2, 0f);
			armT[i].setTranslation(vector);

			Box arm2 = new Box(radius / 2, .04f, .04f, Palette.YELLOW);
			arm2.setPickable(false);
			Transform3D armT2 = new Transform3D();
			armT2.setTranslation(vector2);

			armTG[i] = new TransformGroup();
			seatRotTG[i] = new TransformGroup();
			seatRotTG[i].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
			TransformGroup armTG2 = new TransformGroup();
			endArm[i] = new TransformGroup();
			endArm[i].setTransform(endArmT);
			armRY[i] = new Transform3D();
			armRY[i].rotX((i * 2 * Math.PI) / (numArms));

			armRY[i].mul(armRZ);
			Transform3D armRY2 = new Transform3D(armRY[i]);
			armRY[i].mul(armT[i]);
			armRY2.mul(armT2);
			armTG[i].setTransform(armRY[i]);
			armTG2.setTransform(armRY2);
			armTG[i].addChild(arm[i]);
			armTG2.addChild(arm2);
			armTG[i].addChild(endArm[i]);

			rotTG.addChild(armTG[i]);
			rotTG.addChild(armTG2);

			// Sphere sphere = new Sphere(.2f);
			// Sphere sphere = new Sphere(.2f, Sphere.GENERATE_NORMALS |
			// Sphere.GENERATE_TEXTURE_COORDS,
			// 5, Palette.ORANGE);
			Box across = new Box(.02f, width / 2, .02f, Palette.BLUE);
			Box acrossStrut = new Box(.01f, (width) / 2, .01f, Palette.GREEN);
			Box rim = new Box(rimLength, .02f, .02f, Palette.CYAN);
			Box rim2 = new Box(rimLength, .02f, .02f, Palette.CYAN);

			// add the seats
			Box seat = new Box(width / 6, width / 2, width / 6, Palette.BLUE);
			Box seatSupport = new Box(.666f * rimLength / 2, .02f, .02f, Palette.RED);
			Box seatSupport2 = new Box(.666f * rimLength / 2, .02f, .02f, Palette.RED);
			TransformGroup seatSupportTG = Pos.at(.666f * rimLength / 2, -width / 2,
					0f);
			TransformGroup seatSupport2TG = Pos.at(.666f * rimLength / 2, +width / 2,
					0f);
			seatSupportTG.addChild(seatSupport);
			seatSupport2TG.addChild(seatSupport2);
			// seatTG[i].setTransform(seatT);
			seatTG[i] = new TransformGroup();

			Transform3D seatT = new Transform3D();
			seatT.setTranslation(new Vector3f(0f, -width / 2, 0f));
			TransformGroup seatAdjust = new TransformGroup(seatT);
			seatRotate[i] = new Transform3D();
			seatRotate[i].rotY((Math.PI * 2 * i) / (numArms) - Math.PI / 4);
			seatTG[i].setTransform(seatRotate[i]);
			TransformGroup seatOffset = Pos
					.at(.666f * rimLength + width / 12, 0f, 0f);
			if (i == 0) {
				Transform3D lookForward = new Transform3D();
				lookForward.rotZ(Math.PI / 2);
				riderPos = new TransformGroup();
				TransformGroup lookForwardTG = new TransformGroup(lookForward);
				lookForwardTG.addChild(riderPos);
				riderPos.setCapability(Group.ALLOW_LOCAL_TO_VWORLD_READ);
				riderPos.setCapability(Group.ALLOW_CHILDREN_EXTEND);
				riderPos.setCapability(Group.ALLOW_CHILDREN_WRITE);
				riderPos.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
				seatTG[i].addChild(lookForwardTG);
			}
			// TransformGroup seatSupportOffset = Pos.at(rimLength+width/12, width/8,
			// width/12);
			// TransformGroup seatSupport2Offset = Pos.at(rimLength+width/12, width/8,
			// width/12);
			seatOffset.addChild(seat);
			seatRotTG[i].addChild(seatTG[i]);

			seatTG[i].addChild(seatOffset);
			seatTG[i].addChild(seatSupportTG);
			seatTG[i].addChild(seatSupport2TG);
			TransformGroup rimTG = new TransformGroup();
			TransformGroup rimTG2 = new TransformGroup();
			Transform3D rimT = new Transform3D();
			double rotAmount = Math.PI / 2 - angle;
			TransformGroup acrossPos = Pos.at(0f, -width / 2, 0f);
			TransformGroup acrossStrutPos = Pos.at(0f, -width / 2, 0f);
			acrossStrutPos.addChild(acrossStrut);

			// diagonal supporting struts
			Box firstStrut = new Box((float) Math.sqrt(radius * radius / 16
					+ (width - .1) * (width - .1) / 4), .01f, .01f, Palette.GREEN);
			Box secondStrut = new Box((float) Math.sqrt(radius * radius / 16
					+ (width - .1) * (width - .1) / 4), .01f, .01f, Palette.GREEN);
			double strutAngle = Math.PI / 2
					- Math.acos((double) ((width - .1) * 2 / radius));
			Transform3D strutTurn = new Transform3D();
			strutTurn.rotZ(strutAngle);
			TransformGroup firstStrutTG = new TransformGroup(strutTurn);
			Transform3D halfArmT = new Transform3D();
			halfArmT.setTranslation(new Vector3f(-radius / 4, 0f, 0f));
			TransformGroup halfArmTG = new TransformGroup(halfArmT);
			acrossStrutPos.addChild(halfArmTG);
			halfArmTG.addChild(firstStrutTG);
			firstStrutTG.addChild(firstStrut);

			TransformGroup secondStrutTG = new TransformGroup(strutTurn);
			halfArmT.setTranslation(new Vector3f(radius / 4, 0f, 0f));
			halfArmTG = new TransformGroup(halfArmT);
			acrossStrutPos.addChild(halfArmTG);
			halfArmTG.addChild(secondStrutTG);
			secondStrutTG.addChild(secondStrut);

			TransformGroup rimPos = Pos.at(-rimLength, 0f, 0f);
			TransformGroup rimPos2 = Pos.at(-rimLength, -width, 0f);
			rimT.rotY(rotAmount);
			rimTG.setTransform(rimT);
			rimTG2.setTransform(rimT);
			rimTG.addChild(rimPos);
			rimTG2.addChild(rimPos2);
			rimPos.addChild(rim);
			rimPos2.addChild(rim2);

			acrossPos.addChild(across);
			across.setPickable(false);
			endArm[i].addChild(acrossPos);

			armTG[i].addChild(acrossStrutPos);

			endArm[i].addChild(rimTG);
			endArm[i].addChild(rimTG2);
			endArm[i].addChild(seatAdjust);
			seatAdjust.addChild(seatRotTG[i]);
			//
			rotTG2[i] = new TransformGroup();
			rotTG2[i].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
			endArm[i].addChild(rotTG2[i]);
		}

		group.addChild(tg);
		viewVector = new Vector3f(.0f, 1.0f, 10.0f);
		viewTransform.setTranslation(viewVector);
		vp = universe.getViewingPlatform();
		viewTG = vp.getViewPlatformTransform();
		viewTG.setTransform(viewTransform);
		mover = new Mover(viewTransform, viewVector, viewTG, 0);
	}

	public static void main(String[] args) {

		mainRun = true;
	
		// general Set-up
		JFrame frame = new JFrame("Ferris Wheel");
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent winEvent) {
				System.exit(0);
			}
		});
		GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
		Canvas3D canvas = new Canvas3D(config);
		canvas.setSize(400, 400);
		frame.getContentPane().add(canvas);

		SimpleUniverse uni = new SimpleUniverse(canvas);
		BranchGroup group = new BranchGroup();
		Ferris wheel = new Ferris(group, uni);
		wheel.setUp(group);
		canvas.addKeyListener(wheel);
		Button go = new Button("Go");
		go.addActionListener(wheel);
		go.addKeyListener(wheel);
		frame.pack();
		frame.setVisible(true);
		if (args.length > 0) {
			if (args[0].equals("ON"))
				wheel.getOn();
		}
	}

	public void setUp(BranchGroup group) {
		// directional and ambient white light
		if (onRide)
			riding = true;
		new MyLights(group);
		new SkyBackground(group, null);
		Box floor = new Box(40000f, .1f, 40000f,
				new StandardAppearance(Color.green));
		floor.setPickable(false);

		group.addChild(floor);
		group.compile();

		// frame.getContentPane(). addComponentListener(this);

		universe.addBranchGraph(group);
		timer = new Timer(80, this);
		timer.start();
	}

	public void actionPerformed(ActionEvent e) {

		Mover outsideMover = null;
		try {
			outsideMover = (Mover) e.getSource();
		} catch (Throwable t) {
		}

		if (outsideMover != null) {
			if (justGotOn) {
				outsideMover.setFacingAngle(0);
				justGotOn = false;
			}
			rotY = outsideMover.getFacingAngle();
			// System.out.println(outsideMover.getFacingAngle());
		}

		/*
		 * if (leftArrow && !alt) { System.out.println("leftArrow"); facingAngle =
		 * facingAngle + turnSpeed; }
		 * 
		 * if (rightArrow && !alt) { facingAngle = facingAngle - turnSpeed; } if
		 * (downArrow) { viewVector.z = viewVector.z + (float)(moveSpeed *
		 * Math.cos(facingAngle)); viewVector.x = viewVector.x + (float)(moveSpeed *
		 * Math.sin(facingAngle)); } // strafe left if (leftArrow && alt) {
		 * viewVector.z = viewVector.z - (float)(moveSpeed *
		 * Math.cos(facingAngle+Math.PI/2)); viewVector.x = viewVector.x -
		 * (float)(moveSpeed * Math.sin(facingAngle+Math.PI/2)); } // strafe right
		 * if (rightArrow && alt) { viewVector.z = viewVector.z - (float)(moveSpeed
		 * * Math.cos(facingAngle-Math.PI/2)); viewVector.x = viewVector.x -
		 * (float)(moveSpeed * Math.sin(facingAngle-Math.PI/2)); }
		 */
		if (pageUp && upAngle < (Math.PI / 2)) {
			upAngle = upAngle + turnSpeed;
		}
		if (pageDown && upAngle > (-Math.PI / 2)) {
			upAngle = upAngle - turnSpeed;
		}
		if (end) {
			upAngle = 0;
		}
		tics++;
		rot.rotX(Math.PI * 3.0 / 4.0 + (Math.PI * tics) / 120.0);
		rotTG.setTransform(rot);
		for (int i = 0; i < numArms; i++) {
			// System.out.println("i: "+i+"  tic: "+tics);
			sRot.rotY((Math.PI * tics) / 120.0);
			// System.out.println("sRot set");
			// System.out.println(seatRotTG[i]);
			seatRotTG[i].setTransform(sRot);
			// System.out.println("seatRotTG set");
		}
		if (riding) {
			// try {
			// // endArmLevel2.add()
			// // riderPos.getLocalToVworld(viewTransform);
			// // viewTransform.mul(endArmLevel2);
			// }
			// catch (Throwable blah){
			// System.out.println(blah);
			// }
			stillTransform = new Transform3D();
			stillTransform.setTranslation(new Vector3d(0, 0, 0));
			TransformGroup viewPlatformTransform = vp.getMultiTransformGroup()
					.getTransformGroup(0);

			viewPlatformTransform.setTransform(stillTransform);
			stillTransform.rotY(rotY);

			// /ViewPlatform viewPlatform = vp.getViewPlatform();
			// 
			riderPos.setTransform(stillTransform);
			// TransformGroup viewPlatformTransform = vp.getViewPlatformTransform();

			// viewTG.setTransform(stillTransform );
		}
	}

	public void keyReleased(KeyEvent e) {
		mover.keyReleased(e);
		// Invoked when a key has been released.
		// System.out.println("Key Released");
	}

	public void keyTyped(KeyEvent e) {
		// System.out.println(e.getKeyChar() +" Key Typed "+e.getKeyCode());
		// Invoked when a key has been typed.
	}

	public void keyPressed(KeyEvent e) {

		// Invoked when a key has been pressed.
		mover.keyPressed(e);

	}
}

A Merry-go-round or Carousel - example of model-loading

// Copyright 2003 Resplendent Technology Ltd.
// See objectlessons.com for details of the java3d course.

// A merry-go-round or carousel

import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.geometry.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import javax.swing.Timer;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import java.applet.Applet;
import com.sun.j3d.loaders.Scene;

public class MerryGoRound implements Ride, ActionListener, KeyListener {
protected Transform3D viewTransform = new Transform3D();
protected TransformGroup rotTG;
protected static boolean mainRun=false;
protected Transform3D rot = new Transform3D();
private TransformGroup riderPos;
private static Timer timer;
protected int tics=0;
protected boolean riding=false; // used to determe if viewer is riding
protected static boolean onRide=false; // used for testing from main
protected Mover mover;
protected Vector3f viewVector;
protected ViewingPlatform vp = null;
protected TransformGroup viewTG = null;
protected SimpleUniverse universe = null;
protected Transform3D sRot = new Transform3D();
protected Transform3D tRot = new Transform3D();
protected int rowsOfSteeds = 2;
protected int steedsInRow = 8;
private double rotY;
private Transform3D stillTransform = new Transform3D();
protected TransformGroup[] horseGallop
    = new TransformGroup[rowsOfSteeds*steedsInRow];
protected TransformGroup[] horseStraighten
    = new TransformGroup[rowsOfSteeds*steedsInRow];
private boolean justGotOn;
public MerryGoRound(Group group, SimpleUniverse universe) {
       setUp(group, universe);
}
protected void setUp(Group group, SimpleUniverse universe) {
  this.universe = universe;

  // stationary central column
  float height = 2f;
  float radius = 3f;
  float floorHeight = .2f;
  float heightAboveGround = .02f;
  int numberOfDivisions = 12;
  int numberOfSupports = 6;
  float supportRadius = .06f;

  TransformGroup centerPos = Pos.at(0f,height/2,0);
  Cylinder center = new Cylinder(0.1f, height, Cylinder.GENERATE_NORMALS |
      Cylinder.GENERATE_TEXTURE_COORDS, 6,3, Palette.MAGENTA);
  centerPos.addChild(center);
  group.addChild (centerPos);
  // transformation for rotating ride
  rotTG = new TransformGroup();
  rotTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
  // bottom platform of ride
  Cylinder bottom = new Cylinder(radius, floorHeight, Cylinder.GENERATE_NORMALS
      | Cylinder.GENERATE_TEXTURE_COORDS, numberOfDivisions,3, Palette.BLUE);
  TransformGroup bottomPos = Pos.at(0f,heightAboveGround + floorHeight/2,0);
  bottomPos.addChild(bottom);
  rotTG.addChild (bottomPos);
  // top canopy of ride
  Cylinder top = new Cylinder(radius, floorHeight, Cylinder.GENERATE_NORMALS
      | Cylinder.GENERATE_TEXTURE_COORDS, numberOfDivisions,3, Palette.YELLOW);
  TransformGroup topPos = Pos.at(0f,height-heightAboveGround+floorHeight/2,0);
  topPos.addChild(top);
  rotTG.addChild (topPos);
  Cone canopy = new Cone(radius, floorHeight*4, Cone.GENERATE_NORMALS |
   Cone.GENERATE_TEXTURE_COORDS,numberOfDivisions,3, Palette.YELLOW);
  TransformGroup canopyPos = Pos.at
      (0f,height-heightAboveGround+5*floorHeight/2,0);
  canopyPos.addChild(canopy);
  rotTG.addChild(canopyPos);
  // some posts to support the canopy
  Transform3D rotateTransform = new  Transform3D();
  for (int i = 0; i < numberOfSupports; i++) {
    TransformGroup postOffset = Pos.at(radius - 2* supportRadius,
        (height + floorHeight + heightAboveGround)/2, 0);
    TransformGroup rotation = new TransformGroup();
    rotateTransform.rotY(i*Math.PI*2/numberOfSupports);
    rotation.setTransform(rotateTransform);
    rotation.addChild(postOffset);
    rotTG.addChild(rotation);
    Cylinder support = new Cylinder(supportRadius, height - heightAboveGround -
       floorHeight, Cylinder.GENERATE_NORMALS
      | Cylinder.GENERATE_TEXTURE_COORDS, 6,3, Palette.ORANGE);
    postOffset.addChild(support);
  }
  // add some steeds
  Applet applet = new Applet();
  Transform3D turn = new Transform3D();
  turn.rotY(Math.PI);
  Transform3D scale = new Transform3D();
  scale.set(.9);
  turn.mul(scale);
  //TransformGroup horseTG = new TransformGroup(turn);
  //GetModel.add("horse.obj", horseTG, applet);
  Scene scene = GetModel.get("horse.obj",  applet);
  BranchGroup horse = scene.getSceneGroup();
  GetModel.showGroup(horse);
  //((Shape3D)horse.getChild(0)).setAppearance(Palette.HORSE);
  Appearance[] horseAppearance = {Palette.RED, Palette.GREEN};

  for (int i = 0; i < rowsOfSteeds; i++) {
      Transform3D steedOffsetTransform = new Transform3D();

      Vector3f vec = new Vector3f(.9f*radius*(i+1)/(rowsOfSteeds),
         0, 0);
      steedOffsetTransform.setTranslation(vec);
    for (int j = 0; j < steedsInRow; j++) {
      int sign = ((j + i) % 2) * 2 - 1;
      TransformGroup steedOffset = new TransformGroup(steedOffsetTransform);
      Vector3f upOrDownVector = new Vector3f(0, height/12*sign, 0);
      Transform3D upOrDownTransform = new Transform3D();
      upOrDownTransform.setTranslation( upOrDownVector );
      TransformGroup upOrDown = new TransformGroup(upOrDownTransform);
      TransformGroup rotation = new TransformGroup();
      rotateTransform.rotY(j*Math.PI*2/steedsInRow);
      rotation.setTransform(rotateTransform);
      horseGallop[i*steedsInRow + j] = new TransformGroup();
      horseGallop[i*steedsInRow + j]
          .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
      horseStraighten[i*steedsInRow + j] = new TransformGroup();
      horseStraighten[i*steedsInRow + j]
          .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
      rotation.addChild(horseGallop[i*steedsInRow + j]);
      horseGallop[i*steedsInRow + j].addChild(steedOffset);
      rotTG.addChild(rotation);
     // Box steed =  new Box(.2f, .1f, .1f, Palette.MAGENTA);
     // steedOffset.addChild(steed);
     //GetModel.add("horse.obj", steedOffset, applet);
     TransformGroup horseTG = new TransformGroup(turn);
     //GetModel.add("horse.obj", horseTG, applet);
     if (i==0 && j==0) {
       Shape3D steed = (Shape3D)horse.getChild(0);
       steed.setAppearance(horseAppearance[(i+j)%2]);
       horseTG.addChild(horse);

     } else {
        Shape3D steed = (Shape3D)(horse.getChild(0).cloneNode(true));
        steed.setAppearance(horseAppearance[(i+j)%2]);
        horseTG.addChild(steed);
     }
     steedOffset.addChild(upOrDown);
     upOrDown.addChild(horseStraighten[i*steedsInRow + j]);
     TransformGroup raiseHorses = Pos.at(0f, heightAboveGround + height/2,0f);

     horseStraighten[i*steedsInRow + j].addChild(raiseHorses);
     raiseHorses.addChild(horseTG);
     Cylinder pole = new Cylinder(0.03f, height - heightAboveGround, Cylinder.GENERATE_NORMALS |
        Cylinder.GENERATE_TEXTURE_COORDS, 6,3, Palette.CYAN);
     TransformGroup poleTrans = Pos.at(0,0,-0.1f);
     poleTrans.addChild(pole);
     raiseHorses.addChild(poleTrans);
     if (i == rowsOfSteeds - 1  && j == 0) {
        riderPos = Pos.at(0f,height/2,0.1f);
        riderPos.setCapability(Group.ALLOW_LOCAL_TO_VWORLD_READ);
        riderPos.setCapability(Group.ALLOW_CHILDREN_EXTEND);
        riderPos.setCapability(Group.ALLOW_CHILDREN_WRITE);
        riderPos.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        Transform3D lookForward = new Transform3D();
        lookForward.rotY(Math.PI/7);
        TransformGroup lookForwardTG = new TransformGroup(lookForward);


        lookForwardTG.addChild(riderPos);
        raiseHorses.addChild(lookForwardTG);
     }
    }
  }

  // set up view transformation and add to virtual world
  viewVector = new Vector3f(.0f,1.0f, 10.0f);
  viewTransform.setTranslation(viewVector);
  vp = universe.getViewingPlatform();
  viewTG = vp.getViewPlatformTransform();
  viewTG.setTransform(viewTransform);
  mover = new Mover(viewTransform, viewVector, viewTG, 0);
  group.addChild(rotTG);
}
// implement Ride functions
public void getOn() {
  riding = true;
  vp.detach();
  Virtuland.on = true;
  justGotOn = true;
  riderPos.addChild(vp);
}

public void getOff() {
  riding = false;
  Virtuland.on = false;
  vp.detach();
  ((BranchGroup)(((Locale)(universe.getAllLocales().nextElement())).getAllBranchGraphs().nextElement())).addChild(vp);
}
public boolean riding() {
  return riding;
}
// set up lights sky floor etc. for testing from main
public void setUp(BranchGroup group) {
  // directional and ambient white light
  if (onRide) riding = true;
  new MyLights(group);
  new SkyBackground(group, null);
  Box floor = new Box(40000f,.1f,40000f,new StandardAppearance(Color.green));
  floor.setPickable(false);
  group.addChild(floor);
  group.compile();

   // frame.getContentPane(). addComponentListener(this);

  universe.addBranchGraph(group);
  timer = new Timer(80,this);
  timer.start();
}
// ActionListener and KeyListener interfaces
public void actionPerformed(ActionEvent e ) {
	Mover outsideMover = null;
	try {
		 outsideMover = (Mover) e.getSource();
	} catch (Throwable t) {
	}
  //System.out.println(e.getSource());
  if (outsideMover != null) {
 	 rotY = outsideMover.getFacingAngle();
	 	if (justGotOn) {
	  	outsideMover.setFacingAngle(0);
	  	justGotOn = false;
	  }
  }
    tics++;
    rot.rotY(Math.PI*3.0/4.0 + (Math.PI*tics)/80.0);
    rotTG.setTransform(rot);
    sRot.rotX((Math.PI*tics)/20.0);
    tRot.rotX(-(Math.PI*tics)/20.0);
    for (int i = 0; i < rowsOfSteeds*steedsInRow; i++) {
        horseGallop[i].setTransform(sRot);
        horseStraighten[i].setTransform(tRot);
        //System.out.println("seatRotTG set");
    }

    if (riding) {
//       try {
//          // riderPos.getLocalToVworld(viewTransform);
//          // viewTransform.mul(endArmLevel2);
//       }
//       catch (Throwable error){
//           System.out.println(error);
//       }
//       viewTG.setTransform(viewTransform);
       stillTransform= new Transform3D();
       stillTransform.setTranslation(new Vector3d(0,0,0));
       TransformGroup viewPlatformTransform = vp.getMultiTransformGroup().getTransformGroup(0);

       viewPlatformTransform.setTransform(stillTransform );
      stillTransform.rotY(rotY);
       
      
     
       
            ///ViewPlatform viewPlatform = vp.getViewPlatform();
      // 
       riderPos.setTransform(stillTransform);
    }
  }
public void keyReleased(KeyEvent e){
  mover.keyReleased(e);
  // Invoked when a key has been released.
  }
public void keyTyped(KeyEvent e){
  //System.out.println(e.getKeyChar() +" Key Typed "+e.getKeyCode());
  //Invoked when a key has been typed.
}
public void keyPressed(KeyEvent e) {
  //Invoked when a key has been pressed.
  mover.keyPressed(e);
}
// main function for testing
public static void main( String[] args ) {
  mainRun = true;
 
  // general Set-up
  JFrame frame = new JFrame("Carousel");
  frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent winEvent) {
        System.exit(0);
      }	});
  GraphicsConfiguration config =
            SimpleUniverse.getPreferredConfiguration();
  Canvas3D canvas = new Canvas3D(config);
  canvas.setSize(400, 400);
  frame.getContentPane().add(canvas);
  SimpleUniverse uni = new SimpleUniverse(canvas);
  BranchGroup group = new BranchGroup();
  MerryGoRound ride = new MerryGoRound(group,uni);
  ride.setUp(group);
  canvas.addKeyListener(ride);
  Button go = new Button("Go");
  go.addActionListener(ride);
  go.addKeyListener(ride);
  frame.pack();
  frame.setVisible(true);
  if (args.length > 0) {
		if (args[0].equals("ON"))
			ride.getOn();
	}
}
} // end of class

A Twister Ride - you can vary the number of arms

// Copyright 2003 Resplendent Technology Ltd.
// See objectlessons.com for details of the java3d course.

// The  Spinner Class is for a ride that can be called Spider
// or Bermuda Triangle it has a central spinning section
// which splits into more sections that also spin

import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.geometry.Sphere;
import javax.media.j3d.*;
import javax.vecmath.*;
import javax.swing.Timer;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import com.sun.j3d.utils.universe.ViewingPlatform;

//package park;

public class Spinner implements Ride, ActionListener, KeyListener {

private Transform3D viewTransform = new Transform3D();
private TransformGroup rotTG;
private TransformGroup[] rotTG2;
private Transform3D rot = new Transform3D();
private Transform3D rot2 = new Transform3D();
private TransformGroup riderPos = new TransformGroup();
//private TransformGroup rotTG2;
private static Timer timer;
private int tics=0;
private int numArms = 4;
private SimpleUniverse universe = null;
private Transform3D endArmLevel2;
private Transform3D turn = new Transform3D();
private Vector3f viewVector;
private ViewingPlatform vp = null;
private TransformGroup viewTG = null;
private Mover mover;
private boolean riding=false;
private static boolean onRide=false;

private float armLength = 1.2f;
private float armAngle = (float)(-2*Math.PI)/14;

protected static boolean mainRun=false;

public void getOn() {
  riding = true;
  Virtuland.on = true;
}
public void getOff() {
  riding = false;
  Virtuland.on = false;
}
public boolean riding() {
  return riding;
}

public Spinner(Group group, SimpleUniverse universe, int numArms) {

       setUp(group, universe,
             numArms, armLength, armAngle, 3, .8f, (float)(-2*Math.PI)/6,
             Palette.CYAN, Palette.RED, Palette.BLUE, Palette.WHITE,
             Palette.RED, Palette.YELLOW);
}
public Spinner(Group group, SimpleUniverse universe,
                    int numArms,
                    float armLength,
                    float armAngle,
                    int numArms2,
                    float armLength2,
                    float armAngle2,
                    Appearance columnColor,
                    Appearance capColor,
                    Appearance armColor,
                    Appearance endArmColor,
                    Appearance armColor2,
                    Appearance seatColor) {

       setUp(group, universe, numArms, armLength, armAngle, numArms2,armLength2,
             armAngle2, columnColor, capColor, armColor, endArmColor, armColor2,
             seatColor);
}


protected void setUp(Group group,
                     SimpleUniverse universe,
                     int numArms,
                     float armLength,
                     float armAngle,
                     int numArms2,
                     float armLength2,
                     float armAngle2,
                     Appearance columnColor,
                     Appearance capColor,
                     Appearance armColor,
                     Appearance endArmColor,
                     Appearance armColor2,
                     Appearance seatColor) {


    this.universe = universe;
    this.numArms = numArms;
    this.armAngle = armAngle;
    this.armLength = armLength;


    turn.rotY((Math.PI));

    // rotate top of ride
    TransformGroup tg  = new TransformGroup();
    rotTG = new TransformGroup();
    rotTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    Transform3D rot = new Transform3D();
    rot.rotY(Math.PI*3.0/4.0);
    rotTG.setTransform(rot);
    Vector3f vector = new Vector3f(-.2f, 3f , .3f);
    Transform3D transform = new Transform3D();
    transform.setTranslation(vector);
    tg.setTransform(transform);
    tg.addChild(rotTG);
    Box aBox = new Box(0.15f,0.20f,0.15f, capColor);
    aBox.setPickable(false);
    rotTG.addChild ( aBox );

    TransformGroup pos = Pos.at(0f,-1.4f,0f);
    Box bBox = new Box(.1f,1.6f,.1f,columnColor);
    bBox.setPickable(false);
    pos.addChild(bBox);
    tg.addChild(pos);
    // First level


    Box[] arm = new Box[numArms];
    Transform3D[] armT = new Transform3D[numArms];
    Transform3D[] armRY = new Transform3D[numArms];
    TransformGroup[] armTG = new TransformGroup[numArms];
    rotTG2 = new TransformGroup[numArms];
    Transform3D armRZ = new Transform3D();

    armRZ.rotZ(armAngle);
    TransformGroup[] endArm = new TransformGroup[numArms];
    Transform3D endArmT = new Transform3D();
    vector = new Vector3f(armLength,0f,0f);
    endArmT.setTranslation(vector);

    Transform3D endArmLevel = new Transform3D();
    endArmLevel.rotZ(-armAngle);
    endArmT.mul(endArmLevel);

    // Second level, use suffix 2
    //int numArms2 = 3;

    Box[] arm2 = new Box[numArms2];
    Transform3D[] armT2 = new Transform3D[numArms2];
    Transform3D[] armRY2 = new Transform3D[numArms2];
    TransformGroup[] armTG2 = new TransformGroup[numArms2];
    Transform3D armRZ2 = new Transform3D();
    armRZ.rotZ(armAngle);
    armRZ2.rotZ(armAngle2);
    TransformGroup[] endArm2 = new TransformGroup[numArms2];
    Transform3D endArmT2 = new Transform3D();
    Vector3f vector2 = new Vector3f(armLength2,0f,0f);
    Vector3f vector3 = new Vector3f(0f,-.5f,0f);
    Transform3D downAbit = new Transform3D();
    downAbit.setTranslation(vector3);
    endArmT2.setTranslation(vector2);
    endArmLevel2 = new Transform3D();
    endArmLevel2.rotZ(-armAngle2);
    //endArmLevel2.mul(endArmT2);
    endArmT2.mul(endArmLevel2);
    endArmLevel2.mul(turn);
    endArmLevel2.mul(downAbit);

    for (int i = 0; i < numArms; i++) {
      arm[i]=new Box(armLength,.07f,.07f, armColor);
      arm[i].setPickable(false);
      armT[i] = new Transform3D();
      //vector = new Vector3f(armLength, 0f, 0f);
      armT[i].setTranslation(vector);
      armTG[i] = new TransformGroup();
      endArm[i] = new TransformGroup();
      endArm[i].setTransform(endArmT);
      armRY[i] = new Transform3D();
      armRY[i].rotY((i * 2*Math.PI)/(numArms));
      armRY[i].mul(armRZ);
      armRY[i].mul(armT[i]);
      armTG[i].setTransform(armRY[i]);
      armTG[i].addChild(arm[i]);
      armTG[i].addChild(endArm[i]);
      rotTG.addChild(armTG[i]);
      //Sphere sphere = new Sphere(.2f);
      Sphere sphere = new Sphere(.2f, Sphere.GENERATE_NORMALS |
                                  Sphere.GENERATE_TEXTURE_COORDS,
				  5, endArmColor);
      sphere.setPickable(false);
      endArm[i].addChild(sphere);
      rotTG2[i] = new TransformGroup();
      rotTG2[i].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
      endArm[i].addChild(rotTG2[i]);
      for (int j = 0; j < numArms2; j++) {
        arm2[j]=new Box(armLength2,.05f,.05f,armColor2);
        arm2[j].setPickable(false);
        armT2[j] = new Transform3D();
        vector2 = new Vector3f(armLength2, 0f, 0f);
        armT2[j].setTranslation(vector2);
        armTG2[j] = new TransformGroup();
        endArm2[j] = new TransformGroup();
        endArm2[j].setTransform(endArmT2);
        armRY2[j] = new Transform3D();
        armRY2[j].rotY((j * 2*Math.PI)/(numArms2));
        armRY2[j].mul(armRZ2);
        armRY2[j].mul(armT2[j]);

        armTG2[j].setTransform(armRY2[j]);
        armTG2[j].addChild(arm2[j]);
        armTG2[j].addChild(endArm2[j]);

        //rotTG2.addChild(armTG2[i]);
        rotTG2[i].addChild(armTG2[j]);
        if (j==0) {
          riderPos = endArm2[j];
          riderPos.setCapability(Group.ALLOW_LOCAL_TO_VWORLD_READ);

        }
        Box cBox = new Box(.2f,.06f,.3f,seatColor);
        TransformGroup pos1 = Pos.at(.1f,-.1f,0f);
        TransformGroup pos2 = Pos.at(.0f,0f,0f);
        Transform3D rSeatT = new Transform3D();
        rSeatT.rotY(-Math.PI/2);
        TransformGroup rSeat = new TransformGroup(rSeatT);
        TransformGroup rSeat2 = new TransformGroup(rSeatT);
        Box dBox = new Box(.07f,.2f,.3f,seatColor);
        cBox.setPickable(false);
        endArm2[j].addChild(rSeat);
        rSeat.addChild(pos1);
        pos1.addChild(cBox);
        endArm2[j].addChild(rSeat2);
        rSeat.addChild(pos2);
        pos2.addChild(dBox);
        //Sphere(.2f));
      }
    }

    group.addChild(tg);

    //group.addChild(new Box(0.3f,0.3f,0.3f, new A(C.green)));


   // universe.getViewingPlatform().setNominalViewingTransform();

    viewVector = new Vector3f(.0f,1.0f, 10.0f);
    viewTransform.setTranslation(viewVector);


    vp = universe.getViewingPlatform();

    viewTG =vp.getViewPlatformTransform();
    viewTG.setTransform(viewTransform);
    mover = new Mover(viewTransform, viewVector,
                            viewTG, 0);
    //viewTG.setCapability(vp.ALLOW_DETACH);
    //vp.detach();

  }
  public static void main( String[] args ) {

        mainRun = true;
        // general Set-up
        JFrame frame = new JFrame("Spinner");
        frame.addWindowListener(new WindowAdapter() {
          public void windowClosing(WindowEvent winEvent) {
				  System.exit(0);
			  }	});
        GraphicsConfiguration config =
            SimpleUniverse.getPreferredConfiguration();
        Canvas3D canvas = new Canvas3D(config);
        canvas.setSize(400, 400);
        frame.getContentPane().add(canvas);

        SimpleUniverse uni = new SimpleUniverse(canvas);
        BranchGroup group = new BranchGroup();
        Spinner spinner = new Spinner(group,uni,4);
        spinner.setUp(group);
        canvas.addKeyListener(spinner);
        Button go = new Button("Go");
        go.addActionListener(spinner);
        go.addKeyListener(spinner);
        frame.pack();
        frame.setVisible(true);
        if (args.length > 0) {
      		if (args[0].equals("ON"))
      			spinner.getOn();
      	}
  }

  public void setUp(BranchGroup group) {
    // directional and ambient white light
    if (onRide) riding = true;
    new MyLights(group);
    new SkyBackground(group, null);
    Box floor = new Box(40000f,.1f,40000f,new StandardAppearance(Color.green));
    floor.setPickable(false);

    group.addChild(floor);
    group.compile();

   // frame.getContentPane(). addComponentListener(this);

    universe.addBranchGraph(group);
    timer = new Timer(80,this);
    timer.start();
  }

  public void actionPerformed(ActionEvent e ) {
    //mover.actionPerformed(e);
    //System.out.println("tic"+tics);
    tics++;
    rot.rotY((Math.PI*tics)/36.0);
    rotTG.setTransform(rot);
    for (int i = 0; i < numArms; i++) {
    //  int s = i%2;
    //  s = s*2-1;
      rot2.rotY((-Math.PI*tics)/12.0);
      rotTG2[i].setTransform(rot2);
    }

    if (riding) {
       try {
           riderPos.getLocalToVworld(viewTransform);
           viewTransform.mul(endArmLevel2);
       }
       catch (Throwable blah){
           System.out.println(blah);
       }
       viewTG.setTransform(viewTransform);
    }
  }
public void keyReleased(KeyEvent e){
  mover.keyReleased(e);
// Invoked when a key has been released.
//  System.out.println("Key Released");
  }
public void keyTyped(KeyEvent e){
//System.out.println(e.getKeyChar() +" Key Typed "+e.getKeyCode());
//Invoked when a key has been typed.
}
public void keyPressed(KeyEvent e) {

  //Invoked when a key has been pressed.
  mover.keyPressed(e);

}
}

Virtuland - a virtual funfair with several rides

// Copyright 2003 Resplendent Technology Ltd.
// See objectlessons.com for details of the java3d course.

// The Virtuland class sets up the window and canvas and
// reads in the options for the virtual world program.

import javax.media.j3d.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.universe.*;
import javax.vecmath.*;
import java.applet.Applet;
import javax.swing.JApplet;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Primitive;

public class Virtuland extends Applet implements ActionListener, KeyListener {
	protected Button go = new Button("   Start   ");
	private Timer timer;
	private SimpleUniverse universe = null;
	private Vector3f viewVector;
	private ViewingPlatform vp = null;
	private TransformGroup viewTG = null;
	private Transform3D viewTransform = new Transform3D();
	private Mover mover;
	// private RootPaneContainer frame;
	private Ride activeRide = null;
	private boolean onRide = false;
	private Ride spinner = null;
	private Ride spinner2 = null;
	protected static boolean redblue = false;
	protected static int screenSize = 420;
	protected static boolean mainRun = false;
	protected static int mode;
	protected PrintCaptureCanvas3D canvas3D;
	protected static boolean sound = false;
	public static boolean on = false;
	protected Canvas3D canvas3D2;
	protected int delay = 70;
	protected boolean firstTime = true;
	private static int NUMRIDES = 4;
	protected Point3f[] rideLocation = new Point3f[NUMRIDES];
	protected Ride[] ride = new Ride[NUMRIDES];

	public static final int THREE_DIMENSIONAL = 1, TWO_DIMENSIONAL = 2,
			STEREO = 3;

	public static void main(String[] args) {
		mainRun = true;
		mode = TWO_DIMENSIONAL;
		if (args.length > 0) {
			if (args[0].equals("3D"))
				mode = THREE_DIMENSIONAL;
			if (args[0].equals("REDBLUE")) {
				mode = THREE_DIMENSIONAL;
				redblue = true;
			}
			if (args[0].equals("STEREO")) {
				mode = STEREO;
				screenSize = 500;
			}
		}
		if (args.length > 1) {
			if (args[1].equals("SOUND"))
				sound = true;
		}
		if (args.length > 2) {
			int s = 0;
			try {
				s = Integer.parseInt(args[2]);
			} catch (Throwable t) {
			}
			if (s > 0)
				screenSize = s;

			System.out.println("Size = " + screenSize);
		}
		Virtuland land = new Virtuland();
		if (mode == STEREO) {
			new MainFrame(land, screenSize, screenSize / 2);
		} else {
			// Frame frame =
			new MainFrame(land, screenSize, screenSize);
		}
		// p.playGame();

	}

	public Virtuland() {
		// go = new Button("Go");
		go.addActionListener(this);
		go.addKeyListener(this);

		// general Set-up
		if (mainRun) {
			System.out.println("run from main");
			/*
			 * JFrame jFrame = new JFrame("Welcome to Virtuland");
			 * jFrame.addWindowListener(new WindowAdapter() { public void
			 * windowClosing(WindowEvent winEvent) { System.exit(0); } }); frame =
			 * jFrame;
			 */
		} else {
			System.out.println("run from Applet");
			// frame = this;
			/*
			 * if (mode == STEREO) { mainFrame = new MainFrame(this, screenSize,
			 * screenSize/2); } else { Frame mainFrame = new MainFrame(this,
			 * screenSize, screenSize); }
			 */
		}
		this.setUp();
	}

	public void setUp() {
		// if (mode == STEREO) {
		// setLayout(new FlowLayout());
		// } else {
		setLayout(new BorderLayout());
		// }
		// Panel p =new Panel();
		// p.add(go);
		add(go, BorderLayout.NORTH);

		// create and size canvas, add it to frame
		GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
		// canvas3D = new Canvas3D(config);
		canvas3D = new PrintCaptureCanvas3D(config);
		if (mode == STEREO) {
			int canvasSize = screenSize / 2 - 7;
			canvas3D.setSize(canvasSize, canvasSize);
			canvas3D.setMonoscopicViewPolicy(View.LEFT_EYE_VIEW);
			// add(canvas3D);

			canvas3D2 = new Canvas3D(config);
			canvas3D2.setSize(canvasSize, canvasSize);
			canvas3D2.setMonoscopicViewPolicy(View.RIGHT_EYE_VIEW);
			// System.out.println("adding second canvas");
			// add(canvas3D2);
		} else {
			// add("Center", canvas3D);
		}

		timer = new Timer(delay, this);

		/*
		 * canvas.setSize(400, 400); canvas.addKeyListener(this);
		 * getContentPane().setLayout(new BorderLayout());
		 * frame.getContentPane().add(go); frame.getContentPane().add(canvas); //
		 * for JFrame
		 * 
		 * //frame.add3(canvas);
		 */
		universe = new SimpleUniverse(canvas3D);
		BranchGroup group = new BranchGroup();
		group.setCapability(group.ALLOW_CHILDREN_WRITE);
		group.setCapability(group.ALLOW_CHILDREN_EXTEND);
		// directional and ambient white light
		new MyLights(group);

		// background
		new SkyBackground(group, this); // use this for icons: , C.blue);
		// Create a textured floor

		int primFlags = Primitive.GENERATE_NORMALS
				+ Primitive.GENERATE_TEXTURE_COORDS;
		// Sphere sphere = new Sphere(0.5f, primflags, ap);
		Appearance ap = new Appearance();
		Palette.addTexture("tile2.gif", ap, this);
		// Palette.addTexture("tiles.jpg",ap,this);

		Box floor = new Box(4000f, 4000f, 0.05f, primFlags, new StandardAppearance(
				Color.green), 2);
		floor.getShape(Box.FRONT).setAppearance(ap);
		// use this for icons -
		// floor.getShape(Box.FRONT).setAppearance(Palette.GREEN);
		// Box floor = new Box(4000f,.1f,4000f,new A(C.green));
		// floor.setPickable(false);
		Transform3D turnFloor = new Transform3D();
		turnFloor.rotX(-Math.PI / 2);
		Transform3D turnFloor2 = new Transform3D();
		turnFloor2.rotZ(Math.PI / 4);
		turnFloor.mul(turnFloor2);
		TransformGroup floorTG = new TransformGroup(turnFloor);
		floorTG.addChild(floor);
		group.addChild(floorTG);
		addRides(group, universe);
		// set position of viewer
		viewVector = new Vector3f(.0f, 1.8f, 10.0f);
		viewTransform.setTranslation(viewVector);

		vp = universe.getViewingPlatform();

		viewTG = vp.getViewPlatformTransform();
		viewTG.setTransform(viewTransform);
		mover = new Mover(viewTransform, viewVector, viewTG, 0);
		// group.compile();
		// if (mainRun) {
		// ((JFrame)frame).pack();
		// ((JFrame)frame).show();
		// }
		/*
		 * Button go = new Button("Go"); go.addActionListener(this);
		 * go.addKeyListener(this);
		 */
		universe.addBranchGraph(group);
		if (mode == STEREO) {
			View view0 = universe.getViewer().getView();
			View view = new View();
			PhysicalBody myBod = view0.getPhysicalBody();
			myBod.setLeftEyePosition(new Point3d(-.001, 0.0, 0.0)); // default
																															// is(-0.033, 0.0,
																															// 0.0)
			myBod.setRightEyePosition(new Point3d(+.001, 0.0, 0.0));
			view.setPhysicalBody(myBod);
			view.setPhysicalEnvironment(view0.getPhysicalEnvironment());
			view.attachViewPlatform(universe.getViewingPlatform().getViewPlatform());
			view.addCanvas3D(canvas3D2);
		}
		// timer = new Timer(80,this);
		// timer.start();

	}

	public void addRides(Group group, SimpleUniverse universe) {
		// rideLocation = new )Point3f[2];
		// ride = new Ride[2];
		TransformGroup firstSpinnerPos = Pos.at(-5f, 0f, 0f);
		rideLocation[0] = new Point3f(-5f, 0f, 0f);
		group.addChild(firstSpinnerPos);
		TransformGroup secondSpinnerPos = Pos.at(11f, 0f, 0f);
		rideLocation[1] = new Point3f(11f, 0f, 0f);
		group.addChild(secondSpinnerPos);
		TransformGroup ferrisPos = Pos.at(0f, 0f, -15f);
		group.addChild(ferrisPos);
		rideLocation[2] = new Point3f(0f, 0f, -15f);
		TransformGroup objectPos = Pos.at(1f, 3.1f, -2f);
		Transform3D scaleT = new Transform3D();
		scaleT.set(3);
		TransformGroup scale = new TransformGroup(scaleT);
		objectPos.addChild(scale);
		GetModel.add("tour.obj", scale, this);
		// GetModel.add("super3.obj", scale, this);
		group.addChild(objectPos);
		TransformGroup merryPos = Pos.at(7f, 0f, 7f);
		rideLocation[3] = new Point3f(7f, 0f, 7f);
		group.addChild(merryPos);
		ride[3] = new MerryGoRound(merryPos, universe);
		ride[0] = new Spinner(firstSpinnerPos, universe, 4);
		ride[1] = new Spinner(secondSpinnerPos, universe, 3, 1.2f,
				(float) (-2 * Math.PI) / 18, 3, .8f, (float) (-2 * Math.PI) / 6,
				Palette.ORANGE, Palette.GREEN, Palette.CYAN, Palette.BLUE, Palette.RED,
				Palette.CYAN);
		Transform3D ferrisAngleT = new Transform3D();
		ferrisAngleT.rotY(Math.PI / 9);
		TransformGroup ferrisAngle = new TransformGroup(ferrisAngleT);
		ferrisPos.addChild(ferrisAngle);
		ride[2] = new Ferris(ferrisAngle, universe);

		activeRide = ride[0];

	}

	public void destroy() {
		universe.removeAllLocales();
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == go) {
			if (!timer.isRunning()) {
				timer.start();
				canvas3D.writeJPEG_ = false;
				canvas3D.saveJPEG_ = false;
				go.setLabel("Pause");
				// add("Center", canvas3D);

				if (firstTime) {
					firstTime = false;
					if (mode == STEREO) {
						add(canvas3D, BorderLayout.WEST);
						System.out.println("adding second canvas");
						add(canvas3D2, BorderLayout.EAST);
					} else {
						add("Center", canvas3D);
					}
					canvas3D.addKeyListener(this);
					validate();
				}
			} else {
				System.out.println("trying to save image");
				timer.stop();
				canvas3D.writeJPEG_ = true;
				canvas3D.saveJPEG_ = true;
				canvas3D.repaint();
				go.setLabel("Continue");

			}
		} else {
			mover.actionPerformed(e);
			if (activeRide != null) {
				e.setSource(mover);
				activeRide.actionPerformed(e);
			}
			float closest = Float.MAX_VALUE;
			for (int i = 0; i < NUMRIDES; i++) {
				float distSquared = mover.ahead(0f).distanceSquared(rideLocation[i]);
				if (distSquared < closest) {
					closest = distSquared;
					activeRide = ride[i];
				}
				/*
				 * float distSquared = viewVector.x*mover.viewVector.x +
				 * mover.viewVector.y*mover.viewVector.y +
				 * mover.viewVector.z*mover.viewVector.z;
				 */
				if (!ride[i].riding()) {
					// System.out.println("Distance: "+ distSquared);
					if ((distSquared) < 30) {
						// System.out.println("Get on the ride!");
						on = true;
						ride[i].getOn();

					}
				} else {
					if ((distSquared) >= 30) {
						ride[i].getOff();
						on = false;
					}
				}
			}
		}
	}

	public void keyReleased(KeyEvent e) {
		mover.keyReleased(e);
		// Invoked when a key has been released.
		// System.out.println("Key Released");
	}

	public void keyTyped(KeyEvent e) {
		// System.out.println(e.getKeyChar() +" Key Typed "+e.getKeyCode());
		// Invoked when a key has been typed.
	}

	public void keyPressed(KeyEvent e) {
		// Invoked when a key has been pressed.
		// System.out.println(e.getKeyChar() +" Key Pressed "+e.getKeyCode());
		mover.keyPressed(e);
	}

	// equivalent of main for applet
	public void init() {
		if (!mainRun) {
			mode = TWO_DIMENSIONAL;
			String v = getParameter("mode");
			String s = getParameter("sound");
			if ("ON".equals(s))
				sound = true;
			if ("3D".equals(v))
				mode = THREE_DIMENSIONAL;
			if ("STEREO".equals(v))
				mode = STEREO;
			int sz = 0;
			String siz = getParameter("screenSize");
			if (siz != null) {
				try {
					sz = Integer.parseInt(siz);
				} catch (Throwable t) {
				}
			}
			if (sz > 0)
				screenSize = sz;
			try {
				setUp();
			} catch (Throwable thrown) {
				System.out.println(thrown);
				thrown.printStackTrace();
			}
			// playGame();
		}
	}
} // end of class

Mover - a class for moving through virtual worlds

// Copyright 2003 Resplendent Technology Ltd.
// See objectlessons.com for details of the java3d course.

import java.awt.event.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class Mover implements ActionListener, KeyListener {
	private Transform3D viewTransform;
	private Transform3D rotationY;
	private Transform3D rotationX;
	public Vector3f viewVector;
	private TransformGroup viewTG;
	private double facingAngle = 0;
	private double upAngle = 0;
	private double moveSpeed = .15;
	private double turnSpeed = .045;
	private boolean downArrow = false;
	private boolean upArrow = false;
	private boolean leftArrow = false;
	private boolean rightArrow = false;
	private boolean alt = false;
	public boolean pageDown = false;
	public boolean pageUp = false;
	public boolean end = false;

	public Mover(Transform3D viewTransform, Vector3f viewVector,
			TransformGroup viewTG, double facingAngle) {
		this.viewTransform = viewTransform;
		this.viewVector = viewVector;
		this.viewTG = viewTG;
		this.facingAngle = facingAngle;
		rotationY = new Transform3D();
		rotationX = new Transform3D();

	};

	public Point3f ahead(double distance) {
		float z1 = viewVector.z - (float) (distance * Math.cos(facingAngle));
		float x1 = viewVector.x - (float) (distance * Math.sin(facingAngle));
		float y1 = viewVector.y;
		return new Point3f(x1, y1, z1);
	}

	public void actionPerformed(ActionEvent e) {
		// called by timer
		// int k = e.getKeyCode();
		// switch(k) {
		// case 27: // escape
		// case 18: // alt
		// case 16: // shift

		if (leftArrow && !alt) {
			facingAngle = facingAngle + turnSpeed;
		}
		if (upArrow) {
			viewVector.z = viewVector.z - (float) (moveSpeed * Math.cos(facingAngle));
			viewVector.x = viewVector.x - (float) (moveSpeed * Math.sin(facingAngle));
		}
		if (rightArrow && !alt) {
			facingAngle = facingAngle - turnSpeed;
		}
		if (downArrow) {
			viewVector.z = viewVector.z + (float) (moveSpeed * Math.cos(facingAngle));
			viewVector.x = viewVector.x + (float) (moveSpeed * Math.sin(facingAngle));
		}
		// strafe left
		if (leftArrow && alt) {
			viewVector.z = viewVector.z
					- (float) (moveSpeed * Math.cos(facingAngle + Math.PI / 2));
			viewVector.x = viewVector.x
					- (float) (moveSpeed * Math.sin(facingAngle + Math.PI / 2));
		}
		// strafe right
		if (rightArrow && alt) {
			viewVector.z = viewVector.z
					- (float) (moveSpeed * Math.cos(facingAngle - Math.PI / 2));
			viewVector.x = viewVector.x
					- (float) (moveSpeed * Math.sin(facingAngle - Math.PI / 2));
		}
		if (pageUp && upAngle < (Math.PI / 2)) {
			upAngle = upAngle + turnSpeed;
		}
		if (pageDown && upAngle > (-Math.PI / 2)) {
			upAngle = upAngle - turnSpeed;
		}
		if (end) {
			upAngle = 0;
		}
		viewTransform = new Transform3D();
		viewTransform.setTranslation(viewVector);
		rotationY.rotY(facingAngle);
		rotationX.rotX(upAngle);
		viewTransform.mul(rotationY);
		viewTransform.mul(rotationX);
		if (!Virtuland.on) {
			viewTG.setTransform(viewTransform);
			// System.out.println("actionPerformed: " + facingAngle);
		}
	}

	public void keyReleased(KeyEvent e) {
		// Invoked when a key has been released.
		int k = e.getKeyCode();
		// System.out.println("Key released: "+k);
		switch (k) {
		// case 27: // escape
		case 18: // alt
			alt = false;
			break;
		case 33:
			pageUp = false;
			break;
		case 34:
			pageDown = false;
			break;
		case 35:
			end = false;
			break;
		// case 16: // shift
		case 37: // left
			leftArrow = false;
			break;
		case 38: // up arrow
			upArrow = false;
			break;
		case 39: // right arrow
			rightArrow = false;
			break;
		case 40: // down arrow
			downArrow = false;
			break;
		}
	}

	public void keyTyped(KeyEvent e) {
		// Invoked when a key has been typed.
	}

	public void keyPressed(KeyEvent e) {

		// Invoked when a key has been pressed.
		// System.out.println(e.getKeyChar() +" Key Pressed "+e.getKeyCode());
		int k = e.getKeyCode();
		switch (k) {
		// case 27: // escape
		case 18: // alt
			alt = true;
			break;
		case 33:
			pageUp = true;
			break;
		case 34:
			pageDown = true;
			break;
		case 35:
			end = true;
			break;
		// case 16: // shift
		case 37: // left
			leftArrow = true;
			break;
		case 38: // up arrow
			upArrow = true;
			break;
		case 39: // right arrow
			rightArrow = true;
			break;
		case 40: // down arrow
			downArrow = true;
			break;
		}

	}

	public double getFacingAngle() {
		return facingAngle;
	}

	public void setFacingAngle(double facingAngle) {
		this.facingAngle = facingAngle;
	}

} // end of class

FairBuilder - drag and drop to build a funfair

//import java.util.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.net.URL;
import java.applet.*;

public class FairBuilder implements ActionListener{
  private Timer timer;
  protected  JPanel mainPanel;

  protected  JPanel waitPanel;
  protected  Container buttonsAndTarget;
  protected  JPanel fairPanel;
  protected Fair fair;
public static DragSourceJLabel createLabel(String filename, Applet applet) {
  URL url = FindUrl.get(filename, applet);
  ImageIcon icon = new ImageIcon(url,"description");
  DragSourceJLabel imageLabel = new DragSourceJLabel(icon);//("image");
  return imageLabel;
}
   // Possible Look & Feels
    private static final String mac      =
            "com.sun.java.swing.plaf.mac.MacLookAndFeel";
    private static final String metal    =
            "javax.swing.plaf.metal.MetalLookAndFeel";
    private static final String motif    =
            "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
    private static final String windows  =
            "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
public static void main(String[] args) {
  new FairBuilder();
}
public FairBuilder() {
  try {
    UIManager.setLookAndFeel(windows);
  } catch(Throwable thrown){System.out.println(thrown);}

  JFrame topFrame = new JFrame("Build a funfair");
  topFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  //JTabbedPane buildTab = new JTabbedPane(JTabbedPane.TOP);
  mainPanel = new JPanel();
  new BoxLayout(mainPanel,BoxLayout.Y_AXIS);
  waitPanel = new JPanel();
  waitPanel.add(new JLabel("Building the fair. Please wait..."));
  Box panel = Box.createHorizontalBox(); 
  //panel.setLayout(new BoxLayout());
  new BoxLayout(panel,BoxLayout.X_AXIS);
  panel.setPreferredSize(new Dimension(600,120));
  //panel.setBackground(Color.cyan);
  buttonsAndTarget = Box.createVerticalBox();//new JPanel();
  new BoxLayout(buttonsAndTarget,BoxLayout.Y_AXIS);
  buttonsAndTarget.add(panel);
  fairPanel = new JPanel();
  new BoxLayout(fairPanel,BoxLayout.Y_AXIS);
  fair = new Fair(fairPanel, this);
 
  Applet applet = new Applet();

  DragSourceJLabel imageLabel = createLabel("bigwheelbutton.jpg",applet);
  imageLabel.setToolTipText("A Ferris Wheel");
  imageLabel.setName("ferris");
  panel.add(imageLabel,BoxLayout.X_AXIS);

  DragSourceJLabel twisterLabel = createLabel("twisterbutton.jpg",applet);
  twisterLabel.setToolTipText("Twister - a spinning ride");
  twisterLabel.setName("twister");
  panel.add(twisterLabel);

  DragSourceJLabel carouselLabel = createLabel("merrygoroundbutton.jpg",applet);
  carouselLabel.setToolTipText("A Carousel");
  carouselLabel.setName("carousel");
  panel.add(carouselLabel,BoxLayout.X_AXIS);

  DragSourceJLabel bermudaLabel = createLabel("bermudatrianglebutton.jpg",applet);
  bermudaLabel.setToolTipText("Bermuda Triangle - a spinning ride");
  bermudaLabel.setName("bermuda");
  panel.add(bermudaLabel);
  DrawingBoard target = new DrawingBoard();
  target.setFair(fair);
  target.setBackground(Color.green);
  target.setPreferredSize(new Dimension(300,300));
  JButton button = new JButton("Explore the Fair");
  button.addActionListener(this);
  buttonsAndTarget.add(target);

  //button.setBackground(Color.cyan);
  //Component content=new DragSourceJLabel("hello", DnDConstants.ACTION_MOVE);
  //Container container = new Panel();
  //container.setSize(300,200);
  //container.add(content);
  //content.setLocation(0,0);
  //container.validate();

  topFrame.getContentPane().add(mainPanel);// , BorderLayout.NORTH);
  //mainPanel.add(buildTab, BorderLayout.NORTH);
  Box box = new Box(BoxLayout.X_AXIS);
  box.add(button);
  buttonsAndTarget.add(box);
  mainPanel.add(buttonsAndTarget);

  topFrame.pack();
  topFrame.setVisible(true);
}
public void actionPerformed(ActionEvent e ) {
  if (e.getSource() != timer) {
  System.out.println("Button pressed.");
  mainPanel.removeAll();
  mainPanel.add(waitPanel);
  mainPanel.validate();
  mainPanel.repaint();
  timer = new Timer(80,this);
  timer.start();
} else {

  timer.stop();
  mainPanel.removeAll();
  mainPanel.add(fairPanel);
  try {
    fair.initialize();
  }
  catch(Throwable error) {
    System.out.println("Too much memory used?");
    fair = null;
    System.runFinalization();
    System.out.println(error.getMessage());
    backToTheDrawingBoard();
  }
  mainPanel.validate();
  mainPanel.repaint();
}}
public void backToTheDrawingBoard() {
  mainPanel.removeAll();
  mainPanel.add(buttonsAndTarget);
  mainPanel.repaint();
  //fairPanel.removeAll();
}
} // end of class

Download all of the code and resources for the fair example.

This is an applet (requires Java 3D plug-in) for exploring the funfair. See our instructions for running Java3D applets if you can't see the funfair below.

Explore the funfair using the arrow keys.