Browse Source

*** empty log message ***

David Rose 25 years ago
parent
commit
872a902de4
41 changed files with 1188 additions and 232 deletions
  1. 4 0
      panda/src/cull/config_cull.cxx
  2. 31 1
      panda/src/cull/cullTraverser.I
  3. 134 16
      panda/src/cull/cullTraverser.cxx
  4. 13 3
      panda/src/cull/cullTraverser.h
  5. 57 22
      panda/src/cull/geomBin.I
  6. 182 41
      panda/src/cull/geomBin.cxx
  7. 26 9
      panda/src/cull/geomBin.h
  8. 6 6
      panda/src/cull/geomBinAttribute.I
  9. 6 6
      panda/src/cull/geomBinAttribute.cxx
  10. 4 4
      panda/src/cull/geomBinAttribute.h
  11. 2 2
      panda/src/cull/geomBinBackToFront.I
  12. 3 4
      panda/src/cull/geomBinBackToFront.h
  13. 2 2
      panda/src/cull/geomBinFixed.I
  14. 3 4
      panda/src/cull/geomBinFixed.h
  15. 2 30
      panda/src/cull/geomBinGroup.I
  16. 148 0
      panda/src/cull/geomBinGroup.cxx
  17. 10 5
      panda/src/cull/geomBinGroup.h
  18. 10 5
      panda/src/cull/geomBinNormal.cxx
  19. 3 3
      panda/src/cull/geomBinNormal.h
  20. 6 6
      panda/src/cull/geomBinTransition.I
  21. 62 6
      panda/src/cull/geomBinTransition.cxx
  22. 16 6
      panda/src/cull/geomBinTransition.h
  23. 2 2
      panda/src/cull/geomBinUnsorted.I
  24. 3 3
      panda/src/cull/geomBinUnsorted.h
  25. 45 15
      panda/src/doc/eggSyntax.txt
  26. 64 8
      panda/src/egg/eggAlphaMode.I
  27. 12 0
      panda/src/egg/eggAlphaMode.cxx
  28. 9 3
      panda/src/egg/eggAlphaMode.h
  29. 51 0
      panda/src/egg/eggGroup.cxx
  30. 4 0
      panda/src/egg/eggGroup.h
  31. 53 0
      panda/src/egg/eggNode.cxx
  32. 5 0
      panda/src/egg/eggNode.h
  33. 60 0
      panda/src/egg/eggPrimitive.cxx
  34. 4 0
      panda/src/egg/eggPrimitive.h
  35. 16 5
      panda/src/egg/parser.yxx
  36. 1 1
      panda/src/egg2sg/Sources.pp
  37. 39 7
      panda/src/egg2sg/eggLoader.cxx
  38. 16 0
      panda/src/express/typeHandle.cxx
  39. 8 7
      panda/src/sgattrib/config_sgattrib.cxx
  40. 57 0
      panda/src/sgattrib/depthWriteTransition.cxx
  41. 9 0
      panda/src/sgattrib/depthWriteTransition.h

+ 4 - 0
panda/src/cull/config_cull.cxx

@@ -31,6 +31,10 @@ ConfigureFn(config_cull) {
   GeomBinAttribute::init_type();
   GeomBinFixed::init_type();
   DirectRenderTransition::init_type();
+
+  //Registration of writeable object's creation
+  //functions with BamReader's factory
+  GeomBinTransition::register_with_read_factory();
 }
 
 // Set this true to force all of the caching to blow itself away every

+ 31 - 1
panda/src/cull/cullTraverser.I

@@ -20,7 +20,8 @@
 INLINE void CullTraverser::
 set_default_bin(GeomBin *bin) {
   nassertv(bin != (GeomBin *)NULL);
-  bin->attach_to(this, bin->get_sort());
+  nassertv(bin->is_attached());
+  nassertv(bin->get_traverser() == this);
   _default_bin = bin;
 }
 
