sceneLighting.cpp 31 KB

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