David Rose 20 лет назад
Родитель
Сommit
4eb9de4b8e

+ 68 - 18
panda/src/gobj/qpgeom.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "qpgeom.h"
+#include "qpgeomPoints.h"
 #include "qpgeomVertexReader.h"
 #include "qpgeomVertexRewriter.h"
 #include "pStatTimer.h"
@@ -232,8 +233,14 @@ make_nonindexed(bool composite_only) {
     PT(qpGeomPrimitive) primitive = (*pi)->make_copy();
     new_prims.push_back(primitive);
 
+    // GeomPoints are considered "composite" for the purposes of
+    // making nonindexed, since there's no particular advantage to
+    // having indexed points (as opposed to, say, indexed triangles or
+    // indexed lines).
     if (primitive->is_indexed() && 
-        (primitive->is_composite() || !composite_only)) {
+        (primitive->is_composite() || 
+         primitive->is_exact_type(qpGeomPoints::get_class_type()) || 
+         !composite_only)) {
       primitive->make_nonindexed(new_data, orig_data);
       ++num_changed;
     } else {
@@ -281,18 +288,16 @@ set_primitive(int i, const qpGeomPrimitive *primitive) {
   nassertv(cdata->_primitive_type == PT_none ||
            cdata->_primitive_type == primitive->get_primitive_type());
 
-  // They also should have the same fundamental shade model, but
-  // SM_uniform is compatible with anything.
-  nassertv(cdata->_shade_model == SM_uniform ||
-           primitive->get_shade_model() == SM_uniform ||
-           cdata->_shade_model == primitive->get_shade_model());
+  // They also should have the a compatible shade model.
+  CPT(qpGeomPrimitive) compat = primitive->match_shade_model(cdata->_shade_model);
+  nassertv_always(compat != (qpGeomPrimitive *)NULL);
 
-  cdata->_primitives[i] = (qpGeomPrimitive *)primitive;
-  PrimitiveType new_primitive_type = primitive->get_primitive_type();
+  cdata->_primitives[i] = (qpGeomPrimitive *)compat.p();
+  PrimitiveType new_primitive_type = compat->get_primitive_type();
   if (new_primitive_type != cdata->_primitive_type) {
     cdata->_primitive_type = new_primitive_type;
   }
-  ShadeModel new_shade_model = primitive->get_shade_model();
+  ShadeModel new_shade_model = compat->get_shade_model();
   if (new_shade_model != cdata->_shade_model &&
       new_shade_model != SM_uniform) {
     cdata->_shade_model = new_shade_model;
@@ -323,18 +328,16 @@ add_primitive(const qpGeomPrimitive *primitive) {
   nassertv(cdata->_primitive_type == PT_none ||
            cdata->_primitive_type == primitive->get_primitive_type());
 
-  // They also should have the same fundamental shade model, but
-  // SM_uniform is compatible with anything.
-  nassertv(cdata->_shade_model == SM_uniform ||
-           primitive->get_shade_model() == SM_uniform ||
-           cdata->_shade_model == primitive->get_shade_model());
+  // They also should have the a compatible shade model.
+  CPT(qpGeomPrimitive) compat = primitive->match_shade_model(cdata->_shade_model);
+  nassertv_always(compat != (qpGeomPrimitive *)NULL);
 
-  cdata->_primitives.push_back((qpGeomPrimitive *)primitive);
-  PrimitiveType new_primitive_type = primitive->get_primitive_type();
+  cdata->_primitives.push_back((qpGeomPrimitive *)compat.p());
+  PrimitiveType new_primitive_type = compat->get_primitive_type();
   if (new_primitive_type != cdata->_primitive_type) {
     cdata->_primitive_type = new_primitive_type;
   }
-  ShadeModel new_shade_model = primitive->get_shade_model();
+  ShadeModel new_shade_model = compat->get_shade_model();
   if (new_shade_model != cdata->_shade_model &&
       new_shade_model != SM_uniform) {
     cdata->_shade_model = new_shade_model;
@@ -515,6 +518,51 @@ unify_in_place() {
   reset_geom_rendering(cdata);
 }
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeom::copy_primitives_from
+//       Access: Published
+//  Description: Copies the primitives from the indicated Geom into
+//               this one.  This does require that both Geoms contain
+//               the same fundamental type primitives, both have a
+//               compatible shade model, and both use the same
+//               GeomVertexData.
+//
+//               Returns true if the copy is successful, or false
+//               otherwise (because the Geoms were mismatched).
+////////////////////////////////////////////////////////////////////
+bool qpGeom::
+copy_primitives_from(const qpGeom *other) {
+  if (get_primitive_type() != PT_none &&
+      other->get_primitive_type() != get_primitive_type()) {
+    return false;
+  }
+  if (get_vertex_data() != other->get_vertex_data()) {
+    return false;
+  }
+
+  ShadeModel this_shade_model = get_shade_model();
+  ShadeModel other_shade_model = other->get_shade_model();
+  if (this_shade_model != SM_uniform && other_shade_model != SM_uniform &&
+      this_shade_model != other_shade_model) {
+    if ((this_shade_model == SM_flat_first_vertex && other_shade_model == SM_flat_last_vertex) ||
+        (this_shade_model == SM_flat_last_vertex && other_shade_model == SM_flat_first_vertex)) {
+      // This is acceptable; we can rotate the primitives to match.
+
+    } else {
+      // Otherwise, we have incompatible shade models.
+      return false;
+    }
+  }
+
+  int num_primitives = other->get_num_primitives();
+  for (int i = 0; i < num_primitives; i++) {
+    add_primitive(other->get_primitive(i));
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::get_num_bytes
 //       Access: Published
@@ -682,11 +730,13 @@ output(ostream &out) const {
   CDReader cdata(_cycler);
 
   // Get a list of the primitive types contained within this object.
+  int num_faces = 0;
   pset<TypeHandle> types;
   Primitives::const_iterator pi;
   for (pi = cdata->_primitives.begin(); 
        pi != cdata->_primitives.end();
        ++pi) {
+    num_faces += (*pi)->get_num_faces();
     types.insert((*pi)->get_type());
   }
 
@@ -695,7 +745,7 @@ output(ostream &out) const {
   for (ti = types.begin(); ti != types.end(); ++ti) {
     out << " " << (*ti);
   }
-  out << " ], " << cdata->_data->get_num_rows() << " vertices";
+  out << " ], " << num_faces << " faces";
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 0
panda/src/gobj/qpgeom.h

@@ -96,6 +96,8 @@ PUBLISHED:
   void rotate_in_place();
   void unify_in_place();
 
+  bool copy_primitives_from(const qpGeom *other);
+
   int get_num_bytes() const;
   INLINE UpdateSeq get_modified() const;
 

+ 59 - 1
panda/src/gobj/qpgeomPrimitive.cxx

@@ -675,7 +675,9 @@ decompose() const {
 //     Function: qpGeomPrimitive::rotate
 //       Access: Published
 //  Description: Returns a new primitive with the shade_model reversed
-//               (if it is flat shaded).
+//               (if it is flat shaded), if possible.  If the
+//               primitive type cannot be rotated, returns the
+//               original primitive, unrotated.
 //
 //               If the current shade_model indicates
 //               flat_vertex_last, this should bring the last vertex
@@ -694,14 +696,70 @@ rotate() const {
   CPT(qpGeomVertexArrayData) rotated_vertices = rotate_impl();
 
   if (rotated_vertices == (qpGeomVertexArrayData *)NULL) {
+    // This primitive type can't be rotated.
     return this;
   }
 
   PT(qpGeomPrimitive) new_prim = make_copy();
   new_prim->set_vertices(rotated_vertices);
+
+  switch (get_shade_model()) {
+  case SM_flat_first_vertex:
+    new_prim->set_shade_model(SM_flat_last_vertex);
+    break;
+
+  case SM_flat_last_vertex:
+    new_prim->set_shade_model(SM_flat_first_vertex);
+    break;
+
+  default:
+    break;
+  }
+
   return new_prim;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomPrimitive::match_shade_model
+//       Access: Published
+//  Description: Returns a new primitive that is compatible with the
+//               indicated shade model, if possible, or NULL if this
+//               is not possible.
+//
+//               In most cases, this will return either NULL or the
+//               original primitive.  In the case of a
+//               SM_flat_first_vertex vs. a SM_flat_last_vertex (or
+//               vice-versa), however, it will return a rotated
+//               primitive.
+////////////////////////////////////////////////////////////////////
+CPT(qpGeomPrimitive) qpGeomPrimitive::
+match_shade_model(qpGeomPrimitive::ShadeModel shade_model) const {
+  ShadeModel this_shade_model = get_shade_model();
+  if (this_shade_model == shade_model) {
+    // Trivially compatible.
+    return this;
+  }
+
+  if (this_shade_model == SM_uniform || shade_model == SM_uniform) {
+    // SM_uniform is compatible with anything.
+    return this;
+  }
+
+  if ((this_shade_model == SM_flat_first_vertex && shade_model == SM_flat_last_vertex) ||
+      (this_shade_model == SM_flat_last_vertex && shade_model == SM_flat_first_vertex)) {
+    // Needs to be rotated.
+    CPT(qpGeomPrimitive) rotated = rotate();
+    if (rotated == this) {
+      // Oops, can't be rotated, sorry.
+      return NULL;
+    }
+    return rotated;
+  }
+
+  // Not compatible, sorry.
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomPrimitive::get_num_bytes
 //       Access: Published

+ 1 - 0
panda/src/gobj/qpgeomPrimitive.h

@@ -122,6 +122,7 @@ PUBLISHED:
 
   CPT(qpGeomPrimitive) decompose() const;
   CPT(qpGeomPrimitive) rotate() const;
+  CPT(qpGeomPrimitive) match_shade_model(ShadeModel shade_model) const;
 
   int get_num_bytes() const;
   INLINE int get_data_size_bytes() const;

+ 69 - 0
panda/src/pgraph/geomNode.cxx

@@ -449,6 +449,75 @@ add_geoms_from(const GeomNode *other) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::unify
+//       Access: Published
+//  Description: Attempts to unify all of the Geoms contained within
+//               this node into a single Geom, or at least as few
+//               Geoms as possible.  In turn, the individual
+//               GeomPrimitives contained within each resulting Geom
+//               are also unified.  The goal is to reduce the number
+//               of GeomPrimitives within the node as far as possible.
+//               This may result in composite primitives, such as
+//               triangle strips and triangle fans, being decomposed
+//               into triangles.  See also Geom::unify().
+//
+//               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() {
+  CDWriter cdata(_cycler);
+
+  Geoms new_geoms;
+
+  // Try to unify each Geom with each preceding Geom.  This is an n^2
+  // operation, but usually there are only a handful of Geoms to
+  // consider, so that's not a big deal.
+  Geoms::iterator gi;
+  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+    const GeomEntry &entry = (*gi);
+    
+    bool unified = false;
+    if (entry._geom->is_of_type(qpGeom::get_class_type())) {
+      Geoms::iterator gj;
+      for (gj = new_geoms.begin(); gj != new_geoms.end() && !unified; ++gj) {
+        GeomEntry &new_entry = (*gj);
+        if (new_entry._geom->is_of_type(qpGeom::get_class_type())) {
+          if (entry._state == new_entry._state) {
+            // Both states match, so try to combine the primitives.
+            if (DCAST(qpGeom, new_entry._geom)->copy_primitives_from
+                (DCAST(qpGeom, entry._geom))) {
+              // Successfully combined!
+              unified = true;
+            }
+          }
+        }
+      }
+    }
+
+    if (!unified) {
+      // Couldn't unify this Geom with anything, so just add it to the
+      // output list.
+      new_geoms.push_back(entry);
+    }
+  }
+
+  // Done!  We'll keep whatever's left in the output list.
+  cdata->_geoms.swap(new_geoms);
+  new_geoms.clear();
+
+  // Finally, go back through and unify the resulting geom(s).
+  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+    const GeomEntry &entry = (*gi);
+    if (entry._geom->is_of_type(qpGeom::get_class_type())) {
+      DCAST(qpGeom, entry._geom)->unify_in_place();
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::write_geoms
 //       Access: Published

+ 2 - 0
panda/src/pgraph/geomNode.h

@@ -68,6 +68,8 @@ PUBLISHED:
   INLINE void remove_geom(int n);
   INLINE void remove_all_geoms();
 
+  void unify();
+
   void write_geoms(ostream &out, int indent_level) const;
   void write_verbose(ostream &out, int indent_level) const;
 

+ 1 - 1
panda/src/pgraph/geomTransformer.cxx

@@ -617,7 +617,7 @@ collect_vertex_data(qpGeom *geom, int collect_bits) {
 //       Access: Public
 //  Description: Collects together individual GeomVertexData
 //               structures that share the same format into one big
-//               GeomVertexData structure.  This is designed to
+//               GeomVertexData structure.  This is intended to
 //               minimize context switches on the graphics card.
 ////////////////////////////////////////////////////////////////////
 int GeomTransformer::

+ 11 - 0
panda/src/pgraph/sceneGraphReducer.I

@@ -133,3 +133,14 @@ INLINE int SceneGraphReducer::
 make_nonindexed(PandaNode *root, int nonindexed_bits) {
   return r_make_nonindexed(root, nonindexed_bits);
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneGraphReducer::unify
+//       Access: Published
+//  Description: Calls unify() on every GeomNode at this level and
+//               below.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneGraphReducer::
+unify(PandaNode *root) {
+  r_unify(root);
+}

+ 19 - 0
panda/src/pgraph/sceneGraphReducer.cxx

@@ -674,3 +674,22 @@ r_make_nonindexed(PandaNode *node, int nonindexed_bits) {
     
   return num_changed;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneGraphReducer::r_unify
+//       Access: Private
+//  Description: The recursive implementation of unify().
+////////////////////////////////////////////////////////////////////
+void SceneGraphReducer::
+r_unify(PandaNode *node) {
+  if (node->is_geom_node()) {
+    GeomNode *geom_node = DCAST(GeomNode, node);
+    geom_node->unify();
+  }
+
+  PandaNode::Children children = node->get_children();
+  int num_children = children.get_num_children();
+  for (int i = 0; i < num_children; ++i) {
+    r_unify(children.get_child(i));
+  }
+}

+ 2 - 0
panda/src/pgraph/sceneGraphReducer.h

@@ -114,6 +114,7 @@ PUBLISHED:
 
   INLINE int collect_vertex_data(PandaNode *root, int collect_bits = ~0);
   INLINE int make_nonindexed(PandaNode *root, int nonindexed_bits = ~0);
+  INLINE void unify(PandaNode *root);
 
 protected:
   void r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
@@ -143,6 +144,7 @@ protected:
   int r_collect_vertex_data(PandaNode *node, int collect_bits,
                             GeomTransformer &transformer);
   int r_make_nonindexed(PandaNode *node, int collect_bits);
+  void r_unify(PandaNode *node);
 
 private:
   GeomTransformer _transformer;

+ 9 - 1
panda/src/testbed/pview.cxx

@@ -82,6 +82,12 @@ event_W(CPT_Event, void *) {
   }
 }
 
+void
+event_F(CPT_Event, void *) {
+  // shift-F: flatten the model hierarchy.
+  framework.get_models().flatten_strong();
+}
+
 void
 event_Enter(CPT_Event, void *) {
   // alt-enter: toggle between window/fullscreen in the same scene.
@@ -133,8 +139,9 @@ event_0(CPT_Event event, void *) {
   // 0: run hacky test.
 
   SceneGraphReducer gr;
-  gr.make_nonindexed(framework.get_models().node());
   gr.collect_vertex_data(framework.get_models().node());
+  gr.unify(framework.get_models().node());
+  gr.make_nonindexed(framework.get_models().node());
 
   /*
   static int count = 0;
@@ -312,6 +319,7 @@ main(int argc, char *argv[]) {
 
     framework.enable_default_keys();
     framework.define_key("shift-w", "open a new window", event_W, NULL);
+    framework.define_key("shift-f", "flatten hierarchy", event_F, NULL);
     framework.define_key("alt-enter", "toggle between window/fullscreen", event_Enter, NULL);
     framework.define_key("2", "split the window", event_2, NULL);
     if (pview_test_hack) {