1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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
;
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
51 _AttenuationBegin
= 10;
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
;
66 computeAttenuationFactors();
67 computeSpotAttenuationFactors();
69 cAtt
= _ConstantAttenuation
;
70 lAtt
= _LinearAttenuation
;
71 qAtt
= _QuadraticAttenuation
;
72 spotCAE
= _CosSpotAngleEnd
;
73 spotCOOD
= _OOCosSpotAngleDelta
;
74 spotEXP
= _SpotExponent
;
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)
98 // ***************************************************************************
99 CPointLight
&CPointLight::operator=(const CPointLight
&o
)
101 /// copy all but _LightedModels !!
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
;
128 // ***************************************************************************
129 CPointLight::~CPointLight()
131 resetLightedModels();
135 // ***************************************************************************
136 void CPointLight::setType(TType type
)
140 CPointLight::TType
CPointLight::getType() const
145 // ***************************************************************************
146 void CPointLight::setupAttenuation(float attenuationBegin
, float attenuationEnd
)
149 attenuationBegin
= max(attenuationBegin
, 0.f
);
150 attenuationEnd
= max(attenuationEnd
, attenuationBegin
);
151 _AttenuationBegin
= attenuationBegin
;
152 _AttenuationEnd
= attenuationEnd
;
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
;
169 computeSpotAttenuationFactors();
173 // ***************************************************************************
174 void CPointLight::setupSpotDirection(const CVector
&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;
194 // precompute attenuation values, with help of CLight formula!!
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;
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
);
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
);
227 _SpotExponent
= (float)(log (0.5)/divid
);
231 // ***************************************************************************
232 void CPointLight::serial(NLMISC::IStream
&f
)
234 sint ver
= f
.serialVersion(2);
237 f
.serial(_AddAmbientWithSun
);
239 _AddAmbientWithSun
= false;
244 f
.serial(_SpotDirection
);
245 f
.serial(_SpotAngleBegin
);
246 f
.serial(_SpotAngleEnd
);
248 else if(f
.isReading())
251 _SpotDirection
.set(0,1,0);
252 _SpotAngleBegin
= float(NLMISC::Pi
/4);
253 _SpotAngleEnd
= float(NLMISC::Pi
/2);
260 f
.serial(_AttenuationBegin
);
261 f
.serial(_AttenuationEnd
);
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
284 // Attenuation Distance
285 if(_AttenuationEnd
==0)
289 float distMinusRadius
= dist
- modelRadius
;
290 if(distMinusRadius
<_AttenuationBegin
)
292 else if(distMinusRadius
<_AttenuationEnd
)
294 gAtt
= (_AttenuationEnd
- distMinusRadius
) * _OODeltaAttenuation
;
301 if(_Type
== SpotLight
)
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.
313 // If the pointLight is in the model, consider no spotAtt
314 if(modelRadius
> dist
)
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
)
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
;
341 spotAtt
= (cosAngleDirSpot
- _CosSpotAngleEnd
) * _OOCosSpotAngleDelta
;
345 clamp(spotAtt
, 0.f
, 1.f
);
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
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
,
399 // ***************************************************************************
400 void CPointLight::resetLightedModels()
402 // For each transform, resetLighting him.
403 while(_LightedModels
.begin() != _LightedModels
.end() )
405 CTransform
*model
= *_LightedModels
.begin();
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();
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
;