//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "platform/platform.h"
#include "T3D/gameFunctions.h"
#include "T3D/gameBase/gameConnection.h"
#include "T3D/camera.h"
#include "T3D/sfx/sfx3DWorld.h"
#include "console/consoleTypes.h"
#include "gui/3d/guiTSControl.h"
#include "core/util/journal/process.h"
#include "materials/materialManager.h"
#include "math/mEase.h"
#include "core/module.h"
#include "console/engineAPI.h"
#include "platform/output/IDisplayDevice.h"
#include "postFx/postEffectManager.h"
#include "gfx/gfxTransformSaver.h"

static void RegisterGameFunctions();
static void Process3D();

MODULE_BEGIN( 3D )

   MODULE_INIT_AFTER( Process )
   MODULE_INIT_AFTER( Scene )
   
   MODULE_SHUTDOWN_BEFORE( Process )
   MODULE_SHUTDOWN_BEFORE( Sim )
   MODULE_SHUTDOWN_AFTER( Scene )
   
   MODULE_INIT
   {
      Process::notify(Process3D, PROCESS_TIME_ORDER);

      GameConnection::smFovUpdate.notify(GameSetCameraFov);

      RegisterGameFunctions();
   }
   
   MODULE_SHUTDOWN
   {
      GameConnection::smFovUpdate.remove(GameSetCameraFov);

      Process::remove(Process3D);
   }

MODULE_END;


static S32 gEaseInOut = Ease::InOut;
static S32 gEaseIn = Ease::In;
static S32 gEaseOut = Ease::Out;

static S32 gEaseLinear = Ease::Linear;
static S32 gEaseQuadratic= Ease::Quadratic;
static S32 gEaseCubic= Ease::Cubic;
static S32 gEaseQuartic = Ease::Quartic;
static S32 gEaseQuintic = Ease::Quintic;
static S32 gEaseSinusoidal= Ease::Sinusoidal;
static S32 gEaseExponential = Ease::Exponential;
static S32 gEaseCircular = Ease::Circular;
static S32 gEaseElastic = Ease::Elastic;
static S32 gEaseBack = Ease::Back;
static S32 gEaseBounce = Ease::Bounce;	


extern bool gEditingMission;

extern void ShowInit();

//------------------------------------------------------------------------------
/// Camera and FOV info
namespace CameraAndFOV{

   const  U32 MaxZoomSpeed             = 2000;     ///< max number of ms to reach target FOV

   static F32 sConsoleCameraFov        = 90.f;     ///< updated to camera FOV each frame
   static F32 sDefaultFov              = 90.f;     ///< normal FOV
   static F32 sCameraFov               = 90.f;     ///< current camera FOV
   static F32 sTargetFov               = 90.f;     ///< the desired FOV
   static F32 sLastCameraUpdateTime    = 0;        ///< last time camera was updated
   static S32 sZoomSpeed               = 500;      ///< ms per 90deg fov change

   /// A scale to apply to the normal visible distance
   /// typically used for tuning performance.
   static F32 sVisDistanceScale = 1.0f;

} // namespace {}

// query
static SimpleQueryList sgServerQueryList;
static U32 sgServerQueryIndex = 0;

//SERVER FUNCTIONS ONLY
ConsoleFunctionGroupBegin( Containers, "Spatial query functions. <b>Server side only!</b>");

DefineEngineFunction( containerFindFirst, const char*, (U32 typeMask, Point3F origin, Point3F size), , "(int mask, Point3F point, float x, float y, float z)"
   "@brief Find objects matching the bitmask type within a box centered at point, with extents x, y, z.\n\n"
   "@returns The first object found, or an empty string if nothing was found.  Thereafter, you can get more "
   "results using containerFindNext()."
   "@see containerFindNext\n"
   "@ingroup Game")
{
   //find out what we're looking for

   //build the container volume
   Box3F queryBox;
   queryBox.minExtents = origin;
   queryBox.maxExtents = origin;
   queryBox.minExtents -= size;
   queryBox.maxExtents += size;

   //initialize the list, and do the query
   sgServerQueryList.mList.clear();
   gServerContainer.findObjects(queryBox, typeMask, SimpleQueryList::insertionCallback, &sgServerQueryList);

   //return the first element
   sgServerQueryIndex = 0;
   static const U32 bufSize = 100;
   char *buff = Con::getReturnBuffer(bufSize);
   if (sgServerQueryList.mList.size())
      dSprintf(buff, bufSize, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId());
   else
      buff[0] = '\0';

   return buff;
}

