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