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 DCAST(OccluderEffect, node_effects->get_effect(OccluderEffect::get_class_type()))); 00122 } 00123 } 00124 00125 //////////////////////////////////////////////////////////////////// 00126 // Function: CullTraverserData::is_in_view_impl 00127 // Access: Private 00128 // Description: The private implementation of is_in_view(). 00129 //////////////////////////////////////////////////////////////////// 00130 bool CullTraverserData:: 00131 is_in_view_impl() { 00132 CPT(BoundingVolume) node_volume = _node_reader.get_bounds(); 00133 nassertr(node_volume->is_of_type(GeometricBoundingVolume::get_class_type()), false); 00134 const GeometricBoundingVolume *node_gbv = 00135 DCAST(GeometricBoundingVolume, node_volume); 00136 00137 if (_view_frustum != (GeometricBoundingVolume *)NULL) { 00138 int result = _view_frustum->contains(node_gbv); 00139 00140 if (pgraph_cat.is_spam()) { 00141 pgraph_cat.spam() 00142 << _node_path << " cull result = " << hex << result << dec << "\n"; 00143 } 00144 00145 if (result == BoundingVolume::IF_no_intersection) { 00146 // No intersection at all. Cull. 00147 if (!fake_view_frustum_cull) { 00148 return false; 00149 } 00150 00151 // If we have fake view-frustum culling enabled, instead of 00152 // actually culling an object we simply force it to be drawn in 00153 // red wireframe. 00154 _view_frustum = (GeometricBoundingVolume *)NULL; 00155 CPT(RenderState) fake_state = get_fake_view_frustum_cull_state(); 00156 _state = _state->compose(fake_state); 00157 00158 } else if ((result & BoundingVolume::IF_all) != 0) { 00159 // The node and its descendents are completely enclosed within 00160 // the frustum. No need to cull further. 00161 _view_frustum = (GeometricBoundingVolume *)NULL; 00162 00163 } else { 00164 // The node is partially, but not completely, within the viewing 00165 // frustum. 00166 if (_node_reader.is_final()) { 00167 // Normally we'd keep testing child bounding volumes as we 00168 // continue down. But this node has the "final" flag, so the 00169 // user is claiming that there is some important reason we 00170 // should consider everything visible at this point. So be it. 00171 _view_frustum = (GeometricBoundingVolume *)NULL; 00172 } 00173 } 00174 } 00175 00176 if (!_cull_planes->is_empty()) { 00177 // Also cull against the current clip planes. 00178 int result; 00179 _cull_planes = _cull_planes->do_cull(result, _state, node_gbv); 00180 00181 if (pgraph_cat.is_spam()) { 00182 pgraph_cat.spam() 00183 << _node_path << " cull planes cull result = " << hex 00184 << result << dec << "\n"; 00185 _cull_planes->write(pgraph_cat.spam(false)); 00186 } 00187 00188 if (_node_reader.is_final()) { 00189 // Even though the node may be partially within the clip planes, 00190 // do no more culling against them below this node. 00191 _cull_planes = CullPlanes::make_empty(); 00192 00193 if (pgraph_cat.is_spam()) { 00194 pgraph_cat.spam() 00195 << _node_path << " is_final, cull planes disabled, state:\n"; 00196 _state->write(pgraph_cat.spam(false), 2); 00197 } 00198 } 00199 00200 if (result == BoundingVolume::IF_no_intersection) { 00201 // No intersection at all. Cull. 00202 if (!fake_view_frustum_cull) { 00203 return false; 00204 } 00205 _cull_planes = CullPlanes::make_empty(); 00206 CPT(RenderState) fake_state = get_fake_view_frustum_cull_state(); 00207 _state = _state->compose(fake_state); 00208 00209 } else if ((result & BoundingVolume::IF_all) != 0) { 00210 // The node and its descendents are completely in front of all 00211 // of the clip planes and occluders. The do_cull() call should 00212 // therefore have removed all of the clip planes and occluders. 00213 nassertr(_cull_planes->is_empty(), true); 00214 } 00215 } 00216 00217 return true; 00218 } 00219 00220 //////////////////////////////////////////////////////////////////// 00221 // Function: CullTraverserData::get_fake_view_frustum_cull_state 00222 // Access: Private, Static 00223 // Description: Returns a RenderState for rendering stuff in red 00224 // wireframe, strictly for the fake_view_frustum_cull 00225 // effect. 00226 //////////////////////////////////////////////////////////////////// 00227 CPT(RenderState) CullTraverserData:: 00228 get_fake_view_frustum_cull_state() { 00229 // Once someone asks for this pointer, we hold its reference count 00230 // and never free it. 00231 static CPT(RenderState) state = (const RenderState *)NULL; 00232 if (state == (const RenderState *)NULL) { 00233 state = RenderState::make 00234 (ColorAttrib::make_flat(LColor(1.0f, 0.0f, 0.0f, 1.0f)), 00235 TextureAttrib::make_all_off(), 00236 RenderModeAttrib::make(RenderModeAttrib::M_wireframe), 00237 RenderState::get_max_priority()); 00238 } 00239 return state; 00240 } 00241