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)) {
333 if (node_reader->get_nested_vertices() < min_occlusion_vertices) {
339 node_reader->is_final() ||
341 node_reader->get_nested_vertices() <= max_occlusion_vertices) {
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));
462 strip->add_next_vertices(num_stacks * 2);
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);
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);
545 void PipeOcclusionCullTraverser::
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 =
588 _internal_trav->get_world_transform()->compose(net_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(),
614 net_transform = net_transform->compose(local_transform);
616 CPT(TransformState) modelview_transform =
617 _internal_trav->get_world_transform()->compose(net_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);
714 (DepthWriteAttrib::make(DepthWriteAttrib::M_off),
715 DepthTestAttrib::make(DepthTestAttrib::M_less),
716 TransparencyAttrib::make(TransparencyAttrib::M_alpha),
717 ColorAttrib::make_flat(color));
723 _internal_cull_handler->
record_object(internal_viz, _internal_trav);
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.
CullHandler * get_cull_handler() const
Returns the object that will receive the culled Geoms.
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.
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
virtual GraphicsOutput * get_host()
This is normally called only from within make_texture_buffer().
virtual void end_traverse()
Should be called when the traverser has finished traversing its scene, this gives it a chance to do a...
const RenderEffects * get_effects() const
Returns the complete RenderEffects that will be applied to this node.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
GraphicsPipe * get_pipe() const
Returns the graphics pipe on which this GSG was created.
bool get_supports_occlusion_query() const
Returns true if this GSG supports an occlusion query.
virtual void traverse_below(CullTraverserData &data)
Traverses all the children of the indicated node, with the given data, which has been converted into ...
bool is_empty() const
Any kind of volume might be empty.
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Makes the specified DisplayRegion current.
Thread * get_current_thread() const
Returns the currently-executing thread object, as passed to the CullTraverser constructor.
This is a convenience class to specialize ConfigVariable as a boolean type.
This defines a bounding sphere, consisting of a center and a radius.
bool is_infinite() const
The other side of the empty coin is an infinite volume.
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 ...
void set_size(const LVector2i &size)
Specifies the requested size of the window, in pixels.
Defines a series of triangle strips.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
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...
This specialization of CullTraverser uses the graphics pipe itself to perform occlusion culling...
const TransformState * get_cs_world_transform() const
Returns the position from the starting node relative to the camera, in the GSG's internal coordinate ...
const string & get_draw_name() const
Returns the name of the thread that will handle sending the actual graphics primitives to the graphic...
PandaNode * node() const
Returns the node traversed to so far.
GraphicsEngine * get_engine() const
Returns the graphics engine that created this GSG.
const TransformState * get_cs_transform() const
Returns the transform from the camera's coordinate system to the GSG's internal coordinate system...
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
const string & get_cull_name() const
Returns the name of the thread that will handle culling in this model.
A lightweight class that represents a single element that may be timed and/or counted via stats...
bool has_show_bounds() const
This function is provided as an optimization, to speed up the render-time checking for the existance ...
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.
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.
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.
const LPoint3 & get_maxq() const
An inline accessor for the maximum value.
static GraphicsStateGuardianBase * get_gsg(int n)
Returns the nth GSG in the universe.
A container for geometry primitives.
virtual void begin_occlusion_query()
Begins a new occlusion query.
void add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row. ...
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...
This is the base class for all three-component vectors and points.
int get_num_words() const
Returns the number of words in the variable's value.
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...
GraphicsStateGuardian * get_gsg() const
Returns the GSG that is associated with this window.
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 ...
Encapsulates the data from a PandaNode, pre-fetched for one stage of the pipeline.
SceneSetup * get_scene() const
Returns the SceneSetup object.
Encapsulates all the communication with a particular instance of a given rendering backend...
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.
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...
Returned from a GSG in response to begin_occlusion_query() .
This class is the main interface to controlling the render process.
TypeHandle is the identifier used to differentiate C++ class types.
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
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.
virtual bool is_geom_node() const
A simple downcast check.
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.
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" ...
const LPoint3 & get_minq() const
An inline accessor for the minimum value.
void set_cull_handler(CullHandler *cull_handler)
Specifies the object that will receive the culled Geoms.