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