15 #include "pipeOcclusionCullTraverser.h"
16 #include "graphicsEngine.h"
17 #include "graphicsPipe.h"
18 #include "drawCullHandler.h"
19 #include "boundingSphere.h"
20 #include "boundingBox.h"
21 #include "geomVertexWriter.h"
22 #include "geomTristrips.h"
23 #include "geomTriangles.h"
24 #include "pStatTimer.h"
25 #include "depthWriteAttrib.h"
26 #include "depthTestAttrib.h"
27 #include "colorWriteAttrib.h"
28 #include "colorAttrib.h"
29 #include "cullBinManager.h"
30 #include "configVariableInt.h"
31 #include "config_grutil.h"
34 PStatCollector PipeOcclusionCullTraverser::_setup_occlusion_pcollector(
"Cull:Occlusion:Setup");
35 PStatCollector PipeOcclusionCullTraverser::_draw_occlusion_pcollector(
"Cull:Occlusion:Occluders");
36 PStatCollector PipeOcclusionCullTraverser::_test_occlusion_pcollector(
"Cull:Occlusion:Test");
37 PStatCollector PipeOcclusionCullTraverser::_finish_occlusion_pcollector(
"Cull:Occlusion:Finish");
39 PStatCollector PipeOcclusionCullTraverser::_occlusion_untested_pcollector(
"Occlusion results:Not tested");
40 PStatCollector PipeOcclusionCullTraverser::_occlusion_passed_pcollector(
"Occlusion results:Visible");
41 PStatCollector PipeOcclusionCullTraverser::_occlusion_failed_pcollector(
"Occlusion results:Occluded");
42 PStatCollector PipeOcclusionCullTraverser::_occlusion_tests_pcollector(
"Occlusion tests");
44 TypeHandle PipeOcclusionCullTraverser::_type_handle;
47 (
"min-occlusion-vertices", 300,
48 PRC_DESC(
"The minimum number of vertices a PandaNode or Geom must contain "
49 "in order to perform an occlusion query for it. Nodes and Geoms "
50 "smaller than this will be rendered directly, without bothering "
51 "with an occlusion query."));
54 (
"max-occlusion-vertices", 3000,
55 PRC_DESC(
"The maximum number of vertices that may be included in a PandaNode "
56 "and its descendents in order to perform an occlusion query for "
57 "it. Subgraphs whose total vertex count exceeds this number will "
58 "be subdivided further before performing an occlusion test--the "
59 "hope is that we can eventually get to a finer-grained answer. "
60 "GeomNodes and Geoms will not be subdivided, regardless of this "
64 (
"show-occlusion",
false,
65 PRC_DESC(
"Set this true to visualize the efforts of the occlusion test."));
68 (
"occlusion-size",
"256 256",
69 PRC_DESC(
"Specify the x y size of the buffer used for occlusion testing."));
72 (
"occlusion-depth-bits", 1,
73 PRC_DESC(
"The minimum number of depth bits requested for the occlusion "
81 PipeOcclusionCullTraverser::
90 <<
"Occlusion queries are not supported by graphics pipe.\n";
98 fb_prop.set_depth_bits(occlusion_depth_bits);
101 win_prop.
set_size(occlusion_size, occlusion_size);
103 win_prop.
set_size(occlusion_size[0], occlusion_size[1]);
106 _buffer = engine->
make_output(pipe,
"occlusion", 0, fb_prop, win_prop,
107 GraphicsPipe::BF_refuse_window,
113 _buffer->set_active(0);
115 _display_region = _buffer->make_display_region();
116 _internal_cull_handler = NULL;
120 make_solid_test_state();
130 PipeOcclusionCullTraverser::
144 bool dr_incomplete_render) {
150 PStatTimer timer(_setup_occlusion_pcollector);
153 nassertv(gsg == gsgbase);
156 if (!_buffer->begin_frame(GraphicsOutput::FM_render, current_thread)) {
157 grutil_cat.error() <<
"begin_frame failed\n";
160 _buffer->clear(current_thread);
164 _buffer->change_scenes(&dr_reader);
168 _scene->set_display_region(_display_region);
169 _scene->set_viewport_size(_display_region->get_pixel_width(),
170 _display_region->get_pixel_height());
172 if (_scene->get_cull_center() != _scene->get_camera_path()) {
176 NodePath cull_center = _scene->get_cull_center();
178 CPT(TransformState) camera_transform = cull_center.
get_transform(scene_parent, current_thread);
179 CPT(TransformState) world_transform = scene_parent.
get_transform(cull_center, current_thread);
180 CPT(TransformState) cs_world_transform = _scene->get_cs_transform()->compose(world_transform);
181 _scene->set_camera_transform(camera_transform);
182 _scene->set_world_transform(world_transform);
183 _scene->set_cs_world_transform(cs_world_transform);
186 _inv_cs_world_transform = cs_world_transform->get_inverse();
188 _inv_cs_world_transform = _scene->get_cs_world_transform()->get_inverse();
195 grutil_cat.error() <<
"begin_scene failed\n";
208 _internal_trav->set_scene(_scene, gsg, dr_incomplete_render);
210 _internal_trav->set_camera_mask(_occlusion_mask);
212 _current_query = NULL;
216 PStatTimer timer2(_draw_occlusion_pcollector);
217 _internal_trav->traverse(_scene->get_scene_root());
233 PStatTimer timer(_finish_occlusion_pcollector);
237 _current_query = NULL;
240 PendingObjects::iterator oi;
241 for (oi = _pending_objects.begin(); oi != _pending_objects.end(); ++oi) {
242 PendingObject &pobj = (*oi);
244 _occlusion_untested_pcollector.add_level(1);
247 int num_fragments = pobj._query->get_num_fragments();
248 if (num_fragments != 0) {
249 _occlusion_passed_pcollector.add_level(1);
252 _occlusion_failed_pcollector.add_level(1);
263 _pending_objects.clear();
267 _buffer->end_frame(GraphicsOutput::FM_render, current_thread);
269 _buffer->begin_flip();
272 delete _internal_cull_handler;
273 _internal_cull_handler = NULL;
275 _occlusion_untested_pcollector.flush_level();
276 _occlusion_passed_pcollector.flush_level();
277 _occlusion_failed_pcollector.flush_level();
278 _occlusion_tests_pcollector.flush_level();
289 if (_texture != (
Texture *)NULL) {
293 _texture =
new Texture(
"occlusion");
298 _texture->load(image);
302 _buffer->add_render_texture(_texture, GraphicsOutput::RTM_bind_or_copy);
313 bool PipeOcclusionCullTraverser::
317 if (!CullTraverser::is_in_view(data)) {
346 CPT(TransformState) net_transform = data.get_net_transform(this);
347 CPT(TransformState) internal_transform;
350 if (get_volume_viz(vol, geom, net_transform, internal_transform)) {
352 perform_occlusion_test(geom, net_transform, internal_transform);
366 void PipeOcclusionCullTraverser::
372 _current_query = _next_query;
378 _current_query = prev_query;
397 void PipeOcclusionCullTraverser::
399 nassertv(traverser ==
this);
400 PendingObject pobj(
object);
407 pobj._query = _next_query;
412 pobj._query = _current_query;
414 }
else if (object->_geom->get_nested_vertices(current_thread) < min_occlusion_vertices) {
419 CPT(
BoundingVolume) vol =
object->_geom->get_bounds(current_thread);
420 CPT(TransformState) net_transform = _inv_cs_world_transform->compose(
object->_internal_transform);
421 CPT(TransformState) internal_transform;
423 if (get_volume_viz(vol, geom, net_transform, internal_transform)) {
425 perform_occlusion_test(geom, net_transform, internal_transform);
429 _pending_objects.push_back(pobj);
438 void PipeOcclusionCullTraverser::
447 ("occlusion_sphere", GeomVertexFormat::get_v3(),
Geom::UH_static);
451 for (
int sl = 0; sl < num_slices; ++sl) {
452 PN_stdfloat longitude0 = (PN_stdfloat)sl / (PN_stdfloat)num_slices;
453 PN_stdfloat longitude1 = (PN_stdfloat)(sl + 1) / (PN_stdfloat)num_slices;
454 vertex.add_data3(compute_sphere_point(0.0, longitude0));
455 for (
int st = 1; st < num_stacks; ++st) {
456 PN_stdfloat latitude = (PN_stdfloat)st / (PN_stdfloat)num_stacks;
457 vertex.add_data3(compute_sphere_point(latitude, longitude0));
458 vertex.add_data3(compute_sphere_point(latitude, longitude1));
460 vertex.add_data3(compute_sphere_point(1.0, longitude0));
463 strip->close_primitive();
466 _sphere_geom =
new Geom(vdata);
467 _sphere_geom->add_primitive(strip);
476 LVertex PipeOcclusionCullTraverser::
477 compute_sphere_point(PN_stdfloat latitude, PN_stdfloat longitude) {
479 csincos(latitude * MathNumbers::pi, &s1, &c1);
482 csincos(longitude * 2.0f * MathNumbers::pi, &s2, &c2);
484 LVertex p(s1 * c2, s1 * s2, c1);
494 void PipeOcclusionCullTraverser::
497 ("occlusion_box", GeomVertexFormat::get_v3(),
Geom::UH_static);
500 vertex.add_data3(0.0f, 0.0f, 0.0f);
501 vertex.add_data3(0.0f, 0.0f, 1.0f);
502 vertex.add_data3(0.0f, 1.0f, 0.0f);
503 vertex.add_data3(0.0f, 1.0f, 1.0f);
504 vertex.add_data3(1.0f, 0.0f, 0.0f);
505 vertex.add_data3(1.0f, 0.0f, 1.0f);
506 vertex.add_data3(1.0f, 1.0f, 0.0f);
507 vertex.add_data3(1.0f, 1.0f, 1.0f);
510 tris->add_vertices(0, 4, 5);
511 tris->close_primitive();
512 tris->add_vertices(0, 5, 1);
513 tris->close_primitive();
514 tris->add_vertices(4, 6, 7);
515 tris->close_primitive();
516 tris->add_vertices(4, 7, 5);
517 tris->close_primitive();
518 tris->add_vertices(6, 2, 3);
519 tris->close_primitive();
520 tris->add_vertices(6, 3, 7);
521 tris->close_primitive();
522 tris->add_vertices(2, 0, 1);
523 tris->close_primitive();
524 tris->add_vertices(2, 1, 3);
525 tris->close_primitive();
526 tris->add_vertices(1, 5, 7);
527 tris->close_primitive();
528 tris->add_vertices(1, 7, 3);
529 tris->close_primitive();
530 tris->add_vertices(2, 6, 4);
531 tris->close_primitive();
532 tris->add_vertices(2, 4, 0);
533 tris->close_primitive();
535 _box_geom = new
Geom(vdata);
536 _box_geom->add_primitive(tris);
546 make_solid_test_state() {
547 _solid_test_state = RenderState::make
548 (DepthWriteAttrib::make(DepthWriteAttrib::M_off),
549 DepthTestAttrib::make(DepthTestAttrib::M_less),
550 ColorWriteAttrib::make(ColorWriteAttrib::C_off));
569 bool PipeOcclusionCullTraverser::
572 CPT(TransformState) &net_transform,
573 CPT(TransformState) &internal_transform
581 CPT(TransformState) local_transform =
582 TransformState::make_pos_hpr_scale(sphere->get_center(),
584 sphere->get_radius());
585 net_transform = net_transform->compose(local_transform);
587 CPT(TransformState) modelview_transform =
594 const
LPoint3 ¢er = modelview_transform->get_pos();
595 const LVecBase3 &radius = modelview_transform->get_scale();
596 if (center[1] - radius[1] < 0.0f) {
601 internal_transform = _internal_trav->get_scene()->
602 get_cs_transform()->compose(modelview_transform);
608 }
else if (vol->
is_exact_type(BoundingBox::get_class_type())) {
610 CPT(TransformState) local_transform =
611 TransformState::make_pos_hpr_scale(box->get_minq(),
613 box->get_maxq() - box->get_minq());
614 net_transform = net_transform->compose(local_transform);
616 CPT(TransformState) modelview_transform =
623 static const
LPoint3 points[8] = {
633 const LMatrix4 &mat = modelview_transform->get_mat();
634 for (
int i = 0; i < 8; ++i) {
642 internal_transform = _internal_trav->get_scene()->
643 get_cs_transform()->compose(modelview_transform);
661 perform_occlusion_test(const
Geom *geom, const TransformState *net_transform,
662 const TransformState *internal_transform) {
663 _occlusion_tests_pcollector.add_level(1);
682 if (show_occlusion) {
687 int num_fragments = query->get_num_fragments();
688 show_results(num_fragments, geom, net_transform, internal_transform);
700 void PipeOcclusionCullTraverser::
701 show_results(
int num_fragments,
const Geom *geom,
702 const TransformState *net_transform,
703 const TransformState *internal_transform) {
705 if (num_fragments == 0) {
707 color.set(0.8f, 0.0f, 1.0f, 0.4f);
710 color.set(1.0f, 1.0f, 0.5f, 0.4f);
723 _internal_cull_handler->record_object(internal_viz, _internal_trav);
726 internal_transform =
get_scene()->get_cs_world_transform()->compose(net_transform);
729 _true_cull_handler->record_object(main_viz, this);
virtual void set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsg, bool dr_incomplete_render)
Sets the SceneSetup object that indicates the initial camera position, etc.
A basic node of the scene graph or data graph.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
An axis-aligned bounding box; that is, a minimum and maximum coordinate triple.
Encapsulates the data from a DisplayRegion, pre-fetched for one stage of the pipeline.
This is the base class for all three-component vectors and points.
virtual GraphicsOutput * get_host()
This is normally called only from within make_texture_buffer().
int get_num_words() const
Returns the number of words in the variable's value.
Enables or disables writing to the depth buffer.
virtual void end_traverse()
Should be called when the traverser has finished traversing its scene, this gives it a chance to do a...
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
bool has_show_bounds() const
This function is provided as an optimization, to speed up the render-time checking for the existance ...
virtual void traverse_below(CullTraverserData &data)
Traverses all the children of the indicated node, with the given data, which has been converted into ...
NodePath get_parent(Thread *current_thread=Thread::get_current_thread()) const
Returns the NodePath to the parent of the referenced node: that is, this NodePath, shortened by one node.
Enables or disables writing to the depth buffer.
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Makes the specified DisplayRegion current.
This is a convenience class to specialize ConfigVariable as a boolean type.
This controls the enabling of transparency.
This defines a bounding sphere, consisting of a center and a radius.
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
This collects together the pieces of data that are accumulated for each node while walking the scene ...
GraphicsPipe * get_pipe() const
Returns the graphics pipe on which this GSG was created.
void set_size(const LVector2i &size)
Specifies the requested size of the window, in pixels.
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Defines a series of triangle strips.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
PandaNode * node() const
Returns the node traversed to so far.
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
virtual void end_traverse()
Should be called when the traverser has finished traversing its scene, this gives it a chance to do a...
bool is_final() const
Returns the current state of the "final" flag.
This specialization of CullTraverser uses the graphics pipe itself to perform occlusion culling...
SceneSetup * get_scene() const
Returns the SceneSetup object.
GraphicsStateGuardianBase * get_gsg() const
Returns the GraphicsStateGuardian in effect.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
A lightweight class that represents a single element that may be timed and/or counted via stats...
A container for the various kinds of properties we might ask to have on a graphics window before we o...
The smallest atom of cull.
const string & get_cull_name() const
Returns the name of the thread that will handle culling in this model.
This is a 4-by-4 transform matrix.
Texture * get_texture()
Returns a Texture that can be used to visualize the efforts of the occlusion cull.
virtual void record_object(CullableObject *object, const CullTraverser *traverser)
This callback function is intended to be overridden by a derived class.
CullHandler * get_cull_handler() const
Returns the object that will receive the culled Geoms.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
An object to create GraphicsOutputs that share a particular 3-D API.
static GraphicsStateGuardianBase * get_gsg(int n)
Returns the nth GSG in the universe.
A container for geometry primitives.
const string & get_draw_name() const
Returns the name of the thread that will handle sending the actual graphics primitives to the graphic...
virtual void begin_occlusion_query()
Begins a new occlusion query.
GraphicsStateGuardian * get_gsg() const
Returns the GSG that is associated with this window.
This is a base class for the various different classes that represent the result of a frame of render...
GraphicsOutput * make_output(GraphicsPipe *pipe, const string &name, int sort, const FrameBufferProperties &fb_prop, const WindowProperties &win_prop, int flags, GraphicsStateGuardian *gsg=NULL, GraphicsOutput *host=NULL)
Creates a new window (or buffer) and returns it.
This represents the user's specification of how a particular frame is handled by the various threads...
PandaNodePipelineReader * node_reader()
Returns the PipelineReader for the node traversed to so far.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Thread * get_current_thread() const
Returns the currently-executing thread object, as passed to the CullTraverser constructor.
void add_next_vertices(int num_vertices)
Adds the next n vertices in sequence, beginning from the last vertex added to the primitive + 1...
This is the base class for all three-component vectors and points.
const RenderEffects * get_effects() const
Returns the complete RenderEffects that will be applied to this node.
bool is_infinite() const
The other side of the empty coin is an infinite volume.
This special kind of CullHandler immediately draws its contents as soon as it receives them...
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
int get_nested_vertices() const
Returns the total number of vertices that will be rendered by this node and all of its descendents...
bool get_supports_occlusion_query() const
Returns true if this GSG supports an occlusion query.
A thread; that is, a lightweight process.
GeometricBoundingVolume * get_view_frustum() const
Returns the bounding volume that corresponds to the view frustum, or NULL if the view frustum is not ...
const TransformState * get_world_transform() const
Returns the position of the starting node relative to the camera.
Encapsulates the data from a PandaNode, pre-fetched for one stage of the pipeline.
Encapsulates all the communication with a particular instance of a given rendering backend...
This is a convenience class to specialize ConfigVariable as an integer type.
Defines a series of disconnected triangles.
virtual void end_scene()
Called between begin_frame() and end_frame() to mark the end of drawing commands for a "scene" (usual...
const TransformState * get_cs_transform() const
Returns the transform from the camera's coordinate system to the GSG's internal coordinate system...
Indicates what color should be applied to renderable geometry.
const GraphicsThreadingModel & get_threading_model() const
Returns the threading model that was used to create this GSG.
GraphicsEngine * get_engine() const
Returns the graphics engine that created this GSG.
Returned from a GSG in response to begin_occlusion_query() .
This class is the main interface to controlling the render process.
virtual bool is_geom_node() const
A simple downcast check.
bool is_empty() const
Any kind of volume might be empty.
TypeHandle is the identifier used to differentiate C++ class types.
This object holds the camera position, etc., and other general setup information for rendering a part...
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
bool set_scene(SceneSetup *scene_setup)
Sets the SceneSetup object that indicates the initial camera position, etc.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
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 bool begin_scene()
Called between begin_frame() and end_frame() to mark the beginning of drawing commands for a "scene" ...
void set_cull_handler(CullHandler *cull_handler)
Specifies the object that will receive the culled Geoms.