Răsfoiți Sursa

*** empty log message ***

David Rose 25 ani în urmă
părinte
comite
98a4f21207

+ 6 - 2
panda/src/egg/Sources.pp

@@ -19,7 +19,10 @@
     eggFilenameNode.cxx eggFilenameNode.h eggGroup.I eggGroup.cxx \
     eggFilenameNode.cxx eggFilenameNode.h eggGroup.I eggGroup.cxx \
     eggGroup.h eggGroupNode.I eggGroupNode.cxx eggGroupNode.h \
     eggGroup.h eggGroupNode.I eggGroupNode.cxx eggGroupNode.h \
     eggGroupUniquifier.h eggGroupUniquifier.cxx \
     eggGroupUniquifier.h eggGroupUniquifier.cxx \
-    eggMaterial.I eggMaterial.cxx eggMaterial.h eggMiscFuncs.I \
+    eggMaterial.I eggMaterial.cxx eggMaterial.h \
+    eggMaterialCollection.I eggMaterialCollection.cxx \
+    eggMaterialCollection.h \
+    eggMiscFuncs.I \
     eggMiscFuncs.cxx eggMiscFuncs.h eggNamedObject.I eggNamedObject.cxx \
     eggMiscFuncs.cxx eggMiscFuncs.h eggNamedObject.I eggNamedObject.cxx \
     eggNamedObject.h eggNameUniquifier.cxx eggNameUniquifier.h \
     eggNamedObject.h eggNameUniquifier.cxx eggNameUniquifier.h \
     eggNode.I eggNode.cxx eggNode.h eggNurbsCurve.I \
     eggNode.I eggNode.cxx eggNode.h eggNurbsCurve.I \
@@ -51,7 +54,8 @@
     eggExternalReference.h eggFilenameNode.I eggFilenameNode.h \
     eggExternalReference.h eggFilenameNode.I eggFilenameNode.h \
     eggGroup.I eggGroup.h eggGroupNode.I eggGroupNode.h \
     eggGroup.I eggGroup.h eggGroupNode.I eggGroupNode.h \
     eggGroupUniquifier.h eggMaterial.I \
     eggGroupUniquifier.h eggMaterial.I \
-    eggMaterial.h eggMorph.I eggMorph.h eggMorphList.I eggMorphList.h \
+    eggMaterial.h eggMaterialCollection.I eggMaterialCollection.h \
+    eggMorph.I eggMorph.h eggMorphList.I eggMorphList.h \
     eggNamedObject.I eggNamedObject.h eggNameUniquifier.h eggNode.I eggNode.h \
     eggNamedObject.I eggNamedObject.h eggNameUniquifier.h eggNode.I eggNode.h \
     eggNurbsCurve.I eggNurbsCurve.h eggNurbsSurface.I eggNurbsSurface.h \
     eggNurbsCurve.I eggNurbsCurve.h eggNurbsSurface.I eggNurbsSurface.h \
     eggObject.I eggObject.h eggParameters.h eggPoint.I eggPoint.h \
     eggObject.I eggObject.h eggParameters.h eggPoint.I eggPoint.h \

+ 33 - 5
panda/src/egg/eggData.cxx

@@ -6,6 +6,7 @@
 #include "eggData.h"
 #include "eggData.h"
 #include "eggCoordinateSystem.h"
 #include "eggCoordinateSystem.h"
 #include "eggTextureCollection.h"
 #include "eggTextureCollection.h"
+#include "eggMaterialCollection.h"
 #include "eggComment.h"
 #include "eggComment.h"
 #include "eggPoolUniquifier.h"
 #include "eggPoolUniquifier.h"
 #include "config_egg.h"
 #include "config_egg.h"
