Browse Source

Add support for geometry with adjacency information

Example code: https://gist.github.com/rdb/7cebb8941b962c59d5a092048efb3855
rdb 7 years ago
parent
commit
9638eb47eb

+ 33 - 0
panda/src/display/graphicsStateGuardian.cxx

@@ -2381,6 +2381,15 @@ draw_triangles(const GeomPrimitivePipelineReader *, bool) {
   return false;
   return false;
 }
 }
 
 
+
+/**
+ * Draws a series of disconnected triangles with adjacency information.
+ */
+bool GraphicsStateGuardian::
+draw_triangles_adj(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
 /**
 /**
  * Draws a series of triangle strips.
  * Draws a series of triangle strips.
  */
  */
@@ -2389,6 +2398,14 @@ draw_tristrips(const GeomPrimitivePipelineReader *, bool) {
   return false;
   return false;
 }
 }
 
 
+/**
+ * Draws a series of triangle strips with adjacency information.
+ */
+bool GraphicsStateGuardian::
+draw_tristrips_adj(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
 /**
 /**
  * Draws a series of triangle fans.
  * Draws a series of triangle fans.
  */
  */
@@ -2414,6 +2431,14 @@ draw_lines(const GeomPrimitivePipelineReader *, bool) {
   return false;
   return false;
 }
 }
 
 
+/**
+ * Draws a series of disconnected line segments with adjacency information.
+ */
+bool GraphicsStateGuardian::
+draw_lines_adj(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
 /**
 /**
  * Draws a series of line strips.
  * Draws a series of line strips.
  */
  */
@@ -2422,6 +2447,14 @@ draw_linestrips(const GeomPrimitivePipelineReader *, bool) {
   return false;
   return false;
 }
 }
 
 
+/**
+ * Draws a series of line strips with adjacency information.
+ */
+bool GraphicsStateGuardian::
+draw_linestrips_adj(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
 /**
 /**
  * Draws a series of disconnected points.
  * Draws a series of disconnected points.
  */
  */

+ 8 - 0
panda/src/display/graphicsStateGuardian.h

@@ -371,16 +371,24 @@ public:
                                      bool force);
                                      bool force);
   virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader,
                               bool force);
                               bool force);
+  virtual bool draw_triangles_adj(const GeomPrimitivePipelineReader *reader,
+                                  bool force);
   virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader,
                               bool force);
                               bool force);
+  virtual bool draw_tristrips_adj(const GeomPrimitivePipelineReader *reader,
+                                  bool force);
   virtual bool draw_trifans(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_trifans(const GeomPrimitivePipelineReader *reader,
                             bool force);
                             bool force);
   virtual bool draw_patches(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_patches(const GeomPrimitivePipelineReader *reader,
                             bool force);
                             bool force);
   virtual bool draw_lines(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_lines(const GeomPrimitivePipelineReader *reader,
                           bool force);
                           bool force);
+  virtual bool draw_lines_adj(const GeomPrimitivePipelineReader *reader,
+                              bool force);
   virtual bool draw_linestrips(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_linestrips(const GeomPrimitivePipelineReader *reader,
                                bool force);
                                bool force);
+  virtual bool draw_linestrips_adj(const GeomPrimitivePipelineReader *reader,
+                                   bool force);
   virtual bool draw_points(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_points(const GeomPrimitivePipelineReader *reader,
                            bool force);
                            bool force);
   virtual void end_draw_primitives();
   virtual void end_draw_primitives();

+ 361 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -653,6 +653,12 @@ reset() {
     Geom::GR_line_strip |
     Geom::GR_line_strip |
     Geom::GR_flat_last_vertex;
     Geom::GR_flat_last_vertex;
 
 
+#ifndef OPENGLES
+  if (_supports_geometry_shaders) {
+    _supported_geom_rendering |= Geom::GR_adjacency;
+  }
+#endif
+
   _supports_point_parameters = false;
   _supports_point_parameters = false;
 
 
 #ifdef OPENGLES_1
 #ifdef OPENGLES_1
@@ -4543,6 +4549,68 @@ draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) {
   return true;
   return true;
 }
 }
 
 
+/**
+ * Draws a series of disconnected triangles with adjacency information.
+ */
+#ifndef OPENGLES
+bool CLP(GraphicsStateGuardian)::
+draw_triangles_adj(const GeomPrimitivePipelineReader *reader, bool force) {
+  // PStatGPUTimer timer(this, _draw_primitive_pcollector,
+  // reader->get_current_thread());
+
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam() << "draw_triangles_adj: " << *(reader->get_object()) << "\n";
+  }
+#endif  // NDEBUG
+
+#ifdef SUPPORT_IMMEDIATE_MODE
+  if (_use_sender) {
+    draw_immediate_simple_primitives(reader, GL_TRIANGLES_ADJACENCY);
+
+  } else
+#endif  // SUPPORT_IMMEDIATE_MODE
+  {
+    int num_vertices = reader->get_num_vertices();
+    _vertices_tri_pcollector.add_level(num_vertices);
+    _primitive_batches_tri_pcollector.add_level(1);
+
+    if (reader->is_indexed()) {
+      const unsigned char *client_pointer;
+      if (!setup_primitive(client_pointer, reader, force)) {
+        return false;
+      }
+
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawElementsInstanced(GL_TRIANGLES_ADJACENCY, num_vertices,
+                                 get_numeric_type(reader->get_index_type()),
+                                 client_pointer, _instance_count);
+      } else {
+        _glDrawRangeElements(GL_TRIANGLES_ADJACENCY,
+                             reader->get_min_vertex(),
+                             reader->get_max_vertex(),
+                             num_vertices,
+                             get_numeric_type(reader->get_index_type()),
+                             client_pointer);
+      }
+    } else {
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawArraysInstanced(GL_TRIANGLES_ADJACENCY,
+                               reader->get_first_vertex(),
+                               num_vertices, _instance_count);
+      } else {
+        glDrawArrays(GL_TRIANGLES_ADJACENCY,
+                     reader->get_first_vertex(),
+                     num_vertices);
+      }
+    }
+  }
+
+  report_my_gl_errors();
+  return true;
+}
+#endif  // OPENGLES
+
 /**
 /**
  * Draws a series of triangle strips.
  * Draws a series of triangle strips.
  */
  */
@@ -4669,6 +4737,128 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
   return true;
   return true;
 }
 }
 
 
+/**
+ * Draws a series of triangle strips with adjacency information.
+ */
+#ifndef OPENGLES
+bool CLP(GraphicsStateGuardian)::
+draw_tristrips_adj(const GeomPrimitivePipelineReader *reader, bool force) {
+  // PStatGPUTimer timer(this, _draw_primitive_pcollector,
+  // reader->get_current_thread());
+
+  report_my_gl_errors();
+
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam() << "draw_tristrips_adj: " << *(reader->get_object()) << "\n";
+  }
+#endif  // NDEBUG
+
+#ifdef SUPPORT_IMMEDIATE_MODE
+  if (_use_sender) {
+    draw_immediate_composite_primitives(reader, GL_TRIANGLE_STRIP_ADJACENCY);
+
+  } else
+#endif  // SUPPORT_IMMEDIATE_MODE
+  {
+    if (reader->is_indexed() &&
+        (_supported_geom_rendering & GeomEnums::GR_strip_cut_index) != 0) {
+      // One long line strip, connected by strip cut indices.
+      if (_explicit_primitive_restart) {
+        glEnable(GL_PRIMITIVE_RESTART);
+        _glPrimitiveRestartIndex(reader->get_strip_cut_index());
+      }
+      int num_vertices = reader->get_num_vertices();
+      _vertices_tristrip_pcollector.add_level(num_vertices);
+      _primitive_batches_tristrip_pcollector.add_level(1);
+      if (reader->is_indexed()) {
+        const unsigned char *client_pointer;
+        if (!setup_primitive(client_pointer, reader, force)) {
+          return false;
+        }
+        if (_supports_geometry_instancing && _instance_count > 0) {
+          _glDrawElementsInstanced(GL_TRIANGLE_STRIP_ADJACENCY, num_vertices,
+                                   get_numeric_type(reader->get_index_type()),
+                                   client_pointer, _instance_count);
+        } else {
+          _glDrawRangeElements(GL_TRIANGLE_STRIP_ADJACENCY,
+                               reader->get_min_vertex(),
+                               reader->get_max_vertex(),
+                               num_vertices,
+                               get_numeric_type(reader->get_index_type()),
+                               client_pointer);
+        }
+      } else {
+        if (_supports_geometry_instancing && _instance_count > 0) {
+          _glDrawArraysInstanced(GL_TRIANGLE_STRIP_ADJACENCY,
+                                 reader->get_first_vertex(),
+                                 num_vertices, _instance_count);
+        } else {
+          glDrawArrays(GL_TRIANGLE_STRIP_ADJACENCY,
+                          reader->get_first_vertex(),
+                          num_vertices);
+        }
+      }
+      if (_explicit_primitive_restart) {
+        glDisable(GL_PRIMITIVE_RESTART);
+      }
+    } else {
+      // Send the individual triangle strips, stepping over the degenerate
+      // vertices.
+      CPTA_int ends = reader->get_ends();
+
+      _primitive_batches_tristrip_pcollector.add_level(ends.size());
+      if (reader->is_indexed()) {
+        const unsigned char *client_pointer;
+        if (!setup_primitive(client_pointer, reader, force)) {
+          return false;
+        }
+        int index_stride = reader->get_index_stride();
+        GeomVertexReader mins(reader->get_mins(), 0);
+        GeomVertexReader maxs(reader->get_maxs(), 0);
+        nassertr(reader->get_mins()->get_num_rows() == (int)ends.size() &&
+                 reader->get_maxs()->get_num_rows() == (int)ends.size(), false);
+
+        unsigned int start = 0;
+        for (size_t i = 0; i < ends.size(); i++) {
+          _vertices_tristrip_pcollector.add_level(ends[i] - start);
+          if (_supports_geometry_instancing && _instance_count > 0) {
+            _glDrawElementsInstanced(GL_TRIANGLE_STRIP_ADJACENCY, ends[i] - start,
+                                     get_numeric_type(reader->get_index_type()),
+                                     client_pointer + start * index_stride,
+                                     _instance_count);
+          } else {
+            _glDrawRangeElements(GL_TRIANGLE_STRIP_ADJACENCY,
+                                 mins.get_data1i(), maxs.get_data1i(),
+                                 ends[i] - start,
+                                 get_numeric_type(reader->get_index_type()),
+                                 client_pointer + start * index_stride);
+          }
+          start = ends[i] + 1;
+        }
+      } else {
+        unsigned int start = 0;
+        int first_vertex = reader->get_first_vertex();
+        for (size_t i = 0; i < ends.size(); i++) {
+          _vertices_tristrip_pcollector.add_level(ends[i] - start);
+          if (_supports_geometry_instancing && _instance_count > 0) {
+            _glDrawArraysInstanced(GL_TRIANGLE_STRIP_ADJACENCY, first_vertex + start,
+                                   ends[i] - start, _instance_count);
+          } else {
+            glDrawArrays(GL_TRIANGLE_STRIP_ADJACENCY, first_vertex + start,
+                         ends[i] - start);
+          }
+          start = ends[i] + 1;
+        }
+      }
+    }
+  }
+
+  report_my_gl_errors();
+  return true;
+}
+#endif  // OPENGLES
+
 /**
 /**
  * Draws a series of triangle fans.
  * Draws a series of triangle fans.
  */
  */
@@ -4888,6 +5078,66 @@ draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
   return true;
   return true;
 }
 }
 
 
+/**
+ * Draws a series of disconnected line segments with adjacency information.
+ */
+#ifndef OPENGLES
+bool CLP(GraphicsStateGuardian)::
+draw_lines_adj(const GeomPrimitivePipelineReader *reader, bool force) {
+  // PStatGPUTimer timer(this, _draw_primitive_pcollector,
+  // reader->get_current_thread());
+
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam() << "draw_lines_adj: " << *(reader->get_object()) << "\n";
+  }
+#endif  // NDEBUG
+
+#ifdef SUPPORT_IMMEDIATE_MODE
+  if (_use_sender) {
+    draw_immediate_simple_primitives(reader, GL_LINES_ADJACENCY);
+  } else
+#endif  // SUPPORT_IMMEDIATE_MODE
+  {
+    int num_vertices = reader->get_num_vertices();
+    _vertices_other_pcollector.add_level(num_vertices);
+    _primitive_batches_other_pcollector.add_level(1);
+
+    if (reader->is_indexed()) {
+      const unsigned char *client_pointer;
+      if (!setup_primitive(client_pointer, reader, force)) {
+        return false;
+      }
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawElementsInstanced(GL_LINES_ADJACENCY, num_vertices,
+                                 get_numeric_type(reader->get_index_type()),
+                                 client_pointer, _instance_count);
+      } else {
+        _glDrawRangeElements(GL_LINES_ADJACENCY,
+                             reader->get_min_vertex(),
+                             reader->get_max_vertex(),
+                             num_vertices,
+                             get_numeric_type(reader->get_index_type()),
+                             client_pointer);
+      }
+    } else {
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawArraysInstanced(GL_LINES_ADJACENCY,
+                               reader->get_first_vertex(),
+                               num_vertices, _instance_count);
+      } else {
+        glDrawArrays(GL_LINES_ADJACENCY,
+                     reader->get_first_vertex(),
+                     num_vertices);
+      }
+    }
+  }
+
+  report_my_gl_errors();
+  return true;
+}
+#endif  // OPENGLES
+
 /**
 /**
  * Draws a series of line strips.
  * Draws a series of line strips.
  */
  */
@@ -5010,6 +5260,117 @@ draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force) {
   return true;
   return true;
 }
 }
 
 
+/**
+ * Draws a series of line strips with adjacency information.
+ */
+#ifndef OPENGLES
+bool CLP(GraphicsStateGuardian)::
+draw_linestrips_adj(const GeomPrimitivePipelineReader *reader, bool force) {
+  // PStatGPUTimer timer(this, _draw_primitive_pcollector,
+  // reader->get_current_thread());
+
+  report_my_gl_errors();
+
+#ifndef NDEBUG
+  if (GLCAT.is_spam()) {
+    GLCAT.spam() << "draw_linestrips_adj: " << *(reader->get_object()) << "\n";
+  }
+#endif  // NDEBUG
+
+#ifdef SUPPORT_IMMEDIATE_MODE
+  if (_use_sender) {
+    draw_immediate_composite_primitives(reader, GL_LINE_STRIP_ADJACENCY);
+
+  } else
+#endif  // SUPPORT_IMMEDIATE_MODE
+  {
+    if (reader->is_indexed() &&
+        (_supported_geom_rendering & GeomEnums::GR_strip_cut_index) != 0) {
+      // One long line strip, connected by strip cut indices.
+      if (_explicit_primitive_restart) {
+        glEnable(GL_PRIMITIVE_RESTART);
+        _glPrimitiveRestartIndex(reader->get_strip_cut_index());
+      }
+
+      int num_vertices = reader->get_num_vertices();
+      _vertices_other_pcollector.add_level(num_vertices);
+      _primitive_batches_other_pcollector.add_level(1);
+
+      const unsigned char *client_pointer;
+      if (!setup_primitive(client_pointer, reader, force)) {
+        return false;
+      }
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawElementsInstanced(GL_LINE_STRIP_ADJACENCY, num_vertices,
+                                 get_numeric_type(reader->get_index_type()),
+                                 client_pointer, _instance_count);
+      } else {
+        _glDrawRangeElements(GL_LINE_STRIP_ADJACENCY,
+                             reader->get_min_vertex(),
+                             reader->get_max_vertex(),
+                             num_vertices,
+                             get_numeric_type(reader->get_index_type()),
+                             client_pointer);
+      }
+
+      if (_explicit_primitive_restart) {
+        glDisable(GL_PRIMITIVE_RESTART);
+      }
+    } else {
+      // Send the individual line strips, stepping over the strip-cut indices.
+      CPTA_int ends = reader->get_ends();
+
+      _primitive_batches_other_pcollector.add_level(ends.size());
+      if (reader->is_indexed()) {
+        const unsigned char *client_pointer;
+        if (!setup_primitive(client_pointer, reader, force)) {
+          return false;
+        }
+        int index_stride = reader->get_index_stride();
+        GeomVertexReader mins(reader->get_mins(), 0);
+        GeomVertexReader maxs(reader->get_maxs(), 0);
+        nassertr(reader->get_mins()->get_num_rows() == (int)ends.size() &&
+                 reader->get_maxs()->get_num_rows() == (int)ends.size(), false);
+
+        unsigned int start = 0;
+        for (size_t i = 0; i < ends.size(); i++) {
+          _vertices_other_pcollector.add_level(ends[i] - start);
+          if (_supports_geometry_instancing && _instance_count > 0) {
+            _glDrawElementsInstanced(GL_LINE_STRIP_ADJACENCY, ends[i] - start,
+                                     get_numeric_type(reader->get_index_type()),
+                                     client_pointer + start * index_stride,
+                                     _instance_count);
+          } else {
+            _glDrawRangeElements(GL_LINE_STRIP_ADJACENCY,
+                                 mins.get_data1i(), maxs.get_data1i(),
+                                 ends[i] - start,
+                                 get_numeric_type(reader->get_index_type()),
+                                 client_pointer + start * index_stride);
+          }
+          start = ends[i] + 1;
+        }
+      } else {
+        unsigned int start = 0;
+        int first_vertex = reader->get_first_vertex();
+        for (size_t i = 0; i < ends.size(); i++) {
+          _vertices_other_pcollector.add_level(ends[i] - start);
+          if (_supports_geometry_instancing && _instance_count > 0) {
+            _glDrawArraysInstanced(GL_LINE_STRIP_ADJACENCY, first_vertex + start,
+                                   ends[i] - start, _instance_count);
+          } else {
+            glDrawArrays(GL_LINE_STRIP_ADJACENCY, first_vertex + start, ends[i] - start);
+          }
+          start = ends[i] + 1;
+        }
+      }
+    }
+  }
+
+  report_my_gl_errors();
+  return true;
+}
+#endif  // OPENGLES
+
 /**
 /**
  * Draws a series of disconnected points.
  * Draws a series of disconnected points.
  */
  */

+ 16 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -288,16 +288,32 @@ public:
                                      bool force);
                                      bool force);
   virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader,
                               bool force);
                               bool force);
+#ifndef OPENGLES
+  virtual bool draw_triangles_adj(const GeomPrimitivePipelineReader *reader,
+                                  bool force);
+#endif
   virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader,
                               bool force);
                               bool force);
