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   sgTimeTemp = 0;
 445   sgTimeTemp2 = 0;
 446   // Registering vars more than once doesn't hurt anything.
 447   Con::addVariable("$sceneLighting::terminateLighting", TypeBool, &gTerminateLighting);
 448   Con::addVariable("$sceneLighting::lightingProgress", TypeF32, &gLightingProgress);
 449
 450   mLightingInterfaces->initInterfaces();
 451}
 452
 453SceneLighting::~SceneLighting()
 454{
 455   gLighting = NULL;
 456   gLightingProgress = 0.0f;
 457
 458   ObjectProxy ** proxyItr;
 459   for(proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++)
 460      delete *proxyItr;
 461}
 462
 463void SceneLighting::getMLName(const char* misName, const U32 missionCRC, const U32 buffSize, char* filenameBuffer)
 464{
 465   dSprintf(filenameBuffer, buffSize, "%s_%x.ml", misName, missionCRC);
 466}
 467
 468bool SceneLighting::light(BitSet32 flags)
 469{
 470   if(!mSceneManager)
 471      return(false);
 472
 473   mStartTime = Platform::getRealMilliseconds();
 474
 475   // Register static lights
 476   if (!LIGHTMGR)
 477      return false;     // This world doesn't need lighting.
 478   
 479   LIGHTMGR->registerGlobalLights(NULL,true);
 480
 481   // Notify each system factory that we are beginning to light
 482   for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end(); sitr++)
 483   {
 484      SceneLightingInterface* si = (*sitr);
 485      si->processLightingBegin();
 486   }
 487
 488   // grab all the lights
 489   mLights.clear();
 490   LIGHTMGR->getAllUnsortedLights(&mLights);
 491   LIGHTMGR->unregisterAllLights();
 492
 493   if(!mLights.size())
 494      return(false);
 495
 496   // get all the objects and create proxy's for them
 497   SimpleQueryList objects;   
 498   gClientContainer.findObjects(mLightingInterfaces->mAvailableObjectTypes, &SimpleQueryList::insertionCallback, &objects);
 499
 500   for(SceneObject ** itr = objects.mList.begin(); itr != objects.mList.end(); itr++)
 501   {
 502      ObjectProxy * proxy = NULL;
 503      SceneObject* obj = *itr;
 504      if (!obj)
 505         continue;
 506
 507      // Create the right chunk for the system 
 508      for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); 
 509         sitr != mLightingInterfaces->mAvailableSystemInterfaces.end() && proxy == NULL; sitr++)
 510      {
 511         SceneLightingInterface* si = (*sitr);
 512         proxy = si->createObjectProxy(obj, &mSceneObjects);
 513      }
 514
 515      if (proxy)
 516      {
 517         if(!proxy->calcValidation())
 518         {
 519            Con::errorf(ConsoleLogEntry::General, "Failed to calculate validation info for object.  Skipped.");
 520            delete proxy;
 521            continue;
 522         }
 523
 524         if(!proxy->loadResources())
 525         {
 526            Con::errorf(ConsoleLogEntry::General, "Failed to load resources for object.  Skipped.");
 527            delete proxy;
 528            continue;
 529         }
 530
 531         mSceneObjects.push_back(proxy);
 532      }
 533   }
 534
 535   if(!mSceneObjects.size())
 536      return(false);
 537
 538   // grab the missions crc
 539   U32 missionCRC = calcMissionCRC();
 540
 541   // remove the '.mis' extension from the mission name
 542   char misName[256];
 543   dSprintf(misName, sizeof(misName), "%s", Con::getVariable("$Client::MissionFile"));
 544   char * dot = dStrstr((const char*)misName, ".mis");
 545   if(dot)
 546      *dot = '\0';
 547
 548   // get the mission name
 549   getMLName(misName, missionCRC, 1023, mFileName);
 550
 551   // check for some persisted data, check if being forced..
 552   if(!flags.test(ForceAlways</a>|<a href="/coding/class/classscenelighting/#classscenelighting_1ae20788ea028adb489b112108d21b79c9ada7b471ecea31dd16513b9d77f7bd71f">ForceWritable))
 553   {
 554      if(loadPersistInfo(mFileName))
 555      {
 556         Con::printf(" Successfully loaded mission lighting file: '%s'", mFileName);
 557
 558         // touch this file...
 559         if(!Platform::FS::Touch(mFileName))
 560            Con::warnf("  Failed to touch file '%s'.  File may be read only.", mFileName);
 561
 562         return(false);
 563      }
 564
 565      // texture manager must have lighting complete now
 566      if(flags.test(LoadOnly))
 567      {
 568         Con::errorf(ConsoleLogEntry::General, "Failed to load mission lighting!");
 569         return(false);
 570      }
 571   }
 572
 573   // don't light if file is read-only?
 574   if(!flags.test(ForceAlways))
 575   {
 576      FileStream  stream;
 577
 578      stream.open( mFileName, Torque::FS::File::Write );
 579
 580      if(stream.getStatus() != Stream::Ok)
 581      {
 582         Con::errorf(ConsoleLogEntry::General, "SceneLighting::Light: Failed to light mission.  File '%s' cannot be written to.", mFileName);
 583         return(false);
 584      }
 585   }
 586
 587   // initialize the objects for lighting
 588   for(ObjectProxy ** proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++)
 589      (*proxyItr)->init();
 590
 591   // get things started
 592   Sim::postEvent(this, new sgSceneLightingProcessEvent(0, -1,
 593      sgSceneLightingProcessEvent::sgLightingStartEventType), Sim::getTargetTime() + 1);
 594
 595   return(true);
 596}
 597
 598void SceneLighting::completed(bool success)
 599{
 600   // process the cached lighting files
 601   processCache();
 602
 603   // Notify each system factory that we are have lit!
 604   for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end(); sitr++)
 605   {
 606      SceneLightingInterface* si = (*sitr);
 607      si->processLightingCompleted(success);
 608   }
 609
 610   if(gCompleteCallback && gCompleteCallback[0])
 611      Con::executef((const char*)gCompleteCallback);
 612
 613   dFree(gCompleteCallback);
 614   gCompleteCallback = NULL;
 615}
 616
 617//------------------------------------------------------------------------------
 618// Static access method: there can be only one SceneLighting object
 619bool SceneLighting::lightScene(const char * callback, BitSet32 flags)
 620{   
 621   if(gLighting)
 622   {
 623      Con::errorf(ConsoleLogEntry::General, "Lighting is already in progress!");
 624      return false;
 625   }
 626
 627   // register the object
 628   if(!registerObject())
 629   {
 630      AssertFatal(0, "SceneLighting:: Unable to register SceneLighting object!");
 631      Con::errorf(ConsoleLogEntry::General, "SceneLighting:: Unable to register SceneLighting object!");
 632      delete this;
 633      return(false);
 634   }
 635
 636   // could have interior resources but no instances (hey, got this far didnt we...)
 637   GameConnection * con = dynamic_cast<GameConnection*>(NetConnection::getConnectionToServer());
 638   if(!con)
 639   {
 640      Con::errorf(ConsoleLogEntry::General, "SceneLighting:: no GameConnection");
 641      return(false);
 642   }
 643   con->addObject(this);
 644
 645   // set the globals
 646   gLighting = this;
 647   gTerminateLighting = false;
 648   gLightingProgress = 0.0f;
 649   if (gCompleteCallback)
 650      dFree(gCompleteCallback);   
 651   gCompleteCallback = dStrdup(callback);
 652   gConnectionMissionCRC = con->getMissionCRC();
 653
 654   // assumes we are in the world that needs lighting...
 655   mSceneManager = gClientSceneGraph;
 656
 657   if(!light(flags))
 658   {
 659      completed(true);
 660      deleteObject();
 661      return(false);
 662   }
 663   return(true);
 664}
 665
 666bool SceneLighting::isLighting()
 667{
 668   return(bool(gLighting));
 669}
 670
 671/// adds TSStatic objects as shadow casters.
 672void SceneLighting::addStatic(ShadowVolumeBSP *shadowVolume,
 673                              SceneObject *sceneobject, LightInfo *light, S32 level)
 674{
 675   if (!sceneobject)
 676      return;
 677
 678   if(light->getType() != LightInfo::Vector)
 679      return;
 680
 681   ConcretePolyList polylist;
 682   const Box3F box;
 683   const SphereF sphere;
 684   sceneobject->buildPolyList(PLC_Collision, &polylist, box, sphere);
 685
 686   // retrieve the poly list (uses the collision mesh)...
 687   //sobj->sgAdvancedStaticOptionsData.sgBuildPolyList(sobj, &polylist);
 688
 689   S32 i, count, vertind[3];
 690   ConcretePolyList::Poly *poly;
 691
 692   count = polylist.mPolyList.size();
 693
 694   // add the polys to the shadow volume...
 695   for(i=0; i<count; i++)
 696   {
 697      poly = (ConcretePolyList::Poly *)&polylist.mPolyList[i];
 698      AssertFatal((poly->vertexCount == 3), "Hmmm... vert count is greater than 3.");
 699
 700      vertind[0] = polylist.mIndexList[poly->vertexStart];
 701      vertind[1] = polylist.mIndexList[poly->vertexStart + 1];
 702      vertind[2] = polylist.mIndexList[poly->vertexStart + 2];
 703
 704      if(mDot(PlaneF(polylist.mVertexList[vertind[0]], polylist.mVertexList[vertind[1]],
 705         polylist.mVertexList[vertind[2]]), light->getDirection()) < gParellelVectorThresh)
 706      {
 707         ShadowVolumeBSP::SVPoly *svpoly = shadowVolume->createPoly();
 708         svpoly->mWindingCount = 3;
 709
 710         svpoly->mWinding[0].set(polylist.mVertexList[vertind[0]]);
 711         svpoly->mWinding[1].set(polylist.mVertexList[vertind[1]]);
 712         svpoly->mWinding[2].set(polylist.mVertexList[vertind[2]]);
 713         svpoly->mPlane = PlaneF(svpoly->mWinding[0], svpoly->mWinding[1], svpoly->mWinding[2]);
 714         svpoly->mPlane.neg();
 715
 716         shadowVolume->buildPolyVolume(svpoly, light);
 717         shadowVolume->insertPoly(svpoly);
 718      }
 719   }
 720}
 721
 722//------------------------------------------------------------------------------
 723bool SceneLighting::verifyMissionInfo(PersistInfo::PersistChunk * chunk)
 724{
 725   PersistInfo::MissionChunk * info = dynamic_cast<PersistInfo::MissionChunk*>(chunk);
 726   if(!info)
 727      return(false);
 728
 729   PersistInfo::MissionChunk curInfo;
 730   if(!getMissionInfo(&curInfo))
 731      return(false);
 732
 733   return(curInfo.mChunkCRC == info->mChunkCRC);
 734}
 735
 736bool SceneLighting::getMissionInfo(PersistInfo::PersistChunk * chunk)
 737{
 738   PersistInfo::MissionChunk * info = dynamic_cast<PersistInfo::MissionChunk*>(chunk);
 739   if(!info)
 740      return(false);
 741
 742   info->mChunkCRC = gConnectionMissionCRC ^ PersistInfo::smFileVersion;
 743   return(true);
 744}
 745
 746//------------------------------------------------------------------------------
 747bool SceneLighting::loadPersistInfo(const char * fileName)
 748{
 749   FileStream  stream;
 750
 751   stream.open( fileName, Torque::FS::File::Read );
 752
 753   if(stream.getStatus() != Stream::Ok)
 754      return false;
 755
 756   PersistInfo persistInfo;
 757   bool success = persistInfo.read(stream);
 758   stream.close();
 759   if(!success)
 760      return(false);
 761
 762   // verify the mission chunk
 763   if(!verifyMissionInfo(persistInfo.mChunks[0]))
 764      return(false);
 765
 766   // Create the right chunk for the system    
 767   for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end(); sitr++)
 768   {
 769      SceneLightingInterface* si = (*sitr);
 770      if (!si->postProcessLoad(&persistInfo, &mSceneObjects))
 771      {
 772         return false;
 773      }
 774   }
 775
 776   if(mSceneObjects.size() != (persistInfo.mChunks.size() - 1))
 777      return(false);
 778
 779   Vector<PersistInfo::PersistChunk*> chunks;
 780
 781   // ensure that the scene objects are in the same order as the chunks
 782   //  - different instances will depend on this
 783   U32 i;
 784   for(i = 0; i < mSceneObjects.size(); i++)
 785   {
 786      // 0th chunk is the mission chunk
 787      U32 chunkIdx = i+1;
 788      if(chunkIdx >= persistInfo.mChunks.size())
 789         return(false);
 790
 791      if(!mSceneObjects[i]->isValidChunk(persistInfo.mChunks[chunkIdx]))
 792         return(false);
 793      chunks.push_back(persistInfo.mChunks[chunkIdx]);
 794   }
 795
 796   // get the objects to load in the persisted chunks
 797   for(i = 0; i < mSceneObjects.size(); i++)
 798      if(!mSceneObjects[i]->setPersistInfo(chunks[i]))
 799         return(false);
 800
 801   return(true);
 802}
 803
 804bool SceneLighting::savePersistInfo(const char * fileName)
 805{
 806   // open the file
 807   FileStream file;
 808   file.open( fileName, Torque::FS::File::Write );
 809
 810   if(file.getStatus() != Stream::Ok)
 811      return false;
 812
 813   PersistInfo persistInfo;
 814
 815   // add in the mission chunk
 816   persistInfo.mChunks.push_back(new PersistInfo::MissionChunk);
 817
 818   // get the mission info, will return false when there are 0 lights
 819   if(!getMissionInfo(persistInfo.mChunks[0]))
 820      return(false);
 821
 822   // get all the persist chunks
 823   bool bChunkFound;
 824   for(U32 i = 0; i < mSceneObjects.size(); i++)
 825   {
 826      bChunkFound = false;
 827      // Create the right chunk for the system 
 828      for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end() && !bChunkFound; sitr++)
 829      {
 830         SceneLightingInterface* si = (*sitr);
 831         PersistInfo::PersistChunk* chunk;
 832         if (si->createPersistChunkFromProxy(mSceneObjects[i], &chunk))
 833         {
 834            if (chunk)
 835            {
 836               persistInfo.mChunks.push_back(chunk);
 837               bChunkFound = true;
 838            }
 839         }
 840      }
 841
 842      // Make sure the chunk worked.
 843      if (!mSceneObjects[i]->getPersistInfo(persistInfo.mChunks.last()))
 844         return false;
 845   }
 846
 847   if(!persistInfo.write(file))
 848      return(false);
 849
 850   file.close();
 851
 852   return(true);
 853}
 854
 855struct CacheEntry {
 856   Torque::FS::FileNodeRef mFileObject;
 857   const char *mFileName;
 858
 859   CacheEntry() {
 860      mFileName = 0;
 861   };
 862};
 863
 864// object list sort methods: want list in reverse
 865static S32 QSORT_CALLBACK minSizeSort(const void * p1, const void * p2)
 866{
 867   const CacheEntry * entry1 = (const CacheEntry *)p1;
 868   const CacheEntry * entry2 = (const CacheEntry *)p2;
 869
 870   return(entry2->mFileObject->getSize() - entry1->mFileObject->getSize());
 871}
 872
 873static S32 QSORT_CALLBACK maxSizeSort(const void * p1, const void * p2)
 874{
 875   const CacheEntry * entry1 = (const CacheEntry *)p1;
 876   const CacheEntry * entry2 = (const CacheEntry *)p2;
 877
 878   return(entry1->mFileObject->getSize() - entry2->mFileObject->getSize());
 879}
 880
 881static S32 QSORT_CALLBACK lastCreatedSort(const void * p1, const void * p2)
 882{
 883   const CacheEntry * entry1 = (const CacheEntry *)p1;
 884   const CacheEntry * entry2 = (const CacheEntry *)p2;
 885
 886   FileTime create[2];
 887   FileTime modify;
 888
 889   bool ret[2];
 890
 891   ret[0] = Platform::getFileTimes(entry1->mFileName, &create[0], &modify);
 892   ret[1] = Platform::getFileTimes(entry2->mFileName, &create[1], &modify);
 893
 894   // check return values
 895   if(!ret[0] && !ret[1])
 896      return(0);
 897   if(!ret[0])
 898      return(1);
 899   if(!ret[1])
 900      return(-1);
 901
 902   return(Platform::compareFileTimes(create[1], create[0]));
 903}
 904
 905static S32 QSORT_CALLBACK lastModifiedSort(const void * p1, const void * p2)
 906{
 907   const CacheEntry * entry1 = (const CacheEntry *)p1;
 908   const CacheEntry * entry2 = (const CacheEntry *)p2;
 909
 910   FileTime create;
 911   FileTime modify[2];
 912
 913   bool ret[2];
 914
 915   ret[0] = Platform::getFileTimes(entry1->mFileName, &create, &modify[0]);
 916   ret[1] = Platform::getFileTimes(entry2->mFileName, &create, &modify[1]);
 917
 918   // check return values
 919   if(!ret[0] && !ret[1])
 920      return(0);
 921   if(!ret[0])
 922      return(1);
 923   if(!ret[1])
 924      return(-1);
 925
 926   return(Platform::compareFileTimes(modify[1], modify[0]));
 927}
 928
 929void SceneLighting::processCache()
 930{
 931   // get size in kb
 932   S32 quota = Con::getIntVariable("$sceneLighting::cacheSize", -1);
 933
 934   Vector<CacheEntry> files;
 935
 936   Vector<String> fileNames;
 937   Torque::FS::FindByPattern(Torque::Path(Platform::getMainDotCsDir()), "*.ml", true, fileNames);
 938
 939   S32 curCacheSize = 0;
 940
 941   for(S32 i = 0;i < fileNames.size();++i)
 942   {
 943      if(! Torque::FS::IsFile(fileNames[i]))
 944         continue;
 945
 946      Torque::FS::FileNodeRef fileNode = Torque::FS::GetFileNode(fileNames[i]);
 947      if(fileNode == NULL)
 948         continue;
 949
 950      if(dStrstr(fileNames[i], mFileName) == 0)
 951      {
 952         // Don't allow the current file to be removed
 953         CacheEntry entry;
 954         entry.mFileObject = fileNode;
 955         entry.mFileName = StringTable->insert(fileNames[i]);
 956         files.push_back(entry);
 957      }
 958      else
 959         curCacheSize += fileNode->getSize();
 960   }
 961
 962   // remove old files
 963   for(S32 i = files.size() - 1; i >= 0; i--)
 964   {
 965      FileStream *stream;
 966      if((stream = FileStream::createAndOpen( files[i].mFileObject->getName(), Torque::FS::File::Read )) == NULL)
 967         continue;
 968
 969      // read in the version
 970      U32 version;
 971      bool ok = (stream->read(&version) && (version == PersistInfo::smFileVersion));
 972      delete stream;
 973
 974      // ok?
 975      if(ok)
 976         continue;
 977
 978      // no sneaky names
 979      if(!dStrstr(files[i].mFileName, ".."))
 980      {
 981         Con::warnf("Removing old lighting file '%s'.", files[i].mFileName);
 982         dFileDelete(files[i].mFileName);
 983      }
 984
 985      files.pop_back();
 986   }
 987
 988   // no size restriction?
 989   if(quota == -1 || !files.size())
 990      return;
 991
 992   for(U32 i = 0; i < files.size(); i++)
 993      curCacheSize += files[i].mFileObject->getSize();
 994
 995   // need to remove?
 996   if(quota > (curCacheSize >> 10))
 997      return;
 998
 999   // sort the entries by the correct method
1000   const char * purgeMethod = Con::getVariable("$sceneLighting::purgeMethod");
1001   if(!purgeMethod)
1002      purgeMethod = "";
1003
1004   // determine the method (default to least recently used)
1005   if(!dStricmp(purgeMethod, "minSize"))
1006      dQsort(files.address(), files.size(), sizeof(CacheEntry), minSizeSort);
1007   else if(!dStricmp(purgeMethod, "maxSize"))
1008      dQsort(files.address(), files.size(), sizeof(CacheEntry), maxSizeSort);
1009   else if(!dStricmp(purgeMethod, "lastCreated"))
1010      dQsort(files.address(), files.size(), sizeof(CacheEntry), lastCreatedSort);
1011   else
1012      dQsort(files.address(), files.size(), sizeof(CacheEntry), lastModifiedSort);
1013
1014   // go through and remove the best candidate first (sorted reverse)
1015   while(((curCacheSize >> 10) > quota) && files.size())
1016   {
1017      CacheEntry& lastFile = files.last();
1018      curCacheSize -= lastFile.mFileObject->getSize();
1019
1020      // no sneaky names
1021      if (!dStrstr(lastFile.mFileName, ".."))
1022      {
1023         Con::warnf("Removing lighting file '%s'.", lastFile.mFileName);
1024         dFileDelete(lastFile.mFileName);
1025      }
1026
1027      files.pop_back();
1028   }
1029}
1030
1031static S32 QSORT_CALLBACK compareS32(const void * a, const void * b)
1032{
1033   return(*((S32 *)a) - *((S32 *)b));
1034}
1035
1036U32 SceneLighting::calcMissionCRC()
1037{
1038   // all the objects + mission chunk
1039   Vector<U32> crc;
1040
1041   // grab the object crcs
1042   for(U32 i = 0; i < mSceneObjects.size(); i++)
1043      crc.push_back( mSceneObjects[i]->mChunkCRC );
1044
1045   // grab the missions crc
1046   PersistInfo::MissionChunk curInfo;
1047   getMissionInfo(&curInfo);
1048   crc.push_back(curInfo.mChunkCRC);
1049
1050   // sort them (order may not have been preserved)
1051   dQsort(crc.address(), crc.size(), sizeof(U32), compareS32);
1052
1053#ifdef TORQUE_BIG_ENDIAN
1054   // calculateCRC operates on 8-bit chunks of memory. The memory is a vector
1055   // of U32's, and so the result will be different on big/little endian hardware.
1056   // To fix this, swap endians on the CRC's in the vector. This must be done
1057   // _after_ the qsort.
1058   for( S32 i = 0; i < crc.size(); i++ )
1059      crc[i] = endianSwap( crc[i] );
1060#endif
1061
1062   return(CRC::calculateCRC(crc.address(), sizeof(U32) * crc.size(), 0xffffffff));
1063}
1064
1065bool SceneLighting::ObjectProxy::calcValidation()
1066{
1067   mChunkCRC = getResourceCRC();
1068   if(!mChunkCRC)
1069      return(false);
1070
1071   return(true);
1072}
1073
1074bool SceneLighting::ObjectProxy::isValidChunk(PersistInfo::PersistChunk * chunk)
1075{
1076   return(chunk->mChunkCRC == mChunkCRC);
1077}
1078
1079bool SceneLighting::ObjectProxy::getPersistInfo(PersistInfo::PersistChunk * chunk)
1080{
1081   chunk->mChunkCRC = mChunkCRC;
1082   return(true);
1083}
1084
1085bool SceneLighting::ObjectProxy::setPersistInfo(PersistInfo::PersistChunk * chunk)
1086{
1087   mChunkCRC = chunk->mChunkCRC;
1088   return(true);
1089}
1090