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

/* Připraví 3D-svět: osvětlení, pozadí, ovládání pomocí kláves.

   Používá objekt typu Landscape pro načtení krajiny (sítě a
   textury), stěn krajiny, 3D-scenérie a pokrývky povrchu.

   Temně modrá obloha obsahuje hvězdy (světlé tečky).

   Uživatel se po terénu pohybuje pomocí objektu typu KeyBehavior.
   Velikost sítě terénu ovšem znamená, že existuje prodleva před tím,
   než výška uživatele správně odráží výšku terénu v místě, kde stojí.
   
   Například je možné vejít dovnitř hory, ovšem jakmile
   se výpočet výšky (pomocí techniky vybírání) dokončí, výška uživatele
   se opraví.

   Toto chování je záměrné - umožňuje rychlí pohyb uživatele po terénu, aniž by
   byl nucen čekat na dokončení výpočtu výšky.
*/

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

import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;



public class WrapTerra3D 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 Color3f white = new Color3f(1.0f, 1.0f, 1.0f);

  private final static int NUM_STARS = 5000;   // počet hvězd na obloze


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

  private Landscape land;


  public WrapTerra3D(String fname)
  // zkonstruuje 3D-canvas
  {
    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(fname);
    createUserControls();
    
    su.addBranchGraph( sceneBG );
  } // konec metody: WrapTerra3D()



  void createSceneGraph(String fname) 
  // inicializuje scénu
  { 
    sceneBG = new BranchGroup();
    bounds = new BoundingSphere(new Point3d(0,0,0), BOUNDSIZE);
   
    lightScene();         // přidej světla
    addBackground();      // přidej oblohu and stars
    land = new Landscape(sceneBG, fname);    
        // přidej krajinu, scenérii, zdi, stromy

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



  private void lightScene()
  // jedno ambientní a jedno směrové světlo
  {
    // 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()
  // pozadí je modré a jsou na něm náhodně umístěné hvězdy
  { 
    Background back = new Background();
    back.setApplicationBounds( bounds );
    // back.setColor(0.17f, 0.07f, 0.45f);    // temně modrá
    // back.setColor(0.17f, 0.65f, 0.92f);    // světle modrá
    back.setColor(0.17f, 0.50f, 0.92f);
    back.setGeometry( addStars() );
    sceneBG.addChild( back );
  }  // konec metody: addBackground()


  private BranchGroup addStars()
  /* Hvězdy jsou realizovány pomocí bodů v objektu typu PointArray 
     s náhodnými barvami, umístěné v uzlu typu BranchGroup.
     Původně od Kevina Dulinga - viz jeho příklad na adrese
        http://home.earthlink.net/~kduling/Java3D/Stars/index.html
  */
  {
    PointArray starField =  new PointArray(NUM_STARS, PointArray.COORDINATES | 
                                                      PointArray.COLOR_3);
    float[] pt = new float[3];
    float[] brightness = new float[3];
    Random rand = new Random();

    for (int i=0; i < NUM_STARS; i++) {
      pt[0] = (rand.nextInt(2) == 0) ? -rand.nextFloat() : rand.nextFloat();
      pt[1] = rand.nextFloat();     // only above the y-axis
      pt[2] = (rand.nextInt(2) == 0) ? -rand.nextFloat() : rand.nextFloat();
      starField.setCoordinate(i, pt);

      float mag = rand.nextFloat();
      brightness[0] = mag;
      brightness[1] = mag;
      brightness[2] = mag;
      starField.setColor(i, brightness);
    }

    BranchGroup bg = new BranchGroup();
    bg.addChild( new Shape3D(starField) );
    return bg;
  }  // konec metody: addStars()



  private void createUserControls()
  /* Upraví ořezávací vzdálenosti a připraví objekt typu KeyBehavior.
     Objekt chování nastaví výhled do počátku roviny XZ.
     Dále zapne hloubkové třídění, kvůli průhledným obrázků typu GIF
     použitým pro stromy.
  */
  {
    // původní hodnoty ořezání: 10 a 0.1; udržuj poměr mezi 100-1000
    View view = su.getViewer().getView();
    view.setBackClipDistance(20);      // můžeme vidět hodně daleko
    view.setFrontClipDistance(0.05);   // můžeme vidět blízské objekty

    // hloubkové třídění objektů na bázi jejich geometrie
    view.setTransparencySortingPolicy(View.TRANSPARENCY_SORT_GEOMETRY);

    ViewingPlatform vp = su.getViewingPlatform();
    TransformGroup steerTG = vp.getViewPlatformTransform();

    // připrav ovládací prvky klávesnice (a umísti výhled)
    KeyBehavior keybeh = new KeyBehavior(land, steerTG);
    keybeh.setSchedulingBounds(bounds);
    vp.setViewPlatformBehavior(keybeh);
  } // konec metody: createUserControls()

} // konec třídy: WrapTerra3D
