Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / point_light.cpp
blobf280f12db39ffdf19650288384cc9f9475ed7dc3
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "std3d.h"
22 #include "nel/3d/point_light.h"
23 #include "nel/3d/light.h"
24 #include "nel/3d/transform.h"
25 #include "nel/misc/common.h"
27 using namespace NLMISC;
28 using namespace std;
30 #ifdef DEBUG_NEW
31 #define new DEBUG_NEW
32 #endif
34 namespace NL3D
38 // ***************************************************************************
39 //NLMISC::CBlockMemory<CTransform*, false> CPointLight::_LightedModelListMemory(NL3D_LIGHTED_MODEL_ALLOC_BLOCKSIZE);
42 // ***************************************************************************
43 CPointLight::CPointLight() : _LightedModels(/*&_LightedModelListMemory*/)
45 _Position= CVector::Null;
46 _Ambient= CRGBA::Black;
47 _Diffuse= _Specular= CRGBA::White;
49 // Default setup. this is arbitrary
50 _Type= PointLight;
51 _AttenuationBegin= 10;
52 _AttenuationEnd= 30;
54 // Default spot setup. this is arbitrary
55 _SpotDirection.set(0,1,0);
56 _SpotAngleBegin= float(NLMISC::Pi/4);
57 _SpotAngleEnd= float(NLMISC::Pi/2);
59 // compute AttenuationFactors only one time.
60 static bool done= false;
61 static float cAtt, lAtt, qAtt;
62 static float spotCOOD, spotCAE, spotEXP;
63 if(!done)
65 done= true;
66 computeAttenuationFactors();
67 computeSpotAttenuationFactors();
68 // bkup setup.
69 cAtt= _ConstantAttenuation;
70 lAtt= _LinearAttenuation;
71 qAtt= _QuadraticAttenuation;
72 spotCAE= _CosSpotAngleEnd;
73 spotCOOD= _OOCosSpotAngleDelta;
74 spotEXP= _SpotExponent;
76 else
78 // just copy bkuped setup.
79 _ConstantAttenuation= cAtt;
80 _LinearAttenuation= lAtt;
81 _QuadraticAttenuation= qAtt;
82 _CosSpotAngleEnd= spotCAE;
83 _OOCosSpotAngleDelta= spotCOOD;
84 _SpotExponent= spotEXP;
87 _AddAmbientWithSun= false;
91 // ***************************************************************************
92 CPointLight::CPointLight(const CPointLight &o) : _LightedModels(/*&_LightedModelListMemory*/)
94 // copy (no need to init)
95 operator=(o);
98 // ***************************************************************************
99 CPointLight &CPointLight::operator=(const CPointLight &o)
101 /// copy all but _LightedModels !!
102 _Type= o._Type;
103 _Position= o._Position;
104 _Ambient= o._Ambient;
105 _Diffuse= o._Diffuse;
106 _Specular= o._Specular;
108 _AttenuationBegin= o._AttenuationBegin;
109 _AttenuationEnd= o._AttenuationEnd;
110 _OODeltaAttenuation= o._OODeltaAttenuation;
111 _ConstantAttenuation= o._ConstantAttenuation;
112 _LinearAttenuation= o._LinearAttenuation;
113 _QuadraticAttenuation= o._QuadraticAttenuation;
115 _SpotDirection= o._SpotDirection;
116 _SpotAngleBegin= o._SpotAngleBegin;
117 _SpotAngleEnd= o._SpotAngleEnd;
118 _CosSpotAngleEnd= o._CosSpotAngleEnd;
119 _OOCosSpotAngleDelta= o._OOCosSpotAngleDelta;
120 _SpotExponent= o._SpotExponent;
122 _AddAmbientWithSun= o._AddAmbientWithSun;
124 return *this;
128 // ***************************************************************************
129 CPointLight::~CPointLight()
131 resetLightedModels();
135 // ***************************************************************************
136 void CPointLight::setType(TType type)
138 _Type= type;
140 CPointLight::TType CPointLight::getType() const
142 return _Type;
145 // ***************************************************************************
146 void CPointLight::setupAttenuation(float attenuationBegin, float attenuationEnd)
148 // set values.
149 attenuationBegin= max(attenuationBegin, 0.f);
150 attenuationEnd= max(attenuationEnd, attenuationBegin);
151 _AttenuationBegin= attenuationBegin;
152 _AttenuationEnd= attenuationEnd;
154 // update factors.
155 computeAttenuationFactors();
160 // ***************************************************************************
161 void CPointLight::setupSpotAngle(float spotAngleBegin, float spotAngleEnd)
163 clamp(spotAngleBegin, 0.f, float(Pi));
164 clamp(spotAngleEnd, spotAngleBegin, float(Pi));
165 _SpotAngleBegin= spotAngleBegin;
166 _SpotAngleEnd= spotAngleEnd;
168 // update factors.
169 computeSpotAttenuationFactors();
173 // ***************************************************************************
174 void CPointLight::setupSpotDirection(const CVector &dir)
176 _SpotDirection= dir;
177 _SpotDirection.normalize();
181 // ***************************************************************************
182 void CPointLight::computeAttenuationFactors()
184 // disable attenuation?
185 if(_AttenuationBegin==0 && _AttenuationEnd==0)
187 // setup for attenuation disabled.
188 _ConstantAttenuation= 1;
189 _LinearAttenuation= 0;
190 _QuadraticAttenuation= 0;
192 else
194 // precompute attenuation values, with help of CLight formula!!
195 CLight dummyLight;
196 dummyLight.setupAttenuation(_AttenuationBegin, _AttenuationEnd);
197 _ConstantAttenuation= dummyLight.getConstantAttenuation();
198 _LinearAttenuation= dummyLight.getLinearAttenuation();
199 _QuadraticAttenuation= dummyLight.getQuadraticAttenuation();
201 // setup _OODeltaAttenuation
202 _OODeltaAttenuation= _AttenuationEnd - _AttenuationBegin;
203 if(_OODeltaAttenuation <=0 )
204 _OODeltaAttenuation= 0;
205 else
206 _OODeltaAttenuation= 1.0f / _OODeltaAttenuation;
211 // ***************************************************************************
212 void CPointLight::computeSpotAttenuationFactors()
214 // Factors for linear Attenuation.
215 float cosSpotAngleBegin= (float)cosf(_SpotAngleBegin);
216 _CosSpotAngleEnd= (float)cos(_SpotAngleEnd);
217 if(cosSpotAngleBegin - _CosSpotAngleEnd > 0)
218 _OOCosSpotAngleDelta= 1.0f / (cosSpotAngleBegin - _CosSpotAngleEnd);
219 else
220 _OOCosSpotAngleDelta= 1e10f;
222 // compute an exponent such that at middleAngle, att is 0.5f.
223 float caMiddle= (cosSpotAngleBegin + _CosSpotAngleEnd) /2;
224 float divid=(float)log (caMiddle);
225 if (divid==0.f)
226 divid=0.0001f;
227 _SpotExponent= (float)(log (0.5)/divid);
231 // ***************************************************************************
232 void CPointLight::serial(NLMISC::IStream &f)
234 sint ver= f.serialVersion(2);
236 if(ver>=2)
237 f.serial(_AddAmbientWithSun);
238 else
239 _AddAmbientWithSun= false;
241 if(ver>=1)
243 f.serialEnum(_Type);
244 f.serial(_SpotDirection);
245 f.serial(_SpotAngleBegin);
246 f.serial(_SpotAngleEnd);
248 else if(f.isReading())
250 _Type= PointLight;
251 _SpotDirection.set(0,1,0);
252 _SpotAngleBegin= float(NLMISC::Pi/4);
253 _SpotAngleEnd= float(NLMISC::Pi/2);
256 f.serial(_Position);
257 f.serial(_Ambient);
258 f.serial(_Diffuse);
259 f.serial(_Specular);
260 f.serial(_AttenuationBegin);
261 f.serial(_AttenuationEnd);
263 // precompute.
264 if(f.isReading())
266 computeAttenuationFactors();
267 computeSpotAttenuationFactors();
273 // ***************************************************************************
274 float CPointLight::computeLinearAttenuation(const CVector &pos) const
276 return computeLinearAttenuation(pos, (pos - _Position).norm() );
279 // ***************************************************************************
280 float CPointLight::computeLinearAttenuation(const CVector &pos, float dist, float modelRadius) const
282 float gAtt;
284 // Attenuation Distance
285 if(_AttenuationEnd==0)
286 gAtt= 1;
287 else
289 float distMinusRadius= dist - modelRadius;
290 if(distMinusRadius<_AttenuationBegin)
291 gAtt= 1;
292 else if(distMinusRadius<_AttenuationEnd)
294 gAtt= (_AttenuationEnd - distMinusRadius) * _OODeltaAttenuation;
296 else
297 gAtt= 0;
300 // Spot Attenuation
301 if(_Type== SpotLight)
303 float spotAtt;
305 // Compute unnormalized direction
306 CVector dir= pos - _Position;
307 // get cosAngle(dir, SpotDirection):
308 float cosAngleDirSpot= (dir*_SpotDirection) / dist;
310 // Modify with modelRadius. NB: made Only for big models.
311 if(modelRadius>0)
313 // If the pointLight is in the model, consider no spotAtt
314 if(modelRadius > dist)
315 spotAtt= 1;
316 else
318 // compute the angle of the cone made by the model sphere and the pointLightCenter.
319 float cosAngleSphere= modelRadius / sqrtf( sqr(dist) + sqr(modelRadius) );
320 /* If this one is smaller than cosAngleDirSpot, it's mean that the angle of this cone is greater than the
321 angleDirSpot, hence a part of the sphere "ps" exist such that _SportDirection*(ps-_Position).normed() == 1
322 => no spotAttenuation
324 if(cosAngleSphere < cosAngleDirSpot)
325 spotAtt= 1;
326 else
328 // Must compute cos( AngleDirSpot-AngleSphere )
329 float sinAngleSphere= sqrtf(1 - sqr(cosAngleSphere));
330 float sinAngleDirSpot= sqrtf(1 - sqr(cosAngleDirSpot));
331 float cosDelta= cosAngleSphere * cosAngleDirSpot + sinAngleSphere * sinAngleDirSpot;
333 // spot attenuation on the exterior of the sphere
334 spotAtt= (cosDelta - _CosSpotAngleEnd) * _OOCosSpotAngleDelta;
338 else
340 // spot attenuation
341 spotAtt= (cosAngleDirSpot - _CosSpotAngleEnd) * _OOCosSpotAngleDelta;
344 // modulate
345 clamp(spotAtt, 0.f, 1.f);
346 gAtt*= spotAtt;
349 return gAtt;
352 // ***************************************************************************
353 void CPointLight::setupDriverLight(CLight &light, uint8 factor)
355 // expand 0..255 to 0..256, to avoid loss of precision.
356 uint ufactor= factor + (factor>>7); // add 0 or 1.
358 // modulate with factor
359 CRGBA ambient, diffuse, specular;
360 ambient.modulateFromuiRGBOnly(_Ambient, ufactor);
361 diffuse.modulateFromuiRGBOnly(_Diffuse, ufactor);
362 specular.modulateFromuiRGBOnly(_Specular, ufactor);
364 // setup the pointLight
365 if(_Type == SpotLight )
367 light.setupSpotLight(ambient, diffuse, specular, _Position, _SpotDirection,
368 _SpotExponent, float(NLMISC::Pi/2) ,
369 _ConstantAttenuation, _LinearAttenuation, _QuadraticAttenuation);
371 // PointLight or AmbientLight
372 else
374 light.setupPointLight(ambient, diffuse, specular, _Position, CVector::Null,
375 _ConstantAttenuation, _LinearAttenuation, _QuadraticAttenuation);
380 // ***************************************************************************
381 void CPointLight::setupDriverLightUserAttenuation(CLight &light, uint8 factor)
383 // expand 0..255 to 0..256, to avoid loss of precision.
384 uint ufactor= factor + (factor>>7); // add 0 or 1.
386 // modulate with factor
387 CRGBA ambient, diffuse, specular;
388 ambient.modulateFromuiRGBOnly(_Ambient, ufactor);
389 diffuse.modulateFromuiRGBOnly(_Diffuse, ufactor);
390 specular.modulateFromuiRGBOnly(_Specular, ufactor);
392 // setup the pointLight, disabling attenuation.
393 // NB: setup a pointLight even if it is a SpotLight because already attenuated
394 light.setupPointLight(ambient, diffuse, specular, _Position, CVector::Null,
395 1, 0, 0);
399 // ***************************************************************************
400 void CPointLight::resetLightedModels()
402 // For each transform, resetLighting him.
403 while(_LightedModels.begin() != _LightedModels.end() )
405 CTransform *model= *_LightedModels.begin();
406 // reset lighting
407 model->resetLighting();
409 // NB: the transform must erase him from this list.
410 nlassert( _LightedModels.begin() == _LightedModels.end() || *_LightedModels.begin() != model );
415 // ***************************************************************************
416 CPointLight::ItTransformList CPointLight::appendLightedModel(CTransform *model)
418 // append the entry in the list
419 _LightedModels.push_back(model);
420 ItTransformList it= _LightedModels.end();
421 it--;
422 return it;
424 // ***************************************************************************
425 void CPointLight::removeLightedModel(ItTransformList it)
427 // delete the entry in the list.
428 _LightedModels.erase(it);
430 // ***************************************************************************
431 void CPointLight::purge ()
433 //_LightedModelListMemory.purge();
436 // ***************************************************************************
437 void CPointLight::setAddAmbientWithSun(bool state)
439 _AddAmbientWithSun= state;
443 } // NL3D