From 3d415c4e0fa67711c573f3e4a8f9b6235a3103b7 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 8 Sep 2006 17:01:27 +0000 Subject: [PATCH] add show_switch() debugging mode --- panda/src/pgraph/camera.I | 28 ++ panda/src/pgraph/camera.h | 4 + panda/src/pgraph/fadeLodNode.cxx | 4 + panda/src/pgraph/lodNode.I | 350 ++++++++++++------ panda/src/pgraph/lodNode.cxx | 614 ++++++++++++++++++++++++++----- panda/src/pgraph/lodNode.h | 40 +- 6 files changed, 833 insertions(+), 207 deletions(-) diff --git a/panda/src/pgraph/camera.I b/panda/src/pgraph/camera.I index 11ffb96086..bb13e97607 100644 --- a/panda/src/pgraph/camera.I +++ b/panda/src/pgraph/camera.I @@ -152,6 +152,34 @@ get_cull_center() const { 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 // Access: Published diff --git a/panda/src/pgraph/camera.h b/panda/src/pgraph/camera.h index 0fdcfe07ee..5343d91745 100644 --- a/panda/src/pgraph/camera.h +++ b/panda/src/pgraph/camera.h @@ -67,6 +67,9 @@ PUBLISHED: INLINE void set_cull_center(const NodePath &cull_center); 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 CPT(RenderState) get_initial_state() const; @@ -91,6 +94,7 @@ private: bool _active; NodePath _scene; NodePath _cull_center; + NodePath _lod_center; DrawMask _camera_mask; diff --git a/panda/src/pgraph/fadeLodNode.cxx b/panda/src/pgraph/fadeLodNode.cxx index ae0151d727..4c4e4f2a6c 100755 --- a/panda/src/pgraph/fadeLodNode.cxx +++ b/panda/src/pgraph/fadeLodNode.cxx @@ -73,6 +73,10 @@ make_copy() const { //////////////////////////////////////////////////////////////////// bool FadeLODNode:: 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(); NodePath this_np = data._node_path.get_node_path(); FadeLODNodeData *ldata = diff --git a/panda/src/pgraph/lodNode.I b/panda/src/pgraph/lodNode.I index e535053e7d..0720e627d6 100644 --- a/panda/src/pgraph/lodNode.I +++ b/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 ©) : - _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 @@ -179,6 +59,10 @@ add_switch(float in, float out) { CDWriter cdata(_cycler); cdata->_switch_vector.push_back(Switch(in, out)); 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); cdata->_switch_vector[index].set_range(in, out); cdata->check_limits(); + + if (cdata->_num_shown != 0) { + mark_internal_bounds_stale(); + } + return true; } @@ -209,6 +98,10 @@ clear_switches() { cdata->_switch_vector.clear(); cdata->_lowest = 0; cdata->_highest = 0; + + if (cdata->_num_shown != 0) { + mark_internal_bounds_stale(); + } } //////////////////////////////////////////////////////////////////// @@ -319,6 +212,10 @@ INLINE void LODNode:: set_center(const LPoint3f ¢er) { CDWriter cdata(_cycler); cdata->_center = center; + + if (cdata->_num_shown != 0) { + mark_internal_bounds_stale(); + } } //////////////////////////////////////////////////////////////////// @@ -333,3 +230,216 @@ get_center() const { CDReader cdata(_cycler); 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 ©) : + _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(); +} + diff --git a/panda/src/pgraph/lodNode.cxx b/panda/src/pgraph/lodNode.cxx index c833063d89..eaa5049702 100644 --- a/panda/src/pgraph/lodNode.cxx +++ b/panda/src/pgraph/lodNode.cxx @@ -20,82 +20,25 @@ #include "cullTraverserData.h" #include "cullTraverser.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; -//////////////////////////////////////////////////////////////////// -// 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 // Access: Public, Virtual @@ -185,6 +128,10 @@ has_cull_callback() const { //////////////////////////////////////////////////////////////////// bool LODNode:: cull_callback(CullTraverser *trav, CullTraverserData &data) { + if (is_any_shown()) { + return show_switches_cull_callback(trav, data); + } + int index = compute_child(trav, data); if (index >= 0 && index < get_num_children()) { CullTraverserData next_data(data, get_child(index)); @@ -235,6 +182,93 @@ is_lod_node() const { 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 // Access: Protected @@ -255,27 +289,42 @@ compute_child(CullTraverser *trav, CullTraverserData &data) { if (cdata->_got_force_switch) { return cdata->_force_switch; } + + // Get a pointer to the camera node. + Camera *camera = trav->get_scene()->get_camera_node(); - // Get the LOD center in camera space - // CPT(TransformState) rel_transform = - // trav->get_camera_transform()->invert_compose(data._net_transform); + // 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; - // CPT(TransformState) rel_transform = - // trav->get_scene()->get_cull_center().get_net_transform()-> - // invert_compose(data._net_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(); - // 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++) { - if (cdata->_switch_vector[index].in_range(dist)) { + if (cdata->_switch_vector[index].in_range_2(dist2)) { if (pgraph_cat.is_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; @@ -284,12 +333,194 @@ compute_child(CullTraverser *trav, CullTraverserData &data) { if (pgraph_cat.is_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; } + +//////////////////////////////////////////////////////////////////// +// 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 child_volumes; + pvector 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 // Access: Public, Static @@ -345,3 +576,214 @@ fillin(DatagramIterator &scan, BamReader *manager) { PandaNode::fillin(scan, manager); 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()); +} diff --git a/panda/src/pgraph/lodNode.h b/panda/src/pgraph/lodNode.h index e4fa1917b8..c91a121fc4 100644 --- a/panda/src/pgraph/lodNode.h +++ b/panda/src/pgraph/lodNode.h @@ -22,6 +22,8 @@ #include "pandabase.h" #include "pandaNode.h" +#include "luse.h" +#include "pvector.h" //////////////////////////////////////////////////////////////////// // Class : LODNode @@ -70,9 +72,26 @@ PUBLISHED: INLINE void set_center(const LPoint3f ¢er); 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: 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: class Switch { public: @@ -82,18 +101,34 @@ protected: INLINE void set_range(float in, float out); INLINE bool in_range(float dist) const; - + INLINE bool in_range_2(float dist2) const; + 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 read_datagram(DatagramIterator &source); + private: + void compute_ring_viz(); + private: float _in; float _out; + bool _shown; + Colorf _show_color; + PT(PandaNode) _ring_viz; + CPT(RenderState) _viz_model_state; }; typedef pvector SwitchVector; +private: class EXPCL_PANDA CData : public CycleData { public: INLINE CData(); @@ -114,11 +149,14 @@ protected: bool _got_force_switch; int _force_switch; + int _num_shown; }; PipelineCycler _cycler; typedef CycleDataReader CDReader; typedef CycleDataWriter CDWriter; + typedef CycleDataStageReader CDStageReader; + typedef CycleDataStageWriter CDStageWriter; public: static void register_with_read_factory();