Browse Source

show_through()

David Rose 20 years ago
parent
commit
725e82e6f4

+ 16 - 1
panda/src/collide/collisionNode.cxx

@@ -47,7 +47,7 @@ CollisionNode(const string &name) :
   _collide_geom(false)
 {
   // CollisionNodes are hidden by default.
-  set_draw_mask(DrawMask::all_off());
+  set_overall_hidden(true);
 
   // CollisionNodes have a certain set of bits on by default.
   set_into_collide_mask(get_default_collide_mask());
@@ -262,6 +262,21 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionNode::is_renderable
+//       Access: Public, Virtual
+//  Description: Returns true if there is some value to visiting this
+//               particular node during the cull traversal for any
+//               camera, false otherwise.  This will be used to
+//               optimize the result of get_net_draw_show_mask(), so
+//               that any subtrees that contain only nodes for which
+//               is_renderable() is false need not be visited.
+////////////////////////////////////////////////////////////////////
+bool CollisionNode::
+is_renderable() const {
+  return true;
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionNode::output

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

@@ -51,6 +51,7 @@ public:
 
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
+  virtual bool is_renderable() const;
 
   virtual void output(ostream &out) const;
 

+ 0 - 3
panda/src/egg2pg/config_egg2pg.cxx

@@ -82,9 +82,6 @@ ConfigVariableBool egg_rigid_geometry
           "geometry has to be vertex-animated, but there will tend to be "
           "more separate pieces."));
 