+#ifndef OPENGLES
+  virtual bool draw_tristrips_adj(const GeomPrimitivePipelineReader *reader,
+                                  bool force);
+#endif
   virtual bool draw_trifans(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_trifans(const GeomPrimitivePipelineReader *reader,
                             bool force);
                             bool force);
   virtual bool draw_patches(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_patches(const GeomPrimitivePipelineReader *reader,
                             bool force);
                             bool force);
   virtual bool draw_lines(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_lines(const GeomPrimitivePipelineReader *reader,
                           bool force);
                           bool force);
+#ifndef OPENGLES
+  virtual bool draw_lines_adj(const GeomPrimitivePipelineReader *reader,
+                              bool force);
+#endif
   virtual bool draw_linestrips(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_linestrips(const GeomPrimitivePipelineReader *reader,
                                bool force);
                                bool force);
+#ifndef OPENGLES
+  virtual bool draw_linestrips_adj(const GeomPrimitivePipelineReader *reader,
+                                   bool force);
+#endif
   virtual bool draw_points(const GeomPrimitivePipelineReader *reader,
   virtual bool draw_points(const GeomPrimitivePipelineReader *reader,
                            bool force);
                            bool force);
   virtual void end_draw_primitives();
   virtual void end_draw_primitives();

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

@@ -20,11 +20,15 @@
 #include "geomMunger.h"
 #include "geomMunger.h"
 #include "geomPrimitive.h"
 #include "geomPrimitive.h"
 #include "geomTriangles.h"
 #include "geomTriangles.h"
+#include "geomTrianglesAdjacency.h"
 #include "geomTristrips.h"
 #include "geomTristrips.h"
+#include "geomTristripsAdjacency.h"
 #include "geomTrifans.h"
 #include "geomTrifans.h"
 #include "geomPatches.h"
 #include "geomPatches.h"
 #include "geomLines.h"
 #include "geomLines.h"
+#include "geomLinesAdjacency.h"
 #include "geomLinestrips.h"
 #include "geomLinestrips.h"
+#include "geomLinestripsAdjacency.h"
 #include "geomPoints.h"
 #include "geomPoints.h"
 #include "geomVertexArrayData.h"
 #include "geomVertexArrayData.h"
 #include "geomVertexArrayFormat.h"
 #include "geomVertexArrayFormat.h"
@@ -546,14 +550,18 @@ ConfigureFn(config_gobj) {
   GeomPipelineReader::init_type();
   GeomPipelineReader::init_type();
   GeomContext::init_type();
   GeomContext::init_type();
   GeomLines::init_type();
   GeomLines::init_type();
+  GeomLinesAdjacency::init_type();
   GeomLinestrips::init_type();
   GeomLinestrips::init_type();
+  GeomLinestripsAdjacency::init_type();
   GeomMunger::init_type();
   GeomMunger::init_type();
   GeomPoints::init_type();
   GeomPoints::init_type();
   GeomPrimitive::init_type();
   GeomPrimitive::init_type();
   GeomPrimitivePipelineReader::init_type();
   GeomPrimitivePipelineReader::init_type();
   GeomTriangles::init_type();
   GeomTriangles::init_type();
+  GeomTrianglesAdjacency::init_type();
   GeomTrifans::init_type();
   GeomTrifans::init_type();
   GeomTristrips::init_type();
   GeomTristrips::init_type();
+  GeomTristripsAdjacency::init_type();
   GeomPatches::init_type();
   GeomPatches::init_type();
   GeomVertexArrayData::init_type();
   GeomVertexArrayData::init_type();
   GeomVertexArrayDataHandle::init_type();
   GeomVertexArrayDataHandle::init_type();
@@ -601,11 +609,15 @@ ConfigureFn(config_gobj) {
   // factory
   // factory
   Geom::register_with_read_factory();
   Geom::register_with_read_factory();
   GeomLines::register_with_read_factory();
   GeomLines::register_with_read_factory();
+  GeomLinesAdjacency::register_with_read_factory();
   GeomLinestrips::register_with_read_factory();
   GeomLinestrips::register_with_read_factory();
+  GeomLinestripsAdjacency::register_with_read_factory();
   GeomPoints::register_with_read_factory();
   GeomPoints::register_with_read_factory();
   GeomTriangles::register_with_read_factory();
   GeomTriangles::register_with_read_factory();
+  GeomTrianglesAdjacency::register_with_read_factory();
   GeomTrifans::register_with_read_factory();
   GeomTrifans::register_with_read_factory();
   GeomTristrips::register_with_read_factory();
   GeomTristrips::register_with_read_factory();
+  GeomTristripsAdjacency::register_with_read_factory();
   GeomPatches::register_with_read_factory();
   GeomPatches::register_with_read_factory();
   GeomVertexArrayData::register_with_read_factory();
   GeomVertexArrayData::register_with_read_factory();
   GeomVertexArrayFormat::register_with_read_factory();
   GeomVertexArrayFormat::register_with_read_factory();

+ 11 - 0
panda/src/gobj/geom.I

@@ -212,6 +212,17 @@ make_patches() const {
   return new_geom;
   return new_geom;
 }
 }
 
 
+/**
+ * Returns a new Geom with each primitive converted into a corresponding
+ * version with adjacency information.
+ */
+INLINE PT(Geom) Geom::
+make_adjacency() const {
+  PT(Geom) new_geom = make_copy();
+  new_geom->make_adjacency_in_place();
+  return new_geom;
+}
+
 /**
 /**
  * Returns a sequence number which is guaranteed to change at least every time
  * Returns a sequence number which is guaranteed to change at least every time
  * any of the primitives in the Geom is modified, or the set of primitives is
  * any of the primitives in the Geom is modified, or the set of primitives is

+ 36 - 0
panda/src/gobj/geom.cxx

@@ -834,6 +834,42 @@ make_patches_in_place() {
   nassertv(all_is_valid);
   nassertv(all_is_valid);
 }
 }
 
 
+/**
+ * Replaces the GeomPrimitives within this Geom with corresponding versions
+ * with adjacency information.  See GeomPrimitive::make_adjacency().
+ *
+ * Don't call this in a downstream thread unless you don't mind it blowing
+ * away other changes you might have recently made in an upstream thread.
+ */
+void Geom::
+make_adjacency_in_place() {
+  Thread *current_thread = Thread::get_current_thread();
+  CDWriter cdata(_cycler, true, current_thread);
+
+#ifndef NDEBUG
+  bool all_is_valid = true;
+#endif
+  Primitives::iterator pi;
+  for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
+    CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->make_adjacency();
+    if (new_prim != nullptr) {
+      (*pi) = (GeomPrimitive *)new_prim.p();
+
+#ifndef NDEBUG
+      if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) {
+        all_is_valid = false;
+      }
+#endif
+    }
+  }
+
+  cdata->_modified = Geom::get_next_modified();
+  reset_geom_rendering(cdata);
+  clear_cache_stage(current_thread);
+
+  nassertv(all_is_valid);
+}
+
 /**
 /**
  * Copies the primitives from the indicated Geom into this one.  This does
  * Copies the primitives from the indicated Geom into this one.  This does
  * require that both Geoms contain the same fundamental type primitives, both
  * require that both Geoms contain the same fundamental type primitives, both

+ 2 - 0
panda/src/gobj/geom.h

@@ -106,6 +106,7 @@ PUBLISHED:
   INLINE PT(Geom) make_points() const;
   INLINE PT(Geom) make_points() const;
   INLINE PT(Geom) make_lines() const;
   INLINE PT(Geom) make_lines() const;
   INLINE PT(Geom) make_patches() const;
   INLINE PT(Geom) make_patches() const;
+  INLINE PT(Geom) make_adjacency() const;
 
 
   void decompose_in_place();
   void decompose_in_place();
   void doubleside_in_place();
   void doubleside_in_place();
@@ -115,6 +116,7 @@ PUBLISHED:
   void make_points_in_place();
   void make_points_in_place();
   void make_lines_in_place();
   void make_lines_in_place();
   void make_patches_in_place();
   void make_patches_in_place();
+  void make_adjacency_in_place();
 
 
   virtual bool copy_primitives_from(const Geom *other);
   virtual bool copy_primitives_from(const Geom *other);
 
 

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

@@ -132,6 +132,9 @@ PUBLISHED:
     // If a particular non-fill polygon mode is used.
     // If a particular non-fill polygon mode is used.
     GR_render_mode_wireframe= 0x40000,
     GR_render_mode_wireframe= 0x40000,
     GR_render_mode_point    = 0x80000,
     GR_render_mode_point    = 0x80000,
+
+    // The primitive has adjacency information.
+    GR_adjacency            = 0x100000,
   };
   };
 
 
   // The shade model specifies whether the per-vertex colors and normals
   // The shade model specifies whether the per-vertex colors and normals

+ 62 - 0
panda/src/gobj/geomLines.cxx

@@ -18,6 +18,7 @@
 #include "graphicsStateGuardianBase.h"
 #include "graphicsStateGuardianBase.h"
 #include "geomVertexReader.h"
 #include "geomVertexReader.h"
 #include "geomVertexWriter.h"
 #include "geomVertexWriter.h"
+#include "geomLinesAdjacency.h"
 
 
 TypeHandle GeomLines::_type_handle;
 TypeHandle GeomLines::_type_handle;
 
 
@@ -67,6 +68,67 @@ get_primitive_type() const {
   return PT_lines;
   return PT_lines;
 }
 }
 
 
+/**
+ * Adds adjacency information to this primitive.  May return null if this type
+ * of geometry does not support adjacency information.
+ */
+CPT(GeomPrimitive) GeomLines::
+make_adjacency() const {
+ Thread *current_thread = Thread::get_current_thread();
+  PT(GeomLinesAdjacency) adj = new GeomLinesAdjacency(get_usage_hint());
+
+  GeomPrimitivePipelineReader from(this, current_thread);
+  int num_vertices = from.get_num_vertices();
+
+  PT(GeomVertexArrayData) new_vertices = adj->make_index_data();
+  new_vertices->set_num_rows(num_vertices * 2);
+
+  // First, build a map of each vertex to its next vertex, and another map
+  // doing the exact reverse.  Note that this only considers lines that are
+  // ordered the same way as being connected; we may need to change this.
+  map<int, int> forward_map, reverse_map;
+  for (int i = 0; i < num_vertices; i += 2) {
+    int v0 = from.get_vertex(i);
+    int v1 = from.get_vertex(i + 1);
+    forward_map[v0] = v1;
+    reverse_map[v1] = v0;
+  }
+
+  // Now build up the new vertex data.  For each line, we insert the
+  // appropriate connecting vertex.
+  {
+    GeomVertexWriter to(new_vertices, 0);
+    for (int i = 0; i < num_vertices; i += 2) {
+      int v0 = from.get_vertex(i);
+      int v1 = from.get_vertex(i + 1);
+
+      auto it = reverse_map.find(v0);
+      if (it != reverse_map.end()) {
+        to.set_data1i(it->second);
+      } else {
+        // Um, no adjoining line segment?  Just repeat the vertex, I guess.
+        to.set_data1i(v0);
+      }
+
+      to.set_data1i(v0);
+      to.set_data1i(v1);
+
+      // Do the same for the second vertex in the line.
+      it = forward_map.find(v1);
+      if (it != forward_map.end()) {
+        to.set_data1i(it->second);
+      } else {
+        to.set_data1i(v1);
+      }
+    }
+
+    nassertr(to.is_at_end(), nullptr);
+  }
+
+  adj->set_vertices(move(new_vertices));
+  return adj.p();
+}
+
 /**
 /**
  * If the primitive type is a simple type in which all primitives have the
  * 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
  * same number of vertices, like lines, returns the number of vertices per

+ 2 - 0
panda/src/gobj/geomLines.h

@@ -31,6 +31,8 @@ public:
   virtual PT(GeomPrimitive) make_copy() const;
   virtual PT(GeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
   virtual PrimitiveType get_primitive_type() const;
 
 
+  CPT(GeomPrimitive) make_adjacency() const;
+
   virtual int get_num_vertices_per_primitive() const;
   virtual int get_num_vertices_per_primitive() const;
   virtual int get_min_num_vertices_per_primitive() const;
   virtual int get_min_num_vertices_per_primitive() const;
 
 

+ 132 - 0
panda/src/gobj/geomLinesAdjacency.cxx

@@ -0,0 +1,132 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file geomLinesAdjacency.cxx
+ * @author rdb
+ * @date 2018-03-01
+ */
+
+#include "geomLinesAdjacency.h"
+#include "pStatTimer.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "graphicsStateGuardianBase.h"
+#include "geomVertexReader.h"
+#include "geomVertexWriter.h"
+
+TypeHandle GeomLinesAdjacency::_type_handle;
+
+/**
+ *
+ */
+GeomLinesAdjacency::
+GeomLinesAdjacency(GeomEnums::UsageHint usage_hint) :
+  GeomPrimitive(usage_hint)
+{
+}
+
+/**
+ *
+ */
+GeomLinesAdjacency::
+GeomLinesAdjacency(const GeomLinesAdjacency &copy) :
+  GeomPrimitive(copy)
+{
+}
+
+/**
+ *
+ */
+GeomLinesAdjacency::
+~GeomLinesAdjacency() {
+}
+
+/**
+ *
+ */
+PT(GeomPrimitive) GeomLinesAdjacency::
+make_copy() const {
+  return new GeomLinesAdjacency(*this);
+}
+
+/**
+ * Returns the fundamental rendering type of this primitive: whether it is
+ * points, lines, or polygons.
+ *
+ * This is used to set up the appropriate antialiasing settings when
+ * AntialiasAttrib::M_auto is in effect; it also implies the type of primitive
+ * that will be produced when decompose() is called.
+ */
+GeomPrimitive::PrimitiveType GeomLinesAdjacency::
+get_primitive_type() const {
+  return PT_lines;
+}
+
+/**
+ * Returns the set of GeomRendering bits that represent the rendering
+ * properties required to properly render this primitive.
+ */
+int GeomLinesAdjacency::
+get_geom_rendering() const {
+  return GeomPrimitive::get_geom_rendering() | GR_adjacency;
+}
+
+/**
+ * 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
+ * primitive.  If the primitive type is a more complex type in which different
+ * primitives might have different numbers of vertices, for instance a line
+ * strip, returns 0.
+ */
+int GeomLinesAdjacency::
+get_num_vertices_per_primitive() const {
+  return 4;
+}
+
+/**
+ * Returns the minimum number of vertices that must be added before
+ * close_primitive() may legally be called.
+ */
+int GeomLinesAdjacency::
+get_min_num_vertices_per_primitive() const {
+  return 4;
+}
+
+/**
+ * Calls the appropriate method on the GSG to draw the primitive.
+ */
+bool GeomLinesAdjacency::
+draw(GraphicsStateGuardianBase *gsg, const GeomPrimitivePipelineReader *reader,
+     bool force) const {
+  return gsg->draw_lines_adj(reader, force);
+}
+
+/**
+ * Tells the BamReader how to create objects of type Geom.
+ */
+void GeomLinesAdjacency::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type Geom is encountered in the Bam file.  It should create the Geom and
+ * extract its information from the file.
+ */
+TypedWritable *GeomLinesAdjacency::
+make_from_bam(const FactoryParams &params) {
+  GeomLinesAdjacency *object = new GeomLinesAdjacency(UH_unspecified);
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  object->fillin(scan, manager);
+
+  return object;
+}