@@ -36,6 +37,35 @@ get_default_bin() const {
   return _default_bin;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::has_bin
+//       Access: Public
+//  Description: Returns true if a bin by the given name has been
+//               attached to the CullTraverser, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool CullTraverser::
+has_bin(const string &name) const {
+  return (_toplevel_bins.count(name) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::get_bin
+//       Access: Public
+//  Description: Returns the GeomBin that was previously attached to
+//               the CullTraverser that shares the indicated name, or
+//               the default bin if no GeomBin with a matching name
+//               was added.
+////////////////////////////////////////////////////////////////////
+INLINE GeomBin *CullTraverser::
+get_bin(const string &name) const {
+  ToplevelBins::const_iterator tbi;
+  tbi = _toplevel_bins.find(name);
+  if (tbi == _toplevel_bins.end()) {
+    return get_default_bin();
+  }
+  return (*tbi).second;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::draw_geom
 //       Access: Public

+ 134 - 16
panda/src/cull/cullTraverser.cxx

@@ -8,6 +8,7 @@
 #include "geomBinAttribute.h"
 #include "cullStateSubtree.h"
 #include "geomBinNormal.h"
+#include "geomBinFixed.h"
 #include "directRenderTransition.h"
 #include "config_cull.h"
 
@@ -42,7 +43,15 @@ CullTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type,
 	      const ArcChain &arc_chain) :
   RenderTraverser(gsg, graph_type, arc_chain)
 {
-  _default_bin = new GeomBinNormal("default", this);
+  GeomBinNormal *default_bin = new GeomBinNormal("default");
+  GeomBinFixed *fixed = new GeomBinFixed("fixed");
+  fixed->set_sort(30);
+
+  default_bin->set_traverser(this);
+  fixed->set_traverser(this);
+
+  _default_bin = default_bin;
+
   _nested_count = 0;
 }
 
@@ -56,12 +65,14 @@ CullTraverser::
   // We should detach each of our associated bins when we destruct.
   // We can't just run a for loop, because this is a self-modifying
   // operation.
-  while (!_bins.empty()) {
-    GeomBin *bin = (*_bins.begin());
+  while (!_toplevel_bins.empty()) {
+    GeomBin *bin = (*_toplevel_bins.begin()).second;
     nassertv(bin != (GeomBin *)NULL);
     nassertv(bin->get_traverser() == this);
-    bin->detach();
+    bin->clear_traverser();
   }
+
+  nassertv(_sub_bins.empty());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -86,7 +97,8 @@ traverse(Node *root,
       << *root << "\n";
   }
 
-  nassertv(!_bins.empty());
+  nassertv(!_toplevel_bins.empty());
+  nassertv(!_sub_bins.empty());
 
   bool is_initial = (_nested_count == 0);
   if (is_initial) {
@@ -184,9 +196,9 @@ write(ostream &out, int indent_level) const {
   }
   */
 
-  Bins::const_iterator bi;
-  for (bi = _bins.begin(); bi != _bins.end(); ++bi) {
-    (*bi)->write(out, indent_level);
+  ToplevelBins::const_iterator tbi;
+  for (tbi = _toplevel_bins.begin(); tbi != _toplevel_bins.end(); ++tbi) {
+    (*tbi).second->write(out, indent_level);
   }
   _lookup.write(out, indent_level);
 }
@@ -215,9 +227,9 @@ draw() {
       << " nonempty states of " << _states.size() << " total.\n";
   }
 
-  Bins::iterator bi;
-  for (bi = _bins.begin(); bi != _bins.end(); ++bi) {
-    (*bi)->clear_current_states();
+  SubBins::const_iterator sbi;
+  for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
+    (*sbi).second->clear_current_states();
   }
 
   States::iterator si;
@@ -233,7 +245,7 @@ draw() {
       const GeomBinAttribute *bin_attrib;
       if (get_attribute_into(bin_attrib, cs->get_attributes(),
 			     GeomBinTransition::get_class_type())) {
-	requested_bin = bin_attrib->get_bin();
+	requested_bin = get_bin(bin_attrib->get_bin());
 	draw_order = bin_attrib->get_draw_order();
       }
 
@@ -244,10 +256,10 @@ draw() {
   if (_gsg != (GraphicsStateGuardian *)NULL) {
     if (cull_cat.is_debug()) {
       cull_cat.debug()
-	<< "Drawing " << _bins.size() << " bins.\n";
+	<< "Drawing " << _sub_bins.size() << " bins.\n";
     }
-    for (bi = _bins.begin(); bi != _bins.end(); ++bi) {
-      (*bi)->draw(this);
+    for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
+      (*sbi).second->draw(this);
     }
   }
 }
@@ -394,7 +406,6 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
   bool is_geom = node->is_of_type(GeomNode::get_class_type());
   bool node_has_sub_render = node->has_sub_render();
   int arc_num_sub_render = arc->get_num_sub_render_trans();
-  bool has_direct_render = arc->has_transition(DirectRenderTransition::get_class_type());
   bool has_decal = arc->has_transition(DecalTransition::get_class_type());
 
   if (has_decal) {
@@ -403,6 +414,9 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
     arc_num_sub_render--;
   }
 
+  bool has_direct_render = 
+    arc->has_transition(DirectRenderTransition::get_class_type());
+
 #ifndef NDEBUG
   if (support_decals != SD_on) {
     has_direct_render = has_direct_render && !has_decal;
@@ -533,3 +547,107 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
 
   return true;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::attach_toplevel_bin
+//       Access: Private
+//  Description: This is intended to be called only by
+//               GeomBin::attach().  It stores the bin in the
+//               appropriate structures within the traverser, as a
+//               toplevel bin, e.g. a bin that may be referenced by
+//               name from outside the traverser, or one that geometry
+//               may be explicitly assigned to by name.
+////////////////////////////////////////////////////////////////////
+void CullTraverser::
+attach_toplevel_bin(GeomBin *bin) {
+  if (cull_cat.is_debug()) {
+    cull_cat.debug()
+      << "Attaching toplevel bin " << *bin << "\n";
+  }
+
+  // Insert the new bin by name.
+  bool inserted = 
+    _toplevel_bins.insert(ToplevelBins::value_type(bin->get_name(), bin)).second;
+
+  // If this assertion fails, there was already a bin by the same name
+  // in this traverser, an error condition.
+  nassertv(inserted);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::attach_sub_bin
+//       Access: Private
+//  Description: This is intended to be called only by
+//               GeomBin::attach().  It stores the bin in the
+//               appropriate structures within the traverser, as a
+//               sub bin, e.g. a bin that may have geometry assigned
+//               to it, and may therefore be rendered.
+//
+//               In most cases, a toplevel bin is the same as a sub
+//               bin, except in the case of a GeomBinGroup, which is a
+//               toplevel bin that contains a number of sub bins.
+////////////////////////////////////////////////////////////////////
+void CullTraverser::
+attach_sub_bin(GeomBin *bin) {
+  if (cull_cat.is_debug()) {
+    cull_cat.debug()
+      << "Attaching sub bin " << *bin << "\n";
+  }
+
+  _sub_bins.insert(SubBins::value_type(bin->get_sort(), bin));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::detach_toplevel_bin
+//       Access: Private
+//  Description: This is intended to be called only by
+//               GeomBin::detach().  It removes the bin from the
+//               appropriate structures within the traverser, as a
+//               toplevel bin.  See attach_toplevel_bin().
+////////////////////////////////////////////////////////////////////
+void CullTraverser::
+detach_toplevel_bin(GeomBin *bin) {
+  if (cull_cat.is_debug()) {
+    cull_cat.debug()
+      << "Detaching toplevel bin " << *bin << "\n";
+  }
+
+  ToplevelBins::iterator tbi = _toplevel_bins.find(bin->get_name());
+  nassertv(tbi != _toplevel_bins.end());
+  _toplevel_bins.erase(tbi);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::detach_sub_bin
+//       Access: Private
+//  Description: This is intended to be called only by
+//               GeomBin::detach().  It removes the bin from the
+//               appropriate structures within the traverser, as a
+//               sub bin.  See attach_sub_bin().
+////////////////////////////////////////////////////////////////////
+void CullTraverser::
+detach_sub_bin(GeomBin *bin) {
+  if (cull_cat.is_debug()) {
+    cull_cat.debug()
+      << "Detaching sub bin " << *bin << "\n";
+  }
+
+  // Remove the bin from its place in the sort order list.  This will
+  // be one of the (possibly several) entries in the multimap with the
+  // same sort value.
+  pair<SubBins::iterator, SubBins::iterator> range;
+  range = _sub_bins.equal_range(bin->get_sort());
+
+  SubBins::iterator sbi;
+  for (sbi = range.first; sbi != range.second; ++sbi) {
+    GeomBin *consider_bin = (*sbi).second;
+    nassertv(consider_bin->get_sort() == bin->get_sort());
+
+    if (consider_bin == bin) {
+      // Here's the position!
+      _sub_bins.erase(sbi);
+      return;
+    }
+  }
+  nassertv(false);
+}

+ 13 - 3
panda/src/cull/cullTraverser.h

@@ -43,6 +43,8 @@ public:
 
   INLINE void set_default_bin(GeomBin *bin);
   INLINE GeomBin *get_default_bin() const;
+  INLINE bool has_bin(const string &name) const;
+  INLINE GeomBin *get_bin(const string &name) const;
 
   virtual void traverse(Node *root, 
 			const AllAttributesWrapper &initial_state,
@@ -92,10 +94,17 @@ public:
 	       const CullLevelState &level_state);
 
 private:
+  void attach_toplevel_bin(GeomBin *bin);
+  void attach_sub_bin(GeomBin *bin);
+  void detach_toplevel_bin(GeomBin *bin);
+  void detach_sub_bin(GeomBin *bin);
+
   AllAttributesWrapper _initial_state;
 
-  typedef set<PT(GeomBin)> Bins;
-  Bins _bins;
+  typedef map<string, PT(GeomBin)> ToplevelBins;
+  typedef multimap<int, PT(GeomBin)> SubBins;
+  ToplevelBins _toplevel_bins;
+  SubBins _sub_bins;
   PT(GeomBin) _default_bin;
 
   typedef set<PT(CullState), IndirectCompareTo<CullState> > States;
@@ -128,7 +137,8 @@ public:
 private:
   static TypeHandle _type_handle;
 
-friend class GeomBin;
+  friend class GeomBin;
+  friend class GeomBinGroup;
 };
 
 #include "cullTraverser.I"

+ 57 - 22
panda/src/cull/geomBin.I

@@ -9,13 +9,39 @@
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE GeomBin::
-GeomBin(const string &name, CullTraverser *traverser, int sort) :
+GeomBin(const string &name) :
   Namable(name) 
 {
   _traverser = (CullTraverser *)NULL;
-  if (traverser != (CullTraverser *)NULL) {
-    attach_to(traverser, sort);
-  }
+  _is_attached = false;
+  _sort = 0;
+  _parent = (GeomBin *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBin::get_sort
+//       Access: Public
+//  Description: Returns the sort index associated with this
+//               particular bin.  The CullTraverser will render bins
+//               in order according to their sort index.
+////////////////////////////////////////////////////////////////////
+INLINE int GeomBin::
+get_sort() const {
+  return _sort;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBin::has_traverser
+//       Access: Public
+//  Description: Returns true if the GeomBin is currently attached to
+//               a traverser, false otherwise.  If this is false, and
+//               the bin is not attached to a parent bin
+//               (i.e. has_parent() is also false), don't expect the
+//               bin to be rendered.
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomBin::
+has_traverser() const {
+  return (_traverser != (CullTraverser *)NULL);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -30,31 +56,40 @@ get_traverser() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GeomBin::get_sort
+//     Function: GeomBin::has_parent
 //       Access: Public
-//  Description: Returns the sort index associated with this
-//               particular bin.  The CullTraverser will render bins
-//               in order according to their sort index.
+//  Description: Returns true if the bin is a child of some
+//               GeomBinGroup, false if it is a toplevel bin in its
+//               own right.  See GeomBinGroup.
 ////////////////////////////////////////////////////////////////////
-INLINE int GeomBin::
-get_sort() const {
-  return _sort;
+INLINE bool GeomBin::
+has_parent() const {
+  return _parent != (GeomBin *)NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GeomBin::set_sort
+//     Function: GeomBin::get_parent
 //       Access: Public
-//  Description: Changes the sort index associated with this
-//               particular bin.  The CullTraverser will render bins
-//               in order according to their sort index.
+//  Description: Returns the parent pointer of the GeomBin if the bin
+//               is a child of some GeomBinGropu, or NULL if the bin
+//               does not have a parent.  See GeomBinGroup.
 ////////////////////////////////////////////////////////////////////
-INLINE void GeomBin::
-set_sort(int sort) {
-  if (_traverser != (CullTraverser *)NULL) {
-    attach_to(_traverser, sort);
-  } else {
-    _sort = sort;
-  }
+INLINE GeomBin *GeomBin::
+get_parent() const {
+  return _parent;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBin::is_attached
+//       Access: Public
+//  Description: Returns true if the bin is currently attached to a
+//               CullTraverser, false otherwise.  This should always
+//               be tree if has_traverser() is true, and false if
+//               has_traverser() is false.
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomBin::
+is_attached() const {
+  return _is_attached;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 182 - 41
panda/src/cull/geomBin.cxx

@@ -22,69 +22,188 @@ GeomBin::
 ~GeomBin() {
   // We shouldn't be attached to anything when we destruct.  If we
   // are, something went screwy in the reference counting.
+  nassertv(!_is_attached);
   nassertv(_traverser == (CullTraverser *)NULL);
+
+  // We also shouldn't be part of any parent bin.
+  nassertv(_parent == (GeomBin *)NULL);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GeomBin::attach_to
+//     Function: GeomBin::set_name
 //       Access: Public
-//  Description: Detaches the bin from whichever CullTraverser it is
-//               currently attached to, and attaches it to the
-//               indicated CullTraverser instead, at the indicated
-//               sort level.  The CullTraverser will render all of its
-//               attached GeomBins, in order according to sort level.
+//  Description: Changes the name of the GeomBin, correctly updating
+//               the CullTraverser it's attached to.
+////////////////////////////////////////////////////////////////////
+void GeomBin::
+set_name(const string &name) {
+  if (name == get_name()) {
+    return;
+  }
+
+  // If we're currently attached, we need to need to be unattached
+  // while we change the name--but only if we're a toplevel bin.  A
+  // nested bin doesn't matter.
+  if (is_attached() && !has_parent()) {
+    PT(GeomBin) keep = this;
+
+    nassertv(_traverser != (CullTraverser *)NULL);
+    _traverser->detach_toplevel_bin(this);
+    Namable::set_name(name);
+    _traverser->attach_toplevel_bin(this);
+
+  } else {
+    Namable::set_name(name);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBin::clear_name
+//       Access: Public
+//  Description: Removes the name of the GeomBin.  This also detaches
+//               it (if it is a toplevel bin), and may therefore
+//               inadvertently delete it!  Be very careful.
 ////////////////////////////////////////////////////////////////////
 void GeomBin::
-attach_to(CullTraverser *traverser, int sort) {
-  nassertv(traverser != (CullTraverser *)NULL);
-  PT(GeomBin) keep = detach();
-  _sort = sort;
+clear_name() {
+  PT(GeomBin) keep;
+
+  if (is_attached() && !has_parent()) {
+    cull_cat.warning()
+      << "GeomBin::clear_name() called on an attached GeomBin.\n";
+    keep = detach();
+  }
+
+  Namable::clear_name();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBin::set_sort
+//       Access: Public
+//  Description: Changes the sort index associated with this
+//               particular bin.  The CullTraverser will render bins
+//               in order according to their sort index.
+////////////////////////////////////////////////////////////////////
+void GeomBin::
+set_sort(int sort) {
+  if (sort == _sort) {
+    return;
+  }
+
+  // If we're currently attached, even if we're a nested bin, we need
+  // to be unattached while we adjust the sorting order.
+  if (is_attached()) {
+    PT(GeomBin) keep = this;
+
+    nassertv(_traverser != (CullTraverser *)NULL);
+    _traverser->detach_sub_bin(this);
+    _sort = sort;
+    _traverser->attach_sub_bin(this);
+
+  } else {
+    _sort = sort;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBin::set_traverser
+//       Access: Public
+//  Description: Associates the GeomBin with the indicated
+//               CullTraverser.  A particular bin may only be
+//               associated with one traverser at a time.  This is
+//               normally done only after initially creating the
+//               GeomBin; it is an error to call set_traverser() more
+//               than once on the same bin (without calling
+//               clear_traverser() first).
+//
+//               It is also not necessary (and is an error) to assign
+//               child bins of a GeomBinGroup to a traverser; just
+//               assign the parent bin.
+//
+//               This must be called before the GeomBin can be
+//               rendered; it will thereafter be rendered by the
+//               indicated traverser.
+////////////////////////////////////////////////////////////////////
+void GeomBin::
+set_traverser(CullTraverser *traverser) {
+  if (traverser == (CullTraverser *)NULL) {
+    clear_traverser();
+  }
+
+  nassertv(!is_attached());
+  nassertv(_traverser == (CullTraverser *)NULL);
+
   _traverser = traverser;
-  _traverser->_bins.insert(this);
+  attach();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GeomBin::detach
+//     Function: GeomBin::clear_traverser
 //       Access: Public
-//  Description: Detaches the bin from whichever CullTraverser it is
-//               currently attached to, if any.  The bin will no
-//               longer be rendered.  The return value is a
-//               PT(GeomBin) that refers to the GeomBin itself; the
-//               caller should save this pointer in a local PT
-//               variable to avoid possibly destructing the GeomBin
-//               (since detaching it may remove the last outstanding
-//               reference count).
+//  Description: Disassociates the GeomBin with its current traverser.
+//               The bin will no longer be rendered, and may even be
+//               deleted if you do not immediately save the returned
+//               pointer.
 ////////////////////////////////////////////////////////////////////
 PT(GeomBin) GeomBin::
-detach() {
+clear_traverser() {
   PT(GeomBin) keep = this;
-  if (_traverser != (CullTraverser *)NULL) {
-    _traverser->_bins.erase(this);
+
+  if (is_attached()) {
+    detach();
+    _traverser = (CullTraverser *)NULL;
   }
-  _traverser = (CullTraverser *)NULL;
+  
   return keep;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GeomBin::remove_state
-//       Access: Public, Virtual
-//  Description: Disassociates the indicated state from the bin, if it
-//               was previously associated; presumably because a
-//               change in the scene's initial attributes as resulted
-//               in the indicated CullState switching to a new bin.
-//               
-//               Since the initial attributes will remain constant
-//               throughout a given frame, this function will only be
-//               called for GeomBins that save CullStates between
-//               frames; simple GeomBins that empty the entire list of
-//               CullStates upon a call to clear_current_states()
-//               should never see this function.
+//     Function: GeomBin::attach
+//       Access: Protected, Virtual
+//  Description: Formally adds the GeomBin to its indicated Traverser.
 ////////////////////////////////////////////////////////////////////
 void GeomBin::
-remove_state(CullState *) {
-  cull_cat.error()
-    << "remove_state() unexpectedly called for " << get_type() << "\n";
-  nassertv(false);
+attach() {
+  nassertv(has_name());
+  nassertv(_traverser != (CullTraverser *)NULL);
+  nassertv(!_is_attached);
+
+  // In the ordinary case, a bin is both a toplevel bin (visible by
+  // name from the CullTraverser) and a sub bin (directly renderable).
+  if (!has_parent()) {
+    _traverser->attach_toplevel_bin(this);
+  }
+  _traverser->attach_sub_bin(this);
+
+  _is_attached = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBin::detach
+//       Access: Protected, Virtual
+//  Description: Detaches the bin from whichever CullTraverser it is
+//               currently attached to.  The bin will no longer be
+//               rendered.  The return value is a PT(GeomBin) that
+//               refers to the GeomBin itself; the caller should save
+//               this pointer in a local PT variable to avoid possibly
+//               destructing the GeomBin (since detaching it may
+//               remove the last outstanding reference count).
+////////////////////////////////////////////////////////////////////
+PT(GeomBin) GeomBin::
+detach() {
+  nassertr(_is_attached, NULL);
+  nassertr(_traverser != (CullTraverser *)NULL, NULL);
+    
+  PT(GeomBin) keep = this;
+
+  if (!has_parent()) {
+    _traverser->detach_toplevel_bin(this);
+  }
+  _traverser->detach_sub_bin(this);
+
+  _is_attached = false;
+
+  return keep;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -106,3 +225,25 @@ void GeomBin::
 write(ostream &out, int indent_level) const {
   indent(out, indent_level) << *this << "\n";
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBin::remove_state
+//       Access: Public, Virtual
+//  Description: Disassociates the indicated state from the bin, if it
+//               was previously associated; presumably because a
+//               change in the scene's initial attributes as resulted
+//               in the indicated CullState switching to a new bin.
+//               
+//               Since the initial attributes will remain constant
+//               throughout a given frame, this function will only be
+//               called for GeomBins that save CullStates between
+//               frames; simple GeomBins that empty the entire list of
+//               CullStates upon a call to clear_current_states()
+//               should never see this function.
+////////////////////////////////////////////////////////////////////
+void GeomBin::
+remove_state(CullState *) {
+  cull_cat.error()
+    << "remove_state() unexpectedly called for " << get_type() << "\n";
+  nassertv(false);
+}

+ 26 - 9
panda/src/cull/geomBin.h

@@ -35,16 +35,29 @@ class CullTraverser;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA GeomBin : public TypedReferenceCount, public Namable {
 public:
-  INLINE GeomBin(const string &name, CullTraverser *traverser = NULL, 
-		 int sort = 0);
+  INLINE GeomBin(const string &name);
   virtual ~GeomBin();
 
-  void attach_to(CullTraverser *traverser, int sort = 0);
-  PT(GeomBin) detach();
+PUBLISHED:
+  void set_name(const string &name);
+  void clear_name();
 
-  INLINE CullTraverser *get_traverser() const;
   INLINE int get_sort() const;
-  INLINE void set_sort(int sort);
+  void set_sort(int sort);
+
+  void set_traverser(CullTraverser *traverser);
+  INLINE bool has_traverser() const;
+  INLINE CullTraverser *get_traverser() const;
+  PT(GeomBin) clear_traverser();
+
+  INLINE bool has_parent() const;
+  INLINE GeomBin *get_parent() const;
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+public:
+  INLINE bool is_attached() const;
 
   virtual void clear_current_states()=0;
   virtual void record_current_state(GraphicsStateGuardian *gsg,
@@ -54,15 +67,17 @@ public:
 
   virtual void draw(CullTraverser *trav)=0;
 
-  virtual void output(ostream &out) const;
-  virtual void write(ostream &out, int indent_level = 0) const;
-
 protected:
   INLINE void claim_cull_state(CullState *cs);
   INLINE void disclaim_cull_state(CullState *cs);
 
+  virtual void attach();
+  virtual PT(GeomBin) detach();
+
   CullTraverser *_traverser;
+  bool _is_attached;
   int _sort;
+  GeomBin *_parent;
 
 public:
   static TypeHandle get_class_type() {
@@ -82,6 +97,8 @@ public:
  
 private:
   static TypeHandle _type_handle;
+
+  friend class GeomBinGroup;
 };
 
 INLINE ostream &operator << (ostream &out, const GeomBin &bin) {

+ 6 - 6
panda/src/cull/geomBinAttribute.I

@@ -18,7 +18,7 @@ GeomBinAttribute() {
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE GeomBinAttribute::
-GeomBinAttribute(GeomBin *bin, int draw_order) {
+GeomBinAttribute(const string &bin, int draw_order) {
   set_on(bin, draw_order);
 }
 
@@ -29,8 +29,8 @@ GeomBinAttribute(GeomBin *bin, int draw_order) {
 //               geomBin.
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomBinAttribute::
-set_on(GeomBin *bin, int draw_order) {
-  nassertv(bin != (GeomBin *)NULL);
+set_on(const string &bin, int draw_order) {
+  nassertv(!bin.empty());
   _value = bin;
   _draw_order = draw_order;
   OnOffAttribute::set_on();
@@ -39,13 +39,13 @@ set_on(GeomBin *bin, int draw_order) {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomBinAttribute::get_bin
 //       Access: Public
-//  Description: Returns the bin that the GeomBinAttribute
+//  Description: Returns the bin name that the GeomBinAttribute
 //               represents.  It is only valid to call this if is_on()
 //               has returned true.
 ////////////////////////////////////////////////////////////////////
-INLINE PT(GeomBin) GeomBinAttribute::
+INLINE string GeomBinAttribute::
 get_bin() const {
-  nassertr(is_on(), NULL);
+  nassertr(is_on(), string());
   return _value;
 }
 

+ 6 - 6
panda/src/cull/geomBinAttribute.cxx

@@ -9,6 +9,8 @@
 #include <graphicsStateGuardianBase.h>
 #include <indent.h>
 
+#include <string.h>
+
 TypeHandle GeomBinAttribute::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -56,7 +58,7 @@ set_value_from(const OnOffTransition *other) {
   DCAST_INTO_V(ot, other);
   _value = ot->_value;
   _draw_order = ot->_draw_order;
-  nassertv(_value != (GeomBin *)NULL);
+  nassertv(!_value.empty());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -69,7 +71,7 @@ compare_values(const OnOffAttribute *other) const {
   const GeomBinAttribute *ot;
   DCAST_INTO_R(ot, other, false);
   if (_value != ot->_value) {
-    return (int)(_value - ot->_value);
+    return strcmp(_value.c_str(), ot->_value.c_str());
   }
   return _draw_order - ot->_draw_order;
 }
@@ -81,8 +83,7 @@ compare_values(const OnOffAttribute *other) const {
 ////////////////////////////////////////////////////////////////////
 void GeomBinAttribute::
 output_value(ostream &out) const {
-  nassertv(_value != (GeomBin *)NULL);
-  out << *_value << ":" << _draw_order;
+  out << _value << ":" << _draw_order;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -93,6 +94,5 @@ output_value(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 void GeomBinAttribute::
 write_value(ostream &out, int indent_level) const {
-  nassertv(_value != (GeomBin *)NULL);
-  indent(out, indent_level) << *_value << ":" << _draw_order << "\n";
+  indent(out, indent_level) << _value << ":" << _draw_order << "\n";
 }

+ 4 - 4
panda/src/cull/geomBinAttribute.h

@@ -19,10 +19,10 @@
 class EXPCL_PANDA GeomBinAttribute : public OnOffAttribute {
 public:
   INLINE GeomBinAttribute();
-  INLINE GeomBinAttribute(GeomBin *bin, int draw_order = 0);
+  INLINE GeomBinAttribute(const string &bin, int draw_order = 0);
 
-  INLINE void set_on(GeomBin *bin, int draw_order = 0);
-  INLINE PT(GeomBin) get_bin() const;
+  INLINE void set_on(const string &bin, int draw_order = 0);
+  INLINE string get_bin() const;
   INLINE int get_draw_order() const;
 
   virtual TypeHandle get_handle() const;
@@ -35,7 +35,7 @@ protected:
   virtual void output_value(ostream &out) const;
   virtual void write_value(ostream &out, int indent_level) const;
 
-  PT(GeomBin) _value;
+  string _value;
   int _draw_order;
 
 public:

+ 2 - 2
panda/src/cull/geomBinBackToFront.I

@@ -77,7 +77,7 @@ draw(CullTraverser *trav) const {
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE GeomBinBackToFront::
-GeomBinBackToFront(const string &name, CullTraverser *traverser, int sort) :
-  GeomBin(name, traverser, sort) 
+GeomBinBackToFront(const string &name) :
+  GeomBin(name) 
 {
 }

+ 3 - 4
panda/src/cull/geomBinBackToFront.h

@@ -24,11 +24,10 @@
 //               without a Z-buffer.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA GeomBinBackToFront : public GeomBin {
-public:
-  INLINE GeomBinBackToFront(const string &name, 
-			    CullTraverser *traverser = NULL,
-			    int sort = 0);
+PUBLISHED:
+  INLINE GeomBinBackToFront(const string &name);
 
+public:
   virtual void clear_current_states();
   virtual void record_current_state(GraphicsStateGuardian *gsg,
 				    CullState *cs, int draw_order,

+ 2 - 2
panda/src/cull/geomBinFixed.I

@@ -78,7 +78,7 @@ draw(CullTraverser *trav) const {
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE GeomBinFixed::
-GeomBinFixed(const string &name, CullTraverser *traverser, int sort) :
-  GeomBin(name, traverser, sort) 
+GeomBinFixed(const string &name) :
+  GeomBin(name) 
 {
 }

+ 3 - 4
panda/src/cull/geomBinFixed.h

@@ -21,11 +21,10 @@
 //               specified at each GeomBinTransition.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA GeomBinFixed : public GeomBin {
-public:
-  INLINE GeomBinFixed(const string &name, 
-		      CullTraverser *traverser = NULL,
-		      int sort = 0);
+PUBLISHED:
+  INLINE GeomBinFixed(const string &name);
 
+public:
   virtual void clear_current_states();
   virtual void record_current_state(GraphicsStateGuardian *gsg,
 				    CullState *cs, int draw_order,

+ 2 - 30
panda/src/cull/geomBinGroup.I

@@ -9,23 +9,11 @@
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE GeomBinGroup::
-GeomBinGroup(const string &name, CullTraverser *traverser, int sort) :
-  GeomBin(name, traverser, sort) 
+GeomBinGroup(const string &name) :
+  GeomBin(name) 
 {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomBinGroup::add_sub_bin
-//       Access: Public
-//  Description: Appends the indicated bin to the end of the sub_bin
-//               list, and returns the new index.
-////////////////////////////////////////////////////////////////////
-INLINE int GeomBinGroup::
-add_sub_bin(GeomBin *sub_bin) {
-  _sub_bins.push_back(sub_bin);
-  return _sub_bins.size() - 1;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomBinGroup::get_num_bins
 //       Access: Public
@@ -46,19 +34,3 @@ get_bin(int n) {
   nassertr(n >= 0 && n < (int)_sub_bins.size(), NULL);
   return _sub_bins[n];
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomBinGroup::remove_bin
-//       Access: Public
-//  Description: Removes the nth bin.  All subsequent index numbers
-//               shift down by one.  The return value is the
-//               just-removed bin, which may be deleted if it is not
-//               immediately saved.
-////////////////////////////////////////////////////////////////////
-INLINE PT(GeomBin) GeomBinGroup::
-remove_bin(int n) {
-  nassertr(n >= 0 && n < (int)_sub_bins.size(), NULL);
-  PT(GeomBin) keep = get_bin(n);
-  _sub_bins.erase(_sub_bins.begin() + n);
-  return keep;
-}

+ 148 - 0
panda/src/cull/geomBinGroup.cxx

@@ -12,6 +12,75 @@
 
 TypeHandle GeomBinGroup::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBinGroup::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+GeomBinGroup::
+~GeomBinGroup() {
+  nassertv(!is_attached());
+
+  // Disassociate all of our children before we destruct.
+  SubBins::iterator sbi;
+  for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
+    GeomBin *sub_bin = (*sbi);
+    sub_bin->_parent = (GeomBin *)NULL;
+  }
+  _sub_bins.clear();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBinGroup::add_sub_bin
+//       Access: Public
+//  Description: Appends the indicated bin to the end of the sub_bin
+//               list, and returns the new index.
+////////////////////////////////////////////////////////////////////
+int GeomBinGroup::
+add_sub_bin(GeomBin *sub_bin) {
+  nassertr(!sub_bin->has_parent(), -1);
+  nassertr(!sub_bin->is_attached(), -1);
+  nassertr(sub_bin->has_name(), -1);
+
+  sub_bin->_parent = this;
+  _sub_bins.push_back(sub_bin);
+
+  if (is_attached()) {
+    sub_bin->_traverser = _traverser;
+    _traverser->attach_sub_bin(sub_bin);
+  }
+
+  return _sub_bins.size() - 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBinGroup::remove_bin
+//       Access: Public
+//  Description: Removes the nth bin.  All subsequent index numbers
+//               shift down by one.  The return value is the
+//               just-removed bin, which may be deleted if it is not
+//               immediately saved.
+////////////////////////////////////////////////////////////////////
+PT(GeomBin) GeomBinGroup::
+remove_bin(int n) {
+  nassertr(n >= 0 && n < (int)_sub_bins.size(), NULL);
+  PT(GeomBin) sub_bin = get_bin(n);
+  nassertr(sub_bin->get_parent() == this, NULL);
+
+  if (is_attached()) {
+    nassertr(sub_bin->is_attached(), NULL);
+    nassertr(sub_bin->_traverser == _traverser, NULL);
+    _traverser->detach_sub_bin(sub_bin);
+    sub_bin->_traverser = (CullTraverser *)NULL;
+  }
+
+  _sub_bins.erase(_sub_bins.begin() + n);
+  sub_bin->_parent = (GeomBin *)NULL;
+
+  return sub_bin;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomBinGroup::clear_current_states
 //       Access: Public, Virtual
@@ -22,6 +91,13 @@ TypeHandle GeomBinGroup::_type_handle;
 ////////////////////////////////////////////////////////////////////
 void GeomBinGroup::
 clear_current_states() {
+  // This should never be called for toplevel bins, only sub bins--and
+  // a GeomBinGroup should never be a sub bin.  The function will do
+  // the right thing even if it is called, but this is probably a
+  // mistake.
+  cull_cat.warning()
+    << "GeomBinGroup::clear_current_states() called\n";
+
   SubBins::iterator sbi;
   for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
     (*sbi)->clear_current_states();
@@ -51,6 +127,13 @@ record_current_state(GraphicsStateGuardian *gsg, CullState *cs,
 ////////////////////////////////////////////////////////////////////
 void GeomBinGroup::
 draw(CullTraverser *trav) {
+  // This should never be called for toplevel bins, only sub bins--and
+  // a GeomBinGroup should never be a sub bin.  The function will do
+  // the right thing even if it is called, but this is probably a
+  // mistake.
+  cull_cat.warning()
+    << "GeomBinGroup::draw() called\n";
+
   SubBins::iterator sbi;
   for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
     (*sbi)->draw(trav);
@@ -84,3 +167,68 @@ write(ostream &out, int indent_level) const {
 
   indent(out, indent_level) << "}\n";
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBinGroup::attach
+//       Access: Protected, Virtual
+//  Description: Formally adds the GeomBin to its indicated Traverser.
+////////////////////////////////////////////////////////////////////
+void GeomBinGroup::
+attach() {
+  nassertv(has_name());
+  nassertv(_traverser != (CullTraverser *)NULL);
+  nassertv(!_is_attached);
+
+  // In the case of a GeomBinGroup, the bin is only a toplevel bin
+  // (visible by name from the CullTraverser), and not a sub bin
+  // (directly renderable).
+  if (!has_parent()) {
+    _traverser->attach_toplevel_bin(this);
+  }
+
+  _is_attached = true;
+
+  // Now attach each of our sub bins.
+  SubBins::const_iterator sbi;
+  for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
+    GeomBin *sub_bin = (*sbi);
+    nassertv(!sub_bin->is_attached());
+    sub_bin->_traverser = _traverser;
+    sub_bin->attach();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBinGroup::detach
+//       Access: Protected, Virtual
+//  Description: Detaches the bin from whichever CullTraverser it is
+//               currently attached to.  The bin will no longer be
+//               rendered.  The return value is a PT(GeomBin) that
+//               refers to the GeomBin itself; the caller should save
+//               this pointer in a local PT variable to avoid possibly
+//               destructing the GeomBin (since detaching it may
+//               remove the last outstanding reference count).
+////////////////////////////////////////////////////////////////////
+PT(GeomBin) GeomBinGroup::
+detach() {
+  nassertr(_is_attached, NULL);
+  nassertr(_traverser != (CullTraverser *)NULL, NULL);
+    
+  PT(GeomBin) keep = this;
+
+  if (!has_parent()) {
+    _traverser->detach_toplevel_bin(this);
+  }
+
+  _is_attached = false;
+
+  // Now detach each of our sub bins.
+  SubBins::const_iterator sbi;
+  for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
+    GeomBin *sub_bin = (*sbi);
+    sub_bin->detach();
+    sub_bin->_traverser = (CullTraverser *)NULL;
+  }
+
+  return keep;
+}

+ 10 - 5
panda/src/cull/geomBinGroup.h

@@ -23,15 +23,16 @@
 //               a given CullState should be assigned to.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA GeomBinGroup : public GeomBin {
-public:
-  INLINE GeomBinGroup(const string &name, CullTraverser *traverser = NULL,
-		      int sort = 0);
+PUBLISHED:
+  INLINE GeomBinGroup(const string &name);
+  virtual ~GeomBinGroup();
 
-  INLINE int add_sub_bin(GeomBin *sub_bin);
+  int add_sub_bin(GeomBin *sub_bin);
   INLINE int get_num_bins() const;
   INLINE GeomBin *get_bin(int n);
-  INLINE PT(GeomBin) remove_bin(int n);
+  PT(GeomBin) remove_bin(int n);
 
+public:
   virtual int choose_bin(CullState *cs) const=0;
 
   virtual void clear_current_states();
@@ -44,6 +45,10 @@ public:
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 
+protected:
+  virtual void attach();
+  virtual PT(GeomBin) detach();
+
 private:
   typedef vector<PT(GeomBin)> SubBins;
   SubBins _sub_bins;

+ 10 - 5
panda/src/cull/geomBinNormal.cxx

@@ -19,16 +19,21 @@ TypeHandle GeomBinNormal::_type_handle;
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 GeomBinNormal::
-GeomBinNormal(const string &name, CullTraverser *traverser, int sort) :
-  GeomBinGroup(name, traverser, sort) 
+GeomBinNormal(const string &name) :
+  GeomBinGroup(name) 
 {
-  add_sub_bin(new GeomBinUnsorted("opaque"));
-  add_sub_bin(new GeomBinBackToFront("transparent"));
+  GeomBinUnsorted *opaque = new GeomBinUnsorted("opaque");
+  GeomBinBackToFront *transparent = new GeomBinBackToFront("transparent");
+  opaque->set_sort(10);
+  transparent->set_sort(20);
+
+  add_sub_bin(opaque);
+  add_sub_bin(transparent);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomBinNormal::Constructor
-//       Access: Public
+//       Access: Public, Virtual
 //  Description: Identifies the particular sub-bin the indicated
 //               CullState should be assigned to.
 ////////////////////////////////////////////////////////////////////

+ 3 - 3
panda/src/cull/geomBinNormal.h

@@ -22,10 +22,10 @@
 //               back-to-front.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA GeomBinNormal : public GeomBinGroup {
-public:
-  GeomBinNormal(const string &name, CullTraverser *traverser = NULL,
-		int sort = 0);
+PUBLISHED:
+  GeomBinNormal(const string &name);
 
+public:
   virtual int choose_bin(CullState *cs) const;
 
 public:

+ 6 - 6
panda/src/cull/geomBinTransition.I

@@ -18,7 +18,7 @@ GeomBinTransition() {
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE GeomBinTransition::
-GeomBinTransition(GeomBin *bin, int draw_order) : 
+GeomBinTransition(const string &bin, int draw_order) : 
   OnOffTransition(TD_on),
   _value(bin),
   _draw_order(draw_order)
@@ -44,8 +44,8 @@ off() {
 //               bin.
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomBinTransition::
-set_on(GeomBin *bin, int draw_order) {
-  nassertv(bin != (GeomBin *)NULL);
+set_on(const string &bin, int draw_order) {
+  nassertv(!bin.empty());
   _value = bin;
   _draw_order = draw_order;
   OnOffTransition::set_on();
@@ -54,13 +54,13 @@ set_on(GeomBin *bin, int draw_order) {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomBinTransition::get_bin
 //       Access: Public
-//  Description: Returns the bin that the GeomBinTransition
+//  Description: Returns the bin name that the GeomBinTransition
 //               represents.  It is only valid to call this if is_on()
 //               has returned true.
 ////////////////////////////////////////////////////////////////////
-INLINE PT(GeomBin) GeomBinTransition::
+INLINE string GeomBinTransition::
 get_bin() const {
-  nassertr(is_on(), NULL);
+  nassertr(is_on(), string());
   return _value;
 }
 

+ 62 - 6
panda/src/cull/geomBinTransition.cxx

@@ -8,6 +8,8 @@
 
 #include <indent.h>
 
+#include <string.h>
+
 TypeHandle GeomBinTransition::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -43,7 +45,7 @@ set_value_from(const OnOffTransition *other) {
   DCAST_INTO_V(ot, other);
   _value = ot->_value;
   _draw_order = ot->_draw_order; 
-  nassertv(_value != (GeomBin *)NULL);
+  nassertv(!_value.empty());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -56,7 +58,7 @@ compare_values(const OnOffTransition *other) const {
   const GeomBinTransition *ot;
   DCAST_INTO_R(ot, other, false);
   if (_value != ot->_value) {
-    return (int)(_value - ot->_value);
+    return strcmp(_value.c_str(), ot->_value.c_str());
   }
   return _draw_order - ot->_draw_order;
 }
@@ -68,8 +70,7 @@ compare_values(const OnOffTransition *other) const {
 ////////////////////////////////////////////////////////////////////
 void GeomBinTransition::
 output_value(ostream &out) const {
-  nassertv(_value != (GeomBin *)NULL);
-  out << *_value << ":" << _draw_order;
+  out << _value << ":" << _draw_order;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -80,6 +81,61 @@ output_value(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 void GeomBinTransition::
 write_value(ostream &out, int indent_level) const {
-  nassertv(_value != (GeomBin *)NULL);
-  indent(out, indent_level) << *_value << ":" << _draw_order << "\n";
+  indent(out, indent_level) << _value << ":" << _draw_order << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBinTransition::register_with_factory
+//       Access: Public, Static
+//  Description: Factory method to generate a GeomBinTransition object
+////////////////////////////////////////////////////////////////////
+void GeomBinTransition::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_GeomBinTransition);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBinTransition::write_datagram
+//       Access: Public
+//  Description: Function to write the important information in
+//               the particular object to a Datagram
+////////////////////////////////////////////////////////////////////
+void GeomBinTransition::
+write_datagram(BamWriter *manager, Datagram &me) {
+  OnOffTransition::write_datagram(manager, me);
+  me.add_string(_value);
+  me.add_int32(_draw_order);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBinTransition::make_GeomBinTransition
+//       Access: Public
+//  Description: Factory method to generate a GeomBinTransition object
+////////////////////////////////////////////////////////////////////
+TypedWriteable *GeomBinTransition::
+make_GeomBinTransition(const FactoryParams &params) {
+  GeomBinTransition *me = new GeomBinTransition;
+  BamReader *manager;
+  Datagram packet;
+
+  parse_params(params, manager, packet);
+  DatagramIterator scan(packet);
+
+  me->fillin(scan, manager);
+  return me;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomBinTransition::fillin
+//       Access: Protected
+//  Description: Function that reads out of the datagram (or asks
+//               manager to read) all of the data that is needed to
+//               re-create this object and stores it in the appropiate
+//               place
+////////////////////////////////////////////////////////////////////
+void GeomBinTransition::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  OnOffTransition::fillin(scan, manager);
+  _value = scan.get_string();
+  _draw_order = scan.get_int32();
 }

+ 16 - 6
panda/src/cull/geomBinTransition.h

@@ -16,15 +16,16 @@
 // Description : 
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA GeomBinTransition : public OnOffTransition {
-public:
+PUBLISHED:
   INLINE GeomBinTransition();
-  INLINE GeomBinTransition(GeomBin *bin, int draw_order = 0);
+  INLINE GeomBinTransition(const string &bin, int draw_order);
   INLINE static GeomBinTransition off();
 
-  INLINE void set_on(GeomBin *bin, int draw_order = 0);
-  INLINE PT(GeomBin) get_bin() const;
+  INLINE void set_on(const string &bin, int draw_order);
+  INLINE string get_bin() const;
   INLINE int get_draw_order() const;
-  
+
+public:  
   virtual NodeTransition *make_copy() const;
   virtual NodeAttribute *make_attrib() const;
 
@@ -34,9 +35,18 @@ protected:
   virtual void output_value(ostream &out) const;
   virtual void write_value(ostream &out, int indent_level) const;
 
-  PT(GeomBin) _value;
+  string _value;
   int _draw_order;
 
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter* manager, Datagram &me);  
+
+  static TypedWriteable *make_GeomBinTransition(const FactoryParams &params);
+
+protected:
+  void fillin(DatagramIterator& scan, BamReader* manager);
+
 public:
   virtual TypeHandle get_type() const {
     return get_class_type();

+ 2 - 2
panda/src/cull/geomBinUnsorted.I

@@ -9,7 +9,7 @@
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE GeomBinUnsorted::
-GeomBinUnsorted(const string &name, CullTraverser *traverser, int sort) :
-  GeomBin(name, traverser, sort) 
+GeomBinUnsorted(const string &name) :
+  GeomBin(name) 
 {
 }

+ 3 - 3
panda/src/cull/geomBinUnsorted.h

@@ -23,11 +23,11 @@
 //               order.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA GeomBinUnsorted : public GeomBin {
-public:
-  INLINE GeomBinUnsorted(const string &name, CullTraverser *traverser = NULL,
-			 int sort = 0);
+PUBLISHED:
+  INLINE GeomBinUnsorted(const string &name);
   virtual ~GeomBinUnsorted();
 
+public:
   virtual void clear_current_states();
   virtual void record_current_state(GraphicsStateGuardian *gsg,
 				    CullState *cs, int draw_order,

+ 45 - 15
panda/src/doc/eggSyntax.txt

@@ -174,11 +174,18 @@ appear before they are referenced.
     is any of the other options, it specifies the type of transparency
     to be enabled.
 
+  <Scalar> bin { bin-name }
+
+    This specifies the bin name order of all polygons with this
+    texture applied, in the absence of a bin name specified on the
+    polygon itself.  See the description for bin under polygon
+    attributes.
+
   <Scalar> draw_order { number }
 
     This specifies the fixed drawing order of all polygons with this
     texture applied, in the absence of a drawing order specified on
-    the polygon itself.  See the description for draw-order under
+    the polygon itself.  See the description for draw_order under
     polygon attributes.
 
   <Transform> { transform-definition }
@@ -404,22 +411,39 @@ GEOMETRY ENTRIES
     it to be viewed from either side.
     
 
+  <Scalar> bin { bin-name }
+
+    It is sometimes important to control the order in which objects
+    are rendered, particularly when transparency is in use.  In Panda,
+    this is achieved via the use of named bins and, within certain
+    kinds of bins, sometimes an explicit draw_order is also used (see
+    below).
+
+    In the normal (state-sorting) mode, Panda renders its geometry by
+    first grouping into one or more named bins, and then rendering the
+    bins in a specified order.  The programmer is free to define any
+    number of bins, named whatever he/she desires.
+
+    This scalar specifies which bin this particular polygon is to be
+    rendered within.  If no bin scalar is given, or if the name given
+    does not match any of the known bins, the polygon will be assigned
+    to the default bin, which renders all opaque geometry sorted by
+    state, followed by all transparent geometry sorted back-to-front.
+
+    See also draw_order, below.
+
+
   <Scalar> draw_order { number }
 
-    This defines the fixed drawing order of this polygon.  Drawing
-    order is very important when using certain kinds of transparency.
-    Normally, Performer will draw all opaque objects first, then all
-    transparent objects from back to front.  Sometimes the
-    back-to-front sorting can fail, especially in the case of a small
-    polygon immediately in front of a large polygon.  In this case, it
-    may be necessary to explicitly give the order in which the
-    polygons should be drawn.
+    This works in conjunction with bin, above, to further refine the
+    order in which this polygon is drawn, relative to other geometry
+    in the same bin.  If (and only if) the bin type named in the bin
+    scalar is a GeomBinFixed, this draw_order is used to define the
+    fixed order that all geometry in the same will be rendered, from
+    smaller numbers to larger numbers.
 
-    All objects with a given draw-order will be drawn before all
-    objects with a higher draw-order.  Objects with a zero or
-    unspecified draw-order will be drawn first if they are opaque, and
-    in order from back to front if they are transparent.  A negative
-    draw-order is not allowed.
+    If no bin scalar is specified, the default is a bin named "fixed",
+    which is a GeomBinFixed object that always exists by default.
 
 
 <PointLight> name { 
@@ -797,11 +821,17 @@ GROUPING ENTRIES
     affected by fog.  It will be created with the appropriate GeoState
     to have fog disabled.
 
+  <Scalar> bin { bin-name }
+
+    This specifies the bin name for all polygons at or below this node
+    that do not explicitly set their own bin.  See the description of
+    bin for geometry attributes, above.
+
   <Scalar> draw_order { number }
 
     This specifies the drawing order for all polygons at or below this
     node that do not explicitly set their own drawing order.  See the
-    description of draw-order for geometry attributes, above.
+    description of draw_order for geometry attributes, above.
 
   <Scalar> write_through { boolean-value }
 

+ 64 - 8
panda/src/egg/eggAlphaMode.I

@@ -12,7 +12,7 @@
 INLINE EggAlphaMode::
 EggAlphaMode() {
   _alpha_mode = AM_unspecified;
-  _draw_order = 0.0;
+  _draw_order = 0;
   _has_draw_order = false;
 }
 
@@ -66,21 +66,25 @@ get_alpha_mode() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAlphaMode::set_draw_order
 //       Access: Public
-//  Description: 
+//  Description: Sets the "draw-order" flag associated with this
+//               object.  This specifies a particular order in which
+//               objects of this type should be drawn, within the
+//               specified bin.  If a bin is not explicitly specified,
+//               "fixed" is used.  See also set_bin().
 ////////////////////////////////////////////////////////////////////
 INLINE void EggAlphaMode::
-set_draw_order(double order) {
+set_draw_order(int order) {
   _draw_order = order;
   _has_draw_order = true;
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAlphaMode::get_draw_order
 //       Access: Public
-//  Description: 
+//  Description: Returns the "draw-order" flag as set for this
+//               particular object.  See set_draw_order().
 ////////////////////////////////////////////////////////////////////
-INLINE double EggAlphaMode::
+INLINE int EggAlphaMode::
 get_draw_order() const {
   return _draw_order;
 }
@@ -88,7 +92,8 @@ get_draw_order() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAlphaMode::has_draw_order
 //       Access: Public
-//  Description: 
+//  Description: Returns true if the draw-order flag has been set for
+//               this particular object.  See set_draw_order().
 ////////////////////////////////////////////////////////////////////
 INLINE bool EggAlphaMode::
 has_draw_order() const {
@@ -98,13 +103,64 @@ has_draw_order() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAlphaMode::clear_draw_order
 //       Access: Public
-//  Description: 
+//  Description: Removes the draw-order flag from this particular
+//               object.  See set_draw_order().
 ////////////////////////////////////////////////////////////////////
 INLINE void EggAlphaMode::
 clear_draw_order() {
   _has_draw_order = false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggAlphaMode::set_bin
+//       Access: Public
+//  Description: Sets the "bin" string for this particular object.
+//               This names a particular bin in which the object
+//               should be rendered.  The exact meaning of a bin is
+//               implementation defined, but generally a GeomBin
+//               matching each bin name must also be specifically
+//               added to the rendering engine (e.g. the
+//               CullTraverser) in use for this to work.  See also
+//               set_draw_order().
+////////////////////////////////////////////////////////////////////
+INLINE void EggAlphaMode::
+set_bin(const string &bin) {
+  _bin = bin;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggAlphaMode::get_bin
+//       Access: Public
+//  Description: Returns the bin name that has been set for this
+//               particular object, if any.  See set_bin().
+////////////////////////////////////////////////////////////////////
+INLINE string EggAlphaMode::
+get_bin() const {
+  return _bin;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggAlphaMode::has_bin
+//       Access: Public
+//  Description: Returns true if a bin name has been set for this
+//               particular object.  See set_bin().
+////////////////////////////////////////////////////////////////////
+INLINE bool EggAlphaMode::
+has_bin() const {
+  return !_bin.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggAlphaMode::clear_bin
+//       Access: Public
+//  Description: Removes the bin name that was set for this particular
+//               object.  See set_bin().
+////////////////////////////////////////////////////////////////////
+INLINE void EggAlphaMode::
+clear_bin() {
+  _bin = string();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggAlphaMode::Inequality Operator
 //       Access: Public

+ 12 - 0
panda/src/egg/eggAlphaMode.cxx

@@ -26,6 +26,10 @@ write(ostream &out, int indent_level) const {
     indent(out, indent_level)
       << "<Scalar> draw-order { " << get_draw_order() << " }\n";
   }
+  if (has_bin()) {
+    indent(out, indent_level)
+      << "<Scalar> bin { " << get_bin() << " }\n";
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -46,6 +50,10 @@ operator == (const EggAlphaMode &other) const {
     }
   }
 
+  if (_bin != other._bin) {
+    return false;
+  }
+
   return true;
 }
 
@@ -70,6 +78,10 @@ operator < (const EggAlphaMode &other) const {
     }
   }
 
+  if (_bin != other._bin) {
+    return _bin < other._bin;
+  }
+
   return false;
 }
 

+ 9 - 3
panda/src/egg/eggAlphaMode.h

@@ -40,11 +40,16 @@ public:
   INLINE void set_alpha_mode(AlphaMode mode);
   INLINE AlphaMode get_alpha_mode() const;
 
-  INLINE void set_draw_order(double order);
-  INLINE double get_draw_order() const;
+  INLINE void set_draw_order(int order);
+  INLINE int get_draw_order() const;
   INLINE bool has_draw_order() const;
   INLINE void clear_draw_order();
 
+  INLINE void set_bin(const string &bin);
+  INLINE string get_bin() const;
+  INLINE bool has_bin() const;
+  INLINE void clear_bin();
+
   // Comparison operators are handy.
   bool operator == (const EggAlphaMode &other) const;
   INLINE bool operator != (const EggAlphaMode &other) const;
@@ -54,8 +59,9 @@ public:
 
 private:
   AlphaMode _alpha_mode;
-  double _draw_order;
+  int _draw_order;
   bool _has_draw_order;
+  string _bin;
 
 
 public:

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

@@ -265,6 +265,57 @@ write(ostream &out, int indent_level) const {
   indent(out, indent_level) << "}\n";
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggGroup::determine_alpha_mode
+//       Access: Public, Virtual
+//  Description: Walks back up the hierarchy, looking for an EggGroup
+//               or EggPrimitive or some such object at this level or
+//               above this group that has an alpha_mode other than
+//               AM_unspecified.  Returns a valid EggAlphaMode pointer
+//               if one is found, or NULL otherwise.
+////////////////////////////////////////////////////////////////////
+EggAlphaMode *EggGroup::
+determine_alpha_mode() {
+  if (get_alpha_mode() != AM_unspecified) {
+    return this;
+  }
+  return EggGroupNode::determine_alpha_mode();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggGroup::determine_draw_order
+//       Access: Public, Virtual
+//  Description: Walks back up the hierarchy, looking for an EggGroup
+//               or EggPrimitive or some such object at this level or
+//               above this group that has a draw_order specified.
+//               Returns a valid EggAlphaMode pointer if one is found,
+//               or NULL otherwise.
+////////////////////////////////////////////////////////////////////
+EggAlphaMode *EggGroup::
+determine_draw_order() {
+  if (has_draw_order()) {
+    return this;
+  }
+  return EggGroupNode::determine_draw_order();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggGroup::determine_bin
+//       Access: Public, Virtual
+//  Description: Walks back up the hierarchy, looking for an EggGroup
+//               or EggPrimitive or some such object at this level or
+//               above this group that has a bin specified.  Returns a
+//               valid EggAlphaMode pointer if one is found, or NULL
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+EggAlphaMode *EggGroup::
+determine_bin() {
+  if (has_bin()) {
+    return this;
+  }
+  return EggGroupNode::determine_bin();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggGroup::parse_egg
 //       Access: Public

+ 4 - 0
panda/src/egg/eggGroup.h

@@ -77,6 +77,10 @@ public:
   virtual void write(ostream &out, int indent_level) const;
   bool parse_egg(const string &egg_syntax);
 
+  virtual EggAlphaMode *determine_alpha_mode();
+  virtual EggAlphaMode *determine_draw_order();
+  virtual EggAlphaMode *determine_bin();
+
   void set_group_type(GroupType type);
   INLINE GroupType get_group_type() const;
 

+ 53 - 0
panda/src/egg/eggNode.cxx

@@ -11,6 +11,59 @@
 
 TypeHandle EggNode::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggNode::determine_alpha_mode
+//       Access: Public, Virtual
+//  Description: Walks back up the hierarchy, looking for an EggGroup
+//               or EggPrimitive or some such object at this level or
+//               above this node that has an alpha_mode other than
+//               AM_unspecified.  Returns a valid EggAlphaMode pointer
+//               if one is found, or NULL otherwise.
+////////////////////////////////////////////////////////////////////
+EggAlphaMode *EggNode::
+determine_alpha_mode() {
+  if (_parent == (EggGroupNode *)NULL) {
+    // Too bad; we're done.
+    return (EggAlphaMode *)NULL;
+  }
+  return _parent->determine_alpha_mode();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggNode::determine_draw_order
+//       Access: Public, Virtual
+//  Description: Walks back up the hierarchy, looking for an EggGroup
+//               or EggPrimitive or some such object at this level or
+//               above this node that has a draw_order specified.
+//               Returns a valid EggAlphaMode pointer if one is found,
+//               or NULL otherwise.
+////////////////////////////////////////////////////////////////////
+EggAlphaMode *EggNode::
+determine_draw_order() {
+  if (_parent == (EggGroupNode *)NULL) {
+    // Too bad; we're done.
+    return (EggAlphaMode *)NULL;
+  }
+  return _parent->determine_draw_order();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggNode::determine_bin
+//       Access: Public, Virtual
+//  Description: Walks back up the hierarchy, looking for an EggGroup
+//               or EggPrimitive or some such object at this level or
+//               above this node that has a bin specified.  Returns a
+//               valid EggAlphaMode pointer if one is found, or NULL
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+EggAlphaMode *EggNode::
+determine_bin() {
+  if (_parent == (EggGroupNode *)NULL) {
+    // Too bad; we're done.
+    return (EggAlphaMode *)NULL;
+  }
+  return _parent->determine_bin();
+}
 
 #ifndef NDEBUG
 

+ 5 - 0
panda/src/egg/eggNode.h

@@ -16,6 +16,7 @@
 #include <referenceCount.h>
 
 class EggGroupNode;
+class EggAlphaMode;
 
 ////////////////////////////////////////////////////////////////////
 // 	 Class : EggNode
@@ -36,6 +37,10 @@ public:
   INLINE bool is_under_transform() const;
   INLINE bool is_local_coord() const;
 
+  virtual EggAlphaMode *determine_alpha_mode();
+  virtual EggAlphaMode *determine_draw_order();
+  virtual EggAlphaMode *determine_bin();
+
   INLINE const LMatrix4d &get_vertex_frame() const;
   INLINE const LMatrix4d &get_node_frame() const;
   INLINE const LMatrix4d &get_vertex_frame_inv() const;

+ 60 - 0
panda/src/egg/eggPrimitive.cxx

@@ -13,6 +13,66 @@
 TypeHandle EggPrimitive::_type_handle;
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggPrimitive::determine_alpha_mode
+//       Access: Public, Virtual
+//  Description: Walks back up the hierarchy, looking for an EggPrimitive
+//               or EggPrimitive or some such object at this level or
+//               above this primitive that has an alpha_mode other than
+//               AM_unspecified.  Returns a valid EggAlphaMode pointer
+//               if one is found, or NULL otherwise.
+////////////////////////////////////////////////////////////////////
+EggAlphaMode *EggPrimitive::
+determine_alpha_mode() {
+  if (get_alpha_mode() != AM_unspecified) {
+    return this;
+  }
+  if (has_texture() && get_texture()->get_alpha_mode() != AM_unspecified) {
+    return get_texture();
+  }
+  return EggNode::determine_alpha_mode();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggPrimitive::determine_draw_order
+//       Access: Public, Virtual
+//  Description: Walks back up the hierarchy, looking for an EggPrimitive
+//               or EggPrimitive or some such object at this level or
+//               above this primitive that has a draw_order specified.
+//               Returns a valid EggAlphaMode pointer if one is found,
+//               or NULL otherwise.
+////////////////////////////////////////////////////////////////////
+EggAlphaMode *EggPrimitive::
+determine_draw_order() {
+  if (has_draw_order()) {
+    return this;
+  }
+  if (has_texture() && get_texture()->has_draw_order()) {
+    return get_texture();
+  }
+  return EggNode::determine_draw_order();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggPrimitive::determine_bin
+//       Access: Public, Virtual
+//  Description: Walks back up the hierarchy, looking for an EggPrimitive
+//               or EggPrimitive or some such object at this level or
+//               above this primitive that has a bin specified.  Returns a
+//               valid EggAlphaMode pointer if one is found, or NULL
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+EggAlphaMode *EggPrimitive::
+determine_bin() {
+  if (has_bin()) {
+    return this;
+  }
+  if (has_texture() && get_texture()->has_bin()) {
+    return get_texture();
+  }
+  return EggNode::determine_bin();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggPrimitive::reverse_vertex_ordering
 //       Access: Public, Virtual

+ 4 - 0
panda/src/egg/eggPrimitive.h

@@ -51,6 +51,10 @@ public:
   INLINE EggPrimitive &operator = (const EggPrimitive &copy);
   INLINE ~EggPrimitive();
 
+  virtual EggAlphaMode *determine_alpha_mode();
+  virtual EggAlphaMode *determine_draw_order();
+  virtual EggAlphaMode *determine_bin();
+
   INLINE void set_texture(PT(EggTexture) texture);
   INLINE void clear_texture();
   INLINE PT(EggTexture) get_texture() const;

+ 16 - 5
panda/src/egg/parser.yxx

@@ -384,7 +384,10 @@ texture_body:
     }
 
   } else if (cmp_nocase_uh(name, "draw_order") == 0) {
-    texture->set_draw_order(value);
+    texture->set_draw_order((int)value);
+
+  } else if (cmp_nocase_uh(name, "bin") == 0) {
+    texture->set_bin(strval);
 
   } else if (cmp_nocase_uh(name, "alpha_file") == 0) {
     texture->set_alpha_file(strval);
@@ -811,7 +814,9 @@ group_body:
       group->set_alpha_mode(a);
     }
   } else if (cmp_nocase_uh(name, "draw_order") == 0) {
-    group->set_draw_order(value);
+    group->set_draw_order((int)value);
+  } else if (cmp_nocase_uh(name, "bin") == 0) {
+    group->set_bin(strval);
   } else if (cmp_nocase_uh(name, "collide_mask") == 0) {
     group->set_collide_mask(value);
   } else if (cmp_nocase_uh(name, "from_collide_mask") == 0) {
@@ -1325,7 +1330,9 @@ primitive_body:
       primitive->set_alpha_mode(a);
     }
   } else if (cmp_nocase_uh(name, "draw_order") == 0) {
-    primitive->set_draw_order(value);
+    primitive->set_draw_order((int)value);
+  } else if (cmp_nocase_uh(name, "bin") == 0) {
+    primitive->set_bin(strval);
   } else {
     eggyywarning("Unknown scalar " + name);
   }
@@ -1373,7 +1380,9 @@ nurbs_surface_body:
       primitive->set_alpha_mode(a);
     }
   } else if (cmp_nocase_uh(name, "draw_order") == 0) {
-    primitive->set_draw_order(value);
+    primitive->set_draw_order((int)value);
+  } else if (cmp_nocase_uh(name, "bin") == 0) {
+    primitive->set_bin(strval);
   } else if (cmp_nocase_uh(name, "u_subdiv") == 0) {
     primitive->set_u_subdiv(value);
   } else if (cmp_nocase_uh(name, "v_subdiv") == 0) {
@@ -1418,7 +1427,9 @@ nurbs_curve_body:
       primitive->set_alpha_mode(a);
     }
   } else if (cmp_nocase_uh(name, "draw_order") == 0) {
-    primitive->set_draw_order(value);
+    primitive->set_draw_order((int)value);
+  } else if (cmp_nocase_uh(name, "bin") == 0) {
+    primitive->set_bin(strval);
   } else if (cmp_nocase_uh(name, "subdiv") == 0) {
     primitive->set_subdiv(value);
   } else if (cmp_nocase_uh(name, "type") == 0) {

+ 1 - 1
panda/src/egg2sg/Sources.pp

@@ -4,7 +4,7 @@
 #begin lib_target
   #define TARGET egg2sg
   #define LOCAL_LIBS \
-    collide egg builder loader chan char switchnode cull
+    cull collide egg builder loader chan char switchnode
 
   #define SOURCES \
     animBundleMaker.cxx animBundleMaker.h characterMaker.cxx \

+ 39 - 7
panda/src/egg2sg/eggLoader.cxx

@@ -40,6 +40,7 @@
 #include <decalTransition.h>
 #include <directRenderTransition.h>
 #include <pruneTransition.h>
+#include <depthWriteTransition.h>
 #include <animBundleNode.h>
 #include <character.h>
 #include <notify.h>
@@ -49,6 +50,7 @@
 #include <collisionPlane.h>
 #include <collisionSphere.h>
 #include <dftraverser.h>
+#include <geomBinTransition.h>
 
 #include <ctype.h>
 #include <algorithm>
@@ -895,17 +897,39 @@ setup_bucket(BuilderBucket &bucket, NamedNode *parent,
   }
 
   // Assign the appropriate properties to the bucket.
-  EggAlphaMode::AlphaMode am = egg_prim->get_alpha_mode();
+
+  // The three "alpha mode"-associated properties--alpha mode, draw
+  // order, and bin--can be defined directly at the primitive, at a
+  // group above the primitive, or an a texture applied to the
+  // primitive.  The EggNode::determine_*() functions can find the
+  // right pointer to the level at which this is actually defined for
+  // a given primitive.
+  EggAlphaMode::AlphaMode am = EggAlphaMode::AM_unspecified;
   bool implicit_alpha = false;
+  bool has_draw_order = false;
+  int draw_order = 0;
+  bool has_bin = false;
+  string bin;
+
+  EggAlphaMode *egg_alpha;
+  egg_alpha = egg_prim->determine_alpha_mode();
+  if (egg_alpha != (EggAlphaMode *)NULL) {
+    am = egg_alpha->get_alpha_mode();
+  }
+  egg_alpha = egg_prim->determine_draw_order();
+  if (egg_alpha != (EggAlphaMode *)NULL) {
+    has_draw_order = true;
+    draw_order = egg_alpha->get_draw_order();
+  }
+  egg_alpha = egg_prim->determine_bin();
+  if (egg_alpha != (EggAlphaMode *)NULL) {
+    has_bin = true;
+    bin = egg_alpha->get_bin();
+  }
 
   bucket._trans.set_transition(new TextureTransition(TextureTransition::off()));
   if (egg_prim->has_texture()) {
     PT(EggTexture) egg_tex = egg_prim->get_texture();
-    // If the primitive didn't specify an alpha mode, allow the
-    // texture to specify one.
-    if (am == EggAlphaMode::AM_unspecified) {
-      am = egg_tex->get_alpha_mode();
-    }
     
     const TextureDef &def = _textures[egg_tex];
     if (def._texture != (TextureTransition *)NULL) {
@@ -957,7 +981,8 @@ setup_bucket(BuilderBucket &bucket, NamedNode *parent,
     break;
 
   case EggAlphaMode::AM_blend_no_occlude:
-    bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_alpha_sorted));
+    bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_alpha));
+    bucket._trans.set_transition(new DepthWriteTransition(DepthWriteTransition::off()));
     break;
 
   case EggAlphaMode::AM_ms:
@@ -973,6 +998,13 @@ setup_bucket(BuilderBucket &bucket, NamedNode *parent,
     break;
   }
 
+  if (has_bin) {
+    bucket._trans.set_transition(new GeomBinTransition(bin, draw_order));
+
+  } else if (has_draw_order) {
+    bucket._trans.set_transition(new GeomBinTransition("fixed", draw_order));
+  }
+
   if (egg_prim->get_bface_flag()) {
     // The primitive is marked with backface culling disabled--we want
     // to see both sides.

+ 16 - 0
panda/src/express/typeHandle.cxx

@@ -667,3 +667,19 @@ get_parent_towards(TypeHandle type) const {
 TypedObject::
 ~TypedObject() { 
 }
+
+ 
+////////////////////////////////////////////////////////////////////
+//     Function: TypedObject::get_type
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TypeHandle TypedObject::
+get_type() const {
+  // Normally, this function should never be called, because it is a
+  // pure virtual function.  If it is called, you probably called
+  // get_type() on a recently-destructed object.
+  express_cat.warning()
+    << "TypedObject::get_type() called!\n";
+  return _type_handle;
+}

+ 8 - 7
panda/src/sgattrib/config_sgattrib.cxx

@@ -186,15 +186,16 @@ ConfigureFn(config_sgattrib) {
 
   //Registration of writeable object's creation
   //functions with BamReader's factory
-  RenderRelation::register_with_read_factory();
-  TransformTransition::register_with_read_factory();
-  TextureTransition::register_with_read_factory();
-  TextureApplyTransition::register_with_read_factory();
-  TransparencyTransition::register_with_read_factory();
+  BillboardTransition::register_with_read_factory();
+  ColorMatrixTransition::register_with_read_factory();
   CullFaceTransition::register_with_read_factory();
   DecalTransition::register_with_read_factory();
+  DepthWriteTransition::register_with_read_factory();
   PruneTransition::register_with_read_factory();
-  BillboardTransition::register_with_read_factory();
-  ColorMatrixTransition::register_with_read_factory();
+  RenderRelation::register_with_read_factory();
+  TextureApplyTransition::register_with_read_factory();
+  TextureTransition::register_with_read_factory();
+  TransformTransition::register_with_read_factory();
+  TransparencyTransition::register_with_read_factory();
 }
 

+ 57 - 0
panda/src/sgattrib/depthWriteTransition.cxx

@@ -6,6 +6,11 @@
 #include "depthWriteTransition.h"
 #include "depthWriteAttribute.h"
 
+#include <datagram.h>
+#include <datagramIterator.h>
+#include <bamReader.h>
+#include <bamWriter.h>
+
 TypeHandle DepthWriteTransition::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -28,3 +33,55 @@ NodeAttribute *DepthWriteTransition::
 make_attrib() const {
   return new DepthWriteAttribute;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteTransition::register_with_factory
+//       Access: Public, Static
+//  Description: Factory method to generate a DepthWriteTransition object
+////////////////////////////////////////////////////////////////////
+void DepthWriteTransition::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_DepthWriteTransition);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteTransition::write_datagram
+//       Access: Public
+//  Description: Function to write the important information in
+//               the particular object to a Datagram
+////////////////////////////////////////////////////////////////////
+void DepthWriteTransition::
+write_datagram(BamWriter *manager, Datagram &me) {
+  OnOffTransition::write_datagram(manager, me);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteTransition::make_DepthWriteTransition
+//       Access: Public
+//  Description: Factory method to generate a DepthWriteTransition object
+////////////////////////////////////////////////////////////////////
+TypedWriteable *DepthWriteTransition::
+make_DepthWriteTransition(const FactoryParams &params) {
+  DepthWriteTransition *me = new DepthWriteTransition;
+  BamReader *manager;
+  Datagram packet;
+
+  parse_params(params, manager, packet);
+  DatagramIterator scan(packet);
+
+  me->fillin(scan, manager);
+  return me;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteTransition::fillin
+//       Access: Protected
+//  Description: Function that reads out of the datagram (or asks
+//               manager to read) all of the data that is needed to
+//               re-create this object and stores it in the appropiate
+//               place
+////////////////////////////////////////////////////////////////////
+void DepthWriteTransition::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  OnOffTransition::fillin(scan, manager);
+}

+ 9 - 0
panda/src/sgattrib/depthWriteTransition.h

@@ -23,6 +23,15 @@ public:
   virtual NodeTransition *make_copy() const;
   virtual NodeAttribute *make_attrib() const;
 
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter* manager, Datagram &me);  
+
+  static TypedWriteable *make_DepthWriteTransition(const FactoryParams &params);
+
+protected:
+  void fillin(DatagramIterator& scan, BamReader* manager);
+
 public:
   virtual TypeHandle get_type() const {
     return get_class_type();