Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / client / src / light_cycle_manager.cpp
blob2448bf1ae56d4b4da2db421f90ff6101e1630b20
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2018 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2014-2015 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "stdpch.h"
26 /////////////
27 // INCLUDE //
28 /////////////
29 // 3D Interface
30 #include "nel/3d/u_instance.h"
31 #include "nel/3d/u_scene.h"
32 #include "nel/3d/u_water.h"
33 #include "nel/3d/u_landscape.h"
35 // Client.
36 #include "light_cycle_manager.h"
37 #include "client_cfg.h"
38 #include "ig_client.h"
39 #include "user_entity.h"
40 #include "pacs_client.h"
41 #include "world_database_manager.h"
42 #include "continent_manager.h"
43 #include "continent.h"
44 #include "ig_enum.h"
45 #include "ig_callback.h"
46 #include "pacs_client.h"
47 #include "sound_manager.h"
48 #include "weather_manager_client.h"
49 #include "misc.h"
50 #include "interface_v3/interface_manager.h"
52 //#include "sound_manager.h" // \todo GUIGUI : uncomment after new FE done and class modified
55 H_AUTO_DECL(RZ_LightCycleManager)
57 ///////////
58 // USING //
59 ///////////
60 using namespace NLMISC;
61 using namespace NL3D;
62 using namespace NLPACS;
63 using namespace std;
66 ////////////
67 // EXTERN //
68 ////////////
69 extern UDriver *Driver;
70 extern UScene *Scene;
71 extern UScene *SceneRoot;
73 extern EGSPD::CSeason::TSeason CurrSeason;
76 ////////////
77 // EXTERN //
78 ////////////
79 extern ULandscape *Landscape;
81 static uint diffColors(CRGBA c1, CRGBA c2)
83 return (uint) maxof(abs((sint) c1.R - c2.R), abs((sint) c1.G - c2.G), abs((sint) c1.B - c2.B));
86 //-----------------------------------------------
87 float CLightCycleDesc::getNightTransitionLength() const
89 H_AUTO_USE(RZ_LightCycleManager)
90 return NightTransitionEndHour >= NightTransitionStartHour ? NightTransitionEndHour - NightTransitionStartHour
91 : NightTransitionEndHour + NumHours - NightTransitionStartHour;
94 //-----------------------------------------------
95 float CLightCycleDesc::getDawnTransitionLength() const
97 H_AUTO_USE(RZ_LightCycleManager)
98 return DawnTransitionEndHour >= DawnTransitionStartHour ? DawnTransitionEndHour - DawnTransitionStartHour
99 : DawnTransitionEndHour + NumHours - DawnTransitionStartHour;
102 //-----------------------------------------------
103 CLightCycleManager::CLightCycleManager() :
104 _Hour(0),
105 _Touched(true),
106 _ValidDesc(false),
107 _LightLevel(0),
108 _WeatherLighting(0),
109 _LastWeatherLighting(0),
110 _UpdateFreq(0),
111 _State(StateUnknown),
112 _PrevState(StateUnknown)
114 H_AUTO_USE(RZ_LightCycleManager)
118 //-----------------------------------------------
119 void CLightCycleManager::getLandscapeLightColor(NLMISC::CRGBA &diffuse, NLMISC::CRGBA &ambiant)
121 H_AUTO_USE(RZ_LightCycleManager)
122 nlassert(_LightLevel >= 0 && _LightLevel <= 1.f);
124 if(ContinentMngr.cur())
126 switch(_State)
128 case DayToNight:
129 if (_LightLevel <= _Desc.DuskRatio)
131 // day->dusk
132 uint level = (uint) (256.f * (_Desc.DuskRatio != 0.f ? _LightLevel / _Desc.DuskRatio : 0.f));
133 diffuse.blendFromui(ContinentMngr.cur()->LandscapeLightDay.Diffuse, ContinentMngr.cur()->LandscapeLightDusk.Diffuse, level);
134 ambiant.blendFromui(ContinentMngr.cur()->LandscapeLightDay.Ambiant, ContinentMngr.cur()->LandscapeLightDusk.Ambiant, level);
136 else
138 // dusk->night
139 uint level = (uint) (256.f * (_Desc.DuskRatio != 1.f ? (_LightLevel - _Desc.DuskRatio) / (1.f - _Desc.DuskRatio) : 0.f));
140 diffuse.blendFromui(ContinentMngr.cur()->LandscapeLightDusk.Diffuse, ContinentMngr.cur()->LandscapeLightNight.Diffuse, level);
141 ambiant.blendFromui(ContinentMngr.cur()->LandscapeLightDusk.Ambiant, ContinentMngr.cur()->LandscapeLightNight.Ambiant, level);
143 break;
144 default:
145 uint level = (uint) (256.f * _LightLevel);
146 diffuse.blendFromui(ContinentMngr.cur()->LandscapeLightDay.Diffuse, ContinentMngr.cur()->LandscapeLightNight.Diffuse, level);
147 ambiant.blendFromui(ContinentMngr.cur()->LandscapeLightDay.Ambiant, ContinentMngr.cur()->LandscapeLightNight.Ambiant, level);
148 break;
154 //-----------------------------------------------
155 bool CLightCycleManager::setLightDesc(const CLightCycleDesc &desc)
157 H_AUTO_USE(RZ_LightCycleManager)
158 if (desc.NightTransitionStartHour > desc.NumHours)
160 nlwarning ("NightTransitionStartHour (%d) > NumHours (%d)", desc.NightTransitionStartHour, desc.NumHours);
161 _State = StateUnknown;
162 return false;
165 if (desc.NightTransitionEndHour > desc.NumHours)
167 nlwarning ("NightTransitionEndHour (%d) > NumHours (%d)", desc.NightTransitionEndHour, desc.NumHours);
168 _State = StateUnknown;
169 return false;
172 if (desc.DawnTransitionStartHour > desc.NumHours)
174 nlwarning ("DawnTransitionStartHour (%d) > NumHours (%d)", desc.DawnTransitionStartHour, desc.NumHours);
175 _State = StateUnknown;
176 return false;
178 if (desc.DawnTransitionEndHour > desc.NumHours)
180 nlwarning ("DawnTransitionEndHour (%d) > NumHours (%d)", desc.DawnTransitionEndHour, desc.NumHours);
181 _State = StateUnknown;
182 return false;
185 // test if intervals are not overlapping
186 if (!(desc.DawnTransitionStartHour >= desc.NightTransitionEndHour
187 || desc.DawnTransitionEndHour <= desc.NightTransitionStartHour
191 nlwarning("!(DawnTransitionStartHour (%d) >= NightTransitionEndHour (%d) || DawnTransitionEndHour (%d) <= NightTransitionStartHour (%d))",
192 desc.DawnTransitionStartHour, desc.NightTransitionEndHour, desc.DawnTransitionEndHour, desc.NightTransitionStartHour);
193 _State = StateUnknown;
194 return false;
199 _Desc = desc;
200 _ValidDesc = true;
201 _Hour = 0;
202 updateState();
203 return true;
206 //-----------------------------------------------
207 float CLightCycleManager::getUpdateDuration() const
209 H_AUTO_USE(RZ_LightCycleManager)
210 if (!_ValidDesc || _UpdateFreq == 0.f) return 0.f;
211 return (1.f / _UpdateFreq) / _Desc.RealDayLength * _Desc.NumHours;
215 //-----------------------------------------------
216 std::string CLightCycleManager::getStateString() const
218 H_AUTO_USE(RZ_LightCycleManager)
219 switch(_State)
221 case NightToDay: return "night->day";
222 case DayToNight: return "day->night";
223 case Day: return "day";
224 case Night: return "night";
225 default:
226 return "???";
228 return "???";
231 //-----------------------------------------------
232 void CLightCycleManager::updateState()
234 H_AUTO_USE(RZ_LightCycleManager)
235 _PrevState = _State;
236 if(isInInterval(_Desc.DawnTransitionStartHour, _Desc.DawnTransitionEndHour, _Hour))
238 // night->day
239 _State = NightToDay;
240 return;
242 else if(isInInterval(_Desc.DawnTransitionEndHour, _Desc.NightTransitionStartHour, _Hour))
244 // day
245 _State = Day;
246 return;
248 else if(isInInterval(_Desc.NightTransitionStartHour, _Desc.NightTransitionEndHour, _Hour))
250 // day->night
251 _State = DayToNight;
252 return;
254 else if(isInInterval(_Desc.NightTransitionEndHour, _Desc.DawnTransitionStartHour, _Hour))
256 // night
257 if( _PrevState != Night && _PrevState != StateUnknown)
259 CInterfaceManager * pIM = CInterfaceManager::getInstance();
260 if( pIM )
262 CAHManager::getInstance()->runActionHandler("set",NULL,"dblink=UI:VARIABLES:NIGHT_WARNING_WANTED|value=1");
265 _State = Night;
266 return;
268 _State = StateUnknown;
269 return;
273 //-----------------------------------------------
274 bool CLightCycleManager::isInTransition() const
276 H_AUTO_USE(RZ_LightCycleManager)
277 return isInInterval(_Desc.DawnTransitionStartHour, _Desc.DawnTransitionEndHour, _Hour)
278 || isInInterval(_Desc.NightTransitionStartHour, _Desc.DawnTransitionEndHour, _Hour);
281 //-----------------------------------------------
282 void CLightCycleManager::setHour(float hour, const CWeatherManagerClient &wm, NLMISC::CRGBA lightningColor)
284 H_AUTO_USE(RZ_LightCycleManager)
285 if (!_ValidDesc) return;
286 if (hour < 0) hour = 0;
287 if (hour > _Desc.NumHours)
289 hour -= ::floorf(hour / _Desc.NumHours) * _Desc.NumHours;
291 float updateDuration = getUpdateDuration();
292 if (fabs(_Hour - hour) >= (4.f * updateDuration))
294 touch();
296 _Hour = hour;
297 _LightLevel = getLightLevel(hour);
298 clamp(_LightLevel, 0.f, 1.f); // should not be necessary, but we avoid imprecisions
299 updateState();
301 _WeatherLighting = wm.getCurrWeatherState().Lighting;
303 // Change water lighting
304 NL3D::UWaterHeightMapManager::setBlendFactor(Driver, _LightLevel);
306 if (_PrevState != _State)
308 if (_State == Night || _State == Day)
310 // a transiation has ended, so release textures used for the blend
311 NL3D::UWaterHeightMapManager::releaseBlendTextures();
315 // directionnal lights
316 // (override the global light by the light in the weather manager : during bad weather, the scene can be darker)
317 setupCanopyLight(_WeatherLighting);
318 setupMainLight(_WeatherLighting);
320 // landscape lighting
321 CRGBA diffuse, ambiant;
322 getLandscapeLightColor(diffuse, ambiant);
324 bool colorTouched = diffuse != _LastDiffuse || ambiant != _LastAmbient;
325 if (colorTouched)
327 _LastDiffuse = diffuse;
328 _LastAmbient = ambiant;
330 // change landscape
331 if (Landscape)
333 if(ContinentMngr.cur())
334 Landscape->setupStaticLight(diffuse, ambiant, ContinentMngr.cur()->StaticLightingFactor[CurrSeason]);
335 else
336 Landscape->setupStaticLight(diffuse, ambiant, 1.f);
337 _UpdateFreq = getLandscapePatchUpdateFreq();
338 if (_Touched)
340 Landscape->updateLightingAll();
341 Landscape->setUpdateLightingFrequency(0);
343 else
345 Landscape->setUpdateLightingFrequency(_UpdateFreq);
349 _Touched = false;
352 // Set the Sun color only if not indoor
353 if (ContinentMngr.cur() && ContinentMngr.cur()->Indoor)
355 Scene->setSunAmbient(CRGBA(150, 150, 150, 255));
357 else
359 CRGBA color;
360 color.add(_LastDiffuse, lightningColor);
361 Scene->setLightGroupColor(LightGroupLandscapeDiffuse, color);
362 color.add(_LastAmbient, lightningColor);
363 Scene->setLightGroupColor(LightGroupLandscapeAmbient, color);
364 float nightLevel = _LightLevel*255.f;
365 clamp (nightLevel, 0, 255);
366 uint8 nightLevelColor = (uint8)nightLevel;
367 color.set (nightLevelColor, nightLevelColor, nightLevelColor);
368 Scene->setLightGroupColor (LightGroupNightCycle, color);
369 uint8 dayLevelColor = 255 - nightLevel;
370 color.set (dayLevelColor, dayLevelColor, dayLevelColor);
371 Scene->setLightGroupColor (LightGroupDayCycle, color);
374 if (Landscape)
376 if (!isInInterval(_Desc.DawnTransitionStartHour, _Desc.DawnTransitionEndHour + 4.f * updateDuration, hour)
377 && !isInInterval(_Desc.NightTransitionStartHour, _Desc.NightTransitionEndHour + 4.f * updateDuration, hour)
380 Landscape->setUpdateLightingFrequency(0);
384 /* if (SoundMngr)
386 float ratio;
387 if(isInDayInterval(_Desc.DawnTransitionStartHour, _Desc.DawnTransitionEndHour, _Desc.NumHours, hour, ratio))
389 // night->day
390 SoundMngr->setDayNightRatio (1.0f-ratio);
392 else if(isInDayInterval(_Desc.DawnTransitionEndHour, _Desc.NightTransitionStartHour, _Desc.NumHours, hour, ratio))
394 // day
395 SoundMngr->setDayNightRatio (0.0f);
397 else if(isInDayInterval(_Desc.NightTransitionStartHour, _Desc.NightTransitionEndHour, _Desc.NumHours, hour, ratio))
399 // day->night
400 SoundMngr->setDayNightRatio (ratio);
402 else if(isInDayInterval(_Desc.NightTransitionEndHour, _Desc.DawnTransitionStartHour, _Desc.NumHours, hour, ratio))
404 // night
405 SoundMngr->setDayNightRatio (1.0f);
412 //-----------------------------------------------
413 // NB : interval can be reversed
414 bool CLightCycleManager::isInInterval(float start, float end, float value)
416 H_AUTO_USE(RZ_LightCycleManager)
417 return start <= end ? value >= start && value < end
418 : value >= start || value < end;
422 //-----------------------------------------------
423 // NB : interval can be reversed
424 bool CLightCycleManager::isInDayInterval(float startHour, float endHour, float dayDuration, float hour, float &ratio)
426 H_AUTO_USE(RZ_LightCycleManager)
427 if (startHour <= endHour)
429 if (hour >= startHour && hour < endHour)
431 ratio = startHour != endHour ? (hour - startHour) / (endHour - startHour)
432 : 0;
433 return true;
435 else
436 return false;
438 else
440 if (hour >= startHour || hour < endHour)
442 ratio = hour >= startHour ? (hour - startHour) / (dayDuration - startHour + endHour)
443 : (hour + dayDuration - startHour) / (dayDuration - startHour + endHour);
444 return true;
446 else
447 return false;
451 //-----------------------------------------------
452 float CLightCycleManager::getLightLevel(float hour) const
454 H_AUTO_USE(RZ_LightCycleManager)
455 float lightValue = 0.f;
456 if (isInDayInterval(_Desc.NightTransitionStartHour, _Desc.NightTransitionEndHour, _Desc.NumHours, hour, lightValue))
457 return lightValue;
458 if (isInDayInterval(_Desc.DawnTransitionStartHour, _Desc.DawnTransitionEndHour, _Desc.NumHours, hour, lightValue))
459 return 1.f - lightValue;
461 // No transition, it is night or day
462 if (_Desc.DawnTransitionEndHour <= _Desc.NightTransitionStartHour)
464 return hour >= _Desc.DawnTransitionEndHour && hour < _Desc.NightTransitionStartHour ? 0.f : 1.f;
466 else
468 return hour >= _Desc.DawnTransitionEndHour || hour < _Desc.NightTransitionStartHour ? 0.f : 1.f;
472 //-----------------------------------------------
473 void CLightCycleManager::create()
475 H_AUTO_USE(RZ_LightCycleManager)
476 Driver->enableLight(0, true);
479 //-----------------------------------------------
480 void CLightCycleManager::setDirLight(const CDirLightSetup &setup0, const CDirLightSetup &setup1, float level, float intensity,NL3D::UScene &scene)
482 H_AUTO_USE(RZ_LightCycleManager)
483 CDirLightSetup resultSetup;
484 resultSetup.blend(setup0, setup1, level);
485 resultSetup.modulate(intensity);
487 scene.setSunAmbient (resultSetup.Ambiant);
488 scene.setSunDiffuse (resultSetup.Diffuse);
489 scene.setSunSpecular (resultSetup.Specular);
490 scene.setSunDirection(resultSetup.Direction);
493 //-----------------------------------------------
494 float CLightCycleManager::getLandscapePatchUpdateFreq() const
496 H_AUTO_USE(RZ_LightCycleManager)
497 if(!_ValidDesc)
498 return 0.f;
500 if(ContinentMngr.cur() == 0)
501 return 0.f;
503 // transition duration in seconds
504 float dt = std::min(_Desc.getDawnTransitionLength(), _Desc.getNightTransitionLength()) * _Desc.RealDayLength / _Desc.NumHours;
505 uint numStep = std::max(diffColors(ContinentMngr.cur()->LandscapeLightDay.Diffuse, ContinentMngr.cur()->LandscapeLightNight.Diffuse),
506 diffColors(ContinentMngr.cur()->LandscapeLightDay.Ambiant, ContinentMngr.cur()->LandscapeLightNight.Ambiant)
508 numStep = std::min(numStep, _Desc.MaxNumColorSteps);
509 return (float) numStep / dt;
512 //-----------------------------------------------
513 void CLightCycleManager::touch()
515 H_AUTO_USE(RZ_LightCycleManager)
516 _Touched = true;
520 //-----------------------------------------------
521 void CLightCycleManager::instanceGroupAdded(NL3D::UInstanceGroup * /* ig */)
523 H_AUTO_USE(RZ_LightCycleManager)
527 //-----------------------------------------------
528 // rootLight :
529 // Set the light to render the root.
530 //-----------------------------------------------
531 void CLightCycleManager::setupCanopyLight(float intensity)
533 H_AUTO_USE(RZ_LightCycleManager)
534 if(!SceneRoot)
535 return;
537 if(ContinentMngr.cur())
539 switch (_State)
541 case DayToNight: // blend form day to night with dusk transition
542 setupDayToNightLight(*SceneRoot, ContinentMngr.cur()->RootLightDay, ContinentMngr.cur()->RootLightDusk, ContinentMngr.cur()->RootLightNight, intensity);
543 break;
544 default: // blend from night to day with no other transition
545 setDirLight(ContinentMngr.cur()->RootLightDay, ContinentMngr.cur()->RootLightNight, _LightLevel, intensity, *SceneRoot);
546 break;
549 }// setupCanopyLight //
551 //-----------------------------------------------
552 // mainLight :
553 // Set the light to render the main scene
554 //-----------------------------------------------
555 void CLightCycleManager::setupMainLight(float intensity)
557 H_AUTO_USE(RZ_LightCycleManager)
558 if(!Scene)
559 return;
560 if(ContinentMngr.cur())
562 switch (_State)
564 case DayToNight: // blend form day to night with dusk transition
565 setupDayToNightLight(*Scene, ContinentMngr.cur()->EntityLightDay, ContinentMngr.cur()->EntityLightDusk, ContinentMngr.cur()->EntityLightNight, intensity);
566 break;
567 default: // blend from night to day with no other transition
568 setDirLight(ContinentMngr.cur()->EntityLightDay, ContinentMngr.cur()->EntityLightNight, _LightLevel, intensity, *Scene);
569 break;
572 }// setupMainLight //
574 //-----------------------------------------------
575 void CLightCycleManager::setupDayToNightLight(NL3D::UScene &scene, const CDirLightSetup &dayLight, const CDirLightSetup &duskLight, const CDirLightSetup &nightLight, float lightIntensity)
577 H_AUTO_USE(RZ_LightCycleManager)
578 float blendFactor;
579 if (_LightLevel <= _Desc.DuskRatio)
581 blendFactor = _Desc.DuskRatio != 0 ? _LightLevel / _Desc.DuskRatio : 0.f;
582 setDirLight(dayLight,
583 duskLight,
584 blendFactor,
585 lightIntensity,
586 scene
589 else
591 blendFactor = _Desc.DuskRatio != 1.f ? (_LightLevel - _Desc.DuskRatio) / (1.f - _Desc.DuskRatio) : 0.f;
592 setDirLight(duskLight,
593 nightLight,
594 blendFactor,
595 lightIntensity,
596 scene