David Rose 23 éve
szülő
commit
4e233c54f6

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

@@ -28,6 +28,17 @@ LightInfo() {
   _next_enabled = false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::ClipPlaneInfo::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GraphicsStateGuardian::ClipPlaneInfo::
+ClipPlaneInfo() {
+  _enabled = false;
+  _next_enabled = false;
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::is_closed
@@ -340,3 +351,15 @@ get_light(int light_id) const {
   nassertr(light_id >= 0 && light_id < (int)_light_info.size(), (Light *)NULL);
   return _light_info[light_id]._light;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_clip_plane
+//       Access: Protected
+//  Description: Returns the PlaneNode object that is bound to the
+//               indicated id, or NULL if no PlaneNode is bound.
+////////////////////////////////////////////////////////////////////
+INLINE PlaneNode *GraphicsStateGuardian::
+get_clip_plane(int plane_id) const {
+  nassertr(plane_id >= 0 && plane_id < (int)_clip_plane_info.size(), (PlaneNode *)NULL);
+  return _clip_plane_info[plane_id]._plane;
+}

+ 238 - 2
panda/src/display/graphicsStateGuardian.cxx

@@ -30,7 +30,9 @@
 #include "colorWriteAttrib.h"
 #include "textureAttrib.h"
 #include "lightAttrib.h"
+#include "clipPlaneAttrib.h"
 #include "light.h"
+#include "planeNode.h"
 #include "ambientLight.h"
 
 #include "clockObject.h"
@@ -152,8 +154,10 @@ reset() {
   _scene_graph_color_stale = false;
   _vertex_colors_enabled = true;
   _lighting_enabled = false;
-
   _lighting_enabled_this_frame = false;
+
+  _clip_planes_enabled = false;
+  _clip_planes_enabled_this_frame = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -432,6 +436,21 @@ begin_frame() {
     _lighting_enabled_this_frame = false;
   }
 
+  // Ditto for the clipping planes.
+  if (_clip_planes_enabled_this_frame) {
+    for (int i = 0; i < (int)_clip_plane_info.size(); i++) {
+      if (_clip_plane_info[i]._enabled) {
+        enable_clip_plane(i, false);
+        _clip_plane_info[i]._enabled = false;
+      }
+      _clip_plane_info[i]._plane = (PlaneNode *)NULL;
+    }
+
+    modify_state(get_unclipped_state());
+
+    _clip_planes_enabled_this_frame = false;
+  }
+
 #ifdef DO_PSTATS
   // For Pstats to track our current texture memory usage, we have to
   // reset the set of current textures each frame.
@@ -689,7 +708,7 @@ issue_color(const ColorAttrib *attrib) {
 //               light associated with the same id where possible, but
 //               reusing id's when necessary.  When it is no longer
 //               possible to reuse existing id's (e.g. all id's are in
-//               use), slot_light() is called to prepare the next
+//               use), slot_new_light() is called to prepare the next
 //               sequential light id.
 //
 //               It will call apply_light() each time a light is
@@ -861,6 +880,125 @@ issue_color_blend(const ColorBlendAttrib *attrib) {
   set_blend_mode(_color_write_mode, _color_blend_mode, _transparency_mode);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::issue_clip_plane
+//       Access: Public, Virtual
+//  Description: This is fundametically similar to issue_light(), with
+//               calls to slot_new_clip_plane(), apply_clip_plane(),
+//               and enable_clip_planes(), as appropriate.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+issue_clip_plane(const ClipPlaneAttrib *attrib) {
+  int i;
+  int max_planes = (int)_clip_plane_info.size();
+  for (i = 0; i < max_planes; i++) {
+    _clip_plane_info[i]._next_enabled = false;
+  }
+
+  bool any_bound = false;
+
+  int num_enabled = 0;
+  int num_planes = attrib->get_num_planes();
+  if (attrib->get_operation() == ClipPlaneAttrib::O_remove) {
+    num_planes = 0;
+  }
+  for (int li = 0; li < num_planes; li++) {
+    PlaneNode *plane = attrib->get_plane(li);
+    nassertv(plane != (PlaneNode *)NULL);
+
+    num_enabled++;
+
+    // Planeing should be enabled before we apply any planes.
+    enable_clip_planes(true);
+    _clip_planes_enabled = true;
+    _clip_planes_enabled_this_frame = true;
+
+    // Check to see if this plane has already been bound to an id
+    int cur_plane_id = -1;
+    for (i = 0; i < max_planes; i++) {
+      if (_clip_plane_info[i]._plane == plane) {
+        // Plane has already been bound to an id, we only need to
+        // enable the plane, not reapply it.
+        cur_plane_id = -2;
+        enable_clip_plane(i, true);
+        _clip_plane_info[i]._enabled = true;
+        _clip_plane_info[i]._next_enabled = true;
+        break;
+      }
+    }
+        
+    // See if there are any unbound plane ids
+    if (cur_plane_id == -1) {
+      for (i = 0; i < max_planes; i++) {
+        if (_clip_plane_info[i]._plane == (PlaneNode *)NULL) {
+          _clip_plane_info[i]._plane = plane;
+          cur_plane_id = i;
+          break;
+        }
+      }
+    }
+        
+    // If there were no unbound plane ids, see if we can replace
+    // a currently unused but previously bound id
+    if (cur_plane_id == -1) {
+      for (i = 0; i < max_planes; i++) {
+        if (!attrib->has_plane(_clip_plane_info[i]._plane)) {
+          _clip_plane_info[i]._plane = plane;
+          cur_plane_id = i;
+          break;
+        }
+      }
+    }
+
+    // If we *still* don't have a plane id, slot a new one.
+    if (cur_plane_id == -1) {
+      if (slot_new_clip_plane(max_planes)) {
+        cur_plane_id = max_planes;
+        _clip_plane_info.push_back(ClipPlaneInfo());
+        max_planes++;
+        nassertv(max_planes == (int)_clip_plane_info.size());
+      }
+    }
+        
+    if (cur_plane_id >= 0) {
+      enable_clip_plane(cur_plane_id, true);
+      _clip_plane_info[cur_plane_id]._enabled = true;
+      _clip_plane_info[cur_plane_id]._next_enabled = true;
+      
+      if (!any_bound) {
+        begin_bind_clip_planes();
+        any_bound = true;
+      }
+      
+      // This is the first time this frame that this plane has been
+      // bound to this particular id.
+      bind_clip_plane(plane, cur_plane_id);
+      
+    } else if (cur_plane_id == -1) {
+      gsg_cat.warning()
+        << "Failed to bind " << *plane << " to id.\n";
+    }
+  }
+
+  // Disable all unused planes
+  for (i = 0; i < max_planes; i++) {
+    if (!_clip_plane_info[i]._next_enabled) {
+      enable_clip_plane(i, false);
+      _clip_plane_info[i]._enabled = false;
+    }
+  }
+
+  // If no planes were enabled, disable clip planes in general.
+  if (num_enabled == 0) {
+    enable_clip_planes(false);
+    _clip_planes_enabled = false;
+  }
+
+  if (any_bound) {
+    end_bind_clip_planes();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::bind_light
 //       Access: Public, Virtual
@@ -979,6 +1117,90 @@ void GraphicsStateGuardian::
 end_bind_lights() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::slot_new_clip_plane
+//       Access: Protected, Virtual
+//  Description: This will be called by the base class before a
+//               particular clip plane id will be used for the first
+//               time.  It is intended to allow the derived class to
+//               reserve any additional resources, if required, for
+//               the new clip plane; and also to indicate whether the
+//               hardware supports this many simultaneous clipping
+//               planes.
+//
+//               The return value should be true if the additional
+//               plane is supported, or false if it is not.
+////////////////////////////////////////////////////////////////////
+bool GraphicsStateGuardian::
+slot_new_clip_plane(int plane_id) {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::enable_clip_planes
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by a derived class to
+//               enable or disable the use of clipping planes overall.
+//               This is called by issue_clip_plane() according to
+//               whether any planes are in use or not.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+enable_clip_planes(bool enable) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::enable_clip_plane
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by a derived class to
+//               enable the indicated plane id.  A specific PlaneNode
+//               will already have been bound to this id via
+//               bind_clip_plane().
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+enable_clip_plane(int plane_id, bool enable) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::begin_bind_clip_planes
+//       Access: Protected, Virtual
+//  Description: Called immediately before bind_clip_plane() is called,
+//               this is intended to provide the derived class a hook
+//               in which to set up some state (like transform) that
+//               might apply to several planes.
+//
+//               The sequence is: begin_bind_clip_planes() will be
+//               called, then one or more bind_clip_plane() calls,
+//               then end_bind_clip_planes().
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+begin_bind_clip_planes() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::bind_clip_plane
+//       Access: Public, Virtual
+//  Description: Called the first time a particular clipping plane has been
+//               bound to a given id within a frame, this should set
+//               up the associated hardware (or API) clipping plane
+//               with the plane's properties.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+bind_clip_plane(PlaneNode *plane, int plane_id) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::end_bind_clip_planes
+//       Access: Protected, Virtual
+//  Description: Called after before bind_clip_plane() has been called one
+//               or more times (but before any geometry is issued or
+//               additional state is changed), this is intended to
+//               clean up any temporary changes to the state that may
+//               have been made by begin_bind_clip_planes().
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+end_bind_clip_planes() {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::set_blend_mode
 //       Access: Protected, Virtual
@@ -1248,6 +1470,20 @@ get_unlit_state() {
   return state;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_unclipped_state
+//       Access: Protected, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CPT(RenderState) GraphicsStateGuardian::
+get_unclipped_state() {
+  static CPT(RenderState) state = NULL;
+  if (state == (const RenderState *)NULL) {
+    state = RenderState::make(ClipPlaneAttrib::make_all_off());
+  }
+  return state;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_untextured_state
 //       Access: Protected, Static

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

@@ -35,6 +35,7 @@
 #include "transformState.h"
 #include "renderState.h"
 #include "light.h"
+#include "planeNode.h"
 #include "colorWriteAttrib.h"
 #include "colorBlendAttrib.h"
 #include "transparencyAttrib.h"
@@ -148,6 +149,7 @@ public:
   virtual void issue_color_write(const ColorWriteAttrib *attrib);
   virtual void issue_transparency(const TransparencyAttrib *attrib);
   virtual void issue_color_blend(const ColorBlendAttrib *attrib);
+  virtual void issue_clip_plane(const ClipPlaneAttrib *attrib);
 
   virtual void bind_light(PointLight *light, int light_id);
   virtual void bind_light(DirectionalLight *light, int light_id);
@@ -162,6 +164,14 @@ protected:
   virtual void begin_bind_lights();
   virtual void end_bind_lights();
 
+  INLINE PlaneNode *get_clip_plane(int plane_id) const;
+  virtual bool slot_new_clip_plane(int plane_id);
+  virtual void enable_clip_planes(bool enable);
+  virtual void enable_clip_plane(int plane_id, bool enable);
+  virtual void begin_bind_clip_planes();
+  virtual void bind_clip_plane(PlaneNode *plane, int pane_id);
+  virtual void end_bind_clip_planes();
+
   virtual void set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
                               ColorBlendAttrib::Mode color_blend_mode,
                               TransparencyAttrib::Mode transparency_mode);
@@ -199,6 +209,7 @@ protected:
 #endif
 
   static CPT(RenderState) get_unlit_state();
+  static CPT(RenderState) get_unclipped_state();
   static CPT(RenderState) get_untextured_state();
 
 protected:
@@ -233,6 +244,7 @@ protected:
   bool _scene_graph_color_stale;
   bool _vertex_colors_enabled;
   bool _lighting_enabled;
+  bool _clip_planes_enabled;
 
   enum ColorTransform {
     CT_offset  = 0x01,
@@ -285,6 +297,17 @@ private:
   pvector<LightInfo> _light_info;
   bool _lighting_enabled_this_frame;
 
+  class ClipPlaneInfo {
+  public:
+    INLINE ClipPlaneInfo();
+    PT(PlaneNode) _plane;
+    bool _enabled;
+    bool _next_enabled;
+  };
+
+  pvector<ClipPlaneInfo> _clip_plane_info;
+  bool _clip_planes_enabled_this_frame;
+
   // NOTE: on win32 another DLL (e.g. libpandadx.dll) cannot access
   // these sets directly due to exported template issue
   typedef pset<TextureContext *> Textures;

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

@@ -795,31 +795,6 @@ enable_scissor(bool val)
     }
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GLGraphicsStateGuardian::enable_clip_plane
-//       Access:
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void GLGraphicsStateGuardian::
-enable_clip_plane(int clip_plane, bool val) {
-  if (_clip_plane_enabled[clip_plane] != val) {
-    _clip_plane_enabled[clip_plane] = val;
-    if (val) {
-#ifdef GSG_VERBOSE
-      glgsg_cat.debug()
-        << "glEnable(GL_CLIP_PLANE_" << clip_plane << ")" << endl;
-#endif
-      glEnable(get_clip_plane_id(clip_plane));
-    } else {
-#ifdef GSG_VERBOSE
-      glgsg_cat.debug()
-        << "glDisable(GL_CLIP_PLANE_" << clip_plane << ")" << endl;
-#endif
-      glDisable(get_clip_plane_id(clip_plane));
-    }
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::enable_multisample_alpha_one
 //       Access:

+ 102 - 24
panda/src/glgsg/glGraphicsStateGuardian.cxx

@@ -34,6 +34,7 @@
 #include "directionalLight.h"
 #include "pointLight.h"
 #include "spotlight.h"
+#include "planeNode.h"
 #include "GL/glu.h"
 #include "textureAttrib.h"
 #include "lightAttrib.h"
@@ -128,9 +129,6 @@ issue_transformed_color_gl(const Geom *geom, Geom::ColorIterator &citerator,
 ////////////////////////////////////////////////////////////////////
 GLGraphicsStateGuardian::
 GLGraphicsStateGuardian(GraphicsWindow *win) : GraphicsStateGuardian(win) {
-  _clip_plane_enabled = (bool *)NULL;
-  _cur_clip_plane_enabled = (bool *)NULL;
-
   reset();
 }
 
@@ -256,18 +254,10 @@ reset() {
   glGetIntegerv(GL_MAX_LIGHTS, &max_lights);
   _max_lights = max_lights;
 
-  // Set up the clip plane id map
+  // Count the max number of clipping planes
   GLint max_clip_planes;
   glGetIntegerv(GL_MAX_CLIP_PLANES, &max_clip_planes);
   _max_clip_planes = max_clip_planes;
-  _available_clip_plane_ids = PTA(PlaneNode*)::empty_array(_max_clip_planes);
-  _clip_plane_enabled = new bool[_max_clip_planes];
-  _cur_clip_plane_enabled = new bool[_max_clip_planes];
-  int i;
-  for (i = 0; i < _max_clip_planes; i++) {
-    _available_clip_plane_ids[i] = NULL;
-    _clip_plane_enabled[i] = false;
-  }
 
   _current_projection_mat = LMatrix4f::ident_mat();
   _projection_mat_stack_count = 0;
@@ -3452,6 +3442,106 @@ end_bind_lights() {
   glPopMatrix();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::slot_new_clip_plane
+//       Access: Protected, Virtual
+//  Description: This will be called by the base class before a
+//               particular clip plane id will be used for the first
+//               time.  It is intended to allow the derived class to
+//               reserve any additional resources, if required, for
+//               the new clip plane; and also to indicate whether the
+//               hardware supports this many simultaneous clipping
+//               planes.
+//
+//               The return value should be true if the additional
+//               plane is supported, or false if it is not.
+////////////////////////////////////////////////////////////////////
+bool GLGraphicsStateGuardian::
+slot_new_clip_plane(int plane_id) {
+  return (plane_id < _max_clip_planes);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::enable_clip_plane
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by a derived class to
+//               enable the indicated clip_plane id.  A specific
+//               PlaneNode will already have been bound to this id via
+//               bind_clip_plane().
+////////////////////////////////////////////////////////////////////
+void GLGraphicsStateGuardian::
+enable_clip_plane(int plane_id, bool enable) {
+  if (enable) {
+    glEnable(get_clip_plane_id(plane_id));
+  } else {
+    glDisable(get_clip_plane_id(plane_id));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::begin_bind_clip_planes
+//       Access: Protected, Virtual
+//  Description: Called immediately before bind_clip_plane() is called,
+//               this is intended to provide the derived class a hook
+//               in which to set up some state (like transform) that
+//               might apply to several clip_planes.
+//
+//               The sequence is: begin_bind_clip_planes() will be called,
+//               then one or more bind_clip_plane() calls, then
+//               end_bind_clip_planes().
+////////////////////////////////////////////////////////////////////
+void GLGraphicsStateGuardian::
+begin_bind_clip_planes() {
+  // We need to temporarily load a new matrix so we can define the
+  // clip_plane in a known coordinate system.  We pick the transform of the
+  // root.  (Alternatively, we could leave the current transform where
+  // it is and compute the clip_plane position relative to that transform
+  // instead of relative to the root, by composing with the matrix
+  // computed by _transform->invert_compose(render_transform).  But I
+  // think loading a completely new matrix is simpler.)
+  glMatrixMode(GL_MODELVIEW);
+  glPushMatrix();
+  glLoadMatrixf(_scene_setup->get_render_transform()->get_mat().get_data());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::bind_clip_plane
+//       Access: Protected, Virtual
+//  Description: Called the first time a particular clip_plane has been
+//               bound to a given id within a frame, this should set
+//               up the associated hardware clip_plane with the clip_plane's
+//               properties.
+////////////////////////////////////////////////////////////////////
+void GLGraphicsStateGuardian::
+bind_clip_plane(PlaneNode *plane, int plane_id) {
+  GLenum id = get_clip_plane_id(plane_id);
+
+  NodePath plane_np(plane);
+  const LMatrix4f &plane_mat = plane_np.get_mat(_scene_setup->get_scene_root());
+  Planef xformed_plane = plane->get_plane() * plane_mat;
+
+  Planed double_plane(xformed_plane._a, xformed_plane._b, 
+                      xformed_plane._c, xformed_plane._d);
+  glClipPlane(id, double_plane.get_data());
+
+  report_errors();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::end_bind_clip_planes
+//       Access: Protected, Virtual
+//  Description: Called after before bind_clip_plane() has been called one
+//               or more times (but before any geometry is issued or
+//               additional state is changed), this is intended to
+//               clean up any temporary changes to the state that may
+//               have been made by begin_bind_clip_planes().
+////////////////////////////////////////////////////////////////////
+void GLGraphicsStateGuardian::
+end_bind_clip_planes() {
+  glMatrixMode(GL_MODELVIEW);
+  glPopMatrix();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::set_blend_mode
 //       Access: Protected, Virtual
@@ -3562,14 +3652,6 @@ set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
 ////////////////////////////////////////////////////////////////////
 void GLGraphicsStateGuardian::
 free_pointers() {
-  if (_clip_plane_enabled != (bool *)NULL) {
-    delete[] _clip_plane_enabled;
-    _clip_plane_enabled = (bool *)NULL;
-  }
-  if (_cur_clip_plane_enabled != (bool *)NULL) {
-    delete[] _cur_clip_plane_enabled;
-    _cur_clip_plane_enabled = (bool *)NULL;
-  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -3859,10 +3941,6 @@ dump_state(void)
       dump << "\t\t" << "GL_TEXTURE_2D " << _texturing_enabled << " " << (bool)glIsEnabled(GL_TEXTURE_2D) << "\n";
       dump << "\t\t" << "GL_DITHER " << _dither_enabled << " " << (bool)glIsEnabled(GL_DITHER) << "\n";
       dump << "\t\t" << "GL_STENCIL_TEST " << " " << (bool)glIsEnabled(GL_STENCIL_TEST) << "\n";
-      for(i = 0; i < _max_clip_planes; i++)
-        {
-          dump << "\t\t\t\t" << "GL_CLIP_PLANE" << i << " " << _clip_plane_enabled[i] << " " << (bool)glIsEnabled(GL_CLIP_PLANE0+i) << "\n";
-        }
       dump << "\t\t" << "GL_BLEND " << _blend_enabled << " " << (bool)glIsEnabled(GL_BLEND) << "\n";
       dump << "\t\t" << "GL_DEPTH_TEST " << _depth_test_enabled << " " << (bool)glIsEnabled(GL_DEPTH_TEST) << "\n";
       dump << "\t\t" << "GL_FOG " << _fog_enabled << " " << (bool)glIsEnabled(GL_FOG) << "\n";

+ 7 - 8
panda/src/glgsg/glGraphicsStateGuardian.h

@@ -123,7 +123,6 @@ public:
   virtual void issue_depth_offset(const DepthOffsetAttrib *attrib);
   //  virtual void issue_tex_gen(const TexGenAttrib *attrib);
   //  virtual void issue_stencil(const StencilAttrib *attrib);
-  //  virtual void issue_clip_plane(const ClipPlaneAttrib *attrib);
 
   virtual void bind_light(PointLight *light, int light_id);
   virtual void bind_light(DirectionalLight *light, int light_id);
@@ -153,6 +152,12 @@ protected:
   virtual void begin_bind_lights();
   virtual void end_bind_lights();
 
+  virtual bool slot_new_clip_plane(int plane_id);
+  virtual void enable_clip_plane(int plane_id, bool enable);
+  virtual void begin_bind_clip_planes();
+  virtual void bind_clip_plane(PlaneNode *plane, int plane_id);
+  virtual void end_bind_clip_planes();
+
   virtual void set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
                               ColorBlendAttrib::Mode color_blend_mode,
                               TransparencyAttrib::Mode transparency_mode);
@@ -202,7 +207,6 @@ protected:
   INLINE void enable_scissor(bool val);
   INLINE void enable_dither(bool val);
   INLINE void enable_stencil_test(bool val);
-  INLINE void enable_clip_plane(int clip_plane, bool val);
   INLINE void enable_multisample_alpha_one(bool val);
   INLINE void enable_multisample_alpha_mask(bool val);
   INLINE void enable_blend(bool val);
@@ -307,16 +311,11 @@ protected:
   int _decal_level;
 
   int _max_lights;
+  int _max_clip_planes;
 
   LMatrix4f _current_projection_mat;
   int _projection_mat_stack_count;
 
-  int _max_clip_planes;
-  PTA(PlaneNode *)_available_clip_plane_ids;  // pPlaneNode[_max_clip_planes]
-  bool *_clip_plane_enabled;              // bool[_max_clip_planes]
-  bool *_cur_clip_plane_enabled;          // bool[_max_clip_planes]
-  int _cur_clip_plane_id;
-
   CPT(DisplayRegion) _actual_display_region;
 
   int _pass_number;

+ 57 - 5
panda/src/mathutil/plane_src.I

@@ -73,7 +73,8 @@ FLOATNAME(Plane)(const FLOATNAME(LPoint3) &a, const FLOATNAME(LPoint3) &b,
 //               a point within the plane.
 ////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL FLOATNAME(Plane)::
-FLOATNAME(Plane)(const FLOATNAME(LVector3) &normal, const FLOATNAME(LPoint3) &point) {
+FLOATNAME(Plane)(const FLOATNAME(LVector3) &normal, 
+                 const FLOATNAME(LPoint3) &point) {
   FLOATNAME(LVector3) p = normalize(normal);
 
   _a = p[0];
@@ -82,6 +83,21 @@ FLOATNAME(Plane)(const FLOATNAME(LVector3) &normal, const FLOATNAME(LPoint3) &po
   _d = -dot(p, point);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Plane::Constructor
+//       Access: Public
+//  Description: Constructs a plane given the four terms of the plane
+//               equation.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL FLOATNAME(Plane)::
+FLOATNAME(Plane)(FLOATTYPE a, FLOATTYPE b, FLOATTYPE c, FLOATTYPE d) :
+  _a(a),
+  _b(b),
+  _c(c),
+  _d(d)
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Plane::Operator =
 //       Access: Public
@@ -197,6 +213,30 @@ intersects_line(FLOATTYPE &t,
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Plane::get_data
+//       Access: Public
+//  Description: Returns the address of the first of the four data
+//               elements in the plane equation.  The remaining
+//               elements occupy the next positions consecutively in
+//               memory.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL const FLOATTYPE *FLOATNAME(Plane)::
+get_data() const {
+  return &_a;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Plane::get_num_components
+//       Access: Public
+//  Description: Returns the number of elements in the plane equation,
+//               four.
+////////////////////////////////////////////////////////////////////
+INLINE_MATHUTIL int FLOATNAME(Plane)::
+get_num_components() const {
+  return 4;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Plane::output
 //       Access: Public
@@ -223,12 +263,18 @@ write(ostream &out, int indent_level) const {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL void FLOATNAME(Plane)::
-write_datagram(Datagram &dest)
-{
+write_datagram(Datagram &dest) const {
+#if FLOATTOKEN == 'f'
   dest.add_float32(_a);
   dest.add_float32(_b);
   dest.add_float32(_c);
   dest.add_float32(_d);
+#else
+  dest.add_float64(_a);
+  dest.add_float64(_b);
+  dest.add_float64(_c);
+  dest.add_float64(_d);
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -237,10 +283,16 @@ write_datagram(Datagram &dest)
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE_MATHUTIL void FLOATNAME(Plane)::
-read_datagram(DatagramIterator &source)
-{
+read_datagram(DatagramIterator &source) {
+#if FLOATTOKEN == 'f'
   _a = source.get_float32();
   _b = source.get_float32();
   _c = source.get_float32();
   _d = source.get_float32();
+#else
+  _a = source.get_float64();
+  _b = source.get_float64();
+  _c = source.get_float64();
+  _d = source.get_float64();
+#endif
 }

+ 10 - 4
panda/src/mathutil/plane_src.h

@@ -24,10 +24,13 @@ class EXPCL_PANDA FLOATNAME(Plane) {
 PUBLISHED:
   INLINE_MATHUTIL FLOATNAME(Plane)(void);
   INLINE_MATHUTIL FLOATNAME(Plane)(const FLOATNAME(Plane) &copy);
-  INLINE_MATHUTIL FLOATNAME(Plane)(const FLOATNAME(LPoint3) &a, const FLOATNAME(LPoint3) &b,
-                          const FLOATNAME(LPoint3) &c);
+  INLINE_MATHUTIL FLOATNAME(Plane)(const FLOATNAME(LPoint3) &a, 
+                                   const FLOATNAME(LPoint3) &b,
+                                   const FLOATNAME(LPoint3) &c);
   INLINE_MATHUTIL FLOATNAME(Plane)(const FLOATNAME(LVector3) &normal,
-                          const FLOATNAME(LPoint3) &point);
+                                   const FLOATNAME(LPoint3) &point);
+  INLINE_MATHUTIL FLOATNAME(Plane)(FLOATTYPE a, FLOATTYPE b,
+                                   FLOATTYPE c, FLOATTYPE d);
 
   INLINE_MATHUTIL FLOATNAME(Plane)& operator = (const FLOATNAME(Plane)& copy);
 
@@ -47,11 +50,14 @@ PUBLISHED:
                               const FLOATNAME(LPoint3) &from,
                               const FLOATNAME(LVector3) &delta) const;
 
+  INLINE_MATHUTIL const FLOATTYPE *get_data() const;
+  INLINE_MATHUTIL int get_num_components() const;
+
   INLINE_MATHUTIL void output(ostream &out) const;
   INLINE_MATHUTIL void write(ostream &out, int indent_level = 0) const;
 
 public:
-  INLINE_MATHUTIL void write_datagram(Datagram &dest);
+  INLINE_MATHUTIL void write_datagram(Datagram &dest) const;
   INLINE_MATHUTIL void read_datagram(DatagramIterator &source);
 
 public:

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

@@ -14,6 +14,7 @@
     billboardEffect.I billboardEffect.h \
     binCullHandler.I binCullHandler.h \
     camera.I camera.h \
+    clipPlaneAttrib.I clipPlaneAttrib.h \
     colorAttrib.I colorAttrib.h \
     colorBlendAttrib.I colorBlendAttrib.h \
     colorScaleAttrib.I colorScaleAttrib.h \
@@ -64,6 +65,7 @@
     nodePathComponent.I nodePathComponent.h \
     nodePathLerps.h \
     pandaNode.I pandaNode.h \
+    planeNode.I planeNode.h \
     pointLight.I pointLight.h \
     renderAttrib.I renderAttrib.h \
     renderEffect.I renderEffect.h \
@@ -93,6 +95,7 @@
     billboardEffect.cxx \
     binCullHandler.cxx \
     camera.cxx \
+    clipPlaneAttrib.cxx \
     colorAttrib.cxx \
     colorBlendAttrib.cxx \
     colorScaleAttrib.cxx \
@@ -143,6 +146,7 @@
     nodePathComponent.cxx \
     nodePathLerps.cxx \
     pandaNode.cxx \
+    planeNode.cxx \
     pointLight.cxx \
     renderAttrib.cxx \
     renderEffect.cxx \
@@ -171,6 +175,7 @@
     billboardEffect.I billboardEffect.h \
     binCullHandler.I binCullHandler.h \
     camera.I camera.h \
+    clipPlaneAttrib.I clipPlaneAttrib.h \
     colorAttrib.I colorAttrib.h \
     colorBlendAttrib.I colorBlendAttrib.h \
     colorScaleAttrib.I colorScaleAttrib.h \
@@ -218,6 +223,7 @@
     nodePathComponent.I nodePathComponent.h \
     nodePathLerps.h \
     pandaNode.I pandaNode.h \
+    planeNode.I planeNode.h \
     pointLight.I pointLight.h \
     renderAttrib.I renderAttrib.h \
     renderEffect.I renderEffect.h \

+ 119 - 0
panda/src/pgraph/clipPlaneAttrib.I

@@ -0,0 +1,119 @@
+// Filename: clipPlaneAttrib.I
+// Created by:  drose (11Jul02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::Constructor
+//       Access: Private
+//  Description: Use ClipPlaneAttrib::make() to construct a new
+//               ClipPlaneAttrib object.
+////////////////////////////////////////////////////////////////////
+INLINE ClipPlaneAttrib::
+ClipPlaneAttrib() {
+  _operation = O_set;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::get_operation
+//       Access: Published
+//  Description: Returns the basic operation type of the ClipPlaneAttrib.
+//               If this is O_set, the planes listed here completely
+//               replace any planes that were already on.  If this is
+//               O_add, the planes here are added to the set of of
+//               planes that were already on, and if O_remove, the
+//               planes here are removed from the set of planes that
+//               were on.
+////////////////////////////////////////////////////////////////////
+INLINE ClipPlaneAttrib::Operation ClipPlaneAttrib::
+get_operation() const {
+  return _operation;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::get_num_planes
+//       Access: Published
+//  Description: Returns the number of planes listed in the attribute.
+////////////////////////////////////////////////////////////////////
+INLINE int ClipPlaneAttrib::
+get_num_planes() const {
+  return _planes.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::get_plane
+//       Access: Published
+//  Description: Returns the nth planes listed in the attribute.
+////////////////////////////////////////////////////////////////////
+INLINE PlaneNode *ClipPlaneAttrib::
+get_plane(int n) const {
+  nassertr(n >= 0 && n < (int)_planes.size(), (PlaneNode *)NULL);
+  return _planes[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::add_plane
+//       Access: Published
+//  Description: Returns a new ClipPlaneAttrib, just like this one, but
+//               with the indicated plane added to the list of planes.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(RenderAttrib) ClipPlaneAttrib::
+add_plane(PlaneNode *plane) const {
+  if (_operation == O_remove) {  
+    return compose(make(O_remove, plane));
+  } else {
+    return compose(make(O_add, plane));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::remove_plane
+//       Access: Published
+//  Description: Returns a new ClipPlaneAttrib, just like this one, but
+//               with the indicated plane removed from the list of
+//               planes.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(RenderAttrib) ClipPlaneAttrib::
+remove_plane(PlaneNode *plane) const {
+  if (_operation == O_remove) {  
+    return compose(make(O_add, plane));
+  } else {
+    return compose(make(O_remove, plane));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::is_identity
+//       Access: Published
+//  Description: Returns true if this is an identity attrib: it does
+//               not change the set of planes in use.
+////////////////////////////////////////////////////////////////////
+INLINE bool ClipPlaneAttrib::
+is_identity() const {
+  return _operation != O_set && _planes.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::is_all_off
+//       Access: Published
+//  Description: Returns true if this attrib turns off all planes and
+//               turns none on.
+////////////////////////////////////////////////////////////////////
+INLINE bool ClipPlaneAttrib::
+is_all_off() const {
+  return _operation == O_set && _planes.empty();
+}

+ 489 - 0
panda/src/pgraph/clipPlaneAttrib.cxx

@@ -0,0 +1,489 @@
+// Filename: clipPlaneAttrib.cxx
+// Created by:  drose (11Jul02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "clipPlaneAttrib.h"
+#include "pandaNode.h"
+#include "graphicsStateGuardianBase.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle ClipPlaneAttrib::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::make_all_off
+//       Access: Published, Static
+//  Description: Constructs a new ClipPlaneAttrib object that turns off
+//               all planes (and hence disables planeing).
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+make_all_off() {
+  ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
+  attrib->_operation = O_set;
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new ClipPlaneAttrib object that turns on (or
+//               off, according to op) the indicate plane(s).
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+make(ClipPlaneAttrib::Operation op, PlaneNode *plane) {
+  ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
+  attrib->_operation = op;
+  attrib->_planes.push_back(plane);
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new ClipPlaneAttrib object that turns on (or
+//               off, according to op) the indicate plane(s).
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2) {
+  ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
+  attrib->_operation = op;
+  attrib->_planes.push_back(plane1);
+  attrib->_planes.push_back(plane2);
+
+  attrib->_planes.sort();
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new ClipPlaneAttrib object that turns on (or
+//               off, according to op) the indicate plane(s).
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2,
+     PlaneNode *plane3) {
+  ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
+  attrib->_operation = op;
+  attrib->_planes.push_back(plane1);
+  attrib->_planes.push_back(plane2);
+  attrib->_planes.push_back(plane3);
+
+  attrib->_planes.sort();
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new ClipPlaneAttrib object that turns on (or
+//               off, according to op) the indicate plane(s).
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2,
+     PlaneNode *plane3, PlaneNode *plane4) {
+  ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
+  attrib->_operation = op;
+  attrib->_planes.push_back(plane1);
+  attrib->_planes.push_back(plane2);
+  attrib->_planes.push_back(plane3);
+  attrib->_planes.push_back(plane4);
+
+  attrib->_planes.sort();
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::has_plane
+//       Access: Published
+//  Description: Returns true if the indicated plane is listed in the
+//               attrib, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ClipPlaneAttrib::
+has_plane(PlaneNode *plane) const {
+  return _planes.find(plane) != _planes.end();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::issue
+//       Access: Public, Virtual
+//  Description: Calls the appropriate method on the indicated GSG
+//               to issue the graphics commands appropriate to the
+//               given attribute.  This is normally called
+//               (indirectly) only from
+//               GraphicsStateGuardian::set_state() or modify_state().
+////////////////////////////////////////////////////////////////////
+void ClipPlaneAttrib::
+issue(GraphicsStateGuardianBase *gsg) const {
+  gsg->issue_clip_plane(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ClipPlaneAttrib::
+output(ostream &out) const {
+  out << get_type() << ":";
+  if (_operation == O_set && _planes.empty()) {
+    out << "all off";
+  } else {
+    switch (_operation) {
+    case O_set:
+      out << "set";
+      break;
+    case O_add:
+      out << "add";
+      break;
+    case O_remove:
+      out << "remove";
+      break;
+    }
+
+    Planes::const_iterator li;
+    for (li = _planes.begin(); li != _planes.end(); ++li) {
+      PlaneNode *plane = (*li);
+      out << " " << *plane;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived ClipPlaneAttrib
+//               types to return a unique number indicating whether
+//               this ClipPlaneAttrib is equivalent to the other one.
+//
+//               This should return 0 if the two ClipPlaneAttrib objects
+//               are equivalent, a number less than zero if this one
+//               should be sorted before the other one, and a number
+//               greater than zero otherwise.
+//
+//               This will only be called with two ClipPlaneAttrib
+//               objects whose get_type() functions return the same.
+////////////////////////////////////////////////////////////////////
+int ClipPlaneAttrib::
+compare_to_impl(const RenderAttrib *other) const {
+  const ClipPlaneAttrib *ta;
+  DCAST_INTO_R(ta, other, 0);
+
+  if (_operation != ta->_operation) {
+    return (int)_operation - (int)ta->_operation;
+  }
+
+  Planes::const_iterator li = _planes.begin();
+  Planes::const_iterator oli = ta->_planes.begin();
+
+  while (li != _planes.end() && oli != ta->_planes.end()) {
+    PlaneNode *plane = (*li);
+    PlaneNode *other_plane = (*oli);
+
+    if (plane != other_plane) {
+      return plane < other_plane ? -1 : 1;
+    }
+
+    ++li;
+    ++oli;
+  }
+
+  if (li != _planes.end()) {
+    return 1;
+  }
+  if (oli != ta->_planes.end()) {
+    return -1;
+  }
+  
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::compose_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to specify how two consecutive RenderAttrib
+//               objects of the same type interact.
+//
+//               This should return the result of applying the other
+//               RenderAttrib to a node in the scene graph below this
+//               RenderAttrib, which was already applied.  In most
+//               cases, the result is the same as the other
+//               RenderAttrib (that is, a subsequent RenderAttrib
+//               completely replaces the preceding one).  On the other
+//               hand, some kinds of RenderAttrib (for instance,
+//               ColorTransformAttrib) might combine in meaningful
+//               ways.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+compose_impl(const RenderAttrib *other) const {
+  const ClipPlaneAttrib *ta;
+  DCAST_INTO_R(ta, other, 0);
+
+  if (ta->_operation == O_set) {
+    // If the other type is O_set, it doesn't matter what we are.
+    return ta;
+  }
+
+  if (_operation == ta->_operation) {
+    // If the operation types match, the composition is simply the
+    // union.
+    return do_add(ta, _operation);
+
+  } else if (ta->_operation == O_remove) {
+    // If the other operation type is remove, and our type is add or
+    // set, then remove.
+    return do_remove(ta, _operation);
+
+  } else if (_operation == O_remove) {
+    // If our type is remove, then the other one wins.
+    return ta;
+
+  } else {
+    // Otherwise, the result is the union.
+    return do_add(ta, _operation);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::invert_compose_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to specify how two consecutive RenderAttrib
+//               objects of the same type interact.
+//
+//               See invert_compose() and compose_impl().
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+invert_compose_impl(const RenderAttrib *other) const {
+  // I think in this case the other attrib always wins.  Maybe this
+  // needs a bit more thought.  It's hard to imagine that it's even
+  // important to compute this properly.
+  return other;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::make_default_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived ClipPlaneAttrib
+//               types to specify what the default property for a
+//               ClipPlaneAttrib of this type should be.
+//
+//               This should return a newly-allocated ClipPlaneAttrib of
+//               the same type that corresponds to whatever the
+//               standard default for this kind of ClipPlaneAttrib is.
+////////////////////////////////////////////////////////////////////
+RenderAttrib *ClipPlaneAttrib::
+make_default_impl() const {
+  return new ClipPlaneAttrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::do_add
+//       Access: Private
+//  Description: Returns a new ClipPlaneAttrib that represents all the
+//               planes of this attrib, with those of the other one
+//               added in.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+do_add(const ClipPlaneAttrib *other, ClipPlaneAttrib::Operation op) const {
+  Planes::const_iterator ai = _planes.begin();
+  Planes::const_iterator bi = other->_planes.begin();
+
+  // Create a new ClipPlaneAttrib that will hold the result.
+  ClipPlaneAttrib *new_attrib = new ClipPlaneAttrib;
+  new_attrib->_operation = op;
+  back_insert_iterator<Planes> result = 
+    back_inserter(new_attrib->_planes);
+
+  while (ai != _planes.end() && bi != other->_planes.end()) {
+    if ((*ai) < (*bi)) {
+      // Here is a plane that we have in the original, which is not
+      // present in the secondary.
+      *result = *ai;
+      ++ai;
+      ++result;
+    } else if ((*bi) < (*ai)) {
+      // Here is a new plane we have in the secondary, that was not
+      // present in the original.
+      *result = *bi;
+      ++bi;
+      ++result;
+    } else {
+      // Here is a plane we have in both.
+      *result = *ai;
+      ++ai;
+      ++bi;
+      ++result;
+    }
+  }
+
+  while (ai != _planes.end()) {
+    *result = *ai;
+    ++ai;
+    ++result;
+  }
+
+  while (bi != other->_planes.end()) {
+    *result = *bi;
+    ++bi;
+    ++result;
+  }
+
+  return return_new(new_attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::do_remove
+//       Access: Private
+//  Description: Returns a new ClipPlaneAttrib that represents all the
+//               planes of this attrib, with those of the other one
+//               removed.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+do_remove(const ClipPlaneAttrib *other, ClipPlaneAttrib::Operation op) const {
+  Planes::const_iterator ai = _planes.begin();
+  Planes::const_iterator bi = other->_planes.begin();
+
+  // Create a new ClipPlaneAttrib that will hold the result.
+  ClipPlaneAttrib *new_attrib = new ClipPlaneAttrib;
+  new_attrib->_operation = op;
+  back_insert_iterator<Planes> result = 
+    back_inserter(new_attrib->_planes);
+
+  while (ai != _planes.end() && bi != other->_planes.end()) {
+    if ((*ai) < (*bi)) {
+      // Here is a plane that we have in the original, which is
+      // not present in the secondary.  Keep it.
+      *result = *ai;
+      ++ai;
+      ++result;
+    } else if ((*bi) < (*ai)) {
+      // Here is a new plane we have in the secondary, that was
+      // not present in the original.  Ignore it.
+      ++bi;
+    } else {
+      // Here is a plane we have in both.  Drop it.
+      ++ai;
+      ++bi;
+    }
+  }
+
+  while (ai != _planes.end()) {
+    *result = *ai;
+    ++ai;
+    ++result;
+  }
+
+  return return_new(new_attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               ClipPlaneAttrib.
+////////////////////////////////////////////////////////////////////
+void ClipPlaneAttrib::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void ClipPlaneAttrib::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  RenderAttrib::write_datagram(manager, dg);
+
+  dg.add_int8((int)_operation);
+  PN_uint16 num_planes = _planes.size();
+  nassertv(num_planes == _planes.size());
+  dg.add_uint16(num_planes);
+
+  Planes::const_iterator li;
+  for (li = _planes.begin(); li != _planes.end(); ++li) {
+    PlaneNode *plane = (*li);
+    manager->write_pointer(dg, plane);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int ClipPlaneAttrib::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = RenderAttrib::complete_pointers(p_list, manager);
+
+  Planes::iterator li;
+  for (li = _planes.begin(); li != _planes.end(); ++li) {
+    PlaneNode *node;
+    DCAST_INTO_R(node, p_list[pi++], pi);
+    (*li) = node;
+  }
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type ClipPlaneAttrib is encountered
+//               in the Bam file.  It should create the ClipPlaneAttrib
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *ClipPlaneAttrib::
+make_from_bam(const FactoryParams &params) {
+  ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  attrib->fillin(scan, manager);
+
+  return attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::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 ClipPlaneAttrib.
+////////////////////////////////////////////////////////////////////
+void ClipPlaneAttrib::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  RenderAttrib::fillin(scan, manager);
+
+  _operation = (Operation)scan.get_int8();
+  int num_planes = scan.get_uint16();
+
+  for (int i = 0; i < num_planes; i++) {
+    manager->read_pointer(scan);
+    _planes.push_back(NULL);
+  }
+}

+ 120 - 0
panda/src/pgraph/clipPlaneAttrib.h

@@ -0,0 +1,120 @@
+// Filename: clipPlaneAttrib.h
+// Created by:  drose (11Jul02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CLIPPINGPLANEATTRIB_H
+#define CLIPPINGPLANEATTRIB_H
+
+#include "pandabase.h"
+
+#include "planeNode.h"
+#include "renderAttrib.h"
+#include "ordered_vector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ClipPlaneAttrib
+// Description : This functions similarly to a LightAttrib.  It
+//               indicates the set of clipping planes that modify the
+//               geometry at this level and below.  A ClipPlaneAttrib
+//               can either add planes or remove planes from the total
+//               set of clipping planes in effect.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ClipPlaneAttrib : public RenderAttrib {
+private:
+  INLINE ClipPlaneAttrib();
+
+PUBLISHED:
+  enum Operation {
+    O_set,
+    O_add,
+    O_remove
+  };
+
+  static CPT(RenderAttrib) make_all_off();
+  static CPT(RenderAttrib) make(Operation op, 
+                                PlaneNode *plane);
+  static CPT(RenderAttrib) make(Operation op, 
+                                PlaneNode *plane1, PlaneNode *plane2);
+  static CPT(RenderAttrib) make(Operation op, 
+                                PlaneNode *plane1, PlaneNode *plane2,
+                                PlaneNode *plane3);
+  static CPT(RenderAttrib) make(Operation op, 
+                                PlaneNode *plane1, PlaneNode *plane2,
+                                PlaneNode *plane3, PlaneNode *plane4);
+
+  INLINE Operation get_operation() const;
+
+  INLINE int get_num_planes() const;
+  INLINE PlaneNode *get_plane(int n) const;
+  bool has_plane(PlaneNode *plane) const;
+
+  INLINE CPT(RenderAttrib) add_plane(PlaneNode *plane) const;
+  INLINE CPT(RenderAttrib) remove_plane(PlaneNode *plane) const;
+
+  INLINE bool is_identity() const;
+  INLINE bool is_all_off() const;
+
+public:
+  virtual void issue(GraphicsStateGuardianBase *gsg) const;
+  virtual void output(ostream &out) const;
+
+protected:
+  virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
+  virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
+  virtual RenderAttrib *make_default_impl() const;
+
+private:
+  CPT(RenderAttrib) do_add(const ClipPlaneAttrib *other, Operation op) const;
+  CPT(RenderAttrib) do_remove(const ClipPlaneAttrib *other, Operation op) const;
+
+private:
+  Operation _operation;
+  typedef ov_set< PT(PlaneNode) > Planes;
+  Planes _planes;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    RenderAttrib::init_type();
+    register_type(_type_handle, "ClipPlaneAttrib",
+                  RenderAttrib::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "clipPlaneAttrib.I"
+
+#endif
+

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

@@ -22,6 +22,7 @@
 #include "ambientLight.h"
 #include "billboardEffect.h"
 #include "camera.h"
+#include "clipPlaneAttrib.h"
 #include "colorAttrib.h"
 #include "colorBlendAttrib.h"
 #include "colorScaleAttrib.h"
@@ -58,6 +59,7 @@
 #include "nodePath.h"
 #include "nodePathComponent.h"
 #include "pandaNode.h"
+#include "planeNode.h"
 #include "pointLight.h"
 #include "renderAttrib.h"
 #include "renderEffect.h"
@@ -160,6 +162,7 @@ init_libpgraph() {
   AmbientLight::init_type();
   BillboardEffect::init_type();
   Camera::init_type();
+  ClipPlaneAttrib::init_type();
   ColorAttrib::init_type();
   ColorBlendAttrib::init_type();
   ColorScaleAttrib::init_type();
@@ -195,6 +198,7 @@ init_libpgraph() {
   NodePath::init_type();
   NodePathComponent::init_type();
   PandaNode::init_type();
+  PlaneNode::init_type();
   PointLight::init_type();
   RenderAttrib::init_type();
   RenderEffect::init_type();
@@ -225,6 +229,7 @@ init_libpgraph() {
   AmbientLight::register_with_read_factory();
   BillboardEffect::register_with_read_factory();
   Camera::register_with_read_factory();
+  ClipPlaneAttrib::register_with_read_factory();
   ColorAttrib::register_with_read_factory();
   ColorBlendAttrib::register_with_read_factory();
   ColorScaleAttrib::register_with_read_factory();
@@ -246,6 +251,7 @@ init_libpgraph() {
   ModelNode::register_with_read_factory();
   ModelRoot::register_with_read_factory();
   PandaNode::register_with_read_factory();
+  PlaneNode::register_with_read_factory();
   PointLight::register_with_read_factory();
   RenderEffects::register_with_read_factory();
   RenderModeAttrib::register_with_read_factory();

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

@@ -3,6 +3,7 @@
 #include "billboardEffect.cxx"
 #include "binCullHandler.cxx"
 #include "camera.cxx"
+#include "clipPlaneAttrib.cxx"
 #include "colorAttrib.cxx"
 #include "colorBlendAttrib.cxx"
 #include "colorScaleAttrib.cxx"

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

@@ -16,6 +16,7 @@
 #include "nodePathComponent.cxx"
 #include "nodePathLerps.cxx"
 #include "pandaNode.cxx"
+#include "planeNode.cxx"
 #include "pointLight.cxx"
 #include "renderAttrib.cxx"
 #include "renderEffect.cxx"

+ 63 - 0
panda/src/pgraph/planeNode.I

@@ -0,0 +1,63 @@
+// Filename: planeNode.I
+// Created by:  drose (11Jul02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PlaneNode::CData::
+CData() {
+  // The default plane (perpendicular to the Z-axis) is used until
+  // another one is specified explicitly.
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PlaneNode::CData::
+CData(const PlaneNode::CData &copy) :
+  _plane(copy._plane)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: set_plane
+//       Access: Public
+//  Description: Sets the particular plane represented by the
+//               PlaneNode.
+////////////////////////////////////////////////////////////////////
+INLINE void PlaneNode::
+set_plane(const Planef &plane) {
+  CDWriter cdata(_cycler);
+  cdata->_plane = plane;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: get_plane
+//       Access: Public
+//  Description: Returns the plane represented by the PlaneNode.
+////////////////////////////////////////////////////////////////////
+INLINE const Planef &PlaneNode::
+get_plane() const {
+  CDReader cdata(_cycler);
+  return cdata->_plane;
+}

+ 187 - 0
panda/src/pgraph/planeNode.cxx

@@ -0,0 +1,187 @@
+// Filename: planeNode.cxx
+// Created by:  drose (11Jul02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "planeNode.h"
+#include "geometricBoundingVolume.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle PlaneNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *PlaneNode::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void PlaneNode::CData::
+write_datagram(BamWriter *, Datagram &dg) const {
+  _plane.write_datagram(dg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::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 Light.
+////////////////////////////////////////////////////////////////////
+void PlaneNode::CData::
+fillin(DatagramIterator &scan, BamReader *) {
+  _plane.read_datagram(scan);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PlaneNode::
+PlaneNode(const string &name) :
+  PandaNode(name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+PlaneNode::
+PlaneNode(const PlaneNode &copy) :
+  PandaNode(copy),
+  _cycler(copy._cycler)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::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 *PlaneNode::
+make_copy() const {
+  return new PlaneNode(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::xform
+//       Access: Public, Virtual
+//  Description: Transforms the contents of this PandaNode by the
+//               indicated matrix, if it means anything to do so.  For
+//               most kinds of PandaNodes, this does nothing.
+////////////////////////////////////////////////////////////////////
+void PlaneNode::
+xform(const LMatrix4f &mat) {
+  PandaNode::xform(mat);
+  CDWriter cdata(_cycler);
+  cdata->_plane = cdata->_plane * mat;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void PlaneNode::
+output(ostream &out) const {
+  PandaNode::output(out);
+  out << " " << get_plane();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void PlaneNode::
+write(ostream &out, int indent_level) const {
+  PandaNode::write(out, indent_level);
+  get_plane().write(out, indent_level + 2);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               PlaneNode.
+////////////////////////////////////////////////////////////////////
+void PlaneNode::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void PlaneNode::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  PandaNode::write_datagram(manager, dg);
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type PlaneNode is encountered
+//               in the Bam file.  It should create the PlaneNode
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *PlaneNode::
+make_from_bam(const FactoryParams &params) {
+  PlaneNode *node = new PlaneNode("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::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 PlaneNode.
+////////////////////////////////////////////////////////////////////
+void PlaneNode::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  PandaNode::fillin(scan, manager);
+  manager->read_cdata(scan, _cycler);
+}

+ 96 - 0
panda/src/pgraph/planeNode.h

@@ -0,0 +1,96 @@
+// Filename: planeNode.h
+// Created by:  drose (11Jul02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PLANENODE_H
+#define PLANENODE_H
+
+#include "pandabase.h"
+
+#include "plane.h"
+#include "pandaNode.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : PlaneNode
+// Description : A node that contains a plane.  This is most often
+//               used as a clipping plane, but it can serve other
+//               purposes as well; whenever a plane is needed to be
+//               defined in some coordinate space in the world.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA PlaneNode : public PandaNode {
+PUBLISHED:
+  PlaneNode(const string &name);
+
+protected:
+  PlaneNode(const PlaneNode &copy);
+public:
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+  virtual PandaNode *make_copy() const;
+  virtual void xform(const LMatrix4f &mat);
+
+PUBLISHED:
+  INLINE void set_plane(const Planef &plane);
+  INLINE const Planef &get_plane() const;
+
+private:
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA 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);
+
+    Planef _plane;
+  };
+
+  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, "PlaneNode",
+                  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 "planeNode.I"
+
+#endif