浏览代码

pgraph decals

David Rose 24 年之前
父节点
当前提交
366bcf8520
共有 52 个文件被更改,包括 1898 次插入258 次删除
  1. 0 3
      panda/src/display/Sources.pp
  2. 0 1
      panda/src/display/display_composite1.cxx
  3. 98 0
      panda/src/display/graphicsStateGuardian.cxx
  4. 6 0
      panda/src/display/graphicsStateGuardian.h
  5. 25 37
      panda/src/egg2pg/qpeggLoader.cxx
  6. 1 1
      panda/src/egg2pg/qpeggLoader.h
  7. 0 17
      panda/src/glgsg/glGraphicsStateGuardian.I
  8. 80 4
      panda/src/glgsg/glGraphicsStateGuardian.cxx
  9. 20 15
      panda/src/glgsg/glGraphicsStateGuardian.h
  10. 4 1
      panda/src/gobj/geom.cxx
  11. 12 2
      panda/src/gsgbase/graphicsStateGuardianBase.h
  12. 20 2
      panda/src/pgraph/Sources.pp
  13. 3 5
      panda/src/pgraph/binCullHandler.cxx
  14. 1 5
      panda/src/pgraph/binCullHandler.h
  15. 40 0
      panda/src/pgraph/colorWriteAttrib.I
  16. 170 0
      panda/src/pgraph/colorWriteAttrib.cxx
  17. 88 0
      panda/src/pgraph/colorWriteAttrib.h
  18. 14 0
      panda/src/pgraph/config_pgraph.cxx
  19. 12 4
      panda/src/pgraph/cullBin.cxx
  20. 3 5
      panda/src/pgraph/cullBin.h
  21. 7 10
      panda/src/pgraph/cullBinBackToFront.I
  22. 29 16
      panda/src/pgraph/cullBinBackToFront.cxx
  23. 9 12
      panda/src/pgraph/cullBinBackToFront.h
  24. 0 15
      panda/src/pgraph/cullBinUnsorted.I
  25. 24 11
      panda/src/pgraph/cullBinUnsorted.cxx
  26. 4 16
      panda/src/pgraph/cullBinUnsorted.h
  27. 35 0
      panda/src/pgraph/cullHandler.I
  28. 56 6
      panda/src/pgraph/cullHandler.cxx
  29. 10 9
      panda/src/pgraph/cullHandler.h
  30. 8 6
      panda/src/pgraph/cullResult.I
  31. 2 2
      panda/src/pgraph/cullResult.h
  32. 28 0
      panda/src/pgraph/decalAttrib.I
  33. 128 0
      panda/src/pgraph/decalAttrib.cxx
  34. 72 0
      panda/src/pgraph/decalAttrib.h
  35. 40 0
      panda/src/pgraph/depthTestAttrib.I
  36. 199 0
      panda/src/pgraph/depthTestAttrib.cxx
  37. 92 0
      panda/src/pgraph/depthTestAttrib.h
  38. 40 0
      panda/src/pgraph/depthWriteAttrib.I
  39. 170 0
      panda/src/pgraph/depthWriteAttrib.cxx
  40. 85 0
      panda/src/pgraph/depthWriteAttrib.h
  41. 1 1
      panda/src/pgraph/drawCullHandler.I
  42. 6 7
      panda/src/pgraph/drawCullHandler.cxx
  43. 4 8
      panda/src/pgraph/drawCullHandler.h
  44. 27 2
      panda/src/pgraph/nodeChain.I
  45. 1 1
      panda/src/pgraph/nodeChain.h
  46. 1 0
      panda/src/pgraph/pgraph_composite1.cxx
  47. 5 0
      panda/src/pgraph/pgraph_composite2.cxx
  48. 162 26
      panda/src/pgraph/qpcullTraverser.cxx
  49. 9 0
      panda/src/pgraph/qpcullTraverser.h
  50. 21 0
      panda/src/pgraph/renderState.I
  51. 20 6
      panda/src/pgraph/renderState.cxx
  52. 6 2
      panda/src/pgraph/renderState.h

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

@@ -13,7 +13,6 @@
      config_display.h displayRegion.I displayRegion.h  \
      displayRegionStack.I \
      displayRegionStack.h \
-     drawCullHandler.h drawCullHandler.I \
      frameBufferStack.I frameBufferStack.h \
      geomContext.I geomContext.h geomNodeContext.I geomNodeContext.h \
      graphicsChannel.I graphicsChannel.h \
@@ -33,7 +32,6 @@
      
  #define INCLUDED_SOURCES  \
      config_display.cxx displayRegion.cxx \
-     drawCullHandler.cxx \
      geomContext.cxx geomNodeContext.cxx graphicsChannel.cxx  \
      graphicsEngine.cxx \
      graphicsLayer.cxx graphicsPipe.cxx graphicsStateGuardian.cxx  \
@@ -46,7 +44,6 @@
     config_display.h \
     displayRegion.I displayRegion.h displayRegionStack.I \
     displayRegionStack.h \
-    drawCullHandler.h drawCullHandler.I \
     frameBufferStack.I frameBufferStack.h \
     geomContext.I geomContext.h geomNodeContext.I geomNodeContext.h \
     graphicsChannel.I graphicsChannel.h \

+ 0 - 1
panda/src/display/display_composite1.cxx

@@ -1,6 +1,5 @@
 
 #include "displayRegion.cxx"
-#include "drawCullHandler.cxx"
 #include "geomContext.cxx"
 #include "geomNodeContext.cxx"
 #include "graphicsChannel.cxx"

+ 98 - 0
panda/src/display/graphicsStateGuardian.cxx

@@ -22,6 +22,9 @@
 #include "textureContext.h"
 #include "renderBuffer.h"
 #include "colorAttrib.h"
+#include "renderState.h"
+#include "depthWriteAttrib.h"
+#include "colorWriteAttrib.h"
 
 #include "clockObject.h"
 #include "geomNode.h"
