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 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