+ 70 - 0
panda/src/gobj/geomLinesAdjacency.h

@@ -0,0 +1,70 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file geomLinesAdjacency.h
+ * @author rdb
+ * @date 2018-03-01
+ */
+
+#ifndef GEOMLINESADJACENCY_H
+#define GEOMLINESADJACENCY_H
+
+#include "pandabase.h"
+#include "geomPrimitive.h"
+
+/**
+ * Defines a series of disconnected line segments with adjacency information,
+ * for use with geometry shaders.
+ */
+class EXPCL_PANDA_GOBJ GeomLinesAdjacency : public GeomPrimitive {
+PUBLISHED:
+  explicit GeomLinesAdjacency(UsageHint usage_hint);
+  GeomLinesAdjacency(const GeomLinesAdjacency &copy);
+  virtual ~GeomLinesAdjacency();
+  ALLOC_DELETED_CHAIN(GeomLinesAdjacency);
+
+public:
+  virtual PT(GeomPrimitive) make_copy() const;
+  virtual PrimitiveType get_primitive_type() const;
+  virtual int get_geom_rendering() const;
+
+  virtual int get_num_vertices_per_primitive() const;
+  virtual int get_min_num_vertices_per_primitive() const;
+
+public:
+  virtual bool draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader,
+                    bool force) const;
+
+public:
+  static void register_with_read_factory();
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GeomPrimitive::init_type();
+    register_type(_type_handle, "GeomLinesAdjacency",
+                  GeomPrimitive::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;
+
+  friend class Geom;
+};
+
+#endif

+ 81 - 0
panda/src/gobj/geomLinestrips.cxx

@@ -18,6 +18,7 @@
 #include "bamReader.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "bamWriter.h"
 #include "graphicsStateGuardianBase.h"
 #include "graphicsStateGuardianBase.h"
+#include "geomLinestripsAdjacency.h"
 
 
 TypeHandle GeomLinestrips::_type_handle;
 TypeHandle GeomLinestrips::_type_handle;
 
 
@@ -84,6 +85,86 @@ get_geom_rendering() const {
   }
   }
 }
 }
 
 
