sceneContainer.cpp 57 KB

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