Linux multi-monitor fullscreen support
[ryzomcore.git] / ryzom / client / src / continent.cpp
blob6f8eeceaf934bdf1b736d52cd9e871997e449e8c
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 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/>.
23 /////////////
24 // INCLUDE //
25 /////////////
26 #include "stdpch.h"
27 // Misc
28 #include "nel/misc/vectord.h"
29 #include "nel/misc/path.h"
30 #include "nel/misc/smart_ptr.h"
31 #include "nel/misc/progress_callback.h"
32 #include "nel/misc/async_file_manager.h"
33 #include "nel/misc/bitmap.h"
34 #include "nel/misc/polygon.h"
35 // 3D Interface.
36 #include "nel/3d/u_landscape.h"
37 #include "nel/3d/u_scene.h"
38 #include "nel/3d/u_driver.h"
39 #include "nel/3d/u_texture.h"
40 // Georges
41 #include "nel/georges/u_form.h"
42 #include "nel/georges/u_form_elm.h"
43 #include "nel/georges/u_form_loader.h"
44 // Gui
45 #include "nel/gui/lua_manager.h"
46 // Client
47 #include "global.h"
48 #include "continent.h"
49 #include "light_cycle_manager.h" // \todo GUIGUI : maybe change this when the time will be better managed.
50 #include "door_manager.h"
51 #include "client_cfg.h"
52 #include "ig_client.h"
53 #include "pacs_client.h"
54 #include "village.h"
55 #include "ig_callback.h"
56 #include "streamable_ig.h"
57 #include "weather_manager_client.h"
58 #include "weather.h"
59 #include "entities.h"
60 #include "fog_map.h"
61 #include "demo.h"
62 #include "ingame_database_manager.h"
63 #include "sky_render.h"
64 #include "fix_season_data.h"
65 #include "ig_season_callback.h"
66 #include "user_entity.h"
67 #include "micro_life_manager.h"
68 #include "zone_util.h"
69 #include "mesh_camera_col_manager.h"
70 #include "misc.h"
71 #include "sheet_manager.h"
72 #include "continent_manager.h"
73 #include "connection.h"
74 #include "water_env_map_rdr.h"
75 #include "input.h"
76 #include "bg_downloader_access.h"
78 // Game Share
79 #include "game_share/georges_helper.h"
80 #include "game_share/season_file_ext.h"
82 // R2 Share
83 #include "game_share/scenario_entry_points.h"
86 #include "r2/editor.h"
88 // std
89 #include <vector>
90 #include <string>
91 #include <memory>
92 #include <algorithm>
95 ///////////
96 // USING //
97 ///////////
98 using namespace NLMISC;
99 using namespace NL3D;
100 using namespace std;
101 using namespace NLGEORGES;
104 ////////////
105 // EXTERN //
106 ////////////
107 extern ULandscape *Landscape;
108 extern UScene *Scene;
109 extern UScene *SceneRoot;
110 extern UInstanceGroup *BackgroundIG;
111 extern UDriver *Driver;
112 extern UVisualCollisionManager *CollisionManager;
113 extern bool InitCloudScape;
114 extern bool FirstFrame;
115 extern CContinentManager ContinentMngr;
116 extern CEntityManager EntitiesMngr;
118 //-----------------------------------------------
119 // CUserLandMark
120 //-----------------------------------------------
122 NLMISC::CRGBA CUserLandMark::getColor () const
124 return _LandMarksColor[Type];
128 //-----------------------------------------------
129 // CFogOfWar
130 //-----------------------------------------------
132 //===================================================================================
133 CFogOfWar::~CFogOfWar()
135 if (Tx != NULL)
136 Driver->deleteTextureMem(Tx);
139 //===================================================================================
140 uint8 *CFogOfWar::getData()
142 return (Tx != NULL) ? Tx->getPointer() : NULL;
145 //===================================================================================
146 sint16 CFogOfWar::getRealWidth()
148 return (Tx != NULL) ? (sint16)Tx->getImageWidth() : 0;
151 //===================================================================================
152 sint16 CFogOfWar::getRealHeight()
154 return (Tx != NULL) ? (sint16)Tx->getImageHeight() : 0;
157 //===================================================================================
158 bool CFogOfWar::createData(sint16 w, sint16 h)
160 if (Tx != NULL)
161 Driver->deleteTextureMem(Tx);
163 MapWidth = w;
164 MapHeight = h;
165 uint32 WReal = NLMISC::raiseToNextPowerOf2(MapWidth);
166 uint32 HReal = NLMISC::raiseToNextPowerOf2(MapHeight);
168 Tx = Driver->createTextureMem(WReal, HReal, CBitmap::Alpha);
169 if (Tx == NULL)
170 return false;
172 Tx->setWrapS(NL3D::UTexture::Clamp);
173 Tx->setWrapT(NL3D::UTexture::Clamp);
175 uint8 *pData = Tx->getPointer();
176 // Set the texture to black (not discovered)
177 for (uint32 j = 0; j < WReal*HReal; ++j)
178 pData[j] = 0;
180 // Upload it
181 Tx->touch();
182 return true;
185 //===================================================================================
186 void CFogOfWar::explored(sint16 /* mapPosX */, sint16 /* mapPosY */)
188 // TODO trap : do something better than upload the whole texture
189 if (Tx != NULL)
190 Tx->touch();
194 //===================================================================================
195 void CFogOfWar::load(const std::string &/* contName */)
197 /* // Temporary comment : see this when server ready
198 string fileName = "save/fow_" + contName + "_" + PlayerSelectedFileName + ".ibmp";
199 // Try to load the file corresponding
200 CIFile inFile;
201 if (inFile.open(fileName))
203 serial(inFile);
205 else
207 // If the file do not exist create a new texture
208 // Look in all the maps to know the texture map size and apply a ratio to this size
209 CWorldSheet *pWS = dynamic_cast<CWorldSheet*>(SheetMngr.get(CSheetId("ryzom.world")));
210 if (pWS == NULL) return;
211 for (uint32 i = 0; i < pWS->Maps.size(); ++i)
213 SMap &rM = pWS->Maps[i];
214 if (strncmp(rM.Name.c_str(), "continent", 9) == 0)
216 if (stricmp(rM.ContinentName.c_str(), contName.c_str()) == 0)
218 // get the size of the bitmap
219 uint32 nWidth = 0, nHeight = 0;
220 string path = CPath::lookup(rM.BitmapName, false);
221 if (!path.empty())
222 CBitmap::loadSize(path, nWidth, nHeight);
223 // create the texture fow
224 if ((nWidth != 0) && (nHeight != 0))
226 MapWidth = (sint16)nWidth / 4;
227 MapHeight = (sint16)nHeight / 4;
228 createData(MapWidth, MapHeight);
230 MinX = rM.MinX;
231 MinY = rM.MinY;
232 MaxX = rM.MaxX;
233 MaxY = rM.MaxY;
234 return;
242 //===================================================================================
243 void CFogOfWar::save(const std::string &/* contName */)
245 /* // Temporary comment : see this when server ready
246 string fileName = "save/fow_" + contName + "_" + PlayerSelectedFileName + ".ibmp";
247 // Try to load the file corresponding
248 COFile inFile;
249 if (inFile.open(fileName))
250 serial(inFile);
255 /////////////
256 // METHODS //
257 /////////////
258 //-----------------------------------------------
259 // CContinent :
260 // Constructor.
261 //-----------------------------------------------
262 CContinent::CContinent()
264 // Newbie = false;
265 }// CContinent //
268 class CChangeTaskPriority : public CTaskManager::IChangeTaskPriority
270 virtual float getTaskPriority(const IRunnable &runnable)
272 const NLMISC::IRunnablePos *_3dRunnable = dynamic_cast<const NLMISC::IRunnablePos*>(&runnable);
273 if (_3dRunnable)
275 return (_3dRunnable->Position - UserEntity->pos()).norm();
277 return 0;
279 } ChangeTaskPriority;
284 //===================================================================================
285 void CContinent::setup()
287 if (SheetName.empty())
289 nlwarning("SheetName not set : cannot setup continent");
290 return;
293 CEntitySheet *pES = SheetMngr.get(CSheetId (SheetName));
294 CContinentSheet *pCS = NULL;
295 if (pES->type() == CEntitySheet::CONTINENT)
296 pCS = (CContinentSheet*)pES;
298 if (pCS == NULL)
300 nlwarning("Bad type for continent form %s.", SheetName.c_str());
301 return;
304 // copy basics parameters of sheet
305 CContinentParameters::operator = (pCS->Continent);
307 // setup weather functions
308 for(uint k = 0; k < EGSPD::CSeason::Invalid; ++k)
310 WeatherFunction[k].buildFromSheet(pCS->WeatherFunction[k], WeatherManager);
314 nlinfo("Seasons for continent %s", SheetName.c_str());
315 nlinfo("==================================");
316 for(uint k = 0; k < EGSPD::CSeason::Invalid; ++k)
318 nlinfo("Season = %s, num setups = %d", EGSPD::CSeason::toString((EGSPD::CSeason::TSeason) k).c_str(), (int) WeatherFunction[k].getNumWeatherSetups());
319 for(uint l = 0; l < WeatherFunction[k].getNumWeatherSetups(); ++l)
321 if (WeatherFunction[k].getWeatherSetup(l))
323 nlinfo("Setup %d for season %s is %s", (int) l, EGSPD::CSeason::toString((EGSPD::CSeason::TSeason) k).c_str(), NLMISC::CStringMapper::unmap(WeatherFunction[k].getWeatherSetup(l)->SetupName).c_str());
325 else
327 nlinfo("Setup %d not found", (int) l);
333 uint k;
334 // setup villages
335 for(k = 0; k < pCS->Villages.size(); ++k)
337 CVillage *village = new CVillage;
338 if (village->setupFromSheet(Scene, pCS->Villages[k], &IGLoaded))
339 _Villages.add(village);
340 else
341 delete village;
345 // Setup outpost
346 _Outposts.clear();
347 for(k = 0; k < pCS->Continent.ZCList.size(); ++k)
349 // must add the ruins in 3D?
350 bool mustAddRuins= pCS->Continent.ZCList[k].EnableRuins;
352 // add a village for ruins display?
353 CVillage *village= NULL;
354 if(mustAddRuins)
356 village = new CVillage;
357 if (village->setupOutpost(Scene, pCS->Continent.ZCList[k], k, &IGLoaded))
358 _Villages.add(village);
359 else
361 delete village;
362 village= NULL;
366 // add an outpost
367 COutpost outpost;
368 if (outpost.setupOutpost(pCS->Continent.ZCList[k], k, village))
369 _Outposts.push_back(outpost);
372 // setup fog of war
373 FoW.load(Name);
378 //-----------------------------------------------
379 class CPCBank : public IProgressCallback
381 virtual void progress (float /* progressValue */) {}
383 CPCBank PCBank;
385 static uint getNumZones()
387 if (Landscape == NULL) return 0;
388 std::vector<std::string> zoneLoaded;
389 Landscape->getAllZoneLoaded(zoneLoaded);
390 return (uint)zoneLoaded.size();
393 static uint16 getZoneIdFromName(const string &zoneName)
395 uint16 zoneId = 0;
396 CVector2f pos;
397 if (getPosFromZoneName(zoneName, pos))
399 uint x = (uint)pos.x / 160;
400 uint y = -(uint)pos.y / 160;
401 zoneId = (x&255) + (y<<8);
403 else
405 nlinfo("no zone...");
407 return zoneId;
410 //-----------------------------------------------
411 // select :
412 // Update global parameters like the texture for the micro veget.
413 //-----------------------------------------------
414 void CContinent::select(const CVectorD &pos, NLMISC::IProgressCallback &progress, bool complete, bool unhibernate, EGSPD::CSeason::TSeason season)
416 #ifdef RYZOM_BG_DOWNLOADER
417 pauseBGDownloader();
418 #endif
419 CNiceInputAuto niceInputs;
420 // Season has changed ?
421 Season = season;
423 if (Landscape) Landscape->setTileColor(TileColorMono[Season], TileColorFactor[Season]);
425 // First frame
426 FirstFrame = true;
429 H_AUTO(InitRZWorldLanscapeShow)
430 // If continent is an indoor, hde the landscape
431 if (Landscape)
433 if (Indoor)
434 Landscape->hide();
435 else
436 Landscape->show();
440 bool toto = false;
441 if(complete)
443 // Progress bar
444 progress.progress (0);
445 progress.pushCropedValues (0, 1.f/3.f);
448 H_AUTO(InitRZWorldIndoor)
449 if (!ClientCfg.Light)
451 if (Landscape)
453 Landscape->removeAllZones();
454 toto= true;
455 // If continent is an indoor, hde the landscape
456 if (!Indoor)
458 // Change the MicroVeget Texture.
459 string microVegetSeason = MicroVeget;
460 SeasonPostfixTextureFilename (microVegetSeason, Season);
461 Landscape->loadVegetableTexture (microVegetSeason);
463 // Change the Material for static and dynamic PointLights
464 Landscape->setPointLightDiffuseMaterial(LandscapePointLightMaterial);
469 // Initialize Landscape IG Callbacks.
471 H_AUTO(InitRZWorldInitCallbacks)
472 initLandscapeIGCallbacks();
473 nlassert(IGCallbacks); // forgot to call ::initLandscapeIGCallbacks()
474 if (!ClientCfg.Light)
476 // Add weather as an observer (to know when an ig is added, and to set its lightmaps color accordingly)
477 registerObserver(&LightCycleManager);
478 IGCallbacks->registerObserver(&LightCycleManager);
480 // register the LightCycleManager as an observer to Objects that may create IGs
481 IGSeasonCallback.Season = Season;
482 registerObserver(&IGSeasonCallback);
483 IGCallbacks->registerObserver(&IGSeasonCallback);
485 // register the Camera collision manager
486 registerObserver(&MeshCameraColManager);
488 if (!Indoor)
490 // Change LOD texture
493 // Get the CoarseMeshMap for this season
494 string seasonname = CoarseMeshMap;
495 SeasonPostfixTextureFilename (seasonname, Season);
497 // Set the texture for the coarse mesh manager
498 Scene->setCoarseMeshManagerTexture (CPath::lookup(seasonname).c_str());
500 catch (const Exception &e)
502 nlwarning (e.what());
506 // Register door handling (when an ig is added check all shapes for doors and add to the door manager)
507 registerObserver(&IGDoorCallback);
511 H_AUTO(InitRZWorldPacs)
513 releasePACS();
514 // Init PACS
515 std::string pacsRBankPath = CPath::lookup(PacsRBank, false);
516 std::string pacsGRPath = CPath::lookup(PacsGR, false);
517 if(!pacsRBankPath.empty() && !pacsGRPath.empty())
518 initPACS(pacsRBankPath.c_str(), pacsGRPath.c_str(), progress);
519 else
520 initPACS(0, 0, progress);
521 // Set the move container
522 if (IGCallbacks) IGCallbacks->setMoveContainer(PACS);
526 H_AUTO(InitRZWorldBuildings)
527 // No landscape IG in indoors
528 if (!Indoor)
530 if (!ClientCfg.Light && Landscape)
532 // Init ig
535 LandscapeIGManager.initIG (Scene, CPath::lookup(LandscapeIG).c_str(), Driver, Season, &progress);
537 catch (const Exception &e)
539 nlwarning (e.what());
543 // Get IGs for the continent with their name.
544 vector<pair<UInstanceGroup *, string> > igsWithNames;
545 LandscapeIGManager.getAllIGWithNames(igsWithNames);
547 // Associate IGs with the ZC number or -1 if there is no ZC.
548 for(uint i = 0; i<igsWithNames.size(); ++i)
550 string igZone = toLowerAscii(CFile::getFilenameWithoutExtension(igsWithNames[i].second));
552 // Search for the IG name in the ZC list to associate.
553 for(uint j = 0; j<ZCList.size(); ++j)
555 // Get the outpost ZC
556 COutpost *outpost = getOutpost (j);
557 if (outpost)
559 // If name matching -> this zone should be a ZC.
560 string outpostZone = toLowerAscii(CFile::getFilenameWithoutExtension(ZCList[j].Name));
561 if(igZone == outpostZone)
563 nlctassert(RZ_MAX_BUILDING_PER_OUTPOST==4);
564 // Check there is all information in the IG for a ZC and initialize.
565 bool zcOK[RZ_MAX_BUILDING_PER_OUTPOST+1] = {false, false, false, false, false};
566 UInstanceGroup *ig = igsWithNames[i].first;
567 uint k;
568 for(k=0; k<ig->getNumInstance(); ++k)
570 if(ig->getInstanceName(k) == "flag_zc")
572 // todo hulud handle outpost flag
573 zcOK[0] = true;
576 // For each building
577 uint b;
578 for (b=0; b<RZ_MAX_BUILDING_PER_OUTPOST; b++)
580 // Good one ?
581 if (ig->getInstanceName(k) == ("bat_zc_0"+toString(b+1)))
583 // Set the position
584 CVector pos = ig->getInstancePos(k);
585 CQuat rot = ig->getInstanceRot(k);
586 outpost->setBuildingPosition (b, rot, pos);
587 zcOK[b+1] = true;
592 // There are not enough information in the IG for ZC.
593 for (k=0; k<RZ_MAX_BUILDING_PER_OUTPOST+1; k++)
594 if (!zcOK[k])
595 break;
597 // Failed ?
598 if (k != RZ_MAX_BUILDING_PER_OUTPOST+1)
599 nlwarning("CContinent::select : Zone '%s' should be a ZC but some doomy are missing.", igsWithNames[i].second.c_str());
600 // Outpost found, and setuped. stop
601 break;
605 // Add IGs with the Num ZC associated in the callback system.
606 if (IGCallbacks) IGCallbacks->addIG(igsWithNames[i].first);
609 // Register outpost collisions and Display
610 initOutpost();
614 if (!ClientCfg.Light)
616 // Progress bar
617 progress.popCropedValues ();
618 progress.progress (1.f/3.f);
619 progress.pushCropedValues (1.f/3.f, 2.f/3.f);
622 H_AUTO(InitRZWorldTileBank)
623 // Select the tile bank
624 if (Landscape)
626 if (!Indoor)
628 string farBankSeason = FarBank;
629 SeasonPostfixTextureFilename (farBankSeason, Season);
630 if (!SmallBank.empty() && !farBankSeason.empty())
632 std::string smallBankPath = CPath::lookup(SmallBank, false);
633 std::string farBankSeasonPath = CPath::lookup(farBankSeason, false);
634 if (!smallBankPath.empty() && !farBankSeasonPath.empty())
636 Landscape->loadBankFiles (smallBankPath, farBankSeasonPath);
638 // Postfix tiles and vegetset by the season string
639 Landscape->postfixTileFilename (CSeasonFileExt::getExtension (Season));
640 Landscape->postfixTileVegetableDesc (CSeasonFileExt::getExtension (Season));
642 // Flush the tiles
643 Landscape->flushTiles (progress);
649 // Not an indoor ?
650 if (!Indoor)
653 H_AUTO(InitRZWorldSky)
654 // load the sky if any
655 initSky();
656 // build the canopee
659 nlassert(CurrSeason < EGSPD::CSeason::Invalid);
660 if (!CanopyIGfileName[CurrSeason].empty() && CurrSeason != EGSPD::CSeason::Invalid)
662 BackgroundIG = UInstanceGroup::createInstanceGroup(CanopyIGfileName[CurrSeason]);
664 else
666 BackgroundIG = UInstanceGroup::createInstanceGroup(BackgroundIGName);
669 catch (const Exception &e)
671 nlwarning (e.what());
673 if(BackgroundIG)
675 if (SceneRoot)
677 // Add the IG to the scene
678 BackgroundIG->addToScene(*SceneRoot, Driver);
680 // See for which objects distance should be overriden (object which use a .PLANT sheet)
681 uint numInstances = BackgroundIG->getNumInstance();
682 uint k;
683 for(k = 0; k < numInstances; ++k)
685 // Select the texture function of the season
686 UInstance instance = BackgroundIG->getInstance (k);
687 if (!instance.empty())
689 instance.selectTextureSet (Season);
698 // Progress bar
699 progress.popCropedValues ();
700 progress.progress (2.f/3.f);
701 progress.pushCropedValues (2.f/3.f, 1);
702 }// complete
704 if (complete || unhibernate)
706 H_AUTO(InitRZWorldLightCycleManager)
707 // Touch light setup
708 LightCycleManager.touch();
711 if (complete)
713 H_AUTO(InitRZWorldWaterMap)
714 // init water map
715 initWaterMap();
718 const R2::CScenarioEntryPoints::CCompleteIsland *completeIsland = NULL;
720 progress.pushCropedValues (0, 3.f/4.f);
722 if (!ClientCfg.Light)
724 if (!Indoor)
726 if (Landscape)
728 H_AUTO(InitRZWorldLandscapeIGManager)
729 // If any zone already loaded into the landscape, must load them to the IGManager.
730 // Yoyo: this is important for teleportation near a current position, since not so many landscape zones
731 // will be removed/added.
732 // Hence, a solution is to reload all igs.
733 vector<string> zonesLoaded;
734 Landscape->getAllZoneLoaded(zonesLoaded);
735 // and try to load the ones which fit the IGManager
736 vector<NL3D::UInstanceGroup *> igAdded;
737 LandscapeIGManager.loadArrayZoneIG(zonesLoaded, &igAdded);
739 // Refresh zone to load/remove.
740 vector<string> zonesAdded;
741 vector<string> zonesRemoved;
742 completeIsland = R2::CScenarioEntryPoints::getInstance().getCompleteIslandFromCoords(CVector2f((float) UserEntity->pos().x, (float) UserEntity->pos().y));
743 Landscape->refreshAllZonesAround(pos, ClientCfg.Vision + ExtraZoneLoadingVision, zonesAdded, zonesRemoved, progress, completeIsland ? &(completeIsland->ZoneIDs) : NULL);
745 for (uint i = 0; i < zonesRemoved.size(); i++)
746 EntitiesMngr.removeInstancesInIgZone(getZoneIdFromName(zonesRemoved[i]));
748 for (uint i = 0; i < zonesAdded.size(); i++)
750 CSString luaScript;
751 string luaScriptName = CPath::lookup(zonesAdded[i]+".lua", false);
752 if (!luaScriptName.empty())
754 luaScript.readFromFile(luaScriptName);
755 CLuaManager::getInstance().executeLuaScript(luaScript, true);
756 nlinfo("loading %s", luaScriptName.c_str());
758 else
760 nlinfo("file not found %s", luaScriptName.c_str());
764 LandscapeIGManager.unloadArrayZoneIG(zonesRemoved);
765 LandscapeIGManager.loadArrayZoneIG(zonesAdded, &igAdded);
770 progress.popCropedValues ();
771 progress.progress (3.f/4.f);
772 progress.pushCropedValues (3.f/4.f, 1);
774 // Refresh PACS
775 if(GR)
777 H_AUTO(InitRZWorldRefreshPacs)
778 GR->refreshLrAroundNow(pos, LRRefeshRadius);
781 // Refresh streamable object (villages ..)
782 if (!ClientCfg.Light)
784 if (ClientCfg.VillagesEnabled)
786 H_AUTO(InitRZWorldForceUpdateStreamable)
787 forceUpdateStreamable(pos, progress);
791 progress.popCropedValues ();
794 // Execute only when you change the targeted continent is not the current one.
795 if(complete || unhibernate)
797 // Continent changed -> update entities.
798 EntitiesMngr.changeContinent();
799 R2::getEditor().onContinentChanged();
801 // Ask to reinit cloudscape
802 InitCloudScape = true;
804 // Set the entity sun contribution parameters
805 CollisionManager->setSunContributionPower (EntitySunContributionPower, EntitySunContributionMaxThreshold);
808 if(complete)
810 reloadFogMap();
811 // temp for debug : create visualisation of current pacs primitives
812 // createInstancesFromMoveContainer(Scene, IGCallbacks->getMoveContainer(), NULL);
814 // Progress bar
815 progress.popCropedValues ();
818 if (complete || unhibernate)
820 if (!ClientCfg.Light && ClientCfg.LandscapeEnabled && ClientCfg.MicroLifeEnabled)
822 H_AUTO(InitRZWorldLoadMicroLife)
823 loadMicroLife();
826 // Register callback on streaming
827 CAsyncFileManager::getInstance().registerTaskPriorityCallback(&ChangeTaskPriority);
828 }// select //
830 //=========================================================================
831 void CContinent::reloadFogMap()
833 const R2::CScenarioEntryPoints::CCompleteIsland *completeIsland = R2::CScenarioEntryPoints::getInstance().getCompleteIslandFromCoords(CVector2f((float) UserEntity->pos().x, (float) UserEntity->pos().y));
834 if (completeIsland)
836 std::string islandName = toLowerAscii(completeIsland->Island);
837 std::string::size_type lastPos = islandName.find_last_of("_");
838 if (lastPos != std::string::npos)
840 islandName = islandName.substr(lastPos + 1);
842 // special fogmaps are use for islands
843 CFogMapBuild fmb;
844 enum TMapType { Day = 0, Night, Dusk, Distance, Depth, NoPrecipitation, NumMap };
845 fmb.Map[CFogMapBuild::Day] = "fog_" + islandName + "_day.tga";
846 fmb.Map[CFogMapBuild::Night] = "fog_" + islandName + "_night.tga";
847 fmb.Map[CFogMapBuild::Dusk] = "fog_" + islandName + "_day.tga";
848 fmb.Map[CFogMapBuild::Distance] = "fog_" + islandName + "_dist.tga";
849 fmb.Map[CFogMapBuild::Depth] = "fog_" + islandName + "_depth.tga";
850 fmb.Map[CFogMapBuild::NoPrecipitation] = islandName + "_no_rain.tga";
851 // there is an outtline of 50%, take it in account
852 float deltaX = 0.25f * (completeIsland->XMax - completeIsland->XMin);
853 float deltaY = 0.25f * (completeIsland->XMax - completeIsland->XMin);
854 FogMap.init(fmb,
855 CVector((float) completeIsland->XMin + deltaX, (float) completeIsland->YMin + deltaY, 0.f),
856 CVector((float) completeIsland->XMax - deltaX, (float) completeIsland->YMax - deltaY, 0.f)
858 bool found = false;
859 for(uint k = 0; k < CFogMapBuild::NumMap && !found; ++k)
861 if (FogMap.getMap((CFogMap::TMapType) k).getWidth() != 0) found = true;
863 // as a fallback, assume that new fog maps have not been installed, so fallback on the old ones ...
864 if (!found)
866 FogMap.init(FogMapBuild);
869 else
871 FogMap.init(FogMapBuild);
873 #ifdef NL_DEBUG
874 nlinfo("fog map : (minX, minY) = (%.1f, %.1f), (maxX, maxY) = (%.1f, %.1f)", FogMap.getMinCoord().x, FogMap.getMinCoord().y, FogMap.getMaxCoord().x, FogMap.getMaxCoord().y);
875 #endif
878 //=========================================================================
879 bool CContinent::getCorners(NLMISC::CVector2f &cornerMin, NLMISC::CVector2f &cornerMax) const
881 if (!getPosFromZoneName(ZoneMin, cornerMin))
883 nlwarning("Can't retrieve continent min corner");
884 return false;
886 if (!getPosFromZoneName(ZoneMax, cornerMax))
888 nlwarning("Can't retrieve continent max corner");
889 return false;
891 if(cornerMin.x > cornerMax.x) std::swap(cornerMin.x, cornerMax.x);
892 if(cornerMin.y > cornerMax.y) std::swap(cornerMin.y, cornerMax.y);
893 cornerMax.x += 160.f;
894 cornerMax.y += 160.f;
895 return true;
898 //=========================================================================
899 void CContinent::initWaterMap()
901 NLMISC::CVector2f cornerMin, cornerMax;
902 if (!getCorners(cornerMin, cornerMax)) return;
903 WaterMap.init(cornerMin, cornerMax);
906 //=========================================================================
907 void CContinent::loadMicroLife()
909 CMicroLifeManager::getInstance().release();
910 NLMISC::CVector2f cornerMin, cornerMax;
911 if (!getCorners(cornerMin, cornerMax)) return;
912 CMicroLifeManager::getInstance().init(cornerMin, cornerMax);
913 // register all micro life primitives
914 CMicroLifeManager::getInstance().build(MicroLifeZones);
917 //=========================================================================
918 bool CContinent::isLoadingforced(const NLMISC::CVector &pos) const
920 return _Villages.needCompleteLoading(pos);
924 //=========================================================================
925 void CContinent::updateStreamable(const NLMISC::CVector &pos)
927 _Villages.update(pos);
930 //=========================================================================
931 void CContinent::forceUpdateStreamable(const NLMISC::CVector &playerPos, NLMISC::IProgressCallback &progress)
933 _Villages.forceUpdate(playerPos, progress);
936 //=========================================================================
937 void CContinent::removeVillages()
939 _Villages.removeAll();
942 //=========================================================================
943 void CContinent::unselect()
945 // remove envmap
946 if (SceneRoot && !WaterEnvMapCanopyCam.empty())
948 SceneRoot->deleteCamera(WaterEnvMapCanopyCam);
950 WaterEnvMapCanopyCam.detach();
951 if (CurrentSky.getScene() && !WaterEnvMapSkyCam.empty())
953 CurrentSky.getScene()->deleteCamera(WaterEnvMapSkyCam);
955 WaterEnvMapSkyCam.detach();
957 if (!Indoor)
959 releaseSky();
961 // Setup the Root scene.
962 if (BackgroundIG)
964 nlassert(SceneRoot);
965 BackgroundIG->removeFromScene (*SceneRoot);
966 SceneRoot->deleteInstanceGroup (BackgroundIG);
967 BackgroundIG = NULL;
971 // Remove outpost (collisions etc...)
972 removeOutpost();
974 // Unregister callback on streaming
975 CAsyncFileManager::getInstance().registerTaskPriorityCallback(NULL);
977 // Remove the primitive for all entitites (new PACS coming soon and need new primitives).
978 EntitiesMngr.removeCollision();
980 // Remove the instances (shapes).
981 EntitiesMngr.removeInstances();
983 // release collision primitives
984 if (IGCallbacks)
986 IGCallbacks->resetContainer();
989 // Unload villages
990 _Villages.forceUnload();
992 // Release PACS
993 releasePACS ();
995 // Initialize Landscape IG Callbacks.
996 releaseLandscapeIGCallbacks();
998 // Release the IG manager only if where are not in indoor
999 if (!Indoor)
1001 // Release the IG manager
1002 if (Landscape) LandscapeIGManager.reset ();
1005 // release fog maps
1006 FogMap.release();
1008 // Remove weather as an observer
1009 removeObserver(&LightCycleManager);
1011 // Remove season observer
1012 removeObserver(&IGSeasonCallback);
1014 // Remove Camera collision observer
1015 removeObserver(&MeshCameraColManager);
1017 // Remove door management observers
1018 removeObserver(&IGDoorCallback);
1020 // Release water map
1021 WaterMap.release();
1023 // Release water envmap
1024 #ifdef USE_WATER_ENV_MAP
1025 Driver->deleteWaterEnvMap(WaterEnvMap);
1026 WaterEnvMap = NULL;
1027 #endif
1030 //=========================================================================
1031 /** Override current fog by the fog in the weather system. This is used when there is bad weather or precipitations
1032 * \param dest the fog state to modify
1033 * \param fogIndex the fog to modify (0 for main, 1 for canopy)
1035 static void overrideFog(CFogState &dest, TFogType fogType, float dayNight, float duskRatio, CLightCycleManager::TLightState lightState, bool /* overrideDist */, bool overideByWeatherFogDist = true)
1037 nlassert(fogType < NumFogType); // For now, 2 kinds of fogs : main & canopy
1038 if (!dest.FogEnabled) return;
1039 const CWeatherState &ws = WeatherManager.getCurrWeatherState();
1040 if (ws.FogRatio == 0.f) return; // no need to override fog
1041 // override the fog by the fog indicated in the WeatherManager
1042 NLMISC::CRGBA fogColor;
1043 // if there's a new style sky with a fog bitmap *(giving fog depending on hour & weather, use it)
1044 if (ContinentMngr.cur() && ContinentMngr.cur()->CurrentSky.getScene() != NULL && ContinentMngr.cur()->CurrentSky.hasFogColor())
1046 fogColor = ContinentMngr.cur()->CurrentSky.computeFogColor(CClientDate(RT.getRyzomDay(), (float) DayNightCycleHour), WeatherManager.getWeatherValue());
1048 else
1050 switch(lightState)
1052 case CLightCycleManager::DayToNight:
1054 if (dayNight < duskRatio)
1056 float blendFactor = duskRatio != 0 ? dayNight / duskRatio : 0.f;
1057 fogColor.blendFromui(ws.FogColorDay, ws.FogColorDusk, (uint) (blendFactor * 256.f));
1059 else
1061 float blendFactor = duskRatio != 1 ? (dayNight - duskRatio) / (1.f - duskRatio) : 0.f;
1062 fogColor.blendFromui(ws.FogColorDusk, ws.FogColorNight, (uint) (blendFactor * 256.f));
1065 break;
1066 default: // no dusk transition
1067 fogColor.blendFromui(ws.FogColorDay, ws.FogColorNight, (uint) (dayNight * 256.f));
1068 break;
1071 float finalFogAlpha = (float)dest.FogColor.A * 256.f / 255.f;
1072 dest.FogColor.blendFromui(dest.FogColor, fogColor, (uint) ((256.f-finalFogAlpha) * ws.FogRatio));
1073 if (overideByWeatherFogDist)
1075 dest.FogEndDist = std::min (ws.FogFar[fogType], dest.FogEndDist);
1076 dest.FogStartDist = std::min (ws.FogNear[fogType], dest.FogStartDist);
1080 //=========================================================================
1081 /** Adapt fog dist to the viewport
1083 static inline void makeRangedFog(CFogState &result, UScene &scene)
1085 if (result.FogEndDist != 0.f)
1087 float farPlaneDist = scene.getCam().getFrustum().Far;
1088 float newEndFogDist = std::min(result.FogEndDist, farPlaneDist);
1089 result.FogStartDist = result.FogStartDist * newEndFogDist / result.FogEndDist;
1090 result.FogEndDist = newEndFogDist;
1091 result.FogEnabled = true;
1093 else
1095 result.FogEnabled = false;
1099 //=========================================================================
1100 void CContinent::getFogState(TFogType fogType, float dayNight, float duskRatio, CLightCycleManager::TLightState lightState, const NLMISC::CVectorD &pos, CFogState &result, bool overideByWeatherFogDist /*= true*/)
1102 if (FogStart == FogEnd)
1104 result.FogEnabled = false;
1105 return;
1107 switch(fogType)
1109 case MainFog: FogMap.getFogParams(FogStart, FogEnd, (float) pos.x, (float) pos.y, dayNight, duskRatio, lightState, result); break;
1111 case CanopyFog: FogMap.getFogParams(RootFogStart, RootFogEnd, (float) pos.x, (float) pos.y, dayNight, duskRatio, lightState, result); break;
1112 default:
1113 nlstop;
1114 break;
1116 // Override fog because of weather.
1117 overrideFog(result, fogType, dayNight, duskRatio, lightState, overideByWeatherFogDist);
1118 // normalize fog dist to view frustum
1120 switch(fogType)
1122 case MainFog: if (Scene) makeRangedFog(result, *Scene); break;
1123 case CanopyFog: if (SceneRoot) makeRangedFog(result, *SceneRoot); break;
1127 if (fogType == MainFog)
1129 result.FogStartDist *= (ClientCfg.Vision / ClientCfg.Vision_max);
1130 result.FogEndDist *= (ClientCfg.Vision / ClientCfg.Vision_max);
1136 //=========================================================================
1137 bool CContinent::enumIGs(IIGEnum *callback)
1139 uint numEntities = _Villages.getNumEntities();
1140 bool continueEnum = true;
1141 for(uint k = 0; k < numEntities && continueEnum; ++k)
1143 CVillage *v = dynamic_cast<CVillage *>(_Villages.getEntity(k));
1144 if (v) // well this should always be the case, but it may change in the future
1146 continueEnum = v->enumIGs(callback);
1149 return continueEnum;
1152 //=========================================================================
1153 void CContinent::registerObserver(IIGObserver *obs)
1155 uint numEntities = _Villages.getNumEntities();
1156 for(uint k = 0; k < numEntities; ++k)
1158 CVillage *v = dynamic_cast<CVillage *>(_Villages.getEntity(k));
1159 if (v) // well this should always be the case, but it may change in the future
1161 v->registerObserver(obs);
1167 //=========================================================================
1168 void CContinent::removeObserver(IIGObserver *obs)
1170 uint numEntities = _Villages.getNumEntities();
1171 for(uint k = 0; k < numEntities; ++k)
1173 CVillage *v = dynamic_cast<CVillage *>(_Villages.getEntity(k));
1174 if (v) // well this should always be the case, but it may change in the future
1176 v->removeObserver(obs);
1182 //=========================================================================
1183 bool CContinent::isObserver(IIGObserver *obs) const
1185 uint numEntities = _Villages.getNumEntities();
1186 for(uint k = 0; k < numEntities; ++k)
1188 CVillage *v = dynamic_cast<CVillage *>(_Villages.getEntity(k));
1189 if (v) // well this should always be the case, but it may change in the future
1191 if (v->isObserver(obs))
1192 return true;
1195 return false;
1199 //=========================================================================
1200 COutpost *CContinent::getOutpost (uint outpostId)
1202 for (uint i=0; i<_Outposts.size(); i++)
1204 // Outpost ?
1205 if(_Outposts[i].getOutpostId()==(sint)outpostId)
1206 return &_Outposts[i];
1208 return NULL;
1211 //=========================================================================
1212 void CContinent::initOutpost()
1214 for (uint i=0; i<_Outposts.size(); i++)
1216 _Outposts[i].initOutpost();
1220 //=========================================================================
1221 void CContinent::removeOutpost()
1223 for (uint i=0; i<_Outposts.size(); i++)
1225 _Outposts[i].removeOutpost();
1230 //=========================================================================
1231 void CContinent::dumpVillagesLoadingZones(const std::string &filename)
1233 const uint NUM_METER_PER_PIX = 4;
1234 CBitmap outBitmap;
1235 CVector2f minPos, maxPos;
1236 getPosFromZoneName(ZoneMin, minPos);
1237 getPosFromZoneName(ZoneMax, maxPos);
1238 if (minPos.x > maxPos.x) std::swap(minPos.x, maxPos.x);
1239 if (minPos.y > maxPos.y) std::swap(minPos.y, maxPos.y);
1240 uint width = (uint) ((maxPos.x - minPos.x) / NUM_METER_PER_PIX);
1241 uint height = (uint) ((maxPos.y - minPos.y) / NUM_METER_PER_PIX);
1242 outBitmap.resize(width, height);
1243 for(uint k = 0; k < _Villages.getNumEntities(); ++k)
1245 CVillage &v = *safe_cast<CVillage *>(_Villages.getEntity(k));
1246 if(!v.isOutpost())
1248 const CStreamableIG &sig = v.getIG();
1249 drawDisc(outBitmap, (sig.getPos().x - minPos.x) / NUM_METER_PER_PIX, (sig.getPos().y - minPos.y) / NUM_METER_PER_PIX, sig.getUnloadRadius() / NUM_METER_PER_PIX, CRGBA(0, 0, 100), true);
1250 drawDisc(outBitmap, (sig.getPos().x - minPos.x) / NUM_METER_PER_PIX, (sig.getPos().y - minPos.y) / NUM_METER_PER_PIX, sig.getLoadRadius() / NUM_METER_PER_PIX, CRGBA(0, 100, 0), true);
1251 drawDisc(outBitmap, (sig.getPos().x - minPos.x) / NUM_METER_PER_PIX, (sig.getPos().y - minPos.y) / NUM_METER_PER_PIX, sig.getForceLoadRadius() / NUM_METER_PER_PIX, CRGBA(100, 0, 0), true);
1254 // load the map and do a lerp between the 2 bitmaps
1255 if (!WorldMap.empty())
1257 std::string path = CPath::lookup(WorldMap, false);
1258 if (!path.empty())
1260 CIFile stream;
1261 if (stream.open(path))
1263 CBitmap worldMap;
1264 if (worldMap.load(stream))
1266 worldMap.convertToType(CBitmap::RGBA);
1267 worldMap.flipV();
1268 worldMap.resample(outBitmap.getWidth(), outBitmap.getHeight());
1269 outBitmap.blend(outBitmap, worldMap, 127, true);
1274 drawDisc(outBitmap, ((float) UserEntity->pos().x - minPos.x) / (float) NUM_METER_PER_PIX, ((float) UserEntity->pos().y - minPos.y) / (float) NUM_METER_PER_PIX, 2, CRGBA::Magenta);
1276 // draw grid
1277 const uint subdiv= 500;
1278 const CRGBA lineCol(CRGBA::Yellow);
1279 sint minY= (sint)floor(minPos.y/subdiv);
1280 sint maxY= (sint)ceil (maxPos.y/subdiv);
1281 sint minX= (sint)floor(minPos.x/subdiv);
1282 sint maxX= (sint)ceil (maxPos.x/subdiv);
1283 // draw HLines
1284 for(sint y= minY;y<maxY;y++)
1286 sint ypix= (sint)((y*subdiv-minPos.y)/NUM_METER_PER_PIX);
1287 if(ypix>=0 && ypix<(sint)height)
1289 for(uint x=0;x<width;x++)
1291 CRGBA *pCol= (CRGBA*)(&outBitmap.getPixels()[0]);
1292 pCol[ypix*width+x]= lineCol;
1296 // draw VLines
1297 for(sint x= minX;x<maxX;x++)
1299 sint xpix= (sint)((x*subdiv-minPos.x)/NUM_METER_PER_PIX);
1300 if(xpix>=0 && xpix<(sint)width)
1302 for(uint y=0;y<height;y++)
1304 CRGBA *pCol= (CRGBA*)(&outBitmap.getPixels()[0]);
1305 pCol[y*width+xpix]= lineCol;
1311 COFile outFile;
1312 if (outFile.open(filename))
1316 outBitmap.writeTGA(outFile, 24, true);
1318 catch(const EStream &)
1324 //=========================================================================
1325 void CContinent::dumpFogMap(CFogMapBuild::TMapType mapType, const std::string &filename, TChannel channel /*= ChannelRGBA*/, const CRGBA channelLookup[256] /* = NULL */)
1327 CBitmap outBitmap = FogMap.getMap(mapType);
1328 outBitmap.convertToType(CBitmap::RGBA);
1329 if (channel != ChannelRGBA)
1332 CRGBA *pix = (CRGBA *) &outBitmap.getPixels(0)[0];
1333 const CRGBA *endPix = pix + outBitmap.getWidth() * outBitmap.getHeight();
1334 while (pix != endPix)
1336 uint8 intensity = ((uint8 *) pix)[channel];
1337 if (!channelLookup)
1339 *pix = CRGBA(intensity, intensity, intensity, intensity);
1341 else
1343 *pix = channelLookup[intensity];
1345 ++pix;
1348 if (outBitmap.getWidth() == 0 || outBitmap.getHeight() == 0) return;
1349 CVector pos = MainCam.getMatrix().getPos();
1350 float mx, my;
1351 FogMap.worldPosToMapPos(pos.x, pos.y, mx, my);
1352 // load the map and do a lerp between the 2 bitmaps
1353 if (!WorldMap.empty())
1355 std::string path = CPath::lookup(WorldMap, false);
1356 if (!path.empty())
1358 CIFile stream;
1359 if (stream.open(path))
1361 CBitmap worldMap;
1362 if (worldMap.load(stream))
1364 worldMap.convertToType(CBitmap::RGBA);
1365 worldMap.resample(outBitmap.getWidth(), outBitmap.getHeight());
1366 outBitmap.blend(outBitmap, worldMap, 127, true);
1371 drawDisc(outBitmap, mx * outBitmap.getWidth(), my * outBitmap.getHeight(), 2.f, CRGBA::Magenta);
1372 COFile outFile;
1373 if (outFile.open(filename))
1377 outBitmap.writeTGA(outFile, 24);
1379 catch(const EStream &)
1385 //=========================================================================
1386 void CContinent::initSky()
1388 if (!SkySheet[CurrSeason].empty())
1390 // get the sky sheet
1391 CEntitySheet *skySheet = SheetMngr.get(NLMISC::CSheetId(SkySheet[CurrSeason]));
1392 if (skySheet && skySheet->Type == CEntitySheet::SKY)
1394 // new-style sky
1395 CurrentSky.init(Driver, *static_cast<CSkySheet *>(skySheet), false, WorldLightCycle.NumHours);
1397 #ifdef USE_WATER_ENV_MAP
1398 WaterEnvMapRdr.Sky = &CurrentSky;
1399 #endif
1401 else
1403 createSkyScene();
1404 // fallback to previous sky version
1405 Sky = SkyScene->createInstance(SkyDay);
1406 Sky2ndPass = SkyScene->createInstance(SkyDay); // Sky shape used for second pass. We use it to keep pointers on textures
1407 SkyFogPart = SkyScene->createInstance(SkyFogPartName);
1408 // Setup the 2nd
1409 if (!Sky.empty())
1411 DaySkySetup.buildFromInstance(Sky, 0); // day textures are at stage 0
1412 NightSkySetup.buildFromInstance(Sky, 1); // night textures are at stage 1
1417 //=========================================================================
1418 void CContinent::releaseSky()
1420 // Remove the (old-style) sky
1421 if (!Sky.empty())
1423 SkyScene->deleteInstance (Sky);
1424 SkyScene->deleteInstance (Sky2ndPass);
1425 SkyScene->deleteInstance(SkyFogPart);
1426 Sky = NULL;
1427 Sky2ndPass = NULL;
1428 SkyFogPart = NULL;
1430 deleteSkyScene();
1432 // Release the (new-style) sky
1433 CurrentSky.release();