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