+/**
+ * Adds adjacency information to this primitive.  May return null if this type
+ * of geometry does not support adjacency information.
+ */
+CPT(GeomPrimitive) GeomLinestrips::
+make_adjacency() const {
+  Thread *current_thread = Thread::get_current_thread();
+  PT(GeomLinestripsAdjacency) adj = new GeomLinestripsAdjacency(get_usage_hint());
+
+  GeomPrimitivePipelineReader from(this, current_thread);
+  int num_vertices = from.get_num_vertices();
+  CPTA_int ends = get_ends();
+
+  const int num_unused = 1;
+
+  // First, build a map of each vertex to its next vertex, and another map
+  // doing the exact reverse.
+  map<int, int> forward_map, reverse_map;
+  int vi = -num_unused;
+  int li = 0;
+  while (li < (int)ends.size()) {
+    // Skip unused vertices between linestrips.
+    vi += num_unused;
+    int end = ends[li];
+    nassertr(vi + 1 <= end, nullptr);
+    int v0 = from.get_vertex(vi++);
+    while (vi < end) {
+      int v1 = from.get_vertex(vi++);
+      forward_map[v0] = v1;
+      reverse_map[v1] = v0;
+      v0 = v1;
+    }
+    ++li;
+  }
+  nassertr(vi == num_vertices, nullptr);
+
+  // Now build up the new vertex data.  For each linestrip, we prepend and
+  // append the appropriate connecting vertices.
+  vi = -num_unused;
+  li = 0;
+  while (li < (int)ends.size()) {
+    // Skip unused vertices between linestrips.
+    vi += num_unused;
+    int end = ends[li];
+    nassertr(vi + 1 <= end, nullptr);
+
+    // Look for the line segment connected to the beginning of this strip.
+    int v0 = from.get_vertex(vi++);
+    auto it = reverse_map.find(v0);
+    if (it != reverse_map.end()) {
+      adj->add_vertex(it->second);
+    } else {
+      // Um, no adjoining line segment?  Just repeat the vertex, I guess.
+      adj->add_vertex(v0);
+    }
+
+    // Add the actual vertices in the strip.
+    adj->add_vertex(v0);
+    int v1;
+    while (vi < end) {
+      v1 = from.get_vertex(vi++);
+      adj->add_vertex(v1);
+    }
+
+    // Do the same for the last vertex in the strip.
+    it = forward_map.find(v1);
+    if (it != forward_map.end()) {
+      adj->add_vertex(it->second);
+    } else {
+      adj->add_vertex(v1);
+    }
+
+    adj->close_primitive();
+    ++li;
+  }
+  nassertr(vi == num_vertices, nullptr);
+
+  return adj.p();
+}
+
 /**
 /**
  * Returns the minimum number of vertices that must be added before
  * Returns the minimum number of vertices that must be added before
  * close_primitive() may legally be called.
  * close_primitive() may legally be called.

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

@@ -31,6 +31,9 @@ public:
   virtual PT(GeomPrimitive) make_copy() const;
   virtual PT(GeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
   virtual PrimitiveType get_primitive_type() const;
   virtual int get_geom_rendering() const;
   virtual int get_geom_rendering() const;
+
+  CPT(GeomPrimitive) make_adjacency() const;
+
   virtual int get_min_num_vertices_per_primitive() const;
   virtual int get_min_num_vertices_per_primitive() const;
   virtual int get_num_unused_vertices_per_primitive() const;
   virtual int get_num_unused_vertices_per_primitive() const;
 
 

+ 210 - 0
panda/src/gobj/geomLinestripsAdjacency.cxx

@@ -0,0 +1,210 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file geomLinestripsAdjacency.cxx
+ * @author rdb
+ * @date 2018-03-01
+ */
+
+#include "geomLinestripsAdjacency.h"
+#include "geomLines.h"
+#include "geomVertexRewriter.h"
+#include "pStatTimer.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "graphicsStateGuardianBase.h"
+
+TypeHandle GeomLinestripsAdjacency::_type_handle;
+
+/**
+ *
+ */
+GeomLinestripsAdjacency::
+GeomLinestripsAdjacency(GeomEnums::UsageHint usage_hint) :
+  GeomPrimitive(usage_hint)
+{
+}
+
+/**
+ *
+ */
+GeomLinestripsAdjacency::
+GeomLinestripsAdjacency(const GeomLinestripsAdjacency &copy) :
+  GeomPrimitive(copy)
+{
+}
+
+/**
+ *
+ */
+GeomLinestripsAdjacency::
+~GeomLinestripsAdjacency() {
+}
+
+/**
+ *
+ */
+PT(GeomPrimitive) GeomLinestripsAdjacency::
+make_copy() const {
+  return new GeomLinestripsAdjacency(*this);
+}
+
+/**
+ * Returns the fundamental rendering type of this primitive: whether it is
+ * points, lines, or polygons.
+ *
+ * This is used to set up the appropriate antialiasing settings when
+ * AntialiasAttrib::M_auto is in effect; it also implies the type of primitive
+ * that will be produced when decompose() is called.
+ */
+GeomPrimitive::PrimitiveType GeomLinestripsAdjacency::
+get_primitive_type() const {
+  return PT_lines;
+}
+
+/**
+ * Returns the set of GeomRendering bits that represent the rendering
+ * properties required to properly render this primitive.
+ */
+int GeomLinestripsAdjacency::
+get_geom_rendering() const {
+  if (is_indexed()) {
+    if (get_num_primitives() > 1) {
+      return GR_line_strip | GR_indexed_other | GR_strip_cut_index | GR_adjacency;
+    } else {
+      return GR_line_strip | GR_indexed_other | GR_adjacency;
+    }
+  } else {
+    return GR_line_strip | GR_adjacency;
+  }
+}
+
+/**
+ * Returns the minimum number of vertices that must be added before
+ * close_primitive() may legally be called.
+ */
+int GeomLinestripsAdjacency::
+get_min_num_vertices_per_primitive() const {
+  return 4;
+}
+
+/**
+ * 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 GeomLinestripsAdjacency::
+get_num_unused_vertices_per_primitive() const {
+  return 1;
+}
+
+/**
+ * Calls the appropriate method on the GSG to draw the primitive.
+ */
+bool GeomLinestripsAdjacency::
+draw(GraphicsStateGuardianBase *gsg, const GeomPrimitivePipelineReader *reader,
+     bool force) const {
+  return gsg->draw_linestrips_adj(reader, force);
+}
+
+/**
+ * Decomposes a complex primitive type into a simpler primitive type, for
+ * instance line strips to lines, and returns a pointer to the new primitive
+ * definition.  If the decomposition cannot be performed, this might return
+ * the original object.
+ *
+ * This method is useful for application code that wants to iterate through
+ * the set of lines on the primitive without having to write handlers for each
+ * possible kind of primitive type.
+ */
+CPT(GeomPrimitive) GeomLinestripsAdjacency::
+decompose_impl() const {
+  Thread *current_thread = Thread::get_current_thread();
+  PT(GeomLinesAdjacency) lines = new GeomLinesAdjacency(get_usage_hint());
+  lines->set_shade_model(get_shade_model());
+
+  GeomPrimitivePipelineReader from(this, current_thread);
+  int num_vertices = from.get_num_vertices();
+  CPTA_int ends = get_ends();
+
+  const int num_unused = 1;
+
+  int vi = -num_unused;
+  int li = 0;
+  while (li < (int)ends.size()) {
+    // Skip unused vertices between tristrips.
+    vi += num_unused;
+    int end = ends[li];
+    nassertr(vi + 3 <= end, lines.p());
+    int v0 = from.get_vertex(vi++);
+    int v1 = from.get_vertex(vi++);
+    int v2 = from.get_vertex(vi++);
+    while (vi < end) {
+      int v3 = from.get_vertex(vi++);
+      lines->add_vertex(v0);
+      lines->add_vertex(v1);
+      lines->add_vertex(v2);
+      lines->add_vertex(v3);
+      v0 = v1;
+      v1 = v2;
+      v2 = v3;
+    }
+    ++li;
+  }
+  nassertr(vi == num_vertices, nullptr);
+
+  return lines.p();
+}
+
+/**
+ * Should be redefined to return true in any primitive that implements
+ * append_unused_vertices().
+ */
+bool GeomLinestripsAdjacency::
+requires_unused_vertices() const {
+  return true;
+}
+
+/**
+ * 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 GeomLinestripsAdjacency::
+append_unused_vertices(GeomVertexArrayData *vertices, int vertex) {
+  GeomVertexWriter to(vertices, 0);
+  to.set_row_unsafe(vertices->get_num_rows());
+  to.add_data1i(get_strip_cut_index());
+}
+
+/**
+ * Tells the BamReader how to create objects of type Geom.
+ */
+void GeomLinestripsAdjacency::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type Geom is encountered in the Bam file.  It should create the Geom and
+ * extract its information from the file.
+ */
+TypedWritable *GeomLinestripsAdjacency::
+make_from_bam(const FactoryParams &params) {
+  GeomLinestripsAdjacency *object = new GeomLinestripsAdjacency(UH_unspecified);
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  object->fillin(scan, manager);
+
+  return object;
+}

+ 75 - 0
panda/src/gobj/geomLinestripsAdjacency.h

@@ -0,0 +1,75 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file geomLinestripsAdjacency.h
+ * @author rdb
+ * @date 2018-03-01
+ */
+
+#ifndef GEOMLINESTRIPSADJACENCY_H
+#define GEOMLINESTRIPSADJACENCY_H
+
+#include "pandabase.h"
+#include "geomPrimitive.h"
+
+/**
+ * Defines a series of line strips.
+ */
+class EXPCL_PANDA_GOBJ GeomLinestripsAdjacency : public GeomPrimitive {
+PUBLISHED:
+  explicit GeomLinestripsAdjacency(UsageHint usage_hint);
+  GeomLinestripsAdjacency(const GeomLinestripsAdjacency &copy);
+  virtual ~GeomLinestripsAdjacency();
+  ALLOC_DELETED_CHAIN(GeomLinestripsAdjacency);
+
+public:
+  virtual PT(GeomPrimitive) make_copy() const;
+  virtual PrimitiveType get_primitive_type() const;
+  virtual int get_geom_rendering() const;
+
+  virtual int get_min_num_vertices_per_primitive() const;
+  virtual int get_num_unused_vertices_per_primitive() const;
+
+public:
+  virtual bool draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader,
+                    bool force) const;
+
+protected:
+  virtual CPT(GeomPrimitive) decompose_impl() const;
+  virtual bool requires_unused_vertices() const;
+  virtual void append_unused_vertices(GeomVertexArrayData *vertices,
+                                      int vertex);
+
+public:
+  static void register_with_read_factory();
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GeomPrimitive::init_type();
+    register_type(_type_handle, "GeomLinestripsAdjacency",
+                  GeomPrimitive::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;
+
+  friend class Geom;
+};
+
+#endif

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

