Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/collide/collisionPolygon.cxx

Go to the documentation of this file.
00001 // Filename: collisionPolygon.cxx
00002 // Created by:  drose (25Apr00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
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 //     Function: is_right
00046 //  Description: Returns true if the 2-d v1 is to the right of v2.
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 //     Function: CollisionPolygon::Copy Constructor
00056 //       Access: Public
00057 //  Description:
00058 ////////////////////////////////////////////////////////////////////
00059 CollisionPolygon::
00060 CollisionPolygon(const CollisionPolygon &copy) :
00061   CollisionPlane(copy),
00062   _points(copy._points),
00063   _median(copy._median),
00064   _axis(copy._axis),
00065   _reversed(copy._reversed)
00066 {
00067 }
00068 
00069 ////////////////////////////////////////////////////////////////////
00070 //     Function: CollisionPolygon::make_copy
00071 //       Access: Public, Virtual
00072 //  Description:
00073 ////////////////////////////////////////////////////////////////////
00074 CollisionSolid *CollisionPolygon::
00075 make_copy() {
00076   return new CollisionPolygon(*this);
00077 }
00078 
00079 ////////////////////////////////////////////////////////////////////
00080 //     Function: CollisionPolygon::verify_points
00081 //       Access: Public, Static
00082 //  Description: Verifies that the indicated set of points will define
00083 //               a valid CollisionPolygon: that is, at least three
00084 //               non-collinear points, with no points repeated.
00085 //
00086 //               This does not check that the polygon defined is
00087 //               convex; that check is made later, once we have
00088 //               projected the points to 2-d space where the decision
00089 //               is easier.
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   // Create a plane to determine the planarity of the first three
00099   // points.
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       // Make sure no points are repeated.
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 //     Function: CollisionPolygon::xform
00125 //       Access: Public, Virtual
00126 //  Description: Transforms the solid by the indicated matrix.
00127 ////////////////////////////////////////////////////////////////////
00128 void CollisionPolygon::
00129 xform(const LMatrix4f &mat) {
00130   // We need to convert all the vertices to 3-d for this operation,
00131   // and then convert them back.  Hopefully we won't lose too much
00132   // precision during all of this.
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 //     Function: CollisionPolygon::get_collision_origin
00165 //       Access: Public, Virtual
00166 //  Description: Returns the point in space deemed to be the "origin"
00167 //               of the solid for collision purposes.  The closest
00168 //               intersection point to this origin point is considered
00169 //               to be the most significant.
00170 ////////////////////////////////////////////////////////////////////
00171 LPoint3f CollisionPolygon::
00172 get_collision_origin() const {
00173   return to_3d(_median);
00174 }
00175 
00176 ////////////////////////////////////////////////////////////////////
00177 //     Function: CollisionPolygon::output
00178 //       Access: Public, Virtual
00179 //  Description:
00180 ////////////////////////////////////////////////////////////////////
00181 void CollisionPolygon::
00182 output(ostream &out) const {
00183   out << "cpolygon";
00184 }
00185 
00186 ////////////////////////////////////////////////////////////////////
00187 //     Function: CollisionPolygon::recompute_bound
00188 //       Access: Protected, Virtual
00189 //  Description:
00190 ////////////////////////////////////////////////////////////////////
00191 BoundingVolume *CollisionPolygon::
00192 recompute_bound() {
00193   // First, get ourselves a fresh, empty bounding volume.
00194   BoundingVolume *bound = BoundedObject::recompute_bound();
00195   nassertr(bound != (BoundingVolume*)0L, bound);
00196 
00197   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
00198 
00199   // Now actually compute the bounding volume by putting it around all
00200   // of our vertices.
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 //     Function: CollisionPolygon::test_intersection_from_sphere
00216 //       Access: Protected, Virtual
00217 //  Description: This is part of the double-dispatch implementation of
00218 //               test_intersection().  It is called when the "from"
00219 //               object is a sphere.
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     // If we have a velocity indication, we use that to determine some
00236     // more properties of the collision.
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     // First, there is no collision if the "from" object is moving in
00242     // the same direction as the plane's normal.
00243     float dot = delta.dot(get_normal());
00244     if (dot > 0.0f) {
00245       return NULL;
00246     }
00247 
00248     if (IS_NEARLY_ZERO(dot)) {
00249       // If we're moving parallel to the plane, the sphere is tested
00250       // at its final point.  Leave it as it is.
00251 
00252     } else {
00253       // Otherwise, we're moving into the plane; the sphere is tested
00254       // at the point along its path that is closest to intersecting
00255       // the plane.  This may be the actual intersection point, or it
00256       // may be the starting point or the final point.
00257       float t = -(dist_to_plane(a) / dot);
00258       if (t >= 1.0f) {
00259         // Leave it where it is.
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     // No intersection.
00279     return NULL;
00280   }
00281 
00282   // Ok, we intersected the plane, but did we intersect the polygon?
00283 
00284   // The nearest point within the plane to our center is the
00285   // intersection of the line (center, center+normal) with the plane.
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   // Now we have found a point on the polygon's plane that corresponds
00295   // to the point tangent to our collision sphere where it first
00296   // touches the plane.  We want to decide whether the sphere itself
00297   // will intersect the polygon.  We can approximate this by testing
00298   // whether a circle of the given radius centered around this tangent
00299   // point, in the plane of the polygon, would intersect.
00300 
00301   // But even this approximate test is too difficult.  To approximate
00302   // the approximation, we'll test two points: (1) the center itself.
00303   // If this is inside the polygon, then certainly the circle
00304   // intersects the polygon, and the sphere collides.  (2) a point on
00305   // the outside of the circle, nearest to the center of the polygon.
00306   // If _this_ point is inside the polygon, then again the circle, and
00307   // hence the sphere, intersects.  If neither point is inside the
00308   // polygon, chances are reasonably good the sphere doesn't intersect
00309   // the polygon after all.
00310 
00311   if (is_inside(p)) {
00312     // The circle's center is inside the polygon; we have a collision!
00313 
00314   } else {
00315 
00316     if (from_radius > 0.0f) {
00317       // Now find the point on the rim of the circle nearest the
00318       // polygon's center.
00319 
00320       // First, get a vector from the center of the circle to the center
00321       // of the polygon.
00322       LVector2f rim = _median - p;
00323       float rim_length = length(rim);
00324 
00325       if (rim_length <= from_radius) {
00326         // Here's a surprise: the center of the polygon is within the
00327         // circle!  Since the center is guaranteed to be interior to the
00328         // polygon (the polygon is convex), it follows that the circle
00329         // intersects the polygon.
00330 
00331       } else {
00332         // Now scale this vector to length radius, and get the new point.
00333         rim = (rim * from_radius / rim_length) + p;
00334 
00335         // Is the new point within the polygon?
00336         if (is_inside(rim)) {
00337           // It sure is!  The circle intersects!
00338 
00339         } else {
00340           // No intersection.
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     // We have to base the depth of intersection on the sphere's final
00358     // resting point, not the point from which we tested the
00359     // intersection.
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 //     Function: CollisionPolygon::test_intersection_from_ray
00374 //       Access: Protected, Virtual
00375 //  Description: This is part of the double-dispatch implementation of
00376 //               test_intersection().  It is called when the "from"
00377 //               object is a ray.
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     // No intersection.
00394     return NULL;
00395   }
00396 
00397   if (t < 0.0f) {
00398     // The intersection point is before the start of the ray.
00399     return NULL;
00400   }
00401 
00402   LPoint3f plane_point = from_origin + t * from_direction;
00403   if (!is_inside(to_2d(plane_point))) {
00404     // Outside the polygon's perimeter.
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 //     Function: CollisionPolygon::test_intersection_from_segment
00423 //       Access: Public, Virtual
00424 //  Description: This is part of the double-dispatch implementation of
00425 //               test_intersection().  It is called when the "from"
00426 //               object is a segment.
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     // No intersection.
00444     return NULL;
00445   }
00446 
00447   if (t < 0.0f || t > 1.0f) {
00448     // The intersection point is before the start of the segment or
00449     // after the end of the segment.
00450     return NULL;
00451   }
00452 
00453   LPoint3f plane_point = from_a + t * from_direction;
00454   if (!is_inside(to_2d(plane_point))) {
00455     // Outside the polygon's perimeter.
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 //     Function: CollisionPolygon::fill_viz_geom
00474 //       Access: Protected, Virtual
00475 //  Description: Fills the _viz_geom GeomNode up with Geoms suitable
00476 //               for rendering this solid.
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 //     Function: CollisionPolygon::is_inside
00516 //       Access: Private
00517 //  Description:
00518 ////////////////////////////////////////////////////////////////////
00519 bool CollisionPolygon::
00520 is_inside(const LPoint2f &p) const {
00521   // We insist that the polygon be convex.  This makes things a bit simpler.
00522 
00523   // In the case of a convex polygon, defined with points in counterclockwise
00524   // order, a point is interior to the polygon iff the point is not right of
00525   // each of the edges.
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 //     Function: CollisionPolygon::is_concave
00543 //       Access: Private
00544 //  Description: Returns true if the CollisionPolygon is concave
00545 //               (which is an error), or false otherwise.
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       // Oops, the polygon is concave.
00574       return true;
00575     }
00576   }
00577 
00578   // The polygon is safely convex.
00579   return false;
00580 }
00581 
00582 ////////////////////////////////////////////////////////////////////
00583 //     Function: CollisionPolygon::setup_points
00584 //       Access: Private
00585 //  Description:
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   // Tell the base CollisionPlane class what its plane will be.  We
00595   // can determine this from the first three 3-d points.
00596   Planef plane(begin[0], begin[1], begin[2]);
00597   set_plane(plane);
00598 
00599   LVector3f normal = get_normal();
00600 
00601 #ifndef NDEBUG
00602   // Make sure all the source points are good.
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   // First determine the largest of |normal[0]|, |normal[1]|, and
00628   // |normal[2]|.  This will tell us which axis-aligned plane the
00629   // polygon is most nearly aligned with, and therefore which plane we
00630   // should project onto for determining interiorness of the
00631   // intersection point.
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   // Now project all of the points onto the 2-d plane.
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   // Now make sure the points define a convex polygon.
00678   if (is_concave()) {
00679     collide_cat.error() << "Invalid concave CollisionPolygon defined:\n";
00680     const LPoint3f *pi;
00681     for (pi = begin; pi != end; ++pi) {
00682       collide_cat.error(false) << "  " << (*pi) << "\n";
00683     }
00684     collide_cat.error(false)
00685       << "  normal " << normal << " with length " << normal.length() << "\n";
00686     _points.clear();
00687   }
00688   */
00689 #endif
00690 
00691   // Now average up all the points to get the median.  This is the
00692   // geometric center of the polygon, and (since the polygon must be
00693   // convex) is also a point within the polygon.
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   // One final complication: In projecting the polygon onto the plane,
00701   // we might have lost its counterclockwise-vertex orientation.  If
00702   // this is the case, we must reverse the order of the vertices.
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 //     Function: CollisionPolygon::to_2d
00711 //       Access: Private
00712 //  Description: Assuming the indicated point in 3-d space lies within
00713 //               the polygon's plane, returns the corresponding point
00714 //               in the polygon's 2-d definition space.
00715 ////////////////////////////////////////////////////////////////////
00716 LPoint2f CollisionPolygon::
00717 to_2d(const LPoint3f &point3d) const {
00718   nassertr(!point3d.is_nan(), LPoint2f(0.0f, 0.0f));
00719 
00720   // Project the point of intersection with the plane onto the
00721   // axis-aligned plane we projected the polygon onto, and see if the
00722   // point is interior to the polygon.
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 //     Function: CollisionPolygon::to_3d
00740 //       Access: Private
00741 //  Description: Extrude the indicated point in the polygon's 2-d
00742 //               definition space back into 3-d coordinates.
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 //     Function: CollisionPolygon::write_datagram
00775 //       Access: Public
00776 //  Description: Function to write the important information in
00777 //               the particular object to a Datagram
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 //     Function: CollisionPolygon::fillin
00797 //       Access: Protected
00798 //  Description: Function that reads out of the datagram (or asks
00799 //               manager to read) all of the data that is needed to
00800 //               re-create this object and stores it in the appropiate
00801 //               place
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   // It seems that Windows wants this expression to prevent a
00819   // 'performance warning'.  Whatever.
00820   _reversed = (scan.get_uint8() != 0);
00821 }
00822 
00823 ////////////////////////////////////////////////////////////////////
00824 //     Function: CollisionPolygon::make_CollisionPolygon
00825 //       Access: Protected
00826 //  Description: Factory method to generate a CollisionPolygon object
00827 ////////////////////////////////////////////////////////////////////
00828 TypedWritable* CollisionPolygon::
00829 make_CollisionPolygon(const FactoryParams &params)
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 //     Function: CollisionPolygon::register_with_factory
00842 //       Access: Public, Static
00843 //  Description: Factory method to generate a CollisionPolygon object
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 

Generated on Fri May 2 00:35:39 2003 for Panda by doxygen1.3