Browse Source

initial rough pass at hierarchical z-buffer visibility. experimental.

David Rose 20 years ago
parent
commit
c0844239f1
35 changed files with 1643 additions and 74 deletions
  1. 17 12
      panda/src/cull/Sources.pp
  2. 16 0
      panda/src/cull/config_cull.cxx
  3. 5 0
      panda/src/cull/config_cull.h
  4. 1 2
      panda/src/cull/cullBinBackToFront.cxx
  5. 1 2
      panda/src/cull/cullBinFrontToBack.cxx
  6. 111 0
      panda/src/cull/cullBinHierarchicalZBuffer.I
  7. 779 0
      panda/src/cull/cullBinHierarchicalZBuffer.cxx
  8. 163 0
      panda/src/cull/cullBinHierarchicalZBuffer.h
  9. 0 1
      panda/src/cull/cullBinStateSorted.cxx
  10. 1 0
      panda/src/cull/cull_composite2.cxx
  11. 0 11
      panda/src/display/graphicsStateGuardian.I
  12. 65 0
      panda/src/display/graphicsStateGuardian.cxx
  13. 9 1
      panda/src/display/graphicsStateGuardian.h
  14. 25 22
      panda/src/glstuff/Sources.pp
  15. 106 3
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  16. 16 4
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  17. 12 4
      panda/src/glstuff/glmisc_src.cxx
  18. 1 0
      panda/src/glstuff/glmisc_src.h
  19. 1 0
      panda/src/glstuff/glstuff_src.cxx
  20. 1 0
      panda/src/glstuff/glstuff_src.h
  21. 8 1
      panda/src/gobj/Sources.pp
  22. 4 0
      panda/src/gobj/config_gobj.cxx
  23. 2 1
      panda/src/gobj/gobj_composite2.cxx
  24. 27 0
      panda/src/gobj/occlusionQueryContext.I
  25. 36 0
      panda/src/gobj/occlusionQueryContext.cxx
  26. 61 0
      panda/src/gobj/occlusionQueryContext.h
  27. 27 0
      panda/src/gobj/queryContext.I
  28. 57 0
      panda/src/gobj/queryContext.cxx
  29. 71 0
      panda/src/gobj/queryContext.h
  30. 7 0
      panda/src/gsgbase/graphicsStateGuardianBase.h
  31. 1 0
      panda/src/pgraph/colorWriteAttrib.h
  32. 5 8
      panda/src/pgraph/cullBinManager.cxx
  33. 1 0
      panda/src/pgraph/cullBinManager.h
  34. 4 0
      panda/src/pgraph/cullableObject.h
  35. 2 2
      panda/src/pgraph/portalNode.cxx

+ 17 - 12
panda/src/cull/Sources.pp

@@ -9,13 +9,15 @@
   #define TARGET cull
   #define TARGET cull
 
 
   #define SOURCES \
   #define SOURCES \
-    binCullHandler.h \
+    binCullHandler.h binCullHandler.I \
     config_cull.h \
     config_cull.h \
-    cullBinBackToFront.h cullBinFixed.h \
-    cullBinFrontToBack.h cullBinStateSorted.h cullBinUnsorted.h \
-    drawCullHandler.h binCullHandler.I cullBinBackToFront.I \
-    cullBinFixed.I cullBinFrontToBack.I cullBinStateSorted.I \
-    cullBinUnsorted.I drawCullHandler.I
+    cullBinBackToFront.h cullBinBackToFront.I \
+    cullBinFixed.h cullBinFixed.I \
+    cullBinFrontToBack.h cullBinFrontToBack.I \
+    cullBinHierarchicalZBuffer.h cullBinHierarchicalZBuffer.I \
+    cullBinStateSorted.h cullBinStateSorted.I \
+    cullBinUnsorted.h cullBinUnsorted.I \
+    drawCullHandler.h drawCullHandler.I
 
 
   #define COMBINED_SOURCES \
   #define COMBINED_SOURCES \
     $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
     $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
@@ -26,18 +28,21 @@
     cullBinBackToFront.cxx \
     cullBinBackToFront.cxx \
     cullBinFixed.cxx \
     cullBinFixed.cxx \
     cullBinFrontToBack.cxx \
     cullBinFrontToBack.cxx \
+    cullBinHierarchicalZBuffer.cxx \
     cullBinStateSorted.cxx \
     cullBinStateSorted.cxx \
     cullBinUnsorted.cxx \
     cullBinUnsorted.cxx \
     drawCullHandler.cxx
     drawCullHandler.cxx
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
-    binCullHandler.h \
+    binCullHandler.h binCullHandler.I \
     config_cull.h \
     config_cull.h \
-    cullBinBackToFront.h cullBinFixed.h \
-    cullBinFrontToBack.h cullBinStateSorted.h cullBinUnsorted.h \
-    drawCullHandler.h binCullHandler.I cullBinBackToFront.I \
-    cullBinFixed.I cullBinFrontToBack.I cullBinStateSorted.I \
-    cullBinUnsorted.I drawCullHandler.I
+    cullBinBackToFront.h cullBinBackToFront.I \
+    cullBinFixed.h cullBinFixed.I \
+    cullBinFrontToBack.h cullBinFrontToBack.I \
+    cullBinHierarchicalZBuffer.h cullBinHierarchicalZBuffer.I \
+    cullBinStateSorted.h cullBinStateSorted.I \
+    cullBinUnsorted.h cullBinUnsorted.I \
+    drawCullHandler.h drawCullHandler.I
 
 
   #define IGATESCAN all
   #define IGATESCAN all
 
 

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

@@ -21,6 +21,7 @@
 #include "cullBinBackToFront.h"
 #include "cullBinBackToFront.h"
 #include "cullBinFixed.h"
 #include "cullBinFixed.h"
 #include "cullBinFrontToBack.h"
 #include "cullBinFrontToBack.h"
+#include "cullBinHierarchicalZBuffer.h"
 #include "cullBinStateSorted.h"
 #include "cullBinStateSorted.h"
 #include "cullBinUnsorted.h"
 #include "cullBinUnsorted.h"
 
 
@@ -34,6 +35,18 @@ ConfigureFn(config_cull) {
   init_libcull();
   init_libcull();
 }
 }
 
 
+ConfigVariableInt max_objects_per_octree_node
+("max-objects-per-octree-node", 10,
+ PRC_DESC("Specifies the maximum number of objects collected per octree "
+          "node, by the hierarchical-z cull bin algorithm."));
+
+ConfigVariableDouble octree_multiassign_ratio
+("octree-multiassign-ratio", 0.1,
+ PRC_DESC("Objects that intersect a bisecting plane of an octree node must "
+          "be at least this fraction of the node's linear dimension in "
+          "order to be assigned to the node itself.  If the object is smaller "
+          "than this, it will be multiply assigned to the node's children."));
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libcull
 //     Function: init_libcull
 //  Description: Initializes the library.  This must be called at
 //  Description: Initializes the library.  This must be called at
@@ -53,6 +66,7 @@ init_libcull() {
   CullBinBackToFront::init_type();
   CullBinBackToFront::init_type();
   CullBinFixed::init_type();
   CullBinFixed::init_type();
   CullBinFrontToBack::init_type();
   CullBinFrontToBack::init_type();
+  CullBinHierarchicalZBuffer::init_type();
   CullBinStateSorted::init_type();
   CullBinStateSorted::init_type();
   CullBinUnsorted::init_type();
   CullBinUnsorted::init_type();
 
 
@@ -67,4 +81,6 @@ init_libcull() {
                                  CullBinFrontToBack::make_bin);
                                  CullBinFrontToBack::make_bin);
   bin_manager->register_bin_type(CullBinManager::BT_fixed,
   bin_manager->register_bin_type(CullBinManager::BT_fixed,
                                  CullBinFixed::make_bin);
                                  CullBinFixed::make_bin);
+  bin_manager->register_bin_type(CullBinManager::BT_hierarchical_z,
+                                 CullBinHierarchicalZBuffer::make_bin);
 }
 }

+ 5 - 0
panda/src/cull/config_cull.h

@@ -22,12 +22,17 @@
 #include "pandabase.h"
 #include "pandabase.h"
 #include "notifyCategoryProxy.h"
 #include "notifyCategoryProxy.h"
 #include "dconfig.h"
 #include "dconfig.h"
