Browse Source

replacing CullBinOcclusionTest with PipeOcclusionCullTraverser, some linmath optimizations

David Rose 18 years ago
parent
commit
33a0c3af8e
85 changed files with 2175 additions and 2267 deletions
  1. 8 5
      panda/src/collide/collisionNode.cxx
  2. 2 1
      panda/src/collide/collisionNode.h
  3. 31 6
      panda/src/collide/collisionPolygon.I
  4. 7 2
      panda/src/collide/collisionPolygon.h
  5. 0 1
      panda/src/collide/collisionVisualizer.cxx
  6. 0 3
      panda/src/cull/Sources.pp
  7. 0 22
      panda/src/cull/config_cull.cxx
  8. 0 4
      panda/src/cull/config_cull.h
  9. 0 162
      panda/src/cull/cullBinOcclusionTest.I
  10. 0 1057
      panda/src/cull/cullBinOcclusionTest.cxx
  11. 0 224
      panda/src/cull/cullBinOcclusionTest.h
  12. 0 1
      panda/src/cull/cull_composite2.cxx
  13. 0 8
      panda/src/display/config_display.cxx
  14. 0 1
      panda/src/display/config_display.h
  15. 13 0
      panda/src/display/displayRegion.I
  16. 14 0
      panda/src/display/displayRegion.cxx
  17. 8 0
      panda/src/display/displayRegion.h
  18. 26 14
      panda/src/display/graphicsEngine.cxx
  19. 5 0
      panda/src/display/graphicsEngine.h
  20. 31 37
      panda/src/display/graphicsStateGuardian.cxx
  21. 4 4
      panda/src/display/graphicsStateGuardian.h
  22. 3 2
      panda/src/event/eventHandler.I
  23. 12 10
      panda/src/event/eventHandler.cxx
  24. 5 5
      panda/src/event/eventHandler.h
  25. 23 23
      panda/src/framework/pandaFramework.cxx
  26. 23 23
      panda/src/framework/pandaFramework.h
  27. 5 5
      panda/src/framework/windowFramework.cxx
  28. 5 5
      panda/src/framework/windowFramework.h
  29. 62 8
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  30. 3 0
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  31. 25 1
      panda/src/glstuff/glOcclusionQueryContext_src.cxx
  32. 5 2
      panda/src/gobj/geom.I
  33. 41 15
      panda/src/gobj/geom.cxx
  34. 4 2
      panda/src/gobj/geom.h
  35. 2 0
      panda/src/grutil/Sources.pp
  36. 2 0
      panda/src/grutil/config_grutil.cxx
  37. 76 0
      panda/src/grutil/pipeOcclusionCullTraverser.I
  38. 560 0
      panda/src/grutil/pipeOcclusionCullTraverser.cxx
  39. 155 0
      panda/src/grutil/pipeOcclusionCullTraverser.h
  40. 1 1
      panda/src/lerp/lerp.cxx
  41. 1 1
      panda/src/lerp/lerp.h
  42. 40 29
      panda/src/linmath/compose_matrix_src.cxx
  43. 109 145
      panda/src/linmath/lmatrix3_src.I
  44. 156 40
      panda/src/linmath/lmatrix3_src.cxx
  45. 32 2
      panda/src/linmath/lmatrix3_src.h
  46. 80 134
      panda/src/linmath/lmatrix4_src.I
  47. 132 0
      panda/src/linmath/lmatrix4_src.cxx
  48. 23 6
      panda/src/linmath/lmatrix4_src.h
  49. 12 6
      panda/src/parametrics/ropeNode.cxx
  50. 2 1
      panda/src/parametrics/ropeNode.h
  51. 12 6
      panda/src/parametrics/sheetNode.cxx
  52. 2 1
      panda/src/parametrics/sheetNode.h
  53. 8 0
      panda/src/pgraph/config_pgraph.cxx
  54. 1 0
      panda/src/pgraph/config_pgraph.h
  55. 0 1
      panda/src/pgraph/cullBinEnums.h
  56. 0 6
      panda/src/pgraph/cullBinManager.cxx
  57. 6 87
      panda/src/pgraph/cullTraverser.I
  58. 88 32
      panda/src/pgraph/cullTraverser.cxx
  59. 20 16
      panda/src/pgraph/cullTraverser.h
  60. 0 5
      panda/src/pgraph/cullTraverserData.I
  61. 0 8
      panda/src/pgraph/cullTraverserData.cxx
  62. 0 2
      panda/src/pgraph/cullTraverserData.h
  63. 4 0
      panda/src/pgraph/cullableObject.I
  64. 0 4
      panda/src/pgraph/cullableObject.h
  65. 13 6
      panda/src/pgraph/geomNode.cxx
  66. 2 1
      panda/src/pgraph/geomNode.h
  67. 7 5
      panda/src/pgraph/lodNode.cxx
  68. 2 1
      panda/src/pgraph/lodNode.h
  69. 62 2
      panda/src/pgraph/pandaNode.I
  70. 72 15
      panda/src/pgraph/pandaNode.cxx
  71. 34 9
      panda/src/pgraph/pandaNode.h
  72. 6 3
      panda/src/pgraph/planeNode.cxx
  73. 2 1
      panda/src/pgraph/planeNode.h
  74. 8 7
      panda/src/pgraph/portalNode.cxx
  75. 2 1
      panda/src/pgraph/portalNode.h
  76. 32 4
      panda/src/pgraph/transformState.cxx
  77. 5 0
      panda/src/pgraph/transformState.h
  78. 13 7
      panda/src/pgui/pgItem.cxx
  79. 2 1
      panda/src/pgui/pgItem.h
  80. 2 0
      panda/src/pgui/pgTop.cxx
  81. 5 5
      panda/src/testbed/pview.cxx
  82. 2 4
      panda/src/text/textAssembler.cxx
  83. 11 6
      panda/src/text/textNode.cxx
  84. 2 1
      panda/src/text/textNode.h
  85. 2 2
      panda/src/tform/driveInterface.cxx

+ 8 - 5
panda/src/collide/collisionNode.cxx

@@ -31,6 +31,7 @@
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "clockObject.h"
+#include "boundingSphere.h"
 
 TypeHandle CollisionNode::_type_handle;
 
@@ -305,11 +306,11 @@ set_from_collide_mask(CollideMask mask) {
 //               of substance should redefine this to do the right
 //               thing.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) CollisionNode::
-compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
+void CollisionNode::
+compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+                        Thread *current_thread) const {
   // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage, current_thread);
-  nassertr(bound != (BoundingVolume *)NULL, bound);
+  PT(BoundingVolume) bound = new BoundingSphere;
 
   // Now actually compute the bounding volume by putting it around all
   // of our solids' bounding volumes.
@@ -342,7 +343,9 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
   }
 #endif
 
-  return bound;
+  bdata->_internal_bounds = bound;
+  bdata->_internal_vertices = 0;
+  bdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/collide/collisionNode.h

@@ -75,7 +75,8 @@ PUBLISHED:
   INLINE static CollideMask get_default_collide_mask();
 
 protected:
-  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+                                       Thread *current_thread) const;
 
 private:
   CPT(RenderState) get_last_pos_state();

+ 31 - 6
panda/src/collide/collisionPolygon.I

