00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "collisionPolygon.h"
00021 #include "collisionHandler.h"
00022 #include "collisionEntry.h"
00023 #include "collisionSphere.h"
00024 #include "collisionRay.h"
00025 #include "collisionSegment.h"
00026 #include "config_collide.h"
00027
00028 #include "boundingSphere.h"
00029 #include "pointerToArray.h"
00030 #include "geomNode.h"
00031 #include "geom.h"
00032 #include "datagram.h"
00033 #include "datagramIterator.h"
00034 #include "bamReader.h"
00035 #include "bamWriter.h"
00036 #include "geomPolygon.h"
00037
00038 #include <algorithm>
00039
00040 TypeHandle CollisionPolygon::_type_handle;
00041
00042
00043
00044
00045
00046
00047
00048 INLINE bool
00049 is_right(const LVector2f &v1, const LVector2f &v2) {
00050 return (-v1[0] * v2[1] + v1[1] * v2[0]) > 0;
00051 }
00052
00053
00054
00055
00056
00057
00058
00059 CollisionPolygon::
00060 CollisionPolygon(const CollisionPolygon ©) :
00061 CollisionPlane(copy),
00062 _points(copy._points),
00063 _median(copy._median),
00064 _axis(copy._axis),
00065 _reversed(copy._reversed)
00066 {
00067 }
00068
00069
00070
00071
00072
00073
00074 CollisionSolid *CollisionPolygon::
00075 make_copy() {
00076 return new CollisionPolygon(*this);
00077 }
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091 bool CollisionPolygon::
00092 verify_points(const LPoint3f *begin, const LPoint3f *end) {
00093 int num_points = end - begin;
00094 if (num_points < 3) {
00095 return false;
00096 }
00097
00098
00099
00100 Planef plane(begin[0], begin[1], begin[2]);
00101 LVector3f normal = plane.get_normal();
00102 float normal_length = normal.length();
00103 bool all_ok = IS_THRESHOLD_EQUAL(normal_length, 1.0f, 0.001f);
00104
00105 const LPoint3f *pi;
00106 for (pi = begin; pi != end && all_ok; ++pi) {
00107 if ((*pi).is_nan()) {
00108 all_ok = false;
00109 } else {
00110
00111 const LPoint3f *pj;
00112 for (pj = begin; pj != pi && all_ok; ++pj) {
00113 if ((*pj).almost_equal(*pi)) {
00114 all_ok = false;
00115 }
00116 }
00117 }
00118 }
00119
00120 return all_ok;
00121 }
00122
00123
00124
00125
00126
00127
00128 void CollisionPolygon::
00129 xform(const LMatrix4f &mat) {
00130
00131
00132
00133
00134 if (collide_cat.is_spam()) {
00135 collide_cat.spam()
00136 << "CollisionPolygon transformed by:\n";
00137 mat.write(collide_cat.spam(false), 2);
00138 if (_points.empty()) {
00139 collide_cat.spam(false)
00140 << " (no points)\n";
00141 }
00142 }
00143
00144 if (!_points.empty()) {
00145 pvector<LPoint3f> verts;
00146 Points::const_iterator pi;
00147 for (pi = _points.begin(); pi != _points.end(); ++pi) {
00148 verts.push_back(to_3d(*pi) * mat);
00149 }
00150 if (_reversed) {
00151 reverse(verts.begin(), verts.end());
00152 }
00153
00154 const LPoint3f *verts_begin = &verts[0];
00155 const LPoint3f *verts_end = verts_begin + verts.size();
00156 setup_points(verts_begin, verts_end);
00157 }
00158
00159 mark_viz_stale();
00160 mark_bound_stale();
00161 }
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171 LPoint3f CollisionPolygon::
00172 get_collision_origin() const {
00173 return to_3d(_median);
00174 }
00175
00176
00177
00178
00179
00180
00181 void CollisionPolygon::
00182 output(ostream &out) const {
00183 out << "cpolygon";
00184 }
00185
00186
00187
00188
00189
00190
00191 BoundingVolume *CollisionPolygon::
00192 recompute_bound() {
00193
00194 BoundingVolume *bound = BoundedObject::recompute_bound();
00195 nassertr(bound != (BoundingVolume*)0L, bound);
00196
00197 GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
00198
00199
00200
00201 pvector<LPoint3f> vertices;
00202 Points::const_iterator pi;
00203 for (pi = _points.begin(); pi != _points.end(); ++pi) {
00204 vertices.push_back(to_3d(*pi));
00205 }
00206
00207 const LPoint3f *vertices_begin = &vertices[0];
00208 const LPoint3f *vertices_end = vertices_begin + vertices.size();
00209 gbv->around(vertices_begin, vertices_end);
00210
00211 return bound;
00212 }
00213
00214
00215
00216
00217
00218
00219
00220
00221 PT(CollisionEntry) CollisionPolygon::
00222 test_intersection_from_sphere(const CollisionEntry &entry) const {
00223 if (_points.size() < 3) {
00224 return NULL;
00225 }
00226
00227 const CollisionSphere *sphere;
00228 DCAST_INTO_R(sphere, entry.get_from(), 0);
00229
00230 LPoint3f orig_center = sphere->get_center() * entry.get_wrt_space();
00231 LPoint3f from_center = orig_center;
00232 bool moved_from_center = false;
00233
00234 if (entry.has_from_velocity()) {
00235
00236
00237 LPoint3f b = from_center;
00238 LPoint3f a = (sphere->get_center() - entry.get_from_velocity()) * entry.get_wrt_space();
00239
00240 LVector3f delta = b - a;
00241
00242
00243 float dot = delta.dot(get_normal());
00244 if (dot > 0.0f) {
00245 return NULL;
00246 }
00247
00248 if (IS_NEARLY_ZERO(dot)) {
00249
00250
00251
00252 } else {
00253
00254
00255
00256
00257 float t = -(dist_to_plane(a) / dot);
00258 if (t >= 1.0f) {
00259
00260
00261 } else if (t < 0.0f) {
00262 from_center = a;
00263 moved_from_center = true;
00264
00265 } else {
00266 from_center = a + t * delta;
00267 moved_from_center = true;
00268 }
00269 }
00270 }
00271
00272 LVector3f from_radius_v =
00273 LVector3f(sphere->get_radius(), 0.0f, 0.0f) * entry.get_wrt_space();
00274 float from_radius = length(from_radius_v);
00275
00276 float dist = dist_to_plane(from_center);
00277 if (dist > from_radius || dist < -from_radius) {
00278
00279 return NULL;
00280 }
00281
00282
00283
00284
00285
00286 LPoint3f plane_point;
00287 bool really_intersects =
00288 get_plane().intersects_line(plane_point,
00289 from_center, from_center + get_normal());
00290 nassertr(really_intersects, 0);
00291
00292 LPoint2f p = to_2d(plane_point);
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311 if (is_inside(p)) {
00312
00313
00314 } else {
00315
00316 if (from_radius > 0.0f) {
00317
00318
00319
00320
00321
00322 LVector2f rim = _median - p;
00323 float rim_length = length(rim);
00324
00325 if (rim_length <= from_radius) {
00326
00327
00328
00329
00330
00331 } else {
00332
00333 rim = (rim * from_radius / rim_length) + p;
00334
00335
00336 if (is_inside(rim)) {
00337
00338
00339 } else {
00340
00341 return NULL;
00342 }
00343 }
00344 }
00345 }
00346
00347 if (collide_cat.is_debug()) {
00348 collide_cat.debug()
00349 << "intersection detected from " << *entry.get_from_node() << " into "
00350 << entry.get_into_node_path() << "\n";
00351 }
00352 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
00353
00354 LVector3f from_normal = get_normal() * entry.get_inv_wrt_space();
00355 float from_depth = from_radius - dist;
00356 if (moved_from_center) {
00357
00358
00359
00360 from_depth = from_radius - dist_to_plane(orig_center);
00361 }
00362
00363 new_entry->set_into_surface_normal(get_normal());
00364 new_entry->set_from_surface_normal(from_normal);
00365 new_entry->set_from_depth(from_depth);
00366
00367 new_entry->set_into_intersection_point(from_center - get_normal() * dist);
00368
00369 return new_entry;
00370 }
00371
00372
00373
00374
00375
00376
00377
00378
00379 PT(CollisionEntry) CollisionPolygon::
00380 test_intersection_from_ray(const CollisionEntry &entry) const {
00381 if (_points.size() < 3) {
00382 return NULL;
00383 }
00384
00385 const CollisionRay *ray;
00386 DCAST_INTO_R(ray, entry.get_from(), 0);
00387
00388 LPoint3f from_origin = ray->get_origin() * entry.get_wrt_space();
00389 LVector3f from_direction = ray->get_direction() * entry.get_wrt_space();
00390
00391 float t;
00392 if (!get_plane().intersects_line(t, from_origin, from_direction)) {
00393
00394 return NULL;
00395 }
00396
00397 if (t < 0.0f) {
00398
00399 return NULL;
00400 }
00401
00402 LPoint3f plane_point = from_origin + t * from_direction;
00403 if (!is_inside(to_2d(plane_point))) {
00404
00405 return NULL;
00406 }
00407
00408 if (collide_cat.is_debug()) {
00409 collide_cat.debug()
00410 << "intersection detected from " << *entry.get_from_node() << " into "
00411 << entry.get_into_node_path() << "\n";
00412 }
00413 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
00414
00415 new_entry->set_into_surface_normal(get_normal());
00416 new_entry->set_into_intersection_point(plane_point);
00417
00418 return new_entry;
00419 }
00420
00421
00422
00423
00424
00425
00426
00427
00428 PT(CollisionEntry) CollisionPolygon::
00429 test_intersection_from_segment(const CollisionEntry &entry) const {
00430 if (_points.size() < 3) {
00431 return NULL;
00432 }
00433
00434 const CollisionSegment *segment;
00435 DCAST_INTO_R(segment, entry.get_from(), 0);
00436
00437 LPoint3f from_a = segment->get_point_a() * entry.get_wrt_space();
00438 LPoint3f from_b = segment->get_point_b() * entry.get_wrt_space();
00439 LPoint3f from_direction = from_b - from_a;
00440
00441 float t;
00442 if (!get_plane().intersects_line(t, from_a, from_direction)) {
00443
00444 return NULL;
00445 }
00446
00447 if (t < 0.0f || t > 1.0f) {
00448
00449
00450 return NULL;
00451 }
00452
00453 LPoint3f plane_point = from_a + t * from_direction;
00454 if (!is_inside(to_2d(plane_point))) {
00455
00456 return NULL;
00457 }
00458
00459 if (collide_cat.is_debug()) {
00460 collide_cat.debug()
00461 << "intersection detected from " << *entry.get_from_node() << " into "
00462 << entry.get_into_node_path() << "\n";
00463 }
00464 PT(CollisionEntry) new_entry = new CollisionEntry(entry);
00465
00466 new_entry->set_into_surface_normal(get_normal());
00467 new_entry->set_into_intersection_point(plane_point);
00468
00469 return new_entry;
00470 }
00471
00472
00473
00474
00475
00476
00477
00478 void CollisionPolygon::
00479 fill_viz_geom() {
00480 if (collide_cat.is_debug()) {
00481 collide_cat.debug()
00482 << "Recomputing viz for " << *this << "\n";
00483 }
00484
00485 if (_points.size() < 3) {
00486 if (collide_cat.is_debug()) {
00487 collide_cat.debug()
00488 << "(Degenerate poly, ignoring.)\n";
00489 }
00490 return;
00491 }
00492
00493 PTA_Vertexf verts;
00494 Points::const_iterator pi;
00495 for (pi = _points.begin(); pi != _points.end(); ++pi) {
00496 verts.push_back(to_3d(*pi));
00497 }
00498 if (_reversed) {
00499 reverse(verts.begin(), verts.end());
00500 }
00501
00502 PTA_int lengths;
00503 lengths.push_back(_points.size());
00504
00505 GeomPolygon *polygon = new GeomPolygon;
00506 polygon->set_coords(verts);
00507 polygon->set_num_prims(1);
00508 polygon->set_lengths(lengths);
00509
00510 _viz_geom->add_geom(polygon, get_solid_viz_state());
00511 _viz_geom->add_geom(polygon, get_wireframe_viz_state());
00512 }
00513
00514
00515
00516
00517
00518
00519 bool CollisionPolygon::
00520 is_inside(const LPoint2f &p) const {
00521
00522
00523
00524
00525
00526
00527 for (int i = 0; i < (int)_points.size() - 1; i++) {
00528 if (is_right(p - _points[i], _points[i+1] - _points[i])) {
00529 return false;
00530 }
00531 }
00532 if (is_right(p - _points[_points.size() - 1],
00533 _points[0] - _points[_points.size() - 1])) {
00534 return false;
00535 }
00536
00537 return true;
00538
00539 }
00540
00541
00542
00543
00544
00545
00546
00547 bool CollisionPolygon::
00548 is_concave() const {
00549 nassertr(_points.size() >= 3, true);
00550
00551 LPoint2f p0 = _points[0];
00552 LPoint2f p1 = _points[1];
00553 float dx1 = p1[0] - p0[0];
00554 float dy1 = p1[1] - p0[1];
00555 p0 = p1;
00556 p1 = _points[2];
00557
00558 float dx2 = p1[0] - p0[0];
00559 float dy2 = p1[1] - p0[1];
00560 int asum = ((dx1 * dy2 - dx2 * dy1 >= 0.0f) ? 1 : 0);
00561
00562 for (size_t i = 0; i < _points.size() - 1; i++) {
00563 p0 = p1;
00564 p1 = _points[(i+3) % _points.size()];
00565
00566 dx1 = dx2;
00567 dy1 = dy2;
00568 dx2 = p1[0] - p0[0];
00569 dy2 = p1[1] - p0[1];
00570 int csum = ((dx1 * dy2 - dx2 * dy1 >= 0.0f) ? 1 : 0);
00571
00572 if (csum ^ asum) {
00573
00574 return true;
00575 }
00576 }
00577
00578
00579 return false;
00580 }
00581
00582
00583
00584
00585
00586
00587 void CollisionPolygon::
00588 setup_points(const LPoint3f *begin, const LPoint3f *end) {
00589 int num_points = end - begin;
00590 nassertv(num_points >= 3);
00591
00592 _points.clear();
00593
00594
00595
00596 Planef plane(begin[0], begin[1], begin[2]);
00597 set_plane(plane);
00598
00599 LVector3f normal = get_normal();
00600
00601 #ifndef NDEBUG
00602
00603 {
00604 if (!verify_points(begin, end)) {
00605 collide_cat.error() << "Invalid points in CollisionPolygon:\n";
00606 const LPoint3f *pi;
00607 for (pi = begin; pi != end; ++pi) {
00608 collide_cat.error(false) << " " << (*pi) << "\n";
00609 }
00610 collide_cat.error(false)
00611 << " normal " << normal << " with length " << normal.length() << "\n";
00612
00613 return;
00614 }
00615 }
00616
00617 if (collide_cat.is_spam()) {
00618 collide_cat.spam()
00619 << "CollisionPolygon defined with " << num_points << " vertices:\n";
00620 const LPoint3f *pi;
00621 for (pi = begin; pi != end; ++pi) {
00622 collide_cat.spam(false) << " " << (*pi) << "\n";
00623 }
00624 }
00625 #endif
00626
00627
00628
00629
00630
00631
00632
00633 if (fabs(normal[0]) >= fabs(normal[1])) {
00634 if (fabs(normal[0]) >= fabs(normal[2])) {
00635 _axis = AT_x;
00636 } else {
00637 _axis = AT_z;
00638 }
00639 } else {
00640 if (fabs(normal[1]) >= fabs(normal[2])) {
00641 _axis = AT_y;
00642 } else {
00643 _axis = AT_z;
00644 }
00645 }
00646
00647
00648
00649 const LPoint3f *pi;
00650 switch (_axis) {
00651 case AT_x:
00652 for (pi = begin; pi != end; ++pi) {
00653 const LPoint3f &point = (*pi);
00654 _points.push_back(LPoint2f(point[1], point[2]));
00655 }
00656 break;
00657
00658 case AT_y:
00659 for (pi = begin; pi != end; ++pi) {
00660 const LPoint3f &point = (*pi);
00661 _points.push_back(LPoint2f(point[0], point[2]));
00662 }
00663 break;
00664
00665 case AT_z:
00666 for (pi = begin; pi != end; ++pi) {
00667 const LPoint3f &point = (*pi);
00668 _points.push_back(LPoint2f(point[0], point[1]));
00669 }
00670 break;
00671 }
00672
00673 nassertv(_points.size() >= 3);
00674
00675 #ifndef NDEBUG
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689 #endif
00690
00691
00692
00693
00694 _median = _points[0];
00695 for (int n = 1; n < (int)_points.size(); n++) {
00696 _median += _points[n];
00697 }
00698 _median /= _points.size();
00699
00700
00701
00702
00703 _reversed = is_right(_points[2] - _points[0], _points[1] - _points[0]);
00704 if (_reversed) {
00705 reverse(_points.begin(), _points.end());
00706 }
00707 }
00708
00709
00710
00711
00712
00713
00714
00715
00716 LPoint2f CollisionPolygon::
00717 to_2d(const LPoint3f &point3d) const {
00718 nassertr(!point3d.is_nan(), LPoint2f(0.0f, 0.0f));
00719
00720
00721
00722
00723 switch (_axis) {
00724 case AT_x:
00725 return LPoint2f(point3d[1], point3d[2]);
00726
00727 case AT_y:
00728 return LPoint2f(point3d[0], point3d[2]);
00729
00730 case AT_z:
00731 return LPoint2f(point3d[0], point3d[1]);
00732 }
00733
00734 nassertr(false, LPoint2f(0.0f, 0.0f));
00735 return LPoint2f(0.0f, 0.0f);
00736 }
00737
00738
00739
00740
00741
00742
00743
00744 LPoint3f CollisionPolygon::
00745 to_3d(const LPoint2f &point2d) const {
00746 nassertr(!point2d.is_nan(), LPoint3f(0.0f, 0.0f, 0.0f));
00747
00748 LVector3f normal = get_normal();
00749 float D = get_plane()._d;
00750
00751 nassertr(!normal.is_nan(), LPoint3f(0.0f, 0.0f, 0.0f));
00752 nassertr(!cnan(D), LPoint3f(0.0f, 0.0f, 0.0f));
00753
00754 switch (_axis) {
00755 case AT_x:
00756 return LPoint3f(-(normal[1]*point2d[0] + normal[2]*point2d[1] + D)/normal[0],
00757 point2d[0], point2d[1]);
00758
00759 case AT_y:
00760 return LPoint3f(point2d[0],
00761 -(normal[0]*point2d[0] + normal[2]*point2d[1] + D)/normal[1],
00762 point2d[1]);
00763
00764 case AT_z:
00765 return LPoint3f(point2d[0], point2d[1],
00766 -(normal[0]*point2d[0] + normal[1]*point2d[1] + D)/normal[2]);
00767 }
00768
00769 nassertr(false, LPoint3f(0.0f, 0.0f, 0.0f));
00770 return LPoint3f(0.0f, 0.0f, 0.0f);
00771 }
00772
00773
00774
00775
00776
00777
00778
00779 void CollisionPolygon::
00780 write_datagram(BamWriter *manager, Datagram &me)
00781 {
00782 int i;
00783
00784 CollisionPlane::write_datagram(manager, me);
00785 me.add_uint16(_points.size());
00786 for(i = 0; i < (int)_points.size(); i++)
00787 {
00788 _points[i].write_datagram(me);
00789 }
00790 _median.write_datagram(me);
00791 me.add_uint8(_axis);
00792 me.add_uint8(_reversed);
00793 }
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803 void CollisionPolygon::
00804 fillin(DatagramIterator& scan, BamReader* manager)
00805 {
00806 int i;
00807 LPoint2f temp;
00808 CollisionPlane::fillin(scan, manager);
00809 int size = scan.get_uint16();
00810 for(i = 0; i < size; i++)
00811 {
00812 temp.read_datagram(scan);
00813 _points.push_back(temp);
00814 }
00815 _median.read_datagram(scan);
00816 _axis = (enum AxisType)scan.get_uint8();
00817
00818
00819
00820 _reversed = (scan.get_uint8() != 0);
00821 }
00822
00823
00824
00825
00826
00827
00828 TypedWritable* CollisionPolygon::
00829 make_CollisionPolygon(const FactoryParams ¶ms)
00830 {
00831 CollisionPolygon *me = new CollisionPolygon;
00832 DatagramIterator scan;
00833 BamReader *manager;
00834
00835 parse_params(params, scan, manager);
00836 me->fillin(scan, manager);
00837 return me;
00838 }
00839
00840
00841
00842
00843
00844
00845 void CollisionPolygon::
00846 register_with_read_factory(void)
00847 {
00848 BamReader::get_factory()->register_factory(get_class_type(), make_CollisionPolygon);
00849 }
00850
00851