Browse Source

new callback system

David Rose 17 years ago
parent
commit
0c90c36fd7
78 changed files with 2664 additions and 481 deletions
  1. 6 0
      panda/src/display/Sources.pp
  2. 4 0
      panda/src/display/config_display.cxx
  3. 107 0
      panda/src/display/displayRegion.I
  4. 11 0
      panda/src/display/displayRegion.h
  5. 36 0
      panda/src/display/displayRegionCullCallbackData.I
  6. 66 0
      panda/src/display/displayRegionCullCallbackData.cxx
  7. 66 0
      panda/src/display/displayRegionCullCallbackData.h
  8. 37 0
      panda/src/display/displayRegionDrawCallbackData.I
  9. 88 0
      panda/src/display/displayRegionDrawCallbackData.cxx
  10. 66 0
      panda/src/display/displayRegionDrawCallbackData.h
  11. 2 0
      panda/src/display/display_composite1.cxx
  12. 0 36
      panda/src/display/graphicsEngine.I
  13. 98 164
      panda/src/display/graphicsEngine.cxx
  14. 4 34
      panda/src/display/graphicsEngine.h
  15. 39 5
      panda/src/display/graphicsStateGuardian.cxx
  16. 8 1
      panda/src/display/graphicsStateGuardian.h
  17. 17 9
      panda/src/distort/nonlinearImager.cxx
  18. 5 1
      panda/src/distort/nonlinearImager.h
  19. 14 0
      panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx
  20. 2 0
      panda/src/dxgsg8/dxGraphicsStateGuardian8.h
  21. 14 0
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  22. 2 0
      panda/src/dxgsg9/dxGraphicsStateGuardian9.h
  23. 15 0
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  24. 1 0
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  25. 2 0
      panda/src/gsgbase/graphicsStateGuardianBase.h
  26. 3 0
      panda/src/pgraph/Sources.pp
  27. 2 0
      panda/src/pgraph/config_pgraph.cxx
  28. 7 7
      panda/src/pgraph/cullBin.cxx
  29. 1 6
      panda/src/pgraph/cullHandler.I
  30. 0 47
      panda/src/pgraph/cullHandler.cxx
  31. 0 3
      panda/src/pgraph/cullHandler.h
  32. 5 5
      panda/src/pgraph/cullResult.cxx
  33. 1 0
      panda/src/pgraph/cullResult.h
  34. 19 19
      panda/src/pgraph/cullTraverser.I
  35. 19 81
      panda/src/pgraph/cullTraverser.cxx
  36. 0 1
      panda/src/pgraph/cullTraverser.h
  37. 4 4
      panda/src/pgraph/cullTraverserData.I
  38. 4 4
      panda/src/pgraph/cullTraverserData.cxx
  39. 5 0
      panda/src/pgraph/cullTraverserData.h
  40. 109 14
      panda/src/pgraph/cullableObject.I
  41. 99 9
      panda/src/pgraph/cullableObject.cxx
  42. 25 6
      panda/src/pgraph/cullableObject.h
  43. 92 0
      panda/src/pgraph/geomDrawCallbackData.I
  44. 55 0
      panda/src/pgraph/geomDrawCallbackData.cxx
  45. 70 0
      panda/src/pgraph/geomDrawCallbackData.h
  46. 77 0
      panda/src/pgraph/geomNode.cxx
  47. 1 0
      panda/src/pgraph/geomNode.h
  48. 12 0
      panda/src/pgraph/pandaNode.cxx
  49. 1 0
      panda/src/pgraph/pandaNode.h
  50. 1 0
      panda/src/pgraph/pgraph_composite2.cxx
  51. 18 18
      panda/src/pgraph/sceneSetup.I
  52. 1 0
      panda/src/pgraph/sceneSetup.h
  53. 6 0
      panda/src/pgraphnodes/Sources.pp
  54. 126 0
      panda/src/pgraphnodes/callbackNode.I
  55. 262 0
      panda/src/pgraphnodes/callbackNode.cxx
  56. 98 0
      panda/src/pgraphnodes/callbackNode.h
  57. 9 0
      panda/src/pgraphnodes/config_pgraphnodes.cxx
  58. 52 0
      panda/src/pgraphnodes/nodeCullCallbackData.I
  59. 70 0
      panda/src/pgraphnodes/nodeCullCallbackData.cxx
  60. 65 0
      panda/src/pgraphnodes/nodeCullCallbackData.h
  61. 1 1
      panda/src/pgraphnodes/pgraphnodes_composite1.cxx
  62. 2 0
      panda/src/pgraphnodes/pgraphnodes_composite2.cxx
  63. 16 2
      panda/src/putil/Sources.pp
  64. 26 0
      panda/src/putil/cPointerCallbackObject.I
  65. 31 0
      panda/src/putil/cPointerCallbackObject.cxx
  66. 60 0
      panda/src/putil/cPointerCallbackObject.h
  67. 23 0
      panda/src/putil/callbackData.I
  68. 40 0
      panda/src/putil/callbackData.cxx
  69. 66 0
      panda/src/putil/callbackData.h
  70. 23 0
      panda/src/putil/callbackObject.I
  71. 40 0
      panda/src/putil/callbackObject.cxx
  72. 69 0
      panda/src/putil/callbackObject.h
  73. 9 1
      panda/src/putil/config_util.cxx
  74. 3 3
      panda/src/putil/putil_composite1.cxx
  75. 4 0
      panda/src/putil/putil_composite2.cxx
  76. 14 0
      panda/src/putil/pythonCallbackObject.I
  77. 139 0
      panda/src/putil/pythonCallbackObject.cxx
  78. 69 0
      panda/src/putil/pythonCallbackObject.h

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

@@ -15,6 +15,8 @@
     config_display.h \
     drawableRegion.I drawableRegion.h \
     displayRegion.I displayRegion.h  \
+    displayRegionCullCallbackData.I displayRegionCullCallbackData.h \
+    displayRegionDrawCallbackData.I displayRegionDrawCallbackData.h \
     frameBufferProperties.I frameBufferProperties.h \
     graphicsEngine.I graphicsEngine.h \
     graphicsOutput.I graphicsOutput.h \
@@ -42,6 +44,8 @@
     config_display.cxx \
     drawableRegion.cxx \
     displayRegion.cxx \
+    displayRegionCullCallbackData.cxx \
+    displayRegionDrawCallbackData.cxx \
     frameBufferProperties.cxx \
     graphicsEngine.cxx \
     graphicsOutput.cxx \
@@ -65,6 +69,8 @@
     config_display.h \
     drawableRegion.I drawableRegion.h \
     displayRegion.I displayRegion.h \
+    displayRegionCullCallbackData.I displayRegionCullCallbackData.h \
+    displayRegionDrawCallbackData.I displayRegionDrawCallbackData.h \
     frameBufferProperties.I frameBufferProperties.h \
     graphicsEngine.I graphicsEngine.h \
     graphicsOutput.I graphicsOutput.h \

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

@@ -15,6 +15,8 @@
 
 #include "config_display.h"
 #include "displayRegion.h"
+#include "displayRegionCullCallbackData.h"
+#include "displayRegionDrawCallbackData.h"
 #include "standardMunger.h"
 #include "graphicsStateGuardian.h"
 #include "graphicsPipe.h"
