Panda3D
|
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 ©) : 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 ¢er = 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 }