/*
 * Simple program to display a growing tree.
 *
 * Scott Teresi, www.teresi.us
 * March, 1999
 *
 */

import java.util.Random;
import java.awt.Frame;
import java.awt.BorderLayout;
import java.awt.event.*;

import javax.media.j3d.*;
import javax.vecmath.*;

import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.loaders.Scene;
import com.sun.j3d.loaders.SceneBase;



public class Tree {


  Color3f skyColor = new Color3f(0.0f, 0.2f, 1f);  // (0, .6, .2)
  Color3f sphereColor = new Color3f(0.9f, 0.2f, 0.05f);
  Color3f groundColor = new Color3f(0.1f, .65f, 0.02f);
  int numSpheres = 20;
  float sphereSize = 2.0f;
  int rotateSpeed = 60000;   // 60,000 ms per revolution

  int branchCount = 0;
  int segmentCount = 0;


  public Tree() {


    // set up the display frame

    Frame frame = new Frame("Tree Display");
    frame.setLayout(new BorderLayout());
    WindowListener l = new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
	System.exit(0);
      }
    };
    frame.addWindowListener(l);

    Canvas3D canvas = new Canvas3D(null);
    frame.setSize(800, 700);
    frame.add("Center", canvas);
    frame.setVisible(true);

    // create the universe

    VirtualUniverse u = new VirtualUniverse();
    Locale locale = new Locale(u);


    // set up the view

    // view transformations
    Transform3D transform = new Transform3D();
    transform.setTranslation(new Vector3d(0, 16, 75));
    Transform3D rotateX = new Transform3D();
    rotateX.rotX(-Math.PI/6);
    transform.mul(rotateX, transform);

    BranchGroup viewBranch = new BranchGroup();
    TransformGroup firstViewTrans = new TransformGroup(transform);
    TransformGroup viewTrans = new TransformGroup();
    ViewPlatform viewPlatform = new ViewPlatform();

    viewBranch.addChild(firstViewTrans);
    firstViewTrans.addChild(viewTrans);
    viewTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    viewTrans.addChild(viewPlatform);
    viewPlatform.setActivationRadius(1000.0f);

    /*
      // perform rotation animation on the View
      // (camera just rotates in place; no good)
    RotationInterpolator rotator =
                      new RotationInterpolator(new Alpha(-1, 4000), viewTrans);
    BoundingSphere boundSphere = new BoundingSphere(new Point3d(0d, 0d, 0d),
						    100d);
    rotator.setSchedulingBounds(boundSphere);
    viewTrans.addChild(rotator);
    */

    View view = new View();
    view.attachViewPlatform(viewPlatform);
    view.addCanvas3D(canvas);
    view.setPhysicalBody(new PhysicalBody());
    view.setPhysicalEnvironment(new PhysicalEnvironment());
    view.setBackClipDistance(100000f);
    view.setViewPolicy(View.SCREEN_VIEW);
    view.setWindowResizePolicy(View.VIRTUAL_WORLD);   // doesn't work
    view.setWindowMovementPolicy(View.VIRTUAL_WORLD); // doesn't work

    viewBranch.compile();
    locale.addBranchGraph(viewBranch);


    // apply lighting, background color

    BranchGroup mainBg = new BranchGroup();

    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
					       200.0);
    Background bgrnd = new Background(skyColor);
    bgrnd.setApplicationBounds(bounds);
    mainBg.addChild(bgrnd);

    PointLight light1 = new PointLight();
    light1.setPosition(new Point3f(30, 20, -100));
    light1.setColor(new Color3f(1.0f, 1.0f, 1.0f));
    light1.setInfluencingBounds(bounds);
    mainBg.addChild(light1);

    AmbientLight ambient1 = new AmbientLight(true, new Color3f(1f, 1f, 1f));
    ambient1.setInfluencingBounds(bounds);
    mainBg.addChild(ambient1);


    // get the shape to display

    mainBg.addChild(createTree());
    mainBg.compile();


    // set up rotation animation behavior

    BranchGroup objRoot = new BranchGroup();
    TransformGroup objSpin = new TransformGroup();

    objSpin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    objSpin.addChild(mainBg);
    objRoot.addChild(objSpin);
    RotationInterpolator rotator =
                 new RotationInterpolator(new Alpha(-1, rotateSpeed), objSpin);
    BoundingSphere boundSphere = new BoundingSphere();
    rotator.setSchedulingBounds(boundSphere);
    objSpin.addChild(rotator);

    objRoot.compile();
    locale.addBranchGraph(objRoot);

  }



  BranchGroup createTree() {

    BranchGroup bg = new BranchGroup();
    bg.setCapability(BranchGroup.ALLOW_BOUNDS_READ);

    // create ground

    int numPoints = 4;

    Point3f[] points;
    points = new Point3f[numPoints];
    points[0] = new Point3f(-20f, 0f, 20f);
    points[1] = new Point3f(20f, 0f, 20f);
    points[2] = new Point3f(20f, 0f, -20f);
    points[3] = new Point3f(-20f, 0f, -20f);

    Color3f[] colors = new Color3f[4];
    for (int i = 0; i < 4; i ++)
      colors[i] = groundColor;

    int[] indices = new int[4];
    indices[0] = 0;
    indices[1] = 1;
    indices[2] = 2;
    indices[3] = 3;

    int[] stripCounts = new int[1];
    stripCounts[0] = 4;

    GeometryInfo geoInfo = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
    geoInfo.setStripCounts(stripCounts);
    geoInfo.setCoordinates(points);
    geoInfo.setCoordinateIndices(indices);
    geoInfo.setColors(colors);
    geoInfo.setColorIndices(indices);

    Triangulator tr = new Triangulator();
    tr.triangulate(geoInfo);
    NormalGenerator nm = new NormalGenerator();
    nm.generateNormals(geoInfo);

    Shape3D shape = new Shape3D(geoInfo.getGeometryArray(), new Appearance());

    bg.addChild(shape);


    // initialize tree

    BranchGroup treeRoot = new BranchGroup();
    treeRoot.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
    BranchBehavior branchBehavior =
      new BranchBehavior(treeRoot, new Point3d(0.0, 0.0, 0.0),
			 new Point3d(0.0, 2.5, 0.0), this);
    branchBehavior.setSchedulingBounds(new BoundingSphere(
					      new Point3d(0d, 0d, 0d), 10d));
    bg.addChild(branchBehavior);
    bg.addChild(treeRoot);
    bg.compile();

    return bg;
  }


  public void incrementBranchCount() {
    branchCount ++;
  }

  public void incrementSegmentCount() {
    segmentCount ++;
  }


  public int getBranchCount() {
    return branchCount;
  }

  public int getSegmentCount() {
    return segmentCount;
  }


  public static void main (String args[]) {

    Tree tree = new Tree();
  }


}
