//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "game/showTSShape.h"
#include "gui/core/guiControl.h"
#include "gui/core/guiTSControl.h"
#include "gui/controls/guiTextCtrl.h"
#include "gui/controls/guiTextListCtrl.h"
#include "gui/controls/guiSliderCtrl.h"
#include "gui/controls/guiTextEditCtrl.h"
#include "gui/core/guiCanvas.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sceneState.h"
#include "console/consoleTypes.h"

#include "terrain/terrData.h"
#include "terrain/terrRender.h"

ShowTSShape * ShowTSShape::currentShow = NULL;

Vector<char *> ShowTSShape::loadQueue(__FILE__, __LINE__);

const char * emapName[] = { "emap.bmp" };

MaterialList emapList(1,emapName);
F32 emapAlpha = 0.0f;
F32 ambR = 0.7f;
F32 ambB = 0.7f;
F32 ambG = 0.7f;
F32 diffR = 0.9f;
F32 diffB = 0.9f;
F32 diffG = 0.9f;
Point4F lightDir(0.57f,0.57f,-0.57f,0.0f);
bool gInitLightingSliders = false;

//------------------------------------------------------------
// simple camera methods and variables
//------------------------------------------------------------

Point3F camPos;
MatrixF cameraMatrix;
EulerF camRot;

F32 gShowForwardAction = 0.0f;
F32 gShowBackwardAction = 0.0f;
F32 gShowUpAction = 0.0f;
F32 gShowDownAction = 0.0f;
F32 gShowLeftAction = 0.0f;
F32 gShowRightAction = 0.0f;
F32 gShowMovementSpeed = 1.0f;

float initialShowDistance = 20.0f;
Point3F * orbitPos = NULL;
float orbitDist = 10.0f;
float minOrbitDist = 1.0f;
float maxOrbitASin = 0.2f;
bool keyboardControlsShape = false;

float gShowShapeLeftSpeed = 0.0f;
float gShowShapeRightSpeed = 0.0f;

void setOrbit(Point3F * orb, bool useCurrentDist)
{
   orbitPos = orb;

   if (!useCurrentDist)
   {
      Point3F vec;
      cameraMatrix.getColumn(1, &vec);
      orbitDist = mDot(vec,*orbitPos-camPos);
   }
   if (orbitDist<minOrbitDist)
       orbitDist = minOrbitDist;
}

void setOrbit(Point3F * orb, float newOrbitDist)
{
   orbitPos = orb;

   orbitDist = newOrbitDist;
   if (orbitDist<minOrbitDist)
       orbitDist = minOrbitDist;
}

void setDetailSlider()
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;

   GuiSliderCtrl * slider = static_cast<GuiSliderCtrl*>(Sim::findObject("showDetailSlider"));
   if (!slider)
      return;

   // find current detail level and number of detail levels of currentShow
   S32 dl = currentShow->getCurrentDetail();
   S32 numDL = currentShow->getNumDetails();

   // set slider range to be correct
   slider->setField("range",avar("0 %g",-0.01f + (F32)numDL));
   // set slider ticks to be correct
   slider->setField("ticks",avar("%i",numDL));
   // set slider value to be correct
   slider->setField("value",avar("%i",dl));
}

//------------------------------------------------------------
// show gui related methods
//------------------------------------------------------------