@@ -369,6 +371,8 @@ init_libdisplay() {
   initialized = true;
 
   DisplayRegion::init_type();
+  DisplayRegionCullCallbackData::init_type();
+  DisplayRegionDrawCallbackData::init_type();
   DisplayRegionPipelineReader::init_type();
   GraphicsBuffer::init_type();
   GraphicsDevice::init_type();

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

@@ -192,6 +192,103 @@ get_cube_map_index() const {
   return cdata->_cube_map_index;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::set_cull_callback
+//       Access: Published
+//  Description: Sets the CallbackObject that will be notified when
+//               the DisplayRegion is visited during the cull
+//               traversal.  This callback will be made during the
+//               cull thread.
+//
+//               The cull traversal is responsible for determining
+//               which nodes are visible and within the view frustum,
+//               and for accumulating state and transform, and
+//               generally building up the list of CullableObjects
+//               that are to be eventually passed to the draw
+//               traversal for rendering.
+//
+//               At the time the cull traversal callback is made, the
+//               traversal for this DisplayRegion has not yet started.
+//
+//               The callback is passed an instance of a
+//               DisplayRegionCullCallbackData, which contains
+//               pointers to the current scene information, as well as
+//               the current DisplayRegion and GSG.  The callback
+//               *replaces* the normal cull behavior, so if your
+//               callback does nothing, the scene graph will not be
+//               traversed and therefore nothing will be drawn.  If
+//               you wish the normal cull traversal to be performed
+//               for this DisplayRegion, you must call
+//               cbdata->upcall() from your callback.
+////////////////////////////////////////////////////////////////////
+INLINE void DisplayRegion::
+set_cull_callback(CallbackObject *object) {
+  CDWriter cdata(_cycler);
+  cdata->_cull_callback = object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::get_cull_callback
+//       Access: Published
+//  Description: Returns the CallbackObject set by set_cull_callback().
+////////////////////////////////////////////////////////////////////
+INLINE CallbackObject *DisplayRegion::
+get_cull_callback() const {
+  CDReader cdata(_cycler);
+  return cdata->_cull_callback;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::set_draw_callback
+//       Access: Published
+//  Description: Sets the CallbackObject that will be notified when
+//               the contents of DisplayRegion is drawn during the
+//               draw traversal.  This callback will be made during
+//               the draw thread.
+//
+//               The draw traversal is responsible for actually
+//               issuing the commands to the graphics engine to draw
+//               primitives.  Its job is to walk through the list of
+//               CullableObjects build up by the cull traversal, as
+//               quickly as possible, issuing the appropriate commands
+//               to draw each one.
+//
+//               At the time the draw traversal callback is made, the
+//               graphics state is in the initial state, and no
+//               projection matrix or modelview matrix is in effect.
+//               begin_scene() has not yet been called, and no objects
+//               have yet been drawn.  However, the viewport has
+//               already been set to the appropriate part of the
+//               window, and the clear commands for this DisplayRegion
+//               (if any) have been issued.
+//
+//               The callback is passed an instance of a
+//               DisplayRegionDrawCallbackData, which contains
+//               pointers to the current scene information, as well as
+//               the current DisplayRegion and GSG.  The callback
+//               *replaces* the normal draw behavior, so if your
+//               callback does nothing, nothing in the DisplayRegion
+//               will be drawn.  If you wish the draw traversal to
+//               continue to draw the contents of this DisplayRegion,
+//               you must call cbdata->upcall() from your callback.
+////////////////////////////////////////////////////////////////////
+INLINE void DisplayRegion::
+set_draw_callback(CallbackObject *object) {
+  CDWriter cdata(_cycler);
+  cdata->_draw_callback = object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::get_draw_callback
+//       Access: Published
+//  Description: Returns the CallbackObject set by set_draw_callback().
+////////////////////////////////////////////////////////////////////
+INLINE CallbackObject *DisplayRegion::
+get_draw_callback() const {
+  CDReader cdata(_cycler);
+  return cdata->_draw_callback;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DisplayRegion::get_pixel_width
 //       Access: Published
@@ -579,6 +676,16 @@ get_cube_map_index() const {
   return _cdata->_cube_map_index;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionPipelineReader::get_draw_callback
+//       Access: Published
+//  Description: Returns the CallbackObject set by set_draw_callback().
+////////////////////////////////////////////////////////////////////
+INLINE CallbackObject *DisplayRegionPipelineReader::
+get_draw_callback() const {
+  return _cdata->_draw_callback;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DisplayRegionPipelineReader::get_pixels
 //       Access: Public

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

@@ -36,6 +36,7 @@
 #include "plist.h"
 #include "pStatCollector.h"
 #include "cullTraverser.h"
+#include "callbackObject.h"
 
 class GraphicsOutput;
 class GraphicsPipe;
@@ -106,6 +107,12 @@ PUBLISHED:
   virtual void set_cube_map_index(int cube_map_index);
   INLINE int get_cube_map_index() const;
 
+  INLINE void set_cull_callback(CallbackObject *object);
+  INLINE CallbackObject *get_cull_callback() const;
+
+  INLINE void set_draw_callback(CallbackObject *object);
+  INLINE CallbackObject *get_draw_callback() const;
+
   INLINE int get_pixel_width() const;
   INLINE int get_pixel_height() const;
 
@@ -191,6 +198,9 @@ private:
     int _sort;
     Lens::StereoChannel _stereo_channel;
     int _cube_map_index;
+
+    PT(CallbackObject) _cull_callback;
+    PT(CallbackObject) _draw_callback;
   };
 
   PipelineCycler<CData> _cycler;
@@ -282,6 +292,7 @@ public:
   INLINE Lens::StereoChannel get_stereo_channel();
   INLINE bool get_clear_depth_between_eyes() const;
   INLINE int get_cube_map_index() const;
+  INLINE CallbackObject *get_draw_callback() const;
 
   INLINE void get_pixels(int &pl, int &pr, int &pb, int &pt) const;
   INLINE void get_region_pixels(int &xo, int &yo, int &w, int &h) const;

+ 36 - 0
panda/src/display/displayRegionCullCallbackData.I

@@ -0,0 +1,36 @@
+// Filename: displayRegionCullCallbackData.I
+// Created by:  drose (14Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionCullCallbackData::get_cull_handler
+//       Access: Published
+//  Description: Returns a pointer to the CullHandler, which accepts
+//               each object to be added to the list for drawing.
+////////////////////////////////////////////////////////////////////
+INLINE CullHandler *DisplayRegionCullCallbackData::
+get_cull_handler() const {
+  return _cull_handler;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionCullCallbackData::get_scene_setup
+//       Access: Published
+//  Description: Returns a pointer to the SceneSetup object, which
+//               contains information about the camera and such.
+////////////////////////////////////////////////////////////////////
+INLINE SceneSetup *DisplayRegionCullCallbackData::
+get_scene_setup() const {
+  return _scene_setup;
+}

+ 66 - 0
panda/src/display/displayRegionCullCallbackData.cxx

@@ -0,0 +1,66 @@
+// Filename: displayRegionCullCallbackData.cxx
+// Created by:  drose (14Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "displayRegionCullCallbackData.h"
+#include "cullHandler.h"
+#include "sceneSetup.h"
+#include "graphicsEngine.h"
+
+TypeHandle DisplayRegionCullCallbackData::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionCullCallbackData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DisplayRegionCullCallbackData::
+DisplayRegionCullCallbackData(CullHandler *cull_handler, SceneSetup *scene_setup) :
+  _cull_handler(cull_handler),
+  _scene_setup(scene_setup)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionCullCallbackData::output
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DisplayRegionCullCallbackData::
+output(ostream &out) const {
+  out << get_type() << "(" << (void *)_cull_handler << ", " 
+      << (void *)_scene_setup << ")";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionCullCallbackData::upcall
+//       Access: Published, Virtual
+//  Description: You should make this call during the callback if you
+//               want to continue the normal rendering function that
+//               would have been done in the absence of a callback.
+//
+//               Specifically, this method will perform the cull
+//               traversal for the DisplayRegion's scene graph, and
+//               add all renderable objects to its CullResult.
+////////////////////////////////////////////////////////////////////
+void DisplayRegionCullCallbackData::
+upcall() {
+  Thread *current_thread = Thread::get_current_thread();
+  DisplayRegion *dr = _scene_setup->get_display_region();
+  GraphicsStateGuardian *gsg = dr->get_window()->get_gsg();
+
+  GraphicsEngine::do_cull(_cull_handler, _scene_setup,
+                          gsg, current_thread);
+}
+

+ 66 - 0
panda/src/display/displayRegionCullCallbackData.h

@@ -0,0 +1,66 @@
+// Filename: displayRegionCullCallbackData.h
+// Created by:  drose (14Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DISPLAYREGIONCULLCALLBACKDATA_H
+#define DISPLAYREGIONCULLCALLBACKDATA_H
+
+#include "pandabase.h"
+#include "callbackData.h"
+
+class CullHandler;
+class SceneSetup;
+
+////////////////////////////////////////////////////////////////////
+//       Class : DisplayRegionCullCallbackData
+// Description : This specialization on CallbackData is passed when
+//               the callback is initiated from the cull traversal,
+//               for a DisplayRegion.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_PGRAPH DisplayRegionCullCallbackData : public CallbackData {
+public:
+  DisplayRegionCullCallbackData(CullHandler *cull_handler, SceneSetup *scene_setup);
+
+PUBLISHED:
+  virtual void output(ostream &out) const;
+
+  INLINE CullHandler *get_cull_handler() const;
+  INLINE SceneSetup *get_scene_setup() const;
+
+  virtual void upcall();
+
+private:
+  CullHandler *_cull_handler;
+  SceneSetup *_scene_setup;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CallbackData::init_type();
+    register_type(_type_handle, "DisplayRegionCullCallbackData",
+                  CallbackData::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 "displayRegionCullCallbackData.I"
+
+#endif

+ 37 - 0
panda/src/display/displayRegionDrawCallbackData.I

@@ -0,0 +1,37 @@
+// Filename: displayRegionDrawCallbackData.I
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionDrawCallbackData::get_cull_result
+//       Access: Published
+//  Description: Returns a pointer to the CullResult, the list of
+//               CullableObjects that should be drawn in this
+//               DisplayRegion.
+////////////////////////////////////////////////////////////////////
+INLINE CullResult *DisplayRegionDrawCallbackData::
+get_cull_result() const {
+  return _cull_result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionDrawCallbackData::get_scene_setup
+//       Access: Published
+//  Description: Returns a pointer to the SceneSetup object, which
+//               contains information about the camera and such.
+////////////////////////////////////////////////////////////////////
+INLINE SceneSetup *DisplayRegionDrawCallbackData::
+get_scene_setup() const {
+  return _scene_setup;
+}

+ 88 - 0
panda/src/display/displayRegionDrawCallbackData.cxx

@@ -0,0 +1,88 @@
+// Filename: displayRegionDrawCallbackData.cxx
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "displayRegionDrawCallbackData.h"
+#include "cullResult.h"
+#include "sceneSetup.h"
+
+TypeHandle DisplayRegionDrawCallbackData::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionDrawCallbackData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DisplayRegionDrawCallbackData::
+DisplayRegionDrawCallbackData(CullResult *cull_result, SceneSetup *scene_setup) :
+  _cull_result(cull_result),
+  _scene_setup(scene_setup)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionDrawCallbackData::output
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DisplayRegionDrawCallbackData::
+output(ostream &out) const {
+  out << get_type() << "(" << (void *)_cull_result << ", " 
+      << (void *)_scene_setup << ")";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegionDrawCallbackData::upcall
+//       Access: Published, Virtual
+//  Description: You should make this call during the callback if you
+//               want to continue the normal rendering function that
+//               would have been done in the absence of a callback.
+//
+//               Specifically, this method will draw all of the
+//               objects in the CullResult list that have been built
+//               up for the DisplayRegion during the cull traversal.
+////////////////////////////////////////////////////////////////////
+void DisplayRegionDrawCallbackData::
+upcall() {
+  Thread *current_thread = Thread::get_current_thread();
+  DisplayRegion *dr = _scene_setup->get_display_region();
+  GraphicsStateGuardian *gsg = dr->get_window()->get_gsg();
+
+  if (_cull_result == NULL || _scene_setup == NULL) {
+    // Nothing to see here.
+
+  } else if (dr->is_stereo()) {
+    // We don't actually draw the stereo DisplayRegions.  These are
+    // just placeholders; we draw the individual left and right eyes
+    // instead.  (We might still clear the stereo DisplayRegions,
+    // though, since it's probably faster to clear right and left
+    // channels in one pass, than to clear them in two separate
+    // passes.)
+
+  } else if (!gsg->set_scene(_scene_setup)) {
+    // The scene or lens is inappropriate somehow.
+    display_cat.error()
+      << gsg->get_type() << " cannot render scene with specified lens.\n";
+
+  } else {
+    // Tell the GSG to forget its state.
+    gsg->clear_state_and_transform();
+
+    if (gsg->begin_scene()) {
+      _cull_result->draw(current_thread);
+      gsg->end_scene();
+    }
+  }
+}
+

+ 66 - 0
panda/src/display/displayRegionDrawCallbackData.h

@@ -0,0 +1,66 @@
+// Filename: displayRegionDrawCallbackData.h
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DISPLAYREGIONDRAWCALLBACKDATA_H
+#define DISPLAYREGIONDRAWCALLBACKDATA_H
+
+#include "pandabase.h"
+#include "callbackData.h"
+
+class CullResult;
+class SceneSetup;
+
+////////////////////////////////////////////////////////////////////
+//       Class : DisplayRegionDrawCallbackData
+// Description : This specialization on CallbackData is passed when
+//               the callback is initiated from the draw traversal,
+//               for a DisplayRegion.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_PGRAPH DisplayRegionDrawCallbackData : public CallbackData {
+public:
+  DisplayRegionDrawCallbackData(CullResult *cull_result, SceneSetup *scene_setup);
+
+PUBLISHED:
+  virtual void output(ostream &out) const;
+
+  INLINE CullResult *get_cull_result() const;
+  INLINE SceneSetup *get_scene_setup() const;
+
+  virtual void upcall();
+
+private:
+  CullResult *_cull_result;
+  SceneSetup *_scene_setup;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CallbackData::init_type();
+    register_type(_type_handle, "DisplayRegionDrawCallbackData",
+                  CallbackData::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 "displayRegionDrawCallbackData.I"
+
+#endif

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

@@ -1,6 +1,8 @@
 #include "standardMunger.cxx"
 #include "drawableRegion.cxx"
 #include "displayRegion.cxx"
+#include "displayRegionCullCallbackData.cxx"
+#include "displayRegionDrawCallbackData.cxx"
 #include "graphicsEngine.cxx"
 #include "graphicsPipe.cxx"
 #include "graphicsStateGuardian.cxx"

+ 0 - 36
panda/src/display/graphicsEngine.I

@@ -151,39 +151,3 @@ make_parasite(GraphicsOutput *host, const string &name,
                                        host->get_gsg(), host);
   return result;
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::Callback::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE GraphicsEngine::Callback::
-Callback(CallbackFunction *func, void *data) :
-  _func(func),
-  _data(data)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::Callback::operator <
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE bool GraphicsEngine::Callback::
-operator < (const GraphicsEngine::Callback &other) const {
-  if (_func != other._func) {
-    return _func < other._func;
-  }
-  return _data < other._data;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::Callback::do_callback
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsEngine::Callback::
-do_callback() const {
-  (*_func)(_data);
-}
-

+ 98 - 164
panda/src/display/graphicsEngine.cxx

@@ -43,6 +43,8 @@
 #include "vertexDataBook.h"
 #include "vertexDataPage.h"
 #include "config_pgraph.h"
+#include "displayRegionCullCallbackData.h"
+#include "displayRegionDrawCallbackData.h"
 
 #if defined(WIN32)
   #define WINDOWS_LEAN_AND_MEAN
@@ -1017,53 +1019,6 @@ get_global_ptr() {
   }
   return _global_ptr;
 }
- 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::add_callback
-//       Access: Public
-//  Description: Adds the indicated C/C++ function and an arbitrary
-//               associated data pointer to the list of functions that
-//               will be called in the indicated thread at the
-//               indicated point of the frame.
-//
-//               The thread name may be one of the cull or draw names
-//               specified in set_threading_model(), or it may be the
-//               empty string to indicated the app or main thread.
-//
-//               The return value is true if the function and data
-//               pointer are successfully added to the appropriate
-//               callback list, or false if the same function and data
-//               pointer were already there.
-////////////////////////////////////////////////////////////////////
-bool GraphicsEngine::
-add_callback(const string &thread_name, 
-             GraphicsEngine::CallbackTime callback_time,
-             GraphicsEngine::CallbackFunction *func, void *data) {
-  LightReMutexHolder holder(_lock);
-  WindowRenderer *wr = get_window_renderer(thread_name, 0);
-  return wr->add_callback(callback_time, Callback(func, data));
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::remove_callback
-//       Access: Public
-//  Description: Removes a callback added by a previous call to
-//               add_callback().  All parameters must match the same
-//               parameters passed to add_callback().  The return
-//               value is true if the callback is successfully
-//               removed, or false if it was not on the list (either
-//               one or more of the parameters did not match the call
-//               to add_callback(), or the callback has already been
-//               removed).
-////////////////////////////////////////////////////////////////////
-bool GraphicsEngine::
-remove_callback(const string &thread_name,
-                GraphicsEngine::CallbackTime callback_time,
-                GraphicsEngine::CallbackFunction *func, void *data) {
-  LightReMutexHolder holder(_lock);
-  WindowRenderer *wr = get_window_renderer(thread_name, 0);
-  return wr->remove_callback(callback_time, Callback(func, data));
-}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::texture_uploaded
@@ -1090,6 +1045,49 @@ texture_uploaded(Texture *tex) {
   lt._image_modified = tex->get_image_modified();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::do_cull
+//       Access: Public, Static
+//  Description: Fires off a cull traversal using the indicated camera.
+////////////////////////////////////////////////////////////////////
+void GraphicsEngine::
+do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
+        GraphicsStateGuardian *gsg, Thread *current_thread) {
+  DisplayRegion *dr = scene_setup->get_display_region();
+  PStatTimer timer(dr->get_cull_region_pcollector(), current_thread);
+
+  CullTraverser *trav = dr->get_cull_traverser();
+  trav->set_cull_handler(cull_handler);
+  trav->set_scene(scene_setup, gsg, dr->get_incomplete_render());
+
+  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.
+
+    // First, we have to get the current viewing frustum, which comes
+    // from the lens.
+    PT(BoundingVolume) bv = scene_setup->get_lens()->make_bounds();
+
+    if (bv != (BoundingVolume *)NULL &&
+        bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
+      // Transform it into the appropriate coordinate space.
+      PT(GeometricBoundingVolume) local_frustum;
+      local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy());
+
+      NodePath scene_parent = scene_setup->get_scene_root().get_parent(current_thread);
+      CPT(TransformState) cull_center_transform = 
+        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->traverse(scene_setup->get_scene_root());
+  trav->end_traverse();
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::scene_root_func
@@ -1265,7 +1263,20 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr,
     if (gsg->begin_scene()) {
       delete dr_reader;
       dr_reader = NULL;
-      do_cull(&cull_handler, scene_setup, gsg, current_thread);
+
+      CallbackObject *cbobj = dr->get_cull_callback();
+      if (cbobj != (CallbackObject *)NULL) {
+        // Issue the cull callback on this DisplayRegion.
+        DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup);
+        cbobj->do_callback(&cbdata);
+        
+        // The callback has taken care of the culling.
+        
+      } else {
+        // Perform the cull normally.
+        do_cull(&cull_handler, scene_setup, gsg, current_thread);
+      }
+
       gsg->end_scene();
     }
   }
@@ -1369,7 +1380,18 @@ cull_to_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) {
 
   if (scene_setup != (SceneSetup *)NULL) {
     BinCullHandler cull_handler(cull_result);
-    do_cull(&cull_handler, scene_setup, gsg, current_thread);
+    CallbackObject *cbobj = dr->get_cull_callback();
+    if (cbobj != (CallbackObject *)NULL) {
+      // Issue the cull callback on this DisplayRegion.
+      DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup);
+      cbobj->do_callback(&cbdata);
+
+      // The callback has taken care of the culling.
+
+    } else {
+      // Perform the cull normally.
+      do_cull(&cull_handler, scene_setup, gsg, current_thread);
+    }
 
     PStatTimer timer(_cull_sort_pcollector, current_thread);
     cull_result->finish_cull(scene_setup, current_thread);
@@ -1712,49 +1734,6 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
   return scene_setup;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::do_cull
-//       Access: Private
-//  Description: Fires off a cull traversal using the indicated camera.
-////////////////////////////////////////////////////////////////////
-void GraphicsEngine::
-do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
-        GraphicsStateGuardian *gsg, Thread *current_thread) {
-  DisplayRegion *dr = scene_setup->get_display_region();
-  PStatTimer timer(dr->get_cull_region_pcollector(), current_thread);
-
-  CullTraverser *trav = dr->get_cull_traverser();
-  trav->set_cull_handler(cull_handler);
-  trav->set_scene(scene_setup, gsg, dr->get_incomplete_render());
-
-  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.
-
-    // First, we have to get the current viewing frustum, which comes
-    // from the lens.
-    PT(BoundingVolume) bv = scene_setup->get_lens()->make_bounds();
-
-    if (bv != (BoundingVolume *)NULL &&
-        bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
-      // Transform it into the appropriate coordinate space.
-      PT(GeometricBoundingVolume) local_frustum;
-      local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy());
-
-      NodePath scene_parent = scene_setup->get_scene_root().get_parent(current_thread);
-      CPT(TransformState) cull_center_transform = 
-        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->traverse(scene_setup->get_scene_root());
-  trav->end_traverse();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::do_draw
 //       Access: Private
@@ -1766,21 +1745,40 @@ do_draw(CullResult *cull_result, SceneSetup *scene_setup,
   // Statistics
   PStatTimer timer(dr->get_draw_region_pcollector(), current_thread);
 
-  DisplayRegionPipelineReader *dr_reader = 
-    new DisplayRegionPipelineReader(dr, current_thread);
-
   GraphicsStateGuardian *gsg = win->get_gsg();
-  win->change_scenes(dr_reader);
+  CallbackObject *cbobj;
 
-  gsg->prepare_display_region(dr_reader, dr_reader->get_stereo_channel());
-  if (dr_reader->is_any_clear_active()) {
-    gsg->clear(dr_reader->get_object());
+  {
+    DisplayRegionPipelineReader dr_reader(dr, current_thread);
+    win->change_scenes(&dr_reader);
+    gsg->prepare_display_region(&dr_reader, dr_reader.get_stereo_channel());
+    if (dr_reader.is_any_clear_active()) {
+      gsg->clear(dr_reader.get_object());
+    }
+
+    cbobj = dr_reader.get_draw_callback();
+  }
+
+  if (cbobj != (CallbackObject *)NULL) {
+    // Issue the draw callback on this DisplayRegion.
+
+    // Set the GSG to the initial state.
+    gsg->set_state_and_transform(RenderState::make_empty(), TransformState::make_identity());
+
+    DisplayRegionDrawCallbackData cbdata(cull_result, scene_setup);
+    cbobj->do_callback(&cbdata);
+
+    // We don't trust the state the callback may have left us in.
+    gsg->clear_state_and_transform();
+
+    // The callback has taken care of the drawing.
+    return;
   }
 
   if (cull_result == NULL || scene_setup == NULL) {
     // Nothing to see here.
 
-  } else if (dr_reader->get_object()->is_stereo()) {
+  } else if (dr->is_stereo()) {
     // We don't actually draw the stereo DisplayRegions.  These are
     // just placeholders; we draw the individual left and right eyes
     // instead.  (We might still clear the stereo DisplayRegions,
@@ -1795,16 +1793,10 @@ do_draw(CullResult *cull_result, SceneSetup *scene_setup,
 
   } else {
     if (gsg->begin_scene()) {
-      delete dr_reader;
-      dr_reader = NULL;
       cull_result->draw(current_thread);
       gsg->end_scene();
     }
   }
-
-  if (dr_reader != (DisplayRegionPipelineReader *)NULL) {
-    delete dr_reader;
-  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2369,8 +2361,6 @@ do_frame(GraphicsEngine *engine, Thread *current_thread) {
   PStatTimer timer(engine->_do_frame_pcollector, current_thread);
   LightReMutexHolder holder(_wl_lock);
 
-  do_callbacks(CB_pre_frame);
-
   engine->cull_to_bins(_cull, current_thread);
   engine->cull_and_draw_together(_cdraw, current_thread);
   engine->draw_bins(_draw, current_thread);
@@ -2395,8 +2385,6 @@ do_frame(GraphicsEngine *engine, Thread *current_thread) {
 
     _gsgs.swap(new_gsgs);
   }
-
-  do_callbacks(CB_post_frame);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2509,60 +2497,6 @@ any_done_gsgs() const {
   return false;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::WindowRenderer::add_callback
-//       Access: Public
-//  Description: Adds the indicated callback to this renderer's list
-//               for the indicated callback time.  Returns true if it
-//               is successfully added, false if it was already there.
-////////////////////////////////////////////////////////////////////
-bool GraphicsEngine::WindowRenderer::
-add_callback(GraphicsEngine::CallbackTime callback_time, 
-             const GraphicsEngine::Callback &callback) {
-  nassertr(callback_time >= 0 && callback_time < CB_len, false);
-  LightReMutexHolder holder(_wl_lock);
-  return _callbacks[callback_time].insert(callback).second;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::WindowRenderer::remove_callback
-//       Access: Public
-//  Description: Removes the indicated callback from this renderer's
-//               list for the indicated callback time.  Returns true
-//               if it is successfully removed, or false if it was not
-//               on the list.
-////////////////////////////////////////////////////////////////////
-bool GraphicsEngine::WindowRenderer::
-remove_callback(GraphicsEngine::CallbackTime callback_time, 
-                const GraphicsEngine::Callback &callback) {
-  nassertr(callback_time >= 0 && callback_time < CB_len, false);
-  LightReMutexHolder holder(_wl_lock);
-  Callbacks::iterator cbi = _callbacks[callback_time].find(callback);
-  if (cbi != _callbacks[callback_time].end()) {
-    _callbacks[callback_time].erase(cbi);
-    return true;
-  }
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::WindowRenderer::do_callbacks
-//       Access: Private
-//  Description: Calls all of the callback functions on the indicated
-//               list.  Intended to be called internally when we have
-//               reached the indicated point on the frame.
-////////////////////////////////////////////////////////////////////
-void GraphicsEngine::WindowRenderer::
-do_callbacks(GraphicsEngine::CallbackTime callback_time) {
-  nassertv(callback_time >= 0 && callback_time < CB_len);
-  Callbacks::const_iterator cbi;
-  for (cbi = _callbacks[callback_time].begin();
-       cbi != _callbacks[callback_time].end();
-       ++cbi) {
-    (*cbi).do_callback();
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::RenderThread::Constructor
 //       Access: Public

+ 4 - 34
panda/src/display/graphicsEngine.h

@@ -115,36 +115,15 @@ public:
     TS_done
   };
 
-  enum CallbackTime {
-    CB_pre_frame,
-    CB_post_frame,
-    CB_len  // Not an option; just indicates the size of the list.
-  };
-
-  typedef void CallbackFunction(void *data);
-
-  bool add_callback(const string &thread_name, CallbackTime callback_time,
-                    CallbackFunction *func, void *data);
-  bool remove_callback(const string &thread_name, CallbackTime callback_time,
-                       CallbackFunction *func, void *data);
-
   void texture_uploaded(Texture *tex);
+
+public:
+  static void do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
+                      GraphicsStateGuardian *gsg, Thread *current_thread);
   
 private:
-  class Callback {
-  public:
-    INLINE Callback(CallbackFunction *func, void *data);
-    INLINE bool operator < (const Callback &other) const;
-    INLINE void do_callback() const;
-
-  private:
-    CallbackFunction *_func;
-    void *_data;
-  };
-
   typedef ov_set< PT(GraphicsOutput), IndirectLess<GraphicsOutput> > Windows;
   typedef pset< PT(GraphicsStateGuardian) > GSGs;
-  typedef pset< Callback > Callbacks;
 
   static bool scene_root_func(const PandaNode *node);
   bool is_scene_root(const PandaNode *node);
@@ -169,8 +148,6 @@ private:
 
   PT(SceneSetup) setup_scene(GraphicsStateGuardian *gsg, 
                              DisplayRegionPipelineReader *dr);
-  void do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
-               GraphicsStateGuardian *gsg, Thread *current_thread);
   void do_draw(CullResult *cull_result, SceneSetup *scene_setup,
                GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread);
 
@@ -287,12 +264,6 @@ private:
     void do_pending(GraphicsEngine *engine, Thread *current_thread);
     bool any_done_gsgs() const;
 
-    bool add_callback(CallbackTime callback_time, const Callback &callback);
-    bool remove_callback(CallbackTime callback_time, const Callback &callback);
-
-  private:
-    void do_callbacks(CallbackTime callback_time);
-
   public:
     Windows _cull;    // cull stage
     Windows _cdraw;   // cull-and-draw-together stage
@@ -304,7 +275,6 @@ private:
 
     GSGs _gsgs;       // draw stage
 
-    Callbacks _callbacks[CB_len];
     LightReMutex _wl_lock;
   };
 

+ 39 - 5
panda/src/display/graphicsStateGuardian.cxx

@@ -508,11 +508,12 @@ get_flash_texture() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::set_scene
-//       Access: Public
+//       Access: Published
 //  Description: Sets the SceneSetup object that indicates the initial
 //               camera position, etc.  This must be called before
 //               traversal begins.  Returns true if the scene is
-//               acceptable, false if something's wrong.
+//               acceptable, false if something's wrong.  This should
+//               be called in the draw thread only.
 ////////////////////////////////////////////////////////////////////
 bool GraphicsStateGuardian::
 set_scene(SceneSetup *scene_setup) {
@@ -532,7 +533,7 @@ set_scene(SceneSetup *scene_setup) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_scene
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Returns the current SceneSetup object.
 ////////////////////////////////////////////////////////////////////
 SceneSetup *GraphicsStateGuardian::
@@ -1238,6 +1239,26 @@ prepare_display_region(DisplayRegionPipelineReader *dr,
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::clear_state_and_transform
+//       Access: Public, Virtual
+//  Description: Forgets the current graphics state and current
+//               transform, so that the next call to
+//               set_state_and_transform() will have to reload
+//               everything.  This is a good thing to call when you
+//               are no longer sure what the graphics state is.  This
+//               should only be called from the draw thread.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+clear_state_and_transform() {
+  // Re-issue the modelview and projection transforms.
+  reissue_transforms();
+
+  // Now clear the state flags to unknown.
+  _state_rs = RenderState::make_empty();
+  _state_mask.clear();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::prepare_lens
 //       Access: Public, Virtual
@@ -1306,12 +1327,13 @@ begin_frame(Thread *current_thread) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::begin_scene
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Called between begin_frame() and end_frame() to mark
 //               the beginning of drawing commands for a "scene"
 //               (usually a particular DisplayRegion) within a frame.
 //               All 3-D drawing commands, except the clear operation,
 //               must be enclosed within begin_scene() .. end_scene().
+//               This must be called in the draw thread.
 //
 //               The return value is true if successful (in which case
 //               the scene will be drawn and end_scene() will be
@@ -1326,7 +1348,7 @@ begin_scene() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::end_scene
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Called between begin_frame() and end_frame() to mark
 //               the end of drawing commands for a "scene" (usually a
 //               particular DisplayRegion) within a frame.  All 3-D
@@ -2059,6 +2081,18 @@ create_gamma_table (float gamma, unsigned short *red_table, unsigned short *gree
   }    
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::reissue_transforms
+//       Access: Protected, Virtual
+//  Description: Called by clear_state_and_transform() to ensure that
+//               the current modelview and projection matrices are
+//               properly loaded in the graphics state, after a
+//               callback might have mucked them up.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+reissue_transforms() {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::enable_lighting
 //       Access: Protected, Virtual

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

@@ -181,10 +181,11 @@ PUBLISHED:
   Texture *get_flash_texture() const;
 #endif
 
-public:
+PUBLISHED:
   bool set_scene(SceneSetup *scene_setup);
   virtual SceneSetup *get_scene() const;
 
+public:
   virtual TextureContext *prepare_texture(Texture *tex);
   virtual bool update_texture(TextureContext *tc, bool force);
   virtual void release_texture(TextureContext *tc);
@@ -224,12 +225,16 @@ public:
   virtual void prepare_display_region(DisplayRegionPipelineReader *dr,
                                       Lens::StereoChannel stereo_channel);
 
+  virtual void clear_state_and_transform();
+
   virtual CPT(TransformState) calc_projection_mat(const Lens *lens);
   virtual bool prepare_lens();
 
   virtual bool begin_frame(Thread *current_thread);
+PUBLISHED:
   virtual bool begin_scene();
   virtual void end_scene();
+public:
   virtual void end_frame(Thread *current_thread);
 
   void set_current_properties(const FrameBufferProperties *properties);
@@ -293,6 +298,8 @@ public:
 #endif
 
 protected:
+  virtual void reissue_transforms();
+
   virtual void enable_lighting(bool enable);
   virtual void set_ambient_light(const Colorf &color);
   virtual void enable_light(int light_id, bool enable);

+ 17 - 9
panda/src/distort/nonlinearImager.cxx

@@ -20,6 +20,9 @@
 #include "graphicsOutput.h"
 #include "graphicsEngine.h"
 #include "dcast.h"
+#include "cPointerCallbackObject.h"
+#include "asyncTaskManager.h"
+#include "genericAsyncTask.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: NonlinearImager::Constructor
@@ -42,9 +45,9 @@ NonlinearImager::
   remove_all_screens();
   remove_all_viewers();
 
-  if (_engine != (GraphicsEngine *)NULL) {
-    _engine->remove_callback("", GraphicsEngine::CB_pre_frame, 
-                             recompute_callback, (void *)this);
+  if (_recompute_task != (AsyncTask *)NULL) {
+    AsyncTaskManager *task_mgr = AsyncTaskManager::get_global_ptr();
+    task_mgr->remove(_recompute_task);
   }
 }
 
@@ -330,8 +333,13 @@ add_viewer(DisplayRegion *dr) {
   nassertr(_viewers.empty() || (engine == _engine), -1);
   if (_engine == (GraphicsEngine *)NULL) {
     _engine = engine;
-    _engine->add_callback("", GraphicsEngine::CB_pre_frame, 
-                          recompute_callback, (void *)this);
+  }
+
+  if (_recompute_task == (AsyncTask *)NULL) {
+    _recompute_task = 
+      new GenericAsyncTask("nli_recompute", recompute_callback, (void *)this);
+    AsyncTaskManager *task_mgr = AsyncTaskManager::get_global_ptr();
+    task_mgr->add(_recompute_task);
   }
 
   int previous_vi = find_viewer(dr);
@@ -595,14 +603,14 @@ recompute() {
 ////////////////////////////////////////////////////////////////////
 //     Function: NonlinearImager::recompute_callback
 //       Access: Private, Static
-//  Description: This function is added as a callback to the beginning
-//               of the graphics engine's frame, to ensure that all
+//  Description: This function is added as a task, to ensure that all
 //               frames are up-to-date.
 ////////////////////////////////////////////////////////////////////
-void NonlinearImager::
-recompute_callback(void *data) {
+AsyncTask::DoneStatus NonlinearImager::
+recompute_callback(GenericAsyncTask *, void *data) {
   NonlinearImager *self = (NonlinearImager *)data;
   self->recompute_if_stale();
+  return AsyncTask::DS_cont;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 5 - 1
panda/src/distort/nonlinearImager.h

@@ -27,10 +27,13 @@
 #include "pointerTo.h"
 #include "pvector.h"
 #include "graphicsEngine.h"
+#include "callbackObject.h"
+#include "asyncTask.h"
 
 class GraphicsEngine;
 class GraphicsStateGuardian;
 class GraphicsOutput;
+class GenericAsyncTask;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : NonlinearImager
@@ -130,7 +133,7 @@ PUBLISHED:
   void recompute();
 
 public:
-  static void recompute_callback(void *data);
+  static AsyncTask::DoneStatus recompute_callback(GenericAsyncTask *task, void *data);
   void recompute_if_stale();
 
 private:
@@ -174,6 +177,7 @@ private:
   Screens _screens;
 
   PT(GraphicsEngine) _engine;
+  PT(AsyncTask) _recompute_task;
   NodePath _dark_room;
 
   bool _stale;

+ 14 - 0
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -3022,6 +3022,20 @@ do_issue_texture() {
   _num_active_texture_stages = num_stages;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian8::reissue_transforms
+//       Access: Protected, Virtual
+//  Description: Called by clear_state_and_transform() to ensure that
+//               the current modelview and projection matrices are
+//               properly loaded in the graphics state, after a
+//               callback might have mucked them up.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian8::
+reissue_transforms() {
+  prepare_lens();
+  do_issue_transform();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian8::enable_lighting
 //       Access: Protected, Virtual

+ 2 - 0
panda/src/dxgsg8/dxGraphicsStateGuardian8.h

@@ -151,6 +151,8 @@ protected:
 
   void set_scissor(float left, float right, float bottom, float top);
 
+  virtual void reissue_transforms();
+
   virtual void enable_lighting(bool enable);
   virtual void set_ambient_light(const Colorf &color);
   virtual void enable_light(int light_id, bool enable);

+ 14 - 0
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -4043,6 +4043,20 @@ do_issue_blending() {
   set_render_state(D3DRS_ALPHABLENDENABLE, FALSE);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::reissue_transforms
+//       Access: Protected, Virtual
+//  Description: Called by clear_state_and_transform() to ensure that
+//               the current modelview and projection matrices are
+//               properly loaded in the graphics state, after a
+//               callback might have mucked them up.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+reissue_transforms() {
+  prepare_lens();
+  do_issue_transform();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian9::enable_lighting
 //       Access: Protected, Virtual

+ 2 - 0
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -186,6 +186,8 @@ protected:
   void do_issue_stencil();
   void do_issue_scissor();
 
+  virtual void reissue_transforms();
+
   virtual void enable_lighting(bool enable);
   virtual void set_ambient_light(const Colorf &color);
   virtual void enable_light(int light_id, bool enable);

+ 15 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -5891,6 +5891,21 @@ get_light_color(float light_color[4], Light *light) const {
   return light_color;
 }
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::reissue_transforms
+//       Access: Protected, Virtual
+//  Description: Called by clear_state_and_transform() to ensure that
+//               the current modelview and projection matrices are
+//               properly loaded in the graphics state, after a
+//               callback might have mucked them up.
+////////////////////////////////////////////////////////////////////
+void CLP(GraphicsStateGuardian)::
+reissue_transforms() {
+  prepare_lens();
+  do_issue_transform();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::enable_lighting
 //       Access: Protected, Virtual

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

@@ -259,6 +259,7 @@ protected:
   bool is_at_least_version(int major_version, int minor_version, int release_version = 0) const;
   virtual void *get_extension_func(const char *prefix, const char *name);
 
+  virtual void reissue_transforms();
   virtual void enable_lighting(bool enable);
   virtual void set_ambient_light(const Colorf &color);
   virtual void enable_light(int light_id, bool enable);

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

@@ -128,6 +128,8 @@ public:
   // that display depends on.
   virtual SceneSetup *get_scene() const=0;
 
+  virtual void clear_state_and_transform()=0;
+
 #ifndef CPPPARSER
   // We hide this from interrogate, so that it will be properly
   // exported from the GraphicsStateGuardian class, later.

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

@@ -53,6 +53,7 @@
     findApproxPath.I findApproxPath.h \
     fog.I fog.h \
     fogAttrib.I fogAttrib.h \
+    geomDrawCallbackData.I geomDrawCallbackData.h \
     geomNode.I geomNode.h \
     geomTransformer.I geomTransformer.h \
     internalNameCollection.I internalNameCollection.h \
@@ -156,6 +157,7 @@
     findApproxPath.cxx \
     fog.cxx \
     fogAttrib.cxx \
+    geomDrawCallbackData.cxx \
     geomNode.cxx \
     geomTransformer.cxx \
     internalNameCollection.cxx \
@@ -255,6 +257,7 @@
     fadeLodNodeData.h \
     fog.I fog.h \
     fogAttrib.I fogAttrib.h \
+    geomDrawCallbackData.I geomDrawCallbackData.h \
     geomNode.I geomNode.h \
     geomTransformer.I geomTransformer.h \
     internalNameCollection.I internalNameCollection.h \

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

@@ -42,6 +42,7 @@
 #include "fadeLodNodeData.h"
 #include "fog.h"
 #include "fogAttrib.h"
+#include "geomDrawCallbackData.h"
 #include "geomNode.h"
 #include "geomTransformer.h"
 #include "lensNode.h"
@@ -425,6 +426,7 @@ init_libpgraph() {
   FindApproxLevelEntry::init_type();
   Fog::init_type();
   FogAttrib::init_type();
+  GeomDrawCallbackData::init_type();
   GeomNode::init_type();
   GeomTransformer::init_type();
   LensNode::init_type();

+ 7 - 7
panda/src/pgraph/cullBin.cxx

@@ -144,7 +144,7 @@ void CullBin::ResultGraphBuilder::
 add_object(CullableObject *object) {
   if (_current_transform != object->_modelview_transform || 
       _current_state != object->_state || 
-      object->_next != (CullableObject *)NULL) {
+      object->is_fancy()) {
     // Create a new GeomNode to hold the net transform and state.  We
     // choose to create a new GeomNode for each new state, to make it
     // clearer to the observer when the state changes.
@@ -158,12 +158,12 @@ add_object(CullableObject *object) {
 
   record_one_object(_current_node, object);
 
-  if (object->_next != (CullableObject *)NULL) {
+  if (object->get_next() != (CullableObject *)NULL) {
     // Collect the decal base pieces.
-    CullableObject *base = object->_next;
+    CullableObject *base = object->get_next();
     while (base != (CullableObject *)NULL && base->_geom != (Geom *)NULL) {
       record_one_object(_current_node, base);
-      base = base->_next;
+      base = base->get_next();
     }
 
     if (base != (CullableObject *)NULL) {
@@ -174,11 +174,11 @@ add_object(CullableObject *object) {
       CPT(TransformState) transform;
       CPT(RenderState) state;
       PT(GeomNode) decal_node;
-      CullableObject *decal = base->_next;
+      CullableObject *decal = base->get_next();
       while (decal != (CullableObject *)NULL) {
         if (transform != decal->_modelview_transform || 
             state != decal->_state || 
-            decal->_next != (CullableObject *)NULL) {
+            decal->get_next() != (CullableObject *)NULL) {
           // Create a new GeomNode to hold the net transform.
           transform = decal->_modelview_transform;
           state = decal->_state;
@@ -189,7 +189,7 @@ add_object(CullableObject *object) {
         }
         
         record_one_object(decal_node, decal);
-        decal = decal->_next;
+        decal = decal->get_next();
         ++decal_index;
       }
     }

+ 1 - 6
panda/src/pgraph/cullHandler.I

@@ -23,10 +23,5 @@
 INLINE void CullHandler::
 draw(CullableObject *object, GraphicsStateGuardianBase *gsg,
      bool force, Thread *current_thread) {
-  if (object->_next != (CullableObject *)NULL) {
-    draw_with_decals(object, gsg, force, current_thread);
-  } else {
-    gsg->set_state_and_transform(object->_state, object->_internal_transform);
-    object->draw(gsg, force, current_thread);
-  }
+  object->draw(gsg, force, current_thread);
 }

+ 0 - 47
panda/src/pgraph/cullHandler.cxx

@@ -66,50 +66,3 @@ void CullHandler::
 end_traverse() {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CullHandler::draw_with_decals
-//       Access: Public, Static
-//  Description: Draws the indicated CullableObject, assuming it has
-//               attached decals.
-////////////////////////////////////////////////////////////////////
-void CullHandler::
-draw_with_decals(CullableObject *object, GraphicsStateGuardianBase *gsg,
-                 bool force, Thread *current_thread) {
-  // We draw with a three-step process.
-
-  // First, render all of the base geometry for the first pass.
-  CPT(RenderState) state = gsg->begin_decal_base_first();
-
-  CullableObject *base = object;
-  while (base != (CullableObject *)NULL && base->_geom != (Geom *)NULL) {
-    gsg->set_state_and_transform(base->_state->compose(state), base->_internal_transform);
-    base->draw(gsg, force, current_thread);
-    
-    base = base->_next;
-  }
-
-  if (base != (CullableObject *)NULL) {
-    // Now, draw all the decals.
-    state = gsg->begin_decal_nested();
-
-    CullableObject *decal = base->_next;
-    while (decal != (CullableObject *)NULL) {
-      gsg->set_state_and_transform(decal->_state->compose(state), decal->_internal_transform);
-      decal->draw(gsg, force, current_thread);
-      decal = decal->_next;
-    }
-  }
-
-  // And now, re-draw the base geometry, if required.
-  state = gsg->begin_decal_base_second();
-  if (state != (const RenderState *)NULL) {
-    base = object;
-    while (base != (CullableObject *)NULL && base->_geom != (Geom *)NULL) {
-      gsg->set_state_and_transform(base->_state->compose(state), base->_internal_transform);
-      base->draw(gsg, force, current_thread);
-      
-      base = base->_next;
-    }
-  }
-}
-

+ 0 - 3
panda/src/pgraph/cullHandler.h

@@ -40,9 +40,6 @@ public:
   INLINE static void draw(CullableObject *object,
                           GraphicsStateGuardianBase *gsg,
                           bool force, Thread *current_thread);
-  static void draw_with_decals(CullableObject *object,
-                               GraphicsStateGuardianBase *gsg,
-                               bool force, Thread *current_thread);
 };
 
 #include "cullHandler.I"

+ 5 - 5
panda/src/pgraph/cullResult.cxx

@@ -67,7 +67,7 @@ CullResult(GraphicsStateGuardianBase *gsg,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullResult::make_next
-//       Access: Public
+//       Access: Published
 //  Description: Returns a newly-allocated CullResult object that
 //               contains a copy of just the subset of the data from
 //               this CullResult object that is worth keeping around
@@ -95,7 +95,7 @@ make_next() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullResult::add_object
-//       Access: Public
+//       Access: Published
 //  Description: Adds the indicated CullableObject to the appropriate
 //               bin.  The bin becomes the owner of the object
 //               pointer, and will eventually delete it.
@@ -230,7 +230,7 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullResult::finish_cull
-//       Access: Public
+//       Access: Published
 //  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
@@ -258,7 +258,7 @@ finish_cull(SceneSetup *scene_setup, Thread *current_thread) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullResult::draw
-//       Access: Public
+//       Access: Published
 //  Description: Asks all the bins to draw themselves in the correct
 //               order.
 ////////////////////////////////////////////////////////////////////
@@ -281,7 +281,7 @@ draw(Thread *current_thread) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullResult::make_result_graph
-//       Access: Public
+//       Access: Published
 //  Description: Returns a special scene graph constructed to
 //               represent the results of the cull.  This will be a
 //               hierarchy of nodes, one node for each bin, each of

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

@@ -50,6 +50,7 @@ public:
              const PStatCollector &draw_region_pcollector);
   INLINE ~CullResult();
 
+PUBLISHED:
   PT(CullResult) make_next() const;
 
   INLINE CullBin *get_bin(int bin_index);

+ 19 - 19
panda/src/pgraph/cullTraverser.I

@@ -15,7 +15,7 @@
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_gsg
-//       Access: Public
+//       Access: Published
 //  Description: Returns the GraphicsStateGuardian in effect.
 ////////////////////////////////////////////////////////////////////
 INLINE GraphicsStateGuardianBase *CullTraverser::
@@ -25,7 +25,7 @@ get_gsg() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_current_thread
-//       Access: Public
+//       Access: Published
 //  Description: Returns the currently-executing thread object, as
 //               passed to the CullTraverser constructor.
 ////////////////////////////////////////////////////////////////////
@@ -36,7 +36,7 @@ get_current_thread() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_scene
-//       Access: Public
+//       Access: Published
 //  Description: Returns the SceneSetup object.
 ////////////////////////////////////////////////////////////////////
 INLINE SceneSetup *CullTraverser::
@@ -46,7 +46,7 @@ get_scene() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::has_tag_state_key
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if a nonempty tag state key has been
 //               specified for the scene's camera, false otherwise.
 ////////////////////////////////////////////////////////////////////
@@ -57,7 +57,7 @@ has_tag_state_key() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_tag_state_key
-//       Access: Public
+//       Access: Published
 //  Description: Returns the tag state key that has been specified for
 //               the scene's camera, if any.
 ////////////////////////////////////////////////////////////////////
@@ -68,7 +68,7 @@ get_tag_state_key() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_camera_transform
-//       Access: Public
+//       Access: Published
 //  Description: Returns the position of the camera relative to the
 //               starting node.
 ////////////////////////////////////////////////////////////////////
@@ -79,7 +79,7 @@ get_camera_transform() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_world_transform
-//       Access: Public
+//       Access: Published
 //  Description: Returns the position of the starting node relative
 //               to the camera.  This is the inverse of the camera
 //               transform.
@@ -97,7 +97,7 @@ get_world_transform() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_initial_state
-//       Access: Public
+//       Access: Published
 //  Description: Returns the initial RenderState at the top of the
 //               scene graph we are traversing, or the empty state if
 //               the initial state was never set.
@@ -109,7 +109,7 @@ get_initial_state() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_depth_offset_decals
-//       Access: Public
+//       Access: Published
 //  Description: Returns the depth_offset_decals flag.  See
 //               set_depth_offset_decals().
 ////////////////////////////////////////////////////////////////////
@@ -120,7 +120,7 @@ get_depth_offset_decals() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::set_camera_mask
-//       Access: Public
+//       Access: Published
 //  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
@@ -135,7 +135,7 @@ set_camera_mask(const DrawMask &camera_mask) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_camera_mask
-//       Access: Public
+//       Access: Published
 //  Description: Returns the visibility mask from the camera viewing
 //               the scene.
 ////////////////////////////////////////////////////////////////////
@@ -146,7 +146,7 @@ get_camera_mask() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::set_view_frustum
-//       Access: Public
+//       Access: Published
 //  Description: Specifies the bounding volume that corresponds to the
 //               view frustum.  Any primitives that fall entirely
 //               outside of this volume are not drawn.
@@ -158,7 +158,7 @@ set_view_frustum(GeometricBoundingVolume *view_frustum) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_view_frustum
-//       Access: Public
+//       Access: Published
 //  Description: Returns the bounding volume that corresponds to the
 //               view frustum, or NULL if the view frustum is not in
 //               use or has not been set.
@@ -177,7 +177,7 @@ get_view_frustum() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::set_cull_handler
-//       Access: Public
+//       Access: Published
 //  Description: Specifies the object that will receive the culled
 //               Geoms.  This must be set before calling traverse().
 ////////////////////////////////////////////////////////////////////
@@ -188,7 +188,7 @@ set_cull_handler(CullHandler *cull_handler) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_cull_handler
-//       Access: Public
+//       Access: Published
 //  Description: Returns the object that will receive the culled
 //               Geoms.
 ////////////////////////////////////////////////////////////////////
@@ -198,7 +198,7 @@ get_cull_handler() const {
 }
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::set_portal_clipper
-//       Access: Public
+//       Access: Published
 //  Description: Specifies _portal_clipper object pointer that 
 //               subsequent traverse() or traverse_below may use.
 ////////////////////////////////////////////////////////////////////
@@ -209,7 +209,7 @@ set_portal_clipper(PortalClipper *portal_clipper) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_portal_clipper
-//       Access: Public
+//       Access: Published
 //  Description: Returns the _portal_clipper pointer
 ////////////////////////////////////////////////////////////////////
 INLINE PortalClipper *CullTraverser::
@@ -219,7 +219,7 @@ get_portal_clipper() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::get_effective_incomplete_render
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if the cull traversal is effectively in
 //               incomplete_render state, considering both the GSG's
 //               incomplete_render and the current DisplayRegion's
@@ -235,7 +235,7 @@ get_effective_incomplete_render() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::flush_level
-//       Access: Public, Static
+//       Access: Published, Static
 //  Description: Flushes the PStatCollectors used during traversal.
 ////////////////////////////////////////////////////////////////////
 INLINE void CullTraverser::

+ 19 - 81
panda/src/pgraph/cullTraverser.cxx

@@ -44,7 +44,7 @@ TypeHandle CullTraverser::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::Constructor
-//       Access: Public
+//       Access: Published
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 CullTraverser::
@@ -62,7 +62,7 @@ CullTraverser() :
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::Copy Constructor
-//       Access: Public
+//       Access: Published
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 CullTraverser::
@@ -84,7 +84,7 @@ CullTraverser(const CullTraverser &copy) :
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::set_scene
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Sets the SceneSetup object that indicates the initial
 //               camera position, etc.  This must be called before
 //               traversal begins.
@@ -110,7 +110,7 @@ set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsg,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::traverse
-//       Access: Public
+//       Access: Published
 //  Description: Begins the traversal from the indicated node.
 ////////////////////////////////////////////////////////////////////
 void CullTraverser::
@@ -165,7 +165,7 @@ traverse(const NodePath &root) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::traverse
-//       Access: Public
+//       Access: Published
 //  Description: Traverses from the next node with the given
 //               data, which has been constructed with the node but
 //               has not yet been converted into the node's space.
@@ -226,7 +226,7 @@ traverse(CullTraverserData &data) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::traverse_below
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Traverses all the children of the indicated node,
 //               with the given data, which has been converted into
 //               the node's space.
@@ -247,73 +247,8 @@ traverse_below(CullTraverserData &data) {
     start_decal(data);
     
   } else {
-    if (!this_node_hidden && node->is_geom_node()) {
-      _geom_nodes_pcollector.add_level(1);
-      GeomNode *geom_node = DCAST(GeomNode, node);
-
-      if (pgraph_cat.is_spam()) {
-        pgraph_cat.spam()
-          << "Found " << *geom_node << " in state " << *data._state 
-          << " draw_mask = " << data._draw_mask << "\n";
-      }
-      
-      // Get all the Geoms, with no decalling.
-      GeomNode::Geoms geoms = geom_node->get_geoms(_current_thread);
-      int num_geoms = geoms.get_num_geoms();
-      _geoms_pcollector.add_level(num_geoms);
-      CPT(TransformState) net_transform = data.get_net_transform(this);
-      CPT(TransformState) modelview_transform = data.get_modelview_transform(this);
-      CPT(TransformState) internal_transform = get_gsg()->get_cs_transform()->compose(modelview_transform);
-
-      for (int i = 0; i < num_geoms; i++) {
-        const Geom *geom = geoms.get_geom(i);
-        if (geom->is_empty()) {
-          continue;
-        }
-
-        CPT(RenderState) state = data._state->compose(geoms.get_geom_state(i));
-        if (state->has_cull_callback() && !state->cull_callback(this, data)) {
-          // Cull.
-          continue;
-        }
-
-        // Cull the Geom bounding volume against the view frustum
-        // and/or the cull planes.  Don't bother unless we've got more
-        // than one Geom, since otherwise the bounding volume of the
-        // GeomNode is (probably) the same as that of the one Geom,
-        // and we've already culled against that.
-        if (num_geoms > 1) {
-          if (data._view_frustum != (GeometricBoundingVolume *)NULL) {
-            // Cull the individual Geom against the view frustum.
-            CPT(BoundingVolume) geom_volume = geom->get_bounds();
-            const GeometricBoundingVolume *geom_gbv =
-              DCAST(GeometricBoundingVolume, geom_volume);
-
-            int result = data._view_frustum->contains(geom_gbv);
-            if (result == BoundingVolume::IF_no_intersection) {
-              // Cull this Geom.
-              continue;
-            }
-          }
-          if (!data._cull_planes->is_empty()) {
-            // Also cull the Geom against the cull planes.
-            CPT(BoundingVolume) geom_volume = geom->get_bounds();
-            const GeometricBoundingVolume *geom_gbv =
-              DCAST(GeometricBoundingVolume, geom_volume);
-            int result;
-            data._cull_planes->do_cull(result, state, geom_gbv);
-            if (result == BoundingVolume::IF_no_intersection) {
-              // Cull.
-              continue;
-            }
-          }
-        }
-
-        CullableObject *object = 
-          new CullableObject(geom, state, net_transform, 
-                             modelview_transform, internal_transform);
-        _cull_handler->record_object(object, this);
-      }
+    if (!this_node_hidden) {
+      node->add_for_draw(this, data);
     }
 
     if (has_decal) {
@@ -353,7 +288,7 @@ traverse_below(CullTraverserData &data) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::end_traverse
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Should be called when the traverser has finished
 //               traversing its scene, this gives it a chance to do
 //               any necessary finalization.
@@ -365,7 +300,7 @@ end_traverse() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverser::draw_bounding_volume
-//       Access: Public
+//       Access: Published
 //  Description: Draws an appropriate visualization of the indicated
 //               bounding volume.
 ////////////////////////////////////////////////////////////////////
@@ -723,7 +658,8 @@ start_decal(const CullTraverserData &data) {
 
   // Now create a new, empty CullableObject to separate the decals
   // from the non-decals.
-  CullableObject *separator = new CullableObject(decals);
+  CullableObject *separator = new CullableObject;
+  separator->set_next(decals);
 
   // And now get the base Geoms, again in reverse order.
   CullableObject *object = separator;
@@ -778,11 +714,12 @@ start_decal(const CullTraverserData &data) {
         }
       }
     }
-    
+
+    CullableObject *next = object;
     object =
       new CullableObject(geom, state, net_transform, 
-                         modelview_transform, internal_transform,
-                         object);
+                         modelview_transform, internal_transform);
+    object->set_next(next);
   }
 
   if (object != separator) {
@@ -891,10 +828,11 @@ r_get_decals(CullTraverserData &data, CullableObject *decals) {
           }
         }
 
+        CullableObject *next = decals;
         decals =
           new CullableObject(geom, state, net_transform, 
-                             modelview_transform, internal_transform,
-                             decals);
+                             modelview_transform, internal_transform);
+        decals->set_next(next);
       }
     }
   }

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

@@ -49,7 +49,6 @@ PUBLISHED:
   CullTraverser();
   CullTraverser(const CullTraverser &copy);
 
-public:
   INLINE GraphicsStateGuardianBase *get_gsg() const;
   INLINE Thread *get_current_thread() const;
 

+ 4 - 4
panda/src/pgraph/cullTraverserData.I

@@ -97,7 +97,7 @@ INLINE CullTraverserData::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::node
-//       Access: Public
+//       Access: Published
 //  Description: Returns the node traversed to so far.
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *CullTraverserData::
@@ -129,7 +129,7 @@ node_reader() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::get_net_transform
-//       Access: Public
+//       Access: Published
 //  Description: Returns the net transform: the relative transform
 //               from root of the scene graph to the current node.
 ////////////////////////////////////////////////////////////////////
@@ -140,7 +140,7 @@ get_net_transform(const CullTraverser *) const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::is_in_view
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if the current node is within the view
 //               frustum, false otherwise.  If the node's bounding
 //               volume falls completely within the view frustum, this
@@ -173,7 +173,7 @@ is_in_view(const DrawMask &camera_mask) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::is_this_node_hidden
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if this particular node is hidden, even
 //               though we might be traversing past this node to find
 //               a child node that has had show_through() called for

+ 4 - 4
panda/src/pgraph/cullTraverserData.cxx

@@ -29,7 +29,7 @@
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::get_modelview_transform
-//       Access: Public
+//       Access: Published
 //  Description: Returns the modelview transform: the relative
 //               transform from the camera to the model.
 ////////////////////////////////////////////////////////////////////
@@ -40,7 +40,7 @@ get_modelview_transform(const CullTraverser *trav) const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullTraverserData::apply_transform_and_state
-//       Access: Public
+//       Access: Published
 //  Description: Applies the transform and state from the current
 //               node onto the current data.  This also evaluates
 //               billboards, etc.
@@ -66,8 +66,8 @@ apply_transform_and_state(CullTraverser *trav) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CullTraverserData::apply_specific_transform
-//       Access: Public
+//     Function: CullTraverserData::apply_transform_and_state
+//       Access: Published
 //  Description: Applies the indicated transform and state changes
 //               (e.g. as extracted from a node) onto the current
 //               data.  This also evaluates billboards, etc.

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

@@ -54,10 +54,14 @@ public:
                            PandaNode *child);
   INLINE ~CullTraverserData();
 
+PUBLISHED:
   INLINE PandaNode *node() const;
+
+public:
   INLINE PandaNodePipelineReader *node_reader();
   INLINE const PandaNodePipelineReader *node_reader() const;
 
+PUBLISHED:
   CPT(TransformState) get_modelview_transform(const CullTraverser *trav) const;
   INLINE const TransformState *get_net_transform(const CullTraverser *trav) const;
 
@@ -71,6 +75,7 @@ public:
                                  CPT(RenderEffects) node_effects,
                                  const RenderAttrib *off_clip_planes);
 
+public:
   WorkingNodePath _node_path;
   PandaNodePipelineReader _node_reader;
   CPT(TransformState) _net_transform;

+ 109 - 14
panda/src/pgraph/cullableObject.I

@@ -19,8 +19,8 @@
 //               filled in later.
 ////////////////////////////////////////////////////////////////////
 INLINE CullableObject::
-CullableObject(CullableObject *next) :
-  _next(next)
+CullableObject() :
+  _fancy(false)
 {
 }
 
@@ -34,14 +34,13 @@ INLINE CullableObject::
 CullableObject(const Geom *geom, const RenderState *state,
                const TransformState *net_transform,
                const TransformState *modelview_transform,
-               const GraphicsStateGuardianBase *gsg,
-               CullableObject *next) :
+               const GraphicsStateGuardianBase *gsg) :
   _geom(geom),
   _state(state),
   _net_transform(net_transform),
   _modelview_transform(modelview_transform),
   _internal_transform(gsg->get_cs_transform()->compose(modelview_transform)),
-  _next(next)
+  _fancy(false)
 {
 }
 
@@ -55,14 +54,13 @@ INLINE CullableObject::
 CullableObject(const Geom *geom, const RenderState *state,
                const TransformState *net_transform,
                const TransformState *modelview_transform,
-               const TransformState *internal_transform,
-               CullableObject *next) :
+               const TransformState *internal_transform) :
   _geom(geom),
   _state(state),
   _net_transform(net_transform),
   _modelview_transform(modelview_transform),
   _internal_transform(internal_transform),
-  _next(next)
+  _fancy(false)
 {
 }
   
@@ -82,7 +80,7 @@ CullableObject(const CullableObject &copy) :
   _net_transform(copy._net_transform),
   _modelview_transform(copy._modelview_transform),
   _internal_transform(copy._internal_transform),
-  _next((CullableObject *)NULL)
+  _fancy(false)
 {
 }
 
@@ -94,6 +92,7 @@ CullableObject(const CullableObject &copy) :
 ////////////////////////////////////////////////////////////////////
 INLINE void CullableObject::
 operator = (const CullableObject &copy) {
+  nassertv(!_fancy);
   _geom = copy._geom;
   _munger = copy._munger;
   _munged_data = copy._munged_data;
@@ -103,15 +102,27 @@ operator = (const CullableObject &copy) {
   _internal_transform = copy._internal_transform;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::is_fancy
+//       Access: Public
+//  Description: Returns true if the object has something fancy to it:
+//               decals, maybe, or a draw_callback, that prevents it
+//               from being rendered inline.
+////////////////////////////////////////////////////////////////////
+INLINE bool CullableObject::
+is_fancy() const {
+  return _fancy;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullableObject::has_decals
 //       Access: Public
-//  Description: Returns true if the object has one or more decals
-//               placed on it, false otherwise.
+//  Description: Returns true if the object has decals associated with
+//               it.
 ////////////////////////////////////////////////////////////////////
 INLINE bool CullableObject::
 has_decals() const {
-  return (_next != (CullableObject *)NULL);
+  return _fancy && (_next != (CullableObject *)NULL);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -123,12 +134,18 @@ has_decals() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void CullableObject::
 draw(GraphicsStateGuardianBase *gsg, bool force, Thread *current_thread) {
-  _geom->draw(gsg, _munger, _munged_data, force, current_thread);
+  if (_fancy) {
+    draw_fancy(gsg, force, current_thread);
+  } else {
+    nassertv(_geom != (Geom *)NULL);
+    gsg->set_state_and_transform(_state, _internal_transform);
+    draw_inline(gsg, force, current_thread);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CullableObject::request_resident
-//       Access: Published
+//       Access: Public
 //  Description: Returns true if all the data necessary to render this
 //               object is currently resident in memory.  If this
 //               returns false, the data will be brought back into
@@ -146,6 +163,54 @@ request_resident() const {
   return resident;
 }
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::set_draw_callback
+//       Access: Public
+//  Description: Specifies a CallbackObject that will be responsible
+//               for drawing this object.
+////////////////////////////////////////////////////////////////////
+INLINE void CullableObject::
+set_draw_callback(CallbackObject *draw_callback) {
+  make_fancy();
+  if (draw_callback != _draw_callback) {
+    if (_draw_callback != (CallbackObject *)NULL) {
+      unref_delete(_draw_callback);
+    }
+    _draw_callback = draw_callback;
+    if (_draw_callback != (CallbackObject *)NULL) {
+      _draw_callback->ref();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::set_next
+//       Access: Public
+//  Description: Sets the next object in the decal chain.  This next
+//               object will be destructed when this object destructs.
+////////////////////////////////////////////////////////////////////
+INLINE void CullableObject::
+set_next(CullableObject *next) {
+  make_fancy();
+  nassertv(_next == (CullableObject *)NULL);
+  _next = next;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::get_next
+//       Access: Public
+//  Description: Returns the next object in the decal chain, or NULL
+//               for the end of the chain.
+////////////////////////////////////////////////////////////////////
+INLINE CullableObject *CullableObject::
+get_next() const {
+  if (_fancy) {
+    return _next;
+  }
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullableObject::flush_level
 //       Access: Public, Static
@@ -156,6 +221,36 @@ flush_level() {
   _sw_sprites_pcollector.flush_level();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::make_fancy
+//       Access: Private
+//  Description: Elevates this object to "fancy" status.  This means
+//               that the additional pointers, like _next and
+//               _draw_callback, have meaningful values and should be
+//               examined.
+////////////////////////////////////////////////////////////////////
+INLINE void CullableObject::
+make_fancy() {
+  if (!_fancy) {
+    _fancy = true;
+    _draw_callback = NULL;
+    _next = NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::draw_inline
+//       Access: Private
+//  Description: Draws the cullable object on the GSG immediately, in
+//               the GSG's current state.  This should only be called
+//               from the draw thread.  Assumes the GSG has already
+//               been set to the appropriate state.
+////////////////////////////////////////////////////////////////////
+INLINE void CullableObject::
+draw_inline(GraphicsStateGuardianBase *gsg, bool force, Thread *current_thread) {
+  _geom->draw(gsg, _munger, _munged_data, force, current_thread);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullableObject::SortPoints::Constructor
 //       Access: Public

+ 99 - 9
panda/src/pgraph/cullableObject.cxx

@@ -28,6 +28,7 @@
 #include "geomTriangles.h"
 #include "light.h"
 #include "lightMutexHolder.h"
+#include "geomDrawCallbackData.h"
 
 CullableObject::FormatMap CullableObject::_format_map;
 LightMutex CullableObject::_format_lock;
@@ -99,7 +100,7 @@ munge_geom(GraphicsStateGuardianBase *gsg,
       gsg_bits &= ~(Geom::GR_point_perspective | Geom::GR_point_sprite);
     }
     if (!hardware_points) {
-      // If hardware-points is off, we don't all any kind of point
+      // If hardware-points is off, we don't allow any kind of point
       // rendering, except plain old one-pixel points;
       gsg_bits &= ~(Geom::GR_point_bits & ~Geom::GR_point);
     }
@@ -176,12 +177,16 @@ munge_geom(GraphicsStateGuardianBase *gsg,
     }
 #endif
   }
-  if (_next != (CullableObject *)NULL) {
-    if (_next->_state != (RenderState *)NULL) {
-      _next->munge_geom(gsg, gsg->get_geom_munger(_next->_state, current_thread),
-                        traverser, force);
-    } else {
-      _next->munge_geom(gsg, munger, traverser, force);
+
+  if (_fancy) {
+    // Only check the _next pointer if the _fancy flag is set.
+    if (_next != (CullableObject *)NULL) {
+      if (_next->_state != (RenderState *)NULL) {
+        _next->munge_geom(gsg, gsg->get_geom_munger(_next->_state, current_thread),
+                          traverser, force);
+      } else {
+        _next->munge_geom(gsg, munger, traverser, force);
+      }
     }
   }
 
@@ -195,8 +200,12 @@ munge_geom(GraphicsStateGuardianBase *gsg,
 ////////////////////////////////////////////////////////////////////
 CullableObject::
 ~CullableObject() {
-  if (_next != (CullableObject *)NULL) {
-    delete _next;
+  if (_fancy) {
+    // Only check the _next pointer if the _fancy flag is set.
+    if (_next != (CullableObject *)NULL) {
+      delete _next;
+    }
+    set_draw_callback(NULL);
   }
 }
 
@@ -762,6 +771,87 @@ get_flash_hardware_state() {
   return flash_hardware_state;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::draw_fancy
+//       Access: Private
+//  Description: Something fancy about this object.  Draw it properly.
+////////////////////////////////////////////////////////////////////
+void CullableObject::
+draw_fancy(GraphicsStateGuardianBase *gsg, bool force, 
+           Thread *current_thread) {
+  nassertv(_fancy);
+  if (_draw_callback != (CallbackObject *)NULL) {
+    // It has a callback associated.
+    gsg->set_state_and_transform(_state, _internal_transform);
+    GeomDrawCallbackData cbdata(this, gsg, force);
+    _draw_callback->do_callback(&cbdata);
+    if (cbdata.get_lost_state()) {
+      // Tell the GSG to forget its state.
+      gsg->clear_state_and_transform();
+    }
+    // Now the callback has taken care of drawing.
+
+  } else if (_next != (CullableObject *)NULL) {
+    // It has decals.
+    draw_with_decals(gsg, force, current_thread);
+
+  } else {
+    // Huh, nothing fancy after all.  Somehow the _fancy flag got set
+    // incorrectly; that's a bug.
+    gsg->set_state_and_transform(_state, _internal_transform);
+    draw_inline(gsg, force, current_thread);
+    nassertv(false);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::draw_with_decals
+//       Access: Private
+//  Description: Draws the current CullableObject, assuming it has
+//               attached decals.
+////////////////////////////////////////////////////////////////////
+void CullableObject::
+draw_with_decals(GraphicsStateGuardianBase *gsg, bool force, 
+                 Thread *current_thread) {
+  nassertv(_fancy && _next != (CullableObject *)NULL);  
+  // We draw with a three-step process.
+
+  // First, render all of the base geometry for the first pass.
+  CPT(RenderState) state = gsg->begin_decal_base_first();
+
+  CullableObject *base = this;
+  while (base != (CullableObject *)NULL && base->_geom != (Geom *)NULL) {
+    gsg->set_state_and_transform(base->_state->compose(state), base->_internal_transform);
+    base->draw_inline(gsg, force, current_thread);
+    
+    base = base->_next;
+  }
+
+  if (base != (CullableObject *)NULL) {
+    // Now, draw all the decals.
+    state = gsg->begin_decal_nested();
+
+    CullableObject *decal = base->_next;
+    while (decal != (CullableObject *)NULL) {
+      gsg->set_state_and_transform(decal->_state->compose(state), decal->_internal_transform);
+      decal->draw_inline(gsg, force, current_thread);
+      decal = decal->_next;
+    }
+  }
+
+  // And now, re-draw the base geometry, if required.
+  state = gsg->begin_decal_base_second();
+  if (state != (const RenderState *)NULL) {
+    base = this;
+    while (base != (CullableObject *)NULL && base->_geom != (Geom *)NULL) {
+      gsg->set_state_and_transform(base->_state->compose(state), base->_internal_transform);
+      base->draw_inline(gsg, force, current_thread);
+      
+      base = base->_next;
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullableObject::SourceFormat::Constructor
 //       Access: Public

+ 25 - 6
panda/src/pgraph/cullableObject.h

@@ -30,6 +30,7 @@
 #include "deletedChain.h"
 #include "graphicsStateGuardianBase.h"
 #include "lightMutex.h"
+#include "callbackObject.h"
 
 class CullTraverser;
 
@@ -42,21 +43,20 @@ class CullTraverser;
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_PGRAPH CullableObject {
 public:
-  INLINE CullableObject(CullableObject *next = NULL);
+  INLINE CullableObject();
   INLINE CullableObject(const Geom *geom, const RenderState *state,
                         const TransformState *net_transform,
                         const TransformState *modelview_transform,
-                        const GraphicsStateGuardianBase *gsg,
-                        CullableObject *next = NULL);
+                        const GraphicsStateGuardianBase *gsg);
   INLINE CullableObject(const Geom *geom, const RenderState *state,
                         const TransformState *net_transform,
                         const TransformState *modelview_transform,
-                        const TransformState *internal_transform,
-                        CullableObject *next = NULL);
+                        const TransformState *internal_transform);
     
   INLINE CullableObject(const CullableObject &copy);
   INLINE void operator = (const CullableObject &copy);
 
+  INLINE bool is_fancy() const;
   INLINE bool has_decals() const;
 
   bool munge_geom(GraphicsStateGuardianBase *gsg,
@@ -68,6 +68,10 @@ public:
   INLINE bool request_resident() const;
   INLINE static void flush_level();
 
+  INLINE void set_draw_callback(CallbackObject *draw_callback);
+  INLINE void set_next(CullableObject *next);
+  INLINE CullableObject *get_next() const;
+
 public:
   ~CullableObject();
   ALLOC_DELETED_CHAIN(CullableObject);
@@ -82,15 +86,30 @@ public:
   CPT(TransformState) _net_transform;
   CPT(TransformState) _modelview_transform;
   CPT(TransformState) _internal_transform;
-  CullableObject *_next;
 
 private:
+  bool _fancy;
+
+  // Fancy things below.  These pointers are only meaningful if
+  // _fancy, above, is true.
+  CallbackObject *_draw_callback;
+  CullableObject *_next;  // for decals
+
+private:
+  INLINE void make_fancy();
   bool munge_points_to_quads(const CullTraverser *traverser, bool force);
   bool munge_texcoord_light_vector(const CullTraverser *traverser, bool force);
 
   static CPT(RenderState) get_flash_cpu_state();
   static CPT(RenderState) get_flash_hardware_state();
 
+  INLINE void draw_inline(GraphicsStateGuardianBase *gsg,
+                          bool force, Thread *current_thread);
+  void draw_fancy(GraphicsStateGuardianBase *gsg, bool force, 
+                  Thread *current_thread);
+  void draw_with_decals(GraphicsStateGuardianBase *gsg, bool force, 
+                        Thread *current_thread);
+
 private:
   // This class is used internally by munge_points_to_quads().
   class PointData {

+ 92 - 0
panda/src/pgraph/geomDrawCallbackData.I

@@ -0,0 +1,92 @@
+// Filename: geomDrawCallbackData.I
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomDrawCallbackData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomDrawCallbackData::
+GeomDrawCallbackData(CullableObject *obj, GraphicsStateGuardianBase *gsg, 
+                     bool force) :
+  _obj(obj),
+  _gsg(gsg),
+  _force(force),
+  _lost_state(true)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomDrawCallbackData::get_object
+//       Access: Published
+//  Description: Returns a pointer to the particular object that is
+//               being drawn.
+////////////////////////////////////////////////////////////////////
+INLINE CullableObject *GeomDrawCallbackData::
+get_object() const {
+  return _obj;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomDrawCallbackData::get_gsg
+//       Access: Published
+//  Description: Returns a pointer to the current GSG.
+////////////////////////////////////////////////////////////////////
+INLINE GraphicsStateGuardianBase *GeomDrawCallbackData::
+get_gsg() const {
+  return _gsg;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomDrawCallbackData::get_force
+//       Access: Published
+//  Description: Returns true if any required data should be forced
+//               into memory if necessary to render the object, or
+//               false if the object should be omitted if some of the
+//               data is not available (at least until the data
+//               becomes available later).
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomDrawCallbackData::
+get_force() const {
+  return _force;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomDrawCallbackData::set_lost_state
+//       Access: Published
+//  Description: Sets the lost_state flag.  If this is true, the
+//               callback does not have to be quite so careful to
+//               clean up after itself; Panda will assume that the
+//               graphics state is in an unknown state after the
+//               callback has finished, and will issue all the
+//               necessary calls to restore it.  If this is false,
+//               Panda will assume the callback will leave the
+//               graphics state exactly as it came in, and won't
+//               bother to try to restore it.  The default is true.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomDrawCallbackData::
+set_lost_state(bool lost_state) {
+  _lost_state = lost_state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomDrawCallbackData::get_lost_state
+//       Access: Published
+//  Description: Returns the lost_state flag.  See set_lost_state().
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomDrawCallbackData::
+get_lost_state() const {
+  return _lost_state;
+}

+ 55 - 0
panda/src/pgraph/geomDrawCallbackData.cxx

@@ -0,0 +1,55 @@
+// Filename: geomDrawCallbackData.cxx
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "geomDrawCallbackData.h"
+
+TypeHandle GeomDrawCallbackData::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomDrawCallbackData::output
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void GeomDrawCallbackData::
+output(ostream &out) const {
+  out << get_type() << "(" << (void *)_obj << ", " << (void *)_gsg 
+      << ", " << _force << ")";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomDrawCallbackData::upcall
+//       Access: Published, Virtual
+//  Description: You should make this call during the callback if you
+//               want to continue the normal rendering function that
+//               would have been done in the absence of a callback.
+//
+//               Specifically, this method will add the Geoms in this
+//               node to the list of renderable objects for drawing.
+//               If this callback was made on a CallbackNode, it
+//               doesn't actually do anything, since only a GeomNode
+//               holds geoms.
+////////////////////////////////////////////////////////////////////
+void GeomDrawCallbackData::
+upcall() {
+  // Go ahead and draw the object, if we have one.
+  if (_obj->_geom != (Geom *)NULL) {
+    if (_lost_state) {
+      // Tell the GSG to forget its state.
+      _gsg->clear_state_and_transform();
+    }
+
+    _obj->_geom->draw(_gsg, _obj->_munger, _obj->_munged_data, _force, 
+                      Thread::get_current_thread());
+  }
+}

+ 70 - 0
panda/src/pgraph/geomDrawCallbackData.h

@@ -0,0 +1,70 @@
+// Filename: geomDrawCallbackData.h
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef GEOMDRAWCALLBACKDATA_H
+#define GEOMDRAWCALLBACKDATA_H
+
+#include "pandabase.h"
+#include "callbackData.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : GeomDrawCallbackData
+// Description : This specialization on CallbackData is passed when
+//               the callback is initiated from deep within the draw
+//               traversal, for a particular Geom.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_PGRAPH GeomDrawCallbackData : public CallbackData {
+public:
+  INLINE GeomDrawCallbackData(CullableObject *obj, 
+                              GraphicsStateGuardianBase *gsg, bool force);
+
+PUBLISHED:
+  virtual void output(ostream &out) const;
+
+  INLINE CullableObject *get_object() const;
+  INLINE GraphicsStateGuardianBase *get_gsg() const;
+  INLINE bool get_force() const;
+
+  INLINE void set_lost_state(bool lost_state);
+  INLINE bool get_lost_state() const;
+
+  virtual void upcall();
+
+private:
+  CullableObject *_obj;
+  GraphicsStateGuardianBase *_gsg;
+  bool _force;
+  bool _lost_state;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CallbackData::init_type();
+    register_type(_type_handle, "GeomDrawCallbackData",
+                  CallbackData::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 "geomDrawCallbackData.I"
+
+#endif

+ 77 - 0
panda/src/pgraph/geomNode.cxx

@@ -473,6 +473,83 @@ is_renderable() const {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::add_for_draw
+//       Access: Public, Virtual
+//  Description: Adds the node's contents to the CullResult we are
+//               building up during the cull traversal, so that it
+//               will be drawn at render time.  For most nodes other
+//               than GeomNodes, this is a do-nothing operation.
+////////////////////////////////////////////////////////////////////
+void GeomNode::
+add_for_draw(CullTraverser *trav, CullTraverserData &data) {
+  trav->_geom_nodes_pcollector.add_level(1);
+
+  if (pgraph_cat.is_spam()) {
+    pgraph_cat.spam()
+      << "Found " << *this << " in state " << *data._state 
+      << " draw_mask = " << data._draw_mask << "\n";
+  }
+  
+  // Get all the Geoms, with no decalling.
+  Geoms geoms = get_geoms(trav->get_current_thread());
+  int num_geoms = geoms.get_num_geoms();
+  trav->_geoms_pcollector.add_level(num_geoms);
+  CPT(TransformState) net_transform = data.get_net_transform(trav);
+  CPT(TransformState) modelview_transform = data.get_modelview_transform(trav);
+  CPT(TransformState) internal_transform = trav->get_gsg()->get_cs_transform()->compose(modelview_transform);
+
+  for (int i = 0; i < num_geoms; i++) {
+    const Geom *geom = geoms.get_geom(i);
+    if (geom->is_empty()) {
+      continue;
+    }
+
+    CPT(RenderState) state = data._state->compose(geoms.get_geom_state(i));
+    if (state->has_cull_callback() && !state->cull_callback(trav, data)) {
+      // Cull.
+      continue;
+    }
+    
+    // Cull the Geom bounding volume against the view frustum
+    // and/or the cull planes.  Don't bother unless we've got more
+    // than one Geom, since otherwise the bounding volume of the
+    // GeomNode is (probably) the same as that of the one Geom,
+    // and we've already culled against that.
+    if (num_geoms > 1) {
+      if (data._view_frustum != (GeometricBoundingVolume *)NULL) {
+        // Cull the individual Geom against the view frustum.
+        CPT(BoundingVolume) geom_volume = geom->get_bounds();
+        const GeometricBoundingVolume *geom_gbv =
+          DCAST(GeometricBoundingVolume, geom_volume);
+        
+        int result = data._view_frustum->contains(geom_gbv);
+        if (result == BoundingVolume::IF_no_intersection) {
+          // Cull this Geom.
+          continue;
+        }
+      }
+      if (!data._cull_planes->is_empty()) {
+        // Also cull the Geom against the cull planes.
+        CPT(BoundingVolume) geom_volume = geom->get_bounds();
+        const GeometricBoundingVolume *geom_gbv =
+          DCAST(GeometricBoundingVolume, geom_volume);
+        int result;
+        data._cull_planes->do_cull(result, state, geom_gbv);
+        if (result == BoundingVolume::IF_no_intersection) {
+          // Cull.
+          continue;
+        }
+      }
+    }
+    
+    CullableObject *object = 
+      new CullableObject(geom, state, net_transform, 
+                         modelview_transform, internal_transform);
+    trav->get_cull_handler()->record_object(object, trav);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::get_legal_collide_mask
 //       Access: Published, Virtual

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

@@ -54,6 +54,7 @@ public:
                       const TransformState *transform,
                       Thread *current_thread) const;
   virtual bool is_renderable() const;
+  virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
   virtual CollideMask get_legal_collide_mask() const;
 
   virtual bool safe_to_flatten() const;

+ 12 - 0
panda/src/pgraph/pandaNode.cxx

@@ -576,6 +576,18 @@ is_renderable() const {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::add_for_draw
+//       Access: Public, Virtual
+//  Description: Adds the node's contents to the CullResult we are
+//               building up during the cull traversal, so that it
+//               will be drawn at render time.  For most nodes other
+//               than GeomNodes, this is a do-nothing operation.
+////////////////////////////////////////////////////////////////////
+void PandaNode::
+add_for_draw(CullTraverser *, CullTraverserData &) {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::copy_subgraph
 //       Access: Published

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

@@ -112,6 +112,7 @@ public:
   virtual bool has_single_child_visibility() const;
   virtual int get_visible_child() const;
   virtual bool is_renderable() const;
+  virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
 
 PUBLISHED:
   PT(PandaNode) copy_subgraph(Thread *current_thread = Thread::get_current_thread()) const;

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

@@ -20,5 +20,6 @@
 #include "findApproxLevelEntry.cxx"
 #include "fog.cxx"
 #include "fogAttrib.cxx"
+#include "geomDrawCallbackData.cxx"
 #include "geomNode.cxx"
 #include "geomTransformer.cxx"

+ 18 - 18
panda/src/pgraph/sceneSetup.I

@@ -31,7 +31,7 @@ SceneSetup() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_display_region
-//       Access: Public
+//       Access: Published
 //  Description: Specifies the display region for the scene.
 ////////////////////////////////////////////////////////////////////
 INLINE void SceneSetup::
@@ -41,7 +41,7 @@ set_display_region(DisplayRegion *display_region) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_display_region
-//       Access: Public
+//       Access: Published
 //  Description: Returns the display region for the scene.
 ////////////////////////////////////////////////////////////////////
 INLINE DisplayRegion *SceneSetup::
@@ -51,7 +51,7 @@ get_display_region() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_viewport_size
-//       Access: Public
+//       Access: Published
 //  Description: Specifies the size of the viewport (display region),
 //               in pixels.
 ////////////////////////////////////////////////////////////////////
@@ -63,7 +63,7 @@ set_viewport_size(int width, int height) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_viewport_width
-//       Access: Public
+//       Access: Published
 //  Description: Returns the width of the viewport (display region) in
 //               pixels.
 ////////////////////////////////////////////////////////////////////
@@ -74,7 +74,7 @@ get_viewport_width() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_viewport_height
-//       Access: Public
+//       Access: Published
 //  Description: Returns the height of the viewport (display region) in
 //               pixels.
 ////////////////////////////////////////////////////////////////////
@@ -85,7 +85,7 @@ get_viewport_height() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_scene_root
-//       Access: Public
+//       Access: Published
 //  Description: Specifies the root node of the scene.
 ////////////////////////////////////////////////////////////////////
 INLINE void SceneSetup::
@@ -95,7 +95,7 @@ set_scene_root(const NodePath &scene_root) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_scene_root
-//       Access: Public
+//       Access: Published
 //  Description: Returns the root node of the scene.
 ////////////////////////////////////////////////////////////////////
 INLINE const NodePath &SceneSetup::
@@ -105,7 +105,7 @@ get_scene_root() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_camera_path
-//       Access: Public
+//       Access: Published
 //  Description: Specifies the NodePath to the camera.
 ////////////////////////////////////////////////////////////////////
 INLINE void SceneSetup::
@@ -115,7 +115,7 @@ set_camera_path(const NodePath &camera_path) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_camera_path
-//       Access: Public
+//       Access: Published
 //  Description: Returns the NodePath to the camera.
 ////////////////////////////////////////////////////////////////////
 INLINE const NodePath &SceneSetup::
@@ -125,7 +125,7 @@ get_camera_path() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_camera_node
-//       Access: Public
+//       Access: Published
 //  Description: Specifies the camera used to render the scene.
 ////////////////////////////////////////////////////////////////////
 INLINE void SceneSetup::
@@ -135,7 +135,7 @@ set_camera_node(Camera *camera_node) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_camera_node
-//       Access: Public
+//       Access: Published
 //  Description: Returns the camera used to render the scene.
 ////////////////////////////////////////////////////////////////////
 INLINE Camera *SceneSetup::
@@ -145,7 +145,7 @@ get_camera_node() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_lens
-//       Access: Public
+//       Access: Published
 //  Description: Indicates the particular Lens used for rendering.
 ////////////////////////////////////////////////////////////////////
 INLINE void SceneSetup::
@@ -155,7 +155,7 @@ set_lens(const Lens *lens) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_lens
-//       Access: Public
+//       Access: Published
 //  Description: Returns the particular Lens used for rendering.
 ////////////////////////////////////////////////////////////////////
 INLINE const Lens *SceneSetup::
@@ -191,7 +191,7 @@ get_inverted() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_cull_center
-//       Access: Public
+//       Access: Published
 //  Description: Returns the point from which the culling operations
 //               will be performed.  This is normally the camera, but
 //               if camera->set_cull_center() has been specified, it
@@ -231,7 +231,7 @@ get_initial_state() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_camera_transform
-//       Access: Public
+//       Access: Published
 //  Description: Specifies the position of the camera relative to the
 //               starting node.
 ////////////////////////////////////////////////////////////////////
@@ -242,7 +242,7 @@ set_camera_transform(const TransformState *camera_transform) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_camera_transform
-//       Access: Public
+//       Access: Published
 //  Description: Returns the position of the camera relative to the
 //               starting node.
 ////////////////////////////////////////////////////////////////////
@@ -253,7 +253,7 @@ get_camera_transform() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::set_world_transform
-//       Access: Public
+//       Access: Published
 //  Description: Specifies the position of the starting node relative
 //               to the camera.  This is the inverse of the camera
 //               transform.
@@ -265,7 +265,7 @@ set_world_transform(const TransformState *world_transform) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneSetup::get_world_transform
-//       Access: Public
+//       Access: Published
 //  Description: Returns the position of the starting node relative
 //               to the camera.  This is the inverse of the camera
 //               transform.

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

@@ -36,6 +36,7 @@ class EXPCL_PANDA_PGRAPH SceneSetup : public ReferenceCount {
 public:
   INLINE SceneSetup();
 
+PUBLISHED:
   INLINE void set_display_region(DisplayRegion *display_region);
   INLINE DisplayRegion *get_display_region() const;
 

+ 6 - 0
panda/src/pgraphnodes/Sources.pp

@@ -13,10 +13,12 @@
 
   #define SOURCES \
     ambientLight.h ambientLight.I \
+    callbackNode.h callbackNode.I \
     config_pgraphnodes.h \
     directionalLight.h directionalLight.I \
     lightLensNode.h lightLensNode.I \
     lightNode.h lightNode.I \
+    nodeCullCallbackData.h nodeCullCallbackData.I \
     pointLight.h pointLight.I \
     selectiveChildNode.h selectiveChildNode.I \
     sequenceNode.h sequenceNode.I \
@@ -26,10 +28,12 @@
 
   #define INCLUDED_SOURCES \
     ambientLight.cxx \
+    callbackNode.cxx \
     config_pgraphnodes.cxx \
     directionalLight.cxx \
     lightLensNode.cxx \
     lightNode.cxx \
+    nodeCullCallbackData.cxx \
     pointLight.cxx \
     selectiveChildNode.cxx \
     sequenceNode.cxx \
@@ -39,10 +43,12 @@
 
   #define INSTALL_HEADERS \
     ambientLight.h ambientLight.I \
+    callbackNode.h callbackNode.I \
     config_pgraphnodes.h \
     directionalLight.h directionalLight.I \
     lightLensNode.h lightLensNode.I \
     lightNode.h lightNode.I \
+    nodeCullCallbackData.h nodeCullCallbackData.I \
     pointLight.h pointLight.I \
     selectiveChildNode.h selectiveChildNode.I \
     sequenceNode.h sequenceNode.I \

+ 126 - 0
panda/src/pgraphnodes/callbackNode.I

@@ -0,0 +1,126 @@
+// Filename: callbackNode.I
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::set_cull_callback
+//       Access: Published
+//  Description: Sets the CallbackObject that will be notified when
+//               this node is visited during the cull traversal.  This
+//               callback will be made during the cull thread.
+//
+//               The cull traversal is responsible for determining
+//               which nodes are visible and within the view frustum,
+//               and for accumulating state and transform, and
+//               generally building up the list of CullableObjects
+//               that are to be eventually passed to the draw
+//               traversal for rendering.
+//
+//               At the time the cull traversal callback is made, the
+//               node has been determined to be visible and it has
+//               passed the bounding-volume test, so it lies within
+//               the view frustum.
+//
+//               The callback is passed an instance of a
+//               NodeCullCallbackData, which contains pointers to the
+//               CullTraverser and CullTraverserData--enough data to
+//               examine the current node and its place within the
+//               scene graph.  The callback *replaces* the normal cull
+//               behavior, so if your callback does nothing, the cull
+//               traversal will not continue below this node.  If you
+//               wish the cull traversal to continue to visit this
+//               node and below, you must call cbdata->upcall()
+//               from your callback.
+////////////////////////////////////////////////////////////////////
+INLINE void CallbackNode::
+set_cull_callback(CallbackObject *object) {
+  CDWriter cdata(_cycler);
+  cdata->_cull_callback = object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::get_cull_callback
+//       Access: Published
+//  Description: Returns the CallbackObject set by set_cull_callback().
+////////////////////////////////////////////////////////////////////
+INLINE CallbackObject *CallbackNode::
+get_cull_callback() const {
+  CDReader cdata(_cycler);
+  return cdata->_cull_callback;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::set_draw_callback
+//       Access: Published
+//  Description: Sets the CallbackObject that will be notified when
+//               this node is visited during the draw traversal.  This
+//               callback will be made during the draw thread.
+//
+//               The draw traversal is responsible for actually
+//               issuing the commands to the graphics engine to draw
+//               primitives.  Its job is to walk through the list of
+//               CullableObjects build up by the cull traversal, as
+//               quickly as possible, issuing the appropriate commands
+//               to draw each one.
+//
+//               At the time the draw traversal callback is made, the
+//               graphics state has been loaded with the correct
+//               modelview transform and render state, and the
+//               primitives (if any) in this node are ready to be
+//               drawn.
+//
+//               The callback is passed an instance of a
+//               GeomDrawCallbackData, which contains pointers to the
+//               current state and transform, as well as the current
+//               GSG.  There is a Geom pointer as well, but it will
+//               always be NULL to this callback, since the
+//               CallbackNode does not itself contain any Geoms.
+////////////////////////////////////////////////////////////////////
+INLINE void CallbackNode::
+set_draw_callback(CallbackObject *object) {
+  CDWriter cdata(_cycler);
+  cdata->_draw_callback = object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::get_draw_callback
+//       Access: Published
+//  Description: Returns the CallbackObject set by set_draw_callback().
+////////////////////////////////////////////////////////////////////
+INLINE CallbackObject *CallbackNode::
+get_draw_callback() const {
+  CDReader cdata(_cycler);
+  return cdata->_draw_callback;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CallbackNode::CData::
+CData() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CallbackNode::CData::
+CData(const CallbackNode::CData &copy) :
+  _cull_callback(copy._cull_callback),
+  _draw_callback(copy._draw_callback)
+{
+}

+ 262 - 0
panda/src/pgraphnodes/callbackNode.cxx

@@ -0,0 +1,262 @@
+// Filename: callbackNode.cxx
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+#include "callbackNode.h"
+#include "cullTraverser.h"
+#include "nodeCullCallbackData.h"
+#include "cullableObject.h"
+#include "cullHandler.h"
+#include "omniBoundingVolume.h"
+
+TypeHandle CallbackNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+CallbackNode::
+CallbackNode(const string &name) :
+  PandaNode(name)
+{
+  PandaNode::set_cull_callback();
+
+  // Set up a default, infinite bounding volume, unless the user tells
+  // us otherwise.  Not sure if this is a great idea, because it means
+  // a naive user will never set the bounding volume and always
+  // trigger the callback--but that's not altogether a bad default
+  // behavior.
+  set_internal_bounds(new OmniBoundingVolume);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+CallbackNode::
+CallbackNode(const CallbackNode &copy) :
+  PandaNode(copy),
+  _cycler(copy._cycler)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated Node that is a shallow copy
+//               of this one.  It will be a different Node pointer,
+//               but its internal data may or may not be shared with
+//               that of the original Node.
+////////////////////////////////////////////////////////////////////
+PandaNode *CallbackNode::
+make_copy() const {
+  return new CallbackNode(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::safe_to_combine
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to combine this
+//               particular kind of PandaNode with other kinds of
+//               PandaNodes of compatible type, adding children or
+//               whatever.  For instance, an LODNode should not be
+//               combined with any other PandaNode, because its set of
+//               children is meaningful.
+////////////////////////////////////////////////////////////////////
+bool CallbackNode::
+safe_to_combine() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::cull_callback
+//       Access: Public, Virtual
+//  Description: This function will be called during the cull
+//               traversal to perform any additional operations that
+//               should be performed at cull time.  This may include
+//               additional manipulation of render state or additional
+//               visible/invisible decisions, or any other arbitrary
+//               operation.
+//
+//               Note that this function will *not* be called unless
+//               set_cull_callback() is called in the constructor of
+//               the derived class.  It is necessary to call
+//               set_cull_callback() to indicated that we require
+//               cull_callback() to be called.
+//
+//               By the time this function is called, the node has
+//               already passed the bounding-volume test for the
+//               viewing frustum, and the node's transform and state
+//               have already been applied to the indicated
+//               CullTraverserData object.
+//
+//               The return value is true if this node should be
+//               visible, or false if it should be culled.
+////////////////////////////////////////////////////////////////////
+bool CallbackNode::
+cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  CallbackObject *cbobj = get_cull_callback();
+  if (cbobj != (CallbackObject *)NULL) {
+    NodeCullCallbackData cbdata(trav, data);
+    cbobj->do_callback(&cbdata);
+
+    // No further cull: the callback takes care of all of it.
+    return false;
+  }
+
+  // Recurse onto the node's children.
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::is_renderable
+//       Access: Public, Virtual
+//  Description: Returns true if there is some value to visiting this
+//               particular node during the cull traversal for any
+//               camera, false otherwise.  This will be used to
+//               optimize the result of get_net_draw_show_mask(), so
+//               that any subtrees that contain only nodes for which
+//               is_renderable() is false need not be visited.
+////////////////////////////////////////////////////////////////////
+bool CallbackNode::
+is_renderable() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::add_for_draw
+//       Access: Public, Virtual
+//  Description: Adds the node's contents to the CullResult we are
+//               building up during the cull traversal, so that it
+//               will be drawn at render time.  For most nodes other
+//               than GeomNodes, this is a do-nothing operation.
+////////////////////////////////////////////////////////////////////
+void CallbackNode::
+add_for_draw(CullTraverser *trav, CullTraverserData &data) {
+  // OK, render this node.  Rendering this node means creating a
+  // CullableObject for the draw_callback, if any.  We don't need to
+  // pass any Geoms, however.
+  CallbackObject *cbobj = get_draw_callback();
+  if (cbobj != (CallbackObject *)NULL) {
+    CullableObject *object = 
+      new CullableObject(NULL, data._state,
+                         data.get_net_transform(trav),
+                         data.get_modelview_transform(trav),
+                         trav->get_gsg());
+    object->set_draw_callback(cbobj);
+    trav->get_cull_handler()->record_object(object, trav);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::output
+//       Access: Public, Virtual
+//  Description: Writes a brief description of the node to the
+//               indicated output stream.  This is invoked by the <<
+//               operator.  It may be overridden in derived classes to
+//               include some information relevant to the class.
+////////////////////////////////////////////////////////////////////
+void CallbackNode::
+output(ostream &out) const {
+  PandaNode::output(out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               CallbackNode.
+////////////////////////////////////////////////////////////////////
+void CallbackNode::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void CallbackNode::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  PandaNode::write_datagram(manager, dg);
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type CallbackNode is encountered
+//               in the Bam file.  It should create the CallbackNode
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *CallbackNode::
+make_from_bam(const FactoryParams &params) {
+  CallbackNode *node = new CallbackNode("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new CallbackNode.
+////////////////////////////////////////////////////////////////////
+void CallbackNode::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  PandaNode::fillin(scan, manager);
+  manager->read_cdata(scan, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *CallbackNode::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void CallbackNode::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackNode::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new CallbackNode.
+////////////////////////////////////////////////////////////////////
+void CallbackNode::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+}

+ 98 - 0
panda/src/pgraphnodes/callbackNode.h

@@ -0,0 +1,98 @@
+// Filename: callbackNode.h
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CALLBACKNODE_H
+#define CALLBACKNODE_H
+
+#include "pandabase.h"
+#include "pandaNode.h"
+#include "callbackObject.h"
+#include "pointerTo.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CallbackNode
+// Description : A special node that can issue arbitrary callbacks to
+//               user code, either during the cull or draw traversals.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_PGRAPHNODES CallbackNode : public PandaNode {
+PUBLISHED:
+  CallbackNode(const string &name);
+
+  INLINE void set_cull_callback(CallbackObject *object);
+  INLINE CallbackObject *get_cull_callback() const;
+
+  INLINE void set_draw_callback(CallbackObject *object);
+  INLINE CallbackObject *get_draw_callback() const;
+
+public:
+  CallbackNode(const CallbackNode &copy);
+
+  virtual PandaNode *make_copy() const;
+  virtual bool safe_to_combine() const;
+
+  virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
+  virtual bool is_renderable() const;
+  virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
+
+  virtual void output(ostream &out) const;
+
+private:
+  class EXPCL_PANDA_PGRAPHNODES CData : public CycleData {
+  public:
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+    virtual TypeHandle get_parent_type() const {
+      return CallbackNode::get_class_type();
+    }
+
+    PT(CallbackObject) _cull_callback;
+    PT(CallbackObject) _draw_callback;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PandaNode::init_type();
+    register_type(_type_handle, "CallbackNode",
+                  PandaNode::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 "callbackNode.I"
+
+#endif

+ 9 - 0
panda/src/pgraphnodes/config_pgraphnodes.cxx

@@ -15,9 +15,13 @@
 #include "config_pgraphnodes.h"
 
 #include "ambientLight.h"
+#include "callbackData.h"
+#include "callbackNode.h"
+#include "callbackObject.h"
 #include "directionalLight.h"
 #include "lightLensNode.h"
 #include "lightNode.h"
+#include "nodeCullCallbackData.h"
 #include "pointLight.h"
 #include "selectiveChildNode.h"
 #include "sequenceNode.h"
@@ -51,9 +55,13 @@ init_libpgraphnodes() {
   initialized = true;
 
   AmbientLight::init_type();
+  CallbackData::init_type();
+  CallbackNode::init_type();
+  CallbackObject::init_type();
   DirectionalLight::init_type();
   LightLensNode::init_type();
   LightNode::init_type();
+  NodeCullCallbackData::init_type();
   PointLight::init_type();
   SelectiveChildNode::init_type();
   SequenceNode::init_type();
@@ -62,6 +70,7 @@ init_libpgraphnodes() {
   SwitchNode::init_type();
 
   AmbientLight::register_with_read_factory();
+  CallbackNode::register_with_read_factory();
   DirectionalLight::register_with_read_factory();
   LightLensNode::register_with_read_factory();
   LightNode::register_with_read_factory();

+ 52 - 0
panda/src/pgraphnodes/nodeCullCallbackData.I

@@ -0,0 +1,52 @@
+// Filename: nodeCullCallbackData.I
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeCullCallbackData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE NodeCullCallbackData::
+NodeCullCallbackData(CullTraverser *trav, CullTraverserData &data) :
+  _trav(trav),
+  _data(data)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeCullCallbackData::get_trav
+//       Access: Published
+//  Description: Returns the CullTraverser in use at the time of the
+//               callback.  This object contains data that does not
+//               change during the traversal, such as the
+//               DisplayRegion and Camera in use.
+////////////////////////////////////////////////////////////////////
+INLINE CullTraverser *NodeCullCallbackData::
+get_trav() const {
+  return _trav;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeCullCallbackData::get_data
+//       Access: Published
+//  Description: Returns the CullTraverserData in use at the time of the
+//               callback.  This object contains data that changes at
+//               each node of the traversal, such as the current node
+//               and the current net transform to that node.
+////////////////////////////////////////////////////////////////////
+INLINE CullTraverserData &NodeCullCallbackData::
+get_data() const {
+  return _data;
+}

+ 70 - 0
panda/src/pgraphnodes/nodeCullCallbackData.cxx

@@ -0,0 +1,70 @@
+// Filename: nodeCullCallbackData.cxx
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "nodeCullCallbackData.h"
+#include "callbackNode.h"
+#include "cullTraverser.h"
+#include "cullTraverserData.h"
+#include "cullableObject.h"
+#include "cullHandler.h"
+
+TypeHandle NodeCullCallbackData::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeCullCallbackData::output
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void NodeCullCallbackData::
+output(ostream &out) const {
+  out << get_type() << "(" << (void *)_trav << ", " << (void *)&_data << ")";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeCullCallbackData::upcall
+//       Access: Published, Virtual
+//  Description: You should make this call during the callback if you
+//               want to continue the normal rendering function that
+//               would have been done in the absence of a callback.
+//
+//               Specifically, this method will add this node to the
+//               draw queue, and continue the cull traversal for all
+//               the nodes below.  If you omit this call, this node
+//               and its children will be pruned from the render
+//               result.
+////////////////////////////////////////////////////////////////////
+void NodeCullCallbackData::
+upcall() {
+  PandaNode *node = _data.node();
+  if (node->is_of_type(CallbackNode::get_class_type())) {
+    CallbackNode *cbnode = DCAST(CallbackNode, _data.node());
+
+    // OK, render this node.  Rendering a CallbackNode means creating
+    // a CullableObject for the draw_callback, if any.  We don't need
+    // to pass any Geoms, however.
+    CallbackObject *cbobj = cbnode->get_draw_callback();
+    if (cbobj != (CallbackObject *)NULL) {
+      CullableObject *object = 
+        new CullableObject(NULL, _data._state,
+                           _data.get_net_transform(_trav),
+                           _data.get_modelview_transform(_trav),
+                           _trav->get_gsg());
+      object->set_draw_callback(cbobj);
+      _trav->get_cull_handler()->record_object(object, _trav);
+    }
+  }
+
+  // Now traverse below.
+  _trav->traverse_below(_data);
+}

+ 65 - 0
panda/src/pgraphnodes/nodeCullCallbackData.h

@@ -0,0 +1,65 @@
+// Filename: nodeCullCallbackData.h
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef NODECULLCALLBACKDATA_H
+#define NODECULLCALLBACKDATA_H
+
+#include "pandabase.h"
+#include "callbackData.h"
+#include "cullTraverser.h"
+#include "cullTraverserData.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : NodeCullCallbackData
+// Description : This kind of CallbackData is passed to the
+//               CallbackObject added to
+//               CallbackNode:set_cull_callback().
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_PGRAPHNODES NodeCullCallbackData : public CallbackData {
+public:
+  INLINE NodeCullCallbackData(CullTraverser *trav, CullTraverserData &data);
+
+PUBLISHED:
+  virtual void output(ostream &out) const;
+
+  INLINE CullTraverser *get_trav() const;
+  INLINE CullTraverserData &get_data() const;
+
+  virtual void upcall();
+
+private:
+  CullTraverser *_trav;
+  CullTraverserData &_data;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CallbackData::init_type();
+    register_type(_type_handle, "NodeCullCallbackData",
+                  CallbackData::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 "nodeCullCallbackData.I"
+
+#endif

+ 1 - 1
panda/src/pgraphnodes/pgraphnodes_composite1.cxx

@@ -1,6 +1,6 @@
 #include "ambientLight.cxx"
+#include "callbackNode.cxx"
 #include "config_pgraphnodes.cxx"
 #include "directionalLight.cxx"
 #include "lightLensNode.cxx"
 #include "lightNode.cxx"
-#include "pointLight.cxx"

+ 2 - 0
panda/src/pgraphnodes/pgraphnodes_composite2.cxx

@@ -1,3 +1,5 @@
+#include "nodeCullCallbackData.cxx"
+#include "pointLight.cxx"
 #include "selectiveChildNode.cxx"
 #include "sequenceNode.cxx"
 #include "shaderGenerator.cxx"

+ 16 - 2
panda/src/putil/Sources.pp

@@ -24,16 +24,20 @@
     buttonHandle.I \
     buttonHandle.h buttonRegistry.I buttonRegistry.h \
     cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \
+    callbackData.h callbackData.I \
+    callbackObject.h callbackObject.I \
     clockObject.h clockObject.I \
     collideMask.h \
     copyOnWriteObject.h copyOnWriteObject.I \
     copyOnWritePointer.h copyOnWritePointer.I \
     compareTo.I compareTo.h \
     config_util.N config_util.h configurable.h \
+    cPointerCallbackObject.h cPointerCallbackObject.I \
     datagramInputFile.I datagramInputFile.h \
     datagramOutputFile.I datagramOutputFile.h \
     doubleBitMask.I doubleBitMask.h \
     drawMask.h \
+    factory.I factory.h \
     factoryBase.I factoryBase.h \
     factoryParam.I factoryParam.h factoryParams.I \
     factoryParams.h \
@@ -55,7 +59,8 @@
     pbitops.I pbitops.h \
     portalMask.h \
     pta_double.h \
-    pta_int.h \
+    pta_int.h pta_ushort.h \
+    pythonCallbackObject.h pythonCallbackObject.I \
     simpleHashMap.I simpleHashMap.h \
     sparseArray.I sparseArray.h \
     string_utils.I string_utils.N string_utils.h \
@@ -81,10 +86,13 @@
     bitMask.cxx \
     buttonHandle.cxx buttonRegistry.cxx \
     cachedTypedWritableReferenceCount.cxx \
+    callbackData.cxx \
+    callbackObject.cxx \
     clockObject.cxx \
     copyOnWriteObject.cxx \
     copyOnWritePointer.cxx \
     config_util.cxx configurable.cxx \
+    cPointerCallbackObject.cxx \
     datagramInputFile.cxx datagramOutputFile.cxx \
     doubleBitMask.cxx \
     factoryBase.cxx \
@@ -102,6 +110,7 @@
     pbitops.cxx \
     pta_double.cxx \
     pta_int.cxx pta_ushort.cxx \
+    pythonCallbackObject.cxx \
     simpleHashMap.cxx \
     sparseArray.cxx \
     string_utils.cxx \
@@ -128,16 +137,20 @@
     buttonHandle.I buttonHandle.h buttonRegistry.I \
     buttonRegistry.h \
     cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \
+    callbackData.h callbackData.I \
+    callbackObject.h callbackObject.I \
     clockObject.h clockObject.I \
     collideMask.h \
     copyOnWriteObject.h copyOnWriteObject.I \
     copyOnWritePointer.h copyOnWritePointer.I \
     compareTo.I compareTo.h \
-    config_util.h configurable.h factory.I factory.h \
+    config_util.h configurable.h \
+    cPointerCallbackObject.h cPointerCallbackObject.I \
     datagramInputFile.I datagramInputFile.h \
     datagramOutputFile.I datagramOutputFile.h \
     doubleBitMask.I doubleBitMask.h \
     drawMask.h \
+    factory.I factory.h \
     factoryBase.I factoryBase.h factoryParam.I factoryParam.h \
     factoryParams.I factoryParams.h \
     firstOfPairCompare.I firstOfPairCompare.h \
@@ -160,6 +173,7 @@
     pbitops.I pbitops.h \
     pta_double.h \
     pta_int.h pta_ushort.h \
+    pythonCallbackObject.h pythonCallbackObject.I \
     simpleHashMap.I simpleHashMap.h \
     sparseArray.I sparseArray.h \
     string_utils.I string_utils.h \

+ 26 - 0
panda/src/putil/cPointerCallbackObject.I

@@ -0,0 +1,26 @@
+// Filename: cPointerCallbackObject.I
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPointerCallbackObject::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CPointerCallbackObject::
+CPointerCallbackObject(CPointerCallbackObject::CallbackFunction *func, void *data) :
+  _func(func),
+  _data(data)
+{
+}

+ 31 - 0
panda/src/putil/cPointerCallbackObject.cxx

@@ -0,0 +1,31 @@
+// Filename: cPointerCallbackObject.cxx
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cPointerCallbackObject.h"
+
+TypeHandle CPointerCallbackObject::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPointerCallbackObject::do_callback
+//       Access: Public, Virtual
+//  Description: This method called when the callback is triggered; it
+//               *replaces* the original function.  To continue
+//               performing the original function, you must call
+//               cbdata->upcall() during the callback.
+////////////////////////////////////////////////////////////////////
+void CPointerCallbackObject::
+do_callback(CallbackData *cbdata) {
+  (*_func)(cbdata, _data);
+}
+

+ 60 - 0
panda/src/putil/cPointerCallbackObject.h

@@ -0,0 +1,60 @@
+// Filename: cPointerCallbackObject.h
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CPOINTERCALLBACKOBJECT_H
+#define CPOINTERCALLBACKOBJECT_H
+
+#include "pandabase.h"
+#include "callbackObject.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CPointerCallbackObject
+// Description : This is a specialization on CallbackObject to allow
+//               association with a C-style function pointer and a
+//               void * parameter.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_PUTIL CPointerCallbackObject : public CallbackObject {
+public:
+  typedef void CallbackFunction(CallbackData *cbdata, void *data);
+  INLINE CPointerCallbackObject(CallbackFunction *func, void *data);
+  ALLOC_DELETED_CHAIN(CPointerCallbackObject);
+
+public:
+  virtual void do_callback(CallbackData *cbdata);
+
+private:
+  CallbackFunction *_func;
+  void *_data;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CallbackObject::init_type();
+    register_type(_type_handle, "CPointerCallbackObject",
+                  CallbackObject::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 "cPointerCallbackObject.I"
+
+#endif

+ 23 - 0
panda/src/putil/callbackData.I

@@ -0,0 +1,23 @@
+// Filename: callbackData.I
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackData::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CallbackData::
+CallbackData() {
+}

+ 40 - 0
panda/src/putil/callbackData.cxx

@@ -0,0 +1,40 @@
+// Filename: callbackData.cxx
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "callbackData.h"
+
+TypeHandle CallbackData::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackData::output
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void CallbackData::
+output(ostream &out) const {
+  out << get_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackData::upcall
+//       Access: Published, Virtual
+//  Description: You should make this call during the callback if you
+//               want to continue the normal function that would have
+//               been done in the absence of a callback.
+////////////////////////////////////////////////////////////////////
+void CallbackData::
+upcall() {
+}
+

+ 66 - 0
panda/src/putil/callbackData.h

@@ -0,0 +1,66 @@
+// Filename: callbackData.h
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CALLBACKDATA_H
+#define CALLBACKDATA_H
+
+#include "pandabase.h"
+#include "typedObject.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CallbackData
+// Description : This is a generic data block that is passed along to
+//               a CallbackObject when a callback is made.  It
+//               contains data specific to the particular callback
+//               type in question.
+//
+//               This is actually an abstract base class and contains
+//               no data.  Specializations of this class will contain
+//               the actual data relevant to each callback type.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_PUTIL CallbackData : public TypedObject {
+protected:
+  INLINE CallbackData();
+
+PUBLISHED:
+  virtual void output(ostream &out) const;
+
+  virtual void upcall();
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedObject::init_type();
+    register_type(_type_handle, "CallbackData",
+                  TypedObject::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;
+};
+
+inline ostream &operator << (ostream &out, const CallbackData &cbd) {
+  cbd.output(out);
+  return out;
+}
+
+#include "callbackData.I"
+
+#endif

+ 23 - 0
panda/src/putil/callbackObject.I

@@ -0,0 +1,23 @@
+// Filename: callbackObject.I
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackObject::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CallbackObject::
+CallbackObject() {
+}

+ 40 - 0
panda/src/putil/callbackObject.cxx

@@ -0,0 +1,40 @@
+// Filename: callbackObject.cxx
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "callbackObject.h"
+
+TypeHandle CallbackObject::_type_handle;
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackObject::output
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void CallbackObject::
+output(ostream &out) const {
+  out << get_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CallbackObject::do_callback
+//       Access: Public, Virtual
+//  Description: This method called when the callback is triggered; it
+//               *replaces* the original function.  To continue
+//               performing the original function, you must call
+//               cbdata->upcall() during the callback.
+////////////////////////////////////////////////////////////////////
+void CallbackObject::
+do_callback(CallbackData *) {
+}

+ 69 - 0
panda/src/putil/callbackObject.h

@@ -0,0 +1,69 @@
+// Filename: callbackObject.h
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CALLBACKOBJECT_H
+#define CALLBACKOBJECT_H
+
+#include "pandabase.h"
+#include "typedReferenceCount.h"
+
+class CallbackData;
+
+////////////////////////////////////////////////////////////////////
+//       Class : CallbackObject
+// Description : This is a generic object that can be assigned to a
+//               callback at various points in the rendering process.
+//               This is actually a base class for a handful of
+//               specialized callback object types.  You can also
+//               subclass it yourself to make your own callback
+//               handler.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_PUTIL CallbackObject : public TypedReferenceCount {
+protected:
+  INLINE CallbackObject();
+public:
+  ALLOC_DELETED_CHAIN(CallbackObject);
+
+PUBLISHED:
+  virtual void output(ostream &out) const;
+
+public:
+  virtual void do_callback(CallbackData *cbdata);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "CallbackObject",
+                  TypedReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+inline ostream &operator << (ostream &out, const CallbackObject &cbo) {
+  cbo.output(out);
+  return out;
+}
+
+#include "callbackObject.I"
+
+#endif

+ 9 - 1
panda/src/putil/config_util.cxx

@@ -22,14 +22,18 @@
 #include "bitMask.h"
 #include "buttonHandle.h"
 #include "cachedTypedWritableReferenceCount.h"
+#include "callbackData.h"
+#include "callbackObject.h"
 #include "clockObject.h"
 #include "configurable.h"
 #include "copyOnWriteObject.h"
+#include "cPointerCallbackObject.h"
 #include "datagram.h"
 #include "doubleBitMask.h"
 #include "factoryParam.h"
 #include "namable.h"
 #include "nodeCachedReferenceCount.h"
+#include "pythonCallbackObject.h"
 #include "referenceCount.h"
 #include "sparseArray.h"
 #include "typedObject.h"
@@ -153,16 +157,20 @@ init_libputil() {
   BitMask32::init_type();
   BitMask64::init_type();
   ButtonHandle::init_type();
+  CPointerCallbackObject::init_type();
   CachedTypedWritableReferenceCount::init_type();
+  CallbackData::init_type();
+  CallbackObject::init_type();
   ClockObject::init_type();
   Configurable::init_type();
   CopyOnWriteObject::init_type();
   Datagram::init_type();
   DoubleBitMaskNative::init_type();
-  QuadBitMaskNative::init_type();
   FactoryParam::init_type();
   Namable::init_type();
   NodeCachedReferenceCount::init_type();
+  PythonCallbackObject::init_type();
+  QuadBitMaskNative::init_type();
   ReferenceCount::init_type();
   SparseArray::init_type();
   TypedObject::init_type();

+ 3 - 3
panda/src/putil/putil_composite1.cxx

@@ -12,11 +12,14 @@
 #include "buttonHandle.cxx"
 #include "buttonRegistry.cxx"
 #include "cachedTypedWritableReferenceCount.cxx"
+#include "callbackData.cxx"
+#include "callbackObject.cxx"
 #include "clockObject.cxx"
 #include "config_util.cxx"
 #include "configurable.cxx"
 #include "copyOnWriteObject.cxx"
 #include "copyOnWritePointer.cxx"
+#include "cPointerCallbackObject.cxx"
 #include "datagramInputFile.cxx"
 #include "datagramOutputFile.cxx"
 #include "doubleBitMask.cxx"
@@ -24,6 +27,3 @@
 #include "factoryParam.cxx"
 #include "factoryParams.cxx"
 #include "globalPointerRegistry.cxx"
-#include "ioPtaDatagramFloat.cxx"
-#include "ioPtaDatagramInt.cxx"
-#include "ioPtaDatagramShort.cxx"

+ 4 - 0
panda/src/putil/putil_composite2.cxx

@@ -1,3 +1,6 @@
+#include "ioPtaDatagramFloat.cxx"
+#include "ioPtaDatagramInt.cxx"
+#include "ioPtaDatagramShort.cxx"
 #include "keyboardButton.cxx"
 #include "lineStream.cxx"
 #include "lineStreamBuf.cxx"
@@ -13,6 +16,7 @@
 #include "pta_double.cxx"
 #include "pta_int.cxx"
 #include "pta_ushort.cxx"
+#include "pythonCallbackObject.cxx"
 #include "simpleHashMap.cxx"
 #include "sparseArray.cxx"
 #include "string_utils.cxx"

+ 14 - 0
panda/src/putil/pythonCallbackObject.I

@@ -0,0 +1,14 @@
+// Filename: pythonCallbackObject.I
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 139 - 0
panda/src/putil/pythonCallbackObject.cxx

@@ -0,0 +1,139 @@
+// Filename: pythonCallbackObject.cxx
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pythonCallbackObject.h"
+
+#ifdef HAVE_PYTHON
+#include "py_panda.h"  
+#include "thread.h"
+#include "callbackData.h"
+
+TypeHandle PythonCallbackObject::_type_handle;
+
+#ifndef CPPPARSER
+IMPORT_THIS struct Dtool_PyTypedObject Dtool_TypedObject;
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonCallbackObject::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+PythonCallbackObject::
+PythonCallbackObject(PyObject *function) {
+  _function = Py_None;
+  Py_INCREF(_function);
+
+  set_function(function);
+
+#ifndef SIMPLE_THREADS
+  // Ensure that the Python threading system is initialized and ready
+  // to go.
+  PyEval_InitThreads();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonCallbackObject::Destructor
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+PythonCallbackObject::
+~PythonCallbackObject() {
+  Py_DECREF(_function);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonCallbackObject::set_function
+//       Access: Published
+//  Description: Replaces the function that is called for the callback.
+//               runs.  The parameter should be a Python callable
+//               object.
+////////////////////////////////////////////////////////////////////
+void PythonCallbackObject::
+set_function(PyObject *function) {
+  Py_DECREF(_function);
+  _function = function;
+  Py_INCREF(_function);
+  if (_function != Py_None && !PyCallable_Check(_function)) {
+    nassert_raise("Invalid function passed to PythonCallbackObject");
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonCallbackObject::get_function
+//       Access: Published
+//  Description: Returns the function that is called for the callback.
+////////////////////////////////////////////////////////////////////
+PyObject *PythonCallbackObject::
+get_function() {
+  Py_INCREF(_function);
+  return _function;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonCallbackObject::do_callback
+//       Access: Public, Virtual
+//  Description: This method called when the callback is triggered; it
+//               *replaces* the original function.  To continue
+//               performing the original function, you must call
+//               cbdata->upcall() during the callback.
+////////////////////////////////////////////////////////////////////
+void PythonCallbackObject::
+do_callback(CallbackData *cbdata) {
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  // Use PyGILState to protect this asynchronous call.
+  PyGILState_STATE gstate;
+  gstate = PyGILState_Ensure();
+#endif
+
+  do_python_callback(cbdata);
+
+#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+  PyGILState_Release(gstate);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonCallbackObject::do_python_callback
+//       Access: Private
+//  Description: The Python calls that implement do_callback().  This
+//               function is separate so we can acquire the Python
+//               interpretor lock while it runs.
+////////////////////////////////////////////////////////////////////
+void PythonCallbackObject::
+do_python_callback(CallbackData *cbdata) {
+  nassertv(cbdata != NULL);
+
+  // Wrap the cbdata up in a Python object, then put it in a tuple,
+  // for the argument list.
+  PyObject *pycbdata = 
+    DTool_CreatePyInstanceTyped(cbdata, Dtool_TypedObject,
+                                false, false, cbdata->get_type_index());
+  PyObject *args = Py_BuildValue("(O)", pycbdata);
+  Py_DECREF(pycbdata);
+
+  PyObject *result = 
+    Thread::get_current_thread()->call_python_func(_function, args);
+  Py_DECREF(args);
+
+  if (result == (PyObject *)NULL) {
+    util_cat.error()
+      << "Exception occurred in " << *this << "\n";
+  } else {
+    Py_DECREF(result);
+  }
+}
+
+#endif  // HAVE_PYTHON

+ 69 - 0
panda/src/putil/pythonCallbackObject.h

@@ -0,0 +1,69 @@
+// Filename: pythonCallbackObject.h
+// Created by:  drose (13Mar09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PYTHONCALLBACKOBJECT_H
+#define PYTHONCALLBACKOBJECT_H
+
+#include "pandabase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "callbackObject.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : PythonCallbackObject
+// Description : This is a specialization on CallbackObject to allow
+//               a callback to directly call an arbitarary Python
+//               function.  Powerful!  But use with caution.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_PUTIL PythonCallbackObject : public CallbackObject {
+PUBLISHED:
+  PythonCallbackObject(PyObject *function = Py_None);
+  virtual ~PythonCallbackObject();
+  ALLOC_DELETED_CHAIN(PythonCallbackObject);
+
+  void set_function(PyObject *function);
+  PyObject *get_function();
+
+public:
+  virtual void do_callback(CallbackData *cbdata);
+
+private:
+  void do_python_callback(CallbackData *cbdata);
+
+  PyObject *_function;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CallbackObject::init_type();
+    register_type(_type_handle, "PythonCallbackObject",
+                  CallbackObject::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 "pythonCallbackObject.I"
+
+#endif  // HAVE_PYTHON
+
+#endif