//#define ENABLE_SPARKYS                                                        // Comment-out to disable Sparyk's Collision from compilation
//---------------------------------------------------------------------------
#include "DarkGDK.h"                                                          // DarkGDK
#include "DarkSDKDarkLights.h"                                                // DarkLights
#ifdef ENABLE_SPARKYS
   #include "SC_Collision.h"                                                  // Sparky's Collision
#endif
//---------------------------------------------------------------------------
// This program was written by PMC (A.K.A. The Tall Man from TGC Forums) to
// test/demo the DarkLights source code, available from the official TGC
// DarkBasic Pro Git Repository and slightly modified by me to work with
// DarkGDK. It shows lighting and shadow. See about.txt for notes and usage.
//---------------------------------------------------------------------------
struct coord   { float X, Y, Z; };                                            // A convenient way to store x,y,z coordinates
//---------------------------------------------------------------------------
#define STANDARD_GRAVITY      9.80665                                         // Standard Gravity of Earth
//---------------------------------------------------------------------------
// Global Variables - Configurable Options
bool  Load                       = true;                                      // Load the previously saved (i.e. LightMapped) objects. If true, this disables both Save and DarkLights.
bool  DisableSave                = false;                                     // Disables saving of the objects (they're not saved again anyway if Load==true).
bool  EnableDarkLights           = true;                                      // Enables/Disables DarkLights. Note: If Load==true, DarkLights will be disabled.
bool  ExitAfterLMComputation     = false;                                     // Exits (or not) after the LightMaps have been computed and the objects saved.

bool  EnableCollision            = true;                                      // Enable/Disable collision (room only, exludes the props). Note: Disabling collision also disables Gravity and Jumping.
bool  IncludePropsInCollision    = false;                                     // Only applies to Sparky's, which doesn't seem to handle the current Props well (it's great at most things though).

bool  FullScreen                 = false;                                     // Run in Full-Screen or Windowed
bool  VSync                      = true;                                      // Sync Frame-Refresh with the hardware monitor. Disabling allows for higher frame rates.
int   Fps                        = 60;                                        // Frame Rate
int   ScreenWidth                = 1024;                                      // Screen Resolution Width
int   ScreenHeight               = 768;                                       // Screen Resolution Height

float UnitsPerMeter              = 32;                                        // Everything is scaled by this. Changing this value should have no visible effect.
float CollisionRadius            = 0.25;                                      // This is also the radius of the player (a sphere)

float PlayerHeight               = 1.76;                                      // The height of the player camera in meters
float JumpHeight                 = 0.6532;                                    // The height that the player jumps to when jumping
coord RoomSize                   = { 21, 3, 21 };                             // The X,Y,Z Size of the room in meters (applies when Load==false)
//---------------------------------------------------------------------------
// Global Variables - Initialisations
int   ImageCount                 = 0;                                         // The number of   Images  that have been created, updated by NewImage()
int   ObjectCount                = 0;                                         // The number of   Objects that have been created, updated by NewObject()
int   PropCount                  = 0;                                         // The number of   Objects that are Props
int   ObjectPlayer               = 0;                                         // The Player      Object (a sphere)
int   ObjectRoomLast             = 0;                                         // The highest     Object number of the Room
int   ObjectInvisibleCeiling     = 0;                                         // Used for an invisible lower ceiling for Sparky's. This will not be added to DarkLights.
bool  Continue                   = true;                                      // If this gets set to false, the program exits
bool  DarkLightsStarted          = false;                                     // Indicates that the DarkLights computations have begun
bool  DarkLightsFinished         = false;                                     // Indicates that the DarkLights computations have finished
float Gravity                    = 0;                                         // This is the delta Y-Velocity for 1 frame
float JumpVelocity               = 0;                                         // When the Player Jumps, the Y-Velocity is instantly set to this value
float Falling                    = 0;                                         // This is actually the Player Y-Velocity

// Global Variables - Uninitialized
float DeltaPlayerHeight;                                                      // This is added to the player's Y-position to get the camera's Y-position
float MetersPerUnit;                                                          // The reciprocal of UnitsPerMeter, defined for convenience
coord Player0;                                                                // The player's starting position. Note: The 'R' key resets the player's position and orientation.
int   ObjectsProp[64];                                                        // An array to store the Prop Objects
char  Filename[256];                                                          // Re-usable storage space to store a filename
//---------------------------------------------------------------------------
// Function Prototypes - Section: Main
void  SetupDisplay            (void);                                         // Sets up the Display Mode, the Camera, and main Lighting

