Torque3D Documentation / _generateds / sceneLighting.cpp

sceneLighting.cpp

Engine/source/lighting/common/sceneLighting.cpp

More...

Classes:

Detailed Description

Public Variables

SceneLighting * gLighting 
F32 gParellelVectorThresh 
F32 gPlaneDistThresh 
F32 gPlaneNormThresh 

Public Functions

compareS32(const void * a, const void * b)

lastCreatedSort(const void * p1, const void * p2)

lastModifiedSort(const void * p1, const void * p2)

maxSizeSort(const void * p1, const void * p2)

minSizeSort(const void * p1, const void * p2)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "lighting/common/sceneLighting.h"
  26
  27#include "T3D/gameBase/gameConnection.h"
  28#include "console/engineAPI.h"
  29#include "console/consoleTypes.h"
  30#include "scene/sceneManager.h"
  31#include "lighting/common/shadowVolumeBSP.h"
  32#include "T3D/shapeBase.h"
  33#include "gui/core/guiCanvas.h"
  34#include "ts/tsShape.h"
  35#include "ts/tsShapeInstance.h"
  36#include "T3D/staticShape.h"
  37#include "T3D/tsStatic.h"
  38#include "collision/concretePolyList.h"
  39#include "lighting/lightingInterfaces.h"
  40#include "terrain/terrData.h"
  41#include "platform/platformVolume.h"
  42#include "core/stream/fileStream.h"
  43#include "core/crc.h"
  44
  45namespace
  46{
  47   bool              gTerminateLighting = false;
  48   F32               gLightingProgress = 0.0f;
  49   char *            gCompleteCallback = NULL;
  50   U32               gConnectionMissionCRC = 0xffffffff;
  51}
  52
  53SceneLighting *gLighting = NULL;
  54F32 gParellelVectorThresh = 0.01f;
  55F32 gPlaneNormThresh = 0.999f;
  56F32 gPlaneDistThresh = 0.001f;
  57
  58
  59void SceneLighting::sgNewEvent(U32 light, S32 object, U32 event)
  60{
  61   Sim::postEvent(this, new sgSceneLightingProcessEvent(light, object, event), Sim::getTargetTime() + 1);
  62   // Paint canvas here?
  63}
  64
  65//-----------------------------------------------
  66/*
  67* Called once per scenelighting - entry point for event system
  68*/
  69void SceneLighting::sgLightingStartEvent()
  70{   
  71   Con::printf("");
  72   Con::printf("Starting scene lighting...");
  73
  74   sgTimeTemp2 = Platform::getRealMilliseconds();
  75
  76   // clear interior light maps
  77   for(ObjectProxy **proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++)
  78   {
  79      ObjectProxy* objprox;
  80      objprox = *proxyItr;
  81      // is there an object?
  82      if(!objprox->getObject())
  83      {
  84         AssertFatal(0, "SceneLighting:: missing sceneobject on light start");
  85         Con::errorf(ConsoleLogEntry::General, "   SceneLighting:: missing sceneobject on light start");
  86         continue;
  87      }
  88
  89      objprox->processLightingStart();
  90   }
  91
  92
  93   sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgTGEPassSetupEventType);
  94   //sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgSGPassSetupEventType);
  95}
  96
  97/*
  98* Called once per scenelighting - exit from event system
  99*/
 100void SceneLighting::sgLightingCompleteEvent()
 101{
 102   Vector<TerrainBlock*> terrBlocks;
 103
 104   // initialize the objects for lighting
 105   for(ObjectProxy ** proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++)
 106   {
 107      ObjectProxy* objprox = *proxyItr;
 108      TerrainBlock *terr = dynamic_cast<TerrainBlock *>(objprox->getObject());
 109      if (terr)
 110         terrBlocks.push_back(terr);
 111   }
 112
 113   for (S32 i = 0; i < terrBlocks.size(); i++)
 114      terrBlocks[i]->postLight(terrBlocks);
 115
 116   // save out the lighting?
 117   if(Con::getBoolVariable("$sceneLighting::cacheLighting", true))
 118   {
 119      if(!savePersistInfo(mFileName))
 120         Con::errorf(ConsoleLogEntry::General, "SceneLighting::light: unable to persist lighting!");
 121      else
 122         Con::printf("Successfully saved mission lighting file: '%s'", mFileName);
 123   }
 124
 125   Con::printf("Scene lighting complete (%3.3f seconds)", (Platform::getRealMilliseconds()-sgTimeTemp2)/1000.f);
 126   Con::printf("//-----------------------------------------------");
 127   Con::printf("");
 128   
 129
 130   completed(true);
 131   deleteObject();
 132}
 133
 134//-----------------------------------------------
 135/*
 136* Called once per scenelighting - used for prepping the
 137* event system for TGE style scenelighting
 138*/
 139void SceneLighting::sgTGEPassSetupEvent()
 140{
 141   Con::printf("  Starting TGE based scene lighting...");
 142
 143
 144   sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgTGELightStartEventType);
 145}
 146
 147/*
 148* Called once per light - used for calling preLight on all objects
 149* Only TGE lights call prelight and continue on to the process event
 150*/
 151void SceneLighting::sgTGELightStartEvent(U32 light)
 152{
 153   // catch bad light index and jump to complete event
 154   if(light >= mLights.size())
 155   {
 156      sgNewEvent(light, 0, sgSceneLightingProcessEvent::sgTGELightCompleteEventType);
 157      return;
 158   }
 159
 160   // can we use the light?
 161   if(mLights[light]->getType() != LightInfo::Vector)
 162   {
 163      sgNewEvent((light+1), 0, sgSceneLightingProcessEvent::sgTGELightStartEventType);
 164      return;
 165   }
 166
 167   // process pre-lighting
 168   Con::printf("    Lighting with light #%d (TGE vector light)...", (light+1));
 169   LightInfo *lightobj = mLights[light];
 170   mLitObjects.clear();
 171
 172   for(ObjectProxy **proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++)
 173   {
 174      ObjectProxy* objprox = *proxyItr;
 175
 176      // is there an object?
 177      if(!objprox->getObject())
 178      {
 179         AssertFatal(0, "SceneLighting:: missing sceneobject on light start");
 180         Con::errorf(ConsoleLogEntry::General, "      SceneLighting:: missing sceneobject on light start");
 181         continue;
 182      }
 183
 184      if (objprox->tgePreLight(lightobj))
 185         mLitObjects.push_back(objprox);
 186   }
 187
 188   // kick off lighting
 189
 190   sgNewEvent(light, 0, sgSceneLightingProcessEvent::sgTGELightProcessEventType);
 191}
 192
 193/*
 194* Called once for each TGE light and object - used for calling light on an object
 195*/
 196void SceneLighting::sgTGELightProcessEvent(U32 light, S32 object)
 197{
 198   // catch bad light or object index
 199   if((light >= mLights.size()) || (object >= mLitObjects.size()))
 200   {
 201      sgNewEvent(light, 0, sgSceneLightingProcessEvent::sgTGELightCompleteEventType);
 202      return;
 203   }
 204
 205   //process object and light
 206   S32 time = Platform::getRealMilliseconds();
 207   // light object
 208   LightInfo* li = mLights[light];
 209   mLitObjects[object]->processTGELightProcessEvent(object, mLitObjects.size(), li);
 210
 211   sgTGESetProgress(light, object);
 212   Con::printf("      Object lighting complete (%3.3f seconds)", (Platform::getRealMilliseconds()-time)/1000.f);
 213
 214
 215   // kick off next object event
 216
 217   sgNewEvent(light, (object+1), sgSceneLightingProcessEvent::sgTGELightProcessEventType);
 218}
 219
 220/*
 221* Called once per TGE light - used for calling postLight on all objects
 222*/
 223void SceneLighting::sgTGELightCompleteEvent(U32 light)
 224{
 225   // catch bad light index and move to the next pass event
 226   if(light >= mLights.size())
 227   {
 228      sgTGESetProgress(mLights.size(), mLitObjects.size());
 229      Con::printf("  TGE based scene lighting complete (%3.3f seconds)", (Platform::getRealMilliseconds()-sgTimeTemp2)/1000.f);
 230
 231      sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgSGPassSetupEventType);
 232      //sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgLightingCompleteEventType);
 233      return;
 234   }
 235
 236   // process post-lighting
 237   // don't do this, SG lighting events will copy terrain light map...
 238   /*bool islast = (light == (mLights.size() - 1));
 239   for(U32 o=0; o<mLitObjects.size(); o++)
 240   {
 241   if(dynamic_cast<TerrainProxy *>(mLitObjects[o]))
 242   mLitObjects[o]->postLight(islast);
 243   }*/
 244
 245   // kick off next light event
 246
 247   sgNewEvent((light+1), 0, sgSceneLightingProcessEvent::sgTGELightStartEventType);
 248}
 249
 250void SceneLighting::sgTGESetProgress(U32 light, S32 object)
 251{
 252   // TGE is light based...
 253   F32 val = (F32)(light * mLitObjects.size()) + object;
 254   F32 total = (F32)(mLights.size() * mLitObjects.size());
 255
 256   if(total == 0.0f)
 257      return;
 258
 259   val = getMin(val, total);
 260
 261   // two passes...
 262   total *= 2.0f;
 263
 264   gLightingProgress = val / total;
 265}
 266
 267//-----------------------------------------------
 268/*
 269* Called once per scenelighting - used for prepping the
 270* event system for SG style scenelighting
 271*/
 272void SceneLighting::sgSGPassSetupEvent()
 273{
 274   mLitObjects.clear();
 275   for(ObjectProxy **proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++)
 276   {
 277      // is there an object?
 278      if(!(*proxyItr)->getObject())
 279      {
 280         AssertFatal(0, "SceneLighting:: missing sceneobject on light start");
 281         Con::errorf(ConsoleLogEntry::General, "   SceneLighting:: missing sceneobject on light start");
 282         continue;
 283      }
 284
 285      // add all lights
 286      mLitObjects.push_back(*proxyItr);
 287   }
 288
 289   sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgSGObjectStartEventType);
 290}
 291
 292/*
 293* Called once per object - used for calling preLight on all SG lights
 294*/
 295void SceneLighting::sgSGObjectStartEvent(S32 object)
 296{
 297   // catch bad light index and jump to complete event
 298   if(object >= mLitObjects.size())
 299   {
 300      sgNewEvent(0, object, sgSceneLightingProcessEvent::sgSGObjectCompleteEventType);
 301      return;
 302   }
 303
 304   ObjectProxy *obj = mLitObjects[object];
 305   bool bHandled = obj->processStartObjectLightingEvent(object, mLitObjects.size());
 306   if (!bHandled)
 307   {
 308      Con::printf("    Lighting object %d of %d... %s: %s", (object+1), mLitObjects.size(), obj->getObject()->getClassName(), obj->getObject()->getName());
 309   }
 310
 311   for(U32 i=0; i<mLights.size(); i++)
 312   {
 313      // can we use the light?
 314      LightInfo *lightobj = mLights[i];
 315      //if((lightobj->mType == LightInfo::SGStaticPoint) || (lightobj->mType == LightInfo::SGStaticSpot))
 316      obj->preLight(lightobj);
 317   }
 318
 319   sgTimeTemp = Platform::getRealMilliseconds();
 320
 321   // kick off lighting
 322
 323
 324   // this is slow with multiple objects...
 325   //sgNewEvent(0, object, sgSceneLightingProcessEvent::sgSGObjectProcessEventType);
 326   // jump right to the method...
 327   sgSGObjectProcessEvent(0, object);
 328}
 329
 330/*
 331* Called once per object and SG light - used for calling light on an object
 332*/
 333void SceneLighting::sgSGObjectProcessEvent(U32 light, S32 object)
 334{
 335   // catch bad light or object index
 336   if((light >= mLights.size()) || (object >= mLitObjects.size()))
 337   {
 338      // this is slow with multiple objects...
 339      //sgNewEvent(0, object, sgSceneLightingProcessEvent::sgSGObjectCompleteEventType);
 340      // jump right to the method...
 341      sgSGObjectCompleteEvent(object);
 342      return;
 343   }
 344
 345   // avoid the event overhead...
 346   // 80 lights == 0.6 seconds an interior without ANY lighting (events only)...
 347   U32 time = Platform::getRealMilliseconds();
 348   ObjectProxy* objprox = mLitObjects[object];
 349   while((light < mLights.size()) && ((Platform::getRealMilliseconds() - time) < 500))
 350   {
 351      // can we use the light?
 352      LightInfo *lightobj = mLights[light];
 353
 354      objprox->processSGObjectProcessEvent(lightobj);
 355
 356      sgSGSetProgress(light, object);
 357
 358      light++;
 359   }
 360
 361   light--;
 362
 363   // kick off next light event
 364
 365   sgNewEvent((light+1), object, sgSceneLightingProcessEvent::sgSGObjectProcessEventType);
 366}
 367
 368/*
 369* Called once per object - used for calling postLight on all SG lights
 370*/
 371void SceneLighting::sgSGObjectCompleteEvent(S32 object)
 372{
 373   // catch bad light index and move to the next pass event
 374   if(object >= mLitObjects.size())
 375   {
 376      sgSGSetProgress(mLights.size(), mLitObjects.size());
 377
 378      sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgLightingCompleteEventType);
 379      return;
 380   }
 381
 382   // process post-lighting
 383   Con::printf("    Object lighting complete (%3.3f seconds)", (Platform::getRealMilliseconds()-sgTimeTemp)/1000.f);
 384
 385   // in case Atlas turned off rendering...
 386   GFX->setAllowRender(true);
 387
 388   // only the last light does something
 389   mLitObjects[object]->postLight(true);
 390   
 391   
 392   /*ObjectProxy *obj = mLitObjects[object];
 393   for(U32 i=0; i<mLights.size(); i++)
 394   {
 395   // can we use the light?
 396   LightInfo *lightobj = mLights[i];
 397   if((lightobj->mType == LightInfo::SGStaticPoint) || (lightobj->mType == LightInfo::SGStaticSpot))
 398   obj->postLight((i == (mLights.size() - 1)));
 399   }*/
 400
 401   // kick off next light event
 402
 403
 404   // this is slow with multiple objects...
 405   //sgNewEvent(0, (object+1), sgSceneLightingProcessEvent::sgSGObjectStartEventType);
 406   // jump right to the method...
 407   sgSGObjectStartEvent((object+1));
 408}
 409
 410void SceneLighting::sgSGSetProgress(U32 light, S32 object)
 411{
 412   // SG is object based...
 413   F32 val = (F32)((object * mLights.size()) + light);
 414   F32 total = (F32)(mLights.size() * mLitObjects.size());
 415
 416   if(total == 0.0f)
 417      return;
 418
 419   val = getMin(val, total);
 420
 421   // two passes...
 422   total *= 2.0f;
 423
 424   gLightingProgress = (val / total) + 0.5f;
 425}
 426
 427//-----------------------------------------------
 428
 429void SceneLighting::processEvent(U32 light, S32 object)
 430{
 431   sgNewEvent(light, object, sgSceneLightingProcessEvent::sgLightingStartEventType);
 432}
 433
 434
 435//-----------------------------------------------
 436
 437
 438SceneLighting::SceneLighting(AvailableSLInterfaces* lightingInterfaces)
 439{
 440   mLightingInterfaces = lightingInterfaces;
 441   mStartTime = 0;
 442   mFileName[0] = '\0';
 443   mSceneManager = NULL;
 444
 445   // Registering vars more than once doesn't hurt anything.
 446   Con::addVariable("$sceneLighting::terminateLighting", TypeBool, &gTerminateLighting);
 447   Con::addVariable("$sceneLighting::lightingProgress", TypeF32, &gLightingProgress);
 448
 449   mLightingInterfaces->initInterfaces();
 450}
 451
 452SceneLighting::~SceneLighting()
 453{
 454   gLighting = NULL;
 455   gLightingProgress = 0.0f;
 456
 457   ObjectProxy ** proxyItr;
 458   for(proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++)
 459      delete *proxyItr;
 460}
 461
 462void SceneLighting::getMLName(const char* misName, const U32 missionCRC, const U32 buffSize, char* filenameBuffer)
 463{
 464   dSprintf(filenameBuffer, buffSize, "%s_%x.ml", misName, missionCRC);
 465}
 466
 467bool SceneLighting::light(BitSet32 flags)
 468{
 469   if(!mSceneManager)
 470      return(false);
 471
 472   mStartTime = Platform::getRealMilliseconds();
 473
 474   // Register static lights
 475   if (!LIGHTMGR)
 476      return false;     // This world doesn't need lighting.
 477   
 478   LIGHTMGR->registerGlobalLights(NULL,true);
 479
 480   // Notify each system factory that we are beginning to light
 481   for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end(); sitr++)
 482   {
 483      SceneLightingInterface* si = (*sitr);
 484      si->processLightingBegin();
 485   }
 486
 487   // grab all the lights
 488   mLights.clear();
 489   LIGHTMGR->getAllUnsortedLights(&mLights);
 490   LIGHTMGR->unregisterAllLights();
 491
 492   if(!mLights.size())
 493      return(false);
 494
 495   // get all the objects and create proxy's for them
 496   SimpleQueryList objects;   
 497   gClientContainer.findObjects(mLightingInterfaces->mAvailableObjectTypes, &SimpleQueryList::insertionCallback, &objects);
 498
 499   for(SceneObject ** itr = objects.mList.begin(); itr != objects.mList.end(); itr++)
 500   {
 501      ObjectProxy * proxy = NULL;
 502      SceneObject* obj = *itr;
 503      if (!obj)
 504         continue;
 505
 506      // Create the right chunk for the system 
 507      for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); 
 508         sitr != mLightingInterfaces->mAvailableSystemInterfaces.end() && proxy == NULL; sitr++)
 509      {
 510         SceneLightingInterface* si = (*sitr);
 511         proxy = si->createObjectProxy(obj, &mSceneObjects);
 512      }
 513
 514      if (proxy)
 515      {
 516         if(!proxy->calcValidation())
 517         {
 518            Con::errorf(ConsoleLogEntry::General, "Failed to calculate validation info for object.  Skipped.");
 519            delete proxy;
 520            continue;
 521         }
 522
 523         if(!proxy->loadResources())
 524         {
 525            Con::errorf(ConsoleLogEntry::General, "Failed to load resources for object.  Skipped.");
 526            delete proxy;
 527            continue;
 528         }
 529
 530         mSceneObjects.push_back(proxy);
 531      }
 532   }
 533
 534   if(!mSceneObjects.size())
 535      return(false);
 536
 537   // grab the missions crc
 538   U32 missionCRC = calcMissionCRC();
 539
 540   // remove the '.mis' extension from the mission name
 541   char misName[256];
 542   dSprintf(misName, sizeof(misName), "%s", Con::getVariable("$Client::MissionFile"));
 543   char * dot = dStrstr((const char*)misName, ".mis");
 544   if(dot)
 545      *dot = '\0';
 546
 547   // get the mission name
 548   getMLName(misName, missionCRC, 1023, mFileName);
 549
 550   // check for some persisted data, check if being forced..
 551   if(!flags.test(ForceAlways</a>|<a href="/coding/class/classscenelighting/#classscenelighting_1ae20788ea028adb489b112108d21b79c9ada7b471ecea31dd16513b9d77f7bd71f">ForceWritable))
 552   {
 553      if(loadPersistInfo(mFileName))
 554      {
 555         Con::printf(" Successfully loaded mission lighting file: '%s'", mFileName);
 556
 557         // touch this file...
 558         if(!Platform::FS::Touch(mFileName))
 559            Con::warnf("  Failed to touch file '%s'.  File may be read only.", mFileName);
 560
 561         return(false);
 562      }
 563
 564      // texture manager must have lighting complete now
 565      if(flags.test(LoadOnly))
 566      {
 567         Con::errorf(ConsoleLogEntry::General, "Failed to load mission lighting!");
 568         return(false);
 569      }
 570   }
 571
 572   // don't light if file is read-only?
 573   if(!flags.test(ForceAlways))
 574   {
 575      FileStream  stream;
 576
 577      stream.open( mFileName, Torque::FS::File::Write );
 578
 579      if(stream.getStatus() != Stream::Ok)
 580      {
 581         Con::errorf(ConsoleLogEntry::General, "SceneLighting::Light: Failed to light mission.  File '%s' cannot be written to.", mFileName);
 582         return(false);
 583      }
 584   }
 585
 586   // initialize the objects for lighting
 587   for(ObjectProxy ** proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++)
 588      (*proxyItr)->init();
 589
 590   // get things started
 591   Sim::postEvent(this, new sgSceneLightingProcessEvent(0, -1,
 592      sgSceneLightingProcessEvent::sgLightingStartEventType), Sim::getTargetTime() + 1);
 593
 594   return(true);
 595}
 596
 597void SceneLighting::completed(bool success)
 598{
 599   // process the cached lighting files
 600   processCache();
 601
 602   // Notify each system factory that we are have lit!
 603   for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end(); sitr++)
 604   {
 605      SceneLightingInterface* si = (*sitr);
 606      si->processLightingCompleted(success);
 607   }
 608
 609   if(gCompleteCallback && gCompleteCallback[0])
 610      Con::executef((const char*)gCompleteCallback);
 611
 612   dFree(gCompleteCallback);
 613   gCompleteCallback = NULL;
 614}
 615
 616//------------------------------------------------------------------------------
 617// Static access method: there can be only one SceneLighting object
 618bool SceneLighting::lightScene(const char * callback, BitSet32 flags)
 619{   
 620   if(gLighting)
 621   {
 622      Con::errorf(ConsoleLogEntry::General, "Lighting is already in progress!");
 623      return false;
 624   }
 625
 626   // register the object
 627   if(!registerObject())
 628   {
 629      AssertFatal(0, "SceneLighting:: Unable to register SceneLighting object!");
 630      Con::errorf(ConsoleLogEntry::General, "SceneLighting:: Unable to register SceneLighting object!");
 631      delete this;
 632      return(false);
 633   }
 634
 635   // could have interior resources but no instances (hey, got this far didnt we...)
 636   GameConnection * con = dynamic_cast<GameConnection*>(NetConnection::getConnectionToServer());
 637   if(!con)
 638   {
 639      Con::errorf(ConsoleLogEntry::General, "SceneLighting:: no GameConnection");
 640      return(false);
 641   }
 642   con->addObject(this);
 643
 644   // set the globals
 645   gLighting = this;
 646   gTerminateLighting = false;
 647   gLightingProgress = 0.0f;
 648   if (gCompleteCallback)
 649      dFree(gCompleteCallback);   
 650   gCompleteCallback = dStrdup(callback);
 651   gConnectionMissionCRC = con->getMissionCRC();
 652
 653   // assumes we are in the world that needs lighting...
 654   mSceneManager = gClientSceneGraph;
 655
 656   if(!light(flags))
 657   {
 658      completed(true);
 659      deleteObject();
 660      return(false);
 661   }
 662   return(true);
 663}
 664
 665bool SceneLighting::isLighting()
 666{
 667   return(bool(gLighting));
 668}
 669
 670/// adds TSStatic objects as shadow casters.
 671void SceneLighting::addStatic(ShadowVolumeBSP *shadowVolume,
 672                              SceneObject *sceneobject, LightInfo *light, S32 level)
 673{
 674   if (!sceneobject)
 675      return;
 676
 677   if(light->getType() != LightInfo::Vector)
 678      return;
 679
 680   ConcretePolyList polylist;
 681   const Box3F box;
 682   const SphereF sphere;
 683   sceneobject->buildPolyList(PLC_Collision, &polylist, box, sphere);
 684
 685   // retrieve the poly list (uses the collision mesh)...
 686   //sobj->sgAdvancedStaticOptionsData.sgBuildPolyList(sobj, &polylist);
 687
 688   S32 i, count, vertind[3];
 689   ConcretePolyList::Poly *poly;
 690
 691   count = polylist.mPolyList.size();
 692
 693   // add the polys to the shadow volume...
 694   for(i=0; i<count; i++)
 695   {
 696      poly = (ConcretePolyList::Poly *)&polylist.mPolyList[i];
 697      AssertFatal((poly->vertexCount == 3), "Hmmm... vert count is greater than 3.");
 698
 699      vertind[0] = polylist.mIndexList[poly->vertexStart];
 700      vertind[1] = polylist.mIndexList[poly->vertexStart + 1];
 701      vertind[2] = polylist.mIndexList[poly->vertexStart + 2];
 702
 703      if(mDot(PlaneF(polylist.mVertexList[vertind[0]], polylist.mVertexList[vertind[1]],
 704         polylist.mVertexList[vertind[2]]), light->getDirection()) < gParellelVectorThresh)
 705      {
 706         ShadowVolumeBSP::SVPoly *svpoly = shadowVolume->createPoly();
 707         svpoly->mWindingCount = 3;
 708
 709         svpoly->mWinding[0].set(polylist.mVertexList[vertind[0]]);
 710         svpoly->mWinding[1].set(polylist.mVertexList[vertind[1]]);
 711         svpoly->mWinding[2].set(polylist.mVertexList[vertind[2]]);
 712         svpoly->mPlane = PlaneF(svpoly->mWinding[0], svpoly->mWinding[1], svpoly->mWinding[2]);
 713         svpoly->mPlane.neg();
 714
 715         shadowVolume->buildPolyVolume(svpoly, light);
 716         shadowVolume->insertPoly(svpoly);
 717      }
 718   }
 719}
 720
 721//------------------------------------------------------------------------------
 722bool SceneLighting::verifyMissionInfo(PersistInfo::PersistChunk * chunk)
 723{
 724   PersistInfo::MissionChunk * info = dynamic_cast<PersistInfo::MissionChunk*>(chunk);
 725   if(!info)
 726      return(false);
 727
 728   PersistInfo::MissionChunk curInfo;
 729   if(!getMissionInfo(&curInfo))
 730      return(false);
 731
 732   return(curInfo.mChunkCRC == info->mChunkCRC);
 733}
 734
 735bool SceneLighting::getMissionInfo(PersistInfo::PersistChunk * chunk)
 736{
 737   PersistInfo::MissionChunk * info = dynamic_cast<PersistInfo::MissionChunk*>(chunk);
 738   if(!info)
 739      return(false);
 740
 741   info->mChunkCRC = gConnectionMissionCRC ^ PersistInfo::smFileVersion;
 742   return(true);
 743}
 744
 745//------------------------------------------------------------------------------
 746bool SceneLighting::loadPersistInfo(const char * fileName)
 747{
 748   FileStream  stream;
 749
 750   stream.open( fileName, Torque::FS::File::Read );
 751
 752   if(stream.getStatus() != Stream::Ok)
 753      return false;
 754
 755   PersistInfo persistInfo;
 756   bool success = persistInfo.read(stream);
 757   stream.close();
 758   if(!success)
 759      return(false);
 760
 761   // verify the mission chunk
 762   if(!verifyMissionInfo(persistInfo.mChunks[0]))
 763      return(false);
 764
 765   // Create the right chunk for the system    
 766   for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end(); sitr++)
 767   {
 768      SceneLightingInterface* si = (*sitr);
 769      if (!si->postProcessLoad(&persistInfo, &mSceneObjects))
 770      {
 771         return false;
 772      }
 773   }
 774
 775   if(mSceneObjects.size() != (persistInfo.mChunks.size() - 1))
 776      return(false);
 777
 778   Vector<PersistInfo::PersistChunk*> chunks;
 779
 780   // ensure that the scene objects are in the same order as the chunks
 781   //  - different instances will depend on this
 782   U32 i;
 783   for(i = 0; i < mSceneObjects.size(); i++)
 784   {
 785      // 0th chunk is the mission chunk
 786      U32 chunkIdx = i+1;
 787      if(chunkIdx >= persistInfo.mChunks.size())
 788         return(false);
 789
 790      if(!mSceneObjects[i]->isValidChunk(persistInfo.mChunks[chunkIdx]))
 791         return(false);
 792      chunks.push_back(persistInfo.mChunks[chunkIdx]);
 793   }
 794
 795   // get the objects to load in the persisted chunks
 796   for(i = 0; i < mSceneObjects.size(); i++)
 797      if(!mSceneObjects[i]->setPersistInfo(chunks[i]))
 798         return(false);
 799
 800   return(true);
 801}
 802
 803bool SceneLighting::savePersistInfo(const char * fileName)
 804{
 805   // open the file
 806   FileStream file;
 807   file.open( fileName, Torque::FS::File::Write );
 808
 809   if(file.getStatus() != Stream::Ok)
 810      return false;
 811
 812   PersistInfo persistInfo;
 813
 814   // add in the mission chunk
 815   persistInfo.mChunks.push_back(new PersistInfo::MissionChunk);
 816
 817   // get the mission info, will return false when there are 0 lights
 818   if(!getMissionInfo(persistInfo.mChunks[0]))
 819      return(false);
 820
 821   // get all the persist chunks
 822   bool bChunkFound;
 823   for(U32 i = 0; i < mSceneObjects.size(); i++)
 824   {
 825      bChunkFound = false;
 826      // Create the right chunk for the system 
 827      for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end() && !bChunkFound; sitr++)
 828      {
 829         SceneLightingInterface* si = (*sitr);
 830         PersistInfo::PersistChunk* chunk;
 831         if (si->createPersistChunkFromProxy(mSceneObjects[i], &chunk))
 832         {
 833            if (chunk)
 834            {
 835               persistInfo.mChunks.push_back(chunk);
 836               bChunkFound = true;
 837            }
 838         }
 839      }
 840
 841      // Make sure the chunk worked.
 842      if (!mSceneObjects[i]->getPersistInfo(persistInfo.mChunks.last()))
 843         return false;
 844   }
 845
 846   if(!persistInfo.write(file))
 847      return(false);
 848
 849   file.close();
 850
 851   return(true);
 852}
 853
 854struct CacheEntry {
 855   Torque::FS::FileNodeRef mFileObject;
 856   const char *mFileName;
 857
 858   CacheEntry() {
 859      mFileName = 0;
 860   };
 861};
 862
 863// object list sort methods: want list in reverse
 864static S32 QSORT_CALLBACK minSizeSort(const void * p1, const void * p2)
 865{
 866   const CacheEntry * entry1 = (const CacheEntry *)p1;
 867   const CacheEntry * entry2 = (const CacheEntry *)p2;
 868
 869   return(entry2->mFileObject->getSize() - entry1->mFileObject->getSize());
 870}
 871
 872static S32 QSORT_CALLBACK maxSizeSort(const void * p1, const void * p2)
 873{
 874   const CacheEntry * entry1 = (const CacheEntry *)p1;
 875   const CacheEntry * entry2 = (const CacheEntry *)p2;
 876
 877   return(entry1->mFileObject->getSize() - entry2->mFileObject->getSize());
 878}
 879
 880static S32 QSORT_CALLBACK lastCreatedSort(const void * p1, const void * p2)
 881{
 882   const CacheEntry * entry1 = (const CacheEntry *)p1;
 883   const CacheEntry * entry2 = (const CacheEntry *)p2;
 884
 885   FileTime create[2];
 886   FileTime modify;
 887
 888   bool ret[2];
 889
 890   ret[0] = Platform::getFileTimes(entry1->mFileName, &create[0], &modify);
 891   ret[1] = Platform::getFileTimes(entry2->mFileName, &create[1], &modify);
 892
 893   // check return values
 894   if(!ret[0] && !ret[1])
 895      return(0);
 896   if(!ret[0])
 897      return(1);
 898   if(!ret[1])
 899      return(-1);
 900
 901   return(Platform::compareFileTimes(create[1], create[0]));
 902}
 903
 904static S32 QSORT_CALLBACK lastModifiedSort(const void * p1, const void * p2)
 905{
 906   const CacheEntry * entry1 = (const CacheEntry *)p1;
 907   const CacheEntry * entry2 = (const CacheEntry *)p2;
 908
 909   FileTime create;
 910   FileTime modify[2];
 911
 912   bool ret[2];
 913
 914   ret[0] = Platform::getFileTimes(entry1->mFileName, &create, &modify[0]);
 915   ret[1] = Platform::getFileTimes(entry2->mFileName, &create, &modify[1]);
 916
 917   // check return values
 918   if(!ret[0] && !ret[1])
 919      return(0);
 920   if(!ret[0])
 921      return(1);
 922   if(!ret[1])
 923      return(-1);
 924
 925   return(Platform::compareFileTimes(modify[1], modify[0]));
 926}
 927
 928void SceneLighting::processCache()
 929{
 930   // get size in kb
 931   S32 quota = Con::getIntVariable("$sceneLighting::cacheSize", -1);
 932
 933   Vector<CacheEntry> files;
 934
 935   Vector<String> fileNames;
 936   Torque::FS::FindByPattern(Torque::Path(Platform::getMainDotCsDir()), "*.ml", true, fileNames);
 937
 938   S32 curCacheSize = 0;
 939
 940   for(S32 i = 0;i < fileNames.size();++i)
 941   {
 942      if(! Torque::FS::IsFile(fileNames[i]))
 943         continue;
 944
 945      Torque::FS::FileNodeRef fileNode = Torque::FS::GetFileNode(fileNames[i]);
 946      if(fileNode == NULL)
 947         continue;
 948
 949      if(dStrstr(fileNames[i], mFileName) == 0)
 950      {
 951         // Don't allow the current file to be removed
 952         CacheEntry entry;
 953         entry.mFileObject = fileNode;
 954         entry.mFileName = StringTable->insert(fileNames[i]);
 955         files.push_back(entry);
 956      }
 957      else
 958         curCacheSize += fileNode->getSize();
 959   }
 960
 961   // remove old files
 962   for(S32 i = files.size() - 1; i >= 0; i--)
 963   {
 964      FileStream *stream;
 965      if((stream = FileStream::createAndOpen( files[i].mFileObject->getName(), Torque::FS::File::Read )) == NULL)
 966         continue;
 967
 968      // read in the version
 969      U32 version;
 970      bool ok = (stream->read(&version) && (version == PersistInfo::smFileVersion));
 971      delete stream;
 972
 973      // ok?
 974      if(ok)
 975         continue;
 976
 977      // no sneaky names
 978      if(!dStrstr(files[i].mFileName, ".."))
 979      {
 980         Con::warnf("Removing old lighting file '%s'.", files[i].mFileName);
 981         dFileDelete(files[i].mFileName);
 982      }
 983
 984      files.pop_back();
 985   }
 986
 987   // no size restriction?
 988   if(quota == -1 || !files.size())
 989      return;
 990
 991   for(U32 i = 0; i < files.size(); i++)
 992      curCacheSize += files[i].mFileObject->getSize();
 993
 994   // need to remove?
 995   if(quota > (curCacheSize >> 10))
 996      return;
 997
 998   // sort the entries by the correct method
 999   const char * purgeMethod = Con::getVariable("$sceneLighting::purgeMethod");
1000   if(!purgeMethod)
1001      purgeMethod = "";
1002
1003   // determine the method (default to least recently used)
1004   if(!dStricmp(purgeMethod, "minSize"))
1005      dQsort(files.address(), files.size(), sizeof(CacheEntry), minSizeSort);
1006   else if(!dStricmp(purgeMethod, "maxSize"))
1007      dQsort(files.address(), files.size(), sizeof(CacheEntry), maxSizeSort);
1008   else if(!dStricmp(purgeMethod, "lastCreated"))
1009      dQsort(files.address(), files.size(), sizeof(CacheEntry), lastCreatedSort);
1010   else
1011      dQsort(files.address(), files.size(), sizeof(CacheEntry), lastModifiedSort);
1012
1013   // go through and remove the best candidate first (sorted reverse)
1014   while(((curCacheSize >> 10) > quota) && files.size())
1015   {
1016      CacheEntry& lastFile = files.last();
1017      curCacheSize -= lastFile.mFileObject->getSize();
1018
1019      // no sneaky names
1020      if (!dStrstr(lastFile.mFileName, ".."))
1021      {
1022         Con::warnf("Removing lighting file '%s'.", lastFile.mFileName);
1023         dFileDelete(lastFile.mFileName);
1024      }
1025
1026      files.pop_back();
1027   }
1028}
1029
1030static S32 QSORT_CALLBACK compareS32(const void * a, const void * b)
1031{
1032   return(*((S32 *)a) - *((S32 *)b));
1033}
1034
1035U32 SceneLighting::calcMissionCRC()
1036{
1037   // all the objects + mission chunk
1038   Vector<U32> crc;
1039
1040   // grab the object crcs
1041   for(U32 i = 0; i < mSceneObjects.size(); i++)
1042      crc.push_back( mSceneObjects[i]->mChunkCRC );
1043
1044   // grab the missions crc
1045   PersistInfo::MissionChunk curInfo;
1046   getMissionInfo(&curInfo);
1047   crc.push_back(curInfo.mChunkCRC);
1048
1049   // sort them (order may not have been preserved)
1050   dQsort(crc.address(), crc.size(), sizeof(U32), compareS32);
1051
1052#ifdef TORQUE_BIG_ENDIAN
1053   // calculateCRC operates on 8-bit chunks of memory. The memory is a vector
1054   // of U32's, and so the result will be different on big/little endian hardware.
1055   // To fix this, swap endians on the CRC's in the vector. This must be done
1056   // _after_ the qsort.
1057   for( S32 i = 0; i < crc.size(); i++ )
1058      crc[i] = endianSwap( crc[i] );
1059#endif
1060
1061   return(CRC::calculateCRC(crc.address(), sizeof(U32) * crc.size(), 0xffffffff));
1062}
1063
1064bool SceneLighting::ObjectProxy::calcValidation()
1065{
1066   mChunkCRC = getResourceCRC();
1067   if(!mChunkCRC)
1068      return(false);
1069
1070   return(true);
1071}
1072
1073bool SceneLighting::ObjectProxy::isValidChunk(PersistInfo::PersistChunk * chunk)
1074{
1075   return(chunk->mChunkCRC == mChunkCRC);
1076}
1077
1078bool SceneLighting::ObjectProxy::getPersistInfo(PersistInfo::PersistChunk * chunk)
1079{
1080   chunk->mChunkCRC = mChunkCRC;
1081   return(true);
1082}
1083
1084bool SceneLighting::ObjectProxy::setPersistInfo(PersistInfo::PersistChunk * chunk)
1085{
1086   mChunkCRC = chunk->mChunkCRC;
1087   return(true);
1088}
1089