Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/pgraph/cullResult.cxx

Go to the documentation of this file.
00001 // Filename: cullResult.cxx
00002 // Created by:  drose (28Feb02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "cullResult.h"
00020 #include "cullBinManager.h"
00021 #include "alphaTestAttrib.h"
00022 #include "transparencyAttrib.h"
00023 #include "renderState.h"
00024 #include "clockObject.h"
00025 
00026 // This value is used instead of 1.0 to represent the alpha level of a
00027 // pixel that is to be considered "opaque" for the purposes of M_dual.
00028 
00029 // Ideally, 1.0 is the only correct value for this.  Realistically, we
00030 // have to fudge it lower for two reasons:
00031 
00032 // (1) The modelers tend to paint textures with very slight
00033 // transparency levels in places that are not intended to be
00034 // transparent, without realizing it.  These very faint transparency
00035 // regions are normally (almost) invisible, but when rendered with
00036 // M_dual they may be revealed as regions of poor alpha sorting.
00037 
00038 // (2) There seems to be some problem in DX where, in certain
00039 // circumstances apparently related to automatic texture management,
00040 // it spontaneously drops out the bottom two bits of an eight-bit
00041 // alpha channel, causing a value of 255 to become a value of 252
00042 // instead.
00043 
00044 // We use 256 as the denominator here (instead of, say, 255) because a
00045 // fractional power of two will have a terminating representation in
00046 // base 2, and thus will be more likely to have a precise value in
00047 // whatever internal representation the graphics API will use.
00048 static const float dual_opaque_level = 252.0f / 256.0f;
00049 
00050 ////////////////////////////////////////////////////////////////////
00051 //     Function: CullResult::make_next
00052 //       Access: Public
00053 //  Description: Returns a newly-allocated CullResult object that
00054 //               contains a copy of just the subset of the data from
00055 //               this CullResult object that is worth keeping around
00056 //               for next frame.
00057 ////////////////////////////////////////////////////////////////////
00058 PT(CullResult) CullResult::
00059 make_next() const {
00060   PT(CullResult) new_result = new CullResult(_gsg);
00061   new_result->_bins.reserve(_bins.size());
00062 
00063   for (Bins::const_iterator bi = _bins.begin(); bi != _bins.end(); ++bi) {
00064     CullBin *old_bin = (*bi);
00065     if (old_bin == (CullBin *)NULL) {
00066       new_result->_bins.push_back((CullBin *)NULL);
00067     } else {
00068       new_result->_bins.push_back(old_bin->make_next());
00069     }
00070   }
00071 
00072   return new_result;
00073 }
00074 
00075 ////////////////////////////////////////////////////////////////////
00076 //     Function: CullResult::add_object
00077 //       Access: Public
00078 //  Description: Adds the indicated CullableObject to the appropriate
00079 //               bin.  The bin becomes the owner of the object
00080 //               pointer, and will eventually delete it.
00081 ////////////////////////////////////////////////////////////////////
00082 void CullResult::
00083 add_object(CullableObject *object) {
00084   // Check to see if there's a special transparency setting.
00085   const RenderState *state = object->_state;
00086   nassertv(state != (const RenderState *)NULL);
00087 
00088   const TransparencyAttrib *trans = state->get_transparency();
00089   if (trans != (const TransparencyAttrib *)NULL) {
00090     switch (trans->get_mode()) {
00091     case TransparencyAttrib::M_binary:
00092       // M_binary is implemented by explicitly setting the alpha test.
00093       object->_state = state->compose(get_binary_state());
00094       break;
00095 
00096     case TransparencyAttrib::M_dual:
00097       if (m_dual) {
00098         // M_dual is implemented by drawing the opaque parts first,
00099         // without transparency, then drawing the transparent parts
00100         // later.  This means we must copy the object and add it to
00101         // both bins.  We can only do this if we do not have an
00102         // explicit bin already applied; otherwise, M_dual falls back
00103         // to M_alpha.
00104         const CullBinAttrib *bin_attrib = state->get_bin();
00105         if (bin_attrib == (CullBinAttrib *)NULL || 
00106             bin_attrib->get_bin_name().empty()) {
00107           // We make a copy of the object to draw the transparent part
00108           // without decals; this gets placed in the transparent bin.
00109 #ifndef NDEBUG
00110           if (m_dual_transparent) 
00111 #endif
00112           {
00113             CullableObject *transparent_part = new CullableObject(*object);
00114             CPT(RenderState) transparent_state = object->has_decals() ? 
00115               get_dual_transparent_state_decals() : 
00116               get_dual_transparent_state();
00117             transparent_part->_state = state->compose(transparent_state);
00118             CullBin *bin = get_bin(transparent_part->_state->get_bin_index());
00119             nassertv(bin != (CullBin *)NULL);
00120             bin->add_object(transparent_part);
00121           }
00122 
00123           // Now we can draw the opaque part, with decals.  This will
00124           // end up in the opaque bin.
00125           object->_state = state->compose(get_dual_opaque_state());
00126 #ifndef NDEBUG
00127           if (!m_dual_opaque) {
00128             delete object;
00129             return;
00130           }
00131 #endif
00132         }
00133       }
00134       break;
00135 
00136     default:
00137       // Other kinds of transparency need no special handling.
00138       break;
00139     }
00140   }
00141   
00142   CullBin *bin = get_bin(object->_state->get_bin_index());
00143   nassertv(bin != (CullBin *)NULL);
00144   bin->add_object(object);
00145 }
00146 
00147 ////////////////////////////////////////////////////////////////////
00148 //     Function: CullResult::finish_cull
00149 //       Access: Public
00150 //  Description: Called after all the geoms have been added, this
00151 //               indicates that the cull process is finished for this
00152 //               frame and gives the bins a chance to do any
00153 //               post-processing (like sorting) before moving on to
00154 //               draw.
00155 ////////////////////////////////////////////////////////////////////
00156 void CullResult::
00157 finish_cull() {
00158   for (Bins::iterator bi = _bins.begin(); bi != _bins.end(); ++bi) {
00159     CullBin *bin = (*bi);
00160     if (bin != (CullBin *)NULL) {
00161       bin->finish_cull();
00162     }
00163   }
00164 }
00165 
00166 ////////////////////////////////////////////////////////////////////
00167 //     Function: CullResult::draw
00168 //       Access: Public
00169 //  Description: Asks all the bins to draw themselves in the correct
00170 //               order.
00171 ////////////////////////////////////////////////////////////////////
00172 void CullResult::
00173 draw() {
00174   // Ask the bin manager for the correct order to draw all the bins.
00175   CullBinManager *bin_manager = CullBinManager::get_global_ptr();
00176   int num_bins = bin_manager->get_num_bins();
00177   for (int i = 0; i < num_bins; i++) {
00178     int bin_index = bin_manager->get_bin(i);
00179     nassertv(bin_index >= 0);
00180 
00181     if (bin_index < (int)_bins.size() && _bins[bin_index] != (CullBin *)NULL) {
00182       _bins[bin_index]->draw();
00183     }
00184   }
00185 }
00186 
00187 ////////////////////////////////////////////////////////////////////
00188 //     Function: CullResult::bin_removed
00189 //       Access: Public, Static
00190 //  Description: Intended to be called by
00191 //               CullBinManager::remove_bin(), this informs all the
00192 //               CullResults in the world to remove the indicated
00193 //               bin_index from their cache if it has been cached.
00194 ////////////////////////////////////////////////////////////////////
00195 void CullResult::
00196 bin_removed(int bin_index) {
00197   // Do something here.
00198   nassertv(false);
00199 }
00200 
00201 ////////////////////////////////////////////////////////////////////
00202 //     Function: CullResult::make_new_bin
00203 //       Access: Private
00204 //  Description: Allocates a new CullBin for the given bin_index and
00205 //               stores it for next time.
00206 ////////////////////////////////////////////////////////////////////
00207 CullBin *CullResult::
00208 make_new_bin(int bin_index) {
00209   CullBinManager *bin_manager = CullBinManager::get_global_ptr();
00210   PT(CullBin) bin = bin_manager->make_new_bin(bin_index, _gsg);
00211   if (bin != (CullBin *)NULL) {
00212     // Now store it in the vector.
00213     while (bin_index >= (int)_bins.size()) {
00214       _bins.push_back((CullBin *)NULL);
00215     }
00216     nassertr(bin_index >= 0 && bin_index < (int)_bins.size(), NULL);
00217     _bins[bin_index] = bin;
00218   }
00219 
00220   return bin;
00221 }
00222 
00223 ////////////////////////////////////////////////////////////////////
00224 //     Function: CullResult::get_binary_state
00225 //       Access: Private
00226 //  Description: Returns a RenderState that applies the effects of
00227 //               M_binary.
00228 ////////////////////////////////////////////////////////////////////
00229 CPT(RenderState) CullResult::
00230 get_binary_state() {
00231   static CPT(RenderState) state = NULL;
00232   if (state == (const RenderState *)NULL) {
00233     state = RenderState::make(AlphaTestAttrib::make(AlphaTestAttrib::M_greater_equal, 0.5f),
00234                               TransparencyAttrib::make(TransparencyAttrib::M_none),
00235                               RenderState::get_max_priority());
00236   }
00237   return state;
00238 }
00239 
00240 #ifndef NDEBUG
00241 static const double m_dual_flash_rate = 1.0;  // 1 state change per second
00242 #endif
00243 
00244 ////////////////////////////////////////////////////////////////////
00245 //     Function: CullResult::get_dual_transparent_state
00246 //       Access: Private
00247 //  Description: Returns a RenderState that renders only the
00248 //               transparent parts of an object, in support of M_dual.
00249 //               This state is suitable only for objects that do not
00250 //               contain decals.
00251 ////////////////////////////////////////////////////////////////////
00252 CPT(RenderState) CullResult::
00253 get_dual_transparent_state() {
00254   static CPT(RenderState) state = NULL;
00255   if (state == (const RenderState *)NULL) {
00256     // The alpha test for > 0 prevents us from drawing empty pixels,
00257     // and hence filling up the depth buffer with large empty spaces
00258     // that may obscure other things.  However, this does mean we draw
00259     // pixels twice where the alpha == 1.0 (since they were already
00260     // drawn in the opaque pass).  This is not normally a problem,
00261     // except when we are using decals; in the case of decals, we
00262     // don't want to draw the 1.0 pixels again, since these are the
00263     // ones that may have been decaled onto.
00264     state = RenderState::make(AlphaTestAttrib::make(AlphaTestAttrib::M_greater, 0.0f),
00265                               TransparencyAttrib::make(TransparencyAttrib::M_alpha),
00266                               RenderState::get_max_priority());
00267   }
00268 
00269 #ifndef NDEBUG
00270   if (m_dual_flash) {
00271     int cycle = (int)(ClockObject::get_global_clock()->get_real_time() * m_dual_flash_rate);
00272     if ((cycle & 1) == 0) {
00273       static CPT(RenderState) flash_state = NULL;
00274       if (flash_state == (const RenderState *)NULL) {
00275         flash_state = state->add_attrib(ColorScaleAttrib::make(LVecBase4f(0.8f, 0.2f, 0.2f, 1.0f)));
00276         flash_state = flash_state->add_attrib(AlphaTestAttrib::make(AlphaTestAttrib::M_less, 1.0f));
00277       }
00278       return flash_state;
00279     }
00280   }
00281 #endif  // NDEBUG
00282 
00283   return state;
00284 }
00285 
00286 ////////////////////////////////////////////////////////////////////
00287 //     Function: CullResult::get_dual_transparent_state_decals
00288 //       Access: Private
00289 //  Description: Returns a RenderState that renders only the
00290 //               transparent parts of an object, but suitable for
00291 //               objects that contain decals.
00292 ////////////////////////////////////////////////////////////////////
00293 CPT(RenderState) CullResult::
00294 get_dual_transparent_state_decals() {
00295   static CPT(RenderState) state = NULL;
00296   if (state == (const RenderState *)NULL) {
00297     // This is exactly the same as above except here we make the alpha
00298     // test of < 1.0 instead of > 0.0.  This makes us draw big empty
00299     // pixels where the alpha values are 0.0, but we don't overwrite
00300     // the decals where the pixels are 1.0.
00301     state = RenderState::make(AlphaTestAttrib::make(AlphaTestAttrib::M_less, dual_opaque_level),
00302                               TransparencyAttrib::make(TransparencyAttrib::M_alpha),
00303                               RenderState::get_max_priority());
00304   }
00305 
00306 #ifndef NDEBUG
00307   if (m_dual_flash) {
00308     int cycle = (int)(ClockObject::get_global_clock()->get_real_time() * m_dual_flash_rate);
00309     if ((cycle & 1) == 0) {
00310       static CPT(RenderState) flash_state = NULL;
00311       if (flash_state == (const RenderState *)NULL) {
00312         flash_state = state->add_attrib(ColorScaleAttrib::make(LVecBase4f(0.8f, 0.2f, 0.2f, 1.0f)));
00313       }
00314       return flash_state;
00315     }
00316   }
00317 #endif  // NDEBUG
00318 
00319   return state;
00320 }
00321 
00322 ////////////////////////////////////////////////////////////////////
00323 //     Function: CullResult::get_dual_opaque_state
00324 //       Access: Private
00325 //  Description: Returns a RenderState that renders only the
00326 //               opaque parts of an object, in support of M_dual.
00327 ////////////////////////////////////////////////////////////////////
00328 CPT(RenderState) CullResult::
00329 get_dual_opaque_state() {
00330   static CPT(RenderState) state = NULL;
00331   if (state == (const RenderState *)NULL) {
00332     state = RenderState::make(AlphaTestAttrib::make(AlphaTestAttrib::M_greater_equal, dual_opaque_level),
00333                               TransparencyAttrib::make(TransparencyAttrib::M_none),
00334                               RenderState::get_max_priority());
00335   }
00336 
00337 #ifndef NDEBUG
00338   if (m_dual_flash) {
00339     int cycle = (int)(ClockObject::get_global_clock()->get_real_time() * m_dual_flash_rate);
00340     if ((cycle & 1) == 0) {
00341       static CPT(RenderState) flash_state = NULL;
00342       if (flash_state == (const RenderState *)NULL) {
00343         flash_state = state->add_attrib(ColorScaleAttrib::make(LVecBase4f(0.2f, 0.2f, 0.8f, 1.0f)));
00344       }
00345       return flash_state;
00346     }
00347   }
00348 #endif  // NDEBUG
00349 
00350   return state;
00351 }

Generated on Fri May 2 00:41:28 2003 for Panda by doxygen1.3