2
0

sceneContainer.cpp 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710
  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 "scene/sceneContainer.h"
  24. #include "collision/extrudedPolyList.h"
  25. #include "collision/earlyOutPolyList.h"
  26. #include "scene/sceneObject.h"
  27. #include "platform/profiler.h"
  28. #include "console/engineAPI.h"
  29. #include "math/util/frustum.h"
  30. // [rene, 02-Mar-11]
  31. // - *Loads* of copy&paste sin in this file (among its many other sins); all the findObjectXXX methods
  32. // are trivial permutations of the same snippet of copy&pasted code
  33. // - FindCallback should return a bool so it's possible to use the findObjectXXX methods to look
  34. // for the first object matching a certain criteria
  35. SceneContainer gServerContainer;
  36. SceneContainer gClientContainer;
  37. const U32 SceneContainer::csmNumBins = 16;
  38. const F32 SceneContainer::csmBinSize = 64;
  39. const F32 SceneContainer::csmTotalBinSize = SceneContainer::csmBinSize * SceneContainer::csmNumBins;
  40. const U32 SceneContainer::csmRefPoolBlockSize = 4096;
  41. // Statics used by buildPolyList methods
  42. static AbstractPolyList* sPolyList;
  43. static SphereF sBoundingSphere;
  44. static Box3F sBoundingBox;
  45. //=============================================================================
  46. // SceneContainer::Link.
  47. //=============================================================================
  48. //-----------------------------------------------------------------------------
  49. SceneContainer::Link::Link()
  50. {
  51. mNext = mPrev = this;
  52. }
  53. //-----------------------------------------------------------------------------
  54. void SceneContainer::Link::unlink()
  55. {
  56. mNext->mPrev = mPrev;
  57. mPrev->mNext = mNext;
  58. mNext = mPrev = this;
  59. }
  60. //-----------------------------------------------------------------------------
  61. void SceneContainer::Link::linkAfter(SceneContainer::Link* ptr)
  62. {
  63. mNext = ptr->mNext;
  64. mNext->mPrev = this;
  65. mPrev = ptr;
  66. mPrev->mNext = this;
  67. }
  68. //=============================================================================
  69. // SceneContainer.
  70. //=============================================================================
  71. //-----------------------------------------------------------------------------
  72. SceneContainer::SceneContainer()
  73. {
  74. mSearchInProgress = false;
  75. mCurrSeqKey = 0;
  76. mEnd.mNext = mEnd.mPrev = &mStart;
  77. mStart.mNext = mStart.mPrev = &mEnd;
  78. mBinArray = new SceneObjectRef[csmNumBins * csmNumBins];
  79. for (U32 i = 0; i < csmNumBins; i++)
  80. {
  81. U32 base = i * csmNumBins;
  82. for (U32 j = 0; j < csmNumBins; j++)
  83. {
  84. mBinArray[base + j].object = NULL;
  85. mBinArray[base + j].nextInBin = NULL;
  86. mBinArray[base + j].prevInBin = NULL;
  87. mBinArray[base + j].nextInObj = NULL;
  88. }
  89. }
  90. mOverflowBin.object = NULL;
  91. mOverflowBin.nextInBin = NULL;
  92. mOverflowBin.prevInBin = NULL;
  93. mOverflowBin.nextInObj = NULL;
  94. VECTOR_SET_ASSOCIATION( mRefPoolBlocks );
  95. VECTOR_SET_ASSOCIATION( mSearchList );
  96. VECTOR_SET_ASSOCIATION( mWaterAndZones );
  97. VECTOR_SET_ASSOCIATION( mTerrains );
  98. mFreeRefPool = NULL;
  99. addRefPoolBlock();
  100. cleanupSearchVectors();
  101. }
  102. //-----------------------------------------------------------------------------
  103. SceneContainer::~SceneContainer()
  104. {
  105. delete[] mBinArray;
  106. for (U32 i = 0; i < mRefPoolBlocks.size(); i++)
  107. {
  108. SceneObjectRef* pool = mRefPoolBlocks[i];
  109. for (U32 j = 0; j < csmRefPoolBlockSize; j++)
  110. {
  111. // Depressingly, this can give weird results if its pointing at bad memory...
  112. if(pool[j].object != NULL)
  113. Con::warnf("Error, a %s (%x) isn't properly out of the bins!", pool[j].object->getClassName(), pool[j].object);
  114. // If you're getting this it means that an object created didn't
  115. // remove itself from its container before we destroyed the
  116. // container. Typically you get this behavior from particle
  117. // emitters, as they try to hang around until all their particles
  118. // die. In general it's benign, though if you get it for things
  119. // that aren't particle emitters it can be a bad sign!
  120. }
  121. delete [] pool;
  122. }
  123. mFreeRefPool = NULL;
  124. cleanupSearchVectors();
  125. }
  126. //-----------------------------------------------------------------------------
  127. bool SceneContainer::addObject(SceneObject* obj)
  128. {
  129. AssertFatal(obj->mContainer == NULL, "Adding already added object.");
  130. obj->mContainer = this;
  131. obj->linkAfter(&mStart);
  132. insertIntoBins(obj);
  133. // Also insert water and physical zone types into the special vector.
  134. if ( obj->getTypeMask() & ( WaterObjectType | PhysicalZoneObjectType ) )
  135. mWaterAndZones.push_back(obj);
  136. if( obj->getTypeMask() & TerrainObjectType )
  137. mTerrains.push_back( obj );
  138. return true;
  139. }
  140. //-----------------------------------------------------------------------------
  141. bool SceneContainer::removeObject(SceneObject* obj)
  142. {
  143. AssertFatal(obj->mContainer == this, "Trying to remove from wrong container.");
  144. removeFromBins(obj);
  145. // Remove water and physical zone types from the special vector.
  146. if ( obj->getTypeMask() & ( WaterObjectType | PhysicalZoneObjectType ) )
  147. {
  148. Vector<SceneObject*>::iterator iter = T3D::find( mWaterAndZones.begin(), mWaterAndZones.end(), obj );
  149. if( iter != mTerrains.end() )
  150. mWaterAndZones.erase_fast(iter);
  151. }
  152. // Remove terrain objects from special vector.
  153. if( obj->getTypeMask() & TerrainObjectType )
  154. {
  155. Vector< SceneObject* >::iterator iter = T3D::find( mTerrains.begin(), mTerrains.end(), obj );
  156. if( iter != mTerrains.end() )
  157. mTerrains.erase_fast(iter);
  158. }
  159. obj->mContainer = 0;
  160. obj->unlink();
  161. return true;
  162. }
  163. //-----------------------------------------------------------------------------
  164. void SceneContainer::addRefPoolBlock()
  165. {
  166. mRefPoolBlocks.push_back(new SceneObjectRef[csmRefPoolBlockSize]);
  167. for (U32 i = 0; i < csmRefPoolBlockSize-1; i++)
  168. {
  169. mRefPoolBlocks.last()[i].object = NULL;
  170. mRefPoolBlocks.last()[i].prevInBin = NULL;
  171. mRefPoolBlocks.last()[i].nextInBin = NULL;
  172. mRefPoolBlocks.last()[i].nextInObj = &(mRefPoolBlocks.last()[i+1]);
  173. }
  174. mRefPoolBlocks.last()[csmRefPoolBlockSize-1].object = NULL;
  175. mRefPoolBlocks.last()[csmRefPoolBlockSize-1].prevInBin = NULL;
  176. mRefPoolBlocks.last()[csmRefPoolBlockSize-1].nextInBin = NULL;
  177. mRefPoolBlocks.last()[csmRefPoolBlockSize-1].nextInObj = mFreeRefPool;
  178. mFreeRefPool = &(mRefPoolBlocks.last()[0]);
  179. }
  180. //-----------------------------------------------------------------------------
  181. void SceneContainer::insertIntoBins(SceneObject* obj)
  182. {
  183. AssertFatal(obj != NULL, "No object?");
  184. AssertFatal(obj->mBinRefHead == NULL, "Error, already have a bin chain!");
  185. // The first thing we do is find which bins are covered in x and y...
  186. const Box3F* pWBox = &obj->getWorldBox();
  187. U32 minX, maxX, minY, maxY;
  188. getBinRange(pWBox->minExtents.x, pWBox->maxExtents.x, minX, maxX);
  189. getBinRange(pWBox->minExtents.y, pWBox->maxExtents.y, minY, maxY);
  190. // Store the current regions for later queries
  191. obj->mBinMinX = minX;
  192. obj->mBinMaxX = maxX;
  193. obj->mBinMinY = minY;
  194. obj->mBinMaxY = maxY;
  195. // For huge objects, dump them into the overflow bin. Otherwise, everything
  196. // goes into the grid...
  197. if (!obj->isGlobalBounds() && ((maxX - minX + 1) < csmNumBins || (maxY - minY + 1) < csmNumBins))
  198. {
  199. SceneObjectRef** pCurrInsert = &obj->mBinRefHead;
  200. for (U32 i = minY; i <= maxY; i++)
  201. {
  202. U32 insertY = i % csmNumBins;
  203. U32 base = insertY * csmNumBins;
  204. for (U32 j = minX; j <= maxX; j++)
  205. {
  206. U32 insertX = j % csmNumBins;
  207. SceneObjectRef* ref = allocateObjectRef();
  208. ref->object = obj;
  209. ref->nextInBin = mBinArray[base + insertX].nextInBin;
  210. ref->prevInBin = &mBinArray[base + insertX];
  211. ref->nextInObj = NULL;
  212. if (mBinArray[base + insertX].nextInBin)
  213. mBinArray[base + insertX].nextInBin->prevInBin = ref;
  214. mBinArray[base + insertX].nextInBin = ref;
  215. *pCurrInsert = ref;
  216. pCurrInsert = &ref->nextInObj;
  217. }
  218. }
  219. }
  220. else
  221. {
  222. SceneObjectRef* ref = allocateObjectRef();
  223. ref->object = obj;
  224. ref->nextInBin = mOverflowBin.nextInBin;
  225. ref->prevInBin = &mOverflowBin;
  226. ref->nextInObj = NULL;
  227. if (mOverflowBin.nextInBin)
  228. mOverflowBin.nextInBin->prevInBin = ref;
  229. mOverflowBin.nextInBin = ref;
  230. obj->mBinRefHead = ref;
  231. }
  232. }
  233. //-----------------------------------------------------------------------------
  234. void SceneContainer::insertIntoBins(SceneObject* obj,
  235. U32 minX, U32 maxX,
  236. U32 minY, U32 maxY)
  237. {
  238. PROFILE_START(InsertBins);
  239. AssertFatal(obj != NULL, "No object?");
  240. AssertFatal(obj->mBinRefHead == NULL, "Error, already have a bin chain!");
  241. // Store the current regions for later queries
  242. obj->mBinMinX = minX;
  243. obj->mBinMaxX = maxX;
  244. obj->mBinMinY = minY;
  245. obj->mBinMaxY = maxY;
  246. // For huge objects, dump them into the overflow bin. Otherwise, everything
  247. // goes into the grid...
  248. //
  249. if ((maxX - minX + 1) < csmNumBins || ((maxY - minY + 1) < csmNumBins && !obj->isGlobalBounds()))
  250. {
  251. SceneObjectRef** pCurrInsert = &obj->mBinRefHead;
  252. for (U32 i = minY; i <= maxY; i++)
  253. {
  254. U32 insertY = i % csmNumBins;
  255. U32 base = insertY * csmNumBins;
  256. for (U32 j = minX; j <= maxX; j++)
  257. {
  258. U32 insertX = j % csmNumBins;
  259. SceneObjectRef* ref = allocateObjectRef();
  260. ref->object = obj;
  261. ref->nextInBin = mBinArray[base + insertX].nextInBin;
  262. ref->prevInBin = &mBinArray[base + insertX];
  263. ref->nextInObj = NULL;
  264. if (mBinArray[base + insertX].nextInBin)
  265. mBinArray[base + insertX].nextInBin->prevInBin = ref;
  266. mBinArray[base + insertX].nextInBin = ref;
  267. *pCurrInsert = ref;
  268. pCurrInsert = &ref->nextInObj;
  269. }
  270. }
  271. }
  272. else
  273. {
  274. SceneObjectRef* ref = allocateObjectRef();
  275. ref->object = obj;
  276. ref->nextInBin = mOverflowBin.nextInBin;
  277. ref->prevInBin = &mOverflowBin;
  278. ref->nextInObj = NULL;
  279. if (mOverflowBin.nextInBin)
  280. mOverflowBin.nextInBin->prevInBin = ref;
  281. mOverflowBin.nextInBin = ref;
  282. obj->mBinRefHead = ref;
  283. }
  284. PROFILE_END();
  285. }
  286. //-----------------------------------------------------------------------------
  287. void SceneContainer::removeFromBins(SceneObject* obj)
  288. {
  289. PROFILE_START(RemoveFromBins);
  290. AssertFatal(obj != NULL, "No object?");
  291. SceneObjectRef* chain = obj->mBinRefHead;
  292. obj->mBinRefHead = NULL;
  293. while (chain)
  294. {
  295. SceneObjectRef* trash = chain;
  296. chain = chain->nextInObj;
  297. AssertFatal(trash->prevInBin != NULL, "Error, must have a previous entry in the bin!");
  298. if (trash->nextInBin)
  299. trash->nextInBin->prevInBin = trash->prevInBin;
  300. trash->prevInBin->nextInBin = trash->nextInBin;
  301. freeObjectRef(trash);
  302. }
  303. PROFILE_END();
  304. }
  305. //-----------------------------------------------------------------------------
  306. void SceneContainer::checkBins(SceneObject* obj)
  307. {
  308. AssertFatal(obj != NULL, "No object?");
  309. PROFILE_START(CheckBins);
  310. if (obj->mBinRefHead == NULL)
  311. {
  312. insertIntoBins(obj);
  313. PROFILE_END();
  314. return;
  315. }
  316. // Otherwise, the object is already in the bins. Let's see if it has strayed out of
  317. // the bins that it's currently in...
  318. const Box3F* pWBox = &obj->getWorldBox();
  319. U32 minX, maxX, minY, maxY;
  320. getBinRange(pWBox->minExtents.x, pWBox->maxExtents.x, minX, maxX);
  321. getBinRange(pWBox->minExtents.y, pWBox->maxExtents.y, minY, maxY);
  322. if (obj->mBinMinX != minX || obj->mBinMaxX != maxX ||
  323. obj->mBinMinY != minY || obj->mBinMaxY != maxY)
  324. {
  325. // We have to rebin the object
  326. removeFromBins(obj);
  327. insertIntoBins(obj, minX, maxX, minY, maxY);
  328. }
  329. PROFILE_END();
  330. }
  331. //-----------------------------------------------------------------------------
  332. void SceneContainer::findObjects(const Box3F& box, U32 mask, FindCallback callback, void *key)
  333. {
  334. PROFILE_SCOPE(ContainerFindObjects_Box);
  335. // If we're searching for just water, just physical zones, or
  336. // just water and physical zones then use the optimized path.
  337. if ( mask == WaterObjectType ||
  338. mask == PhysicalZoneObjectType ||
  339. mask == (WaterObjectType|PhysicalZoneObjectType) )
  340. {
  341. _findSpecialObjects( mWaterAndZones, box, mask, callback, key );
  342. return;
  343. }
  344. else if( mask == TerrainObjectType )
  345. {
  346. _findSpecialObjects( mTerrains, box, mask, callback, key );
  347. return;
  348. }
  349. AssertFatal( !mSearchInProgress, "SceneContainer::findObjects - Container queries are not re-entrant" );
  350. mSearchInProgress = true;
  351. U32 minX, maxX, minY, maxY;
  352. getBinRange(box.minExtents.x, box.maxExtents.x, minX, maxX);
  353. getBinRange(box.minExtents.y, box.maxExtents.y, minY, maxY);
  354. mCurrSeqKey++;
  355. for (U32 i = minY; i <= maxY; i++)
  356. {
  357. U32 insertY = i % csmNumBins;
  358. U32 base = insertY * csmNumBins;
  359. for (U32 j = minX; j <= maxX; j++)
  360. {
  361. U32 insertX = j % csmNumBins;
  362. SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
  363. while (chain)
  364. {
  365. if (chain->object->getContainerSeqKey() != mCurrSeqKey)
  366. {
  367. chain->object->setContainerSeqKey(mCurrSeqKey);
  368. if ((chain->object->getTypeMask() & mask) != 0 &&
  369. chain->object->isCollisionEnabled())
  370. {
  371. if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
  372. {
  373. (*callback)(chain->object,key);
  374. }
  375. }
  376. }
  377. chain = chain->nextInBin;
  378. }
  379. }
  380. }
  381. SceneObjectRef* chain = mOverflowBin.nextInBin;
  382. while (chain)
  383. {
  384. if (chain->object->getContainerSeqKey() != mCurrSeqKey)
  385. {
  386. chain->object->setContainerSeqKey(mCurrSeqKey);
  387. if ((chain->object->getTypeMask() & mask) != 0 &&
  388. chain->object->isCollisionEnabled())
  389. {
  390. if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
  391. {
  392. (*callback)(chain->object,key);
  393. }
  394. }
  395. }
  396. chain = chain->nextInBin;
  397. }
  398. mSearchInProgress = false;
  399. }
  400. //-----------------------------------------------------------------------------
  401. void SceneContainer::findObjects( const Frustum &frustum, U32 mask, FindCallback callback, void *key )
  402. {
  403. PROFILE_SCOPE(ContainerFindObjects_Frustum);
  404. Box3F searchBox = frustum.getBounds();
  405. if ( mask == WaterObjectType ||
  406. mask == PhysicalZoneObjectType ||
  407. mask == (WaterObjectType|PhysicalZoneObjectType) )
  408. {
  409. _findSpecialObjects( mWaterAndZones, searchBox, mask, callback, key );
  410. return;
  411. }
  412. else if( mask == TerrainObjectType )
  413. {
  414. _findSpecialObjects( mTerrains, searchBox, mask, callback, key );
  415. return;
  416. }
  417. AssertFatal( !mSearchInProgress, "SceneContainer::findObjects - Container queries are not re-entrant" );
  418. mSearchInProgress = true;
  419. U32 minX, maxX, minY, maxY;
  420. getBinRange(searchBox.minExtents.x, searchBox.maxExtents.x, minX, maxX);
  421. getBinRange(searchBox.minExtents.y, searchBox.maxExtents.y, minY, maxY);
  422. mCurrSeqKey++;
  423. for (U32 i = minY; i <= maxY; i++)
  424. {
  425. U32 insertY = i % csmNumBins;
  426. U32 base = insertY * csmNumBins;
  427. for (U32 j = minX; j <= maxX; j++)
  428. {
  429. U32 insertX = j % csmNumBins;
  430. SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
  431. while (chain)
  432. {
  433. SceneObject *object = chain->object;
  434. if (object->getContainerSeqKey() != mCurrSeqKey)
  435. {
  436. object->setContainerSeqKey(mCurrSeqKey);
  437. if ((object->getTypeMask() & mask) != 0 &&
  438. object->isCollisionEnabled())
  439. {
  440. const Box3F &worldBox = object->getWorldBox();
  441. if ( object->isGlobalBounds() || worldBox.isOverlapped(searchBox) )
  442. {
  443. if ( !frustum.isCulled( worldBox ) )
  444. (*callback)(chain->object,key);
  445. }
  446. }
  447. }
  448. chain = chain->nextInBin;
  449. }
  450. }
  451. }
  452. SceneObjectRef* chain = mOverflowBin.nextInBin;
  453. while (chain)
  454. {
  455. SceneObject *object = chain->object;
  456. if (object->getContainerSeqKey() != mCurrSeqKey)
  457. {
  458. object->setContainerSeqKey(mCurrSeqKey);
  459. if ((object->getTypeMask() & mask) != 0 &&
  460. object->isCollisionEnabled())
  461. {
  462. const Box3F &worldBox = object->getWorldBox();
  463. if ( object->isGlobalBounds() || worldBox.isOverlapped(searchBox) )
  464. {
  465. if ( !frustum.isCulled( worldBox ) )
  466. (*callback)(object,key);
  467. }
  468. }
  469. }
  470. chain = chain->nextInBin;
  471. }
  472. mSearchInProgress = false;
  473. }
  474. //-----------------------------------------------------------------------------
  475. void SceneContainer::polyhedronFindObjects(const Polyhedron& polyhedron, U32 mask, FindCallback callback, void *key)
  476. {
  477. PROFILE_SCOPE(ContainerFindObjects_polyhedron);
  478. U32 i;
  479. Box3F box;
  480. box.minExtents.set(1e9, 1e9, 1e9);
  481. box.maxExtents.set(-1e9, -1e9, -1e9);
  482. for (i = 0; i < polyhedron.mPointList.size(); i++)
  483. {
  484. box.minExtents.setMin(polyhedron.mPointList[i]);
  485. box.maxExtents.setMax(polyhedron.mPointList[i]);
  486. }
  487. if ( mask == WaterObjectType ||
  488. mask == PhysicalZoneObjectType ||
  489. mask == (WaterObjectType|PhysicalZoneObjectType) )
  490. {
  491. _findSpecialObjects( mWaterAndZones, box, mask, callback, key );
  492. return;
  493. }
  494. else if( mask == TerrainObjectType )
  495. {
  496. _findSpecialObjects( mTerrains, mask, callback, key );
  497. return;
  498. }
  499. AssertFatal( !mSearchInProgress, "SceneContainer::polyhedronFindObjects - Container queries are not re-entrant" );
  500. mSearchInProgress = true;
  501. U32 minX, maxX, minY, maxY;
  502. getBinRange(box.minExtents.x, box.maxExtents.x, minX, maxX);
  503. getBinRange(box.minExtents.y, box.maxExtents.y, minY, maxY);
  504. mCurrSeqKey++;
  505. for (i = minY; i <= maxY; i++)
  506. {
  507. U32 insertY = i % csmNumBins;
  508. U32 base = insertY * csmNumBins;
  509. for (U32 j = minX; j <= maxX; j++)
  510. {
  511. U32 insertX = j % csmNumBins;
  512. SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
  513. while (chain)
  514. {
  515. if (chain->object->getContainerSeqKey() != mCurrSeqKey)
  516. {
  517. chain->object->setContainerSeqKey(mCurrSeqKey);
  518. if ((chain->object->getTypeMask() & mask) != 0 &&
  519. chain->object->isCollisionEnabled())
  520. {
  521. if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
  522. {
  523. (*callback)(chain->object,key);
  524. }
  525. }
  526. }
  527. chain = chain->nextInBin;
  528. }
  529. }
  530. }
  531. SceneObjectRef* chain = mOverflowBin.nextInBin;
  532. while (chain)
  533. {
  534. if (chain->object->getContainerSeqKey() != mCurrSeqKey)
  535. {
  536. chain->object->setContainerSeqKey(mCurrSeqKey);
  537. if ((chain->object->getTypeMask() & mask) != 0 &&
  538. chain->object->isCollisionEnabled())
  539. {
  540. if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
  541. {
  542. (*callback)(chain->object,key);
  543. }
  544. }
  545. }
  546. chain = chain->nextInBin;
  547. }
  548. mSearchInProgress = false;
  549. }
  550. //-----------------------------------------------------------------------------
  551. void SceneContainer::findObjectList( const Box3F& searchBox, U32 mask, Vector<SceneObject*> *outFound )
  552. {
  553. PROFILE_SCOPE( Container_FindObjectList_Box );
  554. AssertFatal( !mSearchInProgress, "SceneContainer::findObjectList - Container queries are not re-entrant" );
  555. mSearchInProgress = true;
  556. // TODO: Optimize for water and zones?
  557. U32 minX, maxX, minY, maxY;
  558. getBinRange(searchBox.minExtents.x, searchBox.maxExtents.x, minX, maxX);
  559. getBinRange(searchBox.minExtents.y, searchBox.maxExtents.y, minY, maxY);
  560. mCurrSeqKey++;
  561. for (U32 i = minY; i <= maxY; i++)
  562. {
  563. U32 insertY = i % csmNumBins;
  564. U32 base = insertY * csmNumBins;
  565. for (U32 j = minX; j <= maxX; j++)
  566. {
  567. U32 insertX = j % csmNumBins;
  568. SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
  569. while (chain)
  570. {
  571. SceneObject *object = chain->object;
  572. if (object->getContainerSeqKey() != mCurrSeqKey)
  573. {
  574. object->setContainerSeqKey(mCurrSeqKey);
  575. if ((object->getTypeMask() & mask) != 0 &&
  576. object->isCollisionEnabled())
  577. {
  578. const Box3F &worldBox = object->getWorldBox();
  579. if ( object->isGlobalBounds() || worldBox.isOverlapped( searchBox ) )
  580. {
  581. outFound->push_back( object );
  582. }
  583. }
  584. }
  585. chain = chain->nextInBin;
  586. }
  587. }
  588. }
  589. SceneObjectRef* chain = mOverflowBin.nextInBin;
  590. while (chain)
  591. {
  592. SceneObject *object = chain->object;
  593. if (object->getContainerSeqKey() != mCurrSeqKey)
  594. {
  595. object->setContainerSeqKey(mCurrSeqKey);
  596. if ((object->getTypeMask() & mask) != 0 &&
  597. object->isCollisionEnabled())
  598. {
  599. const Box3F &worldBox = object->getWorldBox();
  600. if ( object->isGlobalBounds() || worldBox.isOverlapped( searchBox ) )
  601. {
  602. outFound->push_back( object );
  603. }
  604. }
  605. }
  606. chain = chain->nextInBin;
  607. }
  608. mSearchInProgress = false;
  609. }
  610. //-----------------------------------------------------------------------------
  611. void SceneContainer::findObjectList( const Frustum &frustum, U32 mask, Vector<SceneObject*> *outFound )
  612. {
  613. PROFILE_SCOPE( Container_FindObjectList_Frustum );
  614. // Do a box find first.
  615. findObjectList( frustum.getBounds(), mask, outFound );
  616. // Now do the frustum testing.
  617. for ( U32 i=0; i < outFound->size(); )
  618. {
  619. const Box3F &worldBox = (*outFound)[i]->getWorldBox();
  620. if ( frustum.isCulled( worldBox ) )
  621. outFound->erase_fast( i );
  622. else
  623. i++;
  624. }
  625. }
  626. //-----------------------------------------------------------------------------
  627. void SceneContainer::findObjectList( U32 mask, Vector<SceneObject*> *outFound )
  628. {
  629. for ( Link* itr = mStart.mNext; itr != &mEnd; itr = itr->mNext)
  630. {
  631. SceneObject* ptr = static_cast<SceneObject*>( itr );
  632. if ( ( ptr->getTypeMask() & mask ) != 0 )
  633. outFound->push_back( ptr );
  634. }
  635. }
  636. //-----------------------------------------------------------------------------
  637. void SceneContainer::findObjects( U32 mask, FindCallback callback, void *key )
  638. {
  639. for (Link* itr = mStart.mNext; itr != &mEnd; itr = itr->mNext) {
  640. SceneObject* ptr = static_cast<SceneObject*>(itr);
  641. if ((ptr->getTypeMask() & mask) != 0 && !ptr->mCollisionCount)
  642. (*callback)(ptr,key);
  643. }
  644. }
  645. //-----------------------------------------------------------------------------
  646. void SceneContainer::_findSpecialObjects( const Vector< SceneObject* >& vector, U32 mask, FindCallback callback, void *key )
  647. {
  648. PROFILE_SCOPE( Container_findSpecialObjects );
  649. Vector<SceneObject*>::const_iterator iter = vector.begin();
  650. for ( ; iter != vector.end(); iter++ )
  651. {
  652. if ( (*iter)->getTypeMask() & mask )
  653. callback( *iter, key );
  654. }
  655. }
  656. //-----------------------------------------------------------------------------
  657. void SceneContainer::_findSpecialObjects( const Vector< SceneObject* >& vector, const Box3F &box, U32 mask, FindCallback callback, void *key )
  658. {
  659. PROFILE_SCOPE( Container_findSpecialObjects_Box );
  660. Vector<SceneObject*>::const_iterator iter = vector.begin();
  661. for ( ; iter != vector.end(); iter++ )
  662. {
  663. SceneObject *pObj = *iter;
  664. if ( pObj->getTypeMask() & mask &&
  665. ( pObj->isGlobalBounds() || pObj->getWorldBox().isOverlapped(box) ) )
  666. {
  667. callback( pObj, key );
  668. }
  669. }
  670. }
  671. //-----------------------------------------------------------------------------
  672. bool SceneContainer::castRay( const Point3F& start, const Point3F& end, U32 mask, RayInfo* info, CastRayCallback callback )
  673. {
  674. AssertFatal( info->userData == NULL, "SceneContainer::castRay - RayInfo->userData cannot be used here!" );
  675. PROFILE_START( SceneContainer_CastRay );
  676. bool result = _castRay( CollisionGeometry, start, end, mask, info, callback );
  677. PROFILE_END();
  678. return result;
  679. }
  680. //-----------------------------------------------------------------------------
  681. bool SceneContainer::castRayRendered( const Point3F& start, const Point3F& end, U32 mask, RayInfo* info, CastRayCallback callback )
  682. {
  683. AssertFatal( info->userData == NULL, "SceneContainer::castRayRendered - RayInfo->userData cannot be used here!" );
  684. PROFILE_START( SceneContainer_CastRayRendered );
  685. bool result = _castRay( RenderedGeometry, start, end, mask, info, callback );
  686. PROFILE_END();
  687. return result;
  688. }
  689. //-----------------------------------------------------------------------------
  690. // DMMNOTE: There are still some optimizations to be done here. In particular:
  691. // - After checking the overflow bin, we can potentially shorten the line
  692. // that we rasterize against the grid if there is a collision with say,
  693. // the terrain.
  694. // - The optimal grid size isn't necessarily what we have set here. possibly
  695. // a resolution of 16 meters would give better results
  696. // - The line rasterizer is pretty lame. Unfortunately we can't use a
  697. // simple bres. here, since we need to check every grid element that the line
  698. // passes through, which bres does _not_ do for us. Possibly there's a
  699. // rasterizer for anti-aliased lines that will serve better than what
  700. // we have below.
  701. bool SceneContainer::_castRay( U32 type, const Point3F& start, const Point3F& end, U32 mask, RayInfo* info, CastRayCallback callback )
  702. {
  703. AssertFatal( !mSearchInProgress, "SceneContainer::_castRay - Container queries are not re-entrant" );
  704. mSearchInProgress = true;
  705. F32 currentT = 2.0;
  706. mCurrSeqKey++;
  707. SceneObjectRef* overflowChain = mOverflowBin.nextInBin;
  708. while (overflowChain)
  709. {
  710. SceneObject* ptr = overflowChain->object;
  711. if (ptr->getContainerSeqKey() != mCurrSeqKey)
  712. {
  713. ptr->setContainerSeqKey(mCurrSeqKey);
  714. // In the overflow bin, the world box is always going to intersect the line,
  715. // so we can omit that test...
  716. if ((ptr->getTypeMask() & mask) != 0 &&
  717. ptr->isCollisionEnabled() == true)
  718. {
  719. Point3F xformedStart, xformedEnd;
  720. ptr->mWorldToObj.mulP(start, &xformedStart);
  721. ptr->mWorldToObj.mulP(end, &xformedEnd);
  722. xformedStart.convolveInverse(ptr->mObjScale);
  723. xformedEnd.convolveInverse(ptr->mObjScale);
  724. RayInfo ri;
  725. ri.generateTexCoord = info->generateTexCoord;
  726. bool result = false;
  727. if (type == CollisionGeometry)
  728. result = ptr->castRay(xformedStart, xformedEnd, &ri);
  729. else if (type == RenderedGeometry)
  730. result = ptr->castRayRendered(xformedStart, xformedEnd, &ri);
  731. if (result)
  732. {
  733. if( ri.t < currentT && ( !callback || callback( &ri ) ) )
  734. {
  735. *info = ri;
  736. info->point.interpolate(start, end, info->t);
  737. currentT = ri.t;
  738. info->distance = (start - info->point).len();
  739. }
  740. }
  741. }
  742. }
  743. overflowChain = overflowChain->nextInBin;
  744. }
  745. // These are just for rasterizing the line against the grid. We want the x coord
  746. // of the start to be <= the x coord of the end
  747. Point3F normalStart, normalEnd;
  748. if (start.x <= end.x)
  749. {
  750. normalStart = start;
  751. normalEnd = end;
  752. }
  753. else
  754. {
  755. normalStart = end;
  756. normalEnd = start;
  757. }
  758. // Ok, let's scan the grids. The simplest way to do this will be to scan across in
  759. // x, finding the y range for each affected bin...
  760. U32 minX, maxX;
  761. U32 minY, maxY;
  762. //if (normalStart.x == normalEnd.x)
  763. // Con::printf("X start = %g, end = %g", normalStart.x, normalEnd.x);
  764. getBinRange(normalStart.x, normalEnd.x, minX, maxX);
  765. getBinRange(getMin(normalStart.y, normalEnd.y),
  766. getMax(normalStart.y, normalEnd.y), minY, maxY);
  767. //if (normalStart.x == normalEnd.x && minX != maxX)
  768. // Con::printf("X min = %d, max = %d", minX, maxX);
  769. //if (normalStart.y == normalEnd.y && minY != maxY)
  770. // Con::printf("Y min = %d, max = %d", minY, maxY);
  771. // We'll optimize the case that the line is contained in one bin row or column, which
  772. // will be quite a few lines. No sense doing more work than we have to...
  773. //
  774. if ((mFabs(normalStart.x - normalEnd.x) < csmTotalBinSize && minX == maxX) ||
  775. (mFabs(normalStart.y - normalEnd.y) < csmTotalBinSize && minY == maxY))
  776. {
  777. U32 count;
  778. U32 incX, incY;
  779. if (minX == maxX)
  780. {
  781. count = maxY - minY + 1;
  782. incX = 0;
  783. incY = 1;
  784. }
  785. else
  786. {
  787. count = maxX - minX + 1;
  788. incX = 1;
  789. incY = 0;
  790. }
  791. U32 x = minX;
  792. U32 y = minY;
  793. for (U32 i = 0; i < count; i++)
  794. {
  795. U32 checkX = x % csmNumBins;
  796. U32 checkY = y % csmNumBins;
  797. SceneObjectRef* chain = mBinArray[(checkY * csmNumBins) + checkX].nextInBin;
  798. while (chain)
  799. {
  800. SceneObject* ptr = chain->object;
  801. if (ptr->getContainerSeqKey() != mCurrSeqKey)
  802. {
  803. ptr->setContainerSeqKey(mCurrSeqKey);
  804. if ((ptr->getTypeMask() & mask) != 0 &&
  805. ptr->isCollisionEnabled() == true)
  806. {
  807. if (ptr->getWorldBox().collideLine(start, end) || chain->object->isGlobalBounds())
  808. {
  809. Point3F xformedStart, xformedEnd;
  810. ptr->mWorldToObj.mulP(start, &xformedStart);
  811. ptr->mWorldToObj.mulP(end, &xformedEnd);
  812. xformedStart.convolveInverse(ptr->mObjScale);
  813. xformedEnd.convolveInverse(ptr->mObjScale);
  814. RayInfo ri;
  815. ri.generateTexCoord = info->generateTexCoord;
  816. bool result = false;
  817. if (type == CollisionGeometry)
  818. result = ptr->castRay(xformedStart, xformedEnd, &ri);
  819. else if (type == RenderedGeometry)
  820. result = ptr->castRayRendered(xformedStart, xformedEnd, &ri);
  821. if (result)
  822. {
  823. if( ri.t < currentT && ( !callback || callback( &ri ) ) )
  824. {
  825. *info = ri;
  826. info->point.interpolate(start, end, info->t);
  827. currentT = ri.t;
  828. info->distance = (start - info->point).len();
  829. }
  830. }
  831. }
  832. }
  833. }
  834. chain = chain->nextInBin;
  835. }
  836. x += incX;
  837. y += incY;
  838. }
  839. }
  840. else
  841. {
  842. // Oh well, let's earn our keep. We know that after the above conditional, we're
  843. // going to cross at least one boundary, so that simplifies our job...
  844. F32 currStartX = normalStart.x;
  845. AssertFatal(currStartX != normalEnd.x, "This is going to cause problems in SceneContainer::castRay");
  846. if(mIsNaN_F(currStartX))
  847. {
  848. PROFILE_END();
  849. return false;
  850. }
  851. while (currStartX != normalEnd.x)
  852. {
  853. F32 currEndX = getMin(currStartX + csmTotalBinSize, normalEnd.x);
  854. F32 currStartT = (currStartX - normalStart.x) / (normalEnd.x - normalStart.x);
  855. F32 currEndT = (currEndX - normalStart.x) / (normalEnd.x - normalStart.x);
  856. F32 y1 = normalStart.y + (normalEnd.y - normalStart.y) * currStartT;
  857. F32 y2 = normalStart.y + (normalEnd.y - normalStart.y) * currEndT;
  858. U32 subMinX, subMaxX;
  859. getBinRange(currStartX, currEndX, subMinX, subMaxX);
  860. F32 subStartX = currStartX;
  861. F32 subEndX = currStartX;
  862. if (currStartX < 0.0f)
  863. subEndX -= mFmod(subEndX, csmBinSize);
  864. else
  865. subEndX += (csmBinSize - mFmod(subEndX, csmBinSize));
  866. for (U32 currXBin = subMinX; currXBin <= subMaxX; currXBin++)
  867. {
  868. U32 checkX = currXBin % csmNumBins;
  869. F32 subStartT = (subStartX - currStartX) / (currEndX - currStartX);
  870. F32 subEndT = getMin(F32((subEndX - currStartX) / (currEndX - currStartX)), 1.f);
  871. F32 subY1 = y1 + (y2 - y1) * subStartT;
  872. F32 subY2 = y1 + (y2 - y1) * subEndT;
  873. U32 newMinY, newMaxY;
  874. getBinRange(getMin(subY1, subY2), getMax(subY1, subY2), newMinY, newMaxY);
  875. for (U32 i = newMinY; i <= newMaxY; i++)
  876. {
  877. U32 checkY = i % csmNumBins;
  878. SceneObjectRef* chain = mBinArray[(checkY * csmNumBins) + checkX].nextInBin;
  879. while (chain)
  880. {
  881. SceneObject* ptr = chain->object;
  882. if (ptr->getContainerSeqKey() != mCurrSeqKey)
  883. {
  884. ptr->setContainerSeqKey(mCurrSeqKey);
  885. if ((ptr->getTypeMask() & mask) != 0 &&
  886. ptr->isCollisionEnabled() == true)
  887. {
  888. if (ptr->getWorldBox().collideLine(start, end))
  889. {
  890. Point3F xformedStart, xformedEnd;
  891. ptr->mWorldToObj.mulP(start, &xformedStart);
  892. ptr->mWorldToObj.mulP(end, &xformedEnd);
  893. xformedStart.convolveInverse(ptr->mObjScale);
  894. xformedEnd.convolveInverse(ptr->mObjScale);
  895. RayInfo ri;
  896. ri.generateTexCoord = info->generateTexCoord;
  897. bool result = false;
  898. if (type == CollisionGeometry)
  899. result = ptr->castRay(xformedStart, xformedEnd, &ri);
  900. else if (type == RenderedGeometry)
  901. result = ptr->castRayRendered(xformedStart, xformedEnd, &ri);
  902. if (result)
  903. {
  904. if( ri.t < currentT && ( !callback || callback( &ri ) ) )
  905. {
  906. *info = ri;
  907. info->point.interpolate(start, end, info->t);
  908. currentT = ri.t;
  909. info->distance = (start - info->point).len();
  910. }
  911. }
  912. }
  913. }
  914. }
  915. chain = chain->nextInBin;
  916. }
  917. }
  918. subStartX = subEndX;
  919. subEndX = getMin(subEndX + csmBinSize, currEndX);
  920. }
  921. currStartX = currEndX;
  922. }
  923. }
  924. mSearchInProgress = false;
  925. // Bump the normal into worldspace if appropriate.
  926. if(currentT != 2)
  927. {
  928. PlaneF fakePlane;
  929. fakePlane.x = info->normal.x;
  930. fakePlane.y = info->normal.y;
  931. fakePlane.z = info->normal.z;
  932. fakePlane.d = 0;
  933. PlaneF result;
  934. mTransformPlane(info->object->getTransform(), info->object->getScale(), fakePlane, &result);
  935. info->normal = result;
  936. return true;
  937. }
  938. else
  939. {
  940. // Do nothing and exit...
  941. return false;
  942. }
  943. }
  944. //-----------------------------------------------------------------------------
  945. // collide with the objects projected object box
  946. bool SceneContainer::collideBox(const Point3F &start, const Point3F &end, U32 mask, RayInfo * info)
  947. {
  948. AssertFatal( info->userData == NULL, "SceneContainer::collideBox - RayInfo->userData cannot be used here!" );
  949. F32 currentT = 2;
  950. for (Link* itr = mStart.mNext; itr != &mEnd; itr = itr->mNext)
  951. {
  952. SceneObject* ptr = static_cast<SceneObject*>(itr);
  953. if (ptr->getTypeMask() & mask && !ptr->mCollisionCount)
  954. {
  955. Point3F xformedStart, xformedEnd;
  956. ptr->mWorldToObj.mulP(start, &xformedStart);
  957. ptr->mWorldToObj.mulP(end, &xformedEnd);
  958. xformedStart.convolveInverse(ptr->mObjScale);
  959. xformedEnd.convolveInverse(ptr->mObjScale);
  960. RayInfo ri;
  961. if(ptr->collideBox(xformedStart, xformedEnd, &ri))
  962. {
  963. if(ri.t < currentT)
  964. {
  965. *info = ri;
  966. info->point.interpolate(start, end, info->t);
  967. currentT = ri.t;
  968. }
  969. }
  970. }
  971. }
  972. return currentT != 2;
  973. }
  974. //-----------------------------------------------------------------------------
  975. static void buildCallback(SceneObject* object,void *key)
  976. {
  977. SceneContainer::CallbackInfo* info = reinterpret_cast<SceneContainer::CallbackInfo*>(key);
  978. object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere);
  979. }
  980. bool SceneContainer::buildPolyList(PolyListContext context, const Box3F &box, U32 mask, AbstractPolyList *polyList)
  981. {
  982. CallbackInfo info;
  983. info.context = context;
  984. info.boundingBox = box;
  985. info.polyList = polyList;
  986. // Build bounding sphere
  987. info.boundingSphere.center = (info.boundingBox.minExtents + info.boundingBox.maxExtents) * 0.5;
  988. VectorF bv = box.maxExtents - info.boundingSphere.center;
  989. info.boundingSphere.radius = bv.len();
  990. sPolyList = polyList;
  991. findObjects(box,mask,buildCallback,&info);
  992. return !polyList->isEmpty();
  993. }
  994. //-----------------------------------------------------------------------------
  995. void SceneContainer::cleanupSearchVectors()
  996. {
  997. for (U32 i = 0; i < mSearchList.size(); i++)
  998. delete mSearchList[i];
  999. mSearchList.clear();
  1000. mCurrSearchPos = -1;
  1001. }
  1002. //-----------------------------------------------------------------------------
  1003. static Point3F sgSortReferencePoint;
  1004. static S32 QSORT_CALLBACK cmpSearchPointers(const void* inP1, const void* inP2)
  1005. {
  1006. SimObjectPtr<SceneObject>** p1 = (SimObjectPtr<SceneObject>**)inP1;
  1007. SimObjectPtr<SceneObject>** p2 = (SimObjectPtr<SceneObject>**)inP2;
  1008. Point3F temp;
  1009. F32 d1, d2;
  1010. if (bool(**p1))
  1011. {
  1012. (**p1)->getWorldBox().getCenter(&temp);
  1013. d1 = (temp - sgSortReferencePoint).len();
  1014. }
  1015. else
  1016. {
  1017. d1 = 0;
  1018. }
  1019. if (bool(**p2))
  1020. {
  1021. (**p2)->getWorldBox().getCenter(&temp);
  1022. d2 = (temp - sgSortReferencePoint).len();
  1023. }
  1024. else
  1025. {
  1026. d2 = 0;
  1027. }
  1028. if (d1 > d2)
  1029. return 1;
  1030. else if (d1 < d2)
  1031. return -1;
  1032. else
  1033. return 0;
  1034. }
  1035. void SceneContainer::initRadiusSearch(const Point3F& searchPoint,
  1036. const F32 searchRadius,
  1037. const U32 searchMask)
  1038. {
  1039. cleanupSearchVectors();
  1040. mSearchReferencePoint = searchPoint;
  1041. Box3F queryBox(searchPoint, searchPoint);
  1042. queryBox.minExtents -= Point3F(searchRadius, searchRadius, searchRadius);
  1043. queryBox.maxExtents += Point3F(searchRadius, searchRadius, searchRadius);
  1044. SimpleQueryList queryList;
  1045. findObjects(queryBox, searchMask, SimpleQueryList::insertionCallback, &queryList);
  1046. F32 radiusSquared = searchRadius * searchRadius;
  1047. const F32* pPoint = &searchPoint.x;
  1048. for (U32 i = 0; i < queryList.mList.size(); i++)
  1049. {
  1050. const F32* bMins;
  1051. const F32* bMaxs;
  1052. bMins = &queryList.mList[i]->getWorldBox().minExtents.x;
  1053. bMaxs = &queryList.mList[i]->getWorldBox().maxExtents.x;
  1054. F32 sum = 0;
  1055. for (U32 j = 0; j < 3; j++)
  1056. {
  1057. if (pPoint[j] < bMins[j])
  1058. sum += (pPoint[j] - bMins[j])*(pPoint[j] - bMins[j]);
  1059. else if (pPoint[j] > bMaxs[j])
  1060. sum += (pPoint[j] - bMaxs[j])*(pPoint[j] - bMaxs[j]);
  1061. }
  1062. if (sum < radiusSquared || queryList.mList[i]->isGlobalBounds())
  1063. {
  1064. mSearchList.push_back(new SimObjectPtr<SceneObject>);
  1065. *(mSearchList.last()) = queryList.mList[i];
  1066. }
  1067. }
  1068. if (mSearchList.size() != 0)
  1069. {
  1070. sgSortReferencePoint = mSearchReferencePoint;
  1071. dQsort(mSearchList.address(), mSearchList.size(),
  1072. sizeof(SimObjectPtr<SceneObject>*), cmpSearchPointers);
  1073. }
  1074. }
  1075. //-----------------------------------------------------------------------------
  1076. void SceneContainer::initTypeSearch(const U32 searchMask)
  1077. {
  1078. cleanupSearchVectors();
  1079. SimpleQueryList queryList;
  1080. findObjects(searchMask, SimpleQueryList::insertionCallback, &queryList);
  1081. for (U32 i = 0; i < queryList.mList.size(); i++)
  1082. {
  1083. mSearchList.push_back(new SimObjectPtr<SceneObject>);
  1084. *(mSearchList.last()) = queryList.mList[i];
  1085. }
  1086. if (mSearchList.size() != 0)
  1087. {
  1088. sgSortReferencePoint = mSearchReferencePoint;
  1089. dQsort(mSearchList.address(), mSearchList.size(),
  1090. sizeof(SimObjectPtr<SceneObject>*), cmpSearchPointers);
  1091. }
  1092. }
  1093. //-----------------------------------------------------------------------------
  1094. SceneObject* SceneContainer::containerSearchNextObject()
  1095. {
  1096. if (mCurrSearchPos >= mSearchList.size())
  1097. return NULL;
  1098. mCurrSearchPos++;
  1099. while (mCurrSearchPos < mSearchList.size() && bool(*mSearchList[mCurrSearchPos]) == false)
  1100. mCurrSearchPos++;
  1101. if (mCurrSearchPos == mSearchList.size())
  1102. return NULL;
  1103. return (*mSearchList[mCurrSearchPos]);
  1104. }
  1105. //-----------------------------------------------------------------------------
  1106. U32 SceneContainer::containerSearchNext()
  1107. {
  1108. SceneObject* object = containerSearchNextObject();
  1109. if( !object )
  1110. return 0;
  1111. return object->getId();
  1112. }
  1113. //-----------------------------------------------------------------------------
  1114. F32 SceneContainer::containerSearchCurrDist()
  1115. {
  1116. AssertFatal(mCurrSearchPos != -1, "Error, must call containerSearchNext before containerSearchCurrDist");
  1117. if (mCurrSearchPos == -1 || mCurrSearchPos >= mSearchList.size() ||
  1118. bool(*mSearchList[mCurrSearchPos]) == false)
  1119. return 0.0;
  1120. Point3F pos;
  1121. (*mSearchList[mCurrSearchPos])->getWorldBox().getCenter(&pos);
  1122. return (pos - mSearchReferencePoint).len();
  1123. }
  1124. //-----------------------------------------------------------------------------
  1125. F32 SceneContainer::containerSearchCurrRadiusDist()
  1126. {
  1127. AssertFatal(mCurrSearchPos != -1, "Error, must call containerSearchNext before containerSearchCurrDist");
  1128. if (mCurrSearchPos == -1 || mCurrSearchPos >= mSearchList.size() ||
  1129. bool(*mSearchList[mCurrSearchPos]) == false)
  1130. return 0.0;
  1131. Point3F pos;
  1132. Box3F worldBox = (*mSearchList[mCurrSearchPos])->getWorldBox();
  1133. worldBox.getCenter(&pos);
  1134. F32 dist = (pos - mSearchReferencePoint).len();
  1135. F32 min = worldBox.len_x();
  1136. if (worldBox.len_y() < min)
  1137. min = worldBox.len_y();
  1138. if (worldBox.len_z() < min)
  1139. min = worldBox.len_z();
  1140. dist -= min;
  1141. if (dist < 0)
  1142. dist = 0;
  1143. return dist;
  1144. }
  1145. //-----------------------------------------------------------------------------
  1146. void SceneContainer::getBinRange( const F32 min, const F32 max, U32& minBin, U32& maxBin )
  1147. {
  1148. AssertFatal(max >= min, avar("Error, bad range in getBinRange. min: %f, max: %f", min, max));
  1149. if ((max - min) >= (SceneContainer::csmTotalBinSize - SceneContainer::csmBinSize))
  1150. {
  1151. F32 minCoord = mFmod(min, SceneContainer::csmTotalBinSize);
  1152. if (minCoord < 0.0f)
  1153. {
  1154. minCoord += SceneContainer::csmTotalBinSize;
  1155. // This is truly lame, but it can happen. There must be a better way to
  1156. // deal with this.
  1157. if (minCoord == SceneContainer::csmTotalBinSize)
  1158. minCoord = SceneContainer::csmTotalBinSize - 0.01f;
  1159. }
  1160. AssertFatal(minCoord >= 0.0 && minCoord < SceneContainer::csmTotalBinSize, "Bad minCoord");
  1161. minBin = U32(minCoord / SceneContainer::csmBinSize);
  1162. AssertFatal(minBin < SceneContainer::csmNumBins, avar("Error, bad clipping! (%g, %d)", minCoord, minBin));
  1163. maxBin = minBin + (SceneContainer::csmNumBins - 1);
  1164. return;
  1165. }
  1166. else
  1167. {
  1168. F32 minCoord = mFmod(min, SceneContainer::csmTotalBinSize);
  1169. if (minCoord < 0.0f)
  1170. {
  1171. minCoord += SceneContainer::csmTotalBinSize;
  1172. // This is truly lame, but it can happen. There must be a better way to
  1173. // deal with this.
  1174. if (minCoord == SceneContainer::csmTotalBinSize)
  1175. minCoord = SceneContainer::csmTotalBinSize - 0.01f;
  1176. }
  1177. AssertFatal(minCoord >= 0.0 && minCoord < SceneContainer::csmTotalBinSize, "Bad minCoord");
  1178. F32 maxCoord = mFmod(max, SceneContainer::csmTotalBinSize);
  1179. if (maxCoord < 0.0f) {
  1180. maxCoord += SceneContainer::csmTotalBinSize;
  1181. // This is truly lame, but it can happen. There must be a better way to
  1182. // deal with this.
  1183. if (maxCoord == SceneContainer::csmTotalBinSize)
  1184. maxCoord = SceneContainer::csmTotalBinSize - 0.01f;
  1185. }
  1186. AssertFatal(maxCoord >= 0.0 && maxCoord < SceneContainer::csmTotalBinSize, "Bad maxCoord");
  1187. minBin = U32(minCoord / SceneContainer::csmBinSize);
  1188. maxBin = U32(maxCoord / SceneContainer::csmBinSize);
  1189. AssertFatal(minBin < SceneContainer::csmNumBins, avar("Error, bad clipping(min)! (%g, %d)", maxCoord, minBin));
  1190. AssertFatal(minBin < SceneContainer::csmNumBins, avar("Error, bad clipping(max)! (%g, %d)", maxCoord, maxBin));
  1191. // MSVC6 seems to be generating some bad floating point code around
  1192. // here when full optimizations are on. The min != max test should
  1193. // not be needed, but it clears up the VC issue.
  1194. if (min != max && minCoord > maxCoord)
  1195. maxBin += SceneContainer::csmNumBins;
  1196. AssertFatal(maxBin >= minBin, "Error, min should always be less than max!");
  1197. }
  1198. }
  1199. //=============================================================================
  1200. // Console API.
  1201. //=============================================================================
  1202. // MARK: ---- Console API ----
  1203. ConsoleFunctionGroupBegin( Containers, "Functions for ray casting and spatial queries.\n\n");
  1204. //-----------------------------------------------------------------------------
  1205. DefineEngineFunction( containerBoxEmpty, bool,
  1206. ( U32 mask, Point3F center, F32 xRadius, F32 yRadius, F32 zRadius, bool useClientContainer ), ( -1, -1, false ),
  1207. "@brief See if any objects of the given types are present in box of given extent.\n\n"
  1208. "@note Extent parameter is last since only one radius is often needed. If "
  1209. "one radius is provided, the yRadius and zRadius are assumed to be the same. Unfortunately, "
  1210. "if you need to use the client container, you'll need to set all of the radius parameters. "
  1211. "Fortunately, this function is mostly used on the server.\n"
  1212. "@param mask Indicates the type of objects we are checking against.\n"
  1213. "@param center Center of box.\n"
  1214. "@param xRadius Search radius in the x-axis. See note above.\n"
  1215. "@param yRadius Search radius in the y-axis. See note above.\n"
  1216. "@param zRadius Search radius in the z-axis. See note above.\n"
  1217. "@param useClientContainer Optionally indicates the search should be within the "
  1218. "client container.\n"
  1219. "@return true if the box is empty, false if any object is found.\n"
  1220. "@ingroup Game")
  1221. {
  1222. Point3F extent( xRadius, yRadius, zRadius );
  1223. extent.y = extent.y >= 0 ? extent.y : extent.x;
  1224. extent.z = extent.z >= 0 ? extent.z : extent.x;
  1225. Box3F B(center - extent, center + extent, true);
  1226. EarlyOutPolyList polyList;
  1227. polyList.mPlaneList.clear();
  1228. polyList.mNormal.set(0,0,0);
  1229. polyList.mPlaneList.setSize(6);
  1230. polyList.mPlaneList[0].set(B.minExtents, VectorF(-1,0,0));
  1231. polyList.mPlaneList[1].set(B.maxExtents, VectorF(0,1,0));
  1232. polyList.mPlaneList[2].set(B.maxExtents, VectorF(1,0,0));
  1233. polyList.mPlaneList[3].set(B.minExtents, VectorF(0,-1,0));
  1234. polyList.mPlaneList[4].set(B.minExtents, VectorF(0,0,-1));
  1235. polyList.mPlaneList[5].set(B.maxExtents, VectorF(0,0,1));
  1236. SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
  1237. return ! pContainer->buildPolyList(PLC_Collision, B, mask, &polyList);
  1238. }
  1239. //-----------------------------------------------------------------------------
  1240. DefineEngineFunction( initContainerRadiusSearch, void, ( Point3F pos, F32 radius, U32 mask, bool useClientContainer ), ( false ),
  1241. "@brief Start a search for items at the given position and within the given radius, filtering by mask.\n\n"
  1242. "@param pos Center position for the search\n"
  1243. "@param radius Search radius\n"
  1244. "@param mask Bitmask of object types to include in the search\n"
  1245. "@param useClientContainer Optionally indicates the search should be within the "
  1246. "client container.\n"
  1247. "@see containerSearchNext\n"
  1248. "@ingroup Game")
  1249. {
  1250. SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
  1251. pContainer->initRadiusSearch( pos, radius, mask );
  1252. }
  1253. //-----------------------------------------------------------------------------
  1254. DefineEngineFunction( initContainerTypeSearch, void, ( U32 mask, bool useClientContainer ), ( false ),
  1255. "@brief Start a search for all items of the types specified by the bitset mask.\n\n"
  1256. "@param mask Bitmask of object types to include in the search\n"
  1257. "@param useClientContainer Optionally indicates the search should be within the "
  1258. "client container.\n"
  1259. "@see containerSearchNext\n"
  1260. "@ingroup Game")
  1261. {
  1262. SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
  1263. pContainer->initTypeSearch( mask );
  1264. }
  1265. //-----------------------------------------------------------------------------
  1266. DefineEngineFunction( containerSearchNext, SceneObject*, ( bool useClientContainer ), ( false ),
  1267. "@brief Get next item from a search started with initContainerRadiusSearch() or "
  1268. "initContainerTypeSearch().\n\n"
  1269. "@param useClientContainer Optionally indicates the search should be within the "
  1270. "client container.\n"
  1271. "@return the next object found in the search, or null if no more\n"
  1272. "@tsexample\n"
  1273. "// print the names of all nearby ShapeBase derived objects\n"
  1274. "%position = %obj.getPosition;\n"
  1275. "%radius = 20;\n"
  1276. "%mask = $TypeMasks::ShapeBaseObjectType;\n"
  1277. "initContainerRadiusSearch( %position, %radius, %mask );\n"
  1278. "while ( (%targetObject = containerSearchNext()) != 0 )\n"
  1279. "{\n"
  1280. " echo( \"Found: \" @ %targetObject.getName() );\n"
  1281. "}\n"
  1282. "@endtsexample\n"
  1283. "@see initContainerRadiusSearch()\n"
  1284. "@see initContainerTypeSearch()\n"
  1285. "@ingroup Game")
  1286. {
  1287. SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
  1288. return pContainer->containerSearchNextObject();
  1289. }
  1290. //-----------------------------------------------------------------------------
  1291. DefineEngineFunction( containerSearchCurrDist, F32, ( bool useClientContainer ), ( false ),
  1292. "@brief Get distance of the center of the current item from the center of the "
  1293. "current initContainerRadiusSearch.\n\n"
  1294. "@param useClientContainer Optionally indicates the search should be within the "
  1295. "client container.\n"
  1296. "@return distance from the center of the current object to the center of "
  1297. "the search\n"
  1298. "@see containerSearchNext\n"
  1299. "@ingroup Game")
  1300. {
  1301. SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
  1302. return pContainer->containerSearchCurrDist();
  1303. }
  1304. //-----------------------------------------------------------------------------
  1305. DefineEngineFunction( containerSearchCurrRadiusDist, F32, ( bool useClientContainer ), ( false ),
  1306. "@brief Get the distance of the closest point of the current item from the center "
  1307. "of the current initContainerRadiusSearch.\n\n"
  1308. "@param useClientContainer Optionally indicates the search should be within the "
  1309. "client container.\n"
  1310. "@return distance from the closest point of the current object to the "
  1311. "center of the search\n"
  1312. "@see containerSearchNext\n"
  1313. "@ingroup Game")
  1314. {
  1315. SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
  1316. return pContainer->containerSearchCurrRadiusDist();
  1317. }
  1318. //-----------------------------------------------------------------------------
  1319. //TODO: make RayInfo an API type
  1320. DefineEngineFunction( containerRayCast, const char*,
  1321. ( Point3F start, Point3F end, U32 mask, SceneObject *pExempt, bool useClientContainer ), ( nullAsType<SceneObject*>(), false ),
  1322. "@brief Cast a ray from start to end, checking for collision against items matching mask.\n\n"
  1323. "If pExempt is specified, then it is temporarily excluded from collision checks (For "
  1324. "instance, you might want to exclude the player if said player was firing a weapon.)\n"
  1325. "@param start An XYZ vector containing the tail position of the ray.\n"
  1326. "@param end An XYZ vector containing the head position of the ray\n"
  1327. "@param mask A bitmask corresponding to the type of objects to check for\n"
  1328. "@param pExempt An optional ID for a single object that ignored for this raycast\n"
  1329. "@param useClientContainer Optionally indicates the search should be within the "
  1330. "client container.\n"
  1331. "@returns A string containing either null, if nothing was struck, or these fields:\n"
  1332. "<ul><li>The ID of the object that was struck.</li>"
  1333. "<li>The x, y, z position that it was struck.</li>"
  1334. "<li>The x, y, z of the normal of the face that was struck.</li>"
  1335. "<li>The distance between the start point and the position we hit.</li></ul>"
  1336. "@ingroup Game")
  1337. {
  1338. if (pExempt)
  1339. pExempt->disableCollision();
  1340. SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
  1341. RayInfo rinfo;
  1342. S32 ret = 0;
  1343. if (pContainer->castRay(start, end, mask, &rinfo) == true)
  1344. ret = rinfo.object->getId();
  1345. if (pExempt)
  1346. pExempt->enableCollision();
  1347. // add the hit position and normal?
  1348. static const U32 bufSize = 256;
  1349. char *returnBuffer = Con::getReturnBuffer(bufSize);
  1350. if(ret)
  1351. {
  1352. dSprintf(returnBuffer, bufSize, "%d %g %g %g %g %g %g %g",
  1353. ret, rinfo.point.x, rinfo.point.y, rinfo.point.z,
  1354. rinfo.normal.x, rinfo.normal.y, rinfo.normal.z, rinfo.distance);
  1355. }
  1356. else
  1357. {
  1358. returnBuffer[0] = '0';
  1359. returnBuffer[1] = '\0';
  1360. }
  1361. return(returnBuffer);
  1362. }
  1363. DefineEngineFunction(materialRayCast, const char*,
  1364. (Point3F start, Point3F end, U32 mask, SceneObject* pExempt, bool useClientContainer), (nullAsType<SceneObject*>(), false),
  1365. "@brief Cast a ray from start to end, checking for collision against items matching mask.\n\n"
  1366. "If pExempt is specified, then it is temporarily excluded from collision checks (For "
  1367. "instance, you might want to exclude the player if said player was firing a weapon.)\n"
  1368. "@param start An XYZ vector containing the tail position of the ray.\n"
  1369. "@param end An XYZ vector containing the head position of the ray\n"
  1370. "@param mask A bitmask corresponding to the type of objects to check for\n"
  1371. "@param pExempt An optional ID for a single object that ignored for this raycast\n"
  1372. "@param useClientContainer Optionally indicates the search should be within the "
  1373. "client container.\n"
  1374. "@returns A string containing either null, if nothing was struck, or these fields:\n"
  1375. "<ul><li>The ID of the object that was struck.</li>"
  1376. "<li>The x, y, z position that it was struck.</li>"
  1377. "<li>The x, y, z of the normal of the face that was struck.</li>"
  1378. "<li>The distance between the start point and the position we hit.</li></ul>"
  1379. "@ingroup Game")
  1380. {
  1381. if (pExempt)
  1382. pExempt->disableCollision();
  1383. SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
  1384. RayInfo rinfo;
  1385. S32 ret = 0;
  1386. if (pContainer->castRayRendered(start, end, mask, &rinfo) == true)
  1387. ret = rinfo.object->getId();
  1388. if (pExempt)
  1389. pExempt->enableCollision();
  1390. // add the hit position and normal?
  1391. static const U32 bufSize = 512;
  1392. char* returnBuffer = Con::getReturnBuffer(bufSize);
  1393. if (ret)
  1394. {
  1395. dSprintf(returnBuffer, bufSize, "%d %g %g %g %g %g %g %g %g %g %s",
  1396. ret, rinfo.point.x, rinfo.point.y, rinfo.point.z,
  1397. rinfo.normal.x, rinfo.normal.y, rinfo.normal.z, rinfo.distance, rinfo.texCoord.x, rinfo.texCoord.y, rinfo.material ? rinfo.material->getMaterial()->getName() : "");
  1398. }
  1399. else
  1400. {
  1401. returnBuffer[0] = '0';
  1402. returnBuffer[1] = '\0';
  1403. }
  1404. return(returnBuffer);
  1405. }
  1406. ConsoleFunctionGroupEnd( Containers );