DefineEngineFunction( containerFindNext, const char*, (), , "()"
   "@brief Get more results from a previous call to containerFindFirst().\n\n"
   "@note You must call containerFindFirst() to begin the search.\n"
   "@returns The next object found, or an empty string if nothing else was found.\n"
   "@see containerFindFirst()\n"
	"@ingroup Game")
{
   //return the next element
   static const U32 bufSize = 100;
   char *buff = Con::getReturnBuffer(bufSize);
   if (sgServerQueryIndex < sgServerQueryList.mList.size())
      dSprintf(buff, bufSize, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId());
   else
      buff[0] = '\0';

   return buff;
}

ConsoleFunctionGroupEnd( Containers );

//------------------------------------------------------------------------------

bool GameGetCameraTransform(MatrixF *mat, Point3F *velocity)
{
   // Return the position and velocity of the control object
   GameConnection* connection = GameConnection::getConnectionToServer();
   return connection && connection->getControlCameraTransform(0, mat) &&
      connection->getControlCameraVelocity(velocity);
}

//------------------------------------------------------------------------------
DefineEngineFunction( setDefaultFov, void, ( F32 defaultFOV ),,
				"@brief Set the default FOV for a camera.\n"
            "@param defaultFOV The default field of view in degrees\n"
				"@ingroup CameraSystem")
{
   CameraAndFOV::sDefaultFov = mClampF(defaultFOV, MinCameraFov, MaxCameraFov);
   if(CameraAndFOV::sCameraFov == CameraAndFOV::sTargetFov)
      CameraAndFOV::sTargetFov = CameraAndFOV::sDefaultFov;
}

DefineEngineFunction( setZoomSpeed, void, ( S32 speed ),,
				"@brief Set the zoom speed of the camera.\n"
            "This affects how quickly the camera changes from one field of view "
            "to another.\n"
            "@param speed The camera's zoom speed in ms per 90deg FOV change\n"
				"@ingroup CameraSystem")
{
   CameraAndFOV::sZoomSpeed = mClamp(speed, 0, CameraAndFOV::MaxZoomSpeed);
}

DefineEngineFunction( setFov, void, ( F32 FOV ),,
				"@brief Set the FOV of the camera.\n"
            "@param FOV The camera's new FOV in degrees\n"
				"@ingroup CameraSystem")
{
   CameraAndFOV::sTargetFov = mClampF(FOV, MinCameraFov, MaxCameraFov);
}

F32 GameGetCameraFov()
{
   return(CameraAndFOV::sCameraFov);
}

void GameSetCameraFov(F32 fov)
{
   CameraAndFOV::sTargetFov = CameraAndFOV::sCameraFov = fov;
}

void GameSetCameraTargetFov(F32 fov)
{
   CameraAndFOV::sTargetFov = fov;
}

void GameUpdateCameraFov()
{
   F32 time = F32(Platform::getVirtualMilliseconds());

   // need to update fov?
   if(CameraAndFOV::sTargetFov != CameraAndFOV::sCameraFov)
   {
      F32 delta = time - CameraAndFOV::sLastCameraUpdateTime;

      // snap zoom?
      if((CameraAndFOV::sZoomSpeed == 0) || (delta <= 0.0f))
         CameraAndFOV::sCameraFov = CameraAndFOV::sTargetFov;
      else
      {
         // gZoomSpeed is time in ms to zoom 90deg
         F32 step = 90.f * (delta / F32(CameraAndFOV::sZoomSpeed));

         if(CameraAndFOV::sCameraFov > CameraAndFOV::sTargetFov)
         {
            CameraAndFOV::sCameraFov -= step;
            if(CameraAndFOV::sCameraFov < CameraAndFOV::sTargetFov)
               CameraAndFOV::sCameraFov = CameraAndFOV::sTargetFov;
         }
         else
         {
            CameraAndFOV::sCameraFov += step;
            if(CameraAndFOV::sCameraFov > CameraAndFOV::sTargetFov)
               CameraAndFOV::sCameraFov = CameraAndFOV::sTargetFov;
         }
      }
   }

   // the game connection controls the vertical and the horizontal
   GameConnection * connection = GameConnection::getConnectionToServer();
   if(connection)
   {
      // check if fov is valid on control object
      if(connection->isValidControlCameraFov(CameraAndFOV::sCameraFov))
         connection->setControlCameraFov(CameraAndFOV::sCameraFov);
      else
      {
         // will set to the closest fov (fails only on invalid control object)
         if(connection->setControlCameraFov(CameraAndFOV::sCameraFov))
         {
            F32 setFov = CameraAndFOV::sCameraFov;
            connection->getControlCameraFov(&setFov);
            CameraAndFOV::sTargetFov =CameraAndFOV::sCameraFov = setFov;
         }
      }
   }

   // update the console variable
   CameraAndFOV::sConsoleCameraFov = CameraAndFOV::sCameraFov;
   CameraAndFOV::sLastCameraUpdateTime = time;
}
//--------------------------------------------------------------------------

