Panda3D
|
00001 // Filename: cullTraverser.cxx 00002 // Created by: drose (23Feb02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "config_pgraph.h" 00016 #include "cullTraverser.h" 00017 #include "cullTraverserData.h" 00018 #include "transformState.h" 00019 #include "renderState.h" 00020 #include "fogAttrib.h" 00021 #include "colorAttrib.h" 00022 #include "renderModeAttrib.h" 00023 #include "cullFaceAttrib.h" 00024 #include "depthOffsetAttrib.h" 00025 #include "cullHandler.h" 00026 #include "dcast.h" 00027 #include "geomNode.h" 00028 #include "config_pgraph.h" 00029 #include "boundingSphere.h" 00030 #include "boundingBox.h" 00031 #include "boundingHexahedron.h" 00032 #include "portalClipper.h" 00033 #include "geom.h" 00034 #include "geomTristrips.h" 00035 #include "geomTriangles.h" 00036 #include "geomLinestrips.h" 00037 #include "geomVertexWriter.h" 00038 00039 PStatCollector CullTraverser::_nodes_pcollector("Nodes"); 00040 PStatCollector CullTraverser::_geom_nodes_pcollector("Nodes:GeomNodes"); 00041 PStatCollector CullTraverser::_geoms_pcollector("Geoms"); 00042 PStatCollector CullTraverser::_geoms_occluded_pcollector("Geoms:Occluded"); 00043 00044 TypeHandle CullTraverser::_type_handle; 00045 00046 //////////////////////////////////////////////////////////////////// 00047 // Function: CullTraverser::Constructor 00048 // Access: Published 00049 // Description: 00050 //////////////////////////////////////////////////////////////////// 00051 CullTraverser:: 00052 CullTraverser() : 00053 _gsg(NULL), 00054 _current_thread(Thread::get_current_thread()) 00055 { 00056 _camera_mask = DrawMask::all_on(); 00057 _has_tag_state_key = false; 00058 _initial_state = RenderState::make_empty(); 00059 _cull_handler = (CullHandler *)NULL; 00060 _portal_clipper = (PortalClipper *)NULL; 00061 _effective_incomplete_render = true; 00062 } 00063 00064 //////////////////////////////////////////////////////////////////// 00065 // Function: CullTraverser::Copy Constructor 00066 // Access: Published 00067 // Description: 00068 //////////////////////////////////////////////////////////////////// 00069 CullTraverser:: 00070 CullTraverser(const CullTraverser ©) : 00071 _gsg(copy._gsg), 00072 _current_thread(copy._current_thread), 00073 _scene_setup(copy._scene_setup), 00074 _camera_mask(copy._camera_mask), 00075 _has_tag_state_key(copy._has_tag_state_key), 00076 _tag_state_key(copy._tag_state_key), 00077 _initial_state(copy._initial_state), 00078 _depth_offset_decals(copy._depth_offset_decals), 00079 _view_frustum(copy._view_frustum), 00080 _cull_handler(copy._cull_handler), 00081 _portal_clipper(copy._portal_clipper), 00082 _effective_incomplete_render(copy._effective_incomplete_render) 00083 { 00084 } 00085 00086 //////////////////////////////////////////////////////////////////// 00087 // Function: CullTraverser::set_scene 00088 // Access: Published, Virtual 00089 // Description: Sets the SceneSetup object that indicates the initial 00090 // camera position, etc. This must be called before 00091 // traversal begins. 00092 //////////////////////////////////////////////////////////////////// 00093 void CullTraverser:: 00094 set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsg, 00095 bool dr_incomplete_render) { 00096 _scene_setup = scene_setup; 00097 _gsg = gsg; 00098 00099 _initial_state = scene_setup->get_initial_state(); 00100 _depth_offset_decals = _gsg->depth_offset_decals() && depth_offset_decals; 00101 00102 _current_thread = Thread::get_current_thread(); 00103 00104 const Camera *camera = scene_setup->get_camera_node(); 00105 _tag_state_key = camera->get_tag_state_key(); 00106 _has_tag_state_key = !_tag_state_key.empty(); 00107 _camera_mask = camera->get_camera_mask(); 00108 00109 _effective_incomplete_render = _gsg->get_incomplete_render() && dr_incomplete_render; 00110 } 00111 00112 //////////////////////////////////////////////////////////////////// 00113 // Function: CullTraverser::traverse 00114 // Access: Published 00115 // Description: Begins the traversal from the indicated node. 00116 //////////////////////////////////////////////////////////////////// 00117 void CullTraverser:: 00118 traverse(const NodePath &root) { 00119 nassertv(_cull_handler != (CullHandler *)NULL); 00120 nassertv(_scene_setup != (SceneSetup *)NULL); 00121 00122 if (allow_portal_cull) { 00123 // This _view_frustum is in cull_center space 00124 //Erik: obsolete? 00125 //PT(GeometricBoundingVolume) vf = _view_frustum; 00126 00127 GeometricBoundingVolume *local_frustum = NULL; 00128 PT(BoundingVolume) bv = _scene_setup->get_lens()->make_bounds(); 00129 if (bv != (BoundingVolume *)NULL && 00130 bv->is_of_type(GeometricBoundingVolume::get_class_type())) { 00131 00132 local_frustum = DCAST(GeometricBoundingVolume, bv); 00133 } 00134 00135 // This local_frustum is in camera space 00136 PortalClipper portal_viewer(local_frustum, _scene_setup); 00137 if (debug_portal_cull) { 00138 portal_viewer.draw_camera_frustum(); 00139 } 00140 00141 // Store this pointer in this 00142 set_portal_clipper(&portal_viewer); 00143 00144 CullTraverserData data(root, TransformState::make_identity(), 00145 _initial_state, _view_frustum, 00146 _current_thread); 00147 00148 traverse(data); 00149 00150 // Finally add the lines to be drawn 00151 if (debug_portal_cull) { 00152 portal_viewer.draw_lines(); 00153 } 00154 00155 // Render the frustum relative to the cull center. 00156 NodePath cull_center = _scene_setup->get_cull_center(); 00157 CPT(TransformState) transform = cull_center.get_transform(root); 00158 00159 CullTraverserData my_data(data, portal_viewer._previous); 00160 my_data._net_transform = my_data._net_transform->compose(transform); 00161 traverse(my_data); 00162 00163 } else { 00164 CullTraverserData data(root, TransformState::make_identity(), 00165 _initial_state, _view_frustum, 00166 _current_thread); 00167 00168 traverse(data); 00169 } 00170 } 00171 00172 //////////////////////////////////////////////////////////////////// 00173 // Function: CullTraverser::traverse 00174 // Access: Published 00175 // Description: Traverses from the next node with the given 00176 // data, which has been constructed with the node but 00177 // has not yet been converted into the node's space. 00178 //////////////////////////////////////////////////////////////////// 00179 void CullTraverser:: 00180 traverse(CullTraverserData &data) { 00181 if (is_in_view(data)) { 00182 if (pgraph_cat.is_spam()) { 00183 pgraph_cat.spam() 00184 << "\n" << data._node_path 00185 << " " << data._draw_mask << "\n"; 00186 } 00187 00188 PandaNodePipelineReader *node_reader = data.node_reader(); 00189 int fancy_bits = node_reader->get_fancy_bits(); 00190 00191 if ((fancy_bits & (PandaNode::FB_transform | 00192 PandaNode::FB_state | 00193 PandaNode::FB_effects | 00194 PandaNode::FB_tag | 00195 PandaNode::FB_draw_mask | 00196 PandaNode::FB_cull_callback)) == 0 && 00197 data._cull_planes->is_empty()) { 00198 // Nothing interesting in this node; just move on. 00199 traverse_below(data); 00200 00201 } else { 00202 // Something in this node is worth taking a closer look. 00203 const RenderEffects *node_effects = node_reader->get_effects(); 00204 if (node_effects->has_show_bounds()) { 00205 // If we should show the bounding volume for this node, make it 00206 // up now. 00207 show_bounds(data, node_effects->has_show_tight_bounds()); 00208 } 00209 00210 data.apply_transform_and_state(this); 00211 00212 const FogAttrib *fog = DCAST(FogAttrib, node_reader->get_state()->get_attrib(FogAttrib::get_class_slot())); 00213 if (fog != (const FogAttrib *)NULL && fog->get_fog() != (Fog *)NULL) { 00214 // If we just introduced a FogAttrib here, call adjust_to_camera() 00215 // now. This maybe isn't the perfect time to call it, but it's 00216 // good enough; and at this time we have all the information we 00217 // need for it. 00218 fog->get_fog()->adjust_to_camera(get_camera_transform()); 00219 } 00220 00221 if (fancy_bits & PandaNode::FB_cull_callback) { 00222 PandaNode *node = data.node(); 00223 if (!node->cull_callback(this, data)) { 00224 return; 00225 } 00226 } 00227 00228 traverse_below(data); 00229 } 00230 } 00231 } 00232 00233 //////////////////////////////////////////////////////////////////// 00234 // Function: CullTraverser::traverse_below 00235 // Access: Published, Virtual 00236 // Description: Traverses all the children of the indicated node, 00237 // with the given data, which has been converted into 00238 // the node's space. 00239 //////////////////////////////////////////////////////////////////// 00240 void CullTraverser:: 00241 traverse_below(CullTraverserData &data) { 00242 _nodes_pcollector.add_level(1); 00243 PandaNodePipelineReader *node_reader = data.node_reader(); 00244 PandaNode *node = data.node(); 00245 00246 bool this_node_hidden = data.is_this_node_hidden(this); 00247 00248 const RenderEffects *node_effects = node_reader->get_effects(); 00249 bool has_decal = !this_node_hidden && node_effects->has_decal(); 00250 if (has_decal && !_depth_offset_decals) { 00251 // Start the three-pass decal rendering if we're not using 00252 // DepthOffsetAttribs to implement decals. 00253 start_decal(data); 00254 00255 } else { 00256 if (!this_node_hidden) { 00257 node->add_for_draw(this, data); 00258 } 00259 00260 if (has_decal) { 00261 // If we *are* implementing decals with DepthOffsetAttribs, 00262 // apply it now, so that each child of this node gets offset by 00263 // a tiny amount. 00264 data._state = data._state->compose(get_depth_offset_state()); 00265 #ifndef NDEBUG 00266 // This is just a sanity check message. 00267 if (!node->is_geom_node()) { 00268 pgraph_cat.error() 00269 << "DecalEffect applied to " << *node << ", not a GeomNode.\n"; 00270 } 00271 #endif 00272 } 00273 00274 // Now visit all the node's children. 00275 PandaNode::Children children = node_reader->get_children(); 00276 node_reader->release(); 00277 int num_children = children.get_num_children(); 00278 if (node->has_selective_visibility()) { 00279 int i = node->get_first_visible_child(); 00280 while (i < num_children) { 00281 CullTraverserData next_data(data, children.get_child(i)); 00282 traverse(next_data); 00283 i = node->get_next_visible_child(i); 00284 } 00285 00286 } else { 00287 for (int i = 0; i < num_children; i++) { 00288 CullTraverserData next_data(data, children.get_child(i)); 00289 traverse(next_data); 00290 } 00291 } 00292 } 00293 } 00294 00295 //////////////////////////////////////////////////////////////////// 00296 // Function: CullTraverser::end_traverse 00297 // Access: Published, Virtual 00298 // Description: Should be called when the traverser has finished 00299 // traversing its scene, this gives it a chance to do 00300 // any necessary finalization. 00301 //////////////////////////////////////////////////////////////////// 00302 void CullTraverser:: 00303 end_traverse() { 00304 _cull_handler->end_traverse(); 00305 } 00306 00307 //////////////////////////////////////////////////////////////////// 00308 // Function: CullTraverser::draw_bounding_volume 00309 // Access: Published 00310 // Description: Draws an appropriate visualization of the indicated 00311 // bounding volume. 00312 //////////////////////////////////////////////////////////////////// 00313 void CullTraverser:: 00314 draw_bounding_volume(const BoundingVolume *vol, 00315 const TransformState *net_transform, 00316 const TransformState *modelview_transform) { 00317 PT(Geom) bounds_viz = make_bounds_viz(vol); 00318 00319 if (bounds_viz != (Geom *)NULL) { 00320 _geoms_pcollector.add_level(2); 00321 CullableObject *outer_viz = 00322 new CullableObject(bounds_viz, get_bounds_outer_viz_state(), 00323 net_transform, modelview_transform, get_gsg()); 00324 _cull_handler->record_object(outer_viz, this); 00325 00326 CullableObject *inner_viz = 00327 new CullableObject(bounds_viz, get_bounds_inner_viz_state(), 00328 net_transform, modelview_transform, get_gsg()); 00329 _cull_handler->record_object(inner_viz, this); 00330 } 00331 } 00332 00333 //////////////////////////////////////////////////////////////////// 00334 // Function: CullTraverser::is_in_view 00335 // Access: Protected, Virtual 00336 // Description: Returns true if the current node is fully or 00337 // partially within the viewing area and should be 00338 // drawn, or false if it (and all of its children) 00339 // should be pruned. 00340 //////////////////////////////////////////////////////////////////// 00341 bool CullTraverser:: 00342 is_in_view(CullTraverserData &data) { 00343 return data.is_in_view(_camera_mask); 00344 } 00345 00346 //////////////////////////////////////////////////////////////////// 00347 // Function: CullTraverser::show_bounds 00348 // Access: Private 00349 // Description: Draws an appropriate visualization of the node's 00350 // external bounding volume. 00351 //////////////////////////////////////////////////////////////////// 00352 void CullTraverser:: 00353 show_bounds(CullTraverserData &data, bool tight) { 00354 PandaNode *node = data.node(); 00355 CPT(TransformState) net_transform = data.get_net_transform(this); 00356 CPT(TransformState) modelview_transform = data.get_modelview_transform(this); 00357 00358 if (tight) { 00359 PT(Geom) bounds_viz = make_tight_bounds_viz(node); 00360 00361 if (bounds_viz != (Geom *)NULL) { 00362 _geoms_pcollector.add_level(1); 00363 CullableObject *outer_viz = 00364 new CullableObject(bounds_viz, get_bounds_outer_viz_state(), 00365 net_transform, modelview_transform, 00366 get_gsg()); 00367 _cull_handler->record_object(outer_viz, this); 00368 } 00369 00370 } else { 00371 draw_bounding_volume(node->get_bounds(), 00372 net_transform, modelview_transform); 00373 00374 if (node->is_geom_node()) { 00375 // Also show the bounding volumes of included Geoms. 00376 net_transform = net_transform->compose(node->get_transform()); 00377 modelview_transform = modelview_transform->compose(node->get_transform()); 00378 GeomNode *gnode = DCAST(GeomNode, node); 00379 int num_geoms = gnode->get_num_geoms(); 00380 for (int i = 0; i < num_geoms; ++i) { 00381 draw_bounding_volume(gnode->get_geom(i)->get_bounds(), 00382 net_transform, modelview_transform); 00383 } 00384 } 00385 } 00386 } 00387 00388 //////////////////////////////////////////////////////////////////// 00389 // Function: CullTraverser::make_bounds_viz 00390 // Access: Private 00391 // Description: Returns an appropriate visualization of the indicated 00392 // bounding volume. 00393 //////////////////////////////////////////////////////////////////// 00394 PT(Geom) CullTraverser:: 00395 make_bounds_viz(const BoundingVolume *vol) { 00396 PT(Geom) geom; 00397 if (vol->is_infinite() || vol->is_empty()) { 00398 // No way to draw an infinite or empty bounding volume. 00399 00400 } else if (vol->is_of_type(BoundingSphere::get_class_type())) { 00401 const BoundingSphere *sphere = DCAST(BoundingSphere, vol); 00402 00403 static const int num_slices = 16; 00404 static const int num_stacks = 8; 00405 00406 PT(GeomVertexData) vdata = new GeomVertexData 00407 ("bounds", GeomVertexFormat::get_v3(), 00408 Geom::UH_stream); 00409 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00410 00411 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); 00412 for (int sl = 0; sl < num_slices; ++sl) { 00413 float longitude0 = (float)sl / (float)num_slices; 00414 float longitude1 = (float)(sl + 1) / (float)num_slices; 00415 vertex.add_data3f(compute_point(sphere, 0.0, longitude0)); 00416 for (int st = 1; st < num_stacks; ++st) { 00417 float latitude = (float)st / (float)num_stacks; 00418 vertex.add_data3f(compute_point(sphere, latitude, longitude0)); 00419 vertex.add_data3f(compute_point(sphere, latitude, longitude1)); 00420 } 00421 vertex.add_data3f(compute_point(sphere, 1.0, longitude0)); 00422 00423 strip->add_next_vertices(num_stacks * 2); 00424 strip->close_primitive(); 00425 } 00426 00427 geom = new Geom(vdata); 00428 geom->add_primitive(strip); 00429 00430 } else if (vol->is_of_type(FiniteBoundingVolume::get_class_type())) { 00431 const FiniteBoundingVolume *fvol = DCAST(FiniteBoundingVolume, vol); 00432 00433 BoundingBox box(fvol->get_min(), fvol->get_max()); 00434 box.local_object(); 00435 00436 PT(GeomVertexData) vdata = new GeomVertexData 00437 ("bounds", GeomVertexFormat::get_v3(), 00438 Geom::UH_stream); 00439 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00440 00441 for (int i = 0; i < 8; ++i ) { 00442 vertex.add_data3f(box.get_point(i)); 00443 } 00444 00445 PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_stream); 00446 tris->add_vertices(0, 4, 5); 00447 tris->close_primitive(); 00448 tris->add_vertices(0, 5, 1); 00449 tris->close_primitive(); 00450 tris->add_vertices(4, 6, 7); 00451 tris->close_primitive(); 00452 tris->add_vertices(4, 7, 5); 00453 tris->close_primitive(); 00454 tris->add_vertices(6, 2, 3); 00455 tris->close_primitive(); 00456 tris->add_vertices(6, 3, 7); 00457 tris->close_primitive(); 00458 tris->add_vertices(2, 0, 1); 00459 tris->close_primitive(); 00460 tris->add_vertices(2, 1, 3); 00461 tris->close_primitive(); 00462 tris->add_vertices(1, 5, 7); 00463 tris->close_primitive(); 00464 tris->add_vertices(1, 7, 3); 00465 tris->close_primitive(); 00466 tris->add_vertices(2, 6, 4); 00467 tris->close_primitive(); 00468 tris->add_vertices(2, 4, 0); 00469 tris->close_primitive(); 00470 00471 geom = new Geom(vdata); 00472 geom->add_primitive(tris); 00473 00474 } else { 00475 pgraph_cat.warning() 00476 << "Don't know how to draw a representation of " 00477 << vol->get_class_type() << "\n"; 00478 } 00479 00480 return geom; 00481 } 00482 00483 //////////////////////////////////////////////////////////////////// 00484 // Function: CullTraverser::make_tight_bounds_viz 00485 // Access: Private 00486 // Description: Returns a bounding-box visualization of the indicated 00487 // node's "tight" bounding volume. 00488 //////////////////////////////////////////////////////////////////// 00489 PT(Geom) CullTraverser:: 00490 make_tight_bounds_viz(PandaNode *node) { 00491 PT(Geom) geom; 00492 00493 NodePath np = NodePath::any_path(node); 00494 00495 LPoint3f n, x; 00496 bool found_any = false; 00497 node->calc_tight_bounds(n, x, found_any, TransformState::make_identity(), 00498 _current_thread); 00499 if (found_any) { 00500 PT(GeomVertexData) vdata = new GeomVertexData 00501 ("bounds", GeomVertexFormat::get_v3(), 00502 Geom::UH_stream); 00503 GeomVertexWriter vertex(vdata, InternalName::get_vertex(), 00504 _current_thread); 00505 00506 vertex.add_data3f(n[0], n[1], n[2]); 00507 vertex.add_data3f(n[0], n[1], x[2]); 00508 vertex.add_data3f(n[0], x[1], n[2]); 00509 vertex.add_data3f(n[0], x[1], x[2]); 00510 vertex.add_data3f(x[0], n[1], n[2]); 00511 vertex.add_data3f(x[0], n[1], x[2]); 00512 vertex.add_data3f(x[0], x[1], n[2]); 00513 vertex.add_data3f(x[0], x[1], x[2]); 00514 00515 PT(GeomLinestrips) strip = new GeomLinestrips(Geom::UH_stream); 00516 00517 // We wind one long linestrip around the wireframe cube. This 00518 // does require backtracking a few times here and there. 00519 strip->add_vertex(0); 00520 strip->add_vertex(1); 00521 strip->add_vertex(3); 00522 strip->add_vertex(2); 00523 strip->add_vertex(0); 00524 strip->add_vertex(4); 00525 strip->add_vertex(5); 00526 strip->add_vertex(7); 00527 strip->add_vertex(6); 00528 strip->add_vertex(4); 00529 strip->add_vertex(6); 00530 strip->add_vertex(2); 00531 strip->add_vertex(3); 00532 strip->add_vertex(7); 00533 strip->add_vertex(5); 00534 strip->add_vertex(1); 00535 strip->close_primitive(); 00536 00537 geom = new Geom(vdata); 00538 geom->add_primitive(strip); 00539 } 00540 00541 return geom; 00542 } 00543 00544 //////////////////////////////////////////////////////////////////// 00545 // Function: CullTraverser::compute_point 00546 // Access: Private, Static 00547 // Description: Returns a point on the surface of the sphere. 00548 // latitude and longitude range from 0.0 to 1.0. 00549 //////////////////////////////////////////////////////////////////// 00550 Vertexf CullTraverser:: 00551 compute_point(const BoundingSphere *sphere, 00552 float latitude, float longitude) { 00553 float s1, c1; 00554 csincos(latitude * MathNumbers::pi_f, &s1, &c1); 00555 00556 float s2, c2; 00557 csincos(longitude * 2.0f * MathNumbers::pi_f, &s2, &c2); 00558 00559 Vertexf p(s1 * c2, s1 * s2, c1); 00560 return p * sphere->get_radius() + sphere->get_center(); 00561 } 00562 00563 //////////////////////////////////////////////////////////////////// 00564 // Function: CullTraverser::get_bounds_outer_viz_state 00565 // Access: Private, Static 00566 // Description: Returns a RenderState for rendering the outside 00567 // surfaces of the bounding volume visualizations. 00568 //////////////////////////////////////////////////////////////////// 00569 CPT(RenderState) CullTraverser:: 00570 get_bounds_outer_viz_state() { 00571 // Once someone asks for this pointer, we hold its reference count 00572 // and never free it. 00573 static CPT(RenderState) state = (const RenderState *)NULL; 00574 if (state == (const RenderState *)NULL) { 00575 state = RenderState::make 00576 (ColorAttrib::make_flat(Colorf(0.3f, 1.0f, 0.5f, 1.0f)), 00577 RenderModeAttrib::make(RenderModeAttrib::M_wireframe), 00578 CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise)); 00579 } 00580 return state; 00581 } 00582 00583 //////////////////////////////////////////////////////////////////// 00584 // Function: CullTraverser::get_bounds_inner_viz_state 00585 // Access: Private, Static 00586 // Description: Returns a RenderState for rendering the inside 00587 // surfaces of the bounding volume visualizations. 00588 //////////////////////////////////////////////////////////////////// 00589 CPT(RenderState) CullTraverser:: 00590 get_bounds_inner_viz_state() { 00591 // Once someone asks for this pointer, we hold its reference count 00592 // and never free it. 00593 static CPT(RenderState) state = (const RenderState *)NULL; 00594 if (state == (const RenderState *)NULL) { 00595 state = RenderState::make 00596 (ColorAttrib::make_flat(Colorf(0.15f, 0.5f, 0.25f, 1.0f)), 00597 RenderModeAttrib::make(RenderModeAttrib::M_wireframe), 00598 CullFaceAttrib::make(CullFaceAttrib::M_cull_counter_clockwise)); 00599 } 00600 return state; 00601 } 00602 00603 //////////////////////////////////////////////////////////////////// 00604 // Function: CullTraverser::get_depth_offset_state 00605 // Access: Private, Static 00606 // Description: Returns a RenderState for increasing the DepthOffset 00607 // by one. 00608 //////////////////////////////////////////////////////////////////// 00609 CPT(RenderState) CullTraverser:: 00610 get_depth_offset_state() { 00611 // Once someone asks for this pointer, we hold its reference count 00612 // and never free it. 00613 static CPT(RenderState) state = (const RenderState *)NULL; 00614 if (state == (const RenderState *)NULL) { 00615 state = RenderState::make 00616 (DepthOffsetAttrib::make(1)); 00617 } 00618 return state; 00619 } 00620 00621 00622 //////////////////////////////////////////////////////////////////// 00623 // Function: CullTraverser::start_decal 00624 // Access: Private 00625 // Description: Collects a base node and all of the decals applied to 00626 // it. This involves recursing below the base GeomNode 00627 // to find all the decal geoms. 00628 //////////////////////////////////////////////////////////////////// 00629 void CullTraverser:: 00630 start_decal(const CullTraverserData &data) { 00631 PandaNode *node = data.node(); 00632 if (!node->is_geom_node()) { 00633 pgraph_cat.error() 00634 << "DecalEffect applied to " << *node << ", not a GeomNode.\n"; 00635 return; 00636 } 00637 00638 const PandaNodePipelineReader *node_reader = data.node_reader(); 00639 00640 // Build a chain of CullableObjects. The head of the chain will be 00641 // all of the base Geoms in order, followed by an empty 00642 // CullableObject node, followed by all of the decal Geoms, in 00643 // order. 00644 00645 // Since the CullableObject is a linked list which gets built in 00646 // LIFO order, we start with the decals. 00647 CullableObject *decals = (CullableObject *)NULL; 00648 PandaNode::Children cr = node_reader->get_children(); 00649 int num_children = cr.get_num_children(); 00650 if (node->has_selective_visibility()) { 00651 int i = node->get_first_visible_child(); 00652 while (i < num_children) { 00653 CullTraverserData next_data(data, cr.get_child(i)); 00654 decals = r_get_decals(next_data, decals); 00655 i = node->get_next_visible_child(i); 00656 } 00657 00658 } else { 00659 for (int i = num_children - 1; i >= 0; i--) { 00660 CullTraverserData next_data(data, cr.get_child(i)); 00661 decals = r_get_decals(next_data, decals); 00662 } 00663 } 00664 00665 // Now create a new, empty CullableObject to separate the decals 00666 // from the non-decals. 00667 CullableObject *separator = new CullableObject; 00668 separator->set_next(decals); 00669 00670 // And now get the base Geoms, again in reverse order. 00671 CullableObject *object = separator; 00672 GeomNode *geom_node = DCAST(GeomNode, node); 00673 GeomNode::Geoms geoms = geom_node->get_geoms(); 00674 int num_geoms = geoms.get_num_geoms(); 00675 _geoms_pcollector.add_level(num_geoms); 00676 CPT(TransformState) net_transform = data.get_net_transform(this); 00677 CPT(TransformState) modelview_transform = data.get_modelview_transform(this); 00678 CPT(TransformState) internal_transform = get_gsg()->get_cs_transform()->compose(modelview_transform); 00679 00680 for (int i = num_geoms - 1; i >= 0; i--) { 00681 const Geom *geom = geoms.get_geom(i); 00682 if (geom->is_empty()) { 00683 continue; 00684 } 00685 00686 CPT(RenderState) state = data._state->compose(geoms.get_geom_state(i)); 00687 if (state->has_cull_callback() && !state->cull_callback(this, data)) { 00688 // Cull. 00689 continue; 00690 } 00691 00692 // Cull the Geom bounding volume against the view frustum 00693 // and/or the cull planes. Don't bother unless we've got more 00694 // than one Geom, since otherwise the bounding volume of the 00695 // GeomNode is (probably) the same as that of the one Geom, 00696 // and we've already culled against that. 00697 if (num_geoms > 1) { 00698 if (data._view_frustum != (GeometricBoundingVolume *)NULL) { 00699 // Cull the individual Geom against the view frustum. 00700 CPT(BoundingVolume) geom_volume = geom->get_bounds(); 00701 const GeometricBoundingVolume *geom_gbv = 00702 DCAST(GeometricBoundingVolume, geom_volume); 00703 00704 int result = data._view_frustum->contains(geom_gbv); 00705 if (result == BoundingVolume::IF_no_intersection) { 00706 // Cull this Geom. 00707 continue; 00708 } 00709 } 00710 if (!data._cull_planes->is_empty()) { 00711 // Also cull the Geom against the cull planes. 00712 CPT(BoundingVolume) geom_volume = geom->get_bounds(); 00713 const GeometricBoundingVolume *geom_gbv = 00714 DCAST(GeometricBoundingVolume, geom_volume); 00715 int result; 00716 data._cull_planes->do_cull(result, state, geom_gbv); 00717 if (result == BoundingVolume::IF_no_intersection) { 00718 // Cull. 00719 continue; 00720 } 00721 } 00722 } 00723 00724 CullableObject *next = object; 00725 object = 00726 new CullableObject(geom, state, net_transform, 00727 modelview_transform, internal_transform); 00728 object->set_next(next); 00729 } 00730 00731 if (object != separator) { 00732 // Finally, send the whole list down to the CullHandler for 00733 // processing. The first Geom in the node now represents the 00734 // overall state. 00735 _cull_handler->record_object(object, this); 00736 } else { 00737 // Never mind; there's nothing to render. 00738 delete object; 00739 } 00740 } 00741 00742 //////////////////////////////////////////////////////////////////// 00743 // Function: CullTraverser::r_get_decals 00744 // Access: Private 00745 // Description: Recursively gets all the decals applied to a 00746 // particular GeomNode. These are built into a 00747 // CullableObject list in LIFO order (so that the 00748 // traversing the list will extract them in the order 00749 // they were encountered in the scene graph). 00750 //////////////////////////////////////////////////////////////////// 00751 CullableObject *CullTraverser:: 00752 r_get_decals(CullTraverserData &data, CullableObject *decals) { 00753 if (is_in_view(data)) { 00754 PandaNodePipelineReader *node_reader = data.node_reader(); 00755 PandaNode *node = data.node(); 00756 00757 const RenderEffects *node_effects = node_reader->get_effects(); 00758 if (node_effects->has_show_bounds()) { 00759 // If we should show the bounding volume for this node, make it 00760 // up now. 00761 show_bounds(data, node_effects->has_show_tight_bounds()); 00762 } 00763 00764 data.apply_transform_and_state(this); 00765 00766 // First, visit all of the node's children. 00767 int num_children = node_reader->get_num_children(); 00768 if (node->has_selective_visibility()) { 00769 int i = node->get_first_visible_child(); 00770 while (i < num_children) { 00771 CullTraverserData next_data(data, node_reader->get_child(i)); 00772 decals = r_get_decals(next_data, decals); 00773 i = node->get_next_visible_child(i); 00774 } 00775 00776 } else { 00777 for (int i = num_children - 1; i >= 0; i--) { 00778 CullTraverserData next_data(data, node_reader->get_child(i)); 00779 decals = r_get_decals(next_data, decals); 00780 } 00781 } 00782 00783 // Now, tack on any geoms within the node. 00784 if (node->is_geom_node()) { 00785 GeomNode *geom_node = DCAST(GeomNode, node); 00786 GeomNode::Geoms geoms = geom_node->get_geoms(); 00787 int num_geoms = geoms.get_num_geoms(); 00788 _geoms_pcollector.add_level(num_geoms); 00789 CPT(TransformState) net_transform = data.get_net_transform(this); 00790 CPT(TransformState) modelview_transform = data.get_modelview_transform(this); 00791 CPT(TransformState) internal_transform = get_gsg()->get_cs_transform()->compose(modelview_transform); 00792 00793 for (int i = num_geoms - 1; i >= 0; i--) { 00794 const Geom *geom = geoms.get_geom(i); 00795 if (geom->is_empty()) { 00796 continue; 00797 } 00798 00799 CPT(RenderState) state = data._state->compose(geoms.get_geom_state(i)); 00800 if (state->has_cull_callback() && !state->cull_callback(this, data)) { 00801 // Cull. 00802 continue; 00803 } 00804 00805 // Cull the Geom bounding volume against the view frustum 00806 // and/or the cull planes. Don't bother unless we've got more 00807 // than one Geom, since otherwise the bounding volume of the 00808 // GeomNode is (probably) the same as that of the one Geom, 00809 // and we've already culled against that. 00810 if (num_geoms > 1) { 00811 if (data._view_frustum != (GeometricBoundingVolume *)NULL) { 00812 // Cull the individual Geom against the view frustum. 00813 CPT(BoundingVolume) geom_volume = geom->get_bounds(); 00814 const GeometricBoundingVolume *geom_gbv = 00815 DCAST(GeometricBoundingVolume, geom_volume); 00816 00817 int result = data._view_frustum->contains(geom_gbv); 00818 if (result == BoundingVolume::IF_no_intersection) { 00819 // Cull this Geom. 00820 continue; 00821 } 00822 } 00823 if (!data._cull_planes->is_empty()) { 00824 // Also cull the Geom against the cull planes. 00825 CPT(BoundingVolume) geom_volume = geom->get_bounds(); 00826 const GeometricBoundingVolume *geom_gbv = 00827 DCAST(GeometricBoundingVolume, geom_volume); 00828 int result; 00829 data._cull_planes->do_cull(result, state, geom_gbv); 00830 if (result == BoundingVolume::IF_no_intersection) { 00831 // Cull. 00832 continue; 00833 } 00834 } 00835 } 00836 00837 CullableObject *next = decals; 00838 decals = 00839 new CullableObject(geom, state, net_transform, 00840 modelview_transform, internal_transform); 00841 decals->set_next(next); 00842 } 00843 } 00844 } 00845 00846 return decals; 00847 }