Bläddra i källkod

more work on egg loader with new geom format

David Rose 21 år sedan
förälder
incheckning
4af9b7b882
45 ändrade filer med 3970 tillägg och 121 borttagningar
  1. 56 3
      panda/src/doc/eggSyntax.txt
  2. 25 7
      panda/src/egg/Sources.pp
  3. 31 4
      panda/src/egg/config_egg.cxx
  4. 16 0
      panda/src/egg/config_egg.h
  5. 20 8
      panda/src/egg/eggAttributes.I
  6. 20 19
      panda/src/egg/eggAttributes.cxx
  7. 2 1
      panda/src/egg/eggAttributes.h
  8. 106 0
      panda/src/egg/eggCompositePrimitive.I
  9. 122 0
      panda/src/egg/eggCompositePrimitive.cxx
  10. 81 0
      panda/src/egg/eggCompositePrimitive.h
  11. 33 4
      panda/src/egg/eggGroupNode.cxx
  12. 8 1
      panda/src/egg/eggGroupNode.h
  13. 18 0
      panda/src/egg/eggMesher.I
  14. 787 0
      panda/src/egg/eggMesher.cxx
  15. 92 0
      panda/src/egg/eggMesher.h
  16. 141 0
      panda/src/egg/eggMesherEdge.I
  17. 84 0
      panda/src/egg/eggMesherEdge.cxx
  18. 74 0
      panda/src/egg/eggMesherEdge.h
  19. 182 0
      panda/src/egg/eggMesherStrip.I
  20. 1477 0
      panda/src/egg/eggMesherStrip.cxx
  21. 158 0
      panda/src/egg/eggMesherStrip.h
  22. 17 0
      panda/src/egg/eggMorph.I
  23. 2 0
      panda/src/egg/eggMorph.h
  24. 23 3
      panda/src/egg/eggMorphList.I
  25. 1 0
      panda/src/egg/eggMorphList.h
  26. 5 6
      panda/src/egg/eggPolygon.I
  27. 6 4
      panda/src/egg/eggPrimitive.I
  28. 38 9
      panda/src/egg/eggPrimitive.cxx
  29. 4 3
      panda/src/egg/eggPrimitive.h
  30. 47 0
      panda/src/egg/eggTriangleStrip.I
  31. 107 0
      panda/src/egg/eggTriangleStrip.cxx
  32. 65 0
      panda/src/egg/eggTriangleStrip.h
  33. 4 0
      panda/src/egg/egg_composite1.cxx
  34. 1 0
      panda/src/egg/egg_composite2.cxx
  35. 12 0
      panda/src/egg/lexer.lxx
  36. 61 2
      panda/src/egg/parser.yxx
  37. 2 24
      panda/src/egg2pg/config_egg2pg.cxx
  38. 1 13
      panda/src/egg2pg/config_egg2pg.h
  39. 17 2
      panda/src/egg2pg/eggLoader.cxx
  40. 1 1
      panda/src/gobj/qpgeom.cxx
  41. 2 3
      panda/src/gobj/qpgeomPrimitive.cxx
  42. 2 3
      panda/src/gobj/qpgeomPrimitive.h
  43. 2 0
      panda/src/gobj/qpgeomTristrips.cxx
  44. 16 1
      pandatool/src/eggprogs/eggTrans.cxx
  45. 1 0
      pandatool/src/eggprogs/eggTrans.h

+ 56 - 3
panda/src/doc/eggSyntax.txt

@@ -10,9 +10,9 @@ for Panda tools.  A number of utilities are provided that read and
 write egg files, for instance to convert to or from some other
 modeling format, or to apply a transform or optimize vertices.  The
 egg file philosophy is to describe objects in an abstract way that
-facilitates easy manipulation; thus, the format doesn't include
-information such as polygon connectivity or triangle meshes.  Egg
-files are furthermore designed to be human-readable to help a
+facilitates easy manipulation; thus, the format doesn't (usually)
+include information such as polygon connectivity or triangle meshes.
+Egg files are furthermore designed to be human-readable to help a
 developer diagnose (and sometimes repair) problems.  Also, the egg
 syntax is always intended to be backward compatible with previous
 versions, so that as the egg syntax is extended, old egg files will
@@ -717,6 +717,59 @@ GEOMETRY ENTRIES
   draw_order, and the "thick" attribute are all valid.
 
 
+<TriangleStrip> name { 
+    [attributes] 
+    <VertexRef> { 
+        indices 
+        <Ref> { pool-name } 
+    }
+    [component attributes]
+}
+
+  A triangle strip is only rarely encountered in an egg file; it is
+  normally generated automatically only during load time, when
+  connected triangles are automatically meshed for loading, and even
+  then it exists only momentarily.  Since a triangle strip is a
+  rendering optimization only and adds no useful scene information
+  over a loose collection of triangles, its usage is contrary to the
+  general egg philosophy of representing a scene in the abstract.
+  Nevertheless, the syntax exists, primarily to allow inspection of
+  the meshing results when needed.  You can also add custom
+  TriangleStrip entries to force a particular mesh arrangement.
+
+  A triangle strip is defined as a series of connected triangles.
+  After the first three vertices, which define the first triangle,
+  each new vertex defines one additional triangle, by alternating up
+  and down.
+
+  It is possible for the individual triangles of a triangle strip to
+  have a separate normal and/or color.  If so, a <Component> entry
+  should be given for each so-modified triangle:
+  
+  <Component> index {
+    <RGBA> { r g b a [morph-list] }
+    <Normal> { x y z [morph-list] }
+  }
+
+  Where index ranges from 0 to the number of components defined by the
+  triangle strip (less 1).  Note that the component attribute list
+  must always follow the vertex list.
+
+
+<TriangleFan> name { 
+    [attributes] 
+    <VertexRef> { 
+        indices 
+        <Ref> { pool-name } 
+    }
+    [component attributes]
+}
+
+  A triangle fan is similar to a triangle strip, except all of the
+  connected triangles share the same vertex, which is the first
+  vertex.  See <TriangleStrip>, above.
+
+
 
 PARAMETRIC DESCRIPTION ENTRIES
 

+ 25 - 7
panda/src/egg/Sources.pp

@@ -13,24 +13,32 @@
   #define SOURCES \
      config_egg.h eggAnimData.I eggAnimData.h eggAttributes.I  \
      eggAttributes.h eggBin.h eggBinMaker.h eggComment.I  \
-     eggComment.h eggCoordinateSystem.I eggCoordinateSystem.h  \
+     eggComment.h \
+     eggCompositePrimitive.I eggCompositePrimitive.h \
+     eggCoordinateSystem.I eggCoordinateSystem.h  \
      eggCurve.I eggCurve.h eggData.I eggData.h  \
      eggExternalReference.I eggExternalReference.h  \
      eggFilenameNode.I eggFilenameNode.h eggGroup.I eggGroup.h  \
      eggGroupNode.I eggGroupNode.h eggGroupUniquifier.h  \
      eggLine.I eggLine.h \
      eggMaterial.I eggMaterial.h eggMaterialCollection.I  \
-     eggMaterialCollection.h eggMiscFuncs.I eggMiscFuncs.h  \
+     eggMaterialCollection.h \
+     eggMesher.h eggMesher.I \
+     eggMesherEdge.h eggMesherEdge.I \
+     eggMesherStrip.h eggMesherStrip.I \
+     eggMiscFuncs.I eggMiscFuncs.h  \
      eggMorph.I eggMorph.h eggMorphList.I eggMorphList.h  \
      eggNamedObject.I eggNamedObject.h eggNameUniquifier.h  \
      eggNode.I eggNode.h eggNurbsCurve.I eggNurbsCurve.h  \
      eggNurbsSurface.I eggNurbsSurface.h eggObject.I eggObject.h  \
      eggParameters.h eggPoint.I eggPoint.h eggPolygon.I  \
-     eggPolygon.h eggPolysetMaker.h eggPoolUniquifier.h eggPrimitive.I  \
-     eggPrimitive.h eggRenderMode.I eggRenderMode.h  \
+     eggPolygon.h eggPolysetMaker.h eggPoolUniquifier.h \
+     eggPrimitive.I eggPrimitive.h \
+     eggRenderMode.I eggRenderMode.h  \
      eggSAnimData.I eggSAnimData.h eggSurface.I eggSurface.h  \
      eggSwitchCondition.h eggTable.I eggTable.h eggTexture.I  \
      eggTexture.h eggTextureCollection.I eggTextureCollection.h  \
+     eggTriangleStrip.I eggTriangleStrip.h \
      eggTransform3d.I eggTransform3d.h \
      eggUserData.I eggUserData.h \
      eggUtilities.I eggUtilities.h \
@@ -44,11 +52,17 @@
 
   #define INCLUDED_SOURCES \
      config_egg.cxx eggAnimData.cxx eggAttributes.cxx eggBin.cxx  \
-     eggBinMaker.cxx eggComment.cxx eggCoordinateSystem.cxx  \
+     eggBinMaker.cxx eggComment.cxx \
+     eggCompositePrimitive.cxx \
+     eggCoordinateSystem.cxx  \
      eggCurve.cxx eggData.cxx eggExternalReference.cxx  \
      eggFilenameNode.cxx eggGroup.cxx eggGroupNode.cxx  \
      eggGroupUniquifier.cxx eggLine.cxx eggMaterial.cxx  \
-     eggMaterialCollection.cxx eggMiscFuncs.cxx eggMorphList.cxx  \
+     eggMaterialCollection.cxx \
+     eggMesher.cxx \
+     eggMesherEdge.cxx \
+     eggMesherStrip.cxx \
+     eggMiscFuncs.cxx eggMorphList.cxx  \
      eggNamedObject.cxx eggNameUniquifier.cxx eggNode.cxx  \
      eggNurbsCurve.cxx eggNurbsSurface.cxx eggObject.cxx  \
      eggParameters.cxx eggPoint.cxx eggPolygon.cxx eggPolysetMaker.cxx  \
@@ -56,6 +70,7 @@
      eggSAnimData.cxx eggSurface.cxx eggSwitchCondition.cxx  \
      eggTable.cxx eggTexture.cxx eggTextureCollection.cxx  \
      eggTransform3d.cxx \
+     eggTriangleStrip.cxx \
      eggUserData.cxx \
      eggUtilities.cxx eggVertex.cxx eggVertexPool.cxx eggVertexUV.cxx \
      eggXfmAnimData.cxx eggXfmSAnim.cxx xx xx pt_EggMaterial.cxx  \
@@ -66,7 +81,9 @@
   #define INSTALL_HEADERS \
     eggAnimData.I eggAnimData.h \
     eggAttributes.I eggAttributes.h eggBin.h eggBinMaker.h eggComment.I \
-    eggComment.h eggCoordinateSystem.I eggCoordinateSystem.h eggCurve.I \
+    eggComment.h \
+    eggCompositePrimitive.I eggCompositePrimitive.h \
+    eggCoordinateSystem.I eggCoordinateSystem.h eggCurve.I \
     eggCurve.h eggData.I eggData.h eggExternalReference.I \
     eggExternalReference.h eggFilenameNode.I eggFilenameNode.h \
     eggGroup.I eggGroup.h eggGroupNode.I eggGroupNode.h \
@@ -84,6 +101,7 @@
     eggSwitchCondition.h eggTable.I eggTable.h eggTexture.I \
     eggTexture.h eggTextureCollection.I eggTextureCollection.h \
     eggTransform3d.I eggTransform3d.h \
+    eggTriangleStrip.I eggTriangleStrip.h \
     eggUserData.I eggUserData.h \
     eggUtilities.I eggUtilities.h eggVertex.I eggVertex.h \
     eggVertexPool.I eggVertexPool.h \

+ 31 - 4
panda/src/egg/config_egg.cxx

@@ -23,6 +23,7 @@
 #include "eggBin.h"
 #include "eggBinMaker.h"
 #include "eggComment.h"
+#include "eggCompositePrimitive.h"
 #include "eggCoordinateSystem.h"
 #include "eggCurve.h"
 #include "eggExternalReference.h"
@@ -46,6 +47,7 @@
 #include "eggSwitchCondition.h"
 #include "eggTable.h"
 #include "eggTexture.h"
+#include "eggTriangleStrip.h"
 #include "eggUserData.h"
 #include "eggVertex.h"
 #include "eggVertexPool.h"
