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" @@ -28,7 +29,7 @@ #include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" CCamera::CCamera() { @@ -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" @@ -37,7 +38,7 @@ #include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpPosition.h" #include "simulation2/components/ICmpRangeManager.h" @@ -581,7 +582,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); @@ -590,7 +591,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" @@ -56,7 +57,7 @@ #include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpPosition.h" #include "simulation2/components/ICmpRangeManager.h" @@ -219,6 +220,7 @@ RegMemFun(this, &CGameView::Initialize, L"CGameView init", 1); RegMemFun(g_TexMan.GetSingletonPtr(), &CTerrainTextureManager::LoadTerrainTextures, L"LoadTerrainTextures", 60); + g_Renderer.GetSceneRenderer().GetWaterRenderer().LoadWaterTextures(); } void CGameView::BeginFrame() @@ -247,7 +249,7 @@ PROFILE3("submit terrain"); CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain(); - float waterHeight = g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight + 0.001f; + const float waterHeight = m->Game->GetWorld()->GetWater()->GetWaterHeight() + 0.001f; const ssize_t patchesPerSide = pTerrain->GetPatchesPerSide(); // find out which patches will be drawn @@ -274,7 +276,7 @@ void CGameView::UnloadResources() { g_TexMan.UnloadTerrainTextures(); - g_Renderer.GetSceneRenderer().GetWaterManager().UnloadWaterTextures(); + g_Renderer.GetSceneRenderer().GetWaterRenderer().UnloadWaterTextures(); } void CGameView::Update(const float deltaRealTime) Index: source/graphics/MapReader.h =================================================================== --- source/graphics/MapReader.h +++ source/graphics/MapReader.h @@ -27,7 +27,7 @@ #include "simulation2/system/Entity.h" class CTerrain; -class WaterManager; +class CWater; class SkyManager; class CLightEnv; class CCinemaManager; @@ -52,11 +52,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 @@ -127,7 +127,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 @@ -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_; @@ -280,6 +281,7 @@ { // initialise the terrain pTerrain->Initialize(m_PatchesPerSide, &m_Heightmap[0]); + pWater->SetMapSize(m_PatchesPerSide * PATCH_SIZE); if (CTerrainTextureManager::IsInitialised()) { @@ -568,6 +570,7 @@ texentry = g_TexMan.FindTexture(texture); m_MapReader.pTerrain->Initialize(patches, NULL); + m_MapReader.pWater->SetMapSize(patches * PATCH_SIZE); // Fill the heightmap u16* heightmap = m_MapReader.pTerrain->GetHeightMap(); @@ -720,6 +723,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())); @@ -728,15 +734,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) \ @@ -755,11 +761,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 @@ -1527,16 +1533,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,10 +27,11 @@ class CLightEnv; class CTerrain; +class CWorld; class CCamera; class CCinemaManager; class CPostprocManager; -class WaterManager; +class WaterRenderer; class SkyManager; class CSimulation2; struct MapTrigger; @@ -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,16 +27,18 @@ #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" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpCinemaManager.h" #include "simulation2/components/ICmpGarrisonHolder.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,26 @@ XMLWriter_Element waterBodyTag(xmlMapFile, "WaterBody"); CmpPtr cmpWaterManager(sim, SYSTEM_ENTITY); ENSURE(cmpWaterManager); - waterBodyTag.Setting("Type", pWaterMan->m_WaterType); + + waterBodyTag.Setting("Height", cmpWaterManager->GetExactWaterLevel(0, 0)); + + 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/hash.h" #include "lib/timer.h" @@ -46,7 +47,7 @@ #include "renderer/Renderer.h" #include "renderer/RenderingOptions.h" #include "renderer/SceneRenderer.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "scriptinterface/Object.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpMinimap.h" @@ -291,7 +292,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; @@ -376,7 +377,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,89 @@ +/* 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 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_WaterRData.SetMapSize(size); + // Unlike in SetWaterHeight, no need to mark the terrain as dirty, + // as we can assume that's already been done. + } + + void SetWaterHeight(float height) + { + m_WaterHeight = height; + m_WaterRData.SetDirty(); + // TODO: for now, the terrain PatchRData holds water vertices, + // so we need to mark them dirty so they get recomputed. + m_Terrain.MakeDirty(RENDERDATA_UPDATE_VERTICES); + } + + float GetWaterHeight() const { return m_WaterHeight; } + float GetExactWaterHeight(float UNUSED(x), float UNUSED(z)) const { return m_WaterHeight; } + + WaterRData& GetRData() { return m_WaterRData; } + + void RecomputeWaterData() { m_WaterRData.SetDirty(); } + +public: + // Water parameters - public for convenience in Atlas + // TODO: ideally these would be wrapped somehow. + + // Which texture to use. + CStrW 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; + +private: + CTerrain& m_Terrain; + WaterRData m_WaterRData; + + float m_WaterHeight = 0.f; +}; + + +#endif // INCLUDED_WATER Index: source/gui/ObjectTypes/CMiniMap.cpp =================================================================== --- source/gui/ObjectTypes/CMiniMap.cpp +++ source/gui/ObjectTypes/CMiniMap.cpp @@ -27,6 +27,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 "lib/bits.h" @@ -42,7 +43,7 @@ #include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "scriptinterface/Object.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpMinimap.h" @@ -269,7 +270,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 @@ -41,7 +41,7 @@ #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" #include "renderer/TimeManager.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "scriptinterface/FunctionWrapper.h" #include "scriptinterface/ScriptInterface.h" #include "scriptinterface/JSON.h" @@ -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 @@ -592,7 +592,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 @@ -40,7 +40,7 @@ #include "renderer/SceneRenderer.h" #include "renderer/SkyManager.h" #include "renderer/TimeManager.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" /////////////////////////////////////////////////////////////////////////////////////////////// // ModelRenderer implementation @@ -714,10 +714,10 @@ if (!boundWaterTexture) { const double period = 1.6; - const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager(); - if (waterManager.m_RenderWater && waterManager.WillRenderFancyWater()) + const WaterRenderer& waterRenderer = g_Renderer.GetSceneRenderer().GetWaterRenderer(); + if (waterRenderer.WillRenderFancyWater()) { - const CTexturePtr& waterTexture = waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)]; + const CTexturePtr& waterTexture = waterRenderer.m_NormalMap[waterRenderer.GetCurrentTextureIndex(time, period)]; waterTexture->UploadBackendTextureIfNeeded(deviceCommandContext); deviceCommandContext->SetTexture( shader->GetBindingSlot(str_waterTex), Index: source/renderer/OverlayRenderer.cpp =================================================================== --- source/renderer/OverlayRenderer.cpp +++ source/renderer/OverlayRenderer.cpp @@ -38,7 +38,6 @@ #include "renderer/VertexArray.h" #include "renderer/VertexBuffer.h" #include "renderer/VertexBufferManager.h" -#include "simulation2/components/ICmpWaterManager.h" #include "simulation2/Simulation2.h" #include "simulation2/system/SimContext.h" 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" @@ -44,7 +45,7 @@ #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" #include "renderer/TerrainRenderer.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "simulation2/components/ICmpWaterManager.h" #include "simulation2/Simulation2.h" @@ -1321,7 +1322,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; @@ -1335,6 +1336,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}, @@ -1392,7 +1395,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/RenderingOptions.cpp =================================================================== --- source/renderer/RenderingOptions.cpp +++ source/renderer/RenderingOptions.cpp @@ -29,7 +29,7 @@ #include "renderer/PostprocManager.h" #include "renderer/SceneRenderer.h" #include "renderer/ShadowMap.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" CRenderingOptions g_RenderingOptions; @@ -203,14 +203,14 @@ CFG_GET_VAL("watereffects", enabled); SetWaterEffects(enabled); if (CRenderer::IsInitialised()) - g_Renderer.GetSceneRenderer().GetWaterManager().RecreateOrLoadTexturesIfNeeded(); + g_Renderer.GetSceneRenderer().GetWaterRenderer().RecreateOrLoadTexturesIfNeeded(); }); m_ConfigHooks->Setup("waterfancyeffects", [this]() { bool enabled; CFG_GET_VAL("waterfancyeffects", enabled); SetWaterFancyEffects(enabled); if (CRenderer::IsInitialised()) - g_Renderer.GetSceneRenderer().GetWaterManager().RecreateOrLoadTexturesIfNeeded(); + g_Renderer.GetSceneRenderer().GetWaterRenderer().RecreateOrLoadTexturesIfNeeded(); }); m_ConfigHooks->Setup("waterrealdepth", m_WaterRealDepth); m_ConfigHooks->Setup("waterrefraction", [this]() { @@ -218,14 +218,14 @@ CFG_GET_VAL("waterrefraction", enabled); SetWaterRefraction(enabled); if (CRenderer::IsInitialised()) - g_Renderer.GetSceneRenderer().GetWaterManager().RecreateOrLoadTexturesIfNeeded(); + g_Renderer.GetSceneRenderer().GetWaterRenderer().RecreateOrLoadTexturesIfNeeded(); }); m_ConfigHooks->Setup("waterreflection", [this]() { bool enabled; CFG_GET_VAL("waterreflection", enabled); SetWaterReflection(enabled); if (CRenderer::IsInitialised()) - g_Renderer.GetSceneRenderer().GetWaterManager().RecreateOrLoadTexturesIfNeeded(); + g_Renderer.GetSceneRenderer().GetWaterRenderer().RecreateOrLoadTexturesIfNeeded(); }); m_ConfigHooks->Setup("particles", m_Particles); Index: source/renderer/SceneRenderer.h =================================================================== --- source/renderer/SceneRenderer.h +++ source/renderer/SceneRenderer.h @@ -39,7 +39,7 @@ class ShadowMap; class SkyManager; class TerrainRenderer; -class WaterManager; +class WaterRenderer; // rendering modes enum ERenderMode { WIREFRAME, SOLID, EDGED_FACES }; @@ -154,11 +154,11 @@ const CCamera& GetCullCamera() const { return m_CullCamera; } /** - * GetWaterManager: Return the renderer's water manager. + * GetWaterRenderer: Return the renderer's water manager. * - * @return the WaterManager object used by the renderer + * @return the WaterRenderer object used by the renderer */ - WaterManager& GetWaterManager(); + WaterRenderer& GetWaterRenderer(); /** * GetSkyManager: Return the renderer's sky manager. 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" @@ -60,7 +61,7 @@ #include "renderer/SkyManager.h" #include "renderer/TerrainOverlay.h" #include "renderer/TerrainRenderer.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include @@ -81,7 +82,7 @@ ~Internals() = default; /// Water manager - WaterManager waterManager; + WaterRenderer waterManager; /// Sky manager SkyManager skyManager; @@ -462,7 +463,7 @@ void CSceneRenderer::ComputeReflectionCamera(CCamera& camera, const CBoundingBoxAligned& scissor) const { - WaterManager& wm = m->waterManager; + WaterRenderer& wm = m->waterManager; CMatrix3D projection; if (m_ViewCamera.GetProjectionType() == CCamera::ProjectionType::PERSPECTIVE) @@ -478,17 +479,19 @@ camera = m_ViewCamera; + const 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; @@ -501,13 +504,13 @@ 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); } void CSceneRenderer::ComputeRefractionCamera(CCamera& camera, const CBoundingBoxAligned& scissor) const { - WaterManager& wm = m->waterManager; + WaterRenderer& wm = m->waterManager; CMatrix3D projection; if (m_ViewCamera.GetProjectionType() == CCamera::ProjectionType::PERSPECTIVE) @@ -528,7 +531,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; @@ -550,7 +553,7 @@ PROFILE3_GPU("water reflections"); GPU_SCOPED_LABEL(deviceCommandContext, "Render water reflections"); - WaterManager& wm = m->waterManager; + WaterRenderer& wm = m->waterManager; // Remember old camera CCamera normalCamera = m_ViewCamera; @@ -621,7 +624,7 @@ PROFILE3_GPU("water refractions"); GPU_SCOPED_LABEL(deviceCommandContext, "Render water refractions"); - WaterManager& wm = m->waterManager; + WaterRenderer& wm = m->waterManager; // Remember old camera CCamera normalCamera = m_ViewCamera; @@ -635,7 +638,7 @@ return; } - 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()); @@ -763,6 +766,8 @@ PROFILE3("render submissions"); GPU_SCOPED_LABEL(deviceCommandContext, "Render submissions"); + GetWaterRenderer().ReloadWaterNormalTexturesIfNeeded(); + m->skyManager.LoadAndUploadSkyTexturesIfNeeded(deviceCommandContext); GetScene().GetLOSTexture().InterpolateLOS(deviceCommandContext); @@ -799,22 +804,19 @@ RenderShadowMap(deviceCommandContext, context); } - if (m->waterManager.m_RenderWater) + if (waterScissor.GetVolume() > 0 && m->waterManager.WillRenderFancyWater()) { - if (waterScissor.GetVolume() > 0 && m->waterManager.WillRenderFancyWater()) - { - m->waterManager.UpdateQuality(); + m->waterManager.UpdateQuality(); - PROFILE3_GPU("water scissor"); - if (g_RenderingOptions.GetWaterReflection()) - RenderReflections(deviceCommandContext, context, waterScissor); + PROFILE3_GPU("water scissor"); + if (g_RenderingOptions.GetWaterReflection()) + RenderReflections(deviceCommandContext, context, waterScissor); - if (g_RenderingOptions.GetWaterRefraction()) - RenderRefractions(deviceCommandContext, context, waterScissor); + if (g_RenderingOptions.GetWaterRefraction()) + RenderRefractions(deviceCommandContext, context, waterScissor); - if (g_RenderingOptions.GetWaterFancyEffects()) - m->terrainRenderer.RenderWaterFoamOccluders(deviceCommandContext, cullGroup); - } + if (g_RenderingOptions.GetWaterFancyEffects()) + m->terrainRenderer.RenderWaterFoamOccluders(deviceCommandContext, cullGroup); } deviceCommandContext->SetGraphicsPipelineState( @@ -858,7 +860,7 @@ RenderModels(deviceCommandContext, context, cullGroup); // render water - if (m->waterManager.m_RenderWater && g_Game && waterScissor.GetVolume() > 0) + if (g_Game && waterScissor.GetVolume() > 0) { if (m->waterManager.WillRenderFancyWater()) { @@ -1116,35 +1118,32 @@ } CBoundingBoxAligned waterScissor; - if (m->waterManager.m_RenderWater) + waterScissor = m->terrainRenderer.ScissorWater(CULL_DEFAULT, m_ViewCamera); + + if (waterScissor.GetVolume() > 0 && m->waterManager.WillRenderFancyWater()) { - waterScissor = m->terrainRenderer.ScissorWater(CULL_DEFAULT, m_ViewCamera); - - if (waterScissor.GetVolume() > 0 && m->waterManager.WillRenderFancyWater()) + if (g_RenderingOptions.GetWaterReflection()) { - if (g_RenderingOptions.GetWaterReflection()) - { - m_CurrentCullGroup = CULL_REFLECTIONS; - - CCamera reflectionCamera; - ComputeReflectionCamera(reflectionCamera, waterScissor); + m_CurrentCullGroup = CULL_REFLECTIONS; - scene.EnumerateObjects(reflectionCamera.GetFrustum(), this); - } + CCamera reflectionCamera; + ComputeReflectionCamera(reflectionCamera, waterScissor); - if (g_RenderingOptions.GetWaterRefraction()) - { - m_CurrentCullGroup = CULL_REFRACTIONS; + scene.EnumerateObjects(reflectionCamera.GetFrustum(), this); + } - CCamera refractionCamera; - ComputeRefractionCamera(refractionCamera, waterScissor); + if (g_RenderingOptions.GetWaterRefraction()) + { + m_CurrentCullGroup = CULL_REFRACTIONS; - scene.EnumerateObjects(refractionCamera.GetFrustum(), this); - } + CCamera refractionCamera; + ComputeRefractionCamera(refractionCamera, waterScissor); - // Render the waves to the Fancy effects texture - m->waterManager.RenderWaves(deviceCommandContext, frustum); + scene.EnumerateObjects(refractionCamera.GetFrustum(), this); } + + // Render the waves to the Fancy effects texture + m->waterManager.RenderWaves(deviceCommandContext, g_Game->GetWorld()->GetWater()->GetRData(), frustum); } m_CurrentCullGroup = -1; @@ -1165,7 +1164,7 @@ m->waterManager.m_NeedsReloading = true; } -WaterManager& CSceneRenderer::GetWaterManager() +WaterRenderer& CSceneRenderer::GetWaterRenderer() { return m->waterManager; } Index: source/renderer/TerrainRenderer.cpp =================================================================== --- source/renderer/TerrainRenderer.cpp +++ source/renderer/TerrainRenderer.cpp @@ -31,6 +31,7 @@ #include "graphics/TerritoryTexture.h" #include "graphics/TextRenderer.h" #include "graphics/TextureManager.h" +#include "graphics/Water.h" #include "maths/MathUtil.h" #include "ps/CLogger.h" #include "ps/CStrInternStatic.h" @@ -46,8 +47,9 @@ #include "renderer/SceneRenderer.h" #include "renderer/ShadowMap.h" #include "renderer/SkyManager.h" +#include "renderer/TimeManager.h" #include "renderer/VertexArray.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" /** * TerrainRenderer keeps track of which phase it is in, to detect @@ -194,7 +196,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, @@ -396,7 +398,7 @@ CSceneRenderer& sceneRenderer = g_Renderer.GetSceneRenderer(); - WaterManager& waterManager = sceneRenderer.GetWaterManager(); + WaterRenderer& waterManager = sceneRenderer.GetWaterRenderer(); CShaderDefines defines = context; // If we're using fancy water, make sure its shader is loaded @@ -416,7 +418,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; @@ -431,7 +432,7 @@ WaterMgr->CreateSuperfancyInfo(); }*/ - const double time = waterManager.m_WaterTexTimer; + const double time = g_Renderer.GetTimeManager().GetGlobalTime(); const float repeatPeriod = waterManager.m_RepeatPeriod; deviceCommandContext->SetGraphicsPipelineState( @@ -441,10 +442,12 @@ const CCamera& camera = g_Renderer.GetSceneRenderer().GetViewCamera(); + const CWater& water = *g_Game->GetWorld()->GetWater(); + const double period = 8.0; // TODO: move uploading to a prepare function during loading. - const CTexturePtr& currentNormalTexture = waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)]; - const CTexturePtr& nextNormalTexture = waterManager.m_NormalMap[waterManager.GetNextTextureIndex(period)]; + const CTexturePtr& currentNormalTexture = waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(time, period)]; + const CTexturePtr& nextNormalTexture = waterManager.m_NormalMap[waterManager.GetNextTextureIndex(time, period)]; currentNormalTexture->UploadBackendTextureIfNeeded(deviceCommandContext); nextNormalTexture->UploadBackendTextureIfNeeded(deviceCommandContext); @@ -528,15 +531,15 @@ deviceCommandContext->SetUniform( fancyWaterShader->GetBindingSlot(str_sunColor), lightEnv.m_SunColor.AsFloatArray()); deviceCommandContext->SetUniform( - fancyWaterShader->GetBindingSlot(str_color), waterManager.m_WaterColor.AsFloatArray()); + fancyWaterShader->GetBindingSlot(str_color), water.m_WaterColor.AsFloatArray()); deviceCommandContext->SetUniform( - fancyWaterShader->GetBindingSlot(str_tint), waterManager.m_WaterTint.AsFloatArray()); + fancyWaterShader->GetBindingSlot(str_tint), water.m_WaterTint.AsFloatArray()); deviceCommandContext->SetUniform( - fancyWaterShader->GetBindingSlot(str_waviness), waterManager.m_Waviness); + fancyWaterShader->GetBindingSlot(str_waviness), water.m_Waviness); deviceCommandContext->SetUniform( - fancyWaterShader->GetBindingSlot(str_murkiness), waterManager.m_Murkiness); + fancyWaterShader->GetBindingSlot(str_murkiness), water.m_Murkiness); deviceCommandContext->SetUniform( - fancyWaterShader->GetBindingSlot(str_windAngle), waterManager.m_WindAngle); + fancyWaterShader->GetBindingSlot(str_windAngle), water.m_WindAngle); deviceCommandContext->SetUniform( fancyWaterShader->GetBindingSlot(str_repeatScale), 1.0f / repeatPeriod); @@ -560,7 +563,7 @@ static_cast(g_Renderer.GetWidth()), static_cast(g_Renderer.GetHeight())); - if (waterManager.m_WaterType == L"clap") + if (water.m_WaterType == L"clap") { deviceCommandContext->SetUniform( fancyWaterShader->GetBindingSlot(str_waveParams1), @@ -569,7 +572,7 @@ fancyWaterShader->GetBindingSlot(str_waveParams2), 0.5f, 0.0f, 0.0f, 0.0f); } - else if (waterManager.m_WaterType == L"lake") + else if (water.m_WaterType == L"lake") { deviceCommandContext->SetUniform( fancyWaterShader->GetBindingSlot(str_waveParams1), @@ -609,10 +612,11 @@ PROFILE3_GPU("simple water"); GPU_SCOPED_LABEL(deviceCommandContext, "Render Simple Water"); - const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager(); + const WaterRenderer& waterRenderer = g_Renderer.GetSceneRenderer().GetWaterRenderer(); + const CWater& water = *g_Game->GetWorld()->GetWater(); CLOSTexture& losTexture = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture(); - const double time = waterManager.m_WaterTexTimer; + const double time = g_Renderer.GetTimeManager().GetGlobalTime(); CShaderDefines context; if (g_Renderer.GetSceneRenderer().GetWaterRenderMode() == WIREFRAME) @@ -625,7 +629,7 @@ deviceCommandContext->BeginPass(); Renderer::Backend::IShaderProgram* waterSimpleShader = waterSimpleTech->GetShader(); - const CTexturePtr& waterTexture = waterManager.m_WaterTexture[waterManager.GetCurrentTextureIndex(1.6)]; + const CTexturePtr& waterTexture = waterRenderer.m_WaterTexture[waterRenderer.GetCurrentTextureIndex(time, 1.6)]; waterTexture->UploadBackendTextureIfNeeded(deviceCommandContext); deviceCommandContext->SetTexture( @@ -644,7 +648,7 @@ deviceCommandContext->SetUniform( waterSimpleShader->GetBindingSlot(str_time), static_cast(time)); deviceCommandContext->SetUniform( - waterSimpleShader->GetBindingSlot(str_color), waterManager.m_WaterColor.AsFloatArray()); + waterSimpleShader->GetBindingSlot(str_color), water.m_WaterColor.AsFloatArray()); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) @@ -662,7 +666,7 @@ Renderer::Backend::IDeviceCommandContext* deviceCommandContext, const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { - const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager(); + const WaterRenderer& waterManager = g_Renderer.GetSceneRenderer().GetWaterRenderer(); if (!waterManager.WillRenderFancyWater()) RenderSimpleWater(deviceCommandContext, cullGroup); @@ -675,7 +679,7 @@ int cullGroup) { CSceneRenderer& sceneRenderer = g_Renderer.GetSceneRenderer(); - const WaterManager& waterManager = sceneRenderer.GetWaterManager(); + const WaterRenderer& waterManager = sceneRenderer.GetWaterRenderer(); if (!waterManager.WillRenderFancyWater()) return; 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 @@ -17,29 +17,20 @@ #include "precompiled.h" -#include "graphics/Terrain.h" -#include "graphics/TextureManager.h" -#include "graphics/ShaderManager.h" -#include "graphics/ShaderProgram.h" -#include "lib/bits.h" -#include "lib/timer.h" +#include "renderer/WaterRData.h" + +#include "graphics/Water.h" #include "maths/MathUtil.h" -#include "maths/Vector2D.h" -#include "ps/CLogger.h" -#include "ps/CStrInternStatic.h" #include "ps/Game.h" -#include "ps/VideoMode.h" #include "ps/World.h" -#include "renderer/backend/IDevice.h" #include "renderer/Renderer.h" -#include "renderer/RenderingOptions.h" -#include "renderer/SceneRenderer.h" -#include "renderer/WaterManager.h" -#include "simulation2/Simulation2.h" -#include "simulation2/components/ICmpWaterManager.h" -#include "simulation2/components/ICmpRangeManager.h" #include +#include +#include +#include +#include +#include struct CoastalPoint { @@ -72,268 +63,27 @@ 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_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(); m_DistanceHeightmap.reset(); m_WindStrength.reset(); - - m_FancyEffectsFramebuffer.reset(); - m_RefractionFramebuffer.reset(); - m_ReflectionFramebuffer.reset(); - - m_FancyTexture.reset(); - m_FancyTextureDepth.reset(); - m_ReflFboDepthTexture.reset(); - m_RefrFboDepthTexture.reset(); -} - - -/////////////////////////////////////////////////////////////////// -// 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.SetAddressMode( - Renderer::Backend::Sampler::AddressMode::REPEAT); - - CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); - texture->Prefetch(); - m_WaterTexture[i] = texture; - } - - m_RenderWater = true; - - // Load normalmaps (for fancy water) - ReloadWaterNormalTextures(); - - // Load CoastalWaves - { - CTextureProperties textureProps(L"art/textures/terrain/types/water/coastalWave.png"); - textureProps.SetAddressMode( - Renderer::Backend::Sampler::AddressMode::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.SetAddressMode( - Renderer::Backend::Sampler::AddressMode::REPEAT); - CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); - texture->Prefetch(); - m_FoamTex = texture; - } - - RecreateOrLoadTexturesIfNeeded(); - - return 0; -} - -void WaterManager::RecreateOrLoadTexturesIfNeeded() -{ - Renderer::Backend::IDevice* backendDevice = g_VideoMode.GetBackendDevice(); - - // Use screen-sized textures for minimum artifacts. - const size_t newRefTextureSize = round_up_to_pow2(g_Renderer.GetHeight()); - - if (m_RefTextureSize != newRefTextureSize) - { - m_ReflectionFramebuffer.reset(); - m_ReflectionTexture.reset(); - m_ReflFboDepthTexture.reset(); - - m_RefractionFramebuffer.reset(); - m_RefractionTexture.reset(); - m_RefrFboDepthTexture.reset(); - - m_RefTextureSize = newRefTextureSize; - } - - // Create reflection textures. - const bool needsReflectionTextures = - g_RenderingOptions.GetWaterEffects() && - g_RenderingOptions.GetWaterReflection(); - if (needsReflectionTextures && !m_ReflectionTexture) - { - m_ReflectionTexture = backendDevice->CreateTexture2D("WaterReflectionTexture", - Renderer::Backend::ITexture::Usage::SAMPLED | - Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT, - Renderer::Backend::Format::R8G8B8A8_UNORM, m_RefTextureSize, m_RefTextureSize, - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::LINEAR, - Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT)); - - m_ReflFboDepthTexture = backendDevice->CreateTexture2D("WaterReflectionDepthTexture", - Renderer::Backend::ITexture::Usage::SAMPLED | - Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT, - Renderer::Backend::Format::D32, m_RefTextureSize, m_RefTextureSize, - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::NEAREST, - Renderer::Backend::Sampler::AddressMode::REPEAT)); - - m_ReflectionFramebuffer = backendDevice->CreateFramebuffer("ReflectionFramebuffer", - m_ReflectionTexture.get(), m_ReflFboDepthTexture.get(), CColor(0.5f, 0.5f, 1.0f, 0.0f)); - if (!m_ReflectionFramebuffer) - { - g_RenderingOptions.SetWaterReflection(false); - UpdateQuality(); - } - } - - // Create refraction textures. - const bool needsRefractionTextures = - g_RenderingOptions.GetWaterEffects() && - g_RenderingOptions.GetWaterRefraction(); - if (needsRefractionTextures && !m_RefractionTexture) - { - m_RefractionTexture = backendDevice->CreateTexture2D("WaterRefractionTexture", - Renderer::Backend::ITexture::Usage::SAMPLED | - Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT, - Renderer::Backend::Format::R8G8B8A8_UNORM, m_RefTextureSize, m_RefTextureSize, - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::LINEAR, - Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT)); - - m_RefrFboDepthTexture = backendDevice->CreateTexture2D("WaterRefractionDepthTexture", - Renderer::Backend::ITexture::Usage::SAMPLED | - Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT, - Renderer::Backend::Format::D32, m_RefTextureSize, m_RefTextureSize, - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::NEAREST, - Renderer::Backend::Sampler::AddressMode::REPEAT)); - - m_RefractionFramebuffer = backendDevice->CreateFramebuffer("RefractionFramebuffer", - m_RefractionTexture.get(), m_RefrFboDepthTexture.get(), CColor(1.0f, 0.0f, 0.0f, 0.0f)); - if (!m_RefractionFramebuffer) - { - g_RenderingOptions.SetWaterRefraction(false); - UpdateQuality(); - } - } - - const uint32_t newWidth = static_cast(g_Renderer.GetWidth()); - const uint32_t newHeight = static_cast(g_Renderer.GetHeight()); - if (m_FancyTexture && (m_FancyTexture->GetWidth() != newWidth || m_FancyTexture->GetHeight() != newHeight)) - { - m_FancyEffectsFramebuffer.reset(); - m_FancyTexture.reset(); - m_FancyTextureDepth.reset(); - } - - // Create the Fancy Effects textures. - const bool needsFancyTextures = - g_RenderingOptions.GetWaterEffects() && - g_RenderingOptions.GetWaterFancyEffects(); - if (needsFancyTextures && !m_FancyTexture) - { - m_FancyTexture = backendDevice->CreateTexture2D("WaterFancyTexture", - Renderer::Backend::ITexture::Usage::SAMPLED | - Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT, - Renderer::Backend::Format::R8G8B8A8_UNORM, g_Renderer.GetWidth(), g_Renderer.GetHeight(), - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::LINEAR, - Renderer::Backend::Sampler::AddressMode::REPEAT)); - - m_FancyTextureDepth = backendDevice->CreateTexture2D("WaterFancyDepthTexture", - Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT, - Renderer::Backend::Format::D32, g_Renderer.GetWidth(), g_Renderer.GetHeight(), - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::LINEAR, - Renderer::Backend::Sampler::AddressMode::REPEAT)); - - m_FancyEffectsFramebuffer = backendDevice->CreateFramebuffer("FancyEffectsFramebuffer", - m_FancyTexture.get(), m_FancyTextureDepth.get()); - if (!m_FancyEffectsFramebuffer) - { - g_RenderingOptions.SetWaterRefraction(false); - UpdateQuality(); - } - } } -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.SetAddressMode( - Renderer::Backend::Sampler::AddressMode::REPEAT); - textureProps.SetAnisotropicFilter(true); - - 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(); - - m_RefractionFramebuffer.reset(); - m_ReflectionFramebuffer.reset(); - m_ReflectionTexture.reset(); - m_RefractionTexture.reset(); + m_NeedInfoUpdate = true; + m_DistanceHeightmap.reset(); + m_WindStrength.reset(); } template @@ -383,7 +133,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()) @@ -405,12 +155,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() { if (m_MapSize == 0) return; @@ -422,7 +172,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; @@ -563,6 +313,7 @@ } } // Generic indexes, max-length + // Generic indexes, max-length m_ShoreWavesVBIndices = g_VBMan.AllocateChunk( sizeof(u16), water_indices.size(), Renderer::Backend::IBuffer::Type::INDEX, false, @@ -571,6 +322,8 @@ float diff = (rand() % 50) / 5.0f; + const float waterHeight = m_Water.GetWaterHeight(); + std::vector vertices, reversed; for (size_t i = 0; i < CoastalPointsChains.size(); ++i) { @@ -618,15 +371,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 +401,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 +482,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,105 +548,7 @@ } } -void WaterManager::RenderWaves( - Renderer::Backend::IDeviceCommandContext* deviceCommandContext, - const CFrustum& frustrum) -{ - if (!m_WaterFancyEffects) - return; - - GPU_SCOPED_LABEL(deviceCommandContext, "Render Waves"); - - deviceCommandContext->SetGraphicsPipelineState( - Renderer::Backend::MakeDefaultGraphicsPipelineStateDesc()); - deviceCommandContext->BeginFramebufferPass(m_FancyEffectsFramebuffer.get()); - deviceCommandContext->ClearFramebuffer(); - - CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_water_waves); - deviceCommandContext->SetGraphicsPipelineState( - tech->GetGraphicsPipelineStateDesc()); - deviceCommandContext->BeginPass(); - Renderer::Backend::IShaderProgram* shader = tech->GetShader(); - - m_WaveTex->UploadBackendTextureIfNeeded(deviceCommandContext); - m_FoamTex->UploadBackendTextureIfNeeded(deviceCommandContext); - - deviceCommandContext->SetTexture( - shader->GetBindingSlot(str_waveTex), m_WaveTex->GetBackendTexture()); - deviceCommandContext->SetTexture( - shader->GetBindingSlot(str_foamTex), m_FoamTex->GetBackendTexture()); - - deviceCommandContext->SetUniform( - shader->GetBindingSlot(str_time), static_cast(m_WaterTexTimer)); - const CMatrix3D transform = - g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection(); - deviceCommandContext->SetUniform( - shader->GetBindingSlot(str_transform), transform.AsFloatArray()); - - 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(); - VBchunk->m_Owner->UploadIfNeeded(deviceCommandContext); - m_ShoreWavesVBIndices->m_Owner->UploadIfNeeded(deviceCommandContext); - - const uint32_t stride = sizeof(SWavesVertex); - const uint32_t firstVertexOffset = VBchunk->m_Index * stride; - - deviceCommandContext->SetVertexAttributeFormat( - Renderer::Backend::VertexAttributeStream::POSITION, - Renderer::Backend::Format::R32G32B32_SFLOAT, - offsetof(SWavesVertex, m_BasePosition), stride, - Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); - deviceCommandContext->SetVertexAttributeFormat( - Renderer::Backend::VertexAttributeStream::NORMAL, - Renderer::Backend::Format::R32G32_SFLOAT, - offsetof(SWavesVertex, m_PerpVect), stride, - Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); - deviceCommandContext->SetVertexAttributeFormat( - Renderer::Backend::VertexAttributeStream::UV0, - Renderer::Backend::Format::R8G8_UINT, - offsetof(SWavesVertex, m_UV), stride, - Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); - - deviceCommandContext->SetVertexAttributeFormat( - Renderer::Backend::VertexAttributeStream::UV1, - Renderer::Backend::Format::R32G32B32_SFLOAT, - offsetof(SWavesVertex, m_ApexPosition), stride, - Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); - deviceCommandContext->SetVertexAttributeFormat( - Renderer::Backend::VertexAttributeStream::UV2, - Renderer::Backend::Format::R32G32B32_SFLOAT, - offsetof(SWavesVertex, m_SplashPosition), stride, - Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); - deviceCommandContext->SetVertexAttributeFormat( - Renderer::Backend::VertexAttributeStream::UV3, - Renderer::Backend::Format::R32G32B32_SFLOAT, - offsetof(SWavesVertex, m_RetreatPosition), stride, - Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); - - deviceCommandContext->SetUniform( - shader->GetBindingSlot(str_translation), m_ShoreWaves[a]->m_TimeDiff); - deviceCommandContext->SetUniform( - shader->GetBindingSlot(str_width), static_cast(m_ShoreWaves[a]->m_Width)); - - deviceCommandContext->SetVertexBuffer( - 0, VBchunk->m_Owner->GetBuffer(), firstVertexOffset); - deviceCommandContext->SetIndexBuffer(m_ShoreWavesVBIndices->m_Owner->GetBuffer()); - - const uint32_t indexCount = (m_ShoreWaves[a]->m_Width - 1) * (7 * 6); - deviceCommandContext->DrawIndexed(m_ShoreWavesVBIndices->m_Index, indexCount, 0); - - g_Renderer.GetStats().m_DrawCalls++; - g_Renderer.GetStats().m_WaterTris += indexCount / 3; - } - deviceCommandContext->EndPass(); - deviceCommandContext->EndFramebufferPass(); -} - -void WaterManager::RecomputeWaterData() +void WaterRData::RecomputeWaterData() { if (!m_MapSize) return; @@ -905,7 +560,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; @@ -917,7 +572,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); @@ -980,12 +635,14 @@ } } + const 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]; @@ -999,8 +656,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 @@ -1022,66 +679,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(); -} - -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/renderer/WaterRenderer.h =================================================================== --- source/renderer/WaterRenderer.h +++ source/renderer/WaterRenderer.h @@ -22,8 +22,7 @@ #ifndef INCLUDED_WATERMANAGER #define INCLUDED_WATERMANAGER -#include "graphics/Color.h" -#include "graphics/Texture.h" +#include "graphics/TextureManager.h" #include "maths/Matrix3D.h" #include "maths/Vector2D.h" #include "renderer/backend/IDeviceCommandContext.h" @@ -32,52 +31,85 @@ #include "renderer/VertexBufferManager.h" #include -#include class CFrustum; - -struct WaveObject; +class CSceneRenderer; +class ShaderModelRenderer; +class TerrainRenderer; +class WaterRData; /** - * Class WaterManager: Maintain rendering-related water settings and textures + * Class WaterRenderer: Maintain rendering-related water settings and textures * Anything that affects gameplay should go in CcmpWaterManager.cpp and passed to this (possibly as copy). */ -class WaterManager +class WaterRenderer { + friend class ShaderModelRenderer; + friend class CSceneRenderer; + friend class TerrainRenderer; public: - CTexturePtr m_WaterTexture[60]; - CTexturePtr m_NormalMap[60]; + WaterRenderer(); + ~WaterRenderer(); - // 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(); - // 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; + /** + * Recreates/loads needed water textures. + */ + void RecreateOrLoadTexturesIfNeeded(); - size_t m_MapSize; - ssize_t m_TexSize; + /** + * ReloadWaterNormalTextures: Reload the normal textures so that changing + * water type in Atlas will actually do the right thing. + */ + void ReloadWaterNormalTexturesIfNeeded(); + + /** + * UnloadWaterTextures: Free any loaded water textures and reset the internal state + * so that another call to LoadWaterTextures will begin progressive loading. + */ + void UnloadWaterTextures(); + + /** + * 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::IDeviceCommandContext* 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; @@ -91,13 +123,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 @@ -116,91 +147,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(); - - /** - * Recreates/loads needed water textures. - */ - void RecreateOrLoadTexturesIfNeeded(); - - /** - * 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::IDeviceCommandContext* 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/WaterRenderer.cpp =================================================================== --- /dev/null +++ source/renderer/WaterRenderer.cpp @@ -0,0 +1,471 @@ +/* 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 . + */ + +#include "precompiled.h" + +#include "renderer/WaterRenderer.h" + +#include "graphics/ShaderManager.h" +#include "graphics/ShaderTechniquePtr.h" +#include "graphics/Water.h" +#include "lib/bits.h" +/* "lib/timer.h"*/ +/* "maths/MathUtil.h"*/ +#include "maths/BoundingBoxAligned.h" +#include "maths/Vector2D.h" +#include "ps/CStrInternStatic.h" +#include "ps/Game.h" +#include "ps/VideoMode.h" +#include "ps/World.h" +#include "renderer/backend/IDevice.h" +#include "renderer/Renderer.h" +#include "renderer/RenderingOptions.h" +#include "renderer/SceneRenderer.h" +#include "renderer/Renderer.h" +#include "renderer/SceneRenderer.h" +#include "renderer/TimeManager.h" +#include "renderer/WaterRData.h" + +#include + +struct CoastalPoint +{ + CoastalPoint(int idx, CVector2D pos) : index(idx), position(pos) {}; + int index; + CVector2D position; +}; + +struct SWavesVertex +{ + // vertex position + CVector3D m_BasePosition; + CVector3D m_ApexPosition; + CVector3D m_SplashPosition; + CVector3D m_RetreatPosition; + + CVector2D m_PerpVect; + u8 m_UV[3]; + + // pad to a power of two + u8 m_Padding[5]; +}; +cassert(sizeof(SWavesVertex) == 64); + +struct WaveObject +{ + CVertexBufferManager::Handle m_VBVertices; + CBoundingBoxAligned m_AABB; + size_t m_Width; + float m_TimeDiff; +}; + +WaterRenderer::WaterRenderer() +{ + m_RefTextureSize = 0; + + + m_RepeatPeriod = 16.0f; + + m_WaterEffects = true; + m_WaterFancyEffects = false; + m_WaterRealDepth = false; + m_WaterRefraction = false; + m_WaterReflection = false; +} + +WaterRenderer::~WaterRenderer() +{ + // Cleanup if the caller messed up + UnloadWaterTextures(); + + + m_FancyEffectsFramebuffer.reset(); + m_RefractionFramebuffer.reset(); + m_ReflectionFramebuffer.reset(); + + m_FancyTexture.reset(); + m_FancyTextureDepth.reset(); + m_ReflFboDepthTexture.reset(); + m_RefrFboDepthTexture.reset(); +} + + +/////////////////////////////////////////////////////////////////// +// Progressive load of water textures +void WaterRenderer::LoadWaterTextures() +{ + 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.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::REPEAT); + + CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); + texture->Prefetch(); + m_WaterTexture[i] = texture; + } + + // Load normalmaps (for fancy water) + ReloadWaterNormalTexturesIfNeeded(); + + // Load CoastalWaves + { + CTextureProperties textureProps(L"art/textures/terrain/types/water/coastalWave.png"); + textureProps.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::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.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::REPEAT); + CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); + texture->Prefetch(); + m_FoamTex = texture; + } + + RecreateOrLoadTexturesIfNeeded(); +} + +void WaterRenderer::RecreateOrLoadTexturesIfNeeded() +{ + Renderer::Backend::IDevice* backendDevice = g_VideoMode.GetBackendDevice(); + + // Use screen-sized textures for minimum artifacts. + const size_t newRefTextureSize = round_up_to_pow2(g_Renderer.GetHeight()); + + if (m_RefTextureSize != newRefTextureSize) + { + m_ReflectionFramebuffer.reset(); + m_ReflectionTexture.reset(); + m_ReflFboDepthTexture.reset(); + + m_RefractionFramebuffer.reset(); + m_RefractionTexture.reset(); + m_RefrFboDepthTexture.reset(); + + m_RefTextureSize = newRefTextureSize; + } + + // Create reflection textures. + const bool needsReflectionTextures = + g_RenderingOptions.GetWaterEffects() && + g_RenderingOptions.GetWaterReflection(); + if (needsReflectionTextures && !m_ReflectionTexture) + { + m_ReflectionTexture = backendDevice->CreateTexture2D("WaterReflectionTexture", + Renderer::Backend::ITexture::Usage::SAMPLED | + Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT, + Renderer::Backend::Format::R8G8B8A8_UNORM, m_RefTextureSize, m_RefTextureSize, + Renderer::Backend::Sampler::MakeDefaultSampler( + Renderer::Backend::Sampler::Filter::LINEAR, + Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT)); + + m_ReflFboDepthTexture = backendDevice->CreateTexture2D("WaterReflectionDepthTexture", + Renderer::Backend::ITexture::Usage::SAMPLED | + Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT, + Renderer::Backend::Format::D32, m_RefTextureSize, m_RefTextureSize, + Renderer::Backend::Sampler::MakeDefaultSampler( + Renderer::Backend::Sampler::Filter::NEAREST, + Renderer::Backend::Sampler::AddressMode::REPEAT)); + + m_ReflectionFramebuffer = backendDevice->CreateFramebuffer("ReflectionFramebuffer", + m_ReflectionTexture.get(), m_ReflFboDepthTexture.get(), CColor(0.5f, 0.5f, 1.0f, 0.0f)); + if (!m_ReflectionFramebuffer) + { + g_RenderingOptions.SetWaterReflection(false); + UpdateQuality(); + } + } + + // Create refraction textures. + const bool needsRefractionTextures = + g_RenderingOptions.GetWaterEffects() && + g_RenderingOptions.GetWaterRefraction(); + if (needsRefractionTextures && !m_RefractionTexture) + { + m_RefractionTexture = backendDevice->CreateTexture2D("WaterRefractionTexture", + Renderer::Backend::ITexture::Usage::SAMPLED | + Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT, + Renderer::Backend::Format::R8G8B8A8_UNORM, m_RefTextureSize, m_RefTextureSize, + Renderer::Backend::Sampler::MakeDefaultSampler( + Renderer::Backend::Sampler::Filter::LINEAR, + Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT)); + + m_RefrFboDepthTexture = backendDevice->CreateTexture2D("WaterRefractionDepthTexture", + Renderer::Backend::ITexture::Usage::SAMPLED | + Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT, + Renderer::Backend::Format::D32, m_RefTextureSize, m_RefTextureSize, + Renderer::Backend::Sampler::MakeDefaultSampler( + Renderer::Backend::Sampler::Filter::NEAREST, + Renderer::Backend::Sampler::AddressMode::REPEAT)); + + m_RefractionFramebuffer = backendDevice->CreateFramebuffer("RefractionFramebuffer", + m_RefractionTexture.get(), m_RefrFboDepthTexture.get(), CColor(1.0f, 0.0f, 0.0f, 0.0f)); + if (!m_RefractionFramebuffer) + { + g_RenderingOptions.SetWaterRefraction(false); + UpdateQuality(); + } + } + + const uint32_t newWidth = static_cast(g_Renderer.GetWidth()); + const uint32_t newHeight = static_cast(g_Renderer.GetHeight()); + if (m_FancyTexture && (m_FancyTexture->GetWidth() != newWidth || m_FancyTexture->GetHeight() != newHeight)) + { + m_FancyEffectsFramebuffer.reset(); + m_FancyTexture.reset(); + m_FancyTextureDepth.reset(); + } + + // Create the Fancy Effects textures. + const bool needsFancyTextures = + g_RenderingOptions.GetWaterEffects() && + g_RenderingOptions.GetWaterFancyEffects(); + if (needsFancyTextures && !m_FancyTexture) + { + m_FancyTexture = backendDevice->CreateTexture2D("WaterFancyTexture", + Renderer::Backend::ITexture::Usage::SAMPLED | + Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT, + Renderer::Backend::Format::R8G8B8A8_UNORM, g_Renderer.GetWidth(), g_Renderer.GetHeight(), + Renderer::Backend::Sampler::MakeDefaultSampler( + Renderer::Backend::Sampler::Filter::LINEAR, + Renderer::Backend::Sampler::AddressMode::REPEAT)); + + m_FancyTextureDepth = backendDevice->CreateTexture2D("WaterFancyDepthTexture", + Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT, + Renderer::Backend::Format::D32, g_Renderer.GetWidth(), g_Renderer.GetHeight(), + Renderer::Backend::Sampler::MakeDefaultSampler( + Renderer::Backend::Sampler::Filter::LINEAR, + Renderer::Backend::Sampler::AddressMode::REPEAT)); + + m_FancyEffectsFramebuffer = backendDevice->CreateFramebuffer("FancyEffectsFramebuffer", + m_FancyTexture.get(), m_FancyTextureDepth.get()); + if (!m_FancyEffectsFramebuffer) + { + g_RenderingOptions.SetWaterRefraction(false); + UpdateQuality(); + } + } +} + +void WaterRenderer::ReloadWaterNormalTexturesIfNeeded() +{ + CWater* water = g_Game->GetWorld()->GetWater(); + const CStrW& waterType = water ? water->m_WaterType : L"ocean"; + + 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.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::REPEAT); + textureProps.SetAnisotropicFilter(true); + + CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); + texture->Prefetch(); + m_NormalMap[i] = texture; + } +} + +/////////////////////////////////////////////////////////////////// +// Unload water textures +void WaterRenderer::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(); + + m_RefractionFramebuffer.reset(); + m_ReflectionFramebuffer.reset(); + + m_ReflectionTexture.reset(); + m_RefractionTexture.reset(); +} + +void WaterRenderer::RenderWaves( + Renderer::Backend::IDeviceCommandContext* deviceCommandContext, + WaterRData& renderData, + const CFrustum& frustrum) +{ + if (!m_WaterFancyEffects) + return; + + GPU_SCOPED_LABEL(deviceCommandContext, "Render Waves"); + + deviceCommandContext->SetGraphicsPipelineState( + Renderer::Backend::MakeDefaultGraphicsPipelineStateDesc()); + deviceCommandContext->BeginFramebufferPass(m_FancyEffectsFramebuffer.get()); + deviceCommandContext->ClearFramebuffer(); + + CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_water_waves); + deviceCommandContext->SetGraphicsPipelineState( + tech->GetGraphicsPipelineStateDesc()); + deviceCommandContext->BeginPass(); + Renderer::Backend::IShaderProgram* shader = tech->GetShader(); + + m_WaveTex->UploadBackendTextureIfNeeded(deviceCommandContext); + m_FoamTex->UploadBackendTextureIfNeeded(deviceCommandContext); + + deviceCommandContext->SetTexture( + shader->GetBindingSlot(str_waveTex), m_WaveTex->GetBackendTexture()); + deviceCommandContext->SetTexture( + shader->GetBindingSlot(str_foamTex), m_FoamTex->GetBackendTexture()); + + const double time = g_Renderer.GetTimeManager().GetGlobalTime(); + + deviceCommandContext->SetUniform( + shader->GetBindingSlot(str_time), static_cast(time)); + const CMatrix3D transform = + g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection(); + deviceCommandContext->SetUniform( + shader->GetBindingSlot(str_transform), transform.AsFloatArray()); + + const std::vector>& shoreWaves = renderData.GetShoreWavesData(); + const CVertexBufferManager::Handle& indices = renderData.GetShoreWavesIndices(); + + for (size_t i = 0; i < shoreWaves.size(); ++i) + { + if (!frustrum.IsBoxVisible(shoreWaves[i]->m_AABB)) + continue; + + CVertexBuffer::VBChunk* VBchunk = shoreWaves[i]->m_VBVertices.Get(); + VBchunk->m_Owner->UploadIfNeeded(deviceCommandContext); + indices->m_Owner->UploadIfNeeded(deviceCommandContext); + + const uint32_t stride = sizeof(SWavesVertex); + const uint32_t firstVertexOffset = VBchunk->m_Index * stride; + + deviceCommandContext->SetVertexAttributeFormat( + Renderer::Backend::VertexAttributeStream::POSITION, + Renderer::Backend::Format::R32G32B32_SFLOAT, + offsetof(SWavesVertex, m_BasePosition), stride, + Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); + deviceCommandContext->SetVertexAttributeFormat( + Renderer::Backend::VertexAttributeStream::NORMAL, + Renderer::Backend::Format::R32G32_SFLOAT, + offsetof(SWavesVertex, m_PerpVect), stride, + Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); + deviceCommandContext->SetVertexAttributeFormat( + Renderer::Backend::VertexAttributeStream::UV0, + Renderer::Backend::Format::R8G8_UINT, + offsetof(SWavesVertex, m_UV), stride, + Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); + + deviceCommandContext->SetVertexAttributeFormat( + Renderer::Backend::VertexAttributeStream::UV1, + Renderer::Backend::Format::R32G32B32_SFLOAT, + offsetof(SWavesVertex, m_ApexPosition), stride, + Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); + deviceCommandContext->SetVertexAttributeFormat( + Renderer::Backend::VertexAttributeStream::UV2, + Renderer::Backend::Format::R32G32B32_SFLOAT, + offsetof(SWavesVertex, m_SplashPosition), stride, + Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); + deviceCommandContext->SetVertexAttributeFormat( + Renderer::Backend::VertexAttributeStream::UV3, + Renderer::Backend::Format::R32G32B32_SFLOAT, + offsetof(SWavesVertex, m_RetreatPosition), stride, + Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0); + + deviceCommandContext->SetUniform( + shader->GetBindingSlot(str_translation), shoreWaves[i]->m_TimeDiff); + deviceCommandContext->SetUniform( + shader->GetBindingSlot(str_width), static_cast(shoreWaves[i]->m_Width)); + + deviceCommandContext->SetVertexBuffer( + 0, VBchunk->m_Owner->GetBuffer(), firstVertexOffset); + deviceCommandContext->SetIndexBuffer(indices->m_Owner->GetBuffer()); + + const uint32_t indexCount = (shoreWaves[i]->m_Width - 1) * (7 * 6); + deviceCommandContext->DrawIndexed(indices->m_Index, indexCount, 0); + + g_Renderer.GetStats().m_DrawCalls++; + g_Renderer.GetStats().m_WaterTris += indexCount / 3; + } + deviceCommandContext->EndPass(); + deviceCommandContext->EndFramebufferPass(); +} + +//////////////////////////////////////////////////////////////////////// +// This will set the bools properly +void WaterRenderer::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 WaterRenderer::WillRenderFancyWater() const +{ + return g_VideoMode.GetBackend() != CVideoMode::Backend::GL_ARB && + g_RenderingOptions.GetWaterEffects(); +} + +size_t WaterRenderer::GetCurrentTextureIndex(const double time, const double period) const +{ + ENSURE(period > 0.0); + return static_cast(time * ARRAY_SIZE(m_WaterTexture) / period) % ARRAY_SIZE(m_WaterTexture); +} + +size_t WaterRenderer::GetNextTextureIndex(const double time, const double period) const +{ + ENSURE(period > 0.0); + return (GetCurrentTextureIndex(time, period) + 1) % ARRAY_SIZE(m_WaterTexture); +} + +float WaterRenderer::GetWaterPlaneHeight() const +{ + return g_Game->GetWorld()->GetWater()->GetWaterHeight(); +} Index: source/simulation2/Simulation2.h =================================================================== --- source/simulation2/Simulation2.h +++ source/simulation2/Simulation2.h @@ -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 @@ -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); @@ -635,8 +637,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 @@ -23,7 +23,7 @@ #include "graphics/Terrain.h" #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "maths/Vector3D.h" #include "simulation2/components/ICmpObstructionManager.h" #include "simulation2/components/ICmpRangeManager.h" @@ -119,12 +119,11 @@ return m_Terrain; } - void ReloadTerrain(bool ReloadWater) override + 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 @@ void Init(const CParamNode& UNUSED(paramNode)) override { + CWorld* world = GetSimContext().GetWorld(); + if (world) + m_Water = world->GetWater(); } void Deinit() override { - // Clear the map size & data. - if (CRenderer::IsInitialised()) - g_Renderer.GetSceneRenderer().GetWaterManager().SetMapSize(0); } void Serialize(ISerializer& serialize) override @@ -70,24 +73,12 @@ Init(paramNode); deserialize.NumberFixed_Unbounded("height", m_WaterHeight); - - if (CRenderer::IsInitialised()) - g_Renderer.GetSceneRenderer().GetWaterManager().SetMapSize(GetSimContext().GetTerrain().GetVerticesPerSide()); - - RecomputeWaterData(); } void HandleMessage(const CMessage& msg, bool UNUSED(global)) override { 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,26 +92,15 @@ } } - void RecomputeWaterData() override - { - 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); - } - - void SetWaterLevel(entity_pos_t h) override + virtual void SetWaterLevel(entity_pos_t h) { if (m_WaterHeight == h) return; 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 @@ -36,11 +36,7 @@ return 100.f; } - void RecomputeWaterData() override - { - } - - void SetWaterLevel(entity_pos_t UNUSED(h)) override + virtual void SetWaterLevel(entity_pos_t UNUSED(h)) { } }; @@ -143,7 +139,7 @@ test.AddMock(SYSTEM_ENTITY, IID_Terrain, terrain); MockWater water; - test.AddMock(SYSTEM_ENTITY, IID_WaterManager, water); + test.AddMock(SYSTEM_ENTITY, IID_WaterRenderer, water); ICmpPosition* cmp = test.Add(CID_Position, "upright23true1"); Index: source/simulation2/system/ComponentTest.h =================================================================== --- source/simulation2/system/ComponentTest.h +++ source/simulation2/system/ComponentTest.h @@ -242,7 +242,7 @@ { } - void ReloadTerrain(bool UNUSED(ReloadWater)) override + 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 @@ -37,6 +37,7 @@ #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" @@ -44,12 +45,13 @@ #include "ps/GameSetup/Config.h" #include "ps/ProfileViewer.h" #include "ps/VideoMode.h" +#include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/RenderingOptions.h" #include "renderer/Scene.h" #include "renderer/SceneRenderer.h" #include "renderer/SkyManager.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "scriptinterface/ScriptContext.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpAttack.h" @@ -70,18 +72,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; @@ -102,12 +103,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; @@ -122,6 +122,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) @@ -271,17 +272,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) @@ -310,7 +313,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); @@ -371,7 +374,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()); } @@ -453,15 +456,15 @@ m.OldSky = g_Renderer.GetSceneRenderer().GetSkyManager().IsSkyVisible(); g_Renderer.GetSceneRenderer().GetSkyManager().SetSkyVisible(false); - m.OldWater = g_Renderer.GetSceneRenderer().GetWaterManager().m_RenderWater; - g_Renderer.GetSceneRenderer().GetWaterManager().m_RenderWater = m.WaterEnabled; + //m.OldWater = g_Renderer.GetSceneRenderer().GetWaterRenderer().m_RenderWater; + //g_Renderer.GetSceneRenderer().GetWaterRenderer().m_RenderWater = m.WaterEnabled; } else { // Restore the old renderer state SetShadowsEnabled(m.OldShadows); g_Renderer.GetSceneRenderer().GetSkyManager().SetSkyVisible(m.OldSky); - g_Renderer.GetSceneRenderer().GetWaterManager().m_RenderWater = m.OldWater; + // g_Renderer.GetSceneRenderer().GetWaterManager().m_RenderWater = m.OldWater; } } @@ -472,6 +475,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); @@ -520,7 +524,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); @@ -555,7 +559,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" @@ -31,7 +32,7 @@ #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" #include "renderer/SkyManager.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpWaterManager.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" @@ -43,7 +44,7 @@ #include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "scriptinterface/Object.h" #include "scriptinterface/JSON.h" #include "simulation2/Simulation2.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) Index: source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp =================================================================== --- source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ source/tools/atlas/GameInterface/Handlers/ObjectHandlers.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 @@ -39,7 +39,7 @@ #include "ps/Game.h" #include "ps/World.h" #include "renderer/Renderer.h" -#include "renderer/WaterManager.h" +#include "renderer/WaterRenderer.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpObstruction.h" #include "simulation2/components/ICmpOwnership.h" Index: source/tools/atlas/GameInterface/View.cpp =================================================================== --- source/tools/atlas/GameInterface/View.cpp +++ source/tools/atlas/GameInterface/View.cpp @@ -143,7 +143,7 @@ m_ActorViewer->SetWalkEnabled(value); else if (name == L"ground") m_ActorViewer->SetGroundEnabled(value); - // TODO: this causes corruption of WaterManager's global state + // TODO: this causes corruption of WaterRenderer's global state // which should be asociated with terrain or simulation instead // see http://trac.wildfiregames.com/ticket/2692 //else if (name == L"water")