From a25a9e655ef27a225e7c48148a3f9037d85db128 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 25 Sep 2014 00:16:44 +0000 Subject: [PATCH] Support strip cut indices and (using those) support direct rendering of linestrips in OpenGL --- panda/src/display/standardMunger.cxx | 10 +- .../glstuff/glGraphicsStateGuardian_src.cxx | 133 +++++++++- .../src/glstuff/glGraphicsStateGuardian_src.h | 2 + panda/src/glstuff/panda_glext.h | 5 + panda/src/gobj/geomEnums.h | 4 +- panda/src/gobj/geomLinestrips.cxx | 53 +++- panda/src/gobj/geomLinestrips.h | 4 + panda/src/gobj/geomPrimitive.I | 26 +- panda/src/gobj/geomPrimitive.cxx | 238 +++++++++++++----- panda/src/gobj/geomPrimitive.h | 4 + panda/src/gobj/geomTristrips.cxx | 20 +- panda/src/gobj/geomTristrips.h | 1 + 12 files changed, 423 insertions(+), 77 deletions(-) diff --git a/panda/src/display/standardMunger.cxx b/panda/src/display/standardMunger.cxx index ab00378536..35c6231cd5 100644 --- a/panda/src/display/standardMunger.cxx +++ b/panda/src/display/standardMunger.cxx @@ -178,7 +178,10 @@ munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, // Even beyond munging the vertex format, we have to convert the // Geom itself into a new primitive type the GSG can render // directly. - if ((unsupported_bits & Geom::GR_composite_bits) != 0) { + // If we don't support a strip cut index, it might be faster to + // just decompose it rather than draw them one by one. + if ((unsupported_bits & Geom::GR_composite_bits) != 0 || + (unsupported_bits & Geom::GR_strip_cut_index) != 0) { // This decomposes everything in the primitive, so that if (for // instance) the primitive contained both strips and fans, but // the GSG didn't support fans, it would decompose the strips @@ -224,7 +227,10 @@ premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) { // Even beyond munging the vertex format, we have to convert the // Geom itself into a new primitive type the GSG can render // directly. - if ((unsupported_bits & Geom::GR_composite_bits) != 0) { + // If we don't support a strip cut index, it might be faster to + // just decompose it rather than draw them one by one. + if ((unsupported_bits & Geom::GR_composite_bits) != 0 || + (unsupported_bits & Geom::GR_strip_cut_index) != 0) { // This decomposes everything in the primitive, so that if (for // instance) the primitive contained both strips and fans, but // the GSG didn't support fans, it would decompose the strips diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 818e03e7bc..a4c1211372 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -515,6 +515,7 @@ reset() { Geom::GR_point | Geom::GR_point_uniform_size | Geom::GR_indexed_other | Geom::GR_triangle_strip | Geom::GR_triangle_fan | + Geom::GR_line_strip | Geom::GR_flat_last_vertex; _supports_point_parameters = false; @@ -551,6 +552,30 @@ reset() { _supported_geom_rendering |= Geom::GR_point_sprite; } + _glPrimitiveRestartIndex = NULL; + + if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_ES3_compatibility")) { + // As long as we enable this, OpenGL will always use the highest possible index + // for a numeric type as strip cut index, which coincides with our convention. + // This saves us a call to glPrimitiveRestartIndex. + glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + _supported_geom_rendering |= Geom::GR_strip_cut_index; + + } else if (is_at_least_gl_version(3, 1)) { + glEnable(GL_PRIMITIVE_RESTART); + _supported_geom_rendering |= Geom::GR_strip_cut_index; + + _glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC) + get_extension_func("glPrimitiveRestartIndex"); + + } else if (has_extension("GL_NV_primitive_restart")) { + glEnable(GL_PRIMITIVE_RESTART_NV); + _supported_geom_rendering |= Geom::GR_strip_cut_index; + + _glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC) + get_extension_func("glPrimitiveRestartIndexNV"); + } + _supports_vertex_blend = has_extension("GL_ARB_vertex_blend"); if (_supports_vertex_blend) { @@ -3800,7 +3825,113 @@ draw_lines(const GeomPrimitivePipelineReader *reader, bool force) { //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force) { - return false; + PStatGPUTimer timer(this, _draw_primitive_pcollector, reader->get_current_thread()); + + report_my_gl_errors(); + +#ifndef NDEBUG + if (GLCAT.is_spam()) { + GLCAT.spam() << "draw_linestrips: " << *(reader->get_object()) << "\n"; + } +#endif // NDEBUG + +#ifdef SUPPORT_IMMEDIATE_MODE + if (_use_sender) { + draw_immediate_composite_primitives(reader, GL_LINE_STRIP); + + } else +#endif // SUPPORT_IMMEDIATE_MODE + { + if (reader->is_indexed() && + (_supported_geom_rendering & GeomEnums::GR_strip_cut_index) != 0) { + // One long triangle strip, connected by strip cut indices. + if (_glPrimitiveRestartIndex != NULL) { + _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; + } +#ifndef OPENGLES + if (_supports_geometry_instancing && _instance_count > 0) { + _glDrawElementsInstanced(GL_LINE_STRIP, num_vertices, + get_numeric_type(reader->get_index_type()), + client_pointer, _instance_count); + } else +#endif + { + _glDrawRangeElements(GL_LINE_STRIP, + reader->get_min_vertex(), + reader->get_max_vertex(), + num_vertices, + get_numeric_type(reader->get_index_type()), + client_pointer); + } + } 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); +#ifndef OPENGLES + if (_supports_geometry_instancing && _instance_count > 0) { + _glDrawElementsInstanced(GL_LINE_STRIP, ends[i] - start, + get_numeric_type(reader->get_index_type()), + client_pointer + start * index_stride, + _instance_count); + } else +#endif + { + _glDrawRangeElements(GL_LINE_STRIP, + 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); +#ifndef OPENGLES + if (_supports_geometry_instancing && _instance_count > 0) { + _glDrawArraysInstanced(GL_LINE_STRIP, first_vertex + start, + ends[i] - start, _instance_count); + } else +#endif + { + glDrawArrays(GL_LINE_STRIP, first_vertex + start, + ends[i] - start); + } + start = ends[i] + 1; + } + } + } + } + + report_my_gl_errors(); + return true; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index b47c22ce48..bfb314e3a1 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -598,6 +598,8 @@ public: PFNGLPOINTPARAMETERFVPROC _glPointParameterfv; bool _supports_point_sprite; + PFNGLPRIMITIVERESTARTINDEXPROC _glPrimitiveRestartIndex; + bool _supports_vertex_blend; PFNGLWEIGHTPOINTERARBPROC _glWeightPointer; PFNGLVERTEXBLENDARBPROC _glVertexBlend; diff --git a/panda/src/glstuff/panda_glext.h b/panda/src/glstuff/panda_glext.h index 6af0dacb1e..1fb1c55a0e 100644 --- a/panda/src/glstuff/panda_glext.h +++ b/panda/src/glstuff/panda_glext.h @@ -1163,6 +1163,11 @@ extern "C" { /* reuse GL_TEXTURE_IMMUTABLE_FORMAT */ #endif +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#endif + #ifndef GL_ARB_multitexture #define GL_TEXTURE0_ARB 0x84C0 #define GL_TEXTURE1_ARB 0x84C1 diff --git a/panda/src/gobj/geomEnums.h b/panda/src/gobj/geomEnums.h index e187398746..ffa4d14515 100644 --- a/panda/src/gobj/geomEnums.h +++ b/panda/src/gobj/geomEnums.h @@ -125,6 +125,9 @@ PUBLISHED: // The union of all of the above composite types. GR_composite_bits = 0x01c00, + // If strip-cut indices are used to restart a composite primitive. + GR_strip_cut_index = 0x20000, + // If the shade model requires a particular vertex for flat shading. GR_flat_first_vertex = 0x02000, GR_flat_last_vertex = 0x04000, @@ -216,4 +219,3 @@ EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, GeomEnums::NumericType nume EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, GeomEnums::Contents contents); #endif - diff --git a/panda/src/gobj/geomLinestrips.cxx b/panda/src/gobj/geomLinestrips.cxx index ea44f6b7ee..33c4d30814 100644 --- a/panda/src/gobj/geomLinestrips.cxx +++ b/panda/src/gobj/geomLinestrips.cxx @@ -89,7 +89,11 @@ get_primitive_type() const { int GeomLinestrips:: get_geom_rendering() const { if (is_indexed()) { - return GR_line_strip | GR_indexed_other; + if (get_num_primitives() > 1) { + return GR_line_strip | GR_indexed_other | GR_strip_cut_index; + } else { + return GR_line_strip | GR_indexed_other; + } } else { return GR_line_strip; } @@ -106,6 +110,20 @@ get_min_num_vertices_per_primitive() const { return 2; } +//////////////////////////////////////////////////////////////////// +// Function: GeomLinestrips::get_num_unused_vertices_per_primitive +// Access: Public, Virtual +// Description: Returns the number of vertices that are added between +// primitives that aren't, strictly speaking, part of +// the primitives themselves. This is used, for +// instance, to define degenerate triangles to connect +// otherwise disconnected triangle strips. +//////////////////////////////////////////////////////////////////// +int GeomLinestrips:: +get_num_unused_vertices_per_primitive() const { + return 1; +} + //////////////////////////////////////////////////////////////////// // Function: GeomLinestrips::draw // Access: Public, Virtual @@ -138,9 +156,13 @@ decompose_impl() const { lines->set_shade_model(get_shade_model()); CPTA_int ends = get_ends(); - int vi = 0; + int num_unused = get_num_unused_vertices_per_primitive(); + + 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 + 1 <= end, lines.p()); int v0 = get_vertex(vi); @@ -210,6 +232,33 @@ rotate_impl() const { return new_vertices; } +//////////////////////////////////////////////////////////////////// +// Function: GeomLinestrips::requires_unused_vertices +// Access: Protected, Virtual +// Description: Should be redefined to return true in any primitive +// that implements append_unused_vertices(). +//////////////////////////////////////////////////////////////////// +bool GeomLinestrips:: +requires_unused_vertices() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomLinestrips::append_unused_vertices +// Access: Protected, Virtual +// Description: Called when a new primitive is begun (other than the +// first primitive), this should add some degenerate +// vertices between primitives, if the primitive type +// requires that. The second parameter is the first +// vertex that begins the new primitive. +//////////////////////////////////////////////////////////////////// +void GeomLinestrips:: +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()); +} + //////////////////////////////////////////////////////////////////// // Function: GeomLinestrips::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/geomLinestrips.h b/panda/src/gobj/geomLinestrips.h index 4d373a9d21..c7bfea935f 100644 --- a/panda/src/gobj/geomLinestrips.h +++ b/panda/src/gobj/geomLinestrips.h @@ -34,6 +34,7 @@ public: 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, @@ -43,6 +44,9 @@ public: protected: virtual CPT(GeomPrimitive) decompose_impl() const; virtual CPT(GeomVertexArrayData) rotate_impl() const; + virtual bool requires_unused_vertices() const; + virtual void append_unused_vertices(GeomVertexArrayData *vertices, + int vertex); public: static void register_with_read_factory(); diff --git a/panda/src/gobj/geomPrimitive.I b/panda/src/gobj/geomPrimitive.I index da47eb154f..bda47efc56 100644 --- a/panda/src/gobj/geomPrimitive.I +++ b/panda/src/gobj/geomPrimitive.I @@ -129,7 +129,7 @@ get_first_vertex() const { //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::get_num_vertices // Access: Published -// Description: Returns the number of vertices used by all the +// Description: Returns the number of indices used by all the // primitives in this object. //////////////////////////////////////////////////////////////////// INLINE int GeomPrimitive:: @@ -310,6 +310,20 @@ get_index_stride() const { return reader.get_index_stride(); } +//////////////////////////////////////////////////////////////////// +// Function: GeomPrimitive::get_strip_cut_index +// Access: Published +// Description: If relevant, returns the index value that may be +// used in some cases to signify the end of a +// primitive. This is typically the highest value +// that the numeric type can store. +//////////////////////////////////////////////////////////////////// +INLINE int GeomPrimitive:: +get_strip_cut_index() const { + CDReader cdata(_cycler); + return get_strip_cut_index(cdata->_index_type); +} + //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::get_ends // Access: Published @@ -679,6 +693,16 @@ get_read_pointer(bool force) const { return _vertices_reader->get_read_pointer(force); } +//////////////////////////////////////////////////////////////////// +// Function: GeomPrimitivePipelineReader::get_strip_cut_index +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int GeomPrimitivePipelineReader:: +get_strip_cut_index() const { + return GeomPrimitive::get_strip_cut_index(_cdata->_index_type); +} + //////////////////////////////////////////////////////////////////// // Function: GeomPrimitivePipelineReader::get_ends // Access: Public diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index 86d07499d0..8b7cb19e15 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -61,18 +61,18 @@ make_cow_copy() { //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::Constructor // Access: Published -// Description: +// Description: //////////////////////////////////////////////////////////////////// GeomPrimitive:: GeomPrimitive(GeomPrimitive::UsageHint usage_hint) { CDWriter cdata(_cycler, true); cdata->_usage_hint = usage_hint; } - + //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::Copy Constructor // Access: Published -// Description: +// Description: //////////////////////////////////////////////////////////////////// GeomPrimitive:: GeomPrimitive(const GeomPrimitive ©) : @@ -80,7 +80,7 @@ GeomPrimitive(const GeomPrimitive ©) : _cycler(copy._cycler) { } - + //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::Copy Assignment Operator // Access: Published @@ -98,7 +98,7 @@ operator = (const GeomPrimitive ©) { //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::Destructor // Access: Published, Virtual -// Description: +// Description: //////////////////////////////////////////////////////////////////// GeomPrimitive:: ~GeomPrimitive() { @@ -197,7 +197,7 @@ add_vertex(int vertex) { int num_primitives = get_num_primitives(); if (num_primitives > 0 && - requires_unused_vertices() && + requires_unused_vertices() && get_num_vertices() == get_primitive_end(num_primitives - 1)) { // If we are beginning a new primitive, give the derived class a // chance to insert some degenerate vertices. @@ -224,7 +224,7 @@ add_vertex(int vertex) { cdata->_got_minmax = false; return; } - + // Otherwise, we need to suddenly become an indexed primitive. do_make_indexed(cdata); } @@ -288,7 +288,7 @@ add_consecutive_vertices(int start, int num_vertices) { cdata->_got_minmax = false; return; } - + // Otherwise, we need to suddenly become an indexed primitive. do_make_indexed(cdata); } @@ -468,12 +468,18 @@ offset_vertices(int offset) { recompute_minmax(cdata); nassertv(cdata->_got_minmax); } - + consider_elevate_index_type(cdata, cdata->_max_vertex + offset); + int strip_cut_index = get_strip_cut_index(cdata->_index_type); + GeomVertexRewriter index(do_modify_vertices(cdata), 0); while (!index.is_at_end()) { - index.set_data1i(index.get_data1i() + offset); + int vertex = index.get_data1i(); + + if (vertex != strip_cut_index) { + index.set_data1i(vertex + offset); + } } } else { @@ -483,7 +489,7 @@ offset_vertices(int offset) { cdata->_modified = Geom::get_next_modified(); cdata->_got_minmax = false; - consider_elevate_index_type(cdata, + consider_elevate_index_type(cdata, cdata->_first_vertex + cdata->_num_vertices - 1); } } @@ -516,14 +522,19 @@ offset_vertices(int offset, int begin_row, int end_row) { if (is_indexed()) { CDWriter cdata(_cycler, true); - + + int strip_cut_index = get_strip_cut_index(cdata->_index_type); + // Calculate the maximum vertex over our range. int max_vertex = 0; { GeomVertexReader index_r(cdata->_vertices.get_read_pointer(), 0); index_r.set_row_unsafe(begin_row); for (int j = begin_row; j < end_row; ++j) { - max_vertex = max(max_vertex, index_r.get_data1i()); + int vertex = index_r.get_data1i(); + if (vertex != strip_cut_index) { + max_vertex = max(max_vertex, vertex); + } } } @@ -532,7 +543,10 @@ offset_vertices(int offset, int begin_row, int end_row) { GeomVertexRewriter index(do_modify_vertices(cdata), 0); index.set_row_unsafe(begin_row); for (int j = begin_row; j < end_row; ++j) { - index.set_data1i(index.get_data1i() + offset); + int vertex = index.get_data1i(); + if (vertex != strip_cut_index) { + index.set_data1i(vertex + offset); + } } } else { @@ -544,7 +558,7 @@ offset_vertices(int offset, int begin_row, int end_row) { cdata->_modified = Geom::get_next_modified(); cdata->_got_minmax = false; - consider_elevate_index_type(cdata, + consider_elevate_index_type(cdata, cdata->_first_vertex + cdata->_num_vertices - 1); } } @@ -554,17 +568,20 @@ offset_vertices(int offset, int begin_row, int end_row) { // Access: Published // Description: Converts the primitive from indexed to nonindexed by // duplicating vertices as necessary into the indicated -// dest GeomVertexData. +// dest GeomVertexData. Note: does not support +// primitives with strip cut indices. //////////////////////////////////////////////////////////////////// void GeomPrimitive:: make_nonindexed(GeomVertexData *dest, const GeomVertexData *source) { Thread *current_thread = Thread::get_current_thread(); int num_vertices = get_num_vertices(); int dest_start = dest->get_num_rows(); + int strip_cut_index = get_strip_cut_index(); dest->set_num_rows(dest_start + num_vertices); for (int i = 0; i < num_vertices; ++i) { int v = get_vertex(i); + nassertd(v != strip_cut_index) continue; dest->copy_row_from(dest_start + i, source, v, current_thread); } @@ -596,14 +613,18 @@ pack_vertices(GeomVertexData *dest, const GeomVertexData *source) { int num_vertices = get_num_vertices(); int dest_start = dest->get_num_rows(); + int strip_cut_index = get_strip_cut_index(); for (int i = 0; i < num_vertices; ++i) { int v = get_vertex(i); + if (v == strip_cut_index) { + continue; + } // Try to add the relation { v : size() }. If that succeeds, // great; if it doesn't, look up whatever we previously added // for v. - pair result = + pair result = copied_indices.insert(CopiedIndices::value_type(v, (int)copied_indices.size())); int v2 = (*result.first).second + dest_start; index.add_data1i(v2); @@ -613,7 +634,7 @@ pack_vertices(GeomVertexData *dest, const GeomVertexData *source) { dest->copy_row_from(v2, source, v, current_thread); } } - + set_vertices(new_vertices); } } @@ -643,7 +664,7 @@ make_indexed() { // Function: GeomPrimitive::get_primitive_start // Access: Published // Description: Returns the element within the _vertices list at which -// the nth primitive starts. +// the nth primitive starts. // // If i is one more than the highest valid primitive // vertex, the return value will be one more than the @@ -723,7 +744,7 @@ get_primitive_num_vertices(int n) const { } else { int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive(); return cdata->_ends[n] - cdata->_ends[n - 1] - num_unused_vertices_per_primitive; - } + } } else { // This is a simple primitive type like a triangle: each primitive @@ -732,6 +753,28 @@ get_primitive_num_vertices(int n) const { } } +//////////////////////////////////////////////////////////////////// +// Function: GeomPrimitive::get_num_used_vertices +// Access: Published +// Description: Returns the number of vertices used by all of the +// primitives. This is the same as summing +// get_primitive_num_vertices(n) for n in +// get_num_primitives(). It is like get_num_vertices +// except that it excludes all of the degenerate +// vertices and strip-cut indices. +//////////////////////////////////////////////////////////////////// +int GeomPrimitive:: +get_num_used_vertices() const { + int num_primitives = get_num_primitives(); + + if (num_primitives > 0) { + return get_num_vertices() - ((num_primitives - 1) * + get_num_unused_vertices_per_primitive()); + } else { + return 0; + } +} + //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::get_primitive_min_vertex // Access: Published @@ -959,10 +1002,14 @@ make_points() const { int num_vertices = get_num_vertices(); if (is_indexed()) { CPT(GeomVertexArrayData) vertices = get_vertices(); + int strip_cut_index = get_strip_cut_index(); GeomVertexReader index(vertices, 0); for (int vi = 0; vi < num_vertices; ++vi) { nassertr(!index.is_at_end(), NULL); - bits.set_bit(index.get_data1i()); + int vertex = index.get_data1i(); + if (vertex != strip_cut_index) { + bits.set_bit(vertex); + } } } else { int first_vertex = get_first_vertex(); @@ -996,14 +1043,13 @@ make_points() const { // Access: Published // Description: Decomposes a complex primitive type into a simpler // primitive type, for instance triangle strips to -// triangles, puts these in a new GeomPatches objectand returns a pointer to the new primitive +// triangles, puts these in a new GeomPatches object +// 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 triangles on the -// primitive without having to write handlers for each -// possible kind of primitive type. +// to use tesselation shaders on arbitrary geometry. //////////////////////////////////////////////////////////////////// CPT(GeomPrimitive) GeomPrimitive:: make_patches() const { @@ -1079,7 +1125,7 @@ request_resident() const { //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::output // Access: Published, Virtual -// Description: +// Description: //////////////////////////////////////////////////////////////////// void GeomPrimitive:: output(ostream &out) const { @@ -1090,7 +1136,7 @@ output(ostream &out) const { //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::write // Access: Published, Virtual -// Description: +// Description: //////////////////////////////////////////////////////////////////// void GeomPrimitive:: write(ostream &out, int indent_level) const { @@ -1448,7 +1494,7 @@ is_prepared(PreparedGraphicsObjects *prepared_objects) const { // rendered. //////////////////////////////////////////////////////////////////// IndexBufferContext *GeomPrimitive:: -prepare_now(PreparedGraphicsObjects *prepared_objects, +prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg) { nassertr(is_indexed(), NULL); @@ -1558,11 +1604,44 @@ clear_prepared(PreparedGraphicsObjects *prepared_objects) { //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::get_highest_index_value // Access: Private, Static -// Description: Returns the largest index value that can be stored in -// an index of the indicated type. +// Description: Returns the largest index value that can be stored +// in an index of the indicated type, minus one (to +// leave room for a potential strip cut index) //////////////////////////////////////////////////////////////////// int GeomPrimitive:: get_highest_index_value(NumericType index_type) { + // Reserve the highest possible index because implementations use + // this as a strip-cut index. + switch (index_type) { + case NT_uint8: + return 0xff - 1; + + case NT_uint16: + return 0xffff - 1; + + case NT_uint32: + // We don't actually allow use of the sign bit, since all of our + // functions receive an "int" instead of an "unsigned int". + return 0x7fffffff - 1; + + default: + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomPrimitive::get_strip_cut_index +// Access: Private, Static +// Description: Returns the index of the indicated type that is +// reserved for use as a strip cut index, if enabled +// for the primitive. When the renderer encounters +// this index, it will restart the primitive. This +// is guaranteed not to point to an actual vertex. +//////////////////////////////////////////////////////////////////// +int GeomPrimitive:: +get_strip_cut_index(NumericType index_type) { + // Reserve the highest possible index because implementations use + // this as a strip-cut index. switch (index_type) { case NT_uint8: return 0xff; @@ -1571,12 +1650,8 @@ get_highest_index_value(NumericType index_type) { return 0xffff; case NT_uint32: - // We don't actually allow use of the sign bit, since all of our - // functions receive an "int" instead of an "unsigned int". - return 0x7fffffff; - default: - return 0; + return -1; } } @@ -1593,7 +1668,7 @@ get_highest_index_value(NumericType index_type) { //////////////////////////////////////////////////////////////////// void GeomPrimitive:: calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, - bool &found_any, + bool &found_any, const GeomVertexData *vertex_data, bool got_mat, const LMatrix4 &mat, const InternalName *column_name, @@ -1613,7 +1688,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, for (int i = 0; i < cdata->_num_vertices; i++) { reader.set_row_unsafe(cdata->_first_vertex + i); LPoint3 vertex = mat.xform_point(reader.get_data3()); - + if (found_any) { min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]), @@ -1631,7 +1706,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, for (int i = 0; i < cdata->_num_vertices; i++) { reader.set_row_unsafe(cdata->_first_vertex + i); const LVecBase3 &vertex = reader.get_data3(); - + if (found_any) { min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]), @@ -1650,13 +1725,17 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, } else { // Indexed case. GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread); + int strip_cut_index = get_strip_cut_index(cdata->_index_type); if (got_mat) { while (!index.is_at_end()) { int ii = index.get_data1i(); + if (ii == strip_cut_index) { + continue; + } reader.set_row_unsafe(ii); LPoint3 vertex = mat.xform_point(reader.get_data3()); - + if (found_any) { min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]), @@ -1673,9 +1752,12 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, } else { while (!index.is_at_end()) { int ii = index.get_data1i(); + if (ii == strip_cut_index) { + continue; + } reader.set_row_unsafe(ii); const LVecBase3 &vertex = reader.get_data3(); - + if (found_any) { min_point.set(min(min_point[0], vertex[0]), min(min_point[1], vertex[1]), @@ -1797,63 +1879,77 @@ recompute_minmax(GeomPrimitive::CData *cdata) { cdata->_max_vertex = 0; cdata->_mins.clear(); cdata->_maxs.clear(); - + } else if (get_num_vertices_per_primitive() == 0) { // This is a complex primitive type like a triangle strip; compute // the minmax of each primitive (as well as the overall minmax). GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0); - + cdata->_mins = make_index_data(); cdata->_maxs = make_index_data(); - + GeomVertexWriter mins(cdata->_mins.get_write_pointer(), 0); mins.reserve_num_rows(cdata->_ends.size()); GeomVertexWriter maxs(cdata->_maxs.get_write_pointer(), 0); maxs.reserve_num_rows(cdata->_ends.size()); - + int pi = 0; - + unsigned int vertex = index.get_data1i(); cdata->_min_vertex = vertex; cdata->_max_vertex = vertex; unsigned int min_prim = vertex; unsigned int max_prim = vertex; - + + int num_unused_vertices = get_num_unused_vertices_per_primitive(); + for (int vi = 1; vi < num_vertices; ++vi) { nassertv(!index.is_at_end()); - unsigned int vertex = index.get_data1i(); - cdata->_min_vertex = min(cdata->_min_vertex, vertex); - cdata->_max_vertex = max(cdata->_max_vertex, vertex); - nassertv(pi < (int)cdata->_ends.size()); + + unsigned int vertex; + if (vi == cdata->_ends[pi]) { + // Skip unused vertices, since they won't be very relevant and + // may contain a strip-cut index, which would distort the result. + if (num_unused_vertices > 0) { + vi += num_unused_vertices; + index.set_row_unsafe(vi); + } + vertex = index.get_data1i(); + mins.add_data1i(min_prim); maxs.add_data1i(max_prim); min_prim = vertex; max_prim = vertex; ++pi; - + } else { + vertex = index.get_data1i(); min_prim = min(min_prim, vertex); max_prim = max(max_prim, vertex); } + + cdata->_min_vertex = min(cdata->_min_vertex, vertex); + cdata->_max_vertex = max(cdata->_max_vertex, vertex); } + mins.add_data1i(min_prim); maxs.add_data1i(max_prim); nassertv(mins.get_array_data()->get_num_rows() == (int)cdata->_ends.size()); - + } else { // This is a simple primitive type like a triangle; just compute // the overall minmax. GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0); - + cdata->_mins.clear(); cdata->_maxs.clear(); - + unsigned int vertex = index.get_data1i(); cdata->_min_vertex = vertex; cdata->_max_vertex = vertex; - + for (int vi = 1; vi < num_vertices; ++vi) { nassertv(!index.is_at_end()); unsigned int vertex = index.get_data1i(); @@ -1862,7 +1958,7 @@ recompute_minmax(GeomPrimitive::CData *cdata) { } } } - + cdata->_got_minmax = true; } @@ -1899,22 +1995,25 @@ do_make_indexed(CData *cdata) { //////////////////////////////////////////////////////////////////// void GeomPrimitive:: consider_elevate_index_type(CData *cdata, int vertex) { + // Note that we reserve the highest possible index of a particular + // index type (ie. -1) because this is commonly used as a strip-cut + // (also known as primitive restart) index. switch (cdata->_index_type) { case NT_uint8: - if (vertex > 0xff) { + if (vertex >= 0xff) { do_set_index_type(cdata, NT_uint16); } break; case NT_uint16: - if (vertex > 0xffff) { + if (vertex >= 0xffff) { do_set_index_type(cdata, NT_uint32); } break; case NT_uint32: // Not much we can do here. - nassertv(vertex <= 0x7fffffff); + nassertv(vertex < 0x7fffffff); break; default: @@ -1929,6 +2028,9 @@ consider_elevate_index_type(CData *cdata, int vertex) { //////////////////////////////////////////////////////////////////// void GeomPrimitive:: do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) { + int old_strip_cut_index = get_strip_cut_index(cdata->_index_type); + int new_strip_cut_index = get_strip_cut_index(index_type); + cdata->_index_type = index_type; if (gobj_cat.is_debug()) { @@ -1938,7 +2040,7 @@ do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) { if (!cdata->_vertices.is_null()) { CPT(GeomVertexArrayFormat) new_format = get_index_format(); - + CPT(GeomVertexArrayData) array_obj = cdata->_vertices.get_read_pointer(); if (array_obj->get_array_format() != new_format) { PT(GeomVertexArrayData) new_vertices = make_index_data(); @@ -1946,9 +2048,13 @@ do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) { GeomVertexReader from(array_obj, 0); GeomVertexWriter to(new_vertices, 0); - + while (!from.is_at_end()) { - to.set_data1i(from.get_data1i()); + int index = from.get_data1i(); + if (index == old_strip_cut_index) { + index = new_strip_cut_index; + } + to.set_data1i(index); } cdata->_vertices = new_vertices; cdata->_got_minmax = false; @@ -2056,7 +2162,7 @@ int GeomPrimitive::CData:: complete_pointers(TypedWritable **p_list, BamReader *manager) { int pi = CycleData::complete_pointers(p_list, manager); - _vertices = DCAST(GeomVertexArrayData, p_list[pi++]); + _vertices = DCAST(GeomVertexArrayData, p_list[pi++]); if (manager->get_file_minor_ver() < 6 && !_vertices.is_null()) { // Older bam files might have a meaningless number in @@ -2106,7 +2212,7 @@ check_minmax() const { #ifdef DO_PIPELINING unref_delete((CycleData *)_cdata); #endif - GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object.p())->_cycler, + GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object.p())->_cycler, false, _current_thread); ((GeomPrimitivePipelineReader *)this)->_cdata = fresh_cdata; #ifdef DO_PIPELINING @@ -2132,7 +2238,7 @@ check_minmax() const { //////////////////////////////////////////////////////////////////// // Function: GeomPrimitivePipelineReader::get_first_vertex // Access: Public -// Description: +// Description: //////////////////////////////////////////////////////////////////// int GeomPrimitivePipelineReader:: get_first_vertex() const { @@ -2170,7 +2276,7 @@ get_vertex(int i) const { //////////////////////////////////////////////////////////////////// // Function: GeomPrimitivePipelineReader::get_num_primitives // Access: Public -// Description: +// Description: //////////////////////////////////////////////////////////////////// int GeomPrimitivePipelineReader:: get_num_primitives() const { diff --git a/panda/src/gobj/geomPrimitive.h b/panda/src/gobj/geomPrimitive.h index 18213be302..ef26ad4977 100644 --- a/panda/src/gobj/geomPrimitive.h +++ b/panda/src/gobj/geomPrimitive.h @@ -118,6 +118,7 @@ PUBLISHED: int get_primitive_start(int n) const; int get_primitive_end(int n) const; int get_primitive_num_vertices(int n) const; + int get_num_used_vertices() const; INLINE int get_num_faces() const; INLINE int get_primitive_num_faces(int n) const; @@ -163,6 +164,7 @@ PUBLISHED: void set_nonindexed_vertices(int first_vertex, int num_vertices); INLINE int get_index_stride() const; + INLINE int get_strip_cut_index() const; INLINE CPTA_int get_ends() const; PTA_int modify_ends(); @@ -194,6 +196,7 @@ public: private: void clear_prepared(PreparedGraphicsObjects *prepared_objects); static int get_highest_index_value(NumericType index_type); + static int get_strip_cut_index(NumericType index_type); public: virtual bool draw(GraphicsStateGuardianBase *gsg, @@ -358,6 +361,7 @@ public: INLINE int get_index_stride() const; INLINE const GeomVertexArrayDataHandle *get_vertices_reader() const; INLINE const unsigned char *get_read_pointer(bool force) const; + INLINE int get_strip_cut_index() const; INLINE CPTA_int get_ends() const; INLINE CPT(GeomVertexArrayData) get_mins() const; INLINE CPT(GeomVertexArrayData) get_maxs() const; diff --git a/panda/src/gobj/geomTristrips.cxx b/panda/src/gobj/geomTristrips.cxx index 3d141687c2..a8f20512d1 100644 --- a/panda/src/gobj/geomTristrips.cxx +++ b/panda/src/gobj/geomTristrips.cxx @@ -95,6 +95,17 @@ get_geom_rendering() const { } } +//////////////////////////////////////////////////////////////////// +// Function: GeomTristrips::get_min_num_vertices_per_primitive +// Access: Public, Virtual +// Description: Returns the minimum number of vertices that must be +// added before close_primitive() may legally be called. +//////////////////////////////////////////////////////////////////// +int GeomTristrips:: +get_min_num_vertices_per_primitive() const { + return 3; +} + //////////////////////////////////////////////////////////////////// // Function: GeomTristrips::get_num_unused_vertices_per_primitive // Access: Public, Virtual @@ -142,6 +153,7 @@ decompose_impl() const { CPTA_int ends = get_ends(); int num_vertices = get_num_vertices(); + int num_unused = get_num_unused_vertices_per_primitive(); // We need a slightly different algorithm for SM_flat_first_vertex // than for SM_flat_last_vertex, to preserve the key vertex in the @@ -150,11 +162,11 @@ decompose_impl() const { if (get_shade_model() == SM_flat_first_vertex) { // Preserve the first vertex of each component triangle as the // first vertex of each generated triangle. - int vi = -2; + int vi = -num_unused; int li = 0; while (li < (int)ends.size()) { // Skip unused vertices between tristrips. - vi += 2; + vi += num_unused; int end = ends[li]; nassertr(vi + 2 <= end, NULL); int v0 = get_vertex(vi); @@ -192,11 +204,11 @@ decompose_impl() const { } else { // Preserve the last vertex of each component triangle as the // last vertex of each generated triangle. - int vi = -2; + int vi = -num_unused; int li = 0; while (li < (int)ends.size()) { // Skip unused vertices between tristrips. - vi += 2; + vi += num_unused; int end = ends[li]; nassertr(vi + 2 <= end, NULL); int v0 = get_vertex(vi); diff --git a/panda/src/gobj/geomTristrips.h b/panda/src/gobj/geomTristrips.h index 2e0c8c4a71..387d72485b 100644 --- a/panda/src/gobj/geomTristrips.h +++ b/panda/src/gobj/geomTristrips.h @@ -33,6 +33,7 @@ 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: