Panda3D
pipeOcclusionCullTraverser.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 pipeOcclusionCullTraverser.cxx
10  * @author drose
11  * @date 2007-05-29
12  */
13 
15 #include "graphicsEngine.h"
16 #include "graphicsPipe.h"
17 #include "drawCullHandler.h"
18 #include "boundingSphere.h"
19 #include "boundingBox.h"
20 #include "geomVertexWriter.h"
21 #include "geomTristrips.h"
22 #include "geomTriangles.h"
23 #include "pStatTimer.h"
24 #include "depthWriteAttrib.h"
25 #include "depthTestAttrib.h"
26 #include "colorWriteAttrib.h"
27 #include "colorAttrib.h"
28 #include "cullBinManager.h"
29 #include "configVariableInt.h"
30 #include "config_grutil.h"
31 #include "pnmImage.h"
32 
33 PStatCollector PipeOcclusionCullTraverser::_setup_occlusion_pcollector("Cull:Occlusion:Setup");
34 PStatCollector PipeOcclusionCullTraverser::_draw_occlusion_pcollector("Cull:Occlusion:Occluders");
35 PStatCollector PipeOcclusionCullTraverser::_test_occlusion_pcollector("Cull:Occlusion:Test");
36 PStatCollector PipeOcclusionCullTraverser::_finish_occlusion_pcollector("Cull:Occlusion:Finish");
37 
38 PStatCollector PipeOcclusionCullTraverser::_occlusion_untested_pcollector("Occlusion results:Not tested");
39 PStatCollector PipeOcclusionCullTraverser::_occlusion_passed_pcollector("Occlusion results:Visible");
40 PStatCollector PipeOcclusionCullTraverser::_occlusion_failed_pcollector("Occlusion results:Occluded");
41 PStatCollector PipeOcclusionCullTraverser::_occlusion_tests_pcollector("Occlusion tests");
42 
43 TypeHandle PipeOcclusionCullTraverser::_type_handle;
44 
45 static ConfigVariableInt min_occlusion_vertices
46 ("min-occlusion-vertices", 300,
47  PRC_DESC("The minimum number of vertices a PandaNode or Geom must contain "
48  "in order to perform an occlusion query for it. Nodes and Geoms "
49  "smaller than this will be rendered directly, without bothering "
50  "with an occlusion query."));
51 
52 static ConfigVariableInt max_occlusion_vertices
53 ("max-occlusion-vertices", 3000,
54  PRC_DESC("The maximum number of vertices that may be included in a PandaNode "
55  "and its descendents in order to perform an occlusion query for "
56  "it. Subgraphs whose total vertex count exceeds this number will "
57  "be subdivided further before performing an occlusion test--the "
58  "hope is that we can eventually get to a finer-grained answer. "
59  "GeomNodes and Geoms will not be subdivided, regardless of this "
60  "limit."));
61 
62 static ConfigVariableBool show_occlusion
63 ("show-occlusion", false,
64  PRC_DESC("Set this true to visualize the efforts of the occlusion test."));
65 
66 static ConfigVariableInt occlusion_size
67 ("occlusion-size", "256 256",
68  PRC_DESC("Specify the x y size of the buffer used for occlusion testing."));
69 
70 static ConfigVariableInt occlusion_depth_bits
71 ("occlusion-depth-bits", 1,
72  PRC_DESC("The minimum number of depth bits requested for the occlusion "
73  "buffer."));
74 
75 /**
76  *
77  */
78 PipeOcclusionCullTraverser::
79 PipeOcclusionCullTraverser(GraphicsOutput *host) {
80  _live = false;
81  GraphicsStateGuardian *gsg = host->get_gsg();
82 
83  GraphicsThreadingModel threading_model = gsg->get_threading_model();
84  nassertv(threading_model.get_cull_name() == threading_model.get_draw_name());
85  if (!gsg->get_supports_occlusion_query()) {
86  grutil_cat.info()
87  << "Occlusion queries are not supported by graphics pipe.\n";
88  return;
89  }
90 
91  GraphicsEngine *engine = gsg->get_engine();
92  GraphicsPipe *pipe = gsg->get_pipe();
93 
94  FrameBufferProperties fb_prop;
95  fb_prop.set_depth_bits(occlusion_depth_bits);
96  WindowProperties win_prop;
97  if (occlusion_size.get_num_words() < 2) {
98  win_prop.set_size(occlusion_size, occlusion_size);
99  } else {
100  win_prop.set_size(occlusion_size[0], occlusion_size[1]);
101  }
102 
103  _buffer = engine->make_output(pipe, "occlusion", 0, fb_prop, win_prop,
104  GraphicsPipe::BF_refuse_window,
105  gsg, host->get_host());
106  nassertv(_buffer != nullptr);
107 
108  // This buffer isn't really active--we render it by hand; we don't want the
109  // GraphicsEngine to render it.
110  _buffer->set_active(0);
111 
112  _display_region = _buffer->make_display_region();
113  _internal_cull_handler = nullptr;
114 
115  make_sphere();
116  make_box();
117  make_solid_test_state();
118 
119  _live = true;
120 }
121 
122 /**
123  *
124  */
127  bool dr_incomplete_render) {
128  CullTraverser::set_scene(scene_setup, gsgbase, dr_incomplete_render);
129  if (!_live) {
130  return;
131  }
132 
133  PStatTimer timer(_setup_occlusion_pcollector);
134 
135  GraphicsStateGuardian *gsg = _buffer->get_gsg();
136  nassertv(gsg == gsgbase);
137 
138  Thread *current_thread = get_current_thread();
139  if (!_buffer->begin_frame(GraphicsOutput::FM_render, current_thread)) {
140  grutil_cat.error() << "begin_frame failed\n";
141  return;
142  }
143  _buffer->clear(current_thread);
144 
145  DisplayRegionPipelineReader dr_reader(_display_region, current_thread);
146 
147  _buffer->change_scenes(&dr_reader);
148  gsg->prepare_display_region(&dr_reader);
149 
150  _scene = new SceneSetup(*scene_setup);
151  _scene->set_display_region(_display_region);
152  _scene->set_viewport_size(_display_region->get_pixel_width(),
153  _display_region->get_pixel_height());
154 
155  if (_scene->get_cull_center() != _scene->get_camera_path()) {
156  // This camera has a special cull center set. For the purposes of
157  // occlusion culling, we want to render the scene from the cull center,
158  // not from the camera root.
159  NodePath cull_center = _scene->get_cull_center();
160  NodePath scene_parent = _scene->get_scene_root().get_parent(current_thread);
161  CPT(TransformState) camera_transform = cull_center.get_transform(scene_parent, current_thread);
162  CPT(TransformState) world_transform = scene_parent.get_transform(cull_center, current_thread);
163  CPT(TransformState) cs_world_transform = _scene->get_cs_transform()->compose(world_transform);
164  _scene->set_camera_transform(camera_transform);
165  _scene->set_world_transform(world_transform);
166  _scene->set_cs_world_transform(cs_world_transform);
167 
168  // We use this to recover the original net_transform.
169  _inv_cs_world_transform = cs_world_transform->get_inverse();
170  } else {
171  _inv_cs_world_transform = _scene->get_cs_world_transform()->get_inverse();
172  }
173 
174  nassertv(_scene->get_cs_transform() == scene_setup->get_cs_transform());
175 
176  gsg->set_scene(_scene);
177  if (!gsg->begin_scene()) {
178  grutil_cat.error() << "begin_scene failed\n";
179  return;
180  }
181 
182  // Hijack the default cull handler so we can perform all of the occlusion
183  // tests on a per-object basis, and then query the results at the end of the
184  // traversal.
185  _true_cull_handler = get_cull_handler();
186  set_cull_handler(this);
187 
188  _internal_cull_handler = new DrawCullHandler(gsg);
189  _internal_trav = new CullTraverser;
190  _internal_trav->set_cull_handler(_internal_cull_handler);
191  _internal_trav->set_scene(_scene, gsg, dr_incomplete_render);
192  _internal_trav->set_view_frustum(get_view_frustum());
193  _internal_trav->set_camera_mask(_occlusion_mask);
194 
195  _current_query = nullptr;
196  _next_query = nullptr;
197 
198  // Begin by rendering all the occluders into our internal scene.
199  PStatTimer timer2(_draw_occlusion_pcollector);
200  _internal_trav->traverse(_scene->get_scene_root());
201 }
202 
203 /**
204  * Should be called when the traverser has finished traversing its scene, this
205  * gives it a chance to do any necessary finalization.
206  */
209  if (!_live) {
210  return;
211  }
212 
213  PStatTimer timer(_finish_occlusion_pcollector);
214  GraphicsStateGuardian *gsg = _buffer->get_gsg();
215  Thread *current_thread = get_current_thread();
216 
217  _current_query = nullptr;
218  _next_query = nullptr;
219 
220  PendingObjects::iterator oi;
221  for (oi = _pending_objects.begin(); oi != _pending_objects.end(); ++oi) {
222  PendingObject &pobj = (*oi);
223  if (pobj._query == nullptr) {
224  _occlusion_untested_pcollector.add_level(1);
225  _true_cull_handler->record_object(pobj._object, this);
226  } else {
227  int num_fragments = pobj._query->get_num_fragments();
228  if (num_fragments != 0) {
229  _occlusion_passed_pcollector.add_level(1);
230  _true_cull_handler->record_object(pobj._object, this);
231  } else {
232  _occlusion_failed_pcollector.add_level(1);
233  delete pobj._object;
234  }
235  }
236 
237  // The CullableObject has by now either been recorded (which will
238  // eventually delete it) or deleted directly.
239 #ifndef NDEBUG
240  pobj._object = nullptr;
241 #endif // NDEBUG
242  }
243  _pending_objects.clear();
245 
246  gsg->end_scene();
247  _buffer->end_frame(GraphicsOutput::FM_render, current_thread);
248 
249  _buffer->begin_flip();
250  _buffer->end_flip();
251 
252  delete _internal_cull_handler;
253  _internal_cull_handler = nullptr;
254 
255  _occlusion_untested_pcollector.flush_level();
256  _occlusion_passed_pcollector.flush_level();
257  _occlusion_failed_pcollector.flush_level();
258  _occlusion_tests_pcollector.flush_level();
259 }
260 
261 /**
262  * Returns a Texture that can be used to visualize the efforts of the
263  * occlusion cull.
264  */
267  if (_texture != nullptr) {
268  return _texture;
269  }
270 
271  _texture = new Texture("occlusion");
272 
273  if (!_live) {
274  // If we're not live, just create a default, black texture.
275  PNMImage image(1, 1);
276  _texture->load(image);
277 
278  } else {
279  // Otherwise, create a render-to-texture.
280  _buffer->add_render_texture(_texture, GraphicsOutput::RTM_bind_or_copy);
281  }
282 
283  return _texture;
284 }
285 
286 /**
287  *
288  */
289 bool PipeOcclusionCullTraverser::
290 is_in_view(CullTraverserData &data) {
291  _next_query = nullptr;
292 
293  if (!CullTraverser::is_in_view(data)) {
294  return false;
295  }
296  if (!_live) {
297  return true;
298  }
299 
300  if (_current_query != nullptr) {
301  // We've already performed an occlusion test for some ancestor of this
302  // node; no need to perform another.
303  return true;
304  }
305 
306  PandaNode *node = data.node();
307  PandaNodePipelineReader *node_reader = data.node_reader();
308 
309  if (node_reader->get_nested_vertices() < min_occlusion_vertices) {
310  // Never mind; let this puny one slide.
311  return true;
312  }
313 
314  if (node->is_geom_node() ||
315  node_reader->is_final() ||
316  node_reader->get_effects()->has_show_bounds() ||
317  node_reader->get_nested_vertices() <= max_occlusion_vertices) {
318  // In any of these cases, there's sufficient reason to perform an
319  // occlusion test on this particular node. Do it.
320 
321  CPT(BoundingVolume) vol = node_reader->get_bounds();
322  CPT(TransformState) net_transform = data.get_net_transform(this);
323  CPT(TransformState) internal_transform;
324 
325  CPT(Geom) geom;
326  if (get_volume_viz(vol, geom, net_transform, internal_transform)) {
327  _next_query =
328  perform_occlusion_test(geom, net_transform, internal_transform);
329  }
330  }
331 
332  return true;
333 }
334 
335 /**
336  * Traverses all the children of the indicated node, with the given data,
337  * which has been converted into the node's space.
338  */
339 void PipeOcclusionCullTraverser::
340 traverse_below(CullTraverserData &data) {
341  // Save and restore _current_query, and clear _next_query, for traversing
342  // the children of this node.
343  PT(OcclusionQueryContext) prev_query = _current_query;
344  if (_next_query != nullptr) {
345  _current_query = _next_query;
346  }
347  _next_query = nullptr;
348 
350 
351  _current_query = prev_query;
352  _next_query = nullptr;
353 }
354 
355 /**
356  * This callback function is intended to be overridden by a derived class.
357  * This is called as each Geom is discovered by the CullTraverser.
358  *
359  * We do a sneaky trick in making PipeOcclusionCullTraverser inherit from both
360  * CullTraverser and CullHandler--the traverser is its own handler! This is
361  * the normal callback into the traverser for rendering objects. We respond
362  * to this by firing off an occlusion test, and queuing up the object until
363  * the end of the scene.
364  */
365 void PipeOcclusionCullTraverser::
366 record_object(CullableObject *object, const CullTraverser *traverser) {
367  nassertv(traverser == this);
368  PendingObject pobj(object);
369 
370  Thread *current_thread = get_current_thread();
371 
372  if (_next_query != nullptr) {
373  // We have just performed an occlusion query for this node. Don't perform
374  // another one.
375  pobj._query = _next_query;
376 
377  } else if (_current_query != nullptr) {
378  // We have previously performed an occlusion query for this node or some
379  // ancestor. Don't perform another one.
380  pobj._query = _current_query;
381 
382  } else if (object->_geom->get_nested_vertices(current_thread) < min_occlusion_vertices) {
383  // This object is too small to bother testing for occlusions.
384 
385  } else {
386  // Issue an occlusion test for this object.
387  CPT(BoundingVolume) vol = object->_geom->get_bounds(current_thread);
388  CPT(TransformState) net_transform = _inv_cs_world_transform->compose(object->_internal_transform);
389  CPT(TransformState) internal_transform;
390  CPT(Geom) geom;
391  if (get_volume_viz(vol, geom, net_transform, internal_transform)) {
392  pobj._query =
393  perform_occlusion_test(geom, net_transform, internal_transform);
394  }
395  }
396 
397  _pending_objects.push_back(pobj);
398 }
399 
400 /**
401  * Constructs a unit sphere for testing visibility of bounding spheres.
402  */
403 void PipeOcclusionCullTraverser::
404 make_sphere() {
405  ConfigVariableInt num_slices("num-slices", 16);
406  ConfigVariableInt num_stacks("num-stacks", 8);
407 
408  // static const int num_slices = 16; static const int num_stacks = 8;
409 
410  PT(GeomVertexData) vdata = new GeomVertexData
411  ("occlusion_sphere", GeomVertexFormat::get_v3(), Geom::UH_static);
412  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
413 
414  PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
415  for (int sl = 0; sl < num_slices; ++sl) {
416  PN_stdfloat longitude0 = (PN_stdfloat)sl / (PN_stdfloat)num_slices;
417  PN_stdfloat longitude1 = (PN_stdfloat)(sl + 1) / (PN_stdfloat)num_slices;
418  vertex.add_data3(compute_sphere_point(0.0, longitude0));
419  for (int st = 1; st < num_stacks; ++st) {
420  PN_stdfloat latitude = (PN_stdfloat)st / (PN_stdfloat)num_stacks;
421  vertex.add_data3(compute_sphere_point(latitude, longitude0));
422  vertex.add_data3(compute_sphere_point(latitude, longitude1));
423  }
424  vertex.add_data3(compute_sphere_point(1.0, longitude0));
425 
426  strip->add_next_vertices(num_stacks * 2);
427  strip->close_primitive();
428  }
429 
430  _sphere_geom = new Geom(vdata);
431  _sphere_geom->add_primitive(strip);
432 }
433 
434 /**
435  * Returns a point on the surface of the unit sphere. latitude and longitude
436  * range from 0.0 to 1.0.
437  */
438 LVertex PipeOcclusionCullTraverser::
439 compute_sphere_point(PN_stdfloat latitude, PN_stdfloat longitude) {
440  PN_stdfloat s1, c1;
441  csincos(latitude * MathNumbers::pi, &s1, &c1);
442 
443  PN_stdfloat s2, c2;
444  csincos(longitude * 2.0f * MathNumbers::pi, &s2, &c2);
445 
446  LVertex p(s1 * c2, s1 * s2, c1);
447  return p;
448 }
449 
450 /**
451  * Constructs a unit box for testing visibility of bounding boxes.
452  */
453 void PipeOcclusionCullTraverser::
454 make_box() {
455  PT(GeomVertexData) vdata = new GeomVertexData
456  ("occlusion_box", GeomVertexFormat::get_v3(), Geom::UH_static);
457  vdata->unclean_set_num_rows(8);
458 
459  {
460  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
461  vertex.set_data3(0.0f, 0.0f, 0.0f);
462  vertex.set_data3(0.0f, 0.0f, 1.0f);
463  vertex.set_data3(0.0f, 1.0f, 0.0f);
464  vertex.set_data3(0.0f, 1.0f, 1.0f);
465  vertex.set_data3(1.0f, 0.0f, 0.0f);
466  vertex.set_data3(1.0f, 0.0f, 1.0f);
467  vertex.set_data3(1.0f, 1.0f, 0.0f);
468  vertex.set_data3(1.0f, 1.0f, 1.0f);
469  }
470 
471  PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
472  tris->add_vertices(0, 4, 5);
473  tris->add_vertices(0, 5, 1);
474  tris->add_vertices(4, 6, 7);
475  tris->add_vertices(4, 7, 5);
476  tris->add_vertices(6, 2, 3);
477  tris->add_vertices(6, 3, 7);
478  tris->add_vertices(2, 0, 1);
479  tris->add_vertices(2, 1, 3);
480  tris->add_vertices(1, 5, 7);
481  tris->add_vertices(1, 7, 3);
482  tris->add_vertices(2, 6, 4);
483  tris->add_vertices(2, 4, 0);
484 
485  _box_geom = new Geom(vdata);
486  _box_geom->add_primitive(tris);
487 }
488 
489 /**
490  * Creates the RenderState appropriate to rendering the occlusion test
491  * geometry invisibly.
492  */
493 void PipeOcclusionCullTraverser::
494 make_solid_test_state() {
495  _solid_test_state = RenderState::make
496  (DepthWriteAttrib::make(DepthWriteAttrib::M_off),
497  DepthTestAttrib::make(DepthTestAttrib::M_less),
498  ColorWriteAttrib::make(ColorWriteAttrib::C_off));
499 }
500 
501 /**
502  * Chooses a suitable Geom to render the indicated bounding volume, and fills
503  * geom and local_transform with the appropriate values. Returns true if the
504  * bounding volume can be rendered, false if there is no suitable
505  * visualization for it.
506  *
507  * On entry, net_transform should be filled with the net transform to the
508  * bounding volume. On exit (when return value is true), it will be composed
509  * with a suitable local transform to render the bounding volume properly, and
510  * internal_transform will also be filled with the appropriate transform.
511  */
512 bool PipeOcclusionCullTraverser::
513 get_volume_viz(const BoundingVolume *vol,
514  CPT(Geom) &geom, // OUT
515  CPT(TransformState) &net_transform, // IN-OUT
516  CPT(TransformState) &internal_transform // OUT
517  ) {
518  if (vol->is_infinite() || vol->is_empty()) {
519  return false;
520  }
521 
522  if (vol->is_exact_type(BoundingSphere::get_class_type())) {
523  const BoundingSphere *sphere = DCAST(BoundingSphere, vol);
524  CPT(TransformState) local_transform =
525  TransformState::make_pos_hpr_scale(sphere->get_center(),
526  LVecBase3(0, 0, 0),
527  sphere->get_radius());
528  net_transform = net_transform->compose(local_transform);
529 
530  CPT(TransformState) modelview_transform =
531  _internal_trav->get_world_transform()->compose(net_transform);
532 
533  // See if the bounding sphere is clipped by the near plane. If it is, the
534  // occlusion test may fail, so we won't bother performing it for this
535  // object. Anyway, it's not occluded by anything, since it's intersecting
536  // the near plane.
537  const LPoint3 &center = modelview_transform->get_pos();
538  const LVecBase3 &radius = modelview_transform->get_scale();
539  if (center[1] - radius[1] < 0.0f) {
540  return false;
541  }
542 
543  // Construct the internal transform for the internal traverser.
544  internal_transform = _internal_trav->get_scene()->
545  get_cs_transform()->compose(modelview_transform);
546 
547  // The sphere looks good.
548  geom = _sphere_geom;
549  return true;
550 
551  } else if (vol->is_exact_type(BoundingBox::get_class_type())) {
552  const BoundingBox *box = DCAST(BoundingBox, vol);
553  CPT(TransformState) local_transform =
554  TransformState::make_pos_hpr_scale(box->get_minq(),
555  LVecBase3(0, 0, 0),
556  box->get_maxq() - box->get_minq());
557  net_transform = net_transform->compose(local_transform);
558 
559  CPT(TransformState) modelview_transform =
560  _internal_trav->get_world_transform()->compose(net_transform);
561 
562  // See if the bounding box is clipped by the near plane. If it is, the
563  // occlusion test may fail, so we won't bother performing it for this
564  // object. Anyway, it's not occluded by anything, since it's intersecting
565  // the near plane.
566  static const LPoint3 points[8] = {
567  LPoint3(0.0f, 0.0f, 0.0f),
568  LPoint3(0.0f, 0.0f, 1.0f),
569  LPoint3(0.0f, 1.0f, 0.0f),
570  LPoint3(0.0f, 1.0f, 1.0f),
571  LPoint3(1.0f, 0.0f, 0.0f),
572  LPoint3(1.0f, 0.0f, 1.0f),
573  LPoint3(1.0f, 1.0f, 0.0f),
574  LPoint3(1.0f, 1.0f, 1.0f),
575  };
576  const LMatrix4 &mat = modelview_transform->get_mat();
577  for (int i = 0; i < 8; ++i) {
578  LPoint3 p = points[i] * mat;
579  if (p[1] < 0.0f) {
580  return false;
581  }
582  }
583 
584  // Construct the internal transform for the internal traverser.
585  internal_transform = _internal_trav->get_scene()->
586  get_cs_transform()->compose(modelview_transform);
587 
588  // The box looks good.
589  geom = _box_geom;
590  return true;
591  }
592 
593  // Don't have a suitable representation for this bounding volume.
594  return false;
595 }
596 
597 /**
598  * Renders the indicated geometry in the internal scene to test its
599  * visibility.
600  */
601 PT(OcclusionQueryContext) PipeOcclusionCullTraverser::
602 perform_occlusion_test(const Geom *geom, const TransformState *net_transform,
603  const TransformState *internal_transform) {
604  _occlusion_tests_pcollector.add_level(1);
605  PStatTimer timer(_test_occlusion_pcollector);
606 
607  GraphicsStateGuardian *gsg = _buffer->get_gsg();
608 
609  gsg->begin_occlusion_query();
610 
611  CullableObject *viz =
612  new CullableObject(geom, _solid_test_state, internal_transform);
613 
614  static ConfigVariableBool test_occlude("test-occlude", false);
615  if (test_occlude) {
616  _true_cull_handler->record_object(viz, _internal_trav);
617  } else {
618  _internal_cull_handler->record_object(viz, _internal_trav);
619  }
620 
621  PT(OcclusionQueryContext) query = gsg->end_occlusion_query();
622 
623  if (show_occlusion) {
624  // Show the results of the occlusion. To do this, we need to get the
625  // results of the query immediately. This will stall the pipe, but we're
626  // rendering a debug effect, so we don't mind too much.
627  int num_fragments = query->get_num_fragments();
628  show_results(num_fragments, geom, net_transform, internal_transform);
629  }
630 
631  return query;
632 }
633 
634 /**
635  * Draws a visualization of the results of occlusion test for a particular
636  * bounding volume.
637  */
638 void PipeOcclusionCullTraverser::
639 show_results(int num_fragments, const Geom *geom,
640  const TransformState *net_transform,
641  const TransformState *internal_transform) {
642  LColor color;
643  if (num_fragments == 0) {
644  // Magenta: culled
645  color.set(0.8f, 0.0f, 1.0f, 0.4f);
646  } else {
647  // Yellow: visible
648  color.set(1.0f, 1.0f, 0.5f, 0.4f);
649  }
650 
651  CPT(RenderState) state = RenderState::make
652  (DepthWriteAttrib::make(DepthWriteAttrib::M_off),
653  DepthTestAttrib::make(DepthTestAttrib::M_less),
654  TransparencyAttrib::make(TransparencyAttrib::M_alpha),
655  ColorAttrib::make_flat(color));
656 
657  CullableObject *internal_viz =
658  new CullableObject(geom, state, internal_transform);
659  _internal_cull_handler->record_object(internal_viz, _internal_trav);
660 
661  // Also render the viz in the main scene.
662  internal_transform = get_scene()->get_cs_world_transform()->compose(net_transform);
663  CullableObject *main_viz =
664  new CullableObject(geom, state, internal_transform);
665  _true_cull_handler->record_object(main_viz, this);
666 }
virtual void set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsg, bool dr_incomplete_render)
Sets the SceneSetup object that indicates the initial camera position, etc.
set_scene
Sets the SceneSetup object that indicates the initial camera position, etc.
bool is_final() const
Returns the current state of the "final" flag.
Definition: pandaNode.I:1531
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
An axis-aligned bounding box; that is, a minimum and maximum coordinate triple.
Definition: boundingBox.h:29
CullHandler * get_cull_handler() const
Returns the object that will receive the culled Geoms.
Encapsulates the data from a DisplayRegion, pre-fetched for one stage of the pipeline.
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:38
Indicates a coordinate-system transform on vertices.
virtual GraphicsOutput * get_host()
This is normally called only from within make_texture_buffer().
get_pipe
Returns the graphics pipe on which this GSG was created.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void end_traverse()
Should be called when the traverser has finished traversing its scene, this gives it a chance to do a...
const RenderEffects * get_effects() const
Returns the complete RenderEffects that will be applied to this node.
Definition: pandaNode.I:1431
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
set_size
Specifies the requested size of the window, in pixels.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_gsg
Returns the nth GSG in the universe.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void traverse_below(CullTraverserData &data)
Traverses all the children of the indicated node, with the given data, which has been converted into ...
bool is_empty() const
Any kind of volume might be empty.
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Makes the specified DisplayRegion current.
Thread * get_current_thread() const
Returns the currently-executing thread object, as passed to the CullTraverser constructor.
Definition: cullTraverser.I:27
This is a convenience class to specialize ConfigVariable as a boolean type.
This defines a bounding sphere, consisting of a center and a radius.
bool is_infinite() const
The other side of the empty coin is an infinite volume.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
int get_nested_vertices() const
Returns the total number of vertices that will be rendered by this node and all of its descendents.
Definition: pandaNode.I:1520
This collects together the pieces of data that are accumulated for each node while walking the scene ...
get_parent
Returns the NodePath to the parent of the referenced node: that is, this NodePath,...
Definition: nodePath.h:244
Defines a series of triangle strips.
Definition: geomTristrips.h:23
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void end_traverse()
Should be called when the traverser has finished traversing its scene, this gives it a chance to do a...
const TransformState * get_cs_world_transform() const
Returns the position from the starting node relative to the camera, in the GSG's internal coordinate ...
Definition: sceneSetup.I:268
GraphicsOutput * make_output(GraphicsPipe *pipe, const std::string &name, int sort, const FrameBufferProperties &fb_prop, const WindowProperties &win_prop, int flags, GraphicsStateGuardian *gsg=nullptr, GraphicsOutput *host=nullptr)
Creates a new window (or buffer) and returns it.
get_supports_occlusion_query
Returns true if this GSG supports an occlusion query.
GraphicsEngine * get_engine() const
Returns the graphics engine that created this GSG.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const TransformState * get_cs_transform() const
Returns the transform from the camera's coordinate system to the GSG's internal coordinate system.
Definition: sceneSetup.I:250
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
A lightweight class that represents a single element that may be timed and/or counted via stats.
size_t get_num_words() const
Returns the number of words in the variable's value.
bool has_show_bounds() const
This function is provided as an optimization, to speed up the render-time checking for the existance ...
A container for the various kinds of properties we might ask to have on a graphics window before we o...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The smallest atom of cull.
Texture * get_texture()
Returns a Texture that can be used to visualize the efforts of the occlusion cull.
virtual void record_object(CullableObject *object, const CullTraverser *traverser)
This callback function is intended to be overridden by a derived class.
Definition: cullHandler.cxx:43
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
const LPoint3 & get_maxq() const
An inline accessor for the maximum value.
Definition: boundingBox.I:52
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for geometry primitives.
Definition: geom.h:54
virtual void begin_occlusion_query()
Begins a new occlusion query.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a base class for the various different classes that represent the result of a frame of render...
This represents the user's specification of how a particular frame is handled by the various threads.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This special kind of CullHandler immediately draws its contents as soon as it receives them.
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
A thread; that is, a lightweight process.
Definition: thread.h:46
GeometricBoundingVolume * get_view_frustum() const
Returns the bounding volume that corresponds to the view frustum, or NULL if the view frustum is not ...
Encapsulates the data from a PandaNode, pre-fetched for one stage of the pipeline.
Definition: pandaNode.h:842
SceneSetup * get_scene() const
Returns the SceneSetup object.
Definition: cullTraverser.I:35
Encapsulates all the communication with a particular instance of a given rendering backend.
This is a convenience class to specialize ConfigVariable as an integer type.
Defines a series of disconnected triangles.
Definition: geomTriangles.h:23
virtual void end_scene()
Called between begin_frame() and end_frame() to mark the end of drawing commands for a "scene" (usual...
Returned from a GSG in response to begin_occlusion_query() .
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is the main interface to controlling the render process.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:758
This object holds the camera position, etc., and other general setup information for rendering a part...
Definition: sceneSetup.h:32
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
PT(OcclusionQueryContext) PipeOcclusionCullTraverser
Renders the indicated geometry in the internal scene to test its visibility.
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2068
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_gsg
Returns the GSG that is associated with this window.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static const GeomVertexFormat * get_v3()
Returns a standard vertex format with just a 3-component vertex position.
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
virtual void set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsg, bool dr_incomplete_render)
Sets the SceneSetup object that indicates the initial camera position, etc.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const GraphicsThreadingModel & get_threading_model() const
Returns the threading model that was used to create this GSG.
virtual bool begin_scene()
Called between begin_frame() and end_frame() to mark the beginning of drawing commands for a "scene" ...
const LPoint3 & get_minq() const
An inline accessor for the minimum value.
Definition: boundingBox.I:41
void set_cull_handler(CullHandler *cull_handler)
Specifies the object that will receive the culled Geoms.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.