diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index d8f72a508a..cfcb0e9a8e 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -2079,20 +2079,18 @@ void CLP(GraphicsStateGuardian):: draw_tristrips(const qpGeomTristrips *primitive) { setup_antialias_polygon(); - int min_vertex = primitive->get_min_vertex(); - int max_vertex = primitive->get_max_vertex(); CPTA_ushort vertices = primitive->get_flat_last_vertices(); CPTA_int ends = primitive->get_ends(); + CPTA_ushort mins = primitive->get_mins(); + CPTA_ushort maxs = primitive->get_maxs(); + nassertv(mins.size() == ends.size() && maxs.size() == ends.size()); - int num_primitives = primitive->get_num_primitives(); - int start = 0; - for (CPTA_int::const_iterator pi = ends.begin(); pi != ends.end(); ++pi) { - int end = (*pi); - + unsigned int start = 0; + for (size_t i = 0; i < ends.size(); i++) { _glDrawRangeElements(GL_TRIANGLE_STRIP, - min_vertex, max_vertex, end - start, + mins[i], maxs[i], ends[i] - start, GL_UNSIGNED_SHORT, vertices + start); - start = end; + start = ends[i]; } report_my_gl_errors(); diff --git a/panda/src/gobj/qpgeomPrimitive.I b/panda/src/gobj/qpgeomPrimitive.I index d1ef9076dd..abcaf7a40a 100644 --- a/panda/src/gobj/qpgeomPrimitive.I +++ b/panda/src/gobj/qpgeomPrimitive.I @@ -145,7 +145,7 @@ get_flat_last_vertices() const { } //////////////////////////////////////////////////////////////////// -// Function: qpGeomPrimitive::get_vertices +// Function: qpGeomPrimitive::get_ends // Access: Published // Description: Returns a const pointer to the primitive ends // array so application code can read it directly. Do @@ -162,6 +162,40 @@ get_ends() const { return cdata->_ends; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::get_mins +// Access: Published +// Description: Returns a const pointer to the primitive mins +// array so application code can read it directly. Do +// not attempt to modify the returned array; use +// modify_mins() or set_mins() for this. +// +// Note that simple primitive types, like triangles, do +// not have a mins array. +//////////////////////////////////////////////////////////////////// +INLINE CPTA_ushort qpGeomPrimitive:: +get_mins() const { + CDReader cdata(_cycler); + return cdata->_mins; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::get_maxs +// Access: Published +// Description: Returns a const pointer to the primitive maxs +// array so application code can read it directly. Do +// not attempt to modify the returned array; use +// modify_maxs() or set_maxs() for this. +// +// Note that simple primitive types, like triangles, do +// not have a maxs array. +//////////////////////////////////////////////////////////////////// +INLINE CPTA_ushort qpGeomPrimitive:: +get_maxs() const { + CDReader cdata(_cycler); + return cdata->_maxs; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::get_min_vertex // Access: Published @@ -177,6 +211,22 @@ get_min_vertex() const { return cdata->_min_vertex; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::get_min_vertex +// Access: Published +// Description: Returns the minimum vertex index number used by the +// ith primitive in this object. +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomPrimitive:: +get_min_vertex(int i) const { + CDReader cdata(_cycler); + if (!cdata->_got_minmax) { + ((qpGeomPrimitive *)this)->recompute_minmax(); + } + nassertr(i >= 0 && i < (int)cdata->_mins.size(), -1); + return cdata->_mins[i]; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::get_max_vertex // Access: Published @@ -192,6 +242,22 @@ get_max_vertex() const { return cdata->_max_vertex; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::get_max_vertex +// Access: Published +// Description: Returns the maximum vertex index number used by the +// ith primitive in this object. +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomPrimitive:: +get_max_vertex(int i) const { + CDReader cdata(_cycler); + if (!cdata->_got_minmax) { + ((qpGeomPrimitive *)this)->recompute_minmax(); + } + nassertr(i >= 0 && i < (int)cdata->_maxs.size(), -1); + return cdata->_maxs[i]; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::get_primitive_end // Access: Published @@ -228,6 +294,8 @@ CData(const qpGeomPrimitive::CData ©) : _vertices(copy._vertices), _rotated_vertices(copy._rotated_vertices), _ends(copy._ends), + _mins(copy._mins), + _maxs(copy._maxs), _got_minmax(copy._got_minmax), _min_vertex(copy._min_vertex), _max_vertex(copy._max_vertex) diff --git a/panda/src/gobj/qpgeomPrimitive.cxx b/panda/src/gobj/qpgeomPrimitive.cxx index 94c248f873..c3c6fa04e9 100644 --- a/panda/src/gobj/qpgeomPrimitive.cxx +++ b/panda/src/gobj/qpgeomPrimitive.cxx @@ -28,7 +28,7 @@ TypeHandle qpGeomPrimitive::_type_handle; -PStatCollector qpGeomPrimitive::_rotate_pcollector("Cull:Rotate"); +PStatCollector qpGeomPrimitive::_rotate_pcollector("Draw:Rotate"); //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::Constructor @@ -100,6 +100,20 @@ add_vertex(int vertex) { if (cdata->_got_minmax) { cdata->_min_vertex = min(cdata->_min_vertex, short_vertex); cdata->_max_vertex = max(cdata->_max_vertex, short_vertex); + + if (get_num_vertices_per_primitive() == 0) { + // Complex primitives also update their per-primitive minmax. + size_t pi = cdata->_ends.size(); + if (pi < cdata->_mins.size()) { + cdata->_mins[pi] = min(cdata->_mins[pi], short_vertex); + cdata->_maxs[pi] = max(cdata->_maxs[pi], short_vertex); + } else { + cdata->_mins.push_back(short_vertex); + cdata->_maxs.push_back(short_vertex); + } + nassertv((cdata->_mins.size() == cdata->_ends.size() + 1) && + (cdata->_maxs.size() == cdata->_ends.size() + 1)); + } } } @@ -126,6 +140,20 @@ add_consecutive_vertices(int start, int num_vertices) { if (cdata->_got_minmax) { cdata->_min_vertex = min(cdata->_min_vertex, short_start); cdata->_max_vertex = max(cdata->_max_vertex, short_end); + + if (get_num_vertices_per_primitive() == 0) { + // Complex primitives also update their per-primitive minmax. + size_t pi = cdata->_ends.size(); + if (pi < cdata->_mins.size()) { + cdata->_mins[pi] = min(cdata->_mins[pi], short_start); + cdata->_maxs[pi] = max(cdata->_maxs[pi], short_end); + } else { + cdata->_mins.push_back(short_start); + cdata->_maxs.push_back(short_end); + } + nassertv((cdata->_mins.size() == cdata->_ends.size() + 1) && + (cdata->_maxs.size() == cdata->_ends.size() + 1)); + } } } @@ -156,6 +184,11 @@ close_primitive() { #endif cdata->_ends.push_back((int)cdata->_vertices.size()); + if (cdata->_got_minmax) { + nassertv((cdata->_mins.size() == cdata->_ends.size()) && + (cdata->_maxs.size() == cdata->_ends.size())); + } + } else { // This is a simple primitive type like a triangle: each primitive // uses the same number of vertices. Assert that we added the @@ -177,6 +210,9 @@ clear_vertices() { cdata->_vertices.clear(); cdata->_rotated_vertices.clear(); cdata->_ends.clear(); + cdata->_mins.clear(); + cdata->_maxs.clear(); + cdata->_got_minmax = false; } //////////////////////////////////////////////////////////////////// @@ -192,6 +228,7 @@ modify_vertices() { clear_cache(); CDWriter cdata(_cycler); cdata->_rotated_vertices.clear(); + cdata->_got_minmax = false; return cdata->_vertices; } @@ -203,11 +240,12 @@ modify_vertices() { // the ends list with set_ends() at the same time. //////////////////////////////////////////////////////////////////// void qpGeomPrimitive:: -set_vertices(PTA_ushort vertices) { +set_vertices(CPTA_ushort vertices) { clear_cache(); CDWriter cdata(_cycler); - cdata->_vertices = vertices; + cdata->_vertices = (PTA_ushort &)vertices; cdata->_rotated_vertices.clear(); + cdata->_got_minmax = false; } //////////////////////////////////////////////////////////////////// @@ -226,6 +264,8 @@ PTA_int qpGeomPrimitive:: modify_ends() { clear_cache(); CDWriter cdata(_cycler); + cdata->_rotated_vertices.clear(); + cdata->_got_minmax = false; return cdata->_ends; } @@ -242,10 +282,12 @@ modify_ends() { // have the same number of vertices, it is not needed. //////////////////////////////////////////////////////////////////// void qpGeomPrimitive:: -set_ends(PTA_int ends) { +set_ends(CPTA_int ends) { clear_cache(); CDWriter cdata(_cycler); - cdata->_ends = ends; + cdata->_ends = (PTA_int &)ends; + cdata->_rotated_vertices.clear(); + cdata->_got_minmax = false; } //////////////////////////////////////////////////////////////////// @@ -257,7 +299,7 @@ set_ends(PTA_int ends) { int qpGeomPrimitive:: get_num_bytes() const { CDReader cdata(_cycler); - return cdata->_vertices.size() * sizeof(short) + + return (cdata->_vertices.size() + cdata->_mins.size() + cdata->_maxs.size()) * sizeof(short) + cdata->_ends.size() * sizeof(int) + sizeof(qpGeomPrimitive); } @@ -633,10 +675,54 @@ recompute_minmax() { if (cdata->_vertices.empty()) { cdata->_min_vertex = 0; 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). + cdata->_mins = PTA_ushort::empty_array(cdata->_ends.size()); + cdata->_maxs = PTA_ushort::empty_array(cdata->_ends.size()); + + int pi = 0; + int vi = 0; + int num_vertices = (int)cdata->_vertices.size(); + + unsigned short vertex = cdata->_vertices[vi]; + cdata->_min_vertex = vertex; + cdata->_mins[pi] = vertex; + cdata->_max_vertex = vertex; + cdata->_maxs[pi] = vertex; + + ++vi; + while (vi < num_vertices) { + unsigned short vertex = cdata->_vertices[vi]; + cdata->_min_vertex = min(cdata->_min_vertex, vertex); + cdata->_max_vertex = max(cdata->_max_vertex, vertex); + + if (vi == cdata->_ends[pi]) { + ++pi; + if (pi < (int)cdata->_ends.size()) { + cdata->_mins[pi] = vertex; + cdata->_maxs[pi] = vertex; + } + } else { + nassertv(pi < (int)cdata->_ends.size()); + cdata->_mins[pi] = min(cdata->_mins[pi], vertex); + cdata->_maxs[pi] = max(cdata->_maxs[pi], vertex); + } + + ++vi; + } + } else { + // This is a simple primitive type like a triangle; just compute + // the overall minmax. PTA_ushort::const_iterator ii = cdata->_vertices.begin(); cdata->_min_vertex = (*ii); cdata->_max_vertex = (*ii); + cdata->_mins.clear(); + cdata->_maxs.clear(); ++ii; while (ii != cdata->_vertices.end()) { diff --git a/panda/src/gobj/qpgeomPrimitive.h b/panda/src/gobj/qpgeomPrimitive.h index 5afe5a6f4e..a4dbefd059 100644 --- a/panda/src/gobj/qpgeomPrimitive.h +++ b/panda/src/gobj/qpgeomPrimitive.h @@ -68,8 +68,22 @@ PUBLISHED: virtual PT(qpGeomPrimitive) make_copy() const=0; enum ShadeModel { - SM_smooth, - SM_uniform, + // SM_smooth: vertices within a single face have different + // colors/normals that should be smoothed across the face. This + // primitive should be rendered with SmoothModelAttrib::M_smooth. + SM_smooth, + + // SM_uniform: all vertices across all faces have the same colors + // and normals. It doesn't matter which ShadeModelAttrib mode is + // used to render this primitive. + SM_uniform, + + // SM_flat_(first,last)_vertex: each face within the primitive + // might have a different color/normal than the other faces, but + // across a particular face there is only one color/normal. Each + // face's color/normal is taken from the (first, last) vertex of + // the face. This primitive should be rendered with + // SmoothModelAttrib::M_flat. SM_flat_first_vertex, SM_flat_last_vertex, }; @@ -88,16 +102,21 @@ PUBLISHED: INLINE CPTA_ushort get_flat_first_vertices() const; INLINE CPTA_ushort get_flat_last_vertices() const; PTA_ushort modify_vertices(); - void set_vertices(PTA_ushort vertices); + void set_vertices(CPTA_ushort vertices); INLINE CPTA_int get_ends() const; PTA_int modify_ends(); - void set_ends(PTA_int ends); + void set_ends(CPTA_int ends); + + INLINE CPTA_ushort get_mins() const; + INLINE CPTA_ushort get_maxs() const; int get_num_bytes() const; INLINE int get_min_vertex() const; + INLINE int get_min_vertex(int i) const; INLINE int get_max_vertex() const; + INLINE int get_max_vertex(int i) const; virtual int get_num_vertices_per_primitive() const; virtual int get_min_num_vertices_per_primitive() const; @@ -146,6 +165,8 @@ private: PTA_ushort _vertices; CPTA_ushort _rotated_vertices; PTA_int _ends; + PTA_ushort _mins; + PTA_ushort _maxs; bool _got_minmax; unsigned short _min_vertex; diff --git a/panda/src/gobj/qpgeomVertexData.I b/panda/src/gobj/qpgeomVertexData.I index 8c748b540e..742cc647fc 100644 --- a/panda/src/gobj/qpgeomVertexData.I +++ b/panda/src/gobj/qpgeomVertexData.I @@ -28,6 +28,19 @@ get_format() const { return _format; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::set_num_vertices +// Access: Published +// Description: Sets the length of the array to n vertices in all of +// the various arrays (presumably by adding vertices). +// The new vertex data is uninitialized. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexData:: +set_num_vertices(int n) { + CDWriter cdata(_cycler); + do_set_num_vertices(n, cdata); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexData::get_num_arrays // Access: Published diff --git a/panda/src/gobj/qpgeomVertexData.cxx b/panda/src/gobj/qpgeomVertexData.cxx index 6ecd7e1df4..a2f42cee69 100644 --- a/panda/src/gobj/qpgeomVertexData.cxx +++ b/panda/src/gobj/qpgeomVertexData.cxx @@ -18,14 +18,14 @@ #include "qpgeomVertexData.h" #include "qpgeomVertexCacheManager.h" +#include "pStatTimer.h" #include "bamReader.h" #include "bamWriter.h" #include "pset.h" TypeHandle qpGeomVertexData::_type_handle; -// Temporarily not a member of the class. -static PStatCollector _munge_pcollector("Cull:Munge:Data"); +PStatCollector qpGeomVertexData::_munge_data_pcollector("Cull:Munge:Data"); //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexData::Default Constructor @@ -128,55 +128,6 @@ get_num_vertices() const { return cdata->_arrays[0].size() / stride; } -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexData::set_num_vertices -// Access: Published -// Description: Sets the length of the array to n vertices in all of -// the various arrays (presumably by adding vertices). -// The new vertex data is uninitialized. -//////////////////////////////////////////////////////////////////// -void qpGeomVertexData:: -set_num_vertices(int n) { - CDWriter cdata(_cycler); - nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size()); - - bool any_changed = false; - - for (size_t i = 0; i < cdata->_arrays.size(); i++) { - int stride = _format->get_array(i)->get_stride(); - int delta = n - (cdata->_arrays[i].size() / stride); - - if (delta != 0) { - any_changed = true; - if (cdata->_arrays[i].get_ref_count() > 1) { - // Copy-on-write: the array is already reffed somewhere else, - // so we're just going to make a copy. - PTA_uchar new_array; - new_array.reserve(n * stride); - new_array.insert(new_array.end(), n * stride, uchar()); - memcpy(new_array, cdata->_arrays[i], - min((size_t)(n * stride), cdata->_arrays[i].size())); - cdata->_arrays[i] = new_array; - - } else { - // We've got the only reference to the array, so we can change - // it directly. - if (delta > 0) { - cdata->_arrays[i].insert(cdata->_arrays[i].end(), delta * stride, uchar()); - - } else { - cdata->_arrays[i].erase(cdata->_arrays[i].begin() + n * stride, - cdata->_arrays[i].end()); - } - } - } - } - - if (any_changed) { - clear_cache(); - } -} - //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexData::clear_vertices // Access: Published @@ -304,7 +255,7 @@ convert_to(const qpGeomVertexFormat *new_format) const { gobj_cat.debug() << "Converting " << num_vertices << " vertices.\n"; } - PStatTimer timer(_munge_pcollector); + PStatTimer timer(_munge_data_pcollector); PT(qpGeomVertexData) new_data = new qpGeomVertexData(new_format); @@ -449,7 +400,8 @@ set_data(int array, const qpGeomVertexDataType *data_type, int array_size = (int)cdata->_arrays[array].size(); if (element + data_type->get_total_bytes() > array_size) { // Whoops, we need more vertices! - set_num_vertices(vertex + 1); + CDWriter cdataw(_cycler, cdata); + do_set_num_vertices(vertex + 1, cdataw); } } @@ -755,6 +707,52 @@ remove_cache_entry(const qpGeomVertexFormat *modifier) const { ((qpGeomVertexData *)this)->_cycler.release_write_stage(0, cdata); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::do_set_num_vertices +// Access: Private +// Description: The private implementation of set_num_vertices(). +//////////////////////////////////////////////////////////////////// +void qpGeomVertexData:: +do_set_num_vertices(int n, CDWriter &cdata) { + nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size()); + + bool any_changed = false; + + for (size_t i = 0; i < cdata->_arrays.size(); i++) { + int stride = _format->get_array(i)->get_stride(); + int delta = n - (cdata->_arrays[i].size() / stride); + + if (delta != 0) { + any_changed = true; + if (cdata->_arrays[i].get_ref_count() > 1) { + // Copy-on-write: the array is already reffed somewhere else, + // so we're just going to make a copy. + PTA_uchar new_array; + new_array.reserve(n * stride); + new_array.insert(new_array.end(), n * stride, uchar()); + memcpy(new_array, cdata->_arrays[i], + min((size_t)(n * stride), cdata->_arrays[i].size())); + cdata->_arrays[i] = new_array; + + } else { + // We've got the only reference to the array, so we can change + // it directly. + if (delta > 0) { + cdata->_arrays[i].insert(cdata->_arrays[i].end(), delta * stride, uchar()); + + } else { + cdata->_arrays[i].erase(cdata->_arrays[i].begin() + n * stride, + cdata->_arrays[i].end()); + } + } + } + } + + if (any_changed) { + clear_cache(); + } +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexData::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/qpgeomVertexData.h b/panda/src/gobj/qpgeomVertexData.h index 131474f876..afcf6c3b3d 100644 --- a/panda/src/gobj/qpgeomVertexData.h +++ b/panda/src/gobj/qpgeomVertexData.h @@ -28,6 +28,7 @@ #include "cycleDataReader.h" #include "cycleDataWriter.h" #include "pipelineCycler.h" +#include "pStatCollector.h" #include "pointerTo.h" #include "pmap.h" #include "pvector.h" @@ -69,7 +70,7 @@ PUBLISHED: INLINE const qpGeomVertexFormat *get_format() const; int get_num_vertices() const; - void set_num_vertices(int n); + INLINE void set_num_vertices(int n); void clear_vertices(); INLINE int get_num_arrays() const; @@ -137,6 +138,11 @@ private: typedef CycleDataReader CDReader; typedef CycleDataWriter CDWriter; +private: + void do_set_num_vertices(int n, CDWriter &cdata); + + static PStatCollector _munge_data_pcollector; + public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &dg); diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index ea05b89a34..2d7a0b2f0c 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -126,7 +126,7 @@ static TimeCollectorProperties time_properties[] = { { 1, "Cull:Show fps", { 0.5, 0.8, 1.0 } }, { 1, "Cull:Bins", { 0.3, 0.6, 0.3 } }, { 1, "Cull:Munge", { 0.3, 0.3, 0.9 } }, - { 1, "Cull:Rotate", { 0.9, 0.8, 0.5 } }, + { 1, "Cull:Munge:Data" { 0.7, 0.5, 0.2 } }, { 1, "Draw", { 1.0, 0.0, 0.0 }, 1.0 / 30.0 }, { 1, "Draw:Make current", { 0.4, 0.2, 0.6 } }, { 1, "Draw:Copy texture", { 0.2, 0.6, 0.4 } }, @@ -138,6 +138,7 @@ static TimeCollectorProperties time_properties[] = { { 1, "Draw:Flip:End", { 0.9, 0.3, 0.6 } }, { 1, "Draw:Bins", { 0.3, 0.6, 0.0 } }, { 0, "Draw:Primitive", { 0.0, 0.0, 0.5 } }, + { 1, "Draw:Rotate", { 0.9, 0.8, 0.5 } }, { 0, NULL } };