@@ -892,6 +895,101 @@ void GraphicsStateGuardian::
 end_decal(GeomNode *) {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::polygon_offset_decals
+//       Access: Public, Virtual
+//  Description: Returns true if this GSG can implement decals using a
+//               PolygonOffsetAttrib, or false if that is unreliable
+//               and the three-step rendering process should be used
+//               instead.
+////////////////////////////////////////////////////////////////////
+bool GraphicsStateGuardian::
+polygon_offset_decals() {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::begin_decal_base_first
+//       Access: Public, Virtual
+//  Description: Called during draw to begin a three-step rendering
+//               phase to draw decals.  The first step,
+//               begin_decal_base_first(), is called prior to drawing the
+//               base geometry.  It should set up whatever internal
+//               state is appropriate, as well as returning a
+//               RenderState object that should be applied to the base
+//               geometry for rendering.
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) GraphicsStateGuardian::
+begin_decal_base_first() {
+  // Turn off writing the depth buffer to render the base geometry.
+  static CPT(RenderState) decal_base_first;
+  if (decal_base_first == (const RenderState *)NULL) {
+    decal_base_first = RenderState::make
+      (DepthWriteAttrib::make(DepthWriteAttrib::M_off));
+  }
+  return decal_base_first;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::begin_decal_nested
+//       Access: Public, Virtual
+//  Description: Called during draw to begin a three-step rendering
+//               phase to draw decals.  The second step,
+//               begin_decal_nested(), is called after drawing the
+//               base geometry and prior to drawing any of the nested
+//               decal geometry that is to be applied to the base
+//               geometry.
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) GraphicsStateGuardian::
+begin_decal_nested() {
+  // We keep the depth buffer off during this operation, although
+  // perhaps it doesn't matter so much here.
+  static CPT(RenderState) decal_nested;
+  if (decal_nested == (const RenderState *)NULL) {
+    decal_nested = RenderState::make
+      (DepthWriteAttrib::make(DepthWriteAttrib::M_off));
+  }
+  return decal_nested;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::begin_decal_base_second
+//       Access: Public, Virtual
+//  Description: Called during draw to begin a three-step rendering
+//               phase to draw decals.  The third step,
+//               begin_decal_base_second(), is called after drawing the
+//               base geometry and the nested decal geometry, and
+//               prior to drawing the base geometry one more time (if
+//               needed).
+//
+//               It should return a RenderState object appropriate for
+//               rendering the base geometry the second time, or NULL
+//               if it is not necessary to re-render the base
+//               geometry.
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) GraphicsStateGuardian::
+begin_decal_base_second() {
+  // Now let the depth buffer go back on, but turn off writing the
+  // color buffer to render the base geometry after the second pass.
+  static CPT(RenderState) decal_base_second;
+  if (decal_base_second == (const RenderState *)NULL) {
+    decal_base_second = RenderState::make
+      (ColorWriteAttrib::make(ColorWriteAttrib::M_off));
+  }
+  return decal_base_second;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::finish_decal
+//       Access: Public, Virtual
+//  Description: Called during draw to clean up after decals are
+//               finished.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+finish_decal() {
+  // No need to do anything special here.
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_internal_coordinate_system
 //       Access: Public, Virtual

+ 6 - 0
panda/src/display/graphicsStateGuardian.h

@@ -135,6 +135,12 @@ public:
   virtual void begin_decal(GeomNode *base_geom, AllTransitionsWrapper &attrib);
   virtual void end_decal(GeomNode *base_geom);
 
+  virtual bool polygon_offset_decals();
+  virtual CPT(RenderState) begin_decal_base_first();
+  virtual CPT(RenderState) begin_decal_nested();
+  virtual CPT(RenderState) begin_decal_base_second();
+  virtual void finish_decal();
+
   virtual void reset();
 
   // *** QP

+ 25 - 37
panda/src/egg2pg/qpeggLoader.cxx

@@ -28,6 +28,7 @@
 #include "cullFaceAttrib.h"
 #include "cullBinAttrib.h"
 #include "transparencyAttrib.h"
+#include "decalAttrib.h"
 #include "qpgeomNode.h"
 #include "string_utils.h"
 #include "eggPrimitive.h"
@@ -39,6 +40,7 @@
 #include "eggPolygon.h"
 #include "eggBin.h"
 #include "eggTable.h"
+#include "nodeChain.h"
 
 #include <ctype.h>
 #include <algorithm>
@@ -123,16 +125,13 @@ build_graph() {
   make_node(&_data, _root);
   _builder.qpbuild();
 
-  /*
-  reset_directs();
+  //  reset_directs();
   reparent_decals();
 
-  apply_deferred_arcs(_root);
-  */
+  //  apply_deferred_arcs(_root);
 }
 
 
-/*
 ////////////////////////////////////////////////////////////////////
 //     Function: qpEggLoader::reparent_decals
 //       Access: Public
@@ -145,38 +144,34 @@ void qpEggLoader::
 reparent_decals() {
   Decals::const_iterator di;
   for (di = _decals.begin(); di != _decals.end(); ++di) {
-    RenderRelation *arc = (*di);
-    nassertv(arc != (RenderRelation *)NULL);
-    PandaNode *node = DCAST(PandaNode, arc->get_child());
+    PandaNode *node = (*di);
     nassertv(node != (PandaNode *)NULL);
 
+    // The NodeChain interface is best for this.
+    NodeChain parent(node);
+
     // First, search for the GeomNode.
-    GeomNode *geom = NULL;
-    int num_children =
-      node->get_num_children(RenderRelation::get_class_type());
+    NodeChain geom_parent;
+    int num_children = parent.get_num_children();
     for (int i = 0; i < num_children; i++) {
-      NodeRelation *child_arc =
-        node->get_child(RenderRelation::get_class_type(), i);
-      nassertv(child_arc != (NodeRelation *)NULL);
-      Node *child = child_arc->get_child();
-      nassertv(child != (Node *)NULL);
+      NodeChain child = parent.get_child(i);
 
-      if (child->is_of_type(GeomNode::get_class_type())) {
-        if (geom != (GeomNode *)NULL) {
+      if (child.node()->is_of_type(qpGeomNode::get_class_type())) {
+        if (!geom_parent.is_empty()) {
           // Oops, too many GeomNodes.
           egg2pg_cat.error()
-            << "Decal onto " << node->get_name()
-            << " uses base geometry with multiple states.\n";
+            << "Decal onto " << parent.node()->get_name()
+            << " uses base geometry with multiple GeomNodes.\n";
           _error = true;
         }
-        DCAST_INTO_V(geom, child);
+        geom_parent = child;
       }
     }
 
-    if (geom == (GeomNode *)NULL) {
+    if (geom_parent.is_empty()) {
       // No children were GeomNodes.
       egg2pg_cat.error()
-        << "Ignoring decal onto " << node->get_name()
+        << "Ignoring decal onto " << parent.node()->get_name()
         << "; no geometry within group.\n";
       _error = true;
     } else {
@@ -185,23 +180,21 @@ reparent_decals() {
       // list.
       int i = 0;
       while (i < num_children) {
-        NodeRelation *child_arc =
-          node->get_child(RenderRelation::get_class_type(), i);
-        nassertv(child_arc != (NodeRelation *)NULL);
-        Node *child = child_arc->get_child();
-        nassertv(child != (Node *)NULL);
+        NodeChain child = parent.get_child(i);
 
-        if (child->is_of_type(GeomNode::get_class_type())) {
+        if (child.node()->is_of_type(qpGeomNode::get_class_type())) {
           i++;
         } else {
-          child_arc->change_parent(geom);
+          child.reparent_to(geom_parent);
           num_children--;
         }
       }
+
+      // Finally, set the DecalAttrib on the base geometry.
+      geom_parent.node()->set_attrib(DecalAttrib::make());
     }
   }
 }
-*/
 
 /*
 ////////////////////////////////////////////////////////////////////
@@ -1516,7 +1509,6 @@ create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) {
     break;
   }
 
-  /*
   if (egg_group->get_decal_flag()) {
     if (egg_ignore_decals) {
       egg2pg_cat.error()
@@ -1528,12 +1520,8 @@ create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) {
     // descendant groups will be decaled onto the geometry within
     // this group.  This means we'll need to reparent things a bit
     // afterward.
-    _decals.insert(arc);
-
-    // We'll also set up the DecalTransition now.
-    arc->set_transition(new DecalTransition);
+    _decals.insert(node);
   }
-  */
 
   /*
   if (egg_group->get_direct_flag()) {

+ 1 - 1
panda/src/egg2pg/qpeggLoader.h

@@ -139,10 +139,10 @@ private:
   Materials _materials_bface;
   */
 
-  /*
   typedef pset<PandaNode *> Decals;
   Decals _decals;
 
+  /*
   typedef pset<PandaNode *> Directs;
   Directs _directs;
 

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

@@ -473,23 +473,6 @@ call_glBlendFunc(GLenum sfunc, GLenum dfunc) {
   }
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GLGraphicsStateGuardian::call_glDepthMask
-//       Access:
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void GLGraphicsStateGuardian::
-call_glDepthMask(GLboolean mask) {
-  if (_depth_mask != mask) {
-    _depth_mask = mask;
-#ifdef GSG_VERBOSE
-    glgsg_cat.debug()
-      << "glDepthMask(" << (int)mask << ")" << endl;
-#endif
-    glDepthMask(mask);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::call_glFogMode
 //       Access:

+ 80 - 4
panda/src/glgsg/glGraphicsStateGuardian.cxx

@@ -68,6 +68,9 @@
 #include "textureAttrib.h"
 #include "cullFaceAttrib.h"
 #include "transparencyAttrib.h"
+#include "depthTestAttrib.h"
+#include "depthWriteAttrib.h"
+#include "colorWriteAttrib.h"
 #include "clockObject.h"
 #include "string_utils.h"
 #include "dcast.h"
@@ -3143,8 +3146,7 @@ issue_depth_test(const DepthTestTransition *attrib) {
 void GLGraphicsStateGuardian::
 issue_depth_write(const DepthWriteTransition *attrib) {
   //  activate();
-
-  call_glDepthMask(attrib->is_on());
+  glDepthMask(attrib->is_on());
   report_errors();
 }
 
@@ -3544,6 +3546,55 @@ issue_transparency(const TransparencyAttrib *attrib) {
   report_errors();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::issue_color_write
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void GLGraphicsStateGuardian::
+issue_color_write(const ColorWriteAttrib *attrib) {
+  ColorWriteAttrib::Mode mode = attrib->get_mode();
+  if (mode == ColorWriteAttrib::M_off) {
+    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+  } else {
+    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+  }
+  report_errors();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::issue_depth_test
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void GLGraphicsStateGuardian::
+issue_depth_test(const DepthTestAttrib *attrib) {
+  DepthTestAttrib::Mode mode = attrib->get_mode();
+  if (mode == DepthTestAttrib::M_none) {
+    enable_depth_test(false);
+  } else {
+    enable_depth_test(true);
+    glDepthFunc(get_depth_func_type(mode));
+  }
+  report_errors();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::issue_depth_write
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void GLGraphicsStateGuardian::
+issue_depth_write(const DepthWriteAttrib *attrib) {
+  DepthWriteAttrib::Mode mode = attrib->get_mode();
+  if (mode == DepthWriteAttrib::M_off) {
+    glDepthMask(GL_FALSE);
+  } else {
+    glDepthMask(GL_TRUE);
+  }
+  report_errors();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::wants_normals
 //       Access: Public, Virtual
@@ -3603,7 +3654,7 @@ begin_decal(GeomNode *base_geom, AllTransitionsWrapper &attrib) {
 
     } else {
       // Turn off writing the depth buffer to render the base geometry.
-      call_glDepthMask(false);
+      glDepthMask(false);
       DepthWriteTransition *dwa = new DepthWriteTransition;
       dwa->set_off();
       attrib.set_transition(dwa);
@@ -3661,7 +3712,7 @@ end_decal(GeomNode *base_geom) {
       glPopMatrix();
 
       // Enable the writing to the depth buffer.
-      call_glDepthMask(true);
+      glDepthMask(true);
 
       // Disable the writing to the color buffer, however we have to
       // do this.
@@ -4360,6 +4411,31 @@ get_depth_func_type(DepthTestProperty::Mode m) const
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::get_depth_func_type
+//       Access: Protected
+//  Description: Maps from the depth func modes to gl version
+////////////////////////////////////////////////////////////////////
+GLenum GLGraphicsStateGuardian::
+get_depth_func_type(DepthTestAttrib::Mode m) const
+{
+  switch(m) {
+  case DepthTestAttrib::M_never: return GL_NEVER;
+  case DepthTestAttrib::M_less: return GL_LESS;
+  case DepthTestAttrib::M_equal: return GL_EQUAL;
+  case DepthTestAttrib::M_less_equal: return GL_LEQUAL;
+  case DepthTestAttrib::M_greater: return GL_GREATER;
+  case DepthTestAttrib::M_not_equal: return GL_NOTEQUAL;
+  case DepthTestAttrib::M_greater_equal: return GL_GEQUAL;
+  case DepthTestAttrib::M_always: return GL_ALWAYS;
+
+  default:
+    glgsg_cat.error()
+      << "Invalid DepthTestAttrib::Mode value" << endl;
+    return GL_LESS;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::get_stencil_func_type
 //       Access: Protected

+ 20 - 15
panda/src/glgsg/glGraphicsStateGuardian.h

@@ -21,19 +21,22 @@
 
 //#define GSG_VERBOSE
 
-#include <pandabase.h>
-
-#include <graphicsStateGuardian.h>
-#include <geomprimitives.h>
-#include <texture.h>
-#include <pixelBuffer.h>
-#include <displayRegion.h>
-#include <material.h>
-#include <textureApplyProperty.h>
-#include <depthTestProperty.h>
-#include <stencilProperty.h>
-#include <fog.h>
-#include <pt_Light.h>
+#include "pandabase.h"
+
+#include "graphicsStateGuardian.h"
+#include "geomprimitives.h"
+#include "texture.h"
+#include "pixelBuffer.h"
+#include "displayRegion.h"
+#include "material.h"
+#include "textureApplyProperty.h"
+#include "depthTestProperty.h"
+#include "stencilProperty.h"
+#include "fog.h"
+#include "pt_Light.h"
+
+#include "depthTestAttrib.h"
+#include "pointerToArray.h"
 
 #ifdef WIN32_VC
 // Must include windows.h before gl.h on NT
@@ -44,7 +47,6 @@
 
 #include <GL/gl.h>
 
-#include <pointerToArray.h>
 
 class PlaneNode;
 class Light;
@@ -160,6 +162,9 @@ public:
   virtual void issue_texture(const TextureAttrib *attrib);
   virtual void issue_cull_face(const CullFaceAttrib *attrib);
   virtual void issue_transparency(const TransparencyAttrib *attrib);
+  virtual void issue_color_write(const ColorWriteAttrib *attrib);
+  virtual void issue_depth_test(const DepthTestAttrib *attrib);
+  virtual void issue_depth_write(const DepthWriteAttrib *attrib);
 
   virtual bool wants_normals(void) const;
   virtual bool wants_texcoords(void) const;
@@ -212,7 +217,6 @@ protected:
   INLINE void call_glClipPlane(GLenum plane, const double equation[4]);
   INLINE void call_glLineWidth(GLfloat width);
   INLINE void call_glPointSize(GLfloat size);
-  INLINE void call_glDepthMask(GLboolean mask);
   INLINE void call_glFogMode(GLint mode);
   INLINE void call_glFogStart(GLfloat start);
   INLINE void call_glFogEnd(GLfloat end);
@@ -261,6 +265,7 @@ protected:
   GLenum get_internal_image_format(PixelBuffer::Format format);
   GLint get_texture_apply_mode_type( TextureApplyProperty::Mode am ) const;
   GLenum get_depth_func_type(DepthTestProperty::Mode m) const;
+  GLenum get_depth_func_type(DepthTestAttrib::Mode m) const;
   GLenum get_stencil_func_type(StencilProperty::Mode m) const;
   GLenum get_stencil_action_type(StencilProperty::Action a) const;
   GLenum get_fog_mode_type(Fog::Mode m) const;

+ 4 - 1
panda/src/gobj/geom.cxx

@@ -492,7 +492,9 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 output(ostream &out) const {
-  out << get_type() << " (" << _numprims << ")"
+  out << get_type() << " (" << _numprims << ")";
+
+  /*
       << " v:" << _coords.size()
       << " n:" << _norms.size()
       << " c:" << _colors.size()
@@ -501,6 +503,7 @@ output(ostream &out) const {
       << " ni:" << _nindex.size()
       << " ci:" << _cindex.size()
       << " ti:" << _tindex.size();
+  */
 }
 
 ////////////////////////////////////////////////////////////////////

+ 12 - 2
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -89,7 +89,7 @@ class MaterialAttrib;
 class RenderModeAttrib;
 class ColorBlendAttrib;
 class TextureApplyAttrib;
-class ColorMaskAttrib;
+class ColorWriteAttrib;
 class DepthTestAttrib;
 class DepthWriteAttrib;
 class TexGenAttrib;
@@ -156,6 +156,16 @@ public:
   // coordinate space differently.
   virtual float compute_distance_to(const LPoint3f &point) const=0;
 
+  // These are used to implement decals.  If polygon_offset_decals()
+  // returns true, none of the remaining functions will be called,
+  // since polygon offsets can be used to implement decals fully (and
+  // usually faster).
+  virtual bool polygon_offset_decals()=0;
+  virtual CPT(RenderState) begin_decal_base_first()=0;
+  virtual CPT(RenderState) begin_decal_nested()=0;
+  virtual CPT(RenderState) begin_decal_base_second()=0;
+  virtual void finish_decal()=0;
+
   // Defined here are some internal interface functions for the
   // GraphicsStateGuardian.  These are here to support
   // double-dispatching from Geoms and NodeTransitions, and are
@@ -238,7 +248,7 @@ public:
   virtual void issue_render_mode(const RenderModeAttrib *) { }
   virtual void issue_color_blend(const ColorBlendAttrib *) { }
   virtual void issue_texture_apply(const TextureApplyAttrib *) { }
-  virtual void issue_color_mask(const ColorMaskAttrib *) { }
+  virtual void issue_color_write(const ColorWriteAttrib *) { }
   virtual void issue_depth_test(const DepthTestAttrib *) { }
   virtual void issue_depth_write(const DepthWriteAttrib *) { }
   virtual void issue_tex_gen(const TexGenAttrib *) { }

+ 20 - 2
panda/src/pgraph/Sources.pp

@@ -10,6 +10,7 @@
     binCullHandler.h binCullHandler.I \
     qpcamera.h qpcamera.I \
     colorAttrib.h colorAttrib.I \
+    colorWriteAttrib.h colorWriteAttrib.I \
     config_pgraph.h \
     cullBin.h cullBin.I \
     cullBinAttrib.h cullBinAttrib.I \
@@ -17,9 +18,14 @@
     cullBinManager.h cullBinManager.I \
     cullBinUnsorted.h cullBinUnsorted.I \
     cullFaceAttrib.h cullFaceAttrib.I \
-    cullHandler.h \
+    cullHandler.h cullHandler.I \
     cullResult.h cullResult.I \
     qpcullTraverser.h qpcullTraverser.I \
+    cullableObject.h cullableObject.I \
+    decalAttrib.h decalAttrib.I \
+    depthTestAttrib.h depthTestAttrib.I \
+    depthWriteAttrib.h depthWriteAttrib.I \
+    drawCullHandler.h drawCullHandler.I \
     qpgeomNode.h qpgeomNode.I \
     qplensNode.h qplensNode.I \
     nodeChain.h nodeChain.I \
@@ -37,6 +43,7 @@
     binCullHandler.cxx \
     qpcamera.cxx \
     colorAttrib.cxx \
+    colorWriteAttrib.cxx \
     config_pgraph.cxx \
     cullBin.cxx \
     cullBinAttrib.cxx \
@@ -47,6 +54,11 @@
     cullHandler.cxx \
     cullResult.cxx \
     qpcullTraverser.cxx \
+    cullableObject.cxx \
+    decalAttrib.cxx \
+    depthTestAttrib.cxx \
+    depthWriteAttrib.cxx \
+    drawCullHandler.cxx \
     qpgeomNode.cxx \
     qplensNode.cxx \
     nodeChain.cxx \
@@ -69,6 +81,7 @@
     binCullHandler.h binCullHandler.I \
     qpcamera.h qpcamera.I \
     colorAttrib.h colorAttrib.I \
+    colorWriteAttrib.h colorWriteAttrib.I \
     config_pgraph.h \
     cullBin.h cullBin.I \
     cullBinAttrib.h cullBinAttrib.I \
@@ -76,9 +89,14 @@
     cullBinManager.h cullBinManager.I \
     cullBinUnsorted.h cullBinUnsorted.I \
     cullFaceAttrib.h cullFaceAttrib.I \
-    cullHandler.h \
+    cullHandler.h cullHandler.I \
     cullResult.h cullResult.I \
     qpcullTraverser.h qpcullTraverser.I \
+    cullableObject.h cullableObject.I \
+    decalAttrib.h decalAttrib.I \
+    depthTestAttrib.h depthTestAttrib.I \
+    depthWriteAttrib.h depthWriteAttrib.I \
+    drawCullHandler.h drawCullHandler.I \
     qpgeomNode.h qpgeomNode.I \
     qplensNode.h qplensNode.I \
     nodeChain.h nodeChain.I \

+ 3 - 5
panda/src/pgraph/binCullHandler.cxx

@@ -18,16 +18,14 @@
 
 #include "binCullHandler.h"
 
-
 ////////////////////////////////////////////////////////////////////
-//     Function: BinCullHandler::record_geom
+//     Function: BinCullHandler::record_object
 //       Access: Public, Virtual
 //  Description: This callback function is intended to be overridden
 //               by a derived class.  This is called as each Geom is
 //               discovered by the CullTraverser.
 ////////////////////////////////////////////////////////////////////
 void BinCullHandler::
-record_geom(Geom *geom, const TransformState *transform,
-            const RenderState *state) {
-  _cull_result->add_geom(geom, transform, state);
+record_object(CullableObject *object) {
+  _cull_result->add_object(object);
 }

+ 1 - 5
panda/src/pgraph/binCullHandler.h

@@ -35,11 +35,7 @@ class EXPCL_PANDA BinCullHandler : public CullHandler {
 public:
   INLINE BinCullHandler(CullResult *cull_result);
 
-  //  virtual void begin_decal();
-  virtual void record_geom(Geom *geom, const TransformState *transform,
-                           const RenderState *state);
-  //  virtual void push_decal();
-  //  virtual void pop_decal();
+  virtual void record_object(CullableObject *object);
 
 private:
   PT(CullResult) _cull_result;

+ 40 - 0
panda/src/pgraph/colorWriteAttrib.I

@@ -0,0 +1,40 @@
+// Filename: colorWriteAttrib.I
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::Constructor
+//       Access: Private
+//  Description: Use ColorWriteAttrib::make() to construct a new
+//               ColorWriteAttrib object.
+////////////////////////////////////////////////////////////////////
+INLINE ColorWriteAttrib::
+ColorWriteAttrib(ColorWriteAttrib::Mode mode) :
+  _mode(mode)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::get_mode
+//       Access: Published
+//  Description: Returns the color write mode.
+////////////////////////////////////////////////////////////////////
+INLINE ColorWriteAttrib::Mode ColorWriteAttrib::
+get_mode() const {
+  return _mode;
+}

+ 170 - 0
panda/src/pgraph/colorWriteAttrib.cxx

@@ -0,0 +1,170 @@
+// Filename: colorWriteAttrib.cxx
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "colorWriteAttrib.h"
+#include "graphicsStateGuardianBase.h"
+#include "dcast.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle ColorWriteAttrib::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new ColorWriteAttrib object that specifies
+//               how to cull geometry.  By Panda convention, vertices
+//               are ordered counterclockwise when seen from the
+//               front, so the M_cull_clockwise will cull backfacing
+//               polygons.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ColorWriteAttrib::
+make(ColorWriteAttrib::Mode mode) {
+  ColorWriteAttrib *attrib = new ColorWriteAttrib(mode);
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::issue
+//       Access: Public, Virtual
+//  Description: Calls the appropriate method on the indicated GSG
+//               to issue the graphics commands appropriate to the
+//               given attribute.  This is normally called
+//               (indirectly) only from
+//               GraphicsStateGuardian::set_state() or modify_state().
+////////////////////////////////////////////////////////////////////
+void ColorWriteAttrib::
+issue(GraphicsStateGuardianBase *gsg) const {
+  gsg->issue_color_write(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ColorWriteAttrib::
+output(ostream &out) const {
+  out << get_type() << ":";
+  switch (get_mode()) {
+  case M_off:
+    out << "off";
+    break;
+  case M_on:
+    out << "on";
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived ColorWriteAttrib
+//               types to return a unique number indicating whether
+//               this ColorWriteAttrib is equivalent to the other one.
+//
+//               This should return 0 if the two ColorWriteAttrib objects
+//               are equivalent, a number less than zero if this one
+//               should be sorted before the other one, and a number
+//               greater than zero otherwise.
+//
+//               This will only be called with two ColorWriteAttrib
+//               objects whose get_type() functions return the same.
+////////////////////////////////////////////////////////////////////
+int ColorWriteAttrib::
+compare_to_impl(const RenderAttrib *other) const {
+  const ColorWriteAttrib *ta;
+  DCAST_INTO_R(ta, other, 0);
+  return (int)_mode - (int)ta->_mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::make_default_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived ColorWriteAttrib
+//               types to specify what the default property for a
+//               ColorWriteAttrib of this type should be.
+//
+//               This should return a newly-allocated ColorWriteAttrib of
+//               the same type that corresponds to whatever the
+//               standard default for this kind of ColorWriteAttrib is.
+////////////////////////////////////////////////////////////////////
+RenderAttrib *ColorWriteAttrib::
+make_default_impl() const {
+  return new ColorWriteAttrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               ColorWriteAttrib.
+////////////////////////////////////////////////////////////////////
+void ColorWriteAttrib::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void ColorWriteAttrib::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  RenderAttrib::write_datagram(manager, dg);
+
+  dg.add_int8(_mode);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type ColorWriteAttrib is encountered
+//               in the Bam file.  It should create the ColorWriteAttrib
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *ColorWriteAttrib::
+make_from_bam(const FactoryParams &params) {
+  ColorWriteAttrib *attrib = new ColorWriteAttrib;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  attrib->fillin(scan, manager);
+
+  return attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new ColorWriteAttrib.
+////////////////////////////////////////////////////////////////////
+void ColorWriteAttrib::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  RenderAttrib::fillin(scan, manager);
+
+  _mode = (Mode)scan.get_int8();
+}

+ 88 - 0
panda/src/pgraph/colorWriteAttrib.h

@@ -0,0 +1,88 @@
+// Filename: colorWriteAttrib.h
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef COLORWRITEATTRIB_H
+#define COLORWRITEATTRIB_H
+
+#include "pandabase.h"
+
+#include "renderAttrib.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ColorWriteAttrib
+// Description : Enables or disables writing to the color buffer.
+//               This is primarily useful for certain special effects
+//               in which it is important to write to the depth buffer
+//               without affecting the color buffer.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ColorWriteAttrib : public RenderAttrib {
+PUBLISHED:
+  enum Mode {
+    M_off,
+    M_on
+  };
+
+private:
+  INLINE ColorWriteAttrib(Mode mode = M_on);
+
+PUBLISHED:
+  static CPT(RenderAttrib) make(Mode mode);
+
+  INLINE Mode get_mode() const;
+
+public:
+  virtual void issue(GraphicsStateGuardianBase *gsg) const;
+  virtual void output(ostream &out) const;
+
+protected:
+  virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual RenderAttrib *make_default_impl() const;
+
+private:
+  Mode _mode;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    RenderAttrib::init_type();
+    register_type(_type_handle, "ColorWriteAttrib",
+                  RenderAttrib::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "colorWriteAttrib.I"
+
+#endif
+

+ 14 - 0
panda/src/pgraph/config_pgraph.cxx

@@ -21,11 +21,16 @@
 #include "billboardAttrib.h"
 #include "qpcamera.h"
 #include "colorAttrib.h"
+#include "colorWriteAttrib.h"
 #include "cullFaceAttrib.h"
 #include "cullBin.h"
 #include "cullBinAttrib.h"
 #include "cullBinBackToFront.h"
 #include "cullBinUnsorted.h"
+#include "cullableObject.h"
+#include "decalAttrib.h"
+#include "depthTestAttrib.h"
+#include "depthWriteAttrib.h"
 #include "qpgeomNode.h"
 #include "qplensNode.h"
 #include "nodeChain.h"
@@ -72,11 +77,16 @@ init_libpgraph() {
   BillboardAttrib::init_type();
   qpCamera::init_type();
   ColorAttrib::init_type();
+  ColorWriteAttrib::init_type();
   CullFaceAttrib::init_type();
   CullBin::init_type();
   CullBinAttrib::init_type();
   CullBinBackToFront::init_type();
   CullBinUnsorted::init_type();
+  CullableObject::init_type();
+  DecalAttrib::init_type();
+  DepthTestAttrib::init_type();
+  DepthWriteAttrib::init_type();
   qpGeomNode::init_type();
   qpLensNode::init_type();
   NodeChain::init_type();
@@ -91,8 +101,12 @@ init_libpgraph() {
 
   BillboardAttrib::register_with_read_factory();
   ColorAttrib::register_with_read_factory();
+  ColorWriteAttrib::register_with_read_factory();
   CullBinAttrib::register_with_read_factory();
   CullFaceAttrib::register_with_read_factory();
+  DecalAttrib::register_with_read_factory();
+  DepthTestAttrib::register_with_read_factory();
+  DepthWriteAttrib::register_with_read_factory();
   qpGeomNode::register_with_read_factory();
   PandaNode::register_with_read_factory();
   RenderState::register_with_read_factory();

+ 12 - 4
panda/src/pgraph/cullBin.cxx

@@ -21,6 +21,15 @@
 
 TypeHandle CullBin::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullBin::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CullBin::
+~CullBin() {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBin::make_next
 //       Access: Public, Virtual
@@ -40,14 +49,13 @@ make_next() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBin::add_geom
+//     Function: CullBin::add_object
 //       Access: Public, Virtual
-//  Description: Adds the geom, along with its associated state, to
+//  Description: Adds a geom, along with its associated state, to
 //               the bin for rendering.
 ////////////////////////////////////////////////////////////////////
 void CullBin::
-add_geom(Geom *geom, const TransformState *transform,
-         const RenderState *state) {
+add_object(CullableObject *) {
 }
 
 ////////////////////////////////////////////////////////////////////

+ 3 - 5
panda/src/pgraph/cullBin.h

@@ -24,9 +24,7 @@
 #include "typedReferenceCount.h"
 #include "pointerTo.h"
 
-class Geom;
-class TransformState;
-class RenderState;
+class CullableObject;
 class GraphicsStateGuardianBase;
 
 ////////////////////////////////////////////////////////////////////
@@ -43,11 +41,11 @@ class GraphicsStateGuardianBase;
 class EXPCL_PANDA CullBin : public TypedReferenceCount {
 public:
   INLINE CullBin(GraphicsStateGuardianBase *gsg);
+  virtual ~CullBin();
 
   virtual PT(CullBin) make_next() const;
 
-  virtual void add_geom(Geom *geom, const TransformState *transform,
-                        const RenderState *state)=0;
+  virtual void add_object(CullableObject *object)=0;
   virtual void finish_cull();
 
   virtual void draw()=0;

+ 7 - 10
panda/src/pgraph/cullBinBackToFront.I

@@ -18,28 +18,25 @@
 
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinBackToFront::GeomData::Constructor
+//     Function: CullBinBackToFront::ObjectData::Constructor
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
-INLINE CullBinBackToFront::GeomData::
-GeomData(Geom *geom, const TransformState *transform,
-         const RenderState *state, float dist) :
-  _geom(geom),
-  _transform(transform),
-  _state(state),
+INLINE CullBinBackToFront::ObjectData::
+ObjectData(CullableObject *object, float dist) :
+  _object(object),
   _dist(dist)
 {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinBackToFront::GeomData::operator <
+//     Function: CullBinBackToFront::ObjectData::operator <
 //       Access: Public
 //  Description: Specifies the correct sort ordering for these
 //               objects.
 ////////////////////////////////////////////////////////////////////
-INLINE bool CullBinBackToFront::GeomData::
-operator < (const GeomData &other) const {
+INLINE bool CullBinBackToFront::ObjectData::
+operator < (const ObjectData &other) const {
   return _dist > other._dist;
 }
 

+ 29 - 16
panda/src/pgraph/cullBinBackToFront.cxx

@@ -19,6 +19,8 @@
 #include "cullBinBackToFront.h"
 #include "graphicsStateGuardianBase.h"
 #include "geometricBoundingVolume.h"
+#include "cullableObject.h"
+#include "cullHandler.h"
 
 #include <algorithm>
 
@@ -26,16 +28,29 @@
 TypeHandle CullBinBackToFront::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinBackToFront::add_geom
+//     Function: CullBinBackToFront::Destructor
 //       Access: Public, Virtual
-//  Description: Adds the geom, along with its associated state, to
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CullBinBackToFront::
+~CullBinBackToFront() {
+  Objects::iterator oi;
+  for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
+    CullableObject *object = (*oi)._object;
+    delete object;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinBackToFront::add_object
+//       Access: Public, Virtual
+//  Description: Adds a geom, along with its associated state, to
 //               the bin for rendering.
 ////////////////////////////////////////////////////////////////////
 void CullBinBackToFront::
-add_geom(Geom *geom, const TransformState *transform,
-         const RenderState *state) {
+add_object(CullableObject *object) {
   // Determine the center of the bounding volume.
-  const BoundingVolume &volume = geom->get_bound();
+  const BoundingVolume &volume = object->_geom->get_bound();
 
   if (!volume.is_empty() &&
       volume.is_of_type(GeometricBoundingVolume::get_class_type())) {
@@ -43,12 +58,11 @@ add_geom(Geom *geom, const TransformState *transform,
     DCAST_INTO_V(gbv, &volume);
     
     LPoint3f center = gbv->get_approx_center();
-    center = center * transform->get_mat();
+    nassertv(object->_transform != (const TransformState *)NULL);
+    center = center * object->_transform->get_mat();
     
-    // Oops!  Don't have compute_distance_to() here yet!
-    //    float distance = gsg->compute_distance_to(center);
-    float distance = -center[2];
-    _geoms.push_back(GeomData(geom, transform, state, distance));
+    float distance = _gsg->compute_distance_to(center);
+    _objects.push_back(ObjectData(object, distance));
   }
 }
 
@@ -63,7 +77,7 @@ add_geom(Geom *geom, const TransformState *transform,
 ////////////////////////////////////////////////////////////////////
 void CullBinBackToFront::
 finish_cull() {
-  sort(_geoms.begin(), _geoms.end());
+  sort(_objects.begin(), _objects.end());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -74,11 +88,10 @@ finish_cull() {
 ////////////////////////////////////////////////////////////////////
 void CullBinBackToFront::
 draw() {
-  Geoms::iterator gi;
-  for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) {
-    GeomData &geom_data = (*gi);
-    _gsg->set_state_and_transform(geom_data._state, geom_data._transform);
-    geom_data._geom->draw(_gsg);
+  Objects::const_iterator oi;
+  for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
+    CullableObject *object = (*oi)._object;
+    CullHandler::draw(object, _gsg);
   }
 }
 

+ 9 - 12
panda/src/pgraph/cullBinBackToFront.h

@@ -38,27 +38,24 @@
 class EXPCL_PANDA CullBinBackToFront : public CullBin {
 public:
   INLINE CullBinBackToFront(GraphicsStateGuardianBase *gsg);
+  virtual ~CullBinBackToFront();
 
-  virtual void add_geom(Geom *geom, const TransformState *transform,
-                        const RenderState *state);
+  virtual void add_object(CullableObject *object);
   virtual void finish_cull();
   virtual void draw();
 
 private:
-  class GeomData {
+  class ObjectData {
   public:
-    INLINE GeomData(Geom *geom, const TransformState *transform,
-                    const RenderState *state, float dist);
-    INLINE bool operator < (const GeomData &other) const;
-
-    PT(Geom) _geom;
-    CPT(TransformState) _transform;
-    CPT(RenderState) _state;
+    INLINE ObjectData(CullableObject *object, float dist);
+    INLINE bool operator < (const ObjectData &other) const;
+    
+    CullableObject *_object;
     float _dist;
   };
 
-  typedef pvector<GeomData> Geoms;
-  Geoms _geoms;
+  typedef pvector<ObjectData> Objects;
+  Objects _objects;
 
 public:
   static TypeHandle get_class_type() {

+ 0 - 15
panda/src/pgraph/cullBinUnsorted.I

@@ -17,21 +17,6 @@
 ////////////////////////////////////////////////////////////////////
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: CullBinUnsorted::GeomData::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE CullBinUnsorted::GeomData::
-GeomData(Geom *geom, const TransformState *transform,
-         const RenderState *state) :
-  _geom(geom),
-  _transform(transform),
-  _state(state)
-{
-}
-
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBinUnsorted::Constructor
 //       Access: Public

+ 24 - 11
panda/src/pgraph/cullBinUnsorted.cxx

@@ -17,36 +17,49 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "cullBinUnsorted.h"
+#include "cullHandler.h"
 #include "graphicsStateGuardianBase.h"
 
 
 TypeHandle CullBinUnsorted::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinUnsorted::add_geom
+//     Function: CullBinUnsorted::Destructor
 //       Access: Public, Virtual
-//  Description: Adds the geom, along with its associated state, to
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CullBinUnsorted::
+~CullBinUnsorted() {
+  Objects::iterator oi;
+  for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
+    CullableObject *object = (*oi);
+    delete object;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinUnsorted::add_object
+//       Access: Public, Virtual
+//  Description: Adds a geom, along with its associated state, to
 //               the bin for rendering.
 ////////////////////////////////////////////////////////////////////
 void CullBinUnsorted::
-add_geom(Geom *geom, const TransformState *transform,
-         const RenderState *state) {
-  _geoms.push_back(GeomData(geom, transform, state));
+add_object(CullableObject *object) {
+  _objects.push_back(object);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBinUnsorted::draw
 //       Access: Public
-//  Description: Draws all the geoms in the bin, in the appropriate
+//  Description: Draws all the objects in the bin, in the appropriate
 //               order.
 ////////////////////////////////////////////////////////////////////
 void CullBinUnsorted::
 draw() {
-  Geoms::iterator gi;
-  for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) {
-    GeomData &geom_data = (*gi);
-    _gsg->set_state_and_transform(geom_data._state, geom_data._transform);
-    geom_data._geom->draw(_gsg);
+  Objects::iterator oi;
+  for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
+    CullableObject *object = (*oi);
+    CullHandler::draw(object, _gsg);
   }
 }
 

+ 4 - 16
panda/src/pgraph/cullBinUnsorted.h

@@ -22,9 +22,6 @@
 #include "pandabase.h"
 
 #include "cullBin.h"
-#include "geom.h"
-#include "transformState.h"
-#include "renderState.h"
 #include "pointerTo.h"
 
 ////////////////////////////////////////////////////////////////////
@@ -37,23 +34,14 @@
 class EXPCL_PANDA CullBinUnsorted : public CullBin {
 public:
   INLINE CullBinUnsorted(GraphicsStateGuardianBase *gsg);
+  ~CullBinUnsorted();
 
-  virtual void add_geom(Geom *geom, const TransformState *transform,
-                        const RenderState *state);
+  virtual void add_object(CullableObject *object);
   virtual void draw();
 
 private:
-  class GeomData {
-  public:
-    INLINE GeomData(Geom *geom, const TransformState *transform,
-                    const RenderState *state);
-    PT(Geom) _geom;
-    CPT(TransformState) _transform;
-    CPT(RenderState) _state;
-  };
-
-  typedef pvector<GeomData> Geoms;
-  Geoms _geoms;
+  typedef pvector<CullableObject *> Objects;
+  Objects _objects;
 
 public:
   static TypeHandle get_class_type() {

+ 35 - 0
panda/src/pgraph/cullHandler.I

@@ -0,0 +1,35 @@
+// Filename: cullHandler.I
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullHandler::draw
+//       Access: Public, Static
+//  Description: Draws the indicated CullableObject, with full support
+//               for decals if they are attached to the object.  The
+//               appropriate state is set on the GSG before drawing
+//               the object.
+////////////////////////////////////////////////////////////////////
+INLINE void CullHandler::
+draw(CullableObject *object, GraphicsStateGuardianBase *gsg) {
+  if (object->_next != (CullableObject *)NULL) {
+    draw_with_decals(object, gsg);
+  } else {
+    gsg->set_state_and_transform(object->_state, object->_transform);
+    object->_geom->draw(gsg);
+  }
+}

+ 56 - 6
panda/src/pgraph/cullHandler.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "cullHandler.h"
+#include "cullableObject.h"
 #include "geom.h"
 #include "transformState.h"
 #include "renderState.h"
@@ -33,17 +34,66 @@ CullHandler::
 
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CullHandler::record_geom
+//     Function: CullHandler::record_object
 //       Access: Public, Virtual
 //  Description: This callback function is intended to be overridden
 //               by a derived class.  This is called as each Geom is
 //               discovered by the CullTraverser.
 //
-//               This default method simply outputs a message to cerr;
-//               it's not intended to be used except for debugging.
+//               The CullHandler becomes the owner of the
+//               CullableObject pointer and is expected to delete it
+//               later.
 ////////////////////////////////////////////////////////////////////
 void CullHandler::
-record_geom(Geom *geom, const TransformState *transform,
-            const RenderState *state) {
-  cerr << *geom << " " << *transform << " " << *state << "\n";
+record_object(CullableObject *object) {
+  cerr << *object->_geom << " " << *object->_transform << " " << *object->_state << "\n";
+  delete object;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullHandler::draw_with_decals
+//       Access: Public, Static
+//  Description: Draws the indicated CullableObject, assuming it has
+//               attached decals.
+////////////////////////////////////////////////////////////////////
+void CullHandler::
+draw_with_decals(CullableObject *object, GraphicsStateGuardianBase *gsg) {
+  // We draw with a three-step process.
+
+  // First, render all of the base geometry for the first pass.
+  CPT(RenderState) state = gsg->begin_decal_base_first();
+
+  CullableObject *base = object;
+  while (base != (CullableObject *)NULL && base->_geom != (Geom *)NULL) {
+    gsg->set_state_and_transform(base->_state->compose(state), base->_transform);
+    base->_geom->draw(gsg);
+
+    base = base->_next;
+  }
+
+  if (base != (CullableObject *)NULL) {
+    // Now, draw all the decals.
+    state = gsg->begin_decal_nested();
+
+    CullableObject *decal = base->_next;
+    while (decal != (CullableObject *)NULL) {
+      gsg->set_state_and_transform(decal->_state->compose(state), decal->_transform);
+      decal->_geom->draw(gsg);
+      
+      decal = decal->_next;
+    }
+  }
+
+  // And now, re-draw the base geometry, if required.
+  state = gsg->begin_decal_base_second();
+  if (state != (const RenderState *)NULL) {
+    base = object;
+    while (base != (CullableObject *)NULL && base->_geom != (Geom *)NULL) {
+      gsg->set_state_and_transform(base->_state->compose(state), base->_transform);
+      base->_geom->draw(gsg);
+      
+      base = base->_next;
+    }
+  }
+}
+

+ 10 - 9
panda/src/pgraph/cullHandler.h

@@ -20,10 +20,8 @@
 #define CULLHANDLER_H
 
 #include "pandabase.h"
-
-class Geom;
-class TransformState;
-class RenderState;
+#include "cullableObject.h"
+#include "graphicsStateGuardianBase.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : CullHandler
@@ -36,13 +34,16 @@ class EXPCL_PANDA CullHandler {
 public:
   virtual ~CullHandler();
 
-  //  virtual void begin_decal();
-  virtual void record_geom(Geom *geom, const TransformState *transform,
-                           const RenderState *state);
-  //  virtual void push_decal();
-  //  virtual void pop_decal();
+  virtual void record_object(CullableObject *object);
+
+  INLINE static void draw(CullableObject *object,
+                          GraphicsStateGuardianBase *gsg);
+  static void draw_with_decals(CullableObject *object,
+                               GraphicsStateGuardianBase *gsg);
 };
 
+#include "cullHandler.I"
+
 #endif
 
 

+ 8 - 6
panda/src/pgraph/cullResult.I

@@ -56,14 +56,16 @@ get_bin(int bin_index) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CullResult::add_geom
+//     Function: CullResult::add_object
 //       Access: Public
-//  Description: Adds the indicated Geom to the appropriate bin.
+//  Description: Adds the indicated CullableObject to the appropriate
+//               bin.  The bin becomes the owner of the object
+//               pointer, and will eventually delete it.
 ////////////////////////////////////////////////////////////////////
 INLINE void CullResult::
-add_geom(Geom *geom, const TransformState *transform,
-         const RenderState *state) {
-  CullBin *bin = get_bin(state->get_bin_index());
+add_object(CullableObject *object) {
+  nassertv(object->_state != (const RenderState *)NULL);
+  CullBin *bin = get_bin(object->_state->get_bin_index());
   nassertv(bin != (CullBin *)NULL);
-  bin->add_geom(geom, transform, state);
+  bin->add_object(object);
 }

+ 2 - 2
panda/src/pgraph/cullResult.h

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 #include "cullBin.h"
 #include "renderState.h"
+#include "cullableObject.h"
 
 #include "referenceCount.h"
 #include "pointerTo.h"
@@ -52,8 +53,7 @@ public:
 
   INLINE CullBin *get_bin(int bin_index);
 
-  INLINE void add_geom(Geom *geom, const TransformState *transform,
-                       const RenderState *state);
+  INLINE void add_object(CullableObject *object);
   void finish_cull();
   void draw();
 

+ 28 - 0
panda/src/pgraph/decalAttrib.I

@@ -0,0 +1,28 @@
+// Filename: decalAttrib.I
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DecalAttrib::Constructor
+//       Access: Private
+//  Description: Use DecalAttrib::make() to construct a new
+//               DecalAttrib object.
+////////////////////////////////////////////////////////////////////
+INLINE DecalAttrib::
+DecalAttrib() {
+}

+ 128 - 0
panda/src/pgraph/decalAttrib.cxx

@@ -0,0 +1,128 @@
+// Filename: decalAttrib.cxx
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "decalAttrib.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle DecalAttrib::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: DecalAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new DecalAttrib object.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) DecalAttrib::
+make() {
+  DecalAttrib *attrib = new DecalAttrib;
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DecalAttrib::compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived DecalAttrib
+//               types to return a unique number indicating whether
+//               this DecalAttrib is equivalent to the other one.
+//
+//               This should return 0 if the two DecalAttrib objects
+//               are equivalent, a number less than zero if this one
+//               should be sorted before the other one, and a number
+//               greater than zero otherwise.
+//
+//               This will only be called with two DecalAttrib
+//               objects whose get_type() functions return the same.
+////////////////////////////////////////////////////////////////////
+int DecalAttrib::
+compare_to_impl(const RenderAttrib *other) const {
+  // All DecalAttribs are equivalent--there are no properties to
+  // store.
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DecalAttrib::make_default_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived DecalAttrib
+//               types to specify what the default property for a
+//               DecalAttrib of this type should be.
+//
+//               This should return a newly-allocated DecalAttrib of
+//               the same type that corresponds to whatever the
+//               standard default for this kind of DecalAttrib is.
+////////////////////////////////////////////////////////////////////
+RenderAttrib *DecalAttrib::
+make_default_impl() const {
+  return new DecalAttrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DecalAttrib::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               DecalAttrib.
+////////////////////////////////////////////////////////////////////
+void DecalAttrib::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DecalAttrib::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void DecalAttrib::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  RenderAttrib::write_datagram(manager, dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DecalAttrib::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type DecalAttrib is encountered
+//               in the Bam file.  It should create the DecalAttrib
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *DecalAttrib::
+make_from_bam(const FactoryParams &params) {
+  DecalAttrib *attrib = new DecalAttrib;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  attrib->fillin(scan, manager);
+
+  return attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DecalAttrib::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new DecalAttrib.
+////////////////////////////////////////////////////////////////////
+void DecalAttrib::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  RenderAttrib::fillin(scan, manager);
+}

+ 72 - 0
panda/src/pgraph/decalAttrib.h

@@ -0,0 +1,72 @@
+// Filename: decalAttrib.h
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DECALATTRIB_H
+#define DECALATTRIB_H
+
+#include "pandabase.h"
+
+#include "renderAttrib.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : DecalAttrib
+// Description : Applied to a GeomNode to indicate that the children
+//               of this GeomNode are coplanar and should be drawn as
+//               decals (eliminating Z-fighting).
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA DecalAttrib : public RenderAttrib {
+private:
+  INLINE DecalAttrib();
+
+PUBLISHED:
+  static CPT(RenderAttrib) make();
+
+protected:
+  virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual RenderAttrib *make_default_impl() const;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    RenderAttrib::init_type();
+    register_type(_type_handle, "DecalAttrib",
+                  RenderAttrib::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "decalAttrib.I"
+
+#endif
+

+ 40 - 0
panda/src/pgraph/depthTestAttrib.I

@@ -0,0 +1,40 @@
+// Filename: depthTestAttrib.I
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::Constructor
+//       Access: Private
+//  Description: Use DepthTestAttrib::make() to construct a new
+//               DepthTestAttrib object.
+////////////////////////////////////////////////////////////////////
+INLINE DepthTestAttrib::
+DepthTestAttrib(DepthTestAttrib::Mode mode) :
+  _mode(mode)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::get_mode
+//       Access: Published
+//  Description: Returns the depth write mode.
+////////////////////////////////////////////////////////////////////
+INLINE DepthTestAttrib::Mode DepthTestAttrib::
+get_mode() const {
+  return _mode;
+}

+ 199 - 0
panda/src/pgraph/depthTestAttrib.cxx

@@ -0,0 +1,199 @@
+// Filename: depthTestAttrib.cxx
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "depthTestAttrib.h"
+#include "graphicsStateGuardianBase.h"
+#include "dcast.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle DepthTestAttrib::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new DepthTestAttrib object that specifies
+//               how to cull geometry.  By Panda convention, vertices
+//               are ordered counterclockwise when seen from the
+//               front, so the M_cull_clockwise will cull backfacing
+//               polygons.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) DepthTestAttrib::
+make(DepthTestAttrib::Mode mode) {
+  DepthTestAttrib *attrib = new DepthTestAttrib(mode);
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::issue
+//       Access: Public, Virtual
+//  Description: Calls the appropriate method on the indicated GSG
+//               to issue the graphics commands appropriate to the
+//               given attribute.  This is normally called
+//               (indirectly) only from
+//               GraphicsStateGuardian::set_state() or modify_state().
+////////////////////////////////////////////////////////////////////
+void DepthTestAttrib::
+issue(GraphicsStateGuardianBase *gsg) const {
+  gsg->issue_depth_test(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void DepthTestAttrib::
+output(ostream &out) const {
+  out << get_type() << ":";
+  switch (get_mode()) {
+  case M_none:
+    out << "none";
+    break;
+
+  case M_never:
+    out << "never";
+    break;
+
+  case M_less:
+    out << "less";
+    break;
+
+  case M_equal:
+    out << "equal";
+    break;
+
+  case M_less_equal:
+    out << "less_equal";
+    break;
+
+  case M_greater:
+    out << "greater";
+    break;
+
+  case M_not_equal:
+    out << "not_equal";
+    break;
+
+  case M_greater_equal:
+    out << "greater_equal";
+    break;
+
+  case M_always:
+    out << "always";
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived DepthTestAttrib
+//               types to return a unique number indicating whether
+//               this DepthTestAttrib is equivalent to the other one.
+//
+//               This should return 0 if the two DepthTestAttrib objects
+//               are equivalent, a number less than zero if this one
+//               should be sorted before the other one, and a number
+//               greater than zero otherwise.
+//
+//               This will only be called with two DepthTestAttrib
+//               objects whose get_type() functions return the same.
+////////////////////////////////////////////////////////////////////
+int DepthTestAttrib::
+compare_to_impl(const RenderAttrib *other) const {
+  const DepthTestAttrib *ta;
+  DCAST_INTO_R(ta, other, 0);
+  return (int)_mode - (int)ta->_mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::make_default_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived DepthTestAttrib
+//               types to specify what the default property for a
+//               DepthTestAttrib of this type should be.
+//
+//               This should return a newly-allocated DepthTestAttrib of
+//               the same type that corresponds to whatever the
+//               standard default for this kind of DepthTestAttrib is.
+////////////////////////////////////////////////////////////////////
+RenderAttrib *DepthTestAttrib::
+make_default_impl() const {
+  return new DepthTestAttrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               DepthTestAttrib.
+////////////////////////////////////////////////////////////////////
+void DepthTestAttrib::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void DepthTestAttrib::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  RenderAttrib::write_datagram(manager, dg);
+
+  dg.add_int8(_mode);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type DepthTestAttrib is encountered
+//               in the Bam file.  It should create the DepthTestAttrib
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *DepthTestAttrib::
+make_from_bam(const FactoryParams &params) {
+  DepthTestAttrib *attrib = new DepthTestAttrib;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  attrib->fillin(scan, manager);
+
+  return attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new DepthTestAttrib.
+////////////////////////////////////////////////////////////////////
+void DepthTestAttrib::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  RenderAttrib::fillin(scan, manager);
+
+  _mode = (Mode)scan.get_int8();
+}

+ 92 - 0
panda/src/pgraph/depthTestAttrib.h

@@ -0,0 +1,92 @@
+// Filename: depthTestAttrib.h
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DEPTHTESTATTRIB_H
+#define DEPTHTESTATTRIB_H
+
+#include "pandabase.h"
+
+#include "renderAttrib.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : DepthTestAttrib
+// Description : Enables or disables writing to the depth buffer.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA DepthTestAttrib : public RenderAttrib {
+PUBLISHED:
+  enum Mode {
+    M_none,             // No depth test; may still write to depth buffer.
+    M_never,            // Never draw.
+    M_less,             // incoming < stored
+    M_equal,            // incoming == stored
+    M_less_equal,       // incoming <= stored
+    M_greater,          // incoming > stored
+    M_not_equal,        // incoming != stored
+    M_greater_equal,    // incoming >= stored
+    M_always            // Always draw.  Same effect as none, more expensive.
+  };
+
+private:
+  INLINE DepthTestAttrib(Mode mode = M_less);
+
+PUBLISHED:
+  static CPT(RenderAttrib) make(Mode mode);
+
+  INLINE Mode get_mode() const;
+
+public:
+  virtual void issue(GraphicsStateGuardianBase *gsg) const;
+  virtual void output(ostream &out) const;
+
+protected:
+  virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual RenderAttrib *make_default_impl() const;
+
+private:
+  Mode _mode;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    RenderAttrib::init_type();
+    register_type(_type_handle, "DepthTestAttrib",
+                  RenderAttrib::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "depthTestAttrib.I"
+
+#endif
+

+ 40 - 0
panda/src/pgraph/depthWriteAttrib.I

@@ -0,0 +1,40 @@
+// Filename: depthWriteAttrib.I
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::Constructor
+//       Access: Private
+//  Description: Use DepthWriteAttrib::make() to construct a new
+//               DepthWriteAttrib object.
+////////////////////////////////////////////////////////////////////
+INLINE DepthWriteAttrib::
+DepthWriteAttrib(DepthWriteAttrib::Mode mode) :
+  _mode(mode)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::get_mode
+//       Access: Published
+//  Description: Returns the depth write mode.
+////////////////////////////////////////////////////////////////////
+INLINE DepthWriteAttrib::Mode DepthWriteAttrib::
+get_mode() const {
+  return _mode;
+}

+ 170 - 0
panda/src/pgraph/depthWriteAttrib.cxx

@@ -0,0 +1,170 @@
+// Filename: depthWriteAttrib.cxx
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "depthWriteAttrib.h"
+#include "graphicsStateGuardianBase.h"
+#include "dcast.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle DepthWriteAttrib::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new DepthWriteAttrib object that specifies
+//               how to cull geometry.  By Panda convention, vertices
+//               are ordered counterclockwise when seen from the
+//               front, so the M_cull_clockwise will cull backfacing
+//               polygons.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) DepthWriteAttrib::
+make(DepthWriteAttrib::Mode mode) {
+  DepthWriteAttrib *attrib = new DepthWriteAttrib(mode);
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::issue
+//       Access: Public, Virtual
+//  Description: Calls the appropriate method on the indicated GSG
+//               to issue the graphics commands appropriate to the
+//               given attribute.  This is normally called
+//               (indirectly) only from
+//               GraphicsStateGuardian::set_state() or modify_state().
+////////////////////////////////////////////////////////////////////
+void DepthWriteAttrib::
+issue(GraphicsStateGuardianBase *gsg) const {
+  gsg->issue_depth_write(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void DepthWriteAttrib::
+output(ostream &out) const {
+  out << get_type() << ":";
+  switch (get_mode()) {
+  case M_off:
+    out << "off";
+    break;
+  case M_on:
+    out << "on";
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived DepthWriteAttrib
+//               types to return a unique number indicating whether
+//               this DepthWriteAttrib is equivalent to the other one.
+//
+//               This should return 0 if the two DepthWriteAttrib objects
+//               are equivalent, a number less than zero if this one
+//               should be sorted before the other one, and a number
+//               greater than zero otherwise.
+//
+//               This will only be called with two DepthWriteAttrib
+//               objects whose get_type() functions return the same.
+////////////////////////////////////////////////////////////////////
+int DepthWriteAttrib::
+compare_to_impl(const RenderAttrib *other) const {
+  const DepthWriteAttrib *ta;
+  DCAST_INTO_R(ta, other, 0);
+  return (int)_mode - (int)ta->_mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::make_default_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived DepthWriteAttrib
+//               types to specify what the default property for a
+//               DepthWriteAttrib of this type should be.
+//
+//               This should return a newly-allocated DepthWriteAttrib of
+//               the same type that corresponds to whatever the
+//               standard default for this kind of DepthWriteAttrib is.
+////////////////////////////////////////////////////////////////////
+RenderAttrib *DepthWriteAttrib::
+make_default_impl() const {
+  return new DepthWriteAttrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               DepthWriteAttrib.
+////////////////////////////////////////////////////////////////////
+void DepthWriteAttrib::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void DepthWriteAttrib::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  RenderAttrib::write_datagram(manager, dg);
+
+  dg.add_int8(_mode);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type DepthWriteAttrib is encountered
+//               in the Bam file.  It should create the DepthWriteAttrib
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *DepthWriteAttrib::
+make_from_bam(const FactoryParams &params) {
+  DepthWriteAttrib *attrib = new DepthWriteAttrib;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  attrib->fillin(scan, manager);
+
+  return attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new DepthWriteAttrib.
+////////////////////////////////////////////////////////////////////
+void DepthWriteAttrib::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  RenderAttrib::fillin(scan, manager);
+
+  _mode = (Mode)scan.get_int8();
+}

+ 85 - 0
panda/src/pgraph/depthWriteAttrib.h

@@ -0,0 +1,85 @@
+// Filename: depthWriteAttrib.h
+// Created by:  drose (04Mar02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DEPTHWRITEATTRIB_H
+#define DEPTHWRITEATTRIB_H
+
+#include "pandabase.h"
+
+#include "renderAttrib.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : DepthWriteAttrib
+// Description : Enables or disables writing to the depth buffer.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA DepthWriteAttrib : public RenderAttrib {
+PUBLISHED:
+  enum Mode {
+    M_off,
+    M_on
+  };
+
+private:
+  INLINE DepthWriteAttrib(Mode mode = M_on);
+
+PUBLISHED:
+  static CPT(RenderAttrib) make(Mode mode);
+
+  INLINE Mode get_mode() const;
+
+public:
+  virtual void issue(GraphicsStateGuardianBase *gsg) const;
+  virtual void output(ostream &out) const;
+
+protected:
+  virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual RenderAttrib *make_default_impl() const;
+
+private:
+  Mode _mode;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    RenderAttrib::init_type();
+    register_type(_type_handle, "DepthWriteAttrib",
+                  RenderAttrib::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "depthWriteAttrib.I"
+
+#endif
+

+ 1 - 1
panda/src/display/drawCullHandler.I → panda/src/pgraph/drawCullHandler.I

@@ -23,7 +23,7 @@
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE DrawCullHandler::
-DrawCullHandler(GraphicsStateGuardian *gsg) :
+DrawCullHandler(GraphicsStateGuardianBase *gsg) :
   _gsg(gsg)
 {
 }

+ 6 - 7
panda/src/display/drawCullHandler.cxx → panda/src/pgraph/drawCullHandler.cxx

@@ -17,23 +17,22 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "drawCullHandler.h"
+#include "cullableObject.h"
 #include "geom.h"
 #include "transformState.h"
 #include "renderState.h"
-#include "graphicsStateGuardian.h"
+#include "graphicsStateGuardianBase.h"
 
 
 ////////////////////////////////////////////////////////////////////
-//     Function: DrawCullHandler::record_geom
+//     Function: DrawCullHandler::record_object
 //       Access: Public, Virtual
 //  Description: This callback function is intended to be overridden
 //               by a derived class.  This is called as each Geom is
 //               discovered by the CullTraverser.
 ////////////////////////////////////////////////////////////////////
 void DrawCullHandler::
-record_geom(Geom *geom, const TransformState *transform,
-            const RenderState *state) {
-  _gsg->set_transform(transform);
-  _gsg->set_state(state);
-  geom->draw(_gsg);
+record_object(CullableObject *object) {
+  draw(object, _gsg);
+  delete object;
 }

+ 4 - 8
panda/src/display/drawCullHandler.h → panda/src/pgraph/drawCullHandler.h

@@ -22,7 +22,7 @@
 #include "pandabase.h"
 #include "cullHandler.h"
 
-class GraphicsStateGuardian;
+class GraphicsStateGuardianBase;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : DrawCullHandler
@@ -37,16 +37,12 @@ class GraphicsStateGuardian;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA DrawCullHandler : public CullHandler {
 public:
-  INLINE DrawCullHandler(GraphicsStateGuardian *gsg);
+  INLINE DrawCullHandler(GraphicsStateGuardianBase *gsg);
 
-  //  virtual void begin_decal();
-  virtual void record_geom(Geom *geom, const TransformState *transform,
-                           const RenderState *state);
-  //  virtual void push_decal();
-  //  virtual void pop_decal();
+  virtual void record_object(CullableObject *object);
 
 private:
-  GraphicsStateGuardian *_gsg;
+  GraphicsStateGuardianBase *_gsg;
 };
 
 #include "drawCullHandler.I"

+ 27 - 2
panda/src/pgraph/nodeChain.I

@@ -163,6 +163,31 @@ node() const {
   return _head->get_node();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodeChain::get_num_children
+//       Access: Published
+//  Description: Returns the number of children of the bottom node.
+////////////////////////////////////////////////////////////////////
+INLINE int NodeChain::
+get_num_children() const {
+  nassertr(!is_empty(), 0);
+  return _head->get_node()->get_num_children();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeChain::get_child
+//       Access: Published
+//  Description: Returns a NodeChain representing the nth child of the
+//               bottom node.
+////////////////////////////////////////////////////////////////////
+INLINE NodeChain NodeChain::
+get_child(int n) const {
+  nassertr(n >= 0 && n < get_num_children(), NodeChain());
+  NodeChain child;
+  child._head = PandaNode::get_component(_head, _head->get_node()->get_child(n));
+  return child;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeChain::has_parent
 //       Access: Published
@@ -184,8 +209,8 @@ has_parent() const {
 INLINE NodeChain NodeChain::
 get_parent() const {
   nassertr(has_parent(), NodeChain::fail());
-  NodeChain parent(*this);
-  parent._head = parent._head->get_next();
+  NodeChain parent;
+  parent._head = _head->get_next();
   return parent;
 }
 

+ 1 - 1
panda/src/pgraph/nodeChain.h

@@ -91,9 +91,9 @@ PUBLISHED:
   /*
   NodeChainCollection get_siblings() const;
   NodeChainCollection get_children() const;
+  */
   INLINE int get_num_children() const;
   INLINE NodeChain get_child(int n) const;
-  */
 
   INLINE bool has_parent() const;
   INLINE NodeChain get_parent() const;

+ 1 - 0
panda/src/pgraph/pgraph_composite1.cxx

@@ -2,6 +2,7 @@
 #include "binCullHandler.cxx"
 #include "qpcamera.cxx"
 #include "colorAttrib.cxx"
+#include "colorWriteAttrib.cxx"
 #include "config_pgraph.cxx"
 #include "cullBin.cxx"
 #include "cullBinAttrib.cxx"

+ 5 - 0
panda/src/pgraph/pgraph_composite2.cxx

@@ -1,3 +1,8 @@
+#include "cullableObject.cxx"
+#include "decalAttrib.cxx"
+#include "depthTestAttrib.cxx"
+#include "depthWriteAttrib.cxx"
+#include "drawCullHandler.cxx"
 #include "qpgeomNode.cxx"
 #include "qplensNode.cxx"
 #include "nodeChain.cxx"

+ 162 - 26
panda/src/pgraph/qpcullTraverser.cxx

@@ -167,10 +167,7 @@ r_traverse(PandaNode *node,
       // actually culling an object we simply force it to be drawn in
       // red wireframe.
       view_frustum = (GeometricBoundingVolume *)NULL;
-      CPT(RenderState) fake_effect = RenderState::make
-        (ColorAttrib::make_flat(Colorf(1.0f, 0.0f, 0.0f, 1.0f)),
-         TextureAttrib::make_off(),
-         1000);
+      CPT(RenderState) fake_effect = get_fake_view_frustum_cull_effect();
       next_state = next_state->compose(fake_effect);
 
     } else if ((result & BoundingVolume::IF_all) != 0) {
@@ -196,16 +193,16 @@ r_traverse(PandaNode *node,
   PT(GeometricBoundingVolume) next_view_frustum = view_frustum;
   PT(GeometricBoundingVolume) next_guard_band = guard_band;
 
-  const TransformState *transform = node->get_transform();
-  if (!transform->is_identity()) {
-    next_render_transform = render_transform->compose(transform);
-    next_net_transform = net_transform->compose(transform);
+  const TransformState *node_transform = node->get_transform();
+  if (!node_transform->is_identity()) {
+    next_render_transform = render_transform->compose(node_transform);
+    next_net_transform = net_transform->compose(node_transform);
 
     if ((view_frustum != (GeometricBoundingVolume *)NULL) ||
         (guard_band != (GeometricBoundingVolume *)NULL)) {
       // We need to move the viewing frustums into the node's
       // coordinate space by applying the node's inverse transform.
-      if (transform->is_singular()) {
+      if (node_transform->is_singular()) {
         // But we can't invert a singular transform!  Instead of
         // trying, we'll just give up on frustum culling from this
         // point down.
@@ -214,7 +211,7 @@ r_traverse(PandaNode *node,
 
       } else {
         CPT(TransformState) inv_transform = 
-          transform->invert_compose(TransformState::make_identity());
+          node_transform->invert_compose(TransformState::make_identity());
         
         if (view_frustum != (GeometricBoundingVolume *)NULL) {
           next_view_frustum = DCAST(GeometricBoundingVolume, view_frustum->make_copy());
@@ -229,9 +226,10 @@ r_traverse(PandaNode *node,
     }
   }
 
-  next_state = next_state->compose(node->get_state());
+  const RenderState *node_state = node->get_state();
+  next_state = next_state->compose(node_state);
 
-  const BillboardAttrib *billboard = state->get_billboard();
+  const BillboardAttrib *billboard = node_state->get_billboard();
   if (billboard != (const BillboardAttrib *)NULL) {
     // Got to apply a billboard transform here.
     CPT(TransformState) billboard_transform = 
@@ -245,24 +243,162 @@ r_traverse(PandaNode *node,
     next_view_frustum = (GeometricBoundingVolume *)NULL;
   }
 
-  if (node->is_geom_node()) {
-    qpGeomNode *geom_node;
-    DCAST_INTO_V(geom_node, node);
-    
-    int num_geoms = geom_node->get_num_geoms();
-    for (int i = 0; i < num_geoms; i++) {
-      Geom *geom = geom_node->get_geom(i);
-      CPT(RenderState) geom_state = 
-        next_state->compose(geom_node->get_geom_state(i));
-      _cull_handler->record_geom(geom, next_render_transform, geom_state);
+  if (node_state->has_decal()) {
+    start_decal(node, next_render_transform, next_state);
+
+  } else {
+    if (node->is_geom_node()) {
+      qpGeomNode *geom_node = DCAST(qpGeomNode, node);
+      
+      // Get all the Geoms, with no decalling.
+      int num_geoms = geom_node->get_num_geoms();
+      for (int i = 0; i < num_geoms; i++) {
+        CullableObject *object = new CullableObject;
+        object->_geom = geom_node->get_geom(i);
+        object->_state = next_state->compose(geom_node->get_geom_state(i));
+        object->_transform = next_render_transform;
+        _cull_handler->record_object(object);
+      }
+    }
+
+    // Now visit all the node's children.
+    PandaNode::Children cr = node->get_children();
+    int num_children = cr.get_num_children();
+    for (int i = 0; i < num_children; i++) {
+      r_traverse(cr.get_child(i), next_render_transform, next_net_transform,
+                 next_state, next_view_frustum, next_guard_band);
     }
   }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::start_decal
+//       Access: Private
+//  Description: Collects a base node and all of the decals applied to
+//               it.  This involves recursing below the base GeomNode
+//               to find all the decal geoms; we don't bother to apply
+//               any view-frustum culling at this point, and we don't
+//               presently support billboards or LOD's within the
+//               decals.  Hard to justify the duplicate code this
+//               would require.
+////////////////////////////////////////////////////////////////////
+void qpCullTraverser::
+start_decal(PandaNode *node, 
+            const TransformState *render_transform,
+            const RenderState *state) {
+  if (!node->is_geom_node()) {
+    pgraph_cat.error()
+      << "DecalAttrib applied to " << *node << ", not a GeomNode.\n";
+    return;
+  }
+
+  // Build a chain of CullableObjects.  The head of the chain will be
+  // all of the base Geoms in order, followed by an empty
+  // CullableObject node, followed by all of the decal Geoms, in
+  // order.
 
-  // Now visit all the node's children.
+  const TransformState *next_render_transform = render_transform;
+  const RenderState *next_state = state;
+
+  // Since the CullableObject is a linked list which gets built in
+  // LIFO order, we start with the decals.
+  CullableObject *decals = (CullableObject *)NULL;
   PandaNode::Children cr = node->get_children();
   int num_children = cr.get_num_children();
-  for (int i = 0; i < num_children; i++) {
-    r_traverse(cr.get_child(i), next_render_transform, next_net_transform,
-               next_state, next_view_frustum, next_guard_band);
+  for (int i = num_children - 1; i >= 0; i--) {
+    decals =
+      r_get_decals(cr.get_child(i), next_render_transform, next_state, decals);
+  }
+
+  // Now create a new, empty CullableObject to separate the decals
+  // from the non-decals.
+  CullableObject *separator = new CullableObject(decals);
+
+  // And now get the base Geoms, again in reverse order.
+  CullableObject *object = separator;
+  qpGeomNode *geom_node = DCAST(qpGeomNode, node);
+      
+  // Get all the Geoms, with no decalling.
+  int num_geoms = geom_node->get_num_geoms();
+  for (int i = num_geoms - 1; i >= 0; i--) {
+    object = new CullableObject(object);
+    object->_geom = geom_node->get_geom(i);
+    object->_state = next_state->compose(geom_node->get_geom_state(i));
+    object->_transform = next_render_transform;
+  }
+
+  if (object != separator) {
+    // Finally, send the whole list down to the CullHandler for
+    // processing.  The first Geom in the node now represents the
+    // overall state.
+    _cull_handler->record_object(object);
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::r_get_decals
+//       Access: Private
+//  Description: Recursively gets all the decals applied to a
+//               particular GeomNode.  These are built into a
+//               CullableObject list in LIFO order (so that the
+//               traversing the list will extract them in the order
+//               they were encountered in the scene graph).
+////////////////////////////////////////////////////////////////////
+CullableObject *qpCullTraverser::
+r_get_decals(PandaNode *node, 
+             const TransformState *render_transform,
+             const RenderState *state,
+             CullableObject *decals) {
+  const TransformState *node_transform = node->get_transform();
+  const RenderState *node_state = node->get_state();
+
+  CPT(TransformState) next_render_transform = 
+    render_transform->compose(node_transform);
+  CPT(RenderState) next_state =
+    state->compose(node_state);
+
+  // First, visit all of the node's children.
+  PandaNode::Children cr = node->get_children();
+  int num_children = cr.get_num_children();
+  for (int i = num_children - 1; i >= 0; i--) {
+    decals =
+      r_get_decals(cr.get_child(i), next_render_transform, next_state, decals);
+  }
+
+  // Now, tack on any geoms within the node.
+  if (node->is_geom_node()) {
+    qpGeomNode *geom_node = DCAST(qpGeomNode, node);
+
+    int num_geoms = geom_node->get_num_geoms();
+    for (int i = num_geoms - 1; i >= 0; i--) {
+      decals = new CullableObject(decals);
+      decals->_geom = geom_node->get_geom(i);
+      decals->_state = next_state->compose(geom_node->get_geom_state(i));
+      decals->_transform = next_render_transform;
+    }
+  }
+
+  return decals;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpCullTraverser::get_fake_view_frustum_cull_effect
+//       Access: Private, Static
+//  Description: Returns a RenderState for rendering stuff in red
+//               wireframe, strictly for the fake_view_frustum_cull
+//               effect.
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) qpCullTraverser::
+get_fake_view_frustum_cull_effect() {
+  // Once someone asks for this pointer, we hold its reference count
+  // and never free it.
+  static CPT(RenderState) effect = (const RenderState *)NULL;
+  if (effect == (const RenderState *)NULL) {
+    effect = RenderState::make
+      (ColorAttrib::make_flat(Colorf(1.0f, 0.0f, 0.0f, 1.0f)),
+       TextureAttrib::make_off(),
+       1000);
+  }
+  return effect;
+}
+

+ 9 - 0
panda/src/pgraph/qpcullTraverser.h

@@ -56,6 +56,15 @@ private:
                   const RenderState *state,
                   GeometricBoundingVolume *view_frustum,
                   GeometricBoundingVolume *guard_band);
+  void start_decal(PandaNode *node, 
+                   const TransformState *render_transform,
+                   const RenderState *state);
+  CullableObject *r_get_decals(PandaNode *node, 
+                               const TransformState *render_transform,
+                               const RenderState *state,
+                               CullableObject *decals);
+
+  static CPT(RenderState) get_fake_view_frustum_cull_effect();
 
   CPT(RenderState) _initial_state;
   CPT(TransformState) _camera_transform;

+ 21 - 0
panda/src/pgraph/renderState.I

@@ -181,6 +181,27 @@ get_billboard() const {
   return _billboard;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::has_decal
+//       Access: Public
+//  Description: This function is provided as an optimization, to
+//               speed up the render-time checking for the existance
+//               of a DecalAttrib on this state.  It returns true if a
+//               DecalAttrib exists, false otherwise.  Note that since
+//               there is no additional information stored on the
+//               DecalAttrib, there's no point in returning it if it
+//               exists.
+////////////////////////////////////////////////////////////////////
+INLINE bool RenderState::
+has_decal() const {
+  if ((_flags & F_checked_decal) == 0) {
+    // We pretend this function is const, even though it transparently
+    // modifies the internal decal cache.
+    ((RenderState *)this)->determine_decal();
+  }
+  return ((_flags & F_has_decal) != 0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::get_bin_index
 //       Access: Public

+ 20 - 6
panda/src/pgraph/renderState.cxx

@@ -540,9 +540,9 @@ write(ostream &out, int indent_level) const {
 CPT(RenderState) RenderState::
 issue_delta_modify(const RenderState *other, 
                    GraphicsStateGuardianBase *gsg) const {
-  if (other == this) {
-    // If the state doesn't change, that's a trivial special case.
-    return other;
+  if (other->is_empty()) {
+    // If the other state is empty, that's a trivial special case.
+    return this;
   }
 
   // First, build a new Attributes member that represents the union of
@@ -622,9 +622,9 @@ issue_delta_modify(const RenderState *other,
 CPT(RenderState) RenderState::
 issue_delta_set(const RenderState *other, 
                 GraphicsStateGuardianBase *gsg) const {
-  if (other->is_empty()) {
-    // If the other state is empty, that's a trivial special case.
-    return this;
+  if (other == this) {
+    // If the state doesn't change, that's a trivial special case.
+    return other;
   }
 
   // We don't need to build a new RenderState, because the return
@@ -860,6 +860,20 @@ determine_billboard() {
   _flags |= F_checked_billboard;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::determine_decal
+//       Access: Private
+//  Description: This is the private implementation of has_decal().
+////////////////////////////////////////////////////////////////////
+void RenderState::
+determine_decal() {
+  const RenderAttrib *attrib = get_attrib(DecalAttrib::get_class_type());
+  if (attrib != (const RenderAttrib *)NULL) {
+    _flags |= F_has_decal;
+  }
+  _flags |= F_checked_decal;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::determine_bin_index
 //       Access: Private

+ 6 - 2
panda/src/pgraph/renderState.h

@@ -88,6 +88,7 @@ PUBLISHED:
 
 public:
   INLINE const BillboardAttrib *get_billboard() const;
+  INLINE bool has_decal() const;
   INLINE int get_bin_index() const;
   INLINE int get_draw_order() const;
 
@@ -103,6 +104,7 @@ private:
   CPT(RenderState) do_compose(const RenderState *other) const;
   CPT(RenderState) do_invert_compose(const RenderState *other) const;
   void determine_billboard();
+  void determine_decal();
   void determine_bin_index();
 
 private:
@@ -157,8 +159,8 @@ private:
   typedef ov_set<Attribute> Attributes;
   Attributes _attributes;
 
-  // We cache the pointer to the BillboardAttrib stored in the state,
-  // if there is one.
+  // We cache the pointer to the some critical attributes stored in
+  // the state, if they exist.
   const BillboardAttrib *_billboard;
   
   // We also cache the index to the associated GeomBin.
@@ -168,6 +170,8 @@ private:
   enum Flags {
     F_checked_billboard    = 0x0001,
     F_checked_bin_index    = 0x0002,
+    F_checked_decal        = 0x0004,
+    F_has_decal            = 0x0008,
   };
   short _flags;