@@ -133,9 +134,9 @@ resolve_externals(const DSearchPath &searchpath) {
 //  Description: Removes duplicate references to the same texture
 //  Description: Removes duplicate references to the same texture
 //               image with the same properties.  Considers two
 //               image with the same properties.  Considers two
 //               texture references with identical properties, but
 //               texture references with identical properties, but
-//               different tref names, to be the equivalent, and
-//               collapses them, choosing one tref name to keep
-//               arbitrarily.  Returns the number of textures removed.
+//               different tref names, to be equivalent, and collapses
+//               them, choosing one tref name to keep arbitrarily.
+//               Returns the number of textures removed.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int EggData::
 int EggData::
 collapse_equivalent_textures() {
 collapse_equivalent_textures() {
@@ -145,6 +146,24 @@ collapse_equivalent_textures() {
     textures.collapse_equivalent_textures(~EggTexture::E_tref_name, this);
     textures.collapse_equivalent_textures(~EggTexture::E_tref_name, this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggData::collapse_equivalent_materials
+//       Access: Public
+//  Description: Removes duplicate references to the same material
+//               with the same properties.  Considers two material
+//               references with identical properties, but different
+//               mref names, to be equivalent, and collapses them,
+//               choosing one mref name to keep arbitrarily.  Returns
+//               the number of materials removed.
+////////////////////////////////////////////////////////////////////
+int EggData::
+collapse_equivalent_materials() {
+  EggMaterialCollection materials;
+  materials.find_used_materials(this);
+  return 
+    materials.collapse_equivalent_materials(~EggMaterial::E_mref_name, this);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggData::write_egg
 //     Function: EggData::write_egg
 //       Access: Public
 //       Access: Public
@@ -286,8 +305,17 @@ pre_write() {
 
 
   textures.insert_textures(this, ci);
   textures.insert_textures(this, ci);
 
 
-  // Also make sure that the vertex pools and materials are uniquely
-  // named.  This also checks textures, which is kind of redundant
+  // Do the same thing with the materials.
+  EggMaterialCollection materials;
+  materials.extract_materials(this);
+  materials.remove_unused_materials(this);
+  materials.collapse_equivalent_materials(~0, this);
+  materials.uniquify_mrefs();
+  materials.sort_by_mref();
+  materials.insert_materials(this, ci);
+
+  // Also make sure that the vertex pools are uniquely named.  This
+  // also checks textures and materials, which is kind of redundant
   // since we just did that, but we don't mind.
   // since we just did that, but we don't mind.
   EggPoolUniquifier pu;
   EggPoolUniquifier pu;
   pu.uniquify(this);
   pu.uniquify(this);

+ 1 - 0
panda/src/egg/eggData.h

@@ -44,6 +44,7 @@ public:
 
 
   bool resolve_externals(const DSearchPath &searchpath = DSearchPath());
   bool resolve_externals(const DSearchPath &searchpath = DSearchPath());
   int collapse_equivalent_textures();
   int collapse_equivalent_textures();
+  int collapse_equivalent_materials();
 
 
   bool write_egg(Filename filename);
   bool write_egg(Filename filename);
   bool write_egg(ostream &out);
   bool write_egg(ostream &out);

+ 47 - 0
panda/src/egg/eggGroupNode.cxx

@@ -13,6 +13,7 @@
 #include "eggVertexPool.h"
 #include "eggVertexPool.h"
 #include "eggVertex.h"
 #include "eggVertex.h"
 #include "eggTextureCollection.h"
 #include "eggTextureCollection.h"
+#include "eggMaterialCollection.h"
 #include "config_egg.h"
 #include "config_egg.h"
 
 
 #include <dSearchPath.h>
 #include <dSearchPath.h>
@@ -830,6 +831,52 @@ find_textures(EggTextureCollection *collection) {
   return num_found;
   return num_found;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggGroupNode::find_materials()
+//       Access: Protected
+//  Description: Walks the tree, looking for EggMaterials.  Each
+//               EggMaterial that is found is removed from the
+//               hierarchy and added to the EggMaterialCollection.
+//               Returns the number of EggMaterials found.
+////////////////////////////////////////////////////////////////////
+int EggGroupNode::
+find_materials(EggMaterialCollection *collection) {
+  int num_found = 0;
+
+  // We can do this ci/cnext iteration through the list as we modify
+  // it, only because we know this works with an STL list type
+  // container.  If this were a vector or a set, this wouldn't
+  // necessarily work.
+
+  Children::iterator ci, cnext;
+  ci = _children.begin();
+  while (ci != _children.end()) {
+    cnext = ci;
+    ++cnext;
+    EggNode *child = *ci;
+
+    if (child->is_of_type(EggMaterial::get_class_type())) {
+      PT(EggMaterial) tex = DCAST(EggMaterial, child);
+
+      // Now remove the EggMaterial entry from our child list.
+      prepare_remove_child(tex);
+      _children.erase(ci);
+
+      // And add it to the collection.
+      collection->add_material(tex);
+      num_found++;
+
+    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
+      num_found +=
+	DCAST(EggGroupNode, child)->find_materials(collection);
+    }
+
+    ci = cnext;
+  }
+
+  return num_found;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggGroupNode::r_resolve_externals
 //     Function: EggGroupNode::r_resolve_externals
 //       Access: Protected
 //       Access: Protected

+ 3 - 0
panda/src/egg/eggGroupNode.h

@@ -18,6 +18,7 @@
 #include <list>
 #include <list>
 
 
 class EggTextureCollection;
 class EggTextureCollection;
+class EggMaterialCollection;
 class EggPolygon;
 class EggPolygon;
 class EggVertex;
 class EggVertex;
 class DSearchPath;
 class DSearchPath;
@@ -119,6 +120,7 @@ protected:
 
 
   CoordinateSystem find_coordsys_entry();
   CoordinateSystem find_coordsys_entry();
   int find_textures(EggTextureCollection *collection);
   int find_textures(EggTextureCollection *collection);
+  int find_materials(EggMaterialCollection *collection);
   bool r_resolve_externals(const DSearchPath &searchpath,
   bool r_resolve_externals(const DSearchPath &searchpath,
 			   CoordinateSystem coordsys);
 			   CoordinateSystem coordsys);
 
 
@@ -163,6 +165,7 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 
 
   friend class EggTextureCollection;
   friend class EggTextureCollection;
+  friend class EggMaterialCollection;
 };
 };
 
 
 #include "eggGroupNode.I"
 #include "eggGroupNode.I"

+ 112 - 2
panda/src/egg/eggMaterial.I

@@ -141,7 +141,7 @@ get_emit() const {
   if (has_emit()) {
   if (has_emit()) {
     return _emit;
     return _emit;
   } else {
   } else {
-    return RGBColorf(1.0, 1.0, 1.0);
+    return RGBColorf(0.0, 0.0, 0.0);
   }
   }
 }
 }
 
 
@@ -188,6 +188,116 @@ get_spec() const {
   if (has_spec()) {
   if (has_spec()) {
     return _spec;
     return _spec;
   } else {
   } else {
-    return RGBColorf(1.0, 1.0, 1.0);
+    return RGBColorf(0.0, 0.0, 0.0);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterial::set_shininess
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void EggMaterial::
+set_shininess(double shininess) {
+  _shininess = shininess;
+  _flags |= F_shininess;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterial::clear_shininess
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void EggMaterial::
+clear_shininess() {
+  _flags &= ~F_shininess;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterial::has_shininess
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMaterial::
+has_shininess() const {
+  return (_flags & F_shininess) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterial::get_shininess
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE double EggMaterial::
+get_shininess() const {
+  if (has_shininess()) {
+    return _shininess;
+  } else {
+    return 0.0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterial::set_local
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void EggMaterial::
+set_local(bool local) {
+  _local = local;
+  _flags |= F_local;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterial::clear_local
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void EggMaterial::
+clear_local() {
+  _flags &= ~F_local;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterial::has_local
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMaterial::
+has_local() const {
+  return (_flags & F_local) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterial::get_local
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool EggMaterial::
+get_local() const {
+  if (has_local()) {
+    return _local;
+  } else {
+    return 0.0;
   }
   }
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: UniqueEggMaterials::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE UniqueEggMaterials::
+UniqueEggMaterials(int eq) : _eq(eq) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UniqueEggMaterials::Function operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool UniqueEggMaterials::
+operator ()(const EggMaterial *t1, const EggMaterial *t2) const {
+  return t1->sorts_less_than(*t2, _eq);
+}
+

+ 122 - 0
panda/src/egg/eggMaterial.cxx

@@ -55,5 +55,127 @@ write(ostream &out, int indent_level) const {
       << "<Scalar> diffb { " << get_diff()[2] << " }\n";
       << "<Scalar> diffb { " << get_diff()[2] << " }\n";
   }
   }
 
 
+  if (has_amb()) {
+    indent(out, indent_level + 2)
+      << "<Scalar> ambr { " << get_amb()[0] << " }\n";
+    indent(out, indent_level + 2)
+      << "<Scalar> ambg { " << get_amb()[1] << " }\n";
+    indent(out, indent_level + 2)
+      << "<Scalar> ambb { " << get_amb()[2] << " }\n";
+  }
+
+  if (has_emit()) {
+    indent(out, indent_level + 2)
+      << "<Scalar> emitr { " << get_emit()[0] << " }\n";
+    indent(out, indent_level + 2)
+      << "<Scalar> emitg { " << get_emit()[1] << " }\n";
+    indent(out, indent_level + 2)
+      << "<Scalar> emitb { " << get_emit()[2] << " }\n";
+  }
+
+  if (has_spec()) {
+    indent(out, indent_level + 2)
+      << "<Scalar> specr { " << get_spec()[0] << " }\n";
+    indent(out, indent_level + 2)
+      << "<Scalar> specg { " << get_spec()[1] << " }\n";
+    indent(out, indent_level + 2)
+      << "<Scalar> specb { " << get_spec()[2] << " }\n";
+  }
+
+  if (has_shininess()) {
+    indent(out, indent_level + 2)
+      << "<Scalar> shininess { " << get_shininess() << " }\n";
+  }
+
+  if (has_local()) {
+    indent(out, indent_level + 2)
+      << "<Scalar> local { " << get_local() << " }\n";
+  }
+
   indent(out, indent_level) << "}\n";
   indent(out, indent_level) << "}\n";
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterial::is_equivalent_to
+//       Access: Public
+//  Description: Returns true if the two materials are equivalent in
+//               all relevant properties (according to eq), false
+//               otherwise.
+//
+//               The Equivalence parameter, eq, should be set to the
+//               bitwise OR of the following properties, according to
+//               what you consider relevant:
+//
+//               EggMaterial::E_attributes:
+//                 All material attributes (diff, spec,
+//                 etc.) except MRef name.
+//
+//               EggMaterial::E_mref_name:
+//                 The MRef name.
+////////////////////////////////////////////////////////////////////
+bool EggMaterial::
+is_equivalent_to(const EggMaterial &other, int eq) const {
+  if (eq & E_attributes) {
+    if (_flags != other._flags ||
+	(has_diff() && get_diff() != other.get_diff()) ||
+	(has_amb() && get_amb() != other.get_amb()) ||
+	(has_emit() && get_emit() != other.get_emit()) ||
+	(has_spec() && get_spec() != other.get_spec()) ||
+	(has_shininess() && get_shininess() != other.get_shininess()) ||
+	(has_local() && get_local() != other.get_local())) {
+      return false;
+    }
+  }
+
+  if (eq & E_mref_name) {
+    if (get_name() != other.get_name()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterial::sorts_less_than
+//       Access: Public
+//  Description: An ordering operator to compare two materials for
+//               sorting order.  This imposes an arbitrary ordering
+//               useful to identify unique materials, according to the
+//               indicated Equivalence factor.  See
+//               is_equivalent_to().
+////////////////////////////////////////////////////////////////////
+bool EggMaterial::
+sorts_less_than(const EggMaterial &other, int eq) const {
+  if (eq & E_attributes) {
+    if (_flags != other._flags) {
+      return _flags < (int)other._flags;
+    }
+    if (has_diff() && get_diff() != other.get_diff()) {
+      return get_diff().compare_to(other.get_diff()) < 0;
+    }
+    if (has_amb() && get_amb() != other.get_amb()) {
+      return get_amb().compare_to(other.get_amb()) < 0;
+    }
+    if (has_emit() && get_emit() != other.get_emit()) {
+      return get_emit().compare_to(other.get_emit()) < 0;
+    }
+    if (has_spec() && get_spec() != other.get_spec()) {
+      return get_spec().compare_to(other.get_spec()) < 0;
+    }
+    if (has_shininess() && get_shininess() != other.get_shininess()) {
+      return get_shininess() < other.get_shininess() ? -1 : 1;
+    }
+    if (has_local() && get_local() != other.get_local()) {
+      return get_local() - other.get_local();
+    }
+  }
+
+  if (eq & E_mref_name) {
+    if (get_name() != other.get_name()) {
+      return get_name() < other.get_name();
+    }
+  }
+
+  return false;
+}

+ 41 - 4
panda/src/egg/eggMaterial.h

@@ -23,6 +23,14 @@ public:
 
 
   virtual void write(ostream &out, int indent_level) const;
   virtual void write(ostream &out, int indent_level) const;
 
 
+  enum Equivalence {
+    E_attributes           = 0x001,
+    E_mref_name            = 0x002,
+  };
+
+  bool is_equivalent_to(const EggMaterial &other, int eq) const;
+  bool sorts_less_than(const EggMaterial &other, int eq) const;
+
   INLINE void set_diff(const RGBColorf &diff);
   INLINE void set_diff(const RGBColorf &diff);
   INLINE void clear_diff();
   INLINE void clear_diff();
   INLINE bool has_diff() const;
   INLINE bool has_diff() const;
@@ -43,18 +51,32 @@ public:
   INLINE bool has_spec() const;
   INLINE bool has_spec() const;
   INLINE RGBColorf get_spec() const;
   INLINE RGBColorf get_spec() const;
 
 
+  INLINE void set_shininess(double shininess);
+  INLINE void clear_shininess();
+  INLINE bool has_shininess() const;
+  INLINE double get_shininess() const;
+
+  INLINE void set_local(bool local);
+  INLINE void clear_local();
+  INLINE bool has_local() const;
+  INLINE bool get_local() const;
+
 private:
 private:
   enum Flags {
   enum Flags {
-    F_diff    = 0x001,
-    F_amb     = 0x002,
-    F_emit    = 0x004,
-    F_spec    = 0x008
+    F_diff      = 0x001,
+    F_amb       = 0x002,
+    F_emit      = 0x004,
+    F_spec      = 0x008,
+    F_shininess = 0x010,
+    F_local     = 0x020
   };
   };
 
 
   RGBColorf _diff;
   RGBColorf _diff;
   RGBColorf _amb;
   RGBColorf _amb;
   RGBColorf _emit;
   RGBColorf _emit;
   RGBColorf _spec;
   RGBColorf _spec;
+  double _shininess;
+  bool _local;
   int _flags;
   int _flags;
 
 
 
 
@@ -76,6 +98,21 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 };
 };
 
 
+///////////////////////////////////////////////////////////////////
+//       Class : UniqueEggMaterials
+// Description : An STL function object for sorting materials into
+//               order by properties.  Returns true if the two
+//               referenced EggMaterial pointers are in sorted order,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG UniqueEggMaterials {
+public:
+  INLINE UniqueEggMaterials(int eq = ~0);
+  INLINE bool operator ()(const EggMaterial *t1, const EggMaterial *t2) const;
+
+  int _eq;
+};
+
 #include "eggMaterial.I"
 #include "eggMaterial.I"
 
 
 #endif
 #endif

+ 27 - 0
panda/src/egg/eggMaterialCollection.I

@@ -0,0 +1,27 @@
+// Filename: eggMaterialCollection.I
+// Created by:  drose (30Apr01)
+// 
+////////////////////////////////////////////////////////////////////
+
+INLINE EggMaterialCollection::iterator EggMaterialCollection::
+begin() const {
+  nassertr(_ordered_materials.size() == _materials.size(),
+	   _ordered_materials.begin());
+  return _ordered_materials.begin();
+}
+ 
+INLINE EggMaterialCollection::iterator EggMaterialCollection::
+end() const {
+  return _ordered_materials.end();
+}
+ 
+INLINE bool EggMaterialCollection::
+empty() const {
+  return _ordered_materials.empty();
+}
+ 
+INLINE EggMaterialCollection::size_type EggMaterialCollection::
+size() const {
+  nassertr(_ordered_materials.size() == _materials.size(), 0);
+  return _ordered_materials.size();
+}

+ 440 - 0
panda/src/egg/eggMaterialCollection.cxx

@@ -0,0 +1,440 @@
+// Filename: eggMaterialCollection.cxx
+// Created by:  drose (30Apr01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "eggMaterialCollection.h"
+#include "eggGroupNode.h"
+#include "eggPrimitive.h"
+#include "eggMaterial.h"
+
+#include <nameUniquifier.h>
+
+#include <algorithm>
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggMaterialCollection::
+EggMaterialCollection() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggMaterialCollection::
+EggMaterialCollection(const EggMaterialCollection &copy) :
+  _materials(copy._materials),
+  _ordered_materials(copy._ordered_materials)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggMaterialCollection &EggMaterialCollection::
+operator = (const EggMaterialCollection &copy) {
+  _materials = copy._materials;
+  _ordered_materials = copy._ordered_materials;
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::clear
+//       Access: Public
+//  Description: Removes all materials from the collection.
+////////////////////////////////////////////////////////////////////
+void EggMaterialCollection::
+clear() {
+  _materials.clear();
+  _ordered_materials.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::extract_materials
+//       Access: Public
+//  Description: Walks the egg hierarchy beginning at the indicated
+//               node, and removes any EggMaterials encountered in the
+//               hierarchy, adding them to the collection.  Returns
+//               the number of EggMaterials encountered.
+////////////////////////////////////////////////////////////////////
+int EggMaterialCollection::
+extract_materials(EggGroupNode *node) {
+  // Since this traversal is destructive, we'll handle it within the
+  // EggGroupNode code.
+  return node->find_materials(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::insert_materials
+//       Access: Public
+//  Description: Adds a series of EggMaterial nodes to the beginning of
+//               the indicated node to reflect each of the materials in
+//               the collection.  Returns the number of material nodes
+//               added.
+////////////////////////////////////////////////////////////////////
+int EggMaterialCollection::
+insert_materials(EggGroupNode *node) {
+  return insert_materials(node, node->begin());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::insert_materials
+//       Access: Public
+//  Description: Adds a series of EggMaterial nodes to the beginning of
+//               the indicated node to reflect each of the materials in
+//               the collection.  Returns the number of material nodes
+//               added.
+////////////////////////////////////////////////////////////////////
+int EggMaterialCollection::
+insert_materials(EggGroupNode *node, EggGroupNode::iterator position) {
+  OrderedMaterials::iterator oti;
+  for (oti = _ordered_materials.begin(); 
+       oti != _ordered_materials.end(); 
+       ++oti) {
+    node->insert(position, (*oti).p());
+  }
+
+  return size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::find_used_materials
+//       Access: Public
+//  Description: Walks the egg hierarchy beginning at the indicated
+//               node, looking for materials that are referenced by
+//               primitives but are not already members of the
+//               collection, adding them to the collection.
+//
+//               If this is called following extract_materials(), it
+//               can be used to pick up any additional material
+//               references that appeared in the egg hierarchy (but
+//               whose EggMaterial node was not actually part of the
+//               hierarchy).
+//
+//               If this is called in lieu of extract_materials(), it
+//               will fill up the collection with all of the
+//               referenced materials (and only the referenced
+//               materials), without destructively removing the
+//               EggMaterials from the hierarchy.
+//
+//               This also has the side effect of incrementing the
+//               internal usage count for a material in the collection
+//               each time a material reference is encountered.  This
+//               side effect is taken advantage of by
+//               remove_unused_materials().
+////////////////////////////////////////////////////////////////////
+int EggMaterialCollection::
+find_used_materials(EggNode *node) {
+  int num_found = 0;
+
+  if (node->is_of_type(EggPrimitive::get_class_type())) {
+    EggPrimitive *primitive = DCAST(EggPrimitive, node);
+    if (primitive->has_material()) {
+      EggMaterial *tex = primitive->get_material();
+      Materials::iterator ti = _materials.find(tex);
+      if (ti == _materials.end()) {
+	// Here's a new material!
+	num_found++;
+	_materials.insert(Materials::value_type(tex, 1));
+	_ordered_materials.push_back(tex);
+      } else {
+	// Here's a material we'd already known about.  Increment its
+	// usage count.
+	(*ti).second++;
+      }
+    }
+
+  } else if (node->is_of_type(EggGroupNode::get_class_type())) {
+    EggGroupNode *group = DCAST(EggGroupNode, node);
+    
+    EggGroupNode::iterator ci;
+    for (ci = group->begin(); ci != group->end(); ++ci) {
+      EggNode *child = *ci;
+
+      num_found += find_used_materials(child);
+    }
+  }
+
+  return num_found;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::remove_unused_materials
+//       Access: Public
+//  Description: Removes any materials from the collection that aren't
+//               referenced by any primitives in the indicated egg
+//               hierarchy.  This also, incidentally, adds materials to
+//               the collection that had been referenced by primitives
+//               but had not previously appeared in the collection.
+////////////////////////////////////////////////////////////////////
+void EggMaterialCollection::
+remove_unused_materials(EggNode *node) {
+  // We'll do this the easy way: First, we'll remove *all* the
+  // materials from the collection, and then we'll add back only those
+  // that appear in the hierarchy.
+  clear();
+  find_used_materials(node);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::collapse_equivalent_materials
+//       Access: Public
+//  Description: Walks through the collection and collapses together
+//               any separate materials that are equivalent according
+//               to the indicated equivalence factor, eq (see
+//               EggMaterial::is_equivalent_to()).  The return value is
+//               the number of materials removed.
+//
+//               This flavor of collapse_equivalent_materials()
+//               automatically adjusts all the primitives in the egg
+//               hierarchy to refer to the new material pointers.
+////////////////////////////////////////////////////////////////////
+int EggMaterialCollection::
+collapse_equivalent_materials(int eq, EggGroupNode *node) {
+  MaterialReplacement removed;
+  int num_collapsed = collapse_equivalent_materials(eq, removed);
+
+  // And now walk the egg hierarchy and replace any references to a
+  // removed material with its replacement.
+  replace_materials(node, removed);
+
+  return num_collapsed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::collapse_equivalent_materials
+//       Access: Public
+//  Description: Walks through the collection and collapses together
+//               any separate materials that are equivalent according
+//               to the indicated equivalence factor, eq (see
+//               EggMaterial::is_equivalent_to()).  The return value is
+//               the number of materials removed.
+//
+//               This flavor of collapse_equivalent_materials() does
+//               not adjust any primitives in the egg hierarchy;
+//               instead, it fills up the 'removed' map with an entry
+//               for each removed material, mapping it back to the
+//               equivalent retained material.  It's up to the user to
+//               then call replace_materials() with this map, if
+//               desired, to apply these changes to the egg hierarchy.
+////////////////////////////////////////////////////////////////////
+int EggMaterialCollection::
+collapse_equivalent_materials(int eq, EggMaterialCollection::MaterialReplacement &removed) {
+  int num_collapsed = 0;
+
+  typedef set<PT(EggMaterial), UniqueEggMaterials> Collapser;
+  UniqueEggMaterials uet(eq);
+  Collapser collapser(uet);
+
+  // First, put all of the materials into the Collapser structure, to
+  // find out the unique materials.
+  OrderedMaterials::const_iterator oti;
+  for (oti = _ordered_materials.begin(); 
+       oti != _ordered_materials.end(); 
+       ++oti) {
+    EggMaterial *tex = (*oti);
+
+    pair<Collapser::const_iterator, bool> result = collapser.insert(tex);
+    if (!result.second) {
+      // This material is non-unique; another one was already there.
+      EggMaterial *first = *(result.first);
+      removed.insert(MaterialReplacement::value_type(tex, first));
+      num_collapsed++;
+    }
+  }
+
+  // Now record all of the unique materials only.
+  clear();
+  Collapser::const_iterator ci;
+  for (ci = collapser.begin(); ci != collapser.end(); ++ci) {
+    add_material(*ci);
+  }
+
+  return num_collapsed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::replace_materials
+//       Access: Public, Static
+//  Description: Walks the egg hierarchy, changing out any reference
+//               to a material appearing on the left side of the map
+//               with its corresponding material on the right side.
+//               This is most often done following a call to
+//               collapse_equivalent_materials().  It does not directly
+//               affect the Collection.
+////////////////////////////////////////////////////////////////////
+void EggMaterialCollection::
+replace_materials(EggGroupNode *node,
+		 const EggMaterialCollection::MaterialReplacement &replace) {
+  EggGroupNode::iterator ci;
+  for (ci = node->begin();
+       ci != node->end();
+       ++ci) {
+    EggNode *child = *ci;
+    if (child->is_of_type(EggPrimitive::get_class_type())) {
+      EggPrimitive *primitive = DCAST(EggPrimitive, child);
+      if (primitive->has_material()) {
+	PT(EggMaterial) tex = primitive->get_material();
+	MaterialReplacement::const_iterator ri;
+	ri = replace.find(tex);
+	if (ri != replace.end()) {
+	  // Here's a material we want to replace.
+	  primitive->set_material((*ri).second);
+	}
+      }
+
+    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
+      EggGroupNode *group_child = DCAST(EggGroupNode, child);
+      replace_materials(group_child, replace);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::uniquify_mrefs
+//       Access: Public
+//  Description: Guarantees that each material in the collection has a
+//               unique MRef name.  This is essential before writing
+//               an egg file.
+////////////////////////////////////////////////////////////////////
+void EggMaterialCollection::
+uniquify_mrefs() {
+  NameUniquifier nu(".mref", "mref");
+
+  OrderedMaterials::const_iterator oti;
+  for (oti = _ordered_materials.begin(); 
+       oti != _ordered_materials.end(); 
+       ++oti) {
+    EggMaterial *tex = (*oti);
+
+    tex->set_name(nu.add_name(tex->get_name()));
+  }
+}
+ 
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::sort_by_mref
+//       Access: Public
+//  Description: Sorts all the materials into alphabetical order by
+//               MRef name.  Subsequent operations using begin()/end()
+//               will traverse in this sorted order.
+////////////////////////////////////////////////////////////////////
+void EggMaterialCollection::
+sort_by_mref() {
+  sort(_ordered_materials.begin(), _ordered_materials.end(),
+       NamableOrderByName());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::add_material
+//       Access: Public
+//  Description: Explicitly adds a new material to the collection.
+//               Returns true if the material was added, false if it
+//               was already there or if there was some error.
+////////////////////////////////////////////////////////////////////
+bool EggMaterialCollection::
+add_material(EggMaterial *material) {
+  nassertr(_materials.size() == _ordered_materials.size(), false);
+
+  PT(EggMaterial) new_tex = material;
+
+  Materials::const_iterator ti;
+  ti = _materials.find(new_tex);
+  if (ti != _materials.end()) {
+    // This material is already a member of the collection.
+    return false;
+  }
+
+  _materials.insert(Materials::value_type(new_tex, 0));
+  _ordered_materials.push_back(new_tex);
+
+  nassertr(_materials.size() == _ordered_materials.size(), false);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::remove_material
+//       Access: Public
+//  Description: Explicitly removes a material from the collection.
+//               Returns true if the material was removed, false if it
+//               wasn't there or if there was some error.
+////////////////////////////////////////////////////////////////////
+bool EggMaterialCollection::
+remove_material(EggMaterial *material) {
+  nassertr(_materials.size() == _ordered_materials.size(), false);
+
+  Materials::iterator ti;
+  ti = _materials.find(material);
+  if (ti == _materials.end()) {
+    // This material is not a member of the collection.
+    return false;
+  }
+
+  _materials.erase(ti);
+
+  OrderedMaterials::iterator oti;
+  PT(EggMaterial) ptex = material;
+  oti = find(_ordered_materials.begin(), _ordered_materials.end(), ptex);
+  nassertr(oti != _ordered_materials.end(), false);
+
+  _ordered_materials.erase(oti);
+
+  nassertr(_materials.size() == _ordered_materials.size(), false);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::create_unique_material
+//       Access: Public
+//  Description: Creates a new material if there is not already one
+//               equivalent (according to eq, see
+//               EggMaterial::is_equivalent_to()) to the indicated
+//               material, or returns the existing one if there is.
+////////////////////////////////////////////////////////////////////
+EggMaterial *EggMaterialCollection::
+create_unique_material(const EggMaterial &copy, int eq) {
+  // This requires a complete linear traversal, not terribly
+  // efficient.
+  OrderedMaterials::const_iterator oti;
+  for (oti = _ordered_materials.begin(); 
+       oti != _ordered_materials.end(); 
+       ++oti) {
+    EggMaterial *tex = (*oti);
+    if (copy.is_equivalent_to(*tex, eq)) {
+      return tex;
+    }
+  }
+
+  EggMaterial *new_material = new EggMaterial(copy);
+  add_material(new_material);
+  return new_material;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggMaterialCollection::find_mref
+//       Access: Public
+//  Description: Returns the material with the indicated MRef name, or
+//               NULL if no material matches.
+////////////////////////////////////////////////////////////////////
+EggMaterial *EggMaterialCollection::
+find_mref(const string &mref_name) const {
+  // This requires a complete linear traversal, not terribly
+  // efficient.
+  OrderedMaterials::const_iterator oti;
+  for (oti = _ordered_materials.begin(); 
+       oti != _ordered_materials.end(); 
+       ++oti) {
+    EggMaterial *tex = (*oti);
+    if (tex->get_name() == mref_name) {
+      return tex;
+    }
+  }
+
+  return (EggMaterial *)NULL;
+}

+ 92 - 0
panda/src/egg/eggMaterialCollection.h

@@ -0,0 +1,92 @@
+// Filename: eggMaterialCollection.h
+// Created by:  drose (30Apr01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGMATERIALCOLLECTION_H
+#define EGGMATERIALCOLLECTION_H
+
+#include <pandabase.h>
+
+#include "eggMaterial.h"
+#include "eggGroupNode.h"
+
+#include <string>
+#include <map>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : EggMaterialCollection
+// Description : This is a collection of materials by MRef name.  It
+//               can extract the materials from an egg file and sort
+//               them all together; it can also manage the creation of
+//               unique materials and the assignment of unique MRef
+//               names.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEGG EggMaterialCollection {
+
+  // This is a bit of private interface stuff that must be here as a
+  // forward reference.  This allows us to define the
+  // EggMaterialCollection as an STL container.
+
+private:
+  typedef map<PT(EggMaterial), int> Materials;
+  typedef vector< PT(EggMaterial) > OrderedMaterials;
+
+public:
+  typedef OrderedMaterials::const_iterator iterator;
+  typedef iterator const_iterator;
+  typedef OrderedMaterials::size_type size_type;
+
+  typedef map<PT(EggMaterial),  PT(EggMaterial) > MaterialReplacement;
+
+  // Here begins the actual public interface to EggMaterialCollection.
+
+public:
+  EggMaterialCollection();
+  EggMaterialCollection(const EggMaterialCollection &copy);
+  EggMaterialCollection &operator = (const EggMaterialCollection &copy);
+
+  void clear();
+
+  int extract_materials(EggGroupNode *node);
+  int insert_materials(EggGroupNode *node);
+  int insert_materials(EggGroupNode *node, EggGroupNode::iterator position);
+
+  int find_used_materials(EggNode *node);
+  void remove_unused_materials(EggNode *node);
+
+  int collapse_equivalent_materials(int eq, EggGroupNode *node);
+  int collapse_equivalent_materials(int eq, MaterialReplacement &removed);
+  static void replace_materials(EggGroupNode *node,
+			       const MaterialReplacement &replace);
+
+  void uniquify_mrefs();
+  void sort_by_mref();
+
+  // Can be used to traverse all the materials in the collection, in
+  // order as last sorted.
+  INLINE iterator begin() const;
+  INLINE iterator end() const;
+  INLINE bool empty() const;
+  INLINE size_type size() const;
+
+  bool add_material(EggMaterial *material);
+  bool remove_material(EggMaterial *material);
+
+  // create_unique_material() creates a new material if there is not
+  // already one equivalent (according to eq, see
+  // EggMaterial::is_equivalent_to()) to the indicated material, or
+  // returns the existing one if there is.
+  EggMaterial *create_unique_material(const EggMaterial &copy, int eq);
+
+  // Find a material with a particular MRef name.
+  EggMaterial *find_mref(const string &mref_name) const;
+
+private:
+  Materials _materials;
+  OrderedMaterials _ordered_materials;
+};
+
+#include "eggMaterialCollection.I"
+
+#endif

+ 15 - 15
panda/src/egg/eggTexture.cxx

@@ -196,17 +196,17 @@ is_equivalent_to(const EggTexture &other, int eq) const {
 
 
     if (eq & E_basename) {
     if (eq & E_basename) {
       if (a.get_basename_wo_extension() != b.get_basename_wo_extension()) {
       if (a.get_basename_wo_extension() != b.get_basename_wo_extension()) {
-    return false;
+	return false;
       }
       }
     }
     }
     if (eq & E_extension) {
     if (eq & E_extension) {
       if (a.get_extension() != b.get_extension()) {
       if (a.get_extension() != b.get_extension()) {
-    return false;
+	return false;
       }
       }
     }
     }
     if (eq & E_dirname) {
     if (eq & E_dirname) {
       if (a.get_dirname() != b.get_dirname()) {
       if (a.get_dirname() != b.get_dirname()) {
-    return false;
+	return false;
       }
       }
     }
     }
   }
   }
@@ -218,21 +218,21 @@ is_equivalent_to(const EggTexture &other, int eq) const {
     
     
     if (has_transform() && other.has_transform()) {
     if (has_transform() && other.has_transform()) {
       if (!_transform.almost_equal(other._transform, 0.0001)) {
       if (!_transform.almost_equal(other._transform, 0.0001)) {
-    return false;
+	return false;
       }
       }
     }
     }
   }
   }
 
 
   if (eq & E_attributes) {
   if (eq & E_attributes) {
     if (_format != other._format ||
     if (_format != other._format ||
-    _wrap_mode != other._wrap_mode ||
-    _wrap_u != other._wrap_u ||
-    _wrap_v != other._wrap_v ||
-    _minfilter != other._minfilter ||
-    _magfilter != other._magfilter ||
-    _magfilteralpha != other._magfilteralpha ||
-    _magfiltercolor != other._magfiltercolor ||
-    _env_type != other._env_type) {
+	_wrap_mode != other._wrap_mode ||
+	_wrap_u != other._wrap_u ||
+	_wrap_v != other._wrap_v ||
+	_minfilter != other._minfilter ||
+	_magfilter != other._magfilter ||
+	_magfilteralpha != other._magfilteralpha ||
+	_magfiltercolor != other._magfiltercolor ||
+	_env_type != other._env_type) {
       return false;
       return false;
     }
     }
     if (EggRenderMode::operator != (other)) {
     if (EggRenderMode::operator != (other)) {
@@ -270,17 +270,17 @@ sorts_less_than(const EggTexture &other, int eq) const {
 
 
     if (eq & E_basename) {
     if (eq & E_basename) {
       if (a.get_basename_wo_extension() != b.get_basename_wo_extension()) {
       if (a.get_basename_wo_extension() != b.get_basename_wo_extension()) {
-    return a.get_basename_wo_extension() < b.get_basename_wo_extension();
+	return a.get_basename_wo_extension() < b.get_basename_wo_extension();
       }
       }
     }
     }
     if (eq & E_extension) {
     if (eq & E_extension) {
       if (a.get_extension() != b.get_extension()) {
       if (a.get_extension() != b.get_extension()) {
-    return a.get_extension() < b.get_extension();
+	return a.get_extension() < b.get_extension();
       }
       }
     }
     }
     if (eq & E_dirname) {
     if (eq & E_dirname) {
       if (a.get_dirname() != b.get_dirname()) {
       if (a.get_dirname() != b.get_dirname()) {
-    return a.get_dirname() < b.get_dirname();
+	return a.get_dirname() < b.get_dirname();
       }
       }
     }
     }
   }
   }

+ 1 - 11
panda/src/egg/eggTexture.h

@@ -165,7 +165,7 @@ private:
 };
 };
 
 
 ///////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////
-//   Class : UniqueEggTextures
+//       Class : UniqueEggTextures
 // Description : An STL function object for sorting textures into
 // Description : An STL function object for sorting textures into
 //               order by properties.  Returns true if the two
 //               order by properties.  Returns true if the two
 //               referenced EggTexture pointers are in sorted order,
 //               referenced EggTexture pointers are in sorted order,
@@ -179,16 +179,6 @@ public:
   int _eq;
   int _eq;
 };
 };
 
 
-///////////////////////////////////////////////////////////////////
-//   Class : TRefEggTextures
-// Description : An STL function object for sorting textures into
-//               order by TRef name.
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDAEGG TRefEggTextures {
-public:
-  INLINE bool operator ()(const EggTexture *t1, const EggTexture *t2) const;
-};
-
 INLINE ostream &operator << (ostream &out, const EggTexture &n) {
 INLINE ostream &operator << (ostream &out, const EggTexture &n) {
   return out << n.get_filename();
   return out << n.get_filename();
 }
 }

+ 6 - 0
panda/src/egg/parser.yxx

@@ -516,6 +516,12 @@ material_body:
     spec[2] = value;
     spec[2] = value;
     material->set_spec(spec);
     material->set_spec(spec);
 
 
+  } else if (cmp_nocase_uh(name, "shininess") == 0) {
+    material->set_shininess(value);
+
+  } else if (cmp_nocase_uh(name, "local") == 0) {
+    material->set_local(value != 0.0);
+
   } else {
   } else {
     eggyywarning("Unsupported material scalar: " + name);
     eggyywarning("Unsupported material scalar: " + name);
   }
   }

+ 69 - 0
panda/src/egg2sg/eggLoader.cxx

@@ -37,6 +37,9 @@
 #include <builderVertex.h>
 #include <builderVertex.h>
 #include <texturePool.h>
 #include <texturePool.h>
 #include <textureTransition.h>
 #include <textureTransition.h>
+#include <material.h>
+#include <materialPool.h>
+#include <materialTransition.h>
 #include <transformTransition.h>
 #include <transformTransition.h>
 #include <transparencyTransition.h>
 #include <transparencyTransition.h>
 #include <cullFaceTransition.h>
 #include <cullFaceTransition.h>
@@ -862,6 +865,65 @@ apply_texture_apply_attributes(TextureApplyTransition *apply,
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::get_material_transition
+//       Access: Private
+//  Description: Returns a transition suitable for enabling the
+//               material indicated by the given EggMaterial, and with
+//               the indicated backface flag.
+////////////////////////////////////////////////////////////////////
+MaterialTransition *EggLoader::
+get_material_transition(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()) {
+    RGBColorf diff = egg_mat->get_diff();
+    mat->set_diffuse(Colorf(diff[0], diff[1], diff[2], 1.0));
+    // By default, ambient is the same as diffuse, if diffuse is
+    // specified but ambient is not.
+    mat->set_ambient(Colorf(diff[0], diff[1], diff[2], 1.0));
+  }
+  if (egg_mat->has_amb()) {
+    RGBColorf amb = egg_mat->get_amb();
+    mat->set_ambient(Colorf(amb[0], amb[1], amb[2], 1.0));
+  }
+  if (egg_mat->has_emit()) {
+    RGBColorf emit = egg_mat->get_emit();
+    mat->set_emission(Colorf(emit[0], emit[1], emit[2], 1.0));
+  }
+  if (egg_mat->has_spec()) {
+    RGBColorf spec = egg_mat->get_spec();
+    mat->set_specular(Colorf(spec[0], spec[1], spec[2], 1.0));
+  }
+  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 MaterialTransition for this Material.
+  PT(MaterialTransition) mt = new MaterialTransition(shared_mat);
+  materials.insert(Materials::value_type(egg_mat, mt));
+
+  return mt;
+}
+
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::setup_bucket
 //     Function: EggLoader::setup_bucket
@@ -957,6 +1019,13 @@ setup_bucket(BuilderBucket &bucket, NamedNode *parent,
     }
     }
   }
   }
 
 
+  if (egg_prim->has_material()) {
+    MaterialTransition *mt = get_material_transition(egg_prim->get_material(),
+						     egg_prim->get_bface_flag());
+    bucket._trans.set_transition(mt);
+  }
+    
+
   // Also check the color of the primitive to see if we should assume
   // Also check the color of the primitive to see if we should assume
   // alpha based on the alpha values specified in the egg file.
   // alpha based on the alpha values specified in the egg file.
   if (am == EggRenderMode::AM_unspecified) {
   if (am == EggRenderMode::AM_unspecified) {

+ 10 - 0
panda/src/egg2sg/eggLoader.h

@@ -13,6 +13,7 @@
 #include <eggData.h>
 #include <eggData.h>
 #include <eggTexture.h>
 #include <eggTexture.h>
 #include <eggGroup.h>
 #include <eggGroup.h>
+#include <eggMaterial.h>
 #include <texture.h>
 #include <texture.h>
 #include <namedNode.h>
 #include <namedNode.h>
 #include <pt_NamedNode.h>
 #include <pt_NamedNode.h>
@@ -22,6 +23,7 @@
 #include <indirectCompareTo.h>
 #include <indirectCompareTo.h>
 #include <textureTransition.h>
 #include <textureTransition.h>
 #include <textureApplyTransition.h>
 #include <textureApplyTransition.h>
+#include <materialTransition.h>
 
 
 class EggNode;
 class EggNode;
 class EggBin;
 class EggBin;
@@ -29,6 +31,7 @@ class EggTable;
 class EggNurbsCurve;
 class EggNurbsCurve;
 class EggPrimitive;
 class EggPrimitive;
 class EggPolygon;
 class EggPolygon;
+class EggMaterial;
 class ComputedVerticesMaker;
 class ComputedVerticesMaker;
 class RenderRelation;
 class RenderRelation;
 class CollisionSolid;
 class CollisionSolid;
@@ -73,6 +76,9 @@ private:
   void apply_texture_apply_attributes(TextureApplyTransition *apply, 
   void apply_texture_apply_attributes(TextureApplyTransition *apply, 
 				      const EggTexture *egg_tex);
 				      const EggTexture *egg_tex);
 
 
+  MaterialTransition *get_material_transition(const EggMaterial *egg_mat, 
+					      bool bface);
+
   void setup_bucket(BuilderBucket &bucket, NamedNode *parent,
   void setup_bucket(BuilderBucket &bucket, NamedNode *parent,
 		    EggPrimitive *egg_prim);
 		    EggPrimitive *egg_prim);
 
 
@@ -116,6 +122,10 @@ private:
   typedef map<PT(EggTexture), TextureDef> Textures;
   typedef map<PT(EggTexture), TextureDef> Textures;
   Textures _textures;
   Textures _textures;
 
 
+  typedef map<CPT(EggMaterial), PT(MaterialTransition) > Materials;
+  Materials _materials;
+  Materials _materials_bface;
+
   typedef set<RenderRelation *> Decals;
   typedef set<RenderRelation *> Decals;
   Decals _decals;
   Decals _decals;
 
 

+ 17 - 0
panda/src/glgsg/glGraphicsStateGuardian.I

@@ -1159,3 +1159,20 @@ INLINE const float &GLGraphicsStateGuardian::
 get_current_alpha_scale() const {
 get_current_alpha_scale() const {
   return _current_alpha_scale;
   return _current_alpha_scale;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::report_errors
+//       Access: Protected
+//  Description: Checks for any outstanding error codes and outputs
+//               them, if found.  If NDEBUG is defined, this function
+//               does nothing.
+////////////////////////////////////////////////////////////////////
+INLINE void GLGraphicsStateGuardian::
+report_errors() const {
+#ifndef NDEBUG
+  GLenum error_code = glGetError();
+  if (error_code != GL_NO_ERROR) {
+    report_errors_loop(error_code);
+  }
+#endif
+}

+ 84 - 15
panda/src/glgsg/glGraphicsStateGuardian.cxx

@@ -466,6 +466,7 @@ render_frame(const AllAttributesWrapper &initial_state) {
 #endif
 #endif
 
 
   _win->begin_frame();
   _win->begin_frame();
+  report_errors();
   _decal_level = 0;
   _decal_level = 0;
 
 
   if (_clear_buffer_type != 0) {
   if (_clear_buffer_type != 0) {
@@ -539,6 +540,7 @@ render_frame(const AllAttributesWrapper &initial_state) {
   // more aware of whether their parameters or positions have changed
   // more aware of whether their parameters or positions have changed
   // at all?
   // at all?
 
 
+  report_errors();
   _win->end_frame();
   _win->end_frame();
 
 
 #ifdef GSG_VERBOSE
 #ifdef GSG_VERBOSE
@@ -740,6 +742,7 @@ draw_point(const GeomPoint *geom) {
   }
   }
 
 
   glEnd();
   glEnd();
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -799,6 +802,7 @@ draw_line(const GeomLine* geom) {
   }
   }
 
 
   glEnd();
   glEnd();
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -876,6 +880,7 @@ draw_linestrip(const GeomLinestrip* geom) {
     }
     }
     glEnd();
     glEnd();
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1181,6 +1186,7 @@ draw_sprite(const GeomSprite *geom) {
     glMatrixMode(GL_PROJECTION);
     glMatrixMode(GL_PROJECTION);
     glLoadMatrixf(_current_projection_mat.get_data());
     glLoadMatrixf(_current_projection_mat.get_data());
   #endif
   #endif
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1252,6 +1258,7 @@ draw_polygon(const GeomPolygon *geom) {
     }
     }
     glEnd();
     glEnd();
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1318,6 +1325,7 @@ draw_tri(const GeomTri *geom) {
   }
   }
 
 
   glEnd();
   glEnd();
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1384,6 +1392,7 @@ draw_quad(const GeomQuad *geom) {
   }
   }
 
 
   glEnd();
   glEnd();
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1472,6 +1481,7 @@ draw_tristrip(const GeomTristrip *geom) {
     }
     }
     glEnd();
     glEnd();
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1560,6 +1570,7 @@ draw_trifan(const GeomTrifan *geom) {
     }
     }
     glEnd();
     glEnd();
   }
   }
+  report_errors();
 }
 }
 
 
 
 
@@ -1639,6 +1650,7 @@ draw_sphere(const GeomSphere *geom) {
   }
   }
 
 
   gluDeleteQuadric(sph);
   gluDeleteQuadric(sph);
+  report_errors();
 }
 }
 
 
 
 
@@ -1669,7 +1681,8 @@ prepare_texture(Texture *tex) {
   // which shouldn't be possible, since the texture itself should
   // which shouldn't be possible, since the texture itself should
   // detect this.
   // detect this.
   nassertr(inserted, NULL);
   nassertr(inserted, NULL);
-
+ 
+  report_errors();
   return gtc;
   return gtc;
 }
 }
 
 
@@ -1689,6 +1702,7 @@ apply_texture(TextureContext *tc) {
     specify_texture(tex);
     specify_texture(tex);
     apply_texture_immediate(tex);
     apply_texture_immediate(tex);
   */
   */
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1715,6 +1729,7 @@ release_texture(TextureContext *tc) {
   tex->clear_gsg(this);
   tex->clear_gsg(this);
 
 
   delete gtc;
   delete gtc;
+  report_errors();
 }
 }
 
 
 static int logs[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
 static int logs[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
@@ -1898,6 +1913,7 @@ texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb) {
   texture_to_pixel_buffer(tc, pb, dr);
   texture_to_pixel_buffer(tc, pb, dr);
 
 
   pop_frame_buffer(old_fb);
   pop_frame_buffer(old_fb);
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1925,6 +1941,7 @@ texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb,
     pb->_image = PTA_uchar(w * h * pb->get_num_components());
     pb->_image = PTA_uchar(w * h * pb->get_num_components());
     copy_pixel_buffer(pb, dr);
     copy_pixel_buffer(pb, dr);
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1994,6 +2011,7 @@ copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr) {
 			   pb->_image.p() );
 			   pb->_image.p() );
 
 
   nassertv(!pb->_image.empty());
   nassertv(!pb->_image.empty());
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2131,6 +2149,7 @@ draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
   glPopMatrix();
   glPopMatrix();
 
 
   pop_display_region(old_dr);
   pop_display_region(old_dr);
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2188,6 +2207,7 @@ void GLGraphicsStateGuardian::apply_material(const Material *material) {
   
   
   call_glLightModelLocal(material->get_local());
   call_glLightModelLocal(material->get_local());
   call_glLightModelTwoSide(material->get_twoside());
   call_glLightModelTwoSide(material->get_twoside());
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2211,6 +2231,7 @@ apply_fog(Fog *fog) {
       break;
       break;
   }
   }
   call_glFogColor(fog->get_color());
   call_glFogColor(fog->get_color());
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2270,6 +2291,7 @@ void GLGraphicsStateGuardian::apply_light( PointLight* light )
   glgsg_cat.debug()
   glgsg_cat.debug()
     << "glPopMatrix()" << endl;
     << "glPopMatrix()" << endl;
 #endif
 #endif
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2327,6 +2349,7 @@ void GLGraphicsStateGuardian::apply_light( DirectionalLight* light )
   glgsg_cat.debug()
   glgsg_cat.debug()
     << "glPopMatrix()" << endl;
     << "glPopMatrix()" << endl;
 #endif
 #endif
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2382,6 +2405,7 @@ void GLGraphicsStateGuardian::apply_light( Spotlight* light )
   glgsg_cat.debug()
   glgsg_cat.debug()
     << "glPopMatrix()" << endl;
     << "glPopMatrix()" << endl;
 #endif
 #endif
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2438,6 +2462,7 @@ issue_transform(const TransformAttribute *attrib) {
     enable_texturing(texturing_was_enabled);
     enable_texturing(texturing_was_enabled);
   }
   }
 #endif
 #endif
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2489,6 +2514,7 @@ issue_tex_matrix(const TexMatrixAttribute *attrib) {
 #endif
 #endif
   glMatrixMode(GL_TEXTURE);
   glMatrixMode(GL_TEXTURE);
   glLoadMatrixf(attrib->get_matrix().get_data());
   glLoadMatrixf(attrib->get_matrix().get_data());
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2503,6 +2529,7 @@ issue_color(const ColorAttribute *attrib) {
     const Colorf c = attrib->get_color();
     const Colorf c = attrib->get_color();
     glColor4f(c[0], c[1], c[2], c[3]);
     glColor4f(c[0], c[1], c[2], c[3]);
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2522,6 +2549,7 @@ issue_texture(const TextureAttribute *attrib) {
   } else {
   } else {
     enable_texturing(false);
     enable_texturing(false);
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2574,6 +2602,7 @@ issue_tex_gen(const TexGenAttribute *attrib) {
       << "Unknown texgen mode " << (int)mode << endl;
       << "Unknown texgen mode " << (int)mode << endl;
     break;
     break;
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2589,6 +2618,7 @@ issue_material(const MaterialAttribute *attrib) {
     nassertv(material != (const Material *)NULL);
     nassertv(material != (const Material *)NULL);
     apply_material(material);
     apply_material(material);
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2608,6 +2638,7 @@ issue_fog(const FogAttribute *attrib) {
   } else {
   } else {
     enable_fog(false);
     enable_fog(false);
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2636,6 +2667,7 @@ issue_render_mode(const RenderModeAttribute *attrib) {
     glgsg_cat.error()
     glgsg_cat.error()
       << "Unknown render mode " << (int)mode << endl;
       << "Unknown render mode " << (int)mode << endl;
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2732,6 +2764,7 @@ void GLGraphicsStateGuardian::issue_light(const LightAttribute *attrib )
   } else {
   } else {
     call_glLightModelAmbient(_cur_ambient_light);
     call_glLightModelAmbient(_cur_ambient_light);
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2769,6 +2802,7 @@ issue_color_blend(const ColorBlendAttribute *attrib) {
       << "Unknown color blend mode " << (int)mode << endl;
       << "Unknown color blend mode " << (int)mode << endl;
     break;
     break;
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2781,6 +2815,7 @@ issue_texture_apply(const TextureApplyAttribute *attrib) {
   //  activate();
   //  activate();
   GLint glmode = get_texture_apply_mode_type(attrib->get_mode());
   GLint glmode = get_texture_apply_mode_type(attrib->get_mode());
   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, glmode);
   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, glmode);
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2792,9 +2827,10 @@ void GLGraphicsStateGuardian::
 issue_color_mask(const ColorMaskAttribute *attrib) {
 issue_color_mask(const ColorMaskAttribute *attrib) {
   //  activate();
   //  activate();
   glColorMask(attrib->is_write_r(),
   glColorMask(attrib->is_write_r(),
-          attrib->is_write_g(),
-          attrib->is_write_b(),
-          attrib->is_write_a());
+	      attrib->is_write_g(),
+	      attrib->is_write_b(),
+	      attrib->is_write_a());
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2813,6 +2849,7 @@ issue_depth_test(const DepthTestAttribute *attrib) {
     enable_depth_test(true);
     enable_depth_test(true);
     glDepthFunc(get_depth_func_type(mode));
     glDepthFunc(get_depth_func_type(mode));
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2825,6 +2862,7 @@ issue_depth_write(const DepthWriteAttribute *attrib) {
   //  activate();
   //  activate();
   
   
   call_glDepthMask(attrib->is_on());
   call_glDepthMask(attrib->is_on());
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2845,6 +2883,7 @@ issue_stencil(const StencilAttribute *attrib) {
     call_glStencilFunc(get_stencil_func_type(mode));
     call_glStencilFunc(get_stencil_func_type(mode));
     call_glStencilOp(get_stencil_action_type(attrib->get_action()));
     call_glStencilOp(get_stencil_action_type(attrib->get_action()));
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2879,6 +2918,7 @@ issue_cull_face(const CullFaceAttribute *attrib) {
       << "invalid cull face mode " << (int)mode << endl;
       << "invalid cull face mode " << (int)mode << endl;
     break;
     break;
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2967,6 +3007,7 @@ issue_clip_plane(const ClipPlaneAttribute *attrib)
     if (_cur_clip_plane_enabled[i] == false)
     if (_cur_clip_plane_enabled[i] == false)
       enable_clip_plane(i, false);
       enable_clip_plane(i, false);
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3027,6 +3068,7 @@ issue_transparency(const TransparencyAttribute *attrib )
       << "invalid transparency mode " << (int)mode << endl;
       << "invalid transparency mode " << (int)mode << endl;
     break;
     break;
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3038,6 +3080,7 @@ void GLGraphicsStateGuardian::
 issue_linesmooth(const LinesmoothAttribute *attrib) {
 issue_linesmooth(const LinesmoothAttribute *attrib) {
   //  activate();
   //  activate();
   enable_line_smooth(attrib->is_on());
   enable_line_smooth(attrib->is_on());
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3053,6 +3096,7 @@ issue_point_shape(const PointShapeAttribute *attrib) {
     glDisable(GL_POINT_SMOOTH);
     glDisable(GL_POINT_SMOOTH);
   else
   else
     glEnable(GL_POINT_SMOOTH);
     glEnable(GL_POINT_SMOOTH);
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3080,6 +3124,7 @@ issue_polygon_offset(const PolygonOffsetAttribute *attrib) {
   {
   {
     enable_polygon_offset(false);
     enable_polygon_offset(false);
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3180,6 +3225,7 @@ begin_decal(GeomNode *base_geom) {
       // buffer write off during this.
       // buffer write off during this.
     }
     }
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3279,6 +3325,7 @@ end_decal(GeomNode *base_geom) {
       enable_texturing(was_textured);
       enable_texturing(was_textured);
     }
     }
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3299,6 +3346,24 @@ compute_distance_to(const LPoint3f &point) const {
   return -point[2];
   return -point[2];
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::report_errors_loop
+//       Access: Protected
+//  Description: The internal implementation of report_errors().
+//               Don't call this function; use report_errors()
+//               instead.
+////////////////////////////////////////////////////////////////////
+void GLGraphicsStateGuardian::
+report_errors_loop(GLenum error_code) const {
+#ifndef NDEBUG
+  while (error_code != GL_NO_ERROR) {
+    glgsg_cat.error()
+      << gluErrorString(error_code) << "\n";
+    error_code = glGetError();
+  }
+#endif
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::set_draw_buffer
 //     Function: GLGraphicsStateGuardian::set_draw_buffer
 //       Access: Protected
 //       Access: Protected
@@ -3345,6 +3410,7 @@ set_draw_buffer(const RenderBuffer &rb) {
   default:
   default:
     call_glDrawBuffer(GL_FRONT_AND_BACK);
     call_glDrawBuffer(GL_FRONT_AND_BACK);
   }
   }
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3393,6 +3459,7 @@ set_read_buffer(const RenderBuffer &rb) {
   default:
   default:
     call_glReadBuffer(GL_FRONT_AND_BACK);
     call_glReadBuffer(GL_FRONT_AND_BACK);
   }
   }
+  report_errors();
 }
 }
 
 
 
 
@@ -3413,6 +3480,7 @@ bind_texture(TextureContext *tc) {
     << ")" << endl;
     << ")" << endl;
 #endif
 #endif
   glBindTexture(GL_TEXTURE_2D, gtc->_index);
   glBindTexture(GL_TEXTURE_2D, gtc->_index);
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3438,6 +3506,7 @@ specify_texture(Texture *tex) {
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
             get_texture_filter_type(tex->get_magfilter()));
             get_texture_filter_type(tex->get_magfilter()));
   }
   }
+  report_errors();
 }
 }
 
 
 
 
@@ -3494,6 +3563,7 @@ apply_texture_immediate(Texture *tex) {
   glTexImage2D( GL_TEXTURE_2D, 0, internal_format,
   glTexImage2D( GL_TEXTURE_2D, 0, internal_format,
         pb->get_xsize(), pb->get_ysize(), pb->get_border(),
         pb->get_xsize(), pb->get_ysize(), pb->get_border(),
         external_format, type, pb->_image );
         external_format, type, pb->_image );
+  report_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3688,17 +3758,16 @@ get_internal_image_format(PixelBuffer::Format format) {
 GLint GLGraphicsStateGuardian::
 GLint GLGraphicsStateGuardian::
 get_texture_apply_mode_type( TextureApplyProperty::Mode am ) const
 get_texture_apply_mode_type( TextureApplyProperty::Mode am ) const
 {
 {
-    switch( am )
-    {
-    case TextureApplyProperty::M_modulate: return GL_MODULATE;
-    case TextureApplyProperty::M_decal: return GL_DECAL;
-    case TextureApplyProperty::M_blend: return GL_BLEND;
-    case TextureApplyProperty::M_replace: return GL_REPLACE;
-    case TextureApplyProperty::M_add: return GL_ADD;
-    }
-    glgsg_cat.error()
-      << "Invalid TextureApplyProperty::Mode value" << endl;
-    return GL_MODULATE;
+  switch( am ) {
+  case TextureApplyProperty::M_modulate: return GL_MODULATE;
+  case TextureApplyProperty::M_decal: return GL_DECAL;
+  case TextureApplyProperty::M_blend: return GL_BLEND;
+  case TextureApplyProperty::M_replace: return GL_REPLACE;
+  case TextureApplyProperty::M_add: return GL_ADD;
+  }
+  glgsg_cat.error()
+    << "Invalid TextureApplyProperty::Mode value" << endl;
+  return GL_MODULATE;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 3 - 0
panda/src/glgsg/glGraphicsStateGuardian.h

@@ -222,6 +222,9 @@ protected:
   INLINE GLenum get_light_id(int index) const;
   INLINE GLenum get_light_id(int index) const;
   INLINE GLenum get_clip_plane_id(int index) const;
   INLINE GLenum get_clip_plane_id(int index) const;
 
 
+  INLINE void report_errors() const;
+  void report_errors_loop(GLenum error_code) const;
+
   void set_draw_buffer(const RenderBuffer &rb);
   void set_draw_buffer(const RenderBuffer &rb);
   void set_read_buffer(const RenderBuffer &rb);
   void set_read_buffer(const RenderBuffer &rb);
 
 

+ 10 - 2
panda/src/gobj/material.I

@@ -285,7 +285,11 @@ get_local() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void Material::
 INLINE void Material::
 set_local(bool local) {
 set_local(bool local) {
-  _flags |= F_local;
+  if (local) {
+    _flags |= F_local;
+  } else {
+    _flags &= ~F_local;
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -305,7 +309,11 @@ get_twoside() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void Material::
 INLINE void Material::
 set_twoside(bool twoside) {
 set_twoside(bool twoside) {
-  _flags = F_twoside;
+  if (twoside) {
+    _flags |= F_twoside;
+  } else {
+    _flags &= ~F_twoside;
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 33 - 8
panda/src/gobj/material.cxx

@@ -74,10 +74,22 @@ compare_to(const Material &other) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Material::
 void Material::
 output(ostream &out) const {
 output(ostream &out) const {
-  out << "a" << get_ambient()[0] << ",d" << get_diffuse()[0] 
-      << ",s" << get_specular()[0] << ",e" << get_emission()[0]
-      << ",s" << get_shininess() << ",l" << get_local()
-      << ",t" << get_twoside();
+  out << "material";
+  if (has_ambient()) {
+    out << " a(" << get_ambient() << ")";
+  }
+  if (has_diffuse()) {
+    out << " d(" << get_diffuse() << ")";
+  }
+  if (has_specular()) {
+    out << " s(" << get_specular() << ")";
+  }
+  if (has_emission()) {
+    out << " e(" << get_emission() << ")";
+  }
+  out << " s" << get_shininess() 
+      << " l" << get_local() 
+      << " t" << get_twoside();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -87,10 +99,23 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Material::
 void Material::
 write(ostream &out, int indent_level) const {
 write(ostream &out, int indent_level) const {
-  indent(out, indent_level) << "ambient = " << get_ambient() << "\n";
-  indent(out, indent_level) << "diffuse = " << get_diffuse() << "\n";
-  indent(out, indent_level) << "specular = " << get_specular() << "\n";
-  indent(out, indent_level) << "emission = " << get_emission() << "\n";
+  bool any = false;
+  if (has_ambient()) {
+    indent(out, indent_level) << "ambient = " << get_ambient() << "\n";
+    any = true;
+  }
+  if (has_diffuse()) {
+    indent(out, indent_level) << "diffuse = " << get_diffuse() << "\n";
+    any = true;
+  }
+  if (has_specular()) {
+    indent(out, indent_level) << "specular = " << get_specular() << "\n";
+    any = true;
+  }
+  if (has_emission()) {
+    indent(out, indent_level) << "emission = " << get_emission() << "\n";
+    any = true;
+  }
   indent(out, indent_level) << "shininess = " << get_shininess() << "\n";
   indent(out, indent_level) << "shininess = " << get_shininess() << "\n";
   indent(out, indent_level) << "local = " << get_local() << "\n";
   indent(out, indent_level) << "local = " << get_local() << "\n";
   indent(out, indent_level) << "twoside = " << get_twoside() << "\n";
   indent(out, indent_level) << "twoside = " << get_twoside() << "\n";

+ 50 - 0
pandatool/src/lwoegg/cLwoSurface.cxx

@@ -29,6 +29,8 @@ CLwoSurface(LwoToEggConverter *converter, const LwoSurface *surface) :
   _surface(surface)
   _surface(surface)
 {
 {
   _flags = 0;
   _flags = 0;
+  _color.set(1.0, 1.0, 1.0);
+  _checked_material = false;
   _checked_texture = false;
   _checked_texture = false;
   _map_uvs = NULL;
   _map_uvs = NULL;
   _block = (CLwoSurfaceBlock *)NULL;
   _block = (CLwoSurfaceBlock *)NULL;
@@ -146,6 +148,10 @@ apply_properties(EggPrimitive *egg_prim, vector_PT_EggVertex &egg_vertices,
     }
     }
   }
   }
 
 
+  if (check_material()) {
+    egg_prim->set_material(_egg_material);
+  }
+
   // We treat color and transparency separately, because Lightwave
   // We treat color and transparency separately, because Lightwave
   // does, and this will allow us to treat inherited surfaces a little
   // does, and this will allow us to treat inherited surfaces a little
   // more robustly.
   // more robustly.
@@ -269,6 +275,50 @@ check_texture() {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CLwoSurface::check_material
+//       Access: Public
+//  Description: Checks whether the surface demands a material or not.
+//               Returns true if so, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool CLwoSurface::
+check_material() {
+  if (_checked_material) {
+    return (_egg_material != (EggMaterial *)NULL);
+  }
+  _checked_material = true;
+  _egg_material = (EggMaterial *)NULL;
+
+  if (!_converter->_make_materials) {
+    // If we aren't making materials, then don't make a material.
+    return false;
+  }
+
+  _egg_material = new EggMaterial(get_name());
+
+  RGBColorf color = _color;
+  if (check_texture()) {
+    // Texturing overrides the color.
+    color.set(1.0, 1.0, 1.0);
+  }
+
+  if ((_flags & F_diffuse) != 0) {
+    RGBColorf diffuse = color * _diffuse;
+    _egg_material->set_diff(diffuse);
+  }
+  if ((_flags & F_luminosity) != 0) {
+    RGBColorf luminosity = color * _luminosity;
+    _egg_material->set_emit(luminosity);
+  }
+  if ((_flags & F_specular) != 0) {
+    RGBColorf specular = color * _specular;
+    _egg_material->set_spec(specular);
+  }
+
+  return true;
+}
+  
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CLwoSurface::generate_uvs
 //     Function: CLwoSurface::generate_uvs
 //       Access: Private
 //       Access: Private

+ 6 - 0
pandatool/src/lwoegg/cLwoSurface.h

@@ -13,6 +13,7 @@
 #include <lwoSurface.h>
 #include <lwoSurface.h>
 #include <luse.h>
 #include <luse.h>
 #include <eggTexture.h>
 #include <eggTexture.h>
+#include <eggMaterial.h>
 #include <vector_PT_EggVertex.h>
 #include <vector_PT_EggVertex.h>
 
 
 #include <map>
 #include <map>
@@ -38,10 +39,12 @@ public:
 			vector_PT_EggVertex &egg_vertices,
 			vector_PT_EggVertex &egg_vertices,
 			float &smooth_angle);
 			float &smooth_angle);
   bool check_texture();
   bool check_texture();
+  bool check_material();
 
 
   INLINE bool has_named_uvs() const;
   INLINE bool has_named_uvs() const;
   INLINE const string &get_uv_name() const;
   INLINE const string &get_uv_name() const;
 
 
+
   enum Flags {
   enum Flags {
     F_color        = 0x0001,
     F_color        = 0x0001,
     F_diffuse      = 0x0002,
     F_diffuse      = 0x0002,
@@ -68,6 +71,9 @@ public:
   LwoToEggConverter *_converter;
   LwoToEggConverter *_converter;
   CPT(LwoSurface) _surface;
   CPT(LwoSurface) _surface;
 
 
+  bool _checked_material;
+  PT(EggMaterial) _egg_material;
+
   bool _checked_texture;
   bool _checked_texture;
   PT(EggTexture) _egg_texture;
   PT(EggTexture) _egg_texture;
 
 

+ 1 - 0
pandatool/src/lwoegg/lwoToEggConverter.cxx

@@ -31,6 +31,7 @@
 LwoToEggConverter::
 LwoToEggConverter::
 LwoToEggConverter() {
 LwoToEggConverter() {
   _generic_layer = (CLwoLayer *)NULL;
   _generic_layer = (CLwoLayer *)NULL;
+  _make_materials = true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 0
pandatool/src/lwoegg/lwoToEggConverter.h

@@ -45,6 +45,8 @@ public:
 
 
   CLwoSurface *get_surface(const string &name) const;
   CLwoSurface *get_surface(const string &name) const;
 
 
+  bool _make_materials;
+
 private:
 private:
   void cleanup();
   void cleanup();