Panda3D
|
00001 // Filename: cullTraverser.cxx 00002 // Created by: drose (23eb02) 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 "geomLines.h" 00038 #include "geomVertexWriter.h" 00039 00040 PStatCollector CullTraverser::_nodes_pcollector("Nodes"); 00041 PStatCollector CullTraverser::_geom_nodes_pcollector("Nodes:GeomNodes"); 00042 PStatCollector CullTraverser::_geoms_pcollector("Geoms"); 00043 PStatCollector CullTraverser::_geoms_occluded_pcollector("Geoms:Occluded"); 00044 00045 TypeHandle CullTraverser::_type_handle; 00046 00047 //////////////////////////////////////////////////////////////////// 00048 // Function: CullTraverser::Constructor 00049 // Access: Published 00050 // Description: 00051 //////////////////////////////////////////////////////////////////// 00052 CullTraverser:: 00053 CullTraverser() : 00054 _gsg(NULL), 00055 _current_thread(Thread::get_current_thread()) 00056 { 00057 _camera_mask = DrawMask::all_on(); 00058 _has_tag_state_key = false; 00059 _initial_state = RenderState::make_empty(); 00060 _cull_handler = (CullHandler *)NULL; 00061 _portal_clipper = (PortalClipper *)NULL; 00062 _effective_incomplete_render = true; 00063 } 00064 00065 //////////////////////////////////////////////////////////////////// 00066 // Function: CullTraverser::Copy Constructor 00067 // Access: Published 00068 // Description: 00069 //////////////////////////////////////////////////////////////////// 00070 CullTraverser:: 00071 CullTraverser(const CullTraverser ©) : 00072 _gsg(copy._gsg), 00073 _current_thread(copy._current_thread), 00074 _scene_setup(copy._scene_setup), 00075 _camera_mask(copy._camera_mask), 00076 _has_tag_state_key(copy._has_tag_state_key), 00077 _tag_state_key(copy._tag_state_key), 00078 _initial_state(copy._initial_state), 00079 _depth_offset_decals(copy._depth_offset_decals), 00080 _view_frustum(copy._view_frustum), 00081 _cull_handler(copy._cull_handler), 00082 _portal_clipper(copy._portal_clipper), 00083 _effective_incomplete_render(copy._effective_incomplete_render) 00084 { 00085 } 00086 00087 //////////////////////////////////////////////////////////////////// 00088 // Function: CullTraverser::set_scene 00089 // Access: Published, Virtual 00090 // Description: Sets the SceneSetup object that indicates the initial 00091 // camera position, etc. This must be called before 00092 // traversal begins. 00093 //////////////////////////////////////////////////////////////////// 00094 void CullTraverser:: 00095 set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsg, 00096 bool dr_incomplete_render) { 00097 _scene_setup = scene_setup; 00098 _gsg = gsg; 00099 00100 _initial_state = scene_setup->get_initial_state(); 00101 _depth_offset_decals = _gsg->depth_offset_decals() && depth_offset_decals; 00102 00103 _current_thread = Thread::get_current_thread(); 00104 00105 const Camera *camera = scene_setup->get_camera_node(); 00106 _tag_state_key = camera->get_tag_state_key(); 00107 _has_tag_state_key = !_tag_state_key.empty(); 00108 _camera_mask = camera->get_camera_mask(); 00109 00110 _effective_incomplete_render = _gsg->get_incomplete_render() && dr_incomplete_render; 00111 } 00112 00113 //////////////////////////////////////////////////////////////////// 00114 // Function: CullTraverser::traverse 00115 // Access: Published 00116 // Description: Begins the traversal from the indicated node. 00117 //////////////////////////////////////////////////////////////////// 00118 void CullTraverser:: 00119 traverse(const NodePath &root) { 00120 nassertv(_cull_handler != (CullHandler *)NULL); 00121 nassertv(_scene_setup != (SceneSetup *)NULL); 00122 00123 if (allow_portal_cull) { 00124 // This _view_frustum is in cull_center space 00125 //Erik: obsolete? 00126 //PT(GeometricBoundingVolume) vf = _view_frustum; 00127 00128 GeometricBoundingVolume *local_frustum = NULL; 00129 PT(BoundingVolume) bv = _scene_setup->get_lens()->make_bounds(); 00130 if (bv != (BoundingVolume *)NULL && 00131 bv->is_of_type(GeometricBoundingVolume::get_class_type())) { 00132 00133 local_frustum = DCAST(GeometricBoundingVolume, bv); 00134 } 00135 00136 // This local_frustum is in camera space 00137 PortalClipper portal_viewer(local_frustum, _scene_setup); 00138 if (debug_portal_cull) { 00139 portal_viewer.draw_camera_frustum(); 00140 } 00141 00142 // Store this pointer in this 00143 set_portal_clipper(&portal_viewer); 00144 00145 CullTraverserData data(root, TransformState::make_identity(), 00146 _initial_state, _view_frustum, 00147 _current_thread); 00148 00149 traverse(data); 00150 00151 // Finally add the lines to be drawn 00152 if (debug_portal_cull) { 00153 portal_viewer.draw_lines(); 00154 } 00155 00156 // Render the frustum relative to the cull center. 00157 NodePath cull_center = _scene_setup->get_cull_center(); 00158 CPT(TransformState) transform = cull_center.get_transform(root); 00159 00160 CullTraverserData my_data(data, portal_viewer._previous); 00161 my_data._net_transform = my_data._net_transform->compose(transform); 00162 traverse(my_data); 00163 00164 } else { 00165 CullTraverserData data(root, TransformState::make_identity(), 00166 _initial_state, _view_frustum, 00167 _current_thread); 00168 00169 traverse(data); 00170 } 00171 } 00172 00173 //////////////////////////////////////////////////////////////////// 00174 // Function: CullTraverser::traverse 00175 // Access: Published 00176 // Description: Traverses from the next node with the given 00177 // data, which has been constructed with the node but 00178 // has not yet been converted into the node's space. 00179 //////////////////////////////////////////////////////////////////// 00180 void CullTraverser:: 00181 traverse(CullTraverserData &data) { 00182 if (is_in_view(data)) { 00183 if (pgraph_cat.is_spam()) { 00184 pgraph_cat.spam() 00185 << "\n" << data._node_path 00186 << " " << data._draw_mask << "\n"; 00187 } 00188 00189 PandaNodePipelineReader *node_reader = data.node_reader(); 00190 int fancy_bits = node_reader->get_fancy_bits(); 00191 00192 if ((fancy_bits & (PandaNode::FB_transform | 00193 PandaNode::FB_state | 00194 PandaNode::FB_effects | 00195 PandaNode::FB_tag | 00196 PandaNode::FB_draw_mask | 00197 PandaNode::FB_cull_callback)) == 0 && 00198 data._cull_planes->is_empty()) { 00199 // Nothing interesting in this node; just move on. 00200 traverse_below(data); 00201 00202 } else { 00203 // Something in this node is worth taking a closer look. 00204 const RenderEffects *node_effects = node_reader->get_effects(); 00205 if (node_effects->has_show_bounds()) { 00206 // If we should show the bounding volume for this node, make it 00207 // up now. 00208 show_bounds(data, node_effects->has_show_tight_bounds()); 00209 } 00210 00211 data.apply_transform_and_state(this); 00212 00213 const FogAttrib *fog = DCAST(FogAttrib, node_reader->get_state()->get_attrib(FogAttrib::get_class_slot())); 00214 if (fog != (const FogAttrib *)NULL && fog->get_fog() != (Fog *)NULL) { 00215 // If we just introduced a FogAttrib here, call adjust_to_camera() 00216 // now. This maybe isn't the perfect time to call it, but it's 00217 // good enough; and at this time we have all the information we 00218 // need for it. 00219 fog->get_fog()->adjust_to_camera(get_camera_transform()); 00220 } 00221 00222 if (fancy_bits & PandaNode::FB_cull_callback) { 00223 PandaNode *node = data.node(); 00224 if (!node->cull_callback(this, data)) { 00225 return; 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) const { 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, Static 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 PN_stdfloat longitude0 = (PN_stdfloat)sl / (PN_stdfloat)num_slices; 00414 PN_stdfloat longitude1 = (PN_stdfloat)(sl + 1) / (PN_stdfloat)num_slices; 00415 vertex.add_data3(compute_point(sphere, 0.0, longitude0)); 00416 for (int st = 1; st < num_stacks; ++st) { 00417 PN_stdfloat latitude = (PN_stdfloat)st / (PN_stdfloat)num_stacks; 00418 vertex.add_data3(compute_point(sphere, latitude, longitude0)); 00419 vertex.add_data3(compute_point(sphere, latitude, longitude1)); 00420 } 00421 vertex.add_data3(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(BoundingHexahedron::get_class_type())) { 00431 const BoundingHexahedron *fvol = DCAST(BoundingHexahedron, vol); 00432 00433 PT(GeomVertexData) vdata = new GeomVertexData 00434 ("bounds", GeomVertexFormat::get_v3(), 00435 Geom::UH_stream); 00436 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00437 00438 for (int i = 0; i < 8; ++i ) { 00439 vertex.add_data3(fvol->get_point(i)); 00440 } 00441 00442 PT(GeomLines) lines = new GeomLines(Geom::UH_stream); 00443 lines->add_vertices(0, 1); lines->close_primitive(); 00444 lines->add_vertices(1, 2); lines->close_primitive(); 00445 lines->add_vertices(2, 3); lines->close_primitive(); 00446 lines->add_vertices(3, 0); lines->close_primitive(); 00447 00448 lines->add_vertices(4, 5); lines->close_primitive(); 00449 lines->add_vertices(5, 6); lines->close_primitive(); 00450 lines->add_vertices(6, 7); lines->close_primitive(); 00451 lines->add_vertices(7, 4); lines->close_primitive(); 00452 00453 lines->add_vertices(0, 4); lines->close_primitive(); 00454 lines->add_vertices(1, 5); lines->close_primitive(); 00455 lines->add_vertices(2, 6); lines->close_primitive(); 00456 lines->add_vertices(3, 7); lines->close_primitive(); 00457 00458 geom = new Geom(vdata); 00459 geom->add_primitive(lines); 00460 00461 } else if (vol->is_of_type(FiniteBoundingVolume::get_class_type())) { 00462 const FiniteBoundingVolume *fvol = DCAST(FiniteBoundingVolume, vol); 00463 00464 BoundingBox box(fvol->get_min(), fvol->get_max()); 00465 box.local_object(); 00466 00467 PT(GeomVertexData) vdata = new GeomVertexData 00468 ("bounds", GeomVertexFormat::get_v3(), 00469 Geom::UH_stream); 00470 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00471 00472 for (int i = 0; i < 8; ++i ) { 00473 vertex.add_data3(box.get_point(i)); 00474 } 00475 00476 PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_stream); 00477 tris->add_vertices(0, 4, 5); 00478 tris->close_primitive(); 00479 tris->add_vertices(0, 5, 1); 00480 tris->close_primitive(); 00481 tris->add_vertices(4, 6, 7); 00482 tris->close_primitive(); 00483 tris->add_vertices(4, 7, 5); 00484 tris->close_primitive(); 00485 tris->add_vertices(6, 2, 3); 00486 tris->close_primitive(); 00487 tris->add_vertices(6, 3, 7); 00488 tris->close_primitive(); 00489 tris->add_vertices(2, 0, 1); 00490 tris->close_primitive(); 00491 tris->add_vertices(2, 1, 3); 00492 tris->close_primitive(); 00493 tris->add_vertices(1, 5, 7); 00494 tris->close_primitive(); 00495 tris->add_vertices(1, 7, 3); 00496 tris->close_primitive(); 00497 tris->add_vertices(2, 6, 4); 00498 tris->close_primitive(); 00499 tris->add_vertices(2, 4, 0); 00500 tris->close_primitive(); 00501 00502 geom = new Geom(vdata); 00503 geom->add_primitive(tris); 00504 00505 } else { 00506 pgraph_cat.warning() 00507 << "Don't know how to draw a representation of " 00508 << vol->get_class_type() << "\n"; 00509 } 00510 00511 return geom; 00512 } 00513 00514 //////////////////////////////////////////////////////////////////// 00515 // Function: CullTraverser::make_tight_bounds_viz 00516 // Access: Private 00517 // Description: Returns a bounding-box visualization of the indicated 00518 // node's "tight" bounding volume. 00519 //////////////////////////////////////////////////////////////////// 00520 PT(Geom) CullTraverser:: 00521 make_tight_bounds_viz(PandaNode *node) const { 00522 PT(Geom) geom; 00523 00524 NodePath np = NodePath::any_path(node); 00525 00526 LPoint3 n, x; 00527 bool found_any = false; 00528 node->calc_tight_bounds(n, x, found_any, TransformState::make_identity(), 00529 _current_thread); 00530 if (found_any) { 00531 PT(GeomVertexData) vdata = new GeomVertexData 00532 ("bounds", GeomVertexFormat::get_v3(), 00533 Geom::UH_stream); 00534 GeomVertexWriter vertex(vdata, InternalName::get_vertex(), 00535 _current_thread); 00536 00537 vertex.add_data3(n[0], n[1], n[2]); 00538 vertex.add_data3(n[0], n[1], x[2]); 00539 vertex.add_data3(n[0], x[1], n[2]); 00540 vertex.add_data3(n[0], x[1], x[2]); 00541 vertex.add_data3(x[0], n[1], n[2]); 00542 vertex.add_data3(x[0], n[1], x[2]); 00543 vertex.add_data3(x[0], x[1], n[2]); 00544 vertex.add_data3(x[0], x[1], x[2]); 00545 00546 PT(GeomLinestrips) strip = new GeomLinestrips(Geom::UH_stream); 00547 00548 // We wind one long linestrip around the wireframe cube. This 00549 // does require backtracking a few times here and there. 00550 strip->add_vertex(0); 00551 strip->add_vertex(1); 00552 strip->add_vertex(3); 00553 strip->add_vertex(2); 00554 strip->add_vertex(0); 00555 strip->add_vertex(4); 00556 strip->add_vertex(5); 00557 strip->add_vertex(7); 00558 strip->add_vertex(6); 00559 strip->add_vertex(4); 00560 strip->add_vertex(6); 00561 strip->add_vertex(2); 00562 strip->add_vertex(3); 00563 strip->add_vertex(7); 00564 strip->add_vertex(5); 00565 strip->add_vertex(1); 00566 strip->close_primitive(); 00567 00568 geom = new Geom(vdata); 00569 geom->add_primitive(strip); 00570 } 00571 00572 return geom; 00573 } 00574 00575 //////////////////////////////////////////////////////////////////// 00576 // Function: CullTraverser::compute_point 00577 // Access: Private, Static 00578 // Description: Returns a point on the surface of the sphere. 00579 // latitude and longitude range from 0.0 to 1.0. 00580 //////////////////////////////////////////////////////////////////// 00581 LVertex CullTraverser:: 00582 compute_point(const BoundingSphere *sphere, 00583 PN_stdfloat latitude, PN_stdfloat longitude) { 00584 PN_stdfloat s1, c1; 00585 csincos(latitude * MathNumbers::pi, &s1, &c1); 00586 00587 PN_stdfloat s2, c2; 00588 csincos(longitude * 2.0 * MathNumbers::pi, &s2, &c2); 00589 00590 LVertex p(s1 * c2, s1 * s2, c1); 00591 return p * sphere->get_radius() + sphere->get_center(); 00592 } 00593 00594 //////////////////////////////////////////////////////////////////// 00595 // Function: CullTraverser::get_bounds_outer_viz_state 00596 // Access: Private, Static 00597 // Description: Returns a RenderState for rendering the outside 00598 // surfaces of the bounding volume visualizations. 00599 //////////////////////////////////////////////////////////////////// 00600 CPT(RenderState) CullTraverser:: 00601 get_bounds_outer_viz_state() { 00602 // Once someone asks for this pointer, we hold its reference count 00603 // and never free it. 00604 static CPT(RenderState) state = (const RenderState *)NULL; 00605 if (state == (const RenderState *)NULL) { 00606 state = RenderState::make 00607 (ColorAttrib::make_flat(LColor(0.3, 1.0f, 0.5f, 1.0f)), 00608 RenderModeAttrib::make(RenderModeAttrib::M_wireframe), 00609 CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise)); 00610 } 00611 return state; 00612 } 00613 00614 //////////////////////////////////////////////////////////////////// 00615 // Function: CullTraverser::get_bounds_inner_viz_state 00616 // Access: Private, Static 00617 // Description: Returns a RenderState for rendering the inside 00618 // surfaces of the bounding volume visualizations. 00619 //////////////////////////////////////////////////////////////////// 00620 CPT(RenderState) CullTraverser:: 00621 get_bounds_inner_viz_state() { 00622 // Once someone asks for this pointer, we hold its reference count 00623 // and never free it. 00624 static CPT(RenderState) state = (const RenderState *)NULL; 00625 if (state == (const RenderState *)NULL) { 00626 state = RenderState::make 00627 (ColorAttrib::make_flat(LColor(0.15f, 0.5f, 0.25f, 1.0f)), 00628 RenderModeAttrib::make(RenderModeAttrib::M_wireframe), 00629 CullFaceAttrib::make(CullFaceAttrib::M_cull_counter_clockwise)); 00630 } 00631 return state; 00632 } 00633 00634 //////////////////////////////////////////////////////////////////// 00635 // Function: CullTraverser::get_depth_offset_state 00636 // Access: Private, Static 00637 // Description: Returns a RenderState for increasing the DepthOffset 00638 // by one. 00639 //////////////////////////////////////////////////////////////////// 00640 CPT(RenderState) CullTraverser:: 00641 get_depth_offset_state() { 00642 // Once someone asks for this pointer, we hold its reference count 00643 // and never free it. 00644 static CPT(RenderState) state = (const RenderState *)NULL; 00645 if (state == (const RenderState *)NULL) { 00646 state = RenderState::make 00647 (DepthOffsetAttrib::make(1)); 00648 } 00649 return state; 00650 } 00651 00652 00653 //////////////////////////////////////////////////////////////////// 00654 // Function: CullTraverser::start_decal 00655 // Access: Private 00656 // Description: Collects a base node and all of the decals applied to 00657 // it. This involves recursing below the base GeomNode 00658 // to find all the decal geoms. 00659 //////////////////////////////////////////////////////////////////// 00660 void CullTraverser:: 00661 start_decal(const CullTraverserData &data) { 00662 PandaNode *node = data.node(); 00663 if (!node->is_geom_node()) { 00664 pgraph_cat.error() 00665 << "DecalEffect applied to " << *node << ", not a GeomNode.\n"; 00666 return; 00667 } 00668 00669 const PandaNodePipelineReader *node_reader = data.node_reader(); 00670 00671 // Build a chain of CullableObjects. The head of the chain will be 00672 // all of the base Geoms in order, followed by an empty 00673 // CullableObject node, followed by all of the decal Geoms, in 00674 // order. 00675 00676 // Since the CullableObject is a linked list which gets built in 00677 // LIFO order, we start with the decals. 00678 CullableObject *decals = (CullableObject *)NULL; 00679 PandaNode::Children cr = node_reader->get_children(); 00680 int num_children = cr.get_num_children(); 00681 if (node->has_selective_visibility()) { 00682 int i = node->get_first_visible_child(); 00683 while (i < num_children) { 00684 CullTraverserData next_data(data, cr.get_child(i)); 00685 decals = r_get_decals(next_data, decals); 00686 i = node->get_next_visible_child(i); 00687 } 00688 00689 } else { 00690 for (int i = num_children - 1; i >= 0; i--) { 00691 CullTraverserData next_data(data, cr.get_child(i)); 00692 decals = r_get_decals(next_data, decals); 00693 } 00694 } 00695 00696 // Now create a new, empty CullableObject to separate the decals 00697 // from the non-decals. 00698 CullableObject *separator = new CullableObject; 00699 separator->set_next(decals); 00700 00701 // And now get the base Geoms, again in reverse order. 00702 CullableObject *object = separator; 00703 GeomNode *geom_node = DCAST(GeomNode, node); 00704 GeomNode::Geoms geoms = geom_node->get_geoms(); 00705 int num_geoms = geoms.get_num_geoms(); 00706 _geoms_pcollector.add_level(num_geoms); 00707 CPT(TransformState) net_transform = data.get_net_transform(this); 00708 CPT(TransformState) modelview_transform = data.get_modelview_transform(this); 00709 CPT(TransformState) internal_transform = get_gsg()->get_cs_transform()->compose(modelview_transform); 00710 00711 for (int i = num_geoms - 1; i >= 0; i--) { 00712 const Geom *geom = geoms.get_geom(i); 00713 if (geom->is_empty()) { 00714 continue; 00715 } 00716 00717 CPT(RenderState) state = data._state->compose(geoms.get_geom_state(i)); 00718 if (state->has_cull_callback() && !state->cull_callback(this, data)) { 00719 // Cull. 00720 continue; 00721 } 00722 00723 // Cull the Geom bounding volume against the view frustum 00724 // and/or the cull planes. Don't bother unless we've got more 00725 // than one Geom, since otherwise the bounding volume of the 00726 // GeomNode is (probably) the same as that of the one Geom, 00727 // and we've already culled against that. 00728 if (num_geoms > 1) { 00729 if (data._view_frustum != (GeometricBoundingVolume *)NULL) { 00730 // Cull the individual Geom against the view frustum. 00731 CPT(BoundingVolume) geom_volume = geom->get_bounds(); 00732 const GeometricBoundingVolume *geom_gbv = 00733 DCAST(GeometricBoundingVolume, geom_volume); 00734 00735 int result = data._view_frustum->contains(geom_gbv); 00736 if (result == BoundingVolume::IF_no_intersection) { 00737 // Cull this Geom. 00738 continue; 00739 } 00740 } 00741 if (!data._cull_planes->is_empty()) { 00742 // Also cull the Geom against the cull planes. 00743 CPT(BoundingVolume) geom_volume = geom->get_bounds(); 00744 const GeometricBoundingVolume *geom_gbv = 00745 DCAST(GeometricBoundingVolume, geom_volume); 00746 int result; 00747 data._cull_planes->do_cull(result, state, geom_gbv); 00748 if (result == BoundingVolume::IF_no_intersection) { 00749 // Cull. 00750 continue; 00751 } 00752 } 00753 } 00754 00755 CullableObject *next = object; 00756 object = 00757 new CullableObject(geom, state, net_transform, 00758 modelview_transform, internal_transform); 00759 object->set_next(next); 00760 } 00761 00762 if (object != separator) { 00763 // Finally, send the whole list down to the CullHandler for 00764 // processing. The first Geom in the node now represents the 00765 // overall state. 00766 _cull_handler->record_object(object, this); 00767 } else { 00768 // Never mind; there's nothing to render. 00769 delete object; 00770 } 00771 } 00772 00773 //////////////////////////////////////////////////////////////////// 00774 // Function: CullTraverser::r_get_decals 00775 // Access: Private 00776 // Description: Recursively gets all the decals applied to a 00777 // particular GeomNode. These are built into a 00778 // CullableObject list in LIFO order (so that the 00779 // traversing the list will extract them in the order 00780 // they were encountered in the scene graph). 00781 //////////////////////////////////////////////////////////////////// 00782 CullableObject *CullTraverser:: 00783 r_get_decals(CullTraverserData &data, CullableObject *decals) { 00784 if (is_in_view(data)) { 00785 PandaNodePipelineReader *node_reader = data.node_reader(); 00786 PandaNode *node = data.node(); 00787 00788 const RenderEffects *node_effects = node_reader->get_effects(); 00789 if (node_effects->has_show_bounds()) { 00790 // If we should show the bounding volume for this node, make it 00791 // up now. 00792 show_bounds(data, node_effects->has_show_tight_bounds()); 00793 } 00794 00795 data.apply_transform_and_state(this); 00796 00797 // First, visit all of the node's children. 00798 int num_children = node_reader->get_num_children(); 00799 if (node->has_selective_visibility()) { 00800 int i = node->get_first_visible_child(); 00801 while (i < num_children) { 00802 CullTraverserData next_data(data, node_reader->get_child(i)); 00803 decals = r_get_decals(next_data, decals); 00804 i = node->get_next_visible_child(i); 00805 } 00806 00807 } else { 00808 for (int i = num_children - 1; i >= 0; i--) { 00809 CullTraverserData next_data(data, node_reader->get_child(i)); 00810 decals = r_get_decals(next_data, decals); 00811 } 00812 } 00813 00814 // Now, tack on any geoms within the node. 00815 if (node->is_geom_node()) { 00816 GeomNode *geom_node = DCAST(GeomNode, node); 00817 GeomNode::Geoms geoms = geom_node->get_geoms(); 00818 int num_geoms = geoms.get_num_geoms(); 00819 _geoms_pcollector.add_level(num_geoms); 00820 CPT(TransformState) net_transform = data.get_net_transform(this); 00821 CPT(TransformState) modelview_transform = data.get_modelview_transform(this); 00822 CPT(TransformState) internal_transform = get_gsg()->get_cs_transform()->compose(modelview_transform); 00823 00824 for (int i = num_geoms - 1; i >= 0; i--) { 00825 const Geom *geom = geoms.get_geom(i); 00826 if (geom->is_empty()) { 00827 continue; 00828 } 00829 00830 CPT(RenderState) state = data._state->compose(geoms.get_geom_state(i)); 00831 if (state->has_cull_callback() && !state->cull_callback(this, data)) { 00832 // Cull. 00833 continue; 00834 } 00835 00836 // Cull the Geom bounding volume against the view frustum 00837 // and/or the cull planes. Don't bother unless we've got more 00838 // than one Geom, since otherwise the bounding volume of the 00839 // GeomNode is (probably) the same as that of the one Geom, 00840 // and we've already culled against that. 00841 if (num_geoms > 1) { 00842 if (data._view_frustum != (GeometricBoundingVolume *)NULL) { 00843 // Cull the individual Geom against the view frustum. 00844 CPT(BoundingVolume) geom_volume = geom->get_bounds(); 00845 const GeometricBoundingVolume *geom_gbv = 00846 DCAST(GeometricBoundingVolume, geom_volume); 00847 00848 int result = data._view_frustum->contains(geom_gbv); 00849 if (result == BoundingVolume::IF_no_intersection) { 00850 // Cull this Geom. 00851 continue; 00852 } 00853 } 00854 if (!data._cull_planes->is_empty()) { 00855 // Also cull the Geom against the cull planes. 00856 CPT(BoundingVolume) geom_volume = geom->get_bounds(); 00857 const GeometricBoundingVolume *geom_gbv = 00858 DCAST(GeometricBoundingVolume, geom_volume); 00859 int result; 00860 data._cull_planes->do_cull(result, state, geom_gbv); 00861 if (result == BoundingVolume::IF_no_intersection) { 00862 // Cull. 00863 continue; 00864 } 00865 } 00866 } 00867 00868 CullableObject *next = decals; 00869 decals = 00870 new CullableObject(geom, state, net_transform, 00871 modelview_transform, internal_transform); 00872 decals->set_next(next); 00873 } 00874 } 00875 } 00876 00877 return decals; 00878 }