Panda3D
|
00001 // Filename: cullResult.cxx 00002 // Created by: drose (28Feb02) 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 "cullResult.h" 00016 #include "cullBinManager.h" 00017 #include "cullBinAttrib.h" 00018 #include "textureAttrib.h" 00019 #include "lightAttrib.h" 00020 #include "colorAttrib.h" 00021 #include "alphaTestAttrib.h" 00022 #include "depthWriteAttrib.h" 00023 #include "colorScaleAttrib.h" 00024 #include "fogAttrib.h" 00025 #include "transparencyAttrib.h" 00026 #include "renderState.h" 00027 #include "clockObject.h" 00028 #include "config_pgraph.h" 00029 00030 // This value is used instead of 1.0 to represent the alpha level of a 00031 // pixel that is to be considered "opaque" for the purposes of M_dual. 00032 00033 // Ideally, 1.0 is the only correct value for this. Realistically, we 00034 // have to fudge it lower for two reasons: 00035 00036 // (1) The modelers tend to paint textures with very slight 00037 // transparency levels in places that are not intended to be 00038 // transparent, without realizing it. These very faint transparency 00039 // regions are normally (almost) invisible, but when rendered with 00040 // M_dual they may be revealed as regions of poor alpha sorting. 00041 00042 // (2) There seems to be some problem in DX where, in certain 00043 // circumstances apparently related to automatic texture management, 00044 // it spontaneously drops out the bottom two bits of an eight-bit 00045 // alpha channel, causing a value of 255 to become a value of 252 00046 // instead. 00047 00048 // We use 256 as the denominator here (instead of, say, 255) because a 00049 // fractional power of two will have a terminating representation in 00050 // base 2, and thus will be more likely to have a precise value in 00051 // whatever internal representation the graphics API will use. 00052 static const float dual_opaque_level = 252.0f / 256.0f; 00053 static const double bin_color_flash_rate = 1.0; // 1 state change per second 00054 00055 //////////////////////////////////////////////////////////////////// 00056 // Function: CullResult::Constructor 00057 // Access: Public 00058 // Description: 00059 //////////////////////////////////////////////////////////////////// 00060 CullResult:: 00061 CullResult(GraphicsStateGuardianBase *gsg, 00062 const PStatCollector &draw_region_pcollector) : 00063 _gsg(gsg), 00064 _draw_region_pcollector(draw_region_pcollector) 00065 { 00066 } 00067 00068 //////////////////////////////////////////////////////////////////// 00069 // Function: CullResult::make_next 00070 // Access: Published 00071 // Description: Returns a newly-allocated CullResult object that 00072 // contains a copy of just the subset of the data from 00073 // this CullResult object that is worth keeping around 00074 // for next frame. 00075 //////////////////////////////////////////////////////////////////// 00076 PT(CullResult) CullResult:: 00077 make_next() const { 00078 PT(CullResult) new_result = new CullResult(_gsg, _draw_region_pcollector); 00079 new_result->_bins.reserve(_bins.size()); 00080 00081 CullBinManager *bin_manager = CullBinManager::get_global_ptr(); 00082 00083 for (size_t i = 0; i < _bins.size(); ++i) { 00084 CullBin *old_bin = _bins[i]; 00085 if (old_bin == (CullBin *)NULL || 00086 old_bin->get_bin_type() != bin_manager->get_bin_type(i)) { 00087 new_result->_bins.push_back((CullBin *)NULL); 00088 } else { 00089 new_result->_bins.push_back(old_bin->make_next()); 00090 } 00091 } 00092 00093 return new_result; 00094 } 00095 00096 //////////////////////////////////////////////////////////////////// 00097 // Function: CullResult::add_object 00098 // Access: Published 00099 // Description: Adds the indicated CullableObject to the appropriate 00100 // bin. The bin becomes the owner of the object 00101 // pointer, and will eventually delete it. 00102 //////////////////////////////////////////////////////////////////// 00103 void CullResult:: 00104 add_object(CullableObject *object, const CullTraverser *traverser) { 00105 static const Colorf flash_alpha_color(0.92, 0.96, 0.10, 1.0f); 00106 static const Colorf flash_binary_color(0.21f, 0.67f, 0.24f, 1.0f); 00107 static const Colorf flash_multisample_color(0.78f, 0.05f, 0.81f, 1.0f); 00108 static const Colorf flash_dual_color(0.92f, 0.01f, 0.01f, 1.0f); 00109 00110 bool force = !traverser->get_effective_incomplete_render(); 00111 Thread *current_thread = traverser->get_current_thread(); 00112 00113 // Check to see if there's a special transparency setting. 00114 const RenderState *state = object->_state; 00115 nassertv(state != (const RenderState *)NULL); 00116 00117 const TransparencyAttrib *trans = DCAST(TransparencyAttrib, state->get_attrib(TransparencyAttrib::get_class_slot())); 00118 if (trans != (const TransparencyAttrib *)NULL) { 00119 switch (trans->get_mode()) { 00120 case TransparencyAttrib::M_alpha: 00121 // M_alpha implies an alpha-write test, so we don't waste time 00122 // writing 0-valued pixels. 00123 object->_state = state->compose(get_alpha_state()); 00124 #ifndef NDEBUG 00125 check_flash_transparency(object->_state, flash_alpha_color); 00126 #endif 00127 break; 00128 00129 case TransparencyAttrib::M_binary: 00130 // M_binary is implemented by explicitly setting the alpha test. 00131 object->_state = state->compose(get_binary_state()); 00132 #ifndef NDEBUG 00133 check_flash_transparency(object->_state, flash_binary_color); 00134 #endif 00135 break; 00136 00137 case TransparencyAttrib::M_multisample: 00138 case TransparencyAttrib::M_multisample_mask: 00139 // The multisample modes are implemented using M_binary if the 00140 // GSG in use doesn't support multisample. 00141 if (!_gsg->get_supports_multisample()) { 00142 object->_state = state->compose(get_binary_state()); 00143 } 00144 #ifndef NDEBUG 00145 check_flash_transparency(object->_state, flash_multisample_color); 00146 #endif 00147 break; 00148 00149 case TransparencyAttrib::M_dual: 00150 #ifndef NDEBUG 00151 check_flash_transparency(object->_state, flash_dual_color); 00152 state = object->_state; 00153 #endif 00154 if (!m_dual) { 00155 // If m_dual is configured off, it becomes M_alpha. 00156 break; 00157 } 00158 00159 // M_dual is implemented by drawing the opaque parts first, 00160 // without transparency, then drawing the transparent parts 00161 // later. This means we must copy the object and add it to 00162 // both bins. We can only do this if we do not have an 00163 // explicit bin already applied; otherwise, M_dual falls back 00164 // to M_alpha. 00165 { 00166 const CullBinAttrib *bin_attrib = DCAST(CullBinAttrib, state->get_attrib(CullBinAttrib::get_class_slot())); 00167 if (bin_attrib == (CullBinAttrib *)NULL || 00168 bin_attrib->get_bin_name().empty()) { 00169 // We make a copy of the object to draw the transparent part 00170 // without decals; this gets placed in the transparent bin. 00171 #ifndef NDEBUG 00172 if (m_dual_transparent) 00173 #endif 00174 { 00175 CullableObject *transparent_part = new CullableObject(*object); 00176 CPT(RenderState) transparent_state = object->has_decals() ? 00177 get_dual_transparent_state_decals() : 00178 get_dual_transparent_state(); 00179 transparent_part->_state = state->compose(transparent_state); 00180 if (transparent_part->munge_geom 00181 (_gsg, _gsg->get_geom_munger(transparent_part->_state, current_thread), 00182 traverser, force)) { 00183 CullBin *bin = get_bin(transparent_part->_state->get_bin_index()); 00184 nassertv(bin != (CullBin *)NULL); 00185 #ifndef NDEBUG 00186 check_flash_bin(transparent_part->_state, bin); 00187 #endif 00188 bin->add_object(transparent_part, current_thread); 00189 } 00190 } 00191 00192 // Now we can draw the opaque part, with decals. This will 00193 // end up in the opaque bin. 00194 object->_state = state->compose(get_dual_opaque_state()); 00195 #ifndef NDEBUG 00196 if (!m_dual_opaque) { 00197 delete object; 00198 return; 00199 } 00200 #endif 00201 } 00202 // The object is assigned to a specific bin; M_dual becomes 00203 // M_alpha. 00204 } 00205 break; 00206 00207 default: 00208 // Other kinds of transparency need no special handling. 00209 break; 00210 } 00211 } 00212 00213 CullBin *bin = get_bin(object->_state->get_bin_index()); 00214 nassertv(bin != (CullBin *)NULL); 00215 00216 #ifndef NDEBUG 00217 check_flash_bin(object->_state, bin); 00218 #endif 00219 00220 // Munge vertices as needed for the GSG's requirements, and the 00221 // object's current state. 00222 if (object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser, force)) { 00223 // The object may or may not now be fully resident, but this may 00224 // not matter, since the GSG may have the necessary buffers 00225 // already loaded. We'll let the GSG ultimately decide whether to 00226 // render it. 00227 bin->add_object(object, current_thread); 00228 } 00229 } 00230 00231 //////////////////////////////////////////////////////////////////// 00232 // Function: CullResult::finish_cull 00233 // Access: Published 00234 // Description: Called after all the geoms have been added, this 00235 // indicates that the cull process is finished for this 00236 // frame and gives the bins a chance to do any 00237 // post-processing (like sorting) before moving on to 00238 // draw. 00239 //////////////////////////////////////////////////////////////////// 00240 void CullResult:: 00241 finish_cull(SceneSetup *scene_setup, Thread *current_thread) { 00242 CullBinManager *bin_manager = CullBinManager::get_global_ptr(); 00243 00244 for (size_t i = 0; i < _bins.size(); ++i) { 00245 if (!bin_manager->get_bin_active(i)) { 00246 // If the bin isn't active, don't sort it, and don't draw it. 00247 // In fact, clear it. 00248 _bins[i] = NULL; 00249 00250 } else { 00251 CullBin *bin = _bins[i]; 00252 if (bin != (CullBin *)NULL) { 00253 bin->finish_cull(scene_setup, current_thread); 00254 } 00255 } 00256 } 00257 } 00258 00259 //////////////////////////////////////////////////////////////////// 00260 // Function: CullResult::draw 00261 // Access: Published 00262 // Description: Asks all the bins to draw themselves in the correct 00263 // order. 00264 //////////////////////////////////////////////////////////////////// 00265 void CullResult:: 00266 draw(Thread *current_thread) { 00267 bool force = !_gsg->get_effective_incomplete_render(); 00268 00269 // Ask the bin manager for the correct order to draw all the bins. 00270 CullBinManager *bin_manager = CullBinManager::get_global_ptr(); 00271 int num_bins = bin_manager->get_num_bins(); 00272 for (int i = 0; i < num_bins; i++) { 00273 int bin_index = bin_manager->get_bin(i); 00274 nassertv(bin_index >= 0); 00275 00276 if (bin_index < (int)_bins.size() && _bins[bin_index] != (CullBin *)NULL) { 00277 _bins[bin_index]->draw(force, current_thread); 00278 } 00279 } 00280 } 00281 00282 //////////////////////////////////////////////////////////////////// 00283 // Function: CullResult::make_result_graph 00284 // Access: Published 00285 // Description: Returns a special scene graph constructed to 00286 // represent the results of the cull. This will be a 00287 // hierarchy of nodes, one node for each bin, each of 00288 // which will in term be a parent of a number of 00289 // GeomNodes, representing the geometry drawn in each 00290 // bin. 00291 // 00292 // This is useful mainly for high-level debugging and 00293 // abstraction tools; it should not be mistaken for the 00294 // low-level cull result itself. For the low-level cull 00295 // result, use draw() to efficiently draw the culled 00296 // scene. 00297 //////////////////////////////////////////////////////////////////// 00298 PT(PandaNode) CullResult:: 00299 make_result_graph() { 00300 PT(PandaNode) root_node = new PandaNode("cull_result"); 00301 00302 // Ask the bin manager for the correct order to draw all the bins. 00303 CullBinManager *bin_manager = CullBinManager::get_global_ptr(); 00304 int num_bins = bin_manager->get_num_bins(); 00305 for (int i = 0; i < num_bins; i++) { 00306 int bin_index = bin_manager->get_bin(i); 00307 nassertr(bin_index >= 0, NULL); 00308 00309 if (bin_index < (int)_bins.size() && _bins[bin_index] != (CullBin *)NULL) { 00310 root_node->add_child(_bins[bin_index]->make_result_graph()); 00311 } 00312 } 00313 00314 return root_node; 00315 } 00316 00317 //////////////////////////////////////////////////////////////////// 00318 // Function: CullResult::bin_removed 00319 // Access: Public, Static 00320 // Description: Intended to be called by 00321 // CullBinManager::remove_bin(), this informs all the 00322 // CullResults in the world to remove the indicated 00323 // bin_index from their cache if it has been cached. 00324 //////////////////////////////////////////////////////////////////// 00325 void CullResult:: 00326 bin_removed(int bin_index) { 00327 // Do something here. 00328 nassertv(false); 00329 } 00330 00331 //////////////////////////////////////////////////////////////////// 00332 // Function: CullResult::make_new_bin 00333 // Access: Private 00334 // Description: Allocates a new CullBin for the given bin_index and 00335 // stores it for next time. 00336 //////////////////////////////////////////////////////////////////// 00337 CullBin *CullResult:: 00338 make_new_bin(int bin_index) { 00339 CullBinManager *bin_manager = CullBinManager::get_global_ptr(); 00340 PT(CullBin) bin = bin_manager->make_new_bin(bin_index, _gsg, 00341 _draw_region_pcollector); 00342 if (bin != (CullBin *)NULL) { 00343 // Now store it in the vector. 00344 while (bin_index >= (int)_bins.size()) { 00345 _bins.push_back((CullBin *)NULL); 00346 } 00347 nassertr(bin_index >= 0 && bin_index < (int)_bins.size(), NULL); 00348 _bins[bin_index] = bin; 00349 } 00350 00351 return bin; 00352 } 00353 00354 //////////////////////////////////////////////////////////////////// 00355 // Function: CullResult::get_alpha_state 00356 // Access: Private 00357 // Description: Returns a RenderState that changes the alpha test to 00358 // > 0, for implementing M_alpha. 00359 //////////////////////////////////////////////////////////////////// 00360 CPT(RenderState) CullResult:: 00361 get_alpha_state() { 00362 static CPT(RenderState) state = NULL; 00363 if (state == (const RenderState *)NULL) { 00364 // We don't monkey with the priority, since we want to allow the 00365 // user to override this if he desires. 00366 state = RenderState::make(AlphaTestAttrib::make(AlphaTestAttrib::M_greater, 0.0f)); 00367 } 00368 return state; 00369 } 00370 00371 //////////////////////////////////////////////////////////////////// 00372 // Function: CullResult::get_binary_state 00373 // Access: Private 00374 // Description: Returns a RenderState that applies the effects of 00375 // M_binary. 00376 //////////////////////////////////////////////////////////////////// 00377 CPT(RenderState) CullResult:: 00378 get_binary_state() { 00379 static CPT(RenderState) state = NULL; 00380 if (state == (const RenderState *)NULL) { 00381 state = RenderState::make(AlphaTestAttrib::make(AlphaTestAttrib::M_greater_equal, 0.5f), 00382 TransparencyAttrib::make(TransparencyAttrib::M_none), 00383 RenderState::get_max_priority()); 00384 } 00385 return state; 00386 } 00387 00388 //////////////////////////////////////////////////////////////////// 00389 // Function: CullResult::check_flash_bin 00390 // Access: Private 00391 // Description: If the user configured flash-bin-binname, then update 00392 // the object's state to flash all the geometry in the 00393 // bin. 00394 //////////////////////////////////////////////////////////////////// 00395 void CullResult:: 00396 check_flash_bin(CPT(RenderState) &state, CullBin *bin) { 00397 if (bin->has_flash_color()) { 00398 int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * bin_color_flash_rate); 00399 if ((cycle & 1) == 0) { 00400 state = state->remove_attrib(TextureAttrib::get_class_slot()); 00401 state = state->remove_attrib(LightAttrib::get_class_slot()); 00402 state = state->remove_attrib(ColorScaleAttrib::get_class_slot()); 00403 state = state->remove_attrib(FogAttrib::get_class_slot()); 00404 state = state->add_attrib(ColorAttrib::make_flat(bin->get_flash_color()), 00405 RenderState::get_max_priority()); 00406 } 00407 } 00408 } 00409 00410 //////////////////////////////////////////////////////////////////// 00411 // Function: CullResult::check_flash_transparency 00412 // Access: Private 00413 // Description: If the user configured show-transparency, then 00414 // update the object's state to flash the current 00415 // geometry with the specified color. 00416 //////////////////////////////////////////////////////////////////// 00417 void CullResult:: 00418 check_flash_transparency(CPT(RenderState) &state, const Colorf &transparency) { 00419 if (show_transparency) { 00420 int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * bin_color_flash_rate); 00421 if ((cycle & 1) == 0) { 00422 state = state->remove_attrib(TextureAttrib::get_class_slot()); 00423 state = state->remove_attrib(LightAttrib::get_class_slot()); 00424 state = state->remove_attrib(ColorScaleAttrib::get_class_slot()); 00425 state = state->remove_attrib(FogAttrib::get_class_slot()); 00426 state = state->add_attrib(ColorAttrib::make_flat(transparency), 00427 RenderState::get_max_priority()); 00428 } 00429 } 00430 } 00431 00432 //////////////////////////////////////////////////////////////////// 00433 // Function: CullResult::get_dual_transparent_state 00434 // Access: Private 00435 // Description: Returns a RenderState that renders only the 00436 // transparent parts of an object, in support of M_dual. 00437 // This state is suitable only for objects that do not 00438 // contain decals. 00439 //////////////////////////////////////////////////////////////////// 00440 CPT(RenderState) CullResult:: 00441 get_dual_transparent_state() { 00442 static CPT(RenderState) state = NULL; 00443 if (state == (const RenderState *)NULL) { 00444 // The alpha test for > 0 prevents us from drawing empty pixels, 00445 // and hence filling up the depth buffer with large empty spaces 00446 // that may obscure other things. However, this does mean we draw 00447 // pixels twice where the alpha == 1.0 (since they were already 00448 // drawn in the opaque pass). This is not normally a problem, 00449 // except when we are using decals; in the case of decals, we 00450 // don't want to draw the 1.0 pixels again, since these are the 00451 // ones that may have been decaled onto. 00452 state = RenderState::make(AlphaTestAttrib::make(AlphaTestAttrib::M_greater, 0.0f), 00453 TransparencyAttrib::make(TransparencyAttrib::M_alpha), 00454 DepthWriteAttrib::make(DepthWriteAttrib::M_off), 00455 RenderState::get_max_priority()); 00456 } 00457 00458 #ifndef NDEBUG 00459 if (m_dual_flash) { 00460 int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * bin_color_flash_rate); 00461 if ((cycle & 1) == 0) { 00462 static CPT(RenderState) flash_state = NULL; 00463 if (flash_state == (const RenderState *)NULL) { 00464 flash_state = state->add_attrib(ColorAttrib::make_flat(Colorf(0.8f, 0.2f, 0.2f, 1.0f)), 00465 RenderState::get_max_priority()); 00466 00467 flash_state = flash_state->add_attrib(ColorScaleAttrib::make(LVecBase4f(1.0f, 1.0f, 1.0f, 1.0f)), 00468 RenderState::get_max_priority()); 00469 00470 flash_state = flash_state->add_attrib(AlphaTestAttrib::make(AlphaTestAttrib::M_less, 1.0f), 00471 RenderState::get_max_priority()); 00472 } 00473 return flash_state; 00474 } 00475 } 00476 #endif // NDEBUG 00477 00478 return state; 00479 } 00480 00481 //////////////////////////////////////////////////////////////////// 00482 // Function: CullResult::get_dual_transparent_state_decals 00483 // Access: Private 00484 // Description: Returns a RenderState that renders only the 00485 // transparent parts of an object, but suitable for 00486 // objects that contain decals. 00487 //////////////////////////////////////////////////////////////////// 00488 CPT(RenderState) CullResult:: 00489 get_dual_transparent_state_decals() { 00490 static CPT(RenderState) state = NULL; 00491 if (state == (const RenderState *)NULL) { 00492 // This is exactly the same as above except here we make the alpha 00493 // test of < 1.0 instead of > 0.0. This makes us draw big empty 00494 // pixels where the alpha values are 0.0, but we don't overwrite 00495 // the decals where the pixels are 1.0. 00496 state = RenderState::make(AlphaTestAttrib::make(AlphaTestAttrib::M_less, dual_opaque_level), 00497 TransparencyAttrib::make(TransparencyAttrib::M_alpha), 00498 DepthWriteAttrib::make(DepthWriteAttrib::M_off), 00499 RenderState::get_max_priority()); 00500 } 00501 00502 #ifndef NDEBUG 00503 if (m_dual_flash) { 00504 int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * bin_color_flash_rate); 00505 if ((cycle & 1) == 0) { 00506 static CPT(RenderState) flash_state = NULL; 00507 if (flash_state == (const RenderState *)NULL) { 00508 flash_state = state->add_attrib(ColorAttrib::make_flat(Colorf(0.8f, 0.2f, 0.2f, 1.0f)), 00509 RenderState::get_max_priority()); 00510 flash_state = flash_state->add_attrib(ColorScaleAttrib::make(LVecBase4f(1.0f, 1.0f, 1.0f, 1.0f)), 00511 RenderState::get_max_priority()); 00512 00513 } 00514 return flash_state; 00515 } 00516 } 00517 #endif // NDEBUG 00518 00519 return state; 00520 } 00521 00522 //////////////////////////////////////////////////////////////////// 00523 // Function: CullResult::get_dual_opaque_state 00524 // Access: Private 00525 // Description: Returns a RenderState that renders only the 00526 // opaque parts of an object, in support of M_dual. 00527 //////////////////////////////////////////////////////////////////// 00528 CPT(RenderState) CullResult:: 00529 get_dual_opaque_state() { 00530 static CPT(RenderState) state = NULL; 00531 if (state == (const RenderState *)NULL) { 00532 state = RenderState::make(AlphaTestAttrib::make(AlphaTestAttrib::M_greater_equal, dual_opaque_level), 00533 TransparencyAttrib::make(TransparencyAttrib::M_none), 00534 RenderState::get_max_priority()); 00535 } 00536 00537 #ifndef NDEBUG 00538 if (m_dual_flash) { 00539 int cycle = (int)(ClockObject::get_global_clock()->get_frame_time() * bin_color_flash_rate); 00540 if ((cycle & 1) == 0) { 00541 static CPT(RenderState) flash_state = NULL; 00542 if (flash_state == (const RenderState *)NULL) { 00543 flash_state = state->add_attrib(ColorAttrib::make_flat(Colorf(0.2f, 0.2f, 0.8f, 1.0f)), 00544 RenderState::get_max_priority()); 00545 flash_state = flash_state->add_attrib(ColorScaleAttrib::make(LVecBase4f(1.0f, 1.0f, 1.0f, 1.0f)), 00546 RenderState::get_max_priority()); 00547 00548 } 00549 return flash_state; 00550 } 00551 } 00552 #endif // NDEBUG 00553 00554 return state; 00555 } 00556