void showUpdateThreadControl()
{
   char buffer[32];

   // update dialogs
   GuiTextListCtrl * threadList = dynamic_cast<GuiTextListCtrl*>(Sim::findObject("threadList"));
   GuiTextListCtrl * sequenceList = dynamic_cast<GuiTextListCtrl*>(Sim::findObject("sequenceList"));
   if (!threadList || !sequenceList)
   {
      // just for debugging, look up w/o dynamic cast...
      threadList = threadList ? NULL : static_cast<GuiTextListCtrl*>(Sim::findObject("threadList"));
      sequenceList = sequenceList ? NULL : static_cast<GuiTextListCtrl*>(Sim::findObject("sequenceList"));
      AssertFatal(!threadList && !sequenceList,"showUpdateThreadControl: threadList or sequenceList of wrong gui type");
      // something wrong, just exit
      return;
   }

   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
   {
      // no current show...zero out lists
      threadList->clear();
      sequenceList->clear();
      return;
   }

   // the thread list:

   threadList->setCellSize(Point2I(threadList->mBounds.extent.x,16));
   U32 count = currentShow->getThreadCount();

   if(count != threadList->getNumEntries())
   {
      threadList->clear();
      for(U32 i = 0; i < count; i++)
      {
         dSprintf(buffer, sizeof(buffer), "%i", i);
         threadList->addEntry(i, buffer);
      }
   }
   S32 selectedThread = threadList->getSelectedCell().y;
   if (threadList->getNumEntries()==0 && selectedThread>=0)
      // no threads, select nothing...
      threadList->deselectCells();
   else if (threadList->getNumEntries()>0 && selectedThread<0)
      // we have threads but nothing selected...
      threadList->setSelectedCell(Point2I(0,0));
   selectedThread = threadList->getSelectedCell().y;

   // the sequence list:

   sequenceList->setCellSize(Point2I(sequenceList->mBounds.extent.x,16));
   // only change the text if it needs changing ... this avoids infinite loops since
   // changing text can result in this routine getting called again
   S32 i;
   for (i=0; i<currentShow->getSequenceCount() && i<sequenceList->getNumEntries(); i++)
      if (dStrcmp(currentShow->getSequenceName(i),sequenceList->mList[i].text))
         break;
   while (i<sequenceList->getNumEntries())
      sequenceList->removeEntryByIndex(i);
   for (; i<currentShow->getSequenceCount(); i++)
      sequenceList->addEntry(i, currentShow->getSequenceName(i));
   if (selectedThread==-1)
   {
      if (sequenceList->getSelectedCell().y >= 0)
         sequenceList->deselectCells();
   }
   else if (sequenceList->getSelectedCell().y != currentShow->getSequenceNum(selectedThread))
      sequenceList->setSelectedCell(Point2I(0,currentShow->getSequenceNum(selectedThread)));

   // update displayed scale value
   GuiTextCtrl * text = dynamic_cast<GuiTextCtrl*>(Sim::findObject("showScaleValue"));
   if (text && selectedThread>=0)
   {
      dSprintf(buffer,32,"scale = %2.2f",currentShow->getThreadScale(selectedThread));
      text->setText(buffer);
   }
}


//------------------------------------------------------------
// basic class for displaying a tsshape
//------------------------------------------------------------

ShowTSShape::ShowTSShape(const char * shapeName)
{
   mTypeMask = StaticObjectType;

   shapeInstance = NULL;
   dStrcpy(shapeNameBuffer,shapeName);
   timeScale = 1.0f;
   addGround = false;
   stayGrounded = true;
   char fileBuffer[512];
   dStrcpy(fileBuffer,shapeName);

   // before loading shape, execute associated script
   char *ext = dStrstr( static_cast<char*>( fileBuffer ), const_cast<char*>( ".dts" ) );
   if (ext)
   {
      ext[1]='c';
      ext[2]='s';
      ext[3]='\0';
      Con::executef(2,"exec",fileBuffer);
      ext[1]='d';
      ext[2]='t';
      ext[3]='s';
   }

   Resource<TSShape> hShape;
   hShape = ResourceManager->load(fileBuffer);
   if (!bool(hShape))
      return;

   shapeInstance = new TSShapeInstance(hShape, true);
   emapList.load(MeshTexture,0,true);
   shapeInstance->setEnvironmentMap(emapList.getMaterial(0));

   newThread(); // does nothing if shape has no sequences

//   // TODO: fix this in exporter at some point...
   Point3F & center = const_cast<Point3F&>(shapeInstance->getShape()->center);
   center = shapeInstance->getShape()->bounds.min + shapeInstance->getShape()->bounds.max;
   center *= 0.5f;

   mat.identity();
   setPosition(camPos);
}

//--------------------------------------------------------------------------
bool ShowTSShape::prepRenderImage(SceneState* state, const U32 stateKey,
                                       const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
   if (isLastState(state, stateKey))
      return false;
   setLastState(state, stateKey);

   SceneRenderImage* image = new SceneRenderImage;
   image->obj = this;
   image->isTranslucent = true;
   image->sortType = SceneRenderImage::EndSort;
   state->insertRenderImage(image);

   return false;
}

