collisionBox.cxx 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237
  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 collisionBox.cxx
  10. * @author amith tudur
  11. * @date 2009-07-31
  12. */
  13. #include "collisionBox.h"
  14. #include "collisionLine.h"
  15. #include "collisionRay.h"
  16. #include "collisionSphere.h"
  17. #include "collisionSegment.h"
  18. #include "collisionHandler.h"
  19. #include "collisionEntry.h"
  20. #include "config_collide.h"
  21. #include "boundingSphere.h"
  22. #include "datagram.h"
  23. #include "datagramIterator.h"
  24. #include "bamReader.h"
  25. #include "bamWriter.h"
  26. #include "nearly_zero.h"
  27. #include "cmath.h"
  28. #include "mathNumbers.h"
  29. #include "geom.h"
  30. #include "geomTriangles.h"
  31. #include "geomVertexWriter.h"
  32. #include "config_mathutil.h"
  33. #include "dcast.h"
  34. #include <math.h>
  35. PStatCollector CollisionBox::_volume_pcollector("Collision Volumes:CollisionBox");
  36. PStatCollector CollisionBox::_test_pcollector("Collision Tests:CollisionBox");
  37. TypeHandle CollisionBox::_type_handle;
  38. const int CollisionBox::plane_def[6][4] = {
  39. {0, 4, 5, 1},
  40. {4, 6, 7, 5},
  41. {6, 2, 3, 7},
  42. {2, 0, 1, 3},
  43. {1, 5, 7, 3},
  44. {2, 6, 4, 0},
  45. };
  46. /**
  47. *
  48. */
  49. CollisionSolid *CollisionBox::
  50. make_copy() {
  51. return new CollisionBox(*this);
  52. }
  53. /**
  54. * Compute parameters for each of the box's sides
  55. */
  56. void CollisionBox::
  57. setup_box(){
  58. for(int plane = 0; plane < 6; plane++) {
  59. LPoint3 array[4];
  60. array[0] = get_point(plane_def[plane][0]);
  61. array[1] = get_point(plane_def[plane][1]);
  62. array[2] = get_point(plane_def[plane][2]);
  63. array[3] = get_point(plane_def[plane][3]);
  64. setup_points(array, array+4, plane);
  65. }
  66. }
  67. /**
  68. * Computes the plane and 2d projection of points that make up this side.
  69. */
  70. void CollisionBox::
  71. setup_points(const LPoint3 *begin, const LPoint3 *end, int plane) {
  72. int num_points = end - begin;
  73. nassertv(num_points >= 3);
  74. _points[plane].clear();
  75. // Construct a matrix that rotates the points from the (X,0,Z) plane into
  76. // the 3-d plane.
  77. LMatrix4 to_3d_mat;
  78. calc_to_3d_mat(to_3d_mat, plane);
  79. // And the inverse matrix rotates points from 3-d space into the 2-d plane.
  80. _to_2d_mat[plane].invert_from(to_3d_mat);
  81. // Now project all of the points onto the 2-d plane.
  82. const LPoint3 *pi;
  83. for (pi = begin; pi != end; ++pi) {
  84. LPoint3 point = (*pi) * _to_2d_mat[plane];
  85. _points[plane].push_back(PointDef(point[0], point[2]));
  86. }
  87. nassertv(_points[plane].size() >= 3);
  88. #ifndef NDEBUG
  89. /*
  90. // Now make sure the points define a convex polygon.
  91. if (is_concave()) {
  92. collide_cat.error() << "Invalid concave CollisionPolygon defined:\n";
  93. const LPoint3 *pi;
  94. for (pi = begin; pi != end; ++pi) {
  95. collide_cat.error(false) << " " << (*pi) << "\n";
  96. }
  97. collide_cat.error(false)
  98. << " normal " << normal << " with length " << normal.length() << "\n";
  99. _points.clear();
  100. }
  101. */
  102. #endif
  103. compute_vectors(_points[plane]);
  104. }
  105. /**
  106. * First Dispatch point for box as a FROM object
  107. */
  108. PT(CollisionEntry) CollisionBox::
  109. test_intersection(const CollisionEntry &entry) const {
  110. return entry.get_into()->test_intersection_from_box(entry);
  111. }
  112. /**
  113. * Transforms the solid by the indicated matrix.
  114. */
  115. void CollisionBox::
  116. xform(const LMatrix4 &mat) {
  117. _min = _min * mat;
  118. _max = _max * mat;
  119. _center = _center * mat;
  120. for(int v = 0; v < 8; v++) {
  121. _vertex[v] = _vertex[v] * mat;
  122. }
  123. for(int p = 0; p < 6 ; p++) {
  124. _planes[p] = set_plane(p);
  125. }
  126. _x = _vertex[0].get_x() - _center.get_x();
  127. _y = _vertex[0].get_y() - _center.get_y();
  128. _z = _vertex[0].get_z() - _center.get_z();
  129. _radius = sqrt(_x * _x + _y * _y + _z * _z);
  130. setup_box();
  131. mark_viz_stale();
  132. mark_internal_bounds_stale();
  133. }
  134. /**
  135. * Returns the point in space deemed to be the "origin" of the solid for
  136. * collision purposes. The closest intersection point to this origin point is
  137. * considered to be the most significant.
  138. */
  139. LPoint3 CollisionBox::
  140. get_collision_origin() const {
  141. return _center;
  142. }
  143. /**
  144. * Returns a PStatCollector that is used to count the number of bounding
  145. * volume tests made against a solid of this type in a given frame.
  146. */
  147. PStatCollector &CollisionBox::
  148. get_volume_pcollector() {
  149. return _volume_pcollector;
  150. }
  151. /**
  152. * Returns a PStatCollector that is used to count the number of intersection
  153. * tests made against a solid of this type in a given frame.
  154. */
  155. PStatCollector &CollisionBox::
  156. get_test_pcollector() {
  157. return _test_pcollector;
  158. }
  159. /**
  160. *
  161. */
  162. void CollisionBox::
  163. output(ostream &out) const {
  164. }
  165. /**
  166. * Sphere is chosen as the Bounding Volume type for speed and efficiency
  167. */
  168. PT(BoundingVolume) CollisionBox::
  169. compute_internal_bounds() const {
  170. return new BoundingSphere(_center, _radius);
  171. }
  172. /**
  173. * Double dispatch point for sphere as FROM object
  174. */
  175. PT(CollisionEntry) CollisionBox::
  176. test_intersection_from_sphere(const CollisionEntry &entry) const {
  177. const CollisionSphere *sphere;
  178. DCAST_INTO_R(sphere, entry.get_from(), NULL);
  179. CPT(TransformState) wrt_space = entry.get_wrt_space();
  180. CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space();
  181. const LMatrix4 &wrt_mat = wrt_space->get_mat();
  182. LPoint3 orig_center = sphere->get_center() * wrt_mat;
  183. LPoint3 from_center = orig_center;
  184. bool moved_from_center = false;
  185. PN_stdfloat t = 1.0f;
  186. LPoint3 contact_point(from_center);
  187. PN_stdfloat actual_t = 1.0f;
  188. LVector3 from_radius_v =
  189. LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
  190. PN_stdfloat from_radius_2 = from_radius_v.length_squared();
  191. PN_stdfloat from_radius = csqrt(from_radius_2);
  192. int ip;
  193. PN_stdfloat max_dist = 0.0;
  194. PN_stdfloat dist = 0.0;
  195. bool intersect;
  196. LPlane plane;
  197. LVector3 normal;
  198. for(ip = 0, intersect = false; ip < 6 && !intersect; ip++) {
  199. plane = get_plane( ip );
  200. if (_points[ip].size() < 3) {
  201. continue;
  202. }
  203. if (wrt_prev_space != wrt_space) {
  204. // If we have a delta between the previous position and the current
  205. // position, we use that to determine some more properties of the
  206. // collision.
  207. LPoint3 b = from_center;
  208. LPoint3 a = sphere->get_center() * wrt_prev_space->get_mat();
  209. LVector3 delta = b - a;
  210. // First, there is no collision if the "from" object is definitely
  211. // moving in the same direction as the plane's normal.
  212. PN_stdfloat dot = delta.dot(plane.get_normal());
  213. if (dot > 0.1f) {
  214. continue; // no intersection
  215. }
  216. if (IS_NEARLY_ZERO(dot)) {
  217. // If we're moving parallel to the plane, the sphere is tested at its
  218. // final point. Leave it as it is.
  219. } else {
  220. /*
  221. * Otherwise, we're moving into the plane; the sphere is tested at the point
  222. * along its path that is closest to intersecting the plane. This may be the
  223. * actual intersection point, or it may be the starting point or the final
  224. * point. dot is equal to the (negative) magnitude of 'delta' along the
  225. * direction of the plane normal t = ratio of (distance from start pos to
  226. * plane) to (distance from start pos to end pos), along axis of plane normal
  227. */
  228. PN_stdfloat dist_to_p = plane.dist_to_plane(a);
  229. t = (dist_to_p / -dot);
  230. // also compute the actual contact point and time of contact for
  231. // handlers that need it
  232. actual_t = ((dist_to_p - from_radius) / -dot);
  233. actual_t = min((PN_stdfloat)1.0, max((PN_stdfloat)0.0, actual_t));
  234. contact_point = a + (actual_t * delta);
  235. if (t >= 1.0f) {
  236. // Leave it where it is.
  237. } else if (t < 0.0f) {
  238. from_center = a;
  239. moved_from_center = true;
  240. } else {
  241. from_center = a + t * delta;
  242. moved_from_center = true;
  243. }
  244. }
  245. }
  246. normal = (has_effective_normal() && sphere->get_respect_effective_normal()) ? get_effective_normal() : plane.get_normal();
  247. #ifndef NDEBUG
  248. /*if (!IS_THRESHOLD_EQUAL(normal.length_squared(), 1.0f, 0.001), NULL) {
  249. std::cout
  250. << "polygon within " << entry.get_into_node_path()
  251. << " has normal " << normal << " of length " << normal.length()
  252. << "\n";
  253. normal.normalize();
  254. }*/
  255. #endif
  256. // The nearest point within the plane to our center is the intersection of
  257. // the line (center, center - normal) with the plane.
  258. if (!plane.intersects_line(dist, from_center, -(plane.get_normal()))) {
  259. // No intersection with plane? This means the plane's effective normal
  260. // was within the plane itself. A useless polygon.
  261. continue;
  262. }
  263. if (dist > from_radius || dist < -from_radius) {
  264. // No intersection with the plane.
  265. continue;
  266. }
  267. LPoint2 p = to_2d(from_center - dist * plane.get_normal(), ip);
  268. PN_stdfloat edge_dist = 0.0f;
  269. const ClipPlaneAttrib *cpa = entry.get_into_clip_planes();
  270. if (cpa != (ClipPlaneAttrib *)NULL) {
  271. // We have a clip plane; apply it.
  272. Points new_points;
  273. if (apply_clip_plane(new_points, cpa, entry.get_into_node_path().get_net_transform(),ip)) {
  274. // All points are behind the clip plane; just do the default test.
  275. edge_dist = dist_to_polygon(p, _points[ip]);
  276. } else if (new_points.empty()) {
  277. // The polygon is completely clipped.
  278. continue;
  279. } else {
  280. // Test against the clipped polygon.
  281. edge_dist = dist_to_polygon(p, new_points);
  282. }
  283. } else {
  284. // No clip plane is in effect. Do the default test.
  285. edge_dist = dist_to_polygon(p, _points[ip]);
  286. }
  287. max_dist = from_radius;
  288. // Now we have edge_dist, which is the distance from the sphere center to
  289. // the nearest edge of the polygon, within the polygon's plane.
  290. // edge_dist<0 means the point is within the polygon.
  291. if(edge_dist < 0) {
  292. intersect = true;
  293. continue;
  294. }
  295. if((edge_dist > 0) &&
  296. ((edge_dist * edge_dist + dist * dist) > from_radius_2)) {
  297. // No intersection; the circle is outside the polygon.
  298. continue;
  299. }
  300. // The sphere appears to intersect the polygon. If the edge is less than
  301. // from_radius away, the sphere may be resting on an edge of the polygon.
  302. // Determine how far the center of the sphere must remain from the plane,
  303. // based on its distance from the nearest edge.
  304. if (edge_dist >= 0.0f) {
  305. PN_stdfloat max_dist_2 = max(from_radius_2 - edge_dist * edge_dist, (PN_stdfloat)0.0);
  306. max_dist = csqrt(max_dist_2);
  307. }
  308. if (dist > max_dist) {
  309. // There's no intersection: the sphere is hanging off the edge.
  310. continue;
  311. }
  312. intersect = true;
  313. }
  314. if( !intersect )
  315. return NULL;
  316. if (collide_cat.is_debug()) {
  317. collide_cat.debug()
  318. << "intersection detected from " << entry.get_from_node_path()
  319. << " into " << entry.get_into_node_path() << "\n";
  320. }
  321. PT(CollisionEntry) new_entry = new CollisionEntry(entry);
  322. PN_stdfloat into_depth = max_dist - dist;
  323. if (moved_from_center) {
  324. // We have to base the depth of intersection on the sphere's final resting
  325. // point, not the point from which we tested the intersection.
  326. PN_stdfloat orig_dist;
  327. plane.intersects_line(orig_dist, orig_center, -normal);
  328. into_depth = max_dist - orig_dist;
  329. }
  330. // Clamp the surface point to the box bounds.
  331. LPoint3 surface = from_center - normal * dist;
  332. surface = surface.fmax(_min);
  333. surface = surface.fmin(_max);
  334. new_entry->set_surface_normal(normal);
  335. new_entry->set_surface_point(surface);
  336. new_entry->set_interior_point(surface - normal * into_depth);
  337. new_entry->set_contact_pos(contact_point);
  338. new_entry->set_contact_normal(plane.get_normal());
  339. new_entry->set_t(actual_t);
  340. return new_entry;
  341. }
  342. /**
  343. * Double dispatch point for ray as a FROM object
  344. */
  345. PT(CollisionEntry) CollisionBox::
  346. test_intersection_from_ray(const CollisionEntry &entry) const {
  347. const CollisionRay *ray;
  348. DCAST_INTO_R(ray, entry.get_from(), NULL);
  349. const LMatrix4 &wrt_mat = entry.get_wrt_mat();
  350. LPoint3 from_origin = ray->get_origin() * wrt_mat;
  351. LVector3 from_direction = ray->get_direction() * wrt_mat;
  352. int i, j;
  353. PN_stdfloat t;
  354. PN_stdfloat near_t = 0.0;
  355. bool intersect;
  356. LPlane plane;
  357. LPlane near_plane;
  358. // Returns the details about the first plane of the box that the ray
  359. // intersects.
  360. for (i = 0, intersect = false, t = 0, j = 0; i < 6 && j < 2; i++) {
  361. plane = get_plane(i);
  362. if (!plane.intersects_line(t, from_origin, from_direction)) {
  363. // No intersection. The ray is parallel to the plane.
  364. continue;
  365. }
  366. if (t < 0.0f) {
  367. // The intersection point is before the start of the ray, and so the ray
  368. // is entirely in front of the plane.
  369. continue;
  370. }
  371. LPoint3 plane_point = from_origin + t * from_direction;
  372. LPoint2 p = to_2d(plane_point, i);
  373. if (!point_is_inside(p, _points[i])){
  374. continue;
  375. }
  376. intersect = true;
  377. if (j) {
  378. if(t < near_t) {
  379. near_plane = plane;
  380. near_t = t;
  381. }
  382. }
  383. else {
  384. near_plane = plane;
  385. near_t = t;
  386. }
  387. ++j;
  388. }
  389. if(!intersect) {
  390. // No intersection with ANY of the box's planes has been detected
  391. return NULL;
  392. }
  393. if (collide_cat.is_debug()) {
  394. collide_cat.debug()
  395. << "intersection detected from " << entry.get_from_node_path()
  396. << " into " << entry.get_into_node_path() << "\n";
  397. }
  398. PT(CollisionEntry) new_entry = new CollisionEntry(entry);
  399. LPoint3 into_intersection_point = from_origin + near_t * from_direction;
  400. LVector3 normal =
  401. (has_effective_normal() && ray->get_respect_effective_normal())
  402. ? get_effective_normal() : near_plane.get_normal();
  403. new_entry->set_surface_normal(normal);
  404. new_entry->set_surface_point(into_intersection_point);
  405. return new_entry;
  406. }
  407. /**
  408. * Double dispatch point for segment as a FROM object
  409. */
  410. PT(CollisionEntry) CollisionBox::
  411. test_intersection_from_segment(const CollisionEntry &entry) const {
  412. const CollisionSegment *seg;
  413. DCAST_INTO_R(seg, entry.get_from(), NULL);
  414. const LMatrix4 &wrt_mat = entry.get_wrt_mat();
  415. LPoint3 from_origin = seg->get_point_a() * wrt_mat;
  416. LPoint3 from_extent = seg->get_point_b() * wrt_mat;
  417. LVector3 from_direction = from_extent - from_origin;
  418. int i, j;
  419. PN_stdfloat t;
  420. PN_stdfloat near_t = 0.0;
  421. bool intersect;
  422. LPlane plane;
  423. LPlane near_plane;
  424. // Returns the details about the first plane of the box that the segment
  425. // intersects.
  426. for(i = 0, intersect = false, t = 0, j = 0; i < 6 && j < 2; i++) {
  427. plane = get_plane(i);
  428. if (!plane.intersects_line(t, from_origin, from_direction)) {
  429. // No intersection. The segment is parallel to the plane.
  430. continue;
  431. }
  432. if (t < 0.0f || t > 1.0f) {
  433. // The intersection point is before the start of the segment, or after
  434. // the end of the segment, so the segment is either entirely in front of
  435. // or behind the plane.
  436. continue;
  437. }
  438. LPoint3 plane_point = from_origin + t * from_direction;
  439. LPoint2 p = to_2d(plane_point, i);
  440. if (!point_is_inside(p, _points[i])){
  441. continue;
  442. }
  443. intersect = true;
  444. if(j) {
  445. if(t < near_t) {
  446. near_plane = plane;
  447. near_t = t;
  448. }
  449. }
  450. else {
  451. near_plane = plane;
  452. near_t = t;
  453. }
  454. ++j;
  455. }
  456. if(!intersect) {
  457. // No intersection with ANY of the box's planes has been detected
  458. return NULL;
  459. }
  460. if (collide_cat.is_debug()) {
  461. collide_cat.debug()
  462. << "intersection detected from " << entry.get_from_node_path()
  463. << " into " << entry.get_into_node_path() << "\n";
  464. }
  465. PT(CollisionEntry) new_entry = new CollisionEntry(entry);
  466. LPoint3 into_intersection_point = from_origin + near_t * from_direction;
  467. LVector3 normal =
  468. (has_effective_normal() && seg->get_respect_effective_normal())
  469. ? get_effective_normal() : near_plane.get_normal();
  470. new_entry->set_surface_normal(normal);
  471. new_entry->set_surface_point(into_intersection_point);
  472. return new_entry;
  473. }
  474. /**
  475. * Double dispatch point for box as a FROM object
  476. */
  477. PT(CollisionEntry) CollisionBox::
  478. test_intersection_from_box(const CollisionEntry &entry) const {
  479. const CollisionBox *box;
  480. DCAST_INTO_R(box, entry.get_from(), NULL);
  481. const LMatrix4 &wrt_mat = entry.get_wrt_mat();
  482. LPoint3 diff = wrt_mat.xform_point_general(box->get_center()) - _center;
  483. LVector3 from_extents = box->get_dimensions() * 0.5f;
  484. LVector3 into_extents = get_dimensions() * 0.5f;
  485. LVecBase3 box_x = wrt_mat.get_row3(0);
  486. LVecBase3 box_y = wrt_mat.get_row3(1);
  487. LVecBase3 box_z = wrt_mat.get_row3(2);
  488. // To make the math simpler, normalize the box basis vectors, instead
  489. // applying the scale to the box dimensions. Note that this doesn't work
  490. // for a non-uniform scales applied after a rotation, since that has the
  491. // possibility of making the box no longer a box.
  492. PN_stdfloat l;
  493. l = box_x.length();
  494. from_extents[0] *= l;
  495. box_x /= l;
  496. l = box_y.length();
  497. from_extents[1] *= l;
  498. box_y /= l;
  499. l = box_z.length();
  500. from_extents[2] *= l;
  501. box_z /= l;
  502. PN_stdfloat r1, r2;
  503. PN_stdfloat min_pen = 0;
  504. PN_stdfloat pen;
  505. int axis = 0;
  506. // SAT test for the three axes of the into cube.
  507. r1 = into_extents[0];
  508. r2 = cabs(box_x[0] * from_extents[0]) +
  509. cabs(box_y[0] * from_extents[1]) +
  510. cabs(box_z[0] * from_extents[2]);
  511. pen = r1 + r2 - cabs(diff[0]);
  512. if (pen < 0) {
  513. return NULL;
  514. }
  515. min_pen = pen;
  516. r1 = into_extents[1];
  517. r2 = cabs(box_x[1] * from_extents[0]) +
  518. cabs(box_y[1] * from_extents[1]) +
  519. cabs(box_z[1] * from_extents[2]);
  520. pen = r1 + r2 - cabs(diff[1]);
  521. if (pen < 0) {
  522. return NULL;
  523. }
  524. if (pen < min_pen) {
  525. min_pen = pen;
  526. axis = 1;
  527. }
  528. r1 = into_extents[2];
  529. r2 = cabs(box_x[2] * from_extents[0]) +
  530. cabs(box_y[2] * from_extents[1]) +
  531. cabs(box_z[2] * from_extents[2]);
  532. pen = r1 + r2 - cabs(diff[2]);
  533. if (pen < 0) {
  534. return NULL;
  535. }
  536. if (pen < min_pen) {
  537. min_pen = pen;
  538. axis = 2;
  539. }
  540. // SAT test for the three axes of the from cube.
  541. r1 = cabs(box_x[0] * into_extents[0]) +
  542. cabs(box_x[1] * into_extents[1]) +
  543. cabs(box_x[2] * into_extents[2]);
  544. r2 = from_extents[0];
  545. pen = r1 + r2 - cabs(diff.dot(box_x));
  546. if (pen < 0) {
  547. return NULL;
  548. }
  549. if (pen < min_pen) {
  550. min_pen = pen;
  551. }
  552. r1 = cabs(box_y[0] * into_extents[0]) +
  553. cabs(box_y[1] * into_extents[1]) +
  554. cabs(box_y[2] * into_extents[2]);
  555. r2 = from_extents[1];
  556. pen = r1 + r2 - cabs(diff.dot(box_y));
  557. if (pen < 0) {
  558. return NULL;
  559. }
  560. if (pen < min_pen) {
  561. min_pen = pen;
  562. }
  563. r1 = cabs(box_z[0] * into_extents[0]) +
  564. cabs(box_z[1] * into_extents[1]) +
  565. cabs(box_z[2] * into_extents[2]);
  566. r2 = from_extents[2];
  567. pen = r1 + r2 - cabs(diff.dot(box_z));
  568. if (pen < 0) {
  569. return NULL;
  570. }
  571. if (pen < min_pen) {
  572. min_pen = pen;
  573. }
  574. // SAT test of the nine cross products.
  575. r1 = into_extents[1] * cabs(box_x[2]) + into_extents[2] * cabs(box_x[1]);
  576. r2 = from_extents[1] * cabs(box_z[0]) + from_extents[2] * cabs(box_y[0]);
  577. if (cabs(diff[2] * box_x[1] - diff[1] * box_x[2]) > r1 + r2) {
  578. return NULL;
  579. }
  580. r1 = into_extents[1] * cabs(box_y[2]) + into_extents[2] * cabs(box_y[1]);
  581. r2 = from_extents[0] * cabs(box_z[0]) + from_extents[2] * cabs(box_x[0]);
  582. if (cabs(diff[2] * box_y[1] - diff[1] * box_y[2]) > r1 + r2) {
  583. return NULL;
  584. }
  585. r1 = into_extents[1] * cabs(box_z[2]) + into_extents[2] * cabs(box_z[1]);
  586. r2 = from_extents[0] * cabs(box_y[0]) + from_extents[1] * cabs(box_x[0]);
  587. if (cabs(diff[2] * box_z[1] - diff[1] * box_z[2]) > r1 + r2) {
  588. return NULL;
  589. }
  590. r1 = into_extents[0] * cabs(box_x[2]) + into_extents[2] * cabs(box_x[0]);
  591. r2 = from_extents[1] * cabs(box_z[1]) + from_extents[2] * cabs(box_y[1]);
  592. if (cabs(diff[0] * box_x[2] - diff[2] * box_x[0]) > r1 + r2) {
  593. return NULL;
  594. }
  595. r1 = into_extents[0] * cabs(box_y[2]) + into_extents[2] * cabs(box_y[0]);
  596. r2 = from_extents[0] * cabs(box_z[1]) + from_extents[2] * cabs(box_x[1]);
  597. if (cabs(diff[0] * box_y[2] - diff[2] * box_y[0]) > r1 + r2) {
  598. return NULL;
  599. }
  600. r1 = into_extents[0] * cabs(box_z[2]) + into_extents[2] * cabs(box_z[0]);
  601. r2 = from_extents[0] * cabs(box_y[1]) + from_extents[1] * cabs(box_x[1]);
  602. if (cabs(diff[0] * box_z[2] - diff[2] * box_z[0]) > r1 + r2) {
  603. return NULL;
  604. }
  605. r1 = into_extents[0] * cabs(box_x[1]) + into_extents[1] * cabs(box_x[0]);
  606. r2 = from_extents[1] * cabs(box_z[2]) + from_extents[2] * cabs(box_y[2]);
  607. if (cabs(diff[1] * box_x[0] - diff[0] * box_x[1]) > r1 + r2) {
  608. return NULL;
  609. }
  610. r1 = into_extents[0] * cabs(box_y[1]) + into_extents[1] * cabs(box_y[0]);
  611. r2 = from_extents[0] * cabs(box_z[2]) + from_extents[2] * cabs(box_x[2]);
  612. if (cabs(diff[1] * box_y[0] - diff[0] * box_y[1]) > r1 + r2) {
  613. return NULL;
  614. }
  615. r1 = into_extents[0] * cabs(box_z[1]) + into_extents[1] * cabs(box_z[0]);
  616. r2 = from_extents[0] * cabs(box_y[2]) + from_extents[1] * cabs(box_x[2]);
  617. if (cabs(diff[1] * box_z[0] - diff[0] * box_z[1]) > r1 + r2) {
  618. return NULL;
  619. }
  620. if (collide_cat.is_debug()) {
  621. collide_cat.debug()
  622. << "intersection detected from " << entry.get_from_node_path()
  623. << " into " << entry.get_into_node_path() << "\n";
  624. }
  625. PT(CollisionEntry) new_entry = new CollisionEntry(entry);
  626. // This isn't always the correct surface point. However, it seems to be
  627. // enough to let the pusher do the right thing.
  628. LPoint3 surface(
  629. min(max(diff[0], -into_extents[0]), into_extents[0]),
  630. min(max(diff[1], -into_extents[1]), into_extents[1]),
  631. min(max(diff[2], -into_extents[2]), into_extents[2]));
  632. // Create the normal along the axis of least penetration.
  633. LVector3 normal(0);
  634. PN_stdfloat diff_axis = diff[axis];
  635. int sign = (diff_axis >= 0) ? 1 : -1;
  636. normal[axis] = sign;
  637. surface[axis] = into_extents[axis] * sign;
  638. new_entry->set_surface_point(surface + _center);
  639. // Does not generate the correct depth. Needs fixing.
  640. new_entry->set_interior_point(surface + _center + normal * -min_pen);
  641. if (has_effective_normal() && box->get_respect_effective_normal()) {
  642. new_entry->set_surface_normal(get_effective_normal());
  643. } else {
  644. new_entry->set_surface_normal(normal);
  645. }
  646. return new_entry;
  647. }
  648. /**
  649. * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
  650. * solid.
  651. */
  652. void CollisionBox::
  653. fill_viz_geom() {
  654. if (collide_cat.is_debug()) {
  655. collide_cat.debug()
  656. << "Recomputing viz for " << *this << "\n";
  657. }
  658. PT(GeomVertexData) vdata = new GeomVertexData
  659. ("collision", GeomVertexFormat::get_v3(),
  660. Geom::UH_static);
  661. vdata->unclean_set_num_rows(8);
  662. {
  663. GeomVertexWriter vertex(vdata, InternalName::get_vertex());
  664. vertex.set_data3(_min[0], _min[1], _min[2]);
  665. vertex.set_data3(_min[0], _max[1], _min[2]);
  666. vertex.set_data3(_max[0], _max[1], _min[2]);
  667. vertex.set_data3(_max[0], _min[1], _min[2]);
  668. vertex.set_data3(_min[0], _min[1], _max[2]);
  669. vertex.set_data3(_min[0], _max[1], _max[2]);
  670. vertex.set_data3(_max[0], _max[1], _max[2]);
  671. vertex.set_data3(_max[0], _min[1], _max[2]);
  672. }
  673. PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
  674. // Bottom
  675. tris->add_vertices(0, 1, 2);
  676. tris->add_vertices(2, 3, 0);
  677. // Top
  678. tris->add_vertices(4, 7, 6);
  679. tris->add_vertices(6, 5, 4);
  680. // Sides
  681. tris->add_vertices(0, 4, 1);
  682. tris->add_vertices(1, 4, 5);
  683. tris->add_vertices(1, 5, 2);
  684. tris->add_vertices(2, 5, 6);
  685. tris->add_vertices(2, 6, 3);
  686. tris->add_vertices(3, 6, 7);
  687. tris->add_vertices(3, 7, 0);
  688. tris->add_vertices(0, 7, 4);
  689. PT(Geom) geom = new Geom(vdata);
  690. geom->add_primitive(tris);
  691. _viz_geom->add_geom(geom, get_solid_viz_state());
  692. _bounds_viz_geom->add_geom(geom, get_solid_bounds_viz_state());
  693. }
  694. /**
  695. * Clips the polygon by all of the clip planes named in the clip plane
  696. * attribute and fills new_points up with the resulting points.
  697. *
  698. * The return value is true if the set of points is unmodified (all points are
  699. * behind all the clip planes), or false otherwise.
  700. */
  701. bool CollisionBox::
  702. apply_clip_plane(CollisionBox::Points &new_points,
  703. const ClipPlaneAttrib *cpa,
  704. const TransformState *net_transform, int plane_no) const {
  705. bool all_in = true;
  706. int num_planes = cpa->get_num_on_planes();
  707. bool first_plane = true;
  708. for (int i = 0; i < num_planes; i++) {
  709. NodePath plane_path = cpa->get_on_plane(i);
  710. PlaneNode *plane_node = DCAST(PlaneNode, plane_path.node());
  711. if ((plane_node->get_clip_effect() & PlaneNode::CE_collision) != 0) {
  712. CPT(TransformState) new_transform =
  713. net_transform->invert_compose(plane_path.get_net_transform());
  714. LPlane plane = plane_node->get_plane() * new_transform->get_mat();
  715. if (first_plane) {
  716. first_plane = false;
  717. if (!clip_polygon(new_points, _points[plane_no], plane, plane_no)) {
  718. all_in = false;
  719. }
  720. } else {
  721. Points last_points;
  722. last_points.swap(new_points);
  723. if (!clip_polygon(new_points, last_points, plane, plane_no)) {
  724. all_in = false;
  725. }
  726. }
  727. }
  728. }
  729. if (!all_in) {
  730. compute_vectors(new_points);
  731. }
  732. return all_in;
  733. }
  734. /**
  735. * Clips the source_points of the polygon by the indicated clipping plane, and
  736. * modifies new_points to reflect the new set of clipped points (but does not
  737. * compute the vectors in new_points).
  738. *
  739. * The return value is true if the set of points is unmodified (all points are
  740. * behind the clip plane), or false otherwise.
  741. */
  742. bool CollisionBox::
  743. clip_polygon(CollisionBox::Points &new_points,
  744. const CollisionBox::Points &source_points,
  745. const LPlane &plane, int plane_no) const {
  746. new_points.clear();
  747. if (source_points.empty()) {
  748. return true;
  749. }
  750. LPoint3 from3d;
  751. LVector3 delta3d;
  752. if (!plane.intersects_plane(from3d, delta3d, get_plane(plane_no))) {
  753. // The clipping plane is parallel to the polygon. The polygon is either
  754. // all in or all out.
  755. if (plane.dist_to_plane(get_plane(plane_no).get_point()) < 0.0) {
  756. // A point within the polygon is behind the clipping plane: the polygon
  757. // is all in.
  758. new_points = source_points;
  759. return true;
  760. }
  761. return false;
  762. }
  763. // Project the line of intersection into the 2-d plane. Now we have a 2-d
  764. // clipping line.
  765. LPoint2 from2d = to_2d(from3d,plane_no);
  766. LVector2 delta2d = to_2d(delta3d,plane_no);
  767. PN_stdfloat a = -delta2d[1];
  768. PN_stdfloat b = delta2d[0];
  769. PN_stdfloat c = from2d[0] * delta2d[1] - from2d[1] * delta2d[0];
  770. // Now walk through the points. Any point on the left of our line gets
  771. // removed, and the line segment clipped at the point of intersection.
  772. // We might increase the number of vertices by as many as 1, if the plane
  773. // clips off exactly one corner. (We might also decrease the number of
  774. // vertices, or keep them the same number.)
  775. new_points.reserve(source_points.size() + 1);
  776. LPoint2 last_point = source_points.back()._p;
  777. bool last_is_in = !is_right(last_point - from2d, delta2d);
  778. bool all_in = last_is_in;
  779. Points::const_iterator pi;
  780. for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
  781. const LPoint2 &this_point = (*pi)._p;
  782. bool this_is_in = !is_right(this_point - from2d, delta2d);
  783. // There appears to be a compiler bug in gcc 4.0: we need to extract this
  784. // comparison outside of the if statement.
  785. bool crossed_over = (this_is_in != last_is_in);
  786. if (crossed_over) {
  787. // We have just crossed over the clipping line. Find the point of
  788. // intersection.
  789. LVector2 d = this_point - last_point;
  790. PN_stdfloat denom = (a * d[0] + b * d[1]);
  791. if (denom != 0.0) {
  792. PN_stdfloat t = -(a * last_point[0] + b * last_point[1] + c) / denom;
  793. LPoint2 p = last_point + t * d;
  794. new_points.push_back(PointDef(p[0], p[1]));
  795. last_is_in = this_is_in;
  796. }
  797. }
  798. if (this_is_in) {
  799. // We are behind the clipping line. Keep the point.
  800. new_points.push_back(PointDef(this_point[0], this_point[1]));
  801. } else {
  802. all_in = false;
  803. }
  804. last_point = this_point;
  805. }
  806. return all_in;
  807. }
  808. /**
  809. * Returns the linear distance from the 2-d point to the nearest part of the
  810. * polygon defined by the points vector. The result is negative if the point
  811. * is within the polygon.
  812. */
  813. PN_stdfloat CollisionBox::
  814. dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const {
  815. // We know that that the polygon is convex and is defined with the points in
  816. // counterclockwise order. Therefore, we simply compare the signed distance
  817. // to each line segment; we ignore any negative values, and take the minimum
  818. // of all the positive values.
  819. // If all values are negative, the point is within the polygon; we therefore
  820. // return an arbitrary negative result.
  821. bool got_dist = false;
  822. PN_stdfloat best_dist = -1.0f;
  823. size_t num_points = points.size();
  824. for (size_t i = 0; i < num_points - 1; ++i) {
  825. PN_stdfloat d = dist_to_line_segment(p, points[i]._p, points[i + 1]._p,
  826. points[i]._v);
  827. if (d >= 0.0f) {
  828. if (!got_dist || d < best_dist) {
  829. best_dist = d;
  830. got_dist = true;
  831. }
  832. }
  833. }
  834. PN_stdfloat d = dist_to_line_segment(p, points[num_points - 1]._p, points[0]._p,
  835. points[num_points - 1]._v);
  836. if (d >= 0.0f) {
  837. if (!got_dist || d < best_dist) {
  838. best_dist = d;
  839. got_dist = true;
  840. }
  841. }
  842. return best_dist;
  843. }
  844. /**
  845. * Returns the linear distance of p to the line segment defined by f and t,
  846. * where v = (t - f).normalize(). The result is negative if p is left of the
  847. * line, positive if it is right of the line. If the result is positive, it
  848. * is constrained by endpoints of the line segment (i.e. the result might be
  849. * larger than it would be for a straight distance-to-line test). If the
  850. * result is negative, we don't bother.
  851. */
  852. PN_stdfloat CollisionBox::
  853. dist_to_line_segment(const LPoint2 &p,
  854. const LPoint2 &f, const LPoint2 &t,
  855. const LVector2 &v) {
  856. LVector2 v1 = (p - f);
  857. PN_stdfloat d = (v1[0] * v[1] - v1[1] * v[0]);
  858. if (d < 0.0f) {
  859. return d;
  860. }
  861. // Compute the nearest point on the line.
  862. LPoint2 q = p + LVector2(-v[1], v[0]) * d;
  863. // Now constrain that point to the line segment.
  864. if (v[0] > 0.0f) {
  865. // X+
  866. if (v[1] > 0.0f) {
  867. // Y+
  868. if (v[0] > v[1]) {
  869. // X-dominant.
  870. if (q[0] < f[0]) {
  871. return (p - f).length();
  872. } if (q[0] > t[0]) {
  873. return (p - t).length();
  874. } else {
  875. return d;
  876. }
  877. } else {
  878. // Y-dominant.
  879. if (q[1] < f[1]) {
  880. return (p - f).length();
  881. } if (q[1] > t[1]) {
  882. return (p - t).length();
  883. } else {
  884. return d;
  885. }
  886. }
  887. } else {
  888. // Y-
  889. if (v[0] > -v[1]) {
  890. // X-dominant.
  891. if (q[0] < f[0]) {
  892. return (p - f).length();
  893. } if (q[0] > t[0]) {
  894. return (p - t).length();
  895. } else {
  896. return d;
  897. }
  898. } else {
  899. // Y-dominant.
  900. if (q[1] > f[1]) {
  901. return (p - f).length();
  902. } if (q[1] < t[1]) {
  903. return (p - t).length();
  904. } else {
  905. return d;
  906. }
  907. }
  908. }
  909. } else {
  910. // X-
  911. if (v[1] > 0.0f) {
  912. // Y+
  913. if (-v[0] > v[1]) {
  914. // X-dominant.
  915. if (q[0] > f[0]) {
  916. return (p - f).length();
  917. } if (q[0] < t[0]) {
  918. return (p - t).length();
  919. } else {
  920. return d;
  921. }
  922. } else {
  923. // Y-dominant.
  924. if (q[1] < f[1]) {
  925. return (p - f).length();
  926. } if (q[1] > t[1]) {
  927. return (p - t).length();
  928. } else {
  929. return d;
  930. }
  931. }
  932. } else {
  933. // Y-
  934. if (-v[0] > -v[1]) {
  935. // X-dominant.
  936. if (q[0] > f[0]) {
  937. return (p - f).length();
  938. } if (q[0] < t[0]) {
  939. return (p - t).length();
  940. } else {
  941. return d;
  942. }
  943. } else {
  944. // Y-dominant.
  945. if (q[1] > f[1]) {
  946. return (p - f).length();
  947. } if (q[1] < t[1]) {
  948. return (p - t).length();
  949. } else {
  950. return d;
  951. }
  952. }
  953. }
  954. }
  955. }
  956. /**
  957. * Returns true if the indicated point is within the polygon's 2-d space,
  958. * false otherwise.
  959. */
  960. bool CollisionBox::
  961. point_is_inside(const LPoint2 &p, const CollisionBox::Points &points) const {
  962. // We insist that the polygon be convex. This makes things a bit simpler.
  963. // In the case of a convex polygon, defined with points in counterclockwise
  964. // order, a point is interior to the polygon iff the point is not right of
  965. // each of the edges.
  966. for (int i = 0; i < (int)points.size() - 1; i++) {
  967. if (is_right(p - points[i]._p, points[i+1]._p - points[i]._p)) {
  968. return false;
  969. }
  970. }
  971. if (is_right(p - points[points.size() - 1]._p,
  972. points[0]._p - points[points.size() - 1]._p)) {
  973. return false;
  974. }
  975. return true;
  976. }
  977. /**
  978. * Now that the _p members of the given points array have been computed, go
  979. * back and compute all of the _v members.
  980. */
  981. void CollisionBox::
  982. compute_vectors(Points &points) {
  983. size_t num_points = points.size();
  984. for (size_t i = 0; i < num_points; i++) {
  985. points[i]._v = points[(i + 1) % num_points]._p - points[i]._p;
  986. points[i]._v.normalize();
  987. }
  988. }
  989. /**
  990. * Factory method to generate a CollisionBox object
  991. */
  992. void CollisionBox::
  993. register_with_read_factory() {
  994. BamReader::get_factory()->register_factory(get_class_type(), make_CollisionBox);
  995. }
  996. /**
  997. * Function to write the important information in the particular object to a
  998. * Datagram
  999. */
  1000. void CollisionBox::
  1001. write_datagram(BamWriter *manager, Datagram &me) {
  1002. CollisionSolid::write_datagram(manager, me);
  1003. _center.write_datagram(me);
  1004. _min.write_datagram(me);
  1005. _max.write_datagram(me);
  1006. for(int i=0; i < 8; i++) {
  1007. _vertex[i].write_datagram(me);
  1008. }
  1009. me.add_stdfloat(_radius);
  1010. me.add_stdfloat(_x);
  1011. me.add_stdfloat(_y);
  1012. me.add_stdfloat(_z);
  1013. for(int i=0; i < 6; i++) {
  1014. _planes[i].write_datagram(me);
  1015. }
  1016. for(int i=0; i < 6; i++) {
  1017. _to_2d_mat[i].write_datagram(me);
  1018. }
  1019. for(int i=0; i < 6; i++) {
  1020. me.add_uint16(_points[i].size());
  1021. for (size_t j = 0; j < _points[i].size(); j++) {
  1022. _points[i][j]._p.write_datagram(me);
  1023. _points[i][j]._v.write_datagram(me);
  1024. }
  1025. }
  1026. }
  1027. /**
  1028. * Factory method to generate a CollisionBox object
  1029. */
  1030. TypedWritable *CollisionBox::
  1031. make_CollisionBox(const FactoryParams &params) {
  1032. CollisionBox *me = new CollisionBox;
  1033. DatagramIterator scan;
  1034. BamReader *manager;
  1035. parse_params(params, scan, manager);
  1036. me->fillin(scan, manager);
  1037. return me;
  1038. }
  1039. /**
  1040. * Function that reads out of the datagram (or asks manager to read) all of
  1041. * the data that is needed to re-create this object and stores it in the
  1042. * appropiate place
  1043. */
  1044. void CollisionBox::
  1045. fillin(DatagramIterator& scan, BamReader* manager) {
  1046. CollisionSolid::fillin(scan, manager);
  1047. _center.read_datagram(scan);
  1048. _min.read_datagram(scan);
  1049. _max.read_datagram(scan);
  1050. for(int i=0; i < 8; i++) {
  1051. _vertex[i].read_datagram(scan);
  1052. }
  1053. _radius = scan.get_stdfloat();
  1054. _x = scan.get_stdfloat();
  1055. _y = scan.get_stdfloat();
  1056. _z = scan.get_stdfloat();
  1057. for(int i=0; i < 6; i++) {
  1058. _planes[i].read_datagram(scan);
  1059. }
  1060. for(int i=0; i < 6; i++) {
  1061. _to_2d_mat[i].read_datagram(scan);
  1062. }
  1063. for(int i=0; i < 6; i++) {
  1064. size_t size = scan.get_uint16();
  1065. for (size_t j = 0; j < size; j++) {
  1066. LPoint2 p;
  1067. LVector2 v;
  1068. p.read_datagram(scan);
  1069. v.read_datagram(scan);
  1070. _points[i].push_back(PointDef(p, v));
  1071. }
  1072. }
  1073. }