Browse Source

occlusion test

David Rose 19 years ago
parent
commit
85ba9e05b8
34 changed files with 1061 additions and 267 deletions
  1. 3 3
      panda/src/cull/Sources.pp
  2. 7 7
      panda/src/cull/config_cull.cxx
  3. 11 11
      panda/src/cull/cullBinBackToFront.I
  4. 1 1
      panda/src/cull/cullBinBackToFront.cxx
  5. 1 1
      panda/src/cull/cullBinBackToFront.h
  6. 11 11
      panda/src/cull/cullBinFixed.I
  7. 1 1
      panda/src/cull/cullBinFixed.cxx
  8. 1 1
      panda/src/cull/cullBinFixed.h
  9. 11 11
      panda/src/cull/cullBinFrontToBack.I
  10. 1 1
      panda/src/cull/cullBinFrontToBack.cxx
  11. 1 1
      panda/src/cull/cullBinFrontToBack.h
  12. 74 37
      panda/src/cull/cullBinOcclusionTest.I
  13. 347 74
      panda/src/cull/cullBinOcclusionTest.cxx
  14. 70 19
      panda/src/cull/cullBinOcclusionTest.h
  15. 11 11
      panda/src/cull/cullBinStateSorted.I
  16. 1 1
      panda/src/cull/cullBinStateSorted.cxx
  17. 1 1
      panda/src/cull/cullBinStateSorted.h
  18. 1 1
      panda/src/cull/cullBinUnsorted.I
  19. 1 1
      panda/src/cull/cull_composite2.cxx
  20. 6 3
      panda/src/display/graphicsEngine.cxx
  21. 2 0
      panda/src/pgraph/Sources.pp
  22. 7 0
      panda/src/pgraph/config_pgraph.cxx
  23. 1 0
      panda/src/pgraph/config_pgraph.h
  24. 64 1
      panda/src/pgraph/cullBin.I
  25. 35 1
      panda/src/pgraph/cullBin.cxx
  26. 23 4
      panda/src/pgraph/cullBin.h
  27. 43 0
      panda/src/pgraph/cullBinEnums.h
  28. 149 2
      panda/src/pgraph/cullBinManager.I
  29. 5 11
      panda/src/pgraph/cullBinManager.cxx
  30. 15 10
      panda/src/pgraph/cullBinManager.h
  31. 148 37
      panda/src/pgraph/cullResult.cxx
  32. 5 1
      panda/src/pgraph/cullResult.h
  33. 1 1
      panda/src/pgraph/cullableObject.h
  34. 2 2
      panda/src/pstatclient/pStatProperties.cxx

+ 3 - 3
panda/src/cull/Sources.pp

@@ -14,7 +14,7 @@
     cullBinBackToFront.h cullBinBackToFront.I \
     cullBinBackToFront.h cullBinBackToFront.I \
     cullBinFixed.h cullBinFixed.I \
     cullBinFixed.h cullBinFixed.I \
     cullBinFrontToBack.h cullBinFrontToBack.I \
     cullBinFrontToBack.h cullBinFrontToBack.I \
-    cullBinHierarchicalZBuffer.h cullBinHierarchicalZBuffer.I \
+    cullBinOcclusionTest.h cullBinOcclusionTest.I \
     cullBinStateSorted.h cullBinStateSorted.I \
     cullBinStateSorted.h cullBinStateSorted.I \
     cullBinUnsorted.h cullBinUnsorted.I \
     cullBinUnsorted.h cullBinUnsorted.I \
     drawCullHandler.h drawCullHandler.I
     drawCullHandler.h drawCullHandler.I
@@ -28,7 +28,7 @@
     cullBinBackToFront.cxx \
     cullBinBackToFront.cxx \
     cullBinFixed.cxx \
     cullBinFixed.cxx \
     cullBinFrontToBack.cxx \
     cullBinFrontToBack.cxx \
-    cullBinHierarchicalZBuffer.cxx \
+    cullBinOcclusionTest.cxx \
     cullBinStateSorted.cxx \
     cullBinStateSorted.cxx \
     cullBinUnsorted.cxx \
     cullBinUnsorted.cxx \
     drawCullHandler.cxx
     drawCullHandler.cxx
@@ -39,7 +39,7 @@
     cullBinBackToFront.h cullBinBackToFront.I \
     cullBinBackToFront.h cullBinBackToFront.I \
     cullBinFixed.h cullBinFixed.I \
     cullBinFixed.h cullBinFixed.I \
     cullBinFrontToBack.h cullBinFrontToBack.I \
     cullBinFrontToBack.h cullBinFrontToBack.I \
-    cullBinHierarchicalZBuffer.h cullBinHierarchicalZBuffer.I \
+    cullBinOcclusionTest.h cullBinOcclusionTest.I \
     cullBinStateSorted.h cullBinStateSorted.I \
     cullBinStateSorted.h cullBinStateSorted.I \
     cullBinUnsorted.h cullBinUnsorted.I \
     cullBinUnsorted.h cullBinUnsorted.I \
     drawCullHandler.h drawCullHandler.I
     drawCullHandler.h drawCullHandler.I

+ 7 - 7
panda/src/cull/config_cull.cxx

@@ -21,7 +21,7 @@
 #include "cullBinBackToFront.h"
 #include "cullBinBackToFront.h"
 #include "cullBinFixed.h"
 #include "cullBinFixed.h"
 #include "cullBinFrontToBack.h"
 #include "cullBinFrontToBack.h"
-#include "cullBinHierarchicalZBuffer.h"
+#include "cullBinOcclusionTest.h"
 #include "cullBinStateSorted.h"
 #include "cullBinStateSorted.h"
 #include "cullBinUnsorted.h"
 #include "cullBinUnsorted.h"
 
 
@@ -38,7 +38,7 @@ ConfigureFn(config_cull) {
 ConfigVariableInt max_objects_per_octree_node
 ConfigVariableInt max_objects_per_octree_node
 ("max-objects-per-octree-node", 10,
 ("max-objects-per-octree-node", 10,
  PRC_DESC("Specifies the maximum number of objects collected per octree "
  PRC_DESC("Specifies the maximum number of objects collected per octree "
-          "node, by the hierarchical-z cull bin algorithm."));
+          "node, by the occlusion test cull bin algorithm."));
 
 
 ConfigVariableDouble octree_multiassign_ratio
 ConfigVariableDouble octree_multiassign_ratio
 ("octree-multiassign-ratio", 0.1,
 ("octree-multiassign-ratio", 0.1,
@@ -49,8 +49,8 @@ ConfigVariableDouble octree_multiassign_ratio
 
 
 ConfigVariableBool show_octree
 ConfigVariableBool show_octree
 ("show-octree", false,
 ("show-octree", false,
- PRC_DESC("When true, visualizes the octree created for the hierarchical "
-          "Z-buffer algorithm, by drawing wireframe cubes around each "
+ PRC_DESC("When true, visualizes the octree created for the occlusion "
+          "test algorithm, by drawing wireframe cubes around each "
           "nonempty octree cell."));
           "nonempty octree cell."));
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -72,7 +72,7 @@ init_libcull() {
   CullBinBackToFront::init_type();
   CullBinBackToFront::init_type();
   CullBinFixed::init_type();
   CullBinFixed::init_type();
   CullBinFrontToBack::init_type();
   CullBinFrontToBack::init_type();
-  CullBinHierarchicalZBuffer::init_type();
+  CullBinOcclusionTest::init_type();
   CullBinStateSorted::init_type();
   CullBinStateSorted::init_type();
   CullBinUnsorted::init_type();
   CullBinUnsorted::init_type();
 
 
@@ -87,6 +87,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);
+  bin_manager->register_bin_type(CullBinManager::BT_occlusion_test,
+                                 CullBinOcclusionTest::make_bin);
 }
 }

+ 11 - 11
panda/src/cull/cullBinBackToFront.I

@@ -17,6 +17,17 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinBackToFront::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullBinBackToFront::
+CullBinBackToFront(const string &name, GraphicsStateGuardianBase *gsg) :
+  CullBin(name, BT_back_to_front, gsg)
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBinBackToFront::ObjectData::Constructor
 //     Function: CullBinBackToFront::ObjectData::Constructor
 //       Access: Public
 //       Access: Public
@@ -40,14 +51,3 @@ operator < (const ObjectData &other) const {
   return _dist > other._dist;
   return _dist > other._dist;
 }
 }
 
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: CullBinBackToFront::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE CullBinBackToFront::
-CullBinBackToFront(const string &name, GraphicsStateGuardianBase *gsg) :
-  CullBin(name, gsg)
-{
-}

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

@@ -86,7 +86,7 @@ add_object(CullableObject *object) {
 //               draw.
 //               draw.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullBinBackToFront::
 void CullBinBackToFront::
-finish_cull() {
+finish_cull(SceneSetup *) {
   PStatTimer timer(_cull_this_pcollector);
   PStatTimer timer(_cull_this_pcollector);
   sort(_objects.begin(), _objects.end());
   sort(_objects.begin(), _objects.end());
 }
 }

+ 1 - 1
panda/src/cull/cullBinBackToFront.h

@@ -44,7 +44,7 @@ public:
   
   
 
 
   virtual void add_object(CullableObject *object);
   virtual void add_object(CullableObject *object);
-  virtual void finish_cull();
+  virtual void finish_cull(SceneSetup *scene_setup);
   virtual void draw();
   virtual void draw();
 
 
 private:
 private:

+ 11 - 11
panda/src/cull/cullBinFixed.I

@@ -17,6 +17,17 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinFixed::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullBinFixed::
+CullBinFixed(const string &name, GraphicsStateGuardianBase *gsg) :
+  CullBin(name, BT_fixed, gsg)
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBinFixed::ObjectData::Constructor
 //     Function: CullBinFixed::ObjectData::Constructor
 //       Access: Public
 //       Access: Public
@@ -40,14 +51,3 @@ operator < (const ObjectData &other) const {
   return _draw_order < other._draw_order;
   return _draw_order < other._draw_order;
 }
 }
 
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: CullBinFixed::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE CullBinFixed::
-CullBinFixed(const string &name, GraphicsStateGuardianBase *gsg) :
-  CullBin(name, gsg)
-{
-}

+ 1 - 1
panda/src/cull/cullBinFixed.cxx

@@ -74,7 +74,7 @@ add_object(CullableObject *object) {
 //               draw.
 //               draw.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullBinFixed::
 void CullBinFixed::
-finish_cull() {
+finish_cull(SceneSetup *) {
   PStatTimer timer(_cull_this_pcollector);
   PStatTimer timer(_cull_this_pcollector);
   stable_sort(_objects.begin(), _objects.end());
   stable_sort(_objects.begin(), _objects.end());
 }
 }

+ 1 - 1
panda/src/cull/cullBinFixed.h

@@ -46,7 +46,7 @@ public:
   static CullBin *make_bin(const string &name, GraphicsStateGuardianBase *gsg);
   static CullBin *make_bin(const string &name, GraphicsStateGuardianBase *gsg);
 
 
   virtual void add_object(CullableObject *object);
   virtual void add_object(CullableObject *object);
-  virtual void finish_cull();
+  virtual void finish_cull(SceneSetup *scene_setup);
   virtual void draw();
   virtual void draw();
 
 
 private:
 private:

+ 11 - 11
panda/src/cull/cullBinFrontToBack.I

@@ -17,6 +17,17 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinFrontToBack::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullBinFrontToBack::
+CullBinFrontToBack(const string &name, GraphicsStateGuardianBase *gsg) :
+  CullBin(name, BT_front_to_back, gsg)
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBinFrontToBack::ObjectData::Constructor
 //     Function: CullBinFrontToBack::ObjectData::Constructor
 //       Access: Public
 //       Access: Public
@@ -40,14 +51,3 @@ operator < (const ObjectData &other) const {
   return _dist < other._dist;
   return _dist < other._dist;
 }
 }
 
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: CullBinFrontToBack::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE CullBinFrontToBack::
-CullBinFrontToBack(const string &name, GraphicsStateGuardianBase *gsg) :
-  CullBin(name, gsg)
-{
-}

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

