From 233f3e080dec6b29d9c1954e6cb88e70ba614023 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 13 Jul 2006 16:09:08 +0000 Subject: [PATCH] egg-max-vertices, egg-max-indices, max-collect-indices --- panda/src/egg/eggGroupNode.cxx | 213 ++++++++++++++++--------- panda/src/egg/eggGroupNode.h | 7 +- panda/src/egg/eggVertexPool.cxx | 22 +++ panda/src/egg/eggVertexPool.h | 4 + panda/src/egg2pg/config_egg2pg.cxx | 10 ++ panda/src/egg2pg/config_egg2pg.h | 2 + panda/src/egg2pg/eggLoader.cxx | 163 ++++++++++++------- panda/src/egg2pg/eggLoader.h | 7 +- panda/src/gobj/geom.I | 16 +- panda/src/gobj/geom.cxx | 48 +++++- panda/src/gobj/geom.h | 4 +- panda/src/gobj/geomPrimitive.cxx | 4 +- panda/src/pgraph/config_pgraph.cxx | 4 +- panda/src/pgraph/geomNode.cxx | 7 +- panda/src/pgraph/geomNode.h | 2 +- panda/src/pgraph/sceneGraphReducer.cxx | 2 +- 16 files changed, 353 insertions(+), 162 deletions(-) diff --git a/panda/src/egg/eggGroupNode.cxx b/panda/src/egg/eggGroupNode.cxx index 84657c7bd2..c178d70273 100644 --- a/panda/src/egg/eggGroupNode.cxx +++ b/panda/src/egg/eggGroupNode.cxx @@ -979,80 +979,6 @@ get_connected_shading() { } } -//////////////////////////////////////////////////////////////////// -// Function: EggGroupNode::rebuild_vertex_pool -// Access: Published -// Description: Copies vertices used by the primitives at this group -// node (and below, if recurse is true) into the -// indicated vertex pool, and updates the primitives to -// reference this pool. It is up to the caller to -// parent the new vertex pool somewhere appropriate in -// the egg hierarchy. -//////////////////////////////////////////////////////////////////// -void EggGroupNode:: -rebuild_vertex_pool(EggVertexPool *vertex_pool, bool recurse) { - Children::iterator ci; - for (ci = _children.begin(); ci != _children.end(); ++ci) { - EggNode *child = *ci; - - if (child->is_of_type(EggCompositePrimitive::get_class_type())) { - typedef pvector< PT(EggVertex) > Vertices; - typedef pvector Attributes; - Vertices vertices; - Attributes attributes; - - EggCompositePrimitive *prim = DCAST(EggCompositePrimitive, child); - EggPrimitive::const_iterator pi; - for (pi = prim->begin(); pi != prim->end(); ++pi) { - vertices.push_back(*pi); - } - int i; - int num_components = prim->get_num_components(); - for (i = 0; i < num_components; i++) { - attributes.push_back(*prim->get_component(i)); - } - - prim->clear(); - - Vertices::const_iterator vi; - for (vi = vertices.begin(); vi != vertices.end(); ++vi) { - EggVertex *vertex = (*vi); - EggVertex *new_vertex = vertex_pool->create_unique_vertex(*vertex); - new_vertex->copy_grefs_from(*vertex); - prim->add_vertex(new_vertex); - } - for (i = 0; i < num_components; i++) { - prim->set_component(i, &attributes[i]); - } - - } else if (child->is_of_type(EggPrimitive::get_class_type())) { - typedef pvector< PT(EggVertex) > Vertices; - Vertices vertices; - - EggPrimitive *prim = DCAST(EggPrimitive, child); - EggPrimitive::const_iterator pi; - for (pi = prim->begin(); pi != prim->end(); ++pi) { - vertices.push_back(*pi); - } - - prim->clear(); - - Vertices::const_iterator vi; - for (vi = vertices.begin(); vi != vertices.end(); ++vi) { - EggVertex *vertex = (*vi); - EggVertex *new_vertex = vertex_pool->create_unique_vertex(*vertex); - new_vertex->copy_grefs_from(*vertex); - prim->add_vertex(new_vertex); - } - - } else if (child->is_of_type(EggGroupNode::get_class_type())) { - if (recurse) { - DCAST(EggGroupNode, child)->rebuild_vertex_pool(vertex_pool, recurse); - } - } - } -} - //////////////////////////////////////////////////////////////////// // Function: EggGroupNode::unify_attributes // Access: Published @@ -1268,6 +1194,145 @@ has_normals() const { return false; } +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::rebuild_vertex_pools +// Access: Published +// Description: Copies vertices used by the primitives at this group +// node (and below, if recurse is true) into one or more +// new vertex pools, and updates the primitives to +// reference these pools. It is up to the caller to +// parent the newly-created vertex pools somewhere +// appropriate in the egg hierarchy. +// +// No more than max_vertices will be placed into any one +// vertex pool. This is the sole criteria for splitting +// vertex pools. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +rebuild_vertex_pools(EggVertexPools &vertex_pools, unsigned int max_vertices, + bool recurse) { + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + EggNode *child = *ci; + + if (child->is_of_type(EggPrimitive::get_class_type())) { + typedef pvector< PT(EggVertex) > Vertices; + Vertices vertices; + EggPrimitive *prim = DCAST(EggPrimitive, child); + + // Copy all of the vertices out. + EggPrimitive::const_iterator pi; + for (pi = prim->begin(); pi != prim->end(); ++pi) { + vertices.push_back(*pi); + } + + if (prim->is_of_type(EggCompositePrimitive::get_class_type())) { + // A compositive primitive has the additional complication of + // dealing with its attributes. + typedef pvector Attributes; + Attributes attributes; + + EggCompositePrimitive *cprim = DCAST(EggCompositePrimitive, prim); + int i; + int num_components = cprim->get_num_components(); + for (i = 0; i < num_components; i++) { + attributes.push_back(*cprim->get_component(i)); + } + + cprim->clear(); + + for (i = 0; i < num_components; i++) { + cprim->set_component(i, &attributes[i]); + } + + } else { + prim->clear(); + } + + // Now look for a new home for the vertices. First, see if any + // of the vertex pools we've already created already have a copy + // of each one of the vertices. + bool found_pool = false; + EggVertexPool *best_pool = NULL; + int best_new_vertices = 0; + + Vertices new_vertices; + EggVertexPools::iterator vpi; + for (vpi = vertex_pools.begin(); + vpi != vertex_pools.end() && !found_pool; + ++vpi) { + EggVertexPool *vertex_pool = (*vpi); + int num_new_vertices = 0; + + new_vertices.clear(); + new_vertices.reserve(vertices.size()); + + Vertices::const_iterator vi; + for (vi = vertices.begin(); + vi != vertices.end() && !found_pool; + ++vi) { + EggVertex *vertex = (*vi); + EggVertex *new_vertex = vertex_pool->find_matching_vertex(*vertex); + new_vertices.push_back(new_vertex); + if (new_vertex == (EggVertex *)NULL) { + ++num_new_vertices; + } + } + + if (num_new_vertices == 0) { + // Great, we found a vertex pool that already shares all + // these vertices. No need to look any further. + found_pool = true; + + } else if (vertex_pool->size() + num_new_vertices <= max_vertices) { + // We would have to add some vertices to this pool, so this + // vertex pool qualifies only if the number of vertices we + // have to add would still keep it within our limit. + if (best_pool == (EggVertexPool *)NULL || + num_new_vertices < best_new_vertices) { + // This is currently our most favorable vertex pool. + best_pool = vertex_pool; + best_new_vertices = num_new_vertices; + } + } + } + + if (!found_pool) { + if (best_pool == (EggVertexPool *)NULL) { + // There was no vertex pool that qualified. We will have to + // create a new vertex pool. + best_pool = new EggVertexPool(""); + vertex_pools.push_back(best_pool); + } + + new_vertices.clear(); + new_vertices.reserve(vertices.size()); + + Vertices::const_iterator vi; + for (vi = vertices.begin(); vi != vertices.end(); ++vi) { + EggVertex *vertex = (*vi); + EggVertex *new_vertex = best_pool->create_unique_vertex(*vertex); + new_vertex->copy_grefs_from(*vertex); + new_vertices.push_back(new_vertex); + } + } + + Vertices::const_iterator vi; + nassertv(new_vertices.size() == vertices.size()); + for (vi = new_vertices.begin(); vi != new_vertices.end(); ++vi) { + EggVertex *new_vertex = (*vi); + nassertv(new_vertex != (EggVertex *)NULL); + prim->add_vertex(new_vertex); + } + + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + if (recurse) { + DCAST(EggGroupNode, child)->rebuild_vertex_pools(vertex_pools, max_vertices, recurse); + } + } + } +} + //////////////////////////////////////////////////////////////////// // Function: EggGroupNode::update_under // Access: Protected, Virtual diff --git a/panda/src/egg/eggGroupNode.h b/panda/src/egg/eggGroupNode.h index b46f917a8c..62a64432ff 100644 --- a/panda/src/egg/eggGroupNode.h +++ b/panda/src/egg/eggGroupNode.h @@ -22,6 +22,7 @@ #include "pandabase.h" #include "eggNode.h" +#include "eggVertexPool.h" #include "coordinateSystem.h" #include "typedObject.h" @@ -150,7 +151,6 @@ PUBLISHED: int remove_invalid_primitives(bool recurse); void clear_connected_shading(); void get_connected_shading(); - void rebuild_vertex_pool(EggVertexPool *vertex_pool, bool recurse); void unify_attributes(bool use_connected_shading, bool allow_per_primitive, bool recurse); void apply_last_attribute(bool recurse); @@ -160,6 +160,11 @@ PUBLISHED: virtual bool joint_has_primitives() const; virtual bool has_normals() const; +public: + void rebuild_vertex_pools(EggVertexPools &vertex_pools, + unsigned int max_vertices, + bool recurse); + protected: virtual void update_under(int depth_offset); diff --git a/panda/src/egg/eggVertexPool.cxx b/panda/src/egg/eggVertexPool.cxx index 3295ed8889..bede4fe0d4 100644 --- a/panda/src/egg/eggVertexPool.cxx +++ b/panda/src/egg/eggVertexPool.cxx @@ -467,6 +467,28 @@ create_unique_vertex(const EggVertex ©) { return add_vertex(new EggVertex(copy)); } +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::find_matching_vertex +// Access: Public +// Description: If the EggVertexPool already has a vertex matching +// the indicated vertex, returns it; otherwise, returns +// NULL. This is similar to create_unique_vertex() +// except that a new vertex is never created. +//////////////////////////////////////////////////////////////////// +EggVertex *EggVertexPool:: +find_matching_vertex(const EggVertex ©) { + UniqueVertices::iterator uvi; + uvi = _unique_vertices.find((EggVertex *)©); + + if (uvi != _unique_vertices.end()) { + // There was already such a vertex. Return it. + return (*uvi); + } + + // No matching vertex. + return NULL; +} + //////////////////////////////////////////////////////////////////// // Function: EggVertexPool::remove_vertex diff --git a/panda/src/egg/eggVertexPool.h b/panda/src/egg/eggVertexPool.h index eace35a9fc..877707f0da 100644 --- a/panda/src/egg/eggVertexPool.h +++ b/panda/src/egg/eggVertexPool.h @@ -27,6 +27,7 @@ #include "pointerTo.h" #include "pset.h" +#include "pvector.h" #include "pmap.h" #include "lmatrix.h" #include "iterator_types.h" @@ -125,6 +126,7 @@ PUBLISHED: // already one identical to the indicated vertex, or returns the // existing one if there is. EggVertex *create_unique_vertex(const EggVertex ©); + EggVertex *find_matching_vertex(const EggVertex ©); void remove_vertex(EggVertex *vertex); int remove_unused_vertices(); @@ -165,6 +167,8 @@ private: friend class EggVertex; }; +typedef pvector< PT(EggVertexPool) > EggVertexPools; + #include "eggVertexPool.I" #endif diff --git a/panda/src/egg2pg/config_egg2pg.cxx b/panda/src/egg2pg/config_egg2pg.cxx index 463aa89804..e28c32e4b7 100644 --- a/panda/src/egg2pg/config_egg2pg.cxx +++ b/panda/src/egg2pg/config_egg2pg.cxx @@ -135,6 +135,16 @@ ConfigVariableEnum egg_alpha_mode "transparent, because of a that involves a non-unity alpha, " "or because of a four-channel texture.")); +ConfigVariableInt egg_max_vertices +("egg-max-vertices", 65535, + PRC_DESC("Specifies the maximum number of vertices that will be " + "added to any one GeomVertexData by the egg loader.")); + +ConfigVariableInt egg_max_indices +("egg-max-indices", 65535, + PRC_DESC("Specifies the maximum number of vertex indices that will be " + "added to any one GeomPrimitive by the egg loader.")); + ConfigureFn(config_egg2pg) { init_libegg2pg(); } diff --git a/panda/src/egg2pg/config_egg2pg.h b/panda/src/egg2pg/config_egg2pg.h index d3e8a12a63..49b1101d2a 100644 --- a/panda/src/egg2pg/config_egg2pg.h +++ b/panda/src/egg2pg/config_egg2pg.h @@ -51,6 +51,8 @@ extern EXPCL_PANDAEGG ConfigVariableBool egg_load_classic_nurbs_curves; extern EXPCL_PANDAEGG ConfigVariableBool egg_accept_errors; extern EXPCL_PANDAEGG ConfigVariableBool egg_suppress_hidden; extern EXPCL_PANDAEGG ConfigVariableEnum egg_alpha_mode; +extern EXPCL_PANDAEGG ConfigVariableInt egg_max_vertices; +extern EXPCL_PANDAEGG ConfigVariableInt egg_max_indices; extern EXPCL_PANDAEGG void init_libegg2pg(); diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index ffe77975cf..8379ddc2f1 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -322,11 +322,13 @@ make_polyset(EggBin *egg_bin, PandaNode *parent, const LMatrix4d *transform, return; } - // Generate an optimal vertex pool for the polygons within just the - // bin (which translates directly to an optimal GeomVertexData - // structure). - PT(EggVertexPool) vertex_pool = new EggVertexPool("bin"); - egg_bin->rebuild_vertex_pool(vertex_pool, false); + // Generate an optimal vertex pool (or multiple vertex pools, if we + // have a lot of vertex) for the polygons within just the bin. Each + // EggVertexPool translates directly to an optimal GeomVertexData + // structure. + EggVertexPools vertex_pools; + egg_bin->rebuild_vertex_pools(vertex_pools, (unsigned int)egg_max_vertices, + false); if (egg_mesh) { // If we're using the mesher, mesh now. @@ -342,67 +344,87 @@ make_polyset(EggBin *egg_bin, PandaNode *parent, const LMatrix4d *transform, // vertices, so we can copy them to the GeomVertexData. egg_bin->apply_first_attribute(false); egg_bin->post_apply_flat_attribute(false); - vertex_pool->remove_unused_vertices(); - // vertex_pool->write(cerr, 0); // egg_bin->write(cerr, 0); - // Now create a handful of GeomPrimitives corresponding to the - // various types of primitives we have. - Primitives primitives; - for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) { - EggPrimitive *egg_prim; - DCAST_INTO_V(egg_prim, (*ci)); - make_primitive(render_state, egg_prim, primitives); - } + PT(GeomNode) geom_node; - if (!primitives.empty()) { - LMatrix4d mat; - if (transform != NULL) { - mat = (*transform); - } else { - mat = egg_bin->get_vertex_to_node(); - } - - // Now convert the vertex pool to a GeomVertexData. - nassertv(vertex_pool != (EggVertexPool *)NULL); - PT(GeomVertexData) vertex_data = - make_vertex_data(render_state, vertex_pool, egg_bin, mat, - is_dynamic, character_maker); - nassertv(vertex_data != (GeomVertexData *)NULL); - - // And create a Geom to hold the primitives. - PT(Geom) geom = new Geom(vertex_data); - - // Add each new primitive to the Geom. - Primitives::const_iterator pi; - for (pi = primitives.begin(); pi != primitives.end(); ++pi) { - GeomPrimitive *primitive = (*pi).second; - geom->add_primitive(primitive); - } - - // vertex_data->write(cerr); - // geom->write(cerr); - // render_state->_state->write(cerr, 0); + // Now iterate through each EggVertexPool. Normally, there's only + // one, but if we have a really big mesh, it might have been split + // into multiple vertex pools (to keep each one within the + // egg_max_vertices constraint). + EggVertexPools::iterator vpi; + for (vpi = vertex_pools.begin(); vpi != vertex_pools.end(); ++vpi) { + EggVertexPool *vertex_pool = (*vpi); + vertex_pool->remove_unused_vertices(); + // vertex_pool->write(cerr, 0); - // Now, is our parent node a GeomNode, or just an ordinary - // PandaNode? If it's a GeomNode, we can add the new Geom directly - // to our parent; otherwise, we need to create a new node. - PT(GeomNode) geom_node; - if (parent->is_geom_node() && !render_state->_hidden) { - geom_node = DCAST(GeomNode, parent); - - } else { - geom_node = new GeomNode(egg_bin->get_name()); - if (render_state->_hidden) { - parent->add_stashed(geom_node); - } else { - parent->add_child(geom_node); + // Create a handful of GeomPrimitives corresponding to the various + // types of primitives that reference this vertex pool. + UniquePrimitives unique_primitives; + Primitives primitives; + for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) { + EggPrimitive *egg_prim; + DCAST_INTO_V(egg_prim, (*ci)); + if (egg_prim->get_pool() == vertex_pool) { + make_primitive(render_state, egg_prim, unique_primitives, primitives); } } - geom_node->add_geom(geom, render_state->_state); - if (egg_show_normals) { + if (!primitives.empty()) { + LMatrix4d mat; + if (transform != NULL) { + mat = (*transform); + } else { + mat = egg_bin->get_vertex_to_node(); + } + + // Now convert this vertex pool to a GeomVertexData. + PT(GeomVertexData) vertex_data = + make_vertex_data(render_state, vertex_pool, egg_bin, mat, + is_dynamic, character_maker); + nassertv(vertex_data != (GeomVertexData *)NULL); + + // And create a Geom to hold the primitives. + PT(Geom) geom = new Geom(vertex_data); + + // Add each new primitive to the Geom. + Primitives::const_iterator pi; + for (pi = primitives.begin(); pi != primitives.end(); ++pi) { + GeomPrimitive *primitive = (*pi); + geom->add_primitive(primitive); + } + + // vertex_data->write(cerr); + // geom->write(cerr); + // render_state->_state->write(cerr, 0); + + // Create a new GeomNode if we haven't already. + if (geom_node == (GeomNode *)NULL) { + // Now, is our parent node a GeomNode, or just an ordinary + // PandaNode? If it's a GeomNode, we can add the new Geom directly + // to our parent; otherwise, we need to create a new node. + if (parent->is_geom_node() && !render_state->_hidden) { + geom_node = DCAST(GeomNode, parent); + + } else { + geom_node = new GeomNode(egg_bin->get_name()); + if (render_state->_hidden) { + parent->add_stashed(geom_node); + } else { + parent->add_child(geom_node); + } + } + } + + geom_node->add_geom(geom, render_state->_state); + } + } + + if (geom_node != (GeomNode *)NULL && egg_show_normals) { + // Create some more geometry to visualize each normal. + for (vpi = vertex_pools.begin(); vpi != vertex_pools.end(); ++vpi) { + EggVertexPool *vertex_pool = (*vpi); show_normals(vertex_pool, geom_node); } } @@ -2243,6 +2265,7 @@ record_morph(GeomVertexArrayFormat *array_format, //////////////////////////////////////////////////////////////////// void EggLoader:: make_primitive(const EggRenderState *render_state, EggPrimitive *egg_prim, + EggLoader::UniquePrimitives &unique_primitives, EggLoader::Primitives &primitives) { PT(GeomPrimitive) primitive; if (egg_prim->is_of_type(EggPolygon::get_class_type())) { @@ -2287,9 +2310,27 @@ make_primitive(const EggRenderState *render_state, EggPrimitive *egg_prim, // Insert the primitive into the set, but if we already have a // primitive of that type, reset the pointer to that one instead. PrimitiveUnifier pu(primitive); - pair result = - primitives.insert(Primitives::value_type(pu, primitive)); - primitive = (*result.first).second; + pair result = + unique_primitives.insert(UniquePrimitives::value_type(pu, primitive)); + + if (result.second) { + // This was the first primitive of this type. Store it. + primitives.push_back(primitive); + } + + GeomPrimitive *orig_prim = (*result.first).second; + + // Make sure we don't try to put more than egg_max_indices into any + // one GeomPrimitive. + if (orig_prim->get_num_vertices() + egg_prim->size() <= (unsigned int)egg_max_indices) { + primitive = orig_prim; + + } else if (orig_prim != primitive) { + // If the old primitive is full, keep the new primitive from now + // on. + (*result.first).second = primitive; + primitives.push_back(primitive); + } // Now add the vertices. EggPrimitive::const_iterator vi; diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index 4b16233b04..9c5d87375b 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -109,7 +109,8 @@ private: TypeHandle _type; GeomPrimitive::ShadeModel _shade_model; }; - typedef pmap Primitives; + typedef pmap UniquePrimitives; + typedef pvector< PT(GeomPrimitive) > Primitives; void show_normals(EggVertexPool *vertex_pool, GeomNode *geom_node); @@ -151,7 +152,9 @@ private: InternalName *column_name, int num_components); void make_primitive(const EggRenderState *render_state, - EggPrimitive *egg_prim, Primitives &primitives); + EggPrimitive *egg_prim, + UniquePrimitives &unique_primitives, + Primitives &primitives); void set_portal_polygon(EggGroup *egg_group, PortalNode *pnode); PT(EggPolygon) find_first_polygon(EggGroup *egg_group); diff --git a/panda/src/gobj/geom.I b/panda/src/gobj/geom.I index 6a5a74ea4d..84e40bb090 100644 --- a/panda/src/gobj/geom.I +++ b/panda/src/gobj/geom.I @@ -177,15 +177,19 @@ rotate() const { // Function: Geom::unify // Access: Published // Description: Unifies all of the primitives contained within this -// Geom into a single primitive object. This may -// require decomposing the primitives if, for instance, -// the Geom contains both triangle strips and triangle -// fans. +// Geom into a single (or as few as possible, within the +// constraints of max_indices) primitive objects. This +// may require decomposing the primitives if, for +// instance, the Geom contains both triangle strips and +// triangle fans. +// +// max_indices represents the maximum number of indices +// that will be put in any one GeomPrimitive. //////////////////////////////////////////////////////////////////// INLINE PT(Geom) Geom:: -unify() const { +unify(int max_indices) const { PT(Geom) new_geom = make_copy(); - new_geom->unify_in_place(); + new_geom->unify_in_place(max_indices); return new_geom; } diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index b10cdf4aa2..dc3ffc2b5b 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -512,17 +512,21 @@ rotate_in_place() { // Function: Geom::unify_in_place // Access: Published // Description: Unifies all of the primitives contained within this -// Geom into a single primitive object. This may -// require decomposing the primitives if, for instance, -// the Geom contains both triangle strips and triangle -// fans. +// Geom into a single (or as few as possible, within the +// constraints of max_indices) primitive objects. This +// may require decomposing the primitives if, for +// instance, the Geom contains both triangle strips and +// triangle fans. +// +// max_indices represents the maximum number of indices +// that will be put in any one GeomPrimitive. // // 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:: -unify_in_place() { +unify_in_place(int max_indices) { Thread *current_thread = Thread::get_current_thread(); if (get_num_primitives() <= 1) { // If we don't have more than one primitive to start with, no need @@ -564,8 +568,7 @@ unify_in_place() { } } - // At the end of the day, we have just one primitive, which becomes - // the one primitive in our list of primitives. + // Now we have just one primitive. nassertv(new_prim->check_valid(cdata->_data)); // The new primitive, naturally, inherits the Geom's overall shade @@ -573,7 +576,36 @@ unify_in_place() { new_prim->set_shade_model(cdata->_shade_model); cdata->_primitives.clear(); - cdata->_primitives.push_back(new_prim); + + // Should we split it up again to satisfy max_indices? + if (new_prim->get_num_vertices() > max_indices) { + // Copy new_prim into smaller prims, no one of which has more than + // max_indices vertices. + + int i = 0; + + while (i < new_prim->get_num_primitives()) { + PT(GeomPrimitive) smaller = new_prim->make_copy(); + smaller->clear_vertices(); + while (i < new_prim->get_num_primitives() && + smaller->get_num_vertices() + new_prim->get_primitive_num_vertices(i) < max_indices) { + int start = new_prim->get_primitive_start(i); + int end = new_prim->get_primitive_end(i); + for (int n = start; n < end; ++n) { + smaller->add_vertex(new_prim->get_vertex(n)); + } + smaller->close_primitive(); + + ++i; + } + + cdata->_primitives.push_back(smaller); + } + + } else { + // The new_prim has few enough vertices; keep it. + cdata->_primitives.push_back(new_prim); + } cdata->_modified = Geom::get_next_modified(); clear_cache_stage(current_thread); diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index aa9924ee7c..e84014ff40 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -93,11 +93,11 @@ PUBLISHED: INLINE PT(Geom) decompose() const; INLINE PT(Geom) rotate() const; - INLINE PT(Geom) unify() const; + INLINE PT(Geom) unify(int max_indices) const; void decompose_in_place(); void rotate_in_place(); - void unify_in_place(); + void unify_in_place(int max_indices); virtual bool copy_primitives_from(const Geom *other); diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index 638b3a64e6..b2caba29e6 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -175,8 +175,8 @@ set_index_type(GeomPrimitive::NumericType index_type) { // Access: Published // Description: Adds the indicated vertex to the list of vertex // indices used by the graphics primitive type. To -// define primitive, you must call add_vertex() for each -// vertex of the new primitve, and then call +// define a primitive, you must call add_vertex() for +// each vertex of the new primitive, and then call // close_primitive() after you have specified the last // vertex of each primitive. // diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index 5a4b3301b6..b8960b4ea2 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -188,7 +188,7 @@ ConfigVariableBool retransform_sprites "necessary in order for fog to work correctly on the sprites.")); ConfigVariableInt max_collect_vertices -("max-collect-vertices", 1024, +("max-collect-vertices", 65535, PRC_DESC("Specifies the maximum number of vertices that are allowed to be " "accumulated into any one GeomVertexData structure as a result " "of collecting objects together during a flatten operation. " @@ -198,7 +198,7 @@ ConfigVariableInt max_collect_vertices "GeomVertexData structure.")); ConfigVariableInt max_collect_indices -("max-collect-indices", 1024, +("max-collect-indices", 65535, PRC_DESC("Specifies the maximum number of vertex indices that are allowed " "to be accumulated into any one GeomPrimitive as a result " "of collecting objects together during a flatten operation. " diff --git a/panda/src/pgraph/geomNode.cxx b/panda/src/pgraph/geomNode.cxx index c331fbffdf..097d663f0c 100644 --- a/panda/src/pgraph/geomNode.cxx +++ b/panda/src/pgraph/geomNode.cxx @@ -432,13 +432,16 @@ check_valid() const { // triangle strips and triangle fans, being decomposed // into triangles. See also Geom::unify(). // +// max_indices represents the maximum number of indices +// that will be put in any one GeomPrimitive. +// // In order for this to be successful, the primitives // must reference the same GeomVertexData, have the same // fundamental primitive type, and have compatible shade // models. //////////////////////////////////////////////////////////////////// void GeomNode:: -unify() { +unify(int max_indices) { Thread *current_thread = Thread::get_current_thread(); OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) { CDStageWriter cdata(_cycler, pipeline_stage, current_thread); @@ -479,7 +482,7 @@ unify() { // Finally, go back through and unify the resulting geom(s). for (gi = new_geoms->begin(); gi != new_geoms->end(); ++gi) { const GeomEntry &entry = (*gi); - entry._geom->unify_in_place(); + entry._geom->unify_in_place(max_indices); } } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); diff --git a/panda/src/pgraph/geomNode.h b/panda/src/pgraph/geomNode.h index 34268c7933..ec05708c70 100644 --- a/panda/src/pgraph/geomNode.h +++ b/panda/src/pgraph/geomNode.h @@ -72,7 +72,7 @@ PUBLISHED: INLINE void remove_all_geoms(); bool check_valid() const; - void unify(); + void unify(int max_indices); void write_geoms(ostream &out, int indent_level) const; void write_verbose(ostream &out, int indent_level) const; diff --git a/panda/src/pgraph/sceneGraphReducer.cxx b/panda/src/pgraph/sceneGraphReducer.cxx index 4f6ce2002e..4409bd7150 100644 --- a/panda/src/pgraph/sceneGraphReducer.cxx +++ b/panda/src/pgraph/sceneGraphReducer.cxx @@ -739,7 +739,7 @@ void SceneGraphReducer:: r_unify(PandaNode *node) { if (node->is_geom_node()) { GeomNode *geom_node = DCAST(GeomNode, node); - geom_node->unify(); + geom_node->unify(max_collect_indices); } PandaNode::Children children = node->get_children();