// Function Prototypes - Section: Objects
void  SetupPlayer             (void);                                         // Creates and sets up the Player
void  SetupRoom               (void);                                         // Creates and sets up the Room
void  SetupProps              (void);                                         // Loads   and sets up the Props
int   NewObject               (void);                                         // Returns a new, unused number to use when creating a new Object
int   NewImage                (void);                                         // Returns a new, unused number to use when creating a new Image

// Function Prototypes - Section: Controls
void  SetupGravity            (float gForce, float jumpHeight);               // Sets up Gravity and Jumping
void  Controls                (float speed, float mouseSensitivity);          // For Movement and User-Input (mouse/keyboard)
void  AssertBoundary          (float& position, float boundN, float boundP);  // A crude collision function based on the passed-in negative and positive boundaries

// Function Prototypes - Section: Controls - Sparky's Collision
void  SparkysStart            (void);                                         // Starts Sparky's Collision
void  SparkysSetupRoom        (void);                                         // Add to Sparky's Collision: The Room Objects (walls, etc)
void  SparkysSetupPropsInit   (void);                                         // Add to Sparky's Collision: The Props (which use object-scaling)
void  SparkysSetupPropsUpdate (void);                                         // Update Sparky's Collision: The Props have now been scaled.
void  SparkysCollision        (coord& position0, coord& position);            // Sparky's Collision Detection and Position Adjustment

// Function Prototypes - Section: Files
void  LoadObjectFile          (int object);                                   // Loads the object using ObjectFilename() as the filename
void  SaveObjectFile          (int object);                                   // Saves the object using ObjectFilename() as the filename
void  ChangeTexturePath       (int object, char* newPath);                    // Replace the existing texture path(s) in the object with newPath
void  ChangeTextureExt        (int object, char* newExtension);               // Replace the existing texture filename extensions with newExtension (useful if you wish to convert texture file names (i.e. .bmp to .dds)
char* ObjectFilename          (int object);                                   // Returns the filename for the passed-in object
char* ExtractFilename         (char* filePathAndName);                        // Returns the filename, exluding the file path
void  RemoveFileExtension     (char* filename);                               // Truncates the filename to remove the extension by replacing the last '.' with a NULL-terminator