#ifdef TORQUE_DEBUG
// ConsoleFunction(dumpTSShapes, void, 1, 1, "dumpTSShapes();")
// {
//    argc, argv;

//    FindMatch match("*.dts", 4096);
//    gResourceManager->findMatches(&match);

//    for (U32 i = 0; i < match.numMatches(); i++)
//    {
//       U32 j;
//       Resource<TSShape> shape = ResourceManager::get().load(match.matchList[i]);
//       if (bool(shape) == false)
//          Con::errorf(" aaa Couldn't load: %s", match.matchList[i]);

//       U32 numMeshes = 0, numSkins = 0;
//       for (j = 0; j < shape->meshes.size(); j++)
//          if (shape->meshes[j])
//             numMeshes++;
//       for (j = 0; j < shape->skins.size(); j++)
//          if (shape->skins[j])
//             numSkins++;

//      Con::printf(" aaa Shape: %s (%d meshes, %d skins)", match.matchList[i], numMeshes, numSkins);
//       Con::printf(" aaa   Meshes");
//       for (j = 0; j < shape->meshes.size(); j++)
//       {
//          if (shape->meshes[j])
//             Con::printf(" aaa     %d -> nf: %d, nmf: %d, nvpf: %d (%d, %d, %d, %d, %d)",
//                         shape->meshes[j]->meshType & TSMesh::TypeMask,
//                         shape->meshes[j]->numFrames,
//                         shape->meshes[j]->numMatFrames,
//                         shape->meshes[j]->vertsPerFrame,
//                         shape->meshes[j]->verts.size(),
//                         shape->meshes[j]->norms.size(),
//                         shape->meshes[j]->tverts.size(),
//                         shape->meshes[j]->primitives.size(),
//                         shape->meshes[j]->indices.size());
//       }
//       Con::printf(" aaa   Skins");
//       for (j = 0; j < shape->skins.size(); j++)
//       {
//          if (shape->skins[j])
//             Con::printf(" aaa     %d -> nf: %d, nmf: %d, nvpf: %d (%d, %d, %d, %d, %d)",
//                         shape->skins[j]->meshType & TSMesh::TypeMask,
//                         shape->skins[j]->numFrames,
//                         shape->skins[j]->numMatFrames,
//                         shape->skins[j]->vertsPerFrame,
//                         shape->skins[j]->verts.size(),
//                         shape->skins[j]->norms.size(),
//                         shape->skins[j]->tverts.size(),
//                         shape->skins[j]->primitives.size(),
//                         shape->skins[j]->indices.size());
//       }
//    }
// }
#endif

bool GameProcessCameraQuery(CameraQuery *query)
{
   GameConnection* connection = GameConnection::getConnectionToServer();

   if (connection && connection->getControlCameraTransform(0.032f, &query->cameraMatrix))
   {
      query->object = dynamic_cast<GameBase*>(connection->getCameraObject());
      query->nearPlane = gClientSceneGraph->getNearClip();

      // Scale the normal visible distance by the performance 
      // tuning scale which we never let over 1.
      CameraAndFOV::sVisDistanceScale = mClampF( CameraAndFOV::sVisDistanceScale, 0.01f, 1.0f );
      query->farPlane = gClientSceneGraph->getVisibleDistance() * CameraAndFOV::sVisDistanceScale;

      // Provide some default values
      query->stereoTargets[0] = 0;
      query->stereoTargets[1] = 0;
      query->eyeOffset[0] = Point3F::Zero;
      query->eyeOffset[1] = Point3F::Zero;
      query->hasFovPort = false;
      query->hasStereoTargets = false;
      query->displayDevice = NULL;
      
      F32 cameraFov = 0.0f;
      bool fovSet = false;

      // Try to use the connection's display deivce, if any, but only if the editor
      // is not open
      if(!gEditingMission && connection->hasDisplayDevice())
      {
         IDisplayDevice* display = connection->getDisplayDevice();

         query->displayDevice = display;

         // Note: all eye values are invalid until this is called
         display->setDrawCanvas(query->drawCanvas);

         display->setCurrentConnection(connection);

         // Display may activate AFTER so we need to call this again just in case
         display->onStartFrame();

         // The connection's display device may want to set the eye offset
         if(display->providesEyeOffsets())
         {
            display->getEyeOffsets(query->eyeOffset);
         }

         // Grab field of view for both eyes
         if (display->providesFovPorts())
         {
            display->getFovPorts(query->fovPort);
            fovSet = true;
            query->hasFovPort = true;
         }
         
         // Grab the latest overriding render view transforms
         connection->getControlCameraEyeTransforms(display, query->eyeTransforms);
         connection->getControlCameraHeadTransform(display, &query->headMatrix);

         display->getStereoViewports(query->stereoViewports);
         display->getStereoTargets(query->stereoTargets);
         query->hasStereoTargets = true;
      }
      else
      {
         query->eyeTransforms[0] = query->cameraMatrix;
         query->eyeTransforms[1] = query->cameraMatrix;
         query->headMatrix = query->cameraMatrix;
      }

      // Use the connection's FOV settings if requried
      if(!connection->getControlCameraFov(&cameraFov))
      {
         return false;
      }

      query->fov = mDegToRad(cameraFov);
      return true;
   }
   return false;
}

void GameRenderWorld()
{
   PROFILE_START(GameRenderWorld);
   FrameAllocator::setWaterMark(0);

   gClientSceneGraph->renderScene( SPT_Diffuse );

   // renderScene leaves some states dirty, which causes problems if GameTSCtrl is the last Gui object rendered
   GFX->updateStates();

   AssertFatal(FrameAllocator::getWaterMark() == 0,
      "Error, someone didn't reset the water mark on the frame allocator!");
   FrameAllocator::setWaterMark(0);
   PROFILE_END();
}

