BsAABox.cpp 12 KB

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