Panda3D
 All Classes Functions Variables Enumerations
pipeOcclusionCullTraverser.cxx
00001 // Filename: pipeOcclusionCullTraverser.cxx
00002 // Created by:  drose (29May07)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pipeOcclusionCullTraverser.h"
00016 #include "graphicsEngine.h"
00017 #include "graphicsPipe.h"
00018 #include "drawCullHandler.h"
00019 #include "boundingSphere.h"
00020 #include "boundingBox.h"
00021 #include "geomVertexWriter.h"
00022 #include "geomTristrips.h"
00023 #include "geomTriangles.h"
00024 #include "pStatTimer.h"
00025 #include "depthWriteAttrib.h"
00026 #include "depthTestAttrib.h"
00027 #include "colorWriteAttrib.h"
00028 #include "colorAttrib.h"
00029 #include "cullBinManager.h"
00030 #include "configVariableInt.h"
00031 #include "config_grutil.h"
00032 #include "pnmImage.h"
00033 
00034 PStatCollector PipeOcclusionCullTraverser::_setup_occlusion_pcollector("Cull:Occlusion:Setup");
00035 PStatCollector PipeOcclusionCullTraverser::_draw_occlusion_pcollector("Cull:Occlusion:Occluders");
00036 PStatCollector PipeOcclusionCullTraverser::_test_occlusion_pcollector("Cull:Occlusion:Test");
00037 PStatCollector PipeOcclusionCullTraverser::_finish_occlusion_pcollector("Cull:Occlusion:Finish");
00038 
00039 PStatCollector PipeOcclusionCullTraverser::_occlusion_untested_pcollector("Occlusion results:Not tested");
00040 PStatCollector PipeOcclusionCullTraverser::_occlusion_passed_pcollector("Occlusion results:Visible");
00041 PStatCollector PipeOcclusionCullTraverser::_occlusion_failed_pcollector("Occlusion results:Occluded");
00042 PStatCollector PipeOcclusionCullTraverser::_occlusion_tests_pcollector("Occlusion tests");
00043 
00044 TypeHandle PipeOcclusionCullTraverser::_type_handle;
00045 
00046 static ConfigVariableInt min_occlusion_vertices
00047 ("min-occlusion-vertices", 300,
00048  PRC_DESC("The minimum number of vertices a PandaNode or Geom must contain "
00049           "in order to perform an occlusion query for it.  Nodes and Geoms "
00050           "smaller than this will be rendered directly, without bothering "
00051           "with an occlusion query."));
00052 
00053 static ConfigVariableInt max_occlusion_vertices
00054 ("max-occlusion-vertices", 3000,
00055  PRC_DESC("The maximum number of vertices that may be included in a PandaNode "
00056           "and its descendents in order to perform an occlusion query for "
00057           "it.  Subgraphs whose total vertex count exceeds this number will "
00058           "be subdivided further before performing an occlusion test--the "
00059           "hope is that we can eventually get to a finer-grained answer.  "
00060           "GeomNodes and Geoms will not be subdivided, regardless of this "
00061           "limit."));
00062 
00063 static ConfigVariableBool show_occlusion
00064 ("show-occlusion", false,
00065  PRC_DESC("Set this true to visualize the efforts of the occlusion test."));
00066 
00067 static ConfigVariableInt occlusion_size
00068 ("occlusion-size", "256 256",
00069  PRC_DESC("Specify the x y size of the buffer used for occlusion testing."));
00070 
00071 static ConfigVariableInt occlusion_depth_bits
00072 ("occlusion-depth-bits", 1,
00073  PRC_DESC("The minimum number of depth bits requested for the occlusion "
00074           "buffer."));
00075 
00076 ////////////////////////////////////////////////////////////////////
00077 //     Function: PipeOcclusionCullTraverser::Constructor
00078 //       Access: Published
00079 //  Description: 
00080 ////////////////////////////////////////////////////////////////////
00081 PipeOcclusionCullTraverser::
00082 PipeOcclusionCullTraverser(GraphicsOutput *host) {
00083   _live = false;
00084   GraphicsStateGuardian *gsg = host->get_gsg();
00085 
00086   GraphicsThreadingModel threading_model = gsg->get_threading_model();
00087   nassertv(threading_model.get_cull_name() == threading_model.get_draw_name());
00088   if (!gsg->get_supports_occlusion_query()) {
00089     grutil_cat.info()
00090       << "Occlusion queries are not supported by graphics pipe.\n";
00091     return;
00092   }
00093 
00094   GraphicsEngine *engine = gsg->get_engine();
00095   GraphicsPipe *pipe = gsg->get_pipe();
00096 
00097   FrameBufferProperties fb_prop;
00098   fb_prop.set_depth_bits(occlusion_depth_bits);
00099   WindowProperties win_prop;
00100   if (occlusion_size.get_num_words() < 2) {
00101     win_prop.set_size(occlusion_size, occlusion_size);
00102   } else {
00103     win_prop.set_size(occlusion_size[0], occlusion_size[1]);
00104   }
00105 
00106   _buffer = engine->make_output(pipe, "occlusion", 0, fb_prop, win_prop, 
00107                                 GraphicsPipe::BF_refuse_window,
00108                                 gsg, host->get_host());
00109   nassertv(_buffer != (GraphicsOutput *)NULL);
00110 
00111   // This buffer isn't really active--we render it by hand; we don't
00112   // want the GraphicsEngine to render it.
00113   _buffer->set_active(0);
00114 
00115   _display_region = _buffer->make_display_region();
00116   _internal_cull_handler = NULL;
00117 
00118   make_sphere();
00119   make_box();
00120   make_solid_test_state();
00121 
00122   _live = true;
00123 }
00124 
00125 ////////////////////////////////////////////////////////////////////
00126 //     Function: PipeOcclusionCullTraverser::Copy Constructor
00127 //       Access: Published
00128 //  Description: 
00129 ////////////////////////////////////////////////////////////////////
00130 PipeOcclusionCullTraverser::
00131 PipeOcclusionCullTraverser(const PipeOcclusionCullTraverser &copy) :
00132   CullTraverser(copy)
00133 {
00134   nassertv(false);
00135 }
00136 
00137 ////////////////////////////////////////////////////////////////////
00138 //     Function: PipeOcclusionCullTraverser::set_scene
00139 //       Access: Published, Virtual
00140 //  Description: 
00141 ////////////////////////////////////////////////////////////////////
00142 void PipeOcclusionCullTraverser::
00143 set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsgbase,
00144           bool dr_incomplete_render) {
00145   CullTraverser::set_scene(scene_setup, gsgbase, dr_incomplete_render);
00146   if (!_live) {
00147     return;
00148   }
00149 
00150   PStatTimer timer(_setup_occlusion_pcollector);
00151 
00152   GraphicsStateGuardian *gsg = _buffer->get_gsg();
00153   nassertv(gsg == gsgbase);
00154 
00155   Thread *current_thread = get_current_thread();
00156   if (!_buffer->begin_frame(GraphicsOutput::FM_render, current_thread)) {
00157     cerr << "begin_frame failed\n";
00158     return;
00159   }
00160   _buffer->clear(current_thread);
00161 
00162   DisplayRegionPipelineReader dr_reader(_display_region, current_thread);
00163 
00164   _buffer->change_scenes(&dr_reader);
00165   gsg->prepare_display_region(&dr_reader);
00166 
00167   _scene = new SceneSetup(*scene_setup);
00168   _scene->set_display_region(_display_region);
00169   _scene->set_viewport_size(_display_region->get_pixel_width(), 
00170                             _display_region->get_pixel_height());
00171 
00172   if (_scene->get_cull_center() != _scene->get_camera_path()) {
00173     // This camera has a special cull center set.  For the purposes of
00174     // occlusion culling, we want to render the scene from the cull
00175     // center, not from the camera root.
00176     NodePath cull_center = _scene->get_cull_center();
00177     NodePath scene_parent = _scene->get_scene_root().get_parent(current_thread);
00178     CPT(TransformState) camera_transform = cull_center.get_transform(scene_parent, current_thread);
00179     CPT(TransformState) world_transform = scene_parent.get_transform(cull_center, current_thread);
00180     _scene->set_camera_transform(camera_transform);
00181     _scene->set_world_transform(world_transform);
00182   }
00183     
00184   gsg->set_scene(_scene);
00185   if (!gsg->begin_scene()) {
00186     cerr << "begin_scene failed\n";
00187     return;
00188   }
00189 
00190   // Hijack the default cull handler so we can perform all of the
00191   // occlusion tests on a per-object basis, and then query the results
00192   // at the end of the traversal.
00193   _true_cull_handler = get_cull_handler();
00194   set_cull_handler(this);
00195 
00196   _internal_cull_handler = new DrawCullHandler(gsg);
00197   _internal_trav = new CullTraverser;
00198   _internal_trav->set_cull_handler(_internal_cull_handler);
00199   _internal_trav->set_scene(_scene, gsg, dr_incomplete_render);
00200   _internal_trav->set_view_frustum(get_view_frustum());
00201   _internal_trav->set_camera_mask(_occlusion_mask);
00202 
00203   _current_query = NULL;
00204   _next_query = NULL;
00205   
00206   // Begin by rendering all the occluders into our internal scene.
00207   PStatTimer timer2(_draw_occlusion_pcollector);
00208   _internal_trav->traverse(_scene->get_scene_root());
00209 }
00210 
00211 ////////////////////////////////////////////////////////////////////
00212 //     Function: PipeOcclusionCullTraverser::end_traverse
00213 //       Access: Public, Virtual
00214 //  Description: Should be called when the traverser has finished
00215 //               traversing its scene, this gives it a chance to do
00216 //               any necessary finalization.
00217 ////////////////////////////////////////////////////////////////////
00218 void PipeOcclusionCullTraverser::
00219 end_traverse() {
00220   if (!_live) {
00221     return;
00222   }
00223 
00224   PStatTimer timer(_finish_occlusion_pcollector);
00225   GraphicsStateGuardian *gsg = _buffer->get_gsg();
00226   Thread *current_thread = get_current_thread();
00227 
00228   _current_query = NULL;
00229   _next_query = NULL;
00230 
00231   PendingObjects::iterator oi;
00232   for (oi = _pending_objects.begin(); oi != _pending_objects.end(); ++oi) {
00233     PendingObject &pobj = (*oi);
00234     if (pobj._query == (OcclusionQueryContext *)NULL) {
00235       _occlusion_untested_pcollector.add_level(1);
00236       _true_cull_handler->record_object(pobj._object, this);
00237     } else {
00238       int num_fragments = pobj._query->get_num_fragments();
00239       if (num_fragments != 0) {
00240         _occlusion_passed_pcollector.add_level(1);
00241         _true_cull_handler->record_object(pobj._object, this);
00242       } else {
00243         _occlusion_failed_pcollector.add_level(1);
00244         delete pobj._object;
00245       }
00246     }
00247 
00248     // The CullableObject has by now either been recorded (which will
00249     // eventually delete it) or deleted directly.
00250 #ifndef NDEBUG
00251     pobj._object = NULL;
00252 #endif  // NDEBUG
00253   }
00254   _pending_objects.clear();
00255   CullTraverser::end_traverse();
00256 
00257   gsg->end_scene();
00258   _buffer->end_frame(GraphicsOutput::FM_render, current_thread);
00259 
00260   _buffer->begin_flip();
00261   _buffer->end_flip();
00262 
00263   delete _internal_cull_handler;
00264   _internal_cull_handler = NULL;
00265 
00266   _occlusion_untested_pcollector.flush_level();
00267   _occlusion_passed_pcollector.flush_level();
00268   _occlusion_failed_pcollector.flush_level();
00269   _occlusion_tests_pcollector.flush_level();
00270 }
00271 
00272 ////////////////////////////////////////////////////////////////////
00273 //     Function: PipeOcclusionCullTraverser::get_texture
00274 //       Access: Published
00275 //  Description: Returns a Texture that can be used to visualize the
00276 //               efforts of the occlusion cull.
00277 ////////////////////////////////////////////////////////////////////
00278 Texture *PipeOcclusionCullTraverser::
00279 get_texture() {
00280   if (_texture != (Texture *)NULL) {
00281     return _texture;
00282   }
00283 
00284   _texture = new Texture("occlusion");
00285 
00286   if (!_live) {
00287     // If we're not live, just create a default, black texture.
00288     PNMImage image(1, 1);
00289     _texture->load(image);
00290 
00291   } else {
00292     // Otherwise, create a render-to-texture.
00293     _buffer->add_render_texture(_texture, GraphicsOutput::RTM_bind_or_copy);
00294   }
00295 
00296   return _texture;
00297 }
00298 
00299 ////////////////////////////////////////////////////////////////////
00300 //     Function: PipeOcclusionCullTraverser::is_in_view
00301 //       Access: Protected, Virtual
00302 //  Description: 
00303 ////////////////////////////////////////////////////////////////////
00304 bool PipeOcclusionCullTraverser::
00305 is_in_view(CullTraverserData &data) {
00306   _next_query = NULL;
00307 
00308   if (!CullTraverser::is_in_view(data)) {
00309     return false;
00310   }
00311   if (!_live) {
00312     return true;
00313   }
00314 
00315   if (_current_query != (OcclusionQueryContext *)NULL) {
00316     // We've already performed an occlusion test for some ancestor of
00317     // this node; no need to perform another.
00318     return true;
00319   }
00320 
00321   PandaNode *node = data.node();
00322   PandaNodePipelineReader *node_reader = data.node_reader();
00323 
00324   if (node_reader->get_nested_vertices() < min_occlusion_vertices) {
00325     // Never mind; let this puny one slide.
00326     return true;
00327   }
00328 
00329   if (node->is_geom_node() ||
00330       node_reader->is_final() ||
00331       node_reader->get_effects()->has_show_bounds() ||
00332       node_reader->get_nested_vertices() <= max_occlusion_vertices) {
00333     // In any of these cases, there's sufficient reason to perform an
00334     // occlusion test on this particular node.  Do it.
00335 
00336     CPT(BoundingVolume) vol = node_reader->get_bounds();
00337     CPT(TransformState) net_transform = data.get_net_transform(this);
00338     CPT(TransformState) modelview_transform;
00339 
00340     CPT(Geom) geom;
00341     if (get_volume_viz(vol, geom, net_transform, modelview_transform)) {
00342       _next_query = 
00343         perform_occlusion_test(geom, net_transform, modelview_transform);
00344     }
00345   }
00346 
00347   return true;
00348 }
00349 
00350 ////////////////////////////////////////////////////////////////////
00351 //     Function: PipeOcclusionCullTraverser::traverse_below
00352 //       Access: Public, Virtual
00353 //  Description: Traverses all the children of the indicated node,
00354 //               with the given data, which has been converted into
00355 //               the node's space.
00356 ////////////////////////////////////////////////////////////////////
00357 void PipeOcclusionCullTraverser::
00358 traverse_below(CullTraverserData &data) {
00359   // Save and restore _current_query, and clear _next_query, for
00360   // traversing the children of this node.
00361   PT(OcclusionQueryContext) prev_query = _current_query;
00362   if (_next_query != (OcclusionQueryContext *)NULL) {
00363     _current_query = _next_query;
00364   }
00365   _next_query = NULL;
00366 
00367   CullTraverser::traverse_below(data);
00368 
00369   _current_query = prev_query;
00370   _next_query = NULL;
00371 }
00372 
00373 ////////////////////////////////////////////////////////////////////
00374 //     Function: PipeOcclusionCullTraverser::record_object
00375 //       Access: Protected, Virtual
00376 //  Description: This callback function is intended to be overridden
00377 //               by a derived class.  This is called as each Geom is
00378 //               discovered by the CullTraverser.
00379 //
00380 //               We do a sneaky trick in making
00381 //               PipeOcclusionCullTraverser inherit from both
00382 //               CullTraverser and CullHandler--the traverser is its
00383 //               own handler!  This is the normal callback into the
00384 //               traverser for rendering objects.  We respond to this
00385 //               by firing off an occlusion test, and queuing up the
00386 //               object until the end of the scene.
00387 ////////////////////////////////////////////////////////////////////
00388 void PipeOcclusionCullTraverser::
00389 record_object(CullableObject *object, const CullTraverser *traverser) {
00390   nassertv(traverser == this);
00391   PendingObject pobj(object);
00392 
00393   Thread *current_thread = get_current_thread();
00394 
00395   if (_next_query != (OcclusionQueryContext *)NULL) {
00396     // We have just performed an occlusion query for this node.  Don't
00397     // perform another one.
00398     pobj._query = _next_query;
00399 
00400   } else if (_current_query != (OcclusionQueryContext *)NULL) {
00401     // We have previously performed an occlusion query for this node
00402     // or some ancestor.  Don't perform another one.
00403     pobj._query = _current_query;
00404 
00405   } else if (object->_geom->get_nested_vertices(current_thread) < min_occlusion_vertices) {
00406     // This object is too small to bother testing for occlusions.
00407 
00408   } else {
00409     // Issue an occlusion test for this object.
00410     CPT(BoundingVolume) vol = object->_geom->get_bounds(current_thread);
00411     CPT(TransformState) net_transform = object->_net_transform;
00412     CPT(TransformState) modelview_transform;
00413     CPT(Geom) geom;
00414     if (get_volume_viz(vol, geom, net_transform, modelview_transform)) {
00415       pobj._query = 
00416         perform_occlusion_test(geom, net_transform, modelview_transform);
00417     }
00418   }
00419 
00420   _pending_objects.push_back(pobj);
00421 }
00422 
00423 ////////////////////////////////////////////////////////////////////
00424 //     Function: PipeOcclusionCullTraverser::make_sphere
00425 //       Access: Private
00426 //  Description: Constructs a unit sphere for testing visibility of
00427 //               bounding spheres.
00428 ////////////////////////////////////////////////////////////////////
00429 void PipeOcclusionCullTraverser::
00430 make_sphere() {
00431   ConfigVariableInt num_slices("num-slices", 16);
00432   ConfigVariableInt num_stacks("num-stacks", 8);
00433 
00434   //  static const int num_slices = 16;
00435   //  static const int num_stacks = 8;
00436 
00437   PT(GeomVertexData) vdata = new GeomVertexData
00438     ("occlusion_sphere", GeomVertexFormat::get_v3(), Geom::UH_static);
00439   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00440   
00441   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
00442   for (int sl = 0; sl < num_slices; ++sl) {
00443     PN_stdfloat longitude0 = (PN_stdfloat)sl / (PN_stdfloat)num_slices;
00444     PN_stdfloat longitude1 = (PN_stdfloat)(sl + 1) / (PN_stdfloat)num_slices;
00445     vertex.add_data3(compute_sphere_point(0.0, longitude0));
00446     for (int st = 1; st < num_stacks; ++st) {
00447       PN_stdfloat latitude = (PN_stdfloat)st / (PN_stdfloat)num_stacks;
00448       vertex.add_data3(compute_sphere_point(latitude, longitude0));
00449       vertex.add_data3(compute_sphere_point(latitude, longitude1));
00450     }
00451     vertex.add_data3(compute_sphere_point(1.0, longitude0));
00452     
00453     strip->add_next_vertices(num_stacks * 2);
00454     strip->close_primitive();
00455   }
00456   
00457   _sphere_geom = new Geom(vdata);
00458   _sphere_geom->add_primitive(strip);
00459 }
00460 
00461 ////////////////////////////////////////////////////////////////////
00462 //     Function: PipeOcclusionCullTraverser::compute_sphere_point
00463 //       Access: Private, Static
00464 //  Description: Returns a point on the surface of the unit sphere.
00465 //               latitude and longitude range from 0.0 to 1.0.  
00466 ////////////////////////////////////////////////////////////////////
00467 LVertex PipeOcclusionCullTraverser::
00468 compute_sphere_point(PN_stdfloat latitude, PN_stdfloat longitude) {
00469   PN_stdfloat s1, c1;
00470   csincos(latitude * MathNumbers::pi, &s1, &c1);
00471 
00472   PN_stdfloat s2, c2;
00473   csincos(longitude * 2.0f * MathNumbers::pi, &s2, &c2);
00474 
00475   LVertex p(s1 * c2, s1 * s2, c1);
00476   return p;
00477 }
00478 
00479 ////////////////////////////////////////////////////////////////////
00480 //     Function: PipeOcclusionCullTraverser::make_box
00481 //       Access: Private
00482 //  Description: Constructs a unit box for testing visibility of
00483 //               bounding boxes.
00484 ////////////////////////////////////////////////////////////////////
00485 void PipeOcclusionCullTraverser::
00486 make_box() {
00487   PT(GeomVertexData) vdata = new GeomVertexData
00488     ("occlusion_box", GeomVertexFormat::get_v3(), Geom::UH_static);
00489   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00490   
00491   vertex.add_data3(0.0f, 0.0f, 0.0f);
00492   vertex.add_data3(0.0f, 0.0f, 1.0f);
00493   vertex.add_data3(0.0f, 1.0f, 0.0f);
00494   vertex.add_data3(0.0f, 1.0f, 1.0f);
00495   vertex.add_data3(1.0f, 0.0f, 0.0f);
00496   vertex.add_data3(1.0f, 0.0f, 1.0f);
00497   vertex.add_data3(1.0f, 1.0f, 0.0f);
00498   vertex.add_data3(1.0f, 1.0f, 1.0f);
00499     
00500   PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
00501   tris->add_vertices(0, 4, 5);
00502   tris->close_primitive();
00503   tris->add_vertices(0, 5, 1);
00504   tris->close_primitive();
00505   tris->add_vertices(4, 6, 7);
00506   tris->close_primitive();
00507   tris->add_vertices(4, 7, 5);
00508   tris->close_primitive();
00509   tris->add_vertices(6, 2, 3);
00510   tris->close_primitive();
00511   tris->add_vertices(6, 3, 7);
00512   tris->close_primitive();
00513   tris->add_vertices(2, 0, 1);
00514   tris->close_primitive();
00515   tris->add_vertices(2, 1, 3);
00516   tris->close_primitive();
00517   tris->add_vertices(1, 5, 7);
00518   tris->close_primitive();
00519   tris->add_vertices(1, 7, 3);
00520   tris->close_primitive();
00521   tris->add_vertices(2, 6, 4);
00522   tris->close_primitive();
00523   tris->add_vertices(2, 4, 0);
00524   tris->close_primitive();
00525   
00526   _box_geom = new Geom(vdata);
00527   _box_geom->add_primitive(tris);
00528 }
00529 
00530 ////////////////////////////////////////////////////////////////////
00531 //     Function: PipeOcclusionCullTraverser::make_solid_test_state
00532 //       Access: Private
00533 //  Description: Creates the RenderState appropriate to rendering the
00534 //               occlusion test geometry invisibly.
00535 ////////////////////////////////////////////////////////////////////
00536 void PipeOcclusionCullTraverser::
00537 make_solid_test_state() {
00538   _solid_test_state = RenderState::make
00539     (DepthWriteAttrib::make(DepthWriteAttrib::M_off),
00540      DepthTestAttrib::make(DepthTestAttrib::M_less),
00541      ColorWriteAttrib::make(ColorWriteAttrib::C_off));
00542 }
00543 
00544 ////////////////////////////////////////////////////////////////////
00545 //     Function: PipeOcclusionCullTraverser::get_volume_viz
00546 //       Access: Private
00547 //  Description: Chooses a suitable Geom to render the indicated
00548 //               bounding volume, and fills geom and local_transform
00549 //               with the appropriate values.  Returns true if the
00550 //               bounding volume can be rendered, false if there is no
00551 //               suitable visualization for it.
00552 //
00553 //               On entry, net_transform should be filled with the net
00554 //               transform to the bounding volume.  On exit (when
00555 //               return value is true), it will be composed with a
00556 //               suitable local transform to render the bounding
00557 //               volume properly, and modelview_transform will also be
00558 //               filled with the appropriate transform.
00559 ////////////////////////////////////////////////////////////////////
00560 bool PipeOcclusionCullTraverser::
00561 get_volume_viz(const BoundingVolume *vol, 
00562                CPT(Geom) &geom,  // OUT
00563                CPT(TransformState) &net_transform, // IN-OUT
00564                CPT(TransformState) &modelview_transform  // OUT
00565                ) {
00566   if (vol->is_infinite() || vol->is_empty()) {
00567     return false;
00568   }
00569 
00570   if (vol->is_exact_type(BoundingSphere::get_class_type())) {
00571     const BoundingSphere *sphere = DCAST(BoundingSphere, vol);
00572     CPT(TransformState) local_transform = 
00573       TransformState::make_pos_hpr_scale(sphere->get_center(),
00574                                          LVecBase3(0, 0, 0),
00575                                          sphere->get_radius());
00576     net_transform = net_transform->compose(local_transform);
00577 
00578     modelview_transform = _internal_trav->get_world_transform()->compose(net_transform);
00579 
00580     // See if the bounding sphere is clipped by the near plane.  If it
00581     // is, the occlusion test may fail, so we won't bother performing
00582     // it for this object.  Anyway, it's not occluded by anything,
00583     // since it's intersecting the near plane.
00584     const LPoint3 &center = modelview_transform->get_pos();
00585     const LVecBase3 &radius = modelview_transform->get_scale();
00586     if (center[1] - radius[1] < 0.0f) {
00587       return false;
00588     }
00589 
00590     // The sphere looks good.
00591     geom = _sphere_geom;
00592     return true;
00593 
00594   } else if (vol->is_exact_type(BoundingBox::get_class_type())) {
00595     const BoundingBox *box = DCAST(BoundingBox, vol);
00596     CPT(TransformState) local_transform = 
00597       TransformState::make_pos_hpr_scale(box->get_minq(),
00598                                          LVecBase3(0, 0, 0),
00599                                          box->get_maxq() - box->get_minq());
00600     net_transform = net_transform->compose(local_transform);
00601 
00602     modelview_transform = _internal_trav->get_world_transform()->compose(net_transform);
00603 
00604     // See if the bounding box is clipped by the near plane.  If it
00605     // is, the occlusion test may fail, so we won't bother performing
00606     // it for this object.  Anyway, it's not occluded by anything,
00607     // since it's intersecting the near plane.
00608     static const LPoint3 points[8] = {
00609       LPoint3(0.0f, 0.0f, 0.0f),
00610       LPoint3(0.0f, 0.0f, 1.0f),
00611       LPoint3(0.0f, 1.0f, 0.0f),
00612       LPoint3(0.0f, 1.0f, 1.0f),
00613       LPoint3(1.0f, 0.0f, 0.0f),
00614       LPoint3(1.0f, 0.0f, 1.0f),
00615       LPoint3(1.0f, 1.0f, 0.0f),
00616       LPoint3(1.0f, 1.0f, 1.0f),
00617     };
00618     const LMatrix4 &mat = modelview_transform->get_mat();
00619     for (int i = 0; i < 8; ++i) {
00620       LPoint3 p = points[i] * mat;
00621       if (p[1] < 0.0f) {
00622         return false;
00623       }
00624     }
00625 
00626     // The box looks good.
00627     geom = _box_geom;
00628     return true;
00629   }
00630 
00631   // Don't have a suitable representation for this bounding volume.
00632   return false;
00633 }
00634 
00635 ////////////////////////////////////////////////////////////////////
00636 //     Function: PipeOcclusionCullTraverser::perform_occlusion_test
00637 //       Access: Private
00638 //  Description: Renders the indicated geometry in the internal scene
00639 //               to test its visibility.
00640 ////////////////////////////////////////////////////////////////////
00641 PT(OcclusionQueryContext) PipeOcclusionCullTraverser::
00642 perform_occlusion_test(const Geom *geom, const TransformState *net_transform,
00643                        const TransformState *modelview_transform) {
00644   _occlusion_tests_pcollector.add_level(1);
00645   PStatTimer timer(_test_occlusion_pcollector);
00646 
00647   GraphicsStateGuardian *gsg = _buffer->get_gsg();
00648 
00649   gsg->begin_occlusion_query();
00650 
00651   CullableObject *viz = 
00652     new CullableObject(geom, _solid_test_state,
00653                        net_transform, modelview_transform, gsg);
00654 
00655   static ConfigVariableBool test_occlude("test-occlude", false);
00656   if (test_occlude) {
00657     _true_cull_handler->record_object(viz, _internal_trav);
00658   } else {
00659     _internal_cull_handler->record_object(viz, _internal_trav);
00660   }
00661   
00662   PT(OcclusionQueryContext) query = gsg->end_occlusion_query();
00663     
00664   if (show_occlusion) {
00665     // Show the results of the occlusion.  To do this, we need to get
00666     // the results of the query immediately.  This will stall the
00667     // pipe, but we're rendering a debug effect, so we don't mind too
00668     // much.
00669     int num_fragments = query->get_num_fragments();
00670     show_results(num_fragments, geom, net_transform, modelview_transform);
00671   }
00672 
00673   return query;
00674 }
00675 
00676 ////////////////////////////////////////////////////////////////////
00677 //     Function: PipeOcclusionCullTraverser::show_results
00678 //       Access: Private
00679 //  Description: Draws a visualization of the results of occlusion
00680 //               test for a particular bounding volume.
00681 ////////////////////////////////////////////////////////////////////
00682 void PipeOcclusionCullTraverser::
00683 show_results(int num_fragments, const Geom *geom, 
00684              const TransformState *net_transform, 
00685              const TransformState *modelview_transform) {
00686   LColor color;
00687   if (num_fragments == 0) {
00688     // Magenta: culled
00689     color.set(0.8f, 0.0f, 1.0f, 0.4);
00690   } else {
00691     // Yellow: visible
00692     color.set(1.0f, 1.0f, 0.5f, 0.4);
00693   }
00694   
00695   CPT(RenderState) state = RenderState::make
00696     (DepthWriteAttrib::make(DepthWriteAttrib::M_off),
00697      DepthTestAttrib::make(DepthTestAttrib::M_less),
00698      TransparencyAttrib::make(TransparencyAttrib::M_alpha),
00699      ColorAttrib::make_flat(color));
00700 
00701   GraphicsStateGuardian *gsg = _buffer->get_gsg();
00702 
00703   CullableObject *internal_viz = 
00704     new CullableObject(geom, state,
00705                        net_transform, modelview_transform, gsg);
00706   _internal_cull_handler->record_object(internal_viz, _internal_trav);
00707 
00708   // Also render the viz in the main scene.
00709   modelview_transform = get_world_transform()->compose(net_transform);
00710   CullableObject *main_viz = 
00711     new CullableObject(geom, state,
00712                        net_transform, modelview_transform, gsg);
00713   _true_cull_handler->record_object(main_viz, this);
00714 }
 All Classes Functions Variables Enumerations