//================================================================================================
//Render a full frame from a given transform and frustum, and render out to a target
//================================================================================================
void renderFrame(GFXTextureTargetRef* target, MatrixF transform, Frustum frustum, U32 typeMask, ColorI canvasClearColor)
{
   if (!GFX->allowRender() || GFX->canCurrentlyRender())
      return;

   PROFILE_START(GameFunctions_RenderFrame);

   GFX->setActiveRenderTarget(*target);
   if (!GFX->getActiveRenderTarget())
      return;

   GFXTarget* renderTarget = GFX->getActiveRenderTarget();
   if (renderTarget == NULL)
      return;

   // Make sure the root control is the size of the canvas.
   Point2I size = renderTarget->getSize();
   if (size.x == 0 || size.y == 0)
      return;

   //Now, getting to the meat of it!
#ifdef TORQUE_GFX_STATE_DEBUG
   GFX->getDebugStateManager()->startFrame();
#endif
   RectI targetRect(0, 0, size.x, size.y);

   // Signal the interested parties.
   GuiCanvas::getGuiCanvasFrameSignal().trigger(true);

   GFXTransformSaver saver;

   // Gross hack to make sure we don't end up with advanced lighting and msaa 
   // at the same time, which causes artifacts. At the same time we don't 
   // want to just throw the settings the user has chosen if the light manager 
   // changes at a later time.
   /*GFXVideoMode mode = mPlatformWindow->getVideoMode();
   if (dStricmp(LIGHTMGR->getId(), "ADVLM") == 0 && mode.antialiasLevel > 0)
   {
      const char *pref = Con::getVariable("$pref::Video::mode");
      mode.parseFromString(pref);
      mode.antialiasLevel = 0;
      mPlatformWindow->setVideoMode(mode);

      Con::printf("AntiAliasing has been disabled; it is not compatible with AdvancedLighting.");
   }
   else if (dStricmp(LIGHTMGR->getId(), "BLM") == 0)
   {
      const char *pref = Con::getVariable("$pref::Video::mode");

      U32 prefAA = dAtoi(StringUnit::getUnit(pref, 5, " "));
      if (prefAA != mode.antialiasLevel)
      {
         mode.parseFromString(pref);
         mPlatformWindow->setVideoMode(mode);

         Con::printf("AntiAliasing has been enabled while running BasicLighting.");
      }
   }*/

   // Begin GFX
   PROFILE_START(GameFunctions_RenderFrame_GFXBeginScene);
   bool beginSceneRes = GFX->beginScene();
   PROFILE_END();

   PROFILE_START(GameFunctions_RenderFrame_OffscreenCanvases);

   // Render all offscreen canvas objects here since we may need them in the render loop
   if (GuiOffscreenCanvas::sList.size() != 0)
   {
      // Reset the entire state since oculus shit will have barfed it.
      GFX->updateStates(true);

      for (Vector<GuiOffscreenCanvas*>::iterator itr = GuiOffscreenCanvas::sList.begin(); itr != GuiOffscreenCanvas::sList.end(); itr++)
      {
         (*itr)->renderFrame(false, false);
      }

      GFX->setActiveRenderTarget(renderTarget);
   }

   PROFILE_END();

   // Can't render if waiting for device to reset.   
   if (!beginSceneRes)
   {
      // Since we already triggered the signal once for begin-of-frame,
      // we should be consistent and trigger it again for end-of-frame.
      GuiCanvas::getGuiCanvasFrameSignal().trigger(false);

      return;
   }

   // Clear the current viewport area
   GFX->setViewport(targetRect);
   GFX->clear(GFXClearZBuffer | GFXClearStencil | GFXClearTarget, canvasClearColor, 1.0f, 0);

   // Make sure we have a clean matrix state 
   // before we start rendering anything!   
   GFX->setWorldMatrix(MatrixF::Identity);
   GFX->setViewMatrix(MatrixF::Identity);
   GFX->setProjectionMatrix(MatrixF::Identity);

   {
      GFXStateBlockDesc d;

      d.cullDefined = true;
      d.cullMode = GFXCullNone;
      d.zDefined = true;
      d.zEnable = false;

      GFXStateBlockRef mDefaultGuiSB = GFX->createStateBlock(d);

      GFX->setClipRect(targetRect);
      GFX->setStateBlock(mDefaultGuiSB);

      GFXTargetRef origTarget = GFX->getActiveRenderTarget();

      // Clear the zBuffer so GUI doesn't hose object rendering accidentally
      GFX->clear(GFXClearZBuffer, ColorI(20, 20, 20), 1.0f, 0);

      GFX->setFrustum(frustum);
      MatrixF mSaveProjection = GFX->getProjectionMatrix();
      
      // We're going to be displaying this render at size of this control in
      // pixels - let the scene know so that it can calculate e.g. reflections
      // correctly for that final display result.
      gClientSceneGraph->setDisplayTargetResolution(size);

      // Set the GFX world matrix to the world-to-camera transform, but don't 
      // change the cameraMatrix in mLastCameraQuery. This is because 
      // mLastCameraQuery.cameraMatrix is supposed to contain the camera-to-world
      // transform. In-place invert would save a copy but mess up any GUIs that
      // depend on that value.
      CameraQuery camera;
      GameProcessCameraQuery(&camera);

      MatrixF worldToCamera = transform;

      RotationF tranRot = RotationF(transform);
      EulerF trf = tranRot.asEulerF(RotationF::Degrees);
      Point3F pos = transform.getPosition();

      GFX->setWorldMatrix(worldToCamera);

      mSaveProjection = GFX->getProjectionMatrix();
      MatrixF mSaveModelview = GFX->getWorldMatrix();

      Point2F mSaveWorldToScreenScale = GFX->getWorldToScreenScale();
      Frustum mSaveFrustum = GFX->getFrustum();
      mSaveFrustum.setTransform(transform);

      // Set the default non-clip projection as some 
      // objects depend on this even in non-reflect cases.
      gClientSceneGraph->setNonClipProjection(mSaveProjection);

      // Give the post effect manager the worldToCamera, and cameraToScreen matrices
      PFXMGR->setFrameMatrices(mSaveModelview, mSaveProjection);

      //renderWorld(guiViewport);
      PROFILE_START(GameFunctions_RenderFrame_RenderWorld);
      FrameAllocator::setWaterMark(0);

      gClientSceneGraph->renderScene(SPT_Reflect, typeMask);

      // renderScene leaves some states dirty, which causes problems if GameTSCtrl is the last Gui object rendered
      GFX->updateStates();

      AssertFatal(FrameAllocator::getWaterMark() == 0,
         "Error, someone didn't reset the water mark on the frame allocator!");
      FrameAllocator::setWaterMark(0);
      PROFILE_END();
   }

   PROFILE_START(GameFunctions_RenderFrame_GFXEndScene);
   GFX->endScene();
   PROFILE_END();

#ifdef TORQUE_GFX_STATE_DEBUG
   GFX->getDebugStateManager()->endFrame();
#endif

   saver.restore();

   PROFILE_END();
}

