# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

lighting_manager.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include "std3d.h"
00027 
00028 #include "3d/lighting_manager.h"
00029 #include "3d/point_light.h"
00030 #include "3d/transform.h"
00031 #include "3d/fast_floor.h"
00032 #include "nel/3d/logic_info.h"
00033 #include "nel/misc/aabbox.h"
00034 
00035 
00036 using namespace NLMISC;
00037 using namespace std;
00038 
00039 
00040 namespace NL3D {
00041 
00042 
00043 // ***************************************************************************
00044 /* LightQuadGrid setup. This is the same setup for StaticLightedModelQuadGrid setup.
00045         NB: with this setup, a light will lies into 4*4=16 squares of a quadGrid at max.
00046 */
00047 #define NL3D_LIGHT_QUAD_GRID_SIZE                               256
00048 #define NL3D_LIGHT_QUAD_GRID_ELTSIZE                    10.f
00049 #define NL3D_LIGHT_QUAD_GRID_RADIUS_LIMIT               20.f
00050 // Factor for a level to the next: size/=factor, eltSize*=factor, and radiusLimit*=factor.
00051 #define NL3D_LIGHT_QUAD_GRID_FACTOR                             4
00052 
00053 
00054 // this is a big radius for light that don't have attenuation
00055 #define NL3D_DEFAULT_NOATT_LIGHT_RADIUS                                 1000.f
00056 // this is used when the model is out of attBegin/attEnd, to modulate the influence
00057 #define NL3D_DEFAULT_OUT_OF_ATT_LIGHT_INF_FACTOR                0.1f
00058 // Defualt LightTransitionThreshold
00059 #define NL3D_DEFAULT_LIGHT_TRANSITION_THRESHOLD                 0.1f
00060 
00061 
00062 // ***************************************************************************
00063 CLightingManager::CLightingManager()
00064 {
00065         // Init the lightQuadGrids and StaticLightedModelQuadGrid
00066         // finer level.
00067         uint    qgSize= NL3D_LIGHT_QUAD_GRID_SIZE;
00068         float   eltSize= NL3D_LIGHT_QUAD_GRID_ELTSIZE;
00069         float   radiusLimit= NL3D_LIGHT_QUAD_GRID_RADIUS_LIMIT;
00070         // for all levels
00071         for(uint i=0;i<NL3D_QUADGRID_LIGHT_NUM_LEVEL;i++)
00072         {
00073                 // init _LightQuadGrid and _StaticLightedModelQuadGrid
00074                 _LightQuadGrid[i].create(qgSize, eltSize);
00075                 _StaticLightedModelQuadGrid[i].create(qgSize, eltSize);
00076                 _LightQuadGridRadiusLimit[i]= radiusLimit;
00077 
00078                 // coarser quadGrid level
00079                 qgSize/= NL3D_LIGHT_QUAD_GRID_FACTOR;
00080                 qgSize= max(qgSize, 1U);
00081                 eltSize*= NL3D_LIGHT_QUAD_GRID_FACTOR;
00082                 radiusLimit*= NL3D_LIGHT_QUAD_GRID_FACTOR;
00083         }
00084 
00085 
00086         // default paramters
00087         _NoAttLightRadius= NL3D_DEFAULT_NOATT_LIGHT_RADIUS;
00088         _OutOfAttLightInfFactor= NL3D_DEFAULT_OUT_OF_ATT_LIGHT_INF_FACTOR;
00089         _LightTransitionThreshold= NL3D_DEFAULT_LIGHT_TRANSITION_THRESHOLD;
00090 
00091 
00092         // Default number of pointLight on an object.
00093         setMaxLightContribution(3);
00094 }
00095 
00096 
00097 // ***************************************************************************
00098 void            CLightingManager::setMaxLightContribution(uint nlights)
00099 {
00100         _MaxLightContribution= min(nlights, (uint)NL3D_MAX_LIGHT_CONTRIBUTION);
00101 }
00102 
00103 
00104 // ***************************************************************************
00105 void            CLightingManager::setNoAttLightRadius(float noAttLightRadius)
00106 {
00107         nlassert(noAttLightRadius>0);
00108         _NoAttLightRadius= noAttLightRadius;
00109 }
00110 
00111 
00112 // ***************************************************************************
00113 void            CLightingManager::setOutOfAttLightInfFactor(float outOfAttLightInfFactor)
00114 {
00115         outOfAttLightInfFactor= max(0.f, outOfAttLightInfFactor);
00116         _OutOfAttLightInfFactor= outOfAttLightInfFactor;
00117 }
00118 
00119 
00120 // ***************************************************************************
00121 void            CLightingManager::setLightTransitionThreshold(float lightTransitionThreshold)
00122 {
00123         clamp(lightTransitionThreshold, 0.f, 1.f);
00124         _LightTransitionThreshold= lightTransitionThreshold;
00125 }
00126 
00127 
00128         
00129 // ***************************************************************************
00130 void            CLightingManager::clearDynamicLights()
00131 {
00132         // for all levels
00133         for(uint i=0;i<NL3D_QUADGRID_LIGHT_NUM_LEVEL;i++)
00134         {
00135                 // clear all the lights in the quadGrid.
00136                 _LightQuadGrid[i].clear();
00137         }
00138 
00139         // clear the list.
00140         _DynamicLightList.clear();
00141 }
00142 
00143 // ***************************************************************************
00144 void            CLightingManager::addDynamicLight(CPointLight *light)
00145 {
00146 
00147         // Insert the light in the quadGrid.
00148         //----------
00149         CPointLightInfo         plInfo;
00150         plInfo.Light= light;
00151 
00152         // build the bounding sphere for this light, with the AttenuationEnd of the light
00153         float   radius=light->getAttenuationEnd();
00154         // if attenuation is disabled (ie getAttenuationEnd() return 0), then have a dummy radius
00155         if(radius==0)
00156                 radius= _NoAttLightRadius;
00157         plInfo.Sphere.Center= light->getPosition();
00158         plInfo.Sphere.Radius= radius;
00159 
00160         // build a bbox, so it includes the sphere
00161         CVector bbmin= light->getPosition();
00162         bbmin-= CVector(radius, radius, radius);
00163         CVector bbmax= light->getPosition();
00164         bbmax+= CVector(radius, radius, radius);
00165 
00166         // choose the correct quadgrid according to the radius of the light.
00167         uint qgId;
00168         for(qgId= 0; qgId<NL3D_QUADGRID_LIGHT_NUM_LEVEL-1; qgId++)
00169         {
00170                 // if radius is inferior to the requested radius for this quadGrid, ok!
00171                 if(radius<_LightQuadGridRadiusLimit[qgId])
00172                         break;
00173         }
00174         // insert this light in the correct quadgrid.
00175         _LightQuadGrid[qgId].insert(bbmin, bbmax, plInfo);
00176 
00177 
00178         // touch the objects around the lights.
00179         //----------
00180 
00181         // Select the static lightedModels to update around this light.
00182         // Use the same level of _StaticLightedModelQuadGrid than me to not select too many squares in the quadGrid
00183         _StaticLightedModelQuadGrid[qgId].select(bbmin, bbmax);
00184         // For all those models.
00185         CQuadGrid<CTransform*>::CIterator       itModel= _StaticLightedModelQuadGrid[qgId].begin();
00186         while(itModel != _StaticLightedModelQuadGrid[qgId].end() )
00187         {
00188                 CTransform      *model= *itModel;
00189                 const CVector &modelPos= model->getWorldMatrix().getPos();
00190 
00191                 // test first if this model is in the area of the light.
00192                 if( plInfo.Sphere.include(modelPos) )
00193                 {
00194                         // yes, then this model must recompute his lighting, because a dynamic light touch him.
00195                         model->resetLighting();
00196                 }
00197 
00198                 // next.
00199                 itModel++;
00200         }
00201 
00202 
00203         // insert the light in the list.
00204         //----------
00205         _DynamicLightList.push_back(light);
00206 
00207 }
00208 
00209 // ***************************************************************************
00210 CLightingManager::CQGItLightedModel     CLightingManager::eraseStaticLightedModel(CQGItLightedModel ite)
00211 {
00212         // Erase the iterator for all levels
00213         for(uint i=0;i<NL3D_QUADGRID_LIGHT_NUM_LEVEL;i++)
00214         {
00215                 // NB: it is possible here that the iterator is NULL (ie end()).
00216                 _StaticLightedModelQuadGrid[i].erase(ite.QgItes[i]);
00217         }
00218 
00219         // return end(), ie NULL iterators
00220         return CQGItLightedModel();
00221 }
00222 
00223 // ***************************************************************************
00224 CLightingManager::CQGItLightedModel     CLightingManager::insertStaticLightedModel(CTransform *model)
00225 {
00226         CQGItLightedModel       ite;
00227         const CVector &worldPos= model->getWorldMatrix().getPos();
00228 
00229         // Insert the models in all levels, because addDynamicLight() may choose the best suited level to select
00230         for(uint i=0;i<NL3D_QUADGRID_LIGHT_NUM_LEVEL;i++)
00231         {
00232                 ite.QgItes[i]= _StaticLightedModelQuadGrid[i].insert(worldPos, worldPos, model);
00233         }
00234 
00235         // return the iterator
00236         return ite;
00237 }
00238 
00239 
00240 // ***************************************************************************
00241 struct  CSortLight
00242 {
00243         CPointLight             *PointLight;
00244         float                   Influence;
00245 
00246 };
00247 
00248 // ***************************************************************************
00249 void            CLightingManager::computeModelLightContributions(CTransform *model, CLightContribution &lightContrib, 
00250         ILogicInfo *logicInfo)
00251 {
00252         sint    i;
00253 
00254         // This is the list of light which touch this model.
00255         static std::vector<CPointLightInfluence>        lightList;
00256         // static, for malloc perf.
00257         lightList.clear();
00258 
00259         // the position of the model.
00260         CVector modelPos;
00261         float   modelRadius;
00262 
00263         // get the untransformed bbox from the model.
00264         CAABBox         bbox;
00265         model->getAABBox(bbox);
00266         // get transformed center pos of bbox
00267         modelPos= model->getWorldMatrix() * bbox.getCenter();
00268         // If the model is a big lightable, must take radius from aabbox, else suppose 0 radius.
00269         if(model->isBigLightable())
00270         {
00271                 // get size of the bbox (bounding sphere)
00272                 modelRadius= bbox.getRadius();
00273         }
00274         else
00275         {
00276                 // Assume 0 radius => faster computeLinearAttenuation()
00277                 modelRadius= 0;
00278         }
00279 
00280 
00281         // First pass, fill the list of light which touch this model.
00282         //=========
00283         // get the dynamic lights around the model
00284         getDynamicPointLightList(modelPos, lightList);
00285 
00286         // if not already precomputed, append staticLights to this list.
00287         if( !lightContrib.FrozenStaticLightSetup )
00288         {
00289                 // If no logicInfo provided
00290                 if(!logicInfo)
00291                 {
00292                         // Default: suppose full SunLight and no PointLights.
00293                         lightContrib.SunContribution= 255;
00294                         // Take full SunAmbient.
00295                         lightContrib.LocalAmbient.set(0,0,0,0);
00296                         // do not append any pointLight to the setup
00297                 }
00298                 else
00299                 {
00300                         // NB: SunContribution is computed by logicInfo
00301                         logicInfo->getStaticLightSetup(lightList, lightContrib.SunContribution, lightContrib.LocalAmbient);
00302                 }
00303         }
00304 
00305         // Second pass, in the lightList, choose the best suited light to lit this model
00306         //=========
00307 
00308         // for each light, modulate the factor of influence
00309         for(i=0; i<(sint)lightList.size();i++)
00310         {
00311                 CPointLight     *pl= lightList[i].PointLight;
00312 
00313                 // get the distance from the light to the model
00314                 float   dist= (pl->getPosition() - modelPos).norm();
00315                 float   distMinusRadius= dist - modelRadius;
00316 
00317                 // modulate the factor by the distance and the attenuation distance.
00318                 float    inf;
00319                 float   attBegin= pl->getAttenuationBegin();
00320                 float   attEnd= pl->getAttenuationEnd();
00321                 // if no attenuation
00322                 if( attEnd==0 )
00323                 {
00324                         // influence is awlays 1.
00325                         inf= 1;
00326 
00327                         // If SpotLight, must modulate with SpotAttenuation.
00328                         if(pl->getType() == CPointLight::SpotLight)
00329                                 inf*= pl->computeLinearAttenuation(modelPos, dist, modelRadius);
00330                 }
00331                 else
00332                 {
00333                         // if correct attenuation radius
00334                         if(distMinusRadius<attBegin)
00335                         {
00336                                 // NB: we are sure that attBegin>0, because dist>=0.
00337                                 // if < attBegin, inf should be ==1, but must select the nearest lights; for better
00338                                 // understanding of the scene
00339                                 inf= 1 + _OutOfAttLightInfFactor * (attBegin - distMinusRadius);        // inf E [1, +oo[
00340                                 // NB: this formula favour big lights (ie light with big attBegin).
00341 
00342                                 // If SpotLight, must modulate with SpotAttenuation.
00343                                 if(pl->getType() == CPointLight::SpotLight)
00344                                         inf*= pl->computeLinearAttenuation(modelPos, dist, modelRadius);
00345                         }
00346                         else if(distMinusRadius<attEnd)
00347                         {
00348                                 // we are sure attEnd-attBegin>0 because of the test
00349                                 // compute influence of the light: attenuation and SpotAttenuation
00350                                 inf= pl->computeLinearAttenuation(modelPos, dist, modelRadius);
00351                         }
00352                         else
00353                         {
00354                                 // if >= attEnd, inf should be ==0, but must select the nearest lights; for better
00355                                 // understanding of the scene
00356                                 inf= _OutOfAttLightInfFactor * (attEnd - distMinusRadius);              // inf E ]-oo, 0]
00357                         }
00358                 }
00359 
00360                 // modulate the influence with this factor
00361                 lightList[i].BkupInfluence= lightList[i].Influence;
00362                 lightList[i].Influence*= inf;
00363 
00364                 // Bkup distance to model.
00365                 lightList[i].DistanceToModel= dist;
00366         }
00367 
00368         // sort the light by influence
00369         sort(lightList.begin(), lightList.end());
00370 
00371 
00372         // and choose only max light.
00373         uint    startId= 0;
00374         uint    ligthSrcId= 0;
00375         // skip already setuped light (statically)
00376         if(lightContrib.FrozenStaticLightSetup)
00377                 startId= lightContrib.NumFrozenStaticLight;
00378 
00379         // If there is still place for Dynamic lights.
00380         if(startId < _MaxLightContribution)
00381         {
00382                 // setup the transition.
00383                 float   deltaMinInfluence= _LightTransitionThreshold;
00384                 float   minInfluence= 0;
00385                 // If there is more light that we can accept, 
00386                 if(lightList.size() > _MaxLightContribution-startId)
00387                 {
00388                         // the minInfluence is the influence of the first light not taken.
00389                         minInfluence= lightList[_MaxLightContribution-startId].Influence;
00390                         // but must still be >=0.
00391                         minInfluence= max(minInfluence, 0.f);
00392                 }
00393                 // Any light under this minInfluence+deltaMinInfluence will be smoothly darken.
00394                 float   minInfluenceStart = minInfluence + deltaMinInfluence;
00395                 float   OOdeltaMinInfluencex255= 255.f / deltaMinInfluence;
00396 
00397                 // fill maxLight at max.
00398                 for(i=startId;i<(sint)_MaxLightContribution; i++)
00399                 {
00400                         // if not so many pointLights found, end!!
00401                         if(ligthSrcId>=lightList.size())
00402                                 break;
00403                         else
00404                         {
00405                                 CPointLight     *pl= lightList[ligthSrcId].PointLight;
00406                                 float           inf= lightList[ligthSrcId].Influence;
00407                                 float           bkupInf= lightList[ligthSrcId].BkupInfluence;
00408                                 float           distToModel= lightList[ligthSrcId].DistanceToModel;
00409                                 // else fill it.
00410                                 lightContrib.PointLight[i]= pl;
00411 
00412                                 // Compute the Final factor of influence of the light.
00413                                 if(inf >= minInfluenceStart)
00414                                 {
00415                                         // For Static LightSetup BiLinear to work correctly, modulate with BkupInfluence
00416                                         // don't worry about the precision of floor, because of *255.
00417                                         lightContrib.Factor[i]= (uint8)OptFastFloor(bkupInf*255);
00418                                 }
00419                                 else
00420                                 {
00421                                         float   f= (inf-minInfluence) * OOdeltaMinInfluencex255;
00422                                         sint    fi;
00423                                         // For Static LightSetup BiLinear to work correctly, modulate with BkupInfluence
00424                                         // don't worry about the precision of floor, because of *255.
00425                                         fi= OptFastFloor( bkupInf*f );
00426                                         clamp(fi, 0, 255);
00427                                         lightContrib.Factor[i]= fi;
00428                                 }
00429 
00430                                 // Compute the Final Att factor for models using Global Attenuation. NB: modulate with Factor
00431                                 // don't worry about the precision of floor, because of *255.
00432                                 // NB: compute att on the center of the model => modelRadius==0
00433                                 sint    attFactor= OptFastFloor( lightContrib.Factor[i] * pl->computeLinearAttenuation(modelPos, distToModel) );
00434                                 lightContrib.AttFactor[i]= (uint8)attFactor;
00435 
00436                                 // must append this lightedModel to the list in the light.
00437                                 lightContrib.TransformIterator[i]= pl->appendLightedModel(model);
00438 
00439                                 // next light.
00440                                 ligthSrcId++;
00441                         }
00442                 }
00443         }
00444         else
00445         {
00446                 // point to end the list
00447                 i= startId;
00448         }
00449 
00450         // End the list.
00451         if(i<NL3D_MAX_LIGHT_CONTRIBUTION)
00452         {
00453                 lightContrib.PointLight[i]= NULL;
00454         }
00455 
00456 }
00457 
00458 
00459 // ***************************************************************************
00460 void            CLightingManager::getDynamicPointLightList(const CVector &worldPos, std::vector<CPointLightInfluence>   &lightList)
00461 {
00462         // For all quadGrids.
00463         for(uint qgId=0; qgId<NL3D_QUADGRID_LIGHT_NUM_LEVEL; qgId++)
00464         {
00465                 CQuadGrid<CPointLightInfo>      &quadGrid= _LightQuadGrid[qgId];
00466 
00467                 // select the lights around this position in the quadGrids.
00468                 quadGrid.select(worldPos, worldPos);
00469 
00470                 // for all possible found lights
00471                 CQuadGrid<CPointLightInfo>::CIterator   itLight;
00472                 for(itLight= quadGrid.begin(); itLight!=quadGrid.end(); itLight++)
00473                 {
00474                         // verify it includes the entity
00475                         if( (*itLight).Sphere.include(worldPos) )
00476                         {
00477                                 // ok, insert in list.
00478                                 CPointLightInfluence    pli;
00479                                 pli.PointLight= (*itLight).Light;
00480                                 // No special Influence degradation scheme for Dynamic lighting
00481                                 pli.Influence= 1;
00482                                 lightList.push_back( pli );
00483                         }
00484                 }
00485         }
00486 }
00487 
00488 
00489 
00490 } // NL3D