@@ -86,7 +86,7 @@ add_object(CullableObject *object) {
 //               draw.
 //               draw.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullBinFrontToBack::
 void CullBinFrontToBack::
-finish_cull() {
+finish_cull(SceneSetup *) {
   PStatTimer timer(_cull_this_pcollector);
   PStatTimer timer(_cull_this_pcollector);
   sort(_objects.begin(), _objects.end());
   sort(_objects.begin(), _objects.end());
 }
 }

+ 1 - 1
panda/src/cull/cullBinFrontToBack.h

@@ -44,7 +44,7 @@ public:
   static CullBin *make_bin(const string &name, GraphicsStateGuardianBase *gsg);
   static CullBin *make_bin(const string &name, GraphicsStateGuardianBase *gsg);
 
 
   virtual void add_object(CullableObject *object);
   virtual void add_object(CullableObject *object);
-  virtual void finish_cull();
+  virtual void finish_cull(SceneSetup *scene_setup);
   virtual void draw();
   virtual void draw();
 
 
 private:
 private:

+ 74 - 37
panda/src/cull/cullBinHierarchicalZBuffer.I → panda/src/cull/cullBinOcclusionTest.I

@@ -1,4 +1,4 @@
-// Filename: cullBinHierarchicalZBuffer.I
+// Filename: cullBinOcclusionTest.I
 // Created by:  drose (24Mar06)
 // Created by:  drose (24Mar06)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -18,11 +18,47 @@
 
 
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::ObjectData::Constructor
+//     Function: CullBinOcclusionTest::Copy Constructor
+//       Access: Protected
+//  Description: This constructor is used in make_new().  It
+//               constructs a new, empty CullBin for rendering the
+//               next frame's data.  However, it preserves the
+//               _prev_draw pointer so that there will be frame
+//               continuity.
+////////////////////////////////////////////////////////////////////
+INLINE CullBinOcclusionTest::
+CullBinOcclusionTest(const CullBinOcclusionTest &copy) :
+  CullBin(copy),
+  _draw_occlusion_pcollector(copy._draw_occlusion_pcollector)
+{
+  nassertv(_gsg->get_supports_occlusion_query());
+
+  _num_objects = 0;
+  _prev_draw = copy._prev_draw;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinOcclusionTest::Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE CullBinHierarchicalZBuffer::ObjectData::
+INLINE CullBinOcclusionTest::
+CullBinOcclusionTest(const string &name, GraphicsStateGuardianBase *gsg) :
+  CullBin(name, BT_occlusion_test, gsg),
+  _draw_occlusion_pcollector(_draw_this_pcollector, "Occlusion")
+{
+  nassertv(_gsg->get_supports_occlusion_query());
+
+  _num_objects = 0;
+  _prev_draw = new PrevDrawData;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinOcclusionTest::ObjectData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullBinOcclusionTest::ObjectData::
 ObjectData(CullableObject *object, BoundingSphere *bounds) :
 ObjectData(CullableObject *object, BoundingSphere *bounds) :
   _object(object),
   _object(object),
   _bounds(bounds)
   _bounds(bounds)
@@ -30,92 +66,93 @@ ObjectData(CullableObject *object, BoundingSphere *bounds) :
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::ObjectData::Copy Constructor
+//     Function: CullBinOcclusionTest::ObjectData::Copy Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE CullBinHierarchicalZBuffer::ObjectData::
-ObjectData(const CullBinHierarchicalZBuffer::ObjectData &copy) :
+INLINE CullBinOcclusionTest::ObjectData::
+ObjectData(const CullBinOcclusionTest::ObjectData &copy) :
   _object(copy._object),
   _object(copy._object),
   _bounds(copy._bounds)
   _bounds(copy._bounds)
 {
 {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::ObjectData::Copy Assignment
+//     Function: CullBinOcclusionTest::ObjectData::Copy Assignment
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE void CullBinHierarchicalZBuffer::ObjectData::
-operator = (const CullBinHierarchicalZBuffer::ObjectData &copy) {
+INLINE void CullBinOcclusionTest::ObjectData::
+operator = (const CullBinOcclusionTest::ObjectData &copy) {
   _object = copy._object;
   _object = copy._object;
   _bounds = copy._bounds;
   _bounds = copy._bounds;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::Constructor
+//     Function: CullBinOcclusionTest::VisibleGeom::Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE CullBinHierarchicalZBuffer::
-CullBinHierarchicalZBuffer(const string &name, GraphicsStateGuardianBase *gsg) :
-  CullBin(name, gsg),
-  _draw_occlusion_pcollector(_draw_this_pcollector, "Occlusion")
+INLINE CullBinOcclusionTest::VisibleGeom::
+VisibleGeom(const Geom *geom, const TransformState *net_transform) :
+  _geom(geom),
+  _net_transform(net_transform),
+  _bounds(geom->get_bounds())
 {
 {
-  nassertv(_gsg->get_supports_occlusion_query());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::initial_assign
+//     Function: CullBinOcclusionTest::VisibleGeom::operator <
 //       Access: Public
 //       Access: Public
-//  Description: Assigns the object to the node, in preparation for
-//               calling group_objects().
+//  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE void CullBinHierarchicalZBuffer::OctreeNode::
-initial_assign(const CullBinHierarchicalZBuffer::ObjectData &object_data) {
-  _objects.push_back(object_data);
+INLINE bool CullBinOcclusionTest::VisibleGeom::
+operator < (const CullBinOcclusionTest::VisibleGeom &other) const {
+  if (_geom != other._geom) {
+    return _geom < other._geom;
+  }
+  if (_net_transform != other._net_transform) {
+    return _net_transform < other._net_transform;
+  }
+  return _bounds < other._bounds;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::get_total_num_objects
+//     Function: CullBinOcclusionTest::OctreeNode::initial_assign
 //       Access: Public
 //       Access: Public
-//  Description: Returns the total number of objects in this node and
-//               all nested nodes.
+//  Description: Assigns the object to the node, in preparation for
+//               calling group_objects().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE int CullBinHierarchicalZBuffer::OctreeNode::
-get_total_num_objects() const {
-  return _total_num_objects;
+INLINE void CullBinOcclusionTest::OctreeNode::
+initial_assign(const CullBinOcclusionTest::ObjectData &object_data) {
+  _objects.push_back(object_data);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::reassign
+//     Function: CullBinOcclusionTest::OctreeNode::reassign
 //       Access: Public
 //       Access: Public
 //  Description: After determining that the object bisects one of the
 //  Description: After determining that the object bisects one of the
 //               major planes of the node, reassigns it back to the
 //               major planes of the node, reassigns it back to the
 //               same node.
 //               same node.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE void CullBinHierarchicalZBuffer::OctreeNode::
-reassign(const CullBinHierarchicalZBuffer::ObjectData &object_data) {
-  ++_total_num_objects;
+INLINE void CullBinOcclusionTest::OctreeNode::
+reassign(const CullBinOcclusionTest::ObjectData &object_data) {
   if (object_data._bounds->get_radius() / _half_side >= octree_multiassign_ratio) {
   if (object_data._bounds->get_radius() / _half_side >= octree_multiassign_ratio) {
     // The object is large enough to keep in this node.
     // The object is large enough to keep in this node.
-    //cerr << "    reassigning.\n";
     _objects.push_back(object_data);
     _objects.push_back(object_data);
   } else {
   } else {
-    //cerr << "    multidistributing.\n";
     multi_assign(object_data);
     multi_assign(object_data);
   }
   }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::assign_to_corner
+//     Function: CullBinOcclusionTest::OctreeNode::assign_to_corner
 //       Access: Private
 //       Access: Private
 //  Description: Assigns the object to the octree node in the
 //  Description: Assigns the object to the octree node in the
 //               indicated corner.
 //               indicated corner.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE void CullBinHierarchicalZBuffer::OctreeNode::
-assign_to_corner(int index, const CullBinHierarchicalZBuffer::ObjectData &object_data) {
-  //cerr << "    Assigning to corner " << hex << index << dec << ".\n";
+INLINE void CullBinOcclusionTest::OctreeNode::
+assign_to_corner(int index, const CullBinOcclusionTest::ObjectData &object_data) {
   nassertv(index >= 0 && index < 8);
   nassertv(index >= 0 && index < 8);
   if (_corners[index] == NULL) {
   if (_corners[index] == NULL) {
     make_corner(index);
     make_corner(index);

+ 347 - 74
panda/src/cull/cullBinHierarchicalZBuffer.cxx → panda/src/cull/cullBinOcclusionTest.cxx

@@ -1,4 +1,4 @@
-// Filename: cullBinHierarchicalZBuffer.cxx
+// Filename: cullBinOcclusionTest.cxx
 // Created by:  drose (24Mar06)
 // Created by:  drose (24Mar06)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -16,7 +16,7 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
-#include "cullBinHierarchicalZBuffer.h"
+#include "cullBinOcclusionTest.h"
 #include "graphicsStateGuardianBase.h"
 #include "graphicsStateGuardianBase.h"
 #include "geometricBoundingVolume.h"
 #include "geometricBoundingVolume.h"
 #include "geomLines.h"
 #include "geomLines.h"
@@ -31,21 +31,47 @@
 #include "config_cull.h"
 #include "config_cull.h"
 #include "thread.h"
 #include "thread.h"
 
 
-PStatCollector CullBinHierarchicalZBuffer::_wait_occlusion_pcollector("Wait:Occlusion test");
-PStatCollector CullBinHierarchicalZBuffer::_geoms_occluded_pcollector("Geoms:Occluded");
+#include <algorithm>
+
+PStatCollector CullBinOcclusionTest::_wait_occlusion_pcollector("Draw:Wait occlusion");
+PStatCollector CullBinOcclusionTest::_occlusion_previous_pcollector("Occlusion test:Previously visible");
+PStatCollector CullBinOcclusionTest::_occlusion_passed_pcollector("Occlusion test:Visible");
+PStatCollector CullBinOcclusionTest::_occlusion_failed_pcollector("Occlusion test:Occluded");
+
+const LPoint3f CullBinOcclusionTest::_corner_points[8] = {
+  LPoint3f(-1.0f, -1.0f, -1.0f),    // 0
+  LPoint3f(1.0f, -1.0f, -1.0f),     // OC_x
+  LPoint3f(-1.0f, 1.0f, -1.0f),     // OC_y
+  LPoint3f(1.0f, 1.0f, -1.0f),      // OC_x | OC_y
+  LPoint3f(-1.0f, -1.0f, 1.0f),     // OC_z
+  LPoint3f(1.0f, -1.0f, 1.0f),      // OC_x | OC_z
+  LPoint3f(-1.0f, 1.0f, 1.0f),      // OC_y | OC_z
+  LPoint3f(1.0f, 1.0f, 1.0f),       // OC_x | OC_y | OC_z
+};
+
+PT(Geom) CullBinOcclusionTest::_octree_solid_test;
+PT(Geom) CullBinOcclusionTest::_octree_wireframe_viz;
+CPT(RenderState) CullBinOcclusionTest::_octree_solid_test_state;
+TypeHandle CullBinOcclusionTest::_type_handle;
+
+// This class is used to sort the corner index numbers into order from
+// closest to the camera to furthest from the camera.
+class SortCornersFrontToBack {
+public:
+  inline bool operator () (int a, int b) {
+    return _distances[a] < _distances[b];
+  }
+  float _distances[8];
+};
 
 
-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
+//     Function: CullBinOcclusionTest::Destructor
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-CullBinHierarchicalZBuffer::
-~CullBinHierarchicalZBuffer() {
+CullBinOcclusionTest::
+~CullBinOcclusionTest() {
   ObjectPointers::iterator pi;
   ObjectPointers::iterator pi;
   for (pi = _object_pointers.begin(); pi != _object_pointers.end(); ++pi) {
   for (pi = _object_pointers.begin(); pi != _object_pointers.end(); ++pi) {
     delete (*pi);
     delete (*pi);
@@ -53,17 +79,17 @@ CullBinHierarchicalZBuffer::
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::make_bin
+//     Function: CullBinOcclusionTest::make_bin
 //       Access: Public, Static
 //       Access: Public, Static
 //  Description: Factory constructor for passing to the CullBinManager.
 //  Description: Factory constructor for passing to the CullBinManager.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-CullBin *CullBinHierarchicalZBuffer::
+CullBin *CullBinOcclusionTest::
 make_bin(const string &name, GraphicsStateGuardianBase *gsg) {
 make_bin(const string &name, GraphicsStateGuardianBase *gsg) {
-  return new CullBinHierarchicalZBuffer(name, gsg);
+  return new CullBinOcclusionTest(name, gsg);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::make_next
+//     Function: CullBinOcclusionTest::make_next
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: Returns a newly-allocated CullBin object that
 //  Description: Returns a newly-allocated CullBin object that
 //               contains a copy of just the subset of the data from
 //               contains a copy of just the subset of the data from
@@ -75,18 +101,21 @@ make_bin(const string &name, GraphicsStateGuardianBase *gsg) {
 //               return NULL (which is the default behavior of this
 //               return NULL (which is the default behavior of this
 //               method).
 //               method).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PT(CullBin) CullBinHierarchicalZBuffer::
+PT(CullBin) CullBinOcclusionTest::
 make_next() const {
 make_next() const {
-  return (CullBin *)NULL;
+  // We use the copy constructor, which creates an empty CullBin
+  // object, but also copies the _prev_draw pointer into it, so that
+  // there will be inter-frame continuity.
+  return new CullBinOcclusionTest(*this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::add_object
+//     Function: CullBinOcclusionTest::add_object
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: Adds a geom, along with its associated state, to
 //  Description: Adds a geom, along with its associated state, to
 //               the bin for rendering.
 //               the bin for rendering.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void CullBinHierarchicalZBuffer::
+void CullBinOcclusionTest::
 add_object(CullableObject *object) {
 add_object(CullableObject *object) {
   // Determine the world-space bounding sphere for the object.
   // Determine the world-space bounding sphere for the object.
   CPT(BoundingVolume) volume = object->_geom->get_bounds();
   CPT(BoundingVolume) volume = object->_geom->get_bounds();
@@ -94,6 +123,8 @@ add_object(CullableObject *object) {
     return;
     return;
   }
   }
 
 
+  ++_num_objects;
+
   PT(BoundingSphere) sphere;
   PT(BoundingSphere) sphere;
 
 
   if (volume->is_exact_type(BoundingSphere::get_class_type())) {
   if (volume->is_exact_type(BoundingSphere::get_class_type())) {
@@ -111,7 +142,7 @@ add_object(CullableObject *object) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::finish_cull
+//     Function: CullBinOcclusionTest::finish_cull
 //       Access: Public
 //       Access: Public
 //  Description: Called after all the geoms have been added, this
 //  Description: Called after all the geoms have been added, this
 //               indicates that the cull process is finished for this
 //               indicates that the cull process is finished for this
@@ -119,8 +150,8 @@ add_object(CullableObject *object) {
 //               post-processing (like sorting) before moving on to
 //               post-processing (like sorting) before moving on to
 //               draw.
 //               draw.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void CullBinHierarchicalZBuffer::
-finish_cull() {
+void CullBinOcclusionTest::
+finish_cull(SceneSetup *scene_setup) {
   PStatTimer timer(_cull_this_pcollector);
   PStatTimer timer(_cull_this_pcollector);
 
 
   // Now we have a loose list of objects that are to be rendered.
   // Now we have a loose list of objects that are to be rendered.
@@ -133,82 +164,146 @@ finish_cull() {
   // starting from the previous frame's octree.
   // starting from the previous frame's octree.
   _root.make_initial_bounds();
   _root.make_initial_bounds();
   _root.group_objects();
   _root.group_objects();
+
+  // Figure out the best front-to-back order of the corners of each
+  // octree node, based on the current viewing orientation.
+  CPT(TransformState) world_transform = scene_setup->get_world_transform();
+  const LMatrix4f &world_mat = world_transform->get_mat();
+
+  // A temporary object to record distances, and manage the sorting.
+  SortCornersFrontToBack sorter;
+
+  for (int i = 0; i < 8; ++i) {
+    _corners_front_to_back[i] = i;
+
+    LPoint3f p = _corner_points[i] * world_mat;
+    sorter._distances[i] = _gsg->compute_distance_to(p);
+  }
+
+  // Now sort, using the STL sort function.
+  ::sort(&_corners_front_to_back[0], &_corners_front_to_back[8], sorter);
+
+  // Finally, use that information to compute the distance of each
+  // octree node fom the camera plane.
+  _root.compute_distance(world_mat, *this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::draw
-//       Access: Public
+//     Function: CullBinOcclusionTest::draw
+//       Access: Public, Virtual
 //  Description: Draws all the geoms in the bin, in the appropriate
 //  Description: Draws all the geoms in the bin, in the appropriate
 //               order.
 //               order.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void CullBinHierarchicalZBuffer::
+void CullBinOcclusionTest::
 draw() {
 draw() {
   PStatTimer timer(_draw_this_pcollector);
   PStatTimer timer(_draw_this_pcollector);
-  _root.draw(*this);
+
+  // We'll want to know the near plane distance.
+  _near_distance = _gsg->get_scene()->get_lens()->get_near();
+
+  // First, draw any objects that were visible last frame.
+  int num_drawn_previous;
+  {
+    MutexHolder holder(_prev_draw->_visible_lock);
+    num_drawn_previous = _root.draw_previous(*this);
+  }
+
+  if (cull_cat.is_spam()) {
+    cull_cat.spam()
+      << "Drew " << num_drawn_previous << " objects.\n";
+  }
+
+  // Now draw the objects that may or may not remain.
+  int num_drawn;
+  num_drawn = _root.draw(*this);
+  if (show_octree) {
+    _root.draw_wireframe(*this);
+  }
 
 
   while (!_pending_nodes.empty()) {
   while (!_pending_nodes.empty()) {
     PendingNode &pending = _pending_nodes.front();
     PendingNode &pending = _pending_nodes.front();
-    bool is_occluded;
+    int num_fragments;
     if (!pending._query->is_answer_ready()) {
     if (!pending._query->is_answer_ready()) {
       // The answer isn't ready yet.  We have to wait.
       // The answer isn't ready yet.  We have to wait.
       PStatTimer timer(_wait_occlusion_pcollector);
       PStatTimer timer(_wait_occlusion_pcollector);
-      is_occluded = (pending._query->get_num_fragments() == 0);
+      num_fragments = pending._query->get_num_fragments();
     } else {
     } else {
       // The answer is ready right now.  There will be no waiting.
       // The answer is ready right now.  There will be no waiting.
-      is_occluded = (pending._query->get_num_fragments() == 0);
+      num_fragments = pending._query->get_num_fragments();
     }
     }
-    if (!is_occluded) {
+    if (cull_cat.is_spam()) {
+      cull_cat.spam()
+        << "OctreeNode " << *pending._octree_node
+        << " shows " << num_fragments << " fragments\n";
+    }
+    if (num_fragments != 0) {
       // The octree cell is at least partially visible.  Draw it, and
       // The octree cell is at least partially visible.  Draw it, and
       // continue recursion.
       // continue recursion.
-      pending._octree_node->draw(*this);
+      num_drawn += pending._octree_node->draw(*this);
       if (show_octree) {
       if (show_octree) {
         pending._octree_node->draw_wireframe(*this);
         pending._octree_node->draw_wireframe(*this);
       }
       }
-    } else {
-      // The octree cell is completely occluded.  Don't draw any of
-      // it, and don't recurse into it.
-      _geoms_occluded_pcollector.add_level(pending._octree_node->get_total_num_objects());
     }
     }
     _pending_nodes.pop_front();
     _pending_nodes.pop_front();
   }
   }
+
+  _occlusion_previous_pcollector.add_level(num_drawn_previous);
+  _occlusion_passed_pcollector.add_level(num_drawn);
+  _occlusion_failed_pcollector.add_level(_num_objects - (num_drawn_previous + num_drawn));
+
+  // Now, store a list of the objects within OctreeNodes that passed
+  // the occlusion test this frame, so we can ensure that they are
+  // drawn first next frame.
+  VisibleGeoms visible_geoms;
+  _root.record_visible_geoms(visible_geoms);
+  visible_geoms.sort();
+
+  {
+    MutexHolder holder(_prev_draw->_visible_lock);
+    _prev_draw->_visible_geoms.swap(visible_geoms);
+  }
 }
 }
 
 
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::Constructor
+//     Function: CullBinOcclusionTest::OctreeNode::Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-CullBinHierarchicalZBuffer::OctreeNode::
+CullBinOcclusionTest::OctreeNode::
 OctreeNode() {
 OctreeNode() {
-  _total_num_objects = 0;
   for (int i = 0; i < 8; ++i) {
   for (int i = 0; i < 8; ++i) {
     _corners[i] = (OctreeNode *)NULL;
     _corners[i] = (OctreeNode *)NULL;
   }
   }
+
+  _is_visible = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::Constructor
+//     Function: CullBinOcclusionTest::OctreeNode::Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-CullBinHierarchicalZBuffer::OctreeNode::
+CullBinOcclusionTest::OctreeNode::
 OctreeNode(float mid_x, float mid_y, float mid_z, float half_side) :
 OctreeNode(float mid_x, float mid_y, float mid_z, float half_side) :
   _mid(mid_x, mid_y, mid_z),
   _mid(mid_x, mid_y, mid_z),
   _half_side(half_side)
   _half_side(half_side)
 {
 {
-  _total_num_objects = 0;
   for (int i = 0; i < 8; ++i) {
   for (int i = 0; i < 8; ++i) {
     _corners[i] = (OctreeNode *)NULL;
     _corners[i] = (OctreeNode *)NULL;
   }
   }
+
+  // OctreeNodes are considered occluded until they pass the occlusion
+  // test.
+  _is_visible = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::Destructor
+//     Function: CullBinOcclusionTest::OctreeNode::Destructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-CullBinHierarchicalZBuffer::OctreeNode::
+CullBinOcclusionTest::OctreeNode::
 ~OctreeNode() {
 ~OctreeNode() {
   for (int i = 0; i < 8; ++i) {
   for (int i = 0; i < 8; ++i) {
     if (_corners[i] != (OctreeNode *)NULL) {
     if (_corners[i] != (OctreeNode *)NULL) {
@@ -218,12 +313,12 @@ CullBinHierarchicalZBuffer::OctreeNode::
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::make_initial_bounds
+//     Function: CullBinOcclusionTest::OctreeNode::make_initial_bounds
 //       Access: Public
 //       Access: Public
 //  Description: Determines the minmax bounding volume of the root
 //  Description: Determines the minmax bounding volume of the root
 //               OctreeNode, based on the objects it contains.
 //               OctreeNode, based on the objects it contains.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void CullBinHierarchicalZBuffer::OctreeNode::
+void CullBinOcclusionTest::OctreeNode::
 make_initial_bounds() {
 make_initial_bounds() {
   if (_objects.empty()) {
   if (_objects.empty()) {
     return;
     return;
@@ -255,16 +350,15 @@ make_initial_bounds() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::group_objects
+//     Function: CullBinOcclusionTest::OctreeNode::group_objects
 //       Access: Public
 //       Access: Public
 //  Description: Recursively groups the objects assigned to this node
 //  Description: Recursively groups the objects assigned to this node
 //               into smaller octree nodes, as appropriate.
 //               into smaller octree nodes, as appropriate.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void CullBinHierarchicalZBuffer::OctreeNode::
+void CullBinOcclusionTest::OctreeNode::
 group_objects() {
 group_objects() {
   if ((int)_objects.size() <= max_objects_per_octree_node) {
   if ((int)_objects.size() <= max_objects_per_octree_node) {
     // No need to do any more subdividing.
     // No need to do any more subdividing.
-    _total_num_objects += (int)_objects.size();
     return;
     return;
   }
   }
 
 
@@ -353,14 +447,35 @@ group_objects() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::occlusion_test
+//     Function: CullBinOcclusionTest::OctreeNode::compute_distance
+//       Access: Public
+//  Description: Recursively computes the _distance member of each
+//               octree node, as the linear distance from the camera
+//               plane to the nearest corner of the octree node.
+////////////////////////////////////////////////////////////////////
+void CullBinOcclusionTest::OctreeNode::
+compute_distance(const LMatrix4f &world_mat,
+                 CullBinOcclusionTest &bin) {
+  int index = bin._corners_front_to_back[0];
+  LPoint3f p = get_corner_point(index) * world_mat;
+  _distance = bin._gsg->compute_distance_to(p);
+  
+  for (int i = 0; i < 8; ++i) {
+    if (_corners[i] != (OctreeNode *)NULL) {
+      _corners[i]->compute_distance(world_mat, bin);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinOcclusionTest::OctreeNode::occlusion_test
 //       Access: Public
 //       Access: Public
 //  Description: Tests the octree node for visibility by rendering the
 //  Description: Tests the octree node for visibility by rendering the
 //               octree cube, invisibly, with a query.  Returns the
 //               octree cube, invisibly, with a query.  Returns the
 //               occlusion query object representing this test.
 //               occlusion query object representing this test.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PT(OcclusionQueryContext) CullBinHierarchicalZBuffer::OctreeNode::
-occlusion_test(CullBinHierarchicalZBuffer &bin) {
+PT(OcclusionQueryContext) CullBinOcclusionTest::OctreeNode::
+occlusion_test(CullBinOcclusionTest &bin) {
   // Draw the bounding volume for visualization.  This is
   // Draw the bounding volume for visualization.  This is
   // complicated because we're doing this at such a low level, here
   // complicated because we're doing this at such a low level, here
   // in the middle of the draw task--we've already completed the
   // in the middle of the draw task--we've already completed the
@@ -389,14 +504,63 @@ occlusion_test(CullBinHierarchicalZBuffer &bin) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::draw
+//     Function: CullBinOcclusionTest::OctreeNode::draw_previous
+//       Access: Public
+//  Description: Recursively draws only those objects which are known
+//               to have been drawn last frame.  Returns the number of
+//               objects drawn.
+////////////////////////////////////////////////////////////////////
+int CullBinOcclusionTest::OctreeNode::
+draw_previous(CullBinOcclusionTest &bin) {
+  int num_drawn = 0;
+
+  if (!_objects.empty()) {
+    Objects::const_iterator oi;
+    for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
+      CullableObject *object = (*oi)._object;
+      if (!object->_already_drawn) {
+        VisibleGeom vg(object->_geom, object->_net_transform);
+        if (bin._prev_draw->_visible_geoms.find(vg) != bin._prev_draw->_visible_geoms.end()) {
+          // This object is visible.
+          CullHandler::draw(object, bin._gsg);
+          object->_already_drawn = true;
+          ++num_drawn;
+        }
+      }
+    }
+  }
+
+  for (int i = 0; i < 8; ++i) {
+    // Render the child octree nodes in order from nearest to
+    // farthest.
+    int index = bin._corners_front_to_back[i];
+    if (_corners[index] != (OctreeNode *)NULL) {
+      num_drawn += _corners[index]->draw_previous(bin);
+    }
+  }
+
+  return num_drawn;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinOcclusionTest::OctreeNode::draw
 //       Access: Public
 //       Access: Public
 //  Description: Draws all of the objects in this node, and
 //  Description: Draws all of the objects in this node, and
 //               recursively performs occlusion tests on all of the
 //               recursively performs occlusion tests on all of the
-//               nested nodes.
+//               nested nodes.  Returns the number of objects drawn.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void CullBinHierarchicalZBuffer::OctreeNode::
-draw(CullBinHierarchicalZBuffer &bin) {
+int CullBinOcclusionTest::OctreeNode::
+draw(CullBinOcclusionTest &bin) {
+  // If the node is being drawn, it must have passed the occlusion
+  // test.  Flag it as such.
+  _is_visible = true;
+  if (cull_cat.is_spam()) {
+    cull_cat.spam()
+      << "Drawing OctreeNode " << this << "\n";
+  }
+
+  int num_drawn = 0;
+
   if (!_objects.empty()) {
   if (!_objects.empty()) {
     // Now draw the objects within the octree node.
     // Now draw the objects within the octree node.
     Objects::const_iterator oi;
     Objects::const_iterator oi;
@@ -405,28 +569,54 @@ draw(CullBinHierarchicalZBuffer &bin) {
       if (!object->_already_drawn) {
       if (!object->_already_drawn) {
         CullHandler::draw(object, bin._gsg);
         CullHandler::draw(object, bin._gsg);
         object->_already_drawn = true;
         object->_already_drawn = true;
+        ++num_drawn;
       }
       }
     }
     }
   }
   }
 
 
+  // Now recurse on each child node.
   for (int i = 0; i < 8; ++i) {
   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);
-      bin._pending_nodes.push_back(pending);
+    // Make sure we render the child octree nodes in order from
+    // nearest to farthest.
+    int index = bin._corners_front_to_back[i];
+    if (_corners[index] != (OctreeNode *)NULL) {
+      if (_corners[index]->_distance < bin._near_distance) {
+        // If a corner of the cube pokes through the near plane, go
+        // ahead and render the whole cube without performing an
+        // occlusion test.  The occlusion test would be invalid (since
+        // some or all of the cube would be clipped), but it's not
+        // likely that anything will be occluding something so close
+        // to the camera anyway.
+        _corners[index]->draw(bin);
+
+      } else {
+        // Otherwise, if the entire cube is in front of the near
+        // plane, perform an occlusion test by rendering out the
+        // (invisible) octree cube and then see if any pixels make it
+        // through the depth test.
+        PendingNode pending;
+        pending._octree_node = _corners[index];
+        pending._query = _corners[index]->occlusion_test(bin);
+
+        // We push it onto the list of nodes that are awaiting
+        // feedback from the graphics pipe.  This way we can go work
+        // on another octree node while we're waiting for this one.
+        bin._pending_nodes.push_back(pending);
+      }
     }
     }
   }
   }
+
+  return num_drawn;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::draw_wireframe
+//     Function: CullBinOcclusionTest::OctreeNode::draw_wireframe
 //       Access: Public
 //       Access: Public
 //  Description: Draws a wireframe representation of the octree cube,
 //  Description: Draws a wireframe representation of the octree cube,
 //               for debugging and visualization purposes.
 //               for debugging and visualization purposes.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void CullBinHierarchicalZBuffer::OctreeNode::
-draw_wireframe(CullBinHierarchicalZBuffer &bin) {
+void CullBinOcclusionTest::OctreeNode::
+draw_wireframe(CullBinOcclusionTest &bin) {
   // As above, this is complicated because we're doing this at such a
   // As above, this is complicated because we're doing this at such a
   // low level.
   // low level.
   CPT(TransformState) net_transform = TransformState::make_pos_hpr_scale
   CPT(TransformState) net_transform = TransformState::make_pos_hpr_scale
@@ -447,14 +637,51 @@ draw_wireframe(CullBinHierarchicalZBuffer &bin) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::get_octree_solid_test
+//     Function: CullBinOcclusionTest::OctreeNode::record_visible_geoms
+//       Access: Public
+//  Description: Records any Geoms associated with OctreeNodes that
+//               passed the occlusion test for next frame, to improve
+//               temporal coherence.
+////////////////////////////////////////////////////////////////////
+void CullBinOcclusionTest::OctreeNode::
+record_visible_geoms(CullBinOcclusionTest::VisibleGeoms &visible_geoms) {
+  if (_is_visible) {
+    Objects::const_iterator oi;
+    for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
+      CullableObject *object = (*oi)._object;
+      nassertv(object->_already_drawn);
+      VisibleGeom vg(object->_geom, object->_net_transform);
+      visible_geoms.push_back(vg);
+    }
+
+    for (int i = 0; i < 8; ++i) {
+      if (_corners[i] != (OctreeNode *)NULL) {
+        _corners[i]->record_visible_geoms(visible_geoms);
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinOcclusionTest::OctreeNode::output
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CullBinOcclusionTest::OctreeNode::
+output(ostream &out) const {
+  out << "OctreeNode " << _mid << ", " << _half_side << ", dist "
+      << _distance;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinOcclusionTest::get_octree_solid_test
 //       Access: Private, Static
 //       Access: Private, Static
 //  Description: Returns a Geom that may be used to render the solid
 //  Description: Returns a Geom that may be used to render the solid
 //               faces of octree cube, presumably invisibly.  This
 //               faces of octree cube, presumably invisibly.  This
 //               returns a cube over the range (-1, -1, -1) - (1, 1,
 //               returns a cube over the range (-1, -1, -1) - (1, 1,
 //               1).
 //               1).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-CPT(Geom) CullBinHierarchicalZBuffer::
+CPT(Geom) CullBinOcclusionTest::
 get_octree_solid_test() {
 get_octree_solid_test() {
   if (_octree_solid_test == (Geom *)NULL) {
   if (_octree_solid_test == (Geom *)NULL) {
     CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3();
     CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3();
@@ -491,13 +718,13 @@ get_octree_solid_test() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::get_octree_wireframe_viz
+//     Function: CullBinOcclusionTest::get_octree_wireframe_viz
 //       Access: Private, Static
 //       Access: Private, Static
 //  Description: Returns a Geom that may be used to render an
 //  Description: Returns a Geom that may be used to render an
 //               OctreeNode in wireframe.  This actually draws a
 //               OctreeNode in wireframe.  This actually draws a
 //               wireframe cube in the range (-1, -1, -1) - (1, 1, 1).
 //               wireframe cube in the range (-1, -1, -1) - (1, 1, 1).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-CPT(Geom) CullBinHierarchicalZBuffer::
+CPT(Geom) CullBinOcclusionTest::
 get_octree_wireframe_viz() {
 get_octree_wireframe_viz() {
   if (_octree_wireframe_viz == (Geom *)NULL) {
   if (_octree_wireframe_viz == (Geom *)NULL) {
     CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp();
     CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp();
@@ -535,12 +762,12 @@ get_octree_wireframe_viz() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::get_octree_solid_test_state
+//     Function: CullBinOcclusionTest::get_octree_solid_test_state
 //       Access: Private, Static
 //       Access: Private, Static
 //  Description: Returns the RenderState appropriate to rendering the
 //  Description: Returns the RenderState appropriate to rendering the
 //               octree test invisibly.
 //               octree test invisibly.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-CPT(RenderState) CullBinHierarchicalZBuffer::
+CPT(RenderState) CullBinOcclusionTest::
 get_octree_solid_test_state() {
 get_octree_solid_test_state() {
   if (_octree_solid_test_state == (RenderState *)NULL) {
   if (_octree_solid_test_state == (RenderState *)NULL) {
     _octree_solid_test_state = RenderState::make
     _octree_solid_test_state = RenderState::make
@@ -553,14 +780,14 @@ get_octree_solid_test_state() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::multi_assign
+//     Function: CullBinOcclusionTest::OctreeNode::multi_assign
 //       Access: Public
 //       Access: Public
 //  Description: The object intersects a center plane, but is too
 //  Description: The object intersects a center plane, but is too
 //               small to justify keeping within this big node.
 //               small to justify keeping within this big node.
 //               Duplicate it into the sub-nodes.
 //               Duplicate it into the sub-nodes.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void CullBinHierarchicalZBuffer::OctreeNode::
-multi_assign(const CullBinHierarchicalZBuffer::ObjectData &object_data) {
+void CullBinOcclusionTest::OctreeNode::
+multi_assign(const CullBinOcclusionTest::ObjectData &object_data) {
   const LPoint3f &c = object_data._bounds->get_center();
   const LPoint3f &c = object_data._bounds->get_center();
   float r = object_data._bounds->get_radius();
   float r = object_data._bounds->get_radius();
 
 
@@ -722,11 +949,11 @@ multi_assign(const CullBinHierarchicalZBuffer::ObjectData &object_data) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CullBinHierarchicalZBuffer::OctreeNode::make_corner
+//     Function: CullBinOcclusionTest::OctreeNode::make_corner
 //       Access: Private
 //       Access: Private
 //  Description: Makes a new octree node for the indicated corner.
 //  Description: Makes a new octree node for the indicated corner.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE void CullBinHierarchicalZBuffer::OctreeNode::
+void CullBinOcclusionTest::OctreeNode::
 make_corner(int index) {
 make_corner(int index) {
   nassertv(_corners[index] == NULL);
   nassertv(_corners[index] == NULL);
 
 
@@ -778,3 +1005,49 @@ make_corner(int index) {
   nassertv(node != (OctreeNode *)NULL);
   nassertv(node != (OctreeNode *)NULL);
   _corners[index] = node;
   _corners[index] = node;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinOcclusionTest::OctreeNode::get_corner_point
+//       Access: Private
+//  Description: Returns the 3-d point, in world space, of the
+//               indicated corner.
+////////////////////////////////////////////////////////////////////
+LPoint3f CullBinOcclusionTest::OctreeNode::
+get_corner_point(int index) {
+  switch (index) {
+  case 0:
+    // -X, -Y, -Z
+    return LPoint3f(_mid[0] - _half_side, _mid[1] - _half_side, _mid[2] - _half_side);
+
+  case OC_x:
+    // +X, -Y, -Z
+    return LPoint3f(_mid[0] + _half_side, _mid[1] - _half_side, _mid[2] - _half_side);
+
+  case OC_y:
+    // -X, +Y, -Z
+    return LPoint3f(_mid[0] - _half_side, _mid[1] + _half_side, _mid[2] - _half_side);
+
+  case OC_x | OC_y:
+    // +X, +Y, -Z
+    return LPoint3f(_mid[0] + _half_side, _mid[1] + _half_side, _mid[2] - _half_side);
+
+  case OC_z:
+    // -X, -Y, +Z
+    return LPoint3f(_mid[0] - _half_side, _mid[1] - _half_side, _mid[2] + _half_side);
+
+  case OC_x | OC_z:
+    // +X, -Y, +Z
+    return LPoint3f(_mid[0] + _half_side, _mid[1] - _half_side, _mid[2] + _half_side);
+
+  case OC_y | OC_z:
+    // -X, +Y, +Z
+    return LPoint3f(_mid[0] - _half_side, _mid[1] + _half_side, _mid[2] + _half_side);
+
+  case OC_x | OC_y | OC_z:
+    // +X, +Y, +Z
+    return LPoint3f(_mid[0] + _half_side, _mid[1] + _half_side, _mid[2] + _half_side);
+  }
+
+  nassertr(false, LPoint3f::zero());
+  return LPoint3f::zero();
+}

+ 70 - 19
panda/src/cull/cullBinHierarchicalZBuffer.h → panda/src/cull/cullBinOcclusionTest.h

@@ -1,4 +1,4 @@
-// Filename: cullBinHierarchicalZBuffer.h
+// Filename: cullBinOcclusionTest.h
 // Created by:  drose (24Mar06)
 // Created by:  drose (24Mar06)
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -16,8 +16,8 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
-#ifndef CULLBINHIERARCHICALZBUFFER_H
-#define CULLBINHIERARCHICALZBUFFER_H
+#ifndef CULLBINOCCLUSIONTEST_H
+#define CULLBINOCCLUSIONTEST_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
 
 
@@ -29,12 +29,13 @@
 #include "boundingSphere.h"
 #include "boundingSphere.h"
 #include "config_cull.h"
 #include "config_cull.h"
 #include "occlusionQueryContext.h"
 #include "occlusionQueryContext.h"
-#include "pdeque.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
+#include "pdeque.h"
+#include "ordered_vector.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//       Class : CullBinHierarchicalZBuffer
-// Description : This cull bin uses a hierarchical Z-buffer algorithm
+//       Class : CullBinOcclusionTest
+// Description : This cull bin uses hardware-supported occlusion tests
 //               to attempt to further eliminate geometry that is
 //               to attempt to further eliminate geometry that is
 //               obscured behind walls, etc.  It imposes some
 //               obscured behind walls, etc.  It imposes some
 //               significant overhead over most of the other kinds of
 //               significant overhead over most of the other kinds of
@@ -46,16 +47,18 @@
 //
 //
 //               This code is still experimental.  Use with caution.
 //               This code is still experimental.  Use with caution.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA CullBinHierarchicalZBuffer : public CullBin {
+class EXPCL_PANDA CullBinOcclusionTest : public CullBin {
+protected:
+  INLINE CullBinOcclusionTest(const CullBinOcclusionTest &copy);
 public:
 public:
-  INLINE CullBinHierarchicalZBuffer(const string &name, GraphicsStateGuardianBase *gsg);
-  virtual ~CullBinHierarchicalZBuffer();
+  INLINE CullBinOcclusionTest(const string &name, GraphicsStateGuardianBase *gsg);
+  virtual ~CullBinOcclusionTest();
 
 
   static CullBin *make_bin(const string &name, GraphicsStateGuardianBase *gsg);
   static CullBin *make_bin(const string &name, GraphicsStateGuardianBase *gsg);
   virtual PT(CullBin) make_next() const;
   virtual PT(CullBin) make_next() const;
 
 
   virtual void add_object(CullableObject *object);
   virtual void add_object(CullableObject *object);
-  virtual void finish_cull();
+  virtual void finish_cull(SceneSetup *scene_setup);
   virtual void draw();
   virtual void draw();
 
 
 private:
 private:
@@ -90,6 +93,23 @@ private:
     OC_z   = 0x04,
     OC_z   = 0x04,
   };
   };
 
 
+  static const LPoint3f _corner_points[8];
+
+  // This class is used to build a table of Geoms (and their
+  // associated net transform) that we have determined to be visible
+  // during the draw pass.  This will be useful information for next
+  // frame's draw.
+  class VisibleGeom {
+  public:
+    INLINE VisibleGeom(const Geom *geom, const TransformState *net_transform);
+    INLINE bool operator < (const VisibleGeom &other) const;
+
+    CPT(Geom) _geom;
+    CPT(TransformState) _net_transform;
+    CPT(BoundingVolume) _bounds;
+  };
+  typedef ov_set<VisibleGeom> VisibleGeoms;
+
   class OctreeNode {
   class OctreeNode {
   public:
   public:
     OctreeNode();
     OctreeNode();
@@ -98,28 +118,38 @@ private:
 
 
     void make_initial_bounds();
     void make_initial_bounds();
     void group_objects();
     void group_objects();
-    PT(OcclusionQueryContext) occlusion_test(CullBinHierarchicalZBuffer &bin);
-    void draw(CullBinHierarchicalZBuffer &bin);
-    void draw_wireframe(CullBinHierarchicalZBuffer &bin);
-
+    void compute_distance(const LMatrix4f &world_mat,
+                          CullBinOcclusionTest &bin);
+
+    PT(OcclusionQueryContext) occlusion_test(CullBinOcclusionTest &bin);
+    int draw_previous(CullBinOcclusionTest &bin);
+    int draw(CullBinOcclusionTest &bin);
+    void draw_wireframe(CullBinOcclusionTest &bin);
+    void record_visible_geoms(VisibleGeoms &visible_geoms);
     INLINE void initial_assign(const ObjectData &object_data);
     INLINE void initial_assign(const ObjectData &object_data);
 
 
-    INLINE int get_total_num_objects() const;
+    void output(ostream &out) const;
 
 
   private:
   private:
     INLINE void reassign(const ObjectData &object_data);
     INLINE void reassign(const ObjectData &object_data);
     INLINE void assign_to_corner(int index, const ObjectData &object_data);
     INLINE void assign_to_corner(int index, const ObjectData &object_data);
     void multi_assign(const ObjectData &object_data);
     void multi_assign(const ObjectData &object_data);
     void make_corner(int index);
     void make_corner(int index);
+    LPoint3f get_corner_point(int index);
 
 
-    int _total_num_objects;
+  private:
     Objects _objects;
     Objects _objects;
     OctreeNode *_corners[8];
     OctreeNode *_corners[8];
     LPoint3f _mid;
     LPoint3f _mid;
     float _half_side;
     float _half_side;
+    float _distance;
+    bool _is_visible;
   };
   };
 
 
   OctreeNode _root;
   OctreeNode _root;
+  int _num_objects;
+  int _corners_front_to_back[8];
+  float _near_distance;
 
 
   // During draw(), we maintain a list of OctreeNodes that have been
   // During draw(), we maintain a list of OctreeNodes that have been
   // tested and have yet to pass the occlusion query and be drawn (or
   // tested and have yet to pass the occlusion query and be drawn (or
@@ -132,9 +162,21 @@ private:
   typedef pdeque<PendingNode> PendingNodes;
   typedef pdeque<PendingNode> PendingNodes;
   PendingNodes _pending_nodes;
   PendingNodes _pending_nodes;
 
 
+  // The pointer to this class is preserved from one frame to the next
+  // (unlike the CullBin itself, which is recreated anew each frame).
+  // It keeps the data from the previous draw operation.
+  class PrevDrawData : public ReferenceCount {
+  public:
+    VisibleGeoms _visible_geoms;
+    Mutex _visible_lock;
+  };
+  PT(PrevDrawData) _prev_draw;
+
   PStatCollector _draw_occlusion_pcollector;
   PStatCollector _draw_occlusion_pcollector;
   static PStatCollector _wait_occlusion_pcollector;
   static PStatCollector _wait_occlusion_pcollector;
-  static PStatCollector _geoms_occluded_pcollector;
+  static PStatCollector _occlusion_previous_pcollector;
+  static PStatCollector _occlusion_passed_pcollector;
+  static PStatCollector _occlusion_failed_pcollector;
 
 
   static PT(Geom) _octree_solid_test;
   static PT(Geom) _octree_solid_test;
   static PT(Geom) _octree_wireframe_viz;
   static PT(Geom) _octree_wireframe_viz;
@@ -146,7 +188,7 @@ public:
   }
   }
   static void init_type() {
   static void init_type() {
     CullBin::init_type();
     CullBin::init_type();
-    register_type(_type_handle, "CullBinHierarchicalZBuffer",
+    register_type(_type_handle, "CullBinOcclusionTest",
                   CullBin::get_class_type());
                   CullBin::get_class_type());
   }
   }
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
@@ -158,9 +200,18 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 
 
   friend class OctreeNode;
   friend class OctreeNode;
+  friend class GraphicsEngine;
+
+  friend ostream &operator << (ostream &out, OctreeNode &node);
 };
 };
 
 
-#include "cullBinHierarchicalZBuffer.I"
+INLINE ostream &
+operator << (ostream &out, CullBinOcclusionTest::OctreeNode &node) {
+  node.output(out);
+  return out;
+}
+
+#include "cullBinOcclusionTest.I"
 
 
 #endif
 #endif
 
 

+ 11 - 11
panda/src/cull/cullBinStateSorted.I

@@ -17,6 +17,17 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinStateSorted::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullBinStateSorted::
+CullBinStateSorted(const string &name, GraphicsStateGuardianBase *gsg) :
+  CullBin(name, BT_state_sorted, gsg)
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBinStateSorted::ObjectData::Constructor
 //     Function: CullBinStateSorted::ObjectData::Constructor
 //       Access: Public
 //       Access: Public
@@ -62,14 +73,3 @@ operator < (const ObjectData &other) const {
   return sa < sb;
   return sa < sb;
 }
 }
 
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: CullBinStateSorted::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE CullBinStateSorted::
-CullBinStateSorted(const string &name, GraphicsStateGuardianBase *gsg) :
-  CullBin(name, gsg)
-{
-}

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

@@ -72,7 +72,7 @@ add_object(CullableObject *object) {
 //               draw.
 //               draw.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullBinStateSorted::
 void CullBinStateSorted::
-finish_cull() {
+finish_cull(SceneSetup *) {
   PStatTimer timer(_cull_this_pcollector);
   PStatTimer timer(_cull_this_pcollector);
   sort(_objects.begin(), _objects.end());
   sort(_objects.begin(), _objects.end());
 }
 }

+ 1 - 1
panda/src/cull/cullBinStateSorted.h

@@ -48,7 +48,7 @@ public:
   static CullBin *make_bin(const string &name, GraphicsStateGuardianBase *gsg);
   static CullBin *make_bin(const string &name, GraphicsStateGuardianBase *gsg);
 
 
   virtual void add_object(CullableObject *object);
   virtual void add_object(CullableObject *object);
-  virtual void finish_cull();
+  virtual void finish_cull(SceneSetup *scene_setup);
   virtual void draw();
   virtual void draw();
 
 
 private:
 private:

+ 1 - 1
panda/src/cull/cullBinUnsorted.I

@@ -24,6 +24,6 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CullBinUnsorted::
 INLINE CullBinUnsorted::
 CullBinUnsorted(const string &name, GraphicsStateGuardianBase *gsg) :
 CullBinUnsorted(const string &name, GraphicsStateGuardianBase *gsg) :
-  CullBin(name, gsg)
+  CullBin(name, BT_unsorted, gsg)
 {
 {
 }
 }

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

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

+ 6 - 3
panda/src/display/graphicsEngine.cxx

@@ -25,6 +25,7 @@
 #include "binCullHandler.h"
 #include "binCullHandler.h"
 #include "cullResult.h"
 #include "cullResult.h"
 #include "cullTraverser.h"
 #include "cullTraverser.h"
+#include "cullBinOcclusionTest.h"
 #include "clockObject.h"
 #include "clockObject.h"
 #include "pStatTimer.h"
 #include "pStatTimer.h"
 #include "pStatClient.h"
 #include "pStatClient.h"
@@ -47,7 +48,7 @@
 
 
 PStatCollector GraphicsEngine::_wait_pcollector("Wait:Thread sync");
 PStatCollector GraphicsEngine::_wait_pcollector("Wait:Thread sync");
 PStatCollector GraphicsEngine::_cycle_pcollector("App:Cycle");
 PStatCollector GraphicsEngine::_cycle_pcollector("App:Cycle");
-PStatCollector GraphicsEngine::_app_pcollector("App:Show code");
+PStatCollector GraphicsEngine::_app_pcollector("App:Show code:General");
 PStatCollector GraphicsEngine::_render_frame_pcollector("App:render_frame");
 PStatCollector GraphicsEngine::_render_frame_pcollector("App:render_frame");
 PStatCollector GraphicsEngine::_do_frame_pcollector("*:do_frame");
 PStatCollector GraphicsEngine::_do_frame_pcollector("*:do_frame");
 PStatCollector GraphicsEngine::_yield_pcollector("App:Yield");
 PStatCollector GraphicsEngine::_yield_pcollector("App:Yield");
@@ -645,7 +646,9 @@ render_frame() {
   CullTraverser::_nodes_pcollector.clear_level();
   CullTraverser::_nodes_pcollector.clear_level();
   CullTraverser::_geom_nodes_pcollector.clear_level();
   CullTraverser::_geom_nodes_pcollector.clear_level();
   CullTraverser::_geoms_pcollector.clear_level();
   CullTraverser::_geoms_pcollector.clear_level();
-  CullTraverser::_geoms_occluded_pcollector.clear_level();
+  CullBinOcclusionTest::_occlusion_previous_pcollector.clear_level();
+  CullBinOcclusionTest::_occlusion_passed_pcollector.clear_level();
+  CullBinOcclusionTest::_occlusion_failed_pcollector.clear_level();
   GeomCacheManager::_geom_cache_active_pcollector.clear_level();
   GeomCacheManager::_geom_cache_active_pcollector.clear_level();
   GeomCacheManager::_geom_cache_record_pcollector.clear_level();
   GeomCacheManager::_geom_cache_record_pcollector.clear_level();
   GeomCacheManager::_geom_cache_erase_pcollector.clear_level();
   GeomCacheManager::_geom_cache_erase_pcollector.clear_level();
@@ -1070,7 +1073,7 @@ cull_to_bins(GraphicsOutput *win, DisplayRegion *dr) {
 
 
     {
     {
       PStatTimer timer(_cull_sort_pcollector);
       PStatTimer timer(_cull_sort_pcollector);
-      cull_result->finish_cull();
+      cull_result->finish_cull(scene_setup);
       // Save the results for next frame.
       // Save the results for next frame.
       dr->set_cull_result(cull_result, scene_setup);
       dr->set_cull_result(cull_result, scene_setup);
     }
     }

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

@@ -26,6 +26,7 @@
     compassEffect.I compassEffect.h \
     compassEffect.I compassEffect.h \
     config_pgraph.h \
     config_pgraph.h \
     cullBin.I cullBin.h \
     cullBin.I cullBin.h \
+    cullBinEnums.h \
     cullBinAttrib.I cullBinAttrib.h \
     cullBinAttrib.I cullBinAttrib.h \
     cullBinManager.I cullBinManager.h \
     cullBinManager.I cullBinManager.h \
     cullFaceAttrib.I cullFaceAttrib.h \
     cullFaceAttrib.I cullFaceAttrib.h \
@@ -226,6 +227,7 @@
     compassEffect.I compassEffect.h \
     compassEffect.I compassEffect.h \
     config_pgraph.h \
     config_pgraph.h \
     cullBin.I cullBin.h \
     cullBin.I cullBin.h \
+    cullBinEnums.h \
     cullBinAttrib.I cullBinAttrib.h \
     cullBinAttrib.I cullBinAttrib.h \
     cullBinManager.I cullBinManager.h \
     cullBinManager.I cullBinManager.h \
     cullFaceAttrib.I cullFaceAttrib.h \
     cullFaceAttrib.I cullFaceAttrib.h \

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

@@ -214,6 +214,13 @@ ConfigVariableBool show_vertex_animation
           "by Panda on the CPU (flash red) or by hardware (flash blue).  "
           "by Panda on the CPU (flash red) or by hardware (flash blue).  "
           "This only has effect when NDEBUG is defined."));
           "This only has effect when NDEBUG is defined."));
 
 
+ConfigVariableBool show_transparency
+("show-transparency", false,
+ PRC_DESC("Set this true to flash any objects that are rendered in "
+          "some transparency mode.  The color chosen is based on the  "
+          "particular transparency mode in effect.  This only has effect "
+          "when NDEBUG is defined."));
+
 ConfigVariableBool m_dual
 ConfigVariableBool m_dual
 ("m-dual", true,
 ("m-dual", true,
  PRC_DESC("Set this false to disable TransparencyAttrib::M_dual altogether "
  PRC_DESC("Set this false to disable TransparencyAttrib::M_dual altogether "

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

@@ -52,6 +52,7 @@ extern ConfigVariableBool polylight_info;
 extern ConfigVariableDouble lod_fade_time;
 extern ConfigVariableDouble lod_fade_time;
 
 
 extern ConfigVariableBool show_vertex_animation;
 extern ConfigVariableBool show_vertex_animation;
+extern ConfigVariableBool show_transparency;
 
 
 extern ConfigVariableBool m_dual;
 extern ConfigVariableBool m_dual;
 extern ConfigVariableBool m_dual_opaque;
 extern ConfigVariableBool m_dual_opaque;

+ 64 - 1
panda/src/pgraph/cullBin.I

@@ -17,15 +17,78 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullBin::Copy Constructor
+//       Access: Protected
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullBin::
+CullBin(const CullBin &copy) : 
+  _name(copy._name),
+  _bin_type(copy._bin_type),
+  _gsg(copy._gsg),
+  _cull_this_pcollector(copy._cull_this_pcollector),
+  _draw_this_pcollector(copy._draw_this_pcollector)
+{
+  check_flash_color();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBin::Constructor
 //     Function: CullBin::Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CullBin::
 INLINE CullBin::
-CullBin(const string &name, GraphicsStateGuardianBase *gsg) : 
+CullBin(const string &name, CullBin::BinType bin_type,
+        GraphicsStateGuardianBase *gsg) : 
+  _name(name),
+  _bin_type(bin_type),
   _gsg(gsg),
   _gsg(gsg),
   _cull_this_pcollector(_cull_bin_pcollector, name),
   _cull_this_pcollector(_cull_bin_pcollector, name),
   _draw_this_pcollector(_draw_bin_pcollector, name)
   _draw_this_pcollector(_draw_bin_pcollector, name)
 {
 {
+  check_flash_color();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBin::get_name
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const string &CullBin::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBin::get_bin_tyep
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CullBin::BinType CullBin::
+get_bin_type() const {
+  return _bin_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBin::has_flash_color
+//       Access: Public
+//  Description: Returns true if this bin has a flash color configured
+//               via the flash-bin-binname config directive, or false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool CullBin::
+has_flash_color() const {
+  return _has_flash_color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBin::get_flash_color
+//       Access: Public
+//  Description: If has_flash_color returns true, this returns the
+//               color specified.
+////////////////////////////////////////////////////////////////////
+INLINE const Colorf &CullBin::
+get_flash_color() const {
+  return _flash_color;
 }
 }

+ 35 - 1
panda/src/pgraph/cullBin.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "cullBin.h"
 #include "cullBin.h"
+#include "config_pgraph.h"
 
 
 PStatCollector CullBin::_cull_bin_pcollector("Cull:Sort");
 PStatCollector CullBin::_cull_bin_pcollector("Cull:Sort");
 PStatCollector CullBin::_draw_bin_pcollector("Draw:Bins");
 PStatCollector CullBin::_draw_bin_pcollector("Draw:Bins");
@@ -70,7 +71,7 @@ add_object(CullableObject *) {
 //               draw.
 //               draw.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullBin::
 void CullBin::
-finish_cull() {
+finish_cull(SceneSetup *) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -83,3 +84,36 @@ void CullBin::
 draw() {
 draw() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullBin::check_flash_color
+//       Access: Private
+//  Description: Checks the config variables for a user variable of
+//               the name flash-bin-binname.  If found, it defines the
+//               r g b color to flash geometry in this bin.
+////////////////////////////////////////////////////////////////////
+void CullBin::
+check_flash_color() {
+#ifdef NDEBUG
+  _has_flash_color = false;
+#else
+  ConfigVariableDouble flash_bin
+    ("flash-bin-" + _name, "", "", ConfigVariable::F_dynamic);
+  if (flash_bin.get_num_words() == 0) {
+    _has_flash_color = false;
+
+  } else if (flash_bin.get_num_words() == 3) {
+    _has_flash_color = true;
+    _flash_color.set(flash_bin[0], flash_bin[1], flash_bin[2], 1.0f);
+
+  } else if (flash_bin.get_num_words() == 4) {
+    _has_flash_color = true;
+    _flash_color.set(flash_bin[0], flash_bin[1], flash_bin[2], flash_bin[3]);
+
+  } else {
+    _has_flash_color = false;
+    pgraph_cat.warning()
+      << "Invalid value for flash-bin-" << _name << ": " 
+      << flash_bin.get_string_value() << "\n";
+  }
+#endif  // NDEBUG
+}

+ 23 - 4
panda/src/pgraph/cullBin.h

@@ -20,13 +20,15 @@
 #define CULLBIN_H
 #define CULLBIN_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-
+#include "cullBinEnums.h"
 #include "typedReferenceCount.h"
 #include "typedReferenceCount.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
+#include "luse.h"
 
 
 class CullableObject;
 class CullableObject;
 class GraphicsStateGuardianBase;
 class GraphicsStateGuardianBase;
+class SceneSetup;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : CullBin
 //       Class : CullBin
@@ -39,21 +41,38 @@ class GraphicsStateGuardianBase;
 //               CullBinStateSorted and CullBinBackToFront provide the
 //               CullBinStateSorted and CullBinBackToFront provide the
 //               actual implementation.
 //               actual implementation.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA CullBin : public TypedReferenceCount {
+class EXPCL_PANDA CullBin : public TypedReferenceCount, public CullBinEnums {
+protected:
+  INLINE CullBin(const CullBin &copy);
 public:
 public:
-  INLINE CullBin(const string &name, GraphicsStateGuardianBase *gsg);
+  INLINE CullBin(const string &name, BinType bin_type,
+                 GraphicsStateGuardianBase *gsg);
   virtual ~CullBin();
   virtual ~CullBin();
 
 
+  INLINE const string &get_name() const;
+  INLINE BinType get_bin_type() const;
+
   virtual PT(CullBin) make_next() const;
   virtual PT(CullBin) make_next() const;
 
 
   virtual void add_object(CullableObject *object)=0;
   virtual void add_object(CullableObject *object)=0;
-  virtual void finish_cull();
+  virtual void finish_cull(SceneSetup *scene_setup);
 
 
   virtual void draw()=0;
   virtual void draw()=0;
 
 
+  INLINE bool has_flash_color() const;
+  INLINE const Colorf &get_flash_color() const;
+
+private:
+  void check_flash_color();
+
 protected:
 protected:
+  string _name;
+  BinType _bin_type;
   GraphicsStateGuardianBase *_gsg;
   GraphicsStateGuardianBase *_gsg;
 
 
+  bool _has_flash_color;
+  Colorf _flash_color;
+
   static PStatCollector _cull_bin_pcollector;
   static PStatCollector _cull_bin_pcollector;
   static PStatCollector _draw_bin_pcollector;
   static PStatCollector _draw_bin_pcollector;
   PStatCollector _cull_this_pcollector;
   PStatCollector _cull_this_pcollector;

+ 43 - 0
panda/src/pgraph/cullBinEnums.h

@@ -0,0 +1,43 @@
+// Filename: cullBinEnums.h
+// Created by:  drose (03Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CULLBINENUMS_H
+#define CULLBINENUMS_H
+
+#include "pandabase.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CullBinEnums
+// Description : Provides scoping for the enumerated type shared by
+//               CullBin and CullBinManager.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA CullBinEnums {
+PUBLISHED:
+  enum BinType {
+    BT_invalid,
+    BT_unsorted,
+    BT_state_sorted,
+    BT_back_to_front,
+    BT_front_to_back,
+    BT_fixed,
+    BT_occlusion_test,
+  };
+};
+
+#endif
+

+ 149 - 2
panda/src/pgraph/cullBinManager.I

@@ -95,8 +95,7 @@ get_bin_name(int bin_index) const {
 //       Access: Published
 //       Access: Published
 //  Description: Returns the type of the bin with the indicated
 //  Description: Returns the type of the bin with the indicated
 //               bin_index (where bin_index was retrieved by get_bin()
 //               bin_index (where bin_index was retrieved by get_bin()
-//               or find_bin()).  The bin's type may not be changed
-//               during the life of the bin.
+//               or find_bin()).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CullBinManager::BinType CullBinManager::
 INLINE CullBinManager::BinType CullBinManager::
 get_bin_type(int bin_index) const {
 get_bin_type(int bin_index) const {
@@ -105,6 +104,54 @@ get_bin_type(int bin_index) const {
   return _bin_definitions[bin_index]._type;
   return _bin_definitions[bin_index]._type;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinManager::get_bin_type
+//       Access: Published
+//  Description: Returns the type of the bin with the indicated
+//               name.
+////////////////////////////////////////////////////////////////////
+INLINE CullBinManager::BinType CullBinManager::
+get_bin_type(const string &name) const {
+  int bin_index = find_bin(name);
+  nassertr(bin_index != -1, BT_invalid);
+  return get_bin_type(bin_index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinManager::set_bin_type
+//       Access: Published
+//  Description: Changes the type of the bin with the indicated
+//               bin_index (where bin_index was retrieved by get_bin()
+//               or find_bin()).
+//
+//               The change might be effective immediately, or it
+//               might take place next frame, depending on the bin
+//               type.
+////////////////////////////////////////////////////////////////////
+INLINE void CullBinManager::
+set_bin_type(int bin_index, CullBinManager::BinType type) {
+  nassertv(bin_index >= 0 && bin_index < (int)_bin_definitions.size());
+  nassertv(_bin_definitions[bin_index]._in_use);
+  _bin_definitions[bin_index]._type = type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinManager::set_bin_type
+//       Access: Published
+//  Description: Changes the type of the bin with the indicated
+//               name.
+//
+//               The change might be effective immediately, or it
+//               might take place next frame, depending on the bin
+//               type.
+////////////////////////////////////////////////////////////////////
+INLINE void CullBinManager::
+set_bin_type(const string &name, CullBinManager::BinType type) {
+  int bin_index = find_bin(name);
+  nassertv(bin_index != -1);
+  set_bin_type(bin_index, type);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBinManager::get_bin_sort
 //     Function: CullBinManager::get_bin_sort
 //       Access: Published
 //       Access: Published
@@ -123,6 +170,23 @@ get_bin_sort(int bin_index) const {
   return _bin_definitions[bin_index]._sort;
   return _bin_definitions[bin_index]._sort;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinManager::get_bin_sort
+//       Access: Published
+//  Description: Returns the sort order of the bin with the indicated
+//               name.
+//
+//               The bins are rendered in increasing order by their
+//               sort order; this number may be changed from time to
+//               time to reorder the bins.
+////////////////////////////////////////////////////////////////////
+INLINE int CullBinManager::
+get_bin_sort(const string &name) const {
+  int bin_index = find_bin(name);
+  nassertr(bin_index != -1, 0);
+  return get_bin_sort(bin_index);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBinManager::set_bin_sort
 //     Function: CullBinManager::set_bin_sort
 //       Access: Published
 //       Access: Published
@@ -141,3 +205,86 @@ set_bin_sort(int bin_index, int sort) {
   _bin_definitions[bin_index]._sort = sort;
   _bin_definitions[bin_index]._sort = sort;
   _bins_are_sorted = false;
   _bins_are_sorted = false;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinManager::set_bin_sort
+//       Access: Published
+//  Description: Changes the sort order of the bin with the indicated
+//               name.
+//
+//               The bins are rendered in increasing order by their
+//               sort order; this number may be changed from time to
+//               time to reorder the bins.
+////////////////////////////////////////////////////////////////////
+INLINE void CullBinManager::
+set_bin_sort(const string &name, int sort) {
+  int bin_index = find_bin(name);
+  nassertv(bin_index != -1);
+  set_bin_sort(bin_index, sort);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinManager::get_bin_active
+//       Access: Published
+//  Description: Returns the active flag of the bin with the indicated
+//               bin_index (where bin_index was retrieved by get_bin()
+//               or find_bin()).
+//
+//               When a bin is marked inactive, all geometry assigned
+//               to it is not rendered.
+////////////////////////////////////////////////////////////////////
+INLINE bool CullBinManager::
+get_bin_active(int bin_index) const {
+  nassertr(bin_index >= 0 && bin_index < (int)_bin_definitions.size(), false);
+  nassertr(_bin_definitions[bin_index]._in_use, false);
+  return _bin_definitions[bin_index]._active;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinManager::get_bin_active
+//       Access: Published
+//  Description: Returns the active flag of the bin with the indicated
+//               name.
+//
+//               When a bin is marked inactive, all geometry assigned
+//               to it is not rendered.
+////////////////////////////////////////////////////////////////////
+INLINE bool CullBinManager::
+get_bin_active(const string &name) const {
+  int bin_index = find_bin(name);
+  nassertr(bin_index != -1, false);
+  return get_bin_active(bin_index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinManager::set_bin_active
+//       Access: Published
+//  Description: Changes the active flag of the bin with the indicated
+//               bin_index (where bin_index was retrieved by get_bin()
+//               or find_bin()).
+//
+//               When a bin is marked inactive, all geometry assigned
+//               to it is not rendered.
+////////////////////////////////////////////////////////////////////
+INLINE void CullBinManager::
+set_bin_active(int bin_index, bool active) {
+  nassertv(bin_index >= 0 && bin_index < (int)_bin_definitions.size());
+  nassertv(_bin_definitions[bin_index]._in_use);
+  _bin_definitions[bin_index]._active = active;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinManager::set_bin_active
+//       Access: Published
+//  Description: Changes the active flag of the bin with the indicated
+//               name.
+//
+//               When a bin is marked inactive, all geometry assigned
+//               to it is not rendered.
+////////////////////////////////////////////////////////////////////
+INLINE void CullBinManager::
+set_bin_active(const string &name, bool active) {
+  int bin_index = find_bin(name);
+  nassertv(bin_index != -1);
+  set_bin_active(bin_index, active);
+}

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

@@ -17,13 +17,6 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "cullBinManager.h"
 #include "cullBinManager.h"
-/*
-#include "cullBinBackToFront.h"
-#include "cullBinFrontToBack.h"
-#include "cullBinFixed.h"
-#include "cullBinStateSorted.h"
-#include "cullBinUnsorted.h"
-*/
 #include "renderState.h"
 #include "renderState.h"
 #include "cullResult.h"
 #include "cullResult.h"
 #include "config_pgraph.h"
 #include "config_pgraph.h"
@@ -118,6 +111,7 @@ add_bin(const string &name, BinType type, int sort) {
   def._name = name;
   def._name = name;
   def._type = type;
   def._type = type;
   def._sort = sort;
   def._sort = sort;
+  def._active = true;
 
 
   _bins_by_name.insert(BinsByName::value_type(name, new_bin_index));
   _bins_by_name.insert(BinsByName::value_type(name, new_bin_index));
   _sorted_bins.push_back(new_bin_index);
   _sorted_bins.push_back(new_bin_index);
@@ -347,8 +341,8 @@ parse_bin_type(const string &bin_type) {
   } 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, "hierarchical_z") == 0) {
-    return BT_hierarchical_z;
+  } else if (cmp_nocase_uh(bin_type, "occlusion_test") == 0) {
+    return BT_occlusion_test;
 
 
   } else {
   } else {
     return BT_invalid;
     return BT_invalid;
@@ -380,8 +374,8 @@ 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";
+  case CullBinManager::BT_occlusion_test:
+    return out << "occlusion_test";
   }
   }
 
 
   return out << "**invalid BinType(" << (int)bin_type << ")**";
   return out << "**invalid BinType(" << (int)bin_type << ")**";

+ 15 - 10
panda/src/pgraph/cullBinManager.h

@@ -21,6 +21,7 @@
 
 
 #include "pandabase.h"
 #include "pandabase.h"
 #include "cullBin.h"
 #include "cullBin.h"
+#include "cullBinEnums.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "pvector.h"
 #include "pvector.h"
 #include "pmap.h"
 #include "pmap.h"
@@ -34,21 +35,13 @@ class GraphicsStateGuardianBase;
 // Description : This is a global object that maintains the collection
 // Description : This is a global object that maintains the collection
 //               of named CullBins in the world.
 //               of named CullBins in the world.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA CullBinManager {
+class EXPCL_PANDA CullBinManager : public CullBinEnums {
 protected:
 protected:
   CullBinManager();
   CullBinManager();
   ~CullBinManager();
   ~CullBinManager();
 
 
 PUBLISHED:
 PUBLISHED:
-  enum BinType {
-    BT_invalid,
-    BT_unsorted,
-    BT_state_sorted,
-    BT_back_to_front,
-    BT_front_to_back,
-    BT_fixed,
-    BT_hierarchical_z,
-  };
+  typedef CullBin::BinType BinType;
 
 
   int add_bin(const string &name, BinType type, int sort);
   int add_bin(const string &name, BinType type, int sort);
   void remove_bin(int bin_index);
   void remove_bin(int bin_index);
@@ -58,10 +51,21 @@ PUBLISHED:
   int find_bin(const string &name) const;
   int find_bin(const string &name) const;
 
 
   INLINE string get_bin_name(int bin_index) const;
   INLINE string get_bin_name(int bin_index) const;
+
   INLINE BinType get_bin_type(int bin_index) const;
   INLINE BinType get_bin_type(int bin_index) const;
+  INLINE BinType get_bin_type(const string &name) const;
+  INLINE void set_bin_type(int bin_index, BinType type);
+  INLINE void set_bin_type(const string &name, BinType type);
 
 
   INLINE int get_bin_sort(int bin_index) const;
   INLINE int get_bin_sort(int bin_index) const;
+  INLINE int get_bin_sort(const string &name) const;
   INLINE void set_bin_sort(int bin_index, int sort);
   INLINE void set_bin_sort(int bin_index, int sort);
+  INLINE void set_bin_sort(const string &name, int sort);
+
+  INLINE bool get_bin_active(int bin_index) const;
+  INLINE bool get_bin_active(const string &name) const;
+  INLINE void set_bin_active(int bin_index, bool active);
+  INLINE void set_bin_active(const string &name, bool active);
 
 
   void write(ostream &out) const;
   void write(ostream &out) const;
 
 
@@ -89,6 +93,7 @@ private:
     string _name;
     string _name;
     BinType _type;
     BinType _type;
     int _sort;
     int _sort;
+    bool _active;
   };
   };
   typedef pvector<BinDefinition> BinDefinitions;
   typedef pvector<BinDefinition> BinDefinitions;
   BinDefinitions _bin_definitions;
   BinDefinitions _bin_definitions;

+ 148 - 37
panda/src/pgraph/cullResult.cxx

@@ -22,6 +22,7 @@
 #include "alphaTestAttrib.h"
 #include "alphaTestAttrib.h"
 #include "depthWriteAttrib.h"
 #include "depthWriteAttrib.h"
 #include "colorScaleAttrib.h"
 #include "colorScaleAttrib.h"
+#include "fogAttrib.h"
 #include "transparencyAttrib.h"
 #include "transparencyAttrib.h"
 #include "renderState.h"
 #include "renderState.h"
 #include "clockObject.h"
 #include "clockObject.h"
@@ -51,6 +52,10 @@
 // whatever internal representation the graphics API will use.
 // whatever internal representation the graphics API will use.
 static const float dual_opaque_level = 252.0f / 256.0f;
 static const float dual_opaque_level = 252.0f / 256.0f;
 
 
+#ifndef NDEBUG
+static const double bin_color_flash_rate = 1.0;  // 1 state change per second
+#endif
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullResult::make_next
 //     Function: CullResult::make_next
 //       Access: Public
 //       Access: Public
@@ -64,9 +69,12 @@ make_next() const {
   PT(CullResult) new_result = new CullResult(_gsg);
   PT(CullResult) new_result = new CullResult(_gsg);
   new_result->_bins.reserve(_bins.size());
   new_result->_bins.reserve(_bins.size());
 
 
-  for (Bins::const_iterator bi = _bins.begin(); bi != _bins.end(); ++bi) {
-    CullBin *old_bin = (*bi);
-    if (old_bin == (CullBin *)NULL) {
+  CullBinManager *bin_manager = CullBinManager::get_global_ptr();
+
+  for (size_t i = 0; i < _bins.size(); ++i) {
+    CullBin *old_bin = _bins[i];
+    if (old_bin == (CullBin *)NULL || 
+        old_bin->get_bin_type() != bin_manager->get_bin_type(i)) {
       new_result->_bins.push_back((CullBin *)NULL);
       new_result->_bins.push_back((CullBin *)NULL);
     } else {
     } else {
       new_result->_bins.push_back(old_bin->make_next());
       new_result->_bins.push_back(old_bin->make_next());
@@ -85,6 +93,11 @@ make_next() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullResult::
 void CullResult::
 add_object(CullableObject *object, const CullTraverser *traverser) {
 add_object(CullableObject *object, const CullTraverser *traverser) {
+  static const Colorf flash_alpha_color(0.92, 0.96, 0.10, 1.0f);
+  static const Colorf flash_binary_color(0.21f, 0.67f, 0.24f, 1.0f);
+  static const Colorf flash_multisample_color(0.78f, 0.05f, 0.81f, 1.0f);
+  static const Colorf flash_dual_color(0.92f, 0.01f, 0.01f, 1.0f);
+
   // Check to see if there's a special transparency setting.
   // Check to see if there's a special transparency setting.
   const RenderState *state = object->_state;
   const RenderState *state = object->_state;
   nassertv(state != (const RenderState *)NULL);
   nassertv(state != (const RenderState *)NULL);
@@ -92,9 +105,21 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
   const TransparencyAttrib *trans = state->get_transparency();
   const TransparencyAttrib *trans = state->get_transparency();
   if (trans != (const TransparencyAttrib *)NULL) {
   if (trans != (const TransparencyAttrib *)NULL) {
     switch (trans->get_mode()) {
     switch (trans->get_mode()) {
+    case TransparencyAttrib::M_alpha:
+      // M_alpha implies an alpha-write test, so we don't waste time
+      // writing 0-valued pixels.
+      object->_state = state->compose(get_alpha_state());
+#ifndef NDEBUG
+      check_flash_transparency(object->_state, flash_alpha_color);
+#endif
+      break;
+
     case TransparencyAttrib::M_binary:
     case TransparencyAttrib::M_binary:
       // M_binary is implemented by explicitly setting the alpha test.
       // M_binary is implemented by explicitly setting the alpha test.
       object->_state = state->compose(get_binary_state());
       object->_state = state->compose(get_binary_state());
+#ifndef NDEBUG
+      check_flash_transparency(object->_state, flash_binary_color);
+#endif
       break;
       break;
 
 
     case TransparencyAttrib::M_multisample:
     case TransparencyAttrib::M_multisample:
@@ -104,16 +129,27 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
       if (!_gsg->get_supports_multisample()) {
       if (!_gsg->get_supports_multisample()) {
         object->_state = state->compose(get_binary_state());
         object->_state = state->compose(get_binary_state());
       }
       }
+#ifndef NDEBUG
+      check_flash_transparency(object->_state, flash_multisample_color);
+#endif
       break;
       break;
 
 
     case TransparencyAttrib::M_dual:
     case TransparencyAttrib::M_dual:
+#ifndef NDEBUG
+      check_flash_transparency(object->_state, flash_dual_color);
+#endif
       if (m_dual) {
       if (m_dual) {
-        // M_dual is implemented by drawing the opaque parts first,
-        // without transparency, then drawing the transparent parts
-        // later.  This means we must copy the object and add it to
-        // both bins.  We can only do this if we do not have an
-        // explicit bin already applied; otherwise, M_dual falls back
-        // to M_alpha.
+        // If m_dual is configured off, it becomes M_alpha.
+        break;
+      }
+
+      // M_dual is implemented by drawing the opaque parts first,
+      // without transparency, then drawing the transparent parts
+      // later.  This means we must copy the object and add it to
+      // both bins.  We can only do this if we do not have an
+      // explicit bin already applied; otherwise, M_dual falls back
+      // to M_alpha. 
+      {
         const CullBinAttrib *bin_attrib = state->get_bin();
         const CullBinAttrib *bin_attrib = state->get_bin();
         if (bin_attrib == (CullBinAttrib *)NULL || 
         if (bin_attrib == (CullBinAttrib *)NULL || 
             bin_attrib->get_bin_name().empty()) {
             bin_attrib->get_bin_name().empty()) {
@@ -122,20 +158,23 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
 #ifndef NDEBUG
 #ifndef NDEBUG
           if (m_dual_transparent) 
           if (m_dual_transparent) 
 #endif
 #endif
-          {
-            CullableObject *transparent_part = new CullableObject(*object);
-            CPT(RenderState) transparent_state = object->has_decals() ? 
-              get_dual_transparent_state_decals() : 
-              get_dual_transparent_state();
-            transparent_part->_state = state->compose(transparent_state);
-            transparent_part->munge_geom
-              (_gsg, _gsg->get_geom_munger(transparent_part->_state),
-               traverser);
-            CullBin *bin = get_bin(transparent_part->_state->get_bin_index());
-            nassertv(bin != (CullBin *)NULL);
-            bin->add_object(transparent_part);
-          }
-
+            {
+              CullableObject *transparent_part = new CullableObject(*object);
+              CPT(RenderState) transparent_state = object->has_decals() ? 
+                get_dual_transparent_state_decals() : 
+                get_dual_transparent_state();
+              transparent_part->_state = state->compose(transparent_state);
+              transparent_part->munge_geom
+                (_gsg, _gsg->get_geom_munger(transparent_part->_state),
+                 traverser);
+              CullBin *bin = get_bin(transparent_part->_state->get_bin_index());
+              nassertv(bin != (CullBin *)NULL);
+#ifndef NDEBUG
+              check_flash_bin(transparent_part->_state, bin);
+#endif
+              bin->add_object(transparent_part);
+            }
+          
           // Now we can draw the opaque part, with decals.  This will
           // Now we can draw the opaque part, with decals.  This will
           // end up in the opaque bin.
           // end up in the opaque bin.
           object->_state = state->compose(get_dual_opaque_state());
           object->_state = state->compose(get_dual_opaque_state());
@@ -146,21 +185,28 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
           }
           }
 #endif
 #endif
         }
         }
+        // The object is assigned to a specific bin; M_dual becomes
+        // M_alpha.
       }
       }
       break;
       break;
-
+      
     default:
     default:
       // Other kinds of transparency need no special handling.
       // Other kinds of transparency need no special handling.
       break;
       break;
     }
     }
   }
   }
 
 
+  CullBin *bin = get_bin(object->_state->get_bin_index());
+  nassertv(bin != (CullBin *)NULL);
+
+#ifndef NDEBUG
+  check_flash_bin(object->_state, bin);
+#endif
+
   // Munge vertices as needed for the GSG's requirements, and the
   // Munge vertices as needed for the GSG's requirements, and the
   // object's current state.
   // object's current state.
   object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state), traverser);
   object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state), traverser);
   
   
-  CullBin *bin = get_bin(object->_state->get_bin_index());
-  nassertv(bin != (CullBin *)NULL);
   bin->add_object(object);
   bin->add_object(object);
 }
 }
 
 
@@ -174,11 +220,20 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
 //               draw.
 //               draw.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CullResult::
 void CullResult::
-finish_cull() {
-  for (Bins::iterator bi = _bins.begin(); bi != _bins.end(); ++bi) {
-    CullBin *bin = (*bi);
-    if (bin != (CullBin *)NULL) {
-      bin->finish_cull();
+finish_cull(SceneSetup *scene_setup) {
+  CullBinManager *bin_manager = CullBinManager::get_global_ptr();
+
+  for (size_t i = 0; i < _bins.size(); ++i) {
+    if (!bin_manager->get_bin_active(i)) {
+      // If the bin isn't active, don't sort it, and don't draw it.
+      // In fact, clear it.
+      _bins[i] = NULL;
+
+    } else {
+      CullBin *bin = _bins[i];
+      if (bin != (CullBin *)NULL) {
+        bin->finish_cull(scene_setup);
+      }
     }
     }
   }
   }
 }
 }
@@ -240,6 +295,23 @@ make_new_bin(int bin_index) {
   return bin;
   return bin;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullResult::get_alpha_state
+//       Access: Private
+//  Description: Returns a RenderState that changes the alpha test to
+//               > 0, for implementing M_alpha.
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) CullResult::
+get_alpha_state() {
+  static CPT(RenderState) state = NULL;
+  if (state == (const RenderState *)NULL) {
+    // We don't monkey with the priority, since we want to allow the
+    // user to override this if he desires.
+    state = RenderState::make(AlphaTestAttrib::make(AlphaTestAttrib::M_greater, 0.0f));
+  }
+  return state;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullResult::get_binary_state
 //     Function: CullResult::get_binary_state
 //       Access: Private
 //       Access: Private
@@ -257,9 +329,47 @@ get_binary_state() {
   return state;
   return state;
 }
 }
 
 
-#ifndef NDEBUG
-static const double m_dual_flash_rate = 1.0;  // 1 state change per second
-#endif
+////////////////////////////////////////////////////////////////////
+//     Function: CullResult::check_flash_bin
+//       Access: Private
+//  Description: If the user configured flash-bin-binname, then update
+//               the object's state to flash all the geometry in the
+//               bin.
+////////////////////////////////////////////////////////////////////
+void CullResult::
+check_flash_bin(CPT(RenderState) &state, CullBin *bin) {
+  if (bin->has_flash_color()) {
+    int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * bin_color_flash_rate);
+    if ((cycle & 1) == 0) {
+      state = state->remove_attrib(TextureAttrib::get_class_type());
+      state = state->remove_attrib(LightAttrib::get_class_type());
+      state = state->remove_attrib(ColorScaleAttrib::get_class_type());
+      state = state->remove_attrib(FogAttrib::get_class_type());
+      state = state->add_attrib(ColorAttrib::make_flat(bin->get_flash_color()));
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullResult::check_flash_transparency
+//       Access: Private
+//  Description: If the user configured show-transparency, then
+//               update the object's state to flash the current
+//               geometry with the specified color.
+////////////////////////////////////////////////////////////////////
+void CullResult::
+check_flash_transparency(CPT(RenderState) &state, const Colorf &transparency) {
+  if (show_transparency) {
+    int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * bin_color_flash_rate);
+    if ((cycle & 1) == 0) {
+      state = state->remove_attrib(TextureAttrib::get_class_type());
+      state = state->remove_attrib(LightAttrib::get_class_type());
+      state = state->remove_attrib(ColorScaleAttrib::get_class_type());
+      state = state->remove_attrib(FogAttrib::get_class_type());
+      state = state->add_attrib(ColorAttrib::make_flat(transparency));
+    }
+  }
+}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullResult::get_dual_transparent_state
 //     Function: CullResult::get_dual_transparent_state
@@ -289,7 +399,7 @@ get_dual_transparent_state() {
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
   if (m_dual_flash) {
   if (m_dual_flash) {
-    int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * m_dual_flash_rate);
+    int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * bin_color_flash_rate);
     if ((cycle & 1) == 0) {
     if ((cycle & 1) == 0) {
       static CPT(RenderState) flash_state = NULL;
       static CPT(RenderState) flash_state = NULL;
       if (flash_state == (const RenderState *)NULL) {
       if (flash_state == (const RenderState *)NULL) {
@@ -327,7 +437,7 @@ get_dual_transparent_state_decals() {
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
   if (m_dual_flash) {
   if (m_dual_flash) {
-    int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * m_dual_flash_rate);
+    int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * bin_color_flash_rate);
     if ((cycle & 1) == 0) {
     if ((cycle & 1) == 0) {
       static CPT(RenderState) flash_state = NULL;
       static CPT(RenderState) flash_state = NULL;
       if (flash_state == (const RenderState *)NULL) {
       if (flash_state == (const RenderState *)NULL) {
@@ -358,7 +468,7 @@ get_dual_opaque_state() {
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
   if (m_dual_flash) {
   if (m_dual_flash) {
-    int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * m_dual_flash_rate);
+    int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * bin_color_flash_rate);
     if ((cycle & 1) == 0) {
     if ((cycle & 1) == 0) {
       static CPT(RenderState) flash_state = NULL;
       static CPT(RenderState) flash_state = NULL;
       if (flash_state == (const RenderState *)NULL) {
       if (flash_state == (const RenderState *)NULL) {
@@ -371,3 +481,4 @@ get_dual_opaque_state() {
 
 
   return state;
   return state;
 }
 }
+

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

@@ -36,6 +36,7 @@ class GraphicsStateGuardianBase;
 class CullTraverser;
 class CullTraverser;
 class TransformState;
 class TransformState;
 class RenderState;
 class RenderState;
+class SceneSetup;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : CullResult
 //       Class : CullResult
@@ -58,7 +59,7 @@ public:
   INLINE CullBin *get_bin(int bin_index);
   INLINE CullBin *get_bin(int bin_index);
 
 
   void add_object(CullableObject *object, const CullTraverser *traverser);
   void add_object(CullableObject *object, const CullTraverser *traverser);
-  void finish_cull();
+  void finish_cull(SceneSetup *scene_setup);
   void draw();
   void draw();
 
 
 public:
 public:
@@ -66,7 +67,10 @@ public:
 
 
 private:
 private:
   CullBin *make_new_bin(int bin_index);
   CullBin *make_new_bin(int bin_index);
+  void check_flash_bin(CPT(RenderState) &state, CullBin *bin);
+  void check_flash_transparency(CPT(RenderState) &state, const Colorf &color);
 
 
+  static CPT(RenderState) get_alpha_state();
   static CPT(RenderState) get_binary_state();
   static CPT(RenderState) get_binary_state();
   static CPT(RenderState) get_dual_transparent_state();
   static CPT(RenderState) get_dual_transparent_state();
   static CPT(RenderState) get_dual_transparent_state_decals();
   static CPT(RenderState) get_dual_transparent_state_decals();

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

@@ -80,7 +80,7 @@ public:
   CullableObject *_next;
   CullableObject *_next;
 
 
   // This flag is only used by certain CullBin types.  In particular,
   // This flag is only used by certain CullBin types.  In particular,
-  // it is used by CullBinHierarchicalZBuffer.
+  // it is used by CullBinOcclusionTest.
   bool _already_drawn;
   bool _already_drawn;
 
 
 private:
 private:

+ 2 - 2
panda/src/pstatclient/pStatProperties.cxx

@@ -110,7 +110,6 @@ static TimeCollectorProperties time_properties[] = {
   { 1, "Wait",                             { 0.6, 0.6, 0.6 } },
   { 1, "Wait",                             { 0.6, 0.6, 0.6 } },
   { 0, "Wait:Mutex block",                 { 0.5, 0.0, 1.0 } },
   { 0, "Wait:Mutex block",                 { 0.5, 0.0, 1.0 } },
   { 1, "Wait:Thread sync",                 { 0.0, 1.0, 0.5 } },
   { 1, "Wait:Thread sync",                 { 0.0, 1.0, 0.5 } },
-  { 1, "Wait:Occlusion test",              { 1.0, 0.5, 0.0 } },
   { 1, "App",                              { 0.0, 0.8, 0.4 },  1.0 / 30.0 },
   { 1, "App",                              { 0.0, 0.8, 0.4 },  1.0 / 30.0 },
   { 1, "App:Collisions",                   { 1.0, 0.5, 0.0 } },
   { 1, "App:Collisions",                   { 1.0, 0.5, 0.0 } },
   { 1, "App:Collisions:Reset",             { 0.0, 0.0, 0.5 } },
   { 1, "App:Collisions:Reset",             { 0.0, 0.0, 0.5 } },
@@ -152,6 +151,7 @@ static TimeCollectorProperties time_properties[] = {
   { 0, "Draw:Transfer data:Texture",       { 0.9, 0.0, 0.1 } },
   { 0, "Draw:Transfer data:Texture",       { 0.9, 0.0, 0.1 } },
   { 0, "Draw:Transfer data:Display lists", { 0.5, 0.0, 0.9 } },
   { 0, "Draw:Transfer data:Display lists", { 0.5, 0.0, 0.9 } },
   { 0, "Draw:Primitive",                   { 0.0, 0.0, 0.5 } },
   { 0, "Draw:Primitive",                   { 0.0, 0.0, 0.5 } },
+  { 1, "Draw:Wait occlusion",              { 1.0, 0.5, 0.0 } },
   { 0, NULL }
   { 0, NULL }
 };
 };
 
 
@@ -184,13 +184,13 @@ static LevelCollectorProperties level_properties[] = {
   { 1, "Nodes",                            { 0.4, 0.2, 0.8 },  "", 500.0 },
   { 1, "Nodes",                            { 0.4, 0.2, 0.8 },  "", 500.0 },
   { 1, "Nodes:GeomNodes",                  { 0.8, 0.2, 0.0 } },
   { 1, "Nodes:GeomNodes",                  { 0.8, 0.2, 0.0 } },
   { 1, "Geoms",                            { 0.4, 0.8, 0.3 },  "", 500.0 },
   { 1, "Geoms",                            { 0.4, 0.8, 0.3 },  "", 500.0 },
-  { 1, "Geoms:Occluded",                   { 0.8, 0.2, 0.2 } },
   { 1, "Cull volumes",                     { 0.7, 0.6, 0.9 },  "", 500.0 },
   { 1, "Cull volumes",                     { 0.7, 0.6, 0.9 },  "", 500.0 },
   { 1, "Cull volumes:Transforms",          { 0.9, 0.6, 0.0 } },
   { 1, "Cull volumes:Transforms",          { 0.9, 0.6, 0.0 } },
   { 1, "State changes",                    { 1.0, 0.5, 0.2 },  "", 500.0 },
   { 1, "State changes",                    { 1.0, 0.5, 0.2 },  "", 500.0 },
   { 1, "State changes:Other",              { 0.2, 0.2, 0.2 } },
   { 1, "State changes:Other",              { 0.2, 0.2, 0.2 } },
   { 1, "State changes:Transforms",         { 0.2, 0.2, 0.8 } },
   { 1, "State changes:Transforms",         { 0.2, 0.2, 0.8 } },
   { 1, "State changes:Textures",           { 0.8, 0.2, 0.2 } },
   { 1, "State changes:Textures",           { 0.8, 0.2, 0.2 } },
+  { 1, "Occlusion test",                   { 0.9, 0.8, 0.3 },  "", 500.0 },
   { 1, "Memory usage",                     { 0.5, 1.0, 0.5 },  "MB", 64, 1048576 },
   { 1, "Memory usage",                     { 0.5, 1.0, 0.5 },  "MB", 64, 1048576 },
   { 1, "Memory usage:C++",                 { 0.2, 0.2, 1.0 } },
   { 1, "Memory usage:C++",                 { 0.2, 0.2, 1.0 } },
   { 1, "Memory usage:Interpreter",         { 0.8, 0.2, 0.5 } },
   { 1, "Memory usage:Interpreter",         { 0.8, 0.2, 0.5 } },