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

/* Třída Sprite3D načte 3D-obrázek a umístí jej do pozice (0,0,0).
   Předpokládáme, že skutečná pozice objektu je (0,0) v rovině XZ.
   Y-ová pozice se může lišit, ale základna objektu se s největší
   pravděpodobností bude dotýkat roviny XZ.

   Pohyby jsou omezeny na rovinu XZ a rotace kolem osy y.

   Objekt se nemůže pohybovat mimo podlahu nebo cestovat skrze překážky
   (definované pomocí objektu typu Obstacles).
*/

import java.text.DecimalFormat;
import java.util.*;
import java.io.*;

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


public class Sprite3D 
{
  private final static double OBS_FACTOR = 0.5;  
             // pro snížení poloměru ohraničující koule kolem spritu
             // při testování průsečíku s překážkami

  private TransformGroup objectTG;    // uzel typu TG, ze kterého načtený objekt visí
  private Transform3D t3d, toMove, toRot;    // pro manipulaci s transformacemi objectTG
  private Switch visSwitch;           // pro zviditelnění/zneviditelnění objektu

  private double radius;
  private boolean isActive;   // je sprite aktivní?

  private Obstacles obs;      // překážky na cestě
  private DecimalFormat df;   // pro jednodušší ladící výstup


  public Sprite3D(String fnm, Obstacles obs)
  { 
    df = new DecimalFormat("0.###");  // 3 dm
    this.obs = obs;

    // načti objekt a souřadnice
    PropManager propMan = new PropManager(fnm, true);
    radius = propMan.getScale();      // předpokládáme radius == scale
    // System.out.println("radius: " + df.format(radius));

    // vytvoř přepínač pro nastavování viditelnosti
    visSwitch = new Switch();
    visSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
    visSwitch.addChild( propMan.getTG() );     // add object to switch
    visSwitch.setWhichChild( Switch.CHILD_ALL );   // zviditelni

    // vytvoř novou transformační skupinu pro objekt
    objectTG = new TransformGroup();
    objectTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
    objectTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    objectTG.addChild( visSwitch );

    t3d = new Transform3D();
    toMove = new Transform3D();
    toRot = new Transform3D();
    isActive = true;
  }  // konec metody: Sprite3D()


  public TransformGroup getTG()
  {  return objectTG; }


  public void setPosition(double xPos, double zPos)
  // přesune sprite do bodu (xPos, zPos)
  { 
    Point3d currLoc = getCurrLoc();
    double xMove = xPos - currLoc.x;   // získej posun
    double zMove = zPos - currLoc.z;
    moveBy(xMove, zMove);
  } // konec metody: setPosition()


  public boolean moveBy(double x, double z)
  /* posunue sprite o offset x a z, ale jen tehdy, pokud je uvnitř
     podlahy a v blízkosti nejsou překážky. */
  {
    if (isActive()) {
      Point3d nextLoc = tryMove(new Vector3d(x, 0, z));
      if (obs.nearObstacle(nextLoc, radius*OBS_FACTOR))
        return false;
      else {
        doMove( new Vector3d(x, 0, z) );   // neefektivní přepočítávání
        return true;
      }
    }
    else    // neaktivní
      return false;
  }  // konec metody: moveBy()


  private void doMove(Vector3d theMove)
  // přesuň sprite dle vektoru theMove
  {
    objectTG.getTransform( t3d );
    toMove.setTranslation(theMove);    // otoč sprite o daný úhel v radiánech kolem jeho osy y
    t3d.mul(toMove);
    objectTG.setTransform(t3d);
  }  // konec metody: doMove()


  private Point3d tryMove(Vector3d theMove)
  /* Vypočítá efekt daného posunu, ale pozici spritu aktualizuje až po jeho otestování */
  {     objectTG.getTransform( t3d );
    toMove.setTranslation(theMove);
    t3d.mul(toMove);
    Vector3d trans = new Vector3d();
    t3d.get( trans );
    // printTuple(trans, "nextLoc");
    return new Point3d( trans.x, trans.y, trans.z);
  }  // konec metody: tryMove()


  public void doRotateY(double radians)
  // Otočí sprite o radians radiánů kolem jeho osy y
  {
    objectTG.getTransform( t3d );
    toRot.rotY(radians);   // přepiš předchozí rotaci
    t3d.mul(toRot);
    objectTG.setTransform(t3d);
  } // konec metody: doRotateY()


  public Point3d getCurrLoc()
  // Vrátí aktuální pozici spritu
  {        
    objectTG.getTransform( t3d );
    Vector3d trans = new Vector3d();
    t3d.get( trans );
    // printTuple(trans, "currLoc");
    return new Point3d( trans.x, trans.y, trans.z);
  } // konec metody: getCurrLoc()


  public boolean isActive() 
  {  return isActive;  }

  public void setActive(boolean b)
  // Změny aktivity mají gvliv na viditelnost spritu
  { isActive = b;
    if (!isActive)
       visSwitch.setWhichChild( Switch.CHILD_NONE );   // zneviditelni
    else if (isActive)
      visSwitch.setWhichChild( Switch.CHILD_ALL );   // zviditelni
  } // konec metody: setActive()


  protected void printTuple(Tuple3d t, String id)
  // pro ladění
  {
    System.out.println(id + " x: " + df.format(t.x) + 
				", " + id + " y: " + df.format(t.y) +
				", " + id + " z: " + df.format(t.z));
  }  // konec metody: printTuple()


}  // konec třídy: Sprite3D
