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

/* Třída AnimSprite3D načítá pomocí třídy PropManager předem určenou 
   sadu 3ds-modelů uvedených v poli poses[]. Všechny modely jsou 
   reprezentovány stejným spritem, ovšem v různých postojích.

   Postoje jsou variantami postavičky "Stick Child" z 3D-aplikace
   Poses 3 společnosti MetaCreations.

   Jsou umístěny v přepínači pod uzlem objectTG typu TransformGroup.

   Sprite se pohybuje tak, že aplikujeme transformace na jeho uzel
   obejctTG. Pomocí přepínače pak zobrazujeme různé postoje. Pohyb
   je omezen na rovinu XZ a rotace kolem osy y.
 
   Třída AnimSprite3D je úzce spojena se třídou Sprite3D v adresáři /Tour3D.
   
   Hlavní změny:
     * žádné překážky, sprite však kontroluje, je-li na podlaze.

*/

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

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


public class AnimSprite3D 
{
  private final static double FLOOR_LEN = 20.0;

  // 3ds-obrázky, s jejichž pomocí se animují příkazy spritu
  // nacházejí se v adresáři /models/xxx.3ds
  private final static String poses[] =
         {"stand", "walk1", "walk2", "rev1", "rev2",
          "rotClock", "rotCC", "mleft", "mright",
          "punch1", "punch2"};
  private final static int STAND_NUM = 0;   // index postoje "stand"


  private TransformGroup objectTG;          // uzel typu TG pod kterým načtený model visí
  private Transform3D t3d, toMove, toRot;   // pro manipulaci s tranformací uzlu objectTG

  private Switch imSwitch;            // pro zobrazení různých postojů
  private BitSet visIms;              // pro přepínání
  private int currPoseNo, maxPoses;   // čílo postoje

  private boolean isActive;   // je sprite aktivní?

  private DecimalFormat df;   // pro jednodušší výstup při ladění



  public AnimSprite3D()
  { 
    df = new DecimalFormat("0.###");  // 3 dm

    loadmoses();

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

    t3d = new Transform3D();
    toMove = new Transform3D();
    toRot = new Transform3D();
    isActive = true;
  }  // konec konstruktoru: AnimSprite3D()

 
  private void loadmoses()
  // načti obrázky odkazované v poli poses[]
  {
    PropManager propMan;

    imSwitch = new Switch(Switch.CHILD_MASK);
    imSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);

    maxPoses = poses.length;
    for (int i=0; i < maxPoses; i++) {
      propMan = new PropManager(poses[i] + ".3ds", true);  
      imSwitch.addChild( propMan.getTG() );     // přidej objekt k přepínači
    }

    visIms = new BitSet( maxPoses );    // pro přepínání používáme bitové pole
    currPoseNo = STAND_NUM;  // sprite stojí nehybně
    setPoseNum( currPoseNo );
  }  // konec metody: loadmoses()


  public TransformGroup getTG()
  // zpřístupní nejvyšší uzel typu TG spritu
  {  return objectTG; } 


  public void setPosition(double xPos, double zPos)
  // umístí sprite na souřadnice (xPos, zPos)
  { Point3d currLoc = getCurrLoc();
    double xMove = xPos - currLoc.x;
    double zMove = zPos - currLoc.z;
    moveBy(xMove, zMove);
  } // konec metody: setPosition()


  public boolean moveBy(double x, double z)
  // přesune sprite o (x,z)
  {
    if (isActive()) {
      Point3d nextLoc = tryMove( new Vector3d(x, 0, z));
      if (beyondEdge(nextLoc.x) || beyondEdge(nextLoc.z))
        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 boolean beyondEdge(double pos)
  // je pos mimo podlahu?
  { if ((pos < -FLOOR_LEN/2) || (pos > FLOOR_LEN/2))
      return true;
    return false;
  } // konec metody: beyondEdge()



  private void doMove(Vector3d 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čítej efekt daného posunutí, pozici objektu
     však neaktualizuj do doby, než bude otestována.
  */
  { 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)
  {
    if (isActive()) {
      objectTG.getTransform( t3d );
      toRot.rotY(radians);
      t3d.mul(toRot);
      objectTG.setTransform(t3d);
    }
  } // konec metody: doRotateY()


  public Point3d getCurrLoc()
  {        
    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()


  // --------------- operace přepínání postojů -----------------------


  public boolean setPose(String name)
  // reprezentuj sprite postojem 'name'
  {
    if (isActive()) {
      int idx = getPoseIndex(name);
      if ((idx < 0) || (idx > maxPoses-1))
        return false;
      setPoseNum( idx );
      return true;
    }
    else
      return false;
  } // konec metody: setPose()


  private int getPoseIndex(String name)
  // najde pozici řetězce name v poli poses[]
  {
    for(int i=0; i < maxPoses; i++)
      if (name.equals(poses[i]))
        return i;
    return -1;
  } // konec metody: getPoseIndex()


  private void setPoseNum(int idx)
  // nastaví postoj na pozici idx
  {
    visIms.clear();
    visIms.set( idx );       // zapni nový postoj
	imSwitch.setChildMask( visIms );
    currPoseNo = idx;
  }  // konec metody: setPoseNum()



  public boolean isActive() 
  {  return isActive;  }

  public void setActive(boolean b) 
  { isActive = b;
    if (!isActive) {
      visIms.clear();
	  imSwitch.setChildMask( visIms );   // nic nezobrazuj
    }
    else if (isActive)
      setPoseNum( currPoseNo );   // zviditelni
  } // konec metody: setActive()



  // ----------------------- ladící metody ------------------


  protected void printTuple(Tuple3d t, String id)
  // používá se 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: AnimSprite3D