@@ -64,13 +66,36 @@ ConfigureFn(config_egg) {
 
 ConfigVariableSearchPath egg_path
 ("egg-path", 
- "The search path along which only egg files are searched.  Generally, you "
- "should use model-path instead of egg-path.");
+ PRC_DESC("The search path along which only egg files are searched.  Generally, you "
+          "should use model-path instead of egg-path."));
 
 ConfigVariableBool egg_support_old_anims
 ("egg-support-old-anims", true,
- "Set this true to support loading of old character animation files, which "
- "had the convention that the order \"phr\" implied a reversed roll.");
+ PRC_DESC("Set this true to support loading of old character animation files, which "
+          "had the convention that the order \"phr\" implied a reversed roll."));
+
+ConfigVariableBool egg_mesh
+("egg-mesh", true);
+ConfigVariableBool egg_retesselate_coplanar
+("egg-retesselate-coplanar", true);
+ConfigVariableBool egg_unroll_fans
+("egg-unroll-fans", true);
+ConfigVariableBool egg_show_tstrips
+("egg-show-tstrips", false);
+ConfigVariableBool egg_show_qsheets
+("egg-show-qsheets", false);
+ConfigVariableBool egg_show_quads
+("egg-show-quads", false);
+ConfigVariableBool egg_subdivide_polys
+("egg-subdivide-polys", true);
+ConfigVariableBool egg_consider_fans
+("egg-consider-fans", true);
+ConfigVariableDouble egg_max_tfan_angle
+("egg-max-tfan-angle", 40.0);
+ConfigVariableInt egg_min_tfan_tris
+("egg-min-tfan-tris", 4);
+ConfigVariableDouble egg_coplanar_threshold
+("egg-coplanar-threshold", 0.01);
 
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libegg
@@ -94,6 +119,7 @@ init_libegg() {
   EggBin::init_type();
   EggBinMaker::init_type();
   EggComment::init_type();
+  EggCompositePrimitive::init_type();
   EggCoordinateSystem::init_type();
   EggCurve::init_type();
   EggData::init_type();
@@ -119,6 +145,7 @@ init_libegg() {
   EggSwitchConditionDistance::init_type();
   EggTable::init_type();
   EggTexture::init_type();
+  EggTriangleStrip::init_type();
   EggUserData::init_type();
   EggVertex::init_type();
   EggVertexPool::init_type();

+ 16 - 0
panda/src/egg/config_egg.h

@@ -23,12 +23,28 @@
 #include "notifyCategoryProxy.h"
 #include "configVariableSearchPath.h"
 #include "configVariableBool.h"
+#include "configVariableEnum.h"
+#include "configVariableDouble.h"
+#include "configVariableInt.h"
 
 NotifyCategoryDecl(egg, EXPCL_PANDAEGG, EXPTP_PANDAEGG);
 
 extern ConfigVariableSearchPath egg_path;
 extern ConfigVariableBool egg_support_old_anims;
 
+extern EXPCL_PANDAEGG ConfigVariableBool egg_mesh;
+extern EXPCL_PANDAEGG ConfigVariableBool egg_retesselate_coplanar;
+extern EXPCL_PANDAEGG ConfigVariableBool egg_unroll_fans;
+extern EXPCL_PANDAEGG ConfigVariableBool egg_show_tstrips;
+extern EXPCL_PANDAEGG ConfigVariableBool egg_show_qsheets;
+extern EXPCL_PANDAEGG ConfigVariableBool egg_show_quads;
+#define egg_false_color (egg_show_tstrips | egg_show_qsheets | egg_show_quads)
+extern EXPCL_PANDAEGG ConfigVariableBool egg_subdivide_polys;
+extern EXPCL_PANDAEGG ConfigVariableBool egg_consider_fans;
+extern EXPCL_PANDAEGG ConfigVariableDouble egg_max_tfan_angle;
+extern EXPCL_PANDAEGG ConfigVariableInt egg_min_tfan_tris;
+extern EXPCL_PANDAEGG ConfigVariableDouble egg_coplanar_threshold;
+
 extern EXPCL_PANDAEGG void init_libegg();
 
 #endif

+ 20 - 8
panda/src/egg/eggAttributes.I

@@ -19,7 +19,7 @@
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::has_normal
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE bool EggAttributes::
@@ -29,7 +29,7 @@ has_normal() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::get_normal
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE const Normald &EggAttributes::
@@ -40,7 +40,7 @@ get_normal() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::set_normal
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void EggAttributes::
@@ -51,7 +51,7 @@ set_normal(const Normald &normal) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::clear_normal
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void EggAttributes::
@@ -61,7 +61,7 @@ clear_normal() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::has_color
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE bool EggAttributes::
@@ -71,7 +71,7 @@ has_color() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::get_color
-//       Access: Public
+//       Access: Published
 //  Description: Returns the color set on this particular attribute.
 //               If there is no color set, returns white.
 ////////////////////////////////////////////////////////////////////
@@ -86,7 +86,7 @@ get_color() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void EggAttributes::
@@ -97,10 +97,22 @@ set_color(const Colorf &color) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void EggAttributes::
 clear_color() {
   _flags &= ~F_has_color;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggAttributes::sorts_less_than
+//       Access: Published
+//  Description: An ordering operator to compare two vertices for
+//               sorting order.  This imposes an arbitrary ordering
+//               useful to identify unique vertices.
+////////////////////////////////////////////////////////////////////
+INLINE bool EggAttributes::
+sorts_less_than(const EggAttributes &other) const {
+  return compare_to(other) < 0;
+}

+ 20 - 19
panda/src/egg/eggAttributes.cxx

@@ -22,14 +22,13 @@
 #include "eggMorphList.h"
 
 #include "indent.h"
-#include <math.h>
 
 TypeHandle EggAttributes::_type_handle;
 
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::Constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 EggAttributes::
@@ -39,7 +38,7 @@ EggAttributes() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::Copy constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 EggAttributes::
@@ -49,7 +48,7 @@ EggAttributes(const EggAttributes &copy) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::Copy assignment operator
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 EggAttributes &EggAttributes::
@@ -64,7 +63,7 @@ operator = (const EggAttributes &copy) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::Destructor
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
 EggAttributes::
@@ -74,7 +73,7 @@ EggAttributes::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::write
-//       Access: Public
+//       Access: Published
 //  Description: Writes the attributes to the indicated output stream in
 //               Egg format.
 ////////////////////////////////////////////////////////////////////
@@ -106,26 +105,27 @@ write(ostream &out, int indent_level) const {
 
 
 ////////////////////////////////////////////////////////////////////
-//     Function: EggAttributes::sorts_less_than
-//       Access: Public
+//     Function: EggAttributes::compare_to
+//       Access: Published
 //  Description: An ordering operator to compare two vertices for
 //               sorting order.  This imposes an arbitrary ordering
 //               useful to identify unique vertices.
 ////////////////////////////////////////////////////////////////////
-bool EggAttributes::
-sorts_less_than(const EggAttributes &other) const {
+int EggAttributes::
+compare_to(const EggAttributes &other) const {
   if (_flags != other._flags) {
-    return _flags < other._flags;
+    return (int)_flags - (int)other._flags;
   }
 
   if (has_normal()) {
     int compare =
       _normal.compare_to(other._normal, egg_parameters->_normal_threshold);
     if (compare != 0) {
-      return compare < 0;
+      return compare;
     }
-    if (_dnormals != other._dnormals) {
-      return _dnormals < other._dnormals;
+    compare = _dnormals.compare_to(other._dnormals);
+    if (compare != 0) {
+      return compare;
     }
   }
 
@@ -133,19 +133,20 @@ sorts_less_than(const EggAttributes &other) const {
     int compare =
       _color.compare_to(other._color, egg_parameters->_color_threshold);
     if (compare != 0) {
-      return compare < 0;
+      return compare;
     }
-    if (_drgbas != other._drgbas) {
-      return _drgbas < other._drgbas;
+    compare = _drgbas.compare_to(other._drgbas);
+    if (compare != 0) {
+      return compare;
     }
   }
 
-  return false;
+  return 0;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAttributes::transform
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Applies the indicated transformation matrix to the
 //               attributes.
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/egg/eggAttributes.h

@@ -56,7 +56,8 @@ PUBLISHED:
   INLINE void clear_color();
 
   void write(ostream &out, int indent_level) const;
-  bool sorts_less_than(const EggAttributes &other) const;
+  INLINE bool sorts_less_than(const EggAttributes &other) const;
+  int compare_to(const EggAttributes &other) const;
 
   void transform(const LMatrix4d &mat);
 

+ 106 - 0
panda/src/egg/eggCompositePrimitive.I

@@ -0,0 +1,106 @@
+// Filename: eggCompositePrimitive.I
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE EggCompositePrimitive::
+EggCompositePrimitive(const string &name) : EggPrimitive(name) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::Copy constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE EggCompositePrimitive::
+EggCompositePrimitive(const EggCompositePrimitive &copy) : EggPrimitive(copy) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::Destructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE EggCompositePrimitive::
+~EggCompositePrimitive() {
+  clear();
+  nassertv(_components.empty());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::Copy assignment operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE EggCompositePrimitive &EggCompositePrimitive::
+operator = (const EggCompositePrimitive &copy) {
+  EggPrimitive::operator = (copy);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::get_num_components
+//       Access: Published
+//  Description: Returns the number of individual component triangles
+//               within the composite.  Each one of these might have a
+//               different set of attributes.
+////////////////////////////////////////////////////////////////////
+INLINE int EggCompositePrimitive::
+get_num_components() const {
+  return _components.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::get_component
+//       Access: Published
+//  Description: Returns the attributes for the nth component
+//               triangle.
+////////////////////////////////////////////////////////////////////
+INLINE const EggAttributes *EggCompositePrimitive::
+get_component(int i) const {
+  nassertr(i >= 0 && i < (int)_components.size(), NULL);
+  return _components[i];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::get_component
+//       Access: Published
+//  Description: Returns the attributes for the nth component
+//               triangle.
+////////////////////////////////////////////////////////////////////
+INLINE EggAttributes *EggCompositePrimitive::
+get_component(int i) {
+  nassertr(i >= 0 && i < (int)_components.size(), NULL);
+  return _components[i];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::set_component
+//       Access: Published
+//  Description: Changes the attributes for the nth component
+//               triangle.
+////////////////////////////////////////////////////////////////////
+INLINE void EggCompositePrimitive::
+set_component(int i, const EggAttributes *attrib) {
+  nassertv(i >= 0 && i < (int)_components.size());
+  _components[i] = new EggAttributes(*attrib);
+}

+ 122 - 0
panda/src/egg/eggCompositePrimitive.cxx

@@ -0,0 +1,122 @@
+// Filename: eggCompositePrimitive.cxx
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "eggCompositePrimitive.h"
+
+TypeHandle EggCompositePrimitive::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::triangulate_in_place
+//       Access: Published
+//  Description: Subdivides the composite primitive into triangles and
+//               adds those triangles to the parent group node in
+//               place of the original primitive.  Returns a pointer
+//               to the original primitive, which is likely about to
+//               be destructed.
+//
+//               If convex_also is true, both concave and convex
+//               polygons will be subdivided into triangles;
+//               otherwise, only concave polygons will be subdivided,
+//               and convex polygons will be copied unchanged into the
+//               container.
+////////////////////////////////////////////////////////////////////
+PT(EggCompositePrimitive) EggCompositePrimitive::
+triangulate_in_place() {
+  EggGroupNode *parent = get_parent();
+  nassertr(parent != (EggGroupNode *)NULL, this);
+
+  PT(EggCompositePrimitive) save_me = this;
+  parent->remove_child(this);
+  do_triangulate(parent);
+
+  return save_me;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::prepare_add_vertex
+//       Access: Protected, Virtual
+//  Description: Marks the vertex as belonging to the primitive.  This
+//               is an internal function called by the STL-like
+//               functions push_back() and insert(), in preparation
+//               for actually adding the vertex.
+//
+//               i indicates the new position of the vertex in the
+//               list; n indicates the new number of vertices after
+//               the operation has completed.
+////////////////////////////////////////////////////////////////////
+void EggCompositePrimitive::
+prepare_add_vertex(EggVertex *vertex, int i, int n) {
+  EggPrimitive::prepare_add_vertex(vertex, i, n);
+
+  if (n >= 3) {
+    i = max(i - 2, 0);
+    nassertv(i <= (int)_components.size());
+    _components.insert(_components.begin() + i, new EggAttributes(*this));
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::prepare_remove_vertex
+//       Access: Protected, Virtual
+//  Description: Marks the vertex as removed from the primitive.  This
+//               is an internal function called by the STL-like
+//               functions pop_back() and erase(), in preparation for
+//               actually doing the removal.
+//
+//               i indicates the former position of the vertex in the
+//               list; n indicates the current number of vertices
+//               before the operation has completed.
+//
+//               It is an error to attempt to remove a vertex that is
+//               not already a vertex of this primitive.
+////////////////////////////////////////////////////////////////////
+void EggCompositePrimitive::
+prepare_remove_vertex(EggVertex *vertex, int i, int n) {
+  EggPrimitive::prepare_remove_vertex(vertex, i, n);
+
+  if (n >= 3) {
+    i = max(i - 2, 0);
+    nassertv(i < (int)_components.size());
+    delete _components[i];
+    _components.erase(_components.begin() + i);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggCompositePrimitive::write_body
+//       Access: Protected
+//  Description: Writes the attributes and the vertices referenced by
+//               the primitive to the indicated output stream in Egg
+//               format.
+////////////////////////////////////////////////////////////////////
+void EggCompositePrimitive::
+write_body(ostream &out, int indent_level) const {
+  EggPrimitive::write_body(out, indent_level);
+
+  for (int i = 0; i < get_num_components(); i++) {
+    const EggAttributes *attrib = get_component(i);
+    if (attrib->compare_to(*this) != 0) {
+      indent(out, indent_level + 2)
+        << "<Component> " << i << " {\n";
+      attrib->write(out, indent_level + 4);
+      indent(out, indent_level + 2) << "}\n";
+    }
+  }
+}

+ 81 - 0
panda/src/egg/eggCompositePrimitive.h

@@ -0,0 +1,81 @@
+// Filename: eggCompositePrimitive.h
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGCOMPOSITEPRIMITIVE_H
+#define EGGCOMPOSITEPRIMITIVE_H
+
+#include "pandabase.h"
+
+#include "eggPrimitive.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : EggCompositePrimitive
+// Description : The base class for primitives such as triangle strips
+//               and triangle fans, which include several component
+//               triangles, each of which might have its own color
+//               and/or normal.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG EggCompositePrimitive : public EggPrimitive {
+PUBLISHED:
+  INLINE EggCompositePrimitive(const string &name = "");
+  INLINE EggCompositePrimitive(const EggCompositePrimitive &copy);
+  INLINE EggCompositePrimitive &operator = (const EggCompositePrimitive &copy);
+  INLINE ~EggCompositePrimitive();
+
+  INLINE int get_num_components() const;
+  INLINE const EggAttributes *get_component(int i) const;
+  INLINE EggAttributes *get_component(int i);
+  INLINE void set_component(int i, const EggAttributes *attrib);
+
+  PT(EggCompositePrimitive) triangulate_in_place();
+
+protected:
+  virtual void prepare_add_vertex(EggVertex *vertex, int i, int n);
+  virtual void prepare_remove_vertex(EggVertex *vertex, int i, int n);
+
+  virtual void do_triangulate(EggGroupNode *container)=0;
+
+  void write_body(ostream &out, int indent_level) const;
+
+private:
+  typedef pvector<EggAttributes *> Components;
+  Components _components;
+
+public:
+
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    EggPrimitive::init_type();
+    register_type(_type_handle, "EggCompositePrimitive",
+                  EggPrimitive::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+};
+
+#include "eggCompositePrimitive.I"
+
+#endif

+ 33 - 4
panda/src/egg/eggGroupNode.cxx

@@ -23,6 +23,8 @@
 #include "eggExternalReference.h"
 #include "eggPrimitive.h"
 #include "eggPolygon.h"
+#include "eggCompositePrimitive.h"
+#include "eggMesher.h"
 #include "eggVertexPool.h"
 #include "eggVertex.h"
 #include "eggTextureCollection.h"
@@ -699,13 +701,13 @@ strip_normals() {
 //               the total number of new triangles produced, less
 //               degenerate polygons removed.
 //
-//               If convex_also is true, both concave and convex
+//               If flags contains T_convex, both concave and convex
 //               polygons will be subdivided into triangles;
 //               otherwise, only concave polygons will be subdivided,
 //               and convex polygons will be largely unchanged.
 ////////////////////////////////////////////////////////////////////
 int EggGroupNode::
-triangulate_polygons(bool convex_also) {
+triangulate_polygons(int flags) {
   int num_produced = 0;
 
   Children children_copy = _children;
@@ -718,10 +720,16 @@ triangulate_polygons(bool convex_also) {
 
     if (child->is_of_type(EggPolygon::get_class_type())) {
       EggPolygon *poly = DCAST(EggPolygon, child);
-      poly->triangulate_in_place(convex_also);
+      poly->triangulate_in_place((flags & T_convex) != 0);
+
+    } else if (child->is_of_type(EggCompositePrimitive::get_class_type())) {
+      EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, child);
+      comp->triangulate_in_place();
 
     } else if (child->is_of_type(EggGroupNode::get_class_type())) {
-      num_produced += DCAST(EggGroupNode, child)->triangulate_polygons(convex_also);
+      if (flags & T_recurse) {
+        num_produced += DCAST(EggGroupNode, child)->triangulate_polygons(flags);
+      }
     }
   }
 
@@ -729,6 +737,27 @@ triangulate_polygons(bool convex_also) {
   return num_produced;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggGroupNode::mesh_triangles
+//       Access: Published
+//  Description: Combine triangles together into triangle strips, at
+//               this group and below.
+////////////////////////////////////////////////////////////////////
+void EggGroupNode::
+mesh_triangles(int flags) {
+  EggMesher mesher;
+  mesher.mesh(this);
+
+  if (flags & T_recurse) {
+    EggGroupNode::iterator ci;
+    for (ci = begin(); ci != end(); ++ci) {
+      if ((*ci)->is_of_type(EggGroupNode::get_class_type())) {
+        EggGroupNode *group_child = DCAST(EggGroupNode, *ci);
+        group_child->mesh_triangles(flags);
+      }
+    }
+  }
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggGroupNode::remove_unused_vertices

+ 8 - 1
panda/src/egg/eggGroupNode.h

@@ -129,7 +129,14 @@ PUBLISHED:
   void recompute_polygon_normals(CoordinateSystem cs = CS_default);
   void strip_normals();
 
-  int triangulate_polygons(bool convex_also);
+  enum TriangulateFlags {
+    T_convex    = 0x001,
+    T_composite = 0x002,
+    T_recurse   = 0x004
+  };
+
+  int triangulate_polygons(int flags);
+  void mesh_triangles(int flags);
 
   int remove_unused_vertices();
   int remove_invalid_primitives();

+ 18 - 0
panda/src/egg/eggMesher.I

@@ -0,0 +1,18 @@
+// Filename: eggMesher.I
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 787 - 0
panda/src/egg/eggMesher.cxx

@@ -0,0 +1,787 @@
+// Filename: eggMesher.cxx
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "eggMesher.h"
+#include "eggPolygon.h"
+#include "eggCompositePrimitive.h"
+#include "eggTriangleStrip.h"
+#include "config_egg.h"
+
+#include <stdlib.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggMesher::
+EggMesher() {
+  _vertex_pool = NULL;
+  _strip_index = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::mesh
+//       Access: Public
+//  Description: Accepts an EggGroupNode, which contains a set of
+//               EggPrimitives--typically, triangles and quads--as
+//               children.  All of the EggPrimitives must reference
+//               the same vertex pool.
+//
+//               At the completion of this function, the triangles in
+//               the group will have been replaced with
+//               EggTriangleStrips as appropriate.
+////////////////////////////////////////////////////////////////////
+void EggMesher::
+mesh(EggGroupNode *group) {
+  _vertex_pool = NULL;
+  _strip_index = 0;
+
+  // Create a temporary node to hold the children of groupthat aren't
+  // involved in the meshing.
+  PT(EggGroupNode) saved_children = new EggGroupNode;
+
+  // Add each primitive in the group to the mesh pool.
+  EggGroupNode::iterator ci = group->begin();
+  while (ci != group->end()) {
+    EggGroupNode::iterator cnext = ci;
+    ++cnext;
+
+    if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
+      add_polygon(DCAST(EggPolygon, *ci), EggMesherStrip::MO_user);
+    } else {
+      // If it's not a polygon, preserve it.
+      saved_children->add_child(*ci);
+    }
+    ci = cnext;
+  }
+
+  do_mesh();
+
+  // Now copy the newly-meshed primitives back to the group.
+  group->clear();
+  group->steal_children(*saved_children);
+
+  Strips::iterator si;
+  for (si = _done.begin(); si != _done.end(); ++si) {
+    PT(EggPrimitive) egg_prim = get_prim(*si);
+    if (egg_prim != (EggPrimitive *)NULL) {
+      group->add_child(egg_prim);
+    }
+  }
+
+  _vertex_pool = NULL;
+  _strip_index = 0;
+  _color_sheets.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void EggMesher::
+write(ostream &out) const {
+  /*
+  out << _edges.size() << " edges:\n";
+  copy(_edges.begin(), _edges.end(), ostream_iterator<Edge>(out, "\n"));
+  */
+
+  out << _verts.size() << " verts:\n";
+  Verts::const_iterator vi;
+
+  for (vi = _verts.begin(); vi != _verts.end(); ++vi) {
+    int v = (*vi).first;
+    const EdgePtrs &edges = (*vi).second;
+    out << v << " shares " << count_vert_edges(edges) << " edges:\n";
+    EdgePtrs::const_iterator ei;
+    for (ei = edges.begin(); ei != edges.end(); ++ei) {
+      if (!(*ei)->_strips.empty() || !(*ei)->_opposite->_strips.empty()) {
+        out << "  " << **ei << "\n";
+      }
+    }
+  }
+
+  Strips::const_iterator si;
+  out << _tris.size() << " tris:\n";
+  for (si = _tris.begin(); si != _tris.end(); ++si) {
+    out << (*si) << "\n";
+  }
+
+  out << _quads.size() << " quads:\n";
+  for (si = _quads.begin(); si != _quads.end(); ++si) {
+    out << (*si) << "\n";
+  }
+
+  out << _strips.size() << " strips:\n";
+  for (si = _strips.begin(); si != _strips.end(); ++si) {
+    out << (*si) << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::add_polygon
+//       Access: Private
+//  Description: Adds a single polygon into the pool of available
+//               primitives for meshing.
+////////////////////////////////////////////////////////////////////
+bool EggMesher::
+add_polygon(const EggPolygon *egg_poly, EggMesherStrip::MesherOrigin origin) {
+  if (_vertex_pool == NULL) {
+    _vertex_pool = egg_poly->get_pool();
+  } else {
+    nassertr(_vertex_pool == egg_poly->get_pool(), false);
+  }
+
+  // Define an initial strip (probably of length 1) for the prim.
+  EggMesherStrip temp_strip(egg_poly, _strip_index++, _vertex_pool);
+  Strips &list = choose_strip_list(temp_strip);
+  list.push_back(temp_strip);
+  EggMesherStrip &strip = list.back();
+  strip._origin = origin;
+
+  int i;
+  int num_verts = egg_poly->size();
+
+  int *vptrs = (int *)alloca(num_verts * sizeof(int));
+  EdgePtrs **eptrs = (EdgePtrs **)alloca(num_verts * sizeof(EdgePtrs *));
+
+  // Get the common vertex pointers for the primitive's vertices.
+  for (i = 0; i < num_verts; i++) {
+    Verts::value_type v(egg_poly->get_vertex(i)->get_index(), EdgePtrs());
+    Verts::iterator n = _verts.insert(v).first;
+
+    vptrs[i] = (*n).first;
+    eptrs[i] = &(*n).second;
+
+    strip._verts.push_back(vptrs[i]);
+  }
+
+  // Now identify the common edges.
+
+  if (egg_poly->size() != 3 && egg_poly->size() != 4) {
+    // If we have a higher-order polygon, triangulate it
+    // automatically.
+    PT(EggGroupNode) temp_group = new EggGroupNode;
+    egg_poly->triangulate_into(temp_group, true);
+    EggGroupNode::iterator ci;
+    for (ci = temp_group->begin(); ci != temp_group->end(); ++ci) {
+      add_polygon(DCAST(EggPolygon, *ci), EggMesherStrip::MO_user);
+    }
+    
+  } else {
+    for (i = 0; i < num_verts; i++) {
+      // Define an inner and outer edge.  A polygon shares an edge with a
+      // neighbor only when one of its inner edges matches a neighbor's
+      // outer edge (and vice-versa).
+      EggMesherEdge inner(vptrs[i], vptrs[(i+1) % num_verts]);
+      EggMesherEdge outer(vptrs[(i+1) % num_verts], vptrs[i]);
+      
+      // Add it to the list and get its common pointer.
+      EggMesherEdge &inner_ref = (EggMesherEdge &)*_edges.insert(inner).first;
+      EggMesherEdge &outer_ref = (EggMesherEdge &)*_edges.insert(outer).first;
+      
+      // Tell the edges about each other.
+      inner_ref._opposite = &outer_ref;
+      outer_ref._opposite = &inner_ref;
+      
+      // Associate the common edge to the strip.
+      strip._edges.push_back(&inner_ref);
+      
+      // Associate the strip, as well as the original prim, to the edge.
+      outer_ref._strips.push_back(&strip);
+      
+      // Associate the common edge with the vertices that share it.
+      //      EggMesherEdge *edge_ptr = inner_ref.common_ptr();
+      eptrs[i]->insert(&outer_ref);
+      eptrs[(i+1) % num_verts]->insert(&outer_ref);
+    }
+  }
+  
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::do_mesh
+//       Access: Private
+//  Description: Performs the meshing process on the set of primitives
+//               that have been added via add_prim(), leaving the
+//               result in _done.
+////////////////////////////////////////////////////////////////////
+void EggMesher::
+do_mesh() {
+  if (egg_consider_fans) {
+    find_fans();
+  }
+
+  // First, we try to make all the best quads we can.
+  if (egg_retesselate_coplanar) {
+    make_quads();
+  }
+
+  // Then, we do the rest of the tris.
+  mesh_list(_tris);
+
+  if (egg_show_quads) {
+    // If we're showing quads, we shouldn't do any more meshing.
+    Strips::iterator si;
+    for (si = _quads.begin(); si != _quads.end(); ++si) {
+      if ((*si)._status == EggMesherStrip::MS_alive) {
+        (*si)._status = EggMesherStrip::MS_done;
+      }
+    }
+    for (si = _strips.begin(); si != _strips.end(); ++si) {
+      if ((*si)._status == EggMesherStrip::MS_alive) {
+        (*si)._status = EggMesherStrip::MS_done;
+      }
+    }
+  }
+
+  // Then, build quads into sheets where possible.
+  build_sheets();
+
+  // Pick up any quads that might have been left behind.
+  mesh_list(_quads);
+
+  // Finally, do the longer strips.
+  mesh_list(_strips);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::get_prim
+//       Access: Private
+//  Description: Creates an EggPrimitive that represents the result of
+//               the meshed EggMesherStrip object.
+////////////////////////////////////////////////////////////////////
+PT(EggPrimitive) EggMesher::
+get_prim(EggMesherStrip &strip) {
+  EggMesherStrip::PrimType orig_type = strip._type;
+  PT(EggPrimitive) egg_prim = strip.make_prim(_vertex_pool);
+
+  if (egg_show_tstrips) {
+    // If we have egg_show_tstrips on, it means we need to color every
+    // primitive according to which, if any, tristrip it is in.
+
+    Colorf color1, color2;
+
+    if (egg_prim->is_of_type(EggTriangleStrip::get_class_type()) /*||
+                egg_prim->is_of_type(EggTriangleFan::get_class_type())*/) {
+      make_random_color(color2);
+      color1 = (color2 * 0.8);   // somewhat darker.
+
+    } else {
+      // not-a-tristrip.
+      color1.set(0.85, 0.85, 0.85, 1.0);
+      color2.set(0.85, 0.85, 0.85, 1.0);
+    }
+
+    // Now color1 and color2 indicate the color for the first triangle
+    // and the rest of the primitive, respectively.
+    if (egg_prim->is_of_type(EggCompositePrimitive::get_class_type())) {
+      EggCompositePrimitive *egg_comp = DCAST(EggCompositePrimitive, egg_prim);
+      int num_components = egg_comp->get_num_components();
+      if (num_components > 0) {
+        egg_comp->get_component(0)->set_color(color1);
+        for (int i = 1; i < num_components; i++) {
+          egg_comp->get_component(i)->set_color(color2);
+        }
+      }
+    } else {
+      egg_prim->set_color(color1);
+    }
+
+    int num_verts = egg_prim->size();
+    for (int i = 0; i < num_verts; i++) {
+      egg_prim->get_vertex(i)->clear_color();
+    }
+
+  } else if (egg_show_qsheets) {
+    // egg_show_qsheets means to color every primitive according to
+    // which, if any, quadsheet it is in.  This is a bit easier,
+    // because the entire primitive gets the same color.
+
+    // Is this a quadsheet?
+    Colorf color1;
+    if (strip._row_id < 0) {
+      // Yep!  Assign a new color, if it doesn't already have one.
+      ColorSheetMap::iterator ci = _color_sheets.find(strip._row_id);
+
+      if (ci == _color_sheets.end()) {
+        make_random_color(color1);
+        _color_sheets[strip._row_id] = color1;
+      } else {
+        color1 = (*ci).second;
+      }
+    }
+
+    // Now color1 is the color we want to assign to the whole
+    // primitive.
+    egg_prim->set_color(color1);
+    int num_verts = egg_prim->size();
+    for (int i = 0; i < num_verts; i++) {
+      egg_prim->get_vertex(i)->clear_color();
+    }
+
+  } else if (egg_show_quads) {
+    // egg_show_quads means to show the assembling of tris into quads
+    // and fans.
+
+    // We use the following color convention:
+
+    // white: unchanged; as supplied by user.
+    // dark blue: quads made in the initial pass.  These are more certain.
+    // light blue: quads made in the second pass.  These are less certain.
+    // very light blue: quadstrips.  These are unlikely to appear.
+    // random shades of red: triangles and tristrips.
+    // green: fans and retesselated fan polygons.
+
+    // We need a handful of entries.
+    Colorf white(0.85, 0.85, 0.85, 1.0);
+    Colorf dark_blue(0.0, 0.0, 0.75, 1.0);
+    Colorf light_blue(0.4, 0.4, 0.8, 1.0);
+    Colorf very_light_blue(0.6, 0.6, 1.0, 1.0);
+    Colorf green(0.2, 0.8, 0.2, 1.0);
+
+    Colorf color1;
+    if (strip._origin == EggMesherStrip::MO_user) {
+      color1 = white;
+    } else if (strip._origin == EggMesherStrip::MO_firstquad) {
+      color1 = dark_blue;
+    } else if (strip._origin == EggMesherStrip::MO_fanpoly) {
+      color1 = green;
+    } else {
+      switch (orig_type) {
+      case EggMesherStrip::PT_quad:
+        color1 = light_blue;
+        break;
+
+      case EggMesherStrip::PT_quadstrip:
+        color1 = very_light_blue;
+        break;
+
+      case EggMesherStrip::PT_tristrip:
+        make_random_color(color1);
+        // Make it a shade of red.
+        if (color1[0] < color1[1]) {
+          float t = color1[0];
+          color1[0] = color1[1];
+          color1[1] = t;
+        }
+        color1[2] = color1[1];
+        break;
+
+      case EggMesherStrip::PT_trifan:
+        make_random_color(color1);
+        // Make it a shade of green.
+        if (color1[0] > color1[1]) {
+          float t = color1[0];
+          color1[0] = color1[1];
+          color1[1] = t;
+        }
+        color1[2] = color1[0];
+        break;
+
+      default:
+        color1 = white;
+      }
+    }
+
+    // Now color1 is the color we want to assign to the whole
+    // primitive.
+    egg_prim->set_color(color1);
+    int num_verts = egg_prim->size();
+    for (int i = 0; i < num_verts; i++) {
+      egg_prim->get_vertex(i)->clear_color();
+    }
+  }
+
+  return egg_prim;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::count_vert_edges
+//       Access: Private
+//  Description: Returns the number of edges in the list that are used
+//               by at least one EggMesherStrip object.
+////////////////////////////////////////////////////////////////////
+int EggMesher::
+count_vert_edges(const EdgePtrs &edges) const {
+  int count = 0;
+  EdgePtrs::const_iterator ei;
+  for (ei = edges.begin(); ei != edges.end(); ++ei) {
+    count += (!(*ei)->_strips.empty() || !(*ei)->_opposite->_strips.empty());
+  }
+  return count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::choose_strip_list
+//       Access: Private
+//  Description: Selects which of several strip lists on the EggMesher
+//               class the indicated EggMesherStrip should be added
+//               to.
+////////////////////////////////////////////////////////////////////
+plist<EggMesherStrip> &EggMesher::
+choose_strip_list(const EggMesherStrip &strip) {
+  switch (strip._status) {
+  case EggMesherStrip::MS_done:
+    return _done;
+
+  case EggMesherStrip::MS_dead:
+    return _dead;
+
+  case EggMesherStrip::MS_alive:
+    switch (strip._type) {
+    case EggMesherStrip::PT_tri:
+      return _tris;
+
+    case EggMesherStrip::PT_quad:
+      return _quads;
+
+    default:
+      return _strips;
+    }
+
+  default:
+    egg_cat.fatal() << "Invalid strip status!\n";
+    abort();
+  }
+
+  return _strips; // Unreachable; this is just to make the compiler happy.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::build_sheets
+//       Access: Private
+//  Description: Attempts to locate large quadsheets in the polygon
+//               soup.  A quadsheet is defined as a uniform
+//               rectangular mesh of quads joined at the corners.
+//
+//               Sheets like this are commonly output by modeling
+//               packages, especially uniform tesselators, and they
+//               are trivially converted into a row of triangle
+//               strips.
+////////////////////////////////////////////////////////////////////
+void EggMesher::
+build_sheets() {
+  int first_row_id = 1;
+
+  // First, move all the quads to our own internal list.
+  Strips pre_sheeted;
+  pre_sheeted.splice(pre_sheeted.end(), _quads);
+
+  while (!pre_sheeted.empty()) {
+    // Pick the first quad on the list.
+
+    Strips::iterator best = pre_sheeted.begin();
+
+    // If the row_id is negative, we've already built a sheet out of
+    // this quad.  Leave it alone.  We also need to leave it be if it
+    // has no available edges.
+    if ((*best)._row_id >= 0 &&
+        (*best)._status == EggMesherStrip::MS_alive &&
+        !(*best)._edges.empty()) {
+      // There are two possible sheets we could make from this quad,
+      // in two different orientations.  Measure them both and figure
+      // out which one is best.
+
+      const EggMesherEdge *edge_a = (*best)._edges.front();
+      const EggMesherEdge *edge_b = (*best).find_adjacent_edge(edge_a);
+
+      int num_prims_a = 0;
+      int num_rows_a = 0;
+      int first_row_id_a = first_row_id;
+      (*best).measure_sheet(edge_a, true, num_prims_a, num_rows_a,
+                           first_row_id_a, 0, 0);
+      first_row_id += num_rows_a;
+      double avg_length_a = (double)num_prims_a / (double)num_rows_a;
+
+      int num_prims_b = 0;
+      int num_rows_b = 0;
+      int first_row_id_b = first_row_id;
+      double avg_length_b;
+      if (edge_b != NULL) {
+        (*best).measure_sheet(edge_b, true, num_prims_b, num_rows_b,
+                             first_row_id_b, 0, 0);
+        first_row_id += num_rows_b;
+        avg_length_b = (double)num_prims_b / (double)num_rows_b;
+      }
+
+      // Which sheet is better?
+      if (edge_b != NULL && avg_length_b >= avg_length_a) {
+        // Sheet b.  That's easy.
+        (*best).cut_sheet(first_row_id_b, true, _vertex_pool);
+
+      } else {
+        // Nope, sheet a is better.  This is a bit of a nuisance
+        // because we've unfortunately wiped out the information we
+        // stored when we measured sheet a.  We'll have to do it
+        // again.
+
+        num_prims_a = 0;
+        num_rows_a = 0;
+        first_row_id_a = first_row_id;
+        (*best).measure_sheet(edge_a, true, num_prims_a, num_rows_a,
+                             first_row_id_a, 0, 0);
+        first_row_id += num_rows_a;
+
+        // Now we can cut it.
+        (*best).cut_sheet(first_row_id_a, true, _vertex_pool);
+      }
+    }
+
+    // Now put it somewhere.  We'll never see this quad again in
+    // build_sheets().
+    Strips &list = choose_strip_list(*best);
+    list.splice(list.end(), pre_sheeted, best);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::find_fans
+//       Access: Private
+//  Description: Looks for cases of multiple polygons all sharing a
+//               common vertex, and replaces these with a single fan.
+//
+//               This step is performed before detecting triangle
+//               strips.  We have to be careful: if we are too
+//               aggressive in detecting fans, we may ruin the ability
+//               to build good triangle strips, and we may thereby end
+//               up with a less-than-optimal solution.
+////////////////////////////////////////////////////////////////////
+void EggMesher::
+find_fans() {
+#if 0
+  pvector<Prim> unrolled_tris;
+
+  // Consider all vertices.  Any vertex with over a certain number of
+  // edges connected to it is eligible to become a fan.
+
+  Verts::iterator vi;
+
+  for (vi = _verts.begin(); vi != _verts.end(); ++vi) {
+    EdgePtrs &edges = (*vi).second;
+
+    // 14 is the magic number of edges.  12 edges or fewer are likely
+    // to be found on nearly every vertex in a quadsheet (six edges
+    // times two, one each way).  We don't want to waste time fanning
+    // out each vertex of a quadsheet, and we don't want to break up
+    // the quadsheets anyway.  We bump this up to 14 because some
+    // quadsheets are defined with triangles flipped here and there.
+    if (edges.size() > 6) {
+      const Vertex &v = (*vi).first;
+
+      // Build up a list of far fan edges.
+      typedef pvector<FanMaker> FanMakers;
+
+      FanMakers fans;
+
+      EdgePtrs::iterator ei;
+      Edge::Strips::iterator si;
+      for (ei = edges.begin(); ei != edges.end(); ++ei) {
+        for (si = (*ei)->_strips.begin();
+             si != (*ei)->_strips.end();
+             ++si) {
+          EggMesherStrip *strip = *si;
+          if (strip->_type == EggMesherStrip::PT_tri) {
+            FanMaker fan(&v, strip, this);
+            if (!fan._edges.empty()) {
+              fans.push_back(fan);
+            }
+          }
+        }
+      }
+
+      // Sort the fans list by edge pointers, and remove duplicates.
+      sort(fans.begin(), fans.end());
+      fans.erase(unique(fans.begin(), fans.end()),
+                 fans.end());
+
+      FanMakers::iterator fi, fi2;
+
+      // Now pull out connected edges.
+      int joined_any;
+      do {
+        joined_any = false;
+        for (fi = fans.begin(); fi != fans.end(); ++fi) {
+          if (!(*fi).is_empty()) {
+            fi2 = fi;
+            for (++fi2; fi2 != fans.end(); ++fi2) {
+              if (!(*fi2).is_empty()) {
+                joined_any = (*fi).join(*fi2);
+              }
+            }
+          }
+        }
+      } while (joined_any);
+
+      for (fi = fans.begin(); fi != fans.end(); ++fi) {
+        if ((*fi).is_valid()) {
+          (*fi).build(unrolled_tris);
+        }
+      }
+    }
+  }
+
+  // Finally, add back in the triangles we might have produced by
+  // unrolling some of the fans.  We can't add these back in safely
+  // until we're done traversing all the vertices and primitives we
+  // had in the first place (since adding them will affect the edge
+  // lists).
+  pvector<Prim>::iterator ti;
+  for (ti = unrolled_tris.begin(); ti != unrolled_tris.end(); ++ti) {
+    add_prim(*ti);
+  }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::make_quads
+//       Access: Private
+//  Description: Attempts to join up each single tri to its neighbor,
+//               to reconstruct a pattern of quads, suitable for
+//               making into quadsheets or at least quadstrips.
+//
+//               Quads have some nice properties that make them easy
+//               to manipulate when meshing.  We will ultimately
+//               convert the quadsheets and quadstrips into tristrips,
+//               but it's easier to work with them first while they're
+//               quads.
+////////////////////////////////////////////////////////////////////
+void EggMesher::
+make_quads() {
+  // Ideally, we want to match tris across their hypotenuse to make a
+  // pattern of quads.  (This assumes that we are working with a
+  // triangulated mesh pattern, of course.  If we have some other
+  // pattern of tris, all bets are off and it doesn't really matter
+  // anyway.)
+
+  // First, we'll find all the tris that have no doubt about their
+  // ideal mate, and pair them up right away.  The others we'll get to
+  // later.  This way, the uncertain matches won't pollute the quad
+  // alignment for everyone else.
+
+  typedef pair<EggMesherStrip *, EggMesherStrip *> Pair;
+  typedef pair<Pair, EggMesherEdge *> Matched;
+  typedef pvector<Matched> SoulMates;
+
+  SoulMates soulmates;
+
+  EggMesherStrip *tri, *mate, *mate2;
+  EggMesherEdge *common_edge, *common_edge2;
+
+  Strips::iterator si;
+  for (si = _tris.begin(); si != _tris.end(); ++si) {
+    tri = &(*si);
+
+    if (tri->_status == EggMesherStrip::MS_alive) {
+      if (tri->find_ideal_mate(mate, common_edge, _vertex_pool)) {
+        // Does our chosen mate want us too?
+        if (mate->_type == EggMesherStrip::PT_tri && 
+            mate->_status == EggMesherStrip::MS_alive &&
+            mate->find_ideal_mate(mate2, common_edge2, _vertex_pool) &&
+            mate2 == tri) {
+          // Hooray!
+          soulmates.push_back(Matched(Pair(tri, mate), common_edge));
+          // We'll temporarily mark the two tris as paired.
+          tri->_status = EggMesherStrip::MS_paired;
+          mate->_status = EggMesherStrip::MS_paired;
+        }
+      }
+    }
+  }
+
+  // Now that we've found all the tris that are sure about each other,
+  // mate them.
+  SoulMates::iterator mi;
+  for (mi = soulmates.begin(); mi != soulmates.end(); ++mi) {
+    tri = (*mi).first.first;
+    mate = (*mi).first.second;
+    common_edge = (*mi).second;
+
+    nassertv(tri->_status == EggMesherStrip::MS_paired);
+    nassertv(mate->_status == EggMesherStrip::MS_paired);
+    tri->_status = EggMesherStrip::MS_alive;
+    mate->_status = EggMesherStrip::MS_alive;
+
+    EggMesherStrip::mate_pieces(common_edge, *tri, *mate, _vertex_pool);
+    tri->_origin = EggMesherStrip::MO_firstquad;
+  }
+
+  // Now move all the strips off the tri list that no longer belong.
+  Strips::iterator next;
+  si = _tris.begin();
+  while (si != _tris.end()) {
+    next = si;
+    ++next;
+
+    Strips &list = choose_strip_list(*si);
+    if (&list != &_tris) {
+      list.splice(list.end(), _tris, si);
+    }
+
+    si = next;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::mesh_list
+//       Access: Private
+//  Description: Processes all of the strips on the indicated list.
+////////////////////////////////////////////////////////////////////
+void EggMesher::
+mesh_list(Strips &strips) {
+  while (!strips.empty()) {
+    // Pick the first strip on the list.
+
+    Strips::iterator best = strips.begin();
+
+    if ((*best)._status == EggMesherStrip::MS_alive) {
+      (*best).mate(_vertex_pool);
+    }
+
+    // Put the strip back on the end of whichever list it wants.  This
+    // might be the same list, if the strip is still alive, or it
+    // might be _done or _dead.
+    Strips &list = choose_strip_list(*best);
+    list.splice(list.end(), strips, best);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesher::make_random_color
+//       Access: Private, Static
+//  Description: Chooses a reasonable random color.
+////////////////////////////////////////////////////////////////////
+void EggMesher::
+make_random_color(Colorf &color) {
+  LVector3f rgb;
+  float len;
+  do {
+    for (int i = 0; i < 3; i++) {
+      rgb[i] = (double)rand() / (double)RAND_MAX;
+    }
+    len = length(rgb);
+
+    // Repeat until we have a color that's not too dark or too light.
+  } while (len < .1 || len > 1.5);
+
+  color.set(rgb[0], rgb[1], rgb[2],
+            0.25 + 0.75 * (double)rand() / (double)RAND_MAX);
+}
+

+ 92 - 0
panda/src/egg/eggMesher.h

@@ -0,0 +1,92 @@
+// Filename: eggMesher.h
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGMESHER_H
+#define EGGMESHER_H
+
+#include "pandabase.h"
+#include "eggMesherEdge.h"
+#include "eggMesherStrip.h"
+//#include "eggMesherFanMaker.h"
+#include "eggPolygon.h"
+#include "pvector.h"
+#include "plist.h"
+#include "pset.h"
+#include "pmap.h"
+
+#include <algorithm>
+
+class MesherFanMaker;
+
+///////////////////////////////////////////////////////////////////
+//       Class : EggMesher
+// Description : Collects together unrelated EggPrimitives, determines
+//               their edge connectivity, and generates a set of
+//               EggTriangleStrips that represent the same geometry.
+////////////////////////////////////////////////////////////////////
+class EggMesher {
+public:
+  EggMesher();
+
+  void mesh(EggGroupNode *group);
+
+  void write(ostream &out) const;
+
+  bool _consider_fans;
+  bool _retesselate_coplanar;
+  bool _show_quads;
+  bool _show_qsheets;
+
+private:
+  bool add_polygon(const EggPolygon *egg_poly, 
+                   EggMesherStrip::MesherOrigin origin);
+  void do_mesh();
+  PT(EggPrimitive) get_prim(EggMesherStrip &strip);
+
+  typedef plist<EggMesherStrip> Strips;
+  typedef pset<EggMesherEdge> Edges;
+  typedef pset<EggMesherEdge *> EdgePtrs;
+  typedef pmap<int, EdgePtrs> Verts;
+
+  // This is used for show-qsheets.
+  typedef pmap<int, Colorf> ColorSheetMap;
+
+  int count_vert_edges(const EdgePtrs &edges) const;
+  plist<EggMesherStrip> &choose_strip_list(const EggMesherStrip &strip);
+
+  void build_sheets();
+  void find_fans();
+  void make_quads();
+  void mesh_list(Strips &strips);
+  static void make_random_color(Colorf &color);
+
+  Strips _tris, _quads, _strips;
+  Strips _dead, _done;
+  Verts _verts;
+  Edges _edges;
+  int _strip_index;
+  EggVertexPool *_vertex_pool;
+  ColorSheetMap _color_sheets;
+
+  friend class EggMesherStrip;
+  friend class EggMesherFanMaker;
+};
+
+#include "eggMesher.I"
+
+#endif

+ 141 - 0
panda/src/egg/eggMesherEdge.I

@@ -0,0 +1,141 @@
+// Filename: eggMesherEdge.I
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::Constructor
+//       Access: Public
+//  Description: Defines an edge as a pair of vertices.  The _opposite
+//               pointer should be filled in explicitly by the caller.
+////////////////////////////////////////////////////////////////////
+INLINE EggMesherEdge::
+EggMesherEdge(int vi_a, int vi_b) : _vi_a(vi_a), _vi_b(vi_b) {
+  _opposite = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE EggMesherEdge::
+EggMesherEdge(const EggMesherEdge &copy) :
+  _vi_a(copy._vi_a),
+  _vi_b(copy._vi_b),
+  _strips(copy._strips),
+  _opposite(copy._opposite)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::contains_vertex
+//       Access: Public
+//  Description: Returns true if the edge contains the indicated
+//               vertex index, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMesherEdge::
+contains_vertex(int vi) const {
+  return (_vi_a == vi || _vi_b == vi);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::matches
+//       Access: Public
+//  Description: Returns true if this edge represents the same line
+//               segment as the other edge, in either direction.
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMesherEdge::
+matches(const EggMesherEdge &other) const {
+  return ((_vi_a == other._vi_a && _vi_b == other._vi_b) ||
+          (_vi_b == other._vi_a && _vi_a == other._vi_b));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::common_ptr
+//       Access: Public
+//  Description: Returns an arbitrary pointer that is used to
+//               represent both this edge and its opposite.
+//               this->common_ptr() is guaranteed to be the same as
+//               this->_opposite->common_ptr().
+////////////////////////////////////////////////////////////////////
+INLINE EggMesherEdge *EggMesherEdge::
+common_ptr() {
+  return min(this, _opposite);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::operator ==
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMesherEdge::
+operator == (const EggMesherEdge &other) const {
+  return _vi_a == other._vi_a && _vi_b == other._vi_b;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::operator !=
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMesherEdge::
+operator != (const EggMesherEdge &other) const {
+  return !operator == (other);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::operator <
+//       Access: Public
+//  Description: Defines an arbitrary ordering for edges, used for
+//               putting edges in a sorted container.
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMesherEdge::
+operator < (const EggMesherEdge &other) const {
+  if (_vi_a != other._vi_a) {
+    return _vi_a < other._vi_a;
+  }
+  return _vi_b < other._vi_b;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::compute_length
+//       Access: Public
+//  Description: Returns the length of the edge in model units.
+////////////////////////////////////////////////////////////////////
+INLINE double EggMesherEdge::
+compute_length(const EggVertexPool *vertex_pool) const {
+  LPoint3d a = vertex_pool->get_vertex(_vi_a)->get_pos3();
+  LPoint3d b = vertex_pool->get_vertex(_vi_b)->get_pos3();
+  return (a - b).length();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::compute_box
+//       Access: Public
+//  Description: Returns a 3-component vector that represents the
+//               lengths of the sides of the smalled axis-aligned box
+//               that contains the edge.  That is, the projection the
+//               edge onto each axis.
+////////////////////////////////////////////////////////////////////
+INLINE LVecBase3d EggMesherEdge::
+compute_box(const EggVertexPool *vertex_pool) const {
+  LPoint3d a = vertex_pool->get_vertex(_vi_a)->get_pos3();
+  LPoint3d b = vertex_pool->get_vertex(_vi_b)->get_pos3();
+  LVector3d v = (a - b);
+  return LVecBase3d(fabs(v[0]), fabs(v[1]), fabs(v[2]));
+}

+ 84 - 0
panda/src/egg/eggMesherEdge.cxx

@@ -0,0 +1,84 @@
+// Filename: eggMesherEdge.cxx
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "eggMesherEdge.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::remove
+//       Access: Public
+//  Description: Removes an edge from a particular strip.
+////////////////////////////////////////////////////////////////////
+void EggMesherEdge::
+remove(EggMesherStrip *strip) {
+  strip->_edges.remove(this);
+  strip->_edges.remove(_opposite);
+
+  _strips.remove(strip);
+  _opposite->_strips.remove(strip);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::change_strip
+//       Access: Public
+//  Description: Reparents the edge from strip "from" to strip "to".
+////////////////////////////////////////////////////////////////////
+void EggMesherEdge::
+change_strip(EggMesherStrip *from, EggMesherStrip *to) {
+  Strips::iterator si;
+
+  for (si = _strips.begin(); si != _strips.end(); ++si) {
+    if (*si == from) {
+      *si = to;
+    }
+  }
+
+  for (si = _opposite->_strips.begin();
+       si != _opposite->_strips.end();
+       ++si) {
+    if (*si == from) {
+      *si = to;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherEdge::output
+//       Access: Public
+//  Description: Formats the edge for output in some sensible way.
+////////////////////////////////////////////////////////////////////
+void EggMesherEdge::
+output(ostream &out) const {
+  out << "Edge [" << _vi_a << " to " << _vi_b << "], "
+      << _strips.size() << " strips:";
+
+  Strips::const_iterator si;
+  for (si = _strips.begin(); si != _strips.end(); ++si) {
+    out << " " << (*si)->_index;
+  }
+
+  if (_opposite!=NULL) {
+    out << " opposite "
+        << _opposite->_strips.size() << " strips:";
+
+    for (si = _opposite->_strips.begin();
+         si != _opposite->_strips.end();
+         ++si) {
+      out << " " << (*si)->_index;
+    }
+  }
+}

+ 74 - 0
panda/src/egg/eggMesherEdge.h

@@ -0,0 +1,74 @@
+// Filename: eggMesherEdge.h
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGMESHEREDGE_H
+#define EGGMESHEREDGE_H
+
+#include "pandabase.h"
+#include "eggVertexPool.h"
+#include "eggVertex.h"
+#include "plist.h"
+
+class EggMesherStrip;
+
+///////////////////////////////////////////////////////////////////
+//       Class : EggMesherEdge
+// Description : Represents one edge of a triangle, as used by the
+//               EggMesher to discover connected triangles.  The edge
+//               is actually represented as a pair of vertex indices
+//               into the same vertex pool.
+////////////////////////////////////////////////////////////////////
+class EggMesherEdge {
+public:
+  INLINE EggMesherEdge(int vi_a, int vi_b);
+  INLINE EggMesherEdge(const EggMesherEdge &copy);
+
+  void remove(EggMesherStrip *strip);
+  void change_strip(EggMesherStrip *from, EggMesherStrip *to);
+
+  INLINE bool contains_vertex(int vi) const;
+
+  INLINE bool matches(const EggMesherEdge &other) const;
+
+  INLINE EggMesherEdge *common_ptr();
+
+  INLINE bool operator == (const EggMesherEdge &other) const;
+  INLINE bool operator != (const EggMesherEdge &other) const;
+  INLINE bool operator < (const EggMesherEdge &other) const;
+
+  INLINE double compute_length(const EggVertexPool *vertex_pool) const;
+  INLINE LVecBase3d compute_box(const EggVertexPool *vertex_pool) const;
+
+  void output(ostream &out) const;
+
+  int _vi_a, _vi_b;
+
+  typedef plist<EggMesherStrip *> Strips;
+  Strips _strips;
+  EggMesherEdge *_opposite;
+};
+
+INLINE ostream &
+operator << (ostream &out, const EggMesherEdge &edge) {
+  edge.output(out);
+  return out;
+}
+
+#include "eggMesherEdge.I"
+
+#endif

+ 182 - 0
panda/src/egg/eggMesherStrip.I

@@ -0,0 +1,182 @@
+// Filename: eggMesherStrip.I
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE EggMesherStrip::
+EggMesherStrip() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE EggMesherStrip::
+EggMesherStrip(const EggMesherStrip &copy) :
+  _prims(copy._prims),
+  _edges(copy._edges),
+  _verts(copy._verts),
+  _type(copy._type),
+  _index(copy._index),
+  _status(copy._status),
+  _planar(copy._planar),
+  _plane_normal(copy._plane_normal),
+  _plane_offset(copy._plane_offset),
+  _row_id(copy._row_id)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::is_coplanar_with
+//       Access: Public
+//  Description: Returns true if the strip and the other strip are
+//               coplanar, within the indicated threshold.  See
+//               coplanarity().
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMesherStrip::
+is_coplanar_with(const EggMesherStrip &other, float threshold) const {
+  return (coplanarity(other) <= threshold);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::coplanarity
+//       Access: Public
+//  Description: Returns the degree to which the two strips are
+//               coplanar.  0.0 is exactly coplanar; numbers somewhat
+//               larger than zero indicate less coplanar.  1.0 is
+//               at right angles; 2.0 is exactly backfacing.  If
+//               either strip is not itself planar, 3.0 is returned.
+////////////////////////////////////////////////////////////////////
+INLINE float EggMesherStrip::
+coplanarity(const EggMesherStrip &other) const {
+  if (_planar && other._planar) {
+    return 1.0 - dot(_plane_normal, other._plane_normal);
+  } else {
+    return 3.0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::type_category
+//       Access: Public
+//  Description: Returns an integer which gives a heuristic about the
+//               similarity of different strip types.  In general,
+//               closer numbers are more similar.
+////////////////////////////////////////////////////////////////////
+INLINE int EggMesherStrip::
+type_category() const {
+  switch (_type) {
+  case PT_tri:
+    return 1;
+
+  case PT_tristrip:
+    return 2;
+
+  case PT_quad:
+  case PT_quadstrip:
+    return 5;
+
+  default:
+    return 10;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::rotate_forward
+//       Access: Public
+//  Description: Rotates a triangle or quad by bringing its first
+//               vertex to the back.
+////////////////////////////////////////////////////////////////////
+INLINE void EggMesherStrip::
+rotate_forward() {
+  _verts.push_back(_verts.front());
+  _verts.pop_front();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::rotate_back
+//       Access: Public
+//  Description: Rotates a triangle or quad by bringing its last
+//               vertex to the front.
+////////////////////////////////////////////////////////////////////
+INLINE void EggMesherStrip::
+rotate_back() {
+  _verts.push_front(_verts.back());
+  _verts.pop_back();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::get_head_edge
+//       Access: Public
+//  Description: Returns an EggMesherEdge which represents the leading
+//               edge in the quadstrip or tristrip.  This
+//               EggMesherEdge will not have pointer equality with any
+//               shared EggMesherEdge.
+////////////////////////////////////////////////////////////////////
+INLINE EggMesherEdge EggMesherStrip::
+get_head_edge() const {
+  Verts::const_iterator vi = _verts.begin();
+  nassertr(vi != _verts.end(), EggMesherEdge(0, 0));
+  ++vi;
+  return EggMesherEdge(_verts.front(), *vi);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::get_tail_edge
+//       Access: Public
+//  Description: Returns an EggMesherEdge which represents the
+//               trailing edge in the quadstrip or tristrip.  This
+//               EggMesherEdge will not have pointer equality with any
+//               shared EggMesherEdge.
+////////////////////////////////////////////////////////////////////
+INLINE EggMesherEdge EggMesherStrip::
+get_tail_edge() const {
+  Verts::const_reverse_iterator vi = _verts.rbegin();
+  nassertr(vi != _verts.rend(), EggMesherEdge(0, 0));
+  ++vi;
+  return EggMesherEdge(*vi, _verts.back());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::operator ==
+//       Access: Public
+//  Description: Defines equality for strips.  This actually tests
+//               only pointer equality; it's used only when removing a
+//               strip from the list.
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMesherStrip::
+operator == (const EggMesherStrip &other) const {
+  return this == &other;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::operator !=
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMesherStrip::
+operator != (const EggMesherStrip &other) const {
+  return !operator == (other);
+}

+ 1477 - 0
panda/src/egg/eggMesherStrip.cxx

@@ -0,0 +1,1477 @@
+// Filename: eggMesherStrip.cxx
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "eggMesherStrip.h"
+#include "eggMesherEdge.h"
+#include "eggPrimitive.h"
+#include "eggPolygon.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+EggMesherStrip::
+EggMesherStrip(const EggPrimitive *prim, int index, 
+               const EggVertexPool *vertex_pool) {
+  _index = index;
+  _row_id = 0;
+  _status = MS_alive;
+  _origin = MO_unknown;
+
+  _type = PT_poly; //prim.get_type();
+
+  // We care only about the prim's attributes in the _prims array.
+  // The vertices get re-added later by EggMesher::add_prim().
+  _prims.push_back(prim);
+
+  if (_type == PT_poly) {
+    switch (prim->size()) {
+    case 3:
+      _type = PT_tri;
+      break;
+
+    case 4:
+      _type = PT_quad;
+      break;
+    }
+  }
+
+  if (_type == PT_quad) {
+    // A quad has two internal triangles; we therefore push the prim
+    // attributes twice.
+    _prims.push_back(prim);
+  }
+
+  _planar = false;
+
+  if (prim->is_of_type(EggPolygon::get_class_type())) {
+    // Although for the most part we ignore the actual value of the
+    // vertices, we will ask the polygon for its plane equation
+    // (i.e. its normal).
+    if (DCAST(EggPolygon, prim)->calculate_normal(_plane_normal)) {
+      _planar = true;
+      LPoint3d p1 = prim->get_vertex(0)->get_pos3();
+      _plane_offset = -dot(_plane_normal, p1);
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::make_prim
+//       Access: Public
+//  Description: Creates an EggPrimitive corresponding to the strip
+//               represented by this node.
+////////////////////////////////////////////////////////////////////
+PT(EggPrimitive) EggMesherStrip::
+make_prim(const EggVertexPool *vertex_pool) {
+  PT(EggPrimitive) prim;
+  PrimType dest_type;
+
+  switch (_type) {
+  case PT_quad:
+    dest_type = egg_show_quads ? PT_poly : PT_tristrip;
+    break;
+
+  case PT_tristrip:
+  case PT_quadstrip:
+    dest_type = PT_tristrip;
+    break;
+
+  case PT_trifan:
+    dest_type = PT_trifan;
+    break;
+
+  default:
+    dest_type = _type;
+  }
+
+  if (dest_type != PT_tristrip && dest_type != PT_trifan) {
+    // The easy case: a simple primitive, i.e. a polygon.
+    prim = new EggPolygon;
+    prim->copy_attributes(*_prims.front());
+
+    Verts::iterator vi;
+    for (vi = _verts.begin(); vi != _verts.end(); ++vi) {
+      prim->add_vertex(vertex_pool->get_vertex(*vi));
+    }
+
+  } else {
+    // The harder case: a tristrip of some kind.
+    convert_to_type(dest_type);
+    prim = new EggTriangleStrip;
+    prim->copy_attributes(*_prims.front());
+
+    PrimType type = dest_type;
+
+    // Now store all the vertices.  Each individual triangle's
+    // attributes, if any, get applied to the third vertex of each
+    // triangle.
+    Verts::iterator vi;
+    Prims::iterator pi;
+    pi = _prims.begin();
+    int count = 0;
+    for (vi = _verts.begin();
+         vi != _verts.end() && pi != _prims.end();
+         ++vi) {
+      PT(EggVertex) vertex = vertex_pool->get_vertex(*vi);
+      prim->add_vertex(vertex);
+      
+      ++count;
+      if (count >= 3) {
+        // Beginning with the third vertex, we increment pi.  Thus, the
+        // first two vertices stand alone, then each vertex beginning
+        // with the third completes a triangle.
+        const EggAttributes *attrib = (*pi);
+        ++pi;
+        //        prim->set_component(count - 2, *attrib);
+      }
+    }
+
+    // If either of these fail, there weren't num_prims + 2 vertices in
+    // the tristrip!
+    nassertr(vi == _verts.end(), prim);
+    nassertr(pi == _prims.end(), prim);
+  }
+
+  return prim;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::measure_sheet
+//       Access: Public
+//  Description: Determines the extents of the quadsheet that can be
+//               derived by starting with this strip, and searching in
+//               the direction indicated by the given edge.
+////////////////////////////////////////////////////////////////////
+void EggMesherStrip::
+measure_sheet(const EggMesherEdge *edge, int new_row, int &num_prims, 
+              int &num_rows, int first_row_id, int this_row_id, 
+              int this_row_distance) {
+  if (new_row) {
+    // If we would create a new row by stepping here, we won't stay if
+    // there was any other row already defined here.
+    if (_row_id >= first_row_id) {
+      return;
+    }
+  } else {
+    // On the other hand, if this is a continuation of the current
+    // row, we'll stay if the other row had to travel farther to get
+    // here.
+    if (_row_id >= first_row_id && _row_distance <= this_row_distance) {
+      return;
+    }
+  }
+
+  num_prims += _prims.size();
+  if (new_row) {
+    ++num_rows;
+    this_row_id = first_row_id + num_rows - 1;
+  }
+
+  _row_id = this_row_id;
+
+  Edges::iterator ei;
+  EggMesherEdge::Strips::iterator si;
+
+  if (_type == PT_quad) {
+    // If this is a quad, it has four neighbors: two in the direction
+    // we are testing, and two in an orthagonal direction.
+
+    int vi_a = edge->_vi_a;
+    int vi_b = edge->_vi_b;
+
+    // We use these vertices to differentiate the edges that run in
+    // our primary direction from those in the secondary direction.
+    // For each edge, we count the number of vertices that the edge
+    // shares with our starting edge.  There are then three cases:
+
+    // (a) The edge shares two vertices.  It is the direction we came
+    // from; forget it.
+
+    // (b) The edge shares one vertex.  It is at right angles to our
+    // starting edge.  This is the primary direction if new_row is
+    // true, and the secondary direction if new_row is false.
+
+    // (c) The edge shares no vertices.  It is directly opposite our
+    // starting edge.  This is the primary direction if new_row is
+    // false, and the secondary direction if new_row is true.
+
+
+    // Here's a silly little for loop that executes the following code
+    // twice: once with secondary == 0, and once with secondary == 1.
+    // This is because we want to find all the primary edges first,
+    // and then all the secondary edges.
+
+    for (int secondary = 0; secondary <= 1; secondary++) {
+      // How many common vertices are we looking for this pass (see
+      // above)?
+
+      int want_count;
+      if (secondary) {
+        want_count = new_row ? 0 : 1;
+      } else {
+        want_count = new_row ? 1 : 0;
+      }
+
+      for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+        int common_verts =
+          ((*ei)->_vi_a == vi_a || (*ei)->_vi_a == vi_b) +
+          ((*ei)->_vi_b == vi_a || (*ei)->_vi_b == vi_b);
+
+        if (common_verts == want_count) {
+          // Here's the edge.  Look at all its connections.  Hopefully,
+          // there will only be one besides ourselves, but there may be
+          // more.  Pick the best.
+
+          EggMesherEdge::Strips &strips = (*ei)->_strips;
+          EggMesherStrip *mate = NULL;
+          for (si = strips.begin(); si != strips.end(); ++si) {
+            if ((*si)->_row_id < first_row_id) {
+              if (mate == NULL || pick_sheet_mate(**si, *mate)) {
+                mate = *si;
+              }
+            }
+          }
+          if (mate!=NULL) {
+            mate->measure_sheet(*ei, secondary, num_prims, num_rows,
+                                first_row_id, this_row_id,
+                                this_row_distance + secondary);
+          }
+        }
+      }
+    }
+
+  } else {
+    // Otherwise, this is not a quad.  It's certainly not a triangle,
+    // because we've built all the single triangles already.
+    nassertv(_type != PT_tri);
+
+    // Therefore, it must be a tristrip or quadstrip.
+    nassertv(_type == PT_tristrip || _type == PT_quadstrip);
+
+    // Since it's a strip, it only has two neighbors: the one we came
+    // from, and the other one.  Find the other one.
+
+    for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+      if (!(*ei)->matches(*edge)) {
+        // Here's the edge.  Same drill as above.
+
+        EggMesherEdge::Strips &strips = (*ei)->_strips;
+        EggMesherStrip *mate = NULL;
+        for (si = strips.begin(); si != strips.end(); ++si) {
+          if ((*si)->_row_id < first_row_id) {
+            if (mate == NULL || pick_sheet_mate(**si, *mate)) {
+              mate = *si;
+            }
+          }
+        }
+        if (mate != NULL) {
+          mate->measure_sheet(*ei, false, num_prims, num_rows,
+                              first_row_id, this_row_id, this_row_distance);
+        }
+      }
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::cut_sheet
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void EggMesherStrip::
+cut_sheet(int first_row_id, int do_mate, const EggVertexPool *vertex_pool) {
+  Edges::iterator ei;
+  EggMesherEdge::Strips::iterator si;
+
+  // First, start the process going on any neighbors that belong to a
+  // later row.  (We must do these first, because we'll change our
+  // neighbor list when we start to mate.)
+
+  // We need to build a temporary list of neighbors first, because
+  // calling cut_sheet() recursively will start things mating, and
+  // could damage our edge list.
+
+  typedef plist<EggMesherStrip *> StripPtrs;
+  StripPtrs strip_ptrs;
+
+  for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+    EggMesherEdge::Strips &strips = (*ei)->_strips;
+    for (si = strips.begin(); si != strips.end(); ++si) {
+      if ((*si)->_row_id > _row_id) {
+        // Here's a different row in the sheet!
+        strip_ptrs.push_back(*si);
+      }
+    }
+  }
+
+  // Now walk the temporary list and do some damage.  We pass do_mate
+  // = true to each of these neighbors, because as far as we know,
+  // they're the first nodes of a particular row.
+  StripPtrs::iterator spi;
+  for (spi = strip_ptrs.begin(); spi != strip_ptrs.end(); ++spi) {
+    if ((*spi)->_status == MS_alive) {
+      (*spi)->cut_sheet(first_row_id, true, vertex_pool);
+    }
+  }
+
+
+  if (do_mate && _status == MS_alive) {
+    // Now mate like a bunny until we don't have any more eligible mates.
+    int not_any;
+    do {
+      not_any = true;
+
+      ei = _edges.begin();
+      while (ei != _edges.end() && not_any) {
+        EggMesherEdge::Strips &strips = (*ei)->_strips;
+        si = strips.begin();
+        while (si != strips.end() && not_any) {
+          if (*si != this && (*si)->_row_id == _row_id) {
+            // Here's one!
+            not_any = false;
+            EggMesherStrip *mate = *si;
+
+            // We also recurse on these guys so they can spread the
+            // word to their own neighbors.  This time we don't need
+            // to build a temporary list, because we'll be restarting
+            // from the beginning of our edge list after we do this.
+            // We also pass do_mate = false to these guys because
+            // we're the ones doing the mating here.
+            mate->cut_sheet(first_row_id, false, vertex_pool);
+
+            if (_status == MS_alive && mate->_status == MS_alive) {
+              // Now mate.  This will either succeed or fail.  It ought
+              // to succeed, but if it doesn't, no harm done; it will
+              // simply remove the common edge and return.  We'll go
+              // around again and not encounter this neighbor next time.
+              mate_pieces(*ei, *this, *mate, vertex_pool);
+            }
+          }
+          if (not_any) {
+            ++si;
+          }
+        }
+        if (not_any) {
+          ++ei;
+        }
+      }
+    } while (!not_any);
+
+    // All done.  Mark this one as down for the count.
+    _row_id = -first_row_id;
+  }
+}
+
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::mate
+//       Access: Public
+//  Description: Finds a neighboring strip and joins up with it to
+//               make a larger strip.  Returns true if mating was
+//               successful or at least possible, false if the strip
+//               has no neighbors.
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+mate(const EggVertexPool *vertex_pool) {
+  // We must walk through the list of our neighbors and choose our
+  // best mate.
+  nassertr(_status == MS_alive, false);
+
+  EggMesherStrip *mate;
+  EggMesherEdge *common_edge;
+
+  if (!find_ideal_mate(mate, common_edge, vertex_pool)) {
+    // We have no more eligible neighbors.  Call us done.
+    _status = MS_done;
+
+    return false;
+  }
+
+  nassertr(!mate->_prims.empty(), false);
+  nassertr(!mate->_verts.empty(), false);
+
+  mate_pieces(common_edge, *this, *mate, vertex_pool);
+
+  // Whether the mate failed or not, the strip still (probably) has
+  // other neighbors to consider.  Return true regardless.
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::find_ideal_mate
+//       Access: Public
+//  Description: Searches our neighbors for the most suitable mate.
+//               Returns true if one is found, false if we have no
+//               neighbors.
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+find_ideal_mate(EggMesherStrip *&mate, EggMesherEdge *&common_edge,
+              const EggVertexPool *vertex_pool) {
+  Edges::iterator ei;
+
+  mate = NULL;
+  common_edge = NULL;
+
+  for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+    EggMesherEdge::Strips &strips = (*ei)->_strips;
+    EggMesherEdge::Strips::iterator si;
+    for (si = strips.begin(); si != strips.end(); ++si) {
+      if (*si != this) {
+        if (mate==NULL || pick_mate(**si, *mate, **ei, *common_edge,
+                                   vertex_pool)) {
+          mate = *si;
+          common_edge = *ei;
+        }
+      }
+    }
+  }
+
+  return (mate!=NULL);
+}
+
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::mate_pieces
+//       Access: Public, Static
+//  Description: Connects two pieces of arbitrary type, if possible.
+//               Returns true if successful, false if failure.
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+mate_pieces(EggMesherEdge *common_edge, EggMesherStrip &front, 
+            EggMesherStrip &back, const EggVertexPool *vertex_pool) {
+  nassertr(front._status == MS_alive, false);
+  nassertr(back._status == MS_alive, false);
+  nassertr(&front != &back, false);
+
+  bool success = true;
+  // remove_sides tracks whether we want to remove all but the leading
+  // edges of the newly joined piece if we succeed.
+  bool remove_sides = true;
+
+  bool is_coplanar = front.is_coplanar_with(back, egg_coplanar_threshold);
+
+  if (front._type == PT_tri && back._type == PT_tri) {
+
+    if (is_coplanar && egg_retesselate_coplanar &&
+        front._prims.front() == back._prims.front() &&
+        convex_quad(common_edge, front, back, vertex_pool)) {
+
+      // If we're joining two equivalent coplanar triangles, call it a
+      // quad.
+      front._type = PT_quad;
+
+      // We add one additional vertex for the new triangle, the one
+      // vertex we didn't already share.
+      int new_vert = back.find_uncommon_vertex(common_edge);
+
+      // Now we just need to find the right place to insert it.  It
+      // belongs in the middle of the common edge, i.e. after the first
+      // vertex that is on the common edge and before the second vertex.
+      Verts::iterator a = front._verts.begin();
+      Verts::iterator b = a;
+      ++b;
+
+      if (common_edge->contains_vertex(*a)) {
+        if (common_edge->contains_vertex(*b)) {
+          // It goes between a and b.
+          front._verts.insert(b, new_vert);
+        } else {
+          // It goes at the end.
+          front._verts.push_back(new_vert);
+        }
+      } else {
+        // It goes between b and c.
+        ++b;
+        front._verts.insert(b, new_vert);
+      }
+
+      front._prims.splice(front._prims.end(), back._prims);
+      back._verts.clear();
+
+      // We leave all four surrounding edges for now, since the quad
+      // might still be joined up in any direction.
+      remove_sides = false;
+
+    } else {
+      // Otherwise, connect the two tris into a tristrip.
+      front._type = PT_tristrip;
+
+      int new_vert = back.find_uncommon_vertex(common_edge);
+      front.rotate_to_back(common_edge);
+
+      front._verts.push_back(new_vert);
+      front._prims.splice(front._prims.end(), back._prims);
+      back._verts.clear();
+    }
+
+  } else if ((front._type == PT_quad || front._type == PT_quadstrip) &&
+             (back._type == PT_quad || back._type == PT_quadstrip)) {
+    // Joining two quads, two quadstrips, or a quad and a quadstrip.
+    // This makes another quadstrip.
+
+    // We expect this to succeed every time with quadstrips.
+    success = mate_strips(common_edge, front, back, PT_quadstrip);
+
+    if (!success) {
+      // Although it might fail in rare circumstances (specifically,
+      // if the two strips we attempted to join were backfacing to
+      // each other).  If so, remove the adjoining edge so these two
+      // don't get tried again.
+      common_edge->remove(&front);
+      common_edge->remove(&back);
+    }
+
+  } else {
+
+    // Otherwise.  This might be two tristrips, a quad and a tristrip,
+    // a triangle and a quad, a triangle and a tristrip, a triangle
+    // and a quadstrip, or a tristrip and a quadstrip.  In any case,
+    // we'll end up with a tristrip.
+
+    // This might fail if the tristrips don't match polarity.
+    success = mate_strips(common_edge, front, back, PT_tristrip);
+
+    if (!success) {
+      // If it does fail, we'll try reversing the connection.  This
+      // makes sense if we are joining a tri or tristrip to a quad or
+      // quadstrip, which might fail in one direction but succeed in
+      // the other.
+      success = mate_strips(common_edge, back, front, PT_tristrip);
+
+      if (success) {
+        // Yay!  Now return all the stuff to front.
+        front._verts.splice(front._verts.end(), back._verts);
+        front._prims.splice(front._prims.end(), back._prims);
+
+      } else {
+        // A miserable failure.  Never try to join these two again.
+        common_edge->remove(&front);
+        common_edge->remove(&back);
+      }
+    }
+  }
+
+  if (success) {
+    front.combine_edges(back, remove_sides);
+    if (!remove_sides) {
+      // If we didn't want to remove the side edges, at least remove
+      // the join edge, which is now internal.
+      common_edge->remove(&front);
+    }
+
+    nassertr(back._prims.empty(), false);
+    nassertr(back._verts.empty(), false);
+
+    // Strip back is no more.
+    back._status = MS_dead;
+
+    // The result is planar if and only if we joined two coplanar
+    // pieces.
+    front._planar = is_coplanar;
+    front._origin = MO_mate;
+  }
+
+  return success;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::mate_strips
+//       Access: Public, Static
+//  Description: Stitches two strips together, producing in "front" a
+//               new strip of the indicated type (quadstrip or
+//               tristrip).  The front strip stores the result, and
+//               the back strip is emptied on success.
+//
+//               Returns true if successful, false if failure
+//               (generally because of incorrect polarity of
+//               tristrips), in which case nothing has changed (or at
+//               least, not much).
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+mate_strips(EggMesherEdge *common_edge, EggMesherStrip &front, 
+            EggMesherStrip &back, EggMesherStrip::PrimType type) {
+  // If we start with a quad or tri, rotate the vertices around so we
+  // start with the common edge.
+  if (front._type == PT_tri || front._type == PT_quad) {
+    front.rotate_to_back(common_edge);
+  }
+  if (back._type == PT_tri || back._type == PT_quad) {
+    back.rotate_to_front(common_edge);
+  }
+
+  bool reverse_front = common_edge->matches(front.get_head_edge());
+  bool reverse_back = !common_edge->matches(back.get_head_edge());
+
+  bool invert_front = false;
+  bool invert_back = false;
+
+  if (reverse_front && front.is_odd()) {
+    // If we're going to reverse the front strip, we have to be
+    // careful.  This will also reverse the facing direction if it has
+    // an odd number of prims.
+    if (!front.can_invert()) {
+      return false;
+    }
+    invert_front = true;
+  }
+
+  if (must_invert(front, back, reverse_back, type)) {
+    if (!back.can_invert()) {
+      return false;
+    }
+    invert_back = true;
+    back.invert();
+  }
+
+  if (invert_front) {
+    front.invert();
+  }
+
+  if (reverse_front) {
+    reverse(front._verts.begin(), front._verts.end());
+    reverse(front._prims.begin(), front._prims.end());
+  }
+
+  if (reverse_back) {
+    reverse(back._verts.begin(), back._verts.end());
+    reverse(back._prims.begin(), back._prims.end());
+  }
+
+  bool will_reverse = front.would_reverse_tail(type);
+  bool is_headtotail = (front.get_tail_edge() == back.get_head_edge());
+  if (will_reverse == is_headtotail) {
+    // Oops, we tried to join two backfacing strips.  This really
+    // shouldn't happen, but it occasionally does for some mysterious
+    // reason.  Maybe one day I'll understand why.  In the meantime,
+    // just recover and carry on.
+    if (reverse_back) {
+      reverse(back._verts.begin(), back._verts.end());
+      reverse(back._prims.begin(), back._prims.end());
+    }
+    if (invert_back) {
+      back.invert();
+    }
+    if (reverse_front) {
+      reverse(front._verts.begin(), front._verts.end());
+      reverse(front._prims.begin(), front._prims.end());
+    }
+    if (invert_front) {
+      front.invert();
+    }
+    return false;
+  }
+
+  front.convert_to_type(type);
+  back.convert_to_type(type);
+
+  /*
+  if (! (front.get_tail_edge() == back.get_head_edge()) ) {
+    builder_cat.error()
+         << "\nFailure, trying to connect " << front
+         << "\nto " << back
+         << "\nreverse_front = " << reverse_front
+         << " reverse_back = " << reverse_back
+         << " invert_front = " << invert_front
+         << "\n";
+    Edges::iterator ei;
+
+    nout << "\nFront edges:\n";
+    for (ei = front._edges.begin(); ei != front._edges.end(); ++ei) {
+      nout << **ei << "\n";
+    }
+
+    nout << "\nBack edges:\n";
+    for (ei = back._edges.begin(); ei != back._edges.end(); ++ei) {
+      nout << **ei << "\n";
+    }
+  }
+  */
+
+  // If this assertion fails, we were misinformed about our ability to
+  // join these two strips.  Either the must_invert() call returned the
+  // incorrect value, or our edge-detection logic failed and we
+  // attempted to join two oppositely-facing strips.
+  //nassertr(front.get_tail_edge() == back.get_head_edge(), false);
+
+  front._verts.pop_back();
+  front._verts.pop_back();
+  front._verts.splice(front._verts.end(), back._verts);
+  front._prims.splice(front._prims.end(), back._prims);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::must_invert
+//       Access: Public, Static
+//  Description: Returns false if the strips can be mated as they
+//               currently are.  Returns true if the back strip must
+//               be inverted first.
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+must_invert(const EggMesherStrip &front, const EggMesherStrip &back,
+            bool will_reverse_back, EggMesherStrip::PrimType type) {
+  bool invert = false;
+
+  if ((front._type == PT_quad || front._type == PT_quadstrip) &&
+      type == PT_tristrip) {
+    // If we'll be converting from quads to tris, the tail edge of the
+    // front strip will always be even.
+
+  } else if (front.is_odd()) {
+    // Otherwise, we have to flip if the tail edge is odd.
+    invert = !invert;
+  }
+
+  if (will_reverse_back) {
+    // With the back strip, we don't care about what will happen to
+    // its tail edge when we convert it, but we do care what happens
+    // to its front edge if we reverse it.
+    if (back.is_odd()) {
+      // Specifically, the front edge will be reversed when the strip
+      // is reversed only if the strip is odd.
+      invert = !invert;
+    }
+  }
+
+  return invert;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::convex_quad
+//       Access: Public, Static
+//  Description: Returns true if the quad that would be formed by
+//               connecting coplanar tris front and back along
+//               common_edge is convex, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+convex_quad(EggMesherEdge *common_edge, EggMesherStrip &front, 
+            EggMesherStrip &back, const EggVertexPool *vertex_pool) {
+  // Find the edge from the apex of one triangle to the apex of the
+  // other.  This is the "other" diagonal of the quad-to-be, other
+  // than the common_edge.
+  int vi_a = front.find_uncommon_vertex(common_edge);
+  int vi_b = back.find_uncommon_vertex(common_edge);
+  nassertr(vi_a >= 0 && vi_b >= 0, false);
+
+  LPoint3d a3, b3, c3, d3;
+  a3 = vertex_pool->get_vertex(vi_a)->get_pos3();
+  b3 = vertex_pool->get_vertex(vi_b)->get_pos3();
+
+  c3 = vertex_pool->get_vertex(common_edge->_vi_a)->get_pos3();
+  d3 = vertex_pool->get_vertex(common_edge->_vi_b)->get_pos3();
+
+  // Project both edges into the 2-d axis plane most nearly
+  // perpendicular to the normal.  We're assuming both tris have the
+  // same normal.
+
+  nassertr(front._planar, false);
+
+  const Normald &n = front._plane_normal;
+  int xi, yi;
+
+  // Find the largest dimension of the normal.
+  if (fabs(n[0]) > fabs(n[1])) {
+    if (fabs(n[0]) > fabs(n[2])) {
+      xi = 1;
+      yi = 2;
+    } else {
+      xi = 0;
+      yi = 1;
+    }
+  } else {
+    if (fabs(n[1]) > fabs(n[2])) {
+      xi = 0;
+      yi = 2;
+    } else {
+      xi = 0;
+      yi = 1;
+    }
+  }
+
+  LVecBase2d a2, b2, c2, d2;
+  a2.set(a3[xi], a3[yi]);
+  b2.set(b3[xi], b3[yi]);
+  c2.set(c3[xi], c3[yi]);
+  d2.set(d3[xi], d3[yi]);
+
+  // Now (c2-d2) is the common edge, and (a2-b2) is the new edge.  The
+  // quad is convex iff (c2-d2) intersects (a2-b2).  We actually only
+  // need to test whether (c2-d2) intersects the infinite line passing
+  // through (a2-b2).
+
+  // The equation for the infinite line containing (a2-b2):
+  // Ax + By + C = 0
+  double A = (b2[1] - a2[1]);
+  double B = (a2[0] - b2[0]);
+  double C = -(A*b2[0] + B*b2[1]);
+
+  // The parametric equations for the line segment (c2-d2):
+  // x = c2[0] + (d2[0]-c2[0])t
+  // y = c2[1] + (d2[1]-c2[1])t
+
+  // Solved for t:
+  double t = - ((A*c2[0] + B*c2[1]) + C) / (A*(d2[0]-c2[0]) + B*(d2[1]-c2[1]));
+
+  // Now the lines intersect if t is in [0, 1].
+  return (0.0 <= t && t <= 1.0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::count_neighbors
+//       Access: Public
+//  Description: Returns the number of neighbors the strip shares.
+////////////////////////////////////////////////////////////////////
+int EggMesherStrip::
+count_neighbors() const {
+  int count = 0;
+  Edges::const_iterator ei;
+
+  for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+    count += (*ei)->_strips.size();
+  }
+  return count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::output_neighbors
+//       Access: Public
+//  Description: Writes all the neighbor indexes to the ostream.
+////////////////////////////////////////////////////////////////////
+void EggMesherStrip::
+output_neighbors(ostream &out) const {
+  Edges::const_iterator ei;
+  EggMesherEdge::Strips::const_iterator si;
+
+  for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+    for (si = (*ei)->_strips.begin();
+         si != (*ei)->_strips.end();
+         ++si) {
+      out << " " << (*si)->_index;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::find_uncommon_vertex
+//       Access: Public
+//  Description: Returns the first vertex found that is not shared by
+//               the given edge.
+////////////////////////////////////////////////////////////////////
+int EggMesherStrip::
+find_uncommon_vertex(const EggMesherEdge *edge) const {
+  int vi_a = edge->_vi_a;
+  int vi_b = edge->_vi_b;
+
+  Edges::const_iterator ei;
+  for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+    EggMesherEdge *e = (*ei);
+
+    if (e->_vi_a != vi_a && e->_vi_a != vi_b) {
+      return e->_vi_a;
+    } else if (e->_vi_b != vi_a && e->_vi_b != vi_b) {
+      return e->_vi_b;
+    }
+  }
+
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::find_opposite_edge
+//       Access: Public
+//  Description: Returns the first edge found that does not contain
+//               the given vertex.  In a tri, this will be the edge
+//               opposite the given vertex.
+////////////////////////////////////////////////////////////////////
+const EggMesherEdge *EggMesherStrip::
+find_opposite_edge(int vi) const {
+  Edges::const_iterator ei;
+  for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+    EggMesherEdge *e = (*ei);
+    if (!e->contains_vertex(vi)) {
+      return e;
+    }
+  }
+
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::find_opposite_edge
+//       Access: Public
+//  Description: Returns the first edge found that shares no vertices
+//               with the given edge.  In a quad, this will be the
+//               edge opposite the given edge.
+////////////////////////////////////////////////////////////////////
+const EggMesherEdge *EggMesherStrip::
+find_opposite_edge(const EggMesherEdge *edge) const {
+  int vi_a = edge->_vi_a;
+  int vi_b = edge->_vi_b;
+
+  Edges::const_iterator ei;
+  for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+    EggMesherEdge *e = (*ei);
+    if (!e->contains_vertex(vi_a) && !e->contains_vertex(vi_b)) {
+      return e;
+    }
+  }
+
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::find_adjacent_edge
+//       Access: Public
+//  Description: Returns the first edge found that shares exactly one
+//               vertex with the given edge.  In a quad, this will be
+//               one of two edges adjacent to the given edge.
+////////////////////////////////////////////////////////////////////
+const EggMesherEdge *EggMesherStrip::
+find_adjacent_edge(const EggMesherEdge *edge) const {
+  int vi_a = edge->_vi_a;
+  int vi_b = edge->_vi_b;
+
+  Edges::const_iterator ei;
+  for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
+    EggMesherEdge *e = (*ei);
+    if (e->contains_vertex(vi_a) != e->contains_vertex(vi_b)) {
+      return e;
+    }
+  }
+
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::rotate_to_front
+//       Access: Public
+//  Description: Rotates a triangle or quad so that the given edge is
+//               first in the vertex list.
+////////////////////////////////////////////////////////////////////
+void EggMesherStrip::
+rotate_to_front(const EggMesherEdge *edge) {
+  int vi_a = edge->_vi_a;
+  int vi_b = edge->_vi_b;
+
+  // See if we're already there.
+  if (_verts.front() == vi_a || _verts.front() == vi_b) {
+    Verts::iterator vi = _verts.begin();
+    ++vi;
+    if (*vi == vi_a || *vi == vi_b) {
+      // Yes!
+      return;
+    }
+
+    // No, we must be right on the line.  Roll back one.
+    rotate_back();
+
+  } else {
+    // Roll forward until it comes into view.
+    int num_verts = _verts.size();
+    while (_verts.front() != vi_a && _verts.front() != vi_b) {
+      // Make sure either vertex exists.
+      num_verts--;
+      nassertv(num_verts > 0);
+      rotate_forward();
+    }
+  }
+
+#ifndef NDEBUG
+  // Now make sure the edge actually exists.
+  Verts::iterator vi = _verts.begin();
+  ++vi;
+  nassertv(*vi == vi_a || *vi == vi_b);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::rotate_to_back
+//       Access: Public
+//  Description: Rotates a triangle or quad so that the given edge is
+//               last in the vertex list.
+////////////////////////////////////////////////////////////////////
+void EggMesherStrip::
+rotate_to_back(const EggMesherEdge *edge) {
+  int vi_a = edge->_vi_a;
+  int vi_b = edge->_vi_b;
+
+  // See if we're already there.
+  if (_verts.back() == vi_a || _verts.back() == vi_b) {
+    Verts::reverse_iterator vi = _verts.rbegin();
+    ++vi;
+    if (*vi == vi_a || *vi == vi_b) {
+      // Yes!
+      return;
+    }
+
+    // No, we must be right on the line.  Roll forward one.
+    rotate_forward();
+
+  } else {
+    // Roll backward until it comes into view.
+    int num_verts = _verts.size();
+    while (_verts.back() != vi_a && _verts.back() != vi_b) {
+      // Make sure either vertex exists.
+      num_verts--;
+      nassertv(num_verts > 0);
+      rotate_back();
+    }
+  }
+
+#ifndef NDEBUG
+  // Now make sure the edge actually exists.
+  Verts::reverse_iterator vi = _verts.rbegin();
+  ++vi;
+  nassertv(*vi == vi_a || *vi == vi_b);
+#endif
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::can_invert
+//       Access: Public
+//  Description: Returns true if the strip can be inverted (reverse
+//               its facing direction).  Generally, this is true for
+//               quadstrips and false for tristrips.
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+can_invert() const {
+  return (_type == PT_quadstrip || _type == PT_quad);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::invert
+//       Access: Public
+//  Description: Reverses the facing of a quadstrip by reversing pairs
+//               of vertices.  Returns true if successful, false if
+//               failure (for instance, on a tristrip).
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+invert() {
+  if (!can_invert()) {
+    return false;
+  }
+  Verts::iterator vi, vi2;
+  vi = _verts.begin();
+  while (vi != _verts.end()) {
+    vi2 = vi;
+    ++vi2;
+    nassertr(vi2 != _verts.end(), false);
+
+    // Exchange vi and vi2
+    int t = *vi2;
+    *vi2 = *vi;
+    *vi = t;
+
+    // Increment
+    vi = vi2;
+    ++vi;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::is_odd
+//       Access: Public
+//  Description: Returns true if the tristrip or quadstrip contains an
+//               odd number of pieces.
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+is_odd() const {
+  if (_type == PT_quadstrip || _type == PT_quad) {
+    // If a quadstrip has a multiple of four vertices, it has an
+    // odd number of quads.
+    return (_verts.size() % 4 == 0);
+  } else {
+    // If a tristrip has an odd number of vertices, it has an odd
+    // number of tris.
+    return (_verts.size() % 2 == 1);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::would_reverse_tail
+//       Access: Public
+//  Description: Returns true if convert_to_type() would reverse the
+//               tail edge of the given strip, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+would_reverse_tail(EggMesherStrip::PrimType want_type) const {
+  bool reverse = false;
+
+  if (_type == want_type) {
+    return false;
+  }
+  if (want_type == PT_tristrip) {
+    switch (_type) {
+    case PT_tri:
+    case PT_tristrip:
+      break;
+
+    case PT_quad:
+    case PT_quadstrip:
+      // When we convert a quadstrip to a tristrip, we reverse the
+      // tail edge if we have a multiple of four verts.
+      reverse = (_verts.size() % 4 == 0);
+      break;
+
+    default:
+      egg_cat.fatal() << "Invalid conversion!\n";
+      abort();
+      break;
+    }
+
+  } else if (want_type == PT_quadstrip) {
+    switch (_type) {
+    case PT_quad:
+    case PT_quadstrip:
+      break;
+
+    case PT_tri:
+    case PT_tristrip:
+      // We don't convert tristrips to quadstrips; fall through.
+
+    default:
+      egg_cat.fatal() << "Invalid conversion!\n";
+      abort();
+      break;
+    }
+  }
+
+  return reverse;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::convert_to_type
+//       Access: Public
+//  Description: Converts the EggMesherStrip from whatever form it
+//               is--triangle, quad, or quadstrip--into a tristrip or
+//               quadstrip.
+////////////////////////////////////////////////////////////////////
+void EggMesherStrip::
+convert_to_type(EggMesherStrip::PrimType want_type) {
+  Verts::iterator vi, vi2;
+  int even;
+
+  if (_type == want_type) {
+    return;
+  }
+  if (want_type == PT_tristrip) {
+    switch (_type) {
+    case PT_tri:
+    case PT_tristrip:
+      break;
+
+    case PT_quad:
+    case PT_quadstrip:
+      // To convert from quad/quadstrip to tristrip, we reverse every
+      // other pair of vertices.
+
+      vi = _verts.begin();
+      even = 0;
+      while (vi != _verts.end()) {
+        vi2 = vi;
+        ++vi2;
+        nassertv(vi2 != _verts.end());
+
+        // vi and vi2 are a pair.  Should we reverse them?
+        if (even) {
+          int t = *vi2;
+          *vi2 = *vi;
+          *vi = t;
+        }
+
+        // Increment
+        vi = vi2;
+        ++vi;
+        even = !even;
+      }
+      break;
+
+    default:
+      egg_cat.fatal() << "Invalid conversion!\n";
+      abort();
+    }
+
+  } else if (want_type == PT_quadstrip) {
+    switch (_type) {
+    case PT_quad:
+    case PT_quadstrip:
+      break;
+
+    case PT_tri:
+    case PT_tristrip:
+      // We don't convert tristrips to quadstrips; fall through.
+
+    default:
+      egg_cat.fatal() << "Invalid conversion!\n";
+      abort();
+    }
+  }
+
+  _type = want_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::combine_edges
+//       Access: Public
+//  Description: Removes the edges from the given strip and appends
+//               them to our own.  If remove_sides is true, then
+//               removes all the edges except the head and the tail.
+////////////////////////////////////////////////////////////////////
+void EggMesherStrip::
+combine_edges(EggMesherStrip &other, int remove_sides) {
+  Edges::iterator ei;
+  for (ei = other._edges.begin(); ei != other._edges.end(); ++ei) {
+    (*ei)->change_strip(&other, this);
+  }
+
+  _edges.splice(_edges.end(), other._edges);
+
+  if (remove_sides) {
+    // Identify the head and tail edges so we can remove everything
+    // else.
+    EggMesherEdge head = get_head_edge();
+    EggMesherEdge tail = get_tail_edge();
+
+    if (!is_odd()) {
+      // If the strip is odd, its true tail edge is the inverse of its
+      // actual edge.
+      tail = EggMesherEdge(tail._vi_b, tail._vi_a);
+    }
+
+    Edges junk_edges;
+
+    Edges::iterator next_ei;
+    ei = _edges.begin();
+    while (ei != _edges.end()) {
+      next_ei = ei;
+      ++next_ei;
+
+      // Is this edge to be saved or is it fodder?
+      if (!(**ei == head) && !(**ei == tail)) {
+        // Fodder!  But we can't remove it right away, because this
+        // will upset the current list; instead, we'll splice it to
+        // junk_edges.
+        junk_edges.splice(junk_edges.end(), _edges, ei);
+      }
+      ei = next_ei;
+    }
+
+    // Now we can safely remove all the to-be-junked edges.
+    for (ei = junk_edges.begin(); ei != junk_edges.end(); ++ei) {
+      (*ei)->remove(this);
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::remove_all_edges
+//       Access: Public
+//  Description: Removes all active edges from the strip.  This
+//               effectively renders it ineligible to mate with
+//               anything else.
+////////////////////////////////////////////////////////////////////
+void EggMesherStrip::
+remove_all_edges() {
+  // First, move all the edges to a safe place so we can traverse the
+  // list without it changing on us.
+  Edges junk_edges;
+  junk_edges.splice(junk_edges.end(), _edges);
+
+  // Now we can safely remove all the to-be-junked edges.
+  Edges::iterator ei;
+  for (ei = junk_edges.begin(); ei != junk_edges.end(); ++ei) {
+    (*ei)->remove(this);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::pick_mate
+//       Access: Public
+//  Description: Defines an ordering to select neighbors to mate with.
+//               This compares strip a with strip b and returns true
+//               if strip a is the preferable choice, false if strip
+//               b.
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+pick_mate(const EggMesherStrip &a_strip, const EggMesherStrip &b_strip,
+          const EggMesherEdge &a_edge, const EggMesherEdge &b_edge,
+          const EggVertexPool *vertex_pool) const {
+  // First, try to avoid polluting quads, quadstrips, and tristrips
+  // with arbitrary triangles.  When we mate a tri or tristrip to a
+  // quadstrip, we end up with a tristrip that may be less versatile
+  // than the original quadstrip.  Better to avoid this if we can.
+  // Try to choose a mate that more closely matches our own type.
+  int a_cat = a_strip.type_category();
+  int b_cat = b_strip.type_category();
+  if (a_cat != b_cat) {
+    int me_cat = type_category();
+    return abs(a_cat - me_cat) < abs(b_cat - me_cat);
+  }
+
+  // Now, if we're connecting two tris, try to connect them up so they
+  // make good quads.
+  if (_type == PT_tri && a_strip._type == PT_tri &&
+      b_strip._type == PT_tri) {
+
+    // This will depend on both coplanarity and edge length.  We can't
+    // use just one or the other, because some tris are nearly
+    // isosceles, and some have more than one coplanar neighbor.
+    // Hopefully the combination of both factors will zero us in on
+    // the correct neighbor first.
+
+    double a_coplanar = coplanarity(a_strip);
+    double b_coplanar = coplanarity(b_strip);
+    double coplanar_diff = a_coplanar - b_coplanar;
+
+    double a_length = a_edge.compute_length(vertex_pool);
+    double b_length = b_edge.compute_length(vertex_pool);
+    double length_diff = (a_length - b_length) / (a_length + b_length);
+
+    // These weights were chosen empirically to yield fairly good results.
+    double sum = 4.0 * coplanar_diff - 1.0 * length_diff;
+    return sum < 0;
+  }
+
+  // Then, get the smallest strip.
+  if (a_strip._prims.size() != b_strip._prims.size()) {
+    return a_strip._prims.size() < b_strip._prims.size();
+  }
+
+  // Finally, get the strip with the fewest neighbors.
+  return a_strip.count_neighbors() < b_strip.count_neighbors();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::pick_sheet_mate
+//       Access: Public
+//  Description: Defines an ordering to select neighbors to follow
+//               when measuring out a quadsheet.  This is only called
+//               when three or more prims share a single edge, which
+//               should be rarely--generally only when coplanar polys
+//               are going on.
+////////////////////////////////////////////////////////////////////
+bool EggMesherStrip::
+pick_sheet_mate(const EggMesherStrip &a_strip, 
+                const EggMesherStrip &b_strip) const {
+  // First, try to get the poly which is closest to our own normal.
+  if (_planar && a_strip._planar && b_strip._planar) {
+    double a_diff = dot(_plane_normal, a_strip._plane_normal);
+    double b_diff = dot(_plane_normal, b_strip._plane_normal);
+
+    if (fabs(a_diff - b_diff) > 0.0001) {
+      return a_diff > b_diff;
+    }
+  }
+
+  // Then, pick the one that's most like our own type.
+  int a_cat = a_strip.type_category();
+  int b_cat = b_strip.type_category();
+  if (a_cat != b_cat) {
+    int me_cat = type_category();
+    return abs(a_cat - me_cat) < abs(b_cat - me_cat);
+  }
+
+  // Oh, just pick any old one.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMesherStrip::output
+//       Access: Public
+//  Description: Formats the vertex for output in some sensible way.
+////////////////////////////////////////////////////////////////////
+void EggMesherStrip::
+output(ostream &out) const {
+  switch (_status) {
+  case MS_alive:
+    break;
+
+  case MS_dead:
+    out << "Dead ";
+    break;
+
+  case MS_done:
+    out << "Done ";
+    break;
+
+  default:
+    out << "Unknown status ";
+  }
+
+  switch (_type) {
+  case PT_tri:
+    out << "Tri";
+    break;
+
+  case PT_quad:
+    out << "Quad";
+    break;
+
+  case PT_tristrip:
+    out << "TriStrip";
+    break;
+
+  case PT_trifan:
+    out << "TriFan";
+    break;
+
+  case PT_quadstrip:
+    out << "QuadStrip";
+    break;
+
+  default:
+    out << "Unknown";
+  }
+
+  if (_planar) {
+    out << " (planar)";
+  }
+
+  out << " " << _index << " [";
+
+  Verts::const_iterator vi;
+  for (vi = _verts.begin(); vi != _verts.end(); vi++) {
+    out << " " << *vi;
+  }
+  out << " ]: " << _prims.size()
+      << " prims, " << count_neighbors() << " neighbors";
+
+  output_neighbors(out);
+
+  out << " edges";
+  Edges::const_iterator ei;
+  for (ei = _edges.begin(); ei != _edges.end(); ei++) {
+    out << " " << (void *)(*ei);
+  }
+
+  out << ".";
+}
+

+ 158 - 0
panda/src/egg/eggMesherStrip.h

@@ -0,0 +1,158 @@
+// Filename: eggMesherStrip.h
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGMESHERSTRIP_H
+#define EGGMESHERSTRIP_H
+
+#include "pandabase.h"
+#include "eggVertexPool.h"
+#include "plist.h"
+
+class EggMesherEdge;
+
+///////////////////////////////////////////////////////////////////
+//       Class : EggMesherStrip
+// Description : Represents a triangle strip in progress, as assembled
+//               by the mesher.  It might also represent a single
+//               polygon such as a triangle or quad, since that's how
+//               all triangle strips start out.
+////////////////////////////////////////////////////////////////////
+class EggMesherStrip {
+public:
+  enum PrimType {
+    PT_poly,
+    PT_point,
+    PT_line,
+    PT_tri,
+    PT_tristrip,
+    PT_trifan,
+    PT_quad,
+    PT_quadstrip,
+    PT_linestrip,
+  };
+
+  enum MesherOrigin {
+    MO_unknown, 
+    MO_user, 
+    MO_firstquad, 
+    MO_fanpoly, 
+    MO_mate
+  };
+
+  INLINE EggMesherStrip();
+  EggMesherStrip(const EggPrimitive *prim, int index, const EggVertexPool *vertex_pool);
+  INLINE EggMesherStrip(const EggMesherStrip &copy);
+
+  PT(EggPrimitive) make_prim(const EggVertexPool *vertex_pool);
+
+  void measure_sheet(const EggMesherEdge *edge, int new_row,
+                    int &num_prims, int &num_rows,
+                    int first_row_id, int this_row_id,
+                    int this_row_distance);
+  void cut_sheet(int first_row_id, int do_mate,
+                 const EggVertexPool *vertex_pool);
+
+  bool mate(const EggVertexPool *vertex_pool);
+  bool find_ideal_mate(EggMesherStrip *&mate, EggMesherEdge *&common_edge,
+                       const EggVertexPool *vertex_pool);
+  static bool mate_pieces(EggMesherEdge *common_edge, EggMesherStrip &front,
+                          EggMesherStrip &back, const EggVertexPool *vertex_pool);
+  static bool mate_strips(EggMesherEdge *common_edge, EggMesherStrip &front,
+                          EggMesherStrip &back, PrimType type);
+  static bool must_invert(const EggMesherStrip &front, const EggMesherStrip &back,
+                          bool will_reverse_back, PrimType type);
+  static bool convex_quad(EggMesherEdge *common_edge, EggMesherStrip &front,
+                          EggMesherStrip &back, const EggVertexPool *vertex_pool);
+
+  int count_neighbors() const;
+  void output_neighbors(ostream &out) const;
+
+  INLINE bool is_coplanar_with(const EggMesherStrip &other, float threshold) const;
+  INLINE float coplanarity(const EggMesherStrip &other) const;
+  INLINE int type_category() const;
+
+  int find_uncommon_vertex(const EggMesherEdge *edge) const;
+  const EggMesherEdge *find_opposite_edge(int vi) const;
+  const EggMesherEdge *find_opposite_edge(const EggMesherEdge *edge) const;
+  const EggMesherEdge *find_adjacent_edge(const EggMesherEdge *edge) const;
+
+  INLINE void rotate_forward();
+  INLINE void rotate_back();
+  void rotate_to_front(const EggMesherEdge *edge);
+  void rotate_to_back(const EggMesherEdge *edge);
+  bool can_invert() const;
+  bool invert();
+
+  INLINE EggMesherEdge get_head_edge() const;
+  INLINE EggMesherEdge get_tail_edge() const;
+
+  bool is_odd() const;
+  bool would_reverse_tail(PrimType want_type) const;
+  void convert_to_type(PrimType want_type);
+
+  void combine_edges(EggMesherStrip &other, int remove_sides);
+  void remove_all_edges();
+
+  // ptr equality
+  INLINE bool operator == (const EggMesherStrip &other) const;
+  INLINE bool operator != (const EggMesherStrip &other) const;
+
+  bool pick_mate(const EggMesherStrip &a_strip, const EggMesherStrip &b_strip,
+                 const EggMesherEdge &a_edge, const EggMesherEdge &b_edge,
+                 const EggVertexPool *vertex_pool) const;
+
+  bool pick_sheet_mate(const EggMesherStrip &a_strip,
+                       const EggMesherStrip &b_strip) const;
+
+  void output(ostream &out) const;
+
+  typedef plist<CPT(EggPrimitive) > Prims;
+  typedef plist<EggMesherEdge *> Edges;
+  typedef plist<int> Verts;
+
+  Prims _prims;
+  Edges _edges;
+  Verts _verts;
+
+  enum MesherStatus {
+    MS_alive, 
+    MS_dead, 
+    MS_done, 
+    MS_paired
+  };
+
+  PrimType _type;
+  int _index;
+  MesherStatus _status;
+
+  int _planar;
+  Normald _plane_normal;
+  float _plane_offset;
+  int _row_id, _row_distance;
+  MesherOrigin _origin;
+};
+
+INLINE ostream &
+operator << (ostream &out, const EggMesherStrip &strip) {
+  strip.output(out);
+  return out;
+}
+
+#include "eggMesherStrip.I"
+
+#endif

+ 17 - 0
panda/src/egg/eggMorph.I

@@ -88,6 +88,23 @@ operator != (const EggMorph<Parameter> &other) const {
   return !operator == (other);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggMorph::compare_to
+//       Access: Public
+//  Description: compare_to() compares a different space than the
+//               operator methods, which only check the name.
+//               compare_to() compares the name and the value as well.
+////////////////////////////////////////////////////////////////////
+template<class Parameter>
+INLINE int EggMorph<Parameter>::
+compare_to(const EggMorph<Parameter> &other) const {
+  int compare = strcmp(get_name().c_str(), other.get_name().c_str());
+  if (compare != 0) {
+    return compare;
+  }
+  return _offset.compare_to(other._offset);
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggMorphVertex output operator

+ 2 - 0
panda/src/egg/eggMorph.h

@@ -44,6 +44,8 @@ public:
   INLINE bool operator == (const EggMorph<Parameter> &other) const;
   INLINE bool operator != (const EggMorph<Parameter> &other) const;
 
+  INLINE int compare_to(const EggMorph<Parameter> &other) const;
+
 private:
   Parameter _offset;
 };

+ 23 - 3
panda/src/egg/eggMorphList.I

@@ -68,7 +68,7 @@ INLINE EggMorphList<MorphType>::
 template<class MorphType>
 INLINE bool EggMorphList<MorphType>::
 operator == (const EggMorphList<MorphType> &other) const {
-  return (_morphs == other._morphs);
+  return compare_to(other) == 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -79,7 +79,7 @@ operator == (const EggMorphList<MorphType> &other) const {
 template<class MorphType>
 INLINE bool EggMorphList<MorphType>::
 operator != (const EggMorphList<MorphType> &other) const {
-  return (_morphs != other._morphs);
+  return compare_to(other) != 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -90,7 +90,27 @@ operator != (const EggMorphList<MorphType> &other) const {
 template<class MorphType>
 INLINE bool EggMorphList<MorphType>::
 operator < (const EggMorphList<MorphType> &other) const {
-  return (_morphs < other._morphs);
+  return compare_to(other) < 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMorphList::compare_to
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class MorphType>
+int EggMorphList<MorphType>::
+compare_to(const EggMorphList<MorphType> &other) const {
+  if (_morphs.size() < other._morphs.size()) {
+    return (int)_morphs.size() - (int)other._morphs.size();
+  }
+  for (size_t i = 0; i < _morphs.size(); i++) {
+    int compare = _morphs[i].compare_to(other._morphs[i]);
+    if (compare < 0) {
+      return compare;
+    }
+  }
+  return 0;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
panda/src/egg/eggMorphList.h

@@ -49,6 +49,7 @@ public:
   INLINE bool operator == (const EggMorphList<MorphType> &other) const;
   INLINE bool operator != (const EggMorphList<MorphType> &other) const;
   INLINE bool operator < (const EggMorphList<MorphType> &other) const;
+  int compare_to(const EggMorphList<MorphType> &other) const;
 
   INLINE iterator begin();
   INLINE const_iterator begin() const;

+ 5 - 6
panda/src/egg/eggPolygon.I

@@ -17,10 +17,9 @@
 ////////////////////////////////////////////////////////////////////
 
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: EggPolygon::Constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE EggPolygon::
@@ -29,7 +28,7 @@ EggPolygon(const string &name) : EggPrimitive(name) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggPolygon::Copy constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE EggPolygon::
@@ -38,7 +37,7 @@ EggPolygon(const EggPolygon &copy) : EggPrimitive(copy) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggPolygon::Copy assignment operator
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE EggPolygon &EggPolygon::
@@ -49,7 +48,7 @@ operator = (const EggPolygon &copy) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggPolygon::recompute_polygon_normal
-//       Access: Public
+//       Access: Published
 //  Description: Recalculates the normal according to the order of the
 //               vertices, and sets it.  Returns true if the normal is
 //               computed correctly, or false if the polygon is
@@ -68,7 +67,7 @@ recompute_polygon_normal(CoordinateSystem cs) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggPolygon::triangulate_into
-//       Access: Public
+//       Access: Published
 //  Description: Subdivides the polygon into triangles and adds each
 //               one to the indicated container.  If the polygon is
 //               already a triangle, adds an exact copy of the polygon

+ 6 - 4
panda/src/egg/eggPrimitive.I

@@ -326,7 +326,7 @@ operator [] (int index) const {
 ////////////////////////////////////////////////////////////////////
 INLINE EggPrimitive::iterator EggPrimitive::
 insert(iterator position, EggVertex *x) {
-  prepare_add_vertex(x);
+  prepare_add_vertex(x, position - _vertices.begin(), _vertices.size() + 1);
   iterator i = _vertices.insert((Vertices::iterator &)position, x);
   x->test_pref_integrity();
   test_vref_integrity();
@@ -340,7 +340,7 @@ insert(iterator position, EggVertex *x) {
 ////////////////////////////////////////////////////////////////////
 INLINE EggPrimitive::iterator EggPrimitive::
 erase(iterator position) {
-  prepare_remove_vertex(*position);
+  prepare_remove_vertex(*position, position - _vertices.begin(), _vertices.size());
   iterator i = _vertices.erase((Vertices::iterator &)position);
   test_vref_integrity();
   return i;
@@ -357,8 +357,10 @@ INLINE void EggPrimitive::
 replace(iterator position, EggVertex *x) {
   nassertv(position != end());
 
-  prepare_remove_vertex(*position);
-  prepare_add_vertex(x);
+  // We pass -1 for i and n so that EggCompositePrimitive won't try to
+  // adjust its _components list.
+  prepare_remove_vertex(*position, -1, -1);
+  prepare_add_vertex(x, -1, -1);
   *(Vertices::iterator &)position = x;
 
   x->test_pref_integrity();

+ 38 - 9
panda/src/egg/eggPrimitive.cxx

@@ -190,6 +190,17 @@ determine_bin() {
   return result;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggPrimitive::copy_attributes
+//       Access: Published
+//  Description: Copies the rendering attributes from the indicated
+//               primitive.
+////////////////////////////////////////////////////////////////////
+void EggPrimitive::
+copy_attributes(const EggAttributes &other) {
+  EggAttributes::operator = (other);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggPrimitive::copy_attributes
 //       Access: Published
@@ -292,6 +303,7 @@ remove_doubled_verts(bool closed) {
     Vertices::iterator vi, vlast;
     vi = _vertices.begin();
     new_vertices.push_back(*vi);
+    int num_removed = 0;
 
     vlast = vi;
     ++vi;
@@ -299,7 +311,9 @@ remove_doubled_verts(bool closed) {
       if ((*vi) != (*vlast)) {
         new_vertices.push_back(*vi);
       } else {
-        prepare_remove_vertex(*vi);
+        prepare_remove_vertex(*vi, vi - _vertices.begin() - num_removed, 
+                              _vertices.size() - num_removed);
+        num_removed++;
       }
       vlast = vi;
       ++vi;
@@ -312,7 +326,8 @@ remove_doubled_verts(bool closed) {
     // remove the vertex from the end if it's a repeat of the
     // beginning.
     while (_vertices.size() > 1 && _vertices.back() == _vertices.front()) {
-      prepare_remove_vertex(_vertices.back());
+      prepare_remove_vertex(_vertices.back(), _vertices.size() - 1, 
+                            _vertices.size());
       _vertices.pop_back();
     }
   }
@@ -331,6 +346,7 @@ void EggPrimitive::
 remove_nonunique_verts() {
   Vertices::iterator vi, vj;
   Vertices new_vertices;
+  int num_removed = 0;
 
   for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) {
     bool okflag = true;
@@ -340,7 +356,9 @@ remove_nonunique_verts() {
     if (okflag) {
       new_vertices.push_back(*vi);
     } else {
-      prepare_remove_vertex(*vi);
+      prepare_remove_vertex(*vi, vi - _vertices.begin() - num_removed,
+                            _vertices.size() - num_removed);
+      num_removed++;
     }
   }
 
@@ -405,8 +423,11 @@ has_normals() const {
 EggPrimitive::iterator EggPrimitive::
 erase(iterator first, iterator last) {
   iterator i;
+  int num_removed = 0;
   for (i = first; i != last; ++i) {
-    prepare_remove_vertex(*i);
+    prepare_remove_vertex(*i, first - _vertices.begin(), 
+                          _vertices.size() - num_removed);
+    num_removed++;
   }
   iterator result = _vertices.erase((Vertices::iterator &)first,
                                     (Vertices::iterator &)last);
@@ -423,7 +444,7 @@ erase(iterator first, iterator last) {
 ////////////////////////////////////////////////////////////////////
 EggVertex *EggPrimitive::
 add_vertex(EggVertex *vertex) {
-  prepare_add_vertex(vertex);
+  prepare_add_vertex(vertex, _vertices.size(), _vertices.size() + 1);
   _vertices.push_back(vertex);
 
   vertex->test_pref_integrity();
@@ -528,14 +549,18 @@ test_vref_integrity() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggPrimitive::prepare_add_vertex
-//       Access: Private
+//       Access: Protected, Virtual
 //  Description: Marks the vertex as belonging to the primitive.  This
 //               is an internal function called by the STL-like
 //               functions push_back() and insert(), in preparation
 //               for actually adding the vertex.
+//
+//               i indicates the new position of the vertex in the
+//               list; n indicates the new number of vertices after
+//               the operation has completed.
 ////////////////////////////////////////////////////////////////////
 void EggPrimitive::
-prepare_add_vertex(EggVertex *vertex) {
+prepare_add_vertex(EggVertex *vertex, int i, int n) {
   // We can't test integrity within this function, because it might be
   // called when the primitive is in an incomplete state.
 
@@ -554,17 +579,21 @@ prepare_add_vertex(EggVertex *vertex) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggPrimitive::prepare_remove_vertex
-//       Access: Private
+//       Access: Protected, Virtual
 //  Description: Marks the vertex as removed from the primitive.  This
 //               is an internal function called by the STL-like
 //               functions pop_back() and erase(), in preparation for
 //               actually doing the removal.
 //
+//               i indicates the former position of the vertex in the
+//               list; n indicates the current number of vertices
+//               before the operation has completed.
+//
 //               It is an error to attempt to remove a vertex that is
 //               not already a vertex of this primitive.
 ////////////////////////////////////////////////////////////////////
 void EggPrimitive::
-prepare_remove_vertex(EggVertex *vertex) {
+prepare_remove_vertex(EggVertex *vertex, int i, int n) {
   // We can't test integrity within this function, because it might be
   // called when the primitive is in an incomplete state.
 

+ 4 - 3
panda/src/egg/eggPrimitive.h

@@ -96,6 +96,7 @@ PUBLISHED:
   INLINE void set_bface_flag(bool flag);
   INLINE bool get_bface_flag() const;
 
+  void copy_attributes(const EggAttributes &other);
   void copy_attributes(const EggPrimitive &other);
 
   bool has_vertex_normal() const;
@@ -166,14 +167,14 @@ PUBLISHED:
   void test_vref_integrity() const { }
 #endif  // NDEBUG
 
-private:
+protected:
   Vertices _vertices;
 
   // Don't try to use these private functions.  User code should add
   // and remove vertices via add_vertex()/remove_vertex(), or via the
   // STL-like push_back()/pop_back() or insert()/erase(), above.
-  void prepare_add_vertex(EggVertex *vertex);
-  void prepare_remove_vertex(EggVertex *vertex);
+  virtual void prepare_add_vertex(EggVertex *vertex, int i, int n);
+  virtual void prepare_remove_vertex(EggVertex *vertex, int i, int n);
 
 
 protected:

+ 47 - 0
panda/src/egg/eggTriangleStrip.I

@@ -0,0 +1,47 @@
+// Filename: eggTriangleStrip.I
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTriangleStrip::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE EggTriangleStrip::
+EggTriangleStrip(const string &name) : EggCompositePrimitive(name) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTriangleStrip::Copy constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE EggTriangleStrip::
+EggTriangleStrip(const EggTriangleStrip &copy) : EggCompositePrimitive(copy) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTriangleStrip::Copy assignment operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE EggTriangleStrip &EggTriangleStrip::
+operator = (const EggTriangleStrip &copy) {
+  EggCompositePrimitive::operator = (copy);
+  return *this;
+}

+ 107 - 0
panda/src/egg/eggTriangleStrip.cxx

@@ -0,0 +1,107 @@
+// Filename: eggTriangleStrip.cxx
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "eggTriangleStrip.h"
+#include "eggGroupNode.h"
+#include "eggPolygon.h"
+#include "plane.h"
+
+#include "indent.h"
+
+#include <algorithm>
+
+TypeHandle EggTriangleStrip::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTriangleStrip::cleanup
+//       Access: Published, Virtual
+//  Description: Cleans up modeling errors in whatever context this
+//               makes sense.  For instance, for a polygon, this calls
+//               remove_doubled_verts(true).  For a point, it calls
+//               remove_nonunique_verts().  Returns true if the
+//               primitive is valid, or false if it is degenerate.
+////////////////////////////////////////////////////////////////////
+bool EggTriangleStrip::
+cleanup() {
+  return size() >= 3;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTriangleStrip::write
+//       Access: Published, Virtual
+//  Description: Writes the triangle strip to the indicated output
+//               stream in Egg format.
+////////////////////////////////////////////////////////////////////
+void EggTriangleStrip::
+write(ostream &out, int indent_level) const {
+  write_header(out, indent_level, "<TriangleStrip>");
+  write_body(out, indent_level+2);
+  indent(out, indent_level) << "}\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTriangleStrip::triangulate_poly
+//       Access: Protected, Virtual
+//  Description: Fills the container up with EggPolygons that
+//               represent the component triangles of this triangle
+//               strip.
+//
+//               It is assumed that the EggTriangleStrip is not
+//               already a child of any other group when this function
+//               is called.
+////////////////////////////////////////////////////////////////////
+void EggTriangleStrip::
+do_triangulate(EggGroupNode *container) {
+  if (size() < 3) {
+    return;
+  }
+  iterator vi = begin();
+  EggVertex *v0 = (*vi);
+  ++vi;
+  EggVertex *v1 = (*vi);
+  ++vi;
+  bool reversed = false;
+
+  for (int i = 0; i < (int)size() - 2; i++) {
+    PT(EggPolygon) poly = new EggPolygon;
+    poly->copy_attributes(*this);
+    const EggAttributes *attrib = get_component(i);
+    if (attrib->has_color()) {
+      poly->set_color(attrib->get_color());
+    }
+    if (attrib->has_normal()) {
+      poly->set_normal(attrib->get_normal());
+    }
+
+    if (reversed) {
+      poly->add_vertex(v1);
+      poly->add_vertex(v0);
+      reversed = false;
+    } else {
+      poly->add_vertex(v0);
+      poly->add_vertex(v1);
+      reversed = true;
+    }
+    poly->add_vertex(*vi);
+    v0 = v1;
+    v1 = *vi;
+    container->add_child(poly);
+    ++vi;
+  }
+}

+ 65 - 0
panda/src/egg/eggTriangleStrip.h

@@ -0,0 +1,65 @@
+// Filename: eggTriangleStrip.h
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGTRIANGLESTRIP_H
+#define EGGTRIANGLESTRIP_H
+
+#include "pandabase.h"
+
+#include "eggCompositePrimitive.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : EggTriangleStrip
+// Description : A connected strip of triangles.  This does not
+//               normally appear in an egg file; it is typically
+//               generated as a result of meshing.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG EggTriangleStrip : public EggCompositePrimitive {
+PUBLISHED:
+  INLINE EggTriangleStrip(const string &name = "");
+  INLINE EggTriangleStrip(const EggTriangleStrip &copy);
+  INLINE EggTriangleStrip &operator = (const EggTriangleStrip &copy);
+
+  virtual bool cleanup();
+
+  virtual void write(ostream &out, int indent_level) const;
+
+protected:
+  virtual void do_triangulate(EggGroupNode *container);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    EggCompositePrimitive::init_type();
+    register_type(_type_handle, "EggTriangleStrip",
+                  EggCompositePrimitive::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "eggTriangleStrip.I"
+
+#endif

+ 4 - 0
panda/src/egg/egg_composite1.cxx

@@ -4,6 +4,7 @@
 #include "eggBin.cxx"
 #include "eggBinMaker.cxx"
 #include "eggComment.cxx"
+#include "eggCompositePrimitive.cxx"
 #include "eggCoordinateSystem.cxx"
 #include "eggCurve.cxx"
 #include "eggData.cxx"
@@ -15,6 +16,9 @@
 #include "eggLine.cxx"
 #include "eggMaterial.cxx"
 #include "eggMaterialCollection.cxx"
+#include "eggMesher.cxx"
+#include "eggMesherEdge.cxx"
+#include "eggMesherStrip.cxx"
 #include "eggMiscFuncs.cxx"
 #include "eggMorphList.cxx"
 #include "eggNamedObject.cxx"

+ 1 - 0
panda/src/egg/egg_composite2.cxx

@@ -13,6 +13,7 @@
 #include "eggTexture.cxx"
 #include "eggTextureCollection.cxx"
 #include "eggTransform3d.cxx"
+#include "eggTriangleStrip.cxx"
 #include "eggUserData.cxx"
 #include "eggUtilities.cxx"
 #include "eggVertex.cxx"

+ 12 - 0
panda/src/egg/lexer.lxx

@@ -371,6 +371,10 @@ NUMERIC         ([+-]?(([0-9]+[.]?)|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
   accept();
   return COMMENT;
 }
+"<COMPONENT>" {
+  accept();
+  return COMPONENT;
+}
 "<COORDINATESYSTEM>" {
   accept();
   return COORDSYSTEM;
@@ -599,6 +603,14 @@ NUMERIC         ([+-]?(([0-9]+[.]?)|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
   accept();
   return TREF;
 }
+"<TRIANGLEFAN>" {
+  accept();
+  return TRIANGLEFAN;
+}
+"<TRIANGLESTRIP>" {
+  accept();
+  return TRIANGLESTRIP;
+}
 "<TRIM>" {
   accept();
   return TRIM;

+ 61 - 2
panda/src/egg/parser.yxx

@@ -15,6 +15,8 @@
 #include "eggVertexPool.h"
 #include "eggVertexUV.h"
 #include "eggPolygon.h"
+#include "eggCompositePrimitive.h"
+#include "eggTriangleStrip.h"
 #include "eggPoint.h"
 #include "eggLine.h"
 #include "eggNurbsSurface.h"
@@ -144,7 +146,7 @@ egg_cleanup_parser() {
 %token <_string> STRING
 
 %token BEZIERCURVE BFACE BILLBOARD BILLBOARDCENTER BUNDLE CLOSED
-%token COLLIDE COMMENT
+%token COLLIDE COMMENT COMPONENT
 %token COORDSYSTEM CV DART
 %token DNORMAL DRGBA DUV DXYZ DCS DISTANCE DTREF
 %token DYNAMICVERTEXPOOL EXTERNAL_FILE
@@ -155,7 +157,8 @@ egg_cleanup_parser() {
 %token OUTTANGENT POINTLIGHT POLYGON REF RGBA ROTATE ROTX ROTY ROTZ
 %token SANIM SCALAR SCALE SEQUENCE SHADING SWITCH SWITCHCONDITION
 %token TABLE TABLE_V TAG TEXLIST TEXTURE TLENGTHS TRANSFORM TRANSLATE
-%token TREF TRIM TXT UKNOTS UV VKNOTS VERTEX VERTEXANIM
+%token TREF TRIANGLEFAN TRIANGLESTRIP 
+%token TRIM TXT UKNOTS UV VKNOTS VERTEX VERTEXANIM
 %token VERTEXPOOL VERTEXREF
 %token XFMANIM XFMSANIM
 
@@ -179,6 +182,7 @@ egg_cleanup_parser() {
 %type <_egg> joint
 %type <_egg> line
 %type <_egg> polygon
+%type <_egg> trianglestrip
 %type <_egg> point_light
 %type <_egg> nurbs_surface
 %type <_egg> nurbs_curve
@@ -247,6 +251,7 @@ node:
         | joint 
         | instance
         | polygon
+        | trianglestrip
         | point_light
         | line
         | nurbs_surface
@@ -1590,6 +1595,25 @@ polygon:
 }
         ;
 
+/*
+ * trianglestrip
+ *
+ * enter:
+ * exit: returns a new EggTriangleStrip.
+ *
+ */
+trianglestrip:
+        TRIANGLESTRIP optional_name
+{
+  egg_stack.push_back(new EggTriangleStrip($2));
+}
+        '{' primitive_body '}'
+{
+  $$ = egg_stack.back();
+  egg_stack.pop_back();
+}
+        ;
+
 /*
  * point_light
  *
@@ -1667,6 +1691,19 @@ nurbs_curve:
         ;
 
 
+/*
+ * primitive_component_body
+ *
+ * enter: TOS is EggPrimitive.
+ * exit: primitive attributes and vertices have been filled.
+ *
+ */
+primitive_component_body:
+        empty
+        | primitive_component_body NORMAL '{' primitive_normal_body '}'
+        | primitive_component_body RGBA '{' primitive_color_body '}'
+        ;
+
 /*
  * primitive_body
  *
@@ -1676,6 +1713,28 @@ nurbs_curve:
  */
 primitive_body:
         empty
+        | primitive_body COMPONENT '{' primitive_component_body '}'
+        | primitive_body COMPONENT integer '{' 
+{
+  if (!egg_stack.back()->is_of_type(EggCompositePrimitive::get_class_type())) {
+    eggyyerror("Not a composite primitive; components are not allowed here.");
+  } else {
+    PT(EggCompositePrimitive) comp = DCAST(EggCompositePrimitive, egg_stack.back());
+    if ($3 < 0 || $3 >= comp->get_num_components()) {
+      eggyyerror("Invalid component number");
+    }
+    // We temporarily add an EggPolygon to the stack, just to receive
+    // the component attributes.
+    egg_stack.push_back(new EggPolygon);
+  }
+}
+ 	primitive_component_body '}'
+{
+  PT(EggPrimitive) prim = DCAST(EggPrimitive, egg_stack.back());
+  egg_stack.pop_back();
+  PT(EggCompositePrimitive) comp = DCAST(EggCompositePrimitive, egg_stack.back());
+  comp->set_component($3, prim);
+}
         | primitive_body TREF '{' primitive_tref_body '}'
         | primitive_body TEXTURE '{' primitive_texture_body '}'
         | primitive_body MREF '{' primitive_material_body '}'

+ 2 - 24
panda/src/egg2pg/config_egg2pg.cxx

@@ -28,32 +28,10 @@
 ConfigureDef(config_egg2pg);
 NotifyCategoryDef(egg2pg, "");
 
-ConfigVariableBool egg_mesh
-("egg-mesh", true);
-ConfigVariableBool egg_retesselate_coplanar
-("egg-retesselate-coplanar", true);
-ConfigVariableBool egg_unroll_fans
-("egg-unroll-fans", true);
-ConfigVariableBool egg_show_tstrips
-("egg-show-tstrips", false);
-ConfigVariableBool egg_show_qsheets
-("egg-show-qsheets", false);
-ConfigVariableBool egg_show_quads
-("egg-show-quads", false);
-ConfigVariableBool egg_show_normals
-("egg-show-normals", false);
 ConfigVariableDouble egg_normal_scale
 ("egg-normal-scale", 1.0);
-ConfigVariableBool egg_subdivide_polys
-("egg-subdivide-polys", true);
-ConfigVariableBool egg_consider_fans
-("egg-consider-fans", true);
-ConfigVariableDouble egg_max_tfan_angle
-("egg-max-tfan-angle", 40.0);
-ConfigVariableInt egg_min_tfan_tris
-("egg-min-tfan-tris", 4);
-ConfigVariableDouble egg_coplanar_threshold
-("egg-coplanar-threshold", 0.01);
+ConfigVariableBool egg_show_normals
+("egg-show-normals", false);
 
 ConfigVariableEnum<CoordinateSystem> egg_coordinate_system
 ("egg-coordinate-system", CS_default);

+ 1 - 13
panda/src/egg2pg/config_egg2pg.h

@@ -33,20 +33,8 @@
 ConfigureDecl(config_egg2pg, EXPCL_PANDAEGG, EXPTP_PANDAEGG);
 NotifyCategoryDecl(egg2pg, EXPCL_PANDAEGG, EXPTP_PANDAEGG);
 
-extern EXPCL_PANDAEGG ConfigVariableBool egg_mesh;
-extern EXPCL_PANDAEGG ConfigVariableBool egg_retesselate_coplanar;
-extern EXPCL_PANDAEGG ConfigVariableBool egg_unroll_fans;
-extern EXPCL_PANDAEGG ConfigVariableBool egg_show_tstrips;
-extern EXPCL_PANDAEGG ConfigVariableBool egg_show_qsheets;
-extern EXPCL_PANDAEGG ConfigVariableBool egg_show_quads;
-#define egg_false_color (egg_show_tstrips | egg_show_qsheets | egg_show_quads)
-extern EXPCL_PANDAEGG ConfigVariableBool egg_show_normals;
 extern EXPCL_PANDAEGG ConfigVariableDouble egg_normal_scale;
-extern EXPCL_PANDAEGG ConfigVariableBool egg_subdivide_polys;
-extern EXPCL_PANDAEGG ConfigVariableBool egg_consider_fans;
-extern EXPCL_PANDAEGG ConfigVariableDouble egg_max_tfan_angle;
-extern EXPCL_PANDAEGG ConfigVariableInt egg_min_tfan_tris;
-extern EXPCL_PANDAEGG ConfigVariableDouble egg_coplanar_threshold;
+extern EXPCL_PANDAEGG ConfigVariableBool egg_show_normals;
 extern EXPCL_PANDAEGG ConfigVariableEnum<CoordinateSystem> egg_coordinate_system;
 extern EXPCL_PANDAEGG ConfigVariableBool egg_ignore_mipmaps;
 extern EXPCL_PANDAEGG ConfigVariableBool egg_ignore_filters;

+ 17 - 2
panda/src/egg2pg/eggLoader.cxx

@@ -22,6 +22,7 @@
 #include "eggRenderState.h"
 #include "egg_parametrics.h"
 #include "config_egg2pg.h"
+#include "config_egg.h"
 #include "nodePath.h"
 #include "renderState.h"
 #include "transformState.h"
@@ -38,6 +39,7 @@
 #include "qpgeomVertexIterator.h"
 #include "qpgeom.h"
 #include "qpgeomTriangles.h"
+#include "qpgeomTristrips.h"
 #include "sequenceNode.h"
 #include "switchNode.h"
 #include "portalNode.h"
@@ -55,6 +57,7 @@
 #include "eggGroupNode.h"
 #include "eggGroup.h"
 #include "eggPolygon.h"
+#include "eggTriangleStrip.h"
 #include "eggBin.h"
 #include "eggTable.h"
 #include "eggBinner.h"
@@ -1442,8 +1445,16 @@ make_polyset(EggBin *egg_bin, PandaNode *parent) {
   PT(qpGeom) geom = new qpGeom;
   geom->set_vertex_data(vertex_data);
 
-  // Automatically triangulate any higher-order polygons we might have.
-  egg_bin->triangulate_polygons(true);
+  if (egg_mesh) {
+    // If we're using the mesher, mesh now.
+    egg_bin->mesh_triangles(0);
+    egg_bin->write(cerr, 0);
+
+  } else {
+    // If we're not using the mesher, at least triangulate any
+    // higher-order polygons we might have.
+    egg_bin->triangulate_polygons(EggGroupNode::T_convex);
+  }
 
   // Now create a handful of GeomPrimitives corresponding to the
   // various types of primitives we have.
@@ -1461,6 +1472,8 @@ make_polyset(EggBin *egg_bin, PandaNode *parent) {
       qpGeomPrimitive *primitive = (*pi).second;
       geom->add_primitive(primitive);
     }
+
+    geom->write(cerr);
     
     // Now, is our parent node a GeomNode, or just an ordinary
     // PandaNode?  If it's a GeomNode, we can add the new Geom directly
@@ -1940,6 +1953,8 @@ make_primitive(EggPrimitive *egg_prim, EggLoader::Primitives &primitives) {
     if (egg_prim->size() == 3) {
       primitive = new qpGeomTriangles;
     }
+  } else if (egg_prim->is_of_type(EggTriangleStrip::get_class_type())) {
+    primitive = new qpGeomTristrips;
   }
 
   if (primitive == NULL) {

+ 1 - 1
panda/src/gobj/qpgeom.cxx

@@ -196,7 +196,7 @@ write(ostream &out, int indent_level) const {
   for (pi = cdata->_primitives.begin(); 
        pi != cdata->_primitives.end();
        ++pi) {
-    (*pi)->write(out, cdata->_data, indent_level);
+    (*pi)->write(out, indent_level);
   }
 }
 

+ 2 - 3
panda/src/gobj/qpgeomPrimitive.cxx

@@ -412,7 +412,7 @@ decompose() {
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::
-output(ostream &out, const qpGeomVertexData *vertex_data) const {
+output(ostream &out) const {
   out << get_type() << ", " << get_num_primitives()
       << ", " << get_num_vertices();
 }
@@ -423,8 +423,7 @@ output(ostream &out, const qpGeomVertexData *vertex_data) const {
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::
-write(ostream &out, const qpGeomVertexData *vertex_data,
-      int indent_level) const {
+write(ostream &out, int indent_level) const {
   indent(out, indent_level)
     << get_type() << ":\n";
   int num_primitives = get_num_primitives();

+ 2 - 3
panda/src/gobj/qpgeomPrimitive.h

@@ -91,9 +91,8 @@ PUBLISHED:
 
   PT(qpGeomPrimitive) decompose();
 
-  virtual void output(ostream &out, const qpGeomVertexData *vertex_data) const;
-  virtual void write(ostream &out, const qpGeomVertexData *vertex_data, 
-                     int indent_level) const;
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
 
   void clear_cache();
 

+ 2 - 0
panda/src/gobj/qpgeomTristrips.cxx

@@ -113,6 +113,8 @@ decompose_impl() {
     }
   }
 
+  triangles->write(cerr, 0);
+
   return triangles.p();
 }
 

+ 16 - 1
pandatool/src/eggprogs/eggTrans.cxx

@@ -63,6 +63,16 @@ EggTrans() {
      "Clean out higher-order polygons by subdividing into triangles.",
      &EggTrans::dispatch_none, &_triangulate_polygons);
 
+  add_option
+    ("mesh", "", 0,
+     "Mesh triangles into triangle strips.  This is mainly useful as a "
+     "tool to visualize the work that the mesher will do, since triangles "
+     "are automatically meshed whenever an egg file is loaded.  Note that, "
+     "unlike the automatic meshing at load time, you are must ensure that "
+     "you do not start out with multiple triangles with different attributes "
+     "(e.g. texture) together in the same group.",
+     &EggTrans::dispatch_none, &_mesh_triangles);
+
   add_option
     ("N", "", 0,
      "Standardize and uniquify group names.",
@@ -86,10 +96,15 @@ run() {
 
   if (_triangulate_polygons) {
     nout << "Triangulating polygons.\n";
-    int num_produced = _data.triangulate_polygons(true);
+    int num_produced = _data.triangulate_polygons(~0);
     nout << "  (" << num_produced << " triangles produced.)\n";
   }
 
+  if (_mesh_triangles) {
+    nout << "Meshing triangles.\n";
+    _data.mesh_triangles(~0);
+  }
+
   if (_apply_texmats) {
     nout << "Applying texture matrices.\n";
     _data.apply_texmats();

+ 1 - 0
pandatool/src/eggprogs/eggTrans.h

@@ -40,6 +40,7 @@ public:
   bool _collapse_equivalent_textures;
   bool _remove_invalid_primitives;
   bool _triangulate_polygons;
+  bool _mesh_triangles;
   bool _standardize_names;
 };