sceneLighting.cpp 32 KB

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