-ConfigVariableBool egg_show_collision_solids
-("egg-show-collision-solids", false);
-
 ConfigVariableBool egg_load_old_curves
 ("egg-load-old-curves", true,
  PRC_DESC("When this is true, a <NurbsCurve> entry appearing in an egg file "

+ 0 - 1
panda/src/egg2pg/config_egg2pg.h

@@ -45,7 +45,6 @@ extern EXPCL_PANDAEGG ConfigVariableBool egg_unify;
 extern EXPCL_PANDAEGG ConfigVariableDouble egg_flatten_radius;
 extern EXPCL_PANDAEGG ConfigVariableBool egg_combine_geoms;
 extern EXPCL_PANDAEGG ConfigVariableBool egg_rigid_geometry;
-extern EXPCL_PANDAEGG ConfigVariableBool egg_show_collision_solids;
 extern EXPCL_PANDAEGG ConfigVariableBool egg_load_old_curves;
 extern EXPCL_PANDAEGG ConfigVariableBool egg_load_classic_nurbs_curves;
 extern EXPCL_PANDAEGG ConfigVariableBool egg_accept_errors;

+ 0 - 4
panda/src/egg2pg/eggLoader.cxx

@@ -1596,10 +1596,6 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
     }
 
     node = create_group_arc(egg_group, parent, node);
-
-    if (!egg_show_collision_solids) {
-      node->set_draw_mask(DrawMask::all_off());
-    }
     return node;
 
   } else if (egg_group->get_portal_flag()) {

+ 3 - 0
panda/src/pgraph/camera.I

@@ -107,6 +107,9 @@ get_display_region(int n) const {
 ////////////////////////////////////////////////////////////////////
 INLINE void Camera::
 set_camera_mask(DrawMask mask) {
+  // You shouldn't attempt to use Panda's reserved "overall" bit as a
+  // camera mask.
+  nassertv((mask & PandaNode::get_overall_bit()).is_zero());
   _camera_mask = mask;
 }
 

+ 1 - 1
panda/src/pgraph/camera.cxx

@@ -32,7 +32,7 @@ Camera::
 Camera(const string &name) :
   LensNode(name),
   _active(true),
-  _camera_mask(DrawMask::all_on()),
+  _camera_mask(~PandaNode::get_overall_bit()),
   _initial_state(RenderState::make_empty())
 {
 }

+ 12 - 6
panda/src/pgraph/cullTraverser.cxx

@@ -152,7 +152,9 @@ traverse(CullTraverserData &data) {
   
   if (data.is_in_view(_camera_mask)) {
     if (pgraph_cat.is_spam()) {
-      pgraph_cat.spam() << "\n" << data._node_path << "\n";
+      pgraph_cat.spam() 
+        << "\n" << data._node_path
+        << " " << data._draw_mask << "\n";
     }
 
     PandaNode *node = data.node();
@@ -189,29 +191,33 @@ traverse(CullTraverserData &data) {
 //     Function: CullTraverser::traverse_below
 //       Access: Public
 //  Description: Traverses all the children of the indicated node,
-//               with the given data, which been converted into the
-//               node's space.
+//               with the given data, which has been converted into
+//               the node's space.
 ////////////////////////////////////////////////////////////////////
 void CullTraverser::
 traverse_below(CullTraverserData &data) {
   _nodes_pcollector.add_level(1);
   PandaNode *node = data.node();
 
+  bool visible = !(data._draw_mask & PandaNode::get_overall_bit()).is_zero() &&
+    !(data._draw_mask & _camera_mask).is_zero();
+
   const RenderEffects *node_effects = node->get_effects();
-  bool has_decal = node_effects->has_decal();
+  bool has_decal = visible && node_effects->has_decal();
   if (has_decal && !_depth_offset_decals) {
     // Start the three-pass decal rendering if we're not using
     // DepthOffsetAttribs to implement decals.
     start_decal(data);
     
   } else {
-    if (node->is_geom_node()) {
+    if (visible && node->is_geom_node()) {
       _geom_nodes_pcollector.add_level(1);
       GeomNode *geom_node = DCAST(GeomNode, node);
 
       if (pgraph_cat.is_spam()) {
         pgraph_cat.spam()
-          << "Found " << *geom_node << " in state " << *data._state << "\n";
+          << "Found " << *geom_node << " in state " << *data._state 
+          << " draw_mask = " << data._draw_mask << "\n";
       }
       
       // Get all the Geoms, with no decalling.

+ 8 - 30
panda/src/pgraph/cullTraverserData.I

@@ -32,7 +32,8 @@ CullTraverserData(const NodePath &start,
   _state(state),
   _view_frustum(view_frustum),
   _guard_band(guard_band),
-  _cull_planes(CullPlanes::make_empty())
+  _cull_planes(CullPlanes::make_empty()),
+  _draw_mask(DrawMask::all_on())
 {
 }
 
@@ -48,7 +49,8 @@ CullTraverserData(const CullTraverserData &copy) :
   _state(copy._state),
   _view_frustum(copy._view_frustum),
   _guard_band(copy._guard_band),
-  _cull_planes(copy._cull_planes)
+  _cull_planes(copy._cull_planes),
+  _draw_mask(copy._draw_mask)
 {
 }
 
@@ -65,6 +67,7 @@ operator = (const CullTraverserData &copy) {
   _view_frustum = copy._view_frustum;
   _guard_band = copy._guard_band;
   _cull_planes = copy._cull_planes;
+  _draw_mask = copy._draw_mask;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -80,7 +83,8 @@ CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
   _state(parent._state),
   _view_frustum(parent._view_frustum),
   _guard_band(parent._guard_band),
-  _cull_planes(parent._cull_planes)
+  _cull_planes(parent._cull_planes),
+  _draw_mask(parent._draw_mask)
 {
 }
 
@@ -130,7 +134,7 @@ is_in_view(const DrawMask &camera_mask) {
     return false;
   }
 
-  if ((node()->get_draw_mask() & camera_mask).is_zero()) {
+  if (!node()->compare_draw_mask(_draw_mask, camera_mask)) {
     // If there are no draw bits in common with the camera, the node
     // is out.
     return false;
@@ -146,29 +150,3 @@ is_in_view(const DrawMask &camera_mask) {
   // Otherwise, compare the bounding volume to the frustum.
   return is_in_view_impl();
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: CullTraverserData::test_within_clip_planes
-//       Access: Public
-//  Description: Returns the BoundingVolume intersection bits
-//               appropriate for the intersection of the current clip
-//               planes with the current node.
-//
-//               That is, if the current node is within all clip
-//               planes (and will not be clipped at all), returns
-//               BoundingVolume::IF_all.  If it is completely behind
-//               at least one clip plane, returns
-//               BoundingVolume::IF_intersection.  If it is partially
-//               behind one or more clip planes, returns
-//               BoundingVolume::IF_some.
-////////////////////////////////////////////////////////////////////
-INLINE int CullTraverserData::
-test_within_clip_planes(const CullTraverser *trav) const {
-  const ClipPlaneAttrib *cpa = _state->get_clip_plane();
-  if (cpa != (ClipPlaneAttrib *)NULL) {
-    return test_within_clip_planes_impl(trav, cpa);
-  }
-  // No clip plane attrib; therefore, the node is not clipped at
-  // all.
-  return BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
-}

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

@@ -62,6 +62,7 @@ apply_transform_and_state(CullTraverser *trav) {
     string tag_state = node->get_tag(trav->get_tag_state_key());
     node_state = node_state->compose(camera->get_tag_state(tag_state));
   }
+  node->compose_draw_mask(_draw_mask);
 
   apply_transform_and_state(trav, node->get_transform(),
                             node_state, node->get_effects(),

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

@@ -64,7 +64,6 @@ public:
   CPT(TransformState) get_net_transform(const CullTraverser *trav) const;
 
   INLINE bool is_in_view(const DrawMask &camera_mask);
-  INLINE int test_within_clip_planes(const CullTraverser *trav) const;
 
   void apply_transform_and_state(CullTraverser *trav);
   void apply_transform_and_state(CullTraverser *trav, 
@@ -79,6 +78,7 @@ public:
   PT(GeometricBoundingVolume) _view_frustum;
   PT(GeometricBoundingVolume) _guard_band;
   CPT(CullPlanes) _cull_planes;
+  DrawMask _draw_mask;
 
 private:
   bool is_in_view_impl();

+ 1 - 2
panda/src/pgraph/findApproxLevelEntry.cxx

@@ -149,8 +149,7 @@ consider_node(NodePathCollection &result, FindApproxLevelEntry *&next_level,
 void FindApproxLevelEntry::
 consider_next_step(PandaNode *child_node, FindApproxLevelEntry *&next_level, 
                    int increment) const {
-  if (!_approx_path.return_hidden() &&
-      child_node->get_draw_mask().is_zero()) {
+  if (!_approx_path.return_hidden() && child_node->is_overall_hidden()) {
     // If the approx path does not allow us to return hidden nodes,
     // and this node has indeed been completely hidden, then stop
     // here.

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

@@ -363,6 +363,21 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
   return next_transform;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::is_renderable
+//       Access: Public, Virtual
+//  Description: Returns true if there is some value to visiting this
+//               particular node during the cull traversal for any
+//               camera, false otherwise.  This will be used to
+//               optimize the result of get_net_draw_show_mask(), so
+//               that any subtrees that contain only nodes for which
+//               is_renderable() is false need not be visited.
+////////////////////////////////////////////////////////////////////
+bool GeomNode::
+is_renderable() const {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::get_legal_collide_mask
 //       Access: Published, Virtual

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

@@ -53,6 +53,7 @@ public:
     calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
                       bool &found_any,
                       const TransformState *transform) const;
+  virtual bool is_renderable() const;
   virtual CollideMask get_legal_collide_mask() const;
 
 PUBLISHED:

+ 54 - 8
panda/src/pgraph/nodePath.I

@@ -1884,11 +1884,14 @@ adjust_all_priorities(int adjustment) {
 //  Description: Undoes the effect of a previous hide() on this node:
 //               makes the referenced node (and the entire subgraph
 //               below this node) visible to all cameras.
+//
+//               This will not reveal the node if a parent node has
+//               been hidden.
 ////////////////////////////////////////////////////////////////////
 INLINE void NodePath::
 show() {
   nassertv_always(!is_empty());
-  node()->set_draw_mask(DrawMask::all_on());
+  node()->adjust_draw_mask(DrawMask::all_off(), DrawMask::all_off(), PandaNode::get_overall_bit());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1896,13 +1899,52 @@ show() {
 //       Access: Published
 //  Description: Makes the referenced node visible just to the
 //               cameras whose camera_mask shares the indicated bits.
-//               That is, this sets the indicated bits in the
-//               node's draw mask.
+//
+//               This undoes the effect of a previous hide() call.  It
+//               will not reveal the node if a parent node has been
+//               hidden.  However, see show_through().
 ////////////////////////////////////////////////////////////////////
 INLINE void NodePath::
 show(DrawMask camera_mask) {
   nassertv_always(!is_empty());
-  node()->set_draw_mask(node()->get_draw_mask() | camera_mask);
+  camera_mask &= ~PandaNode::get_overall_bit();
+  node()->adjust_draw_mask(DrawMask::all_off(), DrawMask::all_off(), camera_mask);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::show_through
+//       Access: Published
+//  Description: Makes the referenced node visible just to the
+//               cameras whose camera_mask shares the indicated bits.
+//
+//               Unlike show(), this will reveal the node even if a
+//               parent node has been hidden, thus "showing through" a
+//               parent's hide().
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+show_through() {
+  nassertv_always(!is_empty());
+  node()->adjust_draw_mask(PandaNode::get_overall_bit(), DrawMask::all_off(), DrawMask::all_off());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::show_through
+//       Access: Published
+//  Description: Makes the referenced node visible just to the
+//               cameras whose camera_mask shares the indicated bits.
+//
+//               Unlike show(), this will reveal the node even if a
+//               parent node has been hidden via the one-parameter
+//               hide() method, thus "showing through" a parent's
+//               hide().  (However, it will not show through a
+//               parent's hide() call if the no-parameter form of
+//               hide() was used.)
+////////////////////////////////////////////////////////////////////
+INLINE void NodePath::
+show_through(DrawMask camera_mask) {
+  nassertv_always(!is_empty());
+  camera_mask &= ~PandaNode::get_overall_bit();
+  node()->adjust_draw_mask(camera_mask, DrawMask::all_off(), DrawMask::all_off());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1917,7 +1959,7 @@ show(DrawMask camera_mask) {
 INLINE void NodePath::
 hide() {
   nassertv_always(!is_empty());
-  node()->set_draw_mask(DrawMask::all_off());
+  node()->adjust_draw_mask(DrawMask::all_off(), PandaNode::get_overall_bit(), DrawMask::all_off());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1925,13 +1967,17 @@ hide() {
 //       Access: Published
 //  Description: Makes the referenced node invisible just to the
 //               cameras whose camera_mask shares the indicated bits.
-//               That is, this clears the indicated bits from the
-//               node's draw mask.
+//
+//               This will also hide any nodes below this node in the
+//               scene graph, including those nodes for which show()
+//               has been called, but it will not hide descendent
+//               nodes for which show_through() has been called.
 ////////////////////////////////////////////////////////////////////
 INLINE void NodePath::
 hide(DrawMask camera_mask) {
   nassertv_always(!is_empty());
-  node()->set_draw_mask(node()->get_draw_mask() & ~camera_mask);
+  camera_mask &= ~PandaNode::get_overall_bit();
+  node()->adjust_draw_mask(DrawMask::all_off(), camera_mask, DrawMask::all_off());
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -5154,7 +5154,8 @@ get_hidden_ancestor(DrawMask camera_mask) const {
        comp != (NodePathComponent *)NULL; 
        comp = comp->get_next(pipeline_stage)) {
     PandaNode *node = comp->get_node();
-    if ((node->get_draw_mask() & camera_mask).is_zero()) {
+    if (node->is_overall_hidden() ||
+        ((node->get_draw_show_mask() | ~node->get_draw_control_mask()) & camera_mask).is_zero()) {
       NodePath result;
       result._head = comp;
       return result;

+ 4 - 2
panda/src/pgraph/nodePath.h

@@ -738,10 +738,12 @@ PUBLISHED:
   // Variants on show and hide
   INLINE void show();
   INLINE void show(DrawMask camera_mask);
+  INLINE void show_through();
+  INLINE void show_through(DrawMask camera_mask);
   INLINE void hide();
   INLINE void hide(DrawMask camera_mask);
-  INLINE bool is_hidden(DrawMask camera_mask = DrawMask::all_on()) const;
-  NodePath get_hidden_ancestor(DrawMask camera_mask = DrawMask::all_on()) const;
+  INLINE bool is_hidden(DrawMask camera_mask = PandaNode::get_overall_bit()) const;
+  NodePath get_hidden_ancestor(DrawMask camera_mask = PandaNode::get_overall_bit()) const;
 
   void stash(int sort = 0);
   void unstash(int sort = 0);

+ 125 - 5
panda/src/pgraph/pandaNode.I

@@ -17,6 +17,63 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::compose_draw_mask
+//       Access: Public
+//  Description: Computes the result of applying this node's draw
+//               masks to a running draw mask, as during a traversal.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+compose_draw_mask(DrawMask &running_draw_mask) const {
+  CDHeavyReader cdata(_cycler_heavy);
+  running_draw_mask = (running_draw_mask & ~cdata->_draw_control_mask) |
+    (cdata->_draw_show_mask & cdata->_draw_control_mask);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::compare_draw_mask
+//       Access: Public
+//  Description: Compares the running draw mask computed during a
+//               traversal with this node's net draw masks.  Returns
+//               true if the node should be traversed into, or false
+//               if there is nothing at this level or below that will
+//               be visible to the indicated camera_mask.
+////////////////////////////////////////////////////////////////////
+INLINE bool PandaNode::
+compare_draw_mask(DrawMask running_draw_mask, DrawMask camera_mask) const {
+  DrawMask net_draw_control_mask, net_draw_show_mask;
+
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  CDBoundsStageReader cdata(_cycler_bounds, pipeline_stage);
+  if (cdata->_last_update != cdata->_next_update) {
+    // The cache is stale; it needs to be rebuilt.
+    CDBoundsStageWriter cdataw = 
+      ((PandaNode *)this)->update_bounds(pipeline_stage, cdata); 
+    net_draw_control_mask = cdataw->_net_draw_control_mask;
+    net_draw_show_mask = cdataw->_net_draw_show_mask;
+  } else {
+    net_draw_control_mask = cdata->_net_draw_control_mask;
+    net_draw_show_mask = cdata->_net_draw_show_mask;
+  }
+
+  // Now the bits that are not in net_draw_control_mask--that is,
+  // those bits that are not changed by any of the nodes at this level
+  // and below--are taken from running_draw_mask, which is inherited
+  // from above.  On the other hand, the bits that *are* in
+  // net_draw_control_mask--those bits that are changed by any of the
+  // nodes at this level and below--are taken from net_draw_show_mask,
+  // which is propagated upwards from below.
+
+  // This way, we will traverse into this node if it has any children
+  // which want to be visited by the traversal, but we will avoid
+  // traversing into it if all of its children are hidden to this
+  // camera.
+  DrawMask compare_mask = (running_draw_mask & ~net_draw_control_mask) | (net_draw_show_mask & net_draw_control_mask);
+
+  return !((compare_mask & _overall_bit).is_zero()) && !((compare_mask & camera_mask).is_zero());
+}
+
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_num_parents
 //       Access: Published
@@ -431,15 +488,75 @@ ls(ostream &out, int indent_level) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::get_draw_mask
+//     Function: PandaNode::get_overall_bit
+//       Access: Published, Static
+//  Description: Returns the special bit that, when specifically
+//               cleared in the node's DrawMask, indicates that the
+//               node is hidden to all cameras, regardless of the
+//               remaining DrawMask bits.
+////////////////////////////////////////////////////////////////////
+INLINE DrawMask PandaNode::
+get_overall_bit() {
+  return _overall_bit;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::is_overall_hidden
+//       Access: Published, Static
+//  Description: Returns true if the node has been hidden to all
+//               cameras by clearing its overall bit.
+////////////////////////////////////////////////////////////////////
+INLINE bool PandaNode::
+is_overall_hidden() const {
+  CDHeavyReader cdata(_cycler_heavy);
+  return ((cdata->_draw_show_mask | ~cdata->_draw_control_mask) & _overall_bit).is_zero();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::set_overall_hidden
+//       Access: Published
+//  Description: Sets or clears the hidden flag.  When the hidden flag
+//               is true, the node and all of its children are
+//               invisible to all cameras, regardless of the setting
+//               of any draw masks.  Setting the hidden flag to false
+//               restores the previous visibility as established by
+//               the draw masks.
+//
+//               This actually works by twiddling the reserved
+//               _overall_bit in the node's draw mask, which has
+//               special meaning.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+set_overall_hidden(bool hidden) {
+  if (hidden) {
+    adjust_draw_mask(DrawMask::all_off(), _overall_bit, DrawMask::all_off());
+  } else {
+    adjust_draw_mask(DrawMask::all_off(), DrawMask::all_off(), _overall_bit);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_draw_control_mask
+//       Access: Published
+//  Description: Returns the set of bits in draw_show_mask that are
+//               considered meaningful.  See adjust_draw_mask().
+////////////////////////////////////////////////////////////////////
+INLINE DrawMask PandaNode::
+get_draw_control_mask() const {
+  CDHeavyReader cdata(_cycler_heavy);
+  return cdata->_draw_control_mask;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_draw_show_mask
 //       Access: Published
 //  Description: Returns the hide/show bits of this particular node.
-//               See set_draw_mask().
+//               See adjust_draw_mask().
 ////////////////////////////////////////////////////////////////////
 INLINE DrawMask PandaNode::
-get_draw_mask() const {
+get_draw_show_mask() const {
   CDHeavyReader cdata(_cycler_heavy);
-  return cdata->_draw_mask;
+  return cdata->_draw_show_mask;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -791,7 +908,8 @@ CDataLight() {
 INLINE PandaNode::CDataHeavy::
 CDataHeavy() {
   _effects = RenderEffects::make_empty();
-  _draw_mask = DrawMask::all_on();
+  _draw_control_mask = DrawMask::all_off();
+  _draw_show_mask = DrawMask::all_on();
   _into_collide_mask = CollideMask::all_off();
   _user_bounds = NULL;
   _internal_bounds = NULL;
@@ -807,6 +925,8 @@ CDataHeavy() {
 INLINE PandaNode::CDataBounds::
 CDataBounds() {
   _net_collide_mask = CollideMask::all_off();
+  _net_draw_control_mask = DrawMask::all_off();
+  _net_draw_show_mask = DrawMask::all_off();
   ++_next_update;
 }
 

+ 289 - 16
panda/src/pgraph/pandaNode.cxx

@@ -28,6 +28,11 @@
 #include "clipPlaneAttrib.h"
 #include "boundingSphere.h"
 
+// This category is just temporary for debugging convenience.
+NotifyCategoryDecl(drawmask, EXPCL_PANDA, EXPTP_PANDA);
+NotifyCategoryDef(drawmask, "");
+
+DrawMask PandaNode::_overall_bit = DrawMask::bit(31);
 TypeHandle PandaNode::_type_handle;
 
 //
@@ -132,7 +137,8 @@ PandaNode(const PandaNode &copy) :
     CDHeavyWriter cdata(_cycler_heavy);
     cdata->_effects = copy_cdata->_effects;
     cdata->_tag_data = copy_cdata->_tag_data;
-    cdata->_draw_mask = copy_cdata->_draw_mask;
+    cdata->_draw_control_mask = copy_cdata->_draw_control_mask;
+    cdata->_draw_show_mask = copy_cdata->_draw_show_mask;
     cdata->_into_collide_mask = copy_cdata->_into_collide_mask;
     cdata->_user_bounds = copy_cdata->_user_bounds;
     cdata->_internal_bounds = NULL;
@@ -507,6 +513,21 @@ get_visible_child() const {
   return 0;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::is_renderable
+//       Access: Public, Virtual
+//  Description: Returns true if there is some value to visiting this
+//               particular node during the cull traversal for any
+//               camera, false otherwise.  This will be used to
+//               optimize the result of get_net_draw_show_mask(), so
+//               that any subtrees that contain only nodes for which
+//               is_renderable() is false need not be visited.
+////////////////////////////////////////////////////////////////////
+bool PandaNode::
+is_renderable() const {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::copy_subgraph
 //       Access: Published
@@ -1380,36 +1401,124 @@ list_tags(ostream &out, const string &separator) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_draw_mask
+//     Function: PandaNode::adjust_draw_mask
 //       Access: Published
-//  Description: Sets the hide/show bits of this particular node.
+//  Description: Adjusts the hide/show bits of this particular node.
+//
+//               These three parameters can be used to adjust the
+//               _draw_control_mask and _draw_show_mask independently,
+//               which work together to provide per-camera visibility
+//               for the node and its descendents.
 //
-//               During the cull traversal, a node is not visited if
-//               none of its draw mask bits intersect with the
-//               camera's draw mask bits.  These masks can be used to
-//               selectively hide and show different parts of the
-//               scene graph from different cameras that are otherwise
-//               viewing the same scene.  See
-//               Camera::set_camera_mask().
+//               _draw_control_mask indicates the bits in
+//               _draw_show_mask that are significant.  Each different
+//               bit corresponds to a different camera (and these bits
+//               are assigned via Camera::set_camera_mask()).
+//
+//               Where _draw_control_mask has a 1 bit, a 1 bit in
+//               _draw_show_mask indicates the node is visible to that
+//               camera, and a 0 bit indicates the node is hidden to
+//               that camera.  Where _draw_control_mask is 0, the node
+//               is hidden only if a parent node is hidden.
+//
+//               The meaning of the three parameters is as follows:
+//
+//               * Wherever show_mask is 1, _draw_show_mask and
+//               _draw_control_mask will be set 1.  Thus, show_mask
+//               indicates the set of cameras to which the node should
+//               be shown.
+//
+//               * Wherever hide_mask is 1, _draw_show_mask will be
+//               set 0 and _draw_control_mask will be set 1.  Thus,
+//               hide_mask indicates the set of cameras from which the
+//               node should be hidden.
+//
+//               * Wherever clear_mask is 1, _draw_control_mask will
+//               be set 0.  Thus, clear_mask indicates the set of
+//               cameras from which the hidden state should be
+//               inherited from a parent.
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
-set_draw_mask(DrawMask mask) {
+adjust_draw_mask(DrawMask show_mask, DrawMask hide_mask, DrawMask clear_mask) {
   bool any_changed = false;
 
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler_heavy) {
     CDHeavyStageWriter cdata(_cycler_heavy, pipeline_stage);
-    if (cdata->_draw_mask != mask) {
-      cdata->_draw_mask = mask;
+    
+    DrawMask draw_control_mask = (cdata->_draw_control_mask | show_mask | hide_mask) & ~clear_mask;
+    DrawMask draw_show_mask = (cdata->_draw_show_mask | show_mask) & ~hide_mask;
+    // The uncontrolled bits are implicitly on.
+    draw_show_mask |= ~draw_control_mask;
+
+    if (cdata->_draw_control_mask != draw_control_mask ||
+        cdata->_draw_show_mask != draw_show_mask) {
+      cdata->_draw_control_mask = draw_control_mask;
+      cdata->_draw_show_mask = draw_show_mask;
       any_changed = true;
     }
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler_heavy);
 
   if (any_changed) {
+    mark_bounds_stale();
     draw_mask_changed();
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_net_draw_control_mask
+//       Access: Published
+//  Description: Returns the set of bits in get_net_draw_show_mask()
+//               that have been explicitly set via adjust_draw_mask(),
+//               rather than implicitly inherited.
+//
+//               A 1 bit in any position of this mask indicates that
+//               (a) this node has renderable children, and (b) some
+//               child of this node has made an explicit hide() or
+//               show_through() call for the corresponding bit.
+////////////////////////////////////////////////////////////////////
+DrawMask PandaNode::
+get_net_draw_control_mask() const {
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  CDBoundsStageReader cdata(_cycler_bounds, pipeline_stage);
+  if (cdata->_last_update != cdata->_next_update) {
+    // The cache is stale; it needs to be rebuilt.
+    CDBoundsStageWriter cdataw = 
+      ((PandaNode *)this)->update_bounds(pipeline_stage, cdata); 
+    return cdataw->_net_draw_control_mask;
+  }
+  return cdata->_net_draw_control_mask;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_net_draw_show_mask
+//       Access: Published
+//  Description: Returns the union of all draw_show_mask values--of
+//               renderable nodes only--at this level and below.  If
+//               any bit in this mask is 0, there is no reason to
+//               traverse below this node for a camera with the
+//               corresponding camera_mask.
+//
+//               The bits in this mask that do not correspond to a 1
+//               bit in the net_draw_control_mask are meaningless (and
+//               will be set to 1).  For bits that *do* correspond to
+//               a 1 bit in the net_draw_control_mask, a 1 bit
+//               indicates that at least one child should be visible,
+//               while a 0 bit indicates that all children are hidden.
+////////////////////////////////////////////////////////////////////
+DrawMask PandaNode::
+get_net_draw_show_mask() const {
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  CDBoundsStageReader cdata(_cycler_bounds, pipeline_stage);
+  if (cdata->_last_update != cdata->_next_update) {
+    // The cache is stale; it needs to be rebuilt.
+    CDBoundsStageWriter cdataw = 
+      ((PandaNode *)this)->update_bounds(pipeline_stage, cdata); 
+    return cdataw->_net_draw_show_mask;
+  }
+  return cdata->_net_draw_show_mask;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::set_into_collide_mask
 //       Access: Published
@@ -1536,6 +1645,26 @@ write(ostream &out, int indent_level) const {
   if (!effects->is_empty()) {
     out << " " << *effects;
   }
+  DrawMask draw_control_mask = get_draw_control_mask();
+  if (!draw_control_mask.is_zero()) {
+    DrawMask draw_show_mask = get_draw_show_mask();
+    if (!(draw_control_mask & _overall_bit).is_zero()) {
+      if (!(draw_show_mask & _overall_bit).is_zero()) {
+        out << " (show_through)";
+      } else {
+        out << " (hidden)";
+      }
+    }
+    if (!(draw_control_mask & ~_overall_bit).is_zero()) {
+      draw_control_mask &= ~_overall_bit;
+      if (!(draw_show_mask & draw_control_mask).is_zero()) {
+        out << " (per-camera show_through)";
+      }
+      if (!(~draw_show_mask & draw_control_mask).is_zero()) {
+        out << " (per-camera hidden)";
+      }
+    }
+  }
   out << "\n";
 }
 
@@ -2633,6 +2762,10 @@ PandaNode::CDBoundsStageWriter PandaNode::
 update_bounds(int pipeline_stage, PandaNode::CDBoundsStageReader &cdata) {
   // We might need to try this a couple of times, in case someone else
   // steps on our result.
+  if (drawmask_cat.is_debug()) {
+    drawmask_cat.debug(false) 
+      << *this << "::update_bounds() {\n";
+  }
   do {
     // Grab the last_update counter, then release the lock.
     UpdateSeq last_update = cdata->_last_update;
@@ -2643,9 +2776,32 @@ update_bounds(int pipeline_stage, PandaNode::CDBoundsStageReader &cdata) {
     // Start with a clean slate, or at least with the contents of the
     // node itself.
     CollideMask net_collide_mask;
+    DrawMask net_draw_control_mask, net_draw_show_mask;
+    DrawMask draw_control_mask, draw_show_mask;
+    bool renderable = is_renderable();
     {
       CDHeavyStageReader cdata(_cycler_heavy, pipeline_stage);
       net_collide_mask = cdata->_into_collide_mask;
+      draw_control_mask = net_draw_control_mask = cdata->_draw_control_mask;
+      draw_show_mask = net_draw_show_mask = cdata->_draw_show_mask;
+    }
+    if (!renderable) {
+      // This is not a "renderable" node.  This means that this node
+      // does not itself contribute any bits to net_draw_show_mask or
+      // net_draw_control_mask, but it may contribute some bits to any
+      // renderable nodes from below.
+      net_draw_show_mask = DrawMask::all_off();
+      net_draw_control_mask = DrawMask::all_off();
+    }
+    draw_show_mask |= ~draw_control_mask;
+
+    if (drawmask_cat.is_debug()) {
+      drawmask_cat.debug(false)
+        << "net_draw_control_mask = " << net_draw_control_mask
+        << "\nnet_draw_show_mask = " << net_draw_show_mask
+        << "\ndraw_control_mask = " << draw_control_mask
+        << "\ndraw_show_mask = " << draw_show_mask
+        << "\n";
     }
     CPT(RenderAttrib) off_clip_planes;
     {
@@ -2683,6 +2839,37 @@ update_bounds(int pipeline_stage, PandaNode::CDBoundsStageReader &cdata) {
 	CDBoundsStageWriter child_cdataw = child->update_bounds(pipeline_stage, child_cdata);
       
 	net_collide_mask |= child_cdataw->_net_collide_mask;
+
+        DrawMask child_control_mask = child_cdataw->_net_draw_control_mask;
+        DrawMask child_show_mask = child_cdataw->_net_draw_show_mask;
+        if (child_control_mask != DrawMask::all_off() ||
+            child_show_mask != DrawMask::all_off()) {
+          // This child includes a renderable node or subtree.  Thus,
+          // we should propagate its draw masks.
+          renderable = true;
+
+          // Compute the set of control bits that are defined on this
+          // node, but not on the child node.
+          DrawMask new_control_mask = draw_control_mask & ~child_control_mask;
+          // Anywhere we have a control bit that our child does not,
+          // the child inherits our show bit.
+          DrawMask new_child_show_mask = (child_show_mask & ~new_control_mask) | (draw_show_mask & new_control_mask);
+          DrawMask new_child_control_mask = child_control_mask | new_control_mask;
+          // Now merge that result with our accumulated draw masks.
+          net_draw_control_mask |= new_child_control_mask;
+          net_draw_show_mask |= new_child_show_mask;
+        }
+
+        if (drawmask_cat.is_debug()) {
+          drawmask_cat.debug(false)
+            << "\nchild update " << *child << ":\n"
+            << "child_control_mask = " << child_control_mask
+            << "\nchild_show_mask = " << child_show_mask
+            << "\nnet_draw_control_mask = " << net_draw_control_mask
+            << "\nnet_draw_show_mask = " << net_draw_show_mask
+            << "\n";
+        }
+            
 	off_clip_planes = orig_cp->compose_off(child_cdataw->_off_clip_planes);
 	child_volumes_ref.push_back(child_cdataw->_external_bounds);
 	child_volumes.push_back(child_cdataw->_external_bounds);
@@ -2690,12 +2877,43 @@ update_bounds(int pipeline_stage, PandaNode::CDBoundsStageReader &cdata) {
       } else {
 	// Child is good.
 	net_collide_mask |= child_cdata->_net_collide_mask;
+
+        // See comments in similar block above.
+        DrawMask child_control_mask = child_cdata->_net_draw_control_mask;
+        DrawMask child_show_mask = child_cdata->_net_draw_show_mask;
+        if (child_control_mask != DrawMask::all_off() ||
+            child_show_mask != DrawMask::all_off()) {
+          renderable = true;
+          DrawMask new_control_mask = draw_control_mask & ~child_control_mask;
+          DrawMask new_child_show_mask = (child_show_mask & ~new_control_mask) | (draw_show_mask & new_control_mask);
+          DrawMask new_child_control_mask = child_control_mask | new_control_mask;
+          net_draw_control_mask |= new_child_control_mask;
+          net_draw_show_mask |= new_child_show_mask;
+        }
+
+        if (drawmask_cat.is_debug()) {
+          drawmask_cat.debug(false)
+            << "\nchild fresh " << *child << ":\n"
+            << "child_control_mask = " << child_control_mask
+            << "\nchild_show_mask = " << child_show_mask
+            << "\nnet_draw_control_mask = " << net_draw_control_mask
+            << "\nnet_draw_show_mask = " << net_draw_show_mask
+            << "\n";
+        }
+
 	off_clip_planes = orig_cp->compose_off(child_cdata->_off_clip_planes);
 	child_volumes_ref.push_back(child_cdata->_external_bounds);
 	child_volumes.push_back(child_cdata->_external_bounds);
       }
     }
 
+    if (renderable) {
+      // This is a "renderable" node.  That means all draw_show_mask
+      // bits, not specifically set on or off, should be assumed to be
+      // on.
+      net_draw_show_mask |= ~net_draw_control_mask;
+    }
+
     {
       // Now grab the write lock on this node.
       CDBoundsStageWriter cdataw(_cycler_bounds, pipeline_stage);
@@ -2704,6 +2922,27 @@ update_bounds(int pipeline_stage, PandaNode::CDBoundsStageReader &cdata) {
 	// Great, no one has monkeyed with these while we were computing
 	// the cache.  Safe to store the computed values and return.
 	cdataw->_net_collide_mask = net_collide_mask;
+
+        if (renderable) {
+          // There are renderable nodes below, so the implicit draw
+          // bits are all on.
+          cdataw->_net_draw_control_mask = net_draw_control_mask;
+          cdataw->_net_draw_show_mask = net_draw_show_mask | ~net_draw_control_mask;
+          if (drawmask_cat.is_debug()) {
+            drawmask_cat.debug(false)
+              << "renderable, set mask " << cdataw->_net_draw_show_mask << "\n";
+          }
+        } else {
+          // There are no renderable nodes below, so the implicit draw
+          // bits are all off.
+          cdataw->_net_draw_control_mask = net_draw_control_mask;
+          cdataw->_net_draw_show_mask = net_draw_show_mask;
+          if (drawmask_cat.is_debug()) {
+            drawmask_cat.debug(false)
+              << "not renderable, set mask " << cdataw->_net_draw_show_mask << "\n";
+          }
+        }
+
 	cdataw->_off_clip_planes = off_clip_planes;
 
 	// Compute the bounding sphere around all of our child
@@ -2722,6 +2961,11 @@ update_bounds(int pipeline_stage, PandaNode::CDBoundsStageReader &cdata) {
 
 	cdataw->_external_bounds = gbv;
 	cdataw->_last_update = next_update;
+
+        if (drawmask_cat.is_debug()) {
+          drawmask_cat.debug(false)
+            << "} " << *this << "::update_bounds();\n";
+        }
 	return cdataw;
       }
       
@@ -2946,7 +3190,8 @@ PandaNode::CDataHeavy::
 CDataHeavy(const PandaNode::CDataHeavy &copy) :
   _effects(copy._effects),
   _tag_data(copy._tag_data),
-  _draw_mask(copy._draw_mask),
+  _draw_control_mask(copy._draw_control_mask),
+  _draw_show_mask(copy._draw_show_mask),
   _into_collide_mask(copy._into_collide_mask),
   _user_bounds(copy._user_bounds),
   _internal_bounds(copy._internal_bounds),
@@ -2998,7 +3243,8 @@ void PandaNode::CDataHeavy::
 write_datagram(BamWriter *manager, Datagram &dg) const {
   manager->write_pointer(dg, _effects);
 
-  dg.add_uint32(_draw_mask.get_word());
+  dg.add_uint32(_draw_control_mask.get_word());
+  dg.add_uint32(_draw_show_mask.get_word());
   dg.add_uint32(_into_collide_mask.get_word());
 
   dg.add_uint32(_tag_data.size());
@@ -3048,7 +3294,32 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   // Read the effects pointer.
   manager->read_pointer(scan);
 
-  _draw_mask.set_word(scan.get_uint32());
+  if (manager->get_file_minor_ver() < 2) {
+    DrawMask draw_mask;
+    draw_mask.set_word(scan.get_uint32());
+
+    if (draw_mask == DrawMask::all_off()) {
+      // Hidden.
+      _draw_control_mask = _overall_bit;
+      _draw_show_mask = ~_overall_bit;
+
+    } else if (draw_mask == DrawMask::all_on()) {
+      // Normally visible.
+      _draw_control_mask = DrawMask::all_off();
+      _draw_show_mask = DrawMask::all_on();
+
+    } else {
+      // Some per-camera combination.
+      draw_mask &= ~_overall_bit;
+      _draw_control_mask = ~draw_mask;
+      _draw_show_mask = draw_mask;
+    }
+
+  } else {
+    _draw_control_mask.set_word(scan.get_uint32());
+    _draw_show_mask.set_word(scan.get_uint32());
+  }
+
   _into_collide_mask.set_word(scan.get_uint32());
 
   // Read in the tag list.
@@ -3106,6 +3377,8 @@ dec_py_refs() {
 PandaNode::CDataBounds::
 CDataBounds(const PandaNode::CDataBounds &copy) :
   _net_collide_mask(copy._net_collide_mask),
+  _net_draw_control_mask(copy._net_draw_control_mask),
+  _net_draw_show_mask(copy._net_draw_show_mask),
   _off_clip_planes(copy._off_clip_planes),
   _external_bounds(copy._external_bounds),
   _last_update(copy._last_update),

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

@@ -103,6 +103,11 @@ public:
   virtual int get_next_visible_child(int n) const;
   virtual bool has_single_child_visibility() const;
   virtual int get_visible_child() const;
+  virtual bool is_renderable() const;
+
+  INLINE void compose_draw_mask(DrawMask &running_draw_mask) const;
+  INLINE bool compare_draw_mask(DrawMask running_draw_mask,
+                                DrawMask camera_mask) const;
 
 PUBLISHED:
   PT(PandaNode) copy_subgraph() const;
@@ -180,8 +185,18 @@ PUBLISHED:
   void copy_tags(PandaNode *other);
   void list_tags(ostream &out, const string &separator = "\n") const;
 
-  void set_draw_mask(DrawMask mask);
-  INLINE DrawMask get_draw_mask() const;
+  INLINE static DrawMask get_overall_bit();
+  INLINE bool is_overall_hidden() const;
+  INLINE void set_overall_hidden(bool overall_hidden);
+
+  void adjust_draw_mask(DrawMask show_mask,
+                        DrawMask hide_mask,
+                        DrawMask clear_mask);
+  INLINE DrawMask get_draw_control_mask() const;
+  INLINE DrawMask get_draw_show_mask() const;
+
+  CollideMask get_net_draw_control_mask() const;
+  CollideMask get_net_draw_show_mask() const;
 
   void set_into_collide_mask(CollideMask mask);
   INLINE CollideMask get_into_collide_mask() const;
@@ -406,8 +421,9 @@ private:
     PythonTagData _python_tag_data;
 #endif  // HAVE_PYTHON
 
-    // This is the draw_mask of this particular node.
-    DrawMask _draw_mask;
+    // These two together determine the per-camera visibility of this
+    // node.  See adjust_draw_mask() for details.
+    DrawMask _draw_control_mask, _draw_show_mask;
 
     // This is the mask that indicates which CollisionNodes may detect
     // a collision with this particular node.  By default it is zero
@@ -453,14 +469,15 @@ private:
     }
 
     // This is the union of all into_collide_mask bits for any nodes
-    // at and below this level.  It's updated automatically whenever
-    // _stale_bounds is true.
+    // at and below this level.
     CollideMask _net_collide_mask;
 
+    // These are similar, for the draw mask.
+    DrawMask _net_draw_control_mask, _net_draw_show_mask;
+
     // This is a ClipPlaneAttrib that represents the union of all clip
-    // planes that have been turned *off* at and below this level.  We
-    // piggyback this automatic update on _stale_bounds.  TODO:
-    // fix the circular reference counts involved here.
+    // planes that have been turned *off* at and below this level.
+    // TODO: fix the circular reference counts involved here.
     CPT(RenderAttrib) _off_clip_planes;
 
     // This is the bounding volume around the _user_bounds, the
@@ -521,6 +538,8 @@ private:
   CDBoundsStageWriter update_bounds(int pipeline_stage, 
 				    CDBoundsStageReader &cdata);
 
+  static DrawMask _overall_bit;
+
 public:
   // This class is returned from get_children_copy().  Use it to walk
   // through the list of children, particularly with a self-modifying

+ 16 - 1
panda/src/pgraph/planeNode.cxx

@@ -78,7 +78,7 @@ PlaneNode(const string &name, const Planef &plane) :
   _priority(0)
 {
   // PlaneNodes are hidden by default.
-  set_draw_mask(DrawMask::all_off());
+  set_overall_hidden(true);
 
   set_plane(plane);
 }
@@ -183,6 +183,21 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PlaneNode::is_renderable
+//       Access: Public, Virtual
+//  Description: Returns true if there is some value to visiting this
+//               particular node during the cull traversal for any
+//               camera, false otherwise.  This will be used to
+//               optimize the result of get_net_draw_show_mask(), so
+//               that any subtrees that contain only nodes for which
+//               is_renderable() is false need not be visited.
+////////////////////////////////////////////////////////////////////
+bool PlaneNode::
+is_renderable() const {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PlaneNode::compute_internal_bounds
 //       Access: Protected, Virtual

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

@@ -53,6 +53,7 @@ public:
 
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
+  virtual bool is_renderable() const;
 
 PUBLISHED:
   INLINE void set_plane(const Planef &plane);

+ 15 - 0
panda/src/pgraph/portalNode.cxx

@@ -334,6 +334,21 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PortalNode::is_renderable
+//       Access: Public, Virtual
+//  Description: Returns true if there is some value to visiting this
+//               particular node during the cull traversal for any
+//               camera, false otherwise.  This will be used to
+//               optimize the result of get_net_draw_show_mask(), so
+//               that any subtrees that contain only nodes for which
+//               is_renderable() is false need not be visited.
+////////////////////////////////////////////////////////////////////
+bool PortalNode::
+is_renderable() const {
+  return true;
+}
+
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PortalNode::output

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

@@ -60,6 +60,7 @@ public:
 
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
+  virtual bool is_renderable() const;
 
   virtual void output(ostream &out) const;
 

+ 7 - 3
panda/src/pgraph/sceneGraphReducer.cxx

@@ -343,8 +343,11 @@ operator () (const PandaNode *node1, const PandaNode *node2) const {
   if (node1->get_effects() != node2->get_effects()) {
     return node1->get_effects() < node2->get_effects();
   }
-  if (node1->get_draw_mask() != node2->get_draw_mask()) {
-    return node1->get_draw_mask() < node2->get_draw_mask();
+  if (node1->get_draw_control_mask() != node2->get_draw_control_mask()) {
+    return node1->get_draw_control_mask() < node2->get_draw_control_mask();
+  }
+  if (node1->get_draw_show_mask() != node2->get_draw_show_mask()) {
+    return node1->get_draw_show_mask() < node2->get_draw_show_mask();
   }
 
   return 0;
@@ -452,7 +455,8 @@ consider_child(PandaNode *grandparent_node, PandaNode *parent_node,
   if (parent_node->get_transform() != child_node->get_transform() ||
       parent_node->get_state() != child_node->get_state() ||
       parent_node->get_effects() != child_node->get_effects() ||
-      parent_node->get_draw_mask() != child_node->get_draw_mask()) {
+      parent_node->get_draw_control_mask() != child_node->get_draw_control_mask() ||
+      parent_node->get_draw_show_mask() != child_node->get_draw_show_mask()) {
     // The two nodes have a different state; too bad.
     return false;
   }

+ 16 - 1
panda/src/pgui/pgItem.cxx

@@ -252,6 +252,21 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PGItem::is_renderable
+//       Access: Public, Virtual
+//  Description: Returns true if there is some value to visiting this
+//               particular node during the cull traversal for any
+//               camera, false otherwise.  This will be used to
+//               optimize the result of get_net_draw_show_mask(), so
+//               that any subtrees that contain only nodes for which
+//               is_renderable() is false need not be visited.
+////////////////////////////////////////////////////////////////////
+bool PGItem::
+is_renderable() const {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PGItem::compute_internal_bounds
 //       Access: Protected, Virtual
@@ -1025,7 +1040,7 @@ play_sound(const string &event) {
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 reduce_region(LVecBase4f &frame, PGItem *obscurer) const {
-  if (obscurer != (PGItem *)NULL && !obscurer->get_draw_mask().is_zero()) {
+  if (obscurer != (PGItem *)NULL && !obscurer->is_overall_hidden()) {
     LVecBase4f oframe = get_relative_frame(obscurer);
 
     // Determine the four rectangular regions on the four sides of the

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

@@ -67,6 +67,7 @@ protected:
 
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
+  virtual bool is_renderable() const;
 
   virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage) const;
 

+ 7 - 7
panda/src/pgui/pgScrollFrame.cxx

@@ -267,18 +267,18 @@ remanage() {
     // Now show or hide the sliders appropriately.
     if (_horizontal_slider != (PGSliderBar *)NULL) {
       if (got_horizontal) {
-        _horizontal_slider->set_draw_mask(DrawMask::all_on());
+        _horizontal_slider->set_overall_hidden(false);
       } else {
-        _horizontal_slider->set_draw_mask(DrawMask::all_off());
+        _horizontal_slider->set_overall_hidden(true);
         _horizontal_slider->set_ratio(0.0f);
         horizontal_width = 0.0f;
       }
     }
     if (_vertical_slider != (PGSliderBar *)NULL) {
       if (got_vertical) {
-        _vertical_slider->set_draw_mask(DrawMask::all_on());
+        _vertical_slider->set_overall_hidden(false);
       } else {
-        _vertical_slider->set_draw_mask(DrawMask::all_off());
+        _vertical_slider->set_overall_hidden(true);
         _vertical_slider->set_ratio(0.0f);
         vertical_width = 0.0f;
       }
@@ -288,14 +288,14 @@ remanage() {
     // flag again indirectly; we clear it again to avoid a feedback
     // loop.
     _needs_remanage = false;
-  }
+}
 
   // Are either or both of the scroll bars hidden?
-  if (got_horizontal && _horizontal_slider->get_draw_mask().is_zero()) {
+  if (got_horizontal && _horizontal_slider->is_overall_hidden()) {
     got_horizontal = false;
     horizontal_width = 0.0f;
   }
-  if (got_vertical && _vertical_slider->get_draw_mask().is_zero()) {
+  if (got_vertical && _vertical_slider->is_overall_hidden()) {
     got_vertical = false;
     vertical_width = 0.0f;
   }

+ 2 - 1
panda/src/putil/bam.h

@@ -36,8 +36,9 @@ static const unsigned short _bam_major_ver = 6;
 // Bumped to major version 5 on 5/6/05 for new Geom implementation.
 // Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData.
 
-static const unsigned short _bam_minor_ver = 1;
+static const unsigned short _bam_minor_ver = 2;
 // Bumped to minor version 1 on 3/12/06 to add Texture::_compression.
+// Bumped to minor version 2 on 3/17/06 to add PandaNode::_draw_control_mask.
 
 
 #endif

+ 2 - 1
panda/src/putil/bitArray.I

@@ -57,10 +57,11 @@ BitArray(const BitArray &copy) :
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-INLINE void BitArray::
+INLINE BitArray &BitArray::
 operator = (const BitArray &copy) {
   _array = copy._array;
   _highest_bits = copy._highest_bits;
+  return *this;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/putil/bitArray.h

@@ -47,7 +47,7 @@ PUBLISHED:
   INLINE BitArray();
   INLINE BitArray(WordType init_value);
   INLINE BitArray(const BitArray &copy);
-  INLINE void operator = (const BitArray &copy);
+  INLINE BitArray &operator = (const BitArray &copy);
 
   INLINE static BitArray all_on();
   INLINE static BitArray all_off();

+ 2 - 1
panda/src/putil/bitMask.I

@@ -61,9 +61,10 @@ BitMask(const BitMask<WType, nbits> &copy) :
 //  Description:
 ////////////////////////////////////////////////////////////////////
 template<class WType, int nbits>
-INLINE void BitMask<WType, nbits>::
+INLINE BitMask<WType, nbits> &BitMask<WType, nbits>::
 operator = (const BitMask<WType, nbits> &copy) {
   _word = copy._word;
+  return *this;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/putil/bitMask.h

@@ -44,7 +44,7 @@ PUBLISHED:
   INLINE BitMask();
   INLINE BitMask(WordType init_value);
   INLINE BitMask(const BitMask<WType, nbits> &copy);
-  INLINE void operator = (const BitMask<WType, nbits> &copy);
+  INLINE BitMask<WType, nbits> &operator = (const BitMask<WType, nbits> &copy);
 
   INLINE static BitMask<WType, nbits> all_on();
   INLINE static BitMask<WType, nbits> all_off();

+ 16 - 15
panda/src/text/textNode.cxx

@@ -194,24 +194,10 @@ write(ostream &out, int indent_level) const {
   indent(out, indent_level)
     << "TextNode " << get_name() << "\n";
   TextProperties::write(out, indent_level + 2);
-
-  out << "\n";
-  LVecBase3f scale, shear, hpr, trans;
-  if (decompose_matrix(_transform, scale, shear, hpr, trans, _coordinate_system)) {
   indent(out, indent_level + 2)
-    << "transform is:\n"
-    << "  scale: " << scale << "\n"
-    << "  shear: " << shear << "\n"
-    << "    hpr: " << hpr << "\n"
-    << "  trans: " << hpr << "\n";
-  } else {
-    indent(out, indent_level + 2)
-      << "transform is:\n" << _transform;
-  }
+    << "transform is: " << *TransformState::make_mat(_transform) << "\n";
   indent(out, indent_level + 2)
     << "in coordinate system " << _coordinate_system << "\n";
-
-  out << "\n";
   indent(out, indent_level + 2)
     << "text is " << get_text() << "\n";
 }
@@ -569,6 +555,21 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::is_renderable
+//       Access: Public, Virtual
+//  Description: Returns true if there is some value to visiting this
+//               particular node during the cull traversal for any
+//               camera, false otherwise.  This will be used to
+//               optimize the result of get_net_draw_show_mask(), so
+//               that any subtrees that contain only nodes for which
+//               is_renderable() is false need not be visited.
+////////////////////////////////////////////////////////////////////
+bool TextNode::
+is_renderable() const {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TextNode::compute_internal_bound
 //       Access: Protected, Virtual

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

@@ -236,6 +236,7 @@ public:
 
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
+  virtual bool is_renderable() const;
 
   virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage) const;
 

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

@@ -1029,7 +1029,7 @@ set_no_mouse() {
   if (_has_mouse) {
     // Hide the mouse pointer.
     if (!_geometry.is_null()) {
-      _geometry->set_draw_mask(DrawMask::all_off());
+      _geometry->set_overall_hidden(true);
     }
   }
   
@@ -1053,7 +1053,7 @@ set_mouse(const LVecBase2f &xy, const LVecBase2f &pixel_xy) {
     _geometry->set_transform(TransformState::make_pos(LVecBase3f(xy[0], 0, xy[1])));
     if (!_has_mouse) {
       // Show the mouse pointer.
-      _geometry->set_draw_mask(DrawMask::all_on());
+      _geometry->set_overall_hidden(false);
     }
   }
   

+ 1 - 1
pandatool/src/bam/bamToEgg.cxx

@@ -396,7 +396,7 @@ bool BamToEgg::
 apply_node_properties(EggGroup *egg_group, PandaNode *node) {
   bool any_applied = false;
 
-  if (node->get_draw_mask().is_zero()) {
+  if (node->is_overall_hidden()) {
     // This node is hidden.  We'll go ahead and convert it, but we'll
     // put in the "backstage" flag to mean it's not real geometry.
     egg_group->add_object_type("backstage");