1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 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/>.
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"
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"
41 #include "nel/georges/u_form.h"
42 #include "nel/georges/u_form_elm.h"
43 #include "nel/georges/u_form_loader.h"
45 #include "nel/gui/lua_manager.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"
55 #include "ig_callback.h"
56 #include "streamable_ig.h"
57 #include "weather_manager_client.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"
71 #include "sheet_manager.h"
72 #include "continent_manager.h"
73 #include "connection.h"
74 #include "water_env_map_rdr.h"
76 #include "bg_downloader_access.h"
79 #include "game_share/georges_helper.h"
80 #include "game_share/season_file_ext.h"
83 #include "game_share/scenario_entry_points.h"
86 #include "r2/editor.h"
98 using namespace NLMISC
;
101 using namespace NLGEORGES
;
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 //-----------------------------------------------
120 //-----------------------------------------------
122 NLMISC::CRGBA
CUserLandMark::getColor () const
124 return _LandMarksColor
[Type
];
128 //-----------------------------------------------
130 //-----------------------------------------------
132 //===================================================================================
133 CFogOfWar::~CFogOfWar()
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
)
161 Driver
->deleteTextureMem(Tx
);
165 uint32 WReal
= NLMISC::raiseToNextPowerOf2(MapWidth
);
166 uint32 HReal
= NLMISC::raiseToNextPowerOf2(MapHeight
);
168 Tx
= Driver
->createTextureMem(WReal
, HReal
, CBitmap::Alpha
);
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
)
185 //===================================================================================
186 void CFogOfWar::explored(sint16
/* mapPosX */, sint16
/* mapPosY */)
188 // TODO trap : do something better than upload the whole texture
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
201 if (inFile.open(fileName))
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);
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);
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
249 if (inFile.open(fileName))
258 //-----------------------------------------------
261 //-----------------------------------------------
262 CContinent::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
);
275 return (_3dRunnable
->Position
- UserEntity
->pos()).norm();
279 } ChangeTaskPriority
;
284 //===================================================================================
285 void CContinent::setup()
287 if (SheetName
.empty())
289 nlwarning("SheetName not set : cannot setup continent");
293 CEntitySheet
*pES
= SheetMngr
.get(CSheetId (SheetName
));
294 CContinentSheet
*pCS
= NULL
;
295 if (pES
->type() == CEntitySheet::CONTINENT
)
296 pCS
= (CContinentSheet
*)pES
;
300 nlwarning("Bad type for continent form %s.", SheetName
.c_str());
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());
327 nlinfo("Setup %d not found", (int) l);
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
);
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
;
356 village
= new CVillage
;
357 if (village
->setupOutpost(Scene
, pCS
->Continent
.ZCList
[k
], k
, &IGLoaded
))
358 _Villages
.add(village
);
368 if (outpost
.setupOutpost(pCS
->Continent
.ZCList
[k
], k
, village
))
369 _Outposts
.push_back(outpost
);
378 //-----------------------------------------------
379 class CPCBank
: public IProgressCallback
381 virtual void progress (float /* progressValue */) {}
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
)
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);
405 nlinfo("no zone...");
410 //-----------------------------------------------
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
419 CNiceInputAuto niceInputs
;
420 // Season has changed ?
423 if (Landscape
) Landscape
->setTileColor(TileColorMono
[Season
], TileColorFactor
[Season
]);
429 H_AUTO(InitRZWorldLanscapeShow
)
430 // If continent is an indoor, hde the landscape
444 progress
.progress (0);
445 progress
.pushCropedValues (0, 1.f
/3.f
);
448 H_AUTO(InitRZWorldIndoor
)
449 if (!ClientCfg
.Light
)
453 Landscape
->removeAllZones();
455 // If continent is an indoor, hde the landscape
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
);
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
)
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
);
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
530 if (!ClientCfg
.Light
&& Landscape
)
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
);
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
;
568 for(k
=0; k
<ig
->getNumInstance(); ++k
)
570 if(ig
->getInstanceName(k
) == "flag_zc")
572 // todo hulud handle outpost flag
578 for (b
=0; b
<RZ_MAX_BUILDING_PER_OUTPOST
; b
++)
581 if (ig
->getInstanceName(k
) == ("bat_zc_0"+toString(b
+1)))
584 CVector pos
= ig
->getInstancePos(k
);
585 CQuat rot
= ig
->getInstanceRot(k
);
586 outpost
->setBuildingPosition (b
, rot
, pos
);
592 // There are not enough information in the IG for ZC.
593 for (k
=0; k
<RZ_MAX_BUILDING_PER_OUTPOST
+1; k
++)
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
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
614 if (!ClientCfg
.Light
)
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
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
));
643 Landscape
->flushTiles (progress
);
653 H_AUTO(InitRZWorldSky
)
654 // load the sky if any
659 nlassert(CurrSeason
< EGSPD::CSeason::Invalid
);
660 if (!CanopyIGfileName
[CurrSeason
].empty() && CurrSeason
!= EGSPD::CSeason::Invalid
)
662 BackgroundIG
= UInstanceGroup::createInstanceGroup(CanopyIGfileName
[CurrSeason
]);
666 BackgroundIG
= UInstanceGroup::createInstanceGroup(BackgroundIGName
);
669 catch (const Exception
&e
)
671 nlwarning (e
.what());
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();
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
);
699 progress
.popCropedValues ();
700 progress
.progress (2.f
/3.f
);
701 progress
.pushCropedValues (2.f
/3.f
, 1);
704 if (complete
|| unhibernate
)
706 H_AUTO(InitRZWorldLightCycleManager
)
708 LightCycleManager
.touch();
713 H_AUTO(InitRZWorldWaterMap
)
718 const R2::CScenarioEntryPoints::CCompleteIsland
*completeIsland
= NULL
;
720 progress
.pushCropedValues (0, 3.f
/4.f
);
722 if (!ClientCfg
.Light
)
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
++)
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());
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);
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
);
811 // temp for debug : create visualisation of current pacs primitives
812 // createInstancesFromMoveContainer(Scene, IGCallbacks->getMoveContainer(), NULL);
815 progress
.popCropedValues ();
818 if (complete
|| unhibernate
)
820 if (!ClientCfg
.Light
&& ClientCfg
.LandscapeEnabled
&& ClientCfg
.MicroLifeEnabled
)
822 H_AUTO(InitRZWorldLoadMicroLife
)
826 // Register callback on streaming
827 CAsyncFileManager::getInstance().registerTaskPriorityCallback(&ChangeTaskPriority
);
830 //=========================================================================
831 void CContinent::reloadFogMap()
833 const R2::CScenarioEntryPoints::CCompleteIsland
*completeIsland
= R2::CScenarioEntryPoints::getInstance().getCompleteIslandFromCoords(CVector2f((float) UserEntity
->pos().x
, (float) UserEntity
->pos().y
));
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
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
);
855 CVector((float) completeIsland
->XMin
+ deltaX
, (float) completeIsland
->YMin
+ deltaY
, 0.f
),
856 CVector((float) completeIsland
->XMax
- deltaX
, (float) completeIsland
->YMax
- deltaY
, 0.f
)
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 ...
866 FogMap
.init(FogMapBuild
);
871 FogMap
.init(FogMapBuild
);
874 nlinfo("fog map : (minX, minY) = (%.1f, %.1f), (maxX, maxY) = (%.1f, %.1f)", FogMap
.getMinCoord().x
, FogMap
.getMinCoord().y
, FogMap
.getMaxCoord().x
, FogMap
.getMaxCoord().y
);
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");
886 if (!getPosFromZoneName(ZoneMax
, cornerMax
))
888 nlwarning("Can't retrieve continent max corner");
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
;
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()
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();
961 // Setup the Root scene.
965 BackgroundIG
->removeFromScene (*SceneRoot
);
966 SceneRoot
->deleteInstanceGroup (BackgroundIG
);
971 // Remove outpost (collisions etc...)
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
986 IGCallbacks
->resetContainer();
990 _Villages
.forceUnload();
995 // Initialize Landscape IG Callbacks.
996 releaseLandscapeIGCallbacks();
998 // Release the IG manager only if where are not in indoor
1001 // Release the IG manager
1002 if (Landscape
) LandscapeIGManager
.reset ();
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
1023 // Release water envmap
1024 #ifdef USE_WATER_ENV_MAP
1025 Driver
->deleteWaterEnvMap(WaterEnvMap
);
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());
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
));
1061 float blendFactor
= duskRatio
!= 1 ? (dayNight
- duskRatio
) / (1.f
- duskRatio
) : 0.f
;
1062 fogColor
.blendFromui(ws
.FogColorDusk
, ws
.FogColorNight
, (uint
) (blendFactor
* 256.f
));
1066 default: // no dusk transition
1067 fogColor
.blendFromui(ws
.FogColorDay
, ws
.FogColorNight
, (uint
) (dayNight
* 256.f
));
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;
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;
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;
1116 // Override fog because of weather.
1117 overrideFog(result
, fogType
, dayNight
, duskRatio
, lightState
, overideByWeatherFogDist
);
1118 // normalize fog dist to view frustum
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
))
1199 //=========================================================================
1200 COutpost
*CContinent::getOutpost (uint outpostId
)
1202 for (uint i
=0; i
<_Outposts
.size(); i
++)
1205 if(_Outposts
[i
].getOutpostId()==(sint
)outpostId
)
1206 return &_Outposts
[i
];
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;
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
));
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);
1261 if (stream
.open(path
))
1264 if (worldMap
.load(stream
))
1266 worldMap
.convertToType(CBitmap::RGBA
);
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
);
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
);
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
;
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
;
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
];
1339 *pix
= CRGBA(intensity
, intensity
, intensity
, intensity
);
1343 *pix
= channelLookup
[intensity
];
1348 if (outBitmap
.getWidth() == 0 || outBitmap
.getHeight() == 0) return;
1349 CVector pos
= MainCam
.getMatrix().getPos();
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);
1359 if (stream
.open(path
))
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
);
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
)
1395 CurrentSky
.init(Driver
, *static_cast<CSkySheet
*>(skySheet
), false, WorldLightCycle
.NumHours
);
1397 #ifdef USE_WATER_ENV_MAP
1398 WaterEnvMapRdr
.Sky
= &CurrentSky
;
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
);
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
1423 SkyScene
->deleteInstance (Sky
);
1424 SkyScene
->deleteInstance (Sky2ndPass
);
1425 SkyScene
->deleteInstance(SkyFogPart
);
1432 // Release the (new-style) sky
1433 CurrentSky
.release();