@@ -1021,6 +1021,15 @@ make_patches() const {
   return patches;
   return patches;
 }
 }
 
 
+/**
+ * Adds adjacency information to this primitive.  May return null if this type
+ * of geometry does not support adjacency information.
+ */
+CPT(GeomPrimitive) GeomPrimitive::
+make_adjacency() const {
+  return nullptr;
+}
+
 /**
 /**
  * Returns the number of bytes consumed by the primitive and its index
  * Returns the number of bytes consumed by the primitive and its index
  * table(s).
  * table(s).

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

@@ -134,6 +134,7 @@ PUBLISHED:
   CPT(GeomPrimitive) make_points() const;
   CPT(GeomPrimitive) make_points() const;
   CPT(GeomPrimitive) make_lines() const;
   CPT(GeomPrimitive) make_lines() const;
   CPT(GeomPrimitive) make_patches() const;
   CPT(GeomPrimitive) make_patches() const;
+  virtual CPT(GeomPrimitive) make_adjacency() const;
 
 
   int get_num_bytes() const;
   int get_num_bytes() const;
   INLINE int get_data_size_bytes() const;
   INLINE int get_data_size_bytes() const;

+ 71 - 0
panda/src/gobj/geomTriangles.cxx

@@ -17,6 +17,7 @@
 #include "bamReader.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "bamWriter.h"
 #include "graphicsStateGuardianBase.h"
 #include "graphicsStateGuardianBase.h"
+#include "geomTrianglesAdjacency.h"
 
 
 TypeHandle GeomTriangles::_type_handle;
 TypeHandle GeomTriangles::_type_handle;
 
 
@@ -66,6 +67,76 @@ get_primitive_type() const {
   return PT_polygons;
   return PT_polygons;
 }
 }
 
 
+/**
+ * Adds adjacency information to this primitive.  May return null if this type
+ * of geometry does not support adjacency information.
+ */
+CPT(GeomPrimitive) GeomTriangles::
+make_adjacency() const {
+  Thread *current_thread = Thread::get_current_thread();
+  PT(GeomTrianglesAdjacency) adj = new GeomTrianglesAdjacency(get_usage_hint());
+
+  GeomPrimitivePipelineReader from(this, current_thread);
+  int num_vertices = from.get_num_vertices();
+
+  PT(GeomVertexArrayData) new_vertices = adj->make_index_data();
+  new_vertices->set_num_rows(num_vertices * 2);
+
+  // First, build a map of each triangle's halfedges to its opposing vertices.
+  map<pair<int, int>, int> edge_map;
+  for (int i = 0; i < num_vertices; i += 3) {
+    int v0 = from.get_vertex(i);
+    int v1 = from.get_vertex(i + 1);
+    int v2 = from.get_vertex(i + 2);
+    edge_map[make_pair(v0, v1)] = v2;
+    edge_map[make_pair(v1, v2)] = v0;
+    edge_map[make_pair(v2, v0)] = v1;
+  }
+
+  // Now build up the new vertex data.  For each edge, we insert the
+  // appropriate connecting vertex.
+  {
+    GeomVertexWriter to(new_vertices, 0);
+    for (int i = 0; i < num_vertices; i += 3) {
+      int v0 = from.get_vertex(i);
+      int v1 = from.get_vertex(i + 1);
+      int v2 = from.get_vertex(i + 2);
+
+      // Get the third vertex of the triangle adjoining this edge.
+      to.set_data1(v0);
+      auto it = edge_map.find(make_pair(v1, v0));
+      if (it != edge_map.end()) {
+        to.set_data1i(it->second);
+      } else {
+        // Um, no adjoining triangle?  Just repeat the vertex, I guess.
+        to.set_data1i(v0);
+      }
+
+      // Do the same for the other two edges.
+      to.set_data1(v1);
+      it = edge_map.find(make_pair(v2, v1));
+      if (it != edge_map.end()) {
+        to.set_data1i(it->second);
+      } else {
+        to.set_data1i(v1);
+      }
+
+      to.set_data1(v2);
+      it = edge_map.find(make_pair(v0, v2));
+      if (it != edge_map.end()) {
+        to.set_data1i(it->second);
+      } else {
+        to.set_data1i(v2);
+      }
+    }
+
+    nassertr(to.is_at_end(), nullptr);
+  }
+
+  adj->set_vertices(move(new_vertices));
+  return adj.p();
+}
+
 /**
 /**
  * If the primitive type is a simple type in which all primitives have the
  * 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
  * same number of vertices, like triangles, returns the number of vertices per

+ 2 - 0
panda/src/gobj/geomTriangles.h

@@ -31,6 +31,8 @@ public:
   virtual PT(GeomPrimitive) make_copy() const;
   virtual PT(GeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
   virtual PrimitiveType get_primitive_type() const;
 
 
+  CPT(GeomPrimitive) make_adjacency() const;
+
   virtual int get_num_vertices_per_primitive() const;
   virtual int get_num_vertices_per_primitive() const;
 
 
 public:
 public:

+ 195 - 0
panda/src/gobj/geomTrianglesAdjacency.cxx

@@ -0,0 +1,195 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file geomTrianglesAdjacency.cxx
+ * @author rdb
+ * @date 2018-03-01
+ */
+
+#include "geomTrianglesAdjacency.h"
+#include "geomVertexRewriter.h"
+#include "pStatTimer.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "graphicsStateGuardianBase.h"
+
+TypeHandle GeomTrianglesAdjacency::_type_handle;
+
+/**
+ *
+ */
+GeomTrianglesAdjacency::
+GeomTrianglesAdjacency(GeomTrianglesAdjacency::UsageHint usage_hint) :
+  GeomPrimitive(usage_hint)
+{
+}
+
+/**
+ *
+ */
+GeomTrianglesAdjacency::
+GeomTrianglesAdjacency(const GeomTrianglesAdjacency &copy) :
+  GeomPrimitive(copy)
+{
+}
+
+/**
+ *
+ */
+GeomTrianglesAdjacency::
+~GeomTrianglesAdjacency() {
+}
+
+/**
+ *
+ */
+PT(GeomPrimitive) GeomTrianglesAdjacency::
+make_copy() const {
+  return new GeomTrianglesAdjacency(*this);
+}
+
+/**
+ * Returns the fundamental rendering type of this primitive: whether it is
+ * points, lines, or polygons.
+ *
+ * This is used to set up the appropriate antialiasing settings when
+ * AntialiasAttrib::M_auto is in effect; it also implies the type of primitive
+ * that will be produced when decompose() is called.
+ */
+GeomPrimitive::PrimitiveType GeomTrianglesAdjacency::
+get_primitive_type() const {
+  return PT_polygons;
+}
+
+/**
+ * Returns the set of GeomRendering bits that represent the rendering
+ * properties required to properly render this primitive.
+ */
+int GeomTrianglesAdjacency::
+get_geom_rendering() const {
+  return GeomPrimitive::get_geom_rendering() | GR_adjacency;
+}
+
+/**
+ * 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 GeomTrianglesAdjacency::
+get_num_vertices_per_primitive() const {
+  return 6;
+}
+
+/**
+ * Calls the appropriate method on the GSG to draw the primitive.
+ */
+bool GeomTrianglesAdjacency::
+draw(GraphicsStateGuardianBase *gsg, const GeomPrimitivePipelineReader *reader,
+     bool force) const {
+  return gsg->draw_triangles_adj(reader, force);
+}
+
+/**
+ * The virtual implementation of doubleside().
+ */
+CPT(GeomPrimitive) GeomTrianglesAdjacency::
+doubleside_impl() const {
+  Thread *current_thread = Thread::get_current_thread();
+  PT(GeomTrianglesAdjacency) reversed = new GeomTrianglesAdjacency(*this);
+
+  GeomPrimitivePipelineReader from(this, current_thread);
+
+  // This is like reverse(), except we don't clear the vertices first.  That
+  // way we double the vertices up.
+
+  // First, rotate the original copy, if necessary, so the flat-firstflat-last
+  // nature of the vertices is consistent throughout the primitive.
+  bool needs_rotate = false;
+  switch (from.get_shade_model()) {
+  case SM_flat_first_vertex:
+  case SM_flat_last_vertex:
+    reversed = (GeomTrianglesAdjacency *)DCAST(GeomTrianglesAdjacency, reversed->rotate());
+    needs_rotate = true;
+
+  default:
+    break;
+  }
+
+  // Now append all the new vertices, in reverse order.
+  for (int i = from.get_num_vertices() - 1; i >= 0; --i) {
+    reversed->add_vertex(from.get_vertex(i));
+  }
+
+  // Finally, re-rotate the whole thing to get back to the original shade
+  // model.
+  if (needs_rotate) {
+    reversed = (GeomTrianglesAdjacency *)DCAST(GeomTrianglesAdjacency, reversed->rotate());
+  }
+
+  return reversed.p();
+}
+
+/**
+ * The virtual implementation of reverse().
+ */
+CPT(GeomPrimitive) GeomTrianglesAdjacency::
+reverse_impl() const {
+  Thread *current_thread = Thread::get_current_thread();
+  PT(GeomTrianglesAdjacency) reversed = new GeomTrianglesAdjacency(*this);
+
+  GeomPrimitivePipelineReader from(this, current_thread);
+  reversed->clear_vertices();
+
+  for (int i = from.get_num_vertices() - 1; i >= 0; --i) {
+    reversed->add_vertex(from.get_vertex(i));
+  }
+
+  switch (from.get_shade_model()) {
+  case SM_flat_first_vertex:
+    reversed->set_shade_model(SM_flat_last_vertex);
+    reversed = (GeomTrianglesAdjacency *)DCAST(GeomTrianglesAdjacency, reversed->rotate());
+    break;
+
+  case SM_flat_last_vertex:
+    reversed->set_shade_model(SM_flat_first_vertex);
+    reversed = (GeomTrianglesAdjacency *)DCAST(GeomTrianglesAdjacency, reversed->rotate());
+    break;
+
+  default:
+    break;
+  }
+
+  return reversed.p();
+}
+
+/**
+ * Tells the BamReader how to create objects of type Geom.
+ */
+void GeomTrianglesAdjacency::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type Geom is encountered in the Bam file.  It should create the Geom and
+ * extract its information from the file.
+ */
+TypedWritable *GeomTrianglesAdjacency::
+make_from_bam(const FactoryParams &params) {
+  GeomTrianglesAdjacency *object = new GeomTrianglesAdjacency(UH_unspecified);
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  object->fillin(scan, manager);
+
+  return object;
+}

