terrCollision.cpp 29 KB

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