@@ -19,7 +19,7 @@
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPolygon::Constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE CollisionPolygon::
@@ -34,7 +34,7 @@ CollisionPolygon(const LVecBase3f &a, const LVecBase3f &b,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPolygon::Constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE CollisionPolygon::
@@ -51,7 +51,7 @@ CollisionPolygon(const LVecBase3f &a, const LVecBase3f &b,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPolygon::Constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE CollisionPolygon::
@@ -69,9 +69,34 @@ INLINE CollisionPolygon::
 CollisionPolygon() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionPolygon::get_num_points
+//       Access: Published
+//  Description: Returns the number of vertices of the
+//               CollisionPolygon.
+////////////////////////////////////////////////////////////////////
+INLINE int CollisionPolygon::
+get_num_points() const {
+  return _points.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionPolygon::get_point
+//       Access: Published
+//  Description: Returns the nth vertex of the CollisionPolygon,
+//               expressed in 3-D space.
+////////////////////////////////////////////////////////////////////
+INLINE LPoint3f CollisionPolygon::
+get_point(int n) const {
+  nassertr(n >= 0 && n < (int)_points.size(), LPoint3f::zero());
+  LMatrix4f to_3d_mat;
+  rederive_to_3d_mat(to_3d_mat);
+  return to_3d(_points[n]._p, to_3d_mat);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPolygon::verify_points
-//       Access: Public, Static
+//       Access: Published, Static
 //  Description: Verifies that the indicated set of points will define
 //               a valid CollisionPolygon: that is, at least three
 //               non-collinear points, with no points repeated.
@@ -88,7 +113,7 @@ verify_points(const LPoint3f &a, const LPoint3f &b,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPolygon::verify_points
-//       Access: Public, Static
+//       Access: Published, Static
 //  Description: Verifies that the indicated set of points will define
 //               a valid CollisionPolygon: that is, at least three
 //               non-collinear points, with no points repeated.
@@ -139,7 +164,7 @@ INLINE void CollisionPolygon::
 calc_to_3d_mat(LMatrix4f &to_3d_mat) const {
   // We have to be explicit about the coordinate system--we
   // specifically mean CS_zup_right, because that points the forward
-  // vector down the Y axis and moves the coords inthe (X, 0, Z).  We
+  // vector down the Y axis and moves the coords in (X, 0, Z).  We
   // want this effect regardless of the user's coordinate system of
   // choice.
 

+ 7 - 2
panda/src/collide/collisionPolygon.h

@@ -41,8 +41,6 @@ PUBLISHED:
                           const LVecBase3f &c, const LVecBase3f &d);
   INLINE CollisionPolygon(const LPoint3f *begin, const LPoint3f *end);
 
-  virtual LPoint3f get_collision_origin() const;
-
 private:
   INLINE CollisionPolygon();
 
@@ -51,6 +49,12 @@ public:
 
   virtual CollisionSolid *make_copy();
 
+PUBLISHED:
+  virtual LPoint3f get_collision_origin() const;
+
+  INLINE int get_num_points() const;
+  INLINE LPoint3f get_point(int n) const;
+
   INLINE static bool verify_points(const LPoint3f &a, const LPoint3f &b,
                                    const LPoint3f &c);
   INLINE static bool verify_points(const LPoint3f &a, const LPoint3f &b,
@@ -60,6 +64,7 @@ public:
   bool is_valid() const;
   bool is_concave() const;
 
+public:
   virtual void xform(const LMatrix4f &mat);
 
   virtual PT(PandaNode) get_viz(const CullTraverser *trav,

+ 0 - 1
panda/src/collide/collisionVisualizer.cxx

@@ -133,7 +133,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
     // transform.
     xform_data._net_transform = TransformState::make_identity();
     xform_data._view_frustum = trav->get_view_frustum();
-    xform_data._guard_band = trav->get_guard_band();
     xform_data.apply_transform_and_state(trav, net_transform, 
                                          RenderState::make_empty(),
                                          RenderEffects::make_empty(),

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

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

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

@@ -21,7 +21,6 @@
 #include "cullBinBackToFront.h"
 #include "cullBinFixed.h"
 #include "cullBinFrontToBack.h"
-#include "cullBinOcclusionTest.h"
 #include "cullBinStateSorted.h"
 #include "cullBinUnsorted.h"
 
@@ -35,24 +34,6 @@ ConfigureFn(config_cull) {
   init_libcull();
 }
 
-ConfigVariableInt max_objects_per_octree_node
-("max-objects-per-octree-node", 10,
- PRC_DESC("Specifies the maximum number of objects collected per octree "
-          "node, by the occlusion test cull bin algorithm."));
-
-ConfigVariableDouble octree_multiassign_ratio
-("octree-multiassign-ratio", 0.1,
- PRC_DESC("Objects that intersect a bisecting plane of an octree node must "
-          "be at least this fraction of the node's linear dimension in "
-          "order to be assigned to the node itself.  If the object is smaller "
-          "than this, it will be multiply assigned to the node's children."));
-
-ConfigVariableBool show_octree
-("show-octree", false,
- PRC_DESC("When true, visualizes the octree created for the occlusion "
-          "test algorithm, by drawing wireframe cubes around each "
-          "nonempty octree cell."));
-
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libcull
 //  Description: Initializes the library.  This must be called at
@@ -72,7 +53,6 @@ init_libcull() {
   CullBinBackToFront::init_type();
   CullBinFixed::init_type();
   CullBinFrontToBack::init_type();
-  CullBinOcclusionTest::init_type();
   CullBinStateSorted::init_type();
   CullBinUnsorted::init_type();
 
@@ -87,6 +67,4 @@ init_libcull() {
                                  CullBinFrontToBack::make_bin);
   bin_manager->register_bin_type(CullBinManager::BT_fixed,
                                  CullBinFixed::make_bin);
-  bin_manager->register_bin_type(CullBinManager::BT_occlusion_test,
-                                 CullBinOcclusionTest::make_bin);
 }

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

@@ -31,10 +31,6 @@ class DSearchPath;
 ConfigureDecl(config_cull, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(cull, EXPCL_PANDA, EXPTP_PANDA);
 
-extern ConfigVariableInt max_objects_per_octree_node;
-extern ConfigVariableDouble octree_multiassign_ratio;
-extern ConfigVariableBool show_octree;
-
 extern EXPCL_PANDA void init_libcull();
 
 #endif

+ 0 - 162
panda/src/cull/cullBinOcclusionTest.I

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

+ 0 - 1057
panda/src/cull/cullBinOcclusionTest.cxx

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

+ 0 - 224
panda/src/cull/cullBinOcclusionTest.h

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

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

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

+ 0 - 8
panda/src/display/config_display.cxx

@@ -170,14 +170,6 @@ ConfigVariableString red_blue_stereo_colors
           "be a two-word string, where each word is one of 'red', 'blue', "
           "'green', or 'alpha'."));
 
-ConfigVariableBool depth_offset_decals
-("depth-offset-decals", false,
- PRC_DESC("Set this true to allow decals to be implemented via the advanced "
-          "depth offset feature, if supported, instead of via the traditional "
-          "(and slower) two-pass approach.  This is false by default "
-          "because it appears that many graphics drivers have issues with "
-          "their depth offset implementation."));
-
 ConfigVariableBool auto_generate_mipmaps
 ("auto-generate-mipmaps", false,
  PRC_DESC("Set this true to use the hardware to generate mipmaps "

+ 0 - 1
panda/src/display/config_display.h

@@ -58,7 +58,6 @@ extern EXPCL_PANDA ConfigVariableBool copy_texture_inverted;
 extern EXPCL_PANDA ConfigVariableBool window_inverted;
 extern EXPCL_PANDA ConfigVariableBool red_blue_stereo;
 extern EXPCL_PANDA ConfigVariableString red_blue_stereo_colors;
-extern EXPCL_PANDA ConfigVariableBool depth_offset_decals;
 extern EXPCL_PANDA ConfigVariableBool auto_generate_mipmaps;
 extern EXPCL_PANDA ConfigVariableBool color_scale_via_lighting;
 extern EXPCL_PANDA ConfigVariableBool alpha_scale_via_texture;

+ 13 - 0
panda/src/display/displayRegion.I

@@ -190,6 +190,19 @@ get_clear_depth_between_eyes() const {
   return _clear_depth_between_eyes;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::set_cull_traverser
+//       Access: Published
+//  Description: Specifies the CullTraverser that will be used to draw
+//               the contents of this DisplayRegion.  Normally the
+//               default CullTraverser is sufficient, but this may be
+//               changed to change the default cull behavior.
+////////////////////////////////////////////////////////////////////
+INLINE void DisplayRegion::
+set_cull_traverser(CullTraverser *trav) {
+  _trav = trav;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DisplayRegion::set_cube_map_index
 //       Access: Published

+ 14 - 0
panda/src/display/displayRegion.cxx

@@ -268,6 +268,20 @@ set_stereo_channel(Lens::StereoChannel stereo_channel) {
   cdata->_stereo_channel = stereo_channel;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::get_cull_traverser
+//       Access: Published
+//  Description: Returns the CullTraverser that will be used to draw
+//               the contents of this DisplayRegion.
+////////////////////////////////////////////////////////////////////
+CullTraverser *DisplayRegion::
+get_cull_traverser() {
+  if (_trav == (CullTraverser *)NULL) {
+    _trav = new CullTraverser;
+  }
+  return _trav;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DisplayRegion::compute_pixels
 //       Access: Published

+ 8 - 0
panda/src/display/displayRegion.h

@@ -38,12 +38,14 @@
 #include "deletedChain.h"
 #include "plist.h"
 #include "pStatCollector.h"
+#include "cullTraverser.h"
 
 class GraphicsOutput;
 class GraphicsPipe;
 class CullHandler;
 class Camera;
 class PNMImage;
+class CullTraverser;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : DisplayRegion
@@ -97,6 +99,9 @@ PUBLISHED:
   INLINE void set_clear_depth_between_eyes(bool clear_depth_between_eyes);
   INLINE bool get_clear_depth_between_eyes() const;
 
+  INLINE void set_cull_traverser(CullTraverser *trav);
+  CullTraverser *get_cull_traverser();
+
   INLINE void set_cube_map_index(int cube_map_index);
   INLINE int get_cube_map_index() const;
 
@@ -141,6 +146,9 @@ private:
   GraphicsOutput *_window;
   bool _clear_depth_between_eyes;
 
+  // Ditto for the cull traverser.
+  PT(CullTraverser) _trav;
+
   // This is the data that is associated with the DisplayRegion that
   // needs to be cycled every frame, but represents the parameters as
   // specified by the user, and which probably will not change that

+ 26 - 14
panda/src/display/graphicsEngine.cxx

@@ -25,7 +25,6 @@
 #include "binCullHandler.h"
 #include "cullResult.h"
 #include "cullTraverser.h"
-#include "cullBinOcclusionTest.h"
 #include "clockObject.h"
 #include "pStatTimer.h"
 #include "pStatClient.h"
@@ -46,6 +45,7 @@
 #include "geomVertexArrayData.h"
 #include "vertexDataSaveFile.h"
 #include "vertexDataBook.h"
+#include "config_pgraph.h"
 
 #if defined(WIN32)
   #define WINDOWS_LEAN_AND_MEAN
@@ -107,6 +107,10 @@ PStatCollector GraphicsEngine::_volume_inv_sphere_pcollector("Collision Volumes:
 PStatCollector GraphicsEngine::_test_inv_sphere_pcollector("Collision Tests:CollisionInvSphere");
 PStatCollector GraphicsEngine::_volume_geom_pcollector("Collision Volumes:CollisionGeom");
 PStatCollector GraphicsEngine::_test_geom_pcollector("Collision Tests:CollisionGeom");
+PStatCollector GraphicsEngine::_occlusion_untested_pcollector("Occlusion results:Not tested");
+PStatCollector GraphicsEngine::_occlusion_passed_pcollector("Occlusion results:Visible");
+PStatCollector GraphicsEngine::_occlusion_failed_pcollector("Occlusion results:Occluded");
+PStatCollector GraphicsEngine::_occlusion_tests_pcollector("Occlusion tests");
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::Constructor
@@ -710,14 +714,13 @@ render_frame() {
   CullTraverser::_nodes_pcollector.clear_level();
   CullTraverser::_geom_nodes_pcollector.clear_level();
   CullTraverser::_geoms_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_record_pcollector.clear_level();
   GeomCacheManager::_geom_cache_erase_pcollector.clear_level();
   GeomCacheManager::_geom_cache_evict_pcollector.clear_level();
   
+  GraphicsStateGuardian::init_frame_pstats();
+
   _transform_states_pcollector.set_level(TransformState::get_num_states());
   _render_states_pcollector.set_level(RenderState::get_num_states());
   if (pstats_unused_states) {
@@ -745,6 +748,10 @@ render_frame() {
   _test_inv_sphere_pcollector.clear_level();
   _volume_geom_pcollector.clear_level();
   _test_geom_pcollector.clear_level();
+  _occlusion_untested_pcollector.clear_level();
+  _occlusion_passed_pcollector.clear_level();
+  _occlusion_failed_pcollector.clear_level();
+  _occlusion_tests_pcollector.clear_level();
 
   if (PStatClient::is_connected()) {
     size_t small_buf = GeomVertexArrayData::get_small_lru()->get_total_size();
@@ -1177,14 +1184,17 @@ cull_to_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) {
   PT(SceneSetup) scene_setup;
   {
     PStatTimer timer(_cull_setup_pcollector, current_thread);
+    DisplayRegionPipelineReader dr_reader(dr, current_thread);
+    scene_setup = setup_scene(gsg, &dr_reader);
     cull_result = dr->get_cull_result(current_thread);
+
     if (cull_result != (CullResult *)NULL) {
       cull_result = cull_result->make_next();
+
     } else {
+      // This DisplayRegion has no cull results; draw it.
       cull_result = new CullResult(gsg, dr->get_draw_region_pcollector());
     }
-    DisplayRegionPipelineReader dr_reader(dr, current_thread);
-    scene_setup = setup_scene(gsg, &dr_reader);
   }
 
   if (scene_setup != (SceneSetup *)NULL) {
@@ -1531,13 +1541,14 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
 void GraphicsEngine::
 do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
         GraphicsStateGuardian *gsg, Thread *current_thread) {
-  PStatTimer timer(scene_setup->get_display_region()->get_cull_region_pcollector(), current_thread);
+  DisplayRegion *dr = scene_setup->get_display_region();
+  PStatTimer timer(dr->get_cull_region_pcollector(), current_thread);
 
-  CullTraverser trav(gsg, current_thread);
-  trav.set_cull_handler(cull_handler);
-  trav.set_depth_offset_decals(depth_offset_decals && gsg->depth_offset_decals());
-  trav.set_scene(scene_setup);
-  
+  CullTraverser *trav = dr->get_cull_traverser();
+  trav->set_cull_handler(cull_handler);
+  trav->set_scene(scene_setup, gsg);
+
+  trav->set_view_frustum(NULL);
   if (view_frustum_cull) {
     // If we're to be performing view-frustum culling, determine the
     // bounding volume associated with the current viewing frustum.
@@ -1557,11 +1568,12 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
         scene_setup->get_cull_center().get_transform(scene_parent, current_thread);
       local_frustum->xform(cull_center_transform->get_mat());
 
-      trav.set_view_frustum(local_frustum);
+      trav->set_view_frustum(local_frustum);
     }
   }
 
-  trav.traverse(scene_setup->get_scene_root(), get_portal_cull());
+  trav->traverse(scene_setup->get_scene_root());
+  trav->end_traverse();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 5 - 0
panda/src/display/graphicsEngine.h

@@ -386,6 +386,11 @@ private:
   static PStatCollector _volume_geom_pcollector;
   static PStatCollector _test_geom_pcollector;
 
+  static PStatCollector _occlusion_untested_pcollector;
+  static PStatCollector _occlusion_passed_pcollector;
+  static PStatCollector _occlusion_failed_pcollector;
+  static PStatCollector _occlusion_tests_pcollector;
+
   friend class WindowRenderer;
   friend class GraphicsOutput;
 };

+ 31 - 37
panda/src/display/graphicsStateGuardian.cxx

@@ -1076,12 +1076,6 @@ bool GraphicsStateGuardian::
 begin_frame(Thread *current_thread) {
   _prepared_objects->begin_frame(this, current_thread);
 
-#ifdef DO_PSTATS
-  // For Pstats to track our current texture memory usage, we have to
-  // reset the set of current textures each frame.
-  init_frame_pstats();
-#endif
-
   // We should reset the state to the default at the beginning of
   // every frame.  Although this will incur additional overhead,
   // particularly in a simple scene, it helps ensure that states that
@@ -1816,6 +1810,37 @@ void GraphicsStateGuardian::
 bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
 }
 
+#ifdef DO_PSTATS
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::init_frame_pstats
+//       Access: Pubilc, Static
+//  Description: Initializes the relevant PStats data at the beginning
+//               of the frame.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+init_frame_pstats() {
+  if (PStatClient::is_connected()) {
+    _data_transferred_pcollector.clear_level();
+    _vertex_buffer_switch_pcollector.clear_level();
+    _index_buffer_switch_pcollector.clear_level();
+
+    _primitive_batches_pcollector.clear_level();
+    _primitive_batches_tristrip_pcollector.clear_level();
+    _primitive_batches_trifan_pcollector.clear_level();
+    _primitive_batches_tri_pcollector.clear_level();
+    _primitive_batches_other_pcollector.clear_level();
+    _vertices_tristrip_pcollector.clear_level();
+    _vertices_trifan_pcollector.clear_level();
+    _vertices_tri_pcollector.clear_level();
+    _vertices_other_pcollector.clear_level();
+
+    _state_pcollector.clear_level();
+    _transform_state_pcollector.clear_level();
+    _texture_state_pcollector.clear_level();
+  }
+}
+#endif  // DO_PSTATS
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::enable_lighting
 //       Access: Protected, Virtual
@@ -2070,37 +2095,6 @@ determine_light_color_scale() {
   }
 }
 
-#ifdef DO_PSTATS
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::init_frame_pstats
-//       Access: Protected
-//  Description: Initializes the relevant PStats data at the beginning
-//               of the frame.
-////////////////////////////////////////////////////////////////////
-void GraphicsStateGuardian::
-init_frame_pstats() {
-  if (PStatClient::is_connected()) {
-    _data_transferred_pcollector.clear_level();
-    _vertex_buffer_switch_pcollector.clear_level();
-    _index_buffer_switch_pcollector.clear_level();
-
-    _primitive_batches_pcollector.clear_level();
-    _primitive_batches_tristrip_pcollector.clear_level();
-    _primitive_batches_trifan_pcollector.clear_level();
-    _primitive_batches_tri_pcollector.clear_level();
-    _primitive_batches_other_pcollector.clear_level();
-    _vertices_tristrip_pcollector.clear_level();
-    _vertices_trifan_pcollector.clear_level();
-    _vertices_tri_pcollector.clear_level();
-    _vertices_other_pcollector.clear_level();
-
-    _state_pcollector.clear_level();
-    _transform_state_pcollector.clear_level();
-    _texture_state_pcollector.clear_level();
-  }
-}
-#endif  // DO_PSTATS
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_unlit_state
 //       Access: Protected, Static

+ 4 - 4
panda/src/display/graphicsStateGuardian.h

@@ -255,6 +255,10 @@ public:
   INLINE void set_stencil_clear_value(unsigned int stencil_clear_value);
   INLINE unsigned int get_stencil_clear_value();
 
+#ifdef DO_PSTATS
+  static void init_frame_pstats();
+#endif
+
 protected:
   INLINE NodePath get_light(int light_id) const;
   virtual void enable_lighting(bool enable);
@@ -278,10 +282,6 @@ protected:
 
   void determine_light_color_scale();
 
-#ifdef DO_PSTATS
-  void init_frame_pstats();
-#endif
-
   static CPT(RenderState) get_unlit_state();
   static CPT(RenderState) get_unclipped_state();
   static CPT(RenderState) get_untextured_state();

+ 3 - 2
panda/src/event/eventHandler.I

@@ -26,9 +26,10 @@
 ////////////////////////////////////////////////////////////////////
 INLINE EventHandler *EventHandler::
 get_global_event_handler(EventQueue *queue) {
+  // The event queue parameter is present for now, for backward
+  // compatibility, but it is ignored.
   if (_global_event_handler == 0) {
-    assert(queue);
-    make_global_event_handler(queue);
+    make_global_event_handler();
   }
   return _global_event_handler;
 }

+ 12 - 10
panda/src/event/eventHandler.cxx

@@ -56,8 +56,9 @@ process_events() {
 //               event.
 ////////////////////////////////////////////////////////////////////
 void EventHandler::
-dispatch_event(const CPT_Event &event) {
-  nassertv(!event.is_null());
+dispatch_event(const Event *event) {
+  nassertv(event != (Event *)NULL);
+
   // Is the event name defined in the hook table?  It will be if
   // anyone has ever assigned a hook to this particular event name.
   Hooks::const_iterator hi;
@@ -71,9 +72,10 @@ dispatch_event(const CPT_Event &event) {
     Functions::const_iterator fi;
     for (fi = copy_functions.begin(); fi != copy_functions.end(); ++fi) {
       if (event_cat.is_spam()) {
-        event_cat->spam() << "calling callback 0x" << (void*)(*fi)
-              << " for event '" << event->get_name() << "'"
-              << endl;
+        event_cat->spam()
+          << "calling callback 0x" << (void*)(*fi)
+          << " for event '" << event->get_name() << "'"
+          << endl;
       }
       (*fi)(event);
     }
@@ -148,8 +150,9 @@ write(ostream &out) const {
 bool EventHandler::
 add_hook(const string &event_name, EventFunction *function) {
   if (event_cat.is_debug()) {
-    event_cat.debug() << "adding hook for event '" << event_name
-               << "' with function 0x" << (void*)function << endl;
+    event_cat.debug()
+      << "adding hook for event '" << event_name
+      << "' with function 0x" << (void*)function << endl;
   }
   assert(!event_name.empty());
   assert(function);
@@ -313,9 +316,8 @@ remove_all_hooks() {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void EventHandler::
-make_global_event_handler(EventQueue *queue) {
-  assert(queue);
-  _global_event_handler = new EventHandler(queue);
+make_global_event_handler() {
+  _global_event_handler = new EventHandler(EventQueue::get_global_event_queue());
 }
 
 

+ 5 - 5
panda/src/event/eventHandler.h

@@ -45,19 +45,19 @@ class EventQueue;
 class EXPCL_PANDA EventHandler : public TypedObject {
 public:
   // Define a function type suitable for receiving events.
-  typedef void EventFunction(CPT_Event);
-  typedef void EventCallbackFunction(CPT_Event, void *);
+  typedef void EventFunction(const Event *);
+  typedef void EventCallbackFunction(const Event *, void *);
 
 PUBLISHED:
   EventHandler(EventQueue *queue);
 
   void process_events();
 
-  virtual void dispatch_event(const CPT_Event &event);
+  virtual void dispatch_event(const Event *);
 
   void write(ostream &out) const;
 
-  INLINE static EventHandler *get_global_event_handler(EventQueue *queue);
+  INLINE static EventHandler *get_global_event_handler(EventQueue *queue = NULL);
 
 public:
   bool add_hook(const string &event_name, EventFunction *function);
@@ -86,7 +86,7 @@ protected:
   EventQueue &_queue;
 
   static EventHandler *_global_event_handler;
-  static void make_global_event_handler(EventQueue *queue);
+  static void make_global_event_handler();
 
 private:
   void write_hook(ostream &out, const Hooks::value_type &hook) const;

+ 23 - 23
panda/src/framework/pandaFramework.cxx

@@ -40,7 +40,7 @@ LoaderOptions PandaFramework::_loader_options;
 ////////////////////////////////////////////////////////////////////
 PandaFramework::
 PandaFramework() :
-  _event_handler(EventQueue::get_global_event_queue())
+  _event_handler(*EventHandler::get_global_event_handler())
 {
   _is_open = false;
   _made_default_pipe = false;
@@ -888,7 +888,7 @@ clear_text() {
 //               window).
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_esc(CPT_Event event, void *data) { 
+event_esc(const Event *event, void *data) { 
   if (event->get_num_parameters() == 1) {
     EventParameter param = event->get_parameter(0);
     WindowFramework *wf;
@@ -926,7 +926,7 @@ event_esc(CPT_Event event, void *data) {
 //               rate.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_f(CPT_Event, void *data) {
+event_f(const Event *, void *data) {
   PandaFramework *self = (PandaFramework *)data;
   self->report_frame_rate(nout);
   self->reset_frame_rate();
@@ -938,7 +938,7 @@ event_f(CPT_Event, void *data) {
 //  Description: Default handler for w key: toggle wireframe.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_w(CPT_Event event, void *) {
+event_w(const Event *event, void *) {
   if (event->get_num_parameters() == 1) {
     EventParameter param = event->get_parameter(0);
     WindowFramework *wf;
@@ -954,7 +954,7 @@ event_w(CPT_Event event, void *) {
 //  Description: Default handler for t key: toggle texture.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_t(CPT_Event event, void *) {
+event_t(const Event *event, void *) {
   if (event->get_num_parameters() == 1) {
     EventParameter param = event->get_parameter(0);
     WindowFramework *wf;
@@ -971,7 +971,7 @@ event_t(CPT_Event event, void *) {
 //               rendering).
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_b(CPT_Event event, void *) {
+event_b(const Event *event, void *) {
   if (event->get_num_parameters() == 1) {
     EventParameter param = event->get_parameter(0);
     WindowFramework *wf;
@@ -987,7 +987,7 @@ event_b(CPT_Event event, void *) {
 //  Description: Default handler for i key: invert one-sided faces.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_i(CPT_Event event, void *) {
+event_i(const Event *event, void *) {
   if (event->get_num_parameters() == 1) {
     EventParameter param = event->get_parameter(0);
     WindowFramework *wf;
@@ -1003,7 +1003,7 @@ event_i(CPT_Event event, void *) {
 //  Description: Default handler for l key: toggle lighting.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_l(CPT_Event event, void *) {
+event_l(const Event *event, void *) {
   if (event->get_num_parameters() == 1) {
     EventParameter param = event->get_parameter(0);
     WindowFramework *wf;
@@ -1020,7 +1020,7 @@ event_l(CPT_Event event, void *) {
 //               the scene, or over the highlighted part of the scene.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_c(CPT_Event event, void *data) {
+event_c(const Event *event, void *data) {
   if (event->get_num_parameters() == 1) {
     EventParameter param = event->get_parameter(0);
     WindowFramework *wf;
@@ -1043,7 +1043,7 @@ event_c(CPT_Event event, void *data) {
 //               controls.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_a(CPT_Event event, void *data) {
+event_a(const Event *event, void *data) {
   if (event->get_num_parameters() == 1) {
     EventParameter param = event->get_parameter(0);
     WindowFramework *wf;
@@ -1060,7 +1060,7 @@ event_a(CPT_Event event, void *data) {
 //               of collision solids.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_C(CPT_Event, void *data) {
+event_C(const Event *, void *data) {
   PandaFramework *self = (PandaFramework *)data;
 
   NodePath node = self->get_highlight();
@@ -1081,7 +1081,7 @@ event_C(CPT_Event, void *data) {
 //               the entire scene.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_B(CPT_Event, void *data) {
+event_B(const Event *, void *data) {
   PandaFramework *self = (PandaFramework *)data;
 
   NodePath node = self->get_highlight();
@@ -1099,7 +1099,7 @@ event_B(CPT_Event, void *data) {
 //               the scene graph, or the highlighted node.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_L(CPT_Event, void *data) {
+event_L(const Event *, void *data) {
   PandaFramework *self = (PandaFramework *)data;
 
   NodePath node = self->get_highlight();
@@ -1118,7 +1118,7 @@ event_L(CPT_Event, void *data) {
 //               arrow keys to highlight different nodes.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_h(CPT_Event, void *data) {
+event_h(const Event *, void *data) {
   PandaFramework *self = (PandaFramework *)data;
   
   if (self->has_highlight()) {
@@ -1135,7 +1135,7 @@ event_h(CPT_Event, void *data) {
 //               move the highlight to the node's parent.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_arrow_up(CPT_Event, void *data) {
+event_arrow_up(const Event *, void *data) {
   PandaFramework *self = (PandaFramework *)data;
 
   if (self->has_highlight()) {
@@ -1153,7 +1153,7 @@ event_arrow_up(CPT_Event, void *data) {
 //               move the highlight to the node's first child.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_arrow_down(CPT_Event, void *data) {
+event_arrow_down(const Event *, void *data) {
   PandaFramework *self = (PandaFramework *)data;
 
   if (self->has_highlight()) {
@@ -1172,7 +1172,7 @@ event_arrow_down(CPT_Event, void *data) {
 //               the left.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_arrow_left(CPT_Event, void *data) {
+event_arrow_left(const Event *, void *data) {
   PandaFramework *self = (PandaFramework *)data;
 
   if (self->has_highlight()) {
@@ -1204,7 +1204,7 @@ event_arrow_left(CPT_Event, void *data) {
 //               the right.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_arrow_right(CPT_Event, void *data) {
+event_arrow_right(const Event *, void *data) {
   PandaFramework *self = (PandaFramework *)data;
 
   if (self->has_highlight()) {
@@ -1235,7 +1235,7 @@ event_arrow_right(CPT_Event, void *data) {
 //  Description: Default handler for shift-S key: activate stats.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_S(CPT_Event, void *) {
+event_S(const Event *, void *) {
 #ifdef DO_PSTATS
   nout << "Connecting to stats host" << endl;
   PStatClient::connect();
@@ -1250,7 +1250,7 @@ event_S(CPT_Event, void *) {
 //  Description: Default handler for f9 key: take screenshot.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_f9(CPT_Event event, void *data) {
+event_f9(const Event *event, void *data) {
   PandaFramework *self = (PandaFramework *)data;
 
   if (event->get_num_parameters() == 1) {
@@ -1292,7 +1292,7 @@ event_f9(CPT_Event event, void *data) {
 //  Description: Default handler for comma key: rotate background color.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_comma(CPT_Event event, void *) {
+event_comma(const Event *event, void *) {
   if (event->get_num_parameters() == 1) {
     EventParameter param = event->get_parameter(0);
     WindowFramework *wf;
@@ -1319,7 +1319,7 @@ event_comma(CPT_Event event, void *) {
 //  Description: Default handler for ? key: show the available keys.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_question(CPT_Event event, void *data) {
+event_question(const Event *event, void *data) {
   PandaFramework *self = (PandaFramework *)data;
   if (event->get_num_parameters() == 1) {
     EventParameter param = event->get_parameter(0);
@@ -1375,7 +1375,7 @@ event_question(CPT_Event event, void *data) {
 //               closed, etc.
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
-event_window_event(CPT_Event event, void *data) {
+event_window_event(const Event *event, void *data) {
   PandaFramework *self = (PandaFramework *)data;
   if (event->get_num_parameters() == 1) {
     // The parameter of the window event is the window itself, rather

+ 23 - 23
panda/src/framework/pandaFramework.h

@@ -124,28 +124,28 @@ protected:
   bool clear_text();
 
 public:
-  static void event_esc(CPT_Event, void *data);
-  static void event_f(CPT_Event, void *data);
-  static void event_w(CPT_Event, void *data);
-  static void event_t(CPT_Event, void *data);
-  static void event_b(CPT_Event, void *data);
-  static void event_i(CPT_Event, void *data);
-  static void event_l(CPT_Event, void *data);
-  static void event_c(CPT_Event, void *data);
-  static void event_a(CPT_Event, void *data);
-  static void event_C(CPT_Event, void *data);
-  static void event_B(CPT_Event, void *data);
-  static void event_L(CPT_Event, void *data);
-  static void event_h(CPT_Event, void *data);
-  static void event_arrow_up(CPT_Event, void *data);
-  static void event_arrow_down(CPT_Event, void *data);
-  static void event_arrow_left(CPT_Event, void *data);
-  static void event_arrow_right(CPT_Event, void *data);
-  static void event_S(CPT_Event, void *data);
-  static void event_f9(CPT_Event, void *data);
-  static void event_comma(CPT_Event, void *data);
-  static void event_question(CPT_Event event, void *data);
-  static void event_window_event(CPT_Event, void *data);
+  static void event_esc(const Event *, void *data);
+  static void event_f(const Event *, void *data);
+  static void event_w(const Event *, void *data);
+  static void event_t(const Event *, void *data);
+  static void event_b(const Event *, void *data);
+  static void event_i(const Event *, void *data);
+  static void event_l(const Event *, void *data);
+  static void event_c(const Event *, void *data);
+  static void event_a(const Event *, void *data);
+  static void event_C(const Event *, void *data);
+  static void event_B(const Event *, void *data);
+  static void event_L(const Event *, void *data);
+  static void event_h(const Event *, void *data);
+  static void event_arrow_up(const Event *, void *data);
+  static void event_arrow_down(const Event *, void *data);
+  static void event_arrow_left(const Event *, void *data);
+  static void event_arrow_right(const Event *, void *data);
+  static void event_S(const Event *, void *data);
+  static void event_f9(const Event *, void *data);
+  static void event_comma(const Event *, void *data);
+  static void event_question(const Event * event, void *data);
+  static void event_window_event(const Event *, void *data);
 
 
 private:
@@ -158,7 +158,7 @@ private:
   GraphicsEngine *_engine;
 
   NodePath _data_root;
-  EventHandler _event_handler;
+  EventHandler &_event_handler;
 
   typedef pvector< PT(WindowFramework) > Windows;
   Windows _windows;

+ 5 - 5
panda/src/framework/windowFramework.cxx

@@ -1392,7 +1392,7 @@ forward_button() {
 //  Description: The static event handler function.
 ////////////////////////////////////////////////////////////////////
 void WindowFramework::
-st_update_anim_controls(CPT_Event, void *data) {
+st_update_anim_controls(const Event *, void *data) {
   WindowFramework *self = (WindowFramework *)data;
   self->update_anim_controls();
 }
@@ -1404,7 +1404,7 @@ st_update_anim_controls(CPT_Event, void *data) {
 //  Description: The static event handler function.
 ////////////////////////////////////////////////////////////////////
 void WindowFramework::
-st_back_button(CPT_Event, void *data) {
+st_back_button(const Event *, void *data) {
   WindowFramework *self = (WindowFramework *)data;
   self->back_button();
 }
@@ -1415,7 +1415,7 @@ st_back_button(CPT_Event, void *data) {
 //  Description: The static event handler function.
 ////////////////////////////////////////////////////////////////////
 void WindowFramework::
-st_pause_button(CPT_Event, void *data) {
+st_pause_button(const Event *, void *data) {
   WindowFramework *self = (WindowFramework *)data;
   self->pause_button();
 }
@@ -1426,7 +1426,7 @@ st_pause_button(CPT_Event, void *data) {
 //  Description: The static event handler function.
 ////////////////////////////////////////////////////////////////////
 void WindowFramework::
-st_play_button(CPT_Event, void *data) {
+st_play_button(const Event *, void *data) {
   WindowFramework *self = (WindowFramework *)data;
   self->play_button();
 }
@@ -1437,7 +1437,7 @@ st_play_button(CPT_Event, void *data) {
 //  Description: The static event handler function.
 ////////////////////////////////////////////////////////////////////
 void WindowFramework::
-st_forward_button(CPT_Event, void *data) {
+st_forward_button(const Event *, void *data) {
   WindowFramework *self = (WindowFramework *)data;
   self->forward_button();
 }

+ 5 - 5
panda/src/framework/windowFramework.h

@@ -148,12 +148,12 @@ private:
   void play_button();
   void forward_button();
 
-  static void st_update_anim_controls(CPT_Event, void *data);
+  static void st_update_anim_controls(const Event *, void *data);
 
-  static void st_back_button(CPT_Event, void *data);
-  static void st_pause_button(CPT_Event, void *data);
-  static void st_play_button(CPT_Event, void *data);
-  static void st_forward_button(CPT_Event, void *data);
+  static void st_back_button(const Event *, void *data);
+  static void st_pause_button(const Event *, void *data);
+  static void st_play_button(const Event *, void *data);
+  static void st_forward_button(const Event *, void *data);
 
 private:
   PandaFramework *_panda_framework;

+ 62 - 8
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -64,6 +64,7 @@ PStatCollector CLP(GraphicsStateGuardian)::_load_display_list_pcollector("Draw:T
 PStatCollector CLP(GraphicsStateGuardian)::_primitive_batches_display_list_pcollector("Primitive batches:Display lists");
 PStatCollector CLP(GraphicsStateGuardian)::_vertices_display_list_pcollector("Vertices:Display lists");
 PStatCollector CLP(GraphicsStateGuardian)::_vertices_immediate_pcollector("Vertices:Immediate mode");
+PStatCollector CLP(GraphicsStateGuardian)::_wait_occlusion_pcollector("Wait:Occlusion");
 
 // The following noop functions are assigned to the corresponding
 // glext function pointers in the class, in case the functions are not
@@ -751,6 +752,8 @@ reset() {
         get_extension_func(GLPREFIX_QUOTED, "EndQuery");
       _glDeleteQueries = (PFNGLDELETEQUERIESPROC)
         get_extension_func(GLPREFIX_QUOTED, "DeleteQueries");
+      _glGetQueryiv = (PFNGLGETQUERYIVPROC)
+        get_extension_func(GLPREFIX_QUOTED, "GetQueryiv");
       _glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)
         get_extension_func(GLPREFIX_QUOTED, "GetQueryObjectuiv");
     } else if (has_extension("GL_ARB_occlusion_query")) {
@@ -763,18 +766,31 @@ reset() {
         get_extension_func(GLPREFIX_QUOTED, "EndQueryARB");
       _glDeleteQueries = (PFNGLDELETEQUERIESPROC)
         get_extension_func(GLPREFIX_QUOTED, "DeleteQueriesARB");
+      _glGetQueryiv = (PFNGLGETQUERYIVPROC)
+        get_extension_func(GLPREFIX_QUOTED, "GetQueryivARB");
       _glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)
         get_extension_func(GLPREFIX_QUOTED, "GetQueryObjectuivARB");
     }
   }
 
-  if (_supports_occlusion_query &&
-      (_glGenQueries == NULL || _glBeginQuery == NULL ||
-       _glEndQuery == NULL || _glDeleteQueries == NULL ||
-       _glGetQueryObjectuiv == NULL)) {
-    GLCAT.warning()
-      << "Occlusion queries advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
-    _supports_occlusion_query = false;
+  if (_supports_occlusion_query) {
+    if (_glGenQueries == NULL || _glBeginQuery == NULL ||
+        _glEndQuery == NULL || _glDeleteQueries == NULL ||
+        _glGetQueryiv == NULL || _glGetQueryObjectuiv == NULL) {
+      GLCAT.warning()
+        << "Occlusion queries advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
+      _supports_occlusion_query = false;
+    } else {
+      GLint num_bits;
+      _glGetQueryiv(GL_SAMPLES_PASSED, GL_QUERY_COUNTER_BITS, &num_bits);
+      if (num_bits == 0) {
+        _supports_occlusion_query = false;
+      }
+      if (GLCAT.is_debug()) {
+        GLCAT.debug()
+          << "Occlusion query counter provides " << num_bits << " bits.\n";
+      }
+    }
   }
 
   _glBlendEquation = NULL;
@@ -3022,9 +3038,16 @@ begin_occlusion_query() {
   PT(CLP(OcclusionQueryContext)) query = new CLP(OcclusionQueryContext)(this);
 
   _glGenQueries(1, &query->_index);
-  _glBeginQuery(GL_SAMPLES_PASSED, query->_index);
 
+  if (GLCAT.is_debug()) {
+    GLCAT.debug()
+      << "beginning occlusion query index " << query->_index << "\n";
+  }
+
+  _glBeginQuery(GL_SAMPLES_PASSED, query->_index);
   _current_occlusion_query = query;
+  
+  report_my_gl_errors();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -3040,9 +3063,40 @@ PT(OcclusionQueryContext) CLP(GraphicsStateGuardian)::
 end_occlusion_query() {
   nassertr(_current_occlusion_query != (OcclusionQueryContext *)NULL, NULL);
   PT(OcclusionQueryContext) result = _current_occlusion_query;
+
+  GLuint index = DCAST(CLP(OcclusionQueryContext), result)->_index;
+    
+  if (GLCAT.is_debug()) {
+    GLCAT.debug()
+      << "ending occlusion query index " << index << "\n";
+  }
+
+#ifndef NDEBUG
+  GLint current_id;
+  _glGetQueryiv(GL_SAMPLES_PASSED, GL_CURRENT_QUERY, &current_id);
+  nassertr(current_id == index, NULL);
+#endif  // NDEBUG
+
   _current_occlusion_query = NULL;
+
   _glEndQuery(GL_SAMPLES_PASSED);
 
+  // Temporary hack to try working around an apparent driver bug on
+  // iMacs.  Occlusion queries sometimes incorrectly report 0 samples,
+  // unless we stall the pipe to keep fewer than a certain maximum
+  // number of queries pending at once.
+  static ConfigVariableInt limit_occlusion_queries("limit-occlusion-queries", 0);
+  if (limit_occlusion_queries > 0) {
+    if (index > (unsigned int)limit_occlusion_queries) {
+      PStatTimer timer(_wait_occlusion_pcollector);
+      GLuint result;
+      _glGetQueryObjectuiv(index - (unsigned int)limit_occlusion_queries, 
+                           GL_QUERY_RESULT, &result);
+    }
+  }
+
+  report_my_gl_errors();
+
   return result;
 }
 

+ 3 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -53,6 +53,7 @@ typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids);
 typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id);
 typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target);
 typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids);
+typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params);
 typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params);
 typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params);
 typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices);
@@ -451,6 +452,7 @@ public:
   PFNGLBEGINQUERYPROC _glBeginQuery;
   PFNGLENDQUERYPROC _glEndQuery;
   PFNGLDELETEQUERIESPROC _glDeleteQueries;
+  PFNGLGETQUERYIVPROC _glGetQueryiv;
   PFNGLGETQUERYOBJECTUIVPROC _glGetQueryObjectuiv;
 
   PFNGLACTIVESTENCILFACEEXTPROC _glActiveStencilFaceEXT;
@@ -471,6 +473,7 @@ public:
   static PStatCollector _primitive_batches_display_list_pcollector;
   static PStatCollector _vertices_display_list_pcollector;
   static PStatCollector _vertices_immediate_pcollector;
+  static PStatCollector _wait_occlusion_pcollector;
 
 public:
   virtual TypeHandle get_type() const {

+ 25 - 1
panda/src/glstuff/glOcclusionQueryContext_src.cxx

@@ -19,6 +19,7 @@
 #include "pnotify.h"
 #include "dcast.h"
 #include "mutexHolder.h"
+#include "pStatTimer.h"
 
 TypeHandle CLP(OcclusionQueryContext)::_type_handle;
 
@@ -55,6 +56,12 @@ is_answer_ready() const {
   DCAST_INTO_R(glgsg, _gsg, false);
   GLuint result;
   glgsg->_glGetQueryObjectuiv(_index, GL_QUERY_RESULT_AVAILABLE, &result);
+
+  if (GLCAT.is_debug()) {
+    GLCAT.debug()
+      << "occlusion query " << _index << " ready = " << result << "\n";
+  }
+
   return (result != 0);
 }
 
@@ -86,7 +93,24 @@ int CLP(OcclusionQueryContext)::
 get_num_fragments() const {
   CLP(GraphicsStateGuardian) *glgsg;
   DCAST_INTO_R(glgsg, _gsg, 0);
+
   GLuint result;
-  glgsg->_glGetQueryObjectuiv(_index, GL_QUERY_RESULT, &result);
+  glgsg->_glGetQueryObjectuiv(_index, GL_QUERY_RESULT_AVAILABLE, &result);
+  if (result) {
+    // The answer is ready now.
+    glgsg->_glGetQueryObjectuiv(_index, GL_QUERY_RESULT, &result);
+  } else {
+    // The answer is not ready; this call will block.
+    PStatTimer timer(glgsg->_wait_occlusion_pcollector);
+    glgsg->_glGetQueryObjectuiv(_index, GL_QUERY_RESULT, &result);
+  }
+
+  if (GLCAT.is_debug()) {
+    GLCAT.debug()
+      << "occlusion query " << _index << " reports " << result
+      << " fragments.\n";
+  }
+
+  glgsg->report_my_gl_errors();
   return result;
 }

+ 5 - 2
panda/src/gobj/geom.I

@@ -240,7 +240,6 @@ set_bounds(const BoundingVolume *volume) {
   } else {
     cdata->_user_bounds = volume->make_copy();
   }
-  mark_internal_bounds_stale(cdata);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -283,8 +282,10 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
                   const GeomVertexData *vertex_data,
                   bool got_mat, const LMatrix4f &mat,
                   Thread *current_thread) const {
+  CDReader cdata(_cycler, current_thread);
+  
   do_calc_tight_bounds(min_point, max_point, found_any, 
-                       vertex_data, got_mat, mat,
+                       vertex_data, got_mat, mat, cdata,
                        current_thread);
 }
 
@@ -430,6 +431,7 @@ CData() :
   _geom_rendering(0),
   _usage_hint(UH_unspecified),
   _got_usage_hint(false),
+  _nested_vertices(0),
   _internal_bounds_stale(true)
 {
 }
@@ -450,6 +452,7 @@ CData(const Geom::CData &copy) :
   _got_usage_hint(copy._got_usage_hint),
   _modified(copy._modified),
   _internal_bounds(copy._internal_bounds),
+  _nested_vertices(copy._nested_vertices),
   _internal_bounds_stale(copy._internal_bounds_stale),
   _user_bounds(copy._user_bounds)
 {

+ 41 - 15
panda/src/gobj/geom.cxx

@@ -787,19 +787,35 @@ check_valid(const GeomVertexData *vertex_data) const {
 CPT(BoundingVolume) Geom::
 get_bounds(Thread *current_thread) const {
   CDLockedReader cdata(_cycler, current_thread);
+  if (cdata->_user_bounds != (BoundingVolume *)NULL) {
+    return cdata->_user_bounds;
+  }
+
   if (cdata->_internal_bounds_stale) {
     CDWriter cdataw(((Geom *)this)->_cycler, cdata, false);
-    if (cdataw->_user_bounds != (BoundingVolume *)NULL) {
-      cdataw->_internal_bounds = cdataw->_user_bounds;
-    } else {
-      cdataw->_internal_bounds = compute_internal_bounds(current_thread);
-    }
-    cdataw->_internal_bounds_stale = false;
+    compute_internal_bounds(cdataw, current_thread);
     return cdataw->_internal_bounds;
   }
   return cdata->_internal_bounds;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Geom::get_nested_vertices
+//       Access: Published
+//  Description: Returns the number of vertices rendered by all
+//               primitives within the Geom.
+////////////////////////////////////////////////////////////////////
+int Geom::
+get_nested_vertices(Thread *current_thread) const {
+  CDLockedReader cdata(_cycler, current_thread);
+  if (cdata->_internal_bounds_stale) {
+    CDWriter cdataw(((Geom *)this)->_cycler, cdata, false);
+    compute_internal_bounds(cdataw, current_thread);
+    return cdataw->_nested_vertices;
+  }
+  return cdata->_nested_vertices;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::output
 //       Access: Published, Virtual
@@ -1054,22 +1070,24 @@ get_next_modified() {
 //  Description: Recomputes the dynamic bounding volume for this Geom.
 //               This includes all of the vertices.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) Geom::
-compute_internal_bounds(Thread *current_thread) const {
+void Geom::
+compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
+  int num_vertices = 0;
+
   // First, get ourselves a fresh, empty bounding volume.
   PT(BoundingVolume) bound = new BoundingSphere;
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
 
   // Get the vertex data, after animation.
-  CPT(GeomVertexData) vertex_data = 
-    get_vertex_data(current_thread)->animate_vertices(current_thread);
+  CPT(GeomVertexData) vertex_data = cdata->_data.get_read_pointer();
+  vertex_data = vertex_data->animate_vertices(current_thread);
 
   // Now actually compute the bounding volume.  We do this by using
   // calc_tight_bounds to determine our minmax first.
   LPoint3f points[2];
   bool found_any = false;
   do_calc_tight_bounds(points[0], points[1], found_any, vertex_data,
-		       false, LMatrix4f::ident_mat(), current_thread);
+		       false, LMatrix4f::ident_mat(), cdata, current_thread);
   if (found_any) {
     // Then we put the bounding volume around both of those points.
     // Technically, we should put it around the eight points at the
@@ -1080,9 +1098,19 @@ compute_internal_bounds(Thread *current_thread) const {
     const LPoint3f *points_begin = &points[0];
     const LPoint3f *points_end = points_begin + 2;
     gbv->around(points_begin, points_end);
+
+    Primitives::const_iterator pi;
+    for (pi = cdata->_primitives.begin(); 
+         pi != cdata->_primitives.end();
+         ++pi) {
+      CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
+      num_vertices += prim->get_num_vertices();
+    }
   }
 
-  return bound;
+  cdata->_internal_bounds = bound;
+  cdata->_nested_vertices = num_vertices;
+  cdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1095,9 +1123,7 @@ do_calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
 		     bool &found_any, 
 		     const GeomVertexData *vertex_data,
 		     bool got_mat, const LMatrix4f &mat,
-		     Thread *current_thread) const {
-  CDReader cdata(_cycler, current_thread);
-  
+                     const CData *cdata, Thread *current_thread) const {
   Primitives::const_iterator pi;
   for (pi = cdata->_primitives.begin(); 
        pi != cdata->_primitives.end();

+ 4 - 2
panda/src/gobj/geom.h

@@ -115,6 +115,7 @@ PUBLISHED:
   bool check_valid(const GeomVertexData *vertex_data) const;
 
   CPT(BoundingVolume) get_bounds(Thread *current_thread = Thread::get_current_thread()) const;
+  int get_nested_vertices(Thread *current_thread = Thread::get_current_thread()) const;
   INLINE void mark_bounds_stale() const;
   INLINE void set_bounds(const BoundingVolume *volume);
   INLINE void clear_bounds();
@@ -157,13 +158,13 @@ private:
   class CData;
 
   INLINE void mark_internal_bounds_stale(CData *cdata);
-  PT(BoundingVolume) compute_internal_bounds(Thread *current_thread) const;
+  void compute_internal_bounds(CData *cdata, Thread *current_thread) const;
 
   void do_calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
 			    bool &found_any, 
 			    const GeomVertexData *vertex_data,
 			    bool got_mat, const LMatrix4f &mat,
-			    Thread *current_thread) const;
+                            const CData *cdata, Thread *current_thread) const;
 
   void clear_prepared(PreparedGraphicsObjects *prepared_objects);
   bool check_will_be_valid(const GeomVertexData *vertex_data) const;
@@ -289,6 +290,7 @@ private:
     UpdateSeq _modified;
   
     CPT(BoundingVolume) _internal_bounds;
+    int _nested_vertices;
     bool _internal_bounds_stale;
     CPT(BoundingVolume) _user_bounds;
     

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

@@ -11,6 +11,8 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx 
 
   #define SOURCES \
+    pipeOcclusionCullTraverser.I pipeOcclusionCullTraverser.h \
+    pipeOcclusionCullTraverser.cxx \
     cardMaker.I cardMaker.h \
     config_grutil.h \
     ffmpegTexture.I ffmpegTexture.h \

+ 2 - 0
panda/src/grutil/config_grutil.cxx

@@ -24,6 +24,7 @@
 #include "texturePool.h"
 #include "nodeVertexTransform.h"
 #include "rigidBodyCombiner.h"
+#include "pipeOcclusionCullTraverser.h"
 
 #include "dconfig.h"
 
@@ -69,6 +70,7 @@ init_libgrutil() {
   FrameRateMeter::init_type();
   NodeVertexTransform::init_type();
   RigidBodyCombiner::init_type();
+  PipeOcclusionCullTraverser::init_type();
 
 #ifdef HAVE_OPENCV
   

+ 76 - 0
panda/src/grutil/pipeOcclusionCullTraverser.I

@@ -0,0 +1,76 @@
+// Filename: pipeOcclusionCullTraverser.I
+// Created by:  drose (29May07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::get_buffer
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE GraphicsOutput *PipeOcclusionCullTraverser::
+get_buffer() const {
+  return _buffer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::set_occlusion_mask
+//       Access: Public
+//  Description: Specifies the DrawMask that should be set on
+//               occlusion polygons for this scene.  This identifies
+//               the polygons that are to be treated as occluders.
+//               Polygons that do not have this draw mask set will not
+//               be considered occluders.
+////////////////////////////////////////////////////////////////////
+INLINE void PipeOcclusionCullTraverser::
+set_occlusion_mask(const DrawMask &occlusion_mask) {
+  _occlusion_mask = occlusion_mask;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::get_occlusion_mask
+//       Access: Public
+//  Description: Returns the DrawMask for occlusion polygons.  See
+//               set_occlusion_mask().
+////////////////////////////////////////////////////////////////////
+INLINE const DrawMask &PipeOcclusionCullTraverser::
+get_occlusion_mask() const {
+  return _occlusion_mask;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::PendingObject::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipeOcclusionCullTraverser::PendingObject::
+PendingObject(CullableObject *object) :
+  _object(object)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::PendingObject::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipeOcclusionCullTraverser::PendingObject::
+~PendingObject() {
+  // Tempting as it is, we shouldn't delete the CullableObject in the
+  // destructor, since it has already been deleted.
+  //  delete _object;
+}

+ 560 - 0
panda/src/grutil/pipeOcclusionCullTraverser.cxx

@@ -0,0 +1,560 @@
+// Filename: pipeOcclusionCullTraverser.cxx
+// Created by:  drose (29May07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pipeOcclusionCullTraverser.h"
+#include "graphicsEngine.h"
+#include "graphicsPipe.h"
+#include "drawCullHandler.h"
+#include "boundingSphere.h"
+#include "geomVertexWriter.h"
+#include "geomTristrips.h"
+#include "pStatTimer.h"
+#include "cullBinManager.h"
+#include "configVariableInt.h"
+
+PStatCollector PipeOcclusionCullTraverser::_setup_occlusion_pcollector("Cull:Occlusion:Setup");
+PStatCollector PipeOcclusionCullTraverser::_draw_occlusion_pcollector("Cull:Occlusion:Occluders");
+PStatCollector PipeOcclusionCullTraverser::_test_occlusion_pcollector("Cull:Occlusion:Test");
+PStatCollector PipeOcclusionCullTraverser::_finish_occlusion_pcollector("Cull:Occlusion:Finish");
+
+PStatCollector PipeOcclusionCullTraverser::_occlusion_untested_pcollector("Occlusion results:Not tested");
+PStatCollector PipeOcclusionCullTraverser::_occlusion_passed_pcollector("Occlusion results:Visible");
+PStatCollector PipeOcclusionCullTraverser::_occlusion_failed_pcollector("Occlusion results:Occluded");
+PStatCollector PipeOcclusionCullTraverser::_occlusion_tests_pcollector("Occlusion tests");
+
+TypeHandle PipeOcclusionCullTraverser::_type_handle;
+
+static ConfigVariableInt min_occlusion_vertices
+("min-occlusion-vertices", 300,
+ PRC_DESC("The minimum number of vertices a PandaNode or Geom must contain "
+          "in order to perform an occlusion query for it.  Nodes and Geoms "
+          "smaller than this will be rendered directly, without bothering "
+          "with an occlusion query."));
+
+static ConfigVariableInt max_occlusion_vertices
+("max-occlusion-vertices", 3000,
+ PRC_DESC("The maximum number of vertices that may be included in a PandaNode "
+          "and its descendents in order to perform an occlusion query for "
+          "it.  Subgraphs whose total vertex count exceeds this number will "
+          "be subdivided further before performing an occlusion test--the "
+          "hope is that we can eventually get to a finer-grained answer.  "
+          "GeomNodes and Geoms will not be subdivided, regardless of this "
+          "limit."));
+
+static ConfigVariableBool show_occlusion
+("show-occlusion", false,
+ PRC_DESC("Set this true to visualize the efforts of the occlusion test."));
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PipeOcclusionCullTraverser::
+PipeOcclusionCullTraverser(GraphicsOutput *host) {
+  FrameBufferProperties fb_prop;
+  fb_prop.set_depth_bits(1);
+  WindowProperties win_prop = WindowProperties::size(256, 256);
+
+  GraphicsStateGuardian *gsg = host->get_gsg();
+  GraphicsEngine *engine = gsg->get_engine();
+  GraphicsPipe *pipe = gsg->get_pipe();
+
+  bool precertify = false;
+  _buffer = engine->make_output(pipe, "occlusion", 0, fb_prop, win_prop, 
+                                GraphicsPipe::BF_refuse_window,
+                                gsg, host->get_host());
+  nassertv(_buffer != (GraphicsOutput *)NULL);
+
+  // This buffer isn't really active--we render it by hand; we don't
+  // want the GraphicsEngine to render it.
+  _buffer->set_active(0);
+
+  _display_region = _buffer->make_display_region();
+  _internal_cull_handler = NULL;
+
+  make_sphere();
+  make_solid_test_state();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::Copy Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PipeOcclusionCullTraverser::
+PipeOcclusionCullTraverser(const PipeOcclusionCullTraverser &copy) :
+  CullTraverser(copy)
+{
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::set_scene
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void PipeOcclusionCullTraverser::
+set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsgbase) {
+  CullTraverser::set_scene(scene_setup, gsgbase);
+  PStatTimer timer(_setup_occlusion_pcollector);
+
+  GraphicsStateGuardian *gsg = _buffer->get_gsg();
+  nassertv(gsg == gsgbase);
+
+  Thread *current_thread = get_current_thread();
+  if (!_buffer->begin_frame(GraphicsOutput::FM_render, current_thread)) {
+    cerr << "begin_frame failed\n";
+    return;
+  }
+  _buffer->clear(current_thread);
+
+  DisplayRegionPipelineReader dr_reader(_display_region, current_thread);
+
+  _buffer->change_scenes(&dr_reader);
+  gsg->prepare_display_region(&dr_reader, dr_reader.get_stereo_channel());
+
+  _scene = new SceneSetup(*scene_setup);
+  _scene->set_display_region(_display_region);
+  _scene->set_viewport_size(_display_region->get_pixel_width(), 
+                            _display_region->get_pixel_height());
+
+  if (_scene->get_cull_center() != _scene->get_camera_path()) {
+    // This camera has a special cull center set.  For the purposes of
+    // occlusion culling, we want to render the scene from the cull
+    // center, not from the camera root.
+    NodePath cull_center = _scene->get_cull_center();
+    NodePath scene_parent = _scene->get_scene_root().get_parent(current_thread);
+    CPT(TransformState) camera_transform = cull_center.get_transform(scene_parent, current_thread);
+    CPT(TransformState) world_transform = scene_parent.get_transform(cull_center, current_thread);
+    _scene->set_camera_transform(camera_transform);
+    _scene->set_world_transform(world_transform);
+  }
+    
+  gsg->set_scene(_scene);
+  if (!gsg->begin_scene()) {
+    cerr << "begin_scene failed\n";
+    return;
+  }
+
+  // Hijack the default cull handler so we can perform all of the
+  // occlusion tests on a per-object basis, and then query the results
+  // at the end of the traversal.
+  _true_cull_handler = get_cull_handler();
+  set_cull_handler(this);
+
+  _internal_cull_handler = new DrawCullHandler(gsg);
+  _internal_trav = new CullTraverser;
+  _internal_trav->set_cull_handler(_internal_cull_handler);
+  _internal_trav->set_scene(_scene, gsg);
+  _internal_trav->set_view_frustum(get_view_frustum());
+  _internal_trav->set_camera_mask(_occlusion_mask);
+
+  _current_query = NULL;
+  _next_query = NULL;
+
+  make_sphere();
+
+  // Begin by rendering all the occluders into our internal scene.
+  PStatTimer timer2(_draw_occlusion_pcollector);
+  _internal_trav->traverse(_scene->get_scene_root());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::end_traverse
+//       Access: Public, Virtual
+//  Description: Should be called when the traverser has finished
+//               traversing its scene, this gives it a chance to do
+//               any necessary finalization.
+////////////////////////////////////////////////////////////////////
+void PipeOcclusionCullTraverser::
+end_traverse() {
+  PStatTimer timer(_finish_occlusion_pcollector);
+  GraphicsStateGuardian *gsg = _buffer->get_gsg();
+  Thread *current_thread = get_current_thread();
+
+  _current_query = NULL;
+  _next_query = NULL;
+
+  PendingObjects::iterator oi;
+  for (oi = _pending_objects.begin(); oi != _pending_objects.end(); ++oi) {
+    PendingObject &pobj = (*oi);
+    if (pobj._query == (OcclusionQueryContext *)NULL) {
+      _occlusion_untested_pcollector.add_level(1);
+      _true_cull_handler->record_object(pobj._object, this);
+    } else {
+      int num_fragments = pobj._query->get_num_fragments();
+      if (num_fragments != 0) {
+        _occlusion_passed_pcollector.add_level(1);
+        _true_cull_handler->record_object(pobj._object, this);
+      } else {
+        _occlusion_failed_pcollector.add_level(1);
+        delete pobj._object;
+      }
+    }
+
+    // The CullableObject has by now either been recorded (which will
+    // eventually delete it) or deleted directly.
+#ifndef NDEBUG
+    pobj._object = NULL;
+#endif  // NDEBUG
+  }
+  _pending_objects.clear();
+
+  gsg->end_scene();
+  _buffer->end_frame(GraphicsOutput::FM_render, current_thread);
+
+  _buffer->begin_flip();
+  _buffer->end_flip();
+
+  delete _internal_cull_handler;
+  _internal_cull_handler = NULL;
+
+  _occlusion_untested_pcollector.flush_level();
+  _occlusion_passed_pcollector.flush_level();
+  _occlusion_failed_pcollector.flush_level();
+  _occlusion_tests_pcollector.flush_level();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::is_in_view
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool PipeOcclusionCullTraverser::
+is_in_view(CullTraverserData &data) {
+  _next_query = NULL;
+
+  if (!CullTraverser::is_in_view(data)) {
+    return false;
+  }
+
+  if (_current_query != (OcclusionQueryContext *)NULL) {
+    // We've already performed an occlusion test for some ancestor of
+    // this node; no need to perform another.
+    return true;
+  }
+
+  PandaNode *node = data.node();
+  PandaNodePipelineReader *node_reader = data.node_reader();
+
+  if (node_reader->get_nested_vertices() < min_occlusion_vertices) {
+    // Never mind; let this puny one slide.
+    return true;
+  }
+
+  if (node->is_geom_node() ||
+      node_reader->is_final() ||
+      node_reader->get_effects()->has_show_bounds() ||
+      node_reader->get_nested_vertices() <= max_occlusion_vertices) {
+    // In any of these cases, there's sufficient reason to perform an
+    // occlusion test on this particular node.  Do it.
+
+    CPT(BoundingVolume) vol = node_reader->get_bounds();
+    CPT(TransformState) net_transform = data.get_net_transform(this);
+    CPT(TransformState) modelview_transform;
+
+    CPT(Geom) geom;
+    if (get_volume_viz(vol, geom, net_transform, modelview_transform)) {
+      _next_query = 
+        perform_occlusion_test(geom, net_transform, modelview_transform);
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::traverse_below
+//       Access: Public, Virtual
+//  Description: Traverses all the children of the indicated node,
+//               with the given data, which has been converted into
+//               the node's space.
+////////////////////////////////////////////////////////////////////
+void PipeOcclusionCullTraverser::
+traverse_below(CullTraverserData &data) {
+  // Save and restore _current_query, and clear _next_query, for
+  // traversing the children of this node.
+  PT(OcclusionQueryContext) prev_query = _current_query;
+  if (_next_query != (OcclusionQueryContext *)NULL) {
+    _current_query = _next_query;
+  }
+  _next_query = NULL;
+
+  CullTraverser::traverse_below(data);
+
+  _current_query = prev_query;
+  _next_query = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::record_object
+//       Access: Protected, Virtual
+//  Description: This callback function is intended to be overridden
+//               by a derived class.  This is called as each Geom is
+//               discovered by the CullTraverser.
+//
+//               We do a sneaky trick in making
+//               PipeOcclusionCullTraverser inherit from both
+//               CullTraverser and CullHandler--the traverser is its
+//               own handler!  This is the normal callback into the
+//               traverser for rendering objects.  We respond to this
+//               by firing off an occlusion test, and queuing up the
+//               object until the end of the scene.
+////////////////////////////////////////////////////////////////////
+void PipeOcclusionCullTraverser::
+record_object(CullableObject *object, const CullTraverser *traverser) {
+  nassertv(traverser == this);
+  PendingObject pobj(object);
+
+  Thread *current_thread = get_current_thread();
+
+  if (_next_query != (OcclusionQueryContext *)NULL) {
+    // We have just performed an occlusion query for this node.  Don't
+    // perform another one.
+    pobj._query = _next_query;
+
+  } else if (_current_query != (OcclusionQueryContext *)NULL) {
+    // We have previously performed an occlusion query for this node
+    // or some ancestor.  Don't perform another one.
+    pobj._query = _current_query;
+
+  } else if (object->_geom->get_nested_vertices(current_thread) < min_occlusion_vertices) {
+    // This object is too small to bother testing for occlusions.
+
+  } else {
+    // Issue an occlusion test for this object.
+    CPT(BoundingVolume) vol = object->_geom->get_bounds(current_thread);
+    CPT(TransformState) net_transform = object->_net_transform;
+    CPT(TransformState) modelview_transform;
+    CPT(Geom) geom;
+    if (get_volume_viz(vol, geom, net_transform, modelview_transform)) {
+      pobj._query = 
+        perform_occlusion_test(geom, net_transform, modelview_transform);
+    }
+  }
+
+  _pending_objects.push_back(pobj);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::make_sphere
+//       Access: Private
+//  Description: Constructs a unit sphere for testing visibility of
+//               bounding spheres.
+////////////////////////////////////////////////////////////////////
+void PipeOcclusionCullTraverser::
+make_sphere() {
+  ConfigVariableInt num_slices("num-slices", 16);
+  ConfigVariableInt num_stacks("num-stacks", 8);
+
+  //  static const int num_slices = 16;
+  //  static const int num_stacks = 8;
+
+  PT(GeomVertexData) vdata = new GeomVertexData
+    ("occlusion", GeomVertexFormat::get_v3(), Geom::UH_static);
+  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+  
+  PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
+  for (int sl = 0; sl < num_slices; ++sl) {
+    float longitude0 = (float)sl / (float)num_slices;
+    float longitude1 = (float)(sl + 1) / (float)num_slices;
+    vertex.add_data3f(compute_sphere_point(0.0, longitude0));
+    for (int st = 1; st < num_stacks; ++st) {
+      float latitude = (float)st / (float)num_stacks;
+      vertex.add_data3f(compute_sphere_point(latitude, longitude0));
+      vertex.add_data3f(compute_sphere_point(latitude, longitude1));
+    }
+    vertex.add_data3f(compute_sphere_point(1.0, longitude0));
+    
+    strip->add_next_vertices(num_stacks * 2);
+    strip->close_primitive();
+  }
+  
+  _sphere_geom = new Geom(vdata);
+  _sphere_geom->add_primitive(strip);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::compute_sphere_point
+//       Access: Private, Static
+//  Description: Returns a point on the surface of the unit sphere.
+//               latitude and longitude range from 0.0 to 1.0.  
+////////////////////////////////////////////////////////////////////
+Vertexf PipeOcclusionCullTraverser::
+compute_sphere_point(float latitude, float longitude) {
+  float s1, c1;
+  csincos(latitude * MathNumbers::pi_f, &s1, &c1);
+
+  float s2, c2;
+  csincos(longitude * 2.0f * MathNumbers::pi_f, &s2, &c2);
+
+  Vertexf p(s1 * c2, s1 * s2, c1);
+  return p;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::make_solid_test_state
+//       Access: Private
+//  Description: Creates the RenderState appropriate to rendering the
+//               occlusion test geometry invisibly.
+////////////////////////////////////////////////////////////////////
+void PipeOcclusionCullTraverser::
+make_solid_test_state() {
+  _solid_test_state = RenderState::make
+    (DepthWriteAttrib::make(DepthWriteAttrib::M_off),
+     DepthTestAttrib::make(DepthTestAttrib::M_less),
+     ColorWriteAttrib::make(ColorWriteAttrib::C_off));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::get_volume_viz
+//       Access: Private
+//  Description: Chooses a suitable Geom to render the indicated
+//               bounding volume, and fills geom and local_transform
+//               with the appropriate values.  Returns true if the
+//               bounding volume can be rendered, false if there is no
+//               suitable visualization for it.
+//
+//               On entry, net_transform should be filled with the net
+//               transform to the bounding volume.  On exit (when
+//               return value is true), it will be composed with a
+//               suitable local transform to render the bounding
+//               volume properly, and modelview_transform will also be
+//               filled with the appropriate transform.
+////////////////////////////////////////////////////////////////////
+bool PipeOcclusionCullTraverser::
+get_volume_viz(const BoundingVolume *vol, 
+               CPT(Geom) &geom,  // OUT
+               CPT(TransformState) &net_transform, // IN-OUT
+               CPT(TransformState) &modelview_transform  // OUT
+               ) {
+  if (vol->is_infinite() || vol->is_empty()) {
+    return false;
+  }
+
+  if (vol->is_of_type(BoundingSphere::get_class_type())) {
+    const BoundingSphere *sphere = DCAST(BoundingSphere, vol);
+    CPT(TransformState) local_transform = 
+      TransformState::make_pos_hpr_scale(sphere->get_center(),
+                                         LVecBase3f(0, 0, 0),
+                                         sphere->get_radius());
+    net_transform = net_transform->compose(local_transform);
+
+    modelview_transform = _internal_trav->get_world_transform()->compose(net_transform);
+
+    // See if the bounding sphere is clipped by the near plane.  If it
+    // is, the occlusion test may fail, so we won't bother performing
+    // it for this object.  Anyway, it's not occluded by anything,
+    // since it's intersecting the near plane.
+    const LPoint3f &center = modelview_transform->get_pos();
+    const LVecBase3f &radius = modelview_transform->get_scale();
+    float max_radius = max(max(radius[0], radius[1]), radius[2]);
+    if (center[1] <= max_radius) {
+      return false;
+    }
+
+    // The sphere looks good.
+    geom = _sphere_geom;
+    return true;
+  }
+
+  // Don't have a suitable representation for this bounding volume.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::perform_occlusion_test
+//       Access: Private
+//  Description: Renders the indicated geometry in the internal scene
+//               to test its visibility.
+////////////////////////////////////////////////////////////////////
+PT(OcclusionQueryContext) PipeOcclusionCullTraverser::
+perform_occlusion_test(const Geom *geom, const TransformState *net_transform,
+                       const TransformState *modelview_transform) {
+  _occlusion_tests_pcollector.add_level(1);
+  PStatTimer timer(_test_occlusion_pcollector);
+
+  GraphicsStateGuardian *gsg = _buffer->get_gsg();
+
+  gsg->begin_occlusion_query();
+
+  CullableObject *viz = 
+    new CullableObject(geom, _solid_test_state,
+                       net_transform, modelview_transform, gsg);
+
+  static ConfigVariableBool test_occlude("test-occlude", false);
+  if (test_occlude) {
+    _true_cull_handler->record_object(viz, _internal_trav);
+  } else {
+    _internal_cull_handler->record_object(viz, _internal_trav);
+  }
+  
+  PT(OcclusionQueryContext) query = gsg->end_occlusion_query();
+    
+  if (show_occlusion) {
+    // Show the results of the occlusion.  To do this, we need to get
+    // the results of the query immediately.  This will stall the
+    // pipe, but we're rendering a debug effect, so we don't mind too
+    // much.
+    int num_fragments = query->get_num_fragments();
+    show_results(num_fragments, geom, net_transform, modelview_transform);
+  }
+
+  return query;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipeOcclusionCullTraverser::show_results
+//       Access: Private
+//  Description: Draws a visualization of the results of occlusion
+//               test for a particular bounding volume.
+////////////////////////////////////////////////////////////////////
+void PipeOcclusionCullTraverser::
+show_results(int num_fragments, const Geom *geom, 
+             const TransformState *net_transform, 
+             const TransformState *modelview_transform) {
+  Colorf color;
+  if (num_fragments == 0) {
+    // Magenta: culled
+    color.set(0.8f, 0.0f, 1.0f, 0.4f);
+  } else {
+    // Yellow: visible
+    color.set(1.0f, 1.0f, 0.5f, 0.4f);
+  }
+  
+  CPT(RenderState) state = RenderState::make
+    (DepthWriteAttrib::make(DepthWriteAttrib::M_off),
+     DepthTestAttrib::make(DepthTestAttrib::M_less),
+     TransparencyAttrib::make(TransparencyAttrib::M_alpha),
+     ColorAttrib::make_flat(color));
+
+  GraphicsStateGuardian *gsg = _buffer->get_gsg();
+
+  CullableObject *internal_viz = 
+    new CullableObject(geom, state,
+                       net_transform, modelview_transform, gsg);
+  _internal_cull_handler->record_object(internal_viz, _internal_trav);
+
+  // Also render the viz in the main scene.
+  modelview_transform = get_world_transform()->compose(net_transform);
+  CullableObject *main_viz = 
+    new CullableObject(geom, state,
+                       net_transform, modelview_transform, gsg);
+  _true_cull_handler->record_object(main_viz, this);
+}

+ 155 - 0
panda/src/grutil/pipeOcclusionCullTraverser.h

@@ -0,0 +1,155 @@
+// Filename: pipeOcclusionCullTraverser.h
+// Created by:  drose (29May07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PIPEOCCLUSIONCULLTRAVERSER_H
+#define PIPEOCCLUSIONCULLTRAVERSER_H
+
+#include "pandabase.h"
+#include "cullTraverser.h"
+#include "graphicsOutput.h"
+#include "displayRegion.h"
+#include "cullHandler.h"
+
+class GraphicsEngine;
+class GraphicsPipe;
+class GraphicsStateGuardian;
+
+////////////////////////////////////////////////////////////////////
+//       Class : PipeOcclusionCullTraverser
+// Description : This specialization of CullTraverser uses the
+//               graphics pipe itself to perform occlusion culling.
+//               As such, it's likely to be inefficient (since it
+//               interferes with the pipe's normal mode of rendering),
+//               and is mainly useful to test other, CPU-based
+//               occlusion algorithms.
+//
+//               This cannot be used in a multithreaded pipeline
+//               environment where cull and draw are operating
+//               simultaneously.
+//
+//               It can't be defined in the cull subdirectory, because
+//               it needs access to GraphicsPipe and DisplayRegion and
+//               other classes in display.  So we put it in grutil
+//               instead, for lack of any better ideas.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA PipeOcclusionCullTraverser : public CullTraverser,
+                                               public CullHandler {
+PUBLISHED:
+  PipeOcclusionCullTraverser(GraphicsOutput *host);
+  PipeOcclusionCullTraverser(const PipeOcclusionCullTraverser &copy);
+
+  virtual void set_scene(SceneSetup *scene_setup,
+                         GraphicsStateGuardianBase *gsg);
+  virtual void end_traverse();
+
+  INLINE GraphicsOutput *get_buffer() const;
+
+  INLINE void set_occlusion_mask(const DrawMask &occlusion_mask);
+  INLINE const DrawMask &get_occlusion_mask() const;
+
+protected:
+  virtual bool is_in_view(CullTraverserData &data);
+  virtual void traverse_below(CullTraverserData &data);
+
+  virtual void record_object(CullableObject *object,
+                             const CullTraverser *traverser);
+
+private:
+  void make_sphere();
+  static Vertexf compute_sphere_point(float latitude, float longitude);
+  void make_solid_test_state();
+
+  bool get_volume_viz(const BoundingVolume *vol, 
+                      CPT(Geom) &geom,  // OUT
+                      CPT(TransformState) &net_transform, // IN-OUT
+                      CPT(TransformState) &modelview_transform  // OUT
+                      );
+  PT(OcclusionQueryContext) 
+    perform_occlusion_test(const Geom *geom, 
+                           const TransformState *net_transform,
+                           const TransformState *modelview_transform);
+
+  void show_results(int num_fragments, const Geom *geom, 
+                    const TransformState *net_transform, 
+                    const TransformState *modelview_transform);
+private:
+  PT(GraphicsOutput) _buffer;
+  PT(DisplayRegion) _display_region;
+  DrawMask _occlusion_mask;
+
+  PT(SceneSetup) _scene;
+  PT(CullTraverser) _internal_trav;
+
+  CullHandler *_internal_cull_handler;
+  CullHandler *_true_cull_handler;
+
+  // This is the query that has already been performed on the current
+  // node or a parent.
+  PT(OcclusionQueryContext) _current_query;
+
+  // This is the query that has been performed for any children.
+  PT(OcclusionQueryContext) _next_query;
+
+  PT(Geom) _sphere_geom;
+  CPT(RenderState) _solid_test_state;
+
+  class PendingObject {
+  public:
+    INLINE PendingObject(CullableObject *object);
+    INLINE ~PendingObject();
+
+    CullableObject *_object;
+    PT(OcclusionQueryContext) _query;
+  };
+  typedef pvector<PendingObject> PendingObjects;
+  PendingObjects _pending_objects;
+
+  static PStatCollector _setup_occlusion_pcollector;
+  static PStatCollector _draw_occlusion_pcollector;
+  static PStatCollector _test_occlusion_pcollector;
+  static PStatCollector _finish_occlusion_pcollector;
+
+  static PStatCollector _occlusion_untested_pcollector;
+  static PStatCollector _occlusion_passed_pcollector;
+  static PStatCollector _occlusion_failed_pcollector;
+  static PStatCollector _occlusion_tests_pcollector;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CullTraverser::init_type();
+    register_type(_type_handle, "PipeOcclusionCullTraverser",
+                  CullTraverser::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "pipeOcclusionCullTraverser.I"
+
+#endif
+
+
+  

+ 1 - 1
panda/src/lerp/lerp.cxx

@@ -201,7 +201,7 @@ void AutonomousLerp::step() {
     throw_event(_event);
 }
 
-void AutonomousLerp::handle_event(CPT(Event), void* data) {
+void AutonomousLerp::handle_event(const Event *, void* data) {
   AutonomousLerp* l = (AutonomousLerp*)data;
   l->step();
 }

+ 1 - 1
panda/src/lerp/lerp.h

@@ -84,7 +84,7 @@ private:
   float _t;
 
   virtual void step();
-  static void handle_event(CPT(Event), void*);
+  static void handle_event(const Event *, void*);
 
 PUBLISHED:
   AutonomousLerp(LerpFunctor* func, float endt, LerpBlendType* blend,

+ 40 - 29
panda/src/linmath/compose_matrix_src.cxx

@@ -66,8 +66,8 @@ unwind_yup_rotation_old_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the roll from the axes, and continue.
   Matrix rot_z;
-  rot_z = Matrix::rotate_mat_normaxis(-roll, FLOATNAME(LVector3)(0.0f, 0.0f, 1.0f),
-                                      CS_yup_right);
+  rot_z.set_rotate_mat_normaxis(-roll, FLOATNAME(LVector3)(0.0f, 0.0f, 1.0f),
+                                CS_yup_right);
   
   x = x * rot_z;
   y = y * rot_z;
@@ -83,8 +83,8 @@ unwind_yup_rotation_old_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the heading, and continue.
   Matrix rot_y;
-  rot_y = Matrix::rotate_mat_normaxis(-heading, FLOATNAME(LVector3)(0.0f, 1.0f, 0.0f),
-                                      CS_yup_right);
+  rot_y.set_rotate_mat_normaxis(-heading, FLOATNAME(LVector3)(0.0f, 1.0f, 0.0f),
+                                CS_yup_right);
   
   x = x * rot_y;
   y = y * rot_y;
@@ -99,8 +99,8 @@ unwind_yup_rotation_old_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the pitch.
   Matrix rot_x;
-  rot_x = Matrix::rotate_mat_normaxis(-pitch, FLOATNAME(LVector3)(1.0f, 0.0f, 0.0f),
-                                      CS_yup_right);
+  rot_x.set_rotate_mat_normaxis(-pitch, FLOATNAME(LVector3)(1.0f, 0.0f, 0.0f),
+                                CS_yup_right);
   
   x = x * rot_x;
   y = y * rot_x;
@@ -155,8 +155,8 @@ unwind_zup_rotation_old_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the roll from the axes, and continue.
   Matrix rot_y;
-  rot_y = Matrix::rotate_mat_normaxis(roll, FLOATNAME(LVector3)(0.0f, 1.0f, 0.0f),
-                                      CS_zup_right);
+  rot_y.set_rotate_mat_normaxis(roll, FLOATNAME(LVector3)(0.0f, 1.0f, 0.0f),
+                                CS_zup_right);
   
   x = x * rot_y;
   y = y * rot_y;
@@ -172,8 +172,8 @@ unwind_zup_rotation_old_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the heading, and continue.
   Matrix rot_z;
-  rot_z = Matrix::rotate_mat_normaxis(-heading, FLOATNAME(LVector3)(0.0f, 0.0f, 1.0f),
-                                      CS_zup_right);
+  rot_z.set_rotate_mat_normaxis(-heading, FLOATNAME(LVector3)(0.0f, 0.0f, 1.0f),
+                                CS_zup_right);
   
   x = x * rot_z;
   y = y * rot_z;
@@ -188,8 +188,8 @@ unwind_zup_rotation_old_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the pitch.
   Matrix rot_x;
-  rot_x = Matrix::rotate_mat_normaxis(-pitch, FLOATNAME(LVector3)(1.0f, 0.0f, 0.0f),
-                                      CS_zup_right);
+  rot_x.set_rotate_mat_normaxis(-pitch, FLOATNAME(LVector3)(1.0f, 0.0f, 0.0f),
+                                CS_zup_right);
   
   x = x * rot_x;
   y = y * rot_x;
@@ -328,11 +328,22 @@ compose_matrix_new_hpr(FLOATNAME(LMatrix3) &mat,
                        const FLOATNAME(LVecBase3) &hpr,
                        CoordinateSystem cs) {
   TAU_PROFILE("void compose_matrix_new_hpr(LMatrix3 &, const LVecBase3 &, const LVecBase3 &, const LVecBase3 &)", " ", TAU_USER);
-  mat =
-    FLOATNAME(LMatrix3)::scale_shear_mat(scale, shear, cs) *
-    FLOATNAME(LMatrix3)::rotate_mat_normaxis(hpr[2], FLOATNAME(LVector3)::forward(cs), cs) *
-    FLOATNAME(LMatrix3)::rotate_mat_normaxis(hpr[1], FLOATNAME(LVector3)::right(cs), cs) *
-    FLOATNAME(LMatrix3)::rotate_mat_normaxis(hpr[0], FLOATNAME(LVector3)::up(cs), cs);
+  mat.set_scale_shear_mat(scale, shear, cs);
+  if (!IS_NEARLY_ZERO(hpr[2])) {
+    FLOATNAME(LMatrix3) r;
+    r.set_rotate_mat_normaxis(hpr[2], FLOATNAME(LVector3)::forward(cs), cs);
+    mat *= r;
+  }
+  if (!IS_NEARLY_ZERO(hpr[1])) {
+    FLOATNAME(LMatrix3) r;
+    r.set_rotate_mat_normaxis(hpr[1], FLOATNAME(LVector3)::right(cs), cs);
+    mat *= r;
+  }
+  if (!IS_NEARLY_ZERO(hpr[0])) {
+    FLOATNAME(LMatrix3) r;
+    r.set_rotate_mat_normaxis(hpr[0], FLOATNAME(LVector3)::up(cs), cs);
+    mat *= r;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -366,8 +377,8 @@ unwind_yup_rotation_new_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the heading, and continue.
   Matrix rot_y;
-  rot_y = Matrix::rotate_mat_normaxis(-heading, FLOATNAME(LVector3)(0.0f, 1.0f, 0.0f),
-                                      CS_yup_right);
+  rot_y.set_rotate_mat_normaxis(-heading, FLOATNAME(LVector3)(0.0f, 1.0f, 0.0f),
+                                CS_yup_right);
   
   x = x * rot_y;
   y = y * rot_y;
@@ -382,8 +393,8 @@ unwind_yup_rotation_new_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the pitch.
   Matrix rot_x;
-  rot_x = Matrix::rotate_mat_normaxis(-pitch, FLOATNAME(LVector3)(1.0f, 0.0f, 0.0f),
-                                      CS_yup_right);
+  rot_x.set_rotate_mat_normaxis(-pitch, FLOATNAME(LVector3)(1.0f, 0.0f, 0.0f),
+                                CS_yup_right);
   
   x = x * rot_x;
   y = y * rot_x;
@@ -398,8 +409,8 @@ unwind_yup_rotation_new_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the roll from the axes, and continue.
   Matrix rot_z;
-  rot_z = Matrix::rotate_mat_normaxis(roll, FLOATNAME(LVector3)(0.0f, 0.0f, 1.0f),
-                                      CS_yup_right);
+  rot_z.set_rotate_mat_normaxis(roll, FLOATNAME(LVector3)(0.0f, 0.0f, 1.0f),
+                                CS_yup_right);
   
   x = x * rot_z;
   y = y * rot_z;
@@ -446,8 +457,8 @@ unwind_zup_rotation_new_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the heading, and continue.
   Matrix rot_z;
-  rot_z = Matrix::rotate_mat_normaxis(-heading, FLOATNAME(LVector3)(0.0f, 0.0f, 1.0f),
-                                      CS_zup_right);
+  rot_z.set_rotate_mat_normaxis(-heading, FLOATNAME(LVector3)(0.0f, 0.0f, 1.0f),
+                                CS_zup_right);
   
   x = x * rot_z;
   y = y * rot_z;
@@ -462,8 +473,8 @@ unwind_zup_rotation_new_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the pitch.
   Matrix rot_x;
-  rot_x = Matrix::rotate_mat_normaxis(-pitch, FLOATNAME(LVector3)(1.0f, 0.0f, 0.0f),
-                                      CS_zup_right);
+  rot_x.set_rotate_mat_normaxis(-pitch, FLOATNAME(LVector3)(1.0f, 0.0f, 0.0f),
+                                CS_zup_right);
   
   x = x * rot_x;
   y = y * rot_x;
@@ -478,8 +489,8 @@ unwind_zup_rotation_new_hpr(FLOATNAME(LMatrix3) &mat, FLOATNAME(LVecBase3) &hpr)
   
   // Unwind the roll from the axes, and continue.
   Matrix rot_y;
-  rot_y = Matrix::rotate_mat_normaxis(-roll, FLOATNAME(LVector3)(0.0f, 1.0f, 0.0f),
-                                      CS_zup_right);
+  rot_y.set_rotate_mat_normaxis(-roll, FLOATNAME(LVector3)(0.0f, 1.0f, 0.0f),
+                                CS_zup_right);
   
   x = x * rot_y;
   y = y * rot_y;

+ 109 - 145
panda/src/linmath/lmatrix3_src.I

@@ -47,7 +47,7 @@ INLINE_LINMATH FLOATNAME(LMatrix3)::
 FLOATNAME(LMatrix3)(const FLOATNAME(LMatrix3) &copy) {
   TAU_PROFILE("LMatrix3::LMatrix3(const LMatrix3 &)", " ", TAU_USER);
   
-  memcpy(_m.data,copy._m.data,sizeof(_m.data));
+  memcpy(_m.data, copy._m.data, sizeof(_m.data));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -59,7 +59,7 @@ INLINE_LINMATH FLOATNAME(LMatrix3) &FLOATNAME(LMatrix3)::
 operator = (const FLOATNAME(LMatrix3) &copy) {
   TAU_PROFILE("void LMatrix3::operator = (const LMatrix3 &)", " ", TAU_USER);
 
-  memcpy(_m.data,copy._m.data,sizeof(_m.data));
+  memcpy(_m.data, copy._m.data, sizeof(_m.data));
   return *this;
 }
 
@@ -539,8 +539,7 @@ INLINE_LINMATH FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
 operator * (const FLOATNAME(LMatrix3) &other) const {
   TAU_PROFILE("LMatrix3 LMatrix3::operator *(const LMatrix3 &)", " ", TAU_USER);
   FLOATNAME(LMatrix3) t;
-
-  MATRIX3_PRODUCT(t,(*this),other);
+  t.multiply(*this, other);
   return t;
 }
 
@@ -553,7 +552,7 @@ multiply(const FLOATNAME(LMatrix3) &other1, const FLOATNAME(LMatrix3) &other2) {
 
   nassertv((&other1 != this) && (&other2 != this));
 
-  MATRIX3_PRODUCT((*this),other1,other2);
+  MATRIX3_PRODUCT((*this), other1, other2);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -646,9 +645,9 @@ operator -= (const FLOATNAME(LMatrix3) &other) {
 INLINE_LINMATH FLOATNAME(LMatrix3) &FLOATNAME(LMatrix3)::
 operator *= (const FLOATNAME(LMatrix3) &other) {
   TAU_PROFILE("LMatrix3 LMatrix3::operator *=(const LMatrix3 &)", " ", TAU_USER);
-  (*this) = (*this) * other;
+  FLOATNAME(LMatrix3) temp = *this;
+  multiply(temp, other);
 
-  // bugbug can we do any optimization here?
   return *this;
 }
 
@@ -755,7 +754,7 @@ transpose_in_place() {
 INLINE_LINMATH FLOATTYPE FLOATNAME(LMatrix3)::
 det2(FLOATTYPE e00, FLOATTYPE e01, FLOATTYPE e10, FLOATTYPE e11) const {
   TAU_PROFILE("FLOATTYPE LMatrix3::det2()", " ", TAU_USER);
-  return DET2(e00,e01,e10,e11);
+  return DET2(e00, e01, e10, e11);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -834,6 +833,49 @@ invert_in_place() {
   return invert_from(temp);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_translate_mat
+//       Access: Public
+//  Description: Fills mat with a matrix that applies the indicated
+//               translation.
+////////////////////////////////////////////////////////////////////
+INLINE_LINMATH void FLOATNAME(LMatrix3)::
+set_translate_mat(const FLOATNAME(LVecBase2) &trans) {
+  set(1.0f, 0.0f, 0.0f,
+      0.0f, 1.0f, 0.0f,
+      trans._v.v._0, trans._v.v._1, 1.0f);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_rotate_mat
+//       Access: Public
+//  Description: Fills mat with a matrix that rotates by the given
+//               angle in degrees counterclockwise.
+////////////////////////////////////////////////////////////////////
+INLINE_LINMATH void FLOATNAME(LMatrix3)::
+set_rotate_mat(FLOATTYPE angle) {
+  TAU_PROFILE("void LMatrix3::rotate_mat(LMatrix3, FLOATTYPE)", " ", TAU_USER);
+  FLOATTYPE angle_rad = deg_2_rad(angle);
+  FLOATTYPE s, c;
+  csincos(angle_rad, &s, &c);
+  set(   c,    s,  0.0f,
+        -s,    c,  0.0f,
+      0.0f,  0.0f, 1.0f);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_scale_mat
+//       Access: Public
+//  Description: Fills mat with a matrix that applies the indicated
+//               scale in each of the two axes.
+////////////////////////////////////////////////////////////////////
+INLINE_LINMATH void FLOATNAME(LMatrix3)::
+set_scale_mat(const FLOATNAME(LVecBase2) &scale) {
+  set(scale._v.v._0, 0.0f, 0.0f,
+      0.0f, scale._v.v._1, 0.0f,
+      0.0f, 0.0f, 1.0f);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LMatrix::translate_mat
 //       Access: Public, Static
@@ -843,8 +885,8 @@ invert_in_place() {
 INLINE_LINMATH FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
 translate_mat(const FLOATNAME(LVecBase2) &trans) {
   return FLOATNAME(LMatrix3)(1.0f, 0.0f, 0.0f,
-                           0.0f, 1.0f, 0.0f,
-                           trans._v.v._0, trans._v.v._1, 1.0f);
+                             0.0f, 1.0f, 0.0f,
+                             trans._v.v._0, trans._v.v._1, 1.0f);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -856,8 +898,8 @@ translate_mat(const FLOATNAME(LVecBase2) &trans) {
 INLINE_LINMATH FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
 translate_mat(FLOATTYPE tx, FLOATTYPE ty) {
   return FLOATNAME(LMatrix3)(1.0f, 0.0f, 0.0f,
-                           0.0f, 1.0f, 0.0f,
-                           tx, ty, 1.0f);
+                             0.0f, 1.0f, 0.0f,
+                             tx, ty, 1.0f);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -868,13 +910,9 @@ translate_mat(FLOATTYPE tx, FLOATTYPE ty) {
 ////////////////////////////////////////////////////////////////////
 INLINE_LINMATH FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
 rotate_mat(FLOATTYPE angle) {
-  TAU_PROFILE("LMatrix3 LMatrix3::rotate_mat(FLOATTYPE)", " ", TAU_USER);
-  FLOATTYPE angle_rad=deg_2_rad(angle);
-  FLOATTYPE s,c;
-  csincos(angle_rad,&s,&c);
-  return FLOATNAME(LMatrix3)(  c,    s,  0.0f,
-                              -s,    c,  0.0f,
-                             0.0f,  0.0f,  1.0f);
+  FLOATNAME(LMatrix3) mat;
+  mat.set_rotate_mat(angle);
+  return mat;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -912,71 +950,8 @@ scale_mat(FLOATTYPE sx, FLOATTYPE sy) {
 INLINE_LINMATH FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
 rotate_mat(FLOATTYPE angle, FLOATNAME(LVecBase3) axis,
            CoordinateSystem cs) {
-  TAU_PROFILE("LMatrix3 LMatrix3::rotate_mat(FLOATTYPE, LVecBase3, CoordinateSystem)", " ", TAU_USER);
-  if (cs == CS_default) {
-    cs = get_default_coordinate_system();
-  }
   FLOATNAME(LMatrix3) mat;
-
-  if(IS_LEFT_HANDED_COORDSYSTEM(cs)) {
-    // In a left-handed coordinate system, counterclockwise is the
-    // other direction.
-    angle = -angle;
-  }
-
-  FLOATTYPE axis_0 = axis._v.v._0;
-  FLOATTYPE axis_1 = axis._v.v._1;
-  FLOATTYPE axis_2 = axis._v.v._2;
-
-  // Normalize the axis.
-  FLOATTYPE length_sq = axis_0 * axis_0 + axis_1 * axis_1 + axis_2 * axis_2;
-  nassertr(length_sq != 0.0f, ident_mat());
-  FLOATTYPE recip_length = 1.0f/csqrt(length_sq);
-
-  axis_0 *= recip_length;
-  axis_1 *= recip_length;
-  axis_2 *= recip_length;
-
-  FLOATTYPE angle_rad=deg_2_rad(angle);
-  FLOATTYPE s,c;
-  csincos(angle_rad,&s,&c);
-  FLOATTYPE t = 1.0f - c;
-
-  FLOATTYPE t0,t1,t2,s0,s1,s2;
-
-  t0 = t * axis_0;
-  t1 = t * axis_1;
-  t2 = t * axis_2;
-  s0 = s * axis_0;
-  s1 = s * axis_1;
-  s2 = s * axis_2;
-
-  mat._m.m._00 = t0 * axis_0 + c;
-  mat._m.m._01 = t0 * axis_1 + s2;
-  mat._m.m._02 = t0 * axis_2 - s1;
-
-  mat._m.m._10 = t1 * axis_0 - s2;
-  mat._m.m._11 = t1 * axis_1 + c;
-  mat._m.m._12 = t1 * axis_2 + s0;
-
-  mat._m.m._20 = t2 * axis_0 + s1;
-  mat._m.m._21 = t2 * axis_1 - s0;
-  mat._m.m._22 = t2 * axis_2 + c;
-
-/*
-  mat._m.m._00 = t * axis._v.v._0 * axis._v.v._0 + c;
-  mat._m.m._01 = t * axis._v.v._0 * axis._v.v._1 + s * axis._v.v._2;
-  mat._m.m._02 = t * axis._v.v._0 * axis._v.v._2 - s * axis._v.v._1;
-
-  mat._m.m._10 = t * axis._v.v._1 * axis._v.v._0 - s * axis._v.v._2;
-  mat._m.m._11 = t * axis._v.v._1 * axis._v.v._1 + c;
-  mat._m.m._12 = t * axis._v.v._1 * axis._v.v._2 + s * axis._v.v._0;
-
-  mat._m.m._20 = t * axis._v.v._2 * axis._v.v._0 + s * axis._v.v._1;
-  mat._m.m._21 = t * axis._v.v._2 * axis._v.v._1 - s * axis._v.v._0;
-  mat._m.m._22 = t * axis._v.v._2 * axis._v.v._2 + c;
-*/
-
+  mat.set_rotate_mat(angle, axis, cs);
   return mat;
 }
 
@@ -990,65 +965,24 @@ rotate_mat(FLOATTYPE angle, FLOATNAME(LVecBase3) axis,
 INLINE_LINMATH FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
 rotate_mat_normaxis(FLOATTYPE angle, const FLOATNAME(LVecBase3) &axis,
            CoordinateSystem cs) {
-  TAU_PROFILE("LMatrix3 LMatrix3::rotate_mat_normaxis(FLOATTYPE, LVecBase3, CoordinateSystem)", " ", TAU_USER);
-  if (cs == CS_default) {
-    cs = get_default_coordinate_system();
-  }
   FLOATNAME(LMatrix3) mat;
-
-  if(IS_LEFT_HANDED_COORDSYSTEM(cs)) {
-    // In a left-handed coordinate system, counterclockwise is the
-    // other direction.
-    angle = -angle;
-  }
-
-  FLOATTYPE axis_0 = axis._v.v._0;
-  FLOATTYPE axis_1 = axis._v.v._1;
-  FLOATTYPE axis_2 = axis._v.v._2;
-
-  FLOATTYPE angle_rad=deg_2_rad(angle);
-  FLOATTYPE s,c;
-  csincos(angle_rad,&s,&c);
-  FLOATTYPE t = 1.0f - c;
-
-  FLOATTYPE t0,t1,t2,s0,s1,s2;
-
-  t0 = t * axis_0;
-  t1 = t * axis_1;
-  t2 = t * axis_2;
-  s0 = s * axis_0;
-  s1 = s * axis_1;
-  s2 = s * axis_2;
-
-  mat._m.m._00 = t0 * axis_0 + c;
-  mat._m.m._01 = t0 * axis_1 + s2;
-  mat._m.m._02 = t0 * axis_2 - s1;
-
-  mat._m.m._10 = t1 * axis_0 - s2;
-  mat._m.m._11 = t1 * axis_1 + c;
-  mat._m.m._12 = t1 * axis_2 + s0;
-
-  mat._m.m._20 = t2 * axis_0 + s1;
-  mat._m.m._21 = t2 * axis_1 - s0;
-  mat._m.m._22 = t2 * axis_2 + c;
-
-/*
-  mat._m.m._00 = t * axis._v.v._0 * axis._v.v._0 + c;
-  mat._m.m._01 = t * axis._v.v._0 * axis._v.v._1 + s * axis._v.v._2;
-  mat._m.m._02 = t * axis._v.v._0 * axis._v.v._2 - s * axis._v.v._1;
-
-  mat._m.m._10 = t * axis._v.v._1 * axis._v.v._0 - s * axis._v.v._2;
-  mat._m.m._11 = t * axis._v.v._1 * axis._v.v._1 + c;
-  mat._m.m._12 = t * axis._v.v._1 * axis._v.v._2 + s * axis._v.v._0;
-
-  mat._m.m._20 = t * axis._v.v._2 * axis._v.v._0 + s * axis._v.v._1;
-  mat._m.m._21 = t * axis._v.v._2 * axis._v.v._1 - s * axis._v.v._0;
-  mat._m.m._22 = t * axis._v.v._2 * axis._v.v._2 + c;
-*/
-
+  mat.set_rotate_mat_normaxis(angle, axis, cs);
   return mat;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_scale_mat
+//       Access: Public
+//  Description: Fills mat with a matrix that applies the indicated
+//               scale in each of the three axes.
+////////////////////////////////////////////////////////////////////
+INLINE_LINMATH void FLOATNAME(LMatrix3)::
+set_scale_mat(const FLOATNAME(LVecBase3) &scale) {
+  set(scale._v.v._0, 0.0f, 0.0f,
+      0.0f, scale._v.v._1, 0.0f,
+      0.0f, 0.0f, scale._v.v._2);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LMatrix::scale_mat
 //       Access: Public, Static
@@ -1075,6 +1009,18 @@ scale_mat(FLOATTYPE sx, FLOATTYPE sy, FLOATTYPE sz) {
                              0.0f, 0.0f, sz);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_shear_mat
+//       Access: Public
+//  Description: Fills mat with a matrix that applies the indicated
+//               shear in each of the three planes.
+////////////////////////////////////////////////////////////////////
+INLINE_LINMATH void FLOATNAME(LMatrix3)::
+set_shear_mat(const FLOATNAME(LVecBase3) &shear, CoordinateSystem cs) {
+  set_scale_shear_mat(FLOATNAME(LVecBase3)(1.0f, 1.0f, 1.0f),
+                      shear, cs);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LMatrix::shear_mat
 //       Access: Public, Static
@@ -1083,8 +1029,9 @@ scale_mat(FLOATTYPE sx, FLOATTYPE sy, FLOATTYPE sz) {
 ////////////////////////////////////////////////////////////////////
 INLINE_LINMATH FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
 shear_mat(const FLOATNAME(LVecBase3) &shear, CoordinateSystem cs) {
-  return scale_shear_mat(FLOATNAME(LVecBase3)(1.0f, 1.0f, 1.0f),
-                         shear, cs);
+  FLOATNAME(LMatrix3) mat;
+  mat.set_shear_mat(shear, cs);
+  return mat;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1095,8 +1042,24 @@ shear_mat(const FLOATNAME(LVecBase3) &shear, CoordinateSystem cs) {
 ////////////////////////////////////////////////////////////////////
 INLINE_LINMATH FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
 shear_mat(FLOATTYPE shxy, FLOATTYPE shxz, FLOATTYPE shyz, CoordinateSystem cs) {
-  return scale_shear_mat(1.0f, 1.0f, 1.0f,
-                         shxy, shxz, shyz, cs);
+  FLOATNAME(LMatrix3) mat;
+  mat.set_shear_mat(FLOATNAME(LVecBase3)(shxy, shxz, shyz), cs);
+  return mat;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::scale_shear_mat
+//       Access: Public, Static
+//  Description: Returns a matrix that applies the indicated
+//               scale and shear.
+////////////////////////////////////////////////////////////////////
+INLINE_LINMATH FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
+scale_shear_mat(const FLOATNAME(LVecBase3) &scale,
+                const FLOATNAME(LVecBase3) &shear, 
+                CoordinateSystem cs) {
+  FLOATNAME(LMatrix3) mat;
+  mat.set_scale_shear_mat(scale, shear, cs);
+  return mat;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1109,9 +1072,10 @@ INLINE_LINMATH FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
 scale_shear_mat(FLOATTYPE sx, FLOATTYPE sy, FLOATTYPE sz,
                 FLOATTYPE shxy, FLOATTYPE shxz, FLOATTYPE shyz,
                 CoordinateSystem cs) {
-  return scale_shear_mat(FLOATNAME(LVecBase3)(sx, sy, sz),
-                         FLOATNAME(LVecBase3)(shxy, shxz, shyz),
-                         cs);
+  FLOATNAME(LMatrix3) mat;
+  mat.set_scale_shear_mat(FLOATNAME(LVecBase3)(sx, sy, sz),
+                          FLOATNAME(LVecBase3)(shxy, shxz, shyz), cs);
+  return mat;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 156 - 40
panda/src/linmath/lmatrix3_src.cxx

@@ -51,16 +51,16 @@ const FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::_ly_to_rz_mat =
 
 
 ////////////////////////////////////////////////////////////////////
-//     Function: LMatrix::scale_shear_mat
-//       Access: Public, Static
-//  Description: Returns a matrix that applies the indicated
+//     Function: LMatrix::set_scale_shear_mat
+//       Access: Public
+//  Description: Fills mat with a matrix that applies the indicated
 //               scale and shear.
 ////////////////////////////////////////////////////////////////////
-FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::
-scale_shear_mat(const FLOATNAME(LVecBase3) &scale,
-                const FLOATNAME(LVecBase3) &shear,
-                CoordinateSystem cs) {
-  TAU_PROFILE("LMatrix3 LMatrix3::scale_shear_mat(const LVecBase3 &, const LVecBase3 &)", " ", TAU_USER);
+void FLOATNAME(LMatrix3)::
+set_scale_shear_mat(const FLOATNAME(LVecBase3) &scale,
+                    const FLOATNAME(LVecBase3) &shear,
+                    CoordinateSystem cs) {
+  TAU_PROFILE("void LMatrix3::set_scale_shear_mat(const LVecBase3 &, const LVecBase3 &)", " ", TAU_USER);
   if (cs == CS_default) {
     cs = get_default_coordinate_system();
   }
@@ -73,57 +73,60 @@ scale_shear_mat(const FLOATNAME(LVecBase3) &scale,
   switch (cs) {
   case CS_zup_right:
     if (temp_hpr_fix) {
-      return FLOATNAME(LMatrix3)(scale._v.v._0, shear._v.v._0 * scale._v.v._0, 0.0f,
-                                 0.0f, scale._v.v._1, 0.0f,
-                                 shear._v.v._1 * scale._v.v._2, shear._v.v._2 * scale._v.v._2, scale._v.v._2);
+      set(scale._v.v._0, shear._v.v._0 * scale._v.v._0, 0.0f,
+          0.0f, scale._v.v._1, 0.0f,
+          shear._v.v._1 * scale._v.v._2, shear._v.v._2 * scale._v.v._2, scale._v.v._2);
     } else {
-      return FLOATNAME(LMatrix3)(scale._v.v._0, 0.0f, 0.0f,
-                                 shear._v.v._0 * scale._v.v._1, scale._v.v._1, 0.0f,
-                                 shear._v.v._1 * scale._v.v._2, shear._v.v._2 * scale._v.v._2, scale._v.v._2);
+      set(scale._v.v._0, 0.0f, 0.0f,
+          shear._v.v._0 * scale._v.v._1, scale._v.v._1, 0.0f,
+          shear._v.v._1 * scale._v.v._2, shear._v.v._2 * scale._v.v._2, scale._v.v._2);
     }
-
+    break;
+    
   case CS_zup_left:
     if (temp_hpr_fix) {
-      return FLOATNAME(LMatrix3)(scale._v.v._0, shear._v.v._0 * scale._v.v._0, 0.0f,
-                                 0.0f, scale._v.v._1, 0.0f,
-                                 -shear._v.v._1 * scale._v.v._2, -shear._v.v._2 * scale._v.v._2, scale._v.v._2);
+      set(scale._v.v._0, shear._v.v._0 * scale._v.v._0, 0.0f,
+          0.0f, scale._v.v._1, 0.0f,
+          -shear._v.v._1 * scale._v.v._2, -shear._v.v._2 * scale._v.v._2, scale._v.v._2);
     } else {
-      return FLOATNAME(LMatrix3)(scale._v.v._0, 0.0f, 0.0f,
-                                 shear._v.v._0 * scale._v.v._1, scale._v.v._1, 0.0f,
-                                 -shear._v.v._1 * scale._v.v._2, -shear._v.v._2 * scale._v.v._2, scale._v.v._2);
+      set(scale._v.v._0, 0.0f, 0.0f,
+          shear._v.v._0 * scale._v.v._1, scale._v.v._1, 0.0f,
+          -shear._v.v._1 * scale._v.v._2, -shear._v.v._2 * scale._v.v._2, scale._v.v._2);
     }
-
+    break;
+    
   case CS_yup_right:
     if (temp_hpr_fix) {
-      return FLOATNAME(LMatrix3)(scale._v.v._0, 0.0f, shear._v.v._1 * scale._v.v._0,
-                                 shear._v.v._0 * scale._v.v._1, scale._v.v._1, shear._v.v._2 * scale._v.v._1,
-                                 0.0f, 0.0f, scale._v.v._2);
+      set(scale._v.v._0, 0.0f, shear._v.v._1 * scale._v.v._0,
+          shear._v.v._0 * scale._v.v._1, scale._v.v._1, shear._v.v._2 * scale._v.v._1,
+          0.0f, 0.0f, scale._v.v._2);
     } else {
-      return FLOATNAME(LMatrix3)(scale._v.v._0, 0.0f, 0.0f,
-                                 shear._v.v._0 * scale._v.v._1, scale._v.v._1, shear._v.v._2 * scale._v.v._1,
-                                 shear._v.v._1 * scale._v.v._2, 0.0f, scale._v.v._2);
+      set(scale._v.v._0, 0.0f, 0.0f,
+          shear._v.v._0 * scale._v.v._1, scale._v.v._1, shear._v.v._2 * scale._v.v._1,
+          shear._v.v._1 * scale._v.v._2, 0.0f, scale._v.v._2);
     }
-
+    break;
+    
   case CS_yup_left:
     if (temp_hpr_fix) {
-      return FLOATNAME(LMatrix3)(scale._v.v._0, 0.0f, -shear._v.v._1 * scale._v.v._0,
-                                 shear._v.v._0 * scale._v.v._1, scale._v.v._1, -shear._v.v._2 * scale._v.v._1,
-                                 0.0f, 0.0f, scale._v.v._2);
+      set(scale._v.v._0, 0.0f, -shear._v.v._1 * scale._v.v._0,
+          shear._v.v._0 * scale._v.v._1, scale._v.v._1, -shear._v.v._2 * scale._v.v._1,
+          0.0f, 0.0f, scale._v.v._2);
     } else {
-      return FLOATNAME(LMatrix3)(scale._v.v._0, 0.0f, 0.0f,
-                                 shear._v.v._0 * scale._v.v._1, scale._v.v._1, -shear._v.v._2 * scale._v.v._1,
-                                 -shear._v.v._1 * scale._v.v._2, 0.0f, scale._v.v._2);
+      set(scale._v.v._0, 0.0f, 0.0f,
+          shear._v.v._0 * scale._v.v._1, scale._v.v._1, -shear._v.v._2 * scale._v.v._1,
+          -shear._v.v._1 * scale._v.v._2, 0.0f, scale._v.v._2);
     }
-
+    break;
+    
   case CS_default:
   case CS_invalid:
+  default:
     // These should not be possible.
+    linmath_cat.error()
+      << "Invalid coordinate system value!\n";
     break;
   }
-
-  linmath_cat.error()
-    << "Invalid coordinate system value!\n";
-  return _ident_mat;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -226,6 +229,119 @@ compare_to(const FLOATNAME(LMatrix3) &other, FLOATTYPE threshold) const {
   return 0;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_rotate_mat
+//       Access: Public
+//  Description: Fills mat with a matrix that rotates by the given
+//               angle in degrees counterclockwise about the indicated
+//               vector.
+////////////////////////////////////////////////////////////////////
+void FLOATNAME(LMatrix3)::
+set_rotate_mat(FLOATTYPE angle, FLOATNAME(LVecBase3) axis,
+               CoordinateSystem cs) {
+  TAU_PROFILE("void LMatrix3::set_rotate_mat(FLOATTYPE, LVecBase3, CoordinateSystem)", " ", TAU_USER);
+  if (cs == CS_default) {
+    cs = get_default_coordinate_system();
+  }
+
+  if (IS_LEFT_HANDED_COORDSYSTEM(cs)) {
+    // In a left-handed coordinate system, counterclockwise is the
+    // other direction.
+    angle = -angle;
+  }
+
+  FLOATTYPE axis_0 = axis._v.v._0;
+  FLOATTYPE axis_1 = axis._v.v._1;
+  FLOATTYPE axis_2 = axis._v.v._2;
+
+  // Normalize the axis.
+  FLOATTYPE length_sq = axis_0 * axis_0 + axis_1 * axis_1 + axis_2 * axis_2;
+  nassertv(length_sq != 0.0f);
+  FLOATTYPE recip_length = 1.0f/csqrt(length_sq);
+
+  axis_0 *= recip_length;
+  axis_1 *= recip_length;
+  axis_2 *= recip_length;
+
+  FLOATTYPE angle_rad = deg_2_rad(angle);
+  FLOATTYPE s,c;
+  csincos(angle_rad, &s, &c);
+  FLOATTYPE t = 1.0f - c;
+
+  FLOATTYPE t0, t1, t2, s0, s1, s2;
+
+  t0 = t * axis_0;
+  t1 = t * axis_1;
+  t2 = t * axis_2;
+  s0 = s * axis_0;
+  s1 = s * axis_1;
+  s2 = s * axis_2;
+
+  _m.m._00 = t0 * axis_0 + c;
+  _m.m._01 = t0 * axis_1 + s2;
+  _m.m._02 = t0 * axis_2 - s1;
+
+  _m.m._10 = t1 * axis_0 - s2;
+  _m.m._11 = t1 * axis_1 + c;
+  _m.m._12 = t1 * axis_2 + s0;
+
+  _m.m._20 = t2 * axis_0 + s1;
+  _m.m._21 = t2 * axis_1 - s0;
+  _m.m._22 = t2 * axis_2 + c;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_rotate_mat_normaxis
+//       Access: Public
+//  Description: Fills mat with a matrix that rotates by the given
+//               angle in degrees counterclockwise about the indicated
+//               vector.  Assumes axis has been normalized.
+////////////////////////////////////////////////////////////////////
+void FLOATNAME(LMatrix3)::
+set_rotate_mat_normaxis(FLOATTYPE angle, const FLOATNAME(LVecBase3) &axis,
+                        CoordinateSystem cs) {
+  TAU_PROFILE("void LMatrix3::set_rotate_mat_normaxis(FLOATTYPE, LVecBase3, CoordinateSystem)", " ", TAU_USER);
+  if (cs == CS_default) {
+    cs = get_default_coordinate_system();
+  }
+
+  if (IS_LEFT_HANDED_COORDSYSTEM(cs)) {
+    // In a left-handed coordinate system, counterclockwise is the
+    // other direction.
+    angle = -angle;
+  }
+
+  FLOATTYPE axis_0 = axis._v.v._0;
+  FLOATTYPE axis_1 = axis._v.v._1;
+  FLOATTYPE axis_2 = axis._v.v._2;
+
+  FLOATTYPE angle_rad = deg_2_rad(angle);
+  FLOATTYPE s, c;
+  csincos(angle_rad, &s, &c);
+  FLOATTYPE t = 1.0f - c;
+
+  FLOATTYPE t0, t1, t2, s0, s1, s2;
+
+  t0 = t * axis_0;
+  t1 = t * axis_1;
+  t2 = t * axis_2;
+  s0 = s * axis_0;
+  s1 = s * axis_1;
+  s2 = s * axis_2;
+
+  _m.m._00 = t0 * axis_0 + c;
+  _m.m._01 = t0 * axis_1 + s2;
+  _m.m._02 = t0 * axis_2 - s1;
+
+  _m.m._10 = t1 * axis_0 - s2;
+  _m.m._11 = t1 * axis_1 + c;
+  _m.m._12 = t1 * axis_2 + s0;
+
+  _m.m._20 = t2 * axis_0 + s1;
+  _m.m._21 = t2 * axis_1 - s0;
+  _m.m._22 = t2 * axis_2 + c;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LMatrix3::almost_equal
 //       Access: Public

+ 32 - 2
panda/src/linmath/lmatrix3_src.h

@@ -25,11 +25,13 @@
 //               texture matrix.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA FLOATNAME(LMatrix3) {
-PUBLISHED:
+public:
   typedef const FLOATTYPE *iterator;
   typedef const FLOATTYPE *const_iterator;
 
   INLINE_LINMATH FLOATNAME(LMatrix3)();
+
+PUBLISHED:
   INLINE_LINMATH FLOATNAME(LMatrix3)(const FLOATNAME(LMatrix3) &other);
   INLINE_LINMATH FLOATNAME(LMatrix3) &operator = (
       const FLOATNAME(LMatrix3) &other);
@@ -141,6 +143,13 @@ PUBLISHED:
   // The following named constructors return 3x3 matrices suitable for
   // affine transforms in 2-d coordinate space.
 
+  INLINE_LINMATH void
+    set_translate_mat(const FLOATNAME(LVecBase2) &trans);
+  INLINE_LINMATH void
+    set_rotate_mat(FLOATTYPE angle);
+  INLINE_LINMATH void
+    set_scale_mat(const FLOATNAME(LVecBase2) &scale);
+
   static INLINE_LINMATH FLOATNAME(LMatrix3)
     translate_mat(const FLOATNAME(LVecBase2) &trans);
   static INLINE_LINMATH FLOATNAME(LMatrix3)
@@ -154,6 +163,15 @@ PUBLISHED:
 
   // The following named constructors return 3x3 matrices suitable for
   // scale/rotate transforms in 3-d coordinate space.
+  void
+    set_rotate_mat(FLOATTYPE angle,
+                   FLOATNAME(LVecBase3) axis,
+                   CoordinateSystem cs = CS_default);
+  void
+    set_rotate_mat_normaxis(FLOATTYPE angle,
+                            const FLOATNAME(LVecBase3) &axis,
+                            CoordinateSystem cs = CS_default);
+
   static INLINE_LINMATH FLOATNAME(LMatrix3)
     rotate_mat(FLOATTYPE angle,
                FLOATNAME(LVecBase3) axis,
@@ -163,11 +181,18 @@ PUBLISHED:
                         const FLOATNAME(LVecBase3) &axis,
                         CoordinateSystem cs = CS_default);
 
+  INLINE_LINMATH void
+    set_scale_mat(const FLOATNAME(LVecBase3) &scale);
+
   static INLINE_LINMATH FLOATNAME(LMatrix3)
     scale_mat(const FLOATNAME(LVecBase3) &scale);
   static INLINE_LINMATH FLOATNAME(LMatrix3)
     scale_mat(FLOATTYPE sx, FLOATTYPE sy, FLOATTYPE sz);
 
+  INLINE_LINMATH void
+    set_shear_mat(const FLOATNAME(LVecBase3) &shear, 
+                  CoordinateSystem cs = CS_default);
+
   static INLINE_LINMATH FLOATNAME(LMatrix3)
     shear_mat(const FLOATNAME(LVecBase3) &shear, 
               CoordinateSystem cs = CS_default);
@@ -175,7 +200,12 @@ PUBLISHED:
     shear_mat(FLOATTYPE shxy, FLOATTYPE shxz, FLOATTYPE shyz, 
               CoordinateSystem cs = CS_default);
 
-  static FLOATNAME(LMatrix3)
+  void
+    set_scale_shear_mat(const FLOATNAME(LVecBase3) &scale,
+                        const FLOATNAME(LVecBase3) &shear, 
+                        CoordinateSystem cs = CS_default);
+
+  static INLINE_LINMATH FLOATNAME(LMatrix3)
     scale_shear_mat(const FLOATNAME(LVecBase3) &scale,
                     const FLOATNAME(LVecBase3) &shear, 
                     CoordinateSystem cs = CS_default);

+ 80 - 134
panda/src/linmath/lmatrix4_src.I

@@ -47,7 +47,7 @@ INLINE_LINMATH FLOATNAME(LMatrix4)::
 FLOATNAME(LMatrix4)(const FLOATNAME(LMatrix4) &copy) {
   TAU_PROFILE("LMatrix4::LMatrix4(const LMatrix4 &)", " ", TAU_USER);
   
-  memcpy(_m.data,copy._m.data,sizeof(_m.data));
+  memcpy(_m.data, copy._m.data, sizeof(_m.data));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -59,7 +59,7 @@ INLINE_LINMATH FLOATNAME(LMatrix4) &FLOATNAME(LMatrix4)::
 operator = (const FLOATNAME(LMatrix4) &copy) {
   TAU_PROFILE("void LMatrix4::operator = (const LMatrix4 &)", " ", TAU_USER);
 
-  memcpy(_m.data,copy._m.data,sizeof(_m.data));
+  memcpy(_m.data, copy._m.data, sizeof(_m.data));
   return *this;
 }
 
@@ -713,7 +713,7 @@ INLINE_LINMATH FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::
 operator * (const FLOATNAME(LMatrix4) &other) const {
   TAU_PROFILE("LMatrix4 LMatrix4::operator *(const LMatrix4 &)", " ", TAU_USER);
   FLOATNAME(LMatrix4) t;
-  MATRIX4_PRODUCT(t,(*this),other);
+  t.multiply(*this, other);
   return t;
 }
 
@@ -842,7 +842,9 @@ operator -= (const FLOATNAME(LMatrix4) &other) {
 INLINE_LINMATH FLOATNAME(LMatrix4) &FLOATNAME(LMatrix4)::
 operator *= (const FLOATNAME(LMatrix4) &other) {
   TAU_PROFILE("LMatrix4 LMatrix4::operator *=(const LMatrix4 &)", " ", TAU_USER);
-  (*this) = (*this) * other;
+  FLOATNAME(LMatrix4) temp = *this;
+  multiply(temp, other);
+
   return *this;
 }
 
@@ -1071,6 +1073,61 @@ invert_in_place() {
   return invert_from(temp);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_translate_mat
+//       Access: Public
+//  Description: Fills mat with a matrix that applies the indicated
+//               translation.
+////////////////////////////////////////////////////////////////////
+INLINE_LINMATH void FLOATNAME(LMatrix4)::
+set_translate_mat(const FLOATNAME(LVecBase3) &trans) {
+  set(1.0f, 0.0f, 0.0f, 0.0f,
+      0.0f, 1.0f, 0.0f, 0.0f,
+      0.0f, 0.0f, 1.0f, 0.0f,
+      trans._v.v._0, trans._v.v._1, trans._v.v._2, 1.0f);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_scale_mat
+//       Access: Public
+//  Description: Fills mat with a matrix that applies the indicated
+//               scale in each of the three axes.
+////////////////////////////////////////////////////////////////////
+INLINE_LINMATH void FLOATNAME(LMatrix4)::
+set_scale_mat(const FLOATNAME(LVecBase3) &scale) {
+  set(scale._v.v._0, 0.0f, 0.0f, 0.0f,
+      0.0f, scale._v.v._1, 0.0f, 0.0f,
+      0.0f, 0.0f, scale._v.v._2, 0.0f,
+      0.0f, 0.0f, 0.0f, 1.0f);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_shear_mat
+//       Access: Public
+//  Description: Fills mat with a matrix that applies the indicated
+//               shear in each of the three planes.
+////////////////////////////////////////////////////////////////////
+INLINE_LINMATH void FLOATNAME(LMatrix4)::
+set_shear_mat(const FLOATNAME(LVecBase3) &shear, CoordinateSystem cs) {
+  set_scale_shear_mat(FLOATNAME(LVecBase3)(1.0f, 1.0f, 1.0f),
+                      shear, cs);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_scale_shear_mat
+//       Access: Public, Static
+//  Description: Fills mat with a matrix that applies the indicated
+//               scale and shear.
+////////////////////////////////////////////////////////////////////
+INLINE_LINMATH void FLOATNAME(LMatrix4)::
+set_scale_shear_mat(const FLOATNAME(LVecBase3) &scale,
+                    const FLOATNAME(LVecBase3) &shear,
+                    CoordinateSystem cs) {
+  FLOATNAME(LMatrix3) m3;
+  m3.set_scale_shear_mat(scale, shear, cs);
+  set_upper_3(m3);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LMatrix::translate_mat
 //       Access: Public, Static
@@ -1080,9 +1137,9 @@ invert_in_place() {
 INLINE_LINMATH FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::
 translate_mat(const FLOATNAME(LVecBase3) &trans) {
   return FLOATNAME(LMatrix4)(1.0f, 0.0f, 0.0f, 0.0f,
-                           0.0f, 1.0f, 0.0f, 0.0f,
-                           0.0f, 0.0f, 1.0f, 0.0f,
-                           trans._v.v._0, trans._v.v._1, trans._v.v._2, 1.0f);
+                             0.0f, 1.0f, 0.0f, 0.0f,
+                             0.0f, 0.0f, 1.0f, 0.0f,
+                             trans._v.v._0, trans._v.v._1, trans._v.v._2, 1.0f);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1108,71 +1165,11 @@ translate_mat(FLOATTYPE tx, FLOATTYPE ty, FLOATTYPE tz) {
 INLINE_LINMATH FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::
 rotate_mat(FLOATTYPE angle, const FLOATNAME(LVecBase3) &axis,
            CoordinateSystem cs) {
-  TAU_PROFILE("LMatrix4 LMatrix4::rotate_mat(FLOATTYPE, const LVecBase3 &, cs)", " ", TAU_USER);
-
-  if (cs == CS_default) {
-    cs = get_default_coordinate_system();
-  }
   FLOATNAME(LMatrix4) mat;
-
-  if(IS_LEFT_HANDED_COORDSYSTEM(cs)) {
-    // In a left-handed coordinate system, counterclockwise is the
-    // other direction.
-    angle = -angle;
-  }
-
-  FLOATTYPE axis_0 = axis._v.v._0;
-  FLOATTYPE axis_1 = axis._v.v._1;
-  FLOATTYPE axis_2 = axis._v.v._2;
-
-  // Normalize the axis.
-  FLOATTYPE length_sq = axis_0 * axis_0 + axis_1 * axis_1 + axis_2 * axis_2;
-  nassertr(length_sq != 0.0f, ident_mat());
-  FLOATTYPE recip_length = 1.0f/csqrt(length_sq);
-  
-  axis_0 *= recip_length;
-  axis_1 *= recip_length;
-  axis_2 *= recip_length;
-
-  FLOATTYPE angle_rad=deg_2_rad(angle);
-  FLOATTYPE s,c;
-  csincos(angle_rad,&s,&c);
-  FLOATTYPE t = 1.0f - c;
-
-  FLOATTYPE t0,t1,t2,s0,s1,s2;
-
-  t0 = t * axis_0;
-  t1 = t * axis_1;
-  t2 = t * axis_2;
-  s0 = s * axis_0;
-  s1 = s * axis_1;
-  s2 = s * axis_2;
-
-  mat._m.m._00 = t0 * axis_0 + c;
-  mat._m.m._01 = t0 * axis_1 + s2;
-  mat._m.m._02 = t0 * axis_2 - s1;
-
-  mat._m.m._10 = t1 * axis_0 - s2;
-  mat._m.m._11 = t1 * axis_1 + c;
-  mat._m.m._12 = t1 * axis_2 + s0;
-
-  mat._m.m._20 = t2 * axis_0 + s1;
-  mat._m.m._21 = t2 * axis_1 - s0;
-  mat._m.m._22 = t2 * axis_2 + c;
-
-  mat._m.m._03 = 0.0f;
-  mat._m.m._13 = 0.0f;
-  mat._m.m._23 = 0.0f;
-
-  mat._m.m._30 = 0.0f;
-  mat._m.m._31 = 0.0f;
-  mat._m.m._32 = 0.0f;
-  mat._m.m._33 = 1.0f;
-
+  mat.set_rotate_mat(angle, axis, cs);
   return mat;
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: LMatrix::rotate_mat_normaxis
 //       Access: Public, Static
@@ -1182,70 +1179,14 @@ rotate_mat(FLOATTYPE angle, const FLOATNAME(LVecBase3) &axis,
 ////////////////////////////////////////////////////////////////////
 INLINE_LINMATH FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::
 rotate_mat_normaxis(FLOATTYPE angle, const FLOATNAME(LVecBase3) &axis,
-           CoordinateSystem cs) {
+                    CoordinateSystem cs) {
   TAU_PROFILE("LMatrix4 LMatrix4::rotate_mat_normaxis(FLOATTYPE, const LVecBase3 &, cs)", " ", TAU_USER);
 
   FLOATNAME(LMatrix4) mat;
-  rotate_mat_normaxis(angle,axis,mat,cs);
+  mat.set_rotate_mat_normaxis(angle, axis, cs);
   return mat;
 }
 
-INLINE_LINMATH void FLOATNAME(LMatrix4)::
-rotate_mat_normaxis(FLOATTYPE angle, const FLOATNAME(LVecBase3) &axis,
-                                        FLOATNAME(LMatrix4) &result_mat, CoordinateSystem cs) {
-  TAU_PROFILE("void LMatrix4::rotate_mat_normaxis(FLOATTYPE, const LVecBase3 &, LMatrix4 &, cs)", " ", TAU_USER);
-
-
-  if (cs == CS_default) {
-    cs = get_default_coordinate_system();
-  }
-
-  if(IS_LEFT_HANDED_COORDSYSTEM(cs)) {
-    // In a left-handed coordinate system, counterclockwise is the
-    // other direction.
-    angle = -angle;
-  }
-
-  FLOATTYPE axis_0 = axis._v.v._0;
-  FLOATTYPE axis_1 = axis._v.v._1;
-  FLOATTYPE axis_2 = axis._v.v._2;
-
-  FLOATTYPE angle_rad=deg_2_rad(angle);
-  FLOATTYPE s,c;
-  csincos(angle_rad,&s,&c);
-  FLOATTYPE t = 1.0f - c;
-
-  FLOATTYPE t0,t1,t2,s0,s1,s2;
-
-  t0 = t * axis_0;
-  t1 = t * axis_1;
-  t2 = t * axis_2;
-  s0 = s * axis_0;
-  s1 = s * axis_1;
-  s2 = s * axis_2;
-
-  result_mat._m.m._00 = t0 * axis_0 + c;
-  result_mat._m.m._01 = t0 * axis_1 + s2;
-  result_mat._m.m._02 = t0 * axis_2 - s1;
-
-  result_mat._m.m._10 = t1 * axis_0 - s2;
-  result_mat._m.m._11 = t1 * axis_1 + c;
-  result_mat._m.m._12 = t1 * axis_2 + s0;
-
-  result_mat._m.m._20 = t2 * axis_0 + s1;
-  result_mat._m.m._21 = t2 * axis_1 - s0;
-  result_mat._m.m._22 = t2 * axis_2 + c;
-
-  result_mat._m.m._03 = 0.0f;
-  result_mat._m.m._13 = 0.0f;
-  result_mat._m.m._23 = 0.0f;
-
-  result_mat._m.m._30 = 0.0f;
-  result_mat._m.m._31 = 0.0f;
-  result_mat._m.m._32 = 0.0f;
-  result_mat._m.m._33 = 1.0f;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: LMatrix::scale_mat
 //       Access: Public, Static
@@ -1297,8 +1238,9 @@ scale_mat(FLOATTYPE scale) {
 ////////////////////////////////////////////////////////////////////
 INLINE_LINMATH FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::
 shear_mat(const FLOATNAME(LVecBase3) &shear, CoordinateSystem cs) {
-  return scale_shear_mat(FLOATNAME(LVecBase3)(1.0f, 1.0f, 1.0f),
-                         shear, cs);
+  FLOATNAME(LMatrix4) mat;
+  mat.set_shear_mat(shear, cs);
+  return mat;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1310,8 +1252,9 @@ shear_mat(const FLOATNAME(LVecBase3) &shear, CoordinateSystem cs) {
 INLINE_LINMATH FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::
 shear_mat(FLOATTYPE shxy, FLOATTYPE shxz, FLOATTYPE shyz, 
           CoordinateSystem cs) {
-  return scale_shear_mat(1.0f, 1.0f, 1.0f,
-                         shxy, shxz, shyz, cs);
+  FLOATNAME(LMatrix4) mat;
+  mat.set_shear_mat(FLOATNAME(LVecBase3)(shxy, shxz, shyz), cs);
+  return mat;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1324,7 +1267,9 @@ INLINE_LINMATH FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::
 scale_shear_mat(const FLOATNAME(LVecBase3) &scale,
                 const FLOATNAME(LVecBase3) &shear,
                 CoordinateSystem cs) {
-  return FLOATNAME(LMatrix4)(FLOATNAME(LMatrix3)::scale_shear_mat(scale, shear, cs));
+  FLOATNAME(LMatrix4) mat;
+  mat.set_scale_shear_mat(scale, shear, cs);
+  return mat;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1337,9 +1282,10 @@ INLINE_LINMATH FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::
 scale_shear_mat(FLOATTYPE sx, FLOATTYPE sy, FLOATTYPE sz,
                 FLOATTYPE shxy, FLOATTYPE shxz, FLOATTYPE shyz,
                 CoordinateSystem cs) {
-  return scale_shear_mat(FLOATNAME(LVecBase3)(sx, sy, sz),
-                         FLOATNAME(LVecBase3)(shxy, shxz, shyz),
-                         cs);
+  FLOATNAME(LMatrix4) mat;
+  mat.set_scale_shear_mat(FLOATNAME(LVecBase3)(sx, sy, sz),
+                          FLOATNAME(LVecBase3)(shxy, shxz, shyz), cs);
+  return mat;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 132 - 0
panda/src/linmath/lmatrix4_src.cxx

@@ -141,6 +141,138 @@ compare_to(const FLOATNAME(LMatrix4) &other, FLOATTYPE threshold) const {
   return 0;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_rotate_mat
+//       Access: Public
+//  Description: Sets mat to a matrix that rotates by the given angle
+//               in degrees counterclockwise about the indicated
+//               vector.
+////////////////////////////////////////////////////////////////////
+void FLOATNAME(LMatrix4)::
+set_rotate_mat(FLOATTYPE angle, const FLOATNAME(LVecBase3) &axis,
+               CoordinateSystem cs) {
+  TAU_PROFILE("void LMatrix4::set_rotate_mat(FLOATTYPE, const LVecBase3 &, cs)", " ", TAU_USER);
+
+  if (cs == CS_default) {
+    cs = get_default_coordinate_system();
+  }
+
+  if (IS_LEFT_HANDED_COORDSYSTEM(cs)) {
+    // In a left-handed coordinate system, counterclockwise is the
+    // other direction.
+    angle = -angle;
+  }
+
+  FLOATTYPE axis_0 = axis._v.v._0;
+  FLOATTYPE axis_1 = axis._v.v._1;
+  FLOATTYPE axis_2 = axis._v.v._2;
+
+  // Normalize the axis.
+  FLOATTYPE length_sq = axis_0 * axis_0 + axis_1 * axis_1 + axis_2 * axis_2;
+  nassertv(length_sq != 0.0f);
+  FLOATTYPE recip_length = 1.0f/csqrt(length_sq);
+  
+  axis_0 *= recip_length;
+  axis_1 *= recip_length;
+  axis_2 *= recip_length;
+
+  FLOATTYPE angle_rad=deg_2_rad(angle);
+  FLOATTYPE s,c;
+  csincos(angle_rad,&s,&c);
+  FLOATTYPE t = 1.0f - c;
+
+  FLOATTYPE t0,t1,t2,s0,s1,s2;
+
+  t0 = t * axis_0;
+  t1 = t * axis_1;
+  t2 = t * axis_2;
+  s0 = s * axis_0;
+  s1 = s * axis_1;
+  s2 = s * axis_2;
+
+  _m.m._00 = t0 * axis_0 + c;
+  _m.m._01 = t0 * axis_1 + s2;
+  _m.m._02 = t0 * axis_2 - s1;
+
+  _m.m._10 = t1 * axis_0 - s2;
+  _m.m._11 = t1 * axis_1 + c;
+  _m.m._12 = t1 * axis_2 + s0;
+
+  _m.m._20 = t2 * axis_0 + s1;
+  _m.m._21 = t2 * axis_1 - s0;
+  _m.m._22 = t2 * axis_2 + c;
+
+  _m.m._03 = 0.0f;
+  _m.m._13 = 0.0f;
+  _m.m._23 = 0.0f;
+
+  _m.m._30 = 0.0f;
+  _m.m._31 = 0.0f;
+  _m.m._32 = 0.0f;
+  _m.m._33 = 1.0f;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LMatrix::set_rotate_mat_normaxis
+//       Access: Public
+//  Description: Fills mat with a matrix that rotates by the given
+//               angle in degrees counterclockwise about the indicated
+//               vector.  Assumes axis has been prenormalized.
+////////////////////////////////////////////////////////////////////
+void FLOATNAME(LMatrix4)::
+set_rotate_mat_normaxis(FLOATTYPE angle, const FLOATNAME(LVecBase3) &axis,
+                        CoordinateSystem cs) {
+  TAU_PROFILE("void LMatrix4::set_rotate_mat_normaxis(FLOATTYPE, const LVecBase3 &, cs)", " ", TAU_USER);
+  if (cs == CS_default) {
+    cs = get_default_coordinate_system();
+  }
+
+  if (IS_LEFT_HANDED_COORDSYSTEM(cs)) {
+    // In a left-handed coordinate system, counterclockwise is the
+    // other direction.
+    angle = -angle;
+  }
+
+  FLOATTYPE axis_0 = axis._v.v._0;
+  FLOATTYPE axis_1 = axis._v.v._1;
+  FLOATTYPE axis_2 = axis._v.v._2;
+
+  FLOATTYPE angle_rad=deg_2_rad(angle);
+  FLOATTYPE s,c;
+  csincos(angle_rad,&s,&c);
+  FLOATTYPE t = 1.0f - c;
+
+  FLOATTYPE t0,t1,t2,s0,s1,s2;
+
+  t0 = t * axis_0;
+  t1 = t * axis_1;
+  t2 = t * axis_2;
+  s0 = s * axis_0;
+  s1 = s * axis_1;
+  s2 = s * axis_2;
+
+  _m.m._00 = t0 * axis_0 + c;
+  _m.m._01 = t0 * axis_1 + s2;
+  _m.m._02 = t0 * axis_2 - s1;
+
+  _m.m._10 = t1 * axis_0 - s2;
+  _m.m._11 = t1 * axis_1 + c;
+  _m.m._12 = t1 * axis_2 + s0;
+
+  _m.m._20 = t2 * axis_0 + s1;
+  _m.m._21 = t2 * axis_1 - s0;
+  _m.m._22 = t2 * axis_2 + c;
+
+  _m.m._03 = 0.0f;
+  _m.m._13 = 0.0f;
+  _m.m._23 = 0.0f;
+
+  _m.m._30 = 0.0f;
+  _m.m._31 = 0.0f;
+  _m.m._32 = 0.0f;
+  _m.m._33 = 1.0f;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LMatrix4::almost_equal
 //       Access: Public

+ 23 - 6
panda/src/linmath/lmatrix4_src.h

@@ -22,11 +22,12 @@
 // Description : This is a 4-by-4 transform matrix.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA FLOATNAME(LMatrix4) {
-PUBLISHED:
+public:
   typedef const FLOATTYPE *iterator;
   typedef const FLOATTYPE *const_iterator;
 
   INLINE_LINMATH FLOATNAME(LMatrix4)();
+PUBLISHED:
   INLINE_LINMATH FLOATNAME(LMatrix4)(const FLOATNAME(LMatrix4) &other);
   INLINE_LINMATH FLOATNAME(LMatrix4) &operator = (const FLOATNAME(LMatrix4) &other);
   INLINE_LINMATH FLOATNAME(LMatrix4) &operator = (FLOATTYPE fill_value);
@@ -130,6 +131,27 @@ PUBLISHED:
   INLINE_LINMATH bool invert_in_place();
 
   INLINE_LINMATH static const FLOATNAME(LMatrix4) &ident_mat();
+
+  INLINE_LINMATH void
+    set_translate_mat(const FLOATNAME(LVecBase3) &trans);
+  void
+    set_rotate_mat(FLOATTYPE angle,
+                   const FLOATNAME(LVecBase3) &axis,
+                   CoordinateSystem cs = CS_default);
+  void
+    set_rotate_mat_normaxis(FLOATTYPE angle,
+                            const FLOATNAME(LVecBase3) &axis,
+                            CoordinateSystem cs = CS_default);
+  INLINE_LINMATH void
+    set_scale_mat(const FLOATNAME(LVecBase3) &scale);
+  INLINE_LINMATH void
+    set_shear_mat(const FLOATNAME(LVecBase3) &shear, 
+                  CoordinateSystem cs = CS_default);
+  INLINE_LINMATH void
+    set_scale_shear_mat(const FLOATNAME(LVecBase3) &scale,
+                        const FLOATNAME(LVecBase3) &shear, 
+                        CoordinateSystem cs = CS_default);
+  
   INLINE_LINMATH static FLOATNAME(LMatrix4)
     translate_mat(const FLOATNAME(LVecBase3) &trans);
   INLINE_LINMATH static FLOATNAME(LMatrix4)
@@ -142,11 +164,6 @@ PUBLISHED:
     rotate_mat_normaxis(FLOATTYPE angle,
                         const FLOATNAME(LVecBase3) &axis,
                         CoordinateSystem cs = CS_default);
-  INLINE_LINMATH static void
-    rotate_mat_normaxis(FLOATTYPE angle,
-                        const FLOATNAME(LVecBase3) &axis,
-                        FLOATNAME(LMatrix4) &result_mat,
-                        CoordinateSystem cs = CS_default);
   INLINE_LINMATH static FLOATNAME(LMatrix4)
     scale_mat(const FLOATNAME(LVecBase3) &scale);
   INLINE_LINMATH static FLOATNAME(LMatrix4)

+ 12 - 6
panda/src/parametrics/ropeNode.cxx

@@ -32,6 +32,7 @@
 #include "geomLinestrips.h"
 #include "geomTristrips.h"
 #include "geomVertexWriter.h"
+#include "boundingSphere.h"
 
 TypeHandle RopeNode::_type_handle;
 
@@ -240,10 +241,16 @@ reset_bound(const NodePath &rel_to) {
 //               of substance should redefine this to do the right
 //               thing.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) RopeNode::
-compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
-  return do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage, 
-                             current_thread);
+void RopeNode::
+compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+                        Thread *current_thread) const {
+  PT(BoundingVolume) bounds = 
+    do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage, 
+                        current_thread);
+
+  bdata->_internal_bounds = bounds;
+  bdata->_internal_vertices = 0;  // TODO--estimate this better.
+  bdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -291,8 +298,7 @@ do_recompute_bounds(const NodePath &rel_to, int pipeline_stage,
   // properties from the current pipeline stage, the lazy way.
 
   // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage, current_thread);
-  nassertr(bound != (BoundingVolume *)NULL, bound);
+  PT(BoundingVolume) bound = new BoundingSphere;
   
   NurbsCurveEvaluator *curve = get_curve();
   if (curve != (NurbsCurveEvaluator *)NULL) {

+ 2 - 1
panda/src/parametrics/ropeNode.h

@@ -146,7 +146,8 @@ PUBLISHED:
   void reset_bound(const NodePath &rel_to);
 
 protected:
-  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+                                       Thread *current_thread) const;
 
 private:
   CPT(GeomVertexFormat) get_format(bool support_normals) const;

+ 12 - 6
panda/src/parametrics/sheetNode.cxx

@@ -29,6 +29,7 @@
 #include "geom.h"
 #include "geomTristrips.h"
 #include "geomVertexWriter.h"
+#include "boundingSphere.h"
 
 TypeHandle SheetNode::_type_handle;
 
@@ -222,10 +223,16 @@ reset_bound(const NodePath &rel_to) {
 //               of substance should redefine this to do the right
 //               thing.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) SheetNode::
-compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
-  return do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage,
-                             current_thread);
+void SheetNode::
+compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+                        Thread *current_thread) const {
+  PT(BoundingVolume) bounds = 
+    do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage, 
+                        current_thread);
+
+  bdata->_internal_bounds = bounds;
+  bdata->_internal_vertices = 0;  // TODO--estimate this better.
+  bdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -241,8 +248,7 @@ do_recompute_bounds(const NodePath &rel_to, int pipeline_stage,
   // properties from the current pipeline stage, the lazy way.
 
   // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage, current_thread);
-  nassertr(bound != (BoundingVolume *)NULL, bound);
+  PT(BoundingVolume) bound = new BoundingSphere;
   
   NurbsSurfaceEvaluator *surface = get_surface();
   if (surface != (NurbsSurfaceEvaluator *)NULL) {

+ 2 - 1
panda/src/parametrics/sheetNode.h

@@ -67,7 +67,8 @@ PUBLISHED:
   void reset_bound(const NodePath &rel_to);
 
 protected:
-  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+                                       Thread *current_thread) const;
 
 private:
   PT(BoundingVolume) do_recompute_bounds(const NodePath &rel_to,

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

@@ -212,6 +212,14 @@ ConfigVariableBool support_fade_lod
           "(ignoring the fade time).  This may be useful, for instance, to "
           "test the performance impact of using FadeLOD nodes."));
 
+ConfigVariableBool depth_offset_decals
+("depth-offset-decals", false,
+ PRC_DESC("Set this true to allow decals to be implemented via the advanced "
+          "depth offset feature, if supported, instead of via the traditional "
+          "(and slower) two-pass approach.  This is false by default "
+          "because it appears that many graphics drivers have issues with "
+          "their depth offset implementation."));
+
 ConfigVariableInt max_collect_vertices
 ("max-collect-vertices", 65535,
  PRC_DESC("Specifies the maximum number of vertices that are allowed to be "

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

@@ -49,6 +49,7 @@ extern ConfigVariableBool transform_cache;
 extern ConfigVariableBool state_cache;
 extern ConfigVariableBool retransform_sprites;
 extern ConfigVariableBool support_fade_lod;
+extern ConfigVariableBool depth_offset_decals;
 extern ConfigVariableInt max_collect_vertices;
 extern ConfigVariableInt max_collect_indices;
 extern ConfigVariableBool premunge_data;

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

@@ -35,7 +35,6 @@ PUBLISHED:
     BT_back_to_front,
     BT_front_to_back,
     BT_fixed,
-    BT_occlusion_test,
   };
 };
 

+ 0 - 6
panda/src/pgraph/cullBinManager.cxx

@@ -348,9 +348,6 @@ parse_bin_type(const string &bin_type) {
   } else if (cmp_nocase_uh(bin_type, "front_to_back") == 0) {
     return BT_front_to_back;
 
-  } else if (cmp_nocase_uh(bin_type, "occlusion_test") == 0) {
-    return BT_occlusion_test;
-
   } else {
     return BT_invalid;
   }
@@ -380,9 +377,6 @@ operator << (ostream &out, CullBinManager::BinType bin_type) {
     
   case CullBinManager::BT_fixed:
     return out << "fixed";
-    
-  case CullBinManager::BT_occlusion_test:
-    return out << "occlusion_test";
   }
 
   return out << "**invalid BinType(" << (int)bin_type << ")**";

+ 6 - 87
panda/src/pgraph/cullTraverser.I

@@ -38,24 +38,6 @@ get_current_thread() const {
   return _current_thread;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CullTraverser::set_scene
-//       Access: Public
-//  Description: Sets the SceneSetup object that indicates the initial
-//               camera position, etc.  This must be called before
-//               traversal begins.
-////////////////////////////////////////////////////////////////////
-INLINE void CullTraverser::
-set_scene(SceneSetup *scene_setup) {
-  _scene_setup = scene_setup;
-  _initial_state = scene_setup->get_initial_state();
-
-  const Camera *camera = scene_setup->get_camera_node();
-  _tag_state_key = camera->get_tag_state_key();
-  _has_tag_state_key = !_tag_state_key.empty();
-  _camera_mask = camera->get_camera_mask();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_scene
 //       Access: Public
@@ -117,18 +99,6 @@ get_world_transform() const {
   return _scene_setup->get_world_transform();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CullTraverser::set_initial_state
-//       Access: Public
-//  Description: Sets the initial RenderState at the top of the scene
-//               graph we are traversing.  If this is not set, the
-//               default is the empty state.
-////////////////////////////////////////////////////////////////////
-INLINE void CullTraverser::
-set_initial_state(const RenderState *initial_state) {
-  _initial_state = initial_state;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_initial_state
 //       Access: Public
@@ -141,20 +111,6 @@ get_initial_state() const {
   return _initial_state;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CullTraverser::set_depth_offset_decals
-//       Access: Public
-//  Description: Sets the depth_offset_decals flag.  If this is true,
-//               decals will be rendered using DepthOffsetAttribs;
-//               otherwise, decals will be rendered with a more
-//               expensive three-pass system.  This is normally set
-//               from the corresponding flag in the GSG.
-////////////////////////////////////////////////////////////////////
-INLINE void CullTraverser::
-set_depth_offset_decals(bool flag) {
-  _depth_offset_decals = flag;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_depth_offset_decals
 //       Access: Public
@@ -169,9 +125,12 @@ get_depth_offset_decals() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::set_camera_mask
 //       Access: Public
-//  Description: Specifies the visibility mask from the camera viewing
-//               the scene.  Any nodes that do not have at least some
-//               bits in common with this mask will not be drawn.
+//  Description: Changes the visibility mask for the camera viewing
+//               the scene.  This is normally set automatically
+//               at the time setup_scene() is called; you should
+//               change this only if you want to render some set of
+//               objects different from what the camera normally would
+//               draw.
 ////////////////////////////////////////////////////////////////////
 INLINE void CullTraverser::
 set_camera_mask(const DrawMask &camera_mask) {
@@ -220,46 +179,6 @@ get_view_frustum() const {
   return _view_frustum;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CullTraverser::set_guard_band
-//       Access: Public
-//  Description: Specifies the bounding volume to use for detecting
-//               guard band clipping.  This is a render optimization
-//               for certain cards that support this feature; the
-//               guard band is a 2-d area than the frame buffer.
-//               If a primitive will appear entirely within the guard
-//               band after perspective transform, it may be drawn
-//               correctly with clipping disabled, for a small
-//               performance gain.
-//
-//               This is the bounding volume that corresponds to the
-//               2-d guard band.  If a primitive is entirely within
-//               this area, clipping will be disabled on the GSG.
-////////////////////////////////////////////////////////////////////
-INLINE void CullTraverser::
-set_guard_band(GeometricBoundingVolume *guard_band) {
-  _guard_band = guard_band;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CullTraverser::get_guard_band
-//       Access: Public
-//  Description: Returns the bounding volume that corresponds to the
-//               guard band, or NULL if the guard band is not in
-//               use or has not been set.
-//
-//               Note that the guard band returned here is always in
-//               the coordinate space of the starting node, not the
-//               current node, even if it is sampled during a
-//               traversal.  To get the guard band in the current
-//               node's coordinate space, check in the current
-//               CullTraverserData.
-////////////////////////////////////////////////////////////////////
-INLINE GeometricBoundingVolume *CullTraverser::
-get_guard_band() const {
-  return _guard_band;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::set_cull_handler
 //       Access: Public

+ 88 - 32
panda/src/pgraph/cullTraverser.cxx

@@ -50,14 +50,13 @@ TypeHandle CullTraverser::_type_handle;
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 CullTraverser::
-CullTraverser(GraphicsStateGuardianBase *gsg, Thread *current_thread) :
-  _gsg(gsg),
-  _current_thread(current_thread)
+CullTraverser() :
+  _gsg(NULL),
+  _current_thread(Thread::get_current_thread())
 {
   _camera_mask = DrawMask::all_on();
   _has_tag_state_key = false;
   _initial_state = RenderState::make_empty();
-  _depth_offset_decals = false;
   _cull_handler = (CullHandler *)NULL;
   _portal_clipper = (PortalClipper *)NULL;
 }
@@ -78,23 +77,45 @@ CullTraverser(const CullTraverser &copy) :
   _initial_state(copy._initial_state),
   _depth_offset_decals(copy._depth_offset_decals),
   _view_frustum(copy._view_frustum),
-  _guard_band(copy._guard_band),
   _cull_handler(copy._cull_handler),
   _portal_clipper(copy._portal_clipper)
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::set_scene
+//       Access: Public, Virtual
+//  Description: Sets the SceneSetup object that indicates the initial
+//               camera position, etc.  This must be called before
+//               traversal begins.
+////////////////////////////////////////////////////////////////////
+void CullTraverser::
+set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsg) {
+  _scene_setup = scene_setup;
+  _gsg = gsg;
+
+  _initial_state = scene_setup->get_initial_state();
+  _depth_offset_decals = _gsg->depth_offset_decals() && depth_offset_decals;
+
+  _current_thread = Thread::get_current_thread();
+
+  const Camera *camera = scene_setup->get_camera_node();
+  _tag_state_key = camera->get_tag_state_key();
+  _has_tag_state_key = !_tag_state_key.empty();
+  _camera_mask = camera->get_camera_mask();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::traverse
 //       Access: Public
 //  Description: Begins the traversal from the indicated node.
 ////////////////////////////////////////////////////////////////////
 void CullTraverser::
-traverse(const NodePath &root, bool python_cull_control) {
+traverse(const NodePath &root) {
   nassertv(_cull_handler != (CullHandler *)NULL);
   nassertv(_scene_setup != (SceneSetup *)NULL);
 
-  if (allow_portal_cull || python_cull_control) {
+  if (allow_portal_cull) {
     // This _view_frustum is in cull_center space
     PT(GeometricBoundingVolume) vf = _view_frustum;
 
@@ -115,7 +136,7 @@ traverse(const NodePath &root, bool python_cull_control) {
 
     CullTraverserData data(root, TransformState::make_identity(),
                            _initial_state, _view_frustum, 
-                           _guard_band, _current_thread);
+                           _current_thread);
     
     traverse(data);
     
@@ -133,7 +154,7 @@ traverse(const NodePath &root, bool python_cull_control) {
   } else {
     CullTraverserData data(root, TransformState::make_identity(),
                            _initial_state, _view_frustum, 
-                           _guard_band, _current_thread);
+                           _current_thread);
     
     traverse(data);
   }
@@ -148,7 +169,7 @@ traverse(const NodePath &root, bool python_cull_control) {
 ////////////////////////////////////////////////////////////////////
 void CullTraverser::
 traverse(CullTraverserData &data) {
-  if (data.is_in_view(_camera_mask)) {
+  if (is_in_view(data)) {
     if (pgraph_cat.is_spam()) {
       pgraph_cat.spam() 
         << "\n" << data._node_path
@@ -202,7 +223,7 @@ traverse(CullTraverserData &data) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::traverse_below
-//       Access: Public
+//       Access: Public, Virtual
 //  Description: Traverses all the children of the indicated node,
 //               with the given data, which has been converted into
 //               the node's space.
@@ -283,6 +304,56 @@ traverse_below(CullTraverserData &data) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::end_traverse
+//       Access: Public, Virtual
+//  Description: Should be called when the traverser has finished
+//               traversing its scene, this gives it a chance to do
+//               any necessary finalization.
+////////////////////////////////////////////////////////////////////
+void CullTraverser::
+end_traverse() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::draw_bounding_volume
+//       Access: Public
+//  Description: Draws an appropriate visualization of the indicated
+//               bounding volume.
+////////////////////////////////////////////////////////////////////
+void CullTraverser::
+draw_bounding_volume(const BoundingVolume *vol, 
+                     const TransformState *net_transform,
+                     const TransformState *modelview_transform) {
+  PT(Geom) bounds_viz = make_bounds_viz(vol);
+  
+  if (bounds_viz != (Geom *)NULL) {
+    _geoms_pcollector.add_level(2);
+    CullableObject *outer_viz = 
+      new CullableObject(bounds_viz, get_bounds_outer_viz_state(), 
+                         net_transform, modelview_transform, get_gsg());
+    _cull_handler->record_object(outer_viz, this);
+    
+    CullableObject *inner_viz = 
+      new CullableObject(bounds_viz, get_bounds_inner_viz_state(), 
+                         net_transform, modelview_transform, get_gsg());
+    _cull_handler->record_object(inner_viz, this);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullTraverser::is_in_view
+//       Access: Protected, Virtual
+//  Description: Returns true if the current node is fully or
+//               partially within the viewing area and should be
+//               drawn, or false if it (and all of its children)
+//               should be pruned.
+////////////////////////////////////////////////////////////////////
+bool CullTraverser::
+is_in_view(CullTraverserData &data) {
+  return data.is_in_view(_camera_mask);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::show_bounds
 //       Access: Private
@@ -307,24 +378,9 @@ show_bounds(CullTraverserData &data, bool tight) {
     }
     
   } else {
-    PT(Geom) bounds_viz = make_bounds_viz(node->get_bounds());
-
-    if (bounds_viz != (Geom *)NULL) {
-      _geoms_pcollector.add_level(2);
-      CullableObject *outer_viz = 
-        new CullableObject(bounds_viz, get_bounds_outer_viz_state(), 
-                           data.get_net_transform(this),
-                           data.get_modelview_transform(this),
-                           get_gsg());
-      _cull_handler->record_object(outer_viz, this);
-      
-      CullableObject *inner_viz = 
-        new CullableObject(bounds_viz, get_bounds_inner_viz_state(), 
-                           data.get_net_transform(this),
-                           data.get_modelview_transform(this),
-                           get_gsg());
-      _cull_handler->record_object(inner_viz, this);
-    }
+    draw_bounding_volume(node->get_bounds(),
+                         data.get_net_transform(this),
+                         data.get_modelview_transform(this));
   }
 }
 
@@ -337,8 +393,8 @@ show_bounds(CullTraverserData &data, bool tight) {
 PT(Geom) CullTraverser::
 make_bounds_viz(const BoundingVolume *vol) {
   PT(Geom) geom;
-  if (vol->is_infinite()) {
-    // No way to draw an infinite bounding volume.
+  if (vol->is_infinite() || vol->is_empty()) {
+    // No way to draw an infinite or empty bounding volume.
 
   } else if (vol->is_of_type(BoundingSphere::get_class_type())) {
     const BoundingSphere *sphere = DCAST(BoundingSphere, vol);
@@ -605,7 +661,7 @@ start_decal(const CullTraverserData &data) {
 ////////////////////////////////////////////////////////////////////
 CullableObject *CullTraverser::
 r_get_decals(CullTraverserData &data, CullableObject *decals) {
-  if (data.is_in_view(_camera_mask)) {
+  if (is_in_view(data)) {
     PandaNodePipelineReader *node_reader = data.node_reader();
     PandaNode *node = data.node();
 

+ 20 - 16
panda/src/pgraph/cullTraverser.h

@@ -29,7 +29,7 @@
 #include "pointerTo.h"
 #include "camera.h"
 #include "drawMask.h"
-#include "typedObject.h"
+#include "typedReferenceCount.h"
 #include "pStatCollector.h"
 
 class GraphicsStateGuardian;
@@ -48,15 +48,17 @@ class NodePath;
 //               Each renderable Geom encountered is passed along with
 //               its associated RenderState to the CullHandler object.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA CullTraverser : public TypedObject {
-public:
-  CullTraverser(GraphicsStateGuardianBase *gsg, Thread *current_thread);
+class EXPCL_PANDA CullTraverser : public TypedReferenceCount {
+PUBLISHED:
+  CullTraverser();
   CullTraverser(const CullTraverser &copy);
 
+public:
   INLINE GraphicsStateGuardianBase *get_gsg() const;
   INLINE Thread *get_current_thread() const;
 
-  INLINE void set_scene(SceneSetup *scene_setup);
+  virtual void set_scene(SceneSetup *scene_setup,
+                         GraphicsStateGuardianBase *gsg);
   INLINE SceneSetup *get_scene() const;
   INLINE bool has_tag_state_key() const;
   INLINE const string &get_tag_state_key() const;
@@ -67,30 +69,33 @@ public:
   INLINE const TransformState *get_camera_transform() const;
   INLINE const TransformState *get_world_transform() const;
 
-  INLINE void set_initial_state(const RenderState *initial_state);
   INLINE const RenderState *get_initial_state() const;
-
-  INLINE void set_depth_offset_decals(bool flag);
   INLINE bool get_depth_offset_decals() const;
 
   INLINE void set_view_frustum(GeometricBoundingVolume *view_frustum);
   INLINE GeometricBoundingVolume *get_view_frustum() const;
 
-  INLINE void set_guard_band(GeometricBoundingVolume *guard_band);
-  INLINE GeometricBoundingVolume *get_guard_band() const;
-
   INLINE void set_cull_handler(CullHandler *cull_handler);
   INLINE CullHandler *get_cull_handler() const;
 
   INLINE void set_portal_clipper(PortalClipper *portal_clipper);
   INLINE PortalClipper *get_portal_clipper() const;
 
-  void traverse(const NodePath &root, bool python_cull_control=false);
+  void traverse(const NodePath &root);
   void traverse(CullTraverserData &data);
-  void traverse_below(CullTraverserData &data);
+  virtual void traverse_below(CullTraverserData &data);
+
+  virtual void end_traverse();
 
   INLINE static void flush_level();
 
+  void draw_bounding_volume(const BoundingVolume *vol, 
+                            const TransformState *net_transform,
+                            const TransformState *modelview_transform);
+
+protected:
+  virtual bool is_in_view(CullTraverserData &data);
+
 public:
   // Statistics
   static PStatCollector _nodes_pcollector;
@@ -120,7 +125,6 @@ private:
   CPT(RenderState) _initial_state;
   bool _depth_offset_decals;
   PT(GeometricBoundingVolume) _view_frustum;
-  PT(GeometricBoundingVolume) _guard_band;
   CullHandler *_cull_handler;
   PortalClipper *_portal_clipper;
   
@@ -129,9 +133,9 @@ public:
     return _type_handle;
   }
   static void init_type() {
-    TypedObject::init_type();
+    TypedReferenceCount::init_type();
     register_type(_type_handle, "CullTraverser",
-                  TypedObject::get_class_type());
+                  TypedReferenceCount::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();

+ 0 - 5
panda/src/pgraph/cullTraverserData.I

@@ -26,14 +26,12 @@ CullTraverserData(const NodePath &start,
                   const TransformState *net_transform,
                   const RenderState *state,
                   GeometricBoundingVolume *view_frustum,
-                  GeometricBoundingVolume *guard_band,
                   Thread *current_thread) :
   _node_path(start),
   _node_reader(start.node(), current_thread),
   _net_transform(net_transform),
   _state(state),
   _view_frustum(view_frustum),
-  _guard_band(guard_band),
   _cull_planes(CullPlanes::make_empty()),
   _draw_mask(DrawMask::all_on())
 {
@@ -52,7 +50,6 @@ CullTraverserData(const CullTraverserData &copy) :
   _net_transform(copy._net_transform),
   _state(copy._state),
   _view_frustum(copy._view_frustum),
-  _guard_band(copy._guard_band),
   _cull_planes(copy._cull_planes),
   _draw_mask(copy._draw_mask)
 {
@@ -70,7 +67,6 @@ operator = (const CullTraverserData &copy) {
   _net_transform = copy._net_transform;
   _state = copy._state;
   _view_frustum = copy._view_frustum;
-  _guard_band = copy._guard_band;
   _cull_planes = copy._cull_planes;
   _draw_mask = copy._draw_mask;
 }
@@ -88,7 +84,6 @@ CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
   _net_transform(parent._net_transform),
   _state(parent._state),
   _view_frustum(parent._view_frustum),
-  _guard_band(parent._guard_band),
   _cull_planes(parent._cull_planes),
   _draw_mask(parent._draw_mask)
 {

+ 0 - 8
panda/src/pgraph/cullTraverserData.cxx

@@ -90,7 +90,6 @@ apply_transform_and_state(CullTraverser *trav,
     _net_transform = _net_transform->compose(node_transform);
 
     if ((_view_frustum != (GeometricBoundingVolume *)NULL) ||
-        (_guard_band != (GeometricBoundingVolume *)NULL) ||
         (!_cull_planes->is_empty())) {
       // We need to move the viewing frustums into the node's
       // coordinate space by applying the node's inverse transform.
@@ -99,7 +98,6 @@ apply_transform_and_state(CullTraverser *trav,
         // trying, we'll just give up on frustum culling from this
         // point down.
         _view_frustum = (GeometricBoundingVolume *)NULL;
-        _guard_band = (GeometricBoundingVolume *)NULL;
         _cull_planes = CullPlanes::make_empty();
 
       } else {
@@ -113,11 +111,6 @@ apply_transform_and_state(CullTraverser *trav,
           _view_frustum->xform(inv_transform->get_mat());
         }
 
-        if (_guard_band != (GeometricBoundingVolume *)NULL) {
-          _guard_band = DCAST(GeometricBoundingVolume, _guard_band->make_copy());
-          _guard_band->xform(inv_transform->get_mat());
-        }
-
         _cull_planes = _cull_planes->xform(inv_transform->get_mat());
       }
     }
@@ -166,7 +159,6 @@ is_in_view_impl() {
       // The node and its descendents are completely enclosed within
       // the frustum.  No need to cull further.
       _view_frustum = (GeometricBoundingVolume *)NULL;
-      _guard_band = (GeometricBoundingVolume *)NULL;
       
     } else {
       // The node is partially, but not completely, within the viewing

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

@@ -51,7 +51,6 @@ public:
                            const TransformState *net_transform,
                            const RenderState *state,
                            GeometricBoundingVolume *view_frustum,
-                           GeometricBoundingVolume *guard_band,
                            Thread *current_thread);
   INLINE CullTraverserData(const CullTraverserData &copy);
   INLINE void operator = (const CullTraverserData &copy); 
@@ -81,7 +80,6 @@ public:
   CPT(TransformState) _net_transform;
   CPT(RenderState) _state;
   PT(GeometricBoundingVolume) _view_frustum;
-  PT(GeometricBoundingVolume) _guard_band;
   CPT(CullPlanes) _cull_planes;
   DrawMask _draw_mask;
 

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

@@ -79,6 +79,8 @@ CullableObject(const Geom *geom, const RenderState *state,
 INLINE CullableObject::
 CullableObject(const CullableObject &copy) :
   _geom(copy._geom),
+  _munger(copy._munger),
+  _munged_data(copy._munged_data),
   _state(copy._state),
   _net_transform(copy._net_transform),
   _modelview_transform(copy._modelview_transform),
@@ -96,6 +98,8 @@ CullableObject(const CullableObject &copy) :
 INLINE void CullableObject::
 operator = (const CullableObject &copy) {
   _geom = copy._geom;
+  _munger = copy._munger;
+  _munged_data = copy._munged_data;
   _state = copy._state;
   _net_transform = copy._net_transform;
   _modelview_transform = copy._modelview_transform;

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

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

+ 13 - 6
panda/src/pgraph/geomNode.cxx

@@ -677,11 +677,13 @@ do_premunge(GraphicsStateGuardianBase *gsg,
 //               be overridden by PandaNode classes that contain
 //               something internally.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) GeomNode::
-compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
+void GeomNode::
+compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+                        Thread *current_thread) const {
+  int num_vertices = 0;
+
   // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage, current_thread);
-  nassertr(bound != (BoundingVolume *)NULL, bound);
+  PT(BoundingVolume) bound = new BoundingSphere;
 
   // Now actually compute the bounding volume by putting it around all
   // of our geoms' bounding volumes.
@@ -693,14 +695,19 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
   CPT(GeomList) geoms = cdata->get_geoms();
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);
-    child_volumes.push_back(entry._geom.get_read_pointer()->get_bounds());
+    CPT(Geom) geom = entry._geom.get_read_pointer();
+    child_volumes.push_back(geom->get_bounds());
+    num_vertices += geom->get_nested_vertices();
   }
 
   const BoundingVolume **child_begin = &child_volumes[0];
   const BoundingVolume **child_end = child_begin + child_volumes.size();
 
   bound->around(child_begin, child_end);
-  return bound;
+
+  bdata->_internal_bounds = bound;
+  bdata->_internal_vertices = num_vertices;
+  bdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/pgraph/geomNode.h

@@ -96,7 +96,8 @@ public:
                    GeomTransformer &transformer);
 
 protected:
-  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+                                       Thread *current_thread) const;
 
 public:
   // This must be declared public so that VC6 will allow the nested

+ 7 - 5
panda/src/pgraph/lodNode.cxx

@@ -456,11 +456,11 @@ show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) {
 //               be overridden by PandaNode classes that contain
 //               something internally.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) LODNode::
-compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
+void LODNode::
+compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+                        Thread *current_thread) const {
   // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage, current_thread);
-  nassertr(bound != (BoundingVolume *)NULL, bound);
+  PT(BoundingVolume) bound = new BoundingSphere;
 
   // If we have any visible rings, those count in the bounding volume.
   if (is_any_shown()) {
@@ -489,7 +489,9 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
     bound->around(child_begin, child_end);
   }
 
-  return bound;
+  bdata->_internal_bounds = bound;
+  bdata->_internal_vertices = 0;
+  bdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/pgraph/lodNode.h

@@ -85,7 +85,8 @@ protected:
   int compute_child(CullTraverser *trav, CullTraverserData &data);
 
   bool show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data);
-  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+                                       Thread *current_thread) const;
 
   INLINE void consider_verify_lods(CullTraverser *trav, CullTraverserData &data);
 

+ 62 - 2
panda/src/pgraph/pandaNode.I

@@ -574,12 +574,27 @@ clear_bounds() {
 //               volume.
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(BoundingVolume) PandaNode::
-get_internal_bounds() const {
-  Thread *current_thread = Thread::get_current_thread();
+get_internal_bounds(Thread *current_thread) const {
   return get_internal_bounds(current_thread->get_pipeline_stage(),
                              current_thread);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_internal_vertices
+//       Access: Published
+//  Description: Returns the total number of vertices that will be
+//               rendered by this particular node alone, not
+//               accounting for its children.
+//
+//               This may not include all vertices for certain dynamic
+//               effects.
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNode::
+get_internal_vertices(Thread *current_thread) const {
+  return get_internal_vertices(current_thread->get_pipeline_stage(),
+                               current_thread);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::set_final
 //       Access: Published
@@ -909,6 +924,32 @@ get_parent() const {
   return _parent;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::BoundsData::Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode::BoundsData::
+BoundsData() :
+  _internal_bounds(NULL),
+  _internal_vertices(0),
+  _internal_bounds_stale(true)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::BoundsData::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode::BoundsData::
+BoundsData(const PandaNode::BoundsData &copy) :
+  _internal_bounds(copy._internal_bounds),
+  _internal_vertices(copy._internal_vertices),
+  _internal_bounds_stale(copy._internal_bounds_stale)
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::CData::set_fancy_bit
 //       Access: Public
@@ -1660,6 +1701,25 @@ get_bounds() const {
   return _cdata->_external_bounds;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::get_nested_vertices
+//       Access: Public
+//  Description: Returns the total number of vertices that will be
+//               rendered by this node and all of its descendents.
+//
+//               This is not necessarily an accurate count of vertices
+//               that will actually be rendered, since this will
+//               include all vertices of all LOD's, and it will also
+//               include hidden nodes.  It may also omit or only
+//               approximate certain kinds of dynamic geometry.
+//               However, it will not include stashed nodes.
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNodePipelineReader::
+get_nested_vertices() const {
+  nassertr(_cdata->_last_update == _cdata->_next_update, _cdata->_nested_vertices);
+  return _cdata->_nested_vertices;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNodePipelineReader::is_final
 //       Access: Public

+ 72 - 15
panda/src/pgraph/pandaNode.cxx

@@ -2091,10 +2091,9 @@ set_bounds(const BoundingVolume *volume) {
     } else {
       cdata->_user_bounds = volume->make_copy();
     }
+    mark_bounds_stale(pipeline_stage, current_thread);
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
-  
-  mark_internal_bounds_stale();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2169,6 +2168,36 @@ get_bounds(UpdateSeq &seq, Thread *current_thread) const {
   return cdata->_external_bounds;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_nested_vertices
+//       Access: Published
+//  Description: Returns the total number of vertices that will be
+//               rendered by this node and all of its descendents.
+//
+//               This is not necessarily an accurate count of vertices
+//               that will actually be rendered, since this will
+//               include all vertices of all LOD's, and it will also
+//               include hidden nodes.  It may also omit or only
+//               approximate certain kinds of dynamic geometry.
+//               However, it will not include stashed nodes.
+////////////////////////////////////////////////////////////////////
+int PandaNode::
+get_nested_vertices(Thread *current_thread) const {
+  int pipeline_stage = current_thread->get_pipeline_stage();
+  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
+  if (cdata->_last_update != cdata->_next_update) {
+    // The cache is stale; it needs to be rebuilt.
+    int result;
+    {
+      CDStageWriter cdataw = 
+	((PandaNode *)this)->update_bounds(pipeline_stage, cdata); 
+      result = cdataw->_nested_vertices;
+    }
+    return result;
+  }
+  return cdata->_nested_vertices;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::mark_bounds_stale
 //       Access: Published
@@ -2272,19 +2301,41 @@ as_light() {
 CPT(BoundingVolume) PandaNode::
 get_internal_bounds(int pipeline_stage, Thread *current_thread) const {
   CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
+  if (cdata->_user_bounds != (BoundingVolume *)NULL) {
+    return cdata->_user_bounds;
+  }
+
   if (cdata->_internal_bounds_stale) {
     CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage, cdata);
-    if (cdataw->_user_bounds != (BoundingVolume *)NULL) {
-      cdataw->_internal_bounds = cdataw->_user_bounds;
-    } else {
-      cdataw->_internal_bounds = compute_internal_bounds(pipeline_stage, current_thread);
-    }
-    cdataw->_internal_bounds_stale = false;
+    compute_internal_bounds(cdataw, pipeline_stage, current_thread);
+    nassertr(!cdataw->_internal_bounds.is_null(), NULL);
     return cdataw->_internal_bounds;
   }
   return cdata->_internal_bounds;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_internal_vertices
+//       Access: Protected
+//  Description: Returns the total number of vertices that will be
+//               rendered by this particular node alone, not
+//               accounting for its children.
+//
+//               This may not include all vertices for certain dynamic
+//               effects.
+////////////////////////////////////////////////////////////////////
+int PandaNode::
+get_internal_vertices(int pipeline_stage, Thread *current_thread) const {
+  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
+  if (cdata->_internal_bounds_stale) {
+    CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage, cdata);
+    compute_internal_bounds(cdataw, pipeline_stage, current_thread);
+    nassertr(!cdataw->_internal_bounds.is_null(), 0);
+    return cdataw->_internal_vertices;
+  }
+  return cdata->_internal_vertices;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::set_internal_bounds
 //       Access: Protected
@@ -2366,9 +2417,12 @@ force_bounds_stale(int pipeline_stage, Thread *current_thread) {
 //               be overridden by PandaNode classes that contain
 //               something internally.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) PandaNode::
-compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
-  return new BoundingSphere;
+void PandaNode::
+compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+                        Thread *current_thread) const {
+  bdata->_internal_bounds = new BoundingSphere;
+  bdata->_internal_vertices = 0;
+  bdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -3364,6 +3418,8 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
     child_volumes_ref.push_back(internal_bounds);
     child_volumes.push_back(internal_bounds);
 
+    int num_vertices = cdata->_internal_vertices;
+
     // Now expand those contents to include all of our children.
     int num_children = children.get_num_children();
 
@@ -3412,6 +3468,7 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
 	off_clip_planes = orig_cp->compose_off(child_cdataw->_off_clip_planes);
 	child_volumes_ref.push_back(child_cdataw->_external_bounds);
 	child_volumes.push_back(child_cdataw->_external_bounds);
+        num_vertices += child_cdataw->_nested_vertices;
 
       } else {
 	// Child is good.
@@ -3443,6 +3500,7 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
 	off_clip_planes = orig_cp->compose_off(child_cdata->_off_clip_planes);
 	child_volumes_ref.push_back(child_cdata->_external_bounds);
 	child_volumes.push_back(child_cdata->_external_bounds);
+        num_vertices += child_cdata->_nested_vertices;
       }
     }
 
@@ -3483,6 +3541,7 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
         }
 
 	cdataw->_off_clip_planes = off_clip_planes;
+        cdataw->_nested_vertices = num_vertices;
 
 	// Compute the bounding sphere around all of our child
 	// volumes.
@@ -3642,8 +3701,6 @@ CData() :
   _draw_show_mask(DrawMask::all_on()),
   _into_collide_mask(CollideMask::all_off()),
   _user_bounds(NULL),
-  _internal_bounds(NULL),
-  _internal_bounds_stale(true),
   _final_bounds(false),
   _fancy_bits(0),
 
@@ -3665,6 +3722,7 @@ CData() :
 ////////////////////////////////////////////////////////////////////
 PandaNode::CData::
 CData(const PandaNode::CData &copy) :
+  BoundsData(copy),
   _state(copy._state),
   _transform(copy._transform),
   _prev_transform(copy._prev_transform),
@@ -3675,8 +3733,6 @@ CData(const PandaNode::CData &copy) :
   _draw_show_mask(copy._draw_show_mask),
   _into_collide_mask(copy._into_collide_mask),
   _user_bounds(copy._user_bounds),
-  _internal_bounds(copy._internal_bounds),
-  _internal_bounds_stale(copy._internal_bounds_stale),
   _final_bounds(copy._final_bounds),
   _fancy_bits(copy._fancy_bits),
 
@@ -3684,6 +3740,7 @@ CData(const PandaNode::CData &copy) :
   _net_draw_control_mask(copy._net_draw_control_mask),
   _net_draw_show_mask(copy._net_draw_show_mask),
   _off_clip_planes(copy._off_clip_planes),
+  _nested_vertices(copy._nested_vertices),
   _external_bounds(copy._external_bounds),
   _last_update(copy._last_update),
   _next_update(copy._next_update),

+ 34 - 9
panda/src/pgraph/pandaNode.h

@@ -251,7 +251,9 @@ PUBLISHED:
   INLINE void clear_bounds();
   CPT(BoundingVolume) get_bounds(Thread *current_thread = Thread::get_current_thread()) const;
   CPT(BoundingVolume) get_bounds(UpdateSeq &seq, Thread *current_thread = Thread::get_current_thread()) const;
-  INLINE CPT(BoundingVolume) get_internal_bounds() const;
+  int get_nested_vertices(Thread *current_thread = Thread::get_current_thread()) const;
+  INLINE CPT(BoundingVolume) get_internal_bounds(Thread *current_thread = Thread::get_current_thread()) const;
+  INLINE int get_internal_vertices(Thread *current_thread = Thread::get_current_thread()) const;
 
   void mark_bounds_stale(Thread *current_thread = Thread::get_current_thread()) const;
   void mark_internal_bounds_stale(Thread *current_thread = Thread::get_current_thread());
@@ -274,8 +276,11 @@ PUBLISHED:
   INLINE int get_fancy_bits(Thread *current_thread = Thread::get_current_thread()) const;
 
 protected:
+  class BoundsData;
+
   INLINE CPT(BoundingVolume) get_user_bounds(int pipeline_stage, Thread *current_thread) const;
   CPT(BoundingVolume) get_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  int get_internal_vertices(int pipeline_stage, Thread *current_thread) const;
   void set_internal_bounds(const BoundingVolume *volume);
 
   INLINE void mark_bounds_stale(int pipeline_stage, Thread *current_thread) const;
@@ -283,7 +288,8 @@ protected:
   void force_bounds_stale(int pipeline_stage, Thread *current_thread);
   INLINE void mark_internal_bounds_stale(int pipeline_stage, Thread *current_thread);
 
-  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+                                       Thread *current_thread) const;
   virtual void parents_changed();
   virtual void children_changed();
   virtual void transform_changed();
@@ -298,6 +304,25 @@ protected:
 
   void set_cull_callback();
 
+protected:
+  // This is a base class of CData, defined below.  It contains just
+  // the protected (not private) part of CData that will be needed by
+  // derived classes to implement compute_internal_bounds().
+  class EXPCL_PANDA BoundsData : public CycleData {
+  protected:
+    INLINE BoundsData();
+    INLINE BoundsData(const BoundsData &copy);
+
+  public:
+    // This is the "internal" bounding volume, which is normally
+    // empty, but which a particular PandaNode subclass may define to
+    // be any arbitrary volume, by calling set_internal_bounds() or by
+    // overriding compute_internal_bounds().
+    CPT(BoundingVolume) _internal_bounds;
+    int _internal_vertices;
+    bool _internal_bounds_stale;
+  };
+
 private:
   class CData;
 
@@ -409,7 +434,7 @@ private:
   
   // This is the data that must be cycled between pipeline stages. 
 
-  class EXPCL_PANDA CData : public CycleData {
+  class EXPCL_PANDA CData : public BoundsData {
   public:
     CData();
     CData(const CData &copy);
@@ -464,12 +489,7 @@ private:
     // user.  It defaults to NULL, which means an empty volume.
     CPT(BoundingVolume) _user_bounds;
 
-    // This is the "internal" bounding volume, which is normally
-    // empty, but which a particular PandaNode subclass may define to
-    // be any arbitrary volume, by calling set_internal_bounds() or by
-    // overriding compute_internal_bounds().
-    CPT(BoundingVolume) _internal_bounds;
-    bool _internal_bounds_stale;
+    // See BoundsData, above, for _internal_bounds.
 
     // This is true if the external bounds of this node should be
     // deemed "final".  See set_final().
@@ -498,6 +518,10 @@ private:
     // TODO: fix the circular reference counts involved here.
     CPT(RenderAttrib) _off_clip_planes;
 
+    // The number of vertices rendered by this node and all child
+    // nodes.
+    int _nested_vertices;
+
     // This is the bounding volume around the _user_bounds, the
     // _internal_bounds, and all of the children's external bounding
     // volumes.
@@ -712,6 +736,7 @@ public:
   INLINE CollideMask get_net_collide_mask() const;
   INLINE CPT(RenderAttrib) get_off_clip_planes() const;
   INLINE CPT(BoundingVolume) get_bounds() const;
+  INLINE int get_nested_vertices() const;
   INLINE bool is_final() const;
   INLINE int get_fancy_bits() const;
 

+ 6 - 3
panda/src/pgraph/planeNode.cxx

@@ -204,10 +204,13 @@ is_renderable() const {
 //               be overridden by PandaNode classes that contain
 //               something internally.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) PlaneNode::
-compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
+void PlaneNode::
+compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+                        Thread *current_thread) const {
   CDStageReader cdata(_cycler, pipeline_stage, current_thread);
-  return new BoundingPlane(cdata->_plane);
+  bdata->_internal_bounds = new BoundingPlane(cdata->_plane);
+  bdata->_internal_vertices = 0;
+  bdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/pgraph/planeNode.h

@@ -76,7 +76,8 @@ public:
   INLINE static UpdateSeq get_sort_seq();
 
 protected:
-  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+                                       Thread *current_thread) const;
   PT(Geom) get_viz(CullTraverser *trav, CullTraverserData &data);
   
 private:

+ 8 - 7
panda/src/pgraph/portalNode.cxx

@@ -305,7 +305,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 
       CullTraverserData next_data(_cell_out, 
                                   cell_transform,
-                                  next_state, new_bh, NULL,
+                                  next_state, new_bh,
                                   current_thread);
       //                                  data._state, new_bh, NULL);
 
@@ -382,12 +382,11 @@ draw() const {
 //               of substance should redefine this to do the right
 //               thing.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) PortalNode::
-compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
+void PortalNode::
+compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+                        Thread *current_thread) const {
   // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage, current_thread);
-  nassertr(bound != (BoundingVolume *)NULL, bound);
-
+  PT(BoundingVolume) bound = new BoundingSphere;
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
 
   // Now actually compute the bounding volume by putting it around all
@@ -399,7 +398,9 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
   // Now actually compute the bounding volume by putting it around all
   gbv->around(vertices_begin, vertices_end);
 
-  return bound;
+  bdata->_internal_bounds = bound;
+  bdata->_internal_vertices = 0;
+  bdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/pgraph/portalNode.h

@@ -97,7 +97,8 @@ PUBLISHED:
   //  void draw () const;
 
 protected:
-  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+                                       Thread *current_thread) const;
 
 private:
   CPT(RenderState) get_last_pos_state();

+ 32 - 4
panda/src/pgraph/transformState.cxx

@@ -36,6 +36,11 @@ UpdateSeq TransformState::_last_cycle_detect;
 PStatCollector TransformState::_cache_update_pcollector("*:State Cache:Update");
 PStatCollector TransformState::_transform_compose_pcollector("*:State Cache:Compose Transform");
 PStatCollector TransformState::_transform_invert_pcollector("*:State Cache:Invert Transform");
+PStatCollector TransformState::_transform_calc_pcollector("*:State Cache:Calc Components");
+PStatCollector TransformState::_transform_break_cycles_pcollector("*:State Cache:Break Cycles");
+PStatCollector TransformState::_transform_new_pcollector("*:State Cache:New");
+PStatCollector TransformState::_transform_validate_pcollector("*:State Cache:Validate");
+PStatCollector TransformState::_transform_hash_pcollector("*:State Cache:Calc Hash");
 PStatCollector TransformState::_node_counter("TransformStates:On nodes");
 PStatCollector TransformState::_cache_counter("TransformStates:Cached");
 
@@ -783,6 +788,8 @@ unref() const {
         // cache, leaving only references in the cache, then we need to
         // check for a cycle involving this TransformState and break it if
         // it exists.
+
+        PStatTimer timer(_transform_break_cycles_pcollector);
         
         ++_last_cycle_detect;
         if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) {
@@ -931,7 +938,7 @@ get_num_states() {
 //               A nonzero return value is not necessarily indicative
 //               of leaked references; it is normal for two
 //               TransformState objects, both of which have references
-//               held outside the cache, to have to result of their
+//               held outside the cache, to have the result of their
 //               composition stored within the cache.  This result
 //               will be retained within the cache until one of the
 //               base TransformStates is released.
@@ -1200,6 +1207,8 @@ validate_states() {
     return true;
   }
 
+  PStatTimer timer(_transform_validate_pcollector);
+
   ReMutexHolder holder(*_states_lock);
   if (_states->empty()) {
     return true;
@@ -1276,6 +1285,8 @@ return_new(TransformState *state) {
   }
 #endif
 
+  PStatTimer timer(_transform_new_pcollector);
+
   ReMutexHolder holder(*_states_lock);
 
   // This should be a newly allocated pointer, not one that was used
@@ -1354,7 +1365,8 @@ do_compose(const TransformState *other) const {
 #ifndef NDEBUG
     if (paranoid_compose) {
       // Now verify against the matrix.
-      LMatrix4f new_mat = other->get_mat() * get_mat();
+      LMatrix4f new_mat;
+      new_mat.multiply(other->get_mat(), get_mat());
       if (!new_mat.almost_equal(result->get_mat(), 0.1)) {
         CPT(TransformState) correct = make_mat(new_mat);
         pgraph_cat.warning()
@@ -1374,7 +1386,8 @@ do_compose(const TransformState *other) const {
     LMatrix3f new_mat = other->get_mat3() * get_mat3();
     return make_mat3(new_mat);
   } else {
-    LMatrix4f new_mat = other->get_mat() * get_mat();
+    LMatrix4f new_mat;
+    new_mat.multiply(other->get_mat(), get_mat());
     return make_mat(new_mat);
   }
 }
@@ -1465,7 +1478,8 @@ do_invert_compose(const TransformState *other) const {
           << "Unexpected singular matrix found for " << *this << "\n";
       } else {
         nassertr(_inv_mat != (LMatrix4f *)NULL, make_invalid());
-        LMatrix4f new_mat = other->get_mat() * (*_inv_mat);
+        LMatrix4f new_mat;
+        new_mat.multiply(other->get_mat(), *_inv_mat);
         if (!new_mat.almost_equal(result->get_mat(), 0.1)) {
           CPT(TransformState) correct = make_mat(new_mat);
           pgraph_cat.warning()
@@ -1695,6 +1709,7 @@ remove_cache_pointers() {
 ////////////////////////////////////////////////////////////////////
 void TransformState::
 do_calc_hash() {
+  PStatTimer timer(_transform_hash_pcollector);
   _hash = 0;
 
   static const int significant_flags = 
@@ -1747,6 +1762,9 @@ calc_singular() {
     // Someone else computed it first.
     return;
   }
+
+  PStatTimer timer(_transform_calc_pcollector);
+
   nassertv((_flags & F_is_invalid) == 0);
 
   // We determine if a matrix is singular by attempting to invert it
@@ -1784,6 +1802,8 @@ do_calc_components() {
     return;
   }
 
+  PStatTimer timer(_transform_calc_pcollector);
+
   nassertv((_flags & F_is_invalid) == 0);
   if ((_flags & F_is_identity) != 0) {
     _scale.set(1.0f, 1.0f, 1.0f);
@@ -1832,6 +1852,8 @@ do_calc_hpr() {
     return;
   }
 
+  PStatTimer timer(_transform_calc_pcollector);
+
   nassertv((_flags & F_is_invalid) == 0);
   if ((_flags & F_components_known) == 0) {
     do_calc_components();
@@ -1858,6 +1880,8 @@ calc_quat() {
     return;
   }
 
+  PStatTimer timer(_transform_calc_pcollector);
+
   nassertv((_flags & F_is_invalid) == 0);
   if ((_flags & F_components_known) == 0) {
     do_calc_components();
@@ -1878,6 +1902,8 @@ calc_quat() {
 ////////////////////////////////////////////////////////////////////
 void TransformState::
 calc_norm_quat() {
+  PStatTimer timer(_transform_calc_pcollector);
+
   LQuaternionf quat = get_quat();
   MutexHolder holder(_lock);
   _norm_quat = quat;
@@ -1898,6 +1924,8 @@ do_calc_mat() {
     return;
   }
 
+  PStatTimer timer(_transform_calc_pcollector);
+
   nassertv((_flags & F_is_invalid) == 0);
   if ((_flags & F_is_identity) != 0) {
     _mat = LMatrix4f::ident_mat();

+ 5 - 0
panda/src/pgraph/transformState.h

@@ -261,6 +261,11 @@ private:
   static PStatCollector _cache_update_pcollector;
   static PStatCollector _transform_compose_pcollector;
   static PStatCollector _transform_invert_pcollector;
+  static PStatCollector _transform_calc_pcollector;
+  static PStatCollector _transform_break_cycles_pcollector;
+  static PStatCollector _transform_new_pcollector;
+  static PStatCollector _transform_validate_pcollector;
+  static PStatCollector _transform_hash_pcollector;
 
   static PStatCollector _node_counter;
   static PStatCollector _cache_counter;

+ 13 - 7
panda/src/pgui/pgItem.cxx

@@ -31,6 +31,7 @@
 #include "cullBinManager.h"
 #include "clipPlaneAttrib.h"
 #include "dcast.h"
+#include "boundingSphere.h"
 
 #ifdef HAVE_AUDIO
 #include "audioSound.h"
@@ -294,11 +295,13 @@ is_renderable() const {
 //               of substance should redefine this to do the right
 //               thing.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) PGItem::
-compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
+void PGItem::
+compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+                        Thread *current_thread) const {
+  int num_vertices = 0;
+
   // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage, current_thread);
-  nassertr(bound != (BoundingVolume *)NULL, bound);
+  PT(BoundingVolume) bound = new BoundingSphere;
 
   // Now actually compute the bounding volume by putting it around all
   // of our states' bounding volumes.
@@ -310,15 +313,18 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
   for (int i = 0; i < (int)_state_defs.size(); i++) {
     NodePath &root = ((PGItem *)this)->get_state_def(i);
     if (!root.is_empty()) {
-      child_volumes.push_back(root.node()->get_bounds());
+      PandaNode *node = root.node();
+      child_volumes.push_back(node->get_bounds(current_thread));
+      num_vertices += node->get_nested_vertices(current_thread);
     }
   }
 
   const BoundingVolume **child_begin = &child_volumes[0];
   const BoundingVolume **child_end = child_begin + child_volumes.size();
 
-  bound->around(child_begin, child_end);
-  return bound;
+  bdata->_internal_bounds = bound;
+  bdata->_internal_vertices = num_vertices;
+  bdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/pgui/pgItem.h

@@ -68,7 +68,8 @@ protected:
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool is_renderable() const;
 
-  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+                                       Thread *current_thread) const;
 
 public:
   virtual void xform(const LMatrix4f &mat);

+ 2 - 0
panda/src/pgui/pgTop.cxx

@@ -114,8 +114,10 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   // PGTop node, for the convenience of PGItems to register themselves
   // as they are drawn.
   PGCullTraverser pg_trav(this, trav);
+  pg_trav.local_object();
   pg_trav._sort_index = _start_sort;
   pg_trav.traverse_below(data);
+  pg_trav.end_traverse();
 
   // Now tell the watcher about the new set of regions.  Strictly
   // speaking, we shouldn't do this until the frame that we're about

+ 5 - 5
panda/src/testbed/pview.cxx

@@ -66,7 +66,7 @@ output_screenshot(Filename &fn)
 }
 
 void
-event_W(CPT_Event, void *) {
+event_W(const Event *, void *) {
   // shift-W: open a new window on the same scene.
 
   // If we already have a window, use the same GSG.
@@ -89,13 +89,13 @@ event_W(CPT_Event, void *) {
 }
 
 void
-event_F(CPT_Event, void *) {
+event_F(const Event *, void *) {
   // shift-F: flatten the model hierarchy.
   framework.get_models().flatten_strong();
 }
 
 void
-event_Enter(CPT_Event, void *) {
+event_Enter(const Event *, void *) {
   // alt-enter: toggle between window/fullscreen in the same scene.
 
   // If we already have a window, use the same GSG.
@@ -125,7 +125,7 @@ event_Enter(CPT_Event, void *) {
 }
 
 void
-event_2(CPT_Event event, void *) {
+event_2(const Event *event, void *) {
   // 2: split the window into two display regions.
 
   EventParameter param = event->get_parameter(0);
@@ -141,7 +141,7 @@ event_2(CPT_Event event, void *) {
 }
 
 void
-event_0(CPT_Event event, void *) {
+event_0(const Event *event, void *) {
   // 0: run hacky test.
 
   BamCache *cache = BamCache::get_global_ptr();

+ 2 - 4
panda/src/text/textAssembler.cxx

@@ -1316,8 +1316,7 @@ tack_on_accent(char accent_mark, TextAssembler::CheesyPosition position,
           break;
 
         case CT_rotate_90:
-          accent_mat =
-            LMatrix4f::rotate_mat_normaxis(90.0f, LVecBase3f(0.0f, -1.0f, 0.0f));
+          accent_mat.set_rotate_mat_normaxis(90.0f, LVecBase3f(0.0f, -1.0f, 0.0f));
           // rotate min, max
           t = min_accent[0];
           u = max_accent[0];
@@ -1339,8 +1338,7 @@ tack_on_accent(char accent_mark, TextAssembler::CheesyPosition position,
           break;
 
         case CT_rotate_270:
-          accent_mat =
-            LMatrix4f::rotate_mat_normaxis(270.0f, LVecBase3f(0.0f, -1.0f, 0.0f));
+          accent_mat.set_rotate_mat_normaxis(270.0f, LVecBase3f(0.0f, -1.0f, 0.0f));
           // rotate min, max
           t = min_accent[0];
           u = max_accent[0];

+ 11 - 6
panda/src/text/textNode.cxx

@@ -50,6 +50,7 @@
 #include "zStream.h"
 #include "pStatCollector.h"
 #include "pStatTimer.h"
+#include "boundingSphere.h"
 
 #include <stdio.h>
 
@@ -608,18 +609,20 @@ is_renderable() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: TextNode::compute_internal_bound
+//     Function: TextNode::compute_internal_bounds
 //       Access: Protected, Virtual
 //  Description: Called when needed to recompute the node's
 //               _internal_bound object.  Nodes that contain anything
 //               of substance should redefine this to do the right
 //               thing.
 ////////////////////////////////////////////////////////////////////
-PT(BoundingVolume) TextNode::
-compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
+void TextNode::
+compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+                        Thread *current_thread) const {
+  int num_vertices = 0;
+
   // First, get ourselves a fresh, empty bounding volume.
-  PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage, current_thread);
-  nassertr(bound != (BoundingVolume *)NULL, bound);
+  PT(BoundingVolume) bound = new BoundingSphere;
 
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
 
@@ -640,7 +643,9 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
 
   gbv->around(vertices, vertices + 8);
 
-  return bound;
+  bdata->_internal_bounds = bound;
+  bdata->_internal_vertices = 0;  // TODO: estimate this better.
+  bdata->_internal_bounds_stale = false;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 1
panda/src/text/textNode.h

@@ -243,7 +243,8 @@ public:
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool is_renderable() const;
 
-  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+                                       Thread *current_thread) const;
 
 private:
   INLINE void invalidate_no_measure();

+ 2 - 2
panda/src/tform/driveInterface.cxx

@@ -350,8 +350,8 @@ apply(double x, double y, bool any_button) {
 
   // rot_mat is the rotation matrix corresponding to our previous
   // heading.
-  LMatrix3f rot_mat =
-    LMatrix3f::rotate_mat_normaxis(_hpr[0], LVector3f::up());
+  LMatrix3f rot_mat;
+  rot_mat.set_rotate_mat_normaxis(_hpr[0], LVector3f::up());
 
   // Take a step in the direction of our previous heading.
   _vel = LVector3f::forward() * distance;