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

/* Obvyklí čachovnicový svět, nyní ovšem s následujícím:
       * scenérie a překážky
           - scenérie se načítá pomocí objektů typu PropManager
           - sprity nemohou chodit skrze překážky 
       * cestující 3D-sprite ovládaný z klávesnice
       * autonomní lovící ruka vetřelce
       * výhled, který se pohybuje se spritem (kamera z pohledu třetí osoby)
       * otexturované pozadí
       * celoobrazovkový režim (stiskněte klávesu q nebo ENTER pro ukončení programu)
*/

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

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


// import com.tornadolabs.j3dtree.*;    // pro zobrazení grafu scény


public class WrapTour3D 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 SimpleUniverse su;
  private BranchGroup sceneBG;
  private BoundingSphere bounds;   // pro uzly prostredí

  private JFrame win;   // potřebné pro ukončení

  private Obstacles obs;
  private TourSprite bob;   // turista

  // private Java3dTree j3dTree;   // rám pro umístění stromového zobrazení


  public WrapTour3D(String tourFnm, JFrame jf)
  // zkonstruuje 3D-canvas
  {
    win = jf;
    setLayout( new BorderLayout() );
    setOpaque( false );

    // setPreferredSize( new Dimension(PWIDTH, PHEIGHT));
    setPreferredSize( Toolkit.getDefaultToolkit().getScreenSize() );  // celoobrazovkový režim

    GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
    Canvas3D canvas3D = new Canvas3D(config);
    add("Center", canvas3D);

    canvas3D.setFocusable(true);
    canvas3D.requestFocus();    // vstup od klávesnice je náš, přijímáme tedy její události

	canvas3D.addKeyListener( new KeyAdapter() {
	// pro snadné ukončení z celoobrazovkové konfigurace
	// naslouchej klávesám esc, q, end, ctrl-c na herní obrazovce
       public void keyPressed(KeyEvent e)
       { int keyCode = e.getKeyCode();
         if ((keyCode == KeyEvent.VK_ESCAPE) || (keyCode == KeyEvent.VK_Q) ||
             (keyCode == KeyEvent.VK_END) ||
             ((keyCode == KeyEvent.VK_C) && e.isControlDown()) ) {
           win.dispose();
           System.exit(0);    // samotné volání exit() ve většině případů nestačí
         }
       }
     });

    su = new SimpleUniverse(canvas3D);

    // j3dTree = new Java3dTree();   // vytvoř zobrazovací strom pro SG

    createSceneGraph(tourFnm);
    su.addBranchGraph( sceneBG );

	// j3dTree.updateNodes( su );    // sestav okno pro zobrazení stromu

  } // konec metody: WrapTour3D()


  private void createSceneGraph(String tourFnm) 
  // 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

    makeScenery(tourFnm);      // přidej scenérii a překážky
    addTourist();              // přidej 3D-sprity ovládaný uživatelem

    addAlien();                // přidej 3D-sprite samo-naváděcího vetřelce alien 3D sprite

        // nastav bity oprávnění pro stromové zobrazení
	// j3dTree.recursiveApplyCapability( sceneBG ); 

    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á obloha s mraky vytvořená pomocí roztažené textury
  {
    TextureLoader bgTexture = new TextureLoader("models/bigSky.jpg", null);
	Background back = new Background(bgTexture.getImage());
    back.setImageScaleMode(Background.SCALE_FIT_MAX);
    // back.setImageScaleMode(Background.SCALE_REPEAT);   // dlaždicový přístup
    back.setApplicationBounds( bounds );

    sceneBG.addChild( back );
  } // konec metody: addBackground() v.2



  // --------------------- překážky -----------------------


  private void makeScenery(String tourFnm)
  /* Analyzuje konfigurační soubor, který obsahuje názvy 
     objektů scenérie, které mají být načteny a pozice překážek.
     Jeho formát vypadá takto:
        <object file>
        [-o (x1,z1) (x2,z2) ... ]     // souřadnice překážek
        <object file>
               :
     Scenérie se načítá pomocí objektů typu PropManager a ke každému
     modelu překpokládáme, že existuje příslušný konfigurační soubor.     

     Překážky jsou uloženy jako objekty typu Obstacle a poté přidány
     do scény.
  */
  { obs = new Obstacles();
    PropManager propMan;
    String tourFile = "models/"+tourFnm;
    System.out.println("Načítám konfigurační soubor:" + tourFile);
    try {
      BufferedReader br = new BufferedReader( new FileReader(tourFile));
      String line;
      while((line = br.readLine()) != null) {
        System.out.println(line);
        if (line.startsWith("-o")) 
          obs.store( line.substring(2).trim() );    // ulož informace o překážce
        else {
          propMan = new PropManager(line.trim(),true);  // načti scenérii
          sceneBG.addChild( propMan.getTG() );    // přidej svět
        }
      }
      br.close();
      // System.out.println("Načítání konfiguračního souboru dokončeno: " + tourFile);
      // obs.print();  // pro ladění
      sceneBG.addChild( obs.getObsGroup() );  // přidej překážky do scény
    } 
    catch (IOException e) 
    { System.out.println("Chyba při čtení konfiguračního souboru: " + tourFile);
      System.exit(1);
    }
  } // konec metody: makeScenery()



  // --------------------- turista ---------------------------


  private void addTourist()
  /* Třída TourSprite reprezentuje robota. Současně se spritem se
     vytváří také objekt ovládající jeho chování, TouristControls, 
     který ovlivňuje výhled, takže mu předáváme uzel typu TG.
  */
  {
    bob = new TourSprite("Coolrobo.3ds", obs);   // sprite
    bob.setPosition(2.0, 1.0);  
    sceneBG.addChild( bob.getTG() );

    ViewingPlatform vp = su.getViewingPlatform();
    TransformGroup viewerTG = vp.getViewPlatformTransform();   // výhled uzlu typu TG

    TouristControls tcs = new TouristControls(bob, viewerTG);   // ovládání pro sprite
    tcs.setSchedulingBounds( bounds );
    sceneBG.addChild( tcs );
  } // konec metody: addTourist()


  // -------------------------- vetřelec ---------------------------

  private void addAlien()
  /*  Vetřelce je reprezentován modelem ruky. Jeho pozice je pravidelně
      aktualizována voláními z objektu typu TimeBehavior. Chování vetřelce
      je zaměřeno na chycení turisty.
  */
  { AlienSprite al = 
       new AlienSprite("hand1.obj", obs, bob);   // vetřelec
    al.setPosition(-6.0, -6.0);
    sceneBG.addChild( al.getTG() );

    TimeBehavior alienTimer = new TimeBehavior(500, al);  // ovládání vetřelce
    alienTimer.setSchedulingBounds( bounds );
    sceneBG.addChild( alienTimer );
  } // konec metody: addAlien()


} // konec třídy: WrapTour3D