Преглед изворни кода

one override per TextureStage, instead of a global override per TextureAttrib

David Rose пре 15 година
родитељ
комит
9d62ca9f98

+ 2 - 1
panda/src/egg2pg/eggLoader.cxx

@@ -91,6 +91,7 @@
 #include "bitArray.h"
 #include "bitArray.h"
 #include "thread.h"
 #include "thread.h"
 #include "uvScrollNode.h"
 #include "uvScrollNode.h"
+#include "textureStagePool.h"
 
 
 #include <ctype.h>
 #include <ctype.h>
 #include <algorithm>
 #include <algorithm>
@@ -1568,7 +1569,7 @@ make_texture_stage(const EggTexture *egg_tex) {
     stage->set_color(egg_tex->get_color());
     stage->set_color(egg_tex->get_color());
   }
   }
 
 
-  return stage;
+  return TextureStagePool::get_stage(stage);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 3 - 0
panda/src/gobj/Sources.pp

@@ -63,6 +63,7 @@
     texturePoolFilter.I texturePoolFilter.h \
     texturePoolFilter.I texturePoolFilter.h \
     textureReloadRequest.I textureReloadRequest.h \
     textureReloadRequest.I textureReloadRequest.h \
     textureStage.I textureStage.h \
     textureStage.I textureStage.h \
+    textureStagePool.I textureStagePool.h \
     transformBlend.I transformBlend.h \
     transformBlend.I transformBlend.h \
     transformBlendTable.I transformBlendTable.h \
     transformBlendTable.I transformBlendTable.h \
     transformTable.I transformTable.h \
     transformTable.I transformTable.h \
@@ -131,6 +132,7 @@
     texturePoolFilter.cxx \
     texturePoolFilter.cxx \
     textureReloadRequest.cxx \
     textureReloadRequest.cxx \
     textureStage.cxx \
     textureStage.cxx \
+    textureStagePool.cxx \
     transformBlend.cxx \
     transformBlend.cxx \
     transformBlendTable.cxx \
     transformBlendTable.cxx \
     transformTable.cxx \
     transformTable.cxx \
@@ -201,6 +203,7 @@
     texturePoolFilter.I texturePoolFilter.h \
     texturePoolFilter.I texturePoolFilter.h \
     textureReloadRequest.I textureReloadRequest.h \
     textureReloadRequest.I textureReloadRequest.h \
     textureStage.I textureStage.h \
     textureStage.I textureStage.h \
+    textureStagePool.I textureStagePool.h \
     transformBlend.I transformBlend.h \
     transformBlend.I transformBlend.h \
     transformBlendTable.I transformBlendTable.h \
     transformBlendTable.I transformBlendTable.h \
     transformTable.I transformTable.h \
     transformTable.I transformTable.h \

+ 1 - 0
panda/src/gobj/gobj_composite2.cxx

@@ -20,6 +20,7 @@
 #include "texturePoolFilter.cxx"
 #include "texturePoolFilter.cxx"
 #include "textureReloadRequest.cxx"
 #include "textureReloadRequest.cxx"
 #include "textureStage.cxx"
 #include "textureStage.cxx"
+#include "textureStagePool.cxx"
 #include "transformBlend.cxx"
 #include "transformBlend.cxx"
 #include "transformBlendTable.cxx"
 #include "transformBlendTable.cxx"
 #include "transformTable.cxx"
 #include "transformTable.cxx"

+ 24 - 3
panda/src/gobj/materialPool.I