//================================================================================================
static void Process3D()
{
   MATMGR->updateTime();
   
   // Update the SFX world, if there is one.
   
   if( gSFX3DWorld )
      gSFX3DWorld->update();
}

static void RegisterGameFunctions()
{
   Con::addVariable( "$pref::Camera::distanceScale", TypeF32, &CameraAndFOV::sVisDistanceScale, 
      "A scale to apply to the normal visible distance, typically used for tuning performance.\n"
	   "@ingroup Game");
   Con::addVariable( "$cameraFov", TypeF32, &CameraAndFOV::sConsoleCameraFov, 
      "The camera's Field of View.\n\n"
	   "@ingroup Game" );

   // Stuff game types into the console
   Con::setIntVariable("$TypeMasks::StaticObjectType",         StaticObjectType);
   Con::setIntVariable("$TypeMasks::EnvironmentObjectType",    EnvironmentObjectType);
   Con::setIntVariable("$TypeMasks::TerrainObjectType",        TerrainObjectType);
   Con::setIntVariable("$TypeMasks::WaterObjectType",          WaterObjectType);
   Con::setIntVariable("$TypeMasks::TriggerObjectType",        TriggerObjectType);
   Con::setIntVariable("$TypeMasks::MarkerObjectType",         MarkerObjectType);
   Con::setIntVariable("$TypeMasks::GameBaseObjectType",       GameBaseObjectType);
   Con::setIntVariable("$TypeMasks::ShapeBaseObjectType",      ShapeBaseObjectType);
   Con::setIntVariable("$TypeMasks::CameraObjectType",         CameraObjectType);
   Con::setIntVariable("$TypeMasks::StaticShapeObjectType",    StaticShapeObjectType);
   Con::setIntVariable("$TypeMasks::DynamicShapeObjectType",   DynamicShapeObjectType);
   Con::setIntVariable("$TypeMasks::PlayerObjectType",         PlayerObjectType);
   Con::setIntVariable("$TypeMasks::ItemObjectType",           ItemObjectType);
   Con::setIntVariable("$TypeMasks::VehicleObjectType",        VehicleObjectType);
   Con::setIntVariable("$TypeMasks::VehicleBlockerObjectType", VehicleBlockerObjectType);
   Con::setIntVariable("$TypeMasks::ProjectileObjectType",     ProjectileObjectType);
   Con::setIntVariable("$TypeMasks::ExplosionObjectType",      ExplosionObjectType);
   Con::setIntVariable("$TypeMasks::CorpseObjectType",         CorpseObjectType);
   Con::setIntVariable("$TypeMasks::DebrisObjectType",         DebrisObjectType);
   Con::setIntVariable("$TypeMasks::PhysicalZoneObjectType",   PhysicalZoneObjectType);
   Con::setIntVariable("$TypeMasks::LightObjectType",          LightObjectType);
// PATHSHAPE
   Con::setIntVariable("$TypeMasks::PathShapeObjectType",     PathShapeObjectType);
// PATHSHAPE END

   Con::addVariable("Ease::InOut", TypeS32, &gEaseInOut, 
      "InOut ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::In", TypeS32, &gEaseIn, 
      "In ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Out", TypeS32, &gEaseOut, 
      "Out ease for curve movement.\n"
	   "@ingroup Game");

   Con::addVariable("Ease::Linear", TypeS32, &gEaseLinear, 
      "Linear ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Quadratic", TypeS32, &gEaseQuadratic, 
      "Quadratic ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Cubic", TypeS32, &gEaseCubic, 
      "Cubic ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Quartic", TypeS32, &gEaseQuartic, 
      "Quartic ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Quintic", TypeS32, &gEaseQuintic, 
      "Quintic ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Sinusoidal", TypeS32, &gEaseSinusoidal, 
      "Sinusoidal ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Exponential", TypeS32, &gEaseExponential, 
      "Exponential ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Circular", TypeS32, &gEaseCircular, 
      "Circular ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Elastic", TypeS32, &gEaseElastic, 
      "Elastic ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Back", TypeS32, &gEaseBack, 
      "Backwards ease for curve movement.\n"
	   "@ingroup Game");
   Con::addVariable("Ease::Bounce", TypeS32, &gEaseBounce, 
      "Bounce ease for curve movement.\n"
	   "@ingroup Game");
}
