Browse Source

preliminary egg support for new geoms

David Rose 21 năm trước cách đây
mục cha
commit
e20f73799c

+ 4 - 1
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -2593,7 +2593,10 @@ bool DXGraphicsStateGuardian8::
 begin_draw_primitives(const qpGeomVertexData *vertex_data) {
   DO_PSTATS_STUFF(_draw_primitive_pcollector.start());
 
-  GraphicsStateGuardian::begin_draw_primitives(vertex_data);
+  if (!GraphicsStateGuardian::begin_draw_primitives(vertex_data)) {
+    return false;
+  }
+  nassertr(_vertex_data != (qpGeomVertexData *)NULL, false);
 
   const qpGeomVertexFormat *format = _vertex_data->get_format();
 

+ 13 - 0
panda/src/egg/eggBinMaker.cxx

@@ -92,6 +92,17 @@ make_bins(EggGroupNode *root_group) {
   return num_bins;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggBinMaker::prepare_node
+//       Access: Public, Virtual
+//  Description: May be overridden in derived classes to perform some
+//               setup work as each node is encountered.  This will be
+//               called once for each node in the egg hierarchy.
+////////////////////////////////////////////////////////////////////
+void EggBinMaker::
+prepare_node(EggNode *) {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggBinMaker::sorts_less
 //       Access: Public, Virtual
@@ -153,6 +164,8 @@ collect_nodes(EggGroupNode *group) {
     EggNode *node = (*i);
     ++next;
 
+    prepare_node(node);
+
     if (get_bin_number(node) != 0) {
       // Ok, here's a node to be binned.  Add it to the appropriate
       // bin.

+ 11 - 0
panda/src/egg/eggBinMaker.h

@@ -96,6 +96,14 @@
 //
 // You may also redefine any or all of the following functions:
 //
+// virtual void prepare_node(EggNode *node);
+//
+//    This method is called, once, on each node in the egg hierarchy
+//    as it is visited the first time.  It allows the subclass a
+//    chance to analyze the node or do any other initial processing.
+//    This is a fine opportunity to tag an EggUserData onto the node,
+//    for instance.
+//
 // virtual bool sorts_less(int bin_number, const EggNode *a, const EggNode *b);
 //
 //    Sometimes a simple bin number alone is not enough.  For
@@ -254,6 +262,9 @@ PUBLISHED:
 
   int make_bins(EggGroupNode *root_group);
 
+  virtual void
+  prepare_node(EggNode *node);
+
   virtual int
   get_bin_number(const EggNode *node)=0;
 

+ 5 - 0
panda/src/egg/eggGroup.cxx

@@ -61,9 +61,14 @@ operator = (const EggGroup &copy) {
   EggTransform3d::operator = (copy);
   _flags = copy._flags;
   _flags2 = copy._flags2;
+  _collide_mask = copy._collide_mask;
+  _from_collide_mask = copy._from_collide_mask;
+  _into_collide_mask = copy._into_collide_mask;
+  _billboard_center = copy._billboard_center;
   _object_types = copy._object_types;
   _collision_name = copy._collision_name;
   _fps = copy._fps;
+  _lod = copy._lod;
 
   _tag_data = copy._tag_data;
 

+ 61 - 11
panda/src/egg/eggObject.I

@@ -35,7 +35,8 @@ EggObject() {
 INLINE EggObject::
 EggObject(const EggObject &copy) : 
   TypedReferenceCount(copy),
-  _user_data(copy._user_data)
+  _user_data(copy._user_data),
+  _default_user_data(copy._default_user_data)
 {
 }
 
@@ -49,6 +50,7 @@ INLINE EggObject &EggObject::
 operator = (const EggObject &copy) {
   TypedReferenceCount::operator = (copy);
   _user_data = copy._user_data;
+  _default_user_data = copy._default_user_data;
   return *this;
 }
 
@@ -61,54 +63,102 @@ operator = (const EggObject &copy) {
 //               hold its reference count and return the pointer on
 //               request.
 //
+//               The EggObject maintains multiple different
+//               EggUserData pointers, one for each unique type (as
+//               reported by get_type()).  If you know that only one
+//               type of EggUserData object will be added in your
+//               application, you may use the query functions that
+//               accept no parameters, but it is recommended that in
+//               general you pass in the type of your particular user
+//               data, to allow multiple applications to coexist in
+//               the same egg data.
+//
 //               This pointer is also copied by the copy assignment
 //               operator and copy constructor.
 ////////////////////////////////////////////////////////////////////
 INLINE void EggObject::
 set_user_data(EggUserData *user_data) {
-  _user_data = user_data;
+  _user_data[user_data->get_type()] = user_data;
+  _default_user_data = user_data;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggObject::get_user_data
 //       Access: Public
-//  Description: Returns the user data pointer previously stored on
+//  Description: Returns the user data pointer most recently stored on
 //               this object, or NULL if nothing was previously
 //               stored.
 ////////////////////////////////////////////////////////////////////
 INLINE EggUserData *EggObject::
 get_user_data() const {
-  return _user_data;
+  return _default_user_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggObject::get_user_data
+//       Access: Public
+//  Description: Returns the user data pointer of the indicated type,
+//               if it exists, or NULL if it does not.
+////////////////////////////////////////////////////////////////////
+INLINE EggUserData *EggObject::
+get_user_data(TypeHandle type) const {
+  UserData::const_iterator ui;
+  ui = _user_data.find(type);
+  if (ui != _user_data.end()) {
+    return (*ui).second;
+  }
+  return NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggObject::has_user_data
 //       Access: Public
-//  Description: Returns true if the user data pointer has been set,
-//               false otherwise.
+//  Description: Returns true if a generic user data pointer has
+//               recently been set and not yet cleared, false
+//               otherwise.
 ////////////////////////////////////////////////////////////////////
 INLINE bool EggObject::
 has_user_data() const {
-  return !_user_data.is_null();
+  return !_default_user_data.is_null();
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggObject::has_user_data
 //       Access: Public
-//  Description: Returns true if the user data pointer has been set
-//               and is of the indicated type, false otherwise.
+//  Description: Returns true if the user data pointer of the
+//               indicated type has been set, false otherwise.
 ////////////////////////////////////////////////////////////////////
 INLINE bool EggObject::
 has_user_data(TypeHandle type) const {
-  return !_user_data.is_null() && _user_data->is_of_type(type);
+  UserData::const_iterator ui;
+  ui = _user_data.find(type);
+  return (ui != _user_data.end());
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggObject::clear_user_data
 //       Access: Public
-//  Description: Resets the user data pointer to NULL.
+//  Description: Removes *all* user data pointers from the node.
 ////////////////////////////////////////////////////////////////////
 INLINE void EggObject::
 clear_user_data() {
   _user_data.clear();
+  _default_user_data.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggObject::clear_user_data
+//       Access: Public
+//  Description: Removes the user data pointer of the indicated type.
+////////////////////////////////////////////////////////////////////
+INLINE void EggObject::
+clear_user_data(TypeHandle type) {
+  UserData::iterator ui;
+  ui = _user_data.find(type);
+  if (ui != _user_data.end()) {
+    if ((*ui).second == _default_user_data) {
+      _default_user_data.clear();
+    }
+    _user_data.erase(ui);
+  }
 }

+ 6 - 1
panda/src/egg/eggObject.h

@@ -23,6 +23,7 @@
 #include "eggUserData.h"
 #include "typedReferenceCount.h"
 #include "pointerTo.h"
+#include "pmap.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : EggObject
@@ -39,12 +40,16 @@ PUBLISHED:
 
   INLINE void set_user_data(EggUserData *user_data);
   INLINE EggUserData *get_user_data() const;
+  INLINE EggUserData *get_user_data(TypeHandle type) const;
   INLINE bool has_user_data() const;
   INLINE bool has_user_data(TypeHandle type) const;
   INLINE void clear_user_data();
+  INLINE void clear_user_data(TypeHandle type);
 
 private:
-  PT(EggUserData) _user_data;
+  typedef pmap<TypeHandle, PT(EggUserData) > UserData;
+  UserData _user_data;
+  PT(EggUserData) _default_user_data;
 
 public:
   static TypeHandle get_class_type() {

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

@@ -178,6 +178,108 @@ get_highest_index() const {
   return _highest_index;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexPool::get_num_dimensions
+//       Access: Public
+//  Description: Returns the maximum number of dimensions used by any
+//               vertex in the pool.
+////////////////////////////////////////////////////////////////////
+int EggVertexPool::
+get_num_dimensions() const {
+  int num_dimensions = 0;
+
+  IndexVertices::const_iterator ivi;
+  for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) {
+    EggVertex *vertex = (*ivi).second;
+    num_dimensions = max(num_dimensions, vertex->get_num_dimensions());
+  }
+
+  return num_dimensions;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexPool::has_normals
+//       Access: Public
+//  Description: Returns true if any vertex in the pool has a normal
+//               defined, false if none of them do.
+////////////////////////////////////////////////////////////////////
+bool EggVertexPool::
+has_normals() const {
+  IndexVertices::const_iterator ivi;
+  for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) {
+    EggVertex *vertex = (*ivi).second;
+    if (vertex->has_normal()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexPool::has_colors
+//       Access: Public
+//  Description: Returns true if any vertex in the pool has a color
+//               defined, false if none of them do.
+////////////////////////////////////////////////////////////////////
+bool EggVertexPool::
+has_colors() const {
+  IndexVertices::const_iterator ivi;
+  for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) {
+    EggVertex *vertex = (*ivi).second;
+    if (vertex->has_color()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexPool::has_uvs
+//       Access: Public
+//  Description: Returns true if any vertex in the pool has a uv
+//               defined, false if none of them do.
+////////////////////////////////////////////////////////////////////
+bool EggVertexPool::
+has_uvs() const {
+  IndexVertices::const_iterator ivi;
+  for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) {
+    EggVertex *vertex = (*ivi).second;
+    if (vertex->has_uv()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexPool::get_uv_names
+//       Access: Public
+//  Description: Returns the list of UV names that are defined by any
+//               vertices in the pool.  It is the user's
+//               responsibility to clear the vector before calling
+//               this method.
+////////////////////////////////////////////////////////////////////
+void EggVertexPool::
+get_uv_names(vector_string &uv_names) const {
+  pset<string> names;
+  IndexVertices::const_iterator ivi;
+  for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) {
+    EggVertex *vertex = (*ivi).second;
+    EggVertex::const_uv_iterator uvi;
+    for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) {
+      names.insert((*uvi)->get_name());
+    }
+  }
+
+  pset<string>::const_iterator si;
+  for (si = names.begin(); si != names.end(); ++si) {
+    uv_names.push_back(*si);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggVertexPool::begin()
 //       Access: Public

+ 6 - 0
panda/src/egg/eggVertexPool.h

@@ -92,6 +92,12 @@ PUBLISHED:
   // Returns 0 if the pool is empty.
   int get_highest_index() const;
 
+  int get_num_dimensions() const;
+  bool has_normals() const;
+  bool has_colors() const;
+  bool has_uvs() const;
+  void get_uv_names(vector_string &uv_names) const;
+
 public:
   // Can be used to traverse all the vertices in index number order.
   iterator begin() const;

+ 3 - 1
panda/src/egg2pg/Sources.pp

@@ -18,7 +18,8 @@
     config_egg2pg.h \
     deferredNodeProperty.h \
     eggBinner.h \
-    eggLoader.h \
+    eggLoader.h eggLoader.I \
+    eggRenderState.h eggRenderState.I \
     egg_parametrics.h \
     load_egg_file.h \
     loaderFileTypeEgg.h
@@ -31,6 +32,7 @@
     deferredNodeProperty.cxx \
     eggBinner.cxx \
     eggLoader.cxx \
+    eggRenderState.cxx \
     egg_parametrics.cxx \
     load_egg_file.cxx \
     loaderFileTypeEgg.cxx

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

@@ -23,6 +23,7 @@
 #include "loaderFileTypeRegistry.h"
 #include "configVariableManager.h"
 #include "configVariableCore.h"
+#include "eggRenderState.h"
 
 ConfigureDef(config_egg2pg);
 NotifyCategoryDef(egg2pg, "");
@@ -153,6 +154,7 @@ init_libegg2pg() {
      "Defines egg syntax for the named object type.",
      ConfigVariableCore::F_dynamic);
 
+  EggRenderState::init_type();
   LoaderFileTypeEgg::init_type();
 
   LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();

+ 1 - 0
panda/src/egg2pg/egg2pg_composite1.cxx

@@ -3,3 +3,4 @@
 #include "computedVerticesMaker.cxx"
 #include "config_egg2pg.cxx"
 #include "egg_parametrics.cxx"
+#include "eggRenderState.cxx"

+ 73 - 30
panda/src/egg2pg/eggBinner.cxx

@@ -17,11 +17,40 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "eggBinner.h"
-
+#include "eggRenderState.h"
+#include "eggPrimitive.h"
 #include "eggSwitchCondition.h"
 #include "eggGroup.h"
 #include "dcast.h"
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggBinner::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggBinner::
+EggBinner(EggLoader &loader) :
+  _loader(loader)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggBinner::prepare_node
+//       Access: Public, Virtual
+//  Description: May be overridden in derived classes to perform some
+//               setup work as each node is encountered.  This will be
+//               called once for each node in the egg hierarchy.
+////////////////////////////////////////////////////////////////////
+void EggBinner::
+prepare_node(EggNode *node) {
+  if (node->is_of_type(EggPrimitive::get_class_type())) {
+    EggPrimitive *egg_prim = DCAST(EggPrimitive, node);
+    PT(EggRenderState) render_state = new EggRenderState(_loader);
+    render_state->fill_state(egg_prim);
+    egg_prim->set_user_data(render_state);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggBinner::get_bin_number
 //       Access: Public, Virtual
@@ -29,7 +58,10 @@
 ////////////////////////////////////////////////////////////////////
 int EggBinner::
 get_bin_number(const EggNode *node) {
-  if (node->is_of_type(EggGroup::get_class_type())) {
+  if (node->is_of_type(EggPrimitive::get_class_type())) {
+    return (int)BN_polyset;
+
+  } else if (node->is_of_type(EggGroup::get_class_type())) {
     const EggGroup *group = DCAST(EggGroup, node);
     if (group->has_lod()) {
       return (int)BN_lod;
@@ -47,36 +79,47 @@ get_bin_number(const EggNode *node) {
 ////////////////////////////////////////////////////////////////////
 bool EggBinner::
 sorts_less(int bin_number, const EggNode *a, const EggNode *b) {
-  assert((BinNumber)bin_number == BN_lod);
+  switch (bin_number) {
+  case BN_polyset:
+    {
+      const EggPrimitive *pa, *pb;
+      DCAST_INTO_R(pa, a, false);
+      DCAST_INTO_R(pb, b, false);
 
-  const EggGroup *ga = DCAST(EggGroup, a);
-  const EggGroup *gb = DCAST(EggGroup, b);
-
-  const EggSwitchCondition &swa = ga->get_lod();
-  const EggSwitchCondition &swb = gb->get_lod();
-
-  // For now, this is the only kind of switch condition there is.
-  const EggSwitchConditionDistance &swda =
-    *DCAST(EggSwitchConditionDistance, &swa);
-  const EggSwitchConditionDistance &swdb =
-    *DCAST(EggSwitchConditionDistance, &swb);
-
-  // Group LOD nodes in order by switching center.
-  return (swda._center.compare_to(swdb._center) < 0);
-}
+      // Different vertex pools have to be binned separately.
+      if (pa->get_pool() != pb->get_pool()) {
+        return pa->get_pool() < pb->get_pool();
+      }
+      
+      // Otherwise, different render states are binned separately.
+      const EggRenderState *rsa, *rsb;
+      DCAST_INTO_R(rsa, a->get_user_data(EggRenderState::get_class_type()), false);
+      DCAST_INTO_R(rsb, b->get_user_data(EggRenderState::get_class_type()), false);
+      return (*rsa) < (*rsb);
+    }
 
-////////////////////////////////////////////////////////////////////
-//     Function: EggBinner::collapse_group
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-bool EggBinner::
-collapse_group(const EggGroup *group, int) {
-  if (group->get_dart_type() != EggGroup::DT_none) {
-    // A group with the <Dart> flag set means to create a character.
-    // We can't turn the top character node into an LOD.
-    return false;
+  case BN_lod:
+    {
+      const EggGroup *ga = DCAST(EggGroup, a);
+      const EggGroup *gb = DCAST(EggGroup, b);
+      
+      const EggSwitchCondition &swa = ga->get_lod();
+      const EggSwitchCondition &swb = gb->get_lod();
+      
+      // For now, this is the only kind of switch condition there is.
+      const EggSwitchConditionDistance &swda =
+        *DCAST(EggSwitchConditionDistance, &swa);
+      const EggSwitchConditionDistance &swdb =
+        *DCAST(EggSwitchConditionDistance, &swb);
+      
+      // Group LOD nodes in order by switching center.
+      return (swda._center.compare_to(swdb._center) < 0);
+    }
+      
+  case BN_none:
+    break;
   }
 
-  return true;
+  // Shouldn't get here.
+  return false;
 }

+ 11 - 4
panda/src/egg2pg/eggBinner.h

@@ -23,33 +23,40 @@
 
 #include "eggBinMaker.h"
 
+class EggLoader;
+
 ///////////////////////////////////////////////////////////////////
 //       Class : EggBinner
 // Description : A special binner used only within this package to
 //               pre-process the egg tree for the loader and group
 //               things together as appropriate.
 //
-//               Presently, it only groups related LOD children
+//               It is used to collect similar polygons together for a
+//               Geom, as well as to group related LOD children
 //               together under a single LOD node.
 ////////////////////////////////////////////////////////////////////
 class EggBinner : public EggBinMaker {
 public:
-
   // The BinNumber serves to identify why a particular EggBin was
   // created.
   enum BinNumber {
     BN_none = 0,
+    BN_polyset,
     BN_lod,
   };
 
+  EggBinner(EggLoader &loader);
+
+  virtual void
+  prepare_node(EggNode *node);
+
   virtual int
   get_bin_number(const EggNode *node);
 
   virtual bool
   sorts_less(int bin_number, const EggNode *a, const EggNode *b);
 
-  virtual bool
-  collapse_group(const EggGroup *group, int bin_number);
+  EggLoader &_loader;
 };
 
 

+ 31 - 0
panda/src/egg2pg/eggLoader.I

@@ -0,0 +1,31 @@
+// Filename: eggLoader.I
+// Created by:  drose (13Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::VertexPoolTransform::operator <
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool EggLoader::VertexPoolTransform::
+operator < (const EggLoader::VertexPoolTransform &other) const {
+  if (_vertex_pool != other._vertex_pool) {
+    return _vertex_pool < other._vertex_pool;
+  }
+  return _transform.compare_to(other._transform, 0.001) < 0;
+}

+ 324 - 472
panda/src/egg2pg/eggLoader.cxx

@@ -1,4 +1,4 @@
-// Filename: EggLoader.cxx
+// Filename: eggLoader.cxx
 // Created by:  drose (26Feb02)
 //
 ////////////////////////////////////////////////////////////////////
@@ -19,26 +19,25 @@
 #include "pandabase.h"
 
 #include "eggLoader.h"
+#include "eggRenderState.h"
 #include "egg_parametrics.h"
 #include "config_egg2pg.h"
 #include "nodePath.h"
 #include "renderState.h"
 #include "transformState.h"
-#include "textureAttrib.h"
-#include "textureApplyAttrib.h"
 #include "texturePool.h"
 #include "billboardEffect.h"
-#include "cullFaceAttrib.h"
-#include "cullBinAttrib.h"
-#include "transparencyAttrib.h"
 #include "decalEffect.h"
-#include "depthTestAttrib.h"
-#include "depthWriteAttrib.h"
-#include "materialAttrib.h"
-#include "texMatrixAttrib.h"
 #include "colorAttrib.h"
+#include "textureAttrib.h"
 #include "materialPool.h"
 #include "geomNode.h"
+#include "qpgeomVertexFormat.h"
+#include "qpgeomVertexArrayFormat.h"
+#include "qpgeomVertexData.h"
+#include "qpgeomVertexIterator.h"
+#include "qpgeom.h"
+#include "qpgeomTriangles.h"
 #include "sequenceNode.h"
 #include "switchNode.h"
 #include "portalNode.h"
@@ -150,13 +149,13 @@ void EggLoader::
 build_graph() {
   _deferred_nodes.clear();
 
-  // First, bin up the LOD nodes.
-  EggBinner binner;
-  binner.make_bins(&_data);
-
-  // Then load up all of the textures.
+  // First, load up all of the textures.
   load_textures();
 
+  // Then bin up the polysets and LOD nodes.
+  EggBinner binner(*this);
+  binner.make_bins(&_data);
+
   // Now build up the scene graph.
   _root = new ModelRoot(_data.get_egg_filename().get_basename());
   make_node(&_data, _root);
@@ -1276,61 +1275,6 @@ make_texture_stage(const EggTexture *egg_tex) {
   return stage;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: EggLoader::get_material_attrib
-//       Access: Private
-//  Description: Returns a RenderAttrib suitable for enabling the
-//               material indicated by the given EggMaterial, and with
-//               the indicated backface flag.
-////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) EggLoader::
-get_material_attrib(const EggMaterial *egg_mat, bool bface) {
-  Materials &materials = bface ? _materials_bface : _materials;
-
-  // First, check whether we've seen this material before.
-  Materials::const_iterator mi;
-  mi = materials.find(egg_mat);
-  if (mi != materials.end()) {
-    return (*mi).second;
-  }
-
-  // Ok, this is the first time we've seen this particular
-  // EggMaterial.  Create a new Material that matches it.
-  PT(Material) mat = new Material;
-  if (egg_mat->has_diff()) {
-    mat->set_diffuse(egg_mat->get_diff());
-    // By default, ambient is the same as diffuse, if diffuse is
-    // specified but ambient is not.
-    mat->set_ambient(egg_mat->get_diff());
-  }
-  if (egg_mat->has_amb()) {
-    mat->set_ambient(egg_mat->get_amb());
-  }
-  if (egg_mat->has_emit()) {
-    mat->set_emission(egg_mat->get_emit());
-  }
-  if (egg_mat->has_spec()) {
-    mat->set_specular(egg_mat->get_spec());
-  }
-  if (egg_mat->has_shininess()) {
-    mat->set_shininess(egg_mat->get_shininess());
-  }
-  if (egg_mat->has_local()) {
-    mat->set_local(egg_mat->get_local());
-  }
-
-  mat->set_twoside(bface);
-
-  // Now get a global Material pointer, shared with other models.
-  const Material *shared_mat = MaterialPool::get_material(mat);
-
-  // And create a MaterialAttrib for this Material.
-  CPT(RenderAttrib) mt = MaterialAttrib::make(shared_mat);
-  materials.insert(Materials::value_type(egg_mat, mt));
-
-  return mt;
-}
-
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::setup_bucket
@@ -1363,315 +1307,12 @@ setup_bucket(BuilderBucket &bucket, EggLoader::BakeInUVs &bake_in_uvs,
     bucket.set_name(egg_prim->get_name());
   }
 
-  // Assign the appropriate properties to the bucket.
-
-  // The various EggRenderMode properties can be defined directly at
-  // the primitive, at a group above the primitive, or an a texture
-  // applied to the primitive.  The EggNode::determine_*() functions
-  // can find the right pointer to the level at which this is actually
-  // defined for a given primitive.
-  EggRenderMode::AlphaMode am = EggRenderMode::AM_unspecified;
-  EggRenderMode::DepthWriteMode dwm = EggRenderMode::DWM_unspecified;
-  EggRenderMode::DepthTestMode dtm = EggRenderMode::DTM_unspecified;
-  EggRenderMode::VisibilityMode vm = EggRenderMode::VM_unspecified;
-  bool implicit_alpha = false;
-  bool has_draw_order = false;
-  int draw_order = 0;
-  bool has_bin = false;
-  string bin;
-
-  EggRenderMode *render_mode;
-  render_mode = egg_prim->determine_alpha_mode();
-  if (render_mode != (EggRenderMode *)NULL) {
-    am = render_mode->get_alpha_mode();
-  }
-  render_mode = egg_prim->determine_depth_write_mode();
-  if (render_mode != (EggRenderMode *)NULL) {
-    dwm = render_mode->get_depth_write_mode();
-  }
-  render_mode = egg_prim->determine_depth_test_mode();
-  if (render_mode != (EggRenderMode *)NULL) {
-    dtm = render_mode->get_depth_test_mode();
-  }
-  render_mode = egg_prim->determine_visibility_mode();
-  if (render_mode != (EggRenderMode *)NULL) {
-    vm = render_mode->get_visibility_mode();
-  }
-  render_mode = egg_prim->determine_draw_order();
-  if (render_mode != (EggRenderMode *)NULL) {
-    has_draw_order = true;
-    draw_order = render_mode->get_draw_order();
-  }
-  render_mode = egg_prim->determine_bin();
-  if (render_mode != (EggRenderMode *)NULL) {
-    has_bin = true;
-    bin = render_mode->get_bin();
-  }
-
-  bucket.add_attrib(TextureAttrib::make_off());
-  int num_textures = egg_prim->get_num_textures();
-  CPT(RenderAttrib) texture_attrib = NULL;
-  CPT(RenderAttrib) tex_gen_attrib = NULL;
-  CPT(RenderAttrib) tex_mat_attrib = NULL;
-  TexMats tex_mats;
-
-  for (int i = 0; i < num_textures; i++) {
-    PT_EggTexture egg_tex = egg_prim->get_texture(i);
-
-    const TextureDef &def = _textures[egg_tex];
-    if (def._texture != (const RenderAttrib *)NULL) {
-      if (texture_attrib == (RenderAttrib *)NULL) {
-        texture_attrib = def._texture;
-      } else {
-        texture_attrib = texture_attrib->compose(def._texture);
-      }
-
-      // If neither the primitive nor the texture specified an alpha
-      // mode, assume it should be alpha'ed if the texture has an
-      // alpha channel (unless the texture environment type is one
-      // that doesn't apply its alpha to the result).
-      if (am == EggRenderMode::AM_unspecified) {
-        const TextureAttrib *tex_attrib = DCAST(TextureAttrib, def._texture);
-        Texture *tex = tex_attrib->get_texture();
-        nassertv(tex != (Texture *)NULL);
-        int num_components = tex->get_num_components();
-        if (egg_tex->has_alpha_channel(num_components)) {
-          switch (egg_tex->get_env_type()) {
-          case EggTexture::ET_decal:
-          case EggTexture::ET_add:
-            break;
-
-          default:
-            implicit_alpha = true;
-          }
-        }
-      }
-
-      // Check for a texgen attrib.
-      bool has_tex_gen = false;
-      if (egg_tex->get_tex_gen() != EggTexture::TG_unspecified) {
-        has_tex_gen = true;
-        if (tex_gen_attrib == (const RenderAttrib *)NULL) {
-          tex_gen_attrib = TexGenAttrib::make();
-        }
-        tex_gen_attrib = DCAST(TexGenAttrib, tex_gen_attrib)->
-          add_stage(def._stage, get_tex_gen(egg_tex));
-      }
-
-      // Record the texture's associated texture matrix, so we can see
-      // if we can safely bake it into the UV's.  (We need to get the
-      // complete list of textures that share this same set of UV's
-      // per each unique texture matrix.  Whew!)
-      CPT(InternalName) uv_name;
-      if (egg_tex->has_uv_name() && egg_tex->get_uv_name() != string("default")) {
-        uv_name = InternalName::get_texcoord_name(egg_tex->get_uv_name());
-      } else {
-        uv_name = InternalName::get_texcoord();
-      }
-
-      if (has_tex_gen) {
-        // If the texture has a texgen mode, we will always apply its
-        // texture transform, never bake it in.  In fact, we don't
-        // even care about its UV's in this case, since we won't be
-        // using them.
-        tex_mat_attrib = apply_tex_mat(tex_mat_attrib, def._stage, egg_tex);
-
-      } else {
-        // Otherwise, we need to record that there is at least one
-        // texture on this particular UV name and with this particular
-        // texture matrix.  If there are no other textures, or if all
-        // of the other textures use the same texture matrix, then
-        // tex_mats[uv_name].size() will remain 1 (which tells us we
-        // can bake in the texture matrix to the UV's).  On the other
-        // hand, if there is another texture on the same uv name but
-        // with a different transform, it will increase
-        // tex_mats[uv_name].size() to at least 2, indicating we can't
-        // bake in the texture matrix.
-        tex_mats[uv_name][egg_tex->get_transform()].push_back(&def);
-      }
-    }
-  }
-
-  // These parametric primitive types can't have their UV's baked in,
-  // so if we have one of these we always need to apply the texture
-  // matrix as a separate attribute, regardless of how many textures
-  // share the particular UV set.
-  bool needs_tex_mat = (egg_prim->is_of_type(EggCurve::get_class_type()) ||
-                        egg_prim->is_of_type(EggSurface::get_class_type()));
-
-  // Now that we've visited all of the textures in the above loop, we
-  // can go back and see how many of them share the same UV name and
-  // texture matrix.
-  TexMats::const_iterator tmi;
-  for (tmi = tex_mats.begin(); tmi != tex_mats.end(); ++tmi) {
-    const InternalName *uv_name = (*tmi).first;
-    const TexMatTransforms &tmt = (*tmi).second;
-
-    if (tmt.size() == 1 && !needs_tex_mat) {
-      // Only one unique transform sharing this set of UV's.  We can
-      // bake in the transform!
-      const TexMatTextures &tmtex = (*tmt.begin()).second;
-
-      // The first EggTexture on the list is sufficient, since we know
-      // they all have the same transform.
-      nassertv(!tmtex.empty());
-      TexMatTextures::const_iterator tmtexi = tmtex.begin();
-      const EggTexture *egg_tex = (*tmtexi)->_egg_tex;
-      if (egg_tex->has_transform()) {
-        // If there's no transform, it's an identity matrix; don't
-        // bother recording it.  Of course, it would do no harm to
-        // record it if we felt like it.
-        bake_in_uvs[uv_name] = egg_tex;
-      }
-
-    } else {
-      // Multiple transforms on this UV set, or a geometry type that
-      // doesn't support baking in UV's.  We have to apply the
-      // texture matrix to each stage.
-      TexMatTransforms::const_iterator tmti;
-      for (tmti = tmt.begin(); tmti != tmt.end(); ++tmti) {
-        const TexMatTextures &tmtex = (*tmti).second;
-        TexMatTextures::const_iterator tmtexi;
-        for (tmtexi = tmtex.begin(); tmtexi != tmtex.end(); ++tmtexi) {
-          const EggTexture *egg_tex = (*tmtexi)->_egg_tex;
-          TextureStage *stage = (*tmtexi)->_stage;
-          
-          tex_mat_attrib = apply_tex_mat(tex_mat_attrib, stage, egg_tex);
-        }
-      }
-    }
-  }
-
-  if (texture_attrib != (RenderAttrib *)NULL) {
-    bucket.add_attrib(texture_attrib);
-  }
-
-  if (tex_gen_attrib != (RenderAttrib *)NULL) {
-    bucket.add_attrib(tex_gen_attrib);
-  }
-
-  if (tex_mat_attrib != (RenderAttrib *)NULL) {
-    bucket.add_attrib(tex_mat_attrib);
-  }
-
-  if (egg_prim->has_material()) {
-    CPT(RenderAttrib) mt =
-      get_material_attrib(egg_prim->get_material(),
-                          egg_prim->get_bface_flag());
-    bucket.add_attrib(mt);
-  }
-
-
-  // Also check the color of the primitive to see if we should assume
-  // alpha based on the alpha values specified in the egg file.
-  if (am == EggRenderMode::AM_unspecified) {
-    if (egg_prim->has_color()) {
-      if (egg_prim->get_color()[3] != 1.0) {
-        implicit_alpha = true;
-      }
-    }
-    EggPrimitive::const_iterator vi;
-    for (vi = egg_prim->begin();
-         !implicit_alpha && vi != egg_prim->end();
-         ++vi) {
-      if ((*vi)->has_color()) {
-        if ((*vi)->get_color()[3] != 1.0) {
-          implicit_alpha = true;
-        }
-      }
-    }
-
-    if (implicit_alpha) {
-      am = EggRenderMode::AM_on;
-    }
-  }
-
-  if (am == EggRenderMode::AM_on && 
-      egg_alpha_mode != EggRenderMode::AM_unspecified) {
-    // Alpha type "on" means to get the default transparency type.
-    am = egg_alpha_mode;
-  }
-
-  switch (am) {
-  case EggRenderMode::AM_on:
-  case EggRenderMode::AM_blend:
-    bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
-    break;
+  EggRenderState render_state(*this);
+  render_state.fill_state(egg_prim);
 
-  case EggRenderMode::AM_blend_no_occlude:
-    bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
-    bucket.add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off));
-    break;
-
-  case EggRenderMode::AM_ms:
-    bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample));
-    break;
-
-  case EggRenderMode::AM_ms_mask:
-    bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample_mask));
-    break;
-
-  case EggRenderMode::AM_binary:
-    bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_binary));
-    break;
-
-  case EggRenderMode::AM_dual:
-    bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_dual));
-    break;
-
-  default:
-    break;
-  }
-
-  switch (dwm) {
-  case EggRenderMode::DWM_on:
-    bucket.add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_on));
-    break;
-
-  case EggRenderMode::DWM_off:
-    bucket.add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off));
-    break;
-
-  default:
-    break;
-  }
-
-  switch (dtm) {
-  case EggRenderMode::DTM_on:
-    bucket.add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_less));
-    break;
-
-  case EggRenderMode::DTM_off:
-    bucket.add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_none));
-    break;
-
-  default:
-    break;
-  }
-
-  switch (vm) {
-  case EggRenderMode::VM_hidden:
-    bucket._hidden = true;
-    break;
-
-  case EggRenderMode::VM_normal:
-  default:
-    break;
-  }
-
-  if (has_bin) {
-    bucket.add_attrib(CullBinAttrib::make(bin, draw_order));
-
-  } else if (has_draw_order) {
-    bucket.add_attrib(CullBinAttrib::make("fixed", draw_order));
-  }
- 
-
-  if (egg_prim->get_bface_flag()) {
-    // The primitive is marked with backface culling disabled--we want
-    // to see both sides.
-    bucket.add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none));
-  }
+  bucket._state = render_state._state;
+  bucket._hidden = render_state._hidden;
+  bake_in_uvs = render_state._bake_in_uvs;
 }
 
 
@@ -1683,9 +1324,7 @@ setup_bucket(BuilderBucket &bucket, EggLoader::BakeInUVs &bake_in_uvs,
 ////////////////////////////////////////////////////////////////////
 PandaNode *EggLoader::
 make_node(EggNode *egg_node, PandaNode *parent) {
-  if (egg_node->is_of_type(EggPrimitive::get_class_type())) {
-    return make_node(DCAST(EggPrimitive, egg_node), parent);
-  } else if (egg_node->is_of_type(EggBin::get_class_type())) {
+  if (egg_node->is_of_type(EggBin::get_class_type())) {
     return make_node(DCAST(EggBin, egg_node), parent);
   } else if (egg_node->is_of_type(EggGroup::get_class_type())) {
     return make_node(DCAST(EggGroup, egg_node), parent);
@@ -1698,6 +1337,7 @@ make_node(EggNode *egg_node, PandaNode *parent) {
   return (PandaNode *)NULL;
 }
 
+/*
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::make_node (EggPrimitive)
 //       Access: Private
@@ -1727,6 +1367,7 @@ make_node(EggPrimitive *egg_prim, PandaNode *parent) {
   }
   return (PandaNode *)NULL;
 }
+*/
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::make_node (EggBin)
@@ -1735,41 +1376,149 @@ make_node(EggPrimitive *egg_prim, PandaNode *parent) {
 ////////////////////////////////////////////////////////////////////
 PandaNode *EggLoader::
 make_node(EggBin *egg_bin, PandaNode *parent) {
-  // Presently, an EggBin can only mean an LOD node (i.e. a parent of
-  // one or more EggGroups with LOD specifications).  Later it might
-  // mean other things as well.
+  // An EggBin might mean an LOD node (i.e. a parent of one or more
+  // EggGroups with LOD specifications), or it might mean a polyset
+  // node (a parent of one or more similar EggPrimitives).
+  switch (egg_bin->get_bin_number()) {
+  case EggBinner::BN_polyset:
+    return make_polyset(egg_bin, parent);
+
+  case EggBinner::BN_lod:
+    return make_lod(egg_bin, parent);
 
-  nassertr((EggBinner::BinNumber)egg_bin->get_bin_number() == EggBinner::BN_lod, NULL);
+  case EggBinner::BN_none:
+    break;
+  }
+
+  // Shouldn't get here.
+  return (PandaNode *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::make_polyset
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+PandaNode *EggLoader::
+make_polyset(EggBin *egg_bin, PandaNode *parent) {
+  if (egg_bin->empty()) {
+    // If there are no children--no primitives--never mind.
+    return NULL;
+  }
+
+  // We know that all of the primitives in the bin have the same
+  // vertex pool and same render state, so we can get that information
+  // from the first primitive.
+  EggGroup::const_iterator ci = egg_bin->begin();
+  nassertr(ci != egg_bin->end(), NULL);
+  CPT(EggPrimitive) first_prim = DCAST(EggPrimitive, (*ci));
+  nassertr(first_prim != (EggPrimitive *)NULL, NULL);
+  const EggRenderState *render_state;
+  DCAST_INTO_R(render_state, first_prim->get_user_data(EggRenderState::get_class_type()), NULL);
+
+  if (render_state->_hidden && egg_suppress_hidden) {
+    // Eat this polyset.
+    return NULL;
+  }
+
+  if (!use_qpgeom) {
+    // In the old Geom system, just send each primitive to the
+    // Builder.
+    for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) {
+      EggPrimitive *egg_prim;
+      DCAST_INTO_R(egg_prim, (*ci), NULL);
+      make_nonindexed_primitive(egg_prim, parent, NULL, _comp_verts_maker);
+    }
+
+    return NULL;
+  }
+
+  // Convert the primitives' vertex pool to a GeomVertexData.
+  PT(qpGeomVertexData) vertex_data = 
+    make_vertex_data(first_prim->get_pool(), first_prim->get_vertex_to_node());
+  nassertr(vertex_data != (qpGeomVertexData *)NULL, NULL);
+
+  // And now create a Geom to hold the primitives.
+  PT(qpGeom) geom = new qpGeom;
+  geom->set_vertex_data(vertex_data);
+
+  // Automatically triangulate any higher-order polygons we might have.
+  egg_bin->triangulate_polygons(true);
+
+  // 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_R(egg_prim, (*ci), NULL);
+    make_primitive(egg_prim, primitives);
+  }
+
+  if (!primitives.empty()) {
+    // Add each new primitive to the Geom.
+    Primitives::const_iterator pi;
+    for (pi = primitives.begin(); pi != primitives.end(); ++pi) {
+      qpGeomPrimitive *primitive = (*pi).second;
+      geom->add_primitive(primitive);
+    }
+    
+    // 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) {
+      DCAST(GeomNode, parent)->add_geom(geom, render_state->_state);
+      
+    } else {
+      PT(GeomNode) 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);
+    }
+  }
+
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::make_lod
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+PandaNode *EggLoader::
+make_lod(EggBin *egg_bin, PandaNode *parent) {
   LODNode *lod_node = new LODNode(egg_bin->get_name());
 
   pvector<LODInstance> instances;
-
+  
   EggGroup::const_iterator ci;
   for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) {
     LODInstance instance(*ci);
     instances.push_back(instance);
   }
-
+  
   // Now that we've created all of our children, put them in the
   // proper order and tell the LOD node about them.
   sort(instances.begin(), instances.end());
-
+  
   if (!instances.empty()) {
     // Set up the LOD node's center.  All of the children should have
     // the same center, because that's how we binned them.
     lod_node->set_center(LCAST(float, instances[0]._d->_center));
   }
-
+  
   for (size_t i = 0; i < instances.size(); i++) {
     // Create the children in the proper order within the scene graph.
     const LODInstance &instance = instances[i];
     make_node(instance._egg_node, lod_node);
-
+    
     // All of the children should have the same center, because that's
     // how we binned them.
     nassertr(lod_node->get_center().almost_equal
              (LCAST(float, instance._d->_center), 0.01), NULL);
-
+    
     // Tell the LOD node about this child's switching distances.
     lod_node->add_switch(instance._d->_switch_in, instance._d->_switch_out);
   }
@@ -1891,8 +1640,21 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
     }
 
   } else {
-    // A normal group; just create a normal node, and traverse.
-    node = new PandaNode(egg_group->get_name());
+    // A normal group; just create a normal node, and traverse.  But
+    // if all of the children of this group are polysets, anticipate
+    // this for the benefit of smaller grouping, and create a single
+    // GeomNode for all of the children.
+    bool all_polysets = false;
+    bool any_hidden = false;
+    if (use_qpgeom) {
+      check_for_polysets(egg_group, all_polysets, any_hidden);
+    }
+
+    if (all_polysets && !any_hidden) {
+      node = new GeomNode(egg_group->get_name());
+    } else {
+      node = new PandaNode(egg_group->get_name());
+    }
 
     EggGroup::const_iterator ci;
     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
@@ -2030,6 +1792,175 @@ make_node(EggGroupNode *egg_group, PandaNode *parent) {
   return node;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::check_for_polysets
+//       Access: Private
+//  Description: Sets all_polysets true if all of the children of this
+//               node represent a polyset.  Sets any_hidden true if
+//               any of those polysets are flagged hidden.
+////////////////////////////////////////////////////////////////////
+void EggLoader::
+check_for_polysets(EggGroup *egg_group, bool &all_polysets, bool &any_hidden) {
+  all_polysets = (!egg_group->empty());
+  any_hidden = false;
+
+  EggGroup::const_iterator ci;
+  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
+    if ((*ci)->is_of_type(EggBin::get_class_type())) {
+      EggBin *egg_bin = DCAST(EggBin, (*ci));
+      if (egg_bin->get_bin_number() == EggBinner::BN_polyset) {
+        // We know that all of the primitives in the bin have the same
+        // render state, so we can get that information from the first
+        // primitive.
+        EggGroup::const_iterator bci = egg_bin->begin();
+        nassertv(bci != egg_bin->end());
+        const EggPrimitive *first_prim;
+        DCAST_INTO_V(first_prim, (*bci));
+        const EggRenderState *render_state;
+        DCAST_INTO_V(render_state, first_prim->get_user_data(EggRenderState::get_class_type()));
+
+        if (render_state->_hidden) {
+          any_hidden = true;
+        }
+      } else {
+        all_polysets = false;
+        return;
+      }
+    } else {
+      all_polysets = false;
+      return;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::make_vertex_data
+//       Access: Private
+//  Description: Creates a GeomVertexData structure from the vertex
+//               pool, for the indicated transform space.  If a
+//               GeomVertexData has already been created for this
+//               transform, just returns it.
+////////////////////////////////////////////////////////////////////
+PT(qpGeomVertexData) EggLoader::
+make_vertex_data(EggVertexPool *vertex_pool, const LMatrix4d &transform) {
+  VertexPoolTransform vpt;
+  vpt._vertex_pool = vertex_pool;
+  vpt._transform = transform;
+
+  VertexPoolData::iterator di;
+  di = _vertex_pool_data.find(vpt);
+  if (di != _vertex_pool_data.end()) {
+    return (*di).second;
+  }
+
+  // Decide on the format for the vertices.
+  PT(qpGeomVertexArrayFormat) array_format = new qpGeomVertexArrayFormat;
+  array_format->add_data_type
+    (InternalName::get_vertex(), vertex_pool->get_num_dimensions(),
+     qpGeomVertexDataType::NT_float);
+
+  if (vertex_pool->has_normals()) {
+    array_format->add_data_type
+      (InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float);
+  }
+
+  if (vertex_pool->has_colors()) {
+    array_format->add_data_type
+      (InternalName::get_color(), 1, qpGeomVertexDataType::NT_packed_argb);
+  }
+
+  vector_string uv_names;
+  vertex_pool->get_uv_names(uv_names);
+  vector_string::const_iterator ni;
+  for (ni = uv_names.begin(); ni != uv_names.end(); ++ni) {
+    string name = (*ni);
+    if (name == "default") {
+      name = string();
+    }
+    array_format->add_data_type
+      (InternalName::get_texcoord_name(name), 2, 
+       qpGeomVertexDataType::NT_float);
+  }
+
+  CPT(qpGeomVertexFormat) format = 
+    qpGeomVertexFormat::register_format(new qpGeomVertexFormat(array_format));
+
+  // Now create a new GeomVertexData using the indicated format.
+  PT(qpGeomVertexData) vertex_data = new qpGeomVertexData(format);
+
+  // And fill the data from the vertex pool.
+  EggVertexPool::const_iterator vi;
+  for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
+    qpGeomVertexIterator gvi(vertex_data);
+    EggVertex *vertex = (*vi);
+    gvi.set_vertex(vertex->get_index());
+
+    gvi.set_data_type(InternalName::get_vertex());
+    gvi.set_data4(LCAST(float, vertex->get_pos4()));
+
+    if (vertex->has_normal()) {
+      gvi.set_data_type(InternalName::get_normal());
+      gvi.set_data3(LCAST(float, vertex->get_normal()));
+    }
+
+    if (vertex->has_color()) {
+      gvi.set_data_type(InternalName::get_color());
+      gvi.set_data4(vertex->get_color());
+    }
+
+    EggVertex::const_uv_iterator uvi;
+    for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) {
+      EggVertexUV *uv = (*uvi);
+      string name = uv->get_name();
+      if (name == "default") {
+        name = string();
+      }
+      gvi.set_data_type(InternalName::get_texcoord_name(name));
+      gvi.set_data2(LCAST(float, uv->get_uv()));
+    }
+  }
+
+  bool inserted = _vertex_pool_data.insert
+    (VertexPoolData::value_type(vpt, vertex_data)).second;
+  nassertr(inserted, vertex_data);
+
+  return vertex_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::make_primitive
+//       Access: Private
+//  Description: Creates a GeomPrimitive corresponding to the
+//               indicated EggPrimitive, and adds it to the set.
+////////////////////////////////////////////////////////////////////
+void EggLoader::
+make_primitive(EggPrimitive *egg_prim, EggLoader::Primitives &primitives) {
+  PT(qpGeomPrimitive) primitive;
+  if (egg_prim->is_of_type(EggPolygon::get_class_type())) {
+    if (egg_prim->size() == 3) {
+      primitive = new qpGeomTriangles;
+    }
+  }
+
+  if (primitive == NULL) {
+    // Don't know how to make this kind of primitive.
+    return;
+  }
+
+  // Insert the primitive into the set, but if we already have a
+  // primitive of that type, reset the pointer to that one instead.
+  pair<Primitives::iterator, bool> result =
+    primitives.insert(Primitives::value_type(primitive->get_type(), primitive));
+  primitive = (*result.first).second;
+
+  // Now add the vertices.
+  EggPrimitive::const_iterator vi;
+  for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) {
+    primitive->add_vertex((*vi)->get_index());
+  }
+  primitive->close_primitive();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::set_portal_polygon
 //       Access: Private
@@ -3086,84 +3017,5 @@ get_combine_operand(const EggTexture *egg_tex,
   return TextureStage::CO_undefined;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: EggLoader::get_tex_gen
-//       Access: Private, Static
-//  Description: Extracts the tex_gen from the given egg texture,
-//               and returns its corresponding TexGenAttrib mode.
-////////////////////////////////////////////////////////////////////
-TexGenAttrib::Mode EggLoader::
-get_tex_gen(const EggTexture *egg_tex) {
-  switch (egg_tex->get_tex_gen()) {
-  case EggTexture::TG_unspecified:
-    return TexGenAttrib::M_off;
-
-  case EggTexture::TG_eye_sphere_map:
-    return TexGenAttrib::M_eye_sphere_map;
-
-  case EggTexture::TG_world_cube_map:
-    return TexGenAttrib::M_world_cube_map;
-
-  case EggTexture::TG_eye_cube_map:
-    return TexGenAttrib::M_eye_cube_map;
-
-  case EggTexture::TG_world_normal:
-    return TexGenAttrib::M_world_normal;
-
-  case EggTexture::TG_eye_normal:
-    return TexGenAttrib::M_eye_normal;
-
-  case EggTexture::TG_world_position:
-    return TexGenAttrib::M_world_position;
-
-  case EggTexture::TG_object_position:
-    return TexGenAttrib::M_object_position;
-
-  case EggTexture::TG_eye_position:
-    return TexGenAttrib::M_eye_position;
-  };
-
-  return TexGenAttrib::M_off;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: EggLoader::apply_tex_mat
-//       Access: Private, Static
-//  Description: Applies the texture matrix from the indicated egg
-//               texture to the given TexMatrixAttrib, and returns the
-//               new attrib.
-////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) EggLoader::
-apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib, 
-              TextureStage *stage, const EggTexture *egg_tex) {
-  if (egg_tex->has_transform()) {
-    const LMatrix3d &tex_mat = egg_tex->get_transform();
-    LMatrix4f mat4(tex_mat(0, 0), tex_mat(0, 1), 0.0f, tex_mat(0, 2),
-                   tex_mat(1, 0), tex_mat(1, 1), 0.0f, tex_mat(1, 2),
-                   0.0f, 0.0f, 1.0f, 0.0f,
-                   tex_mat(2, 0), tex_mat(2, 1), 0.0f, tex_mat(2, 2));
-    CPT(TransformState) transform;
-
-    LVecBase3f scale, shear, hpr, translate;
-    if (decompose_matrix(mat4, scale, shear, hpr, translate)) {
-      // If the texture matrix can be represented componentwise, do
-      // so.
-      transform = TransformState::make_pos_hpr_scale_shear
-        (translate, hpr, scale, shear);
-
-    } else {
-      // Otherwise, make a matrix transform.
-      transform = TransformState::make_mat(mat4);
-    }
-  
-    if (tex_mat_attrib == (const RenderAttrib *)NULL) {
-      tex_mat_attrib = TexMatrixAttrib::make();
-    }
-    tex_mat_attrib = DCAST(TexMatrixAttrib, tex_mat_attrib)->
-      add_stage(stage, transform);
-  }
-    
-  return tex_mat_attrib;
-}
 
   

+ 25 - 9
panda/src/egg2pg/eggLoader.h

@@ -39,6 +39,8 @@
 #include "texGenAttrib.h"
 #include "eggTransform3d.h"
 #include "computedVerticesMaker.h"
+#include "qpgeomVertexData.h"
+#include "qpgeomPrimitive.h"
 
 class EggNode;
 class EggBin;
@@ -95,6 +97,9 @@ private:
 
   // This structure is returned by setup_bucket().
   typedef pmap<CPT(InternalName), const EggTexture *> BakeInUVs;
+
+  // This is used by make_primitive().
+  typedef pmap<TypeHandle, PT(qpGeomPrimitive) > Primitives;
   
   void make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
                         const LMatrix4d &mat);
@@ -108,21 +113,25 @@ private:
   void apply_texture_attributes(Texture *tex, const EggTexture *egg_tex);
   PT(TextureStage) make_texture_stage(const EggTexture *egg_tex);
 
-  CPT(RenderAttrib) get_material_attrib(const EggMaterial *egg_mat,
-                                        bool bface);
-
   void setup_bucket(BuilderBucket &bucket, BakeInUVs &bake_in_uvs,
                     PandaNode *parent, EggPrimitive *egg_prim);
 
   PandaNode *make_node(EggNode *egg_node, PandaNode *parent);
-  PandaNode *make_node(EggPrimitive *egg_prim, PandaNode *parent);
   PandaNode *make_node(EggBin *egg_bin, PandaNode *parent);
+  PandaNode *make_polyset(EggBin *egg_bin, PandaNode *parent);
+  PandaNode *make_lod(EggBin *egg_bin, PandaNode *parent);
   PandaNode *make_node(EggGroup *egg_group, PandaNode *parent);
   PandaNode *create_group_arc(EggGroup *egg_group, PandaNode *parent,
                                    PandaNode *node);
   PandaNode *make_node(EggTable *egg_table, PandaNode *parent);
   PandaNode *make_node(EggGroupNode *egg_group, PandaNode *parent);
 
+  void check_for_polysets(EggGroup *egg_group, bool &all_polysets, 
+                          bool &any_hidden);
+  PT(qpGeomVertexData) make_vertex_data(EggVertexPool *vertex_pool, 
+                                        const LMatrix4d &transform);
+  void make_primitive(EggPrimitive *egg_prim, Primitives &primitives);
+
   void set_portal_polygon(EggGroup *egg_group, PortalNode *pnode);
   EggPolygon *find_first_polygon(EggGroup *egg_group);
 
@@ -173,11 +182,6 @@ private:
   static TextureStage::CombineOperand
   get_combine_operand(const EggTexture *egg_tex, 
                       EggTexture::CombineChannel channel, int n);
-  static TexGenAttrib::Mode get_tex_gen(const EggTexture *egg_tex);
-
-  static CPT(RenderAttrib)
-  apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib, 
-                TextureStage *stage, const EggTexture *egg_tex);
 
   Builder _builder;
 
@@ -191,6 +195,15 @@ private:
   typedef pset<PandaNode *> Decals;
   Decals _decals;
 
+  class VertexPoolTransform {
+  public:
+    INLINE bool operator < (const VertexPoolTransform &other) const;
+    EggVertexPool *_vertex_pool;
+    LMatrix4d _transform;
+  };
+  typedef pmap<VertexPoolTransform, PT(qpGeomVertexData) > VertexPoolData;
+  VertexPoolData _vertex_pool_data;
+
   DeferredNodes _deferred_nodes;
 
   ComputedVerticesMaker _comp_verts_maker;
@@ -199,7 +212,10 @@ public:
   PT(PandaNode) _root;
   EggData _data;
   bool _error;
+
+  friend class EggRenderState;
 };
 
+#include "eggLoader.I"
 
 #endif

+ 58 - 0
panda/src/egg2pg/eggRenderState.I

@@ -0,0 +1,58 @@
+// Filename: eggRenderState.I
+// Created by:  drose (12Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggRenderState::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+EggRenderState::
+EggRenderState(EggLoader &loader) :
+  _loader(loader),
+  _state(RenderState::make_empty()),
+  _hidden(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggRenderState::add_attrib
+//       Access: Public
+//  Description: A convenience function to add the indicated render
+//               attribute to the aggregate state.
+////////////////////////////////////////////////////////////////////
+INLINE void EggRenderState::
+add_attrib(const RenderAttrib *attrib) {
+  _state = _state->add_attrib(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggRenderState::operator <
+//       Access: Public
+//  Description: Provides a unique ordering for different
+//               EggRenderState objects, so that primitives of similar
+//               state can be grouped together by the EggBinner.
+////////////////////////////////////////////////////////////////////
+INLINE bool EggRenderState::
+operator < (const EggRenderState &other) const {
+  if (_state != other._state) {
+    return _state < other._state;
+  }
+
+  return (int)_hidden < (int)other._hidden;
+}

+ 491 - 0
panda/src/egg2pg/eggRenderState.cxx

@@ -0,0 +1,491 @@
+// Filename: eggRenderState.cxx
+// Created by:  drose (12Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "eggRenderState.h"
+#include "eggRenderMode.h"
+#include "textureAttrib.h"
+#include "renderAttrib.h"
+#include "eggTexture.h"
+#include "texGenAttrib.h"
+#include "internalName.h"
+#include "eggCurve.h"
+#include "eggSurface.h"
+#include "cullBinAttrib.h"
+#include "cullFaceAttrib.h"
+#include "transparencyAttrib.h"
+#include "depthWriteAttrib.h"
+#include "depthTestAttrib.h"
+#include "texMatrixAttrib.h"
+#include "material.h"
+#include "materialAttrib.h"
+#include "materialPool.h"
+#include "config_gobj.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggRenderState::fill_state
+//       Access: Public
+//  Description: Sets up the state as appropriate for the indicated
+//               primitive.
+////////////////////////////////////////////////////////////////////
+void EggRenderState::
+fill_state(EggPrimitive *egg_prim) {
+  // The various EggRenderMode properties can be defined directly at
+  // the primitive, at a group above the primitive, or an a texture
+  // applied to the primitive.  The EggNode::determine_*() functions
+  // can find the right pointer to the level at which this is actually
+  // defined for a given primitive.
+  EggRenderMode::AlphaMode am = EggRenderMode::AM_unspecified;
+  EggRenderMode::DepthWriteMode dwm = EggRenderMode::DWM_unspecified;
+  EggRenderMode::DepthTestMode dtm = EggRenderMode::DTM_unspecified;
+  EggRenderMode::VisibilityMode vm = EggRenderMode::VM_unspecified;
+  bool implicit_alpha = false;
+  bool has_draw_order = false;
+  int draw_order = 0;
+  bool has_bin = false;
+  string bin;
+
+  EggRenderMode *render_mode;
+  render_mode = egg_prim->determine_alpha_mode();
+  if (render_mode != (EggRenderMode *)NULL) {
+    am = render_mode->get_alpha_mode();
+  }
+  render_mode = egg_prim->determine_depth_write_mode();
+  if (render_mode != (EggRenderMode *)NULL) {
+    dwm = render_mode->get_depth_write_mode();
+  }
+  render_mode = egg_prim->determine_depth_test_mode();
+  if (render_mode != (EggRenderMode *)NULL) {
+    dtm = render_mode->get_depth_test_mode();
+  }
+  render_mode = egg_prim->determine_visibility_mode();
+  if (render_mode != (EggRenderMode *)NULL) {
+    vm = render_mode->get_visibility_mode();
+  }
+  render_mode = egg_prim->determine_draw_order();
+  if (render_mode != (EggRenderMode *)NULL) {
+    has_draw_order = true;
+    draw_order = render_mode->get_draw_order();
+  }
+  render_mode = egg_prim->determine_bin();
+  if (render_mode != (EggRenderMode *)NULL) {
+    has_bin = true;
+    bin = render_mode->get_bin();
+  }
+
+  add_attrib(TextureAttrib::make_off());
+  int num_textures = egg_prim->get_num_textures();
+  CPT(RenderAttrib) texture_attrib = NULL;
+  CPT(RenderAttrib) tex_gen_attrib = NULL;
+  CPT(RenderAttrib) tex_mat_attrib = NULL;
+  TexMats tex_mats;
+
+  for (int i = 0; i < num_textures; i++) {
+    PT_EggTexture egg_tex = egg_prim->get_texture(i);
+
+    const TextureDef &def = _loader._textures[egg_tex];
+    if (def._texture != (const RenderAttrib *)NULL) {
+      if (texture_attrib == (RenderAttrib *)NULL) {
+        texture_attrib = def._texture;
+      } else {
+        texture_attrib = texture_attrib->compose(def._texture);
+      }
+
+      // If neither the primitive nor the texture specified an alpha
+      // mode, assume it should be alpha'ed if the texture has an
+      // alpha channel (unless the texture environment type is one
+      // that doesn't apply its alpha to the result).
+      if (am == EggRenderMode::AM_unspecified) {
+        const TextureAttrib *tex_attrib = DCAST(TextureAttrib, def._texture);
+        Texture *tex = tex_attrib->get_texture();
+        nassertv(tex != (Texture *)NULL);
+        int num_components = tex->get_num_components();
+        if (egg_tex->has_alpha_channel(num_components)) {
+          switch (egg_tex->get_env_type()) {
+          case EggTexture::ET_decal:
+          case EggTexture::ET_add:
+            break;
+
+          default:
+            implicit_alpha = true;
+          }
+        }
+      }
+
+      // Check for a texgen attrib.
+      bool has_tex_gen = false;
+      if (egg_tex->get_tex_gen() != EggTexture::TG_unspecified) {
+        has_tex_gen = true;
+        if (tex_gen_attrib == (const RenderAttrib *)NULL) {
+          tex_gen_attrib = TexGenAttrib::make();
+        }
+        tex_gen_attrib = DCAST(TexGenAttrib, tex_gen_attrib)->
+          add_stage(def._stage, get_tex_gen(egg_tex));
+      }
+
+      // Record the texture's associated texture matrix, so we can see
+      // if we can safely bake it into the UV's.  (We need to get the
+      // complete list of textures that share this same set of UV's
+      // per each unique texture matrix.  Whew!)
+      CPT(InternalName) uv_name;
+      if (egg_tex->has_uv_name() && egg_tex->get_uv_name() != string("default")) {
+        uv_name = InternalName::get_texcoord_name(egg_tex->get_uv_name());
+      } else {
+        uv_name = InternalName::get_texcoord();
+      }
+
+      if (has_tex_gen) {
+        // If the texture has a texgen mode, we will always apply its
+        // texture transform, never bake it in.  In fact, we don't
+        // even care about its UV's in this case, since we won't be
+        // using them.
+        tex_mat_attrib = apply_tex_mat(tex_mat_attrib, def._stage, egg_tex);
+
+      } else {
+        // Otherwise, we need to record that there is at least one
+        // texture on this particular UV name and with this particular
+        // texture matrix.  If there are no other textures, or if all
+        // of the other textures use the same texture matrix, then
+        // tex_mats[uv_name].size() will remain 1 (which tells us we
+        // can bake in the texture matrix to the UV's).  On the other
+        // hand, if there is another texture on the same uv name but
+        // with a different transform, it will increase
+        // tex_mats[uv_name].size() to at least 2, indicating we can't
+        // bake in the texture matrix.
+        tex_mats[uv_name][egg_tex->get_transform()].push_back(&def);
+      }
+    }
+  }
+
+  // These parametric primitive types can't have their UV's baked in,
+  // so if we have one of these we always need to apply the texture
+  // matrix as a separate attribute, regardless of how many textures
+  // share the particular UV set.
+  bool needs_tex_mat = (egg_prim->is_of_type(EggCurve::get_class_type()) ||
+                        egg_prim->is_of_type(EggSurface::get_class_type()));
+
+  // Now that we've visited all of the textures in the above loop, we
+  // can go back and see how many of them share the same UV name and
+  // texture matrix.
+  TexMats::const_iterator tmi;
+  for (tmi = tex_mats.begin(); tmi != tex_mats.end(); ++tmi) {
+    const InternalName *uv_name = (*tmi).first;
+    const TexMatTransforms &tmt = (*tmi).second;
+
+    if (tmt.size() == 1 && !needs_tex_mat && !use_qpgeom) {
+      // Only one unique transform sharing this set of UV's.  We can
+      // bake in the transform!
+      const TexMatTextures &tmtex = (*tmt.begin()).second;
+
+      // The first EggTexture on the list is sufficient, since we know
+      // they all have the same transform.
+      nassertv(!tmtex.empty());
+      TexMatTextures::const_iterator tmtexi = tmtex.begin();
+      const EggTexture *egg_tex = (*tmtexi)->_egg_tex;
+      if (egg_tex->has_transform()) {
+        // If there's no transform, it's an identity matrix; don't
+        // bother recording it.  Of course, it would do no harm to
+        // record it if we felt like it.
+        _bake_in_uvs[uv_name] = egg_tex;
+      }
+
+    } else {
+      // Multiple transforms on this UV set, or a geometry type that
+      // doesn't support baking in UV's.  We have to apply the
+      // texture matrix to each stage.
+      TexMatTransforms::const_iterator tmti;
+      for (tmti = tmt.begin(); tmti != tmt.end(); ++tmti) {
+        const TexMatTextures &tmtex = (*tmti).second;
+        TexMatTextures::const_iterator tmtexi;
+        for (tmtexi = tmtex.begin(); tmtexi != tmtex.end(); ++tmtexi) {
+          const EggTexture *egg_tex = (*tmtexi)->_egg_tex;
+          TextureStage *stage = (*tmtexi)->_stage;
+          
+          tex_mat_attrib = apply_tex_mat(tex_mat_attrib, stage, egg_tex);
+        }
+      }
+    }
+  }
+
+  if (texture_attrib != (RenderAttrib *)NULL) {
+    add_attrib(texture_attrib);
+  }
+
+  if (tex_gen_attrib != (RenderAttrib *)NULL) {
+    add_attrib(tex_gen_attrib);
+  }
+
+  if (tex_mat_attrib != (RenderAttrib *)NULL) {
+    add_attrib(tex_mat_attrib);
+  }
+
+  if (egg_prim->has_material()) {
+    CPT(RenderAttrib) mt =
+      get_material_attrib(egg_prim->get_material(),
+                          egg_prim->get_bface_flag());
+    add_attrib(mt);
+  }
+
+
+  // Also check the color of the primitive to see if we should assume
+  // alpha based on the alpha values specified in the egg file.
+  if (am == EggRenderMode::AM_unspecified) {
+    if (egg_prim->has_color()) {
+      if (egg_prim->get_color()[3] != 1.0) {
+        implicit_alpha = true;
+      }
+    }
+    EggPrimitive::const_iterator vi;
+    for (vi = egg_prim->begin();
+         !implicit_alpha && vi != egg_prim->end();
+         ++vi) {
+      if ((*vi)->has_color()) {
+        if ((*vi)->get_color()[3] != 1.0) {
+          implicit_alpha = true;
+        }
+      }
+    }
+
+    if (implicit_alpha) {
+      am = EggRenderMode::AM_on;
+    }
+  }
+
+  if (am == EggRenderMode::AM_on && 
+      egg_alpha_mode != EggRenderMode::AM_unspecified) {
+    // Alpha type "on" means to get the default transparency type.
+    am = egg_alpha_mode;
+  }
+
+  switch (am) {
+  case EggRenderMode::AM_on:
+  case EggRenderMode::AM_blend:
+    add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
+    break;
+
+  case EggRenderMode::AM_blend_no_occlude:
+    add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
+    add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off));
+    break;
+
+  case EggRenderMode::AM_ms:
+    add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample));
+    break;
+
+  case EggRenderMode::AM_ms_mask:
+    add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample_mask));
+    break;
+
+  case EggRenderMode::AM_binary:
+    add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_binary));
+    break;
+
+  case EggRenderMode::AM_dual:
+    add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_dual));
+    break;
+
+  default:
+    break;
+  }
+
+  switch (dwm) {
+  case EggRenderMode::DWM_on:
+    add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_on));
+    break;
+
+  case EggRenderMode::DWM_off:
+    add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off));
+    break;
+
+  default:
+    break;
+  }
+
+  switch (dtm) {
+  case EggRenderMode::DTM_on:
+    add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_less));
+    break;
+
+  case EggRenderMode::DTM_off:
+    add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_none));
+    break;
+
+  default:
+    break;
+  }
+
+  switch (vm) {
+  case EggRenderMode::VM_hidden:
+    _hidden = true;
+    break;
+
+  case EggRenderMode::VM_normal:
+  default:
+    break;
+  }
+
+  if (has_bin) {
+    add_attrib(CullBinAttrib::make(bin, draw_order));
+
+  } else if (has_draw_order) {
+    add_attrib(CullBinAttrib::make("fixed", draw_order));
+  }
+ 
+
+  if (egg_prim->get_bface_flag()) {
+    // The primitive is marked with backface culling disabled--we want
+    // to see both sides.
+    add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggRenderState::get_material_attrib
+//       Access: Private
+//  Description: Returns a RenderAttrib suitable for enabling the
+//               material indicated by the given EggMaterial, and with
+//               the indicated backface flag.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) EggRenderState::
+get_material_attrib(const EggMaterial *egg_mat, bool bface) {
+  Materials &materials = 
+    bface ? _loader._materials_bface : _loader._materials;
+
+  // First, check whether we've seen this material before.
+  Materials::const_iterator mi;
+  mi = materials.find(egg_mat);
+  if (mi != materials.end()) {
+    return (*mi).second;
+  }
+
+  // Ok, this is the first time we've seen this particular
+  // EggMaterial.  Create a new Material that matches it.
+  PT(Material) mat = new Material;
+  if (egg_mat->has_diff()) {
+    mat->set_diffuse(egg_mat->get_diff());
+    // By default, ambient is the same as diffuse, if diffuse is
+    // specified but ambient is not.
+    mat->set_ambient(egg_mat->get_diff());
+  }
+  if (egg_mat->has_amb()) {
+    mat->set_ambient(egg_mat->get_amb());
+  }
+  if (egg_mat->has_emit()) {
+    mat->set_emission(egg_mat->get_emit());
+  }
+  if (egg_mat->has_spec()) {
+    mat->set_specular(egg_mat->get_spec());
+  }
+  if (egg_mat->has_shininess()) {
+    mat->set_shininess(egg_mat->get_shininess());
+  }
+  if (egg_mat->has_local()) {
+    mat->set_local(egg_mat->get_local());
+  }
+
+  mat->set_twoside(bface);
+
+  // Now get a global Material pointer, shared with other models.
+  const Material *shared_mat = MaterialPool::get_material(mat);
+
+  // And create a MaterialAttrib for this Material.
+  CPT(RenderAttrib) mt = MaterialAttrib::make(shared_mat);
+  materials.insert(Materials::value_type(egg_mat, mt));
+
+  return mt;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggRenderState::get_tex_gen
+//       Access: Private, Static
+//  Description: Extracts the tex_gen from the given egg texture,
+//               and returns its corresponding TexGenAttrib mode.
+////////////////////////////////////////////////////////////////////
+TexGenAttrib::Mode EggRenderState::
+get_tex_gen(const EggTexture *egg_tex) {
+  switch (egg_tex->get_tex_gen()) {
+  case EggTexture::TG_unspecified:
+    return TexGenAttrib::M_off;
+
+  case EggTexture::TG_eye_sphere_map:
+    return TexGenAttrib::M_eye_sphere_map;
+
+  case EggTexture::TG_world_cube_map:
+    return TexGenAttrib::M_world_cube_map;
+
+  case EggTexture::TG_eye_cube_map:
+    return TexGenAttrib::M_eye_cube_map;
+
+  case EggTexture::TG_world_normal:
+    return TexGenAttrib::M_world_normal;
+
+  case EggTexture::TG_eye_normal:
+    return TexGenAttrib::M_eye_normal;
+
+  case EggTexture::TG_world_position:
+    return TexGenAttrib::M_world_position;
+
+  case EggTexture::TG_object_position:
+    return TexGenAttrib::M_object_position;
+
+  case EggTexture::TG_eye_position:
+    return TexGenAttrib::M_eye_position;
+  };
+
+  return TexGenAttrib::M_off;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggRenderState::apply_tex_mat
+//       Access: Private, Static
+//  Description: Applies the texture matrix from the indicated egg
+//               texture to the given TexMatrixAttrib, and returns the
+//               new attrib.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) EggRenderState::
+apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib, 
+              TextureStage *stage, const EggTexture *egg_tex) {
+  if (egg_tex->has_transform()) {
+    const LMatrix3d &tex_mat = egg_tex->get_transform();
+    LMatrix4f mat4(tex_mat(0, 0), tex_mat(0, 1), 0.0f, tex_mat(0, 2),
+                   tex_mat(1, 0), tex_mat(1, 1), 0.0f, tex_mat(1, 2),
+                   0.0f, 0.0f, 1.0f, 0.0f,
+                   tex_mat(2, 0), tex_mat(2, 1), 0.0f, tex_mat(2, 2));
+    CPT(TransformState) transform;
+
+    LVecBase3f scale, shear, hpr, translate;
+    if (decompose_matrix(mat4, scale, shear, hpr, translate)) {
+      // If the texture matrix can be represented componentwise, do
+      // so.
+      transform = TransformState::make_pos_hpr_scale_shear
+        (translate, hpr, scale, shear);
+
+    } else {
+      // Otherwise, make a matrix transform.
+      transform = TransformState::make_mat(mat4);
+    }
+  
+    if (tex_mat_attrib == (const RenderAttrib *)NULL) {
+      tex_mat_attrib = TexMatrixAttrib::make();
+    }
+    tex_mat_attrib = DCAST(TexMatrixAttrib, tex_mat_attrib)->
+      add_stage(stage, transform);
+  }
+    
+  return tex_mat_attrib;
+}

+ 84 - 0
panda/src/egg2pg/eggRenderState.h

@@ -0,0 +1,84 @@
+// Filename: eggRenderState.h
+// Created by:  drose (12Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGRENDERSTATE_H
+#define EGGRENDERSTATE_H
+
+#include "pandabase.h"
+
+#include "eggUserData.h"
+#include "eggLoader.h"
+#include "renderState.h"
+#include "renderAttrib.h"
+#include "internalName.h"
+#include "luse.h"
+#include "pointerTo.h"
+#include "pvector.h"
+#include "pmap.h"
+
+class EggPrimitive;
+class EggTexture;
+class EggMaterial;
+
+///////////////////////////////////////////////////////////////////
+//       Class : EggRenderState
+// Description : This class is used within this package only to record
+//               the render state that should be assigned to each
+//               primitive.  It is assigned to EggPrimitive objects
+//               via the EggBinner.
+////////////////////////////////////////////////////////////////////
+class EggRenderState : public EggUserData {
+public:
+  INLINE EggRenderState(EggLoader &loader);
+  INLINE void add_attrib(const RenderAttrib *attrib);
+
+  void fill_state(EggPrimitive *egg_prim);
+
+  INLINE bool operator < (const EggRenderState &other) const;
+
+private:
+  CPT(RenderAttrib) get_material_attrib(const EggMaterial *egg_mat,
+                                        bool bface);
+  static TexGenAttrib::Mode get_tex_gen(const EggTexture *egg_tex);
+
+  static CPT(RenderAttrib)
+  apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib, 
+                TextureStage *stage, const EggTexture *egg_tex);
+
+public:
+  CPT(RenderState) _state;
+  bool _hidden;
+
+  typedef EggLoader::BakeInUVs BakeInUVs;
+  typedef EggLoader::TextureDef TextureDef;
+  typedef EggLoader::Materials Materials;
+
+  BakeInUVs _bake_in_uvs;
+
+private:
+  EggLoader &_loader;
+
+  typedef pvector<const TextureDef *> TexMatTextures;
+  typedef pmap<LMatrix3d, TexMatTextures> TexMatTransforms;
+  typedef pmap<CPT(InternalName), TexMatTransforms> TexMats;
+};
+
+#include "eggRenderState.I"
+
+#endif
+

+ 10 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -547,6 +547,12 @@ reset() {
       << "max texture dimension = " << _max_texture_dimension
       << ", max 3d texture = " << _max_3d_texture_dimension
       << ", max cube map = " << _max_cube_map_dimension << "\n";
+    GLint max_elements_vertices, max_elements_indices;
+    GLP(GetIntegerv)(GL_MAX_ELEMENTS_VERTICES, &max_elements_vertices);
+    GLP(GetIntegerv)(GL_MAX_ELEMENTS_INDICES, &max_elements_indices);
+    GLCAT.debug()
+      << "max_elements_vertices = " << max_elements_vertices
+      << ", max_elements_indices = " << max_elements_indices << "\n";
   }
 
   report_my_gl_errors();
@@ -2020,7 +2026,10 @@ bool CLP(GraphicsStateGuardian)::
 begin_draw_primitives(const qpGeomVertexData *vertex_data) {
   DO_PSTATS_STUFF(_draw_primitive_pcollector.start());
 
-  GraphicsStateGuardian::begin_draw_primitives(vertex_data);
+  if (!GraphicsStateGuardian::begin_draw_primitives(vertex_data)) {
+    return false;
+  }
+  nassertr(_vertex_data != (qpGeomVertexData *)NULL, false);
 
   CPTA_uchar array_data;
   int num_components;

+ 11 - 3
panda/src/gobj/qpgeomPrimitive.cxx

@@ -90,12 +90,13 @@ add_vertex(int vertex) {
   unsigned short short_vertex = vertex;
   nassertv((int)short_vertex == vertex);
 
+  clear_cache();
   CDWriter cdata(_cycler);
   cdata->_vertices.push_back(short_vertex);
 
   if (cdata->_got_minmax) {
     cdata->_min_vertex = min(cdata->_min_vertex, short_vertex);
-    cdata->_max_vertex = max(cdata->_min_vertex, short_vertex);
+    cdata->_max_vertex = max(cdata->_max_vertex, short_vertex);
   }
 }
 
@@ -107,6 +108,7 @@ add_vertex(int vertex) {
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::
 add_consecutive_vertices(int start, int num_vertices) {
+  clear_cache();
   int end = (start + num_vertices) - 1;
   unsigned short short_start = start;
   unsigned short short_end = end;
@@ -119,7 +121,7 @@ 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->_min_vertex, short_end);
+    cdata->_max_vertex = max(cdata->_max_vertex, short_end);
   }
 }
 
@@ -132,6 +134,7 @@ add_consecutive_vertices(int start, int num_vertices) {
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::
 close_primitive() {
+  clear_cache();
   int num_vertices_per_primitive = get_num_vertices_per_primitive();
 
   CDWriter cdata(_cycler);
@@ -159,6 +162,7 @@ close_primitive() {
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::
 clear_vertices() {
+  clear_cache();
   CDWriter cdata(_cycler);
   cdata->_vertices.clear();
   cdata->_lengths.clear();
@@ -174,6 +178,7 @@ clear_vertices() {
 ////////////////////////////////////////////////////////////////////
 PTA_ushort qpGeomPrimitive::
 modify_vertices() {
+  clear_cache();
   CDWriter cdata(_cycler);
   return cdata->_vertices;
 }
@@ -187,6 +192,7 @@ modify_vertices() {
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::
 set_vertices(PTA_ushort vertices) {
+  clear_cache();
   CDWriter cdata(_cycler);
   cdata->_vertices = vertices;
 }
@@ -205,6 +211,7 @@ set_vertices(PTA_ushort vertices) {
 ////////////////////////////////////////////////////////////////////
 PTA_int qpGeomPrimitive::
 modify_lengths() {
+  clear_cache();
   CDWriter cdata(_cycler);
   return cdata->_lengths;
 }
@@ -223,6 +230,7 @@ modify_lengths() {
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::
 set_lengths(PTA_int lengths) {
+  clear_cache();
   CDWriter cdata(_cycler);
   cdata->_lengths = lengths;
 }
@@ -565,7 +573,7 @@ recompute_minmax() {
     ++ii;
     while (ii != cdata->_vertices.end()) {
       cdata->_min_vertex = min(cdata->_min_vertex, (*ii));
-      cdata->_max_vertex = max(cdata->_min_vertex, (*ii));
+      cdata->_max_vertex = max(cdata->_max_vertex, (*ii));
       
       ++ii;
     }

+ 0 - 3
panda/src/gobj/qpgeomVertexArrayFormat.cxx

@@ -174,9 +174,6 @@ operator = (const qpGeomVertexArrayFormat &copy) {
 ////////////////////////////////////////////////////////////////////
 qpGeomVertexArrayFormat::
 ~qpGeomVertexArrayFormat() {
-  // Once registered, these things should not be deallocated.
-  nassertv(!_is_registered);
-
   DataTypes::iterator dti;
   for (dti = _data_types.begin(); dti != _data_types.end(); ++dti) {
     delete (*dti);

+ 80 - 13
panda/src/gobj/qpgeomVertexData.cxx

@@ -455,25 +455,61 @@ set_data(int array, const qpGeomVertexDataType *data_type,
   switch (data_type->get_numeric_type()) {
   case qpGeomVertexDataType::NT_uint8:
     {
-      nassertv(num_values <= data_type->get_num_values());
-      for (int i = 0; i < num_values; i++) {
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
         int value = (int)(data[i] * 255.0f);
-        *(unsigned char *)&array_data[element] = value;
+        array_data[element] = value;
         element += 1;
+        ++i;
       }
+      while (i < data_type->get_num_values()) {
+        array_data[element] = 0;
+        element += 1;
+        ++i;
+      }        
     }
     break;
 
   case qpGeomVertexDataType::NT_packed_argb:
     {
-      nassertv(num_values == 4);
-      *(PN_uint32 *)&array_data[element] = pack_argb(data);
+      if (num_values == 4) {
+        *(PN_uint32 *)&array_data[element] = pack_argb(data);
+      } else {
+        // Elevate (or truncate) to 4 components.
+        float data4[4];
+        memset(data4, 0, 4 * sizeof(float));
+        memcpy(data4, data, min(4, num_values) * sizeof(float));
+        *(PN_uint32 *)&array_data[element] = pack_argb(data4);
+      }
     }
     break;
 
   case qpGeomVertexDataType::NT_float:
-    nassertv(num_values == data_type->get_num_values());
-    memcpy(&array_data[element], data, data_type->get_total_bytes());
+    if (num_values == 4 && sizeof(float) == sizeof(PN_float32)) {
+      // The easy way: we can memcpy the data directly in.
+      memcpy(&array_data[element], data, data_type->get_total_bytes());
+
+    } else {
+      // Elevate or truncate to the right number of components.
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
+        *(PN_float32 *)&array_data[element] = data[i];
+        element += sizeof(PN_float32);
+        ++i;
+      }
+      while (i < data_type->get_num_values()) {
+        if (i == 3 && data_type->get_num_values() == 4) {
+          *(PN_float32 *)&array_data[element] = 1.0f;
+        } else {
+          *(PN_float32 *)&array_data[element] = 0.0f;
+        }
+        element += sizeof(PN_float32);
+        ++i;
+      }
+    }
+      
     break;
   }
 }
@@ -500,25 +536,56 @@ get_data(int array, const qpGeomVertexDataType *data_type,
   switch (data_type->get_numeric_type()) {
   case qpGeomVertexDataType::NT_uint8:
     {
-      nassertv(num_values <= data_type->get_num_values());
-      for (int i = 0; i < num_values; i++) {
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
         int value = *(unsigned char *)&array_data[element];
         element += 1;
         data[i] = (float)value / 255.0f;
+        ++i;
+      }
+      while (i < num_values) {
+        data[i] = 0.0f;
+        ++i;
       }
     }
     break;
 
   case qpGeomVertexDataType::NT_packed_argb:
     {
-      nassertv(num_values == 4);
-      unpack_argb(data, *(PN_uint32 *)&array_data[element]);
+      if (num_values == 4) {
+        unpack_argb(data, *(PN_uint32 *)&array_data[element]);
+      } else {
+        float data4[4];
+        unpack_argb(data4, *(PN_uint32 *)&array_data[element]);
+        memset(data, 0, num_values * sizeof(float));
+        memcpy(data, data4, min(num_values, 4) * sizeof(float));
+      }
     }
     break;
 
   case qpGeomVertexDataType::NT_float:
-    nassertv(num_values <= data_type->get_num_values());
-    memcpy(data, &array_data[element], num_values * sizeof(PN_float32));
+    if (num_values == data_type->get_num_values() && 
+        sizeof(float) == sizeof(PN_float32)) {
+      memcpy(data, &array_data[element], num_values * sizeof(PN_float32));
+    } else {
+      int i = 0;
+      int min_values = min(num_values, data_type->get_num_values());
+      while (i < min_values) {
+        data[i] = *(PN_float32 *)&array_data[element];
+        element += sizeof(PN_float32);
+        ++i;
+      }
+      while (i < num_values) {
+        if (i == 3 && num_values == 4) {
+          data[i] = 1.0f;
+        } else {
+          data[i] = 0.0f;
+        }
+        ++i;
+      }
+    }
+
     break;
   }
 }

+ 5 - 4
panda/src/pgraph/cullResult.cxx

@@ -85,10 +85,6 @@ make_next() const {
 ////////////////////////////////////////////////////////////////////
 void CullResult::
 add_object(CullableObject *object) {
-  // Munge vertices as needed for the GSG's requirements, and the
-  // object's current state.
-  object->munge_vertices(_gsg);
-
   // Check to see if there's a special transparency setting.
   const RenderState *state = object->_state;
   nassertv(state != (const RenderState *)NULL);
@@ -132,6 +128,7 @@ add_object(CullableObject *object) {
               get_dual_transparent_state_decals() : 
               get_dual_transparent_state();
             transparent_part->_state = state->compose(transparent_state);
+            transparent_part->munge_vertices(_gsg);
             CullBin *bin = get_bin(transparent_part->_state->get_bin_index());
             nassertv(bin != (CullBin *)NULL);
             bin->add_object(transparent_part);
@@ -155,6 +152,10 @@ add_object(CullableObject *object) {
       break;
     }
   }
+
+  // Munge vertices as needed for the GSG's requirements, and the
+  // object's current state.
+  object->munge_vertices(_gsg);
   
   CullBin *bin = get_bin(object->_state->get_bin_index());
   nassertv(bin != (CullBin *)NULL);

+ 4 - 1
panda/src/pgraph/cullableObject.I

@@ -114,9 +114,12 @@ INLINE void CullableObject::
 munge_vertices(GraphicsStateGuardianBase *gsg) {
   // Temporary test until the experimental Geom rewrite becomes the
   // actual Geom implementation.
-  if (_geom->is_exact_type(qpGeom::get_class_type())) {
+  if (_geom == (Geom *)NULL || _geom->is_exact_type(qpGeom::get_class_type())) {
     CPT(qpGeomMunger) munger = gsg->get_geom_munger(_state);
     _munged_data = munger->munge_data(((const qpGeom *)_geom.p())->get_vertex_data());
+    if (_next != (CullableObject *)NULL) {
+      _next->munge_vertices(gsg);
+    }
   }
 }