// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/> // 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 <http://www.gnu.org/licenses/>. #include "stdpch.h" #include "nel/misc/file.h" #include "zone_util.h" #include "fog_map.h" #include "client_cfg.h" #include "nel/3d/u_driver.h" #include <algorithm> // #include "interface_v3/interface_manager.h" #include "interface_v3/group_map.h" #include "client_cfg.h" #include "global.h" using namespace NLMISC; H_AUTO_DECL(RZ_FogMap) //=================================================================== void CFogState::setupInDriver(NL3D::UDriver &drv) { H_AUTO_USE(RZ_FogMap) // Are we in wireframe ? bool filled = (drv.getPolygonMode() == NL3D::UDriver::Filled); if (FogEnabled && filled && ClientCfg.Fog) { drv.enableFog(true); drv.setupFog(FogStartDist, FogEndDist, FogColor); } else { drv.enableFog(false); } } //=================================================================== CFogMap::CFogMap() : _MinCoord(0, 0), _MaxCoord(0, 0) { } //=================================================================== void CFogMap::worldPosToMapPos(float inX, float inY, float &outX, float &outY) const { H_AUTO_USE(RZ_FogMap) outX = _MinCoord.x != _MaxCoord.x ? (inX - _MinCoord.x) / (_MaxCoord.x - _MinCoord.x) : _MinCoord.x; outY = _MinCoord.y != _MaxCoord.y ? 1.f - (inY - _MinCoord.y) / (_MaxCoord.y - _MinCoord.y) : 1.f - _MinCoord.y; } //=================================================================== void CFogMap::getFogParams(float startDist, float endDist, float x, float y, float dayNight, float duskRatio, CLightCycleManager::TLightState lightState, CFogState &result) { H_AUTO_USE(RZ_FogMap) NLMISC::clamp(dayNight, 0.f, 1.f); // float mx, my; worldPosToMapPos(x, y, mx, my); // tmp patch for ring island : lookup dist / depth ahead of current pos -> in matis cliffs, avoids to have too thick // fog when looking inside the lands float ddMx = x; float ddMy = y; CInterfaceManager *im = CInterfaceManager::getInstance(); CGroupMap *gm = dynamic_cast<CGroupMap *>(im->getElementFromId("ui:interface:map:content:map_content:actual_map")); if (gm && gm->isIsland()) { CVector front = MainCam.getMatrix().getJ(); ddMx += ClientCfg.FogDistAndDepthLookupBias * front.x; ddMy += ClientCfg.FogDistAndDepthLookupBias * front.y; } // worldPosToMapPos(ddMx, ddMy, ddMx, ddMy); // NLMISC::CRGBAF col0; NLMISC::CRGBAF col1; float blendFactor; switch(lightState) { case CLightCycleManager::DayToNight: // See which transition it is : day->dusk or dusk->night ? if (dayNight < duskRatio) { col0 = getMapValueFromMapCoord(CFogMapBuild::Day, mx, my, CRGBAF(1.f, 1.f, 1.f)); col1 = getMapValueFromMapCoord(CFogMapBuild::Dusk, mx, my, CRGBAF(1.f, 1.f, 1.f)); blendFactor = duskRatio != 0.f ? dayNight / duskRatio : 0.f; } else { col0 = getMapValueFromMapCoord(CFogMapBuild::Dusk, mx, my, CRGBAF(1.f, 1.f, 1.f)); col1 = getMapValueFromMapCoord(CFogMapBuild::Night, mx, my, CRGBAF(1.f, 1.f, 1.f)); blendFactor = duskRatio != 1.f ? (dayNight - duskRatio) / (1.f - duskRatio) : 0.f; } break; default: col0 = getMapValueFromMapCoord(CFogMapBuild::Day, mx, my, CRGBAF(1.f, 1.f, 1.f)); col1 = getMapValueFromMapCoord(CFogMapBuild::Night, mx, my, CRGBAF(1.f, 1.f, 1.f)); blendFactor = dayNight; break; } NLMISC::CRGBAF fogColor = col1 * blendFactor + (1.f - blendFactor) * col0; // result.FogColor = fogColor; // NLMISC::CRGBAF distanceCol = getMapValueFromMapCoord(CFogMapBuild::Distance, ddMx, ddMy, CRGBAF(0.f, 0.f, 0.f)); result.FogStartDist = (distanceCol.R * (endDist - startDist)) + startDist; // NLMISC::CRGBAF depthCol = getMapValueFromMapCoord(CFogMapBuild::Depth,ddMx, ddMy, CRGBAF(1.f, 1.f, 1.f)); result.FogEndDist = std::min(result.FogStartDist + depthCol.R * (endDist - startDist), endDist); result.FogEnabled = true; } //=================================================================== NLMISC::CRGBAF CFogMap::getMapValue(TMapType type, float x, float y, NLMISC::CRGBAF defaultValue) const { H_AUTO_USE(RZ_FogMap) nlassert(type < CFogMapBuild::NumMap); if (_Map[type].getWidth() == 0) return defaultValue; float mx, my; worldPosToMapPos(x, y, mx, my); return _Map[type].getColor(mx, my); } //=================================================================== NLMISC::CRGBAF CFogMap::getMapValueFromMapCoord(TMapType type, float x, float y, NLMISC::CRGBAF defaultValue) const { H_AUTO_USE(RZ_FogMap) nlassert(type < CFogMapBuild::NumMap); if (_Map[type].getWidth() == 0) return defaultValue; return _Map[type].getColor(x, y); } //=================================================================== void CFogMap::release() { H_AUTO_USE(RZ_FogMap) for(uint k = 0; k < CFogMapBuild::NumMap; ++k) { _Map[k].reset(); } _MinCoord.set(0.f, 0.f); _MaxCoord.set(0.f, 0.f); } //=================================================================== bool CFogMap::init(const CFogMapBuild &desc) { H_AUTO_USE(RZ_FogMap) release(); CVector2f minCoord, maxCoord; if (!getPosFromZoneName(desc.ZoneMin, minCoord) || !getPosFromZoneName(desc.ZoneMax, maxCoord)) { release(); return false; } return init(desc, minCoord, maxCoord); } //=================================================================== bool CFogMap::init(const CFogMapBuild &desc, const NLMISC::CVector &minCoord, const NLMISC::CVector &maxCoord) { _MinCoord = minCoord; _MaxCoord = maxCoord; if (_MinCoord.x > _MaxCoord.x) std::swap(_MinCoord.x, _MaxCoord.x); if (_MinCoord.y > _MaxCoord.y) std::swap(_MinCoord.y, _MaxCoord.y); _MaxCoord.set(_MaxCoord.x + 160.f, _MaxCoord.y + 160.f); for(uint k = 0; k < CFogMapBuild::NumMap; ++k) { _Map[k].resize(0, 0); if (!desc.Map[k].empty()) { load(_Map[k], desc.Map[k]); } } // Exception for dusk map : it has been added later, so if the filename is empty, we use the day and night map to get the dusk map if (desc.Map[CFogMapBuild::Dusk].empty()) { if (_Map[CFogMapBuild::Day].getWidth() != 0 && _Map[CFogMapBuild::Day].getHeight() != 0 && _Map[CFogMapBuild::Night].getWidth() != 0 && _Map[CFogMapBuild::Night].getHeight() != 0) { _Map[CFogMapBuild::Dusk].blend(_Map[CFogMapBuild::Day], _Map[CFogMapBuild::Night], 127); } } return true; } //=================================================================== void CFogMap::load(NLMISC::CBitmap &bm, const std::string &filename) { H_AUTO_USE(RZ_FogMap) std::string lookupName = NLMISC::CPath::lookup(filename, false, true); if (lookupName.empty()) { nlwarning("couldn't find %s", filename.c_str()); return; } NLMISC::CIFile inputFile; inputFile.open(lookupName); try { if (!bm.load(inputFile)) { nlwarning ("Error while loading fog map %s (bitmap size is multiple of 4?)", lookupName.c_str()); inputFile.close(); } } catch(const NLMISC::EStream &e) { nlwarning(e.what()); inputFile.close(); } }