+ 72 - 0
panda/src/gobj/geomTrianglesAdjacency.h

@@ -0,0 +1,72 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file geomTrianglesAdjacency.h
+ * @author rdb
+ * @date 2018-03-01
+ */
+
+#ifndef GEOMTRIANGLESADJACENCY_H
+#define GEOMTRIANGLESADJACENCY_H
+
+#include "pandabase.h"
+#include "geomPrimitive.h"
+
+/**
+ * Defines a series of disconnected triangles, with adjacency information.
+ */
+class EXPCL_PANDA_GOBJ GeomTrianglesAdjacency : public GeomPrimitive {
+PUBLISHED:
+  explicit GeomTrianglesAdjacency(UsageHint usage_hint);
+  GeomTrianglesAdjacency(const GeomTrianglesAdjacency &copy);
+  virtual ~GeomTrianglesAdjacency();
+  ALLOC_DELETED_CHAIN(GeomTrianglesAdjacency);
+
+public:
+  virtual PT(GeomPrimitive) make_copy() const;
+  virtual PrimitiveType get_primitive_type() const;
+  virtual int get_geom_rendering() const;
+
+  virtual int get_num_vertices_per_primitive() const;
+
+public:
+  virtual bool draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader,
+                    bool force) const;
+
+protected:
+  virtual CPT(GeomPrimitive) doubleside_impl() const;
+  virtual CPT(GeomPrimitive) reverse_impl() const;
+
+public:
+  static void register_with_read_factory();
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GeomPrimitive::init_type();
+    register_type(_type_handle, "GeomTrianglesAdjacency",
+                  GeomPrimitive::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;
+
+  friend class Geom;
+};
+
+#endif

+ 138 - 0
panda/src/gobj/geomTristrips.cxx

@@ -18,6 +18,7 @@
 #include "bamReader.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "bamWriter.h"
 #include "graphicsStateGuardianBase.h"
 #include "graphicsStateGuardianBase.h"
+#include "geomTristripsAdjacency.h"
 
 
 TypeHandle GeomTristrips::_type_handle;
 TypeHandle GeomTristrips::_type_handle;
 
 
@@ -80,6 +81,143 @@ get_geom_rendering() const {
   }
   }
 }
 }
 
 
+/**
+ * Adds adjacency information to this primitive.  May return null if this type
+ * of geometry does not support adjacency information.
+ */
+CPT(GeomPrimitive) GeomTristrips::
+make_adjacency() const {
+  Thread *current_thread = Thread::get_current_thread();
+  PT(GeomTristripsAdjacency) adj = new GeomTristripsAdjacency(get_usage_hint());
+  CPTA_int ends = get_ends();
+
+  GeomPrimitivePipelineReader from(this, current_thread);
+  int num_vertices = from.get_num_vertices();
+  const int num_unused = 2;
+
+  // First, build a map of each triangle's halfedges to its opposing vertices.
+  map<pair<int, int>, int> edge_map;
+
+  int vi = -num_unused;
+  int li = 0;
+  while (li < (int)ends.size()) {
+    // Skip unused vertices between tristrips.
+    vi += num_unused;
+    int end = ends[li];
+    nassertr(vi + 2 <= end, nullptr);
+
+    int v0 = from.get_vertex(vi++);
+    int v1 = from.get_vertex(vi++);
+    int v2 = from.get_vertex(vi);
+    edge_map[make_pair(v0, v1)] = v2;
+
+    while (true) {
+      v2 = from.get_vertex(vi++);
+      edge_map[make_pair(v2, v0)] = v1;
+
+      if (vi >= end) {
+        // Edge at the end of the strip
+        edge_map[make_pair(v1, v2)] = v0;
+        break;
+      }
+
+      v0 = v1;
+      v1 = v2;
+      v2 = from.get_vertex(vi++);
+      edge_map[make_pair(v0, v2)] = v1;
+
+      if (vi >= end) {
+        // Edge at the end of the strip
+        edge_map[make_pair(v2, v1)] = v0;
+        break;
+      }
+
+      v0 = v1;
+      v1 = v2;
+    }
+    ++li;
+  }
+  nassertr(vi == num_vertices, nullptr);
+
+  // Now build up the new vertex data.  For each edge, we insert the
+  // appropriate connecting vertex.
+  vi = -num_unused;
+  li = 0;
+  while (li < (int)ends.size()) {
+    // Skip unused vertices between tristrips.
+    vi += num_unused;
+    int end = ends[li];
+    nassertr(vi + 2 <= end, nullptr);
+
+    int v0 = from.get_vertex(vi++);
+    int v1 = from.get_vertex(vi++);
+    int v2;
+    adj->add_vertex(v0);
+
+    // Get the third vertex of the triangle adjoining this edge.
+    auto it = edge_map.find(make_pair(v1, v0));
+    if (it != edge_map.end()) {
+      adj->add_vertex(it->second);
+    } else {
+      // Um, no adjoining triangle?  Just repeat the vertex, I guess.
+      adj->add_vertex(v0);
+    }
+    adj->add_vertex(v1);
+
+    while (true) {
+      v2 = from.get_vertex(vi++);
+      it = edge_map.find(make_pair(v0, v2));
+      if (it != edge_map.end()) {
+        adj->add_vertex(it->second);
+      } else {
+        adj->add_vertex(v1);
+      }
+      adj->add_vertex(v2);
+
+      if (vi >= end) {
+        // Edge at the end of the strip
+        it = edge_map.find(make_pair(v2, v1));
+        if (it != edge_map.end()) {
+          adj->add_vertex(it->second);
+        } else {
+          adj->add_vertex(v2);
+        }
+        break;
+      }
+
+      v0 = v1;
+      v1 = v2;
+      v2 = from.get_vertex(vi++);
+      it = edge_map.find(make_pair(v2, v0));
+      if (it != edge_map.end()) {
+        adj->add_vertex(it->second);
+      } else {
+        adj->add_vertex(v1);
+      }
+      adj->add_vertex(v2);
+
+      if (vi >= end) {
+        // Edge at the end of the strip
+        it = edge_map.find(make_pair(v1, v2));
+        if (it != edge_map.end()) {
+          adj->add_vertex(it->second);
+        } else {
+          adj->add_vertex(v1);
+        }
+        break;
+      }
+
+      v0 = v1;
+      v1 = v2;
+    }
+    adj->close_primitive();
+    ++li;
+  }
+  nassertr(vi == num_vertices, nullptr);
+
+  return adj.p();
+}
+
 /**
 /**
  * Returns the minimum number of vertices that must be added before
  * Returns the minimum number of vertices that must be added before
  * close_primitive() may legally be called.
  * close_primitive() may legally be called.

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

@@ -31,6 +31,9 @@ public:
   virtual PT(GeomPrimitive) make_copy() const;
   virtual PT(GeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
   virtual PrimitiveType get_primitive_type() const;
   virtual int get_geom_rendering() const;
   virtual int get_geom_rendering() const;
+
+  CPT(GeomPrimitive) make_adjacency() const;
+
   virtual int get_min_num_vertices_per_primitive() const;
   virtual int get_min_num_vertices_per_primitive() const;
   virtual int get_num_unused_vertices_per_primitive() const;
   virtual int get_num_unused_vertices_per_primitive() const;
 
 

+ 161 - 0
panda/src/gobj/geomTristripsAdjacency.cxx

@@ -0,0 +1,161 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file geomTristripsAdjacency.cxx
+ * @author rdb
+ * @date 2018-03-01
+ */
+
+#include "geomTristripsAdjacency.h"
+#include "geomTriangles.h"
+#include "geomVertexRewriter.h"
+#include "pStatTimer.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "graphicsStateGuardianBase.h"
+
+TypeHandle GeomTristripsAdjacency::_type_handle;
+
+/**
+ *
+ */
+GeomTristripsAdjacency::
+GeomTristripsAdjacency(GeomTristripsAdjacency::UsageHint usage_hint) :
+  GeomPrimitive(usage_hint)
+{
+}
+
+/**
+ *
+ */
+GeomTristripsAdjacency::
+GeomTristripsAdjacency(const GeomTristripsAdjacency &copy) :
+  GeomPrimitive(copy)
+{
+}
+
+/**
+ *
+ */
+GeomTristripsAdjacency::
+~GeomTristripsAdjacency() {
+}
+
+/**
+ *
+ */
+PT(GeomPrimitive) GeomTristripsAdjacency::
+make_copy() const {
+  return new GeomTristripsAdjacency(*this);
+}
+
+/**
+ * Returns the fundamental rendering type of this primitive: whether it is
+ * points, lines, or polygons.
+ *
+ * This is used to set up the appropriate antialiasing settings when
+ * AntialiasAttrib::M_auto is in effect; it also implies the type of primitive
+ * that will be produced when decompose() is called.
+ */
+GeomPrimitive::PrimitiveType GeomTristripsAdjacency::
+get_primitive_type() const {
+  return PT_polygons;
+}
+
+/**
+ * Returns the set of GeomRendering bits that represent the rendering
+ * properties required to properly render this primitive.
+ */
+int GeomTristripsAdjacency::
+get_geom_rendering() const {
+  if (is_indexed()) {
+    if (get_num_primitives() > 1) {
+      return GR_triangle_strip | GR_indexed_other | GR_strip_cut_index | GR_adjacency;
+    } else {
+      return GR_triangle_strip | GR_indexed_other | GR_adjacency;
+    }
+  } else {
+    return GR_triangle_strip | GR_adjacency;
+  }
+}
+
+/**
+ * Returns the minimum number of vertices that must be added before
+ * close_primitive() may legally be called.
+ */
+int GeomTristripsAdjacency::
+get_min_num_vertices_per_primitive() const {
+  return 6;
+}
+
+/**
+ * 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 GeomTristripsAdjacency::
+get_num_unused_vertices_per_primitive() const {
+  return 1;
+}
+
+/**
+ * Calls the appropriate method on the GSG to draw the primitive.
+ */
+bool GeomTristripsAdjacency::
+draw(GraphicsStateGuardianBase *gsg, const GeomPrimitivePipelineReader *reader,
+     bool force) const {
+  return gsg->draw_tristrips_adj(reader, force);
+}
+
+/**
+ * Should be redefined to return true in any primitive that implements
+ * append_unused_vertices().
+ */
+bool GeomTristripsAdjacency::
+requires_unused_vertices() const {
+  return true;
+}
+
+/**
+ * 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 GeomTristripsAdjacency::
+append_unused_vertices(GeomVertexArrayData *vertices, int vertex) {
+  GeomVertexWriter to(vertices, 0);
+  to.set_row_unsafe(vertices->get_num_rows());
+  to.add_data1i(get_strip_cut_index());
+}
+
+/**
+ * Tells the BamReader how to create objects of type Geom.
+ */
+void GeomTristripsAdjacency::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type Geom is encountered in the Bam file.  It should create the Geom and
+ * extract its information from the file.
+ */
+TypedWritable *GeomTristripsAdjacency::
+make_from_bam(const FactoryParams &params) {
+  GeomTristripsAdjacency *object = new GeomTristripsAdjacency(UH_unspecified);
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  object->fillin(scan, manager);
+
+  return object;
+}

