terrCollision.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  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 "terrain/terrCollision.h"
  24. #include "terrain/terrData.h"
  25. #include "collision/abstractPolyList.h"
  26. #include "collision/collision.h"
  27. const F32 TerrainThickness = 0.5f;
  28. static const U32 MaxExtent = 256;
  29. #define MAX_FLOAT 1e20f
  30. //----------------------------------------------------------------------------
  31. Convex sTerrainConvexList;
  32. // Number of vertices followed by point index
  33. S32 sVertexList[5][5] = {
  34. { 3, 1,2,3 }, // 135 B
  35. { 3, 0,1,3 }, // 135 A
  36. { 3, 0,2,3 }, // 45 B
  37. { 3, 0,1,2 }, // 45 A
  38. { 4, 0,1,2,3 } // Convex square
  39. };
  40. // Number of edges followed by edge index pairs
  41. S32 sEdgeList45[16][11] = {
  42. { 0 }, //
  43. { 0 },
  44. { 0 },
  45. { 1, 0,1 }, // 0-1
  46. { 0 },
  47. { 1, 0,1 }, // 0-2
  48. { 1, 0,1 }, // 1-2
  49. { 3, 0,1,1,2,2,0 }, // 0-1,1-2,2-0
  50. { 0 },
  51. { 0,}, //
  52. { 0 },
  53. { 1, 0,1 }, // 0-1,
  54. { 0, }, //
  55. { 1, 0,1 }, // 0-2,
  56. { 1, 0,1 }, // 1-2
  57. { 3, 0,1,1,2,0,2 },
  58. };
  59. S32 sEdgeList135[16][11] = {
  60. { 0 },
  61. { 0 },
  62. { 0 },
  63. { 1, 0,1 }, // 0-1
  64. { 0 },
  65. { 0 },
  66. { 1, 0,1 }, // 1-2
  67. { 2, 0,1,1,2 }, // 0-1,1-2
  68. { 0 },
  69. { 0, }, //
  70. { 1, 0,1 }, // 1-3
  71. { 2, 0,1,1,2 }, // 0-1,1-3,
  72. { 0 }, //
  73. { 0 }, //
  74. { 2, 0,1,2,0 }, // 1-2,3-1
  75. { 3, 0,1,1,2,1,3 },
  76. };
  77. // On split squares, the FaceA diagnal is also removed
  78. S32 sEdgeList45A[16][11] = {
  79. { 0 }, //
  80. { 0 },
  81. { 0 },
  82. { 1, 0,1 }, // 0-1
  83. { 0 },
  84. { 0 }, //
  85. { 1, 0,1 }, // 1-2
  86. { 2, 0,1,1,2 }, // 0-1,1-2
  87. { 0 },
  88. { 0,}, //
  89. { 0 },
  90. { 1, 0,1 }, // 0-1
  91. { 0, }, //
  92. { 0, 0,1 }, //
  93. { 1, 0,1 }, // 1-2
  94. { 3, 0,1,1,2 },
  95. };
  96. S32 sEdgeList135A[16][11] = {
  97. { 0 },
  98. { 0 },
  99. { 0 },
  100. { 1, 0,1 }, // 0-1
  101. { 0 },
  102. { 0 },
  103. { 1, 0,1 }, // 1-2
  104. { 2, 0,1,1,2 }, // 0-1,1-2
  105. { 0 },
  106. { 0 }, //
  107. { 0 }, //
  108. { 1, 0,1 }, // 0-1
  109. { 0 }, //
  110. { 0 }, //
  111. { 1, 0,1 }, // 1-2
  112. { 3, 0,1,1,2 },
  113. };
  114. // Number of faces followed by normal index and vertices
  115. S32 sFaceList45[16][9] = {
  116. { 0 },
  117. { 0 },
  118. { 0 },
  119. { 0 },
  120. { 0 },
  121. { 0 },
  122. { 0 },
  123. { 1, 0,0,1,2 },
  124. { 0 },
  125. { 0 },
  126. { 0 },
  127. { 0 },
  128. { 0 },
  129. { 1, 1,0,1,2 },
  130. { 0 },
  131. { 2, 0,0,1,2, 1,0,2,3 },
  132. };
  133. S32 sFaceList135[16][9] = {
  134. { 0 },
  135. { 0 },
  136. { 0 },
  137. { 0 },
  138. { 0 },
  139. { 0 },
  140. { 0 },
  141. { 0 },
  142. { 0 },
  143. { 0 },
  144. { 0 },
  145. { 1, 0,0,1,2 },
  146. { 0 },
  147. { 0 },
  148. { 1, 1,0,1,2 },
  149. { 2, 0,0,1,3, 1,1,2,3 },
  150. };
  151. TerrainConvex::TerrainConvex()
  152. {
  153. halfA = true;
  154. square = NULL;
  155. squareId = 0;
  156. material = 0;
  157. split45 = false;
  158. mType = TerrainConvexType;
  159. }
  160. TerrainConvex::TerrainConvex( const TerrainConvex &cv )
  161. {
  162. mType = TerrainConvexType;
  163. halfA = false;
  164. square = NULL;
  165. // Only a partial copy...
  166. mObject = cv.mObject;
  167. split45 = cv.split45;
  168. squareId = cv.squareId;
  169. material = cv.material;
  170. point[0] = cv.point[0];
  171. point[1] = cv.point[1];
  172. point[2] = cv.point[2];
  173. point[3] = cv.point[3];
  174. normal[0] = cv.normal[0];
  175. normal[1] = cv.normal[1];
  176. box = cv.box;
  177. }
  178. Box3F TerrainConvex::getBoundingBox() const
  179. {
  180. return box;
  181. }
  182. Box3F TerrainConvex::getBoundingBox(const MatrixF&, const Point3F& ) const
  183. {
  184. // Function should not be called....
  185. return box;
  186. }
  187. Point3F TerrainConvex::support(const VectorF& v) const
  188. {
  189. S32 *vp;
  190. if (halfA)
  191. vp = square ? sVertexList[(split45 << 1) | 1]: sVertexList[4];
  192. else
  193. vp = square ? sVertexList[(split45 << 1)] : sVertexList[4];
  194. S32 *ve = vp + vp[0] + 1;
  195. const Point3F *bp = &point[vp[1]];
  196. F32 bd = mDot(*bp,v);
  197. for (vp += 2; vp < ve; vp++) {
  198. const Point3F* cp = &point[*vp];
  199. F32 dd = mDot(*cp,v);
  200. if (dd > bd) {
  201. bd = dd;
  202. bp = cp;
  203. }
  204. }
  205. return *bp;
  206. }
  207. inline bool isOnPlane(Point3F& p,PlaneF& plane)
  208. {
  209. F32 dist = mDot(plane,p) + plane.d;
  210. return dist < 0.1 && dist > -0.1;
  211. }
  212. void TerrainConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf)
  213. {
  214. U32 i;
  215. cf->material = 0;
  216. cf->mObject = mObject;
  217. // Plane is normal n + support point
  218. PlaneF plane;
  219. plane.set(support(n),n);
  220. S32 vertexCount = cf->mVertexList.size();
  221. // Emit vertices on the plane
  222. S32* vertexListPointer;
  223. if (halfA)
  224. vertexListPointer = square ? sVertexList[(split45 << 1) | 1]: sVertexList[4];
  225. else
  226. vertexListPointer = square ? sVertexList[(split45 << 1)] : sVertexList[4];
  227. S32 pm = 0;
  228. S32 numVerts = *vertexListPointer;
  229. vertexListPointer += 1;
  230. for (i = 0; i < numVerts; i++)
  231. {
  232. Point3F& cp = point[vertexListPointer[i]];
  233. cf->mVertexList.increment();
  234. mat.mulP(cp,&cf->mVertexList.last());
  235. pm |= 1 << vertexListPointer[i];
  236. }
  237. // Emit Edges
  238. S32* ep = (square && halfA)?
  239. (split45 ? sEdgeList45A[pm]: sEdgeList135A[pm]):
  240. (split45 ? sEdgeList45[pm]: sEdgeList135[pm]);
  241. S32 numEdges = *ep;
  242. S32 edgeListStart = cf->mEdgeList.size();
  243. cf->mEdgeList.increment(numEdges);
  244. ep += 1;
  245. for (i = 0; i < numEdges; i++)
  246. {
  247. cf->mEdgeList[edgeListStart + i].vertex[0] = vertexCount + ep[i * 2 + 0];
  248. cf->mEdgeList[edgeListStart + i].vertex[1] = vertexCount + ep[i * 2 + 1];
  249. }
  250. // Emit faces
  251. S32* fp = split45 ? sFaceList45[pm]: sFaceList135[pm];
  252. S32 numFaces = *fp;
  253. fp += 1;
  254. S32 faceListStart = cf->mFaceList.size();
  255. cf->mFaceList.increment(numFaces);
  256. for (i = 0; i < numFaces; i++)
  257. {
  258. ConvexFeature::Face& face = cf->mFaceList[faceListStart + i];
  259. face.normal = normal[fp[i * 4 + 0]];
  260. face.vertex[0] = vertexCount + fp[i * 4 + 1];
  261. face.vertex[1] = vertexCount + fp[i * 4 + 2];
  262. face.vertex[2] = vertexCount + fp[i * 4 + 3];
  263. }
  264. }
  265. void TerrainConvex::getPolyList(AbstractPolyList* list)
  266. {
  267. list->setTransform(&mObject->getTransform(), mObject->getScale());
  268. list->setObject(mObject);
  269. // Emit vertices
  270. U32 array[4];
  271. U32 curr = 0;
  272. S32 numVerts;
  273. S32* vertsStart;
  274. if (halfA)
  275. {
  276. numVerts = square ? sVertexList[(split45 << 1) | 1][0] : sVertexList[4][0];
  277. vertsStart = square ? &sVertexList[(split45 << 1) | 1][1] : &sVertexList[4][1];
  278. }
  279. else
  280. {
  281. numVerts = square ? sVertexList[(split45 << 1)][0] : sVertexList[4][0];
  282. vertsStart = square ? &sVertexList[(split45 << 1)][1] : &sVertexList[4][1];
  283. }
  284. S32 pointMask = 0;
  285. for (U32 i = 0; i < numVerts; i++) {
  286. const Point3F& cp = point[vertsStart[i]];
  287. array[curr++] = list->addPoint(cp);
  288. pointMask |= (1 << vertsStart[i]);
  289. }
  290. S32 numFaces = split45 ? sFaceList45[pointMask][0] : sFaceList135[pointMask][0];
  291. S32* faceStart = split45 ? &sFaceList45[pointMask][1] : &sFaceList135[pointMask][1];
  292. for (U32 j = 0; j < numFaces; j++) {
  293. S32 plane = faceStart[0];
  294. S32 v0 = faceStart[1];
  295. S32 v1 = faceStart[2];
  296. S32 v2 = faceStart[3];
  297. list->begin(0, plane);
  298. list->vertex(array[v0]);
  299. list->vertex(array[v1]);
  300. list->vertex(array[v2]);
  301. list->plane(array[v0], array[v1], array[v2]);
  302. list->end();
  303. faceStart += 4;
  304. }
  305. }
  306. //----------------------------------------------------------------------------
  307. void TerrainBlock::buildConvex(const Box3F& box,Convex* convex)
  308. {
  309. PROFILE_SCOPE( TerrainBlock_buildConvex );
  310. sTerrainConvexList.collectGarbage();
  311. // First check to see if the query misses the
  312. // terrain elevation range.
  313. const Point3F &terrainPos = getPosition();
  314. if ( box.maxExtents.z - terrainPos.z < -TerrainThickness ||
  315. box.minExtents.z - terrainPos.z > fixedToFloat( mFile->getMaxHeight() ) )
  316. return;
  317. // Transform the bounding sphere into the object's coord space. Note that this
  318. // not really optimal.
  319. Box3F osBox = box;
  320. mWorldToObj.mul(osBox);
  321. AssertWarn(mObjScale == Point3F(1, 1, 1), "Error, handle the scale transform on the terrain");
  322. S32 xStart = (S32)mFloor( osBox.minExtents.x / mSquareSize );
  323. S32 xEnd = (S32)mCeil ( osBox.maxExtents.x / mSquareSize );
  324. S32 yStart = (S32)mFloor( osBox.minExtents.y / mSquareSize );
  325. S32 yEnd = (S32)mCeil ( osBox.maxExtents.y / mSquareSize );
  326. S32 xExt = xEnd - xStart;
  327. if (xExt > MaxExtent)
  328. xExt = MaxExtent;
  329. U16 heightMax = floatToFixed(osBox.maxExtents.z);
  330. U16 heightMin = (osBox.minExtents.z < 0)? 0: floatToFixed(osBox.minExtents.z);
  331. const U32 BlockMask = mFile->mSize - 1;
  332. for ( S32 y = yStart; y < yEnd; y++ )
  333. {
  334. S32 yi = y & BlockMask;
  335. //
  336. for ( S32 x = xStart; x < xEnd; x++ )
  337. {
  338. S32 xi = x & BlockMask;
  339. const TerrainSquare *sq = mFile->findSquare( 0, xi, yi );
  340. if ( x != xi || y != yi )
  341. continue;
  342. // holes only in the primary terrain block
  343. if ( ( ( sq->flags & TerrainSquare::Empty ) && x == xi && y == yi ) ||
  344. sq->minHeight > heightMax ||
  345. sq->maxHeight < heightMin )
  346. continue;
  347. U32 sid = (x << 16) + (y & ((1 << 16) - 1));
  348. Convex *cc = 0;
  349. // See if the square already exists as part of the working set.
  350. CollisionWorkingList& wl = convex->getWorkingList();
  351. for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext)
  352. if (itr->mConvex->getType() == TerrainConvexType &&
  353. static_cast<TerrainConvex*>(itr->mConvex)->squareId == sid) {
  354. cc = itr->mConvex;
  355. break;
  356. }
  357. if (cc)
  358. continue;
  359. // Create a new convex.
  360. TerrainConvex* cp = new TerrainConvex;
  361. sTerrainConvexList.registerObject(cp);
  362. convex->addToWorkingList(cp);
  363. cp->halfA = true;
  364. cp->square = 0;
  365. cp->mObject = this;
  366. cp->squareId = sid;
  367. cp->material = mFile->getLayerIndex( xi, yi );
  368. cp->box.minExtents.set((F32)(x * mSquareSize), (F32)(y * mSquareSize), fixedToFloat( sq->minHeight ));
  369. cp->box.maxExtents.x = cp->box.minExtents.x + mSquareSize;
  370. cp->box.maxExtents.y = cp->box.minExtents.y + mSquareSize;
  371. cp->box.maxExtents.z = fixedToFloat( sq->maxHeight );
  372. mObjToWorld.mul(cp->box);
  373. // Build points
  374. Point3F* pos = cp->point;
  375. for (S32 i = 0; i < 4 ; i++,pos++) {
  376. S32 dx = i >> 1;
  377. S32 dy = dx ^ (i & 1);
  378. pos->x = (F32)((x + dx) * mSquareSize);
  379. pos->y = (F32)((y + dy) * mSquareSize);
  380. pos->z = fixedToFloat( mFile->getHeight(xi + dx, yi + dy) );
  381. }
  382. // Build normals, then split into two Convex objects if the
  383. // square is concave
  384. if ((cp->split45 = sq->flags & TerrainSquare::Split45) == true) {
  385. VectorF *vp = cp->point;
  386. mCross(vp[0] - vp[1],vp[2] - vp[1],&cp->normal[0]);
  387. cp->normal[0].normalize();
  388. mCross(vp[2] - vp[3],vp[0] - vp[3],&cp->normal[1]);
  389. cp->normal[1].normalize();
  390. if (mDot(vp[3] - vp[1],cp->normal[0]) > 0) {
  391. TerrainConvex* nc = new TerrainConvex(*cp);
  392. sTerrainConvexList.registerObject(nc);
  393. convex->addToWorkingList(nc);
  394. nc->halfA = false;
  395. nc->square = cp;
  396. cp->square = nc;
  397. }
  398. }
  399. else {
  400. VectorF *vp = cp->point;
  401. mCross(vp[3] - vp[0],vp[1] - vp[0],&cp->normal[0]);
  402. cp->normal[0].normalize();
  403. mCross(vp[1] - vp[2],vp[3] - vp[2],&cp->normal[1]);
  404. cp->normal[1].normalize();
  405. if (mDot(vp[2] - vp[0],cp->normal[0]) > 0) {
  406. TerrainConvex* nc = new TerrainConvex(*cp);
  407. sTerrainConvexList.registerObject(nc);
  408. convex->addToWorkingList(nc);
  409. nc->halfA = false;
  410. nc->square = cp;
  411. cp->square = nc;
  412. }
  413. }
  414. }
  415. }
  416. }
  417. static inline void swap(U32*& a,U32*& b)
  418. {
  419. U32* t = b;
  420. b = a;
  421. a = t;
  422. }
  423. static void clrbuf(U32* p, U32 s)
  424. {
  425. U32* e = p + s;
  426. while (p != e)
  427. *p++ = U32_MAX;
  428. }
  429. bool TerrainBlock::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF&)
  430. {
  431. PROFILE_SCOPE( TerrainBlock_buildPolyList );
  432. // First check to see if the query misses the
  433. // terrain elevation range.
  434. const Point3F &terrainPos = getPosition();
  435. if ( box.maxExtents.z - terrainPos.z < -TerrainThickness ||
  436. box.minExtents.z - terrainPos.z > fixedToFloat( mFile->getMaxHeight() ) )
  437. return false;
  438. // Transform the bounding sphere into the object's coord
  439. // space. Note that this is really optimal.
  440. Box3F osBox = box;
  441. mWorldToObj.mul(osBox);
  442. AssertWarn(mObjScale == Point3F::One, "Error, handle the scale transform on the terrain");
  443. // Setup collision state data
  444. polyList->setTransform(&getTransform(), getScale());
  445. polyList->setObject(this);
  446. S32 xStart = (S32)mFloor( osBox.minExtents.x / mSquareSize );
  447. S32 xEnd = (S32)mCeil ( osBox.maxExtents.x / mSquareSize );
  448. S32 yStart = (S32)mFloor( osBox.minExtents.y / mSquareSize );
  449. S32 yEnd = (S32)mCeil ( osBox.maxExtents.y / mSquareSize );
  450. if ( xStart < 0 )
  451. xStart = 0;
  452. S32 xExt = xEnd - xStart;
  453. if ( xExt > MaxExtent )
  454. xExt = MaxExtent;
  455. xEnd = xStart + xExt;
  456. U32 heightMax = floatToFixed(osBox.maxExtents.z);
  457. U32 heightMin = (osBox.minExtents.z < 0.0f)? 0.0f: floatToFixed(osBox.minExtents.z);
  458. // Index of shared points
  459. U32 bp[(MaxExtent + 1) * 2],*vb[2];
  460. vb[0] = &bp[0];
  461. vb[1] = &bp[xExt + 1];
  462. clrbuf(vb[1],xExt + 1);
  463. const U32 BlockMask = mFile->mSize - 1;
  464. bool emitted = false;
  465. for (S32 y = yStart; y < yEnd; y++)
  466. {
  467. S32 yi = y & BlockMask;
  468. swap(vb[0],vb[1]);
  469. clrbuf(vb[1],xExt + 1);
  470. F32 wy1 = y * mSquareSize, wy2 = (y + 1) * mSquareSize;
  471. if(context == PLC_Navigation &&
  472. ((wy1 > osBox.maxExtents.y && wy2 > osBox.maxExtents.y) ||
  473. (wy1 < osBox.minExtents.y && wy2 < osBox.minExtents.y)))
  474. continue;
  475. //
  476. for (S32 x = xStart; x < xEnd; x++)
  477. {
  478. S32 xi = x & BlockMask;
  479. const TerrainSquare *sq = mFile->findSquare( 0, xi, yi );
  480. F32 wx1 = x * mSquareSize, wx2 = (x + 1) * mSquareSize;
  481. if(context == PLC_Navigation &&
  482. ((wx1 > osBox.maxExtents.x && wx2 > osBox.maxExtents.x) ||
  483. (wx1 < osBox.minExtents.x && wx2 < osBox.minExtents.x)))
  484. continue;
  485. if ( x != xi || y != yi )
  486. continue;
  487. // holes only in the primary terrain block
  488. if ( ( ( sq->flags & TerrainSquare::Empty ) && x == xi && y == yi ) ||
  489. sq->minHeight > heightMax ||
  490. sq->maxHeight < heightMin )
  491. continue;
  492. emitted = true;
  493. // Add the missing points
  494. U32 vi[5];
  495. for (int i = 0; i < 4 ; i++)
  496. {
  497. S32 dx = i >> 1;
  498. S32 dy = dx ^ (i & 1);
  499. U32* vp = &vb[dy][x - xStart + dx];
  500. if (*vp == U32_MAX)
  501. {
  502. Point3F pos;
  503. pos.x = (F32)((x + dx) * mSquareSize);
  504. pos.y = (F32)((y + dy) * mSquareSize);
  505. pos.z = fixedToFloat( mFile->getHeight(xi + dx, yi + dy) );
  506. *vp = polyList->addPoint(pos);
  507. }
  508. vi[i] = *vp;
  509. }
  510. U32* vp = &vi[0];
  511. if ( !( sq->flags & TerrainSquare::Split45 ) )
  512. vi[4] = vi[0], vp++;
  513. BaseMatInstance *material = NULL; //getMaterialInst( xi, yi );
  514. U32 surfaceKey = ((xi << 16) + yi) << 1;
  515. polyList->begin(material,surfaceKey);
  516. polyList->vertex(vp[0]);
  517. polyList->vertex(vp[1]);
  518. polyList->vertex(vp[2]);
  519. polyList->plane(vp[0],vp[1],vp[2]);
  520. polyList->end();
  521. polyList->begin(material,surfaceKey + 1);
  522. polyList->vertex(vp[0]);
  523. polyList->vertex(vp[2]);
  524. polyList->vertex(vp[3]);
  525. polyList->plane(vp[0],vp[2],vp[3]);
  526. polyList->end();
  527. }
  528. }
  529. return emitted;
  530. }
  531. //----------------------------------------------------------------------------
  532. static F32 calcInterceptV(F32 vStart, F32 invDeltaV, F32 intercept)
  533. {
  534. return (intercept - vStart) * invDeltaV;
  535. }
  536. static F32 calcInterceptNone(F32, F32, F32)
  537. {
  538. return MAX_FLOAT;
  539. }
  540. static F32 (*calcInterceptX)(F32, F32, F32);
  541. static F32 (*calcInterceptY)(F32, F32, F32);
  542. static U32 lineCount;
  543. static Point3F lineStart, lineEnd;
  544. bool TerrainBlock::castRay(const Point3F &start, const Point3F &end, RayInfo *info)
  545. {
  546. PROFILE_SCOPE( TerrainBlock_castRay );
  547. if ( !castRayI(start, end, info, false) )
  548. return false;
  549. // Set intersection point.
  550. info->setContactPoint( start, end );
  551. getTransform().mulP( info->point ); // transform to world coordinates for getGridPos
  552. // Set material at contact point.
  553. Point2I gridPos = getGridPos( info->point );
  554. U8 layer = mFile->getLayerIndex( gridPos.x, gridPos.y );
  555. info->material = mFile->getMaterialMapping( layer );
  556. return true;
  557. }
  558. bool TerrainBlock::castRayI(const Point3F &start, const Point3F &end, RayInfo *info, bool collideEmpty)
  559. {
  560. lineCount = 0;
  561. lineStart = start;
  562. lineEnd = end;
  563. info->object = this;
  564. if(start.x == end.x && start.y == end.y)
  565. {
  566. if (end.z == start.z)
  567. return false;
  568. F32 height;
  569. if(!getNormalAndHeight(Point2F(start.x, start.y), &info->normal, &height, true))
  570. return false;
  571. F32 t = (height - start.z) / (end.z - start.z);
  572. if(t < 0 || t > 1)
  573. return false;
  574. info->t = t;
  575. return true;
  576. }
  577. F32 invBlockWorldSize = 1 / getWorldBlockSize();
  578. Point3F pStart(start.x * invBlockWorldSize, start.y * invBlockWorldSize, start.z);
  579. Point3F pEnd(end.x * invBlockWorldSize, end.y * invBlockWorldSize, end.z);
  580. S32 blockX = (S32)mFloor(pStart.x);
  581. S32 blockY = (S32)mFloor(pStart.y);
  582. S32 dx, dy;
  583. F32 invDeltaX;
  584. if(pEnd.x == pStart.x)
  585. {
  586. calcInterceptX = calcInterceptNone;
  587. invDeltaX = 0;
  588. dx = 0;
  589. }
  590. else
  591. {
  592. invDeltaX = 1 / (pEnd.x - pStart.x);
  593. calcInterceptX = calcInterceptV;
  594. if(pEnd.x < pStart.x)
  595. dx = -1;
  596. else
  597. dx = 1;
  598. }
  599. F32 invDeltaY;
  600. if(pEnd.y == pStart.y)
  601. {
  602. calcInterceptY = calcInterceptNone;
  603. invDeltaY = 0;
  604. dy = 0;
  605. }
  606. else
  607. {
  608. invDeltaY = 1 / (pEnd.y - pStart.y);
  609. calcInterceptY = calcInterceptV;
  610. if(pEnd.y < pStart.y)
  611. dy = -1;
  612. else
  613. dy = 1;
  614. }
  615. const U32 BlockSquareWidth = mFile->mSize;
  616. const U32 GridLevels = mFile->mGridLevels;
  617. F32 startT = 0;
  618. for(;;)
  619. {
  620. F32 nextXInt = calcInterceptX(pStart.x, invDeltaX, (F32)(blockX + (dx == 1)));
  621. F32 nextYInt = calcInterceptY(pStart.y, invDeltaY, (F32)(blockY + (dy == 1)));
  622. F32 intersectT = 1;
  623. if(nextXInt < intersectT)
  624. intersectT = nextXInt;
  625. if(nextYInt < intersectT)
  626. intersectT = nextYInt;
  627. if ( castRayBlock( pStart,
  628. pEnd,
  629. Point2I( blockX * BlockSquareWidth,
  630. blockY * BlockSquareWidth ),
  631. GridLevels,
  632. invDeltaX,
  633. invDeltaY,
  634. startT,
  635. intersectT,
  636. info,
  637. collideEmpty ) )
  638. {
  639. info->normal.z *= BlockSquareWidth * mSquareSize;
  640. info->normal.normalize();
  641. return true;
  642. }
  643. startT = intersectT;
  644. if(intersectT >= 1)
  645. break;
  646. if(nextXInt < nextYInt)
  647. blockX += dx;
  648. else if(nextYInt < nextXInt)
  649. blockY += dy;
  650. else
  651. {
  652. blockX += dx;
  653. blockY += dy;
  654. }
  655. }
  656. return false;
  657. }
  658. struct TerrLOSStackNode
  659. {
  660. F32 startT;
  661. F32 endT;
  662. Point2I blockPos;
  663. U32 level;
  664. };
  665. bool TerrainBlock::castRayBlock( const Point3F &pStart,
  666. const Point3F &pEnd,
  667. const Point2I &aBlockPos,
  668. U32 aLevel,
  669. F32 invDeltaX,
  670. F32 invDeltaY,
  671. F32 aStartT,
  672. F32 aEndT,
  673. RayInfo *info,
  674. bool collideEmpty )
  675. {
  676. const U32 BlockSquareWidth = mFile->mSize;
  677. const U32 GridLevels = mFile->mGridLevels;
  678. const U32 BlockMask = mFile->mSize - 1;
  679. F32 invBlockSize = 1 / F32( BlockSquareWidth );
  680. static Vector<TerrLOSStackNode> stack;
  681. stack.setSize( GridLevels * 3 + 1 );
  682. U32 stackSize = 1;
  683. stack[0].startT = aStartT;
  684. stack[0].endT = aEndT;
  685. stack[0].blockPos = aBlockPos;
  686. stack[0].level = aLevel;
  687. if( !aBlockPos.isZero() )
  688. return false;
  689. while(stackSize--)
  690. {
  691. TerrLOSStackNode *sn = stack.address() + stackSize;
  692. U32 level = sn->level;
  693. F32 startT = sn->startT;
  694. F32 endT = sn->endT;
  695. Point2I blockPos = sn->blockPos;
  696. const TerrainSquare *sq = mFile->findSquare( level, blockPos.x, blockPos.y );
  697. F32 startZ = startT * (pEnd.z - pStart.z) + pStart.z;
  698. F32 endZ = endT * (pEnd.z - pStart.z) + pStart.z;
  699. F32 minHeight = fixedToFloat(sq->minHeight);
  700. if(startZ <= minHeight && endZ <= minHeight)
  701. continue;
  702. F32 maxHeight = fixedToFloat(sq->maxHeight);
  703. if(startZ >= maxHeight && endZ >= maxHeight)
  704. continue;
  705. if ( !collideEmpty && ( sq->flags & TerrainSquare::Empty ) &&
  706. blockPos.x == ( blockPos.x & BlockMask ) && blockPos.y == ( blockPos.y & BlockMask ))
  707. continue;
  708. if(level == 0)
  709. {
  710. F32 xs = blockPos.x * invBlockSize;
  711. F32 ys = blockPos.y * invBlockSize;
  712. F32 zBottomLeft = fixedToFloat( mFile->getHeight(blockPos.x, blockPos.y) );
  713. F32 zBottomRight= fixedToFloat( mFile->getHeight(blockPos.x + 1, blockPos.y) );
  714. F32 zTopLeft = fixedToFloat( mFile->getHeight(blockPos.x, blockPos.y + 1) );
  715. F32 zTopRight = fixedToFloat( mFile->getHeight(blockPos.x + 1, blockPos.y + 1) );
  716. PlaneF p1, p2;
  717. PlaneF divider;
  718. Point3F planePoint;
  719. if(sq->flags & TerrainSquare::Split45)
  720. {
  721. p1.set(zBottomLeft - zBottomRight, zBottomRight - zTopRight, invBlockSize);
  722. p2.set(zTopLeft - zTopRight, zBottomLeft - zTopLeft, invBlockSize);
  723. planePoint.set(xs, ys, zBottomLeft);
  724. divider.x = 1;
  725. divider.y = -1;
  726. divider.z = 0;
  727. }
  728. else
  729. {
  730. p1.set(zTopLeft - zTopRight, zBottomRight - zTopRight, invBlockSize);
  731. p2.set(zBottomLeft - zBottomRight, zBottomLeft - zTopLeft, invBlockSize);
  732. planePoint.set(xs + invBlockSize, ys, zBottomRight);
  733. divider.x = 1;
  734. divider.y = 1;
  735. divider.z = 0;
  736. }
  737. p1.setPoint(planePoint);
  738. p2.setPoint(planePoint);
  739. divider.setPoint(planePoint);
  740. F32 t1 = p1.intersect(pStart, pEnd);
  741. F32 t2 = p2.intersect(pStart, pEnd);
  742. F32 td = divider.intersect(pStart, pEnd);
  743. F32 dStart = divider.distToPlane(pStart);
  744. F32 dEnd = divider.distToPlane(pEnd);
  745. // see if the line crosses the divider
  746. if((dStart >= 0 && dEnd < 0) || (dStart < 0 && dEnd >= 0))
  747. {
  748. if(dStart < 0)
  749. {
  750. F32 temp = t1;
  751. t1 = t2;
  752. t2 = temp;
  753. }
  754. if(t1 >= startT && t1 && t1 <= td && t1 <= endT)
  755. {
  756. info->t = t1;
  757. info->normal = p1;
  758. return true;
  759. }
  760. if(t2 >= td && t2 >= startT && t2 <= endT)
  761. {
  762. info->t = t2;
  763. info->normal = p2;
  764. return true;
  765. }
  766. }
  767. else
  768. {
  769. F32 t;
  770. if(dStart >= 0) {
  771. t = t1;
  772. info->normal = p1;
  773. }
  774. else {
  775. t = t2;
  776. info->normal = p2;
  777. }
  778. if(t >= startT && t <= endT)
  779. {
  780. info->t = t;
  781. return true;
  782. }
  783. }
  784. continue;
  785. }
  786. S32 subSqWidth = 1 << (level - 1);
  787. F32 xIntercept = (blockPos.x + subSqWidth) * invBlockSize;
  788. F32 xInt = calcInterceptX(pStart.x, invDeltaX, xIntercept);
  789. F32 yIntercept = (blockPos.y + subSqWidth) * invBlockSize;
  790. F32 yInt = calcInterceptY(pStart.y, invDeltaY, yIntercept);
  791. F32 startX = startT * (pEnd.x - pStart.x) + pStart.x;
  792. F32 startY = startT * (pEnd.y - pStart.y) + pStart.y;
  793. if(xInt < startT)
  794. xInt = MAX_FLOAT;
  795. if(yInt < startT)
  796. yInt = MAX_FLOAT;
  797. U32 x0 = (startX > xIntercept) * subSqWidth;
  798. U32 y0 = (startY > yIntercept) * subSqWidth;
  799. U32 x1 = subSqWidth - x0;
  800. U32 y1 = subSqWidth - y0;
  801. U32 nextLevel = level - 1;
  802. // push the items on the stack in reverse order of processing
  803. if(xInt > endT && yInt > endT)
  804. {
  805. // only test the square the point started in:
  806. stack[stackSize].blockPos.set(blockPos.x + x0, blockPos.y + y0);
  807. stack[stackSize].level = nextLevel;
  808. stackSize++;
  809. }
  810. else if(xInt < yInt)
  811. {
  812. F32 nextIntersect = endT;
  813. if(yInt <= endT)
  814. {
  815. stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1);
  816. stack[stackSize].startT = yInt;
  817. stack[stackSize].endT = endT;
  818. stack[stackSize].level = nextLevel;
  819. nextIntersect = yInt;
  820. stackSize++;
  821. }
  822. stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y0);
  823. stack[stackSize].startT = xInt;
  824. stack[stackSize].endT = nextIntersect;
  825. stack[stackSize].level = nextLevel;
  826. stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0);
  827. stack[stackSize+1].startT = startT;
  828. stack[stackSize+1].endT = xInt;
  829. stack[stackSize+1].level = nextLevel;
  830. stackSize += 2;
  831. }
  832. else if(yInt < xInt)
  833. {
  834. F32 nextIntersect = endT;
  835. if(xInt <= endT)
  836. {
  837. stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1);
  838. stack[stackSize].startT = xInt;
  839. stack[stackSize].endT = endT;
  840. stack[stackSize].level = nextLevel;
  841. nextIntersect = xInt;
  842. stackSize++;
  843. }
  844. stack[stackSize].blockPos.set(blockPos.x + x0, blockPos.y + y1);
  845. stack[stackSize].startT = yInt;
  846. stack[stackSize].endT = nextIntersect;
  847. stack[stackSize].level = nextLevel;
  848. stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0);
  849. stack[stackSize+1].startT = startT;
  850. stack[stackSize+1].endT = yInt;
  851. stack[stackSize+1].level = nextLevel;
  852. stackSize += 2;
  853. }
  854. else
  855. {
  856. stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1);
  857. stack[stackSize].startT = xInt;
  858. stack[stackSize].endT = endT;
  859. stack[stackSize].level = nextLevel;
  860. stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0);
  861. stack[stackSize+1].startT = startT;
  862. stack[stackSize+1].endT = xInt;
  863. stack[stackSize+1].level = nextLevel;
  864. stackSize += 2;
  865. }
  866. }
  867. return false;
  868. }