Explorar o código

egg-max-vertices, egg-max-indices, max-collect-indices

David Rose %!s(int64=19) %!d(string=hai) anos
pai
achega
233f3e080d

+ 139 - 74
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<EggAttributes> 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<EggAttributes> 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

+ 6 - 1
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);
 

+ 22 - 0
panda/src/egg/eggVertexPool.cxx

@@ -467,6 +467,28 @@ create_unique_vertex(const EggVertex &copy) {
   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 &copy) {
+  UniqueVertices::iterator uvi;
+  uvi = _unique_vertices.find((EggVertex *)&copy);
+
+  if (uvi != _unique_vertices.end()) {
+    // There was already such a vertex.  Return it.
+    return (*uvi);
+  }
+
+  // No matching vertex.
+  return NULL;
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggVertexPool::remove_vertex

+ 4 - 0
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 &copy);
+  EggVertex *find_matching_vertex(const EggVertex &copy);
 
   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

+ 10 - 0
panda/src/egg2pg/config_egg2pg.cxx

@@ -135,6 +135,16 @@ ConfigVariableEnum<EggRenderMode::AlphaMode> egg_alpha_mode
           "transparent, because of a <RGBA> 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();
 }

+ 2 - 0
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<EggRenderMode::AlphaMode> egg_alpha_mode;
+extern EXPCL_PANDAEGG ConfigVariableInt egg_max_vertices;
+extern EXPCL_PANDAEGG ConfigVariableInt egg_max_indices;
 
 extern EXPCL_PANDAEGG void init_libegg2pg();
 

+ 102 - 61
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);
-  }
-
-  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);
+  PT(GeomNode) geom_node;
+
+  // 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);
+    
+    // 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);
+      }
     }
 
-    //    vertex_data->write(cerr);
-    //    geom->write(cerr);
-    //    render_state->_state->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);
+    if (!primitives.empty()) {
+      LMatrix4d mat;
+      if (transform != NULL) {
+        mat = (*transform);
       } else {
-        parent->add_child(geom_node);
+        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);
     }
-
-    geom_node->add_geom(geom, render_state->_state);
-    if (egg_show_normals) {
+  }
+   
+  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<Primitives::iterator, bool> result =
-    primitives.insert(Primitives::value_type(pu, primitive));
-  primitive = (*result.first).second;
+  pair<UniquePrimitives::iterator, bool> 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;

+ 5 - 2
panda/src/egg2pg/eggLoader.h

@@ -109,7 +109,8 @@ private:
     TypeHandle _type;
     GeomPrimitive::ShadeModel _shade_model;
   };
-  typedef pmap<PrimitiveUnifier, PT(GeomPrimitive) > Primitives;
+  typedef pmap<PrimitiveUnifier, PT(GeomPrimitive) > 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);

+ 10 - 6
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;
 }
 

+ 40 - 8
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);

+ 2 - 2
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);
 

+ 2 - 2
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.
 //

+ 2 - 2
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.  "

+ 5 - 2
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);

+ 1 - 1
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;

+ 1 - 1
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();