Procházet zdrojové kódy

connect-triangle-strips

David Rose před 21 roky
rodič
revize
08241a99ea

+ 63 - 28
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -2711,6 +2711,7 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomMunger *munger,
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
 draw_triangles(const qpGeomTriangles *primitive) {
+  _vertices_tri_pcollector.add_level(primitive->get_num_vertices());
   if (_vbuffer_active) {
     IndexBufferContext *ibc = ((qpGeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
     nassertv(ibc != (IndexBufferContext *)NULL);
@@ -2744,41 +2745,75 @@ void DXGraphicsStateGuardian8::
 draw_tristrips(const qpGeomTristrips *primitive) {
   int min_vertex = primitive->get_min_vertex();
   int max_vertex = primitive->get_max_vertex();
-  CPTA_ushort vertices = primitive->get_flat_first_vertices();
-  CPTA_int ends = primitive->get_ends();
-  CPTA_ushort mins = primitive->get_mins();
-  CPTA_ushort maxs = primitive->get_maxs();
-  nassertv(mins.size() == ends.size() && maxs.size() == ends.size());
+  //  CPTA_ushort vertices = primitive->get_flat_first_vertices();
+  CPTA_ushort vertices = primitive->get_vertices();
+
+  if (connect_triangle_strips && _current_fill_mode != RenderModeAttrib::M_wireframe) {
+    // One long triangle strip, connected by the degenerate vertices
+    // that have already been set up within the primitive.
+    _vertices_tristrip_pcollector.add_level(primitive->get_num_vertices());
+    if (_vbuffer_active) {
+      IndexBufferContext *ibc = ((qpGeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+      nassertv(ibc != (IndexBufferContext *)NULL);
+      apply_index_buffer(ibc);
 
-  if (_vbuffer_active) {
-    IndexBufferContext *ibc = ((qpGeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
-    nassertv(ibc != (IndexBufferContext *)NULL);
-    apply_index_buffer(ibc);
-
-    unsigned int start = 0;
-    for (size_t i = 0; i < ends.size(); i++) {
       _pD3DDevice->DrawIndexedPrimitive
         (D3DPT_TRIANGLESTRIP,
-         mins[i], maxs[i] - mins[i] + 1, 
-         start, ends[i] - start - 2);
-
-      start = ends[i];
+         primitive->get_min_vertex(),
+         primitive->get_max_vertex() - primitive->get_min_vertex() + 1,
+         0, primitive->get_num_vertices() - 2);
+      
+    } else {
+      _pD3DDevice->DrawIndexedPrimitiveUP
+        (D3DPT_TRIANGLESTRIP, 
+         primitive->get_min_vertex(),
+         primitive->get_max_vertex() - primitive->get_min_vertex() + 1,
+         primitive->get_num_vertices() - 2, 
+         vertices, D3DFMT_INDEX16,
+         _vertex_data->get_array(0)->get_data(),
+         _vertex_data->get_format()->get_array(0)->get_stride());
     }
 
   } else {
-    CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
-    int stride = _vertex_data->get_format()->get_array(0)->get_stride();
-
-    unsigned int start = 0;
-    for (size_t i = 0; i < ends.size(); i++) {
-      _pD3DDevice->DrawIndexedPrimitiveUP
-        (D3DPT_TRIANGLESTRIP, 
-         mins[i], maxs[i] - mins[i] + 1, 
-         ends[i] - start - 2,
-         vertices + start, D3DFMT_INDEX16,
-         array_data, stride);
+    // Send the individual triangle strips, stepping over the
+    // degenerate vertices.
+    CPTA_int ends = primitive->get_ends();
+    CPTA_ushort mins = primitive->get_mins();
+    CPTA_ushort maxs = primitive->get_maxs();
+    nassertv(mins.size() == ends.size() && maxs.size() == ends.size());
+    
+    if (_vbuffer_active) {
+      IndexBufferContext *ibc = ((qpGeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+      nassertv(ibc != (IndexBufferContext *)NULL);
+      apply_index_buffer(ibc);
+      
+      unsigned int start = 0;
+      for (size_t i = 0; i < ends.size(); i++) {
+        _vertices_tristrip_pcollector.add_level(ends[i] - start);
+        _pD3DDevice->DrawIndexedPrimitive
+          (D3DPT_TRIANGLESTRIP,
+           mins[i], maxs[i] - mins[i] + 1, 
+           start, ends[i] - start - 2);
+        
+        start = ends[i] + 2;
+      }
+      
+    } else {
+      CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
+      int stride = _vertex_data->get_format()->get_array(0)->get_stride();
       
-      start = ends[i];
+      unsigned int start = 0;
+      for (size_t i = 0; i < ends.size(); i++) {
+        _vertices_tristrip_pcollector.add_level(ends[i] - start);
+        _pD3DDevice->DrawIndexedPrimitiveUP
+          (D3DPT_TRIANGLESTRIP, 
+           mins[i], maxs[i] - mins[i] + 1, 
+           ends[i] - start - 2,
+           vertices + start, D3DFMT_INDEX16,
+           array_data, stride);
+        
+        start = ends[i] + 2;
+      }
     }
   }
 }

+ 28 - 14
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -2247,22 +2247,36 @@ draw_triangles(const qpGeomTriangles *primitive) {
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
 draw_tristrips(const qpGeomTristrips *primitive) {
-  _vertices_tristrip_pcollector.add_level(primitive->get_num_vertices());
   const unsigned short *client_pointer = setup_primitive(primitive);
 
-  CPTA_int ends = primitive->get_ends();
-  CPTA_ushort mins = primitive->get_mins();
-  CPTA_ushort maxs = primitive->get_maxs();
-  nassertv(mins.size() == ends.size() && maxs.size() == ends.size());
-
-  unsigned int start = 0;
-  for (size_t i = 0; i < ends.size(); i++) {
+  if (connect_triangle_strips && _render_mode != RenderModeAttrib::M_wireframe) {
+    // One long triangle strip, connected by the degenerate vertices
+    // that have already been set up within the primitive.
+    _vertices_tristrip_pcollector.add_level(primitive->get_num_vertices());
     _glDrawRangeElements(GL_TRIANGLE_STRIP, 
-                         mins[i], maxs[i], ends[i] - start,
-                         GL_UNSIGNED_SHORT, client_pointer + start);
-    start = ends[i];
-  }
+                         primitive->get_min_vertex(),
+                         primitive->get_max_vertex(),
+                         primitive->get_num_vertices(),
+                         GL_UNSIGNED_SHORT, client_pointer);
 
+  } else {
+    // Send the individual triangle strips, stepping over the
+    // degenerate vertices.
+    CPTA_int ends = primitive->get_ends();
+    CPTA_ushort mins = primitive->get_mins();
+    CPTA_ushort maxs = primitive->get_maxs();
+    nassertv(mins.size() == ends.size() && maxs.size() == ends.size());
+    
+    unsigned int start = 0;
+    for (size_t i = 0; i < ends.size(); i++) {
+      _vertices_tristrip_pcollector.add_level(ends[i] - start);
+      _glDrawRangeElements(GL_TRIANGLE_STRIP, 
+                           mins[i], maxs[i], ends[i] - start,
+                           GL_UNSIGNED_SHORT, client_pointer + start);
+      start = ends[i] + 2;
+    }
+  }
+    
   report_my_gl_errors();
 }
 
@@ -2273,7 +2287,7 @@ draw_tristrips(const qpGeomTristrips *primitive) {
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
 draw_lines(const qpGeomLines *primitive) {
-  _vertices_tri_pcollector.add_level(primitive->get_num_vertices());
+  _vertices_other_pcollector.add_level(primitive->get_num_vertices());
   const unsigned short *client_pointer = setup_primitive(primitive);
 
   _glDrawRangeElements(GL_LINES, 
@@ -2292,7 +2306,7 @@ draw_lines(const qpGeomLines *primitive) {
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
 draw_points(const qpGeomPoints *primitive) {
-  _vertices_tri_pcollector.add_level(primitive->get_num_vertices());
+  _vertices_other_pcollector.add_level(primitive->get_num_vertices());
   const unsigned short *client_pointer = setup_primitive(primitive);
 
   _glDrawRangeElements(GL_POINTS, 

+ 9 - 0
panda/src/gobj/config_gobj.cxx

@@ -142,6 +142,15 @@ ConfigVariableBool matrix_palette
           "because its support seems to be buggy in certain drivers "
           "(ATI FireGL T2 8.103 in particular.)"));
 
+ConfigVariableBool connect_triangle_strips
+("connect-triangle-strips", true,
+ PRC_DESC("Set this true to set a batch of triangle strips to the graphics "
+          "card as one long triangle strip, connected by degenerate "
+          "triangles, or false to send them as separate triangle strips "
+          "with no degenerate triangles.  In many cases, using one long "
+          "triangle strip can help performance by reducing the number "
+          "of separate graphics calls that have to be made."));
+
 ConfigVariableBool use_qpgeom
 ("use-qpgeom", false,
  PRC_DESC("A temporary variable while the experimental Geom rewrite is "

+ 1 - 0
panda/src/gobj/config_gobj.h

@@ -55,6 +55,7 @@ extern EXPCL_PANDA ConfigVariableBool vertex_buffers;
 extern EXPCL_PANDA ConfigVariableBool display_lists;
 extern EXPCL_PANDA ConfigVariableBool hardware_animated_vertices;
 extern EXPCL_PANDA ConfigVariableBool matrix_palette;
+extern EXPCL_PANDA ConfigVariableBool connect_triangle_strips;
 
 extern EXPCL_PANDA ConfigVariableBool use_qpgeom;
 

+ 3 - 3
panda/src/gobj/qpgeomLines.cxx

@@ -56,7 +56,7 @@ qpGeomLines::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomLines::make_copy
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 PT(qpGeomPrimitive) qpGeomLines::
@@ -66,7 +66,7 @@ make_copy() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomLines::get_primitive_type
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: Returns the fundamental rendering type of this
 //               primitive: whether it is points, lines, or polygons.
 //               This is used primarily to set up the appropriate
@@ -80,7 +80,7 @@ get_primitive_type() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomLines::get_num_vertices_per_primitive
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: If the primitive type is a simple type in which all
 //               primitives have the same number of vertices, like
 //               lines, returns the number of vertices per

+ 1 - 0
panda/src/gobj/qpgeomLines.h

@@ -34,6 +34,7 @@ PUBLISHED:
   qpGeomLines(const qpGeomLines &copy);
   virtual ~qpGeomLines();
 
+public:
   virtual PT(qpGeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
 

+ 2 - 2
panda/src/gobj/qpgeomLinestrips.cxx

@@ -57,7 +57,7 @@ qpGeomLinestrips::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomLinestrips::make_copy
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 PT(qpGeomPrimitive) qpGeomLinestrips::
@@ -67,7 +67,7 @@ make_copy() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomLinestrips::get_primitive_type
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: Returns the fundamental rendering type of this
 //               primitive: whether it is points, lines, or polygons.
 //               This is used primarily to set up the appropriate

+ 1 - 0
panda/src/gobj/qpgeomLinestrips.h

@@ -34,6 +34,7 @@ PUBLISHED:
   qpGeomLinestrips(const qpGeomLinestrips &copy);
   virtual ~qpGeomLinestrips();
 
+public:
   virtual PT(qpGeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
 

+ 3 - 3
panda/src/gobj/qpgeomPoints.cxx

@@ -56,7 +56,7 @@ qpGeomPoints::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPoints::make_copy
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 PT(qpGeomPrimitive) qpGeomPoints::
@@ -66,7 +66,7 @@ make_copy() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPoints::get_primitive_type
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: Returns the fundamental rendering type of this
 //               primitive: whether it is points, lines, or polygons.
 //               This is used primarily to set up the appropriate
@@ -80,7 +80,7 @@ get_primitive_type() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPoints::get_num_vertices_per_primitive
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: If the primitive type is a simple type in which all
 //               primitives have the same number of vertices, like
 //               points, returns the number of vertices per

+ 1 - 0
panda/src/gobj/qpgeomPoints.h

@@ -34,6 +34,7 @@ PUBLISHED:
   qpGeomPoints(const qpGeomPoints &copy);
   virtual ~qpGeomPoints();
 
+public:
   virtual PT(qpGeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
 

+ 131 - 102
panda/src/gobj/qpgeomPrimitive.I

@@ -94,8 +94,133 @@ get_vertex(int i) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_vertices
+//     Function: qpGeomPrimitive::get_num_faces
+//       Access: Published
+//  Description: Returns the number of triangles or other fundamental
+//               type (such as line segments) represented by all the
+//               primitives in this object.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomPrimitive::
+get_num_faces() const {
+  int num_vertices_per_primitive = get_num_vertices_per_primitive();
+
+  if (num_vertices_per_primitive == 0) {
+    int num_primitives = get_num_primitives();
+    int num_vertices = get_num_vertices();
+    int min_num_vertices_per_primitive = get_min_num_vertices_per_primitive();
+    int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
+    return num_vertices - (num_primitives * (min_num_vertices_per_primitive - 1)) - ((num_primitives - 1) * num_unused_vertices_per_primitive);
+  } else {
+    return get_num_primitives();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_primitive_num_faces
+//       Access: Published
+//  Description: Returns the number of triangles or other fundamental
+//               type (such as line segments) represented by the nth
+//               primitive in this object.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomPrimitive::
+get_primitive_num_faces(int n) const {
+  int num_vertices_per_primitive = get_num_vertices_per_primitive();
+
+  if (num_vertices_per_primitive == 0) {
+    return get_primitive_num_vertices(n) - get_min_num_vertices_per_primitive() + 1;
+  } else {
+    return 1;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_min_vertex
 //       Access: Published
+//  Description: Returns the minimum vertex index number used by all
+//               the primitives in this object.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomPrimitive::
+get_min_vertex() const {
+  CDReader cdata(_cycler);
+  if (!cdata->_got_minmax) {
+    CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
+    ((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
+    return cdataw->_min_vertex;
+  }
+  return cdata->_min_vertex;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_primitive_min_vertex
+//       Access: Published
+//  Description: Returns the minimum vertex index number used by the
+//               nth primitive in this object.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomPrimitive::
+get_primitive_min_vertex(int n) const {
+  CPTA_ushort mins = get_mins();
+  nassertr(n >= 0 && n < (int)mins.size(), -1);
+  return mins[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_max_vertex
+//       Access: Published
+//  Description: Returns the maximum vertex index number used by all
+//               the primitives in this object.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomPrimitive::
+get_max_vertex() const {
+  CDReader cdata(_cycler);
+  if (!cdata->_got_minmax) {
+    CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
+    ((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
+    return cdataw->_max_vertex;
+  }
+  return cdata->_max_vertex;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_primitive_max_vertex
+//       Access: Published
+//  Description: Returns the maximum vertex index number used by the
+//               nth primitive in this object.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomPrimitive::
+get_primitive_max_vertex(int n) const {
+  CPTA_ushort maxs = get_maxs();
+  nassertr(n >= 0 && n < (int)maxs.size(), -1);
+  return maxs[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_data_size_bytes
+//       Access: Published
+//  Description: Returns the number of bytes stored in the vertices
+//               array.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomPrimitive::
+get_data_size_bytes() const {
+  CDReader cdata(_cycler);
+  return cdata->_vertices.size() * sizeof(short);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_modified
+//       Access: Published
+//  Description: Returns a sequence number which is guaranteed to
+//               change at least every time the vertex index array is
+//               modified.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq qpGeomPrimitive::
+get_modified() const {
+  CDReader cdata(_cycler);
+  return cdata->_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_vertices
+//       Access: Public
 //  Description: Returns a const pointer to the vertex index array so
 //               application code can read it directly.  Do not
 //               attempt to modify the returned array; use
@@ -109,7 +234,7 @@ get_vertices() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::get_flat_first_vertices
-//       Access: Published
+//       Access: Public
 //  Description: Returns a const pointer to the vertex index array,
 //               suitable for traversing for a renderer that expects
 //               the key vertex of each triangle in a flat-shaded
@@ -138,7 +263,7 @@ get_flat_first_vertices() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::get_flat_last_vertices
-//       Access: Published
+//       Access: Public
 //  Description: Returns a const pointer to the vertex index array,
 //               suitable for traversing for a renderer that expects
 //               the key vertex of each triangle in a flat-shaded
@@ -167,7 +292,7 @@ get_flat_last_vertices() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::get_ends
-//       Access: Published
+//       Access: Public
 //  Description: Returns a const pointer to the primitive ends
 //               array so application code can read it directly.  Do
 //               not attempt to modify the returned array; use
@@ -185,7 +310,7 @@ get_ends() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::get_mins
-//       Access: Published
+//       Access: Public
 //  Description: Returns a const pointer to the primitive mins
 //               array so application code can read it directly.  Do
 //               not attempt to modify the returned array; use
@@ -207,7 +332,7 @@ get_mins() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::get_maxs
-//       Access: Published
+//       Access: Public
 //  Description: Returns a const pointer to the primitive maxs
 //               array so application code can read it directly.  Do
 //               not attempt to modify the returned array; use
@@ -227,102 +352,6 @@ get_maxs() const {
   return cdata->_maxs;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_data_size_bytes
-//       Access: Published
-//  Description: Returns the number of bytes stored in the vertices
-//               array.
-////////////////////////////////////////////////////////////////////
-INLINE int qpGeomPrimitive::
-get_data_size_bytes() const {
-  CDReader cdata(_cycler);
-  return cdata->_vertices.size() * sizeof(short);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_modified
-//       Access: Published
-//  Description: Returns a sequence number which is guaranteed to
-//               change at least every time the vertex index array is
-//               modified.
-////////////////////////////////////////////////////////////////////
-INLINE UpdateSeq qpGeomPrimitive::
-get_modified() const {
-  CDReader cdata(_cycler);
-  return cdata->_modified;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_min_vertex
-//       Access: Published
-//  Description: Returns the minimum vertex index number used by the
-//               primitives in this object.
-////////////////////////////////////////////////////////////////////
-INLINE int qpGeomPrimitive::
-get_min_vertex() const {
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
-    ((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
-    return cdataw->_min_vertex;
-  }
-  return cdata->_min_vertex;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_min_vertex
-//       Access: Published
-//  Description: Returns the minimum vertex index number used by the
-//               ith primitive in this object.
-////////////////////////////////////////////////////////////////////
-INLINE int qpGeomPrimitive::
-get_min_vertex(int i) const {
-  CPTA_ushort mins = get_mins();
-  nassertr(i >= 0 && i < (int)mins.size(), -1);
-  return mins[i];
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_max_vertex
-//       Access: Published
-//  Description: Returns the maximum vertex index number used by the
-//               primitives in this object.
-////////////////////////////////////////////////////////////////////
-INLINE int qpGeomPrimitive::
-get_max_vertex() const {
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
-    ((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
-    return cdataw->_max_vertex;
-  }
-  return cdata->_max_vertex;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_max_vertex
-//       Access: Published
-//  Description: Returns the maximum vertex index number used by the
-//               ith primitive in this object.
-////////////////////////////////////////////////////////////////////
-INLINE int qpGeomPrimitive::
-get_max_vertex(int i) const {
-  CPTA_ushort maxs = get_maxs();
-  nassertr(i >= 0 && i < (int)maxs.size(), -1);
-  return maxs[i];
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_primitive_end
-//       Access: Published
-//  Description: Returns the element within the _vertices list at which
-//               the ith primitive ends.  
-////////////////////////////////////////////////////////////////////
-INLINE int qpGeomPrimitive::
-get_primitive_end(int i) const {
-  return get_primitive_start(i + 1);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::CData::Constructor
 //       Access: Public

+ 234 - 142
panda/src/gobj/qpgeomPrimitive.cxx

@@ -98,6 +98,15 @@ add_vertex(int vertex) {
 
   clear_cache();
   CDWriter cdata(_cycler);
+
+  int num_primitives = get_num_primitives();
+  if (num_primitives > 0 &&
+      cdata->_vertices.size() == get_primitive_end(num_primitives - 1)) {
+    // If we are beginning a new primitive, give the derived class a
+    // chance to insert some degenerate vertices.
+    append_unused_vertices(cdata->_vertices, vertex);
+  }
+
   cdata->_vertices.push_back(short_vertex);
   cdata->_rotated_vertices.clear();
 
@@ -166,9 +175,10 @@ add_consecutive_vertices(int start, int num_vertices) {
 //       Access: Published
 //  Description: Indicates that the previous n calls to add_vertex(),
 //               since the last call to close_primitive(), have fully
-//               defined a new primitive.
+//               defined a new primitive.  Returns true if successful,
+//               false otherwise.
 ////////////////////////////////////////////////////////////////////
-void qpGeomPrimitive::
+bool qpGeomPrimitive::
 close_primitive() {
   clear_cache();
   int num_vertices_per_primitive = get_num_vertices_per_primitive();
@@ -183,22 +193,35 @@ close_primitive() {
       num_added = (int)cdata->_vertices.size();
     } else {
       num_added = (int)cdata->_vertices.size() - cdata->_ends.back();
+      num_added -= get_num_unused_vertices_per_primitive();
     }
-    nassertv(num_added >= get_min_num_vertices_per_primitive());
+    nassertr(num_added >= get_min_num_vertices_per_primitive(), false);
 #endif
     cdata->_ends.push_back((int)cdata->_vertices.size());
 
+#ifndef NDEBUG
     if (cdata->_got_minmax) {
-      nassertv((cdata->_mins.size() == cdata->_ends.size()) &&
-               (cdata->_maxs.size() == cdata->_ends.size()));
+      nassertd((cdata->_mins.size() == cdata->_ends.size()) &&
+               (cdata->_maxs.size() == cdata->_ends.size())) {
+        cdata->_got_minmax = false;
+      }
     }
+#endif
 
   } else {
+#ifndef NDEBUG
     // This is a simple primitive type like a triangle: each primitive
     // uses the same number of vertices.  Assert that we added the
     // correct number of vertices.
-    nassertv((int)cdata->_vertices.size() % num_vertices_per_primitive == 0);
+    int num_vertices_per_primitive = get_num_vertices_per_primitive();
+    int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
+
+    int num_vertices = cdata->_vertices.size();
+    nassertr((num_vertices + num_unused_vertices_per_primitive) % (num_vertices_per_primitive + num_unused_vertices_per_primitive) == 0, false)
+#endif
   }
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -219,121 +242,6 @@ clear_vertices() {
   cdata->_got_minmax = false;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::modify_vertices
-//       Access: Published
-//  Description: Returns a modifiable pointer to the vertex index
-//               list, so application code can directly fiddle with
-//               this data.  Use with caution, since there are no
-//               checks that the data will be left in a stable state.
-////////////////////////////////////////////////////////////////////
-PTA_ushort qpGeomPrimitive::
-modify_vertices() {
-  clear_cache();
-  CDWriter cdata(_cycler);
-  cdata->_rotated_vertices.clear();
-  cdata->_got_minmax = false;
-  return cdata->_vertices;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::set_vertices
-//       Access: Published
-//  Description: Completely replaces the vertex index list with a new
-//               table.  Chances are good that you should also replace
-//               the ends list with set_ends() at the same time.
-////////////////////////////////////////////////////////////////////
-void qpGeomPrimitive::
-set_vertices(CPTA_ushort vertices) {
-  clear_cache();
-  CDWriter cdata(_cycler);
-  cdata->_vertices = (PTA_ushort &)vertices;
-  cdata->_rotated_vertices.clear();
-  cdata->_got_minmax = false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::modify_ends
-//       Access: Published
-//  Description: Returns a modifiable pointer to the primitive ends
-//               array, so application code can directly fiddle with
-//               this data.  Use with caution, since there are no
-//               checks that the data will be left in a stable state.
-//
-//               Note that simple primitive types, like triangles, do
-//               not have a ends array: since all the primitives
-//               have the same number of vertices, it is not needed.
-////////////////////////////////////////////////////////////////////
-PTA_int qpGeomPrimitive::
-modify_ends() {
-  clear_cache();
-  CDWriter cdata(_cycler);
-  cdata->_rotated_vertices.clear();
-  cdata->_got_minmax = false;
-  return cdata->_ends;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::set_ends
-//       Access: Published
-//  Description: Completely replaces the primitive ends array with
-//               a new table.  Chances are good that you should also
-//               replace the vertices list with set_vertices() at the
-//               same time.
-//
-//               Note that simple primitive types, like triangles, do
-//               not have a ends array: since all the primitives
-//               have the same number of vertices, it is not needed.
-////////////////////////////////////////////////////////////////////
-void qpGeomPrimitive::
-set_ends(CPTA_int ends) {
-  clear_cache();
-  CDWriter cdata(_cycler);
-  cdata->_ends = (PTA_int &)ends;
-  cdata->_rotated_vertices.clear();
-  cdata->_got_minmax = false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_num_bytes
-//       Access: Published
-//  Description: Returns the number of bytes consumed by the primitive
-//               and its index table(s).
-////////////////////////////////////////////////////////////////////
-int qpGeomPrimitive::
-get_num_bytes() const {
-  CDReader cdata(_cycler);
-  return (cdata->_vertices.size() + cdata->_mins.size() + cdata->_maxs.size()) * sizeof(short) +
-    cdata->_ends.size() * sizeof(int) + sizeof(qpGeomPrimitive);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_num_vertices_per_primitive
-//       Access: Published, Virtual
-//  Description: If the primitive type is a simple type in which all
-//               primitives have the same number of vertices, like
-//               triangles, returns the number of vertices per
-//               primitive.  If the primitive type is a more complex
-//               type in which different primitives might have
-//               different numbers of vertices, for instance a
-//               triangle strip, returns 0.
-////////////////////////////////////////////////////////////////////
-int qpGeomPrimitive::
-get_num_vertices_per_primitive() const {
-  return 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomPrimitive::get_min_num_vertices_per_primitive
-//       Access: Published, Virtual
-//  Description: Returns the minimum number of vertices that must be
-//               added before close_primitive() may legally be called.
-////////////////////////////////////////////////////////////////////
-int qpGeomPrimitive::
-get_min_num_vertices_per_primitive() const {
-  return 3;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::get_num_primitives
 //       Access: Published
@@ -362,56 +270,86 @@ get_num_primitives() const {
 //     Function: qpGeomPrimitive::get_primitive_start
 //       Access: Published
 //  Description: Returns the element within the _vertices list at which
-//               the ith primitive starts.  
+//               the nth primitive starts.  
 //
 //               If i is one more than the highest valid primitive
 //               vertex, the return value will be one more than the
-//               last valid vertex.  Thus, it is always true that the
-//               vertices used by a particular primitive i are the set
-//               get_primitive_start(i) <= vi < get_primitive_start(i
-//               + 1).
+//               last valid vertex.  Thus, it is generally true that
+//               the vertices used by a particular primitive i are the
+//               set get_primitive_start(n) <= vi <
+//               get_primitive_start(n + 1) (although this range also
+//               includes the unused vertices between primitives).
 ////////////////////////////////////////////////////////////////////
 int qpGeomPrimitive::
-get_primitive_start(int i) const {
+get_primitive_start(int n) const {
   int num_vertices_per_primitive = get_num_vertices_per_primitive();
+  int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
 
   if (num_vertices_per_primitive == 0) {
     // This is a complex primitive type like a triangle strip: each
     // primitive uses a different number of vertices.
     CDReader cdata(_cycler);
-    nassertr(i >= 0 && i <= (int)cdata->_ends.size(), -1);
-    if (i == 0) {
+    nassertr(n >= 0 && n <= (int)cdata->_ends.size(), -1);
+    if (n == 0) {
       return 0;
     } else {
-      return cdata->_ends[i - 1];
+      return cdata->_ends[n - 1] + num_unused_vertices_per_primitive;
     }
 
   } else {
     // This is a simple primitive type like a triangle: each primitive
     // uses the same number of vertices.
-    return i * num_vertices_per_primitive;
+    return n * (num_vertices_per_primitive + num_unused_vertices_per_primitive);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_primitive_end
+//       Access: Published
+//  Description: Returns the element within the _vertices list at which
+//               the nth primitive ends.  This is one past the last
+//               valid element for the nth primitive.
+////////////////////////////////////////////////////////////////////
+int qpGeomPrimitive::
+get_primitive_end(int n) const {
+  int num_vertices_per_primitive = get_num_vertices_per_primitive();
+
+  if (num_vertices_per_primitive == 0) {
+    // This is a complex primitive type like a triangle strip: each
+    // primitive uses a different number of vertices.
+    CDReader cdata(_cycler);
+    nassertr(n >= 0 && n < (int)cdata->_ends.size(), -1);
+    return cdata->_ends[n];
+
+  } else {
+    // This is a simple primitive type like a triangle: each primitive
+    // uses the same number of vertices.
+    int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
+    return n * (num_vertices_per_primitive + num_unused_vertices_per_primitive) + num_vertices_per_primitive;
   }
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::get_primitive_num_vertices
 //       Access: Published
-//  Description: Returns the number of vertices used by the ith
-//               primitive.
+//  Description: Returns the number of vertices used by the nth
+//               primitive.  This is the same thing as
+//               get_primitive_end(n) - get_primitive_start(n).
 ////////////////////////////////////////////////////////////////////
 int qpGeomPrimitive::
-get_primitive_num_vertices(int i) const {
+get_primitive_num_vertices(int n) const {
   int num_vertices_per_primitive = get_num_vertices_per_primitive();
 
   if (num_vertices_per_primitive == 0) {
     // This is a complex primitive type like a triangle strip: each
     // primitive uses a different number of vertices.
     CDReader cdata(_cycler);
-    nassertr(i >= 0 && i < (int)cdata->_ends.size(), 0);
-    if (i == 0) {
+    nassertr(n >= 0 && n < (int)cdata->_ends.size(), 0);
+    if (n == 0) {
       return cdata->_ends[0];
     } else {
-      return cdata->_ends[i] - cdata->_ends[i - 1];
+      int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
+      return cdata->_ends[n] - cdata->_ends[n - 1] - num_unused_vertices_per_primitive;
     }      
 
   } else {
@@ -482,6 +420,19 @@ decompose() const {
   return result;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_num_bytes
+//       Access: Published
+//  Description: Returns the number of bytes consumed by the primitive
+//               and its index table(s).
+////////////////////////////////////////////////////////////////////
+int qpGeomPrimitive::
+get_num_bytes() const {
+  CDReader cdata(_cycler);
+  return (cdata->_vertices.size() + cdata->_mins.size() + cdata->_maxs.size()) * sizeof(short) +
+    cdata->_ends.size() * sizeof(int) + sizeof(qpGeomPrimitive);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::output
 //       Access: Published, Virtual
@@ -503,21 +454,149 @@ write(ostream &out, int indent_level) const {
   indent(out, indent_level)
     << get_type() << ":\n";
   int num_primitives = get_num_primitives();
-  for (int i = 0; i < num_primitives; i++) {
+  int num_vertices = get_num_vertices();
+  int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
+  for (int i = 0; i < num_primitives; ++i) {
     indent(out, indent_level + 2)
       << "[";
     int begin = get_primitive_start(i);
-    int end = get_primitive_start(i + 1);
+    int end = get_primitive_end(i);
     for (int vi = begin; vi < end; vi++) {
       out << " " << get_vertex(vi);
     }
-    out << " ]\n";
+    out << " ]";
+    if (end < num_vertices) {
+      for (int ui = 0; ui < num_unused_vertices_per_primitive; ++ui) {
+        if (end + ui < num_vertices) {
+          out << " " << get_vertex(end + ui);
+        } else {
+          out << " ?";
+        }
+      }
+    }
+    out << "\n";
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::modify_vertices
+//       Access: Public
+//  Description: Returns a modifiable pointer to the vertex index
+//               list, so application code can directly fiddle with
+//               this data.  Use with caution, since there are no
+//               checks that the data will be left in a stable state.
+////////////////////////////////////////////////////////////////////
+PTA_ushort qpGeomPrimitive::
+modify_vertices() {
+  clear_cache();
+  CDWriter cdata(_cycler);
+  cdata->_rotated_vertices.clear();
+  cdata->_got_minmax = false;
+  return cdata->_vertices;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::set_vertices
+//       Access: Public
+//  Description: Completely replaces the vertex index list with a new
+//               table.  Chances are good that you should also replace
+//               the ends list with set_ends() at the same time.
+////////////////////////////////////////////////////////////////////
+void qpGeomPrimitive::
+set_vertices(CPTA_ushort vertices) {
+  clear_cache();
+  CDWriter cdata(_cycler);
+  cdata->_vertices = (PTA_ushort &)vertices;
+  cdata->_rotated_vertices.clear();
+  cdata->_got_minmax = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::modify_ends
+//       Access: Public
+//  Description: Returns a modifiable pointer to the primitive ends
+//               array, so application code can directly fiddle with
+//               this data.  Use with caution, since there are no
+//               checks that the data will be left in a stable state.
+//
+//               Note that simple primitive types, like triangles, do
+//               not have a ends array: since all the primitives
+//               have the same number of vertices, it is not needed.
+////////////////////////////////////////////////////////////////////
+PTA_int qpGeomPrimitive::
+modify_ends() {
+  clear_cache();
+  CDWriter cdata(_cycler);
+  cdata->_rotated_vertices.clear();
+  cdata->_got_minmax = false;
+  return cdata->_ends;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::set_ends
+//       Access: Public
+//  Description: Completely replaces the primitive ends array with
+//               a new table.  Chances are good that you should also
+//               replace the vertices list with set_vertices() at the
+//               same time.
+//
+//               Note that simple primitive types, like triangles, do
+//               not have a ends array: since all the primitives
+//               have the same number of vertices, it is not needed.
+////////////////////////////////////////////////////////////////////
+void qpGeomPrimitive::
+set_ends(CPTA_int ends) {
+  clear_cache();
+  CDWriter cdata(_cycler);
+  cdata->_ends = (PTA_int &)ends;
+  cdata->_rotated_vertices.clear();
+  cdata->_got_minmax = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_num_vertices_per_primitive
+//       Access: Public, Virtual
+//  Description: If the primitive type is a simple type in which all
+//               primitives have the same number of vertices, like
+//               triangles, returns the number of vertices per
+//               primitive.  If the primitive type is a more complex
+//               type in which different primitives might have
+//               different numbers of vertices, for instance a
+//               triangle strip, returns 0.
+////////////////////////////////////////////////////////////////////
+int qpGeomPrimitive::
+get_num_vertices_per_primitive() const {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_min_num_vertices_per_primitive
+//       Access: Public, Virtual
+//  Description: Returns the minimum number of vertices that must be
+//               added before close_primitive() may legally be called.
+////////////////////////////////////////////////////////////////////
+int qpGeomPrimitive::
+get_min_num_vertices_per_primitive() const {
+  return 3;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::get_num_unused_vertices_per_primitive
+//       Access: Public, Virtual
+//  Description: Returns the number of vertices that are added between
+//               primitives that aren't, strictly speaking, part of
+//               the primitives themselves.  This is used, for
+//               instance, to define degenerate triangles to connect
+//               otherwise disconnected triangle strips.
+////////////////////////////////////////////////////////////////////
+int qpGeomPrimitive::
+get_num_unused_vertices_per_primitive() const {
+  return 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::clear_cache
-//       Access: Published
+//       Access: Public
 //  Description: Removes all of the previously-cached results of
 //               decompose().
 ////////////////////////////////////////////////////////////////////
@@ -539,7 +618,7 @@ clear_cache() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::prepare
-//       Access: Published
+//       Access: Public
 //  Description: Indicates that the data should be enqueued to be
 //               prepared in the indicated prepared_objects at the
 //               beginning of the next frame.  This will ensure the
@@ -756,6 +835,19 @@ rotate_impl() const {
   return get_vertices();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::append_unused_vertices
+//       Access: Protected, Virtual
+//  Description: Called when a new primitive is begun (other than the
+//               first primitive), this should add some degenerate
+//               vertices between primitives, if the primitive type
+//               requires that.  The second parameter is the first
+//               vertex that begins the new primitive.
+////////////////////////////////////////////////////////////////////
+void qpGeomPrimitive::
+append_unused_vertices(PTA_ushort &, int) {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::do_rotate
 //       Access: Private

+ 43 - 21
panda/src/gobj/qpgeomPrimitive.h

@@ -105,13 +105,53 @@ PUBLISHED:
   INLINE ShadeModel get_shade_model() const;
   INLINE void set_shade_model(ShadeModel shade_model);
 
+  // The following published methods are provided for safe, high-level
+  // iteration through the vertices and sub-primitives within the
+  // GeomPrimitive class.  These work correctly regardless of the
+  // primitive type and without depending on knowledge about the way
+  // primitives' lengths are encoded.  You can also safely build up a
+  // composite primitive using these methods.
+
   INLINE int get_num_vertices() const;
   INLINE int get_vertex(int i) const;
   void add_vertex(int vertex);
   void add_consecutive_vertices(int start, int num_vertices);
-  void close_primitive();
+  bool close_primitive();
   void clear_vertices();
 
+  int get_num_primitives() const;
+  int get_primitive_start(int n) const;
+  int get_primitive_end(int n) const;
+  int get_primitive_num_vertices(int n) const;
+
+  INLINE int get_num_faces() const;
+  INLINE int get_primitive_num_faces(int n) const;
+
+  INLINE int get_min_vertex() const;
+  INLINE int get_primitive_min_vertex(int n) const;
+  INLINE int get_max_vertex() const;
+  INLINE int get_primitive_max_vertex(int n) const;
+
+  CPT(qpGeomPrimitive) decompose() const;
+
+  int get_num_bytes() const;
+  INLINE int get_data_size_bytes() const;
+  INLINE UpdateSeq get_modified() const;
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
+public:
+  // These public methods are not intended for high-level usage.  They
+  // are public so that C++ code that absolutely needs fast access to
+  // the primitive data can get to it, but using them requires
+  // knowledge about how the component primitives are encoded within
+  // the GeomPrimitive class, and it's easy to screw something up.
+  // Also, if too many code samples depend on this internal knowledge,
+  // it may make it difficult to extend this class later.  It is
+  // recommended that application-level code use the above interfaces
+  // instead.
+
   INLINE CPTA_ushort get_vertices() const;
   INLINE CPTA_ushort get_flat_first_vertices() const;
   INLINE CPTA_ushort get_flat_last_vertices() const;
@@ -125,29 +165,11 @@ PUBLISHED:
   INLINE CPTA_ushort get_mins() const;
   INLINE CPTA_ushort get_maxs() const;
 
-  int get_num_bytes() const;
-  INLINE int get_data_size_bytes() const;
-  INLINE UpdateSeq get_modified() const;
-
-  INLINE int get_min_vertex() const;
-  INLINE int get_min_vertex(int i) const;
-  INLINE int get_max_vertex() const;
-  INLINE int get_max_vertex(int i) const;
-
   virtual int get_num_vertices_per_primitive() const;
   virtual int get_min_num_vertices_per_primitive() const;
-  int get_num_primitives() const;
-  int get_primitive_start(int i) const;
-  INLINE int get_primitive_end(int i) const;
-  int get_primitive_num_vertices(int i) const;
-
-  CPT(qpGeomPrimitive) decompose() const;
-
-  virtual void output(ostream &out) const;
-  virtual void write(ostream &out, int indent_level) const;
+  virtual int get_num_unused_vertices_per_primitive() const;
 
   void clear_cache();
-
   void prepare(PreparedGraphicsObjects *prepared_objects);
 
 public:
@@ -170,6 +192,7 @@ public:
 protected:
   virtual CPT(qpGeomPrimitive) decompose_impl() const;
   virtual CPTA_ushort rotate_impl() const;
+  virtual void append_unused_vertices(PTA_ushort &vertices, int vertex);
 
 protected:
   static PStatCollector _rotate_pcollector;
@@ -195,7 +218,6 @@ private:
     CPT(qpGeomPrimitive) _decomposed;
   };
     
-
   // This is the data that must be cycled between pipeline stages.
   class EXPCL_PANDA CData : public CycleData {
   public:

+ 3 - 3
panda/src/gobj/qpgeomTriangles.cxx

@@ -56,7 +56,7 @@ qpGeomTriangles::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomTriangles::make_copy
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 PT(qpGeomPrimitive) qpGeomTriangles::
@@ -66,7 +66,7 @@ make_copy() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomTriangles::get_primitive_type
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: Returns the fundamental rendering type of this
 //               primitive: whether it is points, lines, or polygons.
 //               This is used primarily to set up the appropriate
@@ -80,7 +80,7 @@ get_primitive_type() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomTriangles::get_num_vertices_per_primitive
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: If the primitive type is a simple type in which all
 //               primitives have the same number of vertices, like
 //               triangles, returns the number of vertices per

+ 1 - 0
panda/src/gobj/qpgeomTriangles.h

@@ -34,6 +34,7 @@ PUBLISHED:
   qpGeomTriangles(const qpGeomTriangles &copy);
   virtual ~qpGeomTriangles();
 
+public:
   virtual PT(qpGeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
 

+ 2 - 2
panda/src/gobj/qpgeomTrifans.cxx

@@ -56,7 +56,7 @@ qpGeomTrifans::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomTrifans::make_copy
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 PT(qpGeomPrimitive) qpGeomTrifans::
@@ -66,7 +66,7 @@ make_copy() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomTrifans::get_primitive_type
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: Returns the fundamental rendering type of this
 //               primitive: whether it is points, lines, or polygons.
 //               This is used primarily to set up the appropriate

+ 1 - 0
panda/src/gobj/qpgeomTrifans.h

@@ -34,6 +34,7 @@ PUBLISHED:
   qpGeomTrifans(const qpGeomTrifans &copy);
   virtual ~qpGeomTrifans();
 
+public:
   virtual PT(qpGeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
 

+ 44 - 4
panda/src/gobj/qpgeomTristrips.cxx

@@ -57,7 +57,7 @@ qpGeomTristrips::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomTristrips::make_copy
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 PT(qpGeomPrimitive) qpGeomTristrips::
@@ -67,7 +67,7 @@ make_copy() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomTristrips::get_primitive_type
-//       Access: Published, Virtual
+//       Access: Public, Virtual
 //  Description: Returns the fundamental rendering type of this
 //               primitive: whether it is points, lines, or polygons.
 //               This is used primarily to set up the appropriate
@@ -79,6 +79,20 @@ get_primitive_type() const {
   return PT_polygons;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomTristrips::get_num_unused_vertices_per_primitive
+//       Access: Public, Virtual
+//  Description: Returns the number of vertices that are added between
+//               primitives that aren't, strictly speaking, part of
+//               the primitives themselves.  This is used, for
+//               instance, to define degenerate triangles to connect
+//               otherwise disconnected triangle strips.
+////////////////////////////////////////////////////////////////////
+int qpGeomTristrips::
+get_num_unused_vertices_per_primitive() const {
+  return 2;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomTristrips::draw
 //       Access: Public, Virtual
@@ -118,9 +132,11 @@ decompose_impl() const {
   if (get_shade_model() == SM_flat_first_vertex) {
     // Preserve the first vertex of each component triangle as the
     // first vertex of each generated triangle.
-    int vi = 0;
+    int vi = -2;
     int li = 0;
     while (li < (int)ends.size()) {
+      // Skip unused vertices between tristrips.
+      vi += 2;
       int end = ends[li];
       nassertr(vi + 2 <= end, triangles.p());
       nassertr(vi < (int)vertices.size(), this);
@@ -154,9 +170,11 @@ decompose_impl() const {
   } else {
     // Preserve the last vertex of each component triangle as the
     // last vertex of each generated triangle.
-    int vi = 0;
+    int vi = -2;
     int li = 0;
     while (li < (int)ends.size()) {
+      // Skip unused vertices between tristrips.
+      vi += 2;
       int end = ends[li];
       nassertr(vi + 2 <= end, triangles.p());
       nassertr(vi < (int)vertices.size(), this);
@@ -216,6 +234,13 @@ rotate_impl() const {
     int end = (*ei);
     int num_vertices = end - begin;
 
+    if (begin != 0) {
+      // Copy in the unused vertices between tristrips.
+      new_vertices.push_back(new_vertices.back());
+      new_vertices.push_back(vertices[end - 1]);
+      begin += 2;
+    }
+
     if ((num_vertices & 1) == 0) {
       for (int vi = end - 1; vi >= begin; --vi) {
         new_vertices.push_back(vertices[vi]);
@@ -235,6 +260,21 @@ rotate_impl() const {
   return new_vertices;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomTristrips::append_unused_vertices
+//       Access: Protected, Virtual
+//  Description: Called when a new primitive is begun (other than the
+//               first primitive), this should add some degenerate
+//               vertices between primitives, if the primitive type
+//               requires that.  The second parameter is the first
+//               vertex that begins the new primitive.
+////////////////////////////////////////////////////////////////////
+void qpGeomTristrips::
+append_unused_vertices(PTA_ushort &vertices, int vertex) {
+  vertices.push_back(vertices.back());
+  vertices.push_back(vertex);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomTristrips::register_with_read_factory
 //       Access: Public, Static

+ 3 - 0
panda/src/gobj/qpgeomTristrips.h

@@ -34,8 +34,10 @@ PUBLISHED:
   qpGeomTristrips(const qpGeomTristrips &copy);
   virtual ~qpGeomTristrips();
 
+public:
   virtual PT(qpGeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
+  virtual int get_num_unused_vertices_per_primitive() const;
 
 public:
   virtual void draw(GraphicsStateGuardianBase *gsg) const;
@@ -43,6 +45,7 @@ public:
 protected:
   virtual CPT(qpGeomPrimitive) decompose_impl() const;
   virtual CPTA_ushort rotate_impl() const;
+  virtual void append_unused_vertices(PTA_ushort &vertices, int vertex);
 
 public:
   static void register_with_read_factory();