diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index d590aa8d37..5817b62e4a 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -2381,6 +2381,15 @@ draw_triangles(const GeomPrimitivePipelineReader *, bool) { 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. */ @@ -2389,6 +2398,14 @@ draw_tristrips(const GeomPrimitivePipelineReader *, bool) { 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. */ @@ -2414,6 +2431,14 @@ draw_lines(const GeomPrimitivePipelineReader *, bool) { 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. */ @@ -2422,6 +2447,14 @@ draw_linestrips(const GeomPrimitivePipelineReader *, bool) { 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. */ diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 9b7855dc31..e508be9c6a 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -371,16 +371,24 @@ public: bool force); virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, bool force); + virtual bool draw_triangles_adj(const GeomPrimitivePipelineReader *reader, + bool force); virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force); + virtual bool draw_tristrips_adj(const GeomPrimitivePipelineReader *reader, + bool force); virtual bool draw_trifans(const GeomPrimitivePipelineReader *reader, bool force); virtual bool draw_patches(const GeomPrimitivePipelineReader *reader, bool force); virtual bool draw_lines(const GeomPrimitivePipelineReader *reader, bool force); + virtual bool draw_lines_adj(const GeomPrimitivePipelineReader *reader, + bool force); virtual bool draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force); + virtual bool draw_linestrips_adj(const GeomPrimitivePipelineReader *reader, + bool force); virtual bool draw_points(const GeomPrimitivePipelineReader *reader, bool force); virtual void end_draw_primitives(); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index af84e2bafd..97917acee0 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -653,6 +653,12 @@ reset() { Geom::GR_line_strip | Geom::GR_flat_last_vertex; +#ifndef OPENGLES + if (_supports_geometry_shaders) { + _supported_geom_rendering |= Geom::GR_adjacency; + } +#endif + _supports_point_parameters = false; #ifdef OPENGLES_1 @@ -4543,6 +4549,68 @@ draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) { 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. */ @@ -4669,6 +4737,128 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) { 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. */ @@ -4888,6 +5078,66 @@ draw_lines(const GeomPrimitivePipelineReader *reader, bool force) { 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. */ @@ -5010,6 +5260,117 @@ draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force) { 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. */ diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 2b357ece5b..ab1d27a2a4 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -288,16 +288,32 @@ public: bool force); virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, bool force); +#ifndef OPENGLES + virtual bool draw_triangles_adj(const GeomPrimitivePipelineReader *reader, + bool force); +#endif virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force); +#ifndef OPENGLES + virtual bool draw_tristrips_adj(const GeomPrimitivePipelineReader *reader, + bool force); +#endif virtual bool draw_trifans(const GeomPrimitivePipelineReader *reader, bool force); virtual bool draw_patches(const GeomPrimitivePipelineReader *reader, bool force); virtual bool draw_lines(const GeomPrimitivePipelineReader *reader, bool force); +#ifndef OPENGLES + virtual bool draw_lines_adj(const GeomPrimitivePipelineReader *reader, + bool force); +#endif virtual bool draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force); +#ifndef OPENGLES + virtual bool draw_linestrips_adj(const GeomPrimitivePipelineReader *reader, + bool force); +#endif virtual bool draw_points(const GeomPrimitivePipelineReader *reader, bool force); virtual void end_draw_primitives(); diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 84876a2b8f..058d7463a4 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -20,11 +20,15 @@ #include "geomMunger.h" #include "geomPrimitive.h" #include "geomTriangles.h" +#include "geomTrianglesAdjacency.h" #include "geomTristrips.h" +#include "geomTristripsAdjacency.h" #include "geomTrifans.h" #include "geomPatches.h" #include "geomLines.h" +#include "geomLinesAdjacency.h" #include "geomLinestrips.h" +#include "geomLinestripsAdjacency.h" #include "geomPoints.h" #include "geomVertexArrayData.h" #include "geomVertexArrayFormat.h" @@ -546,14 +550,18 @@ ConfigureFn(config_gobj) { GeomPipelineReader::init_type(); GeomContext::init_type(); GeomLines::init_type(); + GeomLinesAdjacency::init_type(); GeomLinestrips::init_type(); + GeomLinestripsAdjacency::init_type(); GeomMunger::init_type(); GeomPoints::init_type(); GeomPrimitive::init_type(); GeomPrimitivePipelineReader::init_type(); GeomTriangles::init_type(); + GeomTrianglesAdjacency::init_type(); GeomTrifans::init_type(); GeomTristrips::init_type(); + GeomTristripsAdjacency::init_type(); GeomPatches::init_type(); GeomVertexArrayData::init_type(); GeomVertexArrayDataHandle::init_type(); @@ -601,11 +609,15 @@ ConfigureFn(config_gobj) { // factory Geom::register_with_read_factory(); GeomLines::register_with_read_factory(); + GeomLinesAdjacency::register_with_read_factory(); GeomLinestrips::register_with_read_factory(); + GeomLinestripsAdjacency::register_with_read_factory(); GeomPoints::register_with_read_factory(); GeomTriangles::register_with_read_factory(); + GeomTrianglesAdjacency::register_with_read_factory(); GeomTrifans::register_with_read_factory(); GeomTristrips::register_with_read_factory(); + GeomTristripsAdjacency::register_with_read_factory(); GeomPatches::register_with_read_factory(); GeomVertexArrayData::register_with_read_factory(); GeomVertexArrayFormat::register_with_read_factory(); diff --git a/panda/src/gobj/geom.I b/panda/src/gobj/geom.I index 340cfd099c..413e2de055 100644 --- a/panda/src/gobj/geom.I +++ b/panda/src/gobj/geom.I @@ -212,6 +212,17 @@ make_patches() const { 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 * any of the primitives in the Geom is modified, or the set of primitives is diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index efdd4ba5ca..26dd3c942c 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -834,6 +834,42 @@ make_patches_in_place() { 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 * require that both Geoms contain the same fundamental type primitives, both diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index ec81442703..50b050e578 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -106,6 +106,7 @@ PUBLISHED: INLINE PT(Geom) make_points() const; INLINE PT(Geom) make_lines() const; INLINE PT(Geom) make_patches() const; + INLINE PT(Geom) make_adjacency() const; void decompose_in_place(); void doubleside_in_place(); @@ -115,6 +116,7 @@ PUBLISHED: void make_points_in_place(); void make_lines_in_place(); void make_patches_in_place(); + void make_adjacency_in_place(); virtual bool copy_primitives_from(const Geom *other); diff --git a/panda/src/gobj/geomEnums.h b/panda/src/gobj/geomEnums.h index 7d55a797a1..80af6d0ea2 100644 --- a/panda/src/gobj/geomEnums.h +++ b/panda/src/gobj/geomEnums.h @@ -132,6 +132,9 @@ PUBLISHED: // If a particular non-fill polygon mode is used. GR_render_mode_wireframe= 0x40000, GR_render_mode_point = 0x80000, + + // The primitive has adjacency information. + GR_adjacency = 0x100000, }; // The shade model specifies whether the per-vertex colors and normals diff --git a/panda/src/gobj/geomLines.cxx b/panda/src/gobj/geomLines.cxx index 1010c814be..20c9aa198b 100644 --- a/panda/src/gobj/geomLines.cxx +++ b/panda/src/gobj/geomLines.cxx @@ -18,6 +18,7 @@ #include "graphicsStateGuardianBase.h" #include "geomVertexReader.h" #include "geomVertexWriter.h" +#include "geomLinesAdjacency.h" TypeHandle GeomLines::_type_handle; @@ -67,6 +68,67 @@ get_primitive_type() const { 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 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 * same number of vertices, like lines, returns the number of vertices per diff --git a/panda/src/gobj/geomLines.h b/panda/src/gobj/geomLines.h index e05a4605c6..fc28c509c5 100644 --- a/panda/src/gobj/geomLines.h +++ b/panda/src/gobj/geomLines.h @@ -31,6 +31,8 @@ public: virtual PT(GeomPrimitive) make_copy() const; virtual PrimitiveType get_primitive_type() const; + CPT(GeomPrimitive) make_adjacency() const; + virtual int get_num_vertices_per_primitive() const; virtual int get_min_num_vertices_per_primitive() const; diff --git a/panda/src/gobj/geomLinesAdjacency.cxx b/panda/src/gobj/geomLinesAdjacency.cxx new file mode 100644 index 0000000000..28567cdade --- /dev/null +++ b/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 ©) : + 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 ¶ms) { + GeomLinesAdjacency *object = new GeomLinesAdjacency(UH_unspecified); + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + object->fillin(scan, manager); + + return object; +} diff --git a/panda/src/gobj/geomLinesAdjacency.h b/panda/src/gobj/geomLinesAdjacency.h new file mode 100644 index 0000000000..884875cb39 --- /dev/null +++ b/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 ©); + 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 ¶ms); + +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 diff --git a/panda/src/gobj/geomLinestrips.cxx b/panda/src/gobj/geomLinestrips.cxx index 734cf49c55..8a3365a201 100644 --- a/panda/src/gobj/geomLinestrips.cxx +++ b/panda/src/gobj/geomLinestrips.cxx @@ -18,6 +18,7 @@ #include "bamReader.h" #include "bamWriter.h" #include "graphicsStateGuardianBase.h" +#include "geomLinestripsAdjacency.h" 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 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 * close_primitive() may legally be called. diff --git a/panda/src/gobj/geomLinestrips.h b/panda/src/gobj/geomLinestrips.h index ae8956eb3d..39ff5f518a 100644 --- a/panda/src/gobj/geomLinestrips.h +++ b/panda/src/gobj/geomLinestrips.h @@ -31,6 +31,9 @@ public: virtual PT(GeomPrimitive) make_copy() const; virtual PrimitiveType get_primitive_type() const; virtual int get_geom_rendering() const; + + CPT(GeomPrimitive) make_adjacency() const; + virtual int get_min_num_vertices_per_primitive() const; virtual int get_num_unused_vertices_per_primitive() const; diff --git a/panda/src/gobj/geomLinestripsAdjacency.cxx b/panda/src/gobj/geomLinestripsAdjacency.cxx new file mode 100644 index 0000000000..b8c69fc240 --- /dev/null +++ b/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 ©) : + 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 ¶ms) { + GeomLinestripsAdjacency *object = new GeomLinestripsAdjacency(UH_unspecified); + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + object->fillin(scan, manager); + + return object; +} diff --git a/panda/src/gobj/geomLinestripsAdjacency.h b/panda/src/gobj/geomLinestripsAdjacency.h new file mode 100644 index 0000000000..baf3d19255 --- /dev/null +++ b/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 ©); + 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 ¶ms); + +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 diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index 23a905f8e3..b85747a945 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -1021,6 +1021,15 @@ make_patches() const { 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 * table(s). diff --git a/panda/src/gobj/geomPrimitive.h b/panda/src/gobj/geomPrimitive.h index d596ae7a8e..8748c86799 100644 --- a/panda/src/gobj/geomPrimitive.h +++ b/panda/src/gobj/geomPrimitive.h @@ -134,6 +134,7 @@ PUBLISHED: CPT(GeomPrimitive) make_points() const; CPT(GeomPrimitive) make_lines() const; CPT(GeomPrimitive) make_patches() const; + virtual CPT(GeomPrimitive) make_adjacency() const; int get_num_bytes() const; INLINE int get_data_size_bytes() const; diff --git a/panda/src/gobj/geomTriangles.cxx b/panda/src/gobj/geomTriangles.cxx index 98074d63c1..a6e3b9480f 100644 --- a/panda/src/gobj/geomTriangles.cxx +++ b/panda/src/gobj/geomTriangles.cxx @@ -17,6 +17,7 @@ #include "bamReader.h" #include "bamWriter.h" #include "graphicsStateGuardianBase.h" +#include "geomTrianglesAdjacency.h" TypeHandle GeomTriangles::_type_handle; @@ -66,6 +67,76 @@ get_primitive_type() const { 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, 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 * same number of vertices, like triangles, returns the number of vertices per diff --git a/panda/src/gobj/geomTriangles.h b/panda/src/gobj/geomTriangles.h index d78b783b40..390fd78ea2 100644 --- a/panda/src/gobj/geomTriangles.h +++ b/panda/src/gobj/geomTriangles.h @@ -31,6 +31,8 @@ public: virtual PT(GeomPrimitive) make_copy() const; virtual PrimitiveType get_primitive_type() const; + CPT(GeomPrimitive) make_adjacency() const; + virtual int get_num_vertices_per_primitive() const; public: diff --git a/panda/src/gobj/geomTrianglesAdjacency.cxx b/panda/src/gobj/geomTrianglesAdjacency.cxx new file mode 100644 index 0000000000..0dffa74329 --- /dev/null +++ b/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 ©) : + 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 ¶ms) { + GeomTrianglesAdjacency *object = new GeomTrianglesAdjacency(UH_unspecified); + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + object->fillin(scan, manager); + + return object; +} diff --git a/panda/src/gobj/geomTrianglesAdjacency.h b/panda/src/gobj/geomTrianglesAdjacency.h new file mode 100644 index 0000000000..6ebdbdc6eb --- /dev/null +++ b/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 ©); + 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 ¶ms); + +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 diff --git a/panda/src/gobj/geomTristrips.cxx b/panda/src/gobj/geomTristrips.cxx index e6dea04359..ae7bf0bc25 100644 --- a/panda/src/gobj/geomTristrips.cxx +++ b/panda/src/gobj/geomTristrips.cxx @@ -18,6 +18,7 @@ #include "bamReader.h" #include "bamWriter.h" #include "graphicsStateGuardianBase.h" +#include "geomTristripsAdjacency.h" 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, 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 * close_primitive() may legally be called. diff --git a/panda/src/gobj/geomTristrips.h b/panda/src/gobj/geomTristrips.h index 43e7364b22..86778a9976 100644 --- a/panda/src/gobj/geomTristrips.h +++ b/panda/src/gobj/geomTristrips.h @@ -31,6 +31,9 @@ public: virtual PT(GeomPrimitive) make_copy() const; virtual PrimitiveType get_primitive_type() const; virtual int get_geom_rendering() const; + + CPT(GeomPrimitive) make_adjacency() const; + virtual int get_min_num_vertices_per_primitive() const; virtual int get_num_unused_vertices_per_primitive() const; diff --git a/panda/src/gobj/geomTristripsAdjacency.cxx b/panda/src/gobj/geomTristripsAdjacency.cxx new file mode 100644 index 0000000000..eb8a4c2ad8 --- /dev/null +++ b/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 ©) : + 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 ¶ms) { + GeomTristripsAdjacency *object = new GeomTristripsAdjacency(UH_unspecified); + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + object->fillin(scan, manager); + + return object; +} diff --git a/panda/src/gobj/geomTristripsAdjacency.h b/panda/src/gobj/geomTristripsAdjacency.h new file mode 100644 index 0000000000..7811079401 --- /dev/null +++ b/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 ©); + 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 ¶ms); + +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 diff --git a/panda/src/gobj/p3gobj_composite1.cxx b/panda/src/gobj/p3gobj_composite1.cxx index a18171f215..1c874b455d 100644 --- a/panda/src/gobj/p3gobj_composite1.cxx +++ b/panda/src/gobj/p3gobj_composite1.cxx @@ -10,14 +10,18 @@ #include "geomContext.cxx" #include "geomEnums.cxx" #include "geomLines.cxx" +#include "geomLinesAdjacency.cxx" #include "geomLinestrips.cxx" +#include "geomLinestripsAdjacency.cxx" #include "geomMunger.cxx" #include "geomPatches.cxx" #include "geomPoints.cxx" #include "geomPrimitive.cxx" #include "geomTriangles.cxx" +#include "geomTrianglesAdjacency.cxx" #include "geomTrifans.cxx" #include "geomTristrips.cxx" +#include "geomTristripsAdjacency.cxx" #include "geomVertexAnimationSpec.cxx" #include "geomVertexArrayData.cxx" #include "geomVertexArrayFormat.cxx" diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index 45bf35b958..e993b74de2 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -201,11 +201,15 @@ public: const GeomVertexDataPipelineReader *data_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_adj(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_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_adj(const GeomPrimitivePipelineReader *reader, bool force)=0; virtual bool draw_points(const GeomPrimitivePipelineReader *reader, bool force)=0; virtual void end_draw_primitives()=0; diff --git a/tests/gobj/test_geom_primitives.py b/tests/gobj/test_geom_primitives.py new file mode 100644 index 0000000000..49cce11e31 --- /dev/null +++ b/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, + )