Panda3D
|
00001 // Filename: cullTraverserData.cxx 00002 // Created by: drose (06Mar02) 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 "cullTraverserData.h" 00016 #include "cullTraverser.h" 00017 #include "config_pgraph.h" 00018 #include "pandaNode.h" 00019 #include "colorAttrib.h" 00020 #include "textureAttrib.h" 00021 #include "renderModeAttrib.h" 00022 #include "clipPlaneAttrib.h" 00023 #include "boundingPlane.h" 00024 #include "billboardEffect.h" 00025 #include "compassEffect.h" 00026 #include "polylightEffect.h" 00027 #include "renderState.h" 00028 00029 00030 //////////////////////////////////////////////////////////////////// 00031 // Function: CullTraverserData::get_modelview_transform 00032 // Access: Published 00033 // Description: Returns the modelview transform: the relative 00034 // transform from the camera to the model. 00035 //////////////////////////////////////////////////////////////////// 00036 CPT(TransformState) CullTraverserData:: 00037 get_modelview_transform(const CullTraverser *trav) const { 00038 return trav->get_world_transform()->compose(_net_transform); 00039 } 00040 00041 //////////////////////////////////////////////////////////////////// 00042 // Function: CullTraverserData::apply_transform_and_state 00043 // Access: Published 00044 // Description: Applies the transform and state from the current 00045 // node onto the current data. This also evaluates 00046 // billboards, etc. 00047 //////////////////////////////////////////////////////////////////// 00048 void CullTraverserData:: 00049 apply_transform_and_state(CullTraverser *trav) { 00050 CPT(RenderState) node_state = _node_reader.get_state(); 00051 00052 if (trav->has_tag_state_key() && 00053 _node_reader.has_tag(trav->get_tag_state_key())) { 00054 // Here's a node that has been tagged with the special key for our 00055 // current camera. This indicates some special state transition 00056 // for this node, which is unique to this camera. 00057 const Camera *camera = trav->get_scene()->get_camera_node(); 00058 string tag_state = _node_reader.get_tag(trav->get_tag_state_key()); 00059 node_state = node_state->compose(camera->get_tag_state(tag_state)); 00060 } 00061 _node_reader.compose_draw_mask(_draw_mask); 00062 00063 apply_transform_and_state(trav, _node_reader.get_transform(), 00064 node_state, _node_reader.get_effects(), 00065 _node_reader.get_off_clip_planes()); 00066 } 00067 00068 //////////////////////////////////////////////////////////////////// 00069 // Function: CullTraverserData::apply_transform_and_state 00070 // Access: Published 00071 // Description: Applies the indicated transform and state changes 00072 // (e.g. as extracted from a node) onto the current 00073 // data. This also evaluates billboards, etc. 00074 //////////////////////////////////////////////////////////////////// 00075 void CullTraverserData:: 00076 apply_transform_and_state(CullTraverser *trav, 00077 CPT(TransformState) node_transform, 00078 CPT(RenderState) node_state, 00079 CPT(RenderEffects) node_effects, 00080 const RenderAttrib *off_clip_planes) { 00081 if (node_effects->has_cull_callback()) { 00082 node_effects->cull_callback(trav, *this, node_transform, node_state); 00083 } 00084 00085 if (!node_transform->is_identity()) { 00086 _net_transform = _net_transform->compose(node_transform); 00087 00088 if ((_view_frustum != (GeometricBoundingVolume *)NULL) || 00089 (!_cull_planes->is_empty())) { 00090 // We need to move the viewing frustums into the node's 00091 // coordinate space by applying the node's inverse transform. 00092 if (node_transform->is_singular()) { 00093 // But we can't invert a singular transform! Instead of 00094 // trying, we'll just give up on frustum culling from this 00095 // point down. 00096 _view_frustum = (GeometricBoundingVolume *)NULL; 00097 _cull_planes = CullPlanes::make_empty(); 00098 00099 } else { 00100 CPT(TransformState) inv_transform = 00101 node_transform->invert_compose(TransformState::make_identity()); 00102 00103 // Copy the bounding volumes for the frustums so we can 00104 // transform them. 00105 if (_view_frustum != (GeometricBoundingVolume *)NULL) { 00106 _view_frustum = DCAST(GeometricBoundingVolume, _view_frustum->make_copy()); 00107 _view_frustum->xform(inv_transform->get_mat()); 00108 } 00109 00110 _cull_planes = _cull_planes->xform(inv_transform->get_mat()); 00111 } 00112 } 00113 } 00114 00115 _state = _state->compose(node_state); 00116 00117 if (clip_plane_cull) { 00118 _cull_planes = _cull_planes->apply_state(trav, this, 00119 DCAST(ClipPlaneAttrib, node_state->get_attrib(ClipPlaneAttrib::get_class_slot())), 00120 DCAST(ClipPlaneAttrib, off_clip_planes)); 00121 } 00122 } 00123 00124 //////////////////////////////////////////////////////////////////// 00125 // Function: CullTraverserData::is_in_view_impl 00126 // Access: Private 00127 // Description: The private implementation of is_in_view(). 00128 //////////////////////////////////////////////////////////////////// 00129 bool CullTraverserData:: 00130 is_in_view_impl() { 00131 CPT(BoundingVolume) node_volume = _node_reader.get_bounds(); 00132 nassertr(node_volume->is_of_type(GeometricBoundingVolume::get_class_type()), false); 00133 const GeometricBoundingVolume *node_gbv = 00134 DCAST(GeometricBoundingVolume, node_volume); 00135 00136 if (_view_frustum != (GeometricBoundingVolume *)NULL) { 00137 int result = _view_frustum->contains(node_gbv); 00138 00139 if (pgraph_cat.is_spam()) { 00140 pgraph_cat.spam() 00141 << _node_path << " cull result = " << hex << result << dec << "\n"; 00142 } 00143 00144 if (result == BoundingVolume::IF_no_intersection) { 00145 // No intersection at all. Cull. 00146 if (!fake_view_frustum_cull) { 00147 return false; 00148 } 00149 00150 // If we have fake view-frustum culling enabled, instead of 00151 // actually culling an object we simply force it to be drawn in 00152 // red wireframe. 00153 _view_frustum = (GeometricBoundingVolume *)NULL; 00154 CPT(RenderState) fake_state = get_fake_view_frustum_cull_state(); 00155 _state = _state->compose(fake_state); 00156 00157 } else if ((result & BoundingVolume::IF_all) != 0) { 00158 // The node and its descendents are completely enclosed within 00159 // the frustum. No need to cull further. 00160 _view_frustum = (GeometricBoundingVolume *)NULL; 00161 00162 } else { 00163 // The node is partially, but not completely, within the viewing 00164 // frustum. 00165 if (_node_reader.is_final()) { 00166 // Normally we'd keep testing child bounding volumes as we 00167 // continue down. But this node has the "final" flag, so the 00168 // user is claiming that there is some important reason we 00169 // should consider everything visible at this point. So be it. 00170 _view_frustum = (GeometricBoundingVolume *)NULL; 00171 } 00172 } 00173 } 00174 00175 if (!_cull_planes->is_empty()) { 00176 // Also cull against the current clip planes. 00177 int result; 00178 _cull_planes = _cull_planes->do_cull(result, _state, node_gbv); 00179 00180 if (pgraph_cat.is_spam()) { 00181 pgraph_cat.spam() 00182 << _node_path << " cull planes cull result = " << hex 00183 << result << dec << "\n"; 00184 _cull_planes->write(pgraph_cat.spam(false)); 00185 } 00186 00187 if (_node_reader.is_final()) { 00188 // Even though the node may be partially within the clip planes, 00189 // do no more culling against them below this node. 00190 _cull_planes = CullPlanes::make_empty(); 00191 00192 if (pgraph_cat.is_spam()) { 00193 pgraph_cat.spam() 00194 << _node_path << " is_final, cull planes disabled, state:\n"; 00195 _state->write(pgraph_cat.spam(false), 2); 00196 } 00197 } 00198 00199 if (result == BoundingVolume::IF_no_intersection) { 00200 // No intersection at all. Cull. 00201 return false; 00202 00203 } else if ((result & BoundingVolume::IF_all) != 0) { 00204 // The node and its descendents are completely in front of all 00205 // of the clip planes. The do_cull() call should therefore have 00206 // removed all of the clip planes. 00207 nassertr(_cull_planes->is_empty(), true); 00208 } 00209 } 00210 00211 return true; 00212 } 00213 00214 //////////////////////////////////////////////////////////////////// 00215 // Function: CullTraverserData::get_fake_view_frustum_cull_state 00216 // Access: Private, Static 00217 // Description: Returns a RenderState for rendering stuff in red 00218 // wireframe, strictly for the fake_view_frustum_cull 00219 // effect. 00220 //////////////////////////////////////////////////////////////////// 00221 CPT(RenderState) CullTraverserData:: 00222 get_fake_view_frustum_cull_state() { 00223 // Once someone asks for this pointer, we hold its reference count 00224 // and never free it. 00225 static CPT(RenderState) state = (const RenderState *)NULL; 00226 if (state == (const RenderState *)NULL) { 00227 state = RenderState::make 00228 (ColorAttrib::make_flat(Colorf(1.0f, 0.0f, 0.0f, 1.0f)), 00229 TextureAttrib::make_all_off(), 00230 RenderModeAttrib::make(RenderModeAttrib::M_wireframe), 00231 RenderState::get_max_priority()); 00232 } 00233 return state; 00234 } 00235