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

panda/src/egg/eggPolygon.cxx

Go to the documentation of this file.
00001 // Filename: eggPolygon.cxx
00002 // Created by:  drose (16Jan99)
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 #include "eggPolygon.h"
00020 #include "eggGroupNode.h"
00021 
00022 #include <indent.h>
00023 
00024 #include <algorithm>
00025 
00026 TypeHandle EggPolygon::_type_handle;
00027 
00028 
00029 ////////////////////////////////////////////////////////////////////
00030 //     Function: EggPolygon::cleanup
00031 //       Access: Public, Virtual
00032 //  Description: Cleans up modeling errors in whatever context this
00033 //               makes sense.  For instance, for a polygon, this calls
00034 //               remove_doubled_verts(true).  For a point, it calls
00035 //               remove_nonunique_verts().  Returns true if the
00036 //               primitive is valid, or false if it is degenerate.
00037 ////////////////////////////////////////////////////////////////////
00038 bool EggPolygon::
00039 cleanup() {
00040   remove_doubled_verts(true);
00041 
00042   // Use calculate_normal() to determine if the polygon is degenerate.
00043   Normald normal;
00044   return calculate_normal(normal);
00045 }
00046 
00047 ////////////////////////////////////////////////////////////////////
00048 //     Function: EggPolygon::calculate_normal
00049 //       Access: Public
00050 //  Description: Calculates the true polygon normal--the vector
00051 //               pointing out of the front of the polygon--based on
00052 //               the vertices.  This does not return or change the
00053 //               polygon's normal as set via set_normal().
00054 //
00055 //               The return value is true if the normal is computed
00056 //               correctly, or false if the polygon is degenerate and
00057 //               does not have at least three noncollinear vertices.
00058 ////////////////////////////////////////////////////////////////////
00059 bool EggPolygon::
00060 calculate_normal(Normald &result, CoordinateSystem cs) const {
00061   // Get the first three unique vertices.
00062   Vertexd v[3];
00063   int i = 0;
00064 
00065   const_iterator vi = begin();
00066   while (vi != end()) {
00067     EggVertex *vertex = (*vi);
00068     v[i] = vertex->get_pos3();
00069 
00070     // Make sure the vertex is unique.
00071     bool is_unique = true;
00072     for (int j = 0; j < i && is_unique; j++) {
00073       is_unique = !v[i].almost_equal(v[j]);
00074     }
00075 
00076     if (is_unique) {
00077       if (i < 2) {
00078         i++;
00079 
00080       } else {
00081         // Ok, we have three vertices.  Do they determine a plane?
00082         LVector3d a = v[1] - v[0];
00083         LVector3d b = v[2] - v[0];
00084         LVector3d normal = a.cross(b);
00085 
00086         if (normal.normalize()) {
00087           // If we are in a left-handed coordinate system, we must
00088           // reverse the normal.
00089           if (cs == CS_default) {
00090             cs = default_coordinate_system;
00091           }
00092           if (cs == CS_zup_left || cs == CS_yup_left) {
00093             normal = -normal;
00094           }
00095 
00096           result = normal;
00097           return true;
00098         }
00099 
00100         // No, the three vertices must have been collinear.  Carry on
00101         // and get another vertex.
00102       }
00103     }
00104     ++vi;
00105   }
00106 
00107   // The polygon is degenerate: we don't have enough unique vertices
00108   // to determine a normal.
00109   return false;
00110 }
00111 
00112 ////////////////////////////////////////////////////////////////////
00113 //     Function: EggPolygon::triangulate_in_place
00114 //       Access: Public
00115 //  Description: Subdivides the polygon into triangles and adds those
00116 //               triangles to the parent group node in place of the
00117 //               original polygon.  Returns a pointer to the original
00118 //               polygon, which is likely about to be destructed.
00119 //
00120 //               If convex_also is true, both concave and convex
00121 //               polygons will be subdivided into triangles;
00122 //               otherwise, only concave polygons will be subdivided,
00123 //               and convex polygons will be copied unchanged into the
00124 //               container.
00125 ////////////////////////////////////////////////////////////////////
00126 PT(EggPolygon) EggPolygon::
00127 triangulate_in_place(bool convex_also) {
00128   EggGroupNode *parent = get_parent();
00129   nassertr(parent != (EggGroupNode *)NULL, this);
00130 
00131   PT(EggPolygon) save_me = this;
00132   parent->remove_child(this);
00133   triangulate_poly(parent, convex_also);
00134 
00135   return save_me;
00136 }
00137 
00138 ////////////////////////////////////////////////////////////////////
00139 //     Function: EggPolygon::write
00140 //       Access: Public, Virtual
00141 //  Description: Writes the polygon to the indicated output stream in
00142 //               Egg format.
00143 ////////////////////////////////////////////////////////////////////
00144 void EggPolygon::
00145 write(ostream &out, int indent_level) const {
00146   write_header(out, indent_level, "<Polygon>");
00147   write_body(out, indent_level+2);
00148   indent(out, indent_level) << "}\n";
00149 }
00150 
00151 ////////////////////////////////////////////////////////////////////
00152 //     Function: EggPolygon::decomp_concave
00153 //       Access: Private
00154 //  Description: Decomposes a concave polygon into triangles.  Returns
00155 //               true if successful, false if the polygon is
00156 //               self-intersecting.
00157 ////////////////////////////////////////////////////////////////////
00158 bool EggPolygon::
00159 decomp_concave(EggGroupNode *container, int asum, int x, int y) const {
00160 #define VX(p, c)    p->coord[c]
00161 
00162   struct DecompVtx {
00163     int index;
00164     LPoint3d coord;
00165     struct DecompVtx *next;
00166   };
00167 
00168   DecompVtx *p0, *p1, *p2, *t0, *vert;
00169   DecompVtx *m[3];
00170   double xmin, xmax, ymin, ymax;
00171   int i, init, csum, chek;
00172   double a[3], b[3], c[3], s[3];
00173 
00174   int num_verts = size();
00175   nassertr(num_verts >= 3, false);
00176 
00177   /* Make linked list of verts */
00178   vert = (DecompVtx *) alloca(sizeof(DecompVtx));
00179   vert->index = 0;
00180   vert->coord = get_vertex(0)->get_pos3();
00181   p1 = vert;
00182 
00183   for (i = 1; i < num_verts; i++) {
00184     p0 = (DecompVtx *) alloca(sizeof(DecompVtx));
00185     p0->index = i;
00186     p0->coord = get_vertex(i)->get_pos3();
00187     // There shouldn't be two consecutive identical vertices.  If
00188     // there are, skip one.
00189     if (!(p0->coord == p1->coord)) {
00190       p1->next = p0;
00191       p1 = p0;
00192     }
00193   }
00194   p1->next = vert;
00195 
00196   p0 = vert;
00197   p1 = p0->next;
00198   p2 = p1->next;
00199   m[0] = p0;
00200   m[1] = p1;
00201   m[2] = p2;
00202   chek = 0;
00203 
00204   while (p0 != p2->next) {
00205     /* Polygon is self-intersecting so punt */
00206     if (chek &&
00207         m[0] == p0 &&
00208         m[1] == p1 &&
00209         m[2] == p2) {
00210 
00211       return false;
00212     }
00213 
00214     chek = 1;
00215 
00216     a[0] = VX(p1, y) - VX(p2, y);
00217     b[0] = VX(p2, x) - VX(p1, x);
00218     a[2] = VX(p0, y) - VX(p1, y);
00219     b[2] = VX(p1, x) - VX(p0, x);
00220 
00221     csum = ((b[0] * a[2] - b[2] * a[0] >= 0.0) ? 1 : 0);
00222 
00223     if (csum ^ asum) {
00224       /* current angle is concave */
00225       p0 = p1;
00226       p1 = p2;
00227       p2 = p2->next;
00228 
00229     } else {
00230       /* current angle is convex */
00231       xmin = (VX(p0, x) < VX(p1, x)) ? VX(p0, x) : VX(p1, x);
00232       if (xmin > VX(p2, x))
00233         xmin = VX(p2, x);
00234 
00235       xmax = (VX(p0, x) > VX(p1, x)) ? VX(p0, x) : VX(p1, x);
00236       if (xmax < VX(p2, x))
00237         xmax = VX(p2, x);
00238 
00239       ymin = (VX(p0, y) < VX(p1, y)) ? VX(p0, y) : VX(p1, y);
00240       if (ymin > VX(p2, y))
00241         ymin = VX(p2, y);
00242 
00243       ymax = (VX(p0, y) > VX(p1, y)) ? VX(p0, y) : VX(p1, y);
00244       if (ymax < VX(p2, y))
00245         ymax = VX(p2, y);
00246 
00247       for (init = 1, t0 = p2->next; t0 != p0; t0 = t0->next) {
00248         if (VX(t0, x) >= xmin && VX(t0, x) <= xmax &&
00249             VX(t0, y) >= ymin && VX(t0, y) <= ymax) {
00250           if (init) {
00251             a[1] = VX(p2, y) - VX(p0, y);
00252             b[1] = VX(p0, x) - VX(p2, x);
00253             init = 0;
00254             c[0] = VX(p1, x) * VX(p2, y) - VX(p2, x) * VX(p1, y);
00255             c[1] = VX(p2, x) * VX(p0, y) - VX(p0, x) * VX(p2, y);
00256             c[2] = VX(p0, x) * VX(p1, y) - VX(p1, x) * VX(p0, y);
00257           }
00258 
00259           s[0] = a[0] * VX(t0, x) + b[0] * VX(t0, y) + c[0];
00260           s[1] = a[1] * VX(t0, x) + b[1] * VX(t0, y) + c[1];
00261           s[2] = a[2] * VX(t0, x) + b[2] * VX(t0, y) + c[2];
00262 
00263           if (asum) {
00264             if (s[0] >= 0.0 && s[1] >= 0.0 && s[2] >= 0.0)
00265               break;
00266           } else {
00267             if (s[0] <= 0.0 && s[1] <= 0.0 && s[2] <= 0.0)
00268               break;
00269           }
00270         }
00271       }
00272 
00273       if (t0 != p0) {
00274         p0 = p1;
00275         p1 = p2;
00276         p2 = p2->next;
00277       } else {
00278 
00279         // Here's a triangle to output.
00280         PT(EggPolygon) triangle = new EggPolygon(*this);
00281         triangle->clear();
00282         triangle->add_vertex(get_vertex(p0->index));
00283         triangle->add_vertex(get_vertex(p1->index));
00284         triangle->add_vertex(get_vertex(p2->index));
00285 
00286         if (triangle->cleanup()) {
00287           container->add_child(triangle.p());
00288         }
00289 
00290         p0->next = p1->next;
00291         p1 = p2;
00292         p2 = p2->next;
00293 
00294         m[0] = p0;
00295         m[1] = p1;
00296         m[2] = p2;
00297         chek = 0;
00298       }
00299     }
00300   }
00301 
00302   // One more triangle to output.
00303   PT(EggPolygon) triangle = new EggPolygon(*this);
00304   triangle->clear();
00305   triangle->add_vertex(get_vertex(p0->index));
00306   triangle->add_vertex(get_vertex(p1->index));
00307   triangle->add_vertex(get_vertex(p2->index));
00308 
00309   if (triangle->cleanup()) {
00310     container->add_child(triangle.p());
00311   }
00312 
00313   return true;
00314 }
00315 
00316 
00317 ////////////////////////////////////////////////////////////////////
00318 //     Function: EggPolygon::triangulate_poly
00319 //       Access: Private
00320 //  Description: Breaks a (possibly concave) higher-order polygon into
00321 //               a series of constituent triangles.  Fills the
00322 //               container up with EggPolygons that represent the
00323 //               triangles.  Returns true if successful, false on
00324 //               failure.
00325 //
00326 //               If convex_also is true, both concave and convex
00327 //               polygons will be subdivided into triangles;
00328 //               otherwise, only concave polygons will be subdivided,
00329 //               and convex polygons will be copied unchanged into the
00330 //               container.
00331 //
00332 //               It is assumed that the EggPolygon is not already a
00333 //               child of any other group when this function is
00334 //               called.
00335 ////////////////////////////////////////////////////////////////////
00336 bool EggPolygon::
00337 triangulate_poly(EggGroupNode *container, bool convex_also) {
00338   LPoint3d p0, p1, as;
00339   double dx1, dy1, dx2, dy2, max;
00340   int i, flag, asum, csum, index, x, y, v0, v1, v, even;
00341 
00342   if (!cleanup()) {
00343     return false;
00344   }
00345 
00346   // First see if the polygon is already a triangle
00347   int num_verts = size();
00348   if (num_verts == 3) {
00349     container->add_child(this);
00350 
00351     return true;
00352 
00353   } else if (num_verts < 3) {
00354     // Or if it's a degenerate polygon.
00355     return false;
00356   }
00357 
00358   // calculate signed areas
00359   as[0] = 0.0;
00360   as[1] = 0.0;
00361   as[2] = 0.0;
00362 
00363   for (i = 0; i < num_verts; i++) {
00364     p0 = get_vertex(i)->get_pos3();
00365     p1 = get_vertex((i + 1) % num_verts)->get_pos3();
00366     as[0] += p0[0] * p1[1] - p0[1] * p1[0];
00367     as[1] += p0[0] * p1[2] - p0[2] * p1[0];
00368     as[2] += p0[1] * p1[2] - p0[2] * p1[1];
00369   }
00370 
00371   /* select largest signed area */
00372   max = 0.0;
00373   index = 0;
00374   flag = 0;
00375   for (i = 0; i < 3; i++) {
00376     if (as[i] >= 0.0) {
00377       if (as[i] > max) {
00378         max = as[i];
00379         index = i;
00380         flag = 1;
00381       }
00382     } else {
00383       as[i] = -as[i];
00384       if (as[i] > max) {
00385         max = as[i];
00386         index = i;
00387         flag = 0;
00388       }
00389     }
00390   }
00391 
00392   /* pointer offsets */
00393   switch (index) {
00394   case 0:
00395     x = 0;
00396     y = 1;
00397     break;
00398 
00399   case 1:
00400     x = 0;
00401     y = 2;
00402     break;
00403 
00404   default: // case 2
00405     x = 1;
00406     y = 2;
00407     break;
00408   }
00409 
00410   /* concave check */
00411   p0 = get_vertex(0)->get_pos3();
00412   p1 = get_vertex(1)->get_pos3();
00413   dx1 = p1[x] - p0[x];
00414   dy1 = p1[y] - p0[y];
00415   p0 = p1;
00416   p1 = get_vertex(2)->get_pos3();
00417 
00418   dx2 = p1[x] - p0[x];
00419   dy2 = p1[y] - p0[y];
00420   asum = ((dx1 * dy2 - dx2 * dy1 >= 0.0) ? 1 : 0);
00421 
00422   for (i = 0; i < num_verts - 1; i++) {
00423     p0 = p1;
00424     p1 = get_vertex((i+3) % num_verts)->get_pos3();
00425 
00426     dx1 = dx2;
00427     dy1 = dy2;
00428     dx2 = p1[x] - p0[x];
00429     dy2 = p1[y] - p0[y];
00430     csum = ((dx1 * dy2 - dx2 * dy1 >= 0.0) ? 1 : 0);
00431 
00432     if (csum ^ asum) {
00433       // It's a concave polygon.  This is a little harder.
00434       return decomp_concave(container, flag, x, y);
00435     }
00436   }
00437 
00438   // It's a convex polygon.
00439   if (!convex_also) {
00440     container->add_child(this);
00441 
00442     return true;
00443   }
00444 
00445   v0 = 0;
00446   v1 = 1;
00447   v = num_verts - 1;
00448 
00449   even = 1;
00450 
00451   /*
00452    * Convert to triangles only. Do not fan out from a single vertex
00453    * but zigzag into triangle strip.
00454    */
00455   for (i = 0; i < num_verts - 2; i++) {
00456     if (even) {
00457       PT(EggPolygon) triangle = new EggPolygon(*this);
00458       triangle->clear();
00459       triangle->add_vertex(get_vertex(v0));
00460       triangle->add_vertex(get_vertex(v1));
00461       triangle->add_vertex(get_vertex(v));
00462 
00463       if (triangle->cleanup()) {
00464         container->add_child(triangle.p());
00465       }
00466 
00467       v0 = v1;
00468       v1 = v;
00469       v = v0 + 1;
00470     } else {
00471       PT(EggPolygon) triangle = new EggPolygon(*this);
00472       triangle->clear();
00473       triangle->add_vertex(get_vertex(v1));
00474       triangle->add_vertex(get_vertex(v0));
00475       triangle->add_vertex(get_vertex(v));
00476 
00477       if (triangle->cleanup()) {
00478         container->add_child(triangle.p());
00479       }
00480 
00481       v0 = v1;
00482       v1 = v;
00483       v = v0 - 1;
00484     }
00485 
00486     even = !even;
00487   }
00488 
00489   return true;
00490 }

Generated on Fri May 2 00:37:50 2003 for Panda by doxygen1.3