Pārlūkot izejas kodu

new NodePath::set_attrib(), set_clip_plane(), etc.

David Rose 20 gadi atpakaļ
vecāks
revīzija
10242ea013

+ 74 - 58
panda/src/pgraph/clipPlaneAttrib.I

@@ -19,101 +19,117 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ClipPlaneAttrib::Constructor
 //     Function: ClipPlaneAttrib::Constructor
-//       Access: Private
+//       Access: Protected
 //  Description: Use ClipPlaneAttrib::make() to construct a new
 //  Description: Use ClipPlaneAttrib::make() to construct a new
 //               ClipPlaneAttrib object.
 //               ClipPlaneAttrib object.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE ClipPlaneAttrib::
 INLINE ClipPlaneAttrib::
 ClipPlaneAttrib() {
 ClipPlaneAttrib() {
-  _operation = O_set;
+  _off_all_planes = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     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::Copy Constructor
+//       Access: Protected
+//  Description: Use ClipPlaneAttrib::make() to construct a new
+//               ClipPlaneAttrib object.  The copy constructor is only
+//               defined to facilitate methods like add_on_plane().
+////////////////////////////////////////////////////////////////////
+INLINE ClipPlaneAttrib::
+ClipPlaneAttrib(const ClipPlaneAttrib &copy) :
+  _on_planes(copy._on_planes),
+  _off_planes(copy._off_planes),
+  _off_all_planes(copy._off_all_planes)
+{
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: ClipPlaneAttrib::get_num_planes
+//     Function: ClipPlaneAttrib::get_num_on_planes
 //       Access: Published
 //       Access: Published
-//  Description: Returns the number of planes listed in the attribute.
+//  Description: Returns the number of planes that are enabled by
+//               the attribute.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int ClipPlaneAttrib::
 INLINE int ClipPlaneAttrib::
-get_num_planes() const {
-  return _planes.size();
+get_num_on_planes() const {
+  return _on_planes.size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: ClipPlaneAttrib::get_plane
+//     Function: ClipPlaneAttrib::get_on_plane
 //       Access: Published
 //       Access: Published
-//  Description: Returns the nth planes listed in the attribute.
+//  Description: Returns the nth plane enabled by the attribute,
+//               sorted in render order.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE PlaneNode *ClipPlaneAttrib::
-get_plane(int n) const {
-  nassertr(n >= 0 && n < (int)_planes.size(), (PlaneNode *)NULL);
-  return _planes[n];
+INLINE NodePath ClipPlaneAttrib::
+get_on_plane(int n) const {
+  nassertr(n >= 0 && n < (int)_on_planes.size(), NodePath::fail());
+  return _on_planes[n];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: ClipPlaneAttrib::add_plane
+//     Function: ClipPlaneAttrib::has_on_plane
 //       Access: Published
 //       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));
-  }
+//  Description: Returns true if the indicated plane is enabled by
+//               the attrib, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ClipPlaneAttrib::
+has_on_plane(const NodePath &plane) const {
+  return _on_planes.find(plane) != _on_planes.end();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: ClipPlaneAttrib::remove_plane
+//     Function: ClipPlaneAttrib::get_num_off_planes
 //       Access: Published
 //       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));
-  }
+//  Description: Returns the number of planes that are disabled by
+//               the attribute.
+////////////////////////////////////////////////////////////////////
+INLINE int ClipPlaneAttrib::
+get_num_off_planes() const {
+  return _off_planes.size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: ClipPlaneAttrib::is_identity
+//     Function: ClipPlaneAttrib::get_off_plane
 //       Access: Published
 //       Access: Published
-//  Description: Returns true if this is an identity attrib: it does
-//               not change the set of planes in use.
+//  Description: Returns the nth plane disabled by the attribute,
+//               sorted in arbitrary (pointer) order.
+////////////////////////////////////////////////////////////////////
+INLINE NodePath ClipPlaneAttrib::
+get_off_plane(int n) const {
+  nassertr(n >= 0 && n < (int)_off_planes.size(), NodePath::fail());
+  return _off_planes[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::has_off_plane
+//       Access: Published
+//  Description: Returns true if the indicated plane is disabled by
+//               the attrib, false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool ClipPlaneAttrib::
 INLINE bool ClipPlaneAttrib::
-is_identity() const {
-  return _operation != O_set && _planes.empty();
+has_off_plane(const NodePath &plane) const {
+  return _off_planes.find(plane) != _off_planes.end() ||
+    (_off_all_planes && !has_on_plane(plane));
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: ClipPlaneAttrib::is_all_off
+//     Function: ClipPlaneAttrib::has_all_off
 //       Access: Published
 //       Access: Published
-//  Description: Returns true if this attrib turns off all planes and
-//               turns none on.
+//  Description: Returns true if this attrib disables all planes
+//               (although it may also enable some).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool ClipPlaneAttrib::
 INLINE bool ClipPlaneAttrib::
-is_all_off() const {
-  return _operation == O_set && _planes.empty();
+has_all_off() const {
+  return _off_all_planes;
+}
+
+////////////////////////////////////////////////////////////////////
+//     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 _on_planes.empty() && _off_planes.empty() && !_off_all_planes;
 }
 }

+ 566 - 200
panda/src/pgraph/clipPlaneAttrib.cxx

@@ -24,33 +24,42 @@
 #include "datagram.h"
 #include "datagram.h"
 #include "datagramIterator.h"
 #include "datagramIterator.h"
 
 
+CPT(RenderAttrib) ClipPlaneAttrib::_empty_attrib;
+CPT(RenderAttrib) ClipPlaneAttrib::_all_off_attrib;
 TypeHandle ClipPlaneAttrib::_type_handle;
 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
 //     Function: ClipPlaneAttrib::make
 //       Access: Published, Static
 //       Access: Published, Static
-//  Description: Constructs a new ClipPlaneAttrib object that turns on (or
-//               off, according to op) the indicate plane(s).
+//  Description: Constructs a new ClipPlaneAttrib object that enables (or
+//               disables, according to op) the indicated plane(s).
+//
+//               This method is now deprecated.  Use add_on_plane() or
+//               add_off_plane() instead.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ClipPlaneAttrib::
 CPT(RenderAttrib) ClipPlaneAttrib::
 make(ClipPlaneAttrib::Operation op, PlaneNode *plane) {
 make(ClipPlaneAttrib::Operation op, PlaneNode *plane) {
-  ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
-  attrib->_operation = op;
-  attrib->_planes.push_back(plane);
-  return return_new(attrib);
+  CPT(RenderAttrib) attrib;
+
+  switch (op) {
+  case O_set:
+    attrib = make_all_off();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane));
+    return attrib;
+   
+  case O_add:
+    attrib = make();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane));
+    return attrib;
+
+  case O_remove:
+    attrib = make();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane));
+    return attrib;
+  }
+
+  nassertr(false, make());
+  return make();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -58,16 +67,36 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane) {
 //       Access: Published, Static
 //       Access: Published, Static
 //  Description: Constructs a new ClipPlaneAttrib object that turns on (or
 //  Description: Constructs a new ClipPlaneAttrib object that turns on (or
 //               off, according to op) the indicate plane(s).
 //               off, according to op) the indicate plane(s).
+//
+//               This method is now deprecated.  Use add_on_plane() or
+//               add_off_plane() instead.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ClipPlaneAttrib::
 CPT(RenderAttrib) ClipPlaneAttrib::
 make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2) {
 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);
+  CPT(RenderAttrib) attrib;
+
+  switch (op) {
+  case O_set:
+    attrib = make_all_off();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
+    return attrib;
+   
+  case O_add:
+    attrib = make();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
+    return attrib;
+
+  case O_remove:
+    attrib = make();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane1));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane2));
+    return attrib;
+  }
 
 
-  attrib->_planes.sort();
-  return return_new(attrib);
+  nassertr(false, make());
+  return make();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -75,18 +104,40 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2) {
 //       Access: Published, Static
 //       Access: Published, Static
 //  Description: Constructs a new ClipPlaneAttrib object that turns on (or
 //  Description: Constructs a new ClipPlaneAttrib object that turns on (or
 //               off, according to op) the indicate plane(s).
 //               off, according to op) the indicate plane(s).
+//
+//               This method is now deprecated.  Use add_on_plane() or
+//               add_off_plane() instead.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ClipPlaneAttrib::
 CPT(RenderAttrib) ClipPlaneAttrib::
 make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2,
 make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2,
      PlaneNode *plane3) {
      PlaneNode *plane3) {
-  ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
-  attrib->_operation = op;
-  attrib->_planes.push_back(plane1);
-  attrib->_planes.push_back(plane2);
-  attrib->_planes.push_back(plane3);
+  CPT(RenderAttrib) attrib;
+
+  switch (op) {
+  case O_set:
+    attrib = make_all_off();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane3));
+    return attrib;
+   
+  case O_add:
+    attrib = make();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane3));
+    return attrib;
+
+  case O_remove:
+    attrib = make();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane1));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane2));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane3));
+    return attrib;
+  }
 
 
-  attrib->_planes.sort();
-  return return_new(attrib);
+  nassertr(false, make());
+  return make();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -94,19 +145,110 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2,
 //       Access: Published, Static
 //       Access: Published, Static
 //  Description: Constructs a new ClipPlaneAttrib object that turns on (or
 //  Description: Constructs a new ClipPlaneAttrib object that turns on (or
 //               off, according to op) the indicate plane(s).
 //               off, according to op) the indicate plane(s).
