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