void ShowTSShape::renderObject(SceneState* state, SceneRenderImage*)
{
   AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");

   RectI viewport;
   glMatrixMode(GL_PROJECTION);
   glPushMatrix();
   dglGetViewport(&viewport);

   state->setupObjectProjection(this);

   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
   dglMultMatrix(&mObjToWorld);
   glScalef(mObjScale.x, mObjScale.y, mObjScale.z);

   render();

   glDisable(GL_TEXTURE_2D);
   glDisable(GL_BLEND);
   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

   glMatrixMode(GL_MODELVIEW);
   glPopMatrix();

   glMatrixMode(GL_PROJECTION);
   glPopMatrix();
   glMatrixMode(GL_MODELVIEW);
   dglSetViewport(viewport);

   AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
}



//--------------------------------------
bool ShowTSShape::onAdd()
{
   mWorldToObj = mObjToWorld;
   mWorldToObj.affineInverse();
   resetWorldBox();

   if (!Parent::onAdd() || !shapeInstance)
      // shape instantiation failed
      return false;

   // make us the center of attention
   orbitUs();

   // We're always a client object...
   gClientContainer.addObject(this);
   gClientSceneGraph->addObjectToScene(this);

   mObjBox = shapeInstance->getShape()->bounds;
   resetWorldBox();

   SimSet * showSet = static_cast<SimSet*>(Sim::findObject("showSet"));
   if (!showSet)
   {
      showSet = new SimSet;
      showSet->registerObject("showSet");
      Sim::getRootGroup()->addObject(showSet);
   }
   showSet->addObject(this);

   currentShow = this;

   return true;
}

void ShowTSShape::onRemove()
{
   gClientSceneGraph->removeObjectFromScene(this);
   gClientContainer.removeObject(this);

   if (this == currentShow)
   {
      currentShow = NULL;
      showUpdateThreadControl();
   }
   Parent::onRemove();
}

ShowTSShape::~ShowTSShape()
{
   delete shapeInstance;
   if (orbitPos == &centerPos)
      orbitPos = NULL;
}

void ShowTSShape::newThread()
{
   if (shapeInstance->getShape()->sequences.empty())
      return;

   threads.increment();
   threads.last() = shapeInstance->addThread();
   play.push_back(true);
   scale.push_back(1.0f);
}

void ShowTSShape::deleteThread(S32 th)
{
   if (th<0 || th>=threads.size())
      return;

   shapeInstance->destroyThread(threads[th]);
   threads.erase(th);
   play.erase(th);
   scale.erase(th);
}

void ShowTSShape::setPosition(Point3F p)
{
   if (stayGrounded && gClientSceneGraph->getCurrentTerrain())
      // won't actually change p.z if on empty square
      gClientSceneGraph->getCurrentTerrain()->getHeight(Point2F(p.x,p.y),&p.z);

   centerPos = p + shapeInstance->getShape()->center;
   mat.setColumn(3,p);
   setTransform(mat);
}

void ShowTSShape::addGroundTransform(const MatrixF & ground)
{
   // add some ground transform to our current transform
   MatrixF m;
   m.mul(mat,ground);
   mat = m;
   Point3F p;
   mat.getColumn(3,&p);
   setPosition(p);
}

void ShowTSShape::orbitUs()
{
   // make us the center of attention
   minOrbitDist = shapeInstance->getShape()->radius;
   setOrbit(&centerPos,false);
}

