sceneLighting.cpp
Engine/source/lighting/common/sceneLighting.cpp
Classes:
class
Public Variables
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)
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