فهرست منبع

add show_switch() debugging mode

David Rose 19 سال پیش
والد
کامیت
3d415c4e0f
6فایلهای تغییر یافته به همراه834 افزوده شده و 208 حذف شده
  1. 28 0
      panda/src/pgraph/camera.I
  2. 4 0
      panda/src/pgraph/camera.h
  3. 4 0
      panda/src/pgraph/fadeLodNode.cxx
  4. 230 120
      panda/src/pgraph/lodNode.I
  5. 529 87
      panda/src/pgraph/lodNode.cxx
  6. 39 1
      panda/src/pgraph/lodNode.h

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

@@ -152,6 +152,34 @@ get_cull_center() const {
   return _cull_center;
   return _cull_center;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Camera::set_lod_center
+//       Access: Published
+//  Description: Specifies the point from which the LOD distances
+//               are measured.  Normally, this is the same as the
+//               camera, and that is the default if this is not
+//               specified; but it may sometimes be useful to perform
+//               the distance test from some other viewpoint.  This
+//               may be used, for instance, to reduce LOD popping when
+//               the camera rotates in a small circle about an avatar.
+////////////////////////////////////////////////////////////////////
+INLINE void Camera::
+set_lod_center(const NodePath &lod_center) {
+  _lod_center = lod_center;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Camera::get_lod_center
+//       Access: Published
+//  Description: Returns the point from which the LOD distances will
+//               be measured, if it was set by set_lod_center(), or
+//               the empty NodePath otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE const NodePath &Camera::
+get_lod_center() const {
+  return _lod_center;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Camera::set_initial_state
 //     Function: Camera::set_initial_state
 //       Access: Published
 //       Access: Published

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

@@ -67,6 +67,9 @@ PUBLISHED:
   INLINE void set_cull_center(const NodePath &cull_center);
   INLINE void set_cull_center(const NodePath &cull_center);
   INLINE const NodePath &get_cull_center() const;
   INLINE const NodePath &get_cull_center() const;
 
 
+  INLINE void set_lod_center(const NodePath &lod_center);
+  INLINE const NodePath &get_lod_center() const;
+
   INLINE void set_initial_state(const RenderState *state);
   INLINE void set_initial_state(const RenderState *state);
   INLINE CPT(RenderState) get_initial_state() const;
   INLINE CPT(RenderState) get_initial_state() const;
 
 
@@ -91,6 +94,7 @@ private:
   bool _active;
   bool _active;
   NodePath _scene;
   NodePath _scene;
   NodePath _cull_center;
   NodePath _cull_center;
+  NodePath _lod_center;
 
 
   DrawMask _camera_mask;
   DrawMask _camera_mask;
 
 

+ 4 - 0
panda/src/pgraph/fadeLodNode.cxx

@@ -73,6 +73,10 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool FadeLODNode::
 bool FadeLODNode::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  if (is_any_shown()) {
+    return show_switches_cull_callback(trav, data);
+  }
+
   Camera *camera = trav->get_scene()->get_camera_node();
   Camera *camera = trav->get_scene()->get_camera_node();
   NodePath this_np = data._node_path.get_node_path();
   NodePath this_np = data._node_path.get_node_path();
   FadeLODNodeData *ldata = 
   FadeLODNodeData *ldata = 

+ 230 - 120
panda/src/pgraph/lodNode.I

@@ -17,126 +17,6 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::CData::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE LODNode::CData::
-CData() :
-  _center(0.0f, 0.0f, 0.0f),
-  _lowest(0),
-  _highest(0),
-  _got_force_switch(false),
-  _force_switch(0)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::CData::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE LODNode::CData::
-CData(const LODNode::CData &copy) :
-  _center(copy._center),
-  _switch_vector(copy._switch_vector),
-  _lowest(copy._lowest),
-  _highest(copy._highest),
-  _got_force_switch(copy._got_force_switch),
-  _force_switch(copy._force_switch)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::Switch::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE LODNode::Switch::
-Switch(float in, float out) {
-  set_range(in, out);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::Switch::get_in
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE float LODNode::Switch::
-get_in() const {
-  return _in;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::Switch::get_out
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE float LODNode::Switch::
-get_out() const {
-  return _out;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::Switch::set_range
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void LODNode::Switch::
-set_range(float in, float out) {
-  _in = in;
-  _out = out;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::Switch::in_range
-//       Access: Public
-//  Description: Computes the distance between two points and returns
-//               true if the result is within the range for the LOD.
-////////////////////////////////////////////////////////////////////
-INLINE bool LODNode::Switch::
-in_range(float dist) const {
-  return (dist >= _out && dist < _in);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::Switch::rescale
-//       Access: Public
-//  Description: Scales the switching distances by the indicated factor.
-////////////////////////////////////////////////////////////////////
-INLINE void LODNode::Switch::
-rescale(float factor) {
-  _in *= factor;
-  _out *= factor;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::Switch::write_datagram
-//       Access: Public
-//  Description: Writes the contents of the Switch out to the
-//               datagram, presumably in preparation to writing to a
-//               Bam file.
-////////////////////////////////////////////////////////////////////
-INLINE void LODNode::Switch::
-write_datagram(Datagram &destination) const {
-  destination.add_float32(_in);
-  destination.add_float32(_out);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::Switch::read_datagram
-//       Access: Public
-//  Description: Reads the contents of the Switch from the datagram,
-//               presumably in response to reading a Bam file.
-////////////////////////////////////////////////////////////////////
-INLINE void LODNode::Switch::
-read_datagram(DatagramIterator &source) {
-  _in = source.get_float32();
-  _out = source.get_float32();
-}
-
-
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: LODNode::Constructor
 //     Function: LODNode::Constructor
@@ -179,6 +59,10 @@ add_switch(float in, float out) {
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
   cdata->_switch_vector.push_back(Switch(in, out));
   cdata->_switch_vector.push_back(Switch(in, out));
   cdata->check_limits();
   cdata->check_limits();
+
+  if (cdata->_num_shown != 0) {
+    mark_internal_bounds_stale();
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -193,6 +77,11 @@ set_switch(int index, float in, float out) {
   nassertr(index >= 0 && index < (int)cdata->_switch_vector.size(), false);
   nassertr(index >= 0 && index < (int)cdata->_switch_vector.size(), false);
   cdata->_switch_vector[index].set_range(in, out);
   cdata->_switch_vector[index].set_range(in, out);
   cdata->check_limits();
   cdata->check_limits();
+
+  if (cdata->_num_shown != 0) {
+    mark_internal_bounds_stale();
+  }
+
   return true;
   return true;
 }
 }
 
 
@@ -209,6 +98,10 @@ clear_switches() {
   cdata->_switch_vector.clear();
   cdata->_switch_vector.clear();
   cdata->_lowest = 0;
   cdata->_lowest = 0;
   cdata->_highest = 0;
   cdata->_highest = 0;
+
+  if (cdata->_num_shown != 0) {
+    mark_internal_bounds_stale();
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -319,6 +212,10 @@ INLINE void LODNode::
 set_center(const LPoint3f &center) {
 set_center(const LPoint3f &center) {
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
   cdata->_center = center;
   cdata->_center = center;
+
+  if (cdata->_num_shown != 0) {
+    mark_internal_bounds_stale();
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -333,3 +230,216 @@ get_center() const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
   return cdata->_center;
   return cdata->_center;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::is_any_shown
+//       Access: Published
+//  Description: Returns true if any switch has been shown with
+//               show_switch(), indicating the LODNode is in debug
+//               show mode; or false if it is in the normal mode.
+////////////////////////////////////////////////////////////////////
+INLINE bool LODNode::
+is_any_shown() const {
+  CDReader cdata(_cycler);
+  return (cdata->_num_shown != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE LODNode::CData::
+CData() :
+  _center(0.0f, 0.0f, 0.0f),
+  _lowest(0),
+  _highest(0),
+  _got_force_switch(false),
+  _force_switch(0),
+  _num_shown(0)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE LODNode::CData::
+CData(const LODNode::CData &copy) :
+  _center(copy._center),
+  _switch_vector(copy._switch_vector),
+  _lowest(copy._lowest),
+  _highest(copy._highest),
+  _got_force_switch(copy._got_force_switch),
+  _force_switch(copy._force_switch),
+  _num_shown(copy._num_shown)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE LODNode::Switch::
+Switch(float in, float out) : _shown(false) {
+  set_range(in, out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::get_in
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE float LODNode::Switch::
+get_in() const {
+  return _in;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::get_out
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE float LODNode::Switch::
+get_out() const {
+  return _out;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::set_range
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::Switch::
+set_range(float in, float out) {
+  _in = in;
+  _out = out;
+  _ring_viz.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::in_range
+//       Access: Public
+//  Description: Returns true if the indicated distance is within the
+//               range for the LOD.
+////////////////////////////////////////////////////////////////////
+INLINE bool LODNode::Switch::
+in_range(float dist) const {
+  return (dist >= _out && dist < _in);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::in_range_2
+//       Access: Public
+//  Description: Returns true if the indicated distance squared is
+//               within the range for the LOD.  (The distance value is
+//               understood to be the square of the distance from the
+//               camera to the object.)
+////////////////////////////////////////////////////////////////////
+INLINE bool LODNode::Switch::
+in_range_2(float dist2) const {
+  return (dist2 >= _out * _out && dist2 < _in * _in);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::rescale
+//       Access: Public
+//  Description: Scales the switching distances by the indicated factor.
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::Switch::
+rescale(float factor) {
+  _in *= factor;
+  _out *= factor;
+  _ring_viz.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::is_shown
+//       Access: Public
+//  Description: Returns true if show() has been called.
+////////////////////////////////////////////////////////////////////
+INLINE bool LODNode::Switch::
+is_shown() const {
+  return _shown;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::show
+//       Access: Public
+//  Description: Shows this ring in debug mode using the indicated
+//               color.
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::Switch::
+show(const Colorf &color) {
+  _shown = true;
+  _show_color = color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::hide
+//       Access: Public
+//  Description: Undoes a previous call to show().
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::Switch::
+hide() {
+  _shown = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::get_ring_viz
+//       Access: Public
+//  Description: Returns a PandaNode suitable for rendering the ring
+//               associated with this switch.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode *LODNode::Switch::
+get_ring_viz() const {
+  if (_ring_viz.is_null()) {
+    ((Switch *)this)->compute_ring_viz();
+  }
+
+  return _ring_viz;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::get_viz_model_state
+//       Access: Public
+//  Description: Returns a RenderState suitable for drawing the
+//               visible children of this switch level when the
+//               show_switch() debugging mode is enabled.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderState *LODNode::Switch::
+get_viz_model_state() const {
+  if (_viz_model_state.is_null()) {
+    ((Switch *)this)->compute_ring_viz();
+  }
+
+  return _viz_model_state;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::write_datagram
+//       Access: Public
+//  Description: Writes the contents of the Switch out to the
+//               datagram, presumably in preparation to writing to a
+//               Bam file.
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::Switch::
+write_datagram(Datagram &destination) const {
+  destination.add_float32(_in);
+  destination.add_float32(_out);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::read_datagram
+//       Access: Public
+//  Description: Reads the contents of the Switch from the datagram,
+//               presumably in response to reading a Bam file.
+////////////////////////////////////////////////////////////////////
+INLINE void LODNode::Switch::
+read_datagram(DatagramIterator &source) {
+  _in = source.get_float32();
+  _out = source.get_float32();
+}
+

+ 529 - 87
panda/src/pgraph/lodNode.cxx

@@ -20,82 +20,25 @@
 #include "cullTraverserData.h"
 #include "cullTraverserData.h"
 #include "cullTraverser.h"
 #include "cullTraverser.h"
 #include "config_pgraph.h"
 #include "config_pgraph.h"
+#include "geomVertexData.h"
+#include "geomVertexWriter.h"
+#include "geomVertexFormat.h"
+#include "geomTristrips.h"
+#include "mathNumbers.h"
+#include "geom.h"
+#include "geomNode.h"
+#include "transformState.h"
+#include "material.h"
+#include "materialAttrib.h"
+#include "materialPool.h"
+#include "renderState.h"
+#include "cullFaceAttrib.h"
+#include "textureAttrib.h"
+#include "boundingSphere.h"
+#include "look_at.h"
 
 
 TypeHandle LODNode::_type_handle;
 TypeHandle LODNode::_type_handle;
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::CData::make_copy
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-CycleData *LODNode::CData::
-make_copy() const {
-  return new CData(*this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::CData::check_limits
-//       Access: Public
-//  Description: Ensures that the _lowest and _highest members are set
-//               appropriately after a change to the set of switches.
-////////////////////////////////////////////////////////////////////
-void LODNode::CData::
-check_limits() {
-  _lowest = 0;
-  _highest = 0;
-  for (size_t i = 1; i < _switch_vector.size(); ++i) {
-    if (_switch_vector[i].get_out() > _switch_vector[_lowest].get_out()) {
-      _lowest = i;
-    }
-    if (_switch_vector[i].get_in() < _switch_vector[_highest].get_in()) {
-      _highest = i;
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::CData::write_datagram
-//       Access: Public, Virtual
-//  Description: Writes the contents of this object to the datagram
-//               for shipping out to a Bam file.
-////////////////////////////////////////////////////////////////////
-void LODNode::CData::
-write_datagram(BamWriter *manager, Datagram &dg) const {
-  _center.write_datagram(dg);
-
-  dg.add_uint16(_switch_vector.size());
-
-  SwitchVector::const_iterator si;
-  for (si = _switch_vector.begin();
-       si != _switch_vector.end();
-       ++si) {
-    (*si).write_datagram(dg);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LODNode::CData::fillin
-//       Access: Public, Virtual
-//  Description: This internal function is called by make_from_bam to
-//               read in all of the relevant data from the BamFile for
-//               the new LODNode.
-////////////////////////////////////////////////////////////////////
-void LODNode::CData::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  _center.read_datagram(scan);
-
-  _switch_vector.clear();
-
-  int num_switches = scan.get_uint16();
-  _switch_vector.reserve(num_switches);
-  for (int i = 0; i < num_switches; i++) {
-    Switch sw(0, 0);
-    sw.read_datagram(scan);
-
-    _switch_vector.push_back(sw);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: LODNode::make_copy
 //     Function: LODNode::make_copy
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -185,6 +128,10 @@ has_cull_callback() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool LODNode::
 bool LODNode::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  if (is_any_shown()) {
+    return show_switches_cull_callback(trav, data);
+  }
+
   int index = compute_child(trav, data);
   int index = compute_child(trav, data);
   if (index >= 0 && index < get_num_children()) {
   if (index >= 0 && index < get_num_children()) {
     CullTraverserData next_data(data, get_child(index));
     CullTraverserData next_data(data, get_child(index));
@@ -235,6 +182,93 @@ is_lod_node() const {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::show_switch
+//       Access: Published
+//  Description: This is provided as a debugging aid.  show_switch()
+//               will put the LODNode into a special mode where rather
+//               than computing and drawing the appropriate level of
+//               the LOD, a ring is drawn around the LODNode center
+//               indicating the switch distances from the camera for
+//               the indicated level, and the geometry of the
+//               indicated level is drawn in wireframe.
+//
+//               Multiple different levels can be visualized this way
+//               at once.  Call hide_switch() or hide_all_switches() to
+//               undo this mode and restore the LODNode to its normal
+//               behavior.
+////////////////////////////////////////////////////////////////////
+void LODNode::
+show_switch(int index) {
+  CDWriter cdata(_cycler);
+  do_show_switch(cdata, index, get_default_show_color(index));
+  mark_internal_bounds_stale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::show_switch
+//       Access: Published
+//  Description: This is provided as a debugging aid.  show_switch()
+//               will put the LODNode into a special mode where rather
+//               than computing and drawing the appropriate level of
+//               the LOD, a ring is drawn around the LODNode center
+//               indicating the switch distances from the camera for
+//               the indicated level, and the geometry of the
+//               indicated level is drawn in wireframe.
+//
+//               Multiple different levels can be visualized this way
+//               at once.  Call hide_switch() or hide_all_switches() to
+//               undo this mode and restore the LODNode to its normal
+//               behavior.
+////////////////////////////////////////////////////////////////////
+void LODNode::
+show_switch(int index, const Colorf &color) {
+  CDWriter cdata(_cycler);
+  do_show_switch(cdata, index, color);
+  mark_internal_bounds_stale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::hide_switch
+//       Access: Published
+//  Description: Disables a previous call to show_switch().
+////////////////////////////////////////////////////////////////////
+void LODNode::
+hide_switch(int index) {
+  CDWriter cdata(_cycler);
+  do_hide_switch(cdata, index);
+  mark_internal_bounds_stale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::show_all_switches
+//       Access: Published
+//  Description: Shows all levels in their default colors.
+////////////////////////////////////////////////////////////////////
+void LODNode::
+show_all_switches() {
+  CDWriter cdata(_cycler);
+  for (int i = 0; i < (int)cdata->_switch_vector.size(); ++i) {
+    do_show_switch(cdata, i, get_default_show_color(i));
+  }
+  mark_internal_bounds_stale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::hide_all_switches
+//       Access: Published
+//  Description: Hides all levels, restoring the LODNode to normal
+//               operation.
+////////////////////////////////////////////////////////////////////
+void LODNode::
+hide_all_switches() {
+  CDWriter cdata(_cycler);
+  for (int i = 0; i < (int)cdata->_switch_vector.size(); ++i) {
+    do_hide_switch(cdata, i);
+  }
+  mark_internal_bounds_stale();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: LODNode::compute_child
 //     Function: LODNode::compute_child
 //       Access: Protected
 //       Access: Protected
@@ -255,27 +289,42 @@ compute_child(CullTraverser *trav, CullTraverserData &data) {
   if (cdata->_got_force_switch) {
   if (cdata->_got_force_switch) {
     return cdata->_force_switch;
     return cdata->_force_switch;
   }
   }
-  
-  // Get the LOD center in camera space
-  //  CPT(TransformState) rel_transform =
-  //    trav->get_camera_transform()->invert_compose(data._net_transform);
 
 
-  //   CPT(TransformState) rel_transform = 
-  //     trav->get_scene()->get_cull_center().get_net_transform()->
-  //     invert_compose(data._net_transform);
+  // Get a pointer to the camera node.
+  Camera *camera = trav->get_scene()->get_camera_node();
+  
+  // Get the LOD center in camera space.  If the camera has a special
+  // LOD center defined, use that; otherwise, if it has a cull center,
+  // use that; otherwise, use the modelview transform (which is camera
+  // space).
+  CPT(TransformState) rel_transform;
+
+  NodePath lod_center = camera->get_lod_center();
+  if (!lod_center.is_empty()) {
+    rel_transform = 
+      lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
+  } else {
+    NodePath cull_center = camera->get_cull_center();
+    if (!cull_center.is_empty()) {
+      rel_transform = 
+        cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
+    } else {
+      rel_transform = data.get_modelview_transform(trav);
+    }
+  }
 
 
-  CPT(TransformState) rel_transform = data.get_modelview_transform(trav);
   LPoint3f center = cdata->_center * rel_transform->get_mat();
   LPoint3f center = cdata->_center * rel_transform->get_mat();
 
 
-  // Determine which child to traverse
-  float dist = fabs(dot(center, LVector3f::forward()));
+  // Now measure the distance to the LOD center, and use that to
+  // determine which child to display.
+  float dist2 = center.dot(center);
 
 
   for (int index = 0; index < (int)cdata->_switch_vector.size(); index++) {
   for (int index = 0; index < (int)cdata->_switch_vector.size(); index++) {
-    if (cdata->_switch_vector[index].in_range(dist)) { 
+    if (cdata->_switch_vector[index].in_range_2(dist2)) { 
       if (pgraph_cat.is_debug()) {
       if (pgraph_cat.is_debug()) {
         pgraph_cat.debug()
         pgraph_cat.debug()
-          << data._node_path << " at distance " << dist << ", selected child "
-          << index << "\n";
+          << data._node_path << " at distance " << sqrt(dist2)
+          << ", selected child " << index << "\n";
       }
       }
 
 
       return index;
       return index;
@@ -284,12 +333,194 @@ compute_child(CullTraverser *trav, CullTraverserData &data) {
 
 
   if (pgraph_cat.is_debug()) {
   if (pgraph_cat.is_debug()) {
     pgraph_cat.debug()
     pgraph_cat.debug()
-      << data._node_path << " at distance " << dist << ", no children in range.\n";
+      << data._node_path << " at distance " << sqrt(dist2)
+      << ", no children in range.\n";
   }
   }
 
 
   return -1;
   return -1;
 }
 }
 
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::show_switches_cull_callback
+//       Access: Protected
+//  Description: A special version of cull_callback() that is to be
+//               invoked when the LODNode is in show_switch() mode.
+//               This just draws the rings and the wireframe geometry
+//               for the selected switches.
+////////////////////////////////////////////////////////////////////
+bool LODNode::
+show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  CDReader cdata(_cycler);
+
+  // Get a pointer to the camera node.
+  Camera *camera = trav->get_scene()->get_camera_node();
+  
+  // Get the camera space transform.  This bit is the same as the code
+  // in compute_child(), above.
+  CPT(TransformState) rel_transform;
+
+  NodePath lod_center = camera->get_lod_center();
+  if (!lod_center.is_empty()) {
+    rel_transform = 
+      lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
+  } else {
+    NodePath cull_center = camera->get_cull_center();
+    if (!cull_center.is_empty()) {
+      rel_transform = 
+        cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
+    } else {
+      rel_transform = data.get_modelview_transform(trav);
+    }
+  }
+
+  LPoint3f center = cdata->_center * rel_transform->get_mat();
+  float dist2 = center.dot(center);
+
+  // Now orient the disk(s) in camera space such that their origin is
+  // at center, and the (0, 0, 0) point in camera space is on the disk.
+  LMatrix4f mat;
+  look_at(mat, -center, LVector3f(0.0f, 0.0f, 1.0f));
+  mat.set_row(3, center);
+  CPT(TransformState) viz_transform = TransformState::make_mat(mat);
+
+  viz_transform = rel_transform->invert_compose(viz_transform);
+  viz_transform = data.get_net_transform(trav)->compose(viz_transform);
+
+  SwitchVector::const_iterator si;
+  for (si = cdata->_switch_vector.begin(); 
+       si != cdata->_switch_vector.end(); 
+       ++si) {
+    const Switch &sw = (*si);
+    if (sw.is_shown()) {
+      PT(PandaNode) ring_viz = sw.get_ring_viz();
+      CullTraverserData next_data(data, ring_viz);
+      next_data._net_transform = viz_transform;
+      
+      trav->traverse(next_data);
+
+      if (sw.in_range_2(dist2)) {
+        // This switch level is in range.  Draw its children in the
+        // funny wireframe mode.
+        int index = (si - cdata->_switch_vector.begin());
+        if (index < get_num_children()) {
+          CullTraverserData next_data(data, get_child(index));
+          next_data._state = next_data._state->compose(sw.get_viz_model_state());
+          trav->traverse(next_data);
+        }
+      }
+    }
+  }
+
+  // Finally, draw the visible child(ren) in the appropriate state as
+  // well.
+
+  for (int index = 0; index < (int)cdata->_switch_vector.size(); index++) {
+    if (cdata->_switch_vector[index].in_range_2(dist2)) { 
+    }
+  }
+
+  // Now return false indicating that we have already taken care of
+  // the traversal from here.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::compute_internal_bounds
+//       Access: Protected, Virtual
+//  Description: Returns a newly-allocated BoundingVolume that
+//               represents the internal contents of the node.  Should
+//               be overridden by PandaNode classes that contain
+//               something internally.
+////////////////////////////////////////////////////////////////////
+PT(BoundingVolume) LODNode::
+compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
+  // First, get ourselves a fresh, empty bounding volume.
+  PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage, current_thread);
+  nassertr(bound != (BoundingVolume *)NULL, bound);
+
+  // If we have any visible rings, those count in the bounding volume.
+  if (is_any_shown()) {
+    // Now actually compute the bounding volume by putting it around all
+    // of our geoms' bounding volumes.
+    pvector<const BoundingVolume *> child_volumes;
+    pvector<PT(BoundingVolume) > pt_volumes;
+
+    CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+
+    SwitchVector::const_iterator si;
+    for (si = cdata->_switch_vector.begin();
+         si != cdata->_switch_vector.end();
+         ++si) {
+      const Switch &sw = (*si);
+      if (sw.is_shown()) {
+        PT(BoundingVolume) sphere = new BoundingSphere(cdata->_center, sw.get_in());
+        child_volumes.push_back(sphere);
+        pt_volumes.push_back(sphere);
+      }
+    }
+    
+    const BoundingVolume **child_begin = &child_volumes[0];
+    const BoundingVolume **child_end = child_begin + child_volumes.size();
+    
+    bound->around(child_begin, child_end);
+  }
+
+  return bound;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::do_show_switch
+//       Access: Private
+//  Description: The private implementation of show_switch().
+////////////////////////////////////////////////////////////////////
+void LODNode::
+do_show_switch(LODNode::CData *cdata, int index, const Colorf &color) {
+  nassertv(index >= 0 && index < (int)cdata->_switch_vector.size());
+
+  if (!cdata->_switch_vector[index].is_shown()) {
+    ++cdata->_num_shown;
+  }
+  cdata->_switch_vector[index].show(color);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::do_hide_switch
+//       Access: Private
+//  Description: The private implementation of hide_switch().
+////////////////////////////////////////////////////////////////////
+void LODNode::
+do_hide_switch(LODNode::CData *cdata, int index) {
+  nassertv(index >= 0 && index < (int)cdata->_switch_vector.size());
+
+  if (cdata->_switch_vector[index].is_shown()) {
+    --cdata->_num_shown;
+  }
+  cdata->_switch_vector[index].hide();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::get_default_show_color
+//       Access: Private, Static
+//  Description: Returns a default color appropriate for showing the
+//               indicated level.
+////////////////////////////////////////////////////////////////////
+const Colorf &LODNode::
+get_default_show_color(int index) {
+  static Colorf default_colors[] = {
+    Colorf(1.0f, 0.0f, 0.0f, 0.7f),
+    Colorf(0.0f, 1.0f, 0.0f, 0.7f),
+    Colorf(0.0f, 0.0f, 1.0f, 0.7f),
+    Colorf(0.0f, 1.0f, 1.0f, 0.7f),
+    Colorf(1.0f, 0.0f, 1.0f, 0.7f),
+    Colorf(1.0f, 1.0f, 0.0f, 0.7f),
+  };
+  static const int num_default_colors = sizeof(default_colors) / sizeof(Colorf);
+
+  return default_colors[index % num_default_colors];
+}
+
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: LODNode::register_with_read_factory
 //     Function: LODNode::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static
@@ -345,3 +576,214 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   PandaNode::fillin(scan, manager);
   PandaNode::fillin(scan, manager);
   manager->read_cdata(scan, _cycler);
   manager->read_cdata(scan, _cycler);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *LODNode::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::CData::check_limits
+//       Access: Public
+//  Description: Ensures that the _lowest and _highest members are set
+//               appropriately after a change to the set of switches.
+////////////////////////////////////////////////////////////////////
+void LODNode::CData::
+check_limits() {
+  _lowest = 0;
+  _highest = 0;
+  for (size_t i = 1; i < _switch_vector.size(); ++i) {
+    if (_switch_vector[i].get_out() > _switch_vector[_lowest].get_out()) {
+      _lowest = i;
+    }
+    if (_switch_vector[i].get_in() < _switch_vector[_highest].get_in()) {
+      _highest = i;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void LODNode::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  _center.write_datagram(dg);
+
+  dg.add_uint16(_switch_vector.size());
+
+  SwitchVector::const_iterator si;
+  for (si = _switch_vector.begin();
+       si != _switch_vector.end();
+       ++si) {
+    (*si).write_datagram(dg);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new LODNode.
+////////////////////////////////////////////////////////////////////
+void LODNode::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  _center.read_datagram(scan);
+
+  _switch_vector.clear();
+
+  int num_switches = scan.get_uint16();
+  _switch_vector.reserve(num_switches);
+  for (int i = 0; i < num_switches; i++) {
+    Switch sw(0, 0);
+    sw.read_datagram(scan);
+
+    _switch_vector.push_back(sw);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LODNode::Switch::compute_ring_viz
+//       Access: Private
+//  Description: Computes a Geom suitable for rendering the ring
+//               associated with this switch.
+////////////////////////////////////////////////////////////////////
+void LODNode::Switch::
+compute_ring_viz() {
+  static const int num_slices = 50;
+  static const int num_rings = 1;
+  static const float edge_ratio = 0.1;  // ratio of edge height to diameter.
+
+  const GeomVertexFormat *format = GeomVertexFormat::get_v3n3cp();
+  PT(GeomVertexData) vdata = new GeomVertexData("LOD", format, Geom::UH_static);
+
+  // Fill up the vertex table with all of the vertices.
+  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+  GeomVertexWriter normal(vdata, InternalName::get_normal());
+  GeomVertexWriter color(vdata, InternalName::get_color());
+
+  // First, the vertices for the flat ring.
+  int ri, si;
+  for (ri = 0; ri <= num_rings; ++ri) {
+    // r is in the range [0.0, 1.0].
+    float r = (float)ri / (float)num_rings;
+
+    // d is in the range [_out, _in].
+    float d = r * (_in - _out) + _out;
+
+    for (si = 0; si < num_slices; ++si) {
+      // s is in the range [0.0, 1.0).
+      float s = (float)si / (float)num_slices;
+
+      // t is in the range [0.0, 2pi).
+      float t = MathNumbers::pi_f * 2.0f * s;
+
+      float x = cosf(t);
+      float y = sinf(t);
+      vertex.add_data3f(x * d, y * d, 0.0f);
+      normal.add_data3f(0.0f, 0.0f, 1.0f);
+      color.add_data4f(_show_color);
+    }
+  }
+
+  // Next, the vertices for the inner and outer edges.
+  for (ri = 0; ri <= 1; ++ri) {
+    float r = (float)ri;
+    float d = r * (_in - _out) + _out;
+
+    // Invert the direction of Z on the outer ring, to invert the
+    // vertex winding order (and thereby invert the facing of the
+    // polygons).  We want the outer ring to face inward, and the
+    // inner ring to face outward.
+    float sign = 1.0f - (ri * 2);
+
+    for (si = 0; si < num_slices; ++si) {
+      float s = (float)si / (float)num_slices;
+      float t = MathNumbers::pi_f * 2.0f * s;
+      
+      float x = cosf(t);
+      float y = sinf(t);
+
+      vertex.add_data3f(x * d, y * d, 0.5f * edge_ratio * d * sign);
+      normal.add_data3f(x * sign, y * sign, 0.0f);
+      color.add_data4f(_show_color);
+    }
+
+    for (si = 0; si < num_slices; ++si) {
+      float s = (float)si / (float)num_slices;
+      float t = MathNumbers::pi_f * 2.0f * s;
+      
+      float x = cosf(t);
+      float y = sinf(t);
+
+      vertex.add_data3f(x * d, y * d, -0.5f * edge_ratio * d * sign);
+      normal.add_data3f(x * sign, y * sign, 0.0f);
+      color.add_data4f(_show_color);
+    }
+  }
+
+  // Now create the triangle strips.  One tristrip for each ring.
+  PT(GeomTristrips) strips = new GeomTristrips(Geom::UH_static);
+  for (ri = 0; ri < num_rings; ++ri) {
+    for (si = 0; si < num_slices; ++si) {
+      strips->add_vertex(ri * num_slices + si);
+      strips->add_vertex((ri + 1) * num_slices + si);
+    }
+    strips->add_vertex(ri * num_slices);
+    strips->add_vertex((ri + 1) * num_slices);
+    strips->close_primitive();
+  }
+
+  // And then one triangle strip for each of the inner and outer
+  // edges.
+  for (ri = 0; ri <= 1; ++ri) {
+    for (si = 0; si < num_slices; ++si) {
+      strips->add_vertex((num_rings + 1 + ri * 2) * num_slices + si);
+      strips->add_vertex((num_rings + 1 + ri * 2 + 1) * num_slices + si);
+    }
+    strips->add_vertex((num_rings + 1 + ri * 2) * num_slices);
+    strips->add_vertex((num_rings + 1 + ri * 2 + 1) * num_slices);
+    strips->close_primitive();
+  }
+
+  PT(Geom) ring_geom = new Geom(vdata);
+  ring_geom->add_primitive(strips);
+
+  PT(GeomNode) geom_node = new GeomNode("ring");
+  geom_node->add_geom(ring_geom);
+
+  // Get a material for two-sided lighting.
+  PT(Material) material = new Material();
+  material->set_twoside(true);
+  material = MaterialPool::get_material(material);
+
+  CPT(RenderState) viz_state = RenderState::make(CullFaceAttrib::make(CullFaceAttrib::M_cull_none),
+                                                 TextureAttrib::make_off(),
+                                                 MaterialAttrib::make(material),
+                                                 RenderState::get_max_priority());
+  if (_show_color[3] != 1.0f) {
+    viz_state = viz_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha),
+                                      RenderState::get_max_priority());
+  }
+
+  geom_node->set_state(viz_state);
+
+  _ring_viz = geom_node.p();
+
+  // Also compute a RenderState for rendering the children in
+  // wireframe mode.
+
+  _viz_model_state = RenderState::make(RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
+                                       TextureAttrib::make_off(),
+                                       ColorAttrib::make_flat(_show_color),
+                                       TransparencyAttrib::make(TransparencyAttrib::M_none),
+                                       RenderState::get_max_priority());
+}

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

@@ -22,6 +22,8 @@
 #include "pandabase.h"
 #include "pandabase.h"
 
 
 #include "pandaNode.h"
 #include "pandaNode.h"
+#include "luse.h"
+#include "pvector.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : LODNode
 //       Class : LODNode
@@ -70,9 +72,26 @@ PUBLISHED:
   INLINE void set_center(const LPoint3f &center);
   INLINE void set_center(const LPoint3f &center);
   INLINE const LPoint3f &get_center() const;
   INLINE const LPoint3f &get_center() const;
 
 
+  void show_switch(int index);
+  void show_switch(int index, const Colorf &color);
+  void hide_switch(int index);
+  void show_all_switches();
+  void hide_all_switches();
+  INLINE bool is_any_shown() const;
+
 protected:
 protected:
   int compute_child(CullTraverser *trav, CullTraverserData &data);
   int compute_child(CullTraverser *trav, CullTraverserData &data);
 
 
+  bool show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data);
+  virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const;
+
+private:
+  class CData;
+  void do_show_switch(CData *cdata, int index, const Colorf &color);
+  void do_hide_switch(CData *cdata, int index);
+
+  static const Colorf &get_default_show_color(int index);
+
 protected:
 protected:
   class Switch {
   class Switch {
   public:
   public:
@@ -82,18 +101,34 @@ protected:
 
 
     INLINE void set_range(float in, float out);
     INLINE void set_range(float in, float out);
     INLINE bool in_range(float dist) const;
     INLINE bool in_range(float dist) const;
-    
+    INLINE bool in_range_2(float dist2) const;
+
     INLINE void rescale(float factor);
     INLINE void rescale(float factor);
 
 
+    INLINE bool is_shown() const;
+    INLINE void show(const Colorf &color);
+    INLINE void hide();
+
+    INLINE PandaNode *get_ring_viz() const;
+    INLINE const RenderState *get_viz_model_state() const;
+
     INLINE void write_datagram(Datagram &destination) const;
     INLINE void write_datagram(Datagram &destination) const;
     INLINE void read_datagram(DatagramIterator &source);
     INLINE void read_datagram(DatagramIterator &source);
 
 
+  private:
+    void compute_ring_viz();
+
   private:
   private:
     float _in;
     float _in;
     float _out;
     float _out;
+    bool _shown;
+    Colorf _show_color;
+    PT(PandaNode) _ring_viz;
+    CPT(RenderState) _viz_model_state;
   };
   };
   typedef pvector<Switch> SwitchVector;
   typedef pvector<Switch> SwitchVector;
 
 
+private:
   class EXPCL_PANDA CData : public CycleData {
   class EXPCL_PANDA CData : public CycleData {
   public:
   public:
     INLINE CData();
     INLINE CData();
@@ -114,11 +149,14 @@ protected:
 
 
     bool _got_force_switch;
     bool _got_force_switch;
     int _force_switch;
     int _force_switch;
+    int _num_shown;
   };
   };
 
 
   PipelineCycler<CData> _cycler;
   PipelineCycler<CData> _cycler;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataWriter<CData> CDWriter;
+  typedef CycleDataStageReader<CData> CDStageReader;
+  typedef CycleDataStageWriter<CData> CDStageWriter;
 
 
 public:
 public:
   static void register_with_read_factory();
   static void register_with_read_factory();