bool ShowTSShape::handleMovement(F32 leftSpeed, F32 rightSpeed,
                                 F32 forwardSpeed, F32 backwardSpeed,
                                 F32 upSpeed, F32 downSpeed,
                                 F32 delta)
{
   if (!currentShow)
      return false;

   Point3F vec,p;

   // turn shape left/right
   F32 turnLR = (gShowShapeRightSpeed-gShowShapeLeftSpeed) * delta * 0.1f;
   MatrixF turn(EulerF(0,0,turnLR));
   currentShow->addGroundTransform(turn);

   if (keyboardControlsShape && currentShow)
   {
      currentShow->getFeetPosition(&p);

      // the following moves shape with camera orthoganol to world
      // depends on camera not having roll

      cameraMatrix.getColumn(0, &vec);
      vec.z = 0.0f;
      vec.normalize();
      p += vec * (rightSpeed - leftSpeed) * delta;
      if (!orbitPos)
         // move camera with shape
         camPos += vec * (rightSpeed - leftSpeed) * delta;

      cameraMatrix.getColumn(1, &vec);
      vec.z = 0.0f;
      vec.normalize();
      p += vec * (forwardSpeed - backwardSpeed) * delta;
      if (!orbitPos)
         // move camera with shape
         camPos += vec * (forwardSpeed - backwardSpeed) * delta;

      vec.set(0,0,1.0f);
      p += vec * (upSpeed - downSpeed) * delta;
      if (!orbitPos)
         // move camera with shape
         camPos += vec * (upSpeed - downSpeed) * delta;

      currentShow->setPosition(p);

      // if orbit cam is on, zero out speeds and adjust like normal
      // if not, let normal camera move us with these speeds...
      if (orbitPos)
         leftSpeed = rightSpeed = forwardSpeed = backwardSpeed = upSpeed = downSpeed = 0.0f;
   }

   if (!orbitPos)
      return false;

   // forward/backward = closer/farther

   orbitDist -= (forwardSpeed - backwardSpeed) * delta;
   if (orbitDist<minOrbitDist)
      orbitDist = minOrbitDist;
   F32 invD = orbitDist>0.001f ? 1.0f / orbitDist : 0.0f;

   // up/down & left/right = rotate

   F32 xr = (upSpeed-downSpeed) * invD * delta;    // convert linear speed to angular
   F32 zr = (leftSpeed-rightSpeed) * invD * delta; // convert linear speed to angular

   // cap rotation rate (for when you're real close to orbit point)
   if (xr > maxOrbitASin)
      xr = maxOrbitASin;
   else if (xr < -maxOrbitASin)
      xr = -maxOrbitASin;
   if (zr > maxOrbitASin)
      zr = maxOrbitASin;
   else if (zr < -maxOrbitASin)
      zr = -maxOrbitASin;

   camRot.x += mAsin(xr);
   camRot.z += mAsin(zr);

   MatrixF xRot, zRot, newCameraRot;
   xRot.set(EulerF(camRot.x, 0, 0));
   zRot.set(EulerF(0, 0, camRot.z));
   newCameraRot.mul(zRot,xRot);

   // adjust cameraPos so we still face orbitPos (and are still d units away)
   newCameraRot.getColumn(1,&vec);
   vec *= orbitDist;
   camPos = *orbitPos - vec;

   return true;
}

void ShowTSShape::render()
{
   bool wasLit  = glIsEnabled(GL_LIGHTING);
   bool wasLit0 = glIsEnabled(GL_LIGHT0);

   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);

   glLightfv(GL_LIGHT0,GL_POSITION,(float*)&lightDir);

   GLfloat amb[]  = {ambR,ambG,ambB,0.0f};
   GLfloat diff[] = {diffR,diffG,diffB,1.0f};
   GLfloat matProp[] = {1.0f,1.0f,1.0f,1.0f};
   GLfloat zeroColor[] = {0,0,0,0};

   glLightModelfv(GL_LIGHT_MODEL_AMBIENT,amb);
   glLightfv(GL_LIGHT0,GL_DIFFUSE,diff);
   glLightfv(GL_LIGHT0,GL_AMBIENT,zeroColor);
   glLightfv(GL_LIGHT0,GL_SPECULAR,zeroColor);

   glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,matProp);
   glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,zeroColor);
   glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,zeroColor);

//   glPushMatrix();
//   dglMultMatrix(&mat);

   shapeInstance->setEnvironmentMapOn(true,emapAlpha);

   if (Con::getBoolVariable("$showAutoDetail",false))
   {
      shapeInstance->selectCurrentDetail();
      GuiSliderCtrl * slider = static_cast<GuiSliderCtrl*>(Sim::findObject("showDetailSlider"));
      // set slider value to be correct
      if (slider && slider->mAwake)
      {
         char buffer[32];
         dSprintf(buffer,32,"%g",(F32)shapeInstance->getCurrentDetail()+1.0f-shapeInstance->getCurrentIntraDetail());
         slider->setScriptValue(buffer);
      }
   }
   else
   {
      GuiSliderCtrl * slider = static_cast<GuiSliderCtrl*>(Sim::findObject("showDetailSlider"));
      // set current detail level based on slider
      if (slider)
         shapeInstance->setCurrentDetail((S32)slider->getValue(),1.0f-(F32)((F32)slider->getValue()-(S32)slider->getValue()));
   }
   GuiTextCtrl * detailText1 = static_cast<GuiTextCtrl*>(Sim::findObject("showDetailInfoText1"));
   GuiTextCtrl * detailText2 = static_cast<GuiTextCtrl*>(Sim::findObject("showDetailInfoText2"));
   if (detailText1 && detailText2)
   {
      char buffer[128];
      S32 dl = shapeInstance->getCurrentDetail();
      S32 dlSize = (S32) (dl>=0 ? shapeInstance->getShape()->details[dl].size : 0);
      S32 polys = dl>=0 ? shapeInstance->getShape()->details[dl].polyCount : 0;
      Point3F p;
      mObjToWorld.getColumn(3,&p);
      p -= camPos;
      F32 dist = p.len();
      F32 pixelSize = dglProjectRadius(dist,shapeInstance->getShape()->radius) * dglGetPixelScale() * TSShapeInstance::smDetailAdjust;
      dSprintf(buffer,128,"detail level: %i, detail size: %i",dl,dlSize);
      detailText1->setText(buffer);
      dSprintf(buffer,128,"size: %g, polys: %i, dist: %g",pixelSize,polys,dist);
      detailText2->setText(buffer);
   }

   shapeInstance->animate();

   shapeInstance->render();

