BsAABox.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsAABox.h"
  4. #include "BsRay.h"
  5. #include "BsPlane.h"
  6. #include "BsSphere.h"
  7. #include "BsMath.h"
  8. namespace bs
  9. {
  10. const AABox AABox::BOX_EMPTY;
  11. const UINT32 AABox::CUBE_INDICES[36] =
  12. {
  13. // Near
  14. NEAR_LEFT_BOTTOM, NEAR_LEFT_TOP, NEAR_RIGHT_TOP,
  15. NEAR_LEFT_BOTTOM, NEAR_RIGHT_TOP, NEAR_RIGHT_BOTTOM,
  16. // Far
  17. FAR_RIGHT_BOTTOM, FAR_RIGHT_TOP, FAR_LEFT_TOP,
  18. FAR_RIGHT_BOTTOM, FAR_LEFT_TOP, FAR_LEFT_BOTTOM,
  19. // Left
  20. FAR_LEFT_BOTTOM, FAR_LEFT_TOP, NEAR_LEFT_TOP,
  21. FAR_LEFT_BOTTOM, NEAR_LEFT_TOP, NEAR_LEFT_BOTTOM,
  22. // Right
  23. NEAR_RIGHT_BOTTOM, NEAR_RIGHT_TOP, FAR_RIGHT_TOP,
  24. NEAR_RIGHT_BOTTOM, FAR_RIGHT_TOP, FAR_RIGHT_BOTTOM,
  25. // Top
  26. FAR_LEFT_TOP, FAR_RIGHT_TOP, NEAR_RIGHT_TOP,
  27. FAR_LEFT_TOP, NEAR_RIGHT_TOP, NEAR_LEFT_TOP,
  28. // Bottom
  29. NEAR_LEFT_BOTTOM, NEAR_RIGHT_BOTTOM, FAR_RIGHT_BOTTOM,
  30. NEAR_LEFT_BOTTOM, FAR_RIGHT_BOTTOM, FAR_LEFT_BOTTOM
  31. };
  32. AABox::AABox()
  33. :mMinimum(Vector3::ZERO), mMaximum(Vector3::ONE)
  34. {
  35. // Default to a null box
  36. setMin(Vector3(-0.5f, -0.5f, -0.5f));
  37. setMax(Vector3(0.5f, 0.5f, 0.5f));
  38. }
  39. AABox::AABox(const AABox& copy)
  40. :mMinimum(Vector3::ZERO), mMaximum(Vector3::ONE)
  41. {
  42. setExtents(copy.mMinimum, copy.mMaximum);
  43. }
  44. AABox::AABox(const Vector3& min, const Vector3& max)
  45. :mMinimum(Vector3::ZERO), mMaximum(Vector3::ONE)
  46. {
  47. setExtents(min, max);
  48. }
  49. AABox& AABox::operator=(const AABox& rhs)
  50. {
  51. setExtents(rhs.mMinimum, rhs.mMaximum);
  52. return *this;
  53. }
  54. void AABox::setExtents(const Vector3& min, const Vector3& max)
  55. {
  56. mMinimum = min;
  57. mMaximum = max;
  58. }
  59. void AABox::scale(const Vector3& s)
  60. {
  61. Vector3 center = getCenter();
  62. Vector3 min = center + (mMinimum - center) * s;
  63. Vector3 max = center + (mMaximum - center) * s;
  64. setExtents(min, max);
  65. }
  66. Vector3 AABox::getCorner(Corner cornerToGet) const
  67. {
  68. switch(cornerToGet)
  69. {
  70. case FAR_LEFT_BOTTOM:
  71. return mMinimum;
  72. case FAR_LEFT_TOP:
  73. return Vector3(mMinimum.x, mMaximum.y, mMinimum.z);
  74. case FAR_RIGHT_TOP:
  75. return Vector3(mMaximum.x, mMaximum.y, mMinimum.z);
  76. case FAR_RIGHT_BOTTOM:
  77. return Vector3(mMaximum.x, mMinimum.y, mMinimum.z);
  78. case NEAR_RIGHT_BOTTOM:
  79. return Vector3(mMaximum.x, mMinimum.y, mMaximum.z);
  80. case NEAR_LEFT_BOTTOM:
  81. return Vector3(mMinimum.x, mMinimum.y, mMaximum.z);
  82. case NEAR_LEFT_TOP:
  83. return Vector3(mMinimum.x, mMaximum.y, mMaximum.z);
  84. case NEAR_RIGHT_TOP:
  85. return mMaximum;
  86. default:
  87. return Vector3(BsZero);
  88. }
  89. }
  90. void AABox::merge(const AABox& rhs)
  91. {
  92. Vector3 min = mMinimum;
  93. Vector3 max = mMaximum;
  94. max.ceil(rhs.mMaximum);
  95. min.floor(rhs.mMinimum);
  96. setExtents(min, max);
  97. }
  98. void AABox::merge(const Vector3& point)
  99. {
  100. mMaximum.ceil(point);
  101. mMinimum.floor(point);
  102. }
  103. void AABox::transform(const Matrix4& matrix)
  104. {
  105. // Getting the old values so that we can use the existing merge method.
  106. Vector3 oldMin = mMinimum;
  107. Vector3 oldMax = mMaximum;
  108. Vector3 currentCorner;
  109. // We sequentially compute the corners in the following order :
  110. // 0, 6, 5, 1, 2, 4, 7, 3
  111. // This sequence allows us to only change one member at a time to get at all corners.
  112. // For each one, we transform it using the matrix
  113. // Which gives the resulting point and merge the resulting point.
  114. // First corner
  115. // min min min
  116. currentCorner = oldMin;
  117. merge(matrix.multiplyAffine(currentCorner));
  118. // min,min,max
  119. currentCorner.z = oldMax.z;
  120. merge(matrix.multiplyAffine(currentCorner));
  121. // min max max
  122. currentCorner.y = oldMax.y;
  123. merge(matrix.multiplyAffine(currentCorner));
  124. // min max min
  125. currentCorner.z = oldMin.z;
  126. merge(matrix.multiplyAffine(currentCorner));
  127. // max max min
  128. currentCorner.x = oldMax.x;
  129. merge(matrix.multiplyAffine(currentCorner));
  130. // max max max
  131. currentCorner.z = oldMax.z;
  132. merge(matrix.multiplyAffine(currentCorner));
  133. // max min max
  134. currentCorner.y = oldMin.y;
  135. merge(matrix.multiplyAffine(currentCorner));
  136. // max min min
  137. currentCorner.z = oldMin.z;
  138. merge(matrix.multiplyAffine(currentCorner));
  139. }
  140. void AABox::transformAffine(const Matrix4& m)
  141. {
  142. BS_ASSERT(m.isAffine());
  143. Vector3 centre = getCenter();
  144. Vector3 halfSize = getHalfSize();
  145. Vector3 newCentre = m.multiplyAffine(centre);
  146. Vector3 newHalfSize(
  147. Math::abs(m[0][0]) * halfSize.x + Math::abs(m[0][1]) * halfSize.y + Math::abs(m[0][2]) * halfSize.z,
  148. Math::abs(m[1][0]) * halfSize.x + Math::abs(m[1][1]) * halfSize.y + Math::abs(m[1][2]) * halfSize.z,
  149. Math::abs(m[2][0]) * halfSize.x + Math::abs(m[2][1]) * halfSize.y + Math::abs(m[2][2]) * halfSize.z);
  150. setExtents(newCentre - newHalfSize, newCentre + newHalfSize);
  151. }
  152. bool AABox::intersects(const AABox& b2) const
  153. {
  154. // Use up to 6 separating planes
  155. if (mMaximum.x < b2.mMinimum.x)
  156. return false;
  157. if (mMaximum.y < b2.mMinimum.y)
  158. return false;
  159. if (mMaximum.z < b2.mMinimum.z)
  160. return false;
  161. if (mMinimum.x > b2.mMaximum.x)
  162. return false;
  163. if (mMinimum.y > b2.mMaximum.y)
  164. return false;
  165. if (mMinimum.z > b2.mMaximum.z)
  166. return false;
  167. // Otherwise, must be intersecting
  168. return true;
  169. }
  170. bool AABox::intersects(const Sphere& sphere) const
  171. {
  172. // Use splitting planes
  173. const Vector3& center = sphere.getCenter();
  174. float radius = sphere.getRadius();
  175. const Vector3& min = getMin();
  176. const Vector3& max = getMax();
  177. // Arvo's algorithm
  178. float s, d = 0;
  179. for (int i = 0; i < 3; ++i)
  180. {
  181. if (center[i] < min[i])
  182. {
  183. s = center[i] - min[i];
  184. d += s * s;
  185. }
  186. else if(center[i] > max[i])
  187. {
  188. s = center[i] - max[i];
  189. d += s * s;
  190. }
  191. }
  192. return d <= radius * radius;
  193. }
  194. bool AABox::intersects(const Plane& p) const
  195. {
  196. return (p.getSide(*this) == Plane::BOTH_SIDE);
  197. }
  198. std::pair<bool, float> AABox::intersects(const Ray& ray) const
  199. {
  200. float lowt = 0.0f;
  201. float t;
  202. bool hit = false;
  203. Vector3 hitpoint(BsZero);
  204. const Vector3& min = getMin();
  205. const Vector3& max = getMax();
  206. const Vector3& rayorig = ray.getOrigin();
  207. const Vector3& raydir = ray.getDirection();
  208. // Check origin inside first
  209. if ((rayorig.x > min.x && rayorig.y > min.y && rayorig.z > min.z) && (rayorig.x < max.x && rayorig.y < max.y && rayorig.z < max.z))
  210. {
  211. return std::pair<bool, float>(true, 0.0f);
  212. }
  213. // Check each face in turn, only check closest 3
  214. // Min x
  215. if (rayorig.x <= min.x && raydir.x > 0)
  216. {
  217. t = (min.x - rayorig.x) / raydir.x;
  218. if (t >= 0)
  219. {
  220. // Substitute t back into ray and check bounds and dist
  221. hitpoint = rayorig + raydir * t;
  222. if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
  223. hitpoint.z >= min.z && hitpoint.z <= max.z &&
  224. (!hit || t < lowt))
  225. {
  226. hit = true;
  227. lowt = t;
  228. }
  229. }
  230. }
  231. // Max x
  232. if (rayorig.x >= max.x && raydir.x < 0)
  233. {
  234. t = (max.x - rayorig.x) / raydir.x;
  235. if (t >= 0)
  236. {
  237. // Substitute t back into ray and check bounds and dist
  238. hitpoint = rayorig + raydir * t;
  239. if (hitpoint.y >= min.y && hitpoint.y <= max.y &&
  240. hitpoint.z >= min.z && hitpoint.z <= max.z &&
  241. (!hit || t < lowt))
  242. {
  243. hit = true;
  244. lowt = t;
  245. }
  246. }
  247. }
  248. // Min y
  249. if (rayorig.y <= min.y && raydir.y > 0)
  250. {
  251. t = (min.y - rayorig.y) / raydir.y;
  252. if (t >= 0)
  253. {
  254. // Substitute t back into ray and check bounds and dist
  255. hitpoint = rayorig + raydir * t;
  256. if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
  257. hitpoint.z >= min.z && hitpoint.z <= max.z &&
  258. (!hit || t < lowt))
  259. {
  260. hit = true;
  261. lowt = t;
  262. }
  263. }
  264. }
  265. // Max y
  266. if (rayorig.y >= max.y && raydir.y < 0)
  267. {
  268. t = (max.y - rayorig.y) / raydir.y;
  269. if (t >= 0)
  270. {
  271. // Substitute t back into ray and check bounds and dist
  272. hitpoint = rayorig + raydir * t;
  273. if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
  274. hitpoint.z >= min.z && hitpoint.z <= max.z &&
  275. (!hit || t < lowt))
  276. {
  277. hit = true;
  278. lowt = t;
  279. }
  280. }
  281. }
  282. // Min z
  283. if (rayorig.z <= min.z && raydir.z > 0)
  284. {
  285. t = (min.z - rayorig.z) / raydir.z;
  286. if (t >= 0)
  287. {
  288. // Substitute t back into ray and check bounds and dist
  289. hitpoint = rayorig + raydir * t;
  290. if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
  291. hitpoint.y >= min.y && hitpoint.y <= max.y &&
  292. (!hit || t < lowt))
  293. {
  294. hit = true;
  295. lowt = t;
  296. }
  297. }
  298. }
  299. // Max z
  300. if (rayorig.z >= max.z && raydir.z < 0)
  301. {
  302. t = (max.z - rayorig.z) / raydir.z;
  303. if (t >= 0)
  304. {
  305. // Substitute t back into ray and check bounds and dist
  306. hitpoint = rayorig + raydir * t;
  307. if (hitpoint.x >= min.x && hitpoint.x <= max.x &&
  308. hitpoint.y >= min.y && hitpoint.y <= max.y &&
  309. (!hit || t < lowt))
  310. {
  311. hit = true;
  312. lowt = t;
  313. }
  314. }
  315. }
  316. return std::pair<bool, float>(hit, lowt);
  317. }
  318. bool AABox::intersects(const Ray& ray, float& d1, float& d2) const
  319. {
  320. const Vector3& min = getMin();
  321. const Vector3& max = getMax();
  322. const Vector3& rayorig = ray.getOrigin();
  323. const Vector3& raydir = ray.getDirection();
  324. Vector3 absDir;
  325. absDir[0] = Math::abs(raydir[0]);
  326. absDir[1] = Math::abs(raydir[1]);
  327. absDir[2] = Math::abs(raydir[2]);
  328. // Sort the axis, ensure check minimise floating error axis first
  329. int imax = 0, imid = 1, imin = 2;
  330. if (absDir[0] < absDir[2])
  331. {
  332. imax = 2;
  333. imin = 0;
  334. }
  335. if (absDir[1] < absDir[imin])
  336. {
  337. imid = imin;
  338. imin = 1;
  339. }
  340. else if (absDir[1] > absDir[imax])
  341. {
  342. imid = imax;
  343. imax = 1;
  344. }
  345. float start = 0, end = Math::POS_INFINITY;
  346. #define _CALC_AXIS(i) \
  347. do { \
  348. float denom = 1 / raydir[i]; \
  349. float newstart = (min[i] - rayorig[i]) * denom; \
  350. float newend = (max[i] - rayorig[i]) * denom; \
  351. if (newstart > newend) std::swap(newstart, newend); \
  352. if (newstart > end || newend < start) return false; \
  353. if (newstart > start) start = newstart; \
  354. if (newend < end) end = newend; \
  355. } while(0)
  356. // Check each axis in turn
  357. _CALC_AXIS(imax);
  358. if (absDir[imid] < std::numeric_limits<float>::epsilon())
  359. {
  360. // Parallel with middle and minimise axis, check bounds only
  361. if (rayorig[imid] < min[imid] || rayorig[imid] > max[imid] ||
  362. rayorig[imin] < min[imin] || rayorig[imin] > max[imin])
  363. return false;
  364. }
  365. else
  366. {
  367. _CALC_AXIS(imid);
  368. if (absDir[imin] < std::numeric_limits<float>::epsilon())
  369. {
  370. // Parallel with minimise axis, check bounds only
  371. if (rayorig[imin] < min[imin] || rayorig[imin] > max[imin])
  372. return false;
  373. }
  374. else
  375. {
  376. _CALC_AXIS(imin);
  377. }
  378. }
  379. #undef _CALC_AXIS
  380. d1 = start;
  381. d2 = end;
  382. return true;
  383. }
  384. Vector3 AABox::getCenter() const
  385. {
  386. return Vector3(
  387. (mMaximum.x + mMinimum.x) * 0.5f,
  388. (mMaximum.y + mMinimum.y) * 0.5f,
  389. (mMaximum.z + mMinimum.z) * 0.5f);
  390. }
  391. Vector3 AABox::getSize() const
  392. {
  393. return mMaximum - mMinimum;
  394. }
  395. Vector3 AABox::getHalfSize() const
  396. {
  397. return (mMaximum - mMinimum) * 0.5;
  398. }
  399. float AABox::getRadius() const
  400. {
  401. return (mMaximum - mMinimum).length();
  402. }
  403. float AABox::getVolume() const
  404. {
  405. Vector3 diff = mMaximum - mMinimum;
  406. return diff.x * diff.y * diff.z;
  407. }
  408. bool AABox::contains(const Vector3& v) const
  409. {
  410. return mMinimum.x <= v.x && v.x <= mMaximum.x &&
  411. mMinimum.y <= v.y && v.y <= mMaximum.y &&
  412. mMinimum.z <= v.z && v.z <= mMaximum.z;
  413. }
  414. bool AABox::contains(const AABox& other) const
  415. {
  416. return this->mMinimum.x <= other.mMinimum.x &&
  417. this->mMinimum.y <= other.mMinimum.y &&
  418. this->mMinimum.z <= other.mMinimum.z &&
  419. other.mMaximum.x <= this->mMaximum.x &&
  420. other.mMaximum.y <= this->mMaximum.y &&
  421. other.mMaximum.z <= this->mMaximum.z;
  422. }
  423. bool AABox::operator== (const AABox& rhs) const
  424. {
  425. return this->mMinimum == rhs.mMinimum &&
  426. this->mMaximum == rhs.mMaximum;
  427. }
  428. bool AABox::operator!= (const AABox& rhs) const
  429. {
  430. return !(*this == rhs);
  431. }
  432. }