Panda3D

cullResult.cxx

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 
 All Classes Functions Variables Enumerations