+#include "configVariableInt.h"
+#include "configVariableDouble.h"
 
 
 class DSearchPath;
 class DSearchPath;
 
 
 ConfigureDecl(config_cull, EXPCL_PANDA, EXPTP_PANDA);
 ConfigureDecl(config_cull, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(cull, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(cull, EXPCL_PANDA, EXPTP_PANDA);
 
 
+extern ConfigVariableInt max_objects_per_octree_node;
+extern ConfigVariableDouble octree_multiassign_ratio;
+
 extern EXPCL_PANDA void init_libcull();
 extern EXPCL_PANDA void init_libcull();
 
 
 #endif
 #endif

+ 1 - 2
panda/src/cull/cullBinBackToFront.cxx

@@ -63,8 +63,7 @@ add_object(CullableObject *object) {
   // Determine the center of the bounding volume.
   // Determine the center of the bounding volume.
   CPT(BoundingVolume) volume = object->_geom->get_bounds();
   CPT(BoundingVolume) volume = object->_geom->get_bounds();
 
 
-  if (!volume->is_empty() &&
-      volume->is_of_type(GeometricBoundingVolume::get_class_type())) {
+  if (!volume->is_empty()) {
     const GeometricBoundingVolume *gbv;
     const GeometricBoundingVolume *gbv;
     DCAST_INTO_V(gbv, volume);
     DCAST_INTO_V(gbv, volume);
     
     

+ 1 - 2
panda/src/cull/cullBinFrontToBack.cxx

@@ -63,8 +63,7 @@ add_object(CullableObject *object) {
   // Determine the center of the bounding volume.
   // Determine the center of the bounding volume.
   CPT(BoundingVolume) volume = object->_geom->get_bounds();
   CPT(BoundingVolume) volume = object->_geom->get_bounds();
 
 
-  if (!volume->is_empty() &&
-      volume->is_of_type(GeometricBoundingVolume::get_class_type())) {
+  if (!volume->is_empty()) {
     const GeometricBoundingVolume *gbv;
     const GeometricBoundingVolume *gbv;
     DCAST_INTO_V(gbv, volume);
     DCAST_INTO_V(gbv, volume);
     
     

+ 111 - 0
panda/src/cull/cullBinHierarchicalZBuffer.I

@@ -0,0 +1,111 @@
+// Filename: cullBinHierarchicalZBuffer.I
+// Created by:  drose (24Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::ObjectData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullBinHierarchicalZBuffer::ObjectData::
+ObjectData(CullableObject *object, BoundingSphere *bounds) :
+  _object(object),
+  _bounds(bounds)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::ObjectData::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullBinHierarchicalZBuffer::ObjectData::
+ObjectData(const CullBinHierarchicalZBuffer::ObjectData &copy) :
+  _object(copy._object),
+  _bounds(copy._bounds)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::ObjectData::Copy Assignment
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void CullBinHierarchicalZBuffer::ObjectData::
+operator = (const CullBinHierarchicalZBuffer::ObjectData &copy) {
+  _object = copy._object;
+  _bounds = copy._bounds;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullBinHierarchicalZBuffer::
+CullBinHierarchicalZBuffer(const string &name, GraphicsStateGuardianBase *gsg) :
+  CullBin(name, gsg)
+{
+  nassertv(_gsg->get_supports_occlusion_query());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::initial_assign
+//       Access: Public
+//  Description: Assigns the object to the node, in preparation for
+//               calling group_objects().
+////////////////////////////////////////////////////////////////////
+INLINE void CullBinHierarchicalZBuffer::OctreeNode::
+initial_assign(const CullBinHierarchicalZBuffer::ObjectData &object_data) {
+  _objects.push_back(object_data);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::reassign
+//       Access: Public
+//  Description: After determining that the object bisects one of the
+//               major planes of the node, reassigns it back to the
+//               same node.
+////////////////////////////////////////////////////////////////////
+INLINE void CullBinHierarchicalZBuffer::OctreeNode::
+reassign(const CullBinHierarchicalZBuffer::ObjectData &object_data) {
+  if (object_data._bounds->get_radius() / _half_side >= octree_multiassign_ratio) {
+    // The object is large enough to keep in this node.
+    //cerr << "    reassigning.\n";
+    _objects.push_back(object_data);
+  } else {
+    //cerr << "    multidistributing.\n";
+    multi_assign(object_data);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::assign_to_corner
+//       Access: Private
+//  Description: Assigns the object to the octree node in the
+//               indicated corner.
+////////////////////////////////////////////////////////////////////
+INLINE void CullBinHierarchicalZBuffer::OctreeNode::
+assign_to_corner(int index, const CullBinHierarchicalZBuffer::ObjectData &object_data) {
+  //cerr << "    Assigning to corner " << hex << index << dec << ".\n";
+  nassertv(index >= 0 && index < 8);
+  if (_corners[index] == NULL) {
+    make_corner(index);
+  }
+  _corners[index]->initial_assign(object_data);
+}

+ 779 - 0
panda/src/cull/cullBinHierarchicalZBuffer.cxx

@@ -0,0 +1,779 @@
+// Filename: cullBinHierarchicalZBuffer.cxx
+// Created by:  drose (24Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cullBinHierarchicalZBuffer.h"
+#include "graphicsStateGuardianBase.h"
+#include "geometricBoundingVolume.h"
+#include "geomLines.h"
+#include "geomTriangles.h"
+#include "geomVertexWriter.h"
+#include "depthWriteAttrib.h"
+#include "depthTestAttrib.h"
+#include "colorWriteAttrib.h"
+#include "cullableObject.h"
+#include "cullHandler.h"
+#include "pStatTimer.h"
+#include "config_cull.h"
+#include "thread.h"
+
+PStatCollector CullBinHierarchicalZBuffer::_wait_occlusion_pcollector("Wait:Occlusion test");
+
+PT(Geom) CullBinHierarchicalZBuffer::_octree_solid_test;
+PT(Geom) CullBinHierarchicalZBuffer::_octree_wireframe_viz;
+CPT(RenderState) CullBinHierarchicalZBuffer::_octree_solid_test_state;
+TypeHandle CullBinHierarchicalZBuffer::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CullBinHierarchicalZBuffer::
+~CullBinHierarchicalZBuffer() {
+  ObjectPointers::iterator pi;
+  for (pi = _object_pointers.begin(); pi != _object_pointers.end(); ++pi) {
+    delete (*pi);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::make_bin
+//       Access: Public, Static
+//  Description: Factory constructor for passing to the CullBinManager.
+////////////////////////////////////////////////////////////////////
+CullBin *CullBinHierarchicalZBuffer::
+make_bin(const string &name, GraphicsStateGuardianBase *gsg) {
+  return new CullBinHierarchicalZBuffer(name, gsg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::make_next
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated CullBin object that
+//               contains a copy of just the subset of the data from
+//               this CullBin object that is worth keeping around
+//               for next frame.
+//
+//               If a particular CullBin object has no data worth
+//               preserving till next frame, it is acceptable to
+//               return NULL (which is the default behavior of this
+//               method).
+////////////////////////////////////////////////////////////////////
+PT(CullBin) CullBinHierarchicalZBuffer::
+make_next() const {
+  return (CullBin *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::add_object
+//       Access: Public, Virtual
+//  Description: Adds a geom, along with its associated state, to
+//               the bin for rendering.
+////////////////////////////////////////////////////////////////////
+void CullBinHierarchicalZBuffer::
+add_object(CullableObject *object) {
+  // Determine the world-space bounding sphere for the object.
+  CPT(BoundingVolume) volume = object->_geom->get_bounds();
+  if (volume->is_empty()) {
+    return;
+  }
+
+  PT(BoundingSphere) sphere;
+
+  if (volume->is_exact_type(BoundingSphere::get_class_type())) {
+    sphere = DCAST(BoundingSphere, volume->make_copy());
+  } else {
+    const GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, volume);
+    PT(BoundingSphere) sphere = new BoundingSphere;
+    sphere->around(&gbv, &gbv + 1);
+  }
+
+  object->_already_drawn = false;
+  sphere->xform(object->_net_transform->get_mat());
+  _root.initial_assign(ObjectData(object, sphere));
+  _object_pointers.push_back(object);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::finish_cull
+//       Access: Public
+//  Description: Called after all the geoms have been added, this
+//               indicates that the cull process is finished for this
+//               frame and gives the bins a chance to do any
+//               post-processing (like sorting) before moving on to
+//               draw.
+////////////////////////////////////////////////////////////////////
+void CullBinHierarchicalZBuffer::
+finish_cull() {
+  PStatTimer timer(_cull_this_pcollector);
+
+  // Now we have a loose list of objects that are to be rendered.
+  // We'd rather have them in an octree, which has much better
+  // grouping properties for the purpose of this algorithm.
+
+  // For now, we'll just build an octree here at runtime, a new one
+  // fresh for each frame.  Maybe it won't be *too* bad.  But later,
+  // we can optimize this to take advantage of temporal coherence by
+  // starting from the previous frame's octree.
+  _root.make_initial_bounds();
+  _root.group_objects();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::draw
+//       Access: Public
+//  Description: Draws all the geoms in the bin, in the appropriate
+//               order.
+////////////////////////////////////////////////////////////////////
+void CullBinHierarchicalZBuffer::
+draw() {
+  PStatTimer timer(_draw_this_pcollector);
+  _root.draw(*this);
+
+  while (!_pending_nodes.empty()) {
+    PendingNode &pending = _pending_nodes.front();
+    bool is_occluded;
+    if (!pending._query->is_answer_ready()) {
+      // The answer isn't ready yet.  We have to wait.
+      PStatTimer timer(_wait_occlusion_pcollector);
+      is_occluded = (pending._query->get_num_fragments() == 0);
+    } else {
+      // The answer is ready right now.  There will be no waiting.
+      is_occluded = (pending._query->get_num_fragments() == 0);
+    }
+    if (!is_occluded) {
+      // The octree cell is at least partially visible.  Draw it, and
+      // continue recursion.
+      pending._octree_node->draw(*this);
+    } else {
+      pending._octree_node->draw_wireframe(*this);
+    }
+    _pending_nodes.pop_front();
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CullBinHierarchicalZBuffer::OctreeNode::
+OctreeNode() {
+  for (int i = 0; i < 8; ++i) {
+    _corners[i] = (OctreeNode *)NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CullBinHierarchicalZBuffer::OctreeNode::
+OctreeNode(float mid_x, float mid_y, float mid_z, float half_side) :
+  _mid(mid_x, mid_y, mid_z),
+  _half_side(half_side)
+{
+  for (int i = 0; i < 8; ++i) {
+    _corners[i] = (OctreeNode *)NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CullBinHierarchicalZBuffer::OctreeNode::
+~OctreeNode() {
+  for (int i = 0; i < 8; ++i) {
+    if (_corners[i] != (OctreeNode *)NULL) {
+      delete _corners[i];
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::make_initial_bounds
+//       Access: Public
+//  Description: Determines the minmax bounding volume of the root
+//               OctreeNode, based on the objects it contains.
+////////////////////////////////////////////////////////////////////
+void CullBinHierarchicalZBuffer::OctreeNode::
+make_initial_bounds() {
+  if (_objects.empty()) {
+    return;
+  }
+
+  LPoint3f scene_min = _objects[0]._bounds->get_center();
+  LPoint3f scene_max = _objects[0]._bounds->get_center();
+
+  Objects::const_iterator oi;
+  for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
+    LPoint3f object_min = (*oi)._bounds->get_min();
+    LPoint3f object_max = (*oi)._bounds->get_max();
+    scene_min[0] = min(scene_min[0], object_min[0]);
+    scene_min[1] = min(scene_min[1], object_min[1]);
+    scene_min[2] = min(scene_min[2], object_min[2]);
+    scene_max[0] = max(scene_max[0], object_max[0]);
+    scene_max[1] = max(scene_max[1], object_max[1]);
+    scene_max[2] = max(scene_max[2], object_max[2]);
+  }
+
+  float side = max(max(scene_max[0] - scene_min[0],
+                       scene_max[1] - scene_min[1]),
+                   scene_max[2] - scene_min[2]);
+
+  _mid.set((scene_min[0] + scene_max[0]) * 0.5f,
+           (scene_min[1] + scene_max[1]) * 0.5f,
+           (scene_min[2] + scene_max[2]) * 0.5f);
+  _half_side = side * 0.5f;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::group_objects
+//       Access: Public
+//  Description: Recursively groups the objects assigned to this node
+//               into smaller octree nodes, as appropriate.
+////////////////////////////////////////////////////////////////////
+void CullBinHierarchicalZBuffer::OctreeNode::
+group_objects() {
+  //cerr << "group_objects, " << _objects.size() << " objects.\n";
+  if ((int)_objects.size() <= max_objects_per_octree_node) {
+    // No need to do any more subdividing.
+    //cerr << "  small enough.\n";
+    return;
+  }
+
+  // Assign all objects to one or more corners.
+  
+  Objects old_objects;
+  old_objects.swap(_objects);
+
+  Objects::const_iterator oi;
+  for (oi = old_objects.begin(); oi != old_objects.end(); ++oi) {
+    //cerr << "  object " << (oi - old_objects.begin()) << "\n";
+    const ObjectData &object_data = (*oi);
+    const LPoint3f &c = object_data._bounds->get_center();
+    float r = object_data._bounds->get_radius();
+  
+    if (c[0] + r <= _mid[0]) {
+      // -X
+      if (c[1] + r <= _mid[1]) {
+        // -X, -Y
+        if (c[2] + r <= _mid[2]) {
+          // -X, -Y, -Z
+          assign_to_corner(0, object_data);
+        } else if (c[2] - r >= _mid[2]) {
+          // -X, -Y, +Z
+          assign_to_corner(OC_z, object_data);
+        } else {
+          // -X, -Y, 0
+          reassign(object_data);
+        }
+      } else if (c[1] - r >= _mid[1]) {
+        // -X, +Y
+        if (c[2] + r <= _mid[2]) {
+          // -X, +Y, -Z
+          assign_to_corner(OC_y, object_data);
+        } else if (c[2] - r >= _mid[2]) {
+          // -X, +Y, +Z
+          assign_to_corner(OC_y | OC_z, object_data);
+        } else {
+          // -X, +Y, 0
+          reassign(object_data);
+        }
+      } else {
+        // -X, 0
+        reassign(object_data);
+      }
+    } else if (c[0] - r >= _mid[0]) {
+      // +X
+      if (c[1] + r <= _mid[1]) {
+        // +X, -Y
+        if (c[2] + r <= _mid[2]) {
+          // +X, -Y, -Z
+          assign_to_corner(OC_x, object_data);
+        } else if (c[2] - r >= _mid[2]) {
+          // +X, -Y, +Z
+          assign_to_corner(OC_x | OC_z, object_data);
+        } else {
+          // +X, -Y, 0
+          reassign(object_data);
+        }
+      } else if (c[1] - r >= _mid[1]) {
+        // +X, +Y
+        if (c[2] + r <= _mid[2]) {
+          // +X, +Y, -Z
+          assign_to_corner(OC_x | OC_y, object_data);
+        } else if (c[2] - r >= _mid[2]) {
+          // +X, +Y, +Z
+          assign_to_corner(OC_x | OC_y | OC_z, object_data);
+        } else {
+          // +X, +Y, 0
+          reassign(object_data);
+        }
+      } else {
+        // +X, 0
+        reassign(object_data);
+      }
+    } else {
+      // 0
+      reassign(object_data);
+    }
+  } 
+
+  for (int i = 0; i < 8; ++i) {
+    if (_corners[i] != (OctreeNode *)NULL) {
+      _corners[i]->group_objects();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::occlusion_test
+//       Access: Public
+//  Description: Tests the octree node for visibility by rendering the
+//               octree cube, invisibly, with a query.  Returns the
+//               occlusion query object representing this test.
+////////////////////////////////////////////////////////////////////
+PT(OcclusionQueryContext) CullBinHierarchicalZBuffer::OctreeNode::
+occlusion_test(GraphicsStateGuardianBase *gsg) {
+  // Draw the bounding volume for visualization.  This is
+  // complicated because we're doing this at such a low level, here
+  // in the middle of the draw task--we've already completed the
+  // cull traversal, so we can't just create a CullableObject or do
+  // anything else that requires a pointer to a CullTraverser.
+  // Instead, we have to do the relevant code by hand.
+  CPT(TransformState) net_transform = TransformState::make_pos_hpr_scale
+    (_mid, LVecBase3f(0.0f, 0.0f, 0.0f), 
+     LVecBase3f(_half_side, _half_side, _half_side));
+  CPT(TransformState) world_transform = gsg->get_scene()->get_world_transform();
+  CPT(TransformState) modelview_transform = world_transform->compose(net_transform);
+  
+  CPT(RenderState) state = get_octree_solid_test_state();
+  PT(GeomMunger) munger = gsg->get_geom_munger(state);
+  
+  CPT(Geom) viz = get_octree_solid_test();
+  CPT(GeomVertexData) munged_data = viz->get_vertex_data();
+  munger->munge_geom(viz, munged_data);
+  
+  gsg->set_state_and_transform(state, modelview_transform);
+
+  gsg->begin_occlusion_query();
+  viz->draw(gsg, munger, munged_data);
+  return gsg->end_occlusion_query();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::draw
+//       Access: Public
+//  Description: Draws all of the objects in this node, and
+//               recursively performs occlusion tests on all of the
+//               nested nodes.
+////////////////////////////////////////////////////////////////////
+void CullBinHierarchicalZBuffer::OctreeNode::
+draw(CullBinHierarchicalZBuffer &bin) {
+  if (!_objects.empty()) {
+    // Now draw the objects within the octree node.
+    Objects::const_iterator oi;
+    for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
+      CullableObject *object = (*oi)._object;
+      if (object->_already_drawn) {
+        //cerr << "skipping object.\n";
+      } else {
+        //cerr << "drawing object.\n";
+        CullHandler::draw(object, bin._gsg);
+        object->_already_drawn = true;
+      }
+    }
+  }
+
+  for (int i = 0; i < 8; ++i) {
+    if (_corners[i] != (OctreeNode *)NULL) {
+      PendingNode pending;
+      pending._octree_node = _corners[i];
+      pending._query = _corners[i]->occlusion_test(bin._gsg);
+      bin._pending_nodes.push_back(pending);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::draw_wireframe
+//       Access: Public
+//  Description: Draws a wireframe representation of the octree cube,
+//               for debugging and visualization purposes.
+////////////////////////////////////////////////////////////////////
+void CullBinHierarchicalZBuffer::OctreeNode::
+draw_wireframe(CullBinHierarchicalZBuffer &bin) {
+  // As above, this is complicated because we're doing this at such a
+  // low level.
+  CPT(TransformState) net_transform = TransformState::make_pos_hpr_scale
+    (_mid, LVecBase3f(0.0f, 0.0f, 0.0f), 
+     LVecBase3f(_half_side, _half_side, _half_side));
+  CPT(TransformState) world_transform = bin._gsg->get_scene()->get_world_transform();
+  CPT(TransformState) modelview_transform = world_transform->compose(net_transform);
+  
+  CPT(RenderState) state = RenderState::make_empty();
+  PT(GeomMunger) munger = bin._gsg->get_geom_munger(state);
+  
+  CPT(Geom) viz = get_octree_wireframe_viz();
+  CPT(GeomVertexData) munged_data = viz->get_vertex_data();
+  munger->munge_geom(viz, munged_data);
+  
+  bin._gsg->set_state_and_transform(state, modelview_transform);
+  viz->draw(bin._gsg, munger, munged_data);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::get_octree_solid_test
+//       Access: Private, Static
+//  Description: Returns a Geom that may be used to render the solid
+//               faces of octree cube, presumably invisibly.  This
+//               returns a cube over the range (-1, -1, -1) - (1, 1,
+//               1).
+////////////////////////////////////////////////////////////////////
+CPT(Geom) CullBinHierarchicalZBuffer::
+get_octree_solid_test() {
+  if (_octree_solid_test == (Geom *)NULL) {
+    CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3();
+    PT(GeomVertexData) vdata = 
+      new GeomVertexData("octree", format, Geom::UH_static);
+    GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+    vertex.add_data3f(-1.0f, -1.0f, -1.0f);
+    vertex.add_data3f(1.0f, -1.0f, -1.0f);
+    vertex.add_data3f(-1.0f, -1.0f, 1.0f);
+    vertex.add_data3f(1.0f, -1.0f, 1.0f);
+    vertex.add_data3f(-1.0f, 1.0f, -1.0f);
+    vertex.add_data3f(1.0f, 1.0f, -1.0f);
+    vertex.add_data3f(-1.0f, 1.0f, 1.0f);
+    vertex.add_data3f(1.0f, 1.0f, 1.0f);
+    PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
+    tris->add_vertices(2, 0, 3); tris->close_primitive();
+    tris->add_vertices(0, 1, 3); tris->close_primitive();
+    tris->add_vertices(3, 1, 7); tris->close_primitive();
+    tris->add_vertices(1, 5, 7); tris->close_primitive();
+    tris->add_vertices(7, 5, 6); tris->close_primitive();
+    tris->add_vertices(5, 4, 6); tris->close_primitive();
+    tris->add_vertices(6, 4, 2); tris->close_primitive();
+    tris->add_vertices(4, 0, 2); tris->close_primitive();
+    tris->add_vertices(6, 2, 7); tris->close_primitive();
+    tris->add_vertices(2, 3, 7); tris->close_primitive();
+    tris->add_vertices(0, 4, 1); tris->close_primitive();
+    tris->add_vertices(4, 5, 1); tris->close_primitive();
+
+    _octree_solid_test = new Geom(vdata);
+    _octree_solid_test->add_primitive(tris);
+  }
+
+  return _octree_solid_test;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::get_octree_wireframe_viz
+//       Access: Private, Static
+//  Description: Returns a Geom that may be used to render an
+//               OctreeNode in wireframe.  This actually draws a
+//               wireframe cube in the range (-1, -1, -1) - (1, 1, 1).
+////////////////////////////////////////////////////////////////////
+CPT(Geom) CullBinHierarchicalZBuffer::
+get_octree_wireframe_viz() {
+  if (_octree_wireframe_viz == (Geom *)NULL) {
+    CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp();
+    PT(GeomVertexData) vdata = 
+      new GeomVertexData("octree", format, Geom::UH_static);
+    GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+    vertex.add_data3f(-1.0f, -1.0f, -1.0f);
+    vertex.add_data3f(1.0f, -1.0f, -1.0f);
+    vertex.add_data3f(-1.0f, -1.0f, 1.0f);
+    vertex.add_data3f(1.0f, -1.0f, 1.0f);
+    vertex.add_data3f(-1.0f, 1.0f, -1.0f);
+    vertex.add_data3f(1.0f, 1.0f, -1.0f);
+    vertex.add_data3f(-1.0f, 1.0f, 1.0f);
+    vertex.add_data3f(1.0f, 1.0f, 1.0f);
+    CPT(GeomVertexData) cvdata = vdata->set_color(Colorf(1.0f, 0.5f, 0.0f, 1.0f));
+    PT(GeomLines) lines = new GeomLines(Geom::UH_static);
+    lines->add_vertices(0, 1); lines->close_primitive();
+    lines->add_vertices(1, 3); lines->close_primitive();
+    lines->add_vertices(3, 2); lines->close_primitive();
+    lines->add_vertices(2, 0); lines->close_primitive();
+    lines->add_vertices(0, 4); lines->close_primitive();
+    lines->add_vertices(4, 6); lines->close_primitive();
+    lines->add_vertices(6, 7); lines->close_primitive();
+    lines->add_vertices(7, 5); lines->close_primitive();
+    lines->add_vertices(5, 4); lines->close_primitive();
+    lines->add_vertices(1, 5); lines->close_primitive();
+    lines->add_vertices(3, 7); lines->close_primitive();
+    lines->add_vertices(2, 6); lines->close_primitive();
+
+    _octree_wireframe_viz = new Geom(cvdata);
+    _octree_wireframe_viz->add_primitive(lines);
+  }
+
+  return _octree_wireframe_viz;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::get_octree_solid_test_state
+//       Access: Private, Static
+//  Description: Returns the RenderState appropriate to rendering the
+//               octree test invisibly.
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) CullBinHierarchicalZBuffer::
+get_octree_solid_test_state() {
+  if (_octree_solid_test_state == (RenderState *)NULL) {
+    _octree_solid_test_state = RenderState::make
+      (DepthWriteAttrib::make(DepthWriteAttrib::M_off),
+       DepthTestAttrib::make(DepthTestAttrib::M_less),
+       ColorWriteAttrib::make(ColorWriteAttrib::C_off));
+  }
+
+  return _octree_solid_test_state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::multi_assign
+//       Access: Public
+//  Description: The object intersects a center plane, but is too
+//               small to justify keeping within this big node.
+//               Duplicate it into the sub-nodes.
+////////////////////////////////////////////////////////////////////
+void CullBinHierarchicalZBuffer::OctreeNode::
+multi_assign(const CullBinHierarchicalZBuffer::ObjectData &object_data) {
+  const LPoint3f &c = object_data._bounds->get_center();
+  float r = object_data._bounds->get_radius();
+
+  //cerr << "multi_assigning " << c << " vs. " << _mid << "\n";
+  
+  if (c[0] + r <= _mid[0]) {
+    // -X
+    if (c[1] + r <= _mid[1]) {
+      // -X, -Y
+      if (c[2] + r <= _mid[2]) {
+        // -X, -Y, -Z
+        nassertv(false);
+      } else if (c[2] - r >= _mid[2]) {
+        // -X, -Y, +Z
+        nassertv(false);
+      } else {
+        // -X, -Y, 0
+        assign_to_corner(0, object_data);
+        assign_to_corner(OC_z, object_data);
+      }
+    } else if (c[1] - r >= _mid[1]) {
+      // -X, +Y
+      if (c[2] + r <= _mid[2]) {
+        // -X, +Y, -Z
+        nassertv(false);
+      } else if (c[2] - r >= _mid[2]) {
+        // -X, +Y, +Z
+        nassertv(false);
+      } else {
+        // -X, +Y, 0
+        assign_to_corner(OC_y, object_data);
+        assign_to_corner(OC_y | OC_z, object_data);
+      }
+    } else {
+      // -X, 0
+      if (c[2] + r <= _mid[2]) {
+        // -X, 0, -Z
+        assign_to_corner(0, object_data);
+        assign_to_corner(OC_y, object_data);
+      } else if (c[2] - r >= _mid[2]) {
+        // -X, 0, +Z
+        assign_to_corner(OC_z, object_data);
+        assign_to_corner(OC_y | OC_z, object_data);
+      } else {
+        // -X, 0, 0
+        assign_to_corner(0, object_data);
+        assign_to_corner(OC_z, object_data);
+        assign_to_corner(OC_y, object_data);
+        assign_to_corner(OC_y | OC_z, object_data);
+      }
+    }
+  } else if (c[0] - r >= _mid[0]) {
+    // +X
+    if (c[1] + r <= _mid[1]) {
+      // +X, -Y
+      if (c[2] + r <= _mid[2]) {
+        // +X, -Y, -Z
+        nassertv(false);
+      } else if (c[2] - r >= _mid[2]) {
+        // +X, -Y, +Z
+        nassertv(false);
+      } else {
+        // +X, -Y, 0
+        assign_to_corner(OC_x, object_data);
+        assign_to_corner(OC_x | OC_z, object_data);
+      }
+    } else if (c[1] - r >= _mid[1]) {
+      // +X, +Y
+      if (c[2] + r <= _mid[2]) {
+        // +X, +Y, -Z
+        nassertv(false);
+      } else if (c[2] - r >= _mid[2]) {
+        // +X, +Y, +Z
+        nassertv(false);
+      } else {
+        // +X, +Y, 0
+        assign_to_corner(OC_x | OC_y, object_data);
+        assign_to_corner(OC_x | OC_y | OC_z, object_data);
+      }
+    } else {
+      // +X, 0
+      if (c[2] + r <= _mid[2]) {
+        // +X, 0, -Z
+        assign_to_corner(OC_x, object_data);
+        assign_to_corner(OC_x | OC_y, object_data);
+      } else if (c[2] - r >= _mid[2]) {
+        // +X, 0, +Z
+        assign_to_corner(OC_x | OC_z, object_data);
+        assign_to_corner(OC_x | OC_y | OC_z, object_data);
+      } else {
+        // +X, 0, 0
+        assign_to_corner(OC_x, object_data);
+        assign_to_corner(OC_x | OC_z, object_data);
+        assign_to_corner(OC_x | OC_y, object_data);
+        assign_to_corner(OC_x | OC_y | OC_z, object_data);
+      }
+    }
+  } else {
+    // 0
+    if (c[1] + r <= _mid[1]) {
+      // 0, -Y
+      if (c[2] + r <= _mid[2]) {
+        // 0, -Y, -Z
+        assign_to_corner(0, object_data);
+        assign_to_corner(OC_x, object_data);
+      } else if (c[2] - r >= _mid[2]) {
+        // 0, -Y, +Z
+        assign_to_corner(OC_z, object_data);
+        assign_to_corner(OC_x | OC_z, object_data);
+      } else {
+        // 0, -Y, 0
+        assign_to_corner(0, object_data);
+        assign_to_corner(OC_z, object_data);
+        assign_to_corner(OC_x, object_data);
+        assign_to_corner(OC_x | OC_z, object_data);
+      }
+    } else if (c[1] - r >= _mid[1]) {
+      // 0, +Y
+      if (c[2] + r <= _mid[2]) {
+        // 0, +Y, -Z
+        assign_to_corner(OC_y, object_data);
+        assign_to_corner(OC_x | OC_y, object_data);
+      } else if (c[2] - r >= _mid[2]) {
+        // 0, +Y, +Z
+        assign_to_corner(OC_y | OC_z, object_data);
+        assign_to_corner(OC_x | OC_y | OC_z, object_data);
+      } else {
+        // 0, +Y, 0
+        assign_to_corner(OC_y, object_data);
+        assign_to_corner(OC_y | OC_z, object_data);
+        assign_to_corner(OC_x | OC_y, object_data);
+        assign_to_corner(OC_x | OC_y | OC_z, object_data);
+      }
+    } else {
+      // 0, 0
+      if (c[2] + r <= _mid[2]) {
+        // 0, 0, -Z
+        assign_to_corner(0, object_data);
+        assign_to_corner(OC_y, object_data);
+        assign_to_corner(OC_x, object_data);
+        assign_to_corner(OC_x | OC_y, object_data);
+      } else if (c[2] - r >= _mid[2]) {
+        // 0, 0, +Z
+        assign_to_corner(OC_z, object_data);
+        assign_to_corner(OC_y | OC_z, object_data);
+        assign_to_corner(OC_x | OC_z, object_data);
+        assign_to_corner(OC_x | OC_y | OC_z, object_data);
+      } else {
+        // 0, 0, 0
+        assign_to_corner(0, object_data);
+        assign_to_corner(OC_z, object_data);
+        assign_to_corner(OC_y, object_data);
+        assign_to_corner(OC_y | OC_z, object_data);
+        assign_to_corner(OC_x, object_data);
+        assign_to_corner(OC_x | OC_z, object_data);
+        assign_to_corner(OC_x | OC_y, object_data);
+        assign_to_corner(OC_x | OC_y | OC_z, object_data);
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinHierarchicalZBuffer::OctreeNode::make_corner
+//       Access: Private
+//  Description: Makes a new octree node for the indicated corner.
+////////////////////////////////////////////////////////////////////
+INLINE void CullBinHierarchicalZBuffer::OctreeNode::
+make_corner(int index) {
+  //cerr << "making corner " << hex << index << dec << "\n";
+  nassertv(_corners[index] == NULL);
+
+  OctreeNode *node = NULL;
+
+  double q = _half_side * 0.5f;
+
+  switch (index) {
+  case 0:
+    // -X, -Y, -Z
+    node = new OctreeNode(_mid[0] - q, _mid[1] - q, _mid[2] - q, q);
+    break;
+
+  case OC_x:
+    // +X, -Y, -Z
+    node = new OctreeNode(_mid[0] + q, _mid[1] - q, _mid[2] - q, q);
+    break;
+
+  case OC_y:
+    // -X, +Y, -Z
+    node = new OctreeNode(_mid[0] - q, _mid[1] + q, _mid[2] - q, q);
+    break;
+
+  case OC_x | OC_y:
+    // +X, +Y, -Z
+    node = new OctreeNode(_mid[0] + q, _mid[1] + q, _mid[2] - q, q);
+    break;
+
+  case OC_z:
+    // -X, -Y, +Z
+    node = new OctreeNode(_mid[0] - q, _mid[1] - q, _mid[2] + q, q);
+    break;
+
+  case OC_x | OC_z:
+    // +X, -Y, +Z
+    node = new OctreeNode(_mid[0] + q, _mid[1] - q, _mid[2] + q, q);
+    break;
+
+  case OC_y | OC_z:
+    // -X, +Y, +Z
+    node = new OctreeNode(_mid[0] - q, _mid[1] + q, _mid[2] + q, q);
+    break;
+
+  case OC_x | OC_y | OC_z:
+    // +X, +Y, +Z
+    node = new OctreeNode(_mid[0] + q, _mid[1] + q, _mid[2] + q, q);
+    break;
+  }
+  nassertv(node != (OctreeNode *)NULL);
+  _corners[index] = node;
+}

+ 163 - 0
panda/src/cull/cullBinHierarchicalZBuffer.h

@@ -0,0 +1,163 @@
+// Filename: cullBinHierarchicalZBuffer.h
+// Created by:  drose (24Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CULLBINHIERARCHICALZBUFFER_H
+#define CULLBINHIERARCHICALZBUFFER_H
+
+#include "pandabase.h"
+
+#include "cullBin.h"
+#include "geom.h"
+#include "transformState.h"
+#include "renderState.h"
+#include "pointerTo.h"
+#include "boundingSphere.h"
+#include "config_cull.h"
+#include "occlusionQueryContext.h"
+#include "pdeque.h"
+#include "pStatCollector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CullBinHierarchicalZBuffer
+// Description : This cull bin uses a hierarchical Z-buffer algorithm
+//               to attempt to further eliminate geometry that is
+//               obscured behind walls, etc.  It imposes some
+//               significant overhead over most of the other kinds of
+//               CullBins, and requires additional support from the
+//               graphics card, so it is most appropriate for scenes
+//               in which there might be a large amount of geometry,
+//               within the viewing frustum, but obscured behind large
+//               objects or walls.
+//
+//               This code is still experimental.  Use with caution.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA CullBinHierarchicalZBuffer : public CullBin {
+public:
+  INLINE CullBinHierarchicalZBuffer(const string &name, GraphicsStateGuardianBase *gsg);
+  virtual ~CullBinHierarchicalZBuffer();
+
+  static CullBin *make_bin(const string &name, GraphicsStateGuardianBase *gsg);
+  virtual PT(CullBin) make_next() const;
+
+  virtual void add_object(CullableObject *object);
+  virtual void finish_cull();
+  virtual void draw();
+
+private:
+  void draw_next();
+
+  static CPT(Geom) get_octree_solid_test();
+  static CPT(Geom) get_octree_wireframe_viz();
+
+  static CPT(RenderState) get_octree_solid_test_state();
+
+  // We keep the original list of CullableObjects here in the bin
+  // class, so we can delete them on destruction.  This allows us to
+  // duplicate pointers between different OctreeNodes.
+  typedef pvector<CullableObject *> ObjectPointers;
+  ObjectPointers _object_pointers;
+
+  class ObjectData {
+  public:
+    INLINE ObjectData(CullableObject *object, BoundingSphere *bounds);
+    INLINE ObjectData(const ObjectData &copy);
+    INLINE void operator = (const ObjectData &copy);
+    
+    CullableObject *_object;
+    PT(BoundingSphere) _bounds;
+  };
+
+  typedef pvector<ObjectData> Objects;
+
+  enum OctreeCorners {
+    OC_x   = 0x01,
+    OC_y   = 0x02,
+    OC_z   = 0x04,
+  };
+
+  class OctreeNode {
+  public:
+    OctreeNode();
+    OctreeNode(float mid_x, float mid_y, float mid_z, float half_side);
+    ~OctreeNode();
+
+    void make_initial_bounds();
+    void group_objects();
+    PT(OcclusionQueryContext) occlusion_test(GraphicsStateGuardianBase *gsg);
+    void draw(CullBinHierarchicalZBuffer &bin);
+    void draw_wireframe(CullBinHierarchicalZBuffer &bin);
+
+    INLINE void initial_assign(const ObjectData &object_data);
+
+  private:
+    INLINE void reassign(const ObjectData &object_data);
+    INLINE void assign_to_corner(int index, const ObjectData &object_data);
+    void multi_assign(const ObjectData &object_data);
+    void make_corner(int index);
+
+    Objects _objects;
+    OctreeNode *_corners[8];
+    LPoint3f _mid;
+    float _half_side;
+  };
+
+  OctreeNode _root;
+
+  // During draw(), we maintain a list of OctreeNodes that have been
+  // tested and have yet to pass the occlusion query and be drawn (or
+  // fail and be omitted).
+  class PendingNode {
+  public:
+    OctreeNode *_octree_node;
+    PT(OcclusionQueryContext) _query;
+  };
+  typedef pdeque<PendingNode> PendingNodes;
+  PendingNodes _pending_nodes;
+
+  static PStatCollector _wait_occlusion_pcollector;
+
+  static PT(Geom) _octree_solid_test;
+  static PT(Geom) _octree_wireframe_viz;
+  static CPT(RenderState) _octree_solid_test_state;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CullBin::init_type();
+    register_type(_type_handle, "CullBinHierarchicalZBuffer",
+                  CullBin::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;
+
+  friend class OctreeNode;
+};
+
+#include "cullBinHierarchicalZBuffer.I"
+
+#endif
+
+
+  

+ 0 - 1
panda/src/cull/cullBinStateSorted.cxx

@@ -18,7 +18,6 @@
 
 
 #include "cullBinStateSorted.h"
 #include "cullBinStateSorted.h"
 #include "graphicsStateGuardianBase.h"
 #include "graphicsStateGuardianBase.h"
-#include "geometricBoundingVolume.h"
 #include "cullableObject.h"
 #include "cullableObject.h"
 #include "cullHandler.h"
 #include "cullHandler.h"
 #include "pStatTimer.h"
 #include "pStatTimer.h"

+ 1 - 0
panda/src/cull/cull_composite2.cxx

@@ -1,4 +1,5 @@
 #include "cullBinFrontToBack.cxx"
 #include "cullBinFrontToBack.cxx"
+#include "cullBinHierarchicalZBuffer.cxx"
 #include "cullBinStateSorted.cxx"
 #include "cullBinStateSorted.cxx"
 #include "cullBinUnsorted.cxx"
 #include "cullBinUnsorted.cxx"
 #include "drawCullHandler.cxx"
 #include "drawCullHandler.cxx"

+ 0 - 11
panda/src/display/graphicsStateGuardian.I

@@ -593,17 +593,6 @@ get_global_gsg() {
   return _global_gsg;
   return _global_gsg;
 }
 }
 
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::get_scene
-//       Access: Public
-//  Description: Returns the SceneSetup object.
-////////////////////////////////////////////////////////////////////
-INLINE SceneSetup *GraphicsStateGuardian::
-get_scene() const {
-  return _scene_setup;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::reset_if_new
 //     Function: GraphicsStateGuardian::reset_if_new
 //       Access: Public
 //       Access: Public

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

@@ -35,6 +35,7 @@
 #include "geomLinestrips.h"
 #include "geomLinestrips.h"
 #include "colorWriteAttrib.h"
 #include "colorWriteAttrib.h"
 #include "shader.h"
 #include "shader.h"
+#include "pnotify.h"
 
 
 #include <algorithm>
 #include <algorithm>
 #include <limits.h>
 #include <limits.h>
@@ -137,6 +138,8 @@ GraphicsStateGuardian(const FrameBufferProperties &properties,
   _max_vertex_transforms = 0;
   _max_vertex_transforms = 0;
   _max_vertex_transform_indices = 0;
   _max_vertex_transform_indices = 0;
 
 
+  _supports_occlusion_query = false;
+
   // Initially, we set this to false; a GSG that knows it has this
   // Initially, we set this to false; a GSG that knows it has this
   // property should set it to true.
   // property should set it to true.
   _copy_texture_inverted = false;
   _copy_texture_inverted = false;
@@ -362,6 +365,16 @@ set_scene(SceneSetup *scene_setup) {
   return prepare_lens();
   return prepare_lens();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_scene
+//       Access: Public, Virtual
+//  Description: Returns the current SceneSetup object.
+////////////////////////////////////////////////////////////////////
+SceneSetup *GraphicsStateGuardian::
+get_scene() const {
+  return _scene_setup;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_prepared_objects
 //     Function: GraphicsStateGuardian::get_prepared_objects
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -511,6 +524,58 @@ void GraphicsStateGuardian::
 release_index_buffer(IndexBufferContext *) {
 release_index_buffer(IndexBufferContext *) {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_occlusion_query
+//       Access: Public, Virtual
+//  Description: Returns true if this GSG supports an occlusion query.
+//               If this is true, then begin_occlusion_query() and
+//               end_occlusion_query() may be called to bracket a
+//               sequence of draw_triangles() (or whatever) calls to
+//               measure pixels that pass the depth test.
+////////////////////////////////////////////////////////////////////
+bool GraphicsStateGuardian::
+get_supports_occlusion_query() const {
+  return _supports_occlusion_query;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::begin_occlusion_query
+//       Access: Public, Virtual
+//  Description: Begins a new occlusion query.  After this call, you
+//               may call begin_draw_primitives() and
+//               draw_triangles()/draw_whatever() repeatedly.
+//               Eventually, you should call end_occlusion_query()
+//               before the end of the frame; that will return a new
+//               OcclusionQueryContext object that will tell you how
+//               many pixels represented by the bracketed geometry
+//               passed the depth test.
+//
+//               It is not valid to call begin_occlusion_query()
+//               between another begin_occlusion_query()
+//               .. end_occlusion_query() sequence.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+begin_occlusion_query() {
+  nassertv(_current_occlusion_query == (OcclusionQueryContext *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::end_occlusion_query
+//       Access: Public, Virtual
+//  Description: Ends a previous call to begin_occlusion_query().
+//               This call returns the OcclusionQueryContext object
+//               that will (eventually) report the number of pixels
+//               that passed the depth test between the call to
+//               begin_occlusion_query() and end_occlusion_query().
+////////////////////////////////////////////////////////////////////
+PT(OcclusionQueryContext) GraphicsStateGuardian::
+end_occlusion_query() {
+  nassertr(_current_occlusion_query != (OcclusionQueryContext *)NULL, NULL);
+  PT(OcclusionQueryContext) result = _current_occlusion_query;
+  _current_occlusion_query = NULL;
+  return result;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_geom_munger
 //     Function: GraphicsStateGuardian::get_geom_munger
 //       Access: Public
 //       Access: Public

+ 9 - 1
panda/src/display/graphicsStateGuardian.h

@@ -45,6 +45,7 @@
 #include "shaderContext.h"
 #include "shaderContext.h"
 #include "bitMask.h"
 #include "bitMask.h"
 #include "texture.h"
 #include "texture.h"
+#include "occlusionQueryContext.h"
 
 
 class DrawableRegion;
 class DrawableRegion;
 class GraphicsEngine;
 class GraphicsEngine;
@@ -132,7 +133,7 @@ PUBLISHED:
 
 
 public:
 public:
   bool set_scene(SceneSetup *scene_setup);
   bool set_scene(SceneSetup *scene_setup);
-  INLINE SceneSetup *get_scene() const;
+  virtual SceneSetup *get_scene() const;
 
 
   virtual PreparedGraphicsObjects *get_prepared_objects();
   virtual PreparedGraphicsObjects *get_prepared_objects();
 
 
@@ -152,6 +153,10 @@ public:
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
   virtual void release_index_buffer(IndexBufferContext *ibc);
   virtual void release_index_buffer(IndexBufferContext *ibc);
 
 
+  virtual bool get_supports_occlusion_query() const;
+  virtual void begin_occlusion_query();
+  virtual PT(OcclusionQueryContext) end_occlusion_query();
+
   PT(GeomMunger) get_geom_munger(const RenderState *state);
   PT(GeomMunger) get_geom_munger(const RenderState *state);
   virtual PT(GeomMunger) make_geom_munger(const RenderState *state);
   virtual PT(GeomMunger) make_geom_munger(const RenderState *state);
 
 
@@ -338,6 +343,9 @@ protected:
   int _max_vertex_transforms;
   int _max_vertex_transforms;
   int _max_vertex_transform_indices;
   int _max_vertex_transform_indices;
 
 
+  bool _supports_occlusion_query;
+  PT(OcclusionQueryContext) _current_occlusion_query;
+
   bool _copy_texture_inverted;
   bool _copy_texture_inverted;
   bool _supports_multisample;
   bool _supports_multisample;
   bool _supports_generate_mipmap;
   bool _supports_generate_mipmap;

+ 25 - 22
panda/src/glstuff/Sources.pp

@@ -13,36 +13,39 @@
 
 
     
     
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
-     panda_glext.h \
-     glmisc_src.cxx \
-     glmisc_src.h \
-     glstuff_src.cxx \
-     glstuff_src.h \
-     glstuff_undef_src.h \
-     glVertexBufferContext_src.cxx \
-     glVertexBufferContext_src.I \
-     glVertexBufferContext_src.h \
-     glImmediateModeSender_src.cxx \
-     glImmediateModeSender_src.I \
-     glImmediateModeSender_src.h \
-     glIndexBufferContext_src.cxx \
-     glIndexBufferContext_src.I \
-     glIndexBufferContext_src.h \
-     glGeomContext_src.cxx \
      glGeomContext_src.I \
      glGeomContext_src.I \
+     glGeomContext_src.cxx \
      glGeomContext_src.h \
      glGeomContext_src.h \
-     glGeomMunger_src.cxx \
      glGeomMunger_src.I \
      glGeomMunger_src.I \
+     glGeomMunger_src.cxx \
      glGeomMunger_src.h \
      glGeomMunger_src.h \
-     glGraphicsStateGuardian_src.cxx \
      glGraphicsStateGuardian_src.I \
      glGraphicsStateGuardian_src.I \
+     glGraphicsStateGuardian_src.cxx \
      glGraphicsStateGuardian_src.h \
      glGraphicsStateGuardian_src.h \
-     glTextureContext_src.cxx \
-     glTextureContext_src.I \
-     glTextureContext_src.h \
+     glImmediateModeSender_src.I \
+     glImmediateModeSender_src.cxx \
+     glImmediateModeSender_src.h \
+     glIndexBufferContext_src.I \
+     glIndexBufferContext_src.cxx \
+     glIndexBufferContext_src.h \
+     glOcclusionQueryContext_src.I \
+     glOcclusionQueryContext_src.cxx \
+     glOcclusionQueryContext_src.h \
+     glShaderContext_src.I \
      glShaderContext_src.cxx \
      glShaderContext_src.cxx \
      glShaderContext_src.h \
      glShaderContext_src.h \
-     glShaderContext_src.I \
+     glTextureContext_src.I \
+     glTextureContext_src.cxx \
+     glTextureContext_src.h \
+     glVertexBufferContext_src.I \
+     glVertexBufferContext_src.cxx \
+     glVertexBufferContext_src.h \
+     glmisc_src.cxx \
+     glmisc_src.h \
+     glstuff_src.cxx \
+     glstuff_src.h \
+     glstuff_undef_src.h \
+     panda_glext.h
 
 
   #define SOURCES \
   #define SOURCES \
     $[INSTALL_HEADERS] glpure.cxx
     $[INSTALL_HEADERS] glpure.cxx

+ 106 - 3
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -721,11 +721,52 @@ reset() {
   }
   }
 
 
   _glDrawBuffers = NULL;
   _glDrawBuffers = NULL;
-  if (has_extension("GL_ARB_draw_buffers")) {
-    _glDrawBuffers = (PFNGLDRAWBUFFERSARBPROC)
+  if (is_at_least_version(2, 0)) {
+    _glDrawBuffers = (PFNGLDRAWBUFFERSPROC)
+      get_extension_func(GLPREFIX_QUOTED, "DrawBuffers");
+  } else if (has_extension("GL_ARB_draw_buffers")) {
+    _glDrawBuffers = (PFNGLDRAWBUFFERSPROC)
       get_extension_func(GLPREFIX_QUOTED, "DrawBuffersARB");
       get_extension_func(GLPREFIX_QUOTED, "DrawBuffersARB");
   }
   }
 
 
+  _supports_occlusion_query = false;
+  if (CLP(support_occlusion_query)) {
+    if (is_at_least_version(1, 5)) {
+      _supports_occlusion_query = true;
+      _glGenQueries = (PFNGLGENQUERIESPROC)
+        get_extension_func(GLPREFIX_QUOTED, "GenQueries");
+      _glBeginQuery = (PFNGLBEGINQUERYPROC)
+        get_extension_func(GLPREFIX_QUOTED, "BeginQuery");
+      _glEndQuery = (PFNGLENDQUERYPROC)
+        get_extension_func(GLPREFIX_QUOTED, "EndQuery");
+      _glDeleteQueries = (PFNGLDELETEQUERIESPROC)
+        get_extension_func(GLPREFIX_QUOTED, "DeleteQueries");
+      _glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)
+        get_extension_func(GLPREFIX_QUOTED, "GetQueryObjectuiv");
+    } else if (has_extension("GL_ARB_occlusion_query")) {
+      _supports_occlusion_query = true;
+      _glGenQueries = (PFNGLGENQUERIESPROC)
+        get_extension_func(GLPREFIX_QUOTED, "GenQueriesARB");
+      _glBeginQuery = (PFNGLBEGINQUERYPROC)
+        get_extension_func(GLPREFIX_QUOTED, "BeginQueryARB");
+      _glEndQuery = (PFNGLENDQUERYPROC)
+        get_extension_func(GLPREFIX_QUOTED, "EndQueryARB");
+      _glDeleteQueries = (PFNGLDELETEQUERIESPROC)
+        get_extension_func(GLPREFIX_QUOTED, "DeleteQueriesARB");
+      _glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)
+        get_extension_func(GLPREFIX_QUOTED, "GetQueryObjectuivARB");
+    }
+  }
+
+  if (_supports_occlusion_query && 
+      (_glGenQueries == NULL || _glBeginQuery == NULL || 
+       _glEndQuery == NULL || _glDeleteQueries == NULL ||
+       _glGetQueryObjectuiv == NULL)) {
+    GLCAT.warning()
+      << "Occlusion queries advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
+    _supports_occlusion_query = false;
+  }
+
   _glBlendEquation = NULL;
   _glBlendEquation = NULL;
   bool supports_blend_equation = false;
   bool supports_blend_equation = false;
   if (is_at_least_version(1, 2)) {
   if (is_at_least_version(1, 2)) {
@@ -1265,12 +1306,27 @@ end_frame() {
            ++ddli) {
            ++ddli) {
         if (GLCAT.is_debug()) {
         if (GLCAT.is_debug()) {
           GLCAT.debug()
           GLCAT.debug()
-            << "releasing index " << (*ddli) << "\n";
+            << "releasing display list index " << (*ddli) << "\n";
         }
         }
         GLP(DeleteLists)((*ddli), 1);
         GLP(DeleteLists)((*ddli), 1);
       }
       }
       _deleted_display_lists.clear();
       _deleted_display_lists.clear();
     }
     }
+
+    // And deleted occlusion queries, too.
+    if (!_deleted_queries.empty()) {
+      DeletedDisplayLists::iterator ddli;
+      for (ddli = _deleted_queries.begin();
+           ddli != _deleted_queries.end();
+           ++ddli) {
+        if (GLCAT.is_debug()) {
+          GLCAT.debug()
+            << "releasing query index " << (*ddli) << "\n";
+        }
+        _glDeleteQueries(1, &(*ddli));
+      }
+      _deleted_queries.clear();
+    }
   }
   }
 
 
   {
   {
@@ -2845,6 +2901,53 @@ setup_primitive(const GeomPrimitive *data) {
   return NULL;
   return NULL;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::begin_occlusion_query
+//       Access: Public, Virtual
+//  Description: Begins a new occlusion query.  After this call, you
+//               may call begin_draw_primitives() and
+//               draw_triangles()/draw_whatever() repeatedly.
+//               Eventually, you should call end_occlusion_query()
+//               before the end of the frame; that will return a new
+//               OcclusionQueryContext object that will tell you how
+//               many pixels represented by the bracketed geometry
+//               passed the depth test.
+//
+//               It is not valid to call begin_occlusion_query()
+//               between another begin_occlusion_query()
+//               .. end_occlusion_query() sequence.
+////////////////////////////////////////////////////////////////////
+void CLP(GraphicsStateGuardian)::
+begin_occlusion_query() {
+  nassertv(_supports_occlusion_query);
+  nassertv(_current_occlusion_query == (OcclusionQueryContext *)NULL);
+  PT(CLP(OcclusionQueryContext)) query = new CLP(OcclusionQueryContext)(this);
+
+  _glGenQueries(1, &query->_index);
+  _glBeginQuery(GL_SAMPLES_PASSED, query->_index);
+
+  _current_occlusion_query = query;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::end_occlusion_query
+//       Access: Public, Virtual
+//  Description: Ends a previous call to begin_occlusion_query().
+//               This call returns the OcclusionQueryContext object
+//               that will (eventually) report the number of pixels
+//               that passed the depth test between the call to
+//               begin_occlusion_query() and end_occlusion_query().
+////////////////////////////////////////////////////////////////////
+PT(OcclusionQueryContext) CLP(GraphicsStateGuardian)::
+end_occlusion_query() {
+  nassertr(_current_occlusion_query != (OcclusionQueryContext *)NULL, NULL);
+  PT(OcclusionQueryContext) result = _current_occlusion_query;
+  _current_occlusion_query = NULL;
+  _glEndQuery(GL_SAMPLES_PASSED);
+
+  return result;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::make_geom_munger
 //     Function: GLGraphicsStateGuardian::make_geom_munger
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 16 - 4
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -62,7 +62,7 @@ typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture);
 typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
 typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
 typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
 typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
 typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
 typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
-typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs);
+typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs);
 typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
 typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
 typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
 typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
 typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data);
 typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data);
@@ -82,8 +82,6 @@ class EXPCL_GL CLP(GraphicsStateGuardian) : public GraphicsStateGuardian {
 public:
 public:
   CLP(GraphicsStateGuardian)(const FrameBufferProperties &properties);
   CLP(GraphicsStateGuardian)(const FrameBufferProperties &properties);
   virtual ~CLP(GraphicsStateGuardian)();
   virtual ~CLP(GraphicsStateGuardian)();
-  friend class CLP(ShaderContext);
-  friend class CLP(GraphicsBuffer);
   
   
   virtual void reset();
   virtual void reset();
 
 
@@ -131,6 +129,9 @@ public:
   virtual void release_index_buffer(IndexBufferContext *ibc);
   virtual void release_index_buffer(IndexBufferContext *ibc);
   const unsigned char *setup_primitive(const GeomPrimitive *data);
   const unsigned char *setup_primitive(const GeomPrimitive *data);
 
 
+  virtual void begin_occlusion_query();
+  virtual PT(OcclusionQueryContext) end_occlusion_query();
+
   virtual PT(GeomMunger) make_geom_munger(const RenderState *state);
   virtual PT(GeomMunger) make_geom_munger(const RenderState *state);
 
 
   virtual void framebuffer_copy_to_texture
   virtual void framebuffer_copy_to_texture
@@ -424,7 +425,13 @@ public:
   PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC _glGetFramebufferAttachmentParameteriv;
   PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC _glGetFramebufferAttachmentParameteriv;
   PFNGLGENERATEMIPMAPEXTPROC _glGenerateMipmap;
   PFNGLGENERATEMIPMAPEXTPROC _glGenerateMipmap;
   
   
-  PFNGLDRAWBUFFERSARBPROC _glDrawBuffers;
+  PFNGLDRAWBUFFERSPROC _glDrawBuffers;
+
+  PFNGLGENQUERIESPROC _glGenQueries;
+  PFNGLBEGINQUERYPROC _glBeginQuery;
+  PFNGLENDQUERYPROC _glEndQuery;
+  PFNGLDELETEQUERIESPROC _glDeleteQueries;
+  PFNGLGETQUERYOBJECTUIVPROC _glGetQueryObjectuiv;
 
 
   GLenum _edge_clamp;
   GLenum _edge_clamp;
   GLenum _border_clamp;
   GLenum _border_clamp;
@@ -436,6 +443,7 @@ public:
   Mutex _lock;
   Mutex _lock;
   typedef pvector<GLuint> DeletedDisplayLists;
   typedef pvector<GLuint> DeletedDisplayLists;
   DeletedDisplayLists _deleted_display_lists;
   DeletedDisplayLists _deleted_display_lists;
+  DeletedDisplayLists _deleted_queries;
 
 
   static PStatCollector _load_display_list_pcollector;
   static PStatCollector _load_display_list_pcollector;
   static PStatCollector _primitive_batches_display_list_pcollector;
   static PStatCollector _primitive_batches_display_list_pcollector;
@@ -461,6 +469,10 @@ public:
 
 
 private:
 private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
+
+  friend class CLP(ShaderContext);
+  friend class CLP(GraphicsBuffer);
+  friend class CLP(OcclusionQueryContext);
 };
 };
 
 
 #include "glGraphicsStateGuardian_src.I"
 #include "glGraphicsStateGuardian_src.I"

+ 12 - 4
panda/src/glstuff/glmisc_src.cxx

@@ -62,6 +62,13 @@ ConfigVariableBool CLP(color_mask)
             "is broken (some are).  This will force the use of a (presumably) "
             "is broken (some are).  This will force the use of a (presumably) "
             "more expensive blending operation instead."));
             "more expensive blending operation instead."));
 
 
+ConfigVariableBool CLP(support_occlusion_query)
+  ("gl-support-occlusion-query", true,
+   PRC_DESC("Configure this true to enable the use of the occlusion_query "
+            "extension if the GL claims to support it, or false not to "
+            "use it even if it appears to be available.  (On some OpenGL "
+            "drivers, enabling this mode can force software rendering.)"));
+
 ConfigVariableBool CLP(compile_and_execute)
 ConfigVariableBool CLP(compile_and_execute)
   ("gl-compile-and-execute", false,
   ("gl-compile-and-execute", false,
    PRC_DESC("Configure this true if you know your GL's implementation of "
    PRC_DESC("Configure this true if you know your GL's implementation of "
@@ -73,13 +80,14 @@ ConfigVariableBool CLP(compile_and_execute)
             "rendered at the same time it is being compiled."));
             "rendered at the same time it is being compiled."));
 
 
 void CLP(init_classes)() {
 void CLP(init_classes)() {
-  CLP(GraphicsStateGuardian)::init_type();
-  CLP(TextureContext)::init_type();
   CLP(GeomContext)::init_type();
   CLP(GeomContext)::init_type();
-  CLP(VertexBufferContext)::init_type();
-  CLP(IndexBufferContext)::init_type();
   CLP(GeomMunger)::init_type();
   CLP(GeomMunger)::init_type();
+  CLP(GraphicsStateGuardian)::init_type();
+  CLP(IndexBufferContext)::init_type();
+  CLP(OcclusionQueryContext)::init_type();
   CLP(ShaderContext)::init_type();
   CLP(ShaderContext)::init_type();
+  CLP(TextureContext)::init_type();
+  CLP(VertexBufferContext)::init_type();
 
 
   PandaSystem *ps = PandaSystem::get_global_ptr();
   PandaSystem *ps = PandaSystem::get_global_ptr();
   ps->add_system(GLSYSTEM_NAME);
   ps->add_system(GLSYSTEM_NAME);

+ 1 - 0
panda/src/glstuff/glmisc_src.h

@@ -28,6 +28,7 @@ extern ConfigVariableBool CLP(ignore_filters);
 extern ConfigVariableBool CLP(ignore_mipmaps);
 extern ConfigVariableBool CLP(ignore_mipmaps);
 extern ConfigVariableBool CLP(force_mipmaps);
 extern ConfigVariableBool CLP(force_mipmaps);
 extern ConfigVariableBool CLP(color_mask);
 extern ConfigVariableBool CLP(color_mask);
+extern ConfigVariableBool CLP(support_occlusion_query);
 extern ConfigVariableBool CLP(compile_and_execute);
 extern ConfigVariableBool CLP(compile_and_execute);
 
 
 extern EXPCL_GL void CLP(init_classes)();
 extern EXPCL_GL void CLP(init_classes)();

+ 1 - 0
panda/src/glstuff/glstuff_src.cxx

@@ -25,6 +25,7 @@
 #include "glTextureContext_src.cxx"
 #include "glTextureContext_src.cxx"
 #include "glVertexBufferContext_src.cxx"
 #include "glVertexBufferContext_src.cxx"
 #include "glIndexBufferContext_src.cxx"
 #include "glIndexBufferContext_src.cxx"
+#include "glOcclusionQueryContext_src.cxx"
 #include "glGeomContext_src.cxx"
 #include "glGeomContext_src.cxx"
 #include "glGeomMunger_src.cxx"
 #include "glGeomMunger_src.cxx"
 #include "glShaderContext_src.cxx"
 #include "glShaderContext_src.cxx"

+ 1 - 0
panda/src/glstuff/glstuff_src.h

@@ -61,6 +61,7 @@
 #include "glTextureContext_src.h"
 #include "glTextureContext_src.h"
 #include "glVertexBufferContext_src.h"
 #include "glVertexBufferContext_src.h"
 #include "glIndexBufferContext_src.h"
 #include "glIndexBufferContext_src.h"
+#include "glOcclusionQueryContext_src.h"
 #include "glGeomContext_src.h"
 #include "glGeomContext_src.h"
 #include "glGeomMunger_src.h"
 #include "glGeomMunger_src.h"
 #include "glShaderContext_src.h"
 #include "glShaderContext_src.h"

+ 8 - 1
panda/src/gobj/Sources.pp

@@ -41,10 +41,12 @@
     internalName.I internalName.h \
     internalName.I internalName.h \
     material.I material.h materialPool.I materialPool.h  \
     material.I material.h materialPool.I materialPool.h  \
     matrixLens.I matrixLens.h \
     matrixLens.I matrixLens.h \
+    occlusionQueryContext.I occlusionQueryContext.h \
     orthographicLens.I orthographicLens.h perspectiveLens.I  \
     orthographicLens.I orthographicLens.h perspectiveLens.I  \
     perspectiveLens.h \
     perspectiveLens.h \
     preparedGraphicsObjects.I preparedGraphicsObjects.h \
     preparedGraphicsObjects.I preparedGraphicsObjects.h \
     lens.h lens.I \
     lens.h lens.I \
+    queryContext.I queryContext.h \
     savedContext.I savedContext.h \
     savedContext.I savedContext.h \
     shaderContext.h shaderContext.I \
     shaderContext.h shaderContext.I \
     shaderExpansion.h shaderExpansion.I \
     shaderExpansion.h shaderExpansion.I \
@@ -93,10 +95,13 @@
     indexBufferContext.cxx \
     indexBufferContext.cxx \
     material.cxx  \
     material.cxx  \
     internalName.cxx \
     internalName.cxx \
-    materialPool.cxx matrixLens.cxx orthographicLens.cxx  \
+    materialPool.cxx matrixLens.cxx \
+    occlusionQuery.cxx \
+    orthographicLens.cxx  \
     perspectiveLens.cxx \
     perspectiveLens.cxx \
     preparedGraphicsObjects.cxx \
     preparedGraphicsObjects.cxx \
     lens.cxx  \
     lens.cxx  \
+    queryContext.cxx \
     savedContext.cxx \
     savedContext.cxx \
     shaderContext.cxx \
     shaderContext.cxx \
     shaderExpansion.cxx \
     shaderExpansion.cxx \
@@ -146,10 +151,12 @@
     internalName.I internalName.h \
     internalName.I internalName.h \
     material.I material.h \
     material.I material.h \
     materialPool.I materialPool.h matrixLens.I matrixLens.h \
     materialPool.I materialPool.h matrixLens.I matrixLens.h \
+    occlusionQueryContext.I occlusionQueryContext.h \
     orthographicLens.I orthographicLens.h perspectiveLens.I \
     orthographicLens.I orthographicLens.h perspectiveLens.I \
     perspectiveLens.h \
     perspectiveLens.h \
     preparedGraphicsObjects.I preparedGraphicsObjects.h \
     preparedGraphicsObjects.I preparedGraphicsObjects.h \
     lens.h lens.I \
     lens.h lens.I \
+    queryContext.I queryContext.h \
     savedContext.I savedContext.h \
     savedContext.I savedContext.h \
     shaderContext.h shaderContext.I \
     shaderContext.h shaderContext.I \
     shaderExpansion.h shaderExpansion.I \
     shaderExpansion.h shaderExpansion.I \

+ 4 - 0
panda/src/gobj/config_gobj.cxx

@@ -33,10 +33,12 @@
 #include "geomVertexData.h"
 #include "geomVertexData.h"
 #include "geomVertexFormat.h"
 #include "geomVertexFormat.h"
 #include "material.h"
 #include "material.h"
+#include "occlusionQueryContext.h"
 #include "orthographicLens.h"
 #include "orthographicLens.h"
 #include "matrixLens.h"
 #include "matrixLens.h"
 #include "perspectiveLens.h"
 #include "perspectiveLens.h"
 #include "lens.h"
 #include "lens.h"
+#include "queryContext.h"
 #include "sliderTable.h"
 #include "sliderTable.h"
 #include "texture.h"
 #include "texture.h"
 #include "textureStage.h"
 #include "textureStage.h"
@@ -263,10 +265,12 @@ ConfigureFn(config_gobj) {
   VertexBufferContext::init_type();
   VertexBufferContext::init_type();
   IndexBufferContext::init_type();
   IndexBufferContext::init_type();
   Material::init_type();
   Material::init_type();
+  OcclusionQueryContext::init_type();
   OrthographicLens::init_type();
   OrthographicLens::init_type();
   MatrixLens::init_type();
   MatrixLens::init_type();
   PerspectiveLens::init_type();
   PerspectiveLens::init_type();
   Lens::init_type();
   Lens::init_type();
+  QueryContext::init_type();
   SliderTable::init_type();
   SliderTable::init_type();
   Texture::init_type();
   Texture::init_type();
   TextureStage::init_type();
   TextureStage::init_type();

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

@@ -1,4 +1,3 @@
-
 #include "config_gobj.cxx"
 #include "config_gobj.cxx"
 #include "indexBufferContext.cxx"
 #include "indexBufferContext.cxx"
 #include "internalName.cxx"
 #include "internalName.cxx"
@@ -6,9 +5,11 @@
 #include "material.cxx"
 #include "material.cxx"
 #include "materialPool.cxx"
 #include "materialPool.cxx"
 #include "matrixLens.cxx"
 #include "matrixLens.cxx"
+#include "occlusionQueryContext.cxx"
 #include "orthographicLens.cxx"
 #include "orthographicLens.cxx"
 #include "perspectiveLens.cxx"
 #include "perspectiveLens.cxx"
 #include "preparedGraphicsObjects.cxx"
 #include "preparedGraphicsObjects.cxx"
+#include "queryContext.cxx"
 #include "savedContext.cxx"
 #include "savedContext.cxx"
 #include "sliderTable.cxx"
 #include "sliderTable.cxx"
 #include "texture.cxx"
 #include "texture.cxx"

+ 27 - 0
panda/src/gobj/occlusionQueryContext.I

@@ -0,0 +1,27 @@
+// Filename: occlusionQueryContext.I
+// Created by:  drose (27Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OcclusionQueryContext::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE OcclusionQueryContext::
+OcclusionQueryContext() {
+}

+ 36 - 0
panda/src/gobj/occlusionQueryContext.cxx

@@ -0,0 +1,36 @@
+// Filename: occlusionQueryContext.cxx
+// Created by:  drose (27Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "occlusionQueryContext.h"
+
+TypeHandle OcclusionQueryContext::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: OcclusionQueryContext::get_num_fragments
+//       Access: Public, Virtual
+//  Description: Returns the number of fragments (pixels) of the
+//               specified geometry that passed the depth test.
+//               If is_answer_ready() did not return true, this
+//               function may block before it returns.
+//
+//               It is only valid to call this from the draw thread.
+////////////////////////////////////////////////////////////////////
+int OcclusionQueryContext::
+get_num_fragments() const {
+  return 0;
+}

+ 61 - 0
panda/src/gobj/occlusionQueryContext.h

@@ -0,0 +1,61 @@
+// Filename: occlusionQueryContext.h
+// Created by:  drose (27Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef OCCLUSIONQUERYCONTEXT_H
+#define OCCLUSIONQUERYCONTEXT_H
+
+#include "pandabase.h"
+#include "queryContext.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : OcclusionQueryContext
+// Description : Returned from a GSG in response to
+//               begin_occlusion_query() .. end_occlusion_query(),
+//               this records the number of fragments (pixels) that
+//               passed the depth test between the bracketing calls.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA OcclusionQueryContext : public QueryContext {
+public:
+  INLINE OcclusionQueryContext();
+
+  virtual int get_num_fragments() const=0;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    QueryContext::init_type();
+    register_type(_type_handle, "OcclusionQueryContext",
+                  QueryContext::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;
+
+  friend class PreparedGraphicsObjects;
+};
+
+#include "occlusionQueryContext.I"
+
+#endif
+

+ 27 - 0
panda/src/gobj/queryContext.I

@@ -0,0 +1,27 @@
+// Filename: queryContext.I
+// Created by:  drose (27Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: QueryContext::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE QueryContext::
+QueryContext() {
+}

+ 57 - 0
panda/src/gobj/queryContext.cxx

@@ -0,0 +1,57 @@
+// Filename: queryContext.cxx
+// Created by:  drose (27Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "queryContext.h"
+
+TypeHandle QueryContext::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: QueryContext::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+QueryContext::
+~QueryContext() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: QueryContext::is_answer_ready
+//       Access: Public, Virtual
+//  Description: Returns true if the query's answer is ready, false
+//               otherwise.  If this returns false, the application
+//               must continue to poll until it returns true.
+//
+//               It is only valid to call this from the draw thread.
+////////////////////////////////////////////////////////////////////
+bool QueryContext::
+is_answer_ready() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: QueryContext::waiting_for_answer
+//       Access: Public, Virtual
+//  Description: Requests the graphics engine to expedite the pending
+//               answer--the application is now waiting until the
+//               answer is ready.
+//
+//               It is only valid to call this from the draw thread.
+////////////////////////////////////////////////////////////////////
+void QueryContext::
+waiting_for_answer() {
+}

+ 71 - 0
panda/src/gobj/queryContext.h

@@ -0,0 +1,71 @@
+// Filename: queryContext.h
+// Created by:  drose (27Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef QUERYCONTEXT_H
+#define QUERYCONTEXT_H
+
+#include "pandabase.h"
+#include "typedReferenceCount.h"
+
+class PreparedGraphicsObjects;
+
+////////////////////////////////////////////////////////////////////
+//       Class : QueryContext
+// Description : This is a base class for queries that might require a
+//               round-trip to the graphics engine.  The idea is that
+//               when you ask the GSG to make a particular query, it
+//               returns a QueryContext, which does not necessarily
+//               have the answer right away (but it will eventually).
+//
+//               Unlike SavedContext, QueryContext is
+//               reference-counted.  It removes itself from the GSG
+//               when the last reference goes away.  You're
+//               responsible for keeping the pointer to the
+//               QueryContext as long as you are interested in the
+//               answer.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA QueryContext : public TypedReferenceCount {
+public:
+  INLINE QueryContext();
+  virtual ~QueryContext();
+
+  virtual bool is_answer_ready() const=0;
+  virtual void waiting_for_answer();
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "QueryContext",
+                  TypedReferenceCount::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 "queryContext.I"
+
+#endif
+

+ 7 - 0
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -32,6 +32,7 @@ class NodePath;
 
 
 class VertexBufferContext;
 class VertexBufferContext;
 class IndexBufferContext;
 class IndexBufferContext;
+class OcclusionQueryContext;
 class GeomContext;
 class GeomContext;
 class GeomNode;
 class GeomNode;
 class Geom;
 class Geom;
@@ -57,6 +58,7 @@ class GeomLinestrips;
 class GeomPoints;
 class GeomPoints;
 class GeomMunger;
 class GeomMunger;
 
 
+class SceneSetup;
 class PreparedGraphicsObjects;
 class PreparedGraphicsObjects;
 class GraphicsOutput;
 class GraphicsOutput;
 class Texture;
 class Texture;
@@ -118,11 +120,13 @@ class EXPCL_PANDA GraphicsStateGuardianBase : public TypedWritableReferenceCount
 PUBLISHED:
 PUBLISHED:
   virtual bool get_supports_multisample() const=0;
   virtual bool get_supports_multisample() const=0;
   virtual int get_supported_geom_rendering() const=0;
   virtual int get_supported_geom_rendering() const=0;
+  virtual bool get_supports_occlusion_query() const=0;
 
 
 public:
 public:
   // These are some general interface functions; they're defined here
   // These are some general interface functions; they're defined here
   // mainly to make it easy to call these from code in some directory
   // mainly to make it easy to call these from code in some directory
   // that display depends on.
   // that display depends on.
+  virtual SceneSetup *get_scene() const=0;
   virtual PreparedGraphicsObjects *get_prepared_objects()=0;
   virtual PreparedGraphicsObjects *get_prepared_objects()=0;
 
 
   virtual TextureContext *prepare_texture(Texture *tex)=0;
   virtual TextureContext *prepare_texture(Texture *tex)=0;
@@ -140,6 +144,9 @@ public:
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data)=0;
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data)=0;
   virtual void release_index_buffer(IndexBufferContext *ibc)=0;
   virtual void release_index_buffer(IndexBufferContext *ibc)=0;
 
 
+  virtual void begin_occlusion_query()=0;
+  virtual PT(OcclusionQueryContext) end_occlusion_query()=0;
+
   virtual PT(GeomMunger) get_geom_munger(const RenderState *state)=0;
   virtual PT(GeomMunger) get_geom_munger(const RenderState *state)=0;
 
 
   virtual void set_state_and_transform(const RenderState *state,
   virtual void set_state_and_transform(const RenderState *state,

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

@@ -41,6 +41,7 @@ PUBLISHED:
     C_red    = 0x001,
     C_red    = 0x001,
     C_green  = 0x002,
     C_green  = 0x002,
     C_blue   = 0x004,
     C_blue   = 0x004,
+    C_rgb    = 0x007,  // == C_red | C_green | C_blue
     C_alpha  = 0x008,
     C_alpha  = 0x008,
     C_all    = 0x00f,
     C_all    = 0x00f,
   };
   };

+ 5 - 8
panda/src/pgraph/cullBinManager.cxx

@@ -338,23 +338,17 @@ parse_bin_type(const string &bin_type) {
   } else if (cmp_nocase_uh(bin_type, "state_sorted") == 0) {
   } else if (cmp_nocase_uh(bin_type, "state_sorted") == 0) {
     return BT_state_sorted;
     return BT_state_sorted;
 
 
-  } else if (cmp_nocase_uh(bin_type, "statesorted") == 0) {
-    return BT_state_sorted;
-
   } else if (cmp_nocase_uh(bin_type, "fixed") == 0) {
   } else if (cmp_nocase_uh(bin_type, "fixed") == 0) {
     return BT_fixed;
     return BT_fixed;
 
 
   } else if (cmp_nocase_uh(bin_type, "back_to_front") == 0) {
   } else if (cmp_nocase_uh(bin_type, "back_to_front") == 0) {
     return BT_back_to_front;
     return BT_back_to_front;
 
 
-  } else if (cmp_nocase_uh(bin_type, "backtofront") == 0) {
-    return BT_back_to_front;
-
   } else if (cmp_nocase_uh(bin_type, "front_to_back") == 0) {
   } else if (cmp_nocase_uh(bin_type, "front_to_back") == 0) {
     return BT_front_to_back;
     return BT_front_to_back;
 
 
-  } else if (cmp_nocase_uh(bin_type, "fronttoback") == 0) {
-    return BT_front_to_back;
+  } else if (cmp_nocase_uh(bin_type, "hierarchical_z") == 0) {
+    return BT_hierarchical_z;
 
 
   } else {
   } else {
     return BT_invalid;
     return BT_invalid;
@@ -385,6 +379,9 @@ operator << (ostream &out, CullBinManager::BinType bin_type) {
     
     
   case CullBinManager::BT_fixed:
   case CullBinManager::BT_fixed:
     return out << "fixed";
     return out << "fixed";
+    
+  case CullBinManager::BT_hierarchical_z:
+    return out << "hierarchical_z";
   }
   }
 
 
   return out << "**invalid BinType(" << (int)bin_type << ")**";
   return out << "**invalid BinType(" << (int)bin_type << ")**";

+ 1 - 0
panda/src/pgraph/cullBinManager.h

@@ -47,6 +47,7 @@ PUBLISHED:
     BT_back_to_front,
     BT_back_to_front,
     BT_front_to_back,
     BT_front_to_back,
     BT_fixed,
     BT_fixed,
+    BT_hierarchical_z,
   };
   };
 
 
   int add_bin(const string &name, BinType type, int sort);
   int add_bin(const string &name, BinType type, int sort);

+ 4 - 0
panda/src/pgraph/cullableObject.h

@@ -86,6 +86,10 @@ public:
   CPT(TransformState) _modelview_transform;
   CPT(TransformState) _modelview_transform;
   CullableObject *_next;
   CullableObject *_next;
 
 
+  // This flag is only used by certain CullBin types.  In particular,
+  // it is used by CullBinHierarchicalZBuffer.
+  bool _already_drawn;
+
 private:
 private:
   void munge_points_to_quads(const CullTraverser *traverser);
   void munge_points_to_quads(const CullTraverser *traverser);
   void munge_texcoord_light_vector(const CullTraverser *traverser);
   void munge_texcoord_light_vector(const CullTraverser *traverser);

+ 2 - 2
panda/src/pgraph/portalNode.cxx

@@ -99,9 +99,9 @@ PortalNode(const PortalNode &copy) :
   _vertices(copy._vertices),
   _vertices(copy._vertices),
   _cell_in(copy._cell_in),
   _cell_in(copy._cell_in),
   _cell_out(copy._cell_out),
   _cell_out(copy._cell_out),
+  _clip_plane(copy._clip_plane),
   _visible(copy._visible),
   _visible(copy._visible),
-  _open(copy._open),
-  _clip_plane(copy._clip_plane)
+  _open(copy._open)
 {
 {
 }
 }