00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00078
00079
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
00112
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
00127
00128
00129
00130 PipeOcclusionCullTraverser::
00131 PipeOcclusionCullTraverser(const PipeOcclusionCullTraverser ©) :
00132 CullTraverser(copy)
00133 {
00134 nassertv(false);
00135 }
00136
00137
00138
00139
00140
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
00174
00175
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
00191
00192
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
00207 PStatTimer timer2(_draw_occlusion_pcollector);
00208 _internal_trav->traverse(_scene->get_scene_root());
00209 }
00210
00211
00212
00213
00214
00215
00216
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
00249
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
00274
00275
00276
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
00288 PNMImage image(1, 1);
00289 _texture->load(image);
00290
00291 } else {
00292
00293 _buffer->add_render_texture(_texture, GraphicsOutput::RTM_bind_or_copy);
00294 }
00295
00296 return _texture;
00297 }
00298
00299
00300
00301
00302
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
00317
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
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
00334
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
00352
00353
00354
00355
00356
00357 void PipeOcclusionCullTraverser::
00358 traverse_below(CullTraverserData &data) {
00359
00360
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
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
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
00397
00398 pobj._query = _next_query;
00399
00400 } else if (_current_query != (OcclusionQueryContext *)NULL) {
00401
00402
00403 pobj._query = _current_query;
00404
00405 } else if (object->_geom->get_nested_vertices(current_thread) < min_occlusion_vertices) {
00406
00407
00408 } else {
00409
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
00425
00426
00427
00428
00429 void PipeOcclusionCullTraverser::
00430 make_sphere() {
00431 ConfigVariableInt num_slices("num-slices", 16);
00432 ConfigVariableInt num_stacks("num-stacks", 8);
00433
00434
00435
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
00463
00464
00465
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
00481
00482
00483
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
00532
00533
00534
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
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560 bool PipeOcclusionCullTraverser::
00561 get_volume_viz(const BoundingVolume *vol,
00562 CPT(Geom) &geom,
00563 CPT(TransformState) &net_transform,
00564 CPT(TransformState) &modelview_transform
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
00581
00582
00583
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
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
00605
00606
00607
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
00627 geom = _box_geom;
00628 return true;
00629 }
00630
00631
00632 return false;
00633 }
00634
00635
00636
00637
00638
00639
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
00666
00667
00668
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
00678
00679
00680
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
00689 color.set(0.8f, 0.0f, 1.0f, 0.4);
00690 } else {
00691
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
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 }