//   glPopMatrix();

   if (!wasLit)
      glDisable(GL_LIGHTING);
   if (!wasLit0)
      glDisable(GL_LIGHT0);
}

void ShowTSShape::reset()
{
   SimSet * set = static_cast<SimSet*>(Sim::findObject("showSet"));
   if (!set)
      return;

   for (SimSet::iterator itr = set->begin(); itr!=set->end(); itr++)
      static_cast<ShowTSShape*>(*itr)->resetInstance();
}

void ShowTSShape::resetInstance()
{
   // do nothing for now...
   // eventually, may need to delete shape and then reload it, but seems to work fine now
}

void ShowTSShape::advanceTime(U32 delta)
{
   // get the show set...
   SimSet * set = static_cast<SimSet*>(Sim::findObject("showSet"));

   // update the instances...
   if (set)
      for (SimSet::iterator itr = set->begin(); itr!=set->end(); itr++)
         static_cast<ShowTSShape*>(*itr)->advanceTimeInstance(delta);

   // set thread position slider
   GuiSliderCtrl * slider = static_cast<GuiSliderCtrl*>(Sim::findObject("threadPosition"));
   GuiTextListCtrl * threadList = static_cast<GuiTextListCtrl*>(Sim::findObject("threadList"));
   GuiTextCtrl * transitionSignal = static_cast<GuiTextCtrl*>(Sim::findObject("transitionSignal"));
   bool inTransition = false;
   if (currentShow && threadList && slider && slider->mAwake && threadList->getSelectedCell().y>=0)
   {
      S32 th = threadList->getSelectedCell().y;
      if (currentShow->getPlay(th))
      {
         char buffer[32];
         dSprintf(buffer,32,"%g",currentShow->getPos(th));
         slider->setScriptValue(buffer);
      }
      else
         currentShow->setPos(th,slider->getValue());

      inTransition = currentShow->isInTransition(th);
   }

   if (transitionSignal)
   {
      if (inTransition)
         transitionSignal->setText("T");
      else
         transitionSignal->setText(" ");
   }

   if (!gInitLightingSliders)
   {
      char buffer[32];
      slider = static_cast<GuiSliderCtrl*>(Sim::findObject("emapAlpha"));
      if (slider)
      {
         dSprintf(buffer,32,"%g",emapAlpha);
         slider->setField("value",buffer);
      }
      slider = static_cast<GuiSliderCtrl*>(Sim::findObject("ambR"));
      if (slider)
      {
         dSprintf(buffer,32,"%g",ambR);
         slider->setField("value",buffer);
      }
      slider = static_cast<GuiSliderCtrl*>(Sim::findObject("ambG"));
      if (slider)
      {
         dSprintf(buffer,32,"%g",ambG);
         slider->setField("value",buffer);
      }
      slider = static_cast<GuiSliderCtrl*>(Sim::findObject("ambB"));
      if (slider)
      {
         dSprintf(buffer,32,"%g",ambB);
         slider->setField("value",buffer);
      }
      slider = static_cast<GuiSliderCtrl*>(Sim::findObject("diffR"));
      if (slider)
      {
         dSprintf(buffer,32,"%g",diffR);
         slider->setField("value",buffer);
      }
      slider = static_cast<GuiSliderCtrl*>(Sim::findObject("diffG"));
      if (slider)
      {
         dSprintf(buffer,32,"%g",diffG);
         slider->setField("value",buffer);
      }
      slider = static_cast<GuiSliderCtrl*>(Sim::findObject("diffB"));
      if (slider)
      {
         dSprintf(buffer,32,"%g",diffB);
         slider->setField("value",buffer);
      }
      gInitLightingSliders = true;
   }

   // handle lighting sliders...
   slider = static_cast<GuiSliderCtrl*>(Sim::findObject("emapAlpha"));
   if (slider)
      emapAlpha = slider->getValue();
   slider = static_cast<GuiSliderCtrl*>(Sim::findObject("ambR"));
   if (slider)
      ambR = slider->getValue();
   slider = static_cast<GuiSliderCtrl*>(Sim::findObject("ambG"));
   if (slider)
      ambG = slider->getValue();
   slider = static_cast<GuiSliderCtrl*>(Sim::findObject("ambB"));
   if (slider)
      ambB = slider->getValue();
   slider = static_cast<GuiSliderCtrl*>(Sim::findObject("diffR"));
   if (slider)
      diffR = slider->getValue();
   slider = static_cast<GuiSliderCtrl*>(Sim::findObject("diffG"));
   if (slider)
      diffG = slider->getValue();
   slider = static_cast<GuiSliderCtrl*>(Sim::findObject("diffB"));
   if (slider)
      diffB = slider->getValue();


   // handle movement...a little weird because spliced from early version:

   F32 leftSpeed = gShowLeftAction;
   F32 rightSpeed = gShowRightAction;
   F32 forwardSpeed = gShowForwardAction;
   F32 backwardSpeed = gShowBackwardAction;
   F32 upSpeed = gShowUpAction;
   F32 downSpeed = gShowDownAction;

   F32 timeScale = gShowMovementSpeed * 25.0f * ((F32)delta) / F32(1000);
   if (!ShowTSShape::handleMovement(leftSpeed,rightSpeed,forwardSpeed,backwardSpeed,upSpeed,downSpeed,timeScale))
   {
      Point3F vec;
      cameraMatrix.getColumn(0, &vec);
      camPos += vec * (rightSpeed - leftSpeed) * timeScale;
      cameraMatrix.getColumn(1, &vec);
      camPos += vec * (forwardSpeed - backwardSpeed) * timeScale;
      cameraMatrix.getColumn(2, &vec);
      camPos += vec * (upSpeed - downSpeed) * timeScale;
   }
}