@@ -35,7 +35,28 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Material *MaterialPool::
 INLINE Material *MaterialPool::
 get_material(Material *temp) {
 get_material(Material *temp) {
-  return get_ptr()->ns_get_material(temp);
+  return get_global_ptr()->ns_get_material(temp);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MaterialPool::release_material
+//       Access: Published, Static
+//  Description: Removes the indicated material from the pool.
+////////////////////////////////////////////////////////////////////
+INLINE void MaterialPool::
+release_material(Material *material) {
+  get_global_ptr()->ns_release_material(material);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MaterialPool::release_all_materials
+//       Access: Published, Static
+//  Description: Releases all materials in the pool and restores the
+//               pool to the empty state.
+////////////////////////////////////////////////////////////////////
+INLINE void MaterialPool::
+release_all_materials() {
+  get_global_ptr()->ns_release_all_materials();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -48,7 +69,7 @@ get_material(Material *temp) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int MaterialPool::
 INLINE int MaterialPool::
 garbage_collect() {
 garbage_collect() {
-  return get_ptr()->ns_garbage_collect();
+  return get_global_ptr()->ns_garbage_collect();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -59,7 +80,7 @@ garbage_collect() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void MaterialPool::
 INLINE void MaterialPool::
 list_contents(ostream &out) {
 list_contents(ostream &out) {
-  get_ptr()->ns_list_contents(out);
+  get_global_ptr()->ns_list_contents(out);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 28 - 3
panda/src/gobj/materialPool.cxx

@@ -27,7 +27,7 @@ MaterialPool *MaterialPool::_global_ptr = (MaterialPool *)NULL;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void MaterialPool::
 void MaterialPool::
 write(ostream &out) {
 write(ostream &out) {
-  get_ptr()->ns_list_contents(out);
+  get_global_ptr()->ns_list_contents(out);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -53,6 +53,31 @@ ns_get_material(Material *temp) {
   return (*mi).second;
   return (*mi).second;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MaterialPool::ns_release_material
+//       Access: Private
+//  Description: The nonstatic implementation of release_material().
+////////////////////////////////////////////////////////////////////
+void MaterialPool::
+ns_release_material(Material *temp) {
+  LightMutexHolder holder(_lock);
+
+  CPT(Material) cpttemp = temp;
+  _materials.erase(cpttemp);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MaterialPool::ns_release_all_materials
+//       Access: Private
+//  Description: The nonstatic implementation of release_all_materials().
+////////////////////////////////////////////////////////////////////
+void MaterialPool::
+ns_release_all_materials() {
+  LightMutexHolder holder(_lock);
+
+  _materials.clear();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MaterialPool::ns_garbage_collect
 //     Function: MaterialPool::ns_garbage_collect
 //       Access: Private
 //       Access: Private
@@ -104,13 +129,13 @@ ns_list_contents(ostream &out) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: MaterialPool::get_ptr
+//     Function: MaterialPool::get_global_ptr
 //       Access: Private, Static
 //       Access: Private, Static
 //  Description: Initializes and/or returns the global pointer to the
 //  Description: Initializes and/or returns the global pointer to the
 //               one MaterialPool object in the system.
 //               one MaterialPool object in the system.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MaterialPool *MaterialPool::
 MaterialPool *MaterialPool::
-get_ptr() {
+get_global_ptr() {
   if (_global_ptr == (MaterialPool *)NULL) {
   if (_global_ptr == (MaterialPool *)NULL) {
     _global_ptr = new MaterialPool;
     _global_ptr = new MaterialPool;
   }
   }

+ 8 - 1
panda/src/gobj/materialPool.h

@@ -42,18 +42,25 @@
 class EXPCL_PANDA_GOBJ MaterialPool {
 class EXPCL_PANDA_GOBJ MaterialPool {
 PUBLISHED:
 PUBLISHED:
   INLINE static Material *get_material(Material *temp);
   INLINE static Material *get_material(Material *temp);
+  INLINE static void release_material(Material *temp);
+  INLINE static void release_all_materials();
+
   INLINE static int garbage_collect();
   INLINE static int garbage_collect();
   INLINE static void list_contents(ostream &out);
   INLINE static void list_contents(ostream &out);
+
   static void write(ostream &out);
   static void write(ostream &out);
 
 
 private:
 private:
   INLINE MaterialPool();
   INLINE MaterialPool();
 
 
   Material *ns_get_material(Material *temp);
   Material *ns_get_material(Material *temp);
+  void ns_release_material(Material *temp);
+  void ns_release_all_materials();
+
   int ns_garbage_collect();
   int ns_garbage_collect();
   void ns_list_contents(ostream &out) const;
   void ns_list_contents(ostream &out) const;
 
 
-  static MaterialPool *get_ptr();
+  static MaterialPool *get_global_ptr();
 
 
   static MaterialPool *_global_ptr;
   static MaterialPool *_global_ptr;
 
 

+ 30 - 10
panda/src/gobj/textureStage.I

@@ -114,16 +114,6 @@ get_priority() const {
   return _priority;
   return _priority;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: TextureStage::opertor <
-//       Access: Published
-//  Description: Compare if the sort order is lower
-////////////////////////////////////////////////////////////////////
-INLINE bool TextureStage::
-operator < (const TextureStage &other) const {
-  return (_sort < other._sort);
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureStage::set_texcoord_name
 //     Function: TextureStage::set_texcoord_name
 //       Access: Published
 //       Access: Published
@@ -673,6 +663,36 @@ uses_last_saved_result() const {
   return _uses_last_saved_result;
   return _uses_last_saved_result;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStage::operator ==
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool TextureStage::
+operator == (const TextureStage &other) const {
+  return compare_to(other) == 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStage::operator !=
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool TextureStage::
+operator != (const TextureStage &other) const {
+  return compare_to(other) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStage::operator <
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool TextureStage::
+operator < (const TextureStage &other) const {
+  return compare_to(other) < 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureStage::get_default
 //     Function: TextureStage::get_default
 //       Access: Published, Static
 //       Access: Published, Static

+ 116 - 0
panda/src/gobj/textureStage.cxx

@@ -104,6 +104,122 @@ TextureStage::
 ~TextureStage() {
 ~TextureStage() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStage::compare_to
+//       Access: Published
+//  Description: Returns a number less than zero if this TextureStage
+//               sorts before the other one, greater than zero if it
+//               sorts after, or zero if they are equivalent.  The
+//               sorting order is arbitrary and largely meaningless,
+//               except to differentiate different stages.
+////////////////////////////////////////////////////////////////////
+int TextureStage::
+compare_to(const TextureStage &other) const {
+  // We put the sort parameter first, so that we sorting a list of
+  // TextureStages will happen to put them in sorted order, even
+  // though we don't promise to do that.  But there's no reason not to
+  // do so, and it might be more convenient for the developer.
+  if (get_sort() != other.get_sort()) {
+    return get_sort() < other.get_sort() ? -1 : 1;
+  }
+
+  // The remaining parameters are arbitrary.  We start with the name,
+  // because that's most likely to be consistent between similar
+  // TextureStages, and different between different TextureStages.
+  int compare = strcmp(get_name().c_str(), other.get_name().c_str());
+  if (compare != 0) {
+    return compare;
+  }
+
+  if (get_priority() != other.get_priority()) {
+    return get_priority() < other.get_priority() ? -1 : 1;
+  }
+  if (get_texcoord_name() != other.get_texcoord_name()) {
+    return get_texcoord_name() < other.get_texcoord_name() ? -1 : 1;
+  }
+  if (get_mode() != other.get_mode()) {
+    return get_mode() < other.get_mode() ? -1 : 1;
+  }
+  if (get_rgb_scale() != other.get_rgb_scale()) {
+    return get_rgb_scale() < other.get_rgb_scale() ? -1 : 1;
+  }
+  if (get_alpha_scale() != other.get_alpha_scale()) {
+    return get_alpha_scale() < other.get_alpha_scale() ? -1 : 1;
+  }
+  if (get_saved_result() != other.get_saved_result()) {
+    return get_saved_result() < other.get_saved_result() ? -1 : 1;
+  }
+  if (get_mode() != other.get_mode()) {
+    return get_mode() < other.get_mode() ? -1 : 1;
+  }
+  if (get_mode() == M_combine) {
+    if (get_combine_rgb_mode() != other.get_combine_rgb_mode()) {
+      return get_combine_rgb_mode() < other.get_combine_rgb_mode() ? -1 : 1;
+    }
+    
+    if (get_num_combine_rgb_operands() != other.get_num_combine_rgb_operands()) {
+      return get_num_combine_rgb_operands() < other.get_num_combine_rgb_operands() ? -1 : 1;
+    }
+    if (get_num_combine_rgb_operands() >= 1) {
+      if (get_combine_rgb_source0() != other.get_combine_rgb_source0()) {
+        return get_combine_rgb_source0() < other.get_combine_rgb_source0() ? -1 : 1;
+      }
+      if (get_combine_rgb_operand0() != other.get_combine_rgb_operand0()) {
+        return get_combine_rgb_operand0() < other.get_combine_rgb_operand0() ? -1 : 1;
+      }
+    }
+    if (get_num_combine_rgb_operands() >= 2) {
+      if (get_combine_rgb_source1() != other.get_combine_rgb_source1()) {
+        return get_combine_rgb_source1() < other.get_combine_rgb_source1() ? -1 : 1;
+      }
+      if (get_combine_rgb_operand1() != other.get_combine_rgb_operand1()) {
+        return get_combine_rgb_operand1() < other.get_combine_rgb_operand1() ? -1 : 1;
+      }
+    }
+    if (get_num_combine_rgb_operands() >= 3) {
+      if (get_combine_rgb_source2() != other.get_combine_rgb_source2()) {
+        return get_combine_rgb_source2() < other.get_combine_rgb_source2() ? -1 : 1;
+      }
+      if (get_combine_rgb_operand2() != other.get_combine_rgb_operand2()) {
+        return get_combine_rgb_operand2() < other.get_combine_rgb_operand2() ? -1 : 1;
+      }
+    }
+    if (get_combine_alpha_mode() != other.get_combine_alpha_mode()) {
+      return get_combine_alpha_mode() < other.get_combine_alpha_mode() ? -1 : 1;
+    }
+    
+    if (get_num_combine_alpha_operands() != other.get_num_combine_alpha_operands()) {
+      return get_num_combine_alpha_operands() < other.get_num_combine_alpha_operands() ? -1 : 1;
+    }
+    if (get_num_combine_alpha_operands() >= 1) {
+      if (get_combine_alpha_source0() != other.get_combine_alpha_source0()) {
+        return get_combine_alpha_source0() < other.get_combine_alpha_source0() ? -1 : 1;
+      }
+      if (get_combine_alpha_operand0() != other.get_combine_alpha_operand0()) {
+        return get_combine_alpha_operand0() < other.get_combine_alpha_operand0() ? -1 : 1;
+      }
+    }
+    if (get_num_combine_alpha_operands() >= 2) {
+      if (get_combine_alpha_source1() != other.get_combine_alpha_source1()) {
+        return get_combine_alpha_source1() < other.get_combine_alpha_source1() ? -1 : 1;
+      }
+      if (get_combine_alpha_operand1() != other.get_combine_alpha_operand1()) {
+        return get_combine_alpha_operand1() < other.get_combine_alpha_operand1() ? -1 : 1;
+      }
+    }
+    if (get_num_combine_alpha_operands() >= 3) {
+      if (get_combine_alpha_source2() != other.get_combine_alpha_source2()) {
+        return get_combine_alpha_source2() < other.get_combine_alpha_source2() ? -1 : 1;
+      }
+      if (get_combine_alpha_operand2() != other.get_combine_alpha_operand2()) {
+        return get_combine_alpha_operand2() < other.get_combine_alpha_operand2() ? -1 : 1;
+      }
+    }
+  }
+
+  return 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureStage::Destructor
 //     Function: TextureStage::Destructor
 //       Access: Published
 //       Access: Published

+ 6 - 2
panda/src/gobj/textureStage.h

@@ -109,8 +109,6 @@ PUBLISHED:
   INLINE void set_priority(int priority);
   INLINE void set_priority(int priority);
   INLINE int get_priority() const;
   INLINE int get_priority() const;
 
 
-  INLINE bool operator < (const TextureStage &other) const;
-
   INLINE void set_texcoord_name(InternalName *name);
   INLINE void set_texcoord_name(InternalName *name);
   INLINE void set_texcoord_name(const string &texcoord_name);
   INLINE void set_texcoord_name(const string &texcoord_name);
   INLINE InternalName *get_texcoord_name() const;
   INLINE InternalName *get_texcoord_name() const;
@@ -173,6 +171,12 @@ PUBLISHED:
   INLINE bool uses_primary_color() const;
   INLINE bool uses_primary_color() const;
   INLINE bool uses_last_saved_result() const;
   INLINE bool uses_last_saved_result() const;
 
 
+  INLINE bool operator == (const TextureStage &other) const;
+  INLINE bool operator != (const TextureStage &other) const;
+  INLINE bool operator < (const TextureStage &other) const;
+
+  int compare_to(const TextureStage &other) const;
+
   void write(ostream &out) const;
   void write(ostream &out) const;
   void output(ostream &out) const;
   void output(ostream &out) const;
 
 

+ 115 - 0
panda/src/gobj/textureStagePool.I

@@ -0,0 +1,115 @@
+// Filename: textureStagePool.I
+// Created by:  drose (03May10)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::get_stage
+//       Access: Public, Static
+//  Description: Returns a TextureStage pointer that represents the
+//               same TextureStage described by temp, except that it is a
+//               shared pointer.
+//
+//               Each call to get_stage() passing an equivalent
+//               TextureStage pointer will return the same shared pointer.
+//
+//               If you modify the shared pointer, it will
+//               automatically disassociate it from the pool.
+//
+//               Also, the return value may be a different pointer
+//               than that passed in, or it may be the same pointer.
+//               In either case, the passed in pointer has now been
+//               sacrificed to the greater good and should not be used
+//               again (like any other PointerTo, it will be freed
+//               when the last reference count is removed).
+////////////////////////////////////////////////////////////////////
+INLINE TextureStage *TextureStagePool::
+get_stage(TextureStage *temp) {
+  return get_global_ptr()->ns_get_stage(temp);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::release_stage
+//       Access: Published, Static
+//  Description: Removes the indicated TextureStage from the pool.
+////////////////////////////////////////////////////////////////////
+INLINE void TextureStagePool::
+release_stage(TextureStage *stage) {
+  get_global_ptr()->ns_release_stage(stage);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::release_all_stages
+//       Access: Published, Static
+//  Description: Releases all TextureStages in the pool and restores the
+//               pool to the empty state.
+////////////////////////////////////////////////////////////////////
+INLINE void TextureStagePool::
+release_all_stages() {
+  get_global_ptr()->ns_release_all_stages();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::set_mode
+//       Access: Published, Static
+//  Description: Specifies the fundamental operating mode of the
+//               TextureStagePool.
+//
+//               If this is M_none, each call to get_stage() returns
+//               the same TextureStage pointer that was passed in (the
+//               pool is effectively disabled).  If this is M_name,
+//               each call to get_stage() returns the last
+//               TextureStage passed in with the same name, whether it
+//               has different properties or not.  If this is
+//               M_unique, then each call to get_stage() returns only
+//               TextureStages with identical properties.
+////////////////////////////////////////////////////////////////////
+INLINE void TextureStagePool::
+set_mode(TextureStagePool::Mode mode) {
+  get_global_ptr()->ns_set_mode(mode);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::get_mode
+//       Access: Published, Static
+//  Description: Returns the fundamental operating mode of the
+//               TextureStagePool.  See set_mode().
+////////////////////////////////////////////////////////////////////
+INLINE TextureStagePool::Mode TextureStagePool::
+get_mode() {
+  return get_global_ptr()->ns_get_mode();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::garbage_collect
+//       Access: Public, Static
+//  Description: Releases only those TextureStages in the pool that have a
+//               reference count of exactly 1; i.e. only those
+//               TextureStages that are not being used outside of the pool.
+//               Returns the number of TextureStages released.
+////////////////////////////////////////////////////////////////////
+INLINE int TextureStagePool::
+garbage_collect() {
+  return get_global_ptr()->ns_garbage_collect();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::list_contents
+//       Access: Public, Static
+//  Description: Lists the contents of the TextureStage pool to the
+//               indicated output stream.
+////////////////////////////////////////////////////////////////////
+INLINE void TextureStagePool::
+list_contents(ostream &out) {
+  get_global_ptr()->ns_list_contents(out);
+}

+ 332 - 0
panda/src/gobj/textureStagePool.cxx

@@ -0,0 +1,332 @@
+// Filename: textureStagePool.cxx
+// Created by:  drose (03May10)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+#include "textureStagePool.h"
+#include "config_gobj.h"
+#include "mutexHolder.h"
+#include "configVariableEnum.h"
+
+TextureStagePool *TextureStagePool::_global_ptr = (TextureStagePool *)NULL;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::write
+//       Access: Published, Static
+//  Description: Lists the contents of the TextureStage pool to the
+//               indicated output stream.
+////////////////////////////////////////////////////////////////////
+void TextureStagePool::
+write(ostream &out) {
+  get_global_ptr()->ns_list_contents(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::Constructor
+//       Access: Private
+//  Description: The constructor is not intended to be called
+//               directly; there's only supposed to be one TextureStagePool
+//               in the universe and it constructs itself.
+////////////////////////////////////////////////////////////////////
+TextureStagePool::
+TextureStagePool() {
+  ConfigVariableEnum<Mode> texture_stage_pool_mode
+    ("texture-stage-pool-mode", M_none,
+     PRC_DESC("Defines the initial value of TextureStagePool::set_mode().  "
+              "Set this to 'none' to disable the use of the TextureStagePool, "
+              "to 'name' to group TextureStages by name only, or 'unique' "
+              "to group together only identical TextureStages."));
+  _mode = texture_stage_pool_mode.get_value();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::ns_get_stage
+//       Access: Public
+//  Description: The nonstatic implementation of get_stage().
+////////////////////////////////////////////////////////////////////
+TextureStage *TextureStagePool::
+ns_get_stage(TextureStage *temp) {
+  MutexHolder holder(_lock);
+
+  switch (_mode) {
+  case M_none:
+    return temp;
+
+  case M_name:
+    {
+      StagesByName::iterator ni = _stages_by_name.find(temp->get_name());
+      if (ni == _stages_by_name.end()) {
+        ni = _stages_by_name.insert(StagesByName::value_type(temp->get_name(), temp)).first;
+      } else {
+        if ((*ni).first != (*ni).second->get_name()) {
+          // The pointer no longer matches the original name.  Save a
+          // new one.
+          (*ni).second = temp;
+        }
+      }
+      return (*ni).second;
+    }
+
+  case M_unique:
+    {
+      CPT(TextureStage) cpttemp = temp;
+      StagesByProperties::iterator si = _stages_by_properties.find(cpttemp);
+      if (si == _stages_by_properties.end()) {
+        si = _stages_by_properties.insert(StagesByProperties::value_type(new TextureStage(*temp), temp)).first;
+      } else {
+        if (*(*si).first != *(*si).second) {
+          // The pointer no longer matches its original value.  Save a new
+          // one.
+          (*si).second = temp;
+        }
+      }
+      return (*si).second;
+    }
+  }
+
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::ns_release_stage
+//       Access: Private
+//  Description: The nonstatic implementation of release_stage().
+////////////////////////////////////////////////////////////////////
+void TextureStagePool::
+ns_release_stage(TextureStage *temp) {
+  MutexHolder holder(_lock);
+
+  switch (_mode) {
+  case M_none:
+    break;
+
+  case M_name:
+    _stages_by_name.erase(temp->get_name());
+    break;
+
+  case M_unique:
+    {
+      CPT(TextureStage) cpttemp = temp;
+      _stages_by_properties.erase(cpttemp);
+    }
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::ns_release_all_stages
+//       Access: Private
+//  Description: The nonstatic implementation of release_all_stages().
+////////////////////////////////////////////////////////////////////
+void TextureStagePool::
+ns_release_all_stages() {
+  MutexHolder holder(_lock);
+
+  _stages_by_name.clear();
+  _stages_by_properties.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::ns_set_mode
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TextureStagePool::
+ns_set_mode(TextureStagePool::Mode mode) {
+  MutexHolder holder(_lock);
+
+  if (_mode != mode) {
+    _stages_by_name.clear();
+    _stages_by_properties.clear();
+    _mode = mode;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::ns_get_mode
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TextureStagePool::Mode TextureStagePool::
+ns_get_mode() {
+  MutexHolder holder(_lock);
+
+  return _mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::ns_garbage_collect
+//       Access: Private
+//  Description: The nonstatic implementation of garbage_collect().
+////////////////////////////////////////////////////////////////////
+int TextureStagePool::
+ns_garbage_collect() {
+  MutexHolder holder(_lock);
+
+  switch (_mode) {
+  case M_none:
+    return 0;
+
+  case M_name:
+    {
+      int num_released = 0;
+      StagesByName new_set;
+      
+      StagesByName::iterator ni;
+      for (ni = _stages_by_name.begin(); ni != _stages_by_name.end(); ++ni) {
+        const string &name = (*ni).first;
+        TextureStage *ts2 = (*ni).second;
+        if (name != ts2->get_name() || ts2->get_ref_count() == 1) {
+          if (gobj_cat.is_debug()) {
+            gobj_cat.debug()
+              << "Releasing " << name << "\n";
+          }
+          ++num_released;
+        } else {
+          new_set.insert(new_set.end(), *ni);
+        }
+      }
+      
+      _stages_by_name.swap(new_set);
+      return num_released;
+    }
+
+  case M_unique:
+    {
+      int num_released = 0;
+      StagesByProperties new_set;
+      
+      StagesByProperties::iterator si;
+      for (si = _stages_by_properties.begin(); si != _stages_by_properties.end(); ++si) {
+        const TextureStage *ts1 = (*si).first;
+        TextureStage *ts2 = (*si).second;
+        if ((*ts1) != (*ts2) || ts2->get_ref_count() == 1) {
+          if (gobj_cat.is_debug()) {
+            gobj_cat.debug()
+              << "Releasing " << *ts1 << "\n";
+          }
+          ++num_released;
+        } else {
+          new_set.insert(new_set.end(), *si);
+        }
+      }
+      
+      _stages_by_properties.swap(new_set);
+      return num_released;
+    }
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::ns_list_contents
+//       Access: Private
+//  Description: The nonstatic implementation of list_contents().
+////////////////////////////////////////////////////////////////////
+void TextureStagePool::
+ns_list_contents(ostream &out) const {
+  MutexHolder holder(_lock);
+
+  out << "TextureStagePool in mode " << _mode << "\n";
+
+  switch (_mode) {
+  case M_none:
+    break;
+
+  case M_name:
+    {
+      out << _stages_by_name.size() << " TextureStages:\n";
+      StagesByName::const_iterator ni;
+      for (ni = _stages_by_name.begin(); ni != _stages_by_name.end(); ++ni) {
+        const string &name = (*ni).first;
+        TextureStage *ts2 = (*ni).second;
+        out << "  " << name
+            << " (count = " << ts2->get_ref_count() << ")\n";
+      }
+    }
+    break;
+
+  case M_unique:
+    {
+      out << _stages_by_properties.size() << " TextureStages:\n";
+      StagesByProperties::const_iterator si;
+      for (si = _stages_by_properties.begin(); si != _stages_by_properties.end(); ++si) {
+        const TextureStage *ts1 = (*si).first;
+        TextureStage *ts2 = (*si).second;
+        out << "  " << *ts1
+            << " (count = " << ts2->get_ref_count() << ")\n";
+      }
+    }
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::get_global_ptr
+//       Access: Private, Static
+//  Description: Initializes and/or returns the global pointer to the
+//               one TextureStagePool object in the system.
+////////////////////////////////////////////////////////////////////
+TextureStagePool *TextureStagePool::
+get_global_ptr() {
+  if (_global_ptr == (TextureStagePool *)NULL) {
+    _global_ptr = new TextureStagePool;
+  }
+  return _global_ptr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::Mode output operator
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ostream &
+operator << (ostream &out, TextureStagePool::Mode mode) {
+  switch (mode) {
+  case TextureStagePool::M_none:
+    return out << "none";
+
+  case TextureStagePool::M_name:
+    return out << "name";
+
+  case TextureStagePool::M_unique:
+    return out << "unique";
+  }
+
+  return out << "**invalid mode (" << (int)mode << ")**";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStagePool::Mode input operator
+//  Description: 
+////////////////////////////////////////////////////////////////////
+istream &
+operator >> (istream &in, TextureStagePool::Mode &mode) {
+  string word;
+  in >> word;
+
+  if (cmp_nocase(word, "none") == 0) {
+    mode = TextureStagePool::M_none;
+  } else if (cmp_nocase(word, "name") == 0) {
+    mode = TextureStagePool::M_name;
+  } else if (cmp_nocase(word, "unique") == 0) {
+    mode = TextureStagePool::M_unique;
+
+  } else {
+    gobj_cat->error() << "Invalid TextureStagePool mode value: " << word << "\n";
+    mode = TextureStagePool::M_none;
+  }
+
+  return in;
+}

+ 93 - 0
panda/src/gobj/textureStagePool.h

@@ -0,0 +1,93 @@
+// Filename: textureStagePool.h
+// Created by:  drose (03May10)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TEXTURESTAGEPOOL_H
+#define TEXTURESTAGEPOOL_H
+
+#include "pandabase.h"
+#include "textureStage.h"
+#include "pointerTo.h"
+#include "pmutex.h"
+#include "pset.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : TextureStagePool
+// Description : The TextureStagePool (there is only one in the universe)
+//               serves to unify different pointers to the same
+//               TextureStage, mainly to help developers use a common
+//               pointer to access things that are loaded from
+//               different model files.
+//
+//               It runs in one of three different modes, according to
+//               set_mode().  See that method for more information.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_GOBJ TextureStagePool {
+PUBLISHED:
+  enum Mode {
+    M_none,
+    M_name,
+    M_unique,
+  };
+
+  INLINE static TextureStage *get_stage(TextureStage *temp);
+  INLINE static void release_stage(TextureStage *temp);
+  INLINE static void release_all_stages();
+
+  INLINE static void set_mode(Mode mode);
+  INLINE static Mode get_mode();
+
+  INLINE static int garbage_collect();
+  INLINE static void list_contents(ostream &out);
+  static void write(ostream &out);
+
+private:
+  TextureStagePool();
+
+  TextureStage *ns_get_stage(TextureStage *temp);
+  void ns_release_stage(TextureStage *temp);
+  void ns_release_all_stages();
+
+  void ns_set_mode(Mode mode);
+  Mode ns_get_mode();
+
+  int ns_garbage_collect();
+  void ns_list_contents(ostream &out) const;
+
+  static TextureStagePool *get_global_ptr();
+
+  static TextureStagePool *_global_ptr;
+
+  Mutex _lock;
+
+  // We store a map of CPT(TextureStage) to PT(TextureStage).  These are two
+  // equivalent structures, but different pointers.  The first pointer
+  // never leaves this class.  If the second pointer changes value,
+  // we'll notice it and return a new one.
+  typedef pmap<CPT(TextureStage), PT(TextureStage), indirect_compare_to<const TextureStage *> > StagesByProperties;
+  StagesByProperties _stages_by_properties;
+
+  typedef pmap<string, PT(TextureStage) > StagesByName;
+  StagesByName _stages_by_name;
+
+  Mode _mode;
+};
+
+EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, TextureStagePool::Mode mode);
+EXPCL_PANDA_GOBJ istream &operator >> (istream &in, TextureStagePool::Mode &mode);
+
+#include "textureStagePool.I"
+
+#endif
+
+

+ 7 - 13
panda/src/pgraph/nodePath.cxx

@@ -3420,26 +3420,21 @@ set_texture(Texture *tex, int priority) {
 void NodePath::
 void NodePath::
 set_texture(TextureStage *stage, Texture *tex, int priority) {
 set_texture(TextureStage *stage, Texture *tex, int priority) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
-  if (tex->get_texture_type() == Texture::TT_2d_texture_array){
-    pgraph_cat.error() << "Texture::TT_2d_texture_array is not supported by" << \
-     " the fixed pipeline.\n";
-    return;
-  }
+
   const RenderAttrib *attrib =
   const RenderAttrib *attrib =
     node()->get_attrib(TextureAttrib::get_class_slot());
     node()->get_attrib(TextureAttrib::get_class_slot());
   if (attrib != (const RenderAttrib *)NULL) {
   if (attrib != (const RenderAttrib *)NULL) {
-    priority = max(priority,
-                   node()->get_state()->get_override(TextureAttrib::get_class_slot()));
     const TextureAttrib *tsa = DCAST(TextureAttrib, attrib);
     const TextureAttrib *tsa = DCAST(TextureAttrib, attrib);
+    int sg_priority = node()->get_state()->get_override(TextureAttrib::get_class_slot());
 
 
     // Modify the existing TextureAttrib to add the indicated
     // Modify the existing TextureAttrib to add the indicated
     // texture.
     // texture.
-    node()->set_attrib(tsa->add_on_stage(stage, tex), priority);
+    node()->set_attrib(tsa->add_on_stage(stage, tex, priority), sg_priority);
 
 
   } else {
   } else {
     // Create a new TextureAttrib for this node.
     // Create a new TextureAttrib for this node.
     CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make());
     CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make());
-    node()->set_attrib(tsa->add_on_stage(stage, tex), priority);
+    node()->set_attrib(tsa->add_on_stage(stage, tex, priority));
   }
   }
 }
 }
 
 
@@ -3476,20 +3471,19 @@ set_texture_off(TextureStage *stage, int priority) {
   const RenderAttrib *attrib =
   const RenderAttrib *attrib =
     node()->get_attrib(TextureAttrib::get_class_slot());
     node()->get_attrib(TextureAttrib::get_class_slot());
   if (attrib != (const RenderAttrib *)NULL) {
   if (attrib != (const RenderAttrib *)NULL) {
-    priority = max(priority,
-                   node()->get_state()->get_override(TextureAttrib::get_class_slot()));
     const TextureAttrib *tsa = DCAST(TextureAttrib, attrib);
     const TextureAttrib *tsa = DCAST(TextureAttrib, attrib);
+    int sg_priority = node()->get_state()->get_override(TextureAttrib::get_class_slot());
 
 
     // Modify the existing TextureAttrib to add the indicated texture
     // Modify the existing TextureAttrib to add the indicated texture
     // to the "off" list.  This also, incidentally, removes it from
     // to the "off" list.  This also, incidentally, removes it from
     // the "on" list if it is there.
     // the "on" list if it is there.
-    node()->set_attrib(tsa->add_off_stage(stage), priority);
+    node()->set_attrib(tsa->add_off_stage(stage, priority), sg_priority);
 
 
   } else {
   } else {
     // Create a new TextureAttrib for this node that turns off the
     // Create a new TextureAttrib for this node that turns off the
     // indicated stage.
     // indicated stage.
     CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make());
     CPT(TextureAttrib) tsa = DCAST(TextureAttrib, TextureAttrib::make());
-    node()->set_attrib(tsa->add_off_stage(stage), priority);
+    node()->set_attrib(tsa->add_off_stage(stage, priority));
   }
   }
 }
 }
 
 

+ 2 - 2
panda/src/pgraph/renderAttrib.I

@@ -15,7 +15,7 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::compose
 //     Function: RenderAttrib::compose
-//       Access: Public
+//       Access: Published
 //  Description: Returns a new RenderAttrib object that represents the
 //  Description: Returns a new RenderAttrib object that represents the
 //               composition of this attrib with the other attrib.  In
 //               composition of this attrib with the other attrib.  In
 //               most cases, this is the same as the other attrib; a
 //               most cases, this is the same as the other attrib; a
@@ -30,7 +30,7 @@ compose(const RenderAttrib *other) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::invert_compose
 //     Function: RenderAttrib::invert_compose
-//       Access: Public
+//       Access: Published
 //  Description: Returns a new RenderAttrib object that represents the
 //  Description: Returns a new RenderAttrib object that represents the
 //               composition of the inverse of this attrib with the
 //               composition of the inverse of this attrib with the
 //               other attrib.  In most cases, this is the same as the
 //               other attrib.  In most cases, this is the same as the

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

@@ -80,7 +80,7 @@ RenderAttrib::
 //
 //
 //               This should return false if a RenderAttrib on a
 //               This should return false if a RenderAttrib on a
 //               higher node will compose into a RenderAttrib on a
 //               higher node will compose into a RenderAttrib on a
-//               lower node that has a higher override value, or false
+//               lower node that has a higher override value, or true
 //               if the lower RenderAttrib will completely replace the
 //               if the lower RenderAttrib will completely replace the
 //               state.
 //               state.
 //
 //

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

@@ -65,10 +65,12 @@ private:
 public:
 public:
   virtual ~RenderAttrib();
   virtual ~RenderAttrib();
 
 
+PUBLISHED:
   INLINE CPT(RenderAttrib) compose(const RenderAttrib *other) const;
   INLINE CPT(RenderAttrib) compose(const RenderAttrib *other) const;
   INLINE CPT(RenderAttrib) invert_compose(const RenderAttrib *other) const;
   INLINE CPT(RenderAttrib) invert_compose(const RenderAttrib *other) const;
   virtual bool lower_attrib_can_override() const;
   virtual bool lower_attrib_can_override() const;
 
 
+public:
   INLINE bool always_reissue() const;
   INLINE bool always_reissue() const;
 
 
   virtual bool has_cull_callback() const;
   virtual bool has_cull_callback() const;

+ 42 - 41
panda/src/pgraph/textureAttrib.I

@@ -37,13 +37,11 @@ TextureAttrib() {
 INLINE TextureAttrib::
 INLINE TextureAttrib::
 TextureAttrib(const TextureAttrib &copy) :
 TextureAttrib(const TextureAttrib &copy) :
   _on_stages(copy._on_stages),
   _on_stages(copy._on_stages),
-  _on_ptr_stages(copy._on_ptr_stages),
-  _on_ff_stages(copy._on_ff_stages),
-  _ff_tc_index(copy._ff_tc_index),
+  _render_stages(copy._render_stages),
+  _render_ff_stages(copy._render_ff_stages),
   _next_implicit_sort(copy._next_implicit_sort),
   _next_implicit_sort(copy._next_implicit_sort),
   _off_stages(copy._off_stages),
   _off_stages(copy._off_stages),
   _off_all_stages(copy._off_all_stages),
   _off_all_stages(copy._off_all_stages),
-  _on_textures(copy._on_textures),
   _sort_seq(copy._sort_seq),
   _sort_seq(copy._sort_seq),
   _filtered_seq(UpdateSeq::old())
   _filtered_seq(UpdateSeq::old())
 {
 {
@@ -92,7 +90,7 @@ get_texture() const {
 INLINE int TextureAttrib::
 INLINE int TextureAttrib::
 get_num_on_stages() const {
 get_num_on_stages() const {
   check_sorted();
   check_sorted();
-  return _on_stages.size();
+  return _render_stages.size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -103,8 +101,8 @@ get_num_on_stages() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE TextureStage *TextureAttrib::
 INLINE TextureStage *TextureAttrib::
 get_on_stage(int n) const {
 get_on_stage(int n) const {
-  nassertr(n >= 0 && n < (int)_on_stages.size(), (TextureStage *)NULL);
-  return _on_stages[n]._stage;
+  nassertr(n >= 0 && n < (int)_render_stages.size(), (TextureStage *)NULL);
+  return _render_stages[n]->_stage;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -117,11 +115,11 @@ get_on_stage(int n) const {
 INLINE int TextureAttrib::
 INLINE int TextureAttrib::
 get_num_on_ff_stages() const {
 get_num_on_ff_stages() const {
   check_sorted();
   check_sorted();
-  return _on_ff_stages.size();
+  return _render_ff_stages.size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: TextureAttrib::get_on_ff_stage
+//     Function: TextureAttrib::get_render_ff_stage
 //       Access: Published
 //       Access: Published
 //  Description: Returns the nth stage turned on by the attribute,
 //  Description: Returns the nth stage turned on by the attribute,
 //               sorted in render order, including only those relevant
 //               sorted in render order, including only those relevant
@@ -130,8 +128,8 @@ get_num_on_ff_stages() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE TextureStage *TextureAttrib::
 INLINE TextureStage *TextureAttrib::
 get_on_ff_stage(int n) const {
 get_on_ff_stage(int n) const {
-  nassertr(n >= 0 && n < (int)_on_ff_stages.size(), (TextureStage *)NULL);
-  return _on_ff_stages[n]._stage;
+  nassertr(n >= 0 && n < (int)_render_ff_stages.size(), (TextureStage *)NULL);
+  return _render_ff_stages[n]->_stage;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -141,13 +139,13 @@ get_on_ff_stage(int n) const {
 //               this returns a unique index number for the texture
 //               this returns a unique index number for the texture
 //               coordinate name used by that TextureStage.  It is
 //               coordinate name used by that TextureStage.  It is
 //               guaranteed to remain the same index number for each
 //               guaranteed to remain the same index number for each
-//               texcoord name, even if the texture render order
-//               changes.
+//               texcoord name (for a given set of TextureStages),
+//               even if the texture render order changes.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int TextureAttrib::
 INLINE int TextureAttrib::
 get_ff_tc_index(int n) const {
 get_ff_tc_index(int n) const {
-  nassertr(n >= 0 && n < (int)_ff_tc_index.size(), 0);
-  return _ff_tc_index[n];
+  nassertr(n >= 0 && n < (int)_render_ff_stages.size(), -1);
+  return _render_ff_stages[n]->_ff_tc_index;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -158,7 +156,7 @@ get_ff_tc_index(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool TextureAttrib::
 INLINE bool TextureAttrib::
 has_on_stage(TextureStage *stage) const {
 has_on_stage(TextureStage *stage) const {
-  return _on_textures.find(stage) != _on_textures.end();
+  return _on_stages.find(StageNode(stage)) != _on_stages.end();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -169,10 +167,10 @@ has_on_stage(TextureStage *stage) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Texture *TextureAttrib::
 INLINE Texture *TextureAttrib::
 get_on_texture(TextureStage *stage) const {
 get_on_texture(TextureStage *stage) const {
-  OnTextures::const_iterator ti;
-  ti = _on_textures.find(stage);
-  if (ti != _on_textures.end()) {
-    return (*ti).second;
+  Stages::const_iterator si;
+  si = _on_stages.find(StageNode(stage));
+  if (si != _on_stages.end()) {
+    return (*si)._texture;
   }
   }
   return NULL;
   return NULL;
 }
 }
@@ -197,7 +195,7 @@ get_num_off_stages() const {
 INLINE TextureStage *TextureAttrib::
 INLINE TextureStage *TextureAttrib::
 get_off_stage(int n) const {
 get_off_stage(int n) const {
   nassertr(n >= 0 && n < (int)_off_stages.size(), (TextureStage *)NULL);
   nassertr(n >= 0 && n < (int)_off_stages.size(), (TextureStage *)NULL);
-  return _off_stages[n];
+  return _off_stages[n]._stage;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -208,7 +206,7 @@ get_off_stage(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool TextureAttrib::
 INLINE bool TextureAttrib::
 has_off_stage(TextureStage *stage) const {
 has_off_stage(TextureStage *stage) const {
-  return _off_stages.find(stage) != _off_stages.end() ||
+  return _off_stages.find(StageNode(stage)) != _off_stages.end() ||
     (_off_all_stages && !has_on_stage(stage));
     (_off_all_stages && !has_on_stage(stage));
 }
 }
 
 
@@ -252,14 +250,17 @@ check_sorted() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: TextureAttrib::OnStageNode::Constructor
+//     Function: TextureAttrib::StageNode::Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE TextureAttrib::OnStageNode::
-OnStageNode(TextureStage *stage, unsigned int implicit_sort) :
-  _stage(stage),
-  _implicit_sort(implicit_sort)
+INLINE TextureAttrib::StageNode::
+StageNode(const TextureStage *stage, unsigned int implicit_sort, int override) :
+  // Yeah, we cast away the constness here.  Just too much trouble to
+  // deal with it properly.
+  _stage((TextureStage *)stage),
+  _implicit_sort(implicit_sort),
+  _override(override)
 {
 {
 }
 }
 
 
@@ -272,15 +273,15 @@ OnStageNode(TextureStage *stage, unsigned int implicit_sort) :
 //               within priority, within order by sort.
 //               within priority, within order by sort.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool TextureAttrib::CompareTextureStagePriorities::
 INLINE bool TextureAttrib::CompareTextureStagePriorities::
-operator () (const TextureAttrib::OnStageNode &a, 
-             const TextureAttrib::OnStageNode &b) const {
-  if (a._stage->get_priority() != b._stage->get_priority()) {
-    return a._stage->get_priority() > b._stage->get_priority();
+operator () (const TextureAttrib::StageNode *a, 
+             const TextureAttrib::StageNode *b) const {
+  if (a->_stage->get_priority() != b->_stage->get_priority()) {
+    return a->_stage->get_priority() > b->_stage->get_priority();
   }
   }
-  if (a._stage->get_sort() != b._stage->get_sort()) {
-    return a._stage->get_sort() < b._stage->get_sort();
+  if (a->_stage->get_sort() != b->_stage->get_sort()) {
+    return a->_stage->get_sort() < b->_stage->get_sort();
   }
   }
-  return a._implicit_sort < b._implicit_sort;
+  return a->_implicit_sort < b->_implicit_sort;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -290,12 +291,12 @@ operator () (const TextureAttrib::OnStageNode &a,
 //               texture stages in order by sort.
 //               texture stages in order by sort.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool TextureAttrib::CompareTextureStageSort::
 INLINE bool TextureAttrib::CompareTextureStageSort::
-operator () (const TextureAttrib::OnStageNode &a, 
-             const TextureAttrib::OnStageNode &b) const {
-  if (a._stage->get_sort() != b._stage->get_sort()) {
-    return a._stage->get_sort() < b._stage->get_sort();
+operator () (const TextureAttrib::StageNode *a, 
+             const TextureAttrib::StageNode *b) const {
+  if (a->_stage->get_sort() != b->_stage->get_sort()) {
+    return a->_stage->get_sort() < b->_stage->get_sort();
   }
   }
-  return a._implicit_sort < b._implicit_sort;
+  return a->_implicit_sort < b->_implicit_sort;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -305,7 +306,7 @@ operator () (const TextureAttrib::OnStageNode &a,
 //               texture stages in order by pointer.
 //               texture stages in order by pointer.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool TextureAttrib::CompareTextureStagePointer::
 INLINE bool TextureAttrib::CompareTextureStagePointer::
-operator () (const TextureAttrib::OnStageNode &a, 
-             const TextureAttrib::OnStageNode &b) const {
+operator () (const TextureAttrib::StageNode &a, 
+             const TextureAttrib::StageNode &b) const {
   return a._stage < b._stage;
   return a._stage < b._stage;
 }
 }

+ 186 - 273
panda/src/pgraph/textureAttrib.cxx

@@ -20,6 +20,7 @@
 #include "datagram.h"
 #include "datagram.h"
 #include "datagramIterator.h"
 #include "datagramIterator.h"
 #include "dcast.h"
 #include "dcast.h"
+#include "textureStagePool.h"
 
 
 CPT(RenderAttrib) TextureAttrib::_empty_attrib;
 CPT(RenderAttrib) TextureAttrib::_empty_attrib;
 CPT(RenderAttrib) TextureAttrib::_all_off_attrib;
 CPT(RenderAttrib) TextureAttrib::_all_off_attrib;
@@ -108,10 +109,9 @@ make_default() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int TextureAttrib::
 int TextureAttrib::
 find_on_stage(const TextureStage *stage) const {
 find_on_stage(const TextureStage *stage) const {
-  for (int n = 0; n < (int)_on_stages.size(); ++n) {
-    if (_on_stages[n]._stage == stage) {
-      return n;
-    }
+  Stages::const_iterator si = _on_stages.find(StageNode(stage));
+  if (si != _on_stages.end()) {
+    return (int)(si - _on_stages.begin());
   }
   }
 
 
   return -1;
   return -1;
@@ -125,37 +125,15 @@ find_on_stage(const TextureStage *stage) const {
 //               turned on by this attrib.
 //               turned on by this attrib.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) TextureAttrib::
 CPT(RenderAttrib) TextureAttrib::
-add_on_stage(TextureStage *stage, Texture *tex) const {
+add_on_stage(TextureStage *stage, Texture *tex, int override) const {
   TextureAttrib *attrib = new TextureAttrib(*this);
   TextureAttrib *attrib = new TextureAttrib(*this);
-  pair<OnTextures::iterator, bool> insert_result = 
-    attrib->_on_textures.insert(OnTextures::value_type(stage, tex));
-  if (insert_result.second) {
-    // If the insert was successful--we have added a new stage that
-    // wasn't present before--then add the stage to the linear list
-    // also.
-    attrib->_on_stages.push_back(OnStageNode(stage, attrib->_next_implicit_sort));
-    ++(attrib->_next_implicit_sort);
-
-    // Also ensure it is removed from the off_stages list.
-    attrib->_off_stages.erase(stage);
-
-  } else {
-    // If the insert was unsuccessful, it means there was already a
-    // definition for that stage.  Replace it.
-    (*insert_result.first).second = tex;
-
-    // Also update the implicit sort.
-    OnStages::iterator si;
-    for (si = attrib->_on_stages.begin(); si != attrib->_on_stages.end(); ++si) {
-      if ((*si)._stage == stage) {
-        (*si)._implicit_sort = attrib->_next_implicit_sort;
-        ++(attrib->_next_implicit_sort);
-        break;
-      }
-    }
-  }
+  Stages::iterator si = attrib->_on_stages.insert(StageNode(stage)).first;
+  (*si)._override = override;
+  (*si)._texture = tex;
+  (*si)._implicit_sort = attrib->_next_implicit_sort;
+  ++(attrib->_next_implicit_sort);
 
 
-  // In either case, we now need to re-sort the attrib list.
+  // We now need to re-sort the attrib list.
   attrib->_sort_seq = UpdateSeq::old();
   attrib->_sort_seq = UpdateSeq::old();
   attrib->_filtered_seq = UpdateSeq::old();
   attrib->_filtered_seq = UpdateSeq::old();
 
 
@@ -173,17 +151,9 @@ CPT(RenderAttrib) TextureAttrib::
 remove_on_stage(TextureStage *stage) const {
 remove_on_stage(TextureStage *stage) const {
   TextureAttrib *attrib = new TextureAttrib(*this);
   TextureAttrib *attrib = new TextureAttrib(*this);
 
 
-  OnTextures::iterator ti = attrib->_on_textures.find(stage);
-  if (ti != attrib->_on_textures.end()) {
-    attrib->_on_textures.erase(ti);
-    
-    OnStages::iterator si;
-    for (si = attrib->_on_stages.begin(); si != attrib->_on_stages.end(); ++si) {
-      if ((*si)._stage == stage) {
-        attrib->_on_stages.erase(si);
-        break;
-      }
-    }
+  Stages::iterator si = attrib->_on_stages.find(StageNode(stage));
+  if (si != attrib->_on_stages.end()) {
+    attrib->_on_stages.erase(si);
 
 
     attrib->_sort_seq = UpdateSeq::old();
     attrib->_sort_seq = UpdateSeq::old();
     attrib->_filtered_seq = UpdateSeq::old();
     attrib->_filtered_seq = UpdateSeq::old();
@@ -200,23 +170,17 @@ remove_on_stage(TextureStage *stage) const {
 //               turned off by this attrib.
 //               turned off by this attrib.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) TextureAttrib::
 CPT(RenderAttrib) TextureAttrib::
-add_off_stage(TextureStage *stage) const {
+add_off_stage(TextureStage *stage, int override) const {
   TextureAttrib *attrib = new TextureAttrib(*this);
   TextureAttrib *attrib = new TextureAttrib(*this);
   if (!_off_all_stages) {
   if (!_off_all_stages) {
-    attrib->_off_stages.insert(stage);
+    StageNode sn(stage);
+    Stages::iterator sfi = attrib->_off_stages.insert(sn).first;
+    (*sfi)._override = override;
 
 
     // Also ensure it is removed from the on_stages list.
     // Also ensure it is removed from the on_stages list.
-    OnTextures::iterator ti = attrib->_on_textures.find(stage);
-    if (ti != attrib->_on_textures.end()) {
-      attrib->_on_textures.erase(ti);
-    
-      OnStages::iterator si;
-      for (si = attrib->_on_stages.begin(); si != attrib->_on_stages.end(); ++si) {
-        if ((*si)._stage == stage) {
-          attrib->_on_stages.erase(si);
-          break;
-        }
-      }
+    Stages::iterator si = attrib->_on_stages.find(sn);
+    if (si != attrib->_on_stages.end()) {
+      attrib->_on_stages.erase(si);
       attrib->_sort_seq = UpdateSeq::old();
       attrib->_sort_seq = UpdateSeq::old();
       attrib->_filtered_seq = UpdateSeq::old();
       attrib->_filtered_seq = UpdateSeq::old();
     }
     }
@@ -234,7 +198,7 @@ add_off_stage(TextureStage *stage) const {
 CPT(RenderAttrib) TextureAttrib::
 CPT(RenderAttrib) TextureAttrib::
 remove_off_stage(TextureStage *stage) const {
 remove_off_stage(TextureStage *stage) const {
   TextureAttrib *attrib = new TextureAttrib(*this);
   TextureAttrib *attrib = new TextureAttrib(*this);
-  attrib->_off_stages.erase(stage);
+  attrib->_off_stages.erase(StageNode(stage));
   return return_new(attrib);
   return return_new(attrib);
 }
 }
 
 
@@ -253,41 +217,35 @@ unify_texture_stages(TextureStage *stage) const {
   attrib->_off_all_stages = _off_all_stages;
   attrib->_off_all_stages = _off_all_stages;
   bool any_changed = false;
   bool any_changed = false;
 
 
-  OnStages::const_iterator si;
+  Stages::const_iterator si;
   for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {
   for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {
     TextureStage *this_stage = (*si)._stage;
     TextureStage *this_stage = (*si)._stage;
-    Texture *tex = get_on_texture(this_stage);
-    nassertr(tex != (Texture *)NULL, this);
 
 
     if (this_stage->get_name() == stage->get_name()) {
     if (this_stage->get_name() == stage->get_name()) {
       this_stage = stage;
       this_stage = stage;
       any_changed = true;
       any_changed = true;
     }
     }
 
 
-    bool inserted = attrib->_on_textures.insert(OnTextures::value_type(this_stage, tex)).second;
-    if (inserted) {
-      // If the texture was successfully inserted, it was the first
-      // appearance of this stage.  Add it to the on_stages list.
-      attrib->_on_stages.push_back(OnStageNode(this_stage, (*si)._implicit_sort));
-    } else {
-      // If the texture was not successfully inserted, it was a
-      // duplicate texture stage.  This should only be possible if we
-      // have just collapsed a stage.
-      nassertr(this_stage == stage, this);
-    }
+    Stages::iterator osi = attrib->_on_stages.insert(StageNode(this_stage)).first;
+    (*osi)._texture = (*si)._texture;
+    (*osi)._ff_tc_index = (*si)._ff_tc_index;
+    (*osi)._implicit_sort = (*si)._implicit_sort;
+    (*osi)._override = (*si)._override;
   }
   }
 
 
   attrib->_next_implicit_sort = _next_implicit_sort;
   attrib->_next_implicit_sort = _next_implicit_sort;
 
 
-  OffStages::const_iterator fsi;
+  Stages::const_iterator fsi;
   for (fsi = _off_stages.begin(); fsi != _off_stages.end(); ++fsi) {
   for (fsi = _off_stages.begin(); fsi != _off_stages.end(); ++fsi) {
-    if ((*fsi) != stage && 
-        (*fsi)->get_name() == stage->get_name()) {
-      attrib->_off_stages.insert(stage);
+    TextureStage *this_stage = (*fsi)._stage;
+
+    if (this_stage != stage && 
+        this_stage->get_name() == stage->get_name()) {
+      this_stage = stage;
       any_changed = true;
       any_changed = true;
-    } else {
-      attrib->_off_stages.insert(*fsi);
     }
     }
+
+    attrib->_off_stages.insert(StageNode(this_stage));
   }
   }
 
 
   if (!any_changed) {
   if (!any_changed) {
@@ -330,7 +288,7 @@ filter_to_max(int max_texture_stages) const {
   // case of equal priority, we prefer the stage with the lower sort.
   // case of equal priority, we prefer the stage with the lower sort.
   check_sorted();
   check_sorted();
 
 
-  OnStages priority_stages = _on_stages;
+  RenderStages priority_stages = _render_stages;
 
 
   // This sort function uses the STL function object defined above.
   // This sort function uses the STL function object defined above.
   sort(priority_stages.begin(), priority_stages.end(), 
   sort(priority_stages.begin(), priority_stages.end(), 
@@ -343,13 +301,11 @@ filter_to_max(int max_texture_stages) const {
   // And create a new attrib reflecting these stages.
   // And create a new attrib reflecting these stages.
   PT(TextureAttrib) attrib = new TextureAttrib;
   PT(TextureAttrib) attrib = new TextureAttrib;
 
 
-  OnStages::const_iterator si;
-  for (si = priority_stages.begin(); si != priority_stages.end(); ++si) {
-    TextureStage *stage = (*si)._stage;
-    attrib->_on_textures[stage] = get_on_texture(stage);
+  RenderStages::const_iterator ri;
+  for (ri = priority_stages.begin(); ri != priority_stages.end(); ++ri) {
+    attrib->_on_stages.insert(*(*ri));
   }
   }
 
 
-  attrib->_on_stages.swap(priority_stages);
   attrib->_next_implicit_sort = _next_implicit_sort;
   attrib->_next_implicit_sort = _next_implicit_sort;
 
 
   CPT(RenderAttrib) new_attrib = return_new(attrib);
   CPT(RenderAttrib) new_attrib = return_new(attrib);
@@ -388,10 +344,13 @@ output(ostream &out) const {
 
 
   } else {
   } else {
     out << "off";
     out << "off";
-    OffStages::const_iterator fi;
+    Stages::const_iterator fi;
     for (fi = _off_stages.begin(); fi != _off_stages.end(); ++fi) {
     for (fi = _off_stages.begin(); fi != _off_stages.end(); ++fi) {
-      TextureStage *stage = (*fi);
+      TextureStage *stage = (*fi)._stage;
       out << " " << stage->get_name();
       out << " " << stage->get_name();
+      if ((*fi)._override != 0) {
+        out << "^" << (*fi)._override;
+      }
     }
     }
 
 
     if (!_on_stages.empty()) {
     if (!_on_stages.empty()) {
@@ -399,16 +358,19 @@ output(ostream &out) const {
     }
     }
   }
   }
     
     
-  OnStages::const_iterator si;
-  for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {
-    TextureStage *stage = (*si)._stage;
-    OnTextures::const_iterator ti = _on_textures.find(stage);
-    if (ti != _on_textures.end()) {
-      Texture *tex = (*ti).second;
+  RenderStages::const_iterator ri;
+  for (ri = _render_stages.begin(); ri != _render_stages.end(); ++ri) {
+    const StageNode &sn = *(*ri);
+    TextureStage *stage = sn._stage;
+    Texture *tex = sn._texture;
+    if (tex != NULL) {
       out << " " << stage->get_name() << ":" << tex->get_name();
       out << " " << stage->get_name() << ":" << tex->get_name();
     } else {
     } else {
       out << " " << stage->get_name();
       out << " " << stage->get_name();
     }
     }
+    if (sn._override != 0) {
+      out << "^" << sn._override;
+    }
   }
   }
 }
 }
 
 
@@ -423,9 +385,9 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool TextureAttrib::
 bool TextureAttrib::
 has_cull_callback() const {
 has_cull_callback() const {
-  OnTextures::const_iterator nti;
-  for (nti = _on_textures.begin(); nti != _on_textures.end(); ++nti) {
-    Texture *texture = (*nti).second;
+  Stages::const_iterator si;
+  for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {
+    Texture *texture = (*si)._texture;
     if (texture->has_cull_callback()) {
     if (texture->has_cull_callback()) {
       return true;
       return true;
     }
     }
@@ -449,9 +411,9 @@ has_cull_callback() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool TextureAttrib::
 bool TextureAttrib::
 cull_callback(CullTraverser *trav, const CullTraverserData &data) const {
 cull_callback(CullTraverser *trav, const CullTraverserData &data) const {
-  OnTextures::const_iterator nti;
-  for (nti = _on_textures.begin(); nti != _on_textures.end(); ++nti) {
-    Texture *texture = (*nti).second;
+  Stages::const_iterator si;
+  for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {
+    Texture *texture = (*si)._texture;
     if (!texture->cull_callback(trav, data)) {
     if (!texture->cull_callback(trav, data)) {
       return false;
       return false;
     }
     }
@@ -484,57 +446,22 @@ compare_to_impl(const RenderAttrib *other) const {
     return (int)_off_all_stages - (int)ta->_off_all_stages;
     return (int)_off_all_stages - (int)ta->_off_all_stages;
   }
   }
 
 
-  // First, verify that the texture assigned to each stage is the
-  // same.
-  OnTextures::const_iterator li = _on_textures.begin();
-  OnTextures::const_iterator oli = ta->_on_textures.begin();
+  Stages::const_iterator si = _on_stages.begin();
+  Stages::const_iterator osi = ta->_on_stages.begin();
 
 
-  while (li != _on_textures.end() && oli != ta->_on_textures.end()) {
-    TextureStage *stage = (*li).first;
-    TextureStage *other_stage = (*oli).first;
+  while (si != _on_stages.end() && osi != ta->_on_stages.end()) {
+    TextureStage *stage = (*si)._stage;
+    TextureStage *other_stage = (*osi)._stage;
 
 
     if (stage != other_stage) {
     if (stage != other_stage) {
       return stage < other_stage ? -1 : 1;
       return stage < other_stage ? -1 : 1;
     }
     }
 
 
-    Texture *tex = (*li).second;
-    Texture *other_tex = (*oli).second;
+    Texture *texture = (*si)._texture;
+    Texture *other_texture = (*osi)._texture;
 
 
-    if (tex != other_tex) {
-      return tex < other_tex ? -1 : 1;
-    }
-
-    ++li;
-    ++oli;
-  }
-
-  if (li != _on_textures.end()) {
-    return 1;
-  }
-  if (oli != ta->_on_textures.end()) {
-    return -1;
-  }
-
-  // Then, we also have to check the linear list of texture stages, to
-  // ensure primarily that the implicit sort numbers match between the
-  // two attribs.  (If they did not, then a later call to
-  // texture_stage->set_sort() might make these attribs apply textures
-  // differently, even if they are the same now.)
-  check_sorted();
-  ta->check_sorted();
-
-  nassertr(_on_ptr_stages.size() == _on_stages.size() &&
-           ta->_on_ptr_stages.size() == ta->_on_stages.size(), 0)
-
-  OnStages::const_iterator si = _on_ptr_stages.begin();
-  OnStages::const_iterator osi = ta->_on_ptr_stages.begin();
-
-  while (si != _on_ptr_stages.end() && osi != ta->_on_ptr_stages.end()) {
-    TextureStage *stage = (*si)._stage;
-    TextureStage *other_stage = (*osi)._stage;
-
-    if (stage != other_stage) {
-      return stage < other_stage ? -1 : 1;
+    if (texture != other_texture) {
+      return texture < other_texture ? -1 : 1;
     }
     }
 
 
     int implicit_sort = (*si)._implicit_sort;
     int implicit_sort = (*si)._implicit_sort;
@@ -544,29 +471,43 @@ compare_to_impl(const RenderAttrib *other) const {
       return implicit_sort < other_implicit_sort ? -1 : 1;
       return implicit_sort < other_implicit_sort ? -1 : 1;
     }
     }
 
 
+    int override = (*si)._override;
+    int other_override = (*osi)._override;
+
+    if (override != other_override) {
+      return override < other_override ? -1 : 1;
+    }
+
     ++si;
     ++si;
     ++osi;
     ++osi;
   }
   }
 
 
-  if (si != _on_ptr_stages.end()) {
+  if (si != _on_stages.end()) {
     return 1;
     return 1;
   }
   }
-  if (osi != ta->_on_ptr_stages.end()) {
+  if (osi != ta->_on_stages.end()) {
     return -1;
     return -1;
   }
   }
 
 
   // Finally, ensure that the set of off stages is the same.
   // Finally, ensure that the set of off stages is the same.
-  OffStages::const_iterator fi = _off_stages.begin();
-  OffStages::const_iterator ofi = ta->_off_stages.begin();
+  Stages::const_iterator fi = _off_stages.begin();
+  Stages::const_iterator ofi = ta->_off_stages.begin();
 
 
   while (fi != _off_stages.end() && ofi != ta->_off_stages.end()) {
   while (fi != _off_stages.end() && ofi != ta->_off_stages.end()) {
-    TextureStage *stage = (*fi);
-    TextureStage *other_stage = (*ofi);
+    TextureStage *stage = (*fi)._stage;
+    TextureStage *other_stage = (*ofi)._stage;
 
 
     if (stage != other_stage) {
     if (stage != other_stage) {
       return stage < other_stage ? -1 : 1;
       return stage < other_stage ? -1 : 1;
     }
     }
 
 
+    int override = (*fi)._override;
+    int other_override = (*ofi)._override;
+
+    if (override != other_override) {
+      return override < other_override ? -1 : 1;
+    }
+
     ++fi;
     ++fi;
     ++ofi;
     ++ofi;
   }
   }
@@ -612,145 +553,120 @@ compose_impl(const RenderAttrib *other) const {
   // This is a three-way merge between ai, bi, and ci, except that bi
   // This is a three-way merge between ai, bi, and ci, except that bi
   // and ci should have no intersection and therefore needn't be
   // and ci should have no intersection and therefore needn't be
   // compared to each other.
   // compared to each other.
-  OnTextures::const_iterator ai = _on_textures.begin();
-  OnTextures::const_iterator bi = ta->_on_textures.begin();
-  OffStages::const_iterator ci = ta->_off_stages.begin();
-
-  // TextureStages that are kept from the original attrib are inserted
-  // into a_stages.  Those that are inherited from the secondary
-  // attrib are inserted into b_stages.
-  pset<TextureStage *> a_stages;
-  pset<TextureStage *> b_stages;
+  Stages::const_iterator ai = _on_stages.begin();
+  Stages::const_iterator bi = ta->_on_stages.begin();
+  Stages::const_iterator ci = ta->_off_stages.begin();
 
 
   // Create a new TextureAttrib that will hold the result.
   // Create a new TextureAttrib that will hold the result.
   TextureAttrib *attrib = new TextureAttrib;
   TextureAttrib *attrib = new TextureAttrib;
 
 
-  while (ai != _on_textures.end() && 
-         bi != ta->_on_textures.end() && 
+  while (ai != _on_stages.end() && 
+         bi != ta->_on_stages.end() && 
          ci != ta->_off_stages.end()) {
          ci != ta->_off_stages.end()) {
-    if ((*ai).first < (*bi).first) {
-      if ((*ai).first < (*ci)) {
+    if ((*ai)._stage < (*bi)._stage) {
+      if ((*ai)._stage < (*ci)._stage) {
         // Here is a stage that we have in the original, which is not
         // Here is a stage that we have in the original, which is not
         // present in the secondary.
         // present in the secondary.
-        attrib->_on_textures.insert(attrib->_on_textures.end(), *ai);
-        a_stages.insert((*ai).first);
+        attrib->_on_stages.insert(attrib->_on_stages.end(), *ai);
         ++ai;
         ++ai;
 
 
-      } else if ((*ci) < (*ai).first) {
+      } else if ((*ci)._stage < (*ai)._stage) {
         // Here is a stage that is turned off in the secondary, but
         // Here is a stage that is turned off in the secondary, but
         // was not present in the original.
         // was not present in the original.
         ++ci;
         ++ci;
 
 
-      } else { // (*ci) == (*ai).first
+      } else { // (*ci)._stage == (*ai)._stage
         // Here is a stage that is turned off in the secondary, and
         // Here is a stage that is turned off in the secondary, and
         // was present in the original.
         // was present in the original.
+        if ((*ai)._override > (*ci)._override) {
+          // But never mind, keep it anyway.
+          attrib->_on_stages.insert(attrib->_on_stages.end(), *ai);
+        }
+
         ++ai;
         ++ai;
         ++ci;
         ++ci;
       }
       }
 
 
-    } else if ((*bi).first < (*ai).first) {
+    } else if ((*bi)._stage < (*ai)._stage) {
       // Here is a new stage we have in the secondary, that was not
       // Here is a new stage we have in the secondary, that was not
       // present in the original.
       // present in the original.
-      attrib->_on_textures.insert(attrib->_on_textures.end(), *bi);
-      b_stages.insert((*bi).first);
+      attrib->_on_stages.insert(attrib->_on_stages.end(), *bi);
       ++bi;
       ++bi;
 
 
-    } else {  // (*bi).first == (*ai).first
+    } else {  // (*bi)._stage == (*ai)._stage
       // Here is a stage we have in both.
       // Here is a stage we have in both.
-      attrib->_on_textures.insert(attrib->_on_textures.end(), *bi);
-      b_stages.insert((*bi).first);
+      if ((*ai)._override > (*bi)._override) {
+        attrib->_on_stages.insert(attrib->_on_stages.end(), *ai);
+      } else {
+        attrib->_on_stages.insert(attrib->_on_stages.end(), *bi);
+      }
       ++ai;
       ++ai;
       ++bi;
       ++bi;
     }
     }
   }
   }
 
 
-  while (ai != _on_textures.end() && bi != ta->_on_textures.end()) {
-    if ((*ai).first < (*bi).first) {
+  while (ai != _on_stages.end() && bi != ta->_on_stages.end()) {
+    if ((*ai)._stage < (*bi)._stage) {
       // Here is a stage that we have in the original, which is not
       // Here is a stage that we have in the original, which is not
       // present in the secondary.
       // present in the secondary.
-      attrib->_on_textures.insert(attrib->_on_textures.end(), *ai);
-      a_stages.insert((*ai).first);
+      attrib->_on_stages.insert(attrib->_on_stages.end(), *ai);
       ++ai;
       ++ai;
 
 
-    } else if ((*bi).first < (*ai).first) {
+    } else if ((*bi)._stage < (*ai)._stage) {
       // Here is a new stage we have in the secondary, that was not
       // Here is a new stage we have in the secondary, that was not
       // present in the original.
       // present in the original.
-      attrib->_on_textures.insert(attrib->_on_textures.end(), *bi);
-      b_stages.insert((*bi).first);
+      attrib->_on_stages.insert(attrib->_on_stages.end(), *bi);
       ++bi;
       ++bi;
 
 
     } else {
     } else {
       // Here is a stage we have in both.
       // Here is a stage we have in both.
-      attrib->_on_textures.insert(attrib->_on_textures.end(), *bi);
-      b_stages.insert((*bi).first);
+      if ((*ai)._override > (*bi)._override) {
+        attrib->_on_stages.insert(attrib->_on_stages.end(), *ai);
+      } else {
+        attrib->_on_stages.insert(attrib->_on_stages.end(), *bi);
+      }
       ++ai;
       ++ai;
       ++bi;
       ++bi;
     }
     }
   }
   }
 
 
-  while (ai != _on_textures.end() && ci != ta->_off_stages.end()) {
-    if ((*ai).first < (*ci)) {
+  while (ai != _on_stages.end() && ci != ta->_off_stages.end()) {
+    if ((*ai)._stage < (*ci)._stage) {
       // Here is a stage that we have in the original, which is not
       // Here is a stage that we have in the original, which is not
       // present in the secondary.
       // present in the secondary.
-      attrib->_on_textures.insert(attrib->_on_textures.end(), *ai);
-      a_stages.insert((*ai).first);
+      attrib->_on_stages.insert(attrib->_on_stages.end(), *ai);
       ++ai;
       ++ai;
       
       
-    } else if ((*ci) < (*ai).first) {
+    } else if ((*ci)._stage < (*ai)._stage) {
       // Here is a stage that is turned off in the secondary, but
       // Here is a stage that is turned off in the secondary, but
       // was not present in the original.
       // was not present in the original.
       ++ci;
       ++ci;
       
       
-    } else { // (*ci) == (*ai).first
+    } else { // (*ci)._stage == (*ai)._stage
       // Here is a stage that is turned off in the secondary, and
       // Here is a stage that is turned off in the secondary, and
       // was present in the original.
       // was present in the original.
+      if ((*ai)._override > (*ci)._override) {
+        // But never mind, keep it anyway.
+        attrib->_on_stages.insert(attrib->_on_stages.end(), *ai);
+      }
       ++ai;
       ++ai;
       ++ci;
       ++ci;
     }
     }
   }
   }
 
 
-  while (ai != _on_textures.end()) {
-    attrib->_on_textures.insert(attrib->_on_textures.end(), *ai);
-    a_stages.insert((*ai).first);
+  while (ai != _on_stages.end()) {
+    attrib->_on_stages.insert(attrib->_on_stages.end(), *ai);
     ++ai;
     ++ai;
   }
   }
 
 
-  while (bi != ta->_on_textures.end()) {
-    attrib->_on_textures.insert(attrib->_on_textures.end(), *bi);
-    b_stages.insert((*bi).first);
+  while (bi != ta->_on_stages.end()) {
+    attrib->_on_stages.insert(attrib->_on_stages.end(), *bi);
     ++bi;
     ++bi;
   }
   }
 
 
-  // Now we need to build up the linear list.  We must put this in
-  // order so that the original stages are first, followed by the
-  // secondary stages.  If a texture stage is listed in both, it
-  // receives the secondary sort.
-
-  OnStages::const_iterator asi;
-  for (asi = _on_stages.begin(); asi != _on_stages.end(); ++asi) {
-    TextureStage *stage = (*asi)._stage;
-
-    if (a_stages.find(stage) != a_stages.end()) {
-      // This stage came from the original, and thus receives the
-      // original sort.
-      int implicit_sort = (*asi)._implicit_sort;
-      attrib->_on_stages.push_back(OnStageNode(stage, implicit_sort));
-    }
-  }
-
-  OnStages::const_iterator bsi;
-  for (bsi = ta->_on_stages.begin(); bsi != ta->_on_stages.end(); ++bsi) {
-    TextureStage *stage = (*bsi)._stage;
-
-    if (b_stages.find(stage) != b_stages.end()) {
-      // This stage was inherited from the secondary, and thus
-      // receives the secondary sort.
-      int implicit_sort = _next_implicit_sort + (*bsi)._implicit_sort;
-      attrib->_on_stages.push_back(OnStageNode(stage, implicit_sort));
-    }
-  }
-
   attrib->_next_implicit_sort = _next_implicit_sort + ta->_next_implicit_sort;
   attrib->_next_implicit_sort = _next_implicit_sort + ta->_next_implicit_sort;
+  attrib->_sort_seq = UpdateSeq::old();
+  attrib->_filtered_seq = UpdateSeq::old();
 
 
   return return_new(attrib);
   return return_new(attrib);
 }
 }
@@ -796,23 +712,24 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   // Write the off_stages information
   // Write the off_stages information
   dg.add_bool(_off_all_stages);
   dg.add_bool(_off_all_stages);
   dg.add_uint16(get_num_off_stages());
   dg.add_uint16(get_num_off_stages());
-  OffStages::const_iterator fi;
+  Stages::const_iterator fi;
   for (fi = _off_stages.begin(); fi != _off_stages.end(); ++fi) {
   for (fi = _off_stages.begin(); fi != _off_stages.end(); ++fi) {
-    TextureStage *stage = (*fi);
+    TextureStage *stage = (*fi)._stage;
     manager->write_pointer(dg, stage);
     manager->write_pointer(dg, stage);
   }
   }
 
 
   // Write the on_stages information
   // Write the on_stages information
   dg.add_uint16(get_num_on_stages());
   dg.add_uint16(get_num_on_stages());
-  OnStages::const_iterator si;
+  Stages::const_iterator si;
   for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {
   for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {
     TextureStage *stage = (*si)._stage;
     TextureStage *stage = (*si)._stage;
-    Texture *tex = get_on_texture(stage);
+    Texture *tex = (*si)._texture;
     nassertv(tex != (Texture *)NULL);
     nassertv(tex != (Texture *)NULL);
 
 
     manager->write_pointer(dg, stage);
     manager->write_pointer(dg, stage);
     manager->write_pointer(dg, tex);
     manager->write_pointer(dg, tex);
     dg.add_uint16((*si)._implicit_sort);
     dg.add_uint16((*si)._implicit_sort);
+    dg.add_int32((*si)._override);
   }
   }
 }
 }
 
 
@@ -827,27 +744,33 @@ int TextureAttrib::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = RenderAttrib::complete_pointers(p_list, manager);
   int pi = RenderAttrib::complete_pointers(p_list, manager);
 
 
-  OffStages::iterator ci;
+  Stages::iterator ci;
   for (ci = _off_stages.begin(); ci != _off_stages.end(); ++ci) {
   for (ci = _off_stages.begin(); ci != _off_stages.end(); ++ci) {
     TextureStage *ts = DCAST(TextureStage, p_list[pi++]);
     TextureStage *ts = DCAST(TextureStage, p_list[pi++]);
-    *ci = ts;
+    *ci = StageNode(ts);
   }
   }
 
 
-  size_t sn = 0;
-  while (sn < _on_stages.size()) {
-    TextureStage *ts = DCAST(TextureStage, p_list[pi++]);
+  size_t sni = 0;
+  while (sni < _on_stages.size()) {
+    // Filter the TextureStage through the TextureStagePool.
+    PT(TextureStage) ts = DCAST(TextureStage, p_list[pi++]);
+    ts = TextureStagePool::get_stage(ts);
+
+    // The Texture pointer filters itself through the TexturePool, so
+    // we don't have to do anything special here.
     Texture *tex = DCAST(Texture, p_list[pi++]);
     Texture *tex = DCAST(Texture, p_list[pi++]);
     
     
     if (tex != (Texture *)NULL) {
     if (tex != (Texture *)NULL) {
-      _on_textures[ts] = tex;
-      _on_stages[sn]._stage = ts;
-      ++sn;
+      StageNode &sn = _on_stages[sni];
+      sn._stage = ts;
+      sn._texture = tex;
+      ++sni;
       
       
     } else {
     } else {
       // If we couldn't load a texture pointer, turn off that
       // If we couldn't load a texture pointer, turn off that
       // particular texture stage.
       // particular texture stage.
-      _off_stages.push_back(ts);
-      _on_stages.erase(_on_stages.begin() + sn);
+      _off_stages.push_back(StageNode(ts));
+      _on_stages.erase(_on_stages.begin() + sni);
     }
     }
   }
   }
   _sort_seq = UpdateSeq::old();
   _sort_seq = UpdateSeq::old();
@@ -897,7 +820,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _off_stages.reserve(num_off_stages);
   _off_stages.reserve(num_off_stages);
   for (i = 0; i < num_off_stages; i++) {
   for (i = 0; i < num_off_stages; i++) {
     manager->read_pointer(scan);
     manager->read_pointer(scan);
-    _off_stages.push_back(NULL);
+    _off_stages.push_back(StageNode(NULL));
   }
   }
 
 
   // Read the _on_stages data.
   // Read the _on_stages data.
@@ -917,8 +840,14 @@ fillin(DatagramIterator &scan, BamReader *manager) {
     } else {
     } else {
       implicit_sort = (unsigned int)i;
       implicit_sort = (unsigned int)i;
     }
     }
+    int override = 0;
+    if (manager->get_file_minor_ver() >= 23) {
+      override = scan.get_int32();
+    }
+
     _next_implicit_sort = max(_next_implicit_sort, implicit_sort + 1);
     _next_implicit_sort = max(_next_implicit_sort, implicit_sort + 1);
-    _on_stages.push_back(OnStageNode(NULL, implicit_sort));
+    _on_stages.push_back(StageNode(NULL, _next_implicit_sort, override));
+    ++_next_implicit_sort;
   }
   }
 }
 }
 
 
@@ -931,25 +860,17 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TextureAttrib::
 void TextureAttrib::
 sort_on_stages() {
 sort_on_stages() {
-  // First, we have to build up the tc_index mapping.  We need a
-  // unique number for each different texcoord name for the various
-  // TextureStages.
-
-  // It's important that this assignment not be based on the whims of
-  // render order--it mustn't change arbitrarily--so we must first
-  // sort the on_stages list into pointer order for this purpose.
-  _on_ptr_stages = _on_stages;
-  sort(_on_ptr_stages.begin(), _on_ptr_stages.end(), CompareTextureStagePointer());
-
   typedef pmap<const InternalName *, int> UsedTexcoordIndex;
   typedef pmap<const InternalName *, int> UsedTexcoordIndex;
   UsedTexcoordIndex used_texcoord_index;
   UsedTexcoordIndex used_texcoord_index;
 
 
-  typedef pmap<const TextureStage *, int> TexcoordMap;
-  TexcoordMap tc_map;
+  _render_stages.clear();
+  _render_ff_stages.clear();
 
 
-  OnStages::const_iterator si;
-  for (si = _on_ptr_stages.begin(); si != _on_ptr_stages.end(); ++si) {
-    TextureStage *stage = (*si)._stage;
+  Stages::iterator si;
+  for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {
+    StageNode &sn = (*si);
+    TextureStage *stage = sn._stage;
+    nassertv(stage != NULL);
     if (stage->is_fixed_function()) {
     if (stage->is_fixed_function()) {
       const InternalName *name = stage->get_texcoord_name();
       const InternalName *name = stage->get_texcoord_name();
 
 
@@ -958,32 +879,24 @@ sort_on_stages() {
       // particular texcoord name; otherwise, it will return the same
       // particular texcoord name; otherwise, it will return the same
       // index number it returned before.
       // index number it returned before.
       UsedTexcoordIndex::iterator ti = used_texcoord_index.insert(UsedTexcoordIndex::value_type(name, (int)used_texcoord_index.size())).first;
       UsedTexcoordIndex::iterator ti = used_texcoord_index.insert(UsedTexcoordIndex::value_type(name, (int)used_texcoord_index.size())).first;
-      int texcoord_index = (*ti).second;
+      (*si)._ff_tc_index = (*ti).second;
 
 
-      tc_map[stage] = texcoord_index;
+      _render_ff_stages.push_back(&sn);
+    } else {
+      (*si)._ff_tc_index = -1;
     }
     }
-  }
-
-  // Now we can sort the on_stages list into render order.
-  sort(_on_stages.begin(), _on_stages.end(), CompareTextureStageSort());
-
-  _on_ff_stages.clear();
-  _ff_tc_index.clear();
 
 
-  for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {
-    TextureStage *stage = (*si)._stage;
-    if (stage->is_fixed_function()) {
-      _on_ff_stages.push_back(*si);
-      int texcoord_index = tc_map[stage];
-      _ff_tc_index.push_back(texcoord_index);
-    }
+    _render_stages.push_back(&sn);
   }
   }
 
 
-  // We'd like to clear the _filtered map, in case the priority orders
-  // have changed as well, but we can't do that here: too dangerous.
-  // Clearing _filtered might cause TextureAttribs to be deleted, and
-  // hence removed from the map that we might be in the middle of
-  // traversing!
+  sort(_render_stages.begin(), _render_stages.end(), CompareTextureStageSort());
+  sort(_render_ff_stages.begin(), _render_ff_stages.end(), CompareTextureStageSort());
+
+  // We'd like to clear the _filtered map, in case the TextureStage
+  // priority values have changed as well, but we can't do that here:
+  // it's too dangerous.  Clearing _filtered might cause
+  // TextureAttribs to be deleted, and hence removed from the map that
+  // we might be in the middle of traversing!
 
 
   _sort_seq = TextureStage::get_sort_seq();
   _sort_seq = TextureStage::get_sort_seq();
 }
 }

+ 19 - 17
panda/src/pgraph/textureAttrib.h

@@ -72,9 +72,9 @@ PUBLISHED:
 
 
   INLINE bool is_identity() const;
   INLINE bool is_identity() const;
 
 
-  CPT(RenderAttrib) add_on_stage(TextureStage *stage, Texture *tex) const;
+  CPT(RenderAttrib) add_on_stage(TextureStage *stage, Texture *tex, int override = 0) const;
   CPT(RenderAttrib) remove_on_stage(TextureStage *stage) const;
   CPT(RenderAttrib) remove_on_stage(TextureStage *stage) const;
-  CPT(RenderAttrib) add_off_stage(TextureStage *stage) const;
+  CPT(RenderAttrib) add_off_stage(TextureStage *stage, int override = 0) const;
   CPT(RenderAttrib) remove_off_stage(TextureStage *stage) const;
   CPT(RenderAttrib) remove_off_stage(TextureStage *stage) const;
   CPT(RenderAttrib) unify_texture_stages(TextureStage *stage) const;
   CPT(RenderAttrib) unify_texture_stages(TextureStage *stage) const;
 
 
@@ -96,43 +96,45 @@ private:
   void sort_on_stages();
   void sort_on_stages();
 
 
 private:
 private:
-  class OnStageNode {
+  class StageNode {
   public:
   public:
-    INLINE OnStageNode(TextureStage *stage, unsigned int implicit_sort);
+    INLINE StageNode(const TextureStage *stage, 
+                     unsigned int implicit_sort = 0,
+                     int override = 0);
 
 
     PT(TextureStage) _stage;
     PT(TextureStage) _stage;
+    PT(Texture) _texture;
+    int _ff_tc_index;
     unsigned int _implicit_sort;
     unsigned int _implicit_sort;
+    int _override;
   };
   };
 
 
   class CompareTextureStagePriorities {
   class CompareTextureStagePriorities {
   public:
   public:
-    INLINE bool operator () (const TextureAttrib::OnStageNode &a, const TextureAttrib::OnStageNode &b) const;
+    INLINE bool operator () (const TextureAttrib::StageNode *a, const TextureAttrib::StageNode *b) const;
   };
   };
 
 
   class CompareTextureStageSort {
   class CompareTextureStageSort {
   public:
   public:
-    INLINE bool operator () (const TextureAttrib::OnStageNode &a, const TextureAttrib::OnStageNode &b) const;
+    INLINE bool operator () (const TextureAttrib::StageNode *a, const TextureAttrib::StageNode *b) const;
   };
   };
 
 
   class CompareTextureStagePointer {
   class CompareTextureStagePointer {
   public:
   public:
-    INLINE bool operator () (const TextureAttrib::OnStageNode &a, const TextureAttrib::OnStageNode &b) const;
+    INLINE bool operator () (const TextureAttrib::StageNode &a, const TextureAttrib::StageNode &b) const;
   };
   };
 
 
-  typedef pvector<OnStageNode> OnStages;
-  OnStages _on_stages;      // list of all "on" stages, sorted in render order.
-  OnStages _on_ptr_stages;  // above, sorted in pointer order.
-  OnStages _on_ff_stages;   // fixed-function stages only, in render order.
-  vector_int _ff_tc_index;
+  typedef ov_set<StageNode, CompareTextureStagePointer> Stages;
+  Stages _on_stages;  // set of all "on" stages, indexed by pointer.
+
+  typedef pvector<StageNode *> RenderStages;
+  RenderStages _render_stages;      // all "on" stages, sorted in render order.
+  RenderStages _render_ff_stages;   // fixed-function stages only, in render order.
   unsigned int _next_implicit_sort;
   unsigned int _next_implicit_sort;
   
   
-  typedef ov_set<PT(TextureStage) > OffStages;
-  OffStages _off_stages;
+  Stages _off_stages;
   bool _off_all_stages;
   bool _off_all_stages;
 
 
-  typedef pmap<PT(TextureStage), PT(Texture) > OnTextures;
-  OnTextures _on_textures;
-
   typedef pmap< int, CPT(TextureAttrib) > Filtered;
   typedef pmap< int, CPT(TextureAttrib) > Filtered;
   Filtered _filtered;
   Filtered _filtered;
 
 

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

@@ -44,3 +44,14 @@ operator + (const TextureStageCollection &other) const {
   a += other;
   a += other;
   return a;
   return a;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureStageCollection::CompareTextureStageSort::operator ()
+//       Access: Public
+//  Description: This STL function object is used to sort a list of
+//               texture stages in order by sort.
+////////////////////////////////////////////////////////////////////
+INLINE bool TextureStageCollection::CompareTextureStageSort::
+operator () (const TextureStage *a, const TextureStage *b) const {
+  return a->get_sort() < b->get_sort();
+}

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

@@ -272,7 +272,7 @@ size() const {
 void TextureStageCollection::
 void TextureStageCollection::
 sort() {
 sort() {
   ::sort(_texture_stages.begin(), _texture_stages.end(),
   ::sort(_texture_stages.begin(), _texture_stages.end(),
-         IndirectLess<TextureStage>());
+         CompareTextureStageSort());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 6 - 0
panda/src/pgraph/textureStageCollection.h

@@ -56,6 +56,12 @@ PUBLISHED:
 private:
 private:
   typedef PTA(PT(TextureStage)) TextureStages;
   typedef PTA(PT(TextureStage)) TextureStages;
   TextureStages _texture_stages;
   TextureStages _texture_stages;
+
+  class CompareTextureStageSort {
+  public:
+    INLINE bool operator () (const TextureStage *a, const TextureStage *b) const;
+  };
+
 };
 };
 
 
 INLINE ostream &operator << (ostream &out, const TextureStageCollection &col) {
 INLINE ostream &operator << (ostream &out, const TextureStageCollection &col) {

+ 2 - 1
panda/src/putil/bam.h

@@ -33,7 +33,7 @@ static const unsigned short _bam_major_ver = 6;
 // Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData.
 // Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData.
 
 
 static const unsigned short _bam_first_minor_ver = 14;
 static const unsigned short _bam_first_minor_ver = 14;
-static const unsigned short _bam_minor_ver = 22;
+static const unsigned short _bam_minor_ver = 23;
 // Bumped to minor version 14 on 12/19/07 to change default ColorAttrib.
 // Bumped to minor version 14 on 12/19/07 to change default ColorAttrib.
 // Bumped to minor version 15 on 4/9/08 to add TextureAttrib::_implicit_sort.
 // Bumped to minor version 15 on 4/9/08 to add TextureAttrib::_implicit_sort.
 // Bumped to minor version 16 on 5/13/08 to add Texture::_quality_level.
 // Bumped to minor version 16 on 5/13/08 to add Texture::_quality_level.
@@ -43,6 +43,7 @@ static const unsigned short _bam_minor_ver = 22;
 // Bumped to minor version 20 on 4/21/09 to add MovingPartBase::_forced_channel.
 // Bumped to minor version 20 on 4/21/09 to add MovingPartBase::_forced_channel.
 // Bumped to minor version 21 on 2/26/08 to add BamEnums::BamObjectCode.
 // Bumped to minor version 21 on 2/26/08 to add BamEnums::BamObjectCode.
 // Bumped to minor version 22 on 7/31/09 to add UvScrollNode R speed.
 // Bumped to minor version 22 on 7/31/09 to add UvScrollNode R speed.
+// Bumped to minor version 23 on 5/4/10 to add internal TextureAttrib overrides.
 
 
 
 
 #endif
 #endif