
// WrapTrees3D.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/* Standardní šachovnicová scéna, avšak nyní s několika
   výhodnky stromů, které rostou po dobu několika sekund.
*/


import javax.swing.*;
import java.awt.*;

import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.behaviors.vp.*;
import com.sun.j3d.utils.image.*;



public class WrapTrees3D extends JPanel
// Uchovává 3D-canvas, na nemž se zobrazuje nactený obrázek
{
  private static final int PWIDTH = 512;   // velikost panelu
  private static final int PHEIGHT = 512; 

  private static final int BOUNDSIZE = 100;  // větší než virtuální svět

  private static final Point3d USERPOSN = new Point3d(0,7,30);
    // počáteční pozice uživatele (further back than usual)

  // konstanty pro jednotlivé osy
  private final static int X_AXIS = 0;
  private final static int Y_AXIS = 1;
  private final static int Z_AXIS = 2;


  private SimpleUniverse su;
  private BranchGroup sceneBG;
  private BoundingSphere bounds;   // pro uzly prostredí



  public WrapTrees3D()
  // Panel uchovávající 3D-canvas: obvyklý způsob propojení knihoven Java 3D a Swing
  {
    setLayout( new BorderLayout() );
    setOpaque( false );
    setPreferredSize( new Dimension(PWIDTH, PHEIGHT));

    GraphicsConfiguration config =
					SimpleUniverse.getPreferredConfiguration();
    Canvas3D canvas3D = new Canvas3D(config);
    add("Center", canvas3D);
    canvas3D.setFocusable(true);     // předej focus herní obrazovce
    canvas3D.requestFocus();

    su = new SimpleUniverse(canvas3D);

    createSceneGraph();
    initUserPosition();        // nastav stanoviště pozorovatele
    orbitControls(canvas3D);   // ovládací prvky pro pohyb tohoto stanoviště

    // kvůli průhledným obrázkům listů zapni hloubkové třídění
    View view = su.getViewer().getView();
    // hloubkové třídění průhledných objektů na bázi jednotlivých geometrií
    view.setTransparencySortingPolicy(View.TRANSPARENCY_SORT_GEOMETRY);

    
    su.addBranchGraph( sceneBG );
  } // konec metody: WrapTrees3D()



  private void createSceneGraph() 
  // inicializuje scénu
  { 
    sceneBG = new BranchGroup();
    bounds = new BoundingSphere(new Point3d(0,0,0), BOUNDSIZE);   

    lightScene();         // přidej světla
    addBackground();      // přidej oblohu
    sceneBG.addChild( new CheckerFloor().getBG() );  // přidej podlahu

    growTrees();          // nový prvek

    sceneBG.compile();   // zafixuj scénu
  } // konec metody: createSceneGraph()


  private void lightScene()
  /* Jedno ambientní svetlo, 2 smerová svetla */
  {
    Color3f white = new Color3f(1.0f, 1.0f, 1.0f);

    // připrav ambientní světlo
    AmbientLight ambientLightNode = new AmbientLight(white);
    ambientLightNode.setInfluencingBounds(bounds);
    sceneBG.addChild(ambientLightNode);

    // připrav směrová světla
    Vector3f light1Direction  = new Vector3f(-1.0f, -1.0f, -1.0f);
       // vlevo, dolů, dozadu 
    Vector3f light2Direction  = new Vector3f(1.0f, -1.0f, 1.0f);
       // vpravo, dolu, dopredu

    DirectionalLight light1 = 
            new DirectionalLight(white, light1Direction);
    light1.setInfluencingBounds(bounds);
    sceneBG.addChild(light1);

    DirectionalLight light2 = 
        new DirectionalLight(white, light2Direction);
    light2.setInfluencingBounds(bounds);
    sceneBG.addChild(light2);
  }  // konec metody: lightScene()



  private void addBackground()
  // modrá obhloha
  { Background back = new Background();
    back.setApplicationBounds( bounds );
    back.setColor(0.17f, 0.65f, 0.92f);    // barva oblohy
    sceneBG.addChild( back );
  }  // konec metody: addBackground()



  private void orbitControls(Canvas3D c)
  /* Třída OrbitBehaviour umožňuje uživateli otáčet, přibližovat a oddalovat scénu. */  {
    OrbitBehavior orbit = 
		new OrbitBehavior(c, OrbitBehavior.REVERSE_ALL);
    orbit.setSchedulingBounds(bounds);

    ViewingPlatform vp = su.getViewingPlatform();
    vp.setViewPlatformBehavior(orbit);	    
  }  // konec metody: orbitControls()



  private void initUserPosition()
  // Pomocí metody lookAt() nastaví počáteční výhled uživatele
  {
    ViewingPlatform vp = su.getViewingPlatform();
    TransformGroup steerTG = vp.getViewPlatformTransform();

    Transform3D t3d = new Transform3D();
    steerTG.getTransform(t3d);

    // argumenty jsou: pozice pozorovatele, kam se dívá, směr nahoru
    t3d.lookAt( USERPOSN, new Point3d(0,0,0), new Vector3d(0,1,0));
    t3d.invert();

    steerTG.setTransform(t3d);
  }  // konec metody: initUserPosition()


  // ---------------------- růst stromů -----------------


  private void growTrees()
  /* Umístí tři stromy a vytvoří objekt chování, který řídí
     jejich růst. */
  {
    // počáteční pozice prvního stromu: (0,0,-5)
    Transform3D t3d = new Transform3D();
    t3d.set( new Vector3f(0,0,-5)); 
    TransformGroup tg0 = new TransformGroup(t3d);
    sceneBG.addChild(tg0);
    // vytvoř strom
    TreeLimb t0 = new TreeLimb(Z_AXIS, 0, 0.05f, 0.5f, tg0, null);

    // druhý strom
    t3d.set( new Vector3f(-5,0,5)); 
    TransformGroup tg1 = new TransformGroup(t3d);
    sceneBG.addChild(tg1);
    TreeLimb t1 = new TreeLimb(Y_AXIS, 45, 0.05f, 0.5f, tg1, null);

    // třetí strom
    t3d.set( new Vector3f(5,0,5)); 
    TransformGroup tg2 = new TransformGroup(t3d);
    sceneBG.addChild(tg2);
    TreeLimb t2 = new TreeLimb(Y_AXIS, -60, 0.05f, 0.5f, tg2, null);

    // čtvrtý strom
    t3d.set( new Vector3f(-9,0,-9)); 
    TransformGroup tg3 = new TransformGroup(t3d);
    sceneBG.addChild(tg3);
    TreeLimb t3 = new TreeLimb(Y_AXIS, 30, 0.05f, 0.5f, tg3, null);

    // 5. strom
    t3d.set( new Vector3f(9,0,-9)); 
    TransformGroup tg4 = new TransformGroup(t3d);
    sceneBG.addChild(tg4);
    TreeLimb t4 = new TreeLimb(Y_AXIS, -30, 0.05f, 0.5f, tg4, null);


    // načti obrázky listů používané ve všech stromech
    ImageComponent2D[] leafIms = loadImages("images/leaf", 6);

    // objekt chování, který se stará o růst stromů
    GrowthBehavior grower = new GrowthBehavior(leafIms);   
    grower.setSchedulingBounds( bounds );

    // přidej stromy do objektu typu GrowthBehavior
    grower.addLimb(t0);
    grower.addLimb(t1);
    grower.addLimb(t2);
    grower.addLimb(t3);
    grower.addLimb(t4);

    sceneBG.addChild( grower );
  }  // konec metody: growTrees()



  private ImageComponent2D[] loadImages(String fNms, int numIms)
  /* Načte obrázky listů: všechny začínají prefixem fNms a je jich
     celkem numIms. */
  {
    String filename;
    TextureLoader loader;
    ImageComponent2D[] ims = new ImageComponent2D[numIms];
    System.out.println("Načítám " + numIms + " textur z " + fNms);
    for (int i=0; i < numIms; i++) {
      filename = new String(fNms + i + ".gif");
      loader = new TextureLoader(filename, null);
      ims[i] = loader.getImage();
      if(ims[i] == null)
        System.out.println("Načtení textury selhalo: " + filename);
      ims[i].setCapability(ImageComponent2D.ALLOW_SIZE_READ);
    }
    return ims;
  }  // konec metody: loadImages()


} // konec třídy: WrapTrees3D