sceneContainer.cpp 57 KB

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