boundingBox.cxx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. /**
  2. * PANDA 3D SOFTWARE
  3. * Copyright (c) Carnegie Mellon University. All rights reserved.
  4. *
  5. * All use of this software is subject to the terms of the revised BSD
  6. * license. You should have received a copy of this license along
  7. * with this source code in a file named "LICENSE."
  8. *
  9. * @file boundingBox.cxx
  10. * @author drose
  11. * @date 2007-05-31
  12. */
  13. #include "boundingBox.h"
  14. #include "boundingSphere.h"
  15. #include "boundingHexahedron.h"
  16. #include "boundingLine.h"
  17. #include "boundingPlane.h"
  18. #include "config_mathutil.h"
  19. #include "dcast.h"
  20. #include <math.h>
  21. #include <algorithm>
  22. using std::max;
  23. using std::min;
  24. const int BoundingBox::plane_def[6][3] = {
  25. { 0, 4, 5 },
  26. { 4, 6, 7 },
  27. { 6, 2, 3 },
  28. { 2, 0, 1 },
  29. { 1, 5, 7 },
  30. { 2, 6, 4 },
  31. };
  32. TypeHandle BoundingBox::_type_handle;
  33. /**
  34. *
  35. */
  36. BoundingVolume *BoundingBox::
  37. make_copy() const {
  38. return new BoundingBox(*this);
  39. }
  40. /**
  41. *
  42. */
  43. LPoint3 BoundingBox::
  44. get_min() const {
  45. nassertr(!is_empty(), _min);
  46. nassertr(!is_infinite(), _min);
  47. return _min;
  48. }
  49. /**
  50. *
  51. */
  52. LPoint3 BoundingBox::
  53. get_max() const {
  54. nassertr(!is_empty(), _max);
  55. nassertr(!is_infinite(), _max);
  56. return _max;
  57. }
  58. /**
  59. *
  60. */
  61. PN_stdfloat BoundingBox::
  62. get_volume() const {
  63. nassertr(!is_infinite(), 0.0f);
  64. if (is_empty()) {
  65. return 0.0f;
  66. }
  67. // Volume of a box: width x depth x height
  68. return (_max[0] - _min[0]) * (_max[1] - _min[1]) * (_max[2] - _min[2]);
  69. }
  70. /**
  71. *
  72. */
  73. LPoint3 BoundingBox::
  74. get_approx_center() const {
  75. nassertr(!is_empty(), LPoint3::zero());
  76. nassertr(!is_infinite(), LPoint3::zero());
  77. return (_min + _max) * 0.5f;
  78. }
  79. /**
  80. *
  81. */
  82. void BoundingBox::
  83. xform(const LMatrix4 &mat) {
  84. nassertv(!mat.is_nan());
  85. if (!is_empty() && !is_infinite()) {
  86. // We need to transform the eight corners of the cube, and then determine
  87. // the new box.
  88. LPoint3 x = get_point(0) * mat;
  89. LPoint3 n = x;
  90. for (int i = 1; i < 8; ++i) {
  91. LPoint3 p = get_point(i) * mat;
  92. n.set(min(n[0], p[0]), min(n[1], p[1]), min(n[2], p[2]));
  93. x.set(max(x[0], p[0]), max(x[1], p[1]), max(x[2], p[2]));
  94. }
  95. _max = x;
  96. _min = n;
  97. }
  98. }
  99. /**
  100. *
  101. */
  102. void BoundingBox::
  103. output(std::ostream &out) const {
  104. if (is_empty()) {
  105. out << "bbox, empty";
  106. } else if (is_infinite()) {
  107. out << "bbox, infinite";
  108. } else {
  109. out << "bbox, (" << _min << ") to (" << _max << ")";
  110. }
  111. }
  112. /**
  113. * Virtual downcast method. Returns this object as a pointer of the indicated
  114. * type, if it is in fact that type. Returns NULL if it is not that type.
  115. */
  116. const BoundingBox *BoundingBox::
  117. as_bounding_box() const {
  118. return this;
  119. }
  120. /**
  121. *
  122. */
  123. bool BoundingBox::
  124. extend_other(BoundingVolume *other) const {
  125. return other->extend_by_box(this);
  126. }
  127. /**
  128. *
  129. */
  130. bool BoundingBox::
  131. around_other(BoundingVolume *other,
  132. const BoundingVolume **first,
  133. const BoundingVolume **last) const {
  134. return other->around_boxes(first, last);
  135. }
  136. /**
  137. *
  138. */
  139. int BoundingBox::
  140. contains_other(const BoundingVolume *other) const {
  141. return other->contains_box(this);
  142. }
  143. /**
  144. *
  145. */
  146. bool BoundingBox::
  147. extend_by_point(const LPoint3 &point) {
  148. nassertr(!point.is_nan(), false);
  149. if (is_empty()) {
  150. _min = point;
  151. _max = point;
  152. _flags = 0;
  153. } else if (!is_infinite()) {
  154. _min.set(min(_min[0], point[0]), min(_min[1], point[1]), min(_min[2], point[2]));
  155. _max.set(max(_max[0], point[0]), max(_max[1], point[1]), max(_max[2], point[2]));
  156. }
  157. return true;
  158. }
  159. /**
  160. *
  161. */
  162. bool BoundingBox::
  163. extend_by_box(const BoundingBox *box) {
  164. nassertr(!box->is_empty() && !box->is_infinite(), false);
  165. nassertr(!is_infinite(), false);
  166. if (is_empty()) {
  167. _min = box->_min;
  168. _max = box->_max;
  169. _flags = 0;
  170. } else {
  171. _min.set(min(_min[0], box->_min[0]),
  172. min(_min[1], box->_min[1]),
  173. min(_min[2], box->_min[2]));
  174. _max.set(max(_max[0], box->_max[0]),
  175. max(_max[1], box->_max[1]),
  176. max(_max[2], box->_max[2]));
  177. }
  178. return true;
  179. }
  180. /**
  181. *
  182. */
  183. bool BoundingBox::
  184. extend_by_finite(const FiniteBoundingVolume *volume) {
  185. nassertr(!volume->is_empty() && !volume->is_infinite(), false);
  186. LVector3 min1 = volume->get_min();
  187. LVector3 max1 = volume->get_max();
  188. if (is_empty()) {
  189. _min = min1;
  190. _max = max1;
  191. _flags = 0;
  192. } else {
  193. _min.set(min(_min[0], min1[0]),
  194. min(_min[1], min1[1]),
  195. min(_min[2], min1[2]));
  196. _max.set(max(_max[0], max1[0]),
  197. max(_max[1], max1[1]),
  198. max(_max[2], max1[2]));
  199. }
  200. return true;
  201. }
  202. /**
  203. *
  204. */
  205. bool BoundingBox::
  206. around_points(const LPoint3 *first, const LPoint3 *last) {
  207. nassertr(first != last, false);
  208. // Get the minmax of all the points to construct a bounding box.
  209. const LPoint3 *p = first;
  210. #ifndef NDEBUG
  211. // Skip any NaN points.
  212. int skipped_nan = 0;
  213. while (p != last && (*p).is_nan()) {
  214. ++p;
  215. ++skipped_nan;
  216. }
  217. if (p == last) {
  218. mathutil_cat.warning()
  219. << "BoundingBox around NaN\n";
  220. return false;
  221. }
  222. #endif
  223. _min = *p;
  224. _max = *p;
  225. ++p;
  226. #ifndef NDEBUG
  227. // Skip more NaN points.
  228. while (p != last && (*p).is_nan()) {
  229. ++p;
  230. ++skipped_nan;
  231. }
  232. #endif
  233. while (p != last) {
  234. #ifndef NDEBUG
  235. // Skip more NaN points.
  236. if ((*p).is_nan()) {
  237. ++skipped_nan;
  238. } else
  239. #endif
  240. {
  241. _min.set(min(_min[0], (*p)[0]),
  242. min(_min[1], (*p)[1]),
  243. min(_min[2], (*p)[2]));
  244. _max.set(max(_max[0], (*p)[0]),
  245. max(_max[1], (*p)[1]),
  246. max(_max[2], (*p)[2]));
  247. }
  248. ++p;
  249. }
  250. #ifndef NDEBUG
  251. if (skipped_nan != 0) {
  252. mathutil_cat.warning()
  253. << "BoundingBox ignored " << skipped_nan << " NaN points of "
  254. << (last - first) << " total.\n";
  255. }
  256. #endif
  257. _flags = 0;
  258. return true;
  259. }
  260. /**
  261. *
  262. */
  263. bool BoundingBox::
  264. around_finite(const BoundingVolume **first,
  265. const BoundingVolume **last) {
  266. nassertr(first != last, false);
  267. // We're given a set of bounding volumes, at least the first one of which is
  268. // guaranteed to be finite and nonempty. Some others may not be.
  269. // First, get the box of all the points to construct a bounding box.
  270. const BoundingVolume **p = first;
  271. nassertr(!(*p)->is_empty() && !(*p)->is_infinite(), false);
  272. const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
  273. _min = vol->get_min();
  274. _max = vol->get_max();
  275. for (++p; p != last; ++p) {
  276. nassertr(!(*p)->is_infinite(), false);
  277. if (!(*p)->is_empty()) {
  278. const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
  279. LPoint3 min1 = vol->get_min();
  280. LPoint3 max1 = vol->get_max();
  281. _min.set(min(_min[0], min1[0]),
  282. min(_min[1], min1[1]),
  283. min(_min[2], min1[2]));
  284. _max.set(max(_max[0], max1[0]),
  285. max(_max[1], max1[1]),
  286. max(_max[2], max1[2]));
  287. }
  288. }
  289. _flags = 0;
  290. return true;
  291. }
  292. /**
  293. *
  294. */
  295. int BoundingBox::
  296. contains_point(const LPoint3 &point) const {
  297. nassertr(!point.is_nan(), IF_no_intersection);
  298. if (is_empty()) {
  299. return IF_no_intersection;
  300. } else if (is_infinite()) {
  301. return IF_possible | IF_some | IF_all;
  302. } else {
  303. if (point[0] >= _min[0] && point[0] <= _max[0] &&
  304. point[1] >= _min[1] && point[1] <= _max[1] &&
  305. point[2] >= _min[2] && point[2] <= _max[2]) {
  306. return IF_possible | IF_some | IF_all;
  307. } else {
  308. return IF_no_intersection;
  309. }
  310. }
  311. }
  312. /**
  313. *
  314. */
  315. int BoundingBox::
  316. contains_lineseg(const LPoint3 &a, const LPoint3 &b) const {
  317. nassertr(!a.is_nan() && !b.is_nan(), IF_no_intersection);
  318. if (a == b) {
  319. return contains_point(a);
  320. }
  321. if (is_empty()) {
  322. return IF_no_intersection;
  323. } else if (is_infinite()) {
  324. return IF_possible | IF_some | IF_all;
  325. } else {
  326. // Set a bit for each plane a and b are on the wrong side of.
  327. unsigned int a_bits = 0;
  328. if (a[0] < _min[0]) {
  329. a_bits |= 0x01;
  330. } else if (a[0] > _max[0]) {
  331. a_bits |= 0x02;
  332. }
  333. if (a[1] < _min[1]) {
  334. a_bits |= 0x04;
  335. } else if (a[1] > _max[1]) {
  336. a_bits |= 0x08;
  337. }
  338. if (a[2] < _min[2]) {
  339. a_bits |= 0x10;
  340. } else if (a[2] > _max[2]) {
  341. a_bits |= 0x20;
  342. }
  343. unsigned int b_bits = 0;
  344. if (b[0] < _min[0]) {
  345. b_bits |= 0x01;
  346. } else if (b[0] > _max[0]) {
  347. b_bits |= 0x02;
  348. }
  349. if (b[1] < _min[1]) {
  350. b_bits |= 0x04;
  351. } else if (b[1] > _max[1]) {
  352. b_bits |= 0x08;
  353. }
  354. if (b[2] < _min[2]) {
  355. b_bits |= 0x10;
  356. } else if (b[2] > _max[2]) {
  357. b_bits |= 0x20;
  358. }
  359. if ((a_bits & b_bits) != 0) {
  360. // If there are any bits in common, the segment is wholly outside the
  361. // box (both points are on the wrong side of the same plane).
  362. return IF_no_intersection;
  363. } else if ((a_bits | b_bits) == 0) {
  364. // If there are no bits at all, the segment is wholly within the box.
  365. return IF_possible | IF_some | IF_all;
  366. } else if (a_bits == 0 || b_bits == 0) {
  367. // If either point is within the box, the segment is partially within
  368. // the box.
  369. return IF_possible | IF_some;
  370. } else {
  371. unsigned int differ = (a_bits ^ b_bits);
  372. if (differ == 0x03 || differ == 0x0c || differ == 0x30) {
  373. // If the line segment stretches straight across the box, the segment
  374. // is partially within.
  375. return IF_possible | IF_some;
  376. } else {
  377. // Otherwise, it's hard to tell whether it does or doesn't.
  378. return IF_possible;
  379. }
  380. }
  381. }
  382. }
  383. /**
  384. * Double-dispatch support: called by contains_other() when the type we're
  385. * testing for intersection is known to be a box.
  386. */
  387. int BoundingBox::
  388. contains_box(const BoundingBox *box) const {
  389. nassertr(!is_empty() && !is_infinite(), 0);
  390. nassertr(!box->is_empty() && !box->is_infinite(), 0);
  391. const LPoint3 &min1 = box->get_minq();
  392. const LPoint3 &max1 = box->get_maxq();
  393. if (min1[0] >= _min[0] && max1[0] <= _max[0] &&
  394. min1[1] >= _min[1] && max1[1] <= _max[1] &&
  395. min1[2] >= _min[2] && max1[2] <= _max[2]) {
  396. // The other volume is completely within this volume.
  397. return IF_possible | IF_some | IF_all;
  398. } else if (max1[0] >= _min[0] && min1[0] <= _max[0] &&
  399. max1[1] >= _min[1] && min1[1] <= _max[1] &&
  400. max1[2] >= _min[2] && min1[2] <= _max[2]) {
  401. // The other volume is partially within this volume.
  402. return IF_possible;
  403. } else {
  404. // The other volume is not within this volume.
  405. return IF_no_intersection;
  406. }
  407. }
  408. /**
  409. * Double-dispatch support: called by contains_other() when the type we're
  410. * testing for intersection is known to be a hexahedron.
  411. */
  412. int BoundingBox::
  413. contains_hexahedron(const BoundingHexahedron *hexahedron) const {
  414. // First, try the quick bounding-box test. If that's decisive, we'll accept
  415. // it.
  416. int result = contains_finite(hexahedron);
  417. if (result == IF_no_intersection || ((result & IF_all) != 0)) {
  418. return result;
  419. }
  420. // If that was inconclusive, we'll look more closely with the somewhat more
  421. // expensive reverse answer.
  422. return hexahedron->contains_box(this) & ~IF_all;
  423. }
  424. /**
  425. * Double-dispatch support: called by contains_other() when the type we're
  426. * testing for intersection is known to be a line.
  427. */
  428. int BoundingBox::
  429. contains_line(const BoundingLine *line) const {
  430. return line->contains_box(this) & ~IF_all;
  431. }
  432. /**
  433. * Double-dispatch support: called by contains_other() when the type we're
  434. * testing for intersection is known to be a plane.
  435. */
  436. int BoundingBox::
  437. contains_plane(const BoundingPlane *plane) const {
  438. return plane->contains_box(this) & ~IF_all;
  439. }
  440. /**
  441. *
  442. */
  443. int BoundingBox::
  444. contains_finite(const FiniteBoundingVolume *volume) const {
  445. nassertr(!is_empty() && !is_infinite(), 0);
  446. nassertr(!volume->is_empty() && !volume->is_infinite(), 0);
  447. LPoint3 min1 = volume->get_min();
  448. LPoint3 max1 = volume->get_max();
  449. if (min1[0] >= _min[0] && max1[0] <= _max[0] &&
  450. min1[1] >= _min[1] && max1[1] <= _max[1] &&
  451. min1[2] >= _min[2] && max1[2] <= _max[2]) {
  452. // The other volume is completely within this volume.
  453. return IF_possible | IF_some | IF_all;
  454. } else if (max1[0] >= _min[0] && min1[0] <= _max[0] &&
  455. max1[1] >= _min[1] && min1[1] <= _max[1] &&
  456. max1[2] >= _min[2] && min1[2] <= _max[2]) {
  457. // The other volume is partially within this volume.
  458. return IF_possible;
  459. } else {
  460. // The other volume is not within this volume.
  461. return IF_no_intersection;
  462. }
  463. }