+ 73 - 0
panda/src/gobj/geomTristripsAdjacency.h

@@ -0,0 +1,73 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file geomTristripsAdjacency.h
+ * @author rdb
+ * @date 2018-03-01
+ */
+
+#ifndef GEOMTRISTRIPSADJACENCY_H
+#define GEOMTRISTRIPSADJACENCY_H
+
+#include "pandabase.h"
+#include "geomPrimitive.h"
+
+/**
+ * Defines a series of triangle strips.
+ */
+class EXPCL_PANDA_GOBJ GeomTristripsAdjacency : public GeomPrimitive {
+PUBLISHED:
+  explicit GeomTristripsAdjacency(UsageHint usage_hint);
+  GeomTristripsAdjacency(const GeomTristripsAdjacency &copy);
+  virtual ~GeomTristripsAdjacency();
+  ALLOC_DELETED_CHAIN(GeomTristripsAdjacency);
+
+public:
+  virtual PT(GeomPrimitive) make_copy() const;
+  virtual PrimitiveType get_primitive_type() const;
+  virtual int get_geom_rendering() const;
+  virtual int get_min_num_vertices_per_primitive() const;
+  virtual int get_num_unused_vertices_per_primitive() const;
+
+public:
+  virtual bool draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader,
+                    bool force) const;
+
+protected:
+  virtual bool requires_unused_vertices() const;
+  virtual void append_unused_vertices(GeomVertexArrayData *vertices,
+                                      int vertex);
+
+public:
+  static void register_with_read_factory();
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GeomPrimitive::init_type();
+    register_type(_type_handle, "GeomTristripsAdjacency",
+                  GeomPrimitive::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;
+
+  friend class Geom;
+};
+
+#endif

+ 4 - 0
panda/src/gobj/p3gobj_composite1.cxx

@@ -10,14 +10,18 @@
 #include "geomContext.cxx"
 #include "geomContext.cxx"
 #include "geomEnums.cxx"
 #include "geomEnums.cxx"
 #include "geomLines.cxx"
 #include "geomLines.cxx"
+#include "geomLinesAdjacency.cxx"
 #include "geomLinestrips.cxx"
 #include "geomLinestrips.cxx"
+#include "geomLinestripsAdjacency.cxx"
 #include "geomMunger.cxx"
 #include "geomMunger.cxx"
 #include "geomPatches.cxx"
 #include "geomPatches.cxx"
 #include "geomPoints.cxx"
 #include "geomPoints.cxx"
 #include "geomPrimitive.cxx"
 #include "geomPrimitive.cxx"
 #include "geomTriangles.cxx"
 #include "geomTriangles.cxx"
+#include "geomTrianglesAdjacency.cxx"
 #include "geomTrifans.cxx"
 #include "geomTrifans.cxx"
 #include "geomTristrips.cxx"
 #include "geomTristrips.cxx"
+#include "geomTristripsAdjacency.cxx"
 #include "geomVertexAnimationSpec.cxx"
 #include "geomVertexAnimationSpec.cxx"
 #include "geomVertexArrayData.cxx"
 #include "geomVertexArrayData.cxx"
 #include "geomVertexArrayFormat.cxx"
 #include "geomVertexArrayFormat.cxx"

+ 4 - 0
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -201,11 +201,15 @@ public:
                                      const GeomVertexDataPipelineReader *data_reader,
                                      const GeomVertexDataPipelineReader *data_reader,
                                      bool force)=0;
                                      bool force)=0;
   virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, bool force)=0;
+  virtual bool draw_triangles_adj(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force)=0;
+  virtual bool draw_tristrips_adj(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_trifans(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_trifans(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_patches(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_patches(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_lines(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_lines(const GeomPrimitivePipelineReader *reader, bool force)=0;
+  virtual bool draw_lines_adj(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force)=0;
+  virtual bool draw_linestrips_adj(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_points(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual bool draw_points(const GeomPrimitivePipelineReader *reader, bool force)=0;
   virtual void end_draw_primitives()=0;
   virtual void end_draw_primitives()=0;
 
 

+ 83 - 0
tests/gobj/test_geom_primitives.py

@@ -0,0 +1,83 @@
+from panda3d import core
+
+
+def test_geom_triangles_adjacency():
+    prim = core.GeomTriangles(core.GeomEnums.UH_static)
+    prim.add_vertex(0)
+    prim.add_vertex(1)
+    prim.add_vertex(2)
+    prim.close_primitive()
+    prim.add_vertex(2)
+    prim.add_vertex(1)
+    prim.add_vertex(3)
+    prim.close_primitive()
+
+    adj = prim.make_adjacency()
+    verts = adj.get_vertex_list()
+    assert tuple(verts) == (
+        0, 0, 1, 3, 2, 2,
+        2, 0, 1, 1, 3, 3,
+    )
+
+
+def test_geom_lines_adjacency():
+    prim = core.GeomLines(core.GeomEnums.UH_static)
+    prim.add_vertex(0)
+    prim.add_vertex(1)
+    prim.close_primitive()
+    prim.add_vertex(1)
+    prim.add_vertex(2)
+    prim.close_primitive()
+    prim.add_vertex(2)
+    prim.add_vertex(3)
+    prim.close_primitive()
+
+    adj = prim.make_adjacency()
+    verts = adj.get_vertex_list()
+    assert tuple(verts) == (
+        0, 0, 1, 2,
+        0, 1, 2, 3,
+        1, 2, 3, 3,
+    )
+
+
+def test_geom_linestrips_adjacency():
+    prim = core.GeomLinestrips(core.GeomEnums.UH_static)
+    prim.add_vertex(0)
+    prim.add_vertex(1)
+    prim.close_primitive()
+    prim.add_vertex(1)
+    prim.add_vertex(2)
+    prim.add_vertex(3)
+    prim.close_primitive()
+    prim.add_vertex(3)
+    prim.add_vertex(4)
+    prim.add_vertex(5)
+    prim.add_vertex(6)
+    prim.close_primitive()
+
+    adj = prim.make_adjacency()
+    verts = adj.get_vertex_list()
+    cut = adj.get_strip_cut_index()
+    assert tuple(verts) == (
+        0, 0, 1, 2,
+        cut,
+        0, 1, 2, 3, 4,
+        cut,
+        2, 3, 4, 5, 6, 6,
+    )
+
+    # Check that it decomposes properly to a lines-adjacency primitive
+    prim = adj.decompose()
+    assert isinstance(prim, core.GeomLinesAdjacency)
+    verts = prim.get_vertex_list()
+    assert tuple(verts) == (
+        0, 0, 1, 2,
+
+        0, 1, 2, 3,
+        1, 2, 3, 4,
+
+        2, 3, 4, 5,
+        3, 4, 5, 6,
+        4, 5, 6, 6,
+    )