// Function Prototypes - Section: DarkLights
void  DarkLightsStart         (void);
void  DarkLightsAddObjects    (void);
void  DarkLightsAddLights     (void);
void  DarkLightsBuildLightMaps(void);
//---------------------------------------------------------------------------
// Section: Main
//---------------------------------------------------------------------------
void DarkGDK(void)
{
   // Set The Path Of The Universe!
   SetCurrentDirectory("Universe");

   // Compute Parameters
   MetersPerUnit     = 1.0 / UnitsPerMeter;
   CollisionRadius  *= UnitsPerMeter;
   PlayerHeight     *= UnitsPerMeter;
   RoomSize.X       *= UnitsPerMeter;
   RoomSize.Y       *= UnitsPerMeter;
   RoomSize.Z       *= UnitsPerMeter;

   
   // Setup
   SetupDisplay();
   SparkysStart();
LabelSetup:
   ObjectInvisibleCeiling = NewObject();  // Reserved for Sparky's. Any objects created before the Player will not be added to DarkLights
   SetupPlayer();
   SetupRoom();
   SetupProps();
   SparkysSetupRoom();
   SetupGravity(1, JumpHeight);
   Controls    (0.0625, 0.5);

   // DarkLights Code
   if (EnableDarkLights && !Load)
   {
      DarkLightsStart();
      DarkLightsAddObjects();
      DarkLightsAddLights();
      DarkLightsBuildLightMaps();
   }

   // Save the objects, except when DarkLights started but was terminated early without finishing
   if (!DisableSave && !(DarkLightsStarted && !DarkLightsFinished))
   {
      int object = ObjectCount;
      while (object > ObjectPlayer)
      {
         SaveObjectFile(object);
         object--;
      }

      if (DarkLightsStarted)
      {
         dbLMReset();
      }

      // The following restarts the program loading the newly lightmapped objects
      if (!ExitAfterLMComputation)
      {
         Continue             = true;
         Load                 = true;
         DisableSave          = true;
         DarkLightsFinished   = false;
         while (ObjectCount)  dbDeleteObject(ObjectCount--);
         while ( ImageCount)  dbDeleteImage ( ImageCount--);
         goto LabelSetup;
      }
   }

   // Main Loop
   if (Continue)
   {
   	while (LoopGDK())
	   {
         Controls(0.0625, 0.5);
         dbText(16, 16, dbStr(dbScreenFPS()));
	   	dbSync();
   	}
   }

   // Clean up
   while (ObjectCount)  dbDeleteObject(ObjectCount--);
   while ( ImageCount)  dbDeleteImage ( ImageCount--);
}
//---------------------------------------------------------------------------
void SetupDisplay(void)
{
   // Initialise DarkGDK Environment
	dbSyncOn();
	dbSyncRate(Fps);
   dbSetDisplayModeVSync(ScreenWidth, ScreenHeight, 32, VSync);
   if (FullScreen)
   {
      dbSetWindowOff();
   }
   dbHideMouse();

   // Setup Camera
   dbSetCameraFOV    (45);
   dbSetCameraRange  (0.0625*UnitsPerMeter, 3000*UnitsPerMeter);
   dbSetCameraAspect (4.0/3);
   dbColorBackdrop   (0);
   dbBackdropOn();
   dbAutoCamOff();

   // Setup Regular Lighting
   dbSetAmbientLight(80);
}
//---------------------------------------------------------------------------
// Section: Objects
//---------------------------------------------------------------------------
void SetupPlayer(void)
{
   float    playerRadius;

   DeltaPlayerHeight = PlayerHeight - CollisionRadius;

   ObjectPlayer = NewObject();
   dbMakeObjectSphere(ObjectPlayer, CollisionRadius);
   dbHideObject      (ObjectPlayer);
   Player0.X = 0;
   Player0.Y = CollisionRadius;
   Player0.Z = 0;
   dbPositionObject  (ObjectPlayer, Player0.X, Player0.Y, Player0.Z);
}
//---------------------------------------------------------------------------
void SetupRoom(void)
{
   int   imageWall, imageCeiling, imageFloor;
   int   object, objectFirst;
   int   objectFloor, objectCeiling, objectWallLeft, objectWallRight, objectWallFront, objectWallBack;


   // Load Images
   imageWall    = NewImage(); dbLoadImage("Textures\\OfficeBlue_All_D2.dds" , imageWall   );
   imageCeiling = NewImage(); dbLoadImage("Textures\\Tile Ceiling.dds"      , imageCeiling);
   imageFloor   = NewImage(); dbLoadImage("Textures\\Blue Carpet_FLR_D2.DDS", imageFloor  );

   objectFloor             = NewObject();   objectFirst = objectFloor;
   objectCeiling           = NewObject();
   objectWallFront         = NewObject();
   objectWallBack          = NewObject();
   objectWallLeft          = NewObject();
   objectWallRight         = NewObject();

   if (Load)
   {
      LoadObjectFile(objectFloor);
      LoadObjectFile(objectCeiling);
      LoadObjectFile(objectWallFront);
      LoadObjectFile(objectWallBack);
      LoadObjectFile(objectWallRight);
      LoadObjectFile(objectWallLeft);
   }
   else
   {
      dbMakeObjectPlane (objectFloor    , RoomSize.X, RoomSize.Z);
      dbMakeObjectPlane (objectCeiling  , RoomSize.X, RoomSize.Z);
      dbMakeObjectPlane (objectWallFront, RoomSize.X, RoomSize.Y);
      dbMakeObjectPlane (objectWallBack , RoomSize.X, RoomSize.Y);
      dbMakeObjectPlane (objectWallRight, RoomSize.Z, RoomSize.Y);
      dbMakeObjectPlane (objectWallLeft , RoomSize.Z, RoomSize.Y);

      dbTextureObject   (objectFloor    , imageFloor);
      dbTextureObject   (objectCeiling  , imageCeiling);
      dbTextureObject   (objectWallFront, imageWall);
      dbTextureObject   (objectWallBack , imageWall);
      dbTextureObject   (objectWallRight, imageWall);
      dbTextureObject   (objectWallLeft , imageWall);

      dbScaleObjectTexture (objectFloor    , dbObjectSizeX(objectFloor    ) /  RoomSize.Y      , dbObjectSizeY(objectFloor    ) /  RoomSize.Y);
      dbScaleObjectTexture (objectCeiling  , dbObjectSizeX(objectCeiling  ) /  RoomSize.Y      , dbObjectSizeY(objectCeiling  ) /  RoomSize.Y);
      dbScaleObjectTexture (objectWallFront, dbObjectSizeX(objectWallFront) / (RoomSize.Y*1.50), dbObjectSizeY(objectWallFront) / (RoomSize.Y*1.50));
      dbScaleObjectTexture (objectWallBack , dbObjectSizeX(objectWallBack ) / (RoomSize.Y*1.43), dbObjectSizeY(objectWallBack ) / (RoomSize.Y*1.43));
      dbScaleObjectTexture (objectWallRight, dbObjectSizeX(objectWallRight) / (RoomSize.Y*1.50), dbObjectSizeY(objectWallRight) / (RoomSize.Y*1.50));
      dbScaleObjectTexture (objectWallLeft , dbObjectSizeX(objectWallLeft ) / (RoomSize.Y*1.50), dbObjectSizeY(objectWallLeft ) / (RoomSize.Y*1.50));

      dbScrollObjectTexture(objectWallFront, 0, 0.25);
      dbScrollObjectTexture(objectWallBack , 0, 0.25);
      dbScrollObjectTexture(objectWallRight, 0, 0.25);
      dbScrollObjectTexture(objectWallLeft , 0, 0.25);

      // Perform same operations on all room objects
      object = ObjectCount;
      while (object >= objectFirst)
      {
         dbSetObjectNormals   (object);
         dbSetObjectCull      (object, true);
         object--;
      }
   }


   dbXRotateObject   (objectFloor, 90);
   dbYRotateObject   (objectFloor, 180);                                // This is here so the LightMap will not be rotated by 180 Degrees
   dbPositionObject  (objectFloor, 0, 0, 0);

   dbXRotateObject   (objectCeiling, 270);
   dbYRotateObject   (objectCeiling, 180);                              // This is here so the LightMap will not be rotated by 180 Degrees
   dbPositionObject  (objectCeiling, 0, RoomSize.Y, 0);

   dbYRotateObject   (objectWallFront, 0);
   dbPositionObject  (objectWallFront, 0, RoomSize.Y/2, RoomSize.Z/2);

   dbYRotateObject   (objectWallBack, 180);
   dbPositionObject  (objectWallBack, 0, RoomSize.Y/2, -RoomSize.Z/2);
   
   dbYRotateObject   (objectWallRight, 90);
   dbPositionObject  (objectWallRight, RoomSize.Z/2, RoomSize.Y/2, 0);

   dbYRotateObject   (objectWallLeft, 270);
   dbPositionObject  (objectWallLeft, -RoomSize.Z/2, RoomSize.Y/2, 0);


   // Create an invisible lower ceiling for Sparky's Collision, since the camera's Y-Position is higher than the player's Y-Position
   dbMakeObjectPlane (ObjectInvisibleCeiling, RoomSize.X, RoomSize.Z);
   dbXRotateObject   (ObjectInvisibleCeiling, 270);
   dbPositionObject  (ObjectInvisibleCeiling, 0, RoomSize.Y - DeltaPlayerHeight, 0);
   dbHideObject      (ObjectInvisibleCeiling);

   ObjectRoomLast = ObjectCount;
}
//---------------------------------------------------------------------------
void SetupProps(void)
{
   int      object, objectFirst, propIndex, image;
   float    scaleObject;


   // Define the number of Prop Objects
   PropCount = 4;

   // Assign new object numbers
   propIndex = PropCount;
   while (propIndex--)
   {
      ObjectsProp[propIndex] = NewObject();
   }
   
   if (Load)
   {
      propIndex = PropCount;
      while (propIndex--)
      {
         LoadObjectFile(ObjectsProp[propIndex]);
      }
   }
   else
   {
      dbLoadObject("Props\\Pyramid\\pyramid.dbo", ObjectsProp[0]);
      dbLoadObject("Props\\Miko\\miko.dbo"      , ObjectsProp[1]);
      dbLoadObject("Props\\Piano\\piano2.dbo"   , ObjectsProp[2]);
      dbLoadObject("Props\\Pictures\\Ship1.dbo" , ObjectsProp[3]);

      ChangeTexturePath(ObjectsProp[0], "Props\\Pyramid\\");
      ChangeTexturePath(ObjectsProp[1], "Props\\Miko\\");
      ChangeTexturePath(ObjectsProp[2], "Props\\Piano\\");
      ChangeTexturePath(ObjectsProp[3], "Props\\Pictures\\");

      // Perform same operations on all prop objects
      propIndex = PropCount;
      while (propIndex--)
      {
         object = ObjectsProp[propIndex];
         dbSetObjectNormals (object);
         dbSetObjectCull    (object, true);
      }
   }

   if (IncludePropsInCollision)
   {
      SparkysSetupPropsInit();
   }

   // Prop - Pyramid
   object = ObjectsProp[0];
   scaleObject = UnitsPerMeter / 10;
   dbScaleObject   (object, scaleObject*100, scaleObject*100, scaleObject*100);
   dbRotateObject  (object, -90, 180,0);
   dbPositionObject(object, -(RoomSize.X - dbObjectSizeX(object)*scaleObject)/2, 0, (RoomSize.Z-dbObjectSizeZ(object)*scaleObject)/2);

   // Prop - Miko
   object = ObjectsProp[1];
   scaleObject = UnitsPerMeter / 80;
   dbScaleObject   (object, scaleObject*100, scaleObject*100, scaleObject*100);
   dbPositionObject(object, 0, 0, RoomSize.Z/2 - 2*UnitsPerMeter);

   // Prop - Piano
   object = ObjectsProp[2];
   scaleObject = UnitsPerMeter / 280;
   dbScaleObject   (object, scaleObject*100, scaleObject*100, scaleObject*100);
   dbRotateObject  (object, -90, 180+45,0);
   dbPositionObject(object, (RoomSize.X - dbObjectSizeX(object)*scaleObject)/2, 0, (RoomSize.Z-dbObjectSizeZ(object)*scaleObject)/2);

   // Prop - Ship
   object = ObjectsProp[3];
   scaleObject = UnitsPerMeter / 32;
   dbScaleObject   (object, scaleObject*100, scaleObject*100, scaleObject*100);
   dbRotateObject  (object, 0, 180, 0);
   dbPositionObject(object, 0, RoomSize.Y*2/3, -RoomSize.Z/2 + 0.05*UnitsPerMeter);

   if (IncludePropsInCollision)
   {
      SparkysSetupPropsUpdate();
   }
}
//---------------------------------------------------------------------------
int NewObject(void)
{
   return (++ObjectCount);
}
//---------------------------------------------------------------------------
int NewImage(void)
{
   return (++ImageCount);
}
//---------------------------------------------------------------------------
// Section: Controls
//---------------------------------------------------------------------------
void SetupGravity(float gForce, float jumpHeight)
{
   Gravity = (gForce * STANDARD_GRAVITY / MetersPerUnit) / (double)(Fps*Fps);
   JumpVelocity = (1.0 + sqrt(1.0 + 8.0 * jumpHeight / MetersPerUnit / Gravity)) / 2.0 * Gravity;
}
//---------------------------------------------------------------------------
void Controls(float speed, float mouseSensitivity)
{
   float    mouseX, mouseY;
   float    angleX, angleY;
   coord    position, position0;
   float    deltaPlayerHeight = DeltaPlayerHeight;


   // Orientation Movement
   angleX = dbObjectAngleX(ObjectPlayer);
   angleY = dbObjectAngleY(ObjectPlayer);

   mouseX = dbMouseMoveX();
   mouseY = dbMouseMoveY();

   angleX += mouseY * mouseSensitivity;
   angleY += mouseX * mouseSensitivity;
   AssertBoundary(angleX, -90, 90);
   dbXRotateObject (ObjectPlayer, angleX);
   dbYRotateObject (ObjectPlayer, angleY);

   // Position Movement
   speed *= UnitsPerMeter;
   if (GetKeyState(VK_SHIFT) < 0)
   {
      speed *= 3;
   }

   if (GetKeyState(VK_CONTROL) < 0)
   {
      deltaPlayerHeight -= UnitsPerMeter;
   }

   position.X = dbObjectPositionX(ObjectPlayer); position0.X = position.X;
   position.Y = dbObjectPositionY(ObjectPlayer); position0.Y = position.Y;
   position.Z = dbObjectPositionZ(ObjectPlayer); position0.Z = position.Z;

   if (GetKeyState('R') < 0)
   {
      position.X = Player0.X;
      position.Y = Player0.Y;
      position.Z = Player0.Z;
      dbXRotateObject (ObjectPlayer, 0);
      dbYRotateObject (ObjectPlayer, 0);
   }

   if (GetKeyState('A') < 0)
   {
      position.X -= speed * dbCOS(angleY);
      position.Z += speed * dbSIN(angleY);
   }
   if (GetKeyState('D') < 0)
   {
      position.X += speed * dbCOS(angleY);
      position.Z -= speed * dbSIN(angleY);
   }
   if (GetKeyState('W') < 0)
   {
      position.X += speed * dbSIN(angleY);
      position.Z += speed * dbCOS(angleY);
   }
   if (GetKeyState('S') < 0)
   {
      position.X -= speed * dbSIN(angleY);
      position.Z -= speed * dbCOS(angleY);
   }
   if (GetKeyState('Q') < 0)
   {
      position.Y += speed;
   }
   if (GetKeyState('Z') < 0)
   {
      position.Y -= speed;
   }
   if (JumpVelocity && !Falling && (GetKeyState(' ') < 0))
   {
      Falling += JumpVelocity;
   }
   
   // Room EnableCollision by Boundaries
   if (EnableCollision)
   {
      position.Y += Falling - Gravity;
#ifdef ENABLE_SPARKYS
      SparkysCollision(position0, position);
#else
      AssertBoundary(position.X, -RoomSize.X/2, RoomSize.X/2);
      AssertBoundary(position.Z, -RoomSize.Z/2, RoomSize.Z/2);
      AssertBoundary(position.Y,  0           , RoomSize.Y - deltaPlayerHeight);
#endif
      Falling = position.Y - position0.Y;
   }

   dbPositionObject(ObjectPlayer, position.X, position.Y, position.Z);
   dbPositionCamera(position.X, position.Y + deltaPlayerHeight, position.Z);
   dbSetCameraToObjectOrientation(ObjectPlayer);
}
//---------------------------------------------------------------------------
void AssertBoundary(float& position, float boundN, float boundP)
{
   boundN += CollisionRadius;
   boundP -= CollisionRadius;

   if (position < boundN)
   {
      position = boundN;
   }

   if (position > boundP)
   {
      position = boundP;
   }
}
//---------------------------------------------------------------------------
// Section: Controls --> Sparky's Collision
//---------------------------------------------------------------------------
void SparkysStart(void)
{
#ifdef ENABLE_SPARKYS
   SC_Start();
#endif
}
//---------------------------------------------------------------------------
void SparkysSetupRoom(void)
{
#ifdef ENABLE_SPARKYS
   int   object;

   object = ObjectRoomLast;
   while (object > ObjectPlayer)
   {
      SC_SetupObject(object, 1, SC_POLYGON);
      object--;
   }
   SC_SetupObject(ObjectInvisibleCeiling, 1, SC_POLYGON);
#endif
}
//---------------------------------------------------------------------------
void SparkysSetupPropsInit(void)
{
#ifdef ENABLE_SPARKYS
   int propIndex = PropCount;
   while (propIndex--)
   {
      SC_SetupObject       (ObjectsProp[propIndex], 1, SC_POLYGON);
      SC_AllowObjectScaling(ObjectsProp[propIndex]);
   }
#endif
}
//---------------------------------------------------------------------------
void SparkysSetupPropsUpdate(void)
{
#ifdef ENABLE_SPARKYS
   int propIndex = PropCount;
   while (propIndex--)
   {
      SC_UpdateObject(ObjectsProp[propIndex]);
   }
#endif
}
//---------------------------------------------------------------------------
void SparkysCollision(coord& position0, coord& position)
{
#ifdef ENABLE_SPARKYS
   int objectCollide;

   objectCollide = SC_SphereSlideGroup(1, position0.X, position0.Y, position0.Z, position.X, position.Y, position.Z, CollisionRadius, 0);
   if (objectCollide)
   {
      position.X = SC_GetCollisionSlideX();
      position.Y = SC_GetCollisionSlideY();
      position.Z = SC_GetCollisionSlideZ();
   }
#endif
}
//---------------------------------------------------------------------------
// Section: Files
//---------------------------------------------------------------------------
void LoadObjectFile(int object)
{
   dbLoadObject(ObjectFilename(object), object);
}
//---------------------------------------------------------------------------
void SaveObjectFile(int object)
{
   char* filename = ObjectFilename(object);

   if (dbFileExist(filename))
   {
      dbDeleteFile(filename);
   }
   dbSaveObject(filename, object);
}
//---------------------------------------------------------------------------
void ChangeTexturePath(int object, char* newPath)
{
   int         indexMesh, indexTexture;
   sObject*    obj;
   sMesh*      mesh;
   sTexture*   texture;
   char*       filename;

   obj = dbGetObject(object);
   indexMesh = obj->iMeshCount;
   while (indexMesh--)
   {
      mesh = obj->ppMeshList[indexMesh];
      indexTexture = mesh->dwTextureCount;
      while (indexTexture--)
      {
         texture = &mesh->pTextures[indexTexture];
         filename = ExtractFilename(texture->pName);
         if (filename)
         {
            strcpy(Filename, filename);
            strcpy(texture->pName, newPath);
            strcat(texture->pName, Filename);
         }
      }
   }
}
//---------------------------------------------------------------------------
void ChangeTextureExt(int object, char* newExtension)
{
   int         indexMesh, indexTexture;
   sObject*    obj;
   sMesh*      mesh;
   sTexture*   texture;

   obj = dbGetObject(object);
   indexMesh = obj->iMeshCount;
   while (indexMesh--)
   {
      mesh = obj->ppMeshList[indexMesh];
      indexTexture = mesh->dwTextureCount;
      while (indexTexture--)
      {
         texture = &mesh->pTextures[indexTexture];
         if (texture->pName)
         {
            RemoveFileExtension(texture->pName);
            strcat(texture->pName, newExtension);
         }
      }
   }
}
//---------------------------------------------------------------------------
char* ObjectFilename(int object)
{
   strcpy(Filename, dbStr(object));
   strcat(Filename, ".dbo");

   return (Filename);
}
//---------------------------------------------------------------------------
char* ExtractFilename(char* filePathAndName)
{
   int   index;

   if (!filePathAndName || !*filePathAndName)
   {
      return (NULL);
   }

   index = strlen(filePathAndName);
   while (index-- && (filePathAndName[index] != '\\'));
   if (index)
   {
      index++;
   }
   return (&filePathAndName[index]);
}
//---------------------------------------------------------------------------
void RemoveFileExtension(char* filename)
{
   int   index;
 
   index = strlen(filename);
   if (!index)
   {
      return;
   }

   while (index-- && (filename[index] != '.'));
   filename[index] = 0;
}
//---------------------------------------------------------------------------
// Section: DarkLights
//---------------------------------------------------------------------------
void DarkLightsStart(void)
{
   // Start DarkLights (needed before any other LM commands are called)
   dbLMStart();
   DarkLightsStarted = true;

   // This is the folder where the Lightmap files will be deposited
   dbLMSetLightMapFolder("Lightmaps\\");

   // This is the base filenames for the lightmaps
   dbLMSetLightMapName("Universe");

   // Set LightMap file format to .dds
   dbLMSetLightMapFileFormat(1);
}
//---------------------------------------------------------------------------
void DarkLightsAddObjects(void)
{
   int   object;

   // Add objects to DarkLights
   object = ObjectCount;
   while (object > ObjectPlayer)
   {
      if (object > ObjectRoomLast)
      {
         dbLMAddCollisionObject(object); // Add Object for Light-EnableCollision (so it will make shadows). No not add the room itself.
      }
      dbLMAddLightMapObject (object);    // Add Object for Light-Mapping   (so it will receive the lights)
      object--;
   }
}
//---------------------------------------------------------------------------
void DarkLightsAddLights(void)
{
   float roomMaxSize;
   float i;                                                                   // i for intensity
   float x, z, deltaX, deltaZ, startX, startZ, endX, endZ;


   // Calculate Max Room Size
   roomMaxSize = RoomSize.X;
   if (roomMaxSize < RoomSize.Z)
   {
      roomMaxSize = RoomSize.Z;
   }
   i = 1.0 / 12;

   // Add Ambient Light
   dbLMSetAmbientLight(i, i, i);                                       
   
   // Add 6 Point Lights to illuminate the room
   i = 1;
   deltaX = RoomSize.X / 4;
   deltaZ = RoomSize.Z / 4;
   endX = RoomSize.X / 2; startX = -endX;
   endZ = RoomSize.Z / 2; startZ = -endZ;
   z = startZ + deltaZ*2;
   while (z < endZ)
   {
      x = startX + deltaX;
      while (x < endX)
      {
         dbLMAddPointLight(x, RoomSize.Y*0.75, z, roomMaxSize, i, i, i);
         x += deltaX;
      }
      z += deltaZ;
   }

   // Add 1 Point Light in front of the piano
   dbLMAddPointLight(x - deltaX/2, RoomSize.Y*0.8, z - deltaZ/2, roomMaxSize, i, i, i);

   // Align the back wall with little floor lights, spaced 3 meters apart
   // These are positioned beneath the floor. If the floor had been added to DarkLights collision, it would block these lights, but it wasn't.
   i = 10;
   x = 0;
   while (x < endX)
   {
      x += 3*UnitsPerMeter;
      dbLMAddSpotLight( x, -0.25*UnitsPerMeter, startZ + 0.25*UnitsPerMeter, 0, 1, 0, 1, 90, RoomSize.Y - 0.05*UnitsPerMeter, i/2,i/2,i);
      dbLMAddSpotLight(-x, -0.25*UnitsPerMeter, startZ + 0.25*UnitsPerMeter, 0, 1, 0, 1, 90, RoomSize.Y - 0.05*UnitsPerMeter, i/2,i/2,i);
   }
      dbLMAddSpotLight( 0, -0.25*UnitsPerMeter, startZ + 0.25*UnitsPerMeter, 0, 1, 0, 1, 90, RoomSize.Y - 0.05*UnitsPerMeter, i/2,i/2,i);

   // Add Light Inside of the Pyramid
   if (ObjectsProp[0])
   {
      i = 4;
      dbLMAddPointLight(dbObjectPositionX(ObjectsProp[0]), 0.25 * UnitsPerMeter, dbObjectPositionZ(ObjectsProp[0]), roomMaxSize, i, i, i);
   }

   // Add Lights along the front wall over each prop (the pyramid light is commented-out)
   i = 4;
//   dbLMAddPointLight(-RoomSize.X/2 + 0.125*UnitsPerMeter, RoomSize.Y * 0.3, RoomSize.Z/2 - 0.125*UnitsPerMeter, 6*UnitsPerMeter, i, i, i);
   dbLMAddPointLight( 0                                 , RoomSize.Y * 0.8, RoomSize.Z/2 - 0.125*UnitsPerMeter, 6*UnitsPerMeter, i, i, i);
   dbLMAddPointLight( RoomSize.X/2 - 0.125*UnitsPerMeter, RoomSize.Y * 0.8, RoomSize.Z/2 - 0.125*UnitsPerMeter, 6*UnitsPerMeter, i, i, i);
}
//---------------------------------------------------------------------------
void DarkLightsBuildLightMaps(void)
{
   char  text[256];

   // Build Lightmaps
   dbLMBuildCollisionData();
   dbLMBuildLightMapsThread(1024, 1, 1);

   // Display Status
   do
   {
      dbText(16, 16, dbStr(dbScreenFPS()));
      dbText(16, 48, "DarkLights is Computing LightMaps");
      strcpy(text, "Percent Complete: ");
      strcat(text, dbStr(dbLMGetPercent()));
      dbText(16, 80, text);
      dbText(16, 96, dbLMGetStatus());
      Controls(0.0625, 0.5);
  	   dbSync();

      Continue = LoopGDK();
   }
   while (Continue && !dbLMGetComplete());

   if (Continue)
   {
      DarkLightsFinished = true;
   }
   else
   {
      dbLMTerminateThread();
   }

   // Lightmap Calculations are finished: Exit
   Continue &= !ExitAfterLMComputation;
}
//---------------------------------------------------------------------------