+//
+//               This method is now deprecated.  Use add_on_plane() or
+//               add_off_plane() instead.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) ClipPlaneAttrib::
 CPT(RenderAttrib) ClipPlaneAttrib::
 make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2,
 make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2,
      PlaneNode *plane3, PlaneNode *plane4) {
      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);
+  CPT(RenderAttrib) attrib;
+
+  switch (op) {
+  case O_set:
+    attrib = make_all_off();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane3));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane4));
+    return attrib;
+   
+  case O_add:
+    attrib = make();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane3));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane4));
+    return attrib;
+
+  case O_remove:
+    attrib = make();
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane1));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane2));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane3));
+    attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane4));
+    return attrib;
+  }
 
 
-  attrib->_planes.sort();
-  return return_new(attrib);
+  nassertr(false, make());
+  return make();
+}
+
+////////////////////////////////////////////////////////////////////
+//     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.
+//
+//               This method is now deprecated.  ClipPlaneAttribs
+//               nowadays have a separate list of on_planes and
+//               off_planes, so this method doesn't make sense.  Query
+//               the lists independently.
+////////////////////////////////////////////////////////////////////
+ClipPlaneAttrib::Operation ClipPlaneAttrib::
+get_operation() const {
+  if (has_all_off()) {
+    return O_set;
+
+  } else if (get_num_off_planes() == 0) {
+    return O_add;
+
+  } else {
+    return O_remove;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::get_num_planes
+//       Access: Published
+//  Description: Returns the number of planes listed in the attribute.
+//
+//               This method is now deprecated.  ClipPlaneAttribs
+//               nowadays have a separate list of on_planes and
+//               off_planes, so this method doesn't make sense.  Query
+//               the lists independently.
+////////////////////////////////////////////////////////////////////
+int ClipPlaneAttrib::
+get_num_planes() const {
+  if (get_num_off_planes() == 0) {
+    return get_num_on_planes();
+  } else {
+    return get_num_off_planes();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::get_plane
+//       Access: Published
+//  Description: Returns the nth plane listed in the attribute.
+//
+//               This method is now deprecated.  ClipPlaneAttribs
+//               nowadays have a separate list of on_planes and
+//               off_planes, so this method doesn't make sense.  Query
+//               the lists independently.
+////////////////////////////////////////////////////////////////////
+PlaneNode *ClipPlaneAttrib::
+get_plane(int n) const {
+  if (get_num_off_planes() == 0) {
+    return DCAST(PlaneNode, get_on_plane(n).node());
+  } else {
+    return DCAST(PlaneNode, get_off_plane(n).node());
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -114,10 +256,164 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2,
 //       Access: Published
 //       Access: Published
 //  Description: Returns true if the indicated plane is listed in the
 //  Description: Returns true if the indicated plane is listed in the
 //               attrib, false otherwise.
 //               attrib, false otherwise.
+//
+//               This method is now deprecated.  ClipPlaneAttribs
+//               nowadays have a separate list of on_planes and
+//               off_planes, so this method doesn't make sense.  Query
+//               the lists independently.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool ClipPlaneAttrib::
 bool ClipPlaneAttrib::
 has_plane(PlaneNode *plane) const {
 has_plane(PlaneNode *plane) const {
-  return _planes.find(plane) != _planes.end();
+  if (get_num_off_planes() == 0) {
+    return has_on_plane(NodePath(plane));
+  } else {
+    return has_off_plane(NodePath(plane));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     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.
+//
+//               This method is now deprecated.  Use add_on_plane() or
+//               add_off_plane() instead.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+add_plane(PlaneNode *plane) const {
+  if (get_num_off_planes() == 0) {
+    return add_on_plane(NodePath(plane));
+  } else {
+    return add_off_plane(NodePath(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.
+//
+//               This method is now deprecated.  Use remove_on_plane()
+//               or remove_off_plane() instead.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+remove_plane(PlaneNode *plane) const {
+  if (get_num_off_planes() == 0) {
+    return remove_on_plane(NodePath(plane));
+  } else {
+    return remove_off_plane(NodePath(plane));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::make
+//       Access: Published, Static
+//  Description: Constructs a new ClipPlaneAttrib object that does
+//               nothing.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+make() {
+  // We make it a special case and store a pointer to the empty attrib
+  // forever once we find it the first time, as an optimization.
+  if (_empty_attrib == (RenderAttrib *)NULL) {
+    _empty_attrib = return_new(new ClipPlaneAttrib);
+  }
+
+  return _empty_attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::make_all_off
+//       Access: Published, Static
+//  Description: Constructs a new ClipPlaneAttrib object that disables 
+//               all planes (and hence disables clipping).
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+make_all_off() {
+  // We make it a special case and store a pointer to the off attrib
+  // forever once we find it the first time, as an optimization.
+  if (_all_off_attrib == (RenderAttrib *)NULL) {
+    ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
+    attrib->_off_all_planes = true;
+    _all_off_attrib = return_new(attrib);
+  }
+
+  return _all_off_attrib;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::add_on_plane
+//       Access: Published
+//  Description: Returns a new ClipPlaneAttrib, just like this one, but
+//               with the indicated plane added to the list of planes
+//               enabled by this attrib.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+add_on_plane(const NodePath &plane) const {
+  nassertr(!plane.is_empty() && plane.node()->is_of_type(PlaneNode::get_class_type()), this);
+  ClipPlaneAttrib *attrib = new ClipPlaneAttrib(*this);
+  attrib->_on_planes.insert(plane);
+  attrib->_off_planes.erase(plane);
+
+  pair<Planes::iterator, bool> insert_result = 
+    attrib->_on_planes.insert(Planes::value_type(plane));
+  if (insert_result.second) {
+    // Also ensure it is removed from the off_planes list.
+    attrib->_off_planes.erase(plane);
+  }
+
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::remove_on_plane
+//       Access: Published
+//  Description: Returns a new ClipPlaneAttrib, just like this one, but
+//               with the indicated plane removed from the list of
+//               planes enabled by this attrib.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+remove_on_plane(const NodePath &plane) const {
+  nassertr(!plane.is_empty() && plane.node()->is_of_type(PlaneNode::get_class_type()), this);
+  ClipPlaneAttrib *attrib = new ClipPlaneAttrib(*this);
+  attrib->_on_planes.erase(plane);
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::add_off_plane
+//       Access: Published
+//  Description: Returns a new ClipPlaneAttrib, just like this one, but
+//               with the indicated plane added to the list of planes
+//               disabled by this attrib.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+add_off_plane(const NodePath &plane) const {
+  nassertr(!plane.is_empty() && plane.node()->is_of_type(PlaneNode::get_class_type()), this);
+  ClipPlaneAttrib *attrib = new ClipPlaneAttrib(*this);
+  if (!_off_all_planes) {
+    attrib->_off_planes.insert(plane);
+  }
+  attrib->_on_planes.erase(plane);
+  return return_new(attrib);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::remove_off_plane
+//       Access: Published
+//  Description: Returns a new ClipPlaneAttrib, just like this one, but
+//               with the indicated plane removed from the list of
+//               planes disabled by this attrib.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ClipPlaneAttrib::
+remove_off_plane(const NodePath &plane) const {
+  nassertr(!plane.is_empty() && plane.node()->is_of_type(PlaneNode::get_class_type()), this);
+  ClipPlaneAttrib *attrib = new ClipPlaneAttrib(*this);
+  attrib->_off_planes.erase(plane);
+  return return_new(attrib);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -142,27 +438,39 @@ issue(GraphicsStateGuardianBase *gsg) const {
 void ClipPlaneAttrib::
 void ClipPlaneAttrib::
 output(ostream &out) const {
 output(ostream &out) const {
   out << get_type() << ":";
   out << get_type() << ":";
-  if (_operation == O_set && _planes.empty()) {
-    out << "all off";
+  if (_off_planes.empty()) {
+    if (_on_planes.empty()) {
+      if (_off_all_planes) {
+        out << "all off";
+      } else {
+        out << "identity";
+      }
+    } else {
+      if (_off_all_planes) {
+        out << "set";
+      } else {
+        out << "on";
+      }
+    }
+
   } else {
   } else {
-    switch (_operation) {
-    case O_set:
-      out << "set";
-      break;
-    case O_add:
-      out << "add";
-      break;
-    case O_remove:
-      out << "remove";
-      break;
+    out << "off";
+    Planes::const_iterator fi;
+    for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) {
+      NodePath plane = (*fi);
+      out << " " << plane;
     }
     }
 
 
-    Planes::const_iterator li;
-    for (li = _planes.begin(); li != _planes.end(); ++li) {
-      PlaneNode *plane = (*li);
-      out << " " << *plane;
+    if (!_on_planes.empty()) {
+      out << " on";
     }
     }
   }
   }
+    
+  Planes::const_iterator li;
+  for (li = _on_planes.begin(); li != _on_planes.end(); ++li) {
+    NodePath plane = (*li);
+    out << " " << plane;
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -172,10 +480,10 @@ output(ostream &out) const {
 //               types to return a unique number indicating whether
 //               types to return a unique number indicating whether
 //               this ClipPlaneAttrib is equivalent to the other one.
 //               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 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
 //               This will only be called with two ClipPlaneAttrib
 //               objects whose get_type() functions return the same.
 //               objects whose get_type() functions return the same.
@@ -185,29 +493,53 @@ compare_to_impl(const RenderAttrib *other) const {
   const ClipPlaneAttrib *ta;
   const ClipPlaneAttrib *ta;
   DCAST_INTO_R(ta, other, 0);
   DCAST_INTO_R(ta, other, 0);
 
 
-  if (_operation != ta->_operation) {
-    return (int)_operation - (int)ta->_operation;
+  if (_off_all_planes != ta->_off_all_planes) {
+    return (int)_off_all_planes - (int)ta->_off_all_planes;
   }
   }
 
 
-  Planes::const_iterator li = _planes.begin();
-  Planes::const_iterator oli = ta->_planes.begin();
+  Planes::const_iterator li = _on_planes.begin();
+  Planes::const_iterator oli = ta->_on_planes.begin();
 
 
-  while (li != _planes.end() && oli != ta->_planes.end()) {
-    PlaneNode *plane = (*li);
-    PlaneNode *other_plane = (*oli);
+  while (li != _on_planes.end() && oli != ta->_on_planes.end()) {
+    NodePath plane = (*li);
+    NodePath other_plane = (*oli);
 
 
-    if (plane != other_plane) {
-      return plane < other_plane ? -1 : 1;
+    int compare = plane.compare_to(other_plane);
+    if (compare != 0) {
+      return compare;
     }
     }
 
 
     ++li;
     ++li;
     ++oli;
     ++oli;
   }
   }
 
 
-  if (li != _planes.end()) {
+  if (li != _on_planes.end()) {
     return 1;
     return 1;
   }
   }
-  if (oli != ta->_planes.end()) {
+  if (oli != ta->_on_planes.end()) {
+    return -1;
+  }
+
+  Planes::const_iterator fi = _off_planes.begin();
+  Planes::const_iterator ofi = ta->_off_planes.begin();
+
+  while (fi != _off_planes.end() && ofi != ta->_off_planes.end()) {
+    NodePath plane = (*fi);
+    NodePath other_plane = (*ofi);
+
+    int compare = plane.compare_to(other_plane);
+    if (compare != 0) {
+      return compare;
+    }
+
+    ++fi;
+    ++ofi;
+  }
+
+  if (fi != _off_planes.end()) {
+    return 1;
+  }
+  if (ofi != ta->_off_planes.end()) {
     return -1;
     return -1;
   }
   }
   
   
@@ -236,111 +568,115 @@ compose_impl(const RenderAttrib *other) const {
   const ClipPlaneAttrib *ta;
   const ClipPlaneAttrib *ta;
   DCAST_INTO_R(ta, other, 0);
   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.
+  if (ta->_off_all_planes) {
+    // If the other type turns off all planes, it doesn't matter what
+    // we are.
     return ta;
     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();
+  // This is a three-way merge between ai, bi, and ci, except that bi
+  // and ci should have no intersection and therefore needn't be
+  // compared to each other.
+  Planes::const_iterator ai = _on_planes.begin();
+  Planes::const_iterator bi = ta->_on_planes.begin();
+  Planes::const_iterator ci = ta->_off_planes.begin();
 
 
   // Create a new ClipPlaneAttrib that will hold the result.
   // Create a new ClipPlaneAttrib that will hold the result.
   ClipPlaneAttrib *new_attrib = new ClipPlaneAttrib;
   ClipPlaneAttrib *new_attrib = new ClipPlaneAttrib;
-  new_attrib->_operation = op;
   back_insert_iterator<Planes> result = 
   back_insert_iterator<Planes> result = 
-    back_inserter(new_attrib->_planes);
+    back_inserter(new_attrib->_on_planes);
+
+  while (ai != _on_planes.end() && 
+         bi != ta->_on_planes.end() && 
+         ci != ta->_off_planes.end()) {
+    if ((*ai) < (*bi)) {
+      if ((*ai) < (*ci)) {
+        // Here is a plane that we have in the original, which is not
+        // present in the secondary.
+        *result = *ai;
+        ++ai;
+        ++result;
+
+      } else if ((*ci) < (*ai)) {
+        // Here is a plane that is disabled in the secondary, but
+        // was not present in the original.
+        ++ci;
+
+      } else { // (*ci) == (*ai)
+        // Here is a plane that is disabled in the secondary, and
+        // was present in the original.
+        ++ai;
+        ++ci;
+      }
+
+    } 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;
 
 
-  while (ai != _planes.end() && bi != other->_planes.end()) {
+    } else {  // (*bi) == (*ai)
+      // Here is a plane we have in both.
+      *result = *bi;
+      ++ai;
+      ++bi;
+      ++result;
+    }
+  }
+
+  while (ai != _on_planes.end() && bi != ta->_on_planes.end()) {
     if ((*ai) < (*bi)) {
     if ((*ai) < (*bi)) {
       // Here is a plane that we have in the original, which is not
       // Here is a plane that we have in the original, which is not
       // present in the secondary.
       // present in the secondary.
       *result = *ai;
       *result = *ai;
       ++ai;
       ++ai;
       ++result;
       ++result;
+
     } else if ((*bi) < (*ai)) {
     } else if ((*bi) < (*ai)) {
       // Here is a new plane we have in the secondary, that was not
       // Here is a new plane we have in the secondary, that was not
       // present in the original.
       // present in the original.
       *result = *bi;
       *result = *bi;
       ++bi;
       ++bi;
       ++result;
       ++result;
+
     } else {
     } else {
       // Here is a plane we have in both.
       // Here is a plane we have in both.
-      *result = *ai;
+      *result = *bi;
       ++ai;
       ++ai;
       ++bi;
       ++bi;
       ++result;
       ++result;
     }
     }
   }
   }
 
 
-  while (ai != _planes.end()) {
+  while (ai != _on_planes.end() && ci != ta->_off_planes.end()) {
+    if ((*ai) < (*ci)) {
+      // Here is a plane that we have in the original, which is not
+      // present in the secondary.
+      *result = *ai;
+      ++ai;
+      ++result;
+      
+    } else if ((*ci) < (*ai)) {
+      // Here is a plane that is disabled in the secondary, but
+      // was not present in the original.
+      ++ci;
+      
+    } else { // (*ci) == (*ai)
+      // Here is a plane that is disabled in the secondary, and
+      // was present in the original.
+      ++ai;
+      ++ci;
+    }
+  }
+
+  while (ai != _on_planes.end()) {
     *result = *ai;
     *result = *ai;
     ++ai;
     ++ai;
     ++result;
     ++result;
   }
   }
 
 
-  while (bi != other->_planes.end()) {
+  while (bi != ta->_on_planes.end()) {
     *result = *bi;
     *result = *bi;
     ++bi;
     ++bi;
     ++result;
     ++result;
@@ -350,48 +686,36 @@ do_add(const ClipPlaneAttrib *other, ClipPlaneAttrib::Operation op) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     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.
+//     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::
 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;
-  }
+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;
+}
 
 
-  return return_new(new_attrib);
+////////////////////////////////////////////////////////////////////
+//     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;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -415,15 +739,26 @@ void ClipPlaneAttrib::
 write_datagram(BamWriter *manager, Datagram &dg) {
 write_datagram(BamWriter *manager, Datagram &dg) {
   RenderAttrib::write_datagram(manager, 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);
+  dg.add_bool(_off_all_planes);
 
 
-  Planes::const_iterator li;
-  for (li = _planes.begin(); li != _planes.end(); ++li) {
-    PlaneNode *plane = (*li);
-    manager->write_pointer(dg, plane);
+  // write the number of off_planes
+  dg.add_uint16(get_num_off_planes());
+  // write the off planes pointers if any
+  Planes::const_iterator fi;
+  for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) {
+    NodePath plane = (*fi);
+
+    // Whoops, we don't have a way to write out a NodePath right now.
+    manager->write_pointer(dg, plane.node());
+  }
+
+  // write the number of on planes
+  dg.add_uint16(get_num_on_planes());
+  // write the on planes pointers if any
+  Planes::const_iterator nti;
+  for (nti = _on_planes.begin(); nti != _on_planes.end(); ++nti) {
+    NodePath plane = (*nti);
+    manager->write_pointer(dg, plane.node());
   }
   }
 }
 }
 
 
@@ -438,11 +773,22 @@ int ClipPlaneAttrib::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = RenderAttrib::complete_pointers(p_list, manager);
   int pi = RenderAttrib::complete_pointers(p_list, manager);
 
 
-  Planes::iterator li;
-  for (li = _planes.begin(); li != _planes.end(); ++li) {
-    PlaneNode *node;
+  Planes::iterator ci = _off_planes.begin();
+  while (ci != _off_planes.end()) {
+    PandaNode *node;
+    DCAST_INTO_R(node, p_list[pi++], pi);
+    NodePath np(node);
+    (*ci) = np;
+    ++ci;
+  }
+
+  ci = _on_planes.begin();
+  while (ci != _on_planes.end()) {
+    PandaNode *node;
     DCAST_INTO_R(node, p_list[pi++], pi);
     DCAST_INTO_R(node, p_list[pi++], pi);
-    (*li) = node;
+    NodePath np(node);
+    (*ci) = np;
+    ++ci;
   }
   }
 
 
   return pi;
   return pi;
@@ -479,11 +825,31 @@ void ClipPlaneAttrib::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
   RenderAttrib::fillin(scan, manager);
 
 
-  _operation = (Operation)scan.get_int8();
-  int num_planes = scan.get_uint16();
-
-  for (int i = 0; i < num_planes; i++) {
+  // We cheat a little bit here.  In the middle of bam version 4.10,
+  // we completely redefined the bam storage definition for
+  // ClipPlaneAttribs, without bothering to up the bam version or even to
+  // attempt to read the old definition.  We get away with this,
+  // knowing that the egg loader doesn't create ClipPlaneAttribs, and
+  // hence no old bam files have the old definition for ClipPlaneAttrib
+  // within them.
+
+  _off_all_planes = scan.get_bool();
+
+  int num_off_planes = scan.get_uint16();
+    
+  // Push back a NULL pointer for each off Plane for now, until
+  // we get the actual list of pointers later in complete_pointers().
+  _off_planes.reserve(num_off_planes);
+  int i;
+  for (i = 0; i < num_off_planes; i++) {
+    manager->read_pointer(scan);
+    _off_planes.push_back(NULL);
+  }
+    
+  int num_on_planes = scan.get_uint16();
+  _on_planes.reserve(num_on_planes);
+  for (i = 0; i < num_on_planes; i++) {
     manager->read_pointer(scan);
     manager->read_pointer(scan);
-    _planes.push_back(NULL);
+    _on_planes.push_back(NULL);
   }
   }
 }
 }

+ 31 - 8
panda/src/pgraph/clipPlaneAttrib.h

@@ -24,6 +24,7 @@
 #include "planeNode.h"
 #include "planeNode.h"
 #include "renderAttrib.h"
 #include "renderAttrib.h"
 #include "ordered_vector.h"
 #include "ordered_vector.h"
+#include "nodePath.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : ClipPlaneAttrib
 //       Class : ClipPlaneAttrib
@@ -36,15 +37,19 @@
 class EXPCL_PANDA ClipPlaneAttrib : public RenderAttrib {
 class EXPCL_PANDA ClipPlaneAttrib : public RenderAttrib {
 private:
 private:
   INLINE ClipPlaneAttrib();
   INLINE ClipPlaneAttrib();
+  INLINE ClipPlaneAttrib(const ClipPlaneAttrib &copy);
 
 
 PUBLISHED:
 PUBLISHED:
+
+  // This is the old, deprecated interface to ClipPlaneAttrib.  Do not
+  // use any of these methods for new code; these methods will be
+  // removed soon.
   enum Operation {
   enum Operation {
     O_set,
     O_set,
     O_add,
     O_add,
     O_remove
     O_remove
   };
   };
 
 
-  static CPT(RenderAttrib) make_all_off();
   static CPT(RenderAttrib) make(Operation op, 
   static CPT(RenderAttrib) make(Operation op, 
                                 PlaneNode *plane);
                                 PlaneNode *plane);
   static CPT(RenderAttrib) make(Operation op, 
   static CPT(RenderAttrib) make(Operation op, 
@@ -65,8 +70,27 @@ PUBLISHED:
   INLINE CPT(RenderAttrib) add_plane(PlaneNode *plane) const;
   INLINE CPT(RenderAttrib) add_plane(PlaneNode *plane) const;
   INLINE CPT(RenderAttrib) remove_plane(PlaneNode *plane) const;
   INLINE CPT(RenderAttrib) remove_plane(PlaneNode *plane) const;
 
 
+
+  // The following is the new, more general interface to the
+  // ClipPlaneAttrib.
+  static CPT(RenderAttrib) make();
+  static CPT(RenderAttrib) make_all_off();
+
+  INLINE int get_num_on_planes() const;
+  INLINE NodePath get_on_plane(int n) const;
+  INLINE bool has_on_plane(const NodePath &plane) const;
+
+  INLINE int get_num_off_planes() const;
+  INLINE NodePath get_off_plane(int n) const;
+  INLINE bool has_off_plane(const NodePath &plane) const;
+  INLINE bool has_all_off() const;
+
   INLINE bool is_identity() const;
   INLINE bool is_identity() const;
-  INLINE bool is_all_off() const;
+
+  CPT(RenderAttrib) add_on_plane(const NodePath &plane) const;
+  CPT(RenderAttrib) remove_on_plane(const NodePath &plane) const;
+  CPT(RenderAttrib) add_off_plane(const NodePath &plane) const;
+  CPT(RenderAttrib) remove_off_plane(const NodePath &plane) const;
 
 
 public:
 public:
   virtual void issue(GraphicsStateGuardianBase *gsg) const;
   virtual void issue(GraphicsStateGuardianBase *gsg) const;
@@ -79,13 +103,12 @@ protected:
   virtual RenderAttrib *make_default_impl() const;
   virtual RenderAttrib *make_default_impl() const;
 
 
 private:
 private:
-  CPT(RenderAttrib) do_add(const ClipPlaneAttrib *other, Operation op) const;
-  CPT(RenderAttrib) do_remove(const ClipPlaneAttrib *other, Operation op) const;
+  typedef ov_set<NodePath> Planes;
+  Planes _on_planes, _off_planes;
+  bool _off_all_planes;
 
 
-private:
-  Operation _operation;
-  typedef ov_set< PT(PlaneNode) > Planes;
-  Planes _planes;
+  static CPT(RenderAttrib) _empty_attrib;
+  static CPT(RenderAttrib) _all_off_attrib;
 
 
 public:
 public:
   static void register_with_read_factory();
   static void register_with_read_factory();

+ 143 - 0
panda/src/pgraph/nodePath.I

@@ -509,6 +509,149 @@ get_net_state() const {
   return r_get_net_state(_head);
   return r_get_net_state(_head);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_attrib
+//       Access: Published
+//  Description: Adds the indicated render attribute to the scene
+//               graph on this node.  This attribute will now apply to
+//               this node and everything below.  If there was already
+//               an attribute of the same type, it is replaced.
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+set_attrib(const RenderAttrib *attrib, int priority) {
+  nassertv_always(!is_empty());
+  node()->set_attrib(attrib, priority);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::get_attrib
+//       Access: Published
+//  Description: Returns the render attribute of the indicated type,
+//               if it is defined on the node, or NULL if it is not.
+//               This checks only what is set on this particular node
+//               level, and has nothing to do with what render
+//               attributes may be inherited from parent nodes.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderAttrib *NodePath::
+get_attrib(TypeHandle type) const {
+  nassertr_always(!is_empty(), NULL);
+  return node()->get_attrib(type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_attrib
+//       Access: Published
+//  Description: Returns true if there is a render attribute of the
+//               indicated type defined on this node, or false if
+//               there is not.
+////////////////////////////////////////////////////////////////////
+INLINE bool NodePath::
+has_attrib(TypeHandle type) const {
+  nassertr_always(!is_empty(), false);
+  return node()->has_attrib(type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_attrib
+//       Access: Published
+//  Description: Removes the render attribute of the given type from
+//               this node.  This node, and the subgraph below, will
+//               now inherit the indicated render attribute from the
+//               nodes above this one.
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+clear_attrib(TypeHandle type) {
+  nassertv_always(!is_empty());
+  node()->clear_attrib(type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_effect
+//       Access: Published
+//  Description: Adds the indicated render effect to the scene
+//               graph on this node.  If there was already an effect
+//               of the same type, it is replaced.
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+set_effect(const RenderEffect *effect) {
+  nassertv_always(!is_empty());
+  node()->set_effect(effect);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::get_effect
+//       Access: Published
+//  Description: Returns the render effect of the indicated type,
+//               if it is defined on the node, or NULL if it is not.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderEffect *NodePath::
+get_effect(TypeHandle type) const {
+  nassertr_always(!is_empty(), NULL);
+  return node()->get_effect(type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_effect
+//       Access: Published
+//  Description: Returns true if there is a render effect of the
+//               indicated type defined on this node, or false if
+//               there is not.
+////////////////////////////////////////////////////////////////////
+INLINE bool NodePath::
+has_effect(TypeHandle type) const {
+  nassertr_always(!is_empty(), false);
+  return node()->has_effect(type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_effect
+//       Access: Published
+//  Description: Removes the render effect of the given type from
+//               this node.
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+clear_effect(TypeHandle type) {
+  nassertv_always(!is_empty());
+  node()->clear_effect(type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_effects
+//       Access: Published
+//  Description: Sets the complete RenderEffects that will be applied
+//               this node.  This completely replaces whatever has
+//               been set on this node via repeated calls to
+//               set_attrib().
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+set_effects(const RenderEffects *effects) {
+  nassertv_always(!is_empty());
+  node()->set_effects(effects);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::get_effects
+//       Access: Published
+//  Description: Returns the complete RenderEffects that will be
+//               applied to this node.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderEffects *NodePath::
+get_effects() const {
+  nassertr_always(!is_empty(), RenderEffects::make_empty());
+  return node()->get_effects();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_effects
+//       Access: Published
+//  Description: Resets this node to have no render effects.
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+clear_effects() {
+  nassertv_always(!is_empty());
+  node()->clear_effects();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::get_transform
 //     Function: NodePath::get_transform
 //       Access: Published
 //       Access: Published

+ 211 - 1
panda/src/pgraph/nodePath.cxx

@@ -28,6 +28,7 @@
 #include "texMatrixAttrib.h"
 #include "texMatrixAttrib.h"
 #include "materialAttrib.h"
 #include "materialAttrib.h"
 #include "lightAttrib.h"
 #include "lightAttrib.h"
+#include "clipPlaneAttrib.h"
 #include "polylightEffect.h"
 #include "polylightEffect.h"
 #include "fogAttrib.h"
 #include "fogAttrib.h"
 #include "renderModeAttrib.h"
 #include "renderModeAttrib.h"
@@ -41,6 +42,7 @@
 #include "transparencyAttrib.h"
 #include "transparencyAttrib.h"
 #include "antialiasAttrib.h"
 #include "antialiasAttrib.h"
 #include "texProjectorEffect.h"
 #include "texProjectorEffect.h"
+#include "planeNode.h"
 #include "lensNode.h"
 #include "lensNode.h"
 #include "materialPool.h"
 #include "materialPool.h"
 #include "look_at.h"
 #include "look_at.h"
@@ -2286,7 +2288,7 @@ set_light_off(int priority) {
 //     Function: NodePath::set_light_off
 //     Function: NodePath::set_light_off
 //       Access: Published
 //       Access: Published
 //  Description: Sets the geometry at this level and below to render
 //  Description: Sets the geometry at this level and below to render
-//               using without the indicated Light.  This is different
+//               without using the indicated Light.  This is different
 //               from not specifying the Light; rather, this
 //               from not specifying the Light; rather, this
 //               specifically contradicts set_light() at a higher node
 //               specifically contradicts set_light() at a higher node
 //               level (or, with a priority, overrides a set_light()
 //               level (or, with a priority, overrides a set_light()
@@ -2472,6 +2474,214 @@ has_light_off(const NodePath &light) const {
   return false;
   return false;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_clip_plane
+//       Access: Published
+//  Description: Adds the indicated clipping plane to the list of
+//               planes that apply to geometry at this node and below.
+//               The clipping plane itself, a PlaneNode, should be
+//               parented into the scene graph elsewhere, to represent
+//               the plane's position in space; but until
+//               set_clip_plane() is called it will clip no geometry.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_clip_plane(const NodePath &clip_plane, int priority) {
+  nassertv_always(!is_empty());
+  if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
+    const RenderAttrib *attrib =
+      node()->get_attrib(ClipPlaneAttrib::get_class_type());
+    if (attrib != (const RenderAttrib *)NULL) {
+      priority = max(priority,
+                     node()->get_state()->get_override(ClipPlaneAttrib::get_class_type()));
+      const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
+      
+      // Modify the existing ClipPlaneAttrib to add the indicated
+      // clip_plane.
+      node()->set_attrib(la->add_on_plane(clip_plane), priority);
+      
+    } else {
+      // Create a new ClipPlaneAttrib for this node.
+      CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, ClipPlaneAttrib::make());
+      node()->set_attrib(la->add_on_plane(clip_plane), priority);
+    }
+    return;
+  }
+  nassert_raise("Not a PlaneNode object.");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_clip_plane_off
+//       Access: Published
+//  Description: Sets the geometry at this level and below to render
+//               using no clip_planes at all.  This is different
+//               from not specifying a clip_plane; rather, this
+//               specifically contradicts set_clip_plane() at a higher
+//               node level (or, with a priority, overrides a
+//               set_clip_plane() at a lower level).
+//
+//               If no clip_planes are in effect on a particular piece
+//               of geometry, that geometry is rendered without being
+//               clipped (other than by the viewing frustum).
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_clip_plane_off(int priority) {
+  nassertv_always(!is_empty());
+  node()->set_attrib(ClipPlaneAttrib::make_all_off(), priority);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_clip_plane_off
+//       Access: Published
+//  Description: Sets the geometry at this level and below to render
+//               without being clipped by the indicated PlaneNode.
+//               This is different from not specifying the PlaneNode;
+//               rather, this specifically contradicts
+//               set_clip_plane() at a higher node level (or, with a
+//               priority, overrides a set_clip_plane() at a lower
+//               level).
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_clip_plane_off(const NodePath &clip_plane, int priority) {
+  nassertv_always(!is_empty());
+
+  if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
+    const RenderAttrib *attrib =
+      node()->get_attrib(ClipPlaneAttrib::get_class_type());
+    if (attrib != (const RenderAttrib *)NULL) {
+      priority = max(priority,
+                     node()->get_state()->get_override(ClipPlaneAttrib::get_class_type()));
+      const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
+      
+      // Modify the existing ClipPlaneAttrib to add the indicated clip_plane
+      // to the "off" list.  This also, incidentally, removes it from
+      // the "on" list if it is there.
+      node()->set_attrib(la->add_off_plane(clip_plane), priority);
+      
+    } else {
+      // Create a new ClipPlaneAttrib for this node that turns off the
+      // indicated clip_plane.
+      CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, ClipPlaneAttrib::make());
+      node()->set_attrib(la->add_off_plane(clip_plane), priority);
+    }
+    return;
+  }
+  nassert_raise("Not a PlaneNode object.");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_clip_plane
+//       Access: Published
+//  Description: Completely removes any clip planes that may have been
+//               set via set_clip_plane() or set_clip_plane_off() from
+//               this particular node.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+clear_clip_plane() {
+  nassertv_always(!is_empty());
+  node()->clear_attrib(ClipPlaneAttrib::get_class_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_clip_plane
+//       Access: Published
+//  Description: Removes any reference to the indicated clipping plane
+//               from the NodePath.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+clear_clip_plane(const NodePath &clip_plane) {
+  nassertv_always(!is_empty());
+
+  if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
+    const RenderAttrib *attrib =
+      node()->get_attrib(ClipPlaneAttrib::get_class_type());
+    if (attrib != (const RenderAttrib *)NULL) {
+      CPT(ClipPlaneAttrib) la = DCAST(ClipPlaneAttrib, attrib);
+      la = DCAST(ClipPlaneAttrib, la->remove_on_plane(clip_plane));
+      la = DCAST(ClipPlaneAttrib, la->remove_off_plane(clip_plane));
+        
+      if (la->is_identity()) {
+        node()->clear_attrib(ClipPlaneAttrib::get_class_type());
+          
+      } else {
+        int priority = node()->get_state()->get_override(ClipPlaneAttrib::get_class_type());
+        node()->set_attrib(la, priority);
+      }
+    }
+    return;
+  }
+  nassert_raise("Not a PlaneNode object.");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_clip_plane
+//       Access: Published
+//  Description: Returns true if the indicated clipping plane has been
+//               specifically applied to this particular node.  This
+//               means that someone called set_clip_plane() on this
+//               node with the indicated clip_plane.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+has_clip_plane(const NodePath &clip_plane) const {
+  nassertr_always(!is_empty(), false);
+
+  if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
+    const RenderAttrib *attrib =
+      node()->get_attrib(ClipPlaneAttrib::get_class_type());
+    if (attrib != (const RenderAttrib *)NULL) {
+      const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
+      return la->has_on_plane(clip_plane);
+    }
+    return false;
+  }
+  nassert_raise("Not a PlaneNode object.");
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_clip_plane_off
+//       Access: Published
+//  Description: Returns true if all clipping planes have been
+//               specifically disabled on this particular node.  This
+//               means that someone called set_clip_plane_off() on
+//               this node with no parameters.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+has_clip_plane_off() const {
+  nassertr_always(!is_empty(), false);
+
+  const RenderAttrib *attrib =
+    node()->get_attrib(ClipPlaneAttrib::get_class_type());
+  if (attrib != (const RenderAttrib *)NULL) {
+    const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
+    return la->has_all_off();
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::has_clip_plane_off
+//       Access: Published
+//  Description: Returns true if the indicated clipping plane has been
+//               specifically disabled on this particular node.  This
+//               means that someone called set_clip_plane_off() on
+//               this node with the indicated clip_plane.
+////////////////////////////////////////////////////////////////////
+bool NodePath::
+has_clip_plane_off(const NodePath &clip_plane) const {
+  nassertr_always(!is_empty(), false);
+  if (!clip_plane.is_empty() && clip_plane.node()->is_of_type(PlaneNode::get_class_type())) {
+    const RenderAttrib *attrib =
+      node()->get_attrib(ClipPlaneAttrib::get_class_type());
+    if (attrib != (const RenderAttrib *)NULL) {
+      const ClipPlaneAttrib *la = DCAST(ClipPlaneAttrib, attrib);
+      return la->has_off_plane(clip_plane);
+    }
+  }
+  nassert_raise("Not a PlaneNode object.");
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::set_bin
 //     Function: NodePath::set_bin
 //       Access: Published
 //       Access: Published

+ 23 - 0
panda/src/pgraph/nodePath.h

@@ -246,6 +246,20 @@ PUBLISHED:
   void set_state(const NodePath &other, const RenderState *state);
   void set_state(const NodePath &other, const RenderState *state);
   INLINE CPT(RenderState) get_net_state() const;
   INLINE CPT(RenderState) get_net_state() const;
 
 
+  INLINE void set_attrib(const RenderAttrib *attrib, int priority = 0);
+  INLINE const RenderAttrib *get_attrib(TypeHandle type) const;
+  INLINE bool has_attrib(TypeHandle type) const;
+  INLINE void clear_attrib(TypeHandle type);
+
+  INLINE void set_effect(const RenderEffect *effect);
+  INLINE const RenderEffect *get_effect(TypeHandle type) const;
+  INLINE bool has_effect(TypeHandle type) const;
+  INLINE void clear_effect(TypeHandle type);
+
+  INLINE void set_effects(const RenderEffects *effects);
+  INLINE const RenderEffects *get_effects() const;
+  INLINE void clear_effects();
+
   INLINE const TransformState *get_transform() const;
   INLINE const TransformState *get_transform() const;
   INLINE void clear_transform();
   INLINE void clear_transform();
   INLINE void set_transform(const TransformState *transform);
   INLINE void set_transform(const TransformState *transform);
@@ -512,6 +526,15 @@ PUBLISHED:
   bool has_light_off() const;
   bool has_light_off() const;
   bool has_light_off(const NodePath &light) const;
   bool has_light_off(const NodePath &light) const;
 
 
+  void set_clip_plane(const NodePath &clip_plane, int priority = 0);
+  void set_clip_plane_off(int priority = 0);
+  void set_clip_plane_off(const NodePath &clip_plane, int priority = 0);
+  void clear_clip_plane();
+  void clear_clip_plane(const NodePath &clip_plane);
+  bool has_clip_plane(const NodePath &clip_plane) const;
+  bool has_clip_plane_off() const;
+  bool has_clip_plane_off(const NodePath &clip_plane) const;
+
   void set_bin(const string &bin_name, int draw_order, int priority = 0);
   void set_bin(const string &bin_name, int draw_order, int priority = 0);
   void clear_bin();
   void clear_bin();
   bool has_bin() const;
   bool has_bin() const;