Panda3D
cullTraverserData.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file cullTraverserData.cxx
10  * @author drose
11  * @date 2002-03-06
12  */
13 
14 #include "cullTraverserData.h"
15 #include "cullTraverser.h"
16 #include "config_pgraph.h"
17 #include "pandaNode.h"
18 #include "colorAttrib.h"
19 #include "textureAttrib.h"
20 #include "renderModeAttrib.h"
21 #include "clipPlaneAttrib.h"
22 #include "boundingPlane.h"
23 #include "billboardEffect.h"
24 #include "compassEffect.h"
25 #include "occluderEffect.h"
26 #include "polylightEffect.h"
27 #include "renderState.h"
28 
29 
30 /**
31  * Applies the transform and state from the current node onto the current
32  * data. This also evaluates billboards, etc.
33  */
36  CPT(RenderState) node_state = _node_reader.get_state();
37 
38  if (trav->has_tag_state_key() &&
39  _node_reader.has_tag(trav->get_tag_state_key())) {
40  // Here's a node that has been tagged with the special key for our current
41  // camera. This indicates some special state transition for this node,
42  // which is unique to this camera.
43  const Camera *camera = trav->get_scene()->get_camera_node();
44  std::string tag_state = _node_reader.get_tag(trav->get_tag_state_key());
45  node_state = node_state->compose(camera->get_tag_state(tag_state));
46  }
47  _node_reader.compose_draw_mask(_draw_mask);
48 
49  const RenderEffects *node_effects = _node_reader.get_effects();
50  if (!node_effects->has_cull_callback()) {
51  apply_transform(_node_reader.get_transform());
52  } else {
53  // The cull callback may decide to modify the node_transform.
54  CPT(TransformState) node_transform = _node_reader.get_transform();
55  node_effects->cull_callback(trav, *this, node_transform, node_state);
56  apply_transform(node_transform);
57 
58  // The cull callback may have changed the node properties.
59  _node_reader.check_cached(false);
60  }
61 
62  if (!node_state->is_empty()) {
63  _state = _state->compose(node_state);
64  }
65 
66  if (clip_plane_cull) {
67  _cull_planes = _cull_planes->apply_state(trav, this,
68  (const ClipPlaneAttrib *)node_state->get_attrib(ClipPlaneAttrib::get_class_slot()),
69  (const ClipPlaneAttrib *)_node_reader.get_off_clip_planes(),
70  (const OccluderEffect *)node_effects->get_effect(OccluderEffect::get_class_type()));
71  }
72 }
73 
74 /**
75  * Applies the indicated transform changes onto the current data.
76  */
78 apply_transform(const TransformState *node_transform) {
79  if (!node_transform->is_identity()) {
80  _net_transform = _net_transform->compose(node_transform);
81 
82  if ((_view_frustum != nullptr) ||
83  (!_cull_planes->is_empty())) {
84  // We need to move the viewing frustums into the node's coordinate space
85  // by applying the node's inverse transform.
86  if (node_transform->is_singular()) {
87  // But we can't invert a singular transform! Instead of trying, we'll
88  // just give up on frustum culling from this point down.
89  _view_frustum = nullptr;
90  _cull_planes = CullPlanes::make_empty();
91 
92  } else {
93  CPT(TransformState) inv_transform =
94  node_transform->invert_compose(TransformState::make_identity());
95 
96  // Copy the bounding volumes for the frustums so we can transform
97  // them.
98  if (_view_frustum != nullptr) {
99  _view_frustum = _view_frustum->make_copy()->as_geometric_bounding_volume();
100  nassertv(_view_frustum != nullptr);
101 
102  _view_frustum->xform(inv_transform->get_mat());
103  }
104 
105  _cull_planes = _cull_planes->xform(inv_transform->get_mat());
106  }
107  }
108  }
109 }
110 
111 /**
112  * The private, recursive implementation of get_node_path(), this returns the
113  * NodePathComponent representing the NodePath.
114  */
115 PT(NodePathComponent) CullTraverserData::
116 r_get_node_path() const {
117  if (_next == nullptr) {
118  nassertr(_start != nullptr, nullptr);
119  return _start;
120  }
121 
122 #ifdef _DEBUG
123  nassertr(_start == nullptr, nullptr);
124 #endif
125  nassertr(node() != nullptr, nullptr);
126 
127  PT(NodePathComponent) comp = _next->r_get_node_path();
128  nassertr(comp != nullptr, nullptr);
129 
130  Thread *current_thread = Thread::get_current_thread();
131  int pipeline_stage = current_thread->get_pipeline_stage();
132  PT(NodePathComponent) result =
133  PandaNode::get_component(comp, node(), pipeline_stage, current_thread);
134  if (result == nullptr) {
135  // This means we found a disconnected chain in the CullTraverserData's
136  // ancestry: the node above this node isn't connected. In this case,
137  // don't attempt to go higher; just truncate the NodePath at the bottom of
138  // the disconnect.
139  return PandaNode::get_top_component(node(), true, pipeline_stage, current_thread);
140  }
141 
142  return result;
143 }
144 
145 /**
146  * The private implementation of is_in_view().
147  */
148 bool CullTraverserData::
149 is_in_view_impl() {
150  const GeometricBoundingVolume *node_gbv = nullptr;
151 
152  if (_view_frustum != nullptr) {
153  node_gbv = _node_reader.get_bounds()->as_geometric_bounding_volume();
154  nassertr(node_gbv != nullptr, false);
155 
156  int result = _view_frustum->contains(node_gbv);
157 
158  if (pgraph_cat.is_spam()) {
159  pgraph_cat.spam()
160  << get_node_path() << " cull result = " << std::hex << result << std::dec << "\n";
161  }
162 
163  if (result == BoundingVolume::IF_no_intersection) {
164  // No intersection at all. Cull.
165 #ifdef NDEBUG
166  return false;
167 #else
168  if (!fake_view_frustum_cull) {
169  return false;
170  }
171 
172  // If we have fake view-frustum culling enabled, instead of actually
173  // culling an object we simply force it to be drawn in red wireframe.
174  _view_frustum = nullptr;
175  _state = _state->compose(get_fake_view_frustum_cull_state());
176 #endif
177 
178  } else if ((result & BoundingVolume::IF_all) != 0) {
179  // The node and its descendents are completely enclosed within the
180  // frustum. No need to cull further.
181  _view_frustum = nullptr;
182 
183  } else {
184  // The node is partially, but not completely, within the viewing
185  // frustum.
186  if (_node_reader.is_final()) {
187  // Normally we'd keep testing child bounding volumes as we continue
188  // down. But this node has the "final" flag, so the user is claiming
189  // that there is some important reason we should consider everything
190  // visible at this point. So be it.
191  _view_frustum = nullptr;
192  }
193  }
194  }
195 
196  if (!_cull_planes->is_empty()) {
197  if (node_gbv == nullptr) {
198  node_gbv = _node_reader.get_bounds()->as_geometric_bounding_volume();
199  nassertr(node_gbv != nullptr, false);
200  }
201 
202  // Also cull against the current clip planes.
203  int result;
204  _cull_planes = _cull_planes->do_cull(result, _state, node_gbv);
205 
206  if (pgraph_cat.is_spam()) {
207  pgraph_cat.spam()
208  << get_node_path() << " cull planes cull result = " << std::hex
209  << result << std::dec << "\n";
210  _cull_planes->write(pgraph_cat.spam(false));
211  }
212 
213  if (_node_reader.is_final()) {
214  // Even though the node may be partially within the clip planes, do no
215  // more culling against them below this node.
216  _cull_planes = CullPlanes::make_empty();
217 
218  if (pgraph_cat.is_spam()) {
219  pgraph_cat.spam()
220  << get_node_path() << " is_final, cull planes disabled, state:\n";
221  _state->write(pgraph_cat.spam(false), 2);
222  }
223  }
224 
225  if (result == BoundingVolume::IF_no_intersection) {
226  // No intersection at all. Cull.
227 #ifdef NDEBUG
228  return false;
229 #else
230  if (!fake_view_frustum_cull) {
231  return false;
232  }
233  _cull_planes = CullPlanes::make_empty();
234  _state = _state->compose(get_fake_view_frustum_cull_state());
235 #endif
236 
237  } else if ((result & BoundingVolume::IF_all) != 0) {
238  // The node and its descendents are completely in front of all of the
239  // clip planes and occluders. The do_cull() call should therefore have
240  // removed all of the clip planes and occluders.
241  nassertr(_cull_planes->is_empty(), true);
242  }
243  }
244 
245  return true;
246 }
247 
248 /**
249  * Returns a RenderState for rendering stuff in red wireframe, strictly for
250  * the fake_view_frustum_cull effect.
251  */
252 const RenderState *CullTraverserData::
253 get_fake_view_frustum_cull_state() {
254 #ifdef NDEBUG
255  return nullptr;
256 #else
257  // Once someone asks for this pointer, we hold its reference count and never
258  // free it.
259  static CPT(RenderState) state;
260  if (state == nullptr) {
261  state = RenderState::make
262  (ColorAttrib::make_flat(LColor(1.0f, 0.0f, 0.0f, 1.0f)),
263  TextureAttrib::make_all_off(),
264  RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
265  RenderState::get_max_priority());
266  }
267  return state;
268 #endif
269 }
bool is_final() const
Returns the current state of the "final" flag.
Definition: pandaNode.I:1531
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_tag_state_key() const
Returns true if a nonempty tag state key has been specified for the scene's camera,...
Definition: cullTraverser.I:44
Indicates a coordinate-system transform on vertices.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const RenderEffects * get_effects() const
Returns the complete RenderEffects that will be applied to this node.
Definition: pandaNode.I:1431
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const TransformState * get_transform() const
Returns the transform that has been set on this particular node.
Definition: pandaNode.I:1441
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const RenderState * get_state() const
Returns the complete RenderState that will be applied to all nodes at this level and below,...
Definition: pandaNode.I:1423
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void compose_draw_mask(DrawMask &running_draw_mask) const
Computes the result of applying this node's draw masks to a running draw mask, as during a traversal.
Definition: pandaNode.I:1256
get_node_path
Constructs and returns an actual NodePath that represents the same path we have just traversed.
bool has_tag(const std::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:1475
This functions similarly to a LightAttrib.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_pipeline_stage
Returns the Pipeline stage number associated with this thread.
Definition: thread.h:105
void apply_transform(const TransformState *node_transform)
Applies the indicated transform changes onto the current data.
PandaNode * node() const
Returns the node traversed to so far.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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 ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
void check_cached(bool update_bounds) const
Ensures that the draw masks etc.
Definition: pandaNode.cxx:4106
const std::string & get_tag_state_key() const
Returns the tag state key that has been specified for the scene's camera, if any.
Definition: cullTraverser.I:53
const RenderAttrib * get_off_clip_planes() const
Returns a ClipPlaneAttrib which represents the union of all of the clip planes that have been turned ...
Definition: pandaNode.I:1494
This functions similarly to a LightAttrib or ClipPlaneAttrib.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const RenderEffect * get_effect(size_t n) const
Returns the nth effect in the state.
Camera * get_camera_node() const
Returns the camera used to render the scene.
Definition: sceneSetup.I:115
std::string get_tag(const std::string &key) const
Retrieves the user-defined value that was previously set on this node for the particular key,...
Definition: pandaNode.I:1460
bool is_singular() const
Returns true if the transform represents a singular transform (that is, it has a zero scale,...
PT(NodePathComponent) CullTraverserData
The private, recursive implementation of get_node_path(), this returns the NodePathComponent represen...
const BoundingVolume * get_bounds() const
Returns the external bounding volume of this node: a bounding volume that contains the user bounding ...
Definition: pandaNode.I:1505
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void apply_transform_and_state(CullTraverser *trav)
Applies the transform and state from the current node onto the current data.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A thread; that is, a lightweight process.
Definition: thread.h:46
SceneSetup * get_scene() const
Returns the SceneSetup object.
Definition: cullTraverser.I:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition: camera.h:35
virtual GeometricBoundingVolume * as_geometric_bounding_volume()
Virtual downcast method.
This represents a unique collection of RenderEffect objects that correspond to a particular renderabl...
Definition: renderEffects.h:41
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is one component of a NodePath.
bool is_identity() const
Returns true if the transform represents the identity matrix, false otherwise.