void ShowTSShape::advanceTimeInstance(U32 delta)
{
   float dt = timeScale * 0.001f * (float)delta;

   for (S32 i=0; i<threads.size(); i++)
      if (play[i])
         shapeInstance->advanceTime(scale[i]*dt,threads[i]);

   if (addGround)
   {
      shapeInstance->animateGround();
      addGroundTransform(shapeInstance->getGroundTransform());
   }

   shapeInstance->animate();
}

//------------------------------------------------------------
// TS Control for show objects
//------------------------------------------------------------

bool ShowProcessCameraQuery(CameraQuery *q)
{
   MatrixF xRot, zRot;
   xRot.set(EulerF(camRot.x, 0, 0));
   zRot.set(EulerF(0, 0, camRot.z));

   cameraMatrix.mul(zRot, xRot);
   q->nearPlane = 0.1;
   q->farPlane = 2100.0;
   q->fov = 3.1415 / 2;
   cameraMatrix.setColumn(3, camPos);
   q->cameraMatrix = cameraMatrix;
   return true;
}

void ShowRenderWorld()
{
   glClear(GL_DEPTH_BUFFER_BIT);
   glDisable(GL_CULL_FACE);
   glMatrixMode(GL_MODELVIEW);

   glEnable(GL_DEPTH_TEST);
   glDepthFunc(GL_LEQUAL);

   dglSetCanonicalState();
   gClientSceneGraph->renderScene();
   dglSetCanonicalState();

   glDisable(GL_DEPTH_TEST);
   AssertFatal(dglIsInCanonicalState(), "Not in canonical state on exit!");
}

class ShowTSCtrl : public GuiTSCtrl
{
   typedef GuiTSCtrl Parent;
public:
   bool processCameraQuery(CameraQuery *query);
   void renderWorld(const RectI &updateRect);
   void onRender(Point2I offset, const RectI &updateRect);
//   void onMouseMove(const GuiEvent &evt);

   DECLARE_CONOBJECT(ShowTSCtrl);
};

IMPLEMENT_CONOBJECT(ShowTSCtrl);

bool ShowTSCtrl::processCameraQuery(CameraQuery * q)
{
   return ShowProcessCameraQuery(q);
}

void ShowTSCtrl::onRender(Point2I offset, const RectI &updateRect)
{
   glColor3i(0, 0, 0);
   glDisable(GL_DEPTH_TEST);

   glBegin(GL_TRIANGLE_FAN);
   glVertex2i(updateRect.point.x, updateRect.point.y);
   glVertex2i(updateRect.point.x + updateRect.extent.x, updateRect.point.y);
   glVertex2i(updateRect.point.x + updateRect.extent.x, updateRect.point.y + updateRect.extent.y);
   glVertex2i(updateRect.point.x, updateRect.point.y + updateRect.extent.y);
   glEnd();

   Parent::onRender(offset, updateRect);
}

void ShowTSCtrl::renderWorld(const RectI &updateRect)
{
   ShowRenderWorld();
   dglSetClipRect(updateRect);
}

//------------------------------------------------------------
// console functions for show plugin
//------------------------------------------------------------
ConsoleFunctionGroupBegin( ShowTool, "Functions for controlling the show tool.");

ConsoleFunction( showShapeLoad, void, 2, 3, "(string shapeName, bool faceCamera)")
{
   argc;

   Point3F vec,pos;
   cameraMatrix.getColumn(1,&vec);
   cameraMatrix.getColumn(3,&pos);
   vec *= initialShowDistance;

   ShowTSShape * show = new ShowTSShape(argv[1]);

   if (show->shapeLoaded())
   {
      show->setPosition(pos+vec);
      show->registerObject();
      Sim::getRootGroup()->addObject(show);
   }
   else
      delete show;

   showUpdateThreadControl();

   // make sure detail slider is set correctly
   setDetailSlider();
}

ConsoleFunction( showSequenceLoad, void, 2, 3, "(string sequenceFile, string sequenceName=NULL)")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;

   char buffer[512];

   if (argc==2)
      dSprintf(buffer,512,"new TSShapeConstructor() { baseShape=\"%s\";sequence0=\"%s\"; };",currentShow->getShapeName(),argv[1]);
   else
      dSprintf(buffer,512,"new TSShapeConstructor() { baseShape=\"%s\";sequence0=\"%s %s\"; };",currentShow->getShapeName(),argv[1],argv[2]);

   Con::evaluate(buffer);
   ShowTSShape::reset();
}

ConsoleFunction( showSelectSequence, void, 1, 1, "")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;

   GuiTextListCtrl * threadList = static_cast<GuiTextListCtrl*>(Sim::findObject("threadList"));
   if (!threadList)
      return;

   GuiTextListCtrl * sequenceList = static_cast<GuiTextListCtrl*>(Sim::findObject("sequenceList"));
   if (!sequenceList)
      return;

   S32 threadNum = threadList->getSelectedCell().y;
   S32 seq = sequenceList->getSelectedCell().y;

   if (threadNum>=0 && seq>=0)
   {
      if (Con::getBoolVariable("$showTransition",false) && !currentShow->isBlend(seq))
      {
         F32 pos;
         F32 dur = Con::getFloatVariable("$showTransitionDuration",0.2f);
         GuiSliderCtrl * tpos = static_cast<GuiSliderCtrl*>(Sim::findObject("transitionPosition"));
         if (Con::getBoolVariable("$showTransitionSynched",true) || !tpos)
            pos = currentShow->getPos(threadNum);
         else
            pos = tpos->getValue();
         bool targetPlay = Con::getBoolVariable("$showTransitionTargetPlay",true);
         currentShow->transitionToSequence(threadNum,seq,pos,dur,targetPlay);
      }
      else
         currentShow->setSequence(threadNum,seq);

      GuiSliderCtrl * slider = static_cast<GuiSliderCtrl*>(Sim::findObject("threadPosition"));
      if (slider && slider->getRoot())
      {
         char buffer[32];
         dSprintf(buffer,32,"%g",currentShow->getPos(threadNum));
         slider->setScriptValue(buffer);
      }
   }

   showUpdateThreadControl();
   return;
}

ConsoleFunction( showSetDetailSlider, void, 1, 1, "")
{
   setDetailSlider();
}

ConsoleFunction( showUpdateThreadControl, void, 1, 1, "")
{
   showUpdateThreadControl();
}

ConsoleFunction( showPlay, void, 1, 2, "(int threadNum = -1)")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;
   if (argc==1)
   {
      for (S32 i=0; i < currentShow->getThreadCount(); i++)
         currentShow->setPlay(i,1);
   }
   else
   {
      S32 i = dAtoi(argv[1]);
      if (i>=0)
         currentShow->setPlay(i,1);
   }
}

ConsoleFunction( showStop, void, 1, 2, "(int threadNum = -1)")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;
   if (argc==1)
   {
      for (S32 i=0; i < currentShow->getThreadCount(); i++)
         currentShow->setPlay(i,0);
   }
   else
   {
      S32 i = dAtoi(argv[1]);
      if (i>=0)
         currentShow->setPlay(i,0);
   }
}

ConsoleFunction (showSetScale, void, 3, 3, "(int threadNum, float scale)")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;

   S32 idx = dAtoi(argv[1]);
   float s = dAtof(argv[2]);
   if (idx>=0)
      currentShow->setThreadScale(idx,s);

   showUpdateThreadControl();
}

ConsoleFunction( showSetPos, void, 2, 2, "(int threadNum, float pos)")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;

   S32 idx = dAtoi(argv[1]);
   float s = dAtof(argv[2]);
   if (idx>=0)
      currentShow->setPos(idx,s);
}
ConsoleFunction( showNewThread, void, 1, 1, "")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;

   currentShow->newThread();

   showUpdateThreadControl();
}

ConsoleFunction( showDeleteThread, void, 2, 2, "(int threadNum)")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;

   S32 th = dAtoi(argv[1]);
   if (th>=0)
      currentShow->deleteThread(th);

   showUpdateThreadControl();
}


ConsoleFunction( showToggleRoot, void, 1, 1, "")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;

   currentShow->setAnimateRoot(!currentShow->getAnimateRoot());
}



ConsoleFunction( showToggleStick, void, 1, 1, "")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;
   if (!currentShow)
      return;

   currentShow->setStickToGround(!currentShow->getStickToGround());
}

ConsoleFunction( showSetCamera, void, 2, 2, "(char orbitShape) t or T to orbit, else free-fly.")
{
   ShowTSShape * currentShow = ShowTSShape::currentShow;

   if (argv[1][0]=='t' || argv[1][0]=='T' && currentShow)
      // orbit
      currentShow->orbitUs();
   else
      // no orbit -- camera moves freely
      orbitPos = NULL;
}

ConsoleFunction(showSetKeyboard, void, 2, 2, "(char moveShape) Set to t or T.")
{
   keyboardControlsShape = (argv[1][0]=='t' || argv[1][0]=='T');
}

ConsoleFunction(showTurnLeft, void, 2, 2, "(float amt)")
{
   gShowShapeLeftSpeed = dAtof(argv[1]);
}


ConsoleFunction( showTurnRight, void, 2, 2, "(float amt)")
{
   gShowShapeRightSpeed = dAtof(argv[1]);
}

ConsoleFunction( showSetLightDirection, void, 1, 1, "")
{
   cameraMatrix.getColumn(1,(Point3F*)&lightDir);
   lightDir.x *= -1.0f;
   lightDir.y *= -1.0f;
   lightDir.z *= -1.0f;
   lightDir.w *= -1.0f;
}

ConsoleFunctionGroupEnd( ShowTool );

void ShowInit()
{
   Con::addVariable("showForwardAction",  TypeF32, &gShowForwardAction);
   Con::addVariable("showBackwardAction", TypeF32, &gShowBackwardAction);
   Con::addVariable("showUpAction",       TypeF32, &gShowUpAction);
   Con::addVariable("showDownAction",     TypeF32, &gShowDownAction);
   Con::addVariable("showLeftAction",     TypeF32, &gShowLeftAction);
   Con::addVariable("showRightAction",    TypeF32, &gShowRightAction);

   Con::addVariable("showMovementSpeed",  TypeF32, &gShowMovementSpeed);

   Con::addVariable("showPitch",          TypeF32, &camRot.x);
   Con::addVariable("showYaw",            TypeF32, &camRot.z);

   cameraMatrix.identity();
   camRot.set(0,0,0);
   camPos.set(10,10,10);
   cameraMatrix.setColumn(3,camPos);
}
