Panda3D
cullTraverserData.cxx
1 // Filename: cullTraverserData.cxx
2 // Created by: drose (06Mar02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "cullTraverserData.h"
16 #include "cullTraverser.h"
17 #include "config_pgraph.h"
18 #include "pandaNode.h"
19 #include "colorAttrib.h"
20 #include "textureAttrib.h"
21 #include "renderModeAttrib.h"
22 #include "clipPlaneAttrib.h"
23 #include "boundingPlane.h"
24 #include "billboardEffect.h"
25 #include "compassEffect.h"
26 #include "occluderEffect.h"
27 #include "polylightEffect.h"
28 #include "renderState.h"
29 
30 
31 ////////////////////////////////////////////////////////////////////
32 // Function: CullTraverserData::apply_transform_and_state
33 // Access: Published
34 // Description: Applies the transform and state from the current
35 // node onto the current data. This also evaluates
36 // billboards, etc.
37 ////////////////////////////////////////////////////////////////////
40  CPT(RenderState) node_state = _node_reader.get_state();
41 
42  if (trav->has_tag_state_key() &&
43  _node_reader.has_tag(trav->get_tag_state_key())) {
44  // Here's a node that has been tagged with the special key for our
45  // current camera. This indicates some special state transition
46  // for this node, which is unique to this camera.
47  const Camera *camera = trav->get_scene()->get_camera_node();
48  string tag_state = _node_reader.get_tag(trav->get_tag_state_key());
49  node_state = node_state->compose(camera->get_tag_state(tag_state));
50  }
51  _node_reader.compose_draw_mask(_draw_mask);
52 
53  apply_transform_and_state(trav, _node_reader.get_transform(),
54  node_state, _node_reader.get_effects(),
55  _node_reader.get_off_clip_planes());
56 }
57 
58 ////////////////////////////////////////////////////////////////////
59 // Function: CullTraverserData::apply_transform_and_state
60 // Access: Published
61 // Description: Applies the indicated transform and state changes
62 // (e.g. as extracted from a node) onto the current
63 // data. This also evaluates billboards, etc.
64 ////////////////////////////////////////////////////////////////////
67  CPT(TransformState) node_transform,
68  CPT(RenderState) node_state,
69  CPT(RenderEffects) node_effects,
70  const RenderAttrib *off_clip_planes) {
71  if (node_effects->has_cull_callback()) {
72  node_effects->cull_callback(trav, *this, node_transform, node_state);
73  }
74 
75  if (!node_transform->is_identity()) {
76  _net_transform = _net_transform->compose(node_transform);
77 
78  if ((_view_frustum != (GeometricBoundingVolume *)NULL) ||
79  (!_cull_planes->is_empty())) {
80  // We need to move the viewing frustums into the node's
81  // coordinate space by applying the node's inverse transform.
82  if (node_transform->is_singular()) {
83  // But we can't invert a singular transform! Instead of
84  // trying, we'll just give up on frustum culling from this
85  // point down.
86  _view_frustum = (GeometricBoundingVolume *)NULL;
87  _cull_planes = CullPlanes::make_empty();
88 
89  } else {
90  CPT(TransformState) inv_transform =
91  node_transform->invert_compose(TransformState::make_identity());
92 
93  // Copy the bounding volumes for the frustums so we can
94  // transform them.
95  if (_view_frustum != (GeometricBoundingVolume *)NULL) {
96  _view_frustum = DCAST(GeometricBoundingVolume, _view_frustum->make_copy());
97  _view_frustum->xform(inv_transform->get_mat());
98  }
99 
100  _cull_planes = _cull_planes->xform(inv_transform->get_mat());
101  }
102  }
103  }
104 
105  _state = _state->compose(node_state);
106 
107  if (clip_plane_cull) {
108  _cull_planes = _cull_planes->apply_state(trav, this,
109  DCAST(ClipPlaneAttrib, node_state->get_attrib(ClipPlaneAttrib::get_class_slot())),
110  DCAST(ClipPlaneAttrib, off_clip_planes),
111  DCAST(OccluderEffect, node_effects->get_effect(OccluderEffect::get_class_type())));
112  }
113 }
114 
115 ////////////////////////////////////////////////////////////////////
116 // Function: CullTraverserData::is_in_view_impl
117 // Access: Private
118 // Description: The private implementation of is_in_view().
119 ////////////////////////////////////////////////////////////////////
120 bool CullTraverserData::
121 is_in_view_impl() {
122  const GeometricBoundingVolume *node_gbv = NULL;
123 
124  if (_view_frustum != (GeometricBoundingVolume *)NULL) {
125  DCAST_INTO_R(node_gbv, _node_reader.get_bounds(), false)
126 
127  int result = _view_frustum->contains(node_gbv);
128 
129  if (pgraph_cat.is_spam()) {
130  pgraph_cat.spam()
131  << _node_path << " cull result = " << hex << result << dec << "\n";
132  }
133 
134  if (result == BoundingVolume::IF_no_intersection) {
135  // No intersection at all. Cull.
136 #ifdef NDEBUG
137  return false;
138 #else
139  if (!fake_view_frustum_cull) {
140  return false;
141  }
142 
143  // If we have fake view-frustum culling enabled, instead of
144  // actually culling an object we simply force it to be drawn in
145  // red wireframe.
146  _view_frustum = (GeometricBoundingVolume *)NULL;
147  CPT(RenderState) fake_state = get_fake_view_frustum_cull_state();
148  _state = _state->compose(fake_state);
149 #endif
150 
151  } else if ((result & BoundingVolume::IF_all) != 0) {
152  // The node and its descendents are completely enclosed within
153  // the frustum. No need to cull further.
154  _view_frustum = (GeometricBoundingVolume *)NULL;
155 
156  } else {
157  // The node is partially, but not completely, within the viewing
158  // frustum.
159  if (_node_reader.is_final()) {
160  // Normally we'd keep testing child bounding volumes as we
161  // continue down. But this node has the "final" flag, so the
162  // user is claiming that there is some important reason we
163  // should consider everything visible at this point. So be it.
164  _view_frustum = (GeometricBoundingVolume *)NULL;
165  }
166  }
167  }
168 
169  if (!_cull_planes->is_empty()) {
170  if (node_gbv == (const GeometricBoundingVolume *)NULL) {
171  DCAST_INTO_R(node_gbv, _node_reader.get_bounds(), false)
172  }
173 
174  // Also cull against the current clip planes.
175  int result;
176  _cull_planes = _cull_planes->do_cull(result, _state, node_gbv);
177 
178  if (pgraph_cat.is_spam()) {
179  pgraph_cat.spam()
180  << _node_path << " cull planes cull result = " << hex
181  << result << dec << "\n";
182  _cull_planes->write(pgraph_cat.spam(false));
183  }
184 
185  if (_node_reader.is_final()) {
186  // Even though the node may be partially within the clip planes,
187  // do no more culling against them below this node.
188  _cull_planes = CullPlanes::make_empty();
189 
190  if (pgraph_cat.is_spam()) {
191  pgraph_cat.spam()
192  << _node_path << " is_final, cull planes disabled, state:\n";
193  _state->write(pgraph_cat.spam(false), 2);
194  }
195  }
196 
197  if (result == BoundingVolume::IF_no_intersection) {
198  // No intersection at all. Cull.
199 #ifdef NDEBUG
200  return false;
201 #else
202  if (!fake_view_frustum_cull) {
203  return false;
204  }
205  _cull_planes = CullPlanes::make_empty();
206  CPT(RenderState) fake_state = get_fake_view_frustum_cull_state();
207  _state = _state->compose(fake_state);
208 #endif
209 
210  } else if ((result & BoundingVolume::IF_all) != 0) {
211  // The node and its descendents are completely in front of all
212  // of the clip planes and occluders. The do_cull() call should
213  // therefore have removed all of the clip planes and occluders.
214  nassertr(_cull_planes->is_empty(), true);
215  }
216  }
217 
218  return true;
219 }
220 
221 ////////////////////////////////////////////////////////////////////
222 // Function: CullTraverserData::get_fake_view_frustum_cull_state
223 // Access: Private, Static
224 // Description: Returns a RenderState for rendering stuff in red
225 // wireframe, strictly for the fake_view_frustum_cull
226 // effect.
227 ////////////////////////////////////////////////////////////////////
228 CPT(RenderState) CullTraverserData::
229 get_fake_view_frustum_cull_state() {
230 #ifdef NDEBUG
231  return NULL;
232 #else
233  // Once someone asks for this pointer, we hold its reference count
234  // and never free it.
235  static CPT(RenderState) state = (const RenderState *)NULL;
236  if (state == (const RenderState *)NULL) {
237  state = RenderState::make
238  (ColorAttrib::make_flat(LColor(1.0f, 0.0f, 0.0f, 1.0f)),
239  TextureAttrib::make_all_off(),
240  RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
241  RenderState::get_max_priority());
242  }
243  return state;
244 #endif
245 }
bool has_tag_state_key() const
Returns true if a nonempty tag state key has been specified for the scene&#39;s camera, false otherwise.
Definition: cullTraverser.I:54
const RenderEffects * get_effects() const
Returns the complete RenderEffects that will be applied to this node.
Definition: pandaNode.I:1742
const TransformState * get_transform() const
Returns the transform that has been set on this particular node.
Definition: pandaNode.I:1755
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:60
const RenderState * get_state() const
Returns the complete RenderState that will be applied to all nodes at this level and below...
Definition: pandaNode.I:1731
void compose_draw_mask(DrawMask &running_draw_mask) const
Computes the result of applying this node&#39;s draw masks to a running draw mask, as during a traversal...
Definition: pandaNode.I:1526
This functions similarly to a LightAttrib.
void cull_callback(CullTraverser *trav, CullTraverserData &data, CPT(TransformState) &node_transform, CPT(RenderState) &node_state) const
Calls cull_callback() on all effects.
bool has_cull_callback() const
This function is provided as an optimization, to speed up the render-time checking for the existance ...
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
const string & get_tag_state_key() const
Returns the tag state key that has been specified for the scene&#39;s camera, if any. ...
Definition: cullTraverser.I:65
string get_tag(const string &key) const
Retrieves the user-defined value that was previously set on this node for the particular key...
Definition: pandaNode.I:1780
This functions similarly to a LightAttrib or ClipPlaneAttrib.
Camera * get_camera_node() const
Returns the camera used to render the scene.
Definition: sceneSetup.I:144
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
void apply_transform_and_state(CullTraverser *trav)
Applies the transform and state from the current node onto the current data.
const RenderEffect * get_effect(int n) const
Returns the nth effect in the state.
SceneSetup * get_scene() const
Returns the SceneSetup object.
Definition: cullTraverser.I:43
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition: camera.h:37
bool has_tag(const string &key) const
Returns true if a value has been defined on this node for the particular key (even if that value is t...
Definition: pandaNode.I:1797
This represents a unique collection of RenderEffect objects that correspond to a particular renderabl...
Definition: renderEffects.h:46
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48