// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" ///////////// // INCLUDE // ///////////// // 3D Interface #include "nel/3d/u_instance.h" #include "nel/3d/u_scene.h" #include "nel/3d/u_water.h" #include "nel/3d/u_landscape.h" // Client. #include "light_cycle_manager.h" #include "client_cfg.h" #include "ig_client.h" #include "user_entity.h" #include "pacs_client.h" #include "world_database_manager.h" #include "continent_manager.h" #include "continent.h" #include "ig_enum.h" #include "ig_callback.h" #include "pacs_client.h" #include "sound_manager.h" #include "weather_manager_client.h" #include "misc.h" #include "interface_v3/interface_manager.h" //#include "sound_manager.h" // \todo GUIGUI : uncomment after new FE done and class modified H_AUTO_DECL(RZ_LightCycleManager) /////////// // USING // /////////// using namespace NLMISC; using namespace NL3D; using namespace NLPACS; using namespace std; //////////// // EXTERN // //////////// extern UDriver *Driver; extern UScene *Scene; extern UScene *SceneRoot; extern EGSPD::CSeason::TSeason CurrSeason; //////////// // EXTERN // //////////// extern ULandscape *Landscape; static uint diffColors(CRGBA c1, CRGBA c2) { return (uint) maxof(abs((sint) c1.R - c2.R), abs((sint) c1.G - c2.G), abs((sint) c1.B - c2.B)); } //----------------------------------------------- float CLightCycleDesc::getNightTransitionLength() const { H_AUTO_USE(RZ_LightCycleManager) return NightTransitionEndHour >= NightTransitionStartHour ? NightTransitionEndHour - NightTransitionStartHour : NightTransitionEndHour + NumHours - NightTransitionStartHour; } //----------------------------------------------- float CLightCycleDesc::getDawnTransitionLength() const { H_AUTO_USE(RZ_LightCycleManager) return DawnTransitionEndHour >= DawnTransitionStartHour ? DawnTransitionEndHour - DawnTransitionStartHour : DawnTransitionEndHour + NumHours - DawnTransitionStartHour; } //----------------------------------------------- CLightCycleManager::CLightCycleManager() : _Touched(true), _ValidDesc(false), _LightLevel(0), _WeatherLighting(0), _LastWeatherLighting(0), _Hour(0), _UpdateFreq(0), _State(StateUnknown), _PrevState(StateUnknown) { H_AUTO_USE(RZ_LightCycleManager) } //----------------------------------------------- void CLightCycleManager::getLandscapeLightColor(NLMISC::CRGBA &diffuse, NLMISC::CRGBA &ambiant) { H_AUTO_USE(RZ_LightCycleManager) nlassert(_LightLevel >= 0 && _LightLevel <= 1.f); if(ContinentMngr.cur()) { switch(_State) { case DayToNight: if (_LightLevel <= _Desc.DuskRatio) { // day->dusk uint level = (uint) (256.f * (_Desc.DuskRatio != 0.f ? _LightLevel / _Desc.DuskRatio : 0.f)); diffuse.blendFromui(ContinentMngr.cur()->LandscapeLightDay.Diffuse, ContinentMngr.cur()->LandscapeLightDusk.Diffuse, level); ambiant.blendFromui(ContinentMngr.cur()->LandscapeLightDay.Ambiant, ContinentMngr.cur()->LandscapeLightDusk.Ambiant, level); } else { // dusk->night uint level = (uint) (256.f * (_Desc.DuskRatio != 1.f ? (_LightLevel - _Desc.DuskRatio) / (1.f - _Desc.DuskRatio) : 0.f)); diffuse.blendFromui(ContinentMngr.cur()->LandscapeLightDusk.Diffuse, ContinentMngr.cur()->LandscapeLightNight.Diffuse, level); ambiant.blendFromui(ContinentMngr.cur()->LandscapeLightDusk.Ambiant, ContinentMngr.cur()->LandscapeLightNight.Ambiant, level); } break; default: uint level = (uint) (256.f * _LightLevel); diffuse.blendFromui(ContinentMngr.cur()->LandscapeLightDay.Diffuse, ContinentMngr.cur()->LandscapeLightNight.Diffuse, level); ambiant.blendFromui(ContinentMngr.cur()->LandscapeLightDay.Ambiant, ContinentMngr.cur()->LandscapeLightNight.Ambiant, level); break; } } } //----------------------------------------------- bool CLightCycleManager::setLightDesc(const CLightCycleDesc &desc) { H_AUTO_USE(RZ_LightCycleManager) if (desc.NightTransitionStartHour > desc.NumHours) { nlwarning ("NightTransitionStartHour (%d) > NumHours (%d)", desc.NightTransitionStartHour, desc.NumHours); _State = StateUnknown; return false; } if (desc.NightTransitionEndHour > desc.NumHours) { nlwarning ("NightTransitionEndHour (%d) > NumHours (%d)", desc.NightTransitionEndHour, desc.NumHours); _State = StateUnknown; return false; } // if (desc.DawnTransitionStartHour > desc.NumHours) { nlwarning ("DawnTransitionStartHour (%d) > NumHours (%d)", desc.DawnTransitionStartHour, desc.NumHours); _State = StateUnknown; return false; } if (desc.DawnTransitionEndHour > desc.NumHours) { nlwarning ("DawnTransitionEndHour (%d) > NumHours (%d)", desc.DawnTransitionEndHour, desc.NumHours); _State = StateUnknown; return false; } // // test if intervals are not overlapping if (!(desc.DawnTransitionStartHour >= desc.NightTransitionEndHour || desc.DawnTransitionEndHour <= desc.NightTransitionStartHour ) ) { nlwarning("!(DawnTransitionStartHour (%d) >= NightTransitionEndHour (%d) || DawnTransitionEndHour (%d) <= NightTransitionStartHour (%d))", desc.DawnTransitionStartHour, desc.NightTransitionEndHour, desc.DawnTransitionEndHour, desc.NightTransitionStartHour); _State = StateUnknown; return false; } // // _Desc = desc; _ValidDesc = true; _Hour = 0; updateState(); return true; } //----------------------------------------------- float CLightCycleManager::getUpdateDuration() const { H_AUTO_USE(RZ_LightCycleManager) if (!_ValidDesc || _UpdateFreq == 0.f) return 0.f; return (1.f / _UpdateFreq) / _Desc.RealDayLength * _Desc.NumHours; } //----------------------------------------------- std::string CLightCycleManager::getStateString() const { H_AUTO_USE(RZ_LightCycleManager) switch(_State) { case NightToDay: return "night->day"; case DayToNight: return "day->night"; case Day: return "day"; case Night: return "night"; default: return "???"; } return "???"; } //----------------------------------------------- void CLightCycleManager::updateState() { H_AUTO_USE(RZ_LightCycleManager) _PrevState = _State; if(isInInterval(_Desc.DawnTransitionStartHour, _Desc.DawnTransitionEndHour, _Hour)) { // night->day _State = NightToDay; return; } else if(isInInterval(_Desc.DawnTransitionEndHour, _Desc.NightTransitionStartHour, _Hour)) { // day _State = Day; return; } else if(isInInterval(_Desc.NightTransitionStartHour, _Desc.NightTransitionEndHour, _Hour)) { // day->night _State = DayToNight; return; } else if(isInInterval(_Desc.NightTransitionEndHour, _Desc.DawnTransitionStartHour, _Hour)) { // night if( _PrevState != Night && _PrevState != StateUnknown) { CInterfaceManager * pIM = CInterfaceManager::getInstance(); if( pIM ) { pIM->runActionHandler("set",NULL,"dblink=UI:VARIABLES:NIGHT_WARNING_WANTED|value=1"); } } _State = Night; return; } _State = StateUnknown; return; } // //----------------------------------------------- bool CLightCycleManager::isInTransition() const { H_AUTO_USE(RZ_LightCycleManager) return isInInterval(_Desc.DawnTransitionStartHour, _Desc.DawnTransitionEndHour, _Hour) || isInInterval(_Desc.NightTransitionStartHour, _Desc.DawnTransitionEndHour, _Hour); } //----------------------------------------------- void CLightCycleManager::setHour(float hour, const CWeatherManagerClient &wm, NLMISC::CRGBA lightningColor) { H_AUTO_USE(RZ_LightCycleManager) if (!_ValidDesc) return; if (hour < 0) hour = 0; if (hour > _Desc.NumHours) { hour -= ::floorf(hour / _Desc.NumHours) * _Desc.NumHours; } float updateDuration = getUpdateDuration(); if (fabs(_Hour - hour) >= (4.f * updateDuration)) { touch(); } _Hour = hour; _LightLevel = getLightLevel(hour); clamp(_LightLevel, 0.f, 1.f); // should not be necessary, but we avoid imprecisions updateState(); _WeatherLighting = wm.getCurrWeatherState().Lighting; // Change water lighting NL3D::UWaterHeightMapManager::setBlendFactor(Driver, _LightLevel); if (_PrevState != _State) { if (_State == Night || _State == Day) { // a transiation has ended, so release textures used for the blend NL3D::UWaterHeightMapManager::releaseBlendTextures(); } } // directionnal lights // (override the global light by the light in the weather manager : during bad weather, the scene can be darker) setupCanopyLight(_WeatherLighting); setupMainLight(_WeatherLighting); // landscape lighting CRGBA diffuse, ambiant; getLandscapeLightColor(diffuse, ambiant); bool colorTouched = diffuse != _LastDiffuse || ambiant != _LastAmbient; if (colorTouched) { _LastDiffuse = diffuse; _LastAmbient = ambiant; // change landscape if (Landscape) { if(ContinentMngr.cur()) Landscape->setupStaticLight(diffuse, ambiant, ContinentMngr.cur()->StaticLightingFactor[CurrSeason]); else Landscape->setupStaticLight(diffuse, ambiant, 1.f); _UpdateFreq = getLandscapePatchUpdateFreq(); if (_Touched) { Landscape->updateLightingAll(); Landscape->setUpdateLightingFrequency(0); } else { Landscape->setUpdateLightingFrequency(_UpdateFreq); } } } _Touched = false; // Set the Sun color CRGBA color; color.add(_LastDiffuse, lightningColor); Scene->setLightGroupColor (LightGroupDay, color); // Set the Night color float nightLevel = _LightLevel*255.f; clamp (nightLevel, 0, 255); color.set ((uint8)nightLevel, (uint8)nightLevel, (uint8)nightLevel); Scene->setLightGroupColor (LightGroupNight, color); if (Landscape) { if (!isInInterval(_Desc.DawnTransitionStartHour, _Desc.DawnTransitionEndHour + 4.f * updateDuration, hour) && !isInInterval(_Desc.NightTransitionStartHour, _Desc.NightTransitionEndHour + 4.f * updateDuration, hour) ) { Landscape->setUpdateLightingFrequency(0); } } /* if (SoundMngr) { float ratio; if(isInDayInterval(_Desc.DawnTransitionStartHour, _Desc.DawnTransitionEndHour, _Desc.NumHours, hour, ratio)) { // night->day SoundMngr->setDayNightRatio (1.0f-ratio); } else if(isInDayInterval(_Desc.DawnTransitionEndHour, _Desc.NightTransitionStartHour, _Desc.NumHours, hour, ratio)) { // day SoundMngr->setDayNightRatio (0.0f); } else if(isInDayInterval(_Desc.NightTransitionStartHour, _Desc.NightTransitionEndHour, _Desc.NumHours, hour, ratio)) { // day->night SoundMngr->setDayNightRatio (ratio); } else if(isInDayInterval(_Desc.NightTransitionEndHour, _Desc.DawnTransitionStartHour, _Desc.NumHours, hour, ratio)) { // night SoundMngr->setDayNightRatio (1.0f); } } */ } //----------------------------------------------- // NB : interval can be reversed bool CLightCycleManager::isInInterval(float start, float end, float value) { H_AUTO_USE(RZ_LightCycleManager) return start <= end ? value >= start && value < end : value >= start || value < end; } //----------------------------------------------- // NB : interval can be reversed bool CLightCycleManager::isInDayInterval(float startHour, float endHour, float dayDuration, float hour, float &ratio) { H_AUTO_USE(RZ_LightCycleManager) if (startHour <= endHour) { if (hour >= startHour && hour < endHour) { ratio = startHour != endHour ? (hour - startHour) / (endHour - startHour) : 0; return true; } else return false; } else { if (hour >= startHour || hour < endHour) { ratio = hour >= startHour ? (hour - startHour) / (dayDuration - startHour + endHour) : (hour + dayDuration - startHour) / (dayDuration - startHour + endHour); return true; } else return false; } } //----------------------------------------------- float CLightCycleManager::getLightLevel(float hour) const { H_AUTO_USE(RZ_LightCycleManager) float lightValue; if (isInDayInterval(_Desc.NightTransitionStartHour, _Desc.NightTransitionEndHour, _Desc.NumHours, hour, lightValue)) return lightValue; if (isInDayInterval(_Desc.DawnTransitionStartHour, _Desc.DawnTransitionEndHour, _Desc.NumHours, hour, lightValue)) return 1.f - lightValue; // No transition, it is night or day if (_Desc.DawnTransitionEndHour <= _Desc.NightTransitionStartHour) { return hour >= _Desc.DawnTransitionEndHour && hour < _Desc.NightTransitionStartHour ? 0.f : 1.f; } else { return hour >= _Desc.DawnTransitionEndHour || hour < _Desc.NightTransitionStartHour ? 0.f : 1.f; } } //----------------------------------------------- void CLightCycleManager::create() { H_AUTO_USE(RZ_LightCycleManager) Driver->enableLight(0, true); } //----------------------------------------------- void CLightCycleManager::setDirLight(const CDirLightSetup &setup0, const CDirLightSetup &setup1, float level, float intensity,NL3D::UScene &scene) { H_AUTO_USE(RZ_LightCycleManager) CDirLightSetup resultSetup; resultSetup.blend(setup0, setup1, level); resultSetup.modulate(intensity); // scene.setSunAmbient (resultSetup.Ambiant); scene.setSunDiffuse (resultSetup.Diffuse); scene.setSunSpecular (resultSetup.Specular); scene.setSunDirection(resultSetup.Direction); } //----------------------------------------------- float CLightCycleManager::getLandscapePatchUpdateFreq() const { H_AUTO_USE(RZ_LightCycleManager) if(!_ValidDesc) return 0.f; if(ContinentMngr.cur() == 0) return 0.f; // transition duration in seconds float dt = std::min(_Desc.getDawnTransitionLength(), _Desc.getNightTransitionLength()) * _Desc.RealDayLength / _Desc.NumHours; uint numStep = std::max(diffColors(ContinentMngr.cur()->LandscapeLightDay.Diffuse, ContinentMngr.cur()->LandscapeLightNight.Diffuse), diffColors(ContinentMngr.cur()->LandscapeLightDay.Ambiant, ContinentMngr.cur()->LandscapeLightNight.Ambiant) ); numStep = std::min(numStep, _Desc.MaxNumColorSteps); return (float) numStep / dt; } //----------------------------------------------- void CLightCycleManager::touch() { H_AUTO_USE(RZ_LightCycleManager) _Touched = true; } //----------------------------------------------- void CLightCycleManager::instanceGroupAdded(NL3D::UInstanceGroup * /* ig */) { H_AUTO_USE(RZ_LightCycleManager) } //----------------------------------------------- // rootLight : // Set the light to render the root. //----------------------------------------------- void CLightCycleManager::setupCanopyLight(float intensity) { H_AUTO_USE(RZ_LightCycleManager) if(!SceneRoot) return; if(ContinentMngr.cur()) { switch (_State) { case DayToNight: // blend form day to night with dusk transition setupDayToNightLight(*SceneRoot, ContinentMngr.cur()->RootLightDay, ContinentMngr.cur()->RootLightDusk, ContinentMngr.cur()->RootLightNight, intensity); break; default: // blend from night to day with no other transition setDirLight(ContinentMngr.cur()->RootLightDay, ContinentMngr.cur()->RootLightNight, _LightLevel, intensity, *SceneRoot); break; } } }// setupCanopyLight // //----------------------------------------------- // mainLight : // Set the light to render the main scene //----------------------------------------------- void CLightCycleManager::setupMainLight(float intensity) { H_AUTO_USE(RZ_LightCycleManager) if(!Scene) return; if(ContinentMngr.cur()) { switch (_State) { case DayToNight: // blend form day to night with dusk transition setupDayToNightLight(*Scene, ContinentMngr.cur()->EntityLightDay, ContinentMngr.cur()->EntityLightDusk, ContinentMngr.cur()->EntityLightNight, intensity); break; default: // blend from night to day with no other transition setDirLight(ContinentMngr.cur()->EntityLightDay, ContinentMngr.cur()->EntityLightNight, _LightLevel, intensity, *Scene); break; } } }// setupMainLight // //----------------------------------------------- void CLightCycleManager::setupDayToNightLight(NL3D::UScene &scene, const CDirLightSetup &dayLight, const CDirLightSetup &duskLight, const CDirLightSetup &nightLight, float lightIntensity) { H_AUTO_USE(RZ_LightCycleManager) float blendFactor; if (_LightLevel <= _Desc.DuskRatio) { blendFactor = _Desc.DuskRatio != 0 ? _LightLevel / _Desc.DuskRatio : 0.f; setDirLight(dayLight, duskLight, blendFactor, lightIntensity, scene ); } else { blendFactor = _Desc.DuskRatio != 1.f ? (_LightLevel - _Desc.DuskRatio) / (1.f - _Desc.DuskRatio) : 0.f; setDirLight(duskLight, nightLight, blendFactor, lightIntensity, scene ); } }