Index: source/graphics/Camera.cpp =================================================================== --- source/graphics/Camera.cpp +++ source/graphics/Camera.cpp @@ -21,6 +21,7 @@ #include "graphics/HFTracer.h" #include "graphics/Terrain.h" +#include "graphics/Water.h" #include "maths/MathUtil.h" #include "maths/Vector2D.h" #include "maths/Vector4D.h" @@ -271,7 +272,7 @@ CPlane plane; plane.Set(CVector3D(0.f, 1.f, 0.f), // upwards normal - CVector3D(0.f, g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight, 0.f)); // passes through water plane + CVector3D(0.f, g_Game->GetWorld()->GetWater()->GetWaterHeight(), 0.f)); // passes through water plane bool gotWater = plane.FindRayIntersection( origin, dir, &waterPoint ); @@ -349,7 +350,7 @@ CPlane plane; plane.Set(CVector3D(0.f, 1.f, 0.f), // upwards normal - CVector3D(0.f, g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight, 0.f)); // passes through water plane + CVector3D(0.f, g_Game->GetWorld()->GetWater()->GetWaterHeight(), 0.f)); // passes through water plane bool gotWater = plane.FindRayIntersection( origin, dir, &waterPoint ); Index: source/graphics/CameraController.cpp =================================================================== --- source/graphics/CameraController.cpp +++ source/graphics/CameraController.cpp @@ -21,6 +21,7 @@ #include "graphics/HFTracer.h" #include "graphics/Terrain.h" +#include "graphics/Water.h" #include "i18n/L10n.h" #include "lib/input.h" #include "lib/timer.h" @@ -579,7 +580,7 @@ const float ground = std::max( g_Game->GetWorld()->GetTerrain()->GetExactGroundLevel(nearPoint.X, nearPoint.Z), - g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight); + g_Game->GetWorld()->GetWater()->GetWaterHeight()); // filter ground levels for smooth camera movement const float filtered_near_ground = g_Game->GetWorld()->GetTerrain()->GetFilteredGroundLevel(nearPoint.X, nearPoint.Z, near_radius); @@ -588,7 +589,7 @@ // filtered maximum visible ground level in view const float filtered_ground = std::max( std::max(filtered_near_ground, filtered_pivot_ground), - g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight); + g_Game->GetWorld()->GetWater()->GetWaterHeight()); // target camera height above pivot point const float pivot_height = -forwards.Y * (m_Zoom.GetSmoothedValue() - m_ViewNear); Index: source/graphics/GameView.cpp =================================================================== --- source/graphics/GameView.cpp +++ source/graphics/GameView.cpp @@ -36,6 +36,7 @@ #include "graphics/TerritoryTexture.h" #include "graphics/Unit.h" #include "graphics/UnitManager.h" +#include "graphics/Water.h" #include "lib/input.h" #include "lib/timer.h" #include "lobby/IXmppClient.h" @@ -219,6 +220,9 @@ RegMemFun(this, &CGameView::Initialize, L"CGameView init", 1); RegMemFun(g_TexMan.GetSingletonPtr(), &CTerrainTextureManager::LoadTerrainTextures, L"LoadTerrainTextures", 60); + + g_Renderer.GetSceneRenderer().GetWaterManager().LoadWaterTextures(); + g_Renderer.GetSceneRenderer().GetWaterManager().CreateRenderingTextures(); } void CGameView::BeginFrame() @@ -247,7 +251,7 @@ PROFILE3("submit terrain"); CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain(); - float waterHeight = g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight + 0.001f; + float waterHeight = m->Game->GetWorld()->GetWater()->GetWaterHeight() + 0.001f; const ssize_t patchesPerSide = pTerrain->GetPatchesPerSide(); // find out which patches will be drawn @@ -275,6 +279,7 @@ { g_TexMan.UnloadTerrainTextures(); g_Renderer.GetSceneRenderer().GetWaterManager().UnloadWaterTextures(); + g_Renderer.GetSceneRenderer().GetWaterManager().DestroyRenderingTextures(); } void CGameView::Update(const float deltaRealTime) Index: source/graphics/MapReader.h =================================================================== --- source/graphics/MapReader.h +++ source/graphics/MapReader.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -28,7 +28,7 @@ #include "simulation2/system/Entity.h" class CTerrain; -class WaterManager; +class CWater; class SkyManager; class CLightEnv; class CCinemaManager; @@ -53,11 +53,11 @@ ~CMapReader(); // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful - void LoadMap(const VfsPath& pathname, const ScriptContext& cx, JS::HandleValue settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, + void LoadMap(const VfsPath& pathname, const ScriptContext& cx, JS::HandleValue settings, CTerrain*, CWater*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc, CSimulation2*, const CSimContext*, int playerID, bool skipEntities); - void LoadRandomMap(const CStrW& scriptFile, const ScriptContext& cx, JS::HandleValue settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc_, CSimulation2*, int playerID); + void LoadRandomMap(const CStrW& scriptFile, const ScriptContext& cx, JS::HandleValue settings, CTerrain*, CWater*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc_, CSimulation2*, int playerID); private: // Load script settings for use by scripts @@ -128,7 +128,7 @@ CFileUnpacker unpacker; CTerrain* pTerrain; - WaterManager* pWaterMan; + CWater* pWater; SkyManager* pSkyMan; CPostprocManager* pPostproc; CLightEnv* pLightEnv; Index: source/graphics/MapReader.cpp =================================================================== --- source/graphics/MapReader.cpp +++ source/graphics/MapReader.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -28,6 +28,7 @@ #include "graphics/Terrain.h" #include "graphics/TerrainTextureEntry.h" #include "graphics/TerrainTextureManager.h" +#include "graphics/Water.h" #include "lib/timer.h" #include "lib/external_libraries/libsdl.h" #include "maths/MathUtil.h" @@ -38,7 +39,6 @@ #include "ps/XML/Xeromyces.h" #include "renderer/PostprocManager.h" #include "renderer/SkyManager.h" -#include "renderer/WaterManager.h" #include "scriptinterface/Object.h" #include "scriptinterface/ScriptContext.h" #include "scriptinterface/ScriptRequest.h" @@ -71,14 +71,14 @@ // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful void CMapReader::LoadMap(const VfsPath& pathname, const ScriptContext& cx, JS::HandleValue settings, CTerrain *pTerrain_, - WaterManager* pWaterMan_, SkyManager* pSkyMan_, + CWater* pWater_, SkyManager* pSkyMan_, CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_, CSimulation2 *pSimulation2_, const CSimContext* pSimContext_, int playerID_, bool skipEntities) { pTerrain = pTerrain_; + pWater = pWater_; pLightEnv = pLightEnv_; pGameView = pGameView_; - pWaterMan = pWaterMan_; pSkyMan = pSkyMan_; pCinema = pCinema_; pTrigMan = pTrigMan_; @@ -153,7 +153,7 @@ // LoadRandomMap: try to load the map data; reinitialise the scene to new data if successful void CMapReader::LoadRandomMap(const CStrW& scriptFile, const ScriptContext& cx, JS::HandleValue settings, CTerrain *pTerrain_, - WaterManager* pWaterMan_, SkyManager* pSkyMan_, + CWater* pWater_, SkyManager* pSkyMan_, CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_, CSimulation2 *pSimulation2_, int playerID_) { @@ -162,9 +162,10 @@ pSimContext = pSimulation2 ? &pSimulation2->GetSimContext() : NULL; m_ScriptSettings.init(cx.GetGeneralJSContext(), settings); pTerrain = pTerrain_; + pWater = pWater_; pLightEnv = pLightEnv_; pGameView = pGameView_; - pWaterMan = pWaterMan_; + pSkyMan = pSkyMan_; pCinema = pCinema_; pTrigMan = pTrigMan_; @@ -279,6 +280,8 @@ // initialise the terrain pTerrain->Initialize(m_PatchesPerSide, &m_Heightmap[0]); + pWater->SetMapSize(m_PatchesPerSide * PATCH_SIZE); + // setup the textures on the minipatches STileDesc* tileptr = &m_Tiles[0]; for (ssize_t j=0; jInitialize(patches, NULL); + m_MapReader.pWater->SetMapSize(patches * PATCH_SIZE); // Fill the heightmap u16* heightmap = m_MapReader.pTerrain->GetHeightMap(); @@ -714,6 +718,9 @@ int water_element_name = waterelement.GetNodeName(); if (water_element_name == el_height) { + if (m_MapReader.pWater) + m_MapReader.pWater->SetWaterHeight(waterelement.GetText().ToFloat()); + CmpPtr cmpWaterManager(*m_MapReader.pSimContext, SYSTEM_ENTITY); ENSURE(cmpWaterManager); cmpWaterManager->SetWaterLevel(entity_pos_t::FromString(waterelement.GetText())); @@ -722,15 +729,15 @@ // The rest are purely graphical effects, and should be ignored if // graphics are disabled - if (!m_MapReader.pWaterMan) + if (!m_MapReader.pWater) continue; if (water_element_name == el_type) { if (waterelement.GetText() == "default") - m_MapReader.pWaterMan->m_WaterType = L"ocean"; + m_MapReader.pWater->m_WaterType = L"ocean"; else - m_MapReader.pWaterMan->m_WaterType = waterelement.GetText().FromUTF8(); + m_MapReader.pWater->m_WaterType = waterelement.GetText().FromUTF8(); } #define READ_COLOR(el, out) \ else if (water_element_name == el) \ @@ -749,11 +756,11 @@ out = waterelement.GetText().ToFloat(); \ } \ - READ_COLOR(el_color, m_MapReader.pWaterMan->m_WaterColor) - READ_COLOR(el_tint, m_MapReader.pWaterMan->m_WaterTint) - READ_FLOAT(el_waviness, m_MapReader.pWaterMan->m_Waviness) - READ_FLOAT(el_murkiness, m_MapReader.pWaterMan->m_Murkiness) - READ_FLOAT(el_windangle, m_MapReader.pWaterMan->m_WindAngle) + READ_COLOR(el_color, m_MapReader.pWater->m_WaterColor) + READ_COLOR(el_tint, m_MapReader.pWater->m_WaterTint) + READ_FLOAT(el_waviness, m_MapReader.pWater->m_Waviness) + READ_FLOAT(el_murkiness, m_MapReader.pWater->m_Murkiness) + READ_FLOAT(el_windangle, m_MapReader.pWater->m_WindAngle) #undef READ_FLOAT #undef READ_COLOR @@ -1519,16 +1526,18 @@ cmpWaterManager->SetWaterLevel(entity_pos_t::FromFloat(waterHeight)); // If we have graphics, get rest of settings - if (pWaterMan) + if (pWater) { - GET_ENVIRONMENT_PROPERTY(waterBodyObj, Type, pWaterMan->m_WaterType) - if (pWaterMan->m_WaterType == L"default") - pWaterMan->m_WaterType = L"ocean"; - GET_ENVIRONMENT_PROPERTY(waterBodyObj, Color, pWaterMan->m_WaterColor) - GET_ENVIRONMENT_PROPERTY(waterBodyObj, Tint, pWaterMan->m_WaterTint) - GET_ENVIRONMENT_PROPERTY(waterBodyObj, Waviness, pWaterMan->m_Waviness) - GET_ENVIRONMENT_PROPERTY(waterBodyObj, Murkiness, pWaterMan->m_Murkiness) - GET_ENVIRONMENT_PROPERTY(waterBodyObj, WindAngle, pWaterMan->m_WindAngle) + pWater->SetWaterHeight(waterHeight); + + GET_ENVIRONMENT_PROPERTY(waterBodyObj, Type, pWater->m_WaterType) + if (pWater->m_WaterType == L"default") + pWater->m_WaterType = L"ocean"; + GET_ENVIRONMENT_PROPERTY(waterBodyObj, Color, pWater->m_WaterColor) + GET_ENVIRONMENT_PROPERTY(waterBodyObj, Tint, pWater->m_WaterTint) + GET_ENVIRONMENT_PROPERTY(waterBodyObj, Waviness, pWater->m_Waviness) + GET_ENVIRONMENT_PROPERTY(waterBodyObj, Murkiness, pWater->m_Murkiness) + GET_ENVIRONMENT_PROPERTY(waterBodyObj, WindAngle, pWater->m_WindAngle) } JS::RootedValue fogObject(rq.cx); Index: source/graphics/MapWriter.h =================================================================== --- source/graphics/MapWriter.h +++ source/graphics/MapWriter.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -27,6 +27,7 @@ class CLightEnv; class CTerrain; +class CWorld; class CCamera; class CCinemaManager; class CPostprocManager; @@ -41,8 +42,7 @@ // constructor CMapWriter(); // SaveMap: try to save the current map to the given file - void SaveMap(const VfsPath& pathname, CTerrain* pTerr, - WaterManager* pWaterMan, SkyManager* pSkyMan, + void SaveMap(const VfsPath& pathname, CWorld* pWorld, SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema, CPostprocManager* pPostproc, CSimulation2* pSimulation2); @@ -59,7 +59,7 @@ std::vector& tileIndices); // WriteXML: output some other data (entities, etc) in XML format - void WriteXML(const VfsPath& pathname, WaterManager* pWaterMan, + void WriteXML(const VfsPath& pathname, CWorld* pWorld, SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, CPostprocManager* pPostproc, CSimulation2* pSimulation2); Index: source/graphics/MapWriter.cpp =================================================================== --- source/graphics/MapWriter.cpp +++ source/graphics/MapWriter.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -27,12 +27,14 @@ #include "Terrain.h" #include "TerrainTextureEntry.h" #include "TerrainTextureManager.h" +#include "Water.h" #include "maths/MathUtil.h" #include "maths/NUSpline.h" #include "ps/CLogger.h" #include "ps/Loader.h" #include "ps/Filesystem.h" +#include "ps/World.h" #include "ps/XML/XMLWriter.h" #include "renderer/PostprocManager.h" #include "renderer/SkyManager.h" @@ -56,8 +58,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// // SaveMap: try to save the current map to the given file -void CMapWriter::SaveMap(const VfsPath& pathname, CTerrain* pTerrain, - WaterManager* pWaterMan, SkyManager* pSkyMan, +void CMapWriter::SaveMap(const VfsPath& pathname, CWorld* pWorld, SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* UNUSED(pCinema), CPostprocManager* pPostproc, CSimulation2* pSimulation2) @@ -65,7 +66,7 @@ CFilePacker packer(FILE_VERSION, "PSMP"); // build necessary data - PackMap(packer, pTerrain); + PackMap(packer, pWorld->GetTerrain()); try { @@ -79,7 +80,7 @@ } VfsPath pathnameXML = pathname.ChangeExtension(L".xml"); - WriteXML(pathnameXML, pWaterMan, pSkyMan, pLightEnv, pCamera, pPostproc, pSimulation2); + WriteXML(pathnameXML, pWorld, pSkyMan, pLightEnv, pCamera, pPostproc, pSimulation2); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -185,8 +186,7 @@ packer.PackRaw(&tiles[0],sizeof(STileDesc)*tiles.size()); } -void CMapWriter::WriteXML(const VfsPath& filename, - WaterManager* pWaterMan, SkyManager* pSkyMan, +void CMapWriter::WriteXML(const VfsPath& filename, CWorld* pWorld, SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, CPostprocManager* pPostproc, CSimulation2* pSimulation2) @@ -246,23 +246,25 @@ XMLWriter_Element waterBodyTag(xmlMapFile, "WaterBody"); CmpPtr cmpWaterManager(sim, SYSTEM_ENTITY); ENSURE(cmpWaterManager); - waterBodyTag.Setting("Type", pWaterMan->m_WaterType); + + CWater* pWater = pWorld->GetWater(); + waterBodyTag.Setting("Type", pWater->m_WaterType); { XMLWriter_Element colorTag(xmlMapFile, "Color"); - colorTag.Attribute("r", pWaterMan->m_WaterColor.r); - colorTag.Attribute("g", pWaterMan->m_WaterColor.g); - colorTag.Attribute("b", pWaterMan->m_WaterColor.b); + colorTag.Attribute("r", pWater->m_WaterColor.r); + colorTag.Attribute("g", pWater->m_WaterColor.g); + colorTag.Attribute("b", pWater->m_WaterColor.b); } { XMLWriter_Element tintTag(xmlMapFile, "Tint"); - tintTag.Attribute("r", pWaterMan->m_WaterTint.r); - tintTag.Attribute("g", pWaterMan->m_WaterTint.g); - tintTag.Attribute("b", pWaterMan->m_WaterTint.b); + tintTag.Attribute("r", pWater->m_WaterTint.r); + tintTag.Attribute("g", pWater->m_WaterTint.g); + tintTag.Attribute("b", pWater->m_WaterTint.b); } waterBodyTag.Setting("Height", cmpWaterManager->GetExactWaterLevel(0, 0)); - waterBodyTag.Setting("Waviness", pWaterMan->m_Waviness); - waterBodyTag.Setting("Murkiness", pWaterMan->m_Murkiness); - waterBodyTag.Setting("WindAngle", pWaterMan->m_WindAngle); + waterBodyTag.Setting("Waviness", pWater->m_Waviness); + waterBodyTag.Setting("Murkiness", pWater->m_Murkiness); + waterBodyTag.Setting("WindAngle", pWater->m_WindAngle); } } Index: source/graphics/MiniMapTexture.cpp =================================================================== --- source/graphics/MiniMapTexture.cpp +++ source/graphics/MiniMapTexture.cpp @@ -29,6 +29,7 @@ #include "graphics/TerrainTextureManager.h" #include "graphics/TerritoryTexture.h" #include "graphics/TextureManager.h" +#include "graphics/Water.h" #include "lib/bits.h" #include "lib/timer.h" #include "ps/ConfigDB.h" @@ -182,7 +183,7 @@ void CMiniMapTexture::Update(const float UNUSED(deltaRealTime)) { - if (m_WaterHeight != g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight) + if (m_WaterHeight != g_Game->GetWorld()->GetWater()->GetWaterHeight()) { m_TerrainTextureDirty = true; m_FinalTextureDirty = true; @@ -265,7 +266,7 @@ const u32 width = m_MapSize - 1; const u32 height = m_MapSize - 1; - m_WaterHeight = g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight; + m_WaterHeight = g_Game->GetWorld()->GetWater()->GetWaterHeight(); m_TerrainTextureDirty = false; for (u32 j = 0; j < height; ++j) Index: source/graphics/Water.h =================================================================== --- /dev/null +++ source/graphics/Water.h @@ -0,0 +1,93 @@ +/* Copyright (C) 2022 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#ifndef INCLUDED_WATER +#define INCLUDED_WATER + +#include "graphics/Color.h" +#include "graphics/RenderableObject.h" +#include "graphics/Terrain.h" + +#include "renderer/WaterRData.h" +#include + +//class WaterRData; + +class CWater +{ +public: + CWater(CTerrain& terrain) : m_Terrain(terrain), m_WaterRData(*this) + { + m_WindAngle = 0.0f; + m_Waviness = 8.0f; + m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f); + m_WaterTint = CColor(0.28f, 0.3f, 0.59f, 1.0f); + m_Murkiness = 0.45f; + m_WaterType = L"ocean"; + } + + void SetMapSize(ssize_t size) + { + m_MapSize = size; + m_WaterRData.SetMapSize(size); + // No need to mark the terrain as dirty, we can assume that's already been done. + } + + void SetWaterHeight(float height) + { + m_WaterHeight = height; + m_WaterRData.SetDirty(); + m_Terrain.MakeDirty(RENDERDATA_UPDATE_VERTICES); + } + + float GetWaterHeight() const { return m_WaterHeight; } + + WaterRData& GetRData() { return m_WaterRData; } + + void RecomputeWaterData() { m_WaterRData.SetDirty(); } + +protected: + CTerrain& m_Terrain; + + float m_WaterHeight; + + ssize_t m_MapSize; + +public: + // Water parameters - public for convenience in Atlas + // TODO: ideally these would be wrapped somehow. + + // Which texture to use. + std::wstring m_WaterType; + // Color of the water without refractions. This is what you're seeing when the water's deep or murkiness high. + CColor m_WaterColor; + // Tint of refraction in the water. + CColor m_WaterTint; + // How big the waves are. + float m_Waviness; + // How murky the water is. + float m_Murkiness; + // In which direction the water waves go. + float m_WindAngle; + +protected: + + WaterRData m_WaterRData; +}; + + +#endif // INCLUDED_WATER Index: source/gui/ObjectTypes/CMiniMap.cpp =================================================================== --- source/gui/ObjectTypes/CMiniMap.cpp +++ source/gui/ObjectTypes/CMiniMap.cpp @@ -30,6 +30,7 @@ #include "graphics/TerrainTextureEntry.h" #include "graphics/TerrainTextureManager.h" #include "graphics/TextureManager.h" +#include "graphics/Water.h" #include "gui/CGUI.h" #include "gui/GUIManager.h" #include "gui/GUIMatrix.h" @@ -309,7 +310,7 @@ { // Compute the camera frustum intersected with a fixed-height plane. // Use the water height as a fixed base height, which should be the lowest we can go - const float sampleHeight = g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight; + const float sampleHeight = g_Game->GetWorld()->GetWater()->GetWaterHeight(); const CCamera* camera = g_Game->GetView()->GetCamera(); const std::array hitPoints = { Index: source/ps/Game.cpp =================================================================== --- source/ps/Game.cpp +++ source/ps/Game.cpp @@ -69,7 +69,7 @@ **/ CGame::CGame(bool replayLog): m_World(new CWorld(this)), - m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), g_ScriptContext, m_World->GetTerrain())), + m_Simulation2(new CSimulation2(g_ScriptContext, m_World)), m_GameView(CRenderer::IsInitialised() ? new CGameView(this) : nullptr), m_GameStarted(false), m_Paused(false), @@ -261,8 +261,6 @@ m_World->RegisterInit(mapFile, *scriptInterface.GetContext(), settings, m_PlayerID); } - if (m_GameView) - RegMemFun(&g_Renderer.GetSceneRenderer().GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 80); if (m_IsSavedGame) RegMemFun(this, &CGame::LoadInitialState, L"Loading game", 1000); Index: source/ps/GameSetup/GameSetup.cpp =================================================================== --- source/ps/GameSetup/GameSetup.cpp +++ source/ps/GameSetup/GameSetup.cpp @@ -685,7 +685,7 @@ // on anything else.) if (args.Has("dumpSchema")) { - CSimulation2 sim(NULL, g_ScriptContext, NULL); + CSimulation2 sim(g_ScriptContext, nullptr); sim.LoadDefaultScripts(); std::ofstream f("entity.rng", std::ios_base::out | std::ios_base::trunc); f << sim.GenerateSchema(); Index: source/ps/World.h =================================================================== --- source/ps/World.h +++ source/ps/World.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -37,6 +37,7 @@ class CGame; class CUnitManager; class CTerrain; +class CWater; class CMapReader; class ScriptContext; @@ -57,6 +58,8 @@ **/ CTerrain *m_Terrain; + CWater* m_Water; + /** * pointer to the CUnitManager that holds all the units in the world. **/ @@ -91,6 +94,9 @@ inline CTerrain *GetTerrain() { return m_Terrain; } + inline CWater *GetWater() + { return m_Water; } + /** * Get a reference to the unit manager object. * Index: source/ps/World.cpp =================================================================== --- source/ps/World.cpp +++ source/ps/World.cpp @@ -22,8 +22,8 @@ #include "graphics/MapReader.h" #include "graphics/MapWriter.h" #include "graphics/Terrain.h" -#include "graphics/Terrain.h" #include "graphics/UnitManager.h" +#include "graphics/Water.h" #include "lib/timer.h" #include "ps/CLogger.h" #include "ps/CStr.h" @@ -52,6 +52,7 @@ CWorld::CWorld(CGame *pGame): m_pGame(pGame), m_Terrain(new CTerrain()), + m_Water(new CWater(*m_Terrain)), m_UnitManager(new CUnitManager()), m_MapReader(new CMapReader) { @@ -62,6 +63,8 @@ **/ void CWorld::RegisterInit(const CStrW& mapFile, const ScriptContext& cx, JS::HandleValue settings, int playerID) { + ENSURE(m_pGame); + // Load the map, if one was specified if (mapFile.length()) { @@ -70,8 +73,7 @@ try { CTriggerManager* pTriggerManager = NULL; - m_MapReader->LoadMap(mapfilename, cx, settings, m_Terrain, - CRenderer::IsInitialised() ? &g_Renderer.GetSceneRenderer().GetWaterManager() : NULL, + m_MapReader->LoadMap(mapfilename, cx, settings, m_Terrain, m_Water, CRenderer::IsInitialised() ? &g_Renderer.GetSceneRenderer().GetSkyManager() : NULL, &g_LightEnv, m_pGame->GetView(), m_pGame->GetView() ? m_pGame->GetView()->GetCinema() : NULL, @@ -91,10 +93,11 @@ void CWorld::RegisterInitRMS(const CStrW& scriptFile, const ScriptContext& cx, JS::HandleValue settings, int playerID) { + ENSURE(m_pGame); + // If scriptFile is empty, a blank map will be generated using settings (no RMS run) CTriggerManager* pTriggerManager = NULL; - m_MapReader->LoadRandomMap(scriptFile, cx, settings, m_Terrain, - CRenderer::IsInitialised() ? &g_Renderer.GetSceneRenderer().GetWaterManager() : NULL, + m_MapReader->LoadRandomMap(scriptFile, cx, settings, m_Terrain, m_Water, CRenderer::IsInitialised() ? &g_Renderer.GetSceneRenderer().GetSkyManager() : NULL, &g_LightEnv, m_pGame->GetView(), m_pGame->GetView() ? m_pGame->GetView()->GetCinema() : NULL, @@ -117,6 +120,7 @@ CWorld::~CWorld() { delete m_Terrain; + delete m_Water; delete m_UnitManager; delete m_MapReader; } Index: source/renderer/ModelRenderer.cpp =================================================================== --- source/renderer/ModelRenderer.cpp +++ source/renderer/ModelRenderer.cpp @@ -732,8 +732,11 @@ { const double period = 1.6; const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager(); - if (waterManager.m_RenderWater && waterManager.WillRenderFancyWater()) - shader->BindTexture(str_waterTex, waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)]); + if (waterManager.WillRenderFancyWater()) + { + double time = g_Renderer.GetTimeManager().GetGlobalTime(); + shader->BindTexture(str_waterTex, waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(time, period)]); + } else shader->BindTexture(str_waterTex, g_Renderer.GetTextureManager().GetErrorTexture()); } Index: source/renderer/PatchRData.cpp =================================================================== --- source/renderer/PatchRData.cpp +++ source/renderer/PatchRData.cpp @@ -28,6 +28,7 @@ #include "graphics/TerrainTextureEntry.h" #include "graphics/TextRenderer.h" #include "graphics/TextureManager.h" +#include "graphics/Water.h" #include "lib/allocators/DynamicArena.h" #include "lib/allocators/STLAllocators.h" #include "maths/MathUtil.h" @@ -1225,7 +1226,7 @@ u16 water_shore_index_map[PATCH_SIZE+1][PATCH_SIZE+1]; memset(water_shore_index_map, 0xFF, sizeof(water_shore_index_map)); - const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager(); + WaterRData& waterRData = g_Game->GetWorld()->GetWater()->GetRData(); CPatch* patch = m_Patch; CTerrain* terrain = patch->m_Parent; @@ -1239,6 +1240,8 @@ // To whoever implements different water heights, this is a TODO: water height) float waterHeight = cmpWaterManager->GetExactWaterLevel(0.0f,0.0f); + const float* windStrength = waterRData.GetWindStrength(); + // The 4 points making a water tile. int moves[4][2] = { {0, 0}, @@ -1296,7 +1299,7 @@ m_WaterBounds += vertex.m_Position; - vertex.m_WaterData = CVector2D(waterManager.m_WindStrength[xx + zz*mapSize], depth); + vertex.m_WaterData = CVector2D(windStrength[xx + zz*mapSize], depth); water_index_map[z+moves[i][1]][x+moves[i][0]] = static_cast(water_vertex_data.size()); water_vertex_data.push_back(vertex); Index: source/renderer/SceneRenderer.cpp =================================================================== --- source/renderer/SceneRenderer.cpp +++ source/renderer/SceneRenderer.cpp @@ -35,6 +35,7 @@ #include "graphics/Terrain.h" #include "graphics/Texture.h" #include "graphics/TextureManager.h" +#include "graphics/Water.h" #include "maths/Matrix3D.h" #include "maths/MathUtil.h" #include "ps/CLogger.h" @@ -285,7 +286,7 @@ // need to recreate the shadow map object to resize the shadow texture m->shadow.RecreateTexture(); - m->waterManager.Resize(); + m->waterManager.ResizeRenderingTextures(); } void CSceneRenderer::BeginFrame() @@ -537,17 +538,19 @@ camera = m_ViewCamera; + float waterHeight = wm.GetWaterPlaneHeight(); + // Temporarily change the camera to one that is reflected. // Also, for texturing purposes, make it render to a view port the size of the // water texture, stretch the image according to our aspect ratio so it covers // the whole screen despite being rendered into a square, and cover slightly more // of the view so we can see wavy reflections of slightly off-screen objects. camera.m_Orientation.Scale(1, -1, 1); - camera.m_Orientation.Translate(0, 2 * wm.m_WaterHeight, 0); + camera.m_Orientation.Translate(0, 2 * waterHeight, 0); camera.UpdateFrustum(scissor); // Clip slightly above the water to improve reflections of objects on the water // when the reflections are distorted. - camera.ClipFrustum(CVector4D(0, 1, 0, -wm.m_WaterHeight + 2.0f)); + camera.ClipFrustum(CVector4D(0, 1, 0, -waterHeight + 2.0f)); SViewPort vp; vp.m_Height = wm.m_RefTextureSize; @@ -560,7 +563,7 @@ scaleMat.SetScaling(g_Renderer.GetHeight() / static_cast(std::max(1, g_Renderer.GetWidth())), 1.0f, 1.0f); camera.SetProjection(scaleMat * camera.GetProjection()); - CVector4D camPlane(0, 1, 0, -wm.m_WaterHeight + 0.5f); + CVector4D camPlane(0, 1, 0, -waterHeight + 0.5f); SetObliqueFrustumClipping(camera, camPlane); } @@ -587,7 +590,7 @@ // the whole screen despite being rendered into a square, and cover slightly more // of the view so we can see wavy refractions of slightly off-screen objects. camera.UpdateFrustum(scissor); - camera.ClipFrustum(CVector4D(0, -1, 0, wm.m_WaterHeight + 0.5f)); // add some to avoid artifacts near steep shores. + camera.ClipFrustum(CVector4D(0, -1, 0, wm.GetWaterPlaneHeight() + 0.5f)); // add some to avoid artifacts near steep shores. SViewPort vp; vp.m_Height = wm.m_RefTextureSize; @@ -693,7 +696,7 @@ const CBoundingBoxAligned refractionScissor = m->terrainRenderer.ScissorWater(CULL_DEFAULT, m_ViewCamera); - CVector4D camPlane(0, -1, 0, wm.m_WaterHeight + 2.0f); + CVector4D camPlane(0, -1, 0, wm.GetWaterPlaneHeight() + 2.0f); SetObliqueFrustumClipping(m_ViewCamera, camPlane); g_Renderer.SetViewport(m_ViewCamera.GetViewPort()); @@ -875,7 +878,7 @@ ogl_WarnIfError(); - if (m->waterManager.m_RenderWater) + if (true)//m->waterManager.m_RenderWater) { if (waterScissor.GetVolume() > 0 && m->waterManager.WillRenderFancyWater()) { @@ -927,7 +930,7 @@ ogl_WarnIfError(); // render water - if (m->waterManager.m_RenderWater && g_Game && waterScissor.GetVolume() > 0) + if (g_Game && waterScissor.GetVolume() > 0) { if (m->waterManager.WillRenderFancyWater()) { @@ -1212,7 +1215,7 @@ } CBoundingBoxAligned waterScissor; - if (m->waterManager.m_RenderWater) + if (true)//m->waterManager.m_RenderWater) { waterScissor = m->terrainRenderer.ScissorWater(CULL_DEFAULT, m_ViewCamera); @@ -1239,7 +1242,7 @@ } // Render the waves to the Fancy effects texture - m->waterManager.RenderWaves(deviceCommandContext, frustum); + m->waterManager.RenderWaves(deviceCommandContext, g_Game->GetWorld()->GetWater()->GetRData(), frustum); } } Index: source/renderer/TerrainRenderer.cpp =================================================================== --- source/renderer/TerrainRenderer.cpp +++ source/renderer/TerrainRenderer.cpp @@ -30,6 +30,7 @@ #include "graphics/ShaderManager.h" #include "graphics/TerritoryTexture.h" #include "graphics/TextRenderer.h" +#include "graphics/Water.h" #include "maths/MathUtil.h" #include "ps/CLogger.h" #include "ps/CStrInternStatic.h" @@ -44,6 +45,7 @@ #include "renderer/SceneRenderer.h" #include "renderer/ShadowMap.h" #include "renderer/SkyManager.h" +#include "renderer/TimeManager.h" #include "renderer/VertexArray.h" #include "renderer/WaterManager.h" @@ -199,7 +201,7 @@ if (!waterBounds.IsEmpty()) { // Add a delta to avoid z-fighting. - const float height = g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight + 0.05f; + const float height = g_Game->GetWorld()->GetWater()->GetWaterHeight() + 0.05f; const float waterPos[] = { waterBounds[0].X, height, waterBounds[0].Z, waterBounds[1].X, height, waterBounds[0].Z, @@ -397,7 +399,6 @@ if (!m->fancyWaterTech) { LOGERROR("Failed to load water shader. Falling back to a simple water.\n"); - waterManager.m_RenderWater = false; return false; } waterManager.m_NeedsReloading = false; @@ -412,7 +413,7 @@ WaterMgr->CreateSuperfancyInfo(); }*/ - const double time = waterManager.m_WaterTexTimer; + const double time = g_Renderer.GetTimeManager().GetGlobalTime(); const float repeatPeriod = waterManager.m_RepeatPeriod; glEnable(GL_DEPTH_TEST); @@ -430,9 +431,13 @@ const CCamera& camera = g_Renderer.GetSceneRenderer().GetViewCamera(); + const CWater& water = *g_Game->GetWorld()->GetWater(); + + waterManager.ReloadWaterNormalTextures(water.m_WaterType); + const double period = 8.0; - fancyWaterShader->BindTexture(str_normalMap, waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)]); - fancyWaterShader->BindTexture(str_normalMap2, waterManager.m_NormalMap[waterManager.GetNextTextureIndex(period)]); + fancyWaterShader->BindTexture(str_normalMap, waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(time, period)]); + fancyWaterShader->BindTexture(str_normalMap2, waterManager.m_NormalMap[waterManager.GetNextTextureIndex(time, period)]); if (waterManager.m_WaterFancyEffects) { @@ -471,11 +476,11 @@ fancyWaterShader->Uniform(str_ambient, lightEnv.m_AmbientColor); fancyWaterShader->Uniform(str_sunDir, lightEnv.GetSunDir()); fancyWaterShader->Uniform(str_sunColor, lightEnv.m_SunColor); - fancyWaterShader->Uniform(str_color, waterManager.m_WaterColor); - fancyWaterShader->Uniform(str_tint, waterManager.m_WaterTint); - fancyWaterShader->Uniform(str_waviness, waterManager.m_Waviness); - fancyWaterShader->Uniform(str_murkiness, waterManager.m_Murkiness); - fancyWaterShader->Uniform(str_windAngle, waterManager.m_WindAngle); + fancyWaterShader->Uniform(str_color, water.m_WaterColor); + fancyWaterShader->Uniform(str_tint, water.m_WaterTint); + fancyWaterShader->Uniform(str_waviness, water.m_Waviness); + fancyWaterShader->Uniform(str_murkiness, water.m_Murkiness); + fancyWaterShader->Uniform(str_windAngle, water.m_WindAngle); fancyWaterShader->Uniform(str_repeatScale, 1.0f / repeatPeriod); fancyWaterShader->Uniform(str_losTransform, losTexture.GetTextureMatrix()[0], losTexture.GetTextureMatrix()[12], 0.f, 0.f); @@ -486,12 +491,12 @@ fancyWaterShader->Uniform(str_time, (float)time); fancyWaterShader->Uniform(str_screenSize, (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0.0f, 0.0f); - if (waterManager.m_WaterType == L"clap") + if (water.m_WaterType == L"clap") { fancyWaterShader->Uniform(str_waveParams1, 30.0f,1.5f,20.0f,0.03f); fancyWaterShader->Uniform(str_waveParams2, 0.5f,0.0f,0.0f,0.0f); } - else if (waterManager.m_WaterType == L"lake") + else if (water.m_WaterType == L"lake") { fancyWaterShader->Uniform(str_waveParams1, 8.5f,1.5f,15.0f,0.03f); fancyWaterShader->Uniform(str_waveParams2, 0.2f,0.0f,0.0f,0.07f); @@ -537,13 +542,15 @@ const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager(); CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); + const CWater& water = *g_Game->GetWorld()->GetWater(); + glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); if (g_Renderer.GetSceneRenderer().GetWaterRenderMode() == WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - const double time = waterManager.m_WaterTexTimer; + const double time = g_Renderer.GetTimeManager().GetGlobalTime(); CShaderTechniquePtr waterSimpleTech = g_Renderer.GetShaderManager().LoadEffect(str_water_simple); @@ -552,12 +559,12 @@ waterSimpleTech->GetGraphicsPipelineStateDesc()); const CShaderProgramPtr& waterSimpleShader = waterSimpleTech->GetShader(); - waterSimpleShader->BindTexture(str_baseTex, waterManager.m_WaterTexture[waterManager.GetCurrentTextureIndex(1.6)]); + waterSimpleShader->BindTexture(str_baseTex, waterManager.m_WaterTexture[waterManager.GetCurrentTextureIndex(time, 1.6)]); waterSimpleShader->BindTexture(str_losTex, losTexture.GetTextureSmooth()); waterSimpleShader->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection()); waterSimpleShader->Uniform(str_losTransform, losTexture.GetTextureMatrix()[0], losTexture.GetTextureMatrix()[12], 0.f, 0.f); waterSimpleShader->Uniform(str_time, static_cast(time)); - waterSimpleShader->Uniform(str_color, waterManager.m_WaterColor); + waterSimpleShader->Uniform(str_color, water.m_WaterColor); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) Index: source/renderer/WaterManager.h =================================================================== --- source/renderer/WaterManager.h +++ source/renderer/WaterManager.h @@ -34,8 +34,10 @@ #include class CFrustum; - -struct WaveObject; +class CSceneRenderer; +class ShaderModelRenderer; +class TerrainRenderer; +class WaterRData; /** * Class WaterManager: Maintain rendering-related water settings and textures @@ -44,39 +46,63 @@ class WaterManager { + friend class ShaderModelRenderer; + friend class CSceneRenderer; + friend class TerrainRenderer; public: - CTexturePtr m_WaterTexture[60]; - CTexturePtr m_NormalMap[60]; + WaterManager(); + ~WaterManager(); - // How strong the waves are at point X. % of waviness. - std::unique_ptr m_WindStrength; - // How far from the shore a point is. Manhattan. - std::unique_ptr m_DistanceHeightmap; + void LoadWaterTextures(); + void UnloadWaterTextures(); + void ReloadWaterNormalTextures(const CStrW& waterType); - // Waves vertex buffers - // TODO: measure storing value instead of pointer. - std::vector> m_ShoreWaves; - // Waves indices buffer. Only one since All Wave Objects have the same. - CVertexBufferManager::Handle m_ShoreWavesVBIndices; + /** + * Prepare textures used during rendering. + */ + void CreateRenderingTextures(); + void ResizeRenderingTextures(); + void DestroyRenderingTextures(); - size_t m_MapSize; - ssize_t m_TexSize; + /** + * Updates the settings to the one from the renderer, and sets m_NeedsReloading. + */ + void UpdateQuality(); + + /** + * @return the height of the rendered water plane. Used for placing refraction/reflection cameras. + */ + float GetWaterPlaneHeight() const; + + /** + * Returns true if fancy water shaders will be used (i.e. the hardware is capable + * and it hasn't been configured off) + */ + bool WillRenderFancyWater() const; + + void RenderWaves( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + WaterRData& renderData, + const CFrustum& frustrum); + + /** + * Returns an index of the current texture that should be used for rendering + * water. + */ + size_t GetCurrentTextureIndex(const double time, const double period) const; + size_t GetNextTextureIndex(const double time, const double period) const; + +protected: + CStrW m_CachedWaterType; - CTexturePtr m_WaveTex; - CTexturePtr m_FoamTex; std::unique_ptr m_FancyTexture; std::unique_ptr m_FancyTextureDepth; std::unique_ptr m_ReflFboDepthTexture; std::unique_ptr m_RefrFboDepthTexture; - // used to know what to update when updating parts of the terrain only. - u32 m_updatei0; - u32 m_updatej0; - u32 m_updatei1; - u32 m_updatej1; - - bool m_RenderWater; + // True if the shaders are dirty. + bool m_NeedsReloading; // If disabled, force the use of the simple water shader for rendering. bool m_WaterEffects; @@ -90,13 +116,12 @@ // Use complete reflections instead of showing merely the sky. bool m_WaterReflection; - bool m_NeedsReloading; - // requires also recreating the super fancy information. - bool m_NeedInfoUpdate; + CTexturePtr m_WaterTexture[60]; + CTexturePtr m_NormalMap[60]; - float m_WaterHeight; + CTexturePtr m_WaveTex; + CTexturePtr m_FoamTex; - double m_WaterTexTimer; float m_RepeatPeriod; // Reflection and refraction textures for fancy water @@ -115,92 +140,6 @@ CMatrix3D m_RefractionMatrix; CMatrix3D m_RefractionProjInvMatrix; CMatrix3D m_RefractionViewInvMatrix; - - // Water parameters - std::wstring m_WaterType; // Which texture to use. - CColor m_WaterColor; // Color of the water without refractions. This is what you're seeing when the water's deep or murkiness high. - CColor m_WaterTint; // Tint of refraction in the water. - float m_Waviness; // How big the waves are. - float m_Murkiness; // How murky the water is. - float m_WindAngle; // In which direction the water waves go. - -public: - WaterManager(); - ~WaterManager(); - - /** - * LoadWaterTextures: Load water textures from within the - * progressive load framework. - * - * @return 0 if loading has completed, a value from 1 to 100 (in percent of completion) - * if more textures need to be loaded and a negative error value on failure. - */ - int LoadWaterTextures(); - - /** - * Resize: Updates the fancy water textures so that water will render correctly - * with fancy water. - */ - void Resize(); - - /** - * ReloadWaterNormalTextures: Reload the normal textures so that changing - * water type in Atlas will actually do the right thing. - */ - void ReloadWaterNormalTextures(); - - /** - * UnloadWaterTextures: Free any loaded water textures and reset the internal state - * so that another call to LoadWaterTextures will begin progressive loading. - */ - void UnloadWaterTextures(); - - /** - * RecomputeWaterData: calculates all derived data from the waterheight - */ - void RecomputeWaterData(); - - /** - * RecomputeWindStrength: calculates the intensity of waves - */ - void RecomputeWindStrength(); - - /** - * RecomputeDistanceHeightmap: recalculates (or calculates) the distance heightmap. - */ - void RecomputeDistanceHeightmap(); - - /** - * CreateWaveMeshes: Creates the waves objects (and meshes). - */ - void CreateWaveMeshes(); - - /** - * Updates the map size. Will trigger a complete recalculation of fancy water information the next turn. - */ - void SetMapSize(size_t size); - - /** - * Updates the settings to the one from the renderer, and sets m_NeedsReloading. - */ - void UpdateQuality(); - - /** - * Returns true if fancy water shaders will be used (i.e. the hardware is capable - * and it hasn't been configured off) - */ - bool WillRenderFancyWater() const; - - void RenderWaves( - Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, - const CFrustum& frustrum); - - /** - * Returns an index of the current texture that should be used for rendering - * water. - */ - size_t GetCurrentTextureIndex(const double& period) const; - size_t GetNextTextureIndex(const double& period) const; }; Index: source/renderer/WaterManager.cpp =================================================================== --- source/renderer/WaterManager.cpp +++ source/renderer/WaterManager.cpp @@ -17,10 +17,13 @@ #include "precompiled.h" +#include "renderer/WaterManager.h" + #include "graphics/Terrain.h" #include "graphics/TextureManager.h" #include "graphics/ShaderManager.h" #include "graphics/ShaderProgram.h" +#include "graphics/Water.h" #include "lib/bits.h" #include "lib/timer.h" #include "lib/ogl.h" @@ -32,10 +35,11 @@ #include "ps/Game.h" #include "ps/VideoMode.h" #include "ps/World.h" -#include "renderer/WaterManager.h" #include "renderer/Renderer.h" #include "renderer/RenderingOptions.h" #include "renderer/SceneRenderer.h" +#include "renderer/TimeManager.h" +#include "renderer/WaterRData.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpWaterManager.h" #include "simulation2/components/ICmpRangeManager.h" @@ -75,23 +79,12 @@ WaterManager::WaterManager() { - // water - m_RenderWater = false; // disabled until textures are successfully loaded - m_WaterHeight = 5.0f; - m_RefTextureSize = 0; m_ReflectionFbo = 0; m_RefractionFbo = 0; m_FancyEffectsFBO = 0; - m_WaterTexTimer = 0.0; - - m_WindAngle = 0.0f; - m_Waviness = 8.0f; - m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f); - m_WaterTint = CColor(0.28f, 0.3f, 0.59f, 1.0f); - m_Murkiness = 0.45f; m_RepeatPeriod = 16.0f; m_WaterEffects = true; @@ -99,17 +92,6 @@ m_WaterRealDepth = false; m_WaterRefraction = false; m_WaterReflection = false; - m_WaterType = L"ocean"; - - m_NeedsReloading = false; - m_NeedInfoUpdate = true; - - m_MapSize = 0; - - m_updatei0 = 0; - m_updatej0 = 0; - m_updatei1 = 0; - m_updatej1 = 0; } WaterManager::~WaterManager() @@ -117,12 +99,6 @@ // Cleanup if the caller messed up UnloadWaterTextures(); - m_ShoreWaves.clear(); - m_ShoreWavesVBIndices.Reset(); - - m_DistanceHeightmap.reset(); - m_WindStrength.reset(); - if (!g_Renderer.GetCapabilities().m_PrettyWater) return; @@ -139,11 +115,8 @@ /////////////////////////////////////////////////////////////////// // Progressive load of water textures -int WaterManager::LoadWaterTextures() +void WaterManager::LoadWaterTextures() { - // TODO: this doesn't need to be progressive-loading any more - // (since texture loading is async now) - wchar_t pathname[PATH_MAX]; // Load diffuse grayscale images (for non-fancy water) @@ -158,18 +131,8 @@ m_WaterTexture[i] = texture; } - if (!g_Renderer.GetCapabilities().m_PrettyWater) - { - // Enable rendering, now that we've succeeded this far - m_RenderWater = true; - return 0; - } - -#if CONFIG2_GLES -#warning Fix WaterManager::LoadWaterTextures on GLES -#else // Load normalmaps (for fancy water) - ReloadWaterNormalTextures(); + ReloadWaterNormalTextures(L"ocean"); // Load CoastalWaves { @@ -189,6 +152,44 @@ m_FoamTex = texture; } +} + +void WaterManager::ReloadWaterNormalTextures(const CStrW& waterType) +{ + if (waterType == m_CachedWaterType && m_NormalMap[0]) + return; + m_CachedWaterType = waterType; + wchar_t pathname[PATH_MAX]; + for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i) + { + swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", waterType.c_str(), static_cast(i) + 1); + CTextureProperties textureProps(pathname); + textureProps.SetWrap(GL_REPEAT); + textureProps.SetMaxAnisotropy(4); + + CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); + texture->Prefetch(); + m_NormalMap[i] = texture; + } +} + +/////////////////////////////////////////////////////////////////// +// Unload water textures +void WaterManager::UnloadWaterTextures() +{ + for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++) + m_WaterTexture[i].reset(); + + for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++) + m_NormalMap[i].reset(); +} + + + +/////////////////////////////////////////////////////////////////// +// Progressive load of water textures +void WaterManager::CreateRenderingTextures() +{ // Use screen-sized textures for minimum artifacts. m_RefTextureSize = g_Renderer.GetHeight(); @@ -221,7 +222,7 @@ Renderer::Backend::Sampler::Filter::NEAREST, Renderer::Backend::Sampler::AddressMode::REPEAT)); - Resize(); + ResizeRenderingTextures(); // Create the water framebuffers @@ -273,17 +274,12 @@ } glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - - // Enable rendering, now that we've succeeded this far - m_RenderWater = true; -#endif - return 0; } /////////////////////////////////////////////////////////////////// // Resize: Updates the fancy water textures. -void WaterManager::Resize() +void WaterManager::ResizeRenderingTextures() { // Create the Fancy Effects texture m_FancyTexture = Renderer::Backend::GL::CTexture::Create2D( @@ -299,504 +295,19 @@ Renderer::Backend::Sampler::AddressMode::REPEAT)); } -void WaterManager::ReloadWaterNormalTextures() -{ - wchar_t pathname[PATH_MAX]; - for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i) - { - swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", m_WaterType.c_str(), static_cast(i) + 1); - CTextureProperties textureProps(pathname); - textureProps.SetWrap(GL_REPEAT); - textureProps.SetMaxAnisotropy(4); - - CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); - texture->Prefetch(); - m_NormalMap[i] = texture; - } -} - /////////////////////////////////////////////////////////////////// // Unload water textures -void WaterManager::UnloadWaterTextures() +void WaterManager::DestroyRenderingTextures() { - for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++) - m_WaterTexture[i].reset(); - - if (!g_Renderer.GetCapabilities().m_PrettyWater) - return; - - for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++) - m_NormalMap[i].reset(); - m_ReflectionTexture.reset(); m_RefractionTexture.reset(); glDeleteFramebuffersEXT(1, &m_RefractionFbo); glDeleteFramebuffersEXT(1, &m_ReflectionFbo); } -template -static inline void ComputeDirection(float* distanceMap, const u16* heightmap, float waterHeight, size_t SideSize, size_t maxLevel) -{ -#define ABOVEWATER(x, z) (HEIGHT_SCALE * heightmap[z*SideSize + x] >= waterHeight) -#define UPDATELOOKAHEAD \ - for (; lookahead <= id2+maxLevel && lookahead < SideSize && \ - ((!Transpose && !ABOVEWATER(lookahead, id1)) || (Transpose && !ABOVEWATER(id1, lookahead))); ++lookahead) - // Algorithm: - // We want to know the distance to the closest shore point. Go through each line/column, - // keep track of when we encountered the last shore point and how far ahead the next one is. - for (size_t id1 = 0; id1 < SideSize; ++id1) - { - size_t id2 = 0; - const size_t& x = Transpose ? id1 : id2; - const size_t& z = Transpose ? id2 : id1; - - size_t level = ABOVEWATER(x, z) ? 0 : maxLevel; - size_t lookahead = (size_t)(level > 0); - - UPDATELOOKAHEAD; - - // start moving - for (; id2 < SideSize; ++id2) - { - // update current level - if (ABOVEWATER(x, z)) - level = 0; - else - level = std::min(level+1, maxLevel); - - // move lookahead - if (lookahead == id2) - ++lookahead; - UPDATELOOKAHEAD; - - // This is the important bit: set the distance to either: - // - the distance to the previous shore point (level) - // - the distance to the next shore point (lookahead-id2) - distanceMap[z*SideSize + x] = std::min(distanceMap[z*SideSize + x], (float)std::min(lookahead-id2, level)); - } - } -#undef ABOVEWATER -#undef UPDATELOOKAHEAD -} - -/////////////////////////////////////////////////////////////////// -// Calculate our binary heightmap from the terrain heightmap. -void WaterManager::RecomputeDistanceHeightmap() -{ - CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); - if (!terrain || !terrain->GetHeightMap()) - return; - - size_t SideSize = m_MapSize; - - // we want to look ahead some distance, but not too much (less efficient and not interesting). This is our lookahead. - const size_t maxLevel = 5; - - if (!m_DistanceHeightmap) - { - m_DistanceHeightmap = std::make_unique(SideSize * SideSize); - std::fill(m_DistanceHeightmap.get(), m_DistanceHeightmap.get() + SideSize * SideSize, static_cast(maxLevel)); - } - - // Create a manhattan-distance heightmap. - // This could be refined to only be done near the coast itself, but it's probably not necessary. - - u16* heightmap = terrain->GetHeightMap(); - - ComputeDirection(m_DistanceHeightmap.get(), heightmap, m_WaterHeight, SideSize, maxLevel); - ComputeDirection(m_DistanceHeightmap.get(), heightmap, m_WaterHeight, SideSize, maxLevel); -} - -// This requires m_DistanceHeightmap to be defined properly. -void WaterManager::CreateWaveMeshes() -{ - OGL_SCOPED_DEBUG_GROUP("Create Wave Meshes"); - - if (m_MapSize == 0) - return; - - CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); - if (!terrain || !terrain->GetHeightMap()) - return; - - m_ShoreWaves.clear(); - m_ShoreWavesVBIndices.Reset(); - - if (m_Waviness < 5.0f && m_WaterType != L"ocean") - return; - - size_t SideSize = m_MapSize; - - // First step: get the points near the coast. - std::set CoastalPointsSet; - for (size_t z = 1; z < SideSize-1; ++z) - for (size_t x = 1; x < SideSize-1; ++x) - // get the points not on the shore but near it, ocean-side - if (m_DistanceHeightmap[z*m_MapSize + x] > 0.5f && m_DistanceHeightmap[z*m_MapSize + x] < 1.5f) - CoastalPointsSet.insert((z)*SideSize + x); - - // Second step: create chains out of those coastal points. - static const int around[8][2] = { { -1,-1 }, { -1,0 }, { -1,1 }, { 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 } }; - - std::vector > CoastalPointsChains; - while (!CoastalPointsSet.empty()) - { - int index = *(CoastalPointsSet.begin()); - int x = index % SideSize; - int y = (index - x ) / SideSize; - - std::deque Chain; - - Chain.push_front(CoastalPoint(index,CVector2D(x*4,y*4))); - - // Erase us. - CoastalPointsSet.erase(CoastalPointsSet.begin()); - - // We're our starter points. At most we can have 2 points close to us. - // We'll pick the first one and look for its neighbors (he can only have one new) - // Up until we either reach the end of the chain, or ourselves. - // Then go down the other direction if there is any. - int neighbours[2] = { -1, -1 }; - int nbNeighb = 0; - for (int i = 0; i < 8; ++i) - { - if (CoastalPointsSet.count(x + around[i][0] + (y + around[i][1])*SideSize)) - { - if (nbNeighb < 2) - neighbours[nbNeighb] = x + around[i][0] + (y + around[i][1])*SideSize; - ++nbNeighb; - } - } - if (nbNeighb > 2) - continue; - - for (int i = 0; i < 2; ++i) - { - if (neighbours[i] == -1) - continue; - // Move to our neighboring point - int xx = neighbours[i] % SideSize; - int yy = (neighbours[i] - xx ) / SideSize; - int indexx = xx + yy*SideSize; - int endedChain = false; - - if (i == 0) - Chain.push_back(CoastalPoint(indexx,CVector2D(xx*4,yy*4))); - else - Chain.push_front(CoastalPoint(indexx,CVector2D(xx*4,yy*4))); - - // If there's a loop we'll be the "other" neighboring point already so check for that. - // We'll readd at the end/front the other one to have full squares. - if (CoastalPointsSet.count(indexx) == 0) - break; - - CoastalPointsSet.erase(indexx); - - // Start checking from there. - while(!endedChain) - { - bool found = false; - nbNeighb = 0; - for (int p = 0; p < 8; ++p) - { - if (CoastalPointsSet.count(xx+around[p][0] + (yy + around[p][1])*SideSize)) - { - if (nbNeighb >= 2) - { - CoastalPointsSet.erase(xx + yy*SideSize); - continue; - } - ++nbNeighb; - // We've found a new point around us. - // Move there - xx = xx + around[p][0]; - yy = yy + around[p][1]; - indexx = xx + yy*SideSize; - if (i == 0) - Chain.push_back(CoastalPoint(indexx,CVector2D(xx*4,yy*4))); - else - Chain.push_front(CoastalPoint(indexx,CVector2D(xx*4,yy*4))); - CoastalPointsSet.erase(xx + yy*SideSize); - found = true; - break; - } - } - if (!found) - endedChain = true; - } - } - if (Chain.size() > 10) - CoastalPointsChains.push_back(Chain); - } - - // (optional) third step: Smooth chains out. - // This is also really dumb. - for (size_t i = 0; i < CoastalPointsChains.size(); ++i) - { - // Bump 1 for smoother. - for (int p = 0; p < 3; ++p) - { - for (size_t j = 1; j < CoastalPointsChains[i].size()-1; ++j) - { - CVector2D realPos = CoastalPointsChains[i][j-1].position + CoastalPointsChains[i][j+1].position; - - CoastalPointsChains[i][j].position = (CoastalPointsChains[i][j].position + realPos/2.0f)/2.0f; - } - } - } - - // Fourth step: create waves themselves, using those chains. We basically create subchains. - GLushort waveSizes = 14; // maximal size in width. - - // Construct indices buffer (we can afford one for all of them) - std::vector water_indices; - for (GLushort a = 0; a < waveSizes - 1; ++a) - { - for (GLushort rect = 0; rect < 7; ++rect) - { - water_indices.push_back(a * 9 + rect); - water_indices.push_back(a * 9 + 9 + rect); - water_indices.push_back(a * 9 + 1 + rect); - water_indices.push_back(a * 9 + 9 + rect); - water_indices.push_back(a * 9 + 10 + rect); - water_indices.push_back(a * 9 + 1 + rect); - } - } - // Generic indexes, max-length - m_ShoreWavesVBIndices = g_VBMan.AllocateChunk( - sizeof(GLushort), water_indices.size(), - GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER, - nullptr, CVertexBufferManager::Group::WATER); - m_ShoreWavesVBIndices->m_Owner->UpdateChunkVertices(m_ShoreWavesVBIndices.Get(), &water_indices[0]); - - float diff = (rand() % 50) / 5.0f; - - std::vector vertices, reversed; - for (size_t i = 0; i < CoastalPointsChains.size(); ++i) - { - for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j) - { - if (CoastalPointsChains[i].size()- 1 - j < waveSizes) - break; - - GLushort width = waveSizes; - - // First pass to get some parameters out. - float outmost = 0.0f; // how far to move on the shore. - float avgDepth = 0.0f; - int sign = 1; - CVector2D firstPerp(0,0), perp(0,0), lastPerp(0,0); - for (GLushort a = 0; a < waveSizes;++a) - { - lastPerp = perp; - perp = CVector2D(0,0); - int nb = 0; - CVector2D pos = CoastalPointsChains[i][j+a].position; - CVector2D posPlus; - CVector2D posMinus; - if (a > 0) - { - ++nb; - posMinus = CoastalPointsChains[i][j+a-1].position; - perp += pos-posMinus; - } - if (a < waveSizes-1) - { - ++nb; - posPlus = CoastalPointsChains[i][j+a+1].position; - perp += posPlus-pos; - } - perp /= nb; - perp = CVector2D(-perp.Y,perp.X).Normalized(); - - if (a == 0) - firstPerp = perp; - - if ( a > 1 && perp.Dot(lastPerp) < 0.90f && perp.Dot(firstPerp) < 0.70f) - { - width = a+1; - break; - } - - if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > m_WaterHeight) - sign = -1; - - avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - m_WaterHeight; - - float localOutmost = -2.0f; - while (localOutmost < 0.0f) - { - float depth = terrain->GetExactGroundLevel(pos.X+sign*perp.X*localOutmost, pos.Y+sign*perp.Y*localOutmost) - m_WaterHeight; - if (depth < 0.0f || depth > 0.6f) - localOutmost += 0.2f; - else - break; - } - - outmost += localOutmost; - } - if (width < 5) - { - j += 6; - continue; - } - - outmost /= width; - - if (outmost > -0.5f) - { - j += 3; - continue; - } - outmost = -2.5f + outmost * m_Waviness/10.0f; - - avgDepth /= width; - - if (avgDepth > -1.3f) - { - j += 3; - continue; - } - // we passed the checks, we can create a wave of size "width". - - std::unique_ptr shoreWave = std::make_unique(); - vertices.clear(); - vertices.reserve(9 * width); - - shoreWave->m_Width = width; - shoreWave->m_TimeDiff = diff; - diff += (rand() % 100) / 25.0f + 4.0f; - - for (GLushort a = 0; a < width;++a) - { - perp = CVector2D(0,0); - int nb = 0; - CVector2D pos = CoastalPointsChains[i][j+a].position; - CVector2D posPlus; - CVector2D posMinus; - if (a > 0) - { - ++nb; - posMinus = CoastalPointsChains[i][j+a-1].position; - perp += pos-posMinus; - } - if (a < waveSizes-1) - { - ++nb; - posPlus = CoastalPointsChains[i][j+a+1].position; - perp += posPlus-pos; - } - perp /= nb; - perp = CVector2D(-perp.Y,perp.X).Normalized(); - - SWavesVertex point[9]; - - float baseHeight = 0.04f; - - float halfWidth = (width-1.0f)/2.0f; - float sideNess = sqrtf(Clamp( (halfWidth - fabsf(a - halfWidth)) / 3.0f, 0.0f, 1.0f)); - - point[0].m_UV[0] = a; point[0].m_UV[1] = 8; - point[1].m_UV[0] = a; point[1].m_UV[1] = 7; - point[2].m_UV[0] = a; point[2].m_UV[1] = 6; - point[3].m_UV[0] = a; point[3].m_UV[1] = 5; - point[4].m_UV[0] = a; point[4].m_UV[1] = 4; - point[5].m_UV[0] = a; point[5].m_UV[1] = 3; - point[6].m_UV[0] = a; point[6].m_UV[1] = 2; - point[7].m_UV[0] = a; point[7].m_UV[1] = 1; - point[8].m_UV[0] = a; point[8].m_UV[1] = 0; - - point[0].m_PerpVect = perp; - point[1].m_PerpVect = perp; - point[2].m_PerpVect = perp; - point[3].m_PerpVect = perp; - point[4].m_PerpVect = perp; - point[5].m_PerpVect = perp; - point[6].m_PerpVect = perp; - point[7].m_PerpVect = perp; - point[8].m_PerpVect = perp; - - static const float perpT1[9] = { 6.0f, 6.05f, 6.1f, 6.2f, 6.3f, 6.4f, 6.5f, 6.6f, 9.7f }; - static const float perpT2[9] = { 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 3.0f, 3.3f, 3.6f, 9.5f }; - static const float perpT3[9] = { 1.1f, 0.7f, -0.2f, 0.0f, 0.6f, 1.3f, 2.2f, 3.6f, 9.0f }; - static const float perpT4[9] = { 2.0f, 2.1f, 1.2f, 1.5f, 1.7f, 1.9f, 2.7f, 3.8f, 9.0f }; - - static const float heightT1[9] = { 0.0f, 0.2f, 0.5f, 0.8f, 0.9f, 0.85f, 0.6f, 0.2f, 0.0 }; - static const float heightT2[9] = { -0.8f, -0.4f, 0.0f, 0.1f, 0.1f, 0.03f, 0.0f, 0.0f, 0.0 }; - static const float heightT3[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0 }; - - for (size_t t = 0; t < 9; ++t) - { - float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT1[t]+outmost), - pos.Y+sign*perp.Y*(perpT1[t]+outmost)); - point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight), - pos.Y+sign*perp.Y*(perpT1[t]+outmost)); - } - for (size_t t = 0; t < 9; ++t) - { - float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT2[t]+outmost), - pos.Y+sign*perp.Y*(perpT2[t]+outmost)); - point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight), - pos.Y+sign*perp.Y*(perpT2[t]+outmost)); - } - for (size_t t = 0; t < 9; ++t) - { - float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), - pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); - point[t].m_SplashPosition = CVector3D(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), baseHeight + heightT2[t]*sideNess + std::max(m_WaterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); - } - for (size_t t = 0; t < 9; ++t) - { - float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT4[t]+outmost), - pos.Y+sign*perp.Y*(perpT4[t]+outmost)); - point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(m_WaterHeight,terrHeight), - pos.Y+sign*perp.Y*(perpT4[t]+outmost)); - } - - vertices.push_back(point[8]); - vertices.push_back(point[7]); - vertices.push_back(point[6]); - vertices.push_back(point[5]); - vertices.push_back(point[4]); - vertices.push_back(point[3]); - vertices.push_back(point[2]); - vertices.push_back(point[1]); - vertices.push_back(point[0]); - - shoreWave->m_AABB += point[8].m_SplashPosition; - shoreWave->m_AABB += point[8].m_BasePosition; - shoreWave->m_AABB += point[0].m_SplashPosition; - shoreWave->m_AABB += point[0].m_BasePosition; - shoreWave->m_AABB += point[4].m_ApexPosition; - } - - if (sign == 1) - { - // Let's do some fancy reversing. - reversed.clear(); - reversed.reserve(vertices.size()); - for (int a = width - 1; a >= 0; --a) - { - for (size_t t = 0; t < 9; ++t) - reversed.push_back(vertices[a * 9 + t]); - } - std::swap(vertices, reversed); - } - j += width/2-1; - - shoreWave->m_VBVertices = g_VBMan.AllocateChunk( - sizeof(SWavesVertex), vertices.size(), - GL_STATIC_DRAW, GL_ARRAY_BUFFER, - nullptr, CVertexBufferManager::Group::WATER); - shoreWave->m_VBVertices->m_Owner->UpdateChunkVertices(shoreWave->m_VBVertices.Get(), &vertices[0]); - - m_ShoreWaves.emplace_back(std::move(shoreWave)); - } - } -} - void WaterManager::RenderWaves( Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + WaterRData& renderData, const CFrustum& frustrum) { OGL_SCOPED_DEBUG_GROUP("Render Waves"); @@ -819,6 +330,8 @@ glEnable(GL_DEPTH_TEST); glDepthFunc(GL_ALWAYS); + const double time = g_Renderer.GetTimeManager().GetGlobalTime(); + CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_water_waves); tech->BeginPass(); deviceCommandContext->SetGraphicsPipelineState( @@ -828,15 +341,18 @@ shader->BindTexture(str_waveTex, m_WaveTex); shader->BindTexture(str_foamTex, m_FoamTex); - shader->Uniform(str_time, (float)m_WaterTexTimer); + shader->Uniform(str_time, (float)time); shader->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection()); - for (size_t a = 0; a < m_ShoreWaves.size(); ++a) + const std::vector>& shoreWaves = renderData.GetShoreWavesData(); + const CVertexBufferManager::Handle& indices = renderData.GetShoreWavesIndices(); + + for (size_t a = 0; a < shoreWaves.size(); ++a) { - if (!frustrum.IsBoxVisible(m_ShoreWaves[a]->m_AABB)) + if (!frustrum.IsBoxVisible(shoreWaves[a]->m_AABB)) continue; - CVertexBuffer::VBChunk* VBchunk = m_ShoreWaves[a]->m_VBVertices.Get(); + CVertexBuffer::VBChunk* VBchunk = shoreWaves[a]->m_VBVertices.Get(); SWavesVertex* base = (SWavesVertex*)VBchunk->m_Owner->Bind(); // setup data pointers @@ -851,14 +367,14 @@ shader->AssertPointersBound(); - shader->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff); - shader->Uniform(str_width, (int)m_ShoreWaves[a]->m_Width); + shader->Uniform(str_translation, shoreWaves[a]->m_TimeDiff); + shader->Uniform(str_width, (int)shoreWaves[a]->m_Width); - u8* indexBase = m_ShoreWavesVBIndices->m_Owner->Bind(); - glDrawElements(GL_TRIANGLES, (GLsizei) (m_ShoreWaves[a]->m_Width-1)*(7*6), - GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_ShoreWavesVBIndices->m_Index)); + u8* indexBase = indices->m_Owner->Bind(); + glDrawElements(GL_TRIANGLES, (GLsizei) (shoreWaves[a]->m_Width-1)*(7*6), + GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(indices->m_Index)); - shader->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff + 6.0f); + shader->Uniform(str_translation, shoreWaves[a]->m_TimeDiff + 6.0f); // TODO: figure out why this doesn't work. //g_Renderer.m_Stats.m_DrawCalls++; @@ -873,149 +389,6 @@ #endif } -void WaterManager::RecomputeWaterData() -{ - if (!m_MapSize) - return; - - RecomputeDistanceHeightmap(); - RecomputeWindStrength(); - CreateWaveMeshes(); -} - -/////////////////////////////////////////////////////////////////// -// Calculate the strength of the wind at a given point on the map. -void WaterManager::RecomputeWindStrength() -{ - if (m_MapSize <= 0) - return; - - if (!m_WindStrength) - m_WindStrength = std::make_unique(m_MapSize * m_MapSize); - - CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); - if (!terrain || !terrain->GetHeightMap()) - return; - - CVector2D windDir = CVector2D(cos(m_WindAngle), sin(m_WindAngle)); - - int stepSize = 10; - ssize_t windX = -round(stepSize * windDir.X); - ssize_t windY = -round(stepSize * windDir.Y); - - struct SWindPoint { - SWindPoint(size_t x, size_t y, float strength) : X(x), Y(y), windStrength(strength) {} - ssize_t X; - ssize_t Y; - float windStrength; - }; - - std::vector startingPoints; - std::vector> movement; // Every increment, move each starting point by all of these. - - // Compute starting points (one or two edges of the map) and how much to move each computation increment. - if (fabs(windDir.X) < 0.01f) - { - movement.emplace_back(0, windY > 0.f ? 1 : -1); - startingPoints.reserve(m_MapSize); - size_t start = windY > 0 ? 0 : m_MapSize - 1; - for (size_t x = 0; x < m_MapSize; ++x) - startingPoints.emplace_back(x, start, 0.f); - } - else if (fabs(windDir.Y) < 0.01f) - { - movement.emplace_back(windX > 0.f ? 1 : - 1, 0); - startingPoints.reserve(m_MapSize); - size_t start = windX > 0 ? 0 : m_MapSize - 1; - for (size_t z = 0; z < m_MapSize; ++z) - startingPoints.emplace_back(start, z, 0.f); - } - else - { - startingPoints.reserve(m_MapSize * 2); - // Points along X. - size_t start = windY > 0 ? 0 : m_MapSize - 1; - for (size_t x = 0; x < m_MapSize; ++x) - startingPoints.emplace_back(x, start, 0.f); - // Points along Z, avoid repeating the corner point. - start = windX > 0 ? 0 : m_MapSize - 1; - if (windY > 0) - for (size_t z = 1; z < m_MapSize; ++z) - startingPoints.emplace_back(start, z, 0.f); - else - for (size_t z = 0; z < m_MapSize-1; ++z) - startingPoints.emplace_back(start, z, 0.f); - - // Compute movement array. - movement.reserve(std::max(std::abs(windX),std::abs(windY))); - while (windX != 0 || windY != 0) - { - std::pair move = { - windX == 0 ? 0 : windX > 0 ? +1 : -1, - windY == 0 ? 0 : windY > 0 ? +1 : -1 - }; - windX -= move.first; - windY -= move.second; - movement.push_back(move); - } - } - - // We have all starting points ready, move them all until the map is covered. - for (SWindPoint& point : startingPoints) - { - // Starting velocity is 1.0 unless in shallow water. - m_WindStrength[point.Y * m_MapSize + point.X] = 1.f; - float depth = m_WaterHeight - terrain->GetVertexGroundLevel(point.X, point.Y); - if (depth > 0.f && depth < 2.f) - m_WindStrength[point.Y * m_MapSize + point.X] = depth / 2.f; - point.windStrength = m_WindStrength[point.Y * m_MapSize + point.X]; - - bool onMap = true; - while (onMap) - for (size_t step = 0; step < movement.size(); ++step) - { - // Move wind speed towards the mean. - point.windStrength = 0.15f + point.windStrength * 0.85f; - - // Adjust speed based on height difference, a positive height difference slowly increases speed (simulate venturi effect) - // and a lower height reduces speed (wind protection from hills/...) - float heightDiff = std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X + movement[step].first, point.Y + movement[step].second)) - - std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X, point.Y)); - if (heightDiff > 0.f) - point.windStrength = std::min(2.f, point.windStrength + std::min(4.f, heightDiff) / 40.f); - else - point.windStrength = std::max(0.f, point.windStrength + std::max(-4.f, heightDiff) / 5.f); - - point.X += movement[step].first; - point.Y += movement[step].second; - - if (point.X < 0 || point.X >= static_cast(m_MapSize) || point.Y < 0 || point.Y >= static_cast(m_MapSize)) - { - onMap = false; - break; - } - m_WindStrength[point.Y * m_MapSize + point.X] = point.windStrength; - } - } - // TODO: should perhaps blur a little, or change the above code to incorporate neighboring tiles a bit. -} - -//////////////////////////////////////////////////////////////////////// -// TODO: This will always recalculate for now -void WaterManager::SetMapSize(size_t size) -{ - // TODO: Im' blindly trusting the user here. - m_MapSize = size; - m_NeedInfoUpdate = true; - m_updatei0 = 0; - m_updatei1 = size; - m_updatej0 = 0; - m_updatej1 = size; - - m_DistanceHeightmap.reset(); - m_WindStrength.reset(); -} - //////////////////////////////////////////////////////////////////////// // This will set the bools properly void WaterManager::UpdateQuality() @@ -1049,19 +422,23 @@ bool WaterManager::WillRenderFancyWater() const { - return - m_RenderWater && g_VideoMode.GetBackend() != CVideoMode::Backend::GL_ARB && + return g_VideoMode.GetBackend() != CVideoMode::Backend::GL_ARB && g_RenderingOptions.GetWaterEffects() && g_Renderer.GetCapabilities().m_PrettyWater; } -size_t WaterManager::GetCurrentTextureIndex(const double& period) const +size_t WaterManager::GetCurrentTextureIndex(const double time, const double period) const { ENSURE(period > 0.0); - return static_cast(m_WaterTexTimer * ARRAY_SIZE(m_WaterTexture) / period) % ARRAY_SIZE(m_WaterTexture); + return static_cast(time * ARRAY_SIZE(m_WaterTexture) / period) % ARRAY_SIZE(m_WaterTexture); } -size_t WaterManager::GetNextTextureIndex(const double& period) const +size_t WaterManager::GetNextTextureIndex(const double time, const double period) const { ENSURE(period > 0.0); - return (GetCurrentTextureIndex(period) + 1) % ARRAY_SIZE(m_WaterTexture); + return (GetCurrentTextureIndex(time, period) + 1) % ARRAY_SIZE(m_WaterTexture); +} + +float WaterManager::GetWaterPlaneHeight() const +{ + return g_Game->GetWorld()->GetWater()->GetWaterHeight(); } Index: source/renderer/WaterRData.h =================================================================== --- /dev/null +++ source/renderer/WaterRData.h @@ -0,0 +1,113 @@ +/* Copyright (C) 2022 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#ifndef INCLUDED_WATERRDATA +#define INCLUDED_WATERRDATA + +#include "graphics/Color.h" +#include "graphics/Texture.h" +#include "maths/Matrix3D.h" +#include "maths/Vector2D.h" +#include "renderer/backend/gl/DeviceCommandContext.h" +#include "renderer/backend/gl/Texture.h" +#include "renderer/VertexBufferManager.h" + +#include +#include + +class CFrustum; +class CWater; + +struct WaveObject; + +/** + * Rendering data for water. + */ +class WaterRData +{ +public: + WaterRData(const CWater& water); + ~WaterRData(); + + void SetDirty(); + + void SetMapSize(size_t size); + + const float* GetWindStrength() + { + if (m_NeedInfoUpdate) + RecomputeWaterData(); + return m_WindStrength.get(); + } + + const std::vector>& GetShoreWavesData() + { + if (m_NeedInfoUpdate) + RecomputeWaterData(); + return m_ShoreWaves; + } + + const CVertexBufferManager::Handle& GetShoreWavesIndices() + { + if (m_NeedInfoUpdate) + RecomputeWaterData(); + return m_ShoreWavesVBIndices; + } + +protected: + /** + * RecomputeWaterData: calculates all derived data from the waterheight + */ + void RecomputeWaterData(); + + /** + * RecomputeWindStrength: calculates the intensity of waves + */ + void RecomputeWindStrength(); + + /** + * RecomputeDistanceHeightmap: recalculates (or calculates) the distance heightmap. + */ + void RecomputeDistanceHeightmap(); + + /** + * CreateWaveMeshes: Creates the waves objects (and meshes). + */ + void CreateWaveMeshes(); + +protected: + const CWater& m_Water; + + // How strong the waves are at point X. % of waviness. + std::unique_ptr m_WindStrength; + // How far from the shore a point is. Manhattan. + std::unique_ptr m_DistanceHeightmap; + + // Waves vertex buffers + // TODO: measure storing value instead of pointer. + std::vector> m_ShoreWaves; + // Waves indices buffer. Only one since All Wave Objects have the same. + CVertexBufferManager::Handle m_ShoreWavesVBIndices; + + size_t m_MapSize; + + // requires also recreating the super fancy information. + bool m_NeedInfoUpdate; +}; + + +#endif // INCLUDED_WATERRDATA Index: source/renderer/WaterRData.cpp =================================================================== --- source/renderer/WaterRData.cpp +++ source/renderer/WaterRData.cpp @@ -32,7 +32,7 @@ #include "ps/Game.h" #include "ps/VideoMode.h" #include "ps/World.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRData.h" #include "renderer/Renderer.h" #include "renderer/RenderingOptions.h" #include "renderer/SceneRenderer.h" @@ -40,6 +40,8 @@ #include "simulation2/components/ICmpWaterManager.h" #include "simulation2/components/ICmpRangeManager.h" +#include "graphics/Water.h" + #include struct CoastalPoint @@ -73,50 +75,15 @@ float m_TimeDiff; }; -WaterManager::WaterManager() +WaterRData::WaterRData(const CWater& water) : m_Water(water) { - // water - m_RenderWater = false; // disabled until textures are successfully loaded - m_WaterHeight = 5.0f; - - m_RefTextureSize = 0; - - m_ReflectionFbo = 0; - m_RefractionFbo = 0; - m_FancyEffectsFBO = 0; - - m_WaterTexTimer = 0.0; - - m_WindAngle = 0.0f; - m_Waviness = 8.0f; - m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f); - m_WaterTint = CColor(0.28f, 0.3f, 0.59f, 1.0f); - m_Murkiness = 0.45f; - m_RepeatPeriod = 16.0f; - - m_WaterEffects = true; - m_WaterFancyEffects = false; - m_WaterRealDepth = false; - m_WaterRefraction = false; - m_WaterReflection = false; - m_WaterType = L"ocean"; - - m_NeedsReloading = false; m_NeedInfoUpdate = true; m_MapSize = 0; - - m_updatei0 = 0; - m_updatej0 = 0; - m_updatei1 = 0; - m_updatej1 = 0; } -WaterManager::~WaterManager() +WaterRData::~WaterRData() { - // Cleanup if the caller messed up - UnloadWaterTextures(); - m_ShoreWaves.clear(); m_ShoreWavesVBIndices.Reset(); @@ -125,213 +92,13 @@ if (!g_Renderer.GetCapabilities().m_PrettyWater) return; - - m_FancyTexture.reset(); - m_FancyTextureDepth.reset(); - m_ReflFboDepthTexture.reset(); - m_RefrFboDepthTexture.reset(); - - glDeleteFramebuffersEXT(1, &m_FancyEffectsFBO); - glDeleteFramebuffersEXT(1, &m_RefractionFbo); - glDeleteFramebuffersEXT(1, &m_ReflectionFbo); -} - - -/////////////////////////////////////////////////////////////////// -// Progressive load of water textures -int WaterManager::LoadWaterTextures() -{ - // TODO: this doesn't need to be progressive-loading any more - // (since texture loading is async now) - - wchar_t pathname[PATH_MAX]; - - // Load diffuse grayscale images (for non-fancy water) - for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); ++i) - { - swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/default/diffuse%02d.dds", (int)i+1); - CTextureProperties textureProps(pathname); - textureProps.SetWrap(GL_REPEAT); - - CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); - texture->Prefetch(); - m_WaterTexture[i] = texture; - } - - if (!g_Renderer.GetCapabilities().m_PrettyWater) - { - // Enable rendering, now that we've succeeded this far - m_RenderWater = true; - return 0; - } - -#if CONFIG2_GLES -#warning Fix WaterManager::LoadWaterTextures on GLES -#else - // Load normalmaps (for fancy water) - ReloadWaterNormalTextures(); - - // Load CoastalWaves - { - CTextureProperties textureProps(L"art/textures/terrain/types/water/coastalWave.png"); - textureProps.SetWrap(GL_REPEAT); - CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); - texture->Prefetch(); - m_WaveTex = texture; - } - - // Load Foam - { - CTextureProperties textureProps(L"art/textures/terrain/types/water/foam.png"); - textureProps.SetWrap(GL_REPEAT); - CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); - texture->Prefetch(); - m_FoamTex = texture; - } - - // Use screen-sized textures for minimum artifacts. - m_RefTextureSize = g_Renderer.GetHeight(); - - m_RefTextureSize = round_up_to_pow2(m_RefTextureSize); - - // Create reflection texture - m_ReflectionTexture = Renderer::Backend::GL::CTexture::Create2D( - Renderer::Backend::Format::R8G8B8A8, m_RefTextureSize, m_RefTextureSize, - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::LINEAR, - Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT)); - - // Create refraction texture - m_RefractionTexture = Renderer::Backend::GL::CTexture::Create2D( - Renderer::Backend::Format::R8G8B8A8, m_RefTextureSize, m_RefTextureSize, - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::LINEAR, - Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT)); - - // Create depth textures - m_ReflFboDepthTexture = Renderer::Backend::GL::CTexture::Create2D( - Renderer::Backend::Format::D32, m_RefTextureSize, m_RefTextureSize, - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::NEAREST, - Renderer::Backend::Sampler::AddressMode::REPEAT)); - - m_RefrFboDepthTexture = Renderer::Backend::GL::CTexture::Create2D( - Renderer::Backend::Format::D32, m_RefTextureSize, m_RefTextureSize, - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::NEAREST, - Renderer::Backend::Sampler::AddressMode::REPEAT)); - - Resize(); - - // Create the water framebuffers - - m_ReflectionFbo = 0; - glGenFramebuffersEXT(1, &m_ReflectionFbo); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_ReflectionFbo); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_ReflectionTexture->GetHandle(), 0); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_ReflFboDepthTexture->GetHandle(), 0); - - ogl_WarnIfError(); - - GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) - { - LOGWARNING("Reflection framebuffer object incomplete: 0x%04X", status); - g_RenderingOptions.SetWaterReflection(false); - UpdateQuality(); - } - - m_RefractionFbo = 0; - glGenFramebuffersEXT(1, &m_RefractionFbo); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_RefractionFbo); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_RefractionTexture->GetHandle(), 0); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_RefrFboDepthTexture->GetHandle(), 0); - - ogl_WarnIfError(); - - status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) - { - LOGWARNING("Refraction framebuffer object incomplete: 0x%04X", status); - g_RenderingOptions.SetWaterRefraction(false); - UpdateQuality(); - } - - glGenFramebuffersEXT(1, &m_FancyEffectsFBO); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_FancyTexture->GetHandle(), 0); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_FancyTextureDepth->GetHandle(), 0); - - ogl_WarnIfError(); - - status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) - { - LOGWARNING("Fancy Effects framebuffer object incomplete: 0x%04X", status); - g_RenderingOptions.SetWaterRefraction(false); - UpdateQuality(); - } - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - - // Enable rendering, now that we've succeeded this far - m_RenderWater = true; -#endif - return 0; -} - - -/////////////////////////////////////////////////////////////////// -// Resize: Updates the fancy water textures. -void WaterManager::Resize() -{ - // Create the Fancy Effects texture - m_FancyTexture = Renderer::Backend::GL::CTexture::Create2D( - Renderer::Backend::Format::R8G8B8A8, g_Renderer.GetWidth(), g_Renderer.GetHeight(), - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::LINEAR, - Renderer::Backend::Sampler::AddressMode::REPEAT)); - - m_FancyTextureDepth = Renderer::Backend::GL::CTexture::Create2D( - Renderer::Backend::Format::D32, g_Renderer.GetWidth(), g_Renderer.GetHeight(), - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::LINEAR, - Renderer::Backend::Sampler::AddressMode::REPEAT)); } -void WaterManager::ReloadWaterNormalTextures() +void WaterRData::SetDirty() { - wchar_t pathname[PATH_MAX]; - for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i) - { - swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", m_WaterType.c_str(), static_cast(i) + 1); - CTextureProperties textureProps(pathname); - textureProps.SetWrap(GL_REPEAT); - textureProps.SetMaxAnisotropy(4); - - CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); - texture->Prefetch(); - m_NormalMap[i] = texture; - } -} - -/////////////////////////////////////////////////////////////////// -// Unload water textures -void WaterManager::UnloadWaterTextures() -{ - for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++) - m_WaterTexture[i].reset(); - - if (!g_Renderer.GetCapabilities().m_PrettyWater) - return; - - for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++) - m_NormalMap[i].reset(); - - m_ReflectionTexture.reset(); - m_RefractionTexture.reset(); - glDeleteFramebuffersEXT(1, &m_RefractionFbo); - glDeleteFramebuffersEXT(1, &m_ReflectionFbo); + m_NeedInfoUpdate = true; + m_DistanceHeightmap.reset(); + m_WindStrength.reset(); } template @@ -381,7 +148,7 @@ /////////////////////////////////////////////////////////////////// // Calculate our binary heightmap from the terrain heightmap. -void WaterManager::RecomputeDistanceHeightmap() +void WaterRData::RecomputeDistanceHeightmap() { CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); if (!terrain || !terrain->GetHeightMap()) @@ -403,12 +170,12 @@ u16* heightmap = terrain->GetHeightMap(); - ComputeDirection(m_DistanceHeightmap.get(), heightmap, m_WaterHeight, SideSize, maxLevel); - ComputeDirection(m_DistanceHeightmap.get(), heightmap, m_WaterHeight, SideSize, maxLevel); + ComputeDirection(m_DistanceHeightmap.get(), heightmap, m_Water.GetWaterHeight(), SideSize, maxLevel); + ComputeDirection(m_DistanceHeightmap.get(), heightmap, m_Water.GetWaterHeight(), SideSize, maxLevel); } // This requires m_DistanceHeightmap to be defined properly. -void WaterManager::CreateWaveMeshes() +void WaterRData::CreateWaveMeshes() { OGL_SCOPED_DEBUG_GROUP("Create Wave Meshes"); @@ -422,7 +189,7 @@ m_ShoreWaves.clear(); m_ShoreWavesVBIndices.Reset(); - if (m_Waviness < 5.0f && m_WaterType != L"ocean") + if (m_Water.m_Waviness < 5.0f && m_Water.m_WaterType != L"ocean") return; size_t SideSize = m_MapSize; @@ -571,6 +338,8 @@ float diff = (rand() % 50) / 5.0f; + float waterHeight = m_Water.GetWaterHeight(); + std::vector vertices, reversed; for (size_t i = 0; i < CoastalPointsChains.size(); ++i) { @@ -618,15 +387,15 @@ break; } - if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > m_WaterHeight) + if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > waterHeight) sign = -1; - avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - m_WaterHeight; + avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - waterHeight; float localOutmost = -2.0f; while (localOutmost < 0.0f) { - float depth = terrain->GetExactGroundLevel(pos.X+sign*perp.X*localOutmost, pos.Y+sign*perp.Y*localOutmost) - m_WaterHeight; + float depth = terrain->GetExactGroundLevel(pos.X+sign*perp.X*localOutmost, pos.Y+sign*perp.Y*localOutmost) - waterHeight; if (depth < 0.0f || depth > 0.6f) localOutmost += 0.2f; else @@ -648,7 +417,7 @@ j += 3; continue; } - outmost = -2.5f + outmost * m_Waviness/10.0f; + outmost = -2.5f + outmost * m_Water.m_Waviness/10.0f; avgDepth /= width; @@ -729,27 +498,27 @@ { float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT1[t]+outmost), pos.Y+sign*perp.Y*(perpT1[t]+outmost)); - point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight), + point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(waterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT1[t]+outmost)); } for (size_t t = 0; t < 9; ++t) { float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT2[t]+outmost), pos.Y+sign*perp.Y*(perpT2[t]+outmost)); - point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight), + point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(waterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT2[t]+outmost)); } for (size_t t = 0; t < 9; ++t) { float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); - point[t].m_SplashPosition = CVector3D(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), baseHeight + heightT2[t]*sideNess + std::max(m_WaterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); + point[t].m_SplashPosition = CVector3D(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), baseHeight + heightT2[t]*sideNess + std::max(waterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); } for (size_t t = 0; t < 9; ++t) { float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT4[t]+outmost), pos.Y+sign*perp.Y*(perpT4[t]+outmost)); - point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(m_WaterHeight,terrHeight), + point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(waterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT4[t]+outmost)); } @@ -795,85 +564,7 @@ } } -void WaterManager::RenderWaves( - Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, - const CFrustum& frustrum) -{ - OGL_SCOPED_DEBUG_GROUP("Render Waves"); -#if CONFIG2_GLES - UNUSED2(deviceCommandContext); - UNUSED2(frustrum); - #warning Fix WaterManager::RenderWaves on GLES -#else - if (!m_WaterFancyEffects) - return; - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO); - - GLuint attachments[1] = { GL_COLOR_ATTACHMENT0_EXT }; - glDrawBuffers(1, attachments); - - glClearColor(0.0f,0.0f, 0.0f,0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - - CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_water_waves); - tech->BeginPass(); - deviceCommandContext->SetGraphicsPipelineState( - tech->GetGraphicsPipelineStateDesc()); - const CShaderProgramPtr& shader = tech->GetShader(); - - shader->BindTexture(str_waveTex, m_WaveTex); - shader->BindTexture(str_foamTex, m_FoamTex); - - shader->Uniform(str_time, (float)m_WaterTexTimer); - shader->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection()); - - for (size_t a = 0; a < m_ShoreWaves.size(); ++a) - { - if (!frustrum.IsBoxVisible(m_ShoreWaves[a]->m_AABB)) - continue; - - CVertexBuffer::VBChunk* VBchunk = m_ShoreWaves[a]->m_VBVertices.Get(); - SWavesVertex* base = (SWavesVertex*)VBchunk->m_Owner->Bind(); - - // setup data pointers - GLsizei stride = sizeof(SWavesVertex); - shader->VertexPointer(3, GL_FLOAT, stride, &base[VBchunk->m_Index].m_BasePosition); - shader->TexCoordPointer(GL_TEXTURE0, 2, GL_UNSIGNED_BYTE, stride, &base[VBchunk->m_Index].m_UV); - // NormalPointer(gl_FLOAT, stride, &base[m_VBWater->m_Index].m_UV) - glVertexAttribPointerARB(2, 2, GL_FLOAT, GL_FALSE, stride, &base[VBchunk->m_Index].m_PerpVect); // replaces commented above because my normal is vec2 - shader->VertexAttribPointer(str_a_apexPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_ApexPosition); - shader->VertexAttribPointer(str_a_splashPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_SplashPosition); - shader->VertexAttribPointer(str_a_retreatPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_RetreatPosition); - - shader->AssertPointersBound(); - - shader->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff); - shader->Uniform(str_width, (int)m_ShoreWaves[a]->m_Width); - - u8* indexBase = m_ShoreWavesVBIndices->m_Owner->Bind(); - glDrawElements(GL_TRIANGLES, (GLsizei) (m_ShoreWaves[a]->m_Width-1)*(7*6), - GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_ShoreWavesVBIndices->m_Index)); - - shader->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff + 6.0f); - - // TODO: figure out why this doesn't work. - //g_Renderer.m_Stats.m_DrawCalls++; - //g_Renderer.m_Stats.m_WaterTris += m_ShoreWaves_VBIndices->m_Count / 3; - - CVertexBuffer::Unbind(); - } - tech->EndPass(); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - - glDepthFunc(GL_LEQUAL); -#endif -} - -void WaterManager::RecomputeWaterData() +void WaterRData::RecomputeWaterData() { if (!m_MapSize) return; @@ -885,7 +576,7 @@ /////////////////////////////////////////////////////////////////// // Calculate the strength of the wind at a given point on the map. -void WaterManager::RecomputeWindStrength() +void WaterRData::RecomputeWindStrength() { if (m_MapSize <= 0) return; @@ -897,7 +588,7 @@ if (!terrain || !terrain->GetHeightMap()) return; - CVector2D windDir = CVector2D(cos(m_WindAngle), sin(m_WindAngle)); + CVector2D windDir = CVector2D(cos(m_Water.m_WindAngle), sin(m_Water.m_WindAngle)); int stepSize = 10; ssize_t windX = -round(stepSize * windDir.X); @@ -960,12 +651,14 @@ } } + float waterHeight = m_Water.GetWaterHeight(); + // We have all starting points ready, move them all until the map is covered. for (SWindPoint& point : startingPoints) { // Starting velocity is 1.0 unless in shallow water. m_WindStrength[point.Y * m_MapSize + point.X] = 1.f; - float depth = m_WaterHeight - terrain->GetVertexGroundLevel(point.X, point.Y); + float depth = waterHeight - terrain->GetVertexGroundLevel(point.X, point.Y); if (depth > 0.f && depth < 2.f) m_WindStrength[point.Y * m_MapSize + point.X] = depth / 2.f; point.windStrength = m_WindStrength[point.Y * m_MapSize + point.X]; @@ -979,8 +672,8 @@ // Adjust speed based on height difference, a positive height difference slowly increases speed (simulate venturi effect) // and a lower height reduces speed (wind protection from hills/...) - float heightDiff = std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X + movement[step].first, point.Y + movement[step].second)) - - std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X, point.Y)); + float heightDiff = std::max(waterHeight, terrain->GetVertexGroundLevel(point.X + movement[step].first, point.Y + movement[step].second)) - + std::max(waterHeight, terrain->GetVertexGroundLevel(point.X, point.Y)); if (heightDiff > 0.f) point.windStrength = std::min(2.f, point.windStrength + std::min(4.f, heightDiff) / 40.f); else @@ -1002,66 +695,8 @@ //////////////////////////////////////////////////////////////////////// // TODO: This will always recalculate for now -void WaterManager::SetMapSize(size_t size) +void WaterRData::SetMapSize(size_t size) { - // TODO: Im' blindly trusting the user here. m_MapSize = size; - m_NeedInfoUpdate = true; - m_updatei0 = 0; - m_updatei1 = size; - m_updatej0 = 0; - m_updatej1 = size; - - m_DistanceHeightmap.reset(); - m_WindStrength.reset(); -} - -//////////////////////////////////////////////////////////////////////// -// This will set the bools properly -void WaterManager::UpdateQuality() -{ - if (g_RenderingOptions.GetWaterEffects() != m_WaterEffects) - { - m_WaterEffects = g_RenderingOptions.GetWaterEffects(); - m_NeedsReloading = true; - } - if (g_RenderingOptions.GetWaterFancyEffects() != m_WaterFancyEffects) - { - m_WaterFancyEffects = g_RenderingOptions.GetWaterFancyEffects(); - m_NeedsReloading = true; - } - if (g_RenderingOptions.GetWaterRealDepth() != m_WaterRealDepth) - { - m_WaterRealDepth = g_RenderingOptions.GetWaterRealDepth(); - m_NeedsReloading = true; - } - if (g_RenderingOptions.GetWaterRefraction() != m_WaterRefraction) - { - m_WaterRefraction = g_RenderingOptions.GetWaterRefraction(); - m_NeedsReloading = true; - } - if (g_RenderingOptions.GetWaterReflection() != m_WaterReflection) - { - m_WaterReflection = g_RenderingOptions.GetWaterReflection(); - m_NeedsReloading = true; - } -} - -bool WaterManager::WillRenderFancyWater() const -{ - return - m_RenderWater && g_VideoMode.GetBackend() != CVideoMode::Backend::GL_ARB && - g_RenderingOptions.GetWaterEffects() && g_Renderer.GetCapabilities().m_PrettyWater; -} - -size_t WaterManager::GetCurrentTextureIndex(const double& period) const -{ - ENSURE(period > 0.0); - return static_cast(m_WaterTexTimer * ARRAY_SIZE(m_WaterTexture) / period) % ARRAY_SIZE(m_WaterTexture); -} - -size_t WaterManager::GetNextTextureIndex(const double& period) const -{ - ENSURE(period > 0.0); - return (GetCurrentTextureIndex(period) + 1) % ARRAY_SIZE(m_WaterTexture); + SetDirty(); } Index: source/simulation2/Simulation2.h =================================================================== --- source/simulation2/Simulation2.h +++ source/simulation2/Simulation2.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -32,8 +32,7 @@ class CMessage; class CSimContext; class CSimulation2Impl; -class CTerrain; -class CUnitManager; +class CWorld; class IComponent; class SceneCollector; class ScriptInterface; @@ -48,9 +47,7 @@ NONCOPYABLE(CSimulation2); public: - // TODO: CUnitManager should probably be handled automatically by this - // module, but for now we'll have it passed in externally instead - CSimulation2(CUnitManager* unitManager, std::shared_ptr cx, CTerrain* terrain); + CSimulation2(std::shared_ptr cx, CWorld* world); ~CSimulation2(); void EnableSerializationTest(); Index: source/simulation2/Simulation2.cpp =================================================================== --- source/simulation2/Simulation2.cpp +++ source/simulation2/Simulation2.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -45,6 +45,7 @@ #include "ps/Profile.h" #include "ps/Pyrogenesis.h" #include "ps/Util.h" +#include "ps/World.h" #include "ps/XML/Xeromyces.h" #include @@ -54,13 +55,14 @@ class CSimulation2Impl { public: - CSimulation2Impl(CUnitManager* unitManager, std::shared_ptr cx, CTerrain* terrain) : + CSimulation2Impl(std::shared_ptr cx, CWorld* world) : m_SimContext(), m_ComponentManager(m_SimContext, cx), m_EnableOOSLog(false), m_EnableSerializationTest(false), m_RejoinTestTurn(-1), m_TestingRejoin(false), m_MapSettings(cx->GetGeneralJSContext()), m_InitAttributes(cx->GetGeneralJSContext()) { - m_SimContext.m_UnitManager = unitManager; - m_SimContext.m_Terrain = terrain; + m_SimContext.m_UnitManager = &world->GetUnitManager(); + m_SimContext.m_Terrain = world->GetTerrain(); + m_SimContext.m_World = world; m_ComponentManager.LoadComponentTypes(); RegisterFileReloadFunc(ReloadChangedFileCB, this); @@ -636,8 +638,8 @@ //////////////////////////////////////////////////////////////// -CSimulation2::CSimulation2(CUnitManager* unitManager, std::shared_ptr cx, CTerrain* terrain) : - m(new CSimulation2Impl(unitManager, cx, terrain)) +CSimulation2::CSimulation2(std::shared_ptr cx, CWorld* world) : + m(new CSimulation2Impl(cx, world)) { } Index: source/simulation2/components/CCmpTerrain.cpp =================================================================== --- source/simulation2/components/CCmpTerrain.cpp +++ source/simulation2/components/CCmpTerrain.cpp @@ -119,12 +119,11 @@ return m_Terrain; } - virtual void ReloadTerrain(bool ReloadWater) + virtual void ReloadTerrain() { // TODO: should refactor this code to be nicer u16 tiles = GetTilesPerSide(); - u16 vertices = GetVerticesPerSide(); CmpPtr cmpObstructionManager(GetSystemEntity()); if (cmpObstructionManager) @@ -142,11 +141,6 @@ entity_pos_t::FromInt(tiles*(int)TERRAIN_TILE_SIZE)); } - if (ReloadWater && CRenderer::IsInitialised()) - { - g_Renderer.GetSceneRenderer().GetWaterManager().SetMapSize(vertices); - g_Renderer.GetSceneRenderer().GetWaterManager().RecomputeWaterData(); - } MakeDirty(0, 0, tiles+1, tiles+1); } Index: source/simulation2/components/CCmpWaterManager.cpp =================================================================== --- source/simulation2/components/CCmpWaterManager.cpp +++ source/simulation2/components/CCmpWaterManager.cpp @@ -22,9 +22,10 @@ #include "graphics/RenderableObject.h" #include "graphics/Terrain.h" +#include "graphics/Water.h" #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" -#include "renderer/WaterManager.h" +#include "ps/World.h" #include "simulation2/MessageTypes.h" #include "tools/atlas/GameInterface/GameLoop.h" @@ -34,7 +35,6 @@ static void ClassInit(CComponentManager& componentManager) { // No need to subscribe to WaterChanged since we're actually the one sending those. - componentManager.SubscribeToMessageType(MT_Interpolate); componentManager.SubscribeToMessageType(MT_TerrainChanged); } @@ -42,6 +42,9 @@ // Dynamic state: + // May be absent. + CWater* m_Water = nullptr; + entity_pos_t m_WaterHeight; static std::string GetSchema() @@ -51,13 +54,13 @@ virtual void Init(const CParamNode& UNUSED(paramNode)) { + CWorld* world = GetSimContext().GetWorld(); + if (world) + m_Water = world->GetWater(); } virtual void Deinit() { - // Clear the map size & data. - if (CRenderer::IsInitialised()) - g_Renderer.GetSceneRenderer().GetWaterManager().SetMapSize(0); } virtual void Serialize(ISerializer& serialize) @@ -70,24 +73,12 @@ Init(paramNode); deserialize.NumberFixed_Unbounded("height", m_WaterHeight); - - if (CRenderer::IsInitialised()) - g_Renderer.GetSceneRenderer().GetWaterManager().SetMapSize(GetSimContext().GetTerrain().GetVerticesPerSide()); - - RecomputeWaterData(); } virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) { switch (msg.GetType()) { - case MT_Interpolate: - { - const CMessageInterpolate& msgData = static_cast (msg); - if (CRenderer::IsInitialised()) - g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterTexTimer += msgData.deltaSimTime; - break; - } case MT_TerrainChanged: { // Tell the renderer to redraw part of the map. @@ -101,18 +92,6 @@ } } - virtual void RecomputeWaterData() - { - if (CRenderer::IsInitialised()) - { - g_Renderer.GetSceneRenderer().GetWaterManager().RecomputeWaterData(); - g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight = m_WaterHeight.ToFloat(); - } - - // Tell the terrain it'll need to recompute its cached render data - GetSimContext().GetTerrain().MakeDirty(RENDERDATA_UPDATE_VERTICES); - } - virtual void SetWaterLevel(entity_pos_t h) { if (m_WaterHeight == h) @@ -120,7 +99,8 @@ m_WaterHeight = h; - RecomputeWaterData(); + if (m_Water) + m_Water->SetWaterHeight(h.ToFloat()); CMessageWaterChanged msg; GetSimContext().GetComponentManager().BroadcastMessage(msg); Index: source/simulation2/components/ICmpTerrain.h =================================================================== --- source/simulation2/components/ICmpTerrain.h +++ source/simulation2/components/ICmpTerrain.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -64,7 +64,7 @@ * (TODO: eventually we should manage the CTerrain in this class so nobody * can modify it behind our backs). */ - virtual void ReloadTerrain(bool ReloadWater = true) = 0; + virtual void ReloadTerrain() = 0; /** * Indicate that terrain tiles within the given region (inclusive lower bound, Index: source/simulation2/components/ICmpWaterManager.h =================================================================== --- source/simulation2/components/ICmpWaterManager.h +++ source/simulation2/components/ICmpWaterManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -25,11 +25,6 @@ class ICmpWaterManager : public IComponent { public: - /** - * Recompute all the water information (foam...) - */ - virtual void RecomputeWaterData() = 0; - /** * Set the height of the water level, as a constant value across the whole map. */ Index: source/simulation2/components/ICmpWaterManager.cpp =================================================================== --- source/simulation2/components/ICmpWaterManager.cpp +++ source/simulation2/components/ICmpWaterManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -22,7 +22,6 @@ #include "simulation2/system/InterfaceScripted.h" BEGIN_INTERFACE_WRAPPER(WaterManager) -DEFINE_INTERFACE_METHOD("RecomputeWaterData", ICmpWaterManager, RecomputeWaterData) DEFINE_INTERFACE_METHOD("SetWaterLevel", ICmpWaterManager, SetWaterLevel) DEFINE_INTERFACE_METHOD("GetWaterLevel", ICmpWaterManager, GetWaterLevel) END_INTERFACE_WRAPPER(WaterManager) Index: source/simulation2/components/tests/test_Position.h =================================================================== --- source/simulation2/components/tests/test_Position.h +++ source/simulation2/components/tests/test_Position.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -36,10 +36,6 @@ return 100.f; } - virtual void RecomputeWaterData() - { - } - virtual void SetWaterLevel(entity_pos_t UNUSED(h)) { } Index: source/simulation2/system/ComponentTest.h =================================================================== --- source/simulation2/system/ComponentTest.h +++ source/simulation2/system/ComponentTest.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -242,7 +242,7 @@ { } - virtual void ReloadTerrain(bool UNUSED(ReloadWater)) + virtual void ReloadTerrain() { } }; Index: source/simulation2/system/SimContext.h =================================================================== --- source/simulation2/system/SimContext.h +++ source/simulation2/system/SimContext.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -23,6 +23,7 @@ class CComponentManager; class CUnitManager; class CTerrain; +class CWorld; class ScriptInterface; /** @@ -43,6 +44,9 @@ CTerrain& GetTerrain() const; + // May return nullptr + CWorld* GetWorld() const { return m_World; } + ScriptInterface& GetScriptInterface() const; void SetSystemEntity(CEntityHandle ent) { m_SystemEntity = ent; } @@ -58,6 +62,7 @@ CComponentManager* m_ComponentManager; CUnitManager* m_UnitManager; CTerrain* m_Terrain; + CWorld* m_World; CEntityHandle m_SystemEntity; Index: source/tools/atlas/GameInterface/ActorViewer.cpp =================================================================== --- source/tools/atlas/GameInterface/ActorViewer.cpp +++ source/tools/atlas/GameInterface/ActorViewer.cpp @@ -36,12 +36,14 @@ #include "graphics/TerritoryTexture.h" #include "graphics/Unit.h" #include "graphics/UnitManager.h" +#include "graphics/Water.h" #include "graphics/Overlay.h" #include "maths/MathUtil.h" #include "ps/Filesystem.h" #include "ps/CLogger.h" #include "ps/GameSetup/Config.h" #include "ps/ProfileViewer.h" +#include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/RenderingOptions.h" #include "renderer/Scene.h" @@ -66,18 +68,17 @@ public: ActorViewerImpl() : Entity(INVALID_ENTITY), - Terrain(), + World(nullptr), ColladaManager(g_VFS), MeshManager(ColladaManager), SkeletonAnimManager(ColladaManager), - UnitManager(), - Simulation2(&UnitManager, g_ScriptContext, &Terrain), + Simulation2(g_ScriptContext, &World), ObjectManager(MeshManager, SkeletonAnimManager, Simulation2), LOSTexture(Simulation2), TerritoryTexture(Simulation2), MiniMapTexture(Simulation2) { - UnitManager.SetObjectManager(ObjectManager); + World.GetUnitManager().SetObjectManager(ObjectManager); } entity_id_t Entity; @@ -98,12 +99,11 @@ bool AxesMarkerEnabled; int PropPointsMode; // 0 disabled, 1 for point markers, 2 for point markers + axes - CTerrain Terrain; + CWorld World; CColladaManager ColladaManager; CMeshManager MeshManager; CSkeletonAnimManager SkeletonAnimManager; - CUnitManager UnitManager; CSimulation2 Simulation2; CObjectManager ObjectManager; // Keep this after Simulation2 - it needs it for initialisation. CLOSTexture LOSTexture; @@ -118,6 +118,7 @@ // Simplistic implementation of the Scene interface virtual void EnumerateObjects(const CFrustum& frustum, SceneCollector* c) { + CTerrain& Terrain = *World.GetTerrain(); if (GroundEnabled) { for (ssize_t pj = 0; pj < Terrain.GetPatchesPerSide(); ++pj) @@ -267,17 +268,19 @@ m.AxesMarkerEnabled = false; m.PropPointsMode = 0; + CTerrain& Terrain = *m.World.GetTerrain(); + // Create a tiny empty piece of terrain, just so we can put shadows // on it without having to think too hard - m.Terrain.Initialize(2, NULL); + Terrain.Initialize(2, NULL); CTerrainTextureEntry* tex = g_TexMan.FindTexture("whiteness"); if (tex) { - for (ssize_t pi = 0; pi < m.Terrain.GetPatchesPerSide(); ++pi) + for (ssize_t pi = 0; pi < Terrain.GetPatchesPerSide(); ++pi) { - for (ssize_t pj = 0; pj < m.Terrain.GetPatchesPerSide(); ++pj) + for (ssize_t pj = 0; pj < Terrain.GetPatchesPerSide(); ++pj) { - CPatch* patch = m.Terrain.GetPatch(pi, pj); + CPatch* patch = Terrain.GetPatch(pi, pj); for (ssize_t i = 0; i < PATCH_SIZE; ++i) { for (ssize_t j = 0; j < PATCH_SIZE; ++j) @@ -306,7 +309,7 @@ // Tell the simulation we've already loaded the terrain CmpPtr cmpTerrain(m.Simulation2, SYSTEM_ENTITY); if (cmpTerrain) - cmpTerrain->ReloadTerrain(false); + cmpTerrain->ReloadTerrain(); // Remove FOW since we're in Atlas CmpPtr cmpRangeManager(m.Simulation2, SYSTEM_ENTITY); @@ -367,7 +370,7 @@ CmpPtr cmpPosition(m.Simulation2, m.Entity); if (cmpPosition) { - ssize_t c = TERRAIN_TILE_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2; + ssize_t c = TERRAIN_TILE_SIZE * m.World.GetTerrain()->GetPatchesPerSide()*PATCH_SIZE/2; cmpPosition->JumpTo(entity_pos_t::FromInt(c), entity_pos_t::FromInt(c)); cmpPosition->SetYRotation(entity_angle_t::Pi()); } @@ -449,15 +452,15 @@ m.OldSky = g_Renderer.GetSceneRenderer().GetSkyManager().GetRenderSky(); g_Renderer.GetSceneRenderer().GetSkyManager().SetRenderSky(false); - m.OldWater = g_Renderer.GetSceneRenderer().GetWaterManager().m_RenderWater; - g_Renderer.GetSceneRenderer().GetWaterManager().m_RenderWater = m.WaterEnabled; + //m.OldWater = g_Renderer.GetSceneRenderer().GetWaterManager().m_RenderWater; + //g_Renderer.GetSceneRenderer().GetWaterManager().m_RenderWater = m.WaterEnabled; } else { // Restore the old renderer state SetShadowsEnabled(m.OldShadows); g_Renderer.GetSceneRenderer().GetSkyManager().SetRenderSky(m.OldSky); - g_Renderer.GetSceneRenderer().GetWaterManager().m_RenderWater = m.OldWater; + //g_Renderer.GetSceneRenderer().GetWaterManager().m_RenderWater = m.OldWater; } } @@ -468,6 +471,7 @@ m.WaterEnabled = enabled; // Adjust water level entity_pos_t waterLevel = entity_pos_t::FromFloat(enabled ? 10.f : 0.f); + m.World.GetWater()->SetWaterHeight(waterLevel.ToFloat()); CmpPtr cmpWaterManager(m.Simulation2, SYSTEM_ENTITY); if (cmpWaterManager) cmpWaterManager->SetWaterLevel(waterLevel); @@ -512,7 +516,7 @@ cmpVisual->GetBounds().GetCenter(centre); else centre.Y = 0.f; - centre.X = centre.Z = TERRAIN_TILE_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2; + centre.X = centre.Z = TERRAIN_TILE_SIZE * m.World.GetTerrain()->GetPatchesPerSide()*PATCH_SIZE/2; CCamera camera = AtlasView::GetView_Actor()->GetCamera(); camera.m_Orientation.Translate(centre.X, centre.Y, centre.Z); @@ -548,7 +552,7 @@ float z = cmpPosition->GetPosition().Z.ToFloat(); z -= m.CurrentSpeed*simFrameLength; // Wrap at the edges, so it doesn't run off into the horizon - ssize_t c = TERRAIN_TILE_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2; + ssize_t c = TERRAIN_TILE_SIZE * m.World.GetTerrain()->GetPatchesPerSide()*PATCH_SIZE/2; if (z < c - TERRAIN_TILE_SIZE*PATCH_SIZE * 0.1f) z = c + TERRAIN_TILE_SIZE*PATCH_SIZE * 0.1f; cmpPosition->JumpTo(cmpPosition->GetPosition().X, entity_pos_t::FromFloat(z)); Index: source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp =================================================================== --- source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp +++ source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp @@ -23,6 +23,7 @@ #include "graphics/LightEnv.h" #include "graphics/Terrain.h" +#include "graphics/Water.h" #include "maths/MathUtil.h" #include "ps/CStr.h" #include "ps/Game.h" @@ -41,21 +42,19 @@ { sEnvironmentSettings s; - CmpPtr cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); - ENSURE(cmpWaterManager); + const CWater& water = *g_Game->GetWorld()->GetWater(); - s.waterheight = cmpWaterManager->GetExactWaterLevel(0, 0) / (65536.f * HEIGHT_SCALE); + s.waterheight = water.GetWaterHeight() / (65536.f * HEIGHT_SCALE); - const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager(); - s.watertype = waterManager.m_WaterType; - s.waterwaviness = waterManager.m_Waviness; - s.watermurkiness = waterManager.m_Murkiness; - s.windangle = waterManager.m_WindAngle; + s.watertype = water.m_WaterType; + s.waterwaviness = water.m_Waviness; + s.watermurkiness = water.m_Murkiness; + s.windangle = water.m_WindAngle; // CColor colors #define COLOR(A, B) A = Color((int)(B.r*255), (int)(B.g*255), (int)(B.b*255)) - COLOR(s.watercolor, waterManager.m_WaterColor); - COLOR(s.watertint, waterManager.m_WaterTint); + COLOR(s.watercolor, water.m_WaterColor); + COLOR(s.watertint, water.m_WaterTint); #undef COLOR float sunrotation = g_LightEnv.GetRotation(); @@ -100,19 +99,18 @@ cmpWaterManager->SetWaterLevel(entity_pos_t::FromFloat(s.waterheight * (65536.f * HEIGHT_SCALE))); - WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager(); - waterManager.m_Waviness = s.waterwaviness; - waterManager.m_Murkiness = s.watermurkiness; - waterManager.m_WindAngle = s.windangle; - if (waterManager.m_WaterType != *s.watertype) - { - waterManager.m_WaterType = *s.watertype; - waterManager.ReloadWaterNormalTextures(); - } + CWater& water = *g_Game->GetWorld()->GetWater(); + + water.SetWaterHeight(s.waterheight * (65536.f * HEIGHT_SCALE)); + + water.m_Waviness = s.waterwaviness; + water.m_Murkiness = s.watermurkiness; + water.m_WindAngle = s.windangle; + water.m_WaterType = *s.watertype; #define COLOR(A, B) B = CColor(A->r/255.f, A->g/255.f, A->b/255.f, 1.f) - COLOR(s.watercolor, waterManager.m_WaterColor); - COLOR(s.watertint, waterManager.m_WaterTint); + COLOR(s.watercolor, water.m_WaterColor); + COLOR(s.watertint, water.m_WaterTint); #undef COLOR g_LightEnv.SetRotation(s.sunrotation); @@ -142,8 +140,6 @@ COLOR(s.ambientcolor, g_LightEnv.m_AmbientColor); COLOR(s.fogcolor, g_LightEnv.m_FogColor); #undef COLOR - - cmpWaterManager->RecomputeWaterData(); } BEGIN_COMMAND(SetEnvironmentSettings) @@ -183,10 +179,7 @@ void Redo() { - CmpPtr cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); - ENSURE(cmpWaterManager); - - cmpWaterManager->RecomputeWaterData(); + g_Game->GetWorld()->GetWater()->RecomputeWaterData(); } void Undo() @@ -229,7 +222,7 @@ ENSURE(cmpWaterManager); cmpWaterManager->SetWaterLevel(height); - cmpWaterManager->RecomputeWaterData(); + g_Game->GetWorld()->GetWater()->SetWaterHeight(height.ToFloat()); } }; END_COMMAND(PickWaterHeight) Index: source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp =================================================================== --- source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp +++ source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp @@ -31,6 +31,7 @@ #include "graphics/Terrain.h" #include "graphics/TerrainTextureEntry.h" #include "graphics/TerrainTextureManager.h" +#include "graphics/Water.h" #include "lib/bits.h" #include "lib/file/vfs/vfs_path.h" #include "lib/status.h" @@ -207,6 +208,8 @@ const ssize_t offset = (newSize - terrain->GetPatchesPerSide()) / 2; terrain->ResizeAndOffset(newSize, offset, offset); + g_Game->GetWorld()->GetWater()->SetMapSize(terrain->GetTilesPerSide()); + // copy heightmap data into map u16* heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap(); ENSURE(heightmap_source.size() == (std::size_t) SQR(g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide())); @@ -225,8 +228,8 @@ CMapWriter writer; VfsPath pathname = VfsPath(*msg->filename).ChangeExtension(L".pmp"); writer.SaveMap(pathname, - g_Game->GetWorld()->GetTerrain(), - &g_Renderer.GetSceneRenderer().GetWaterManager(), &g_Renderer.GetSceneRenderer().GetSkyManager(), + g_Game->GetWorld(), + &g_Renderer.GetSceneRenderer().GetSkyManager(), &g_LightEnv, g_Game->GetView()->GetCamera(), g_Game->GetView()->GetCinema(), &g_Renderer.GetPostprocManager(), g_Game->GetSimulation2()); @@ -298,7 +301,7 @@ ssize_t w = dimension; ssize_t h = dimension; - const float waterHeight = g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight; + const float waterHeight = g_Game->GetWorld()->GetWater()->GetWaterHeight(); for (ssize_t j = 0; j < h; ++j) { @@ -422,6 +425,7 @@ { CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); terrain->ResizeAndOffset(patches, -offsetX, -offsetY); + g_Game->GetWorld()->GetWater()->SetMapSize(terrain->GetTilesPerSide()); } void DeleteObjects(const std::vector& deletedObjects)