Merge branch '138-toggle-free-look-with-hotkey' into main/gingo-test
[ryzomcore.git] / ryzom / client / src / water_map.cpp
blob727b483118358e0a2c7e12c1446a611957cd16d1
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdpch.h"
18 #include "water_map.h"
19 #include "continent_manager.h"
20 #include "user_entity.h"
21 #include "misc.h"
22 #include "global.h"
23 #include "client_cfg.h"
25 #include "r2/editor.h"
26 #include "r2/island_collision.h"
28 #include "nel/3d/u_scene.h"
29 #include "nel/3d/u_driver.h"
31 #include "nel/misc/path.h"
32 #include "nel/misc/file.h"
34 #ifdef DEBUG_NEW
35 #define new DEBUG_NEW
36 #endif
38 extern NL3D::UScene *Scene;
39 extern NL3D::UDriver *Driver;
40 extern CContinentManager ContinentMngr;
42 bool DisplayWaterMap = false;
45 using namespace NLMISC;
47 H_AUTO_DECL(RZ_WaterMap)
49 // *********************************************************************************************************
50 CWaterMap::CWaterMap()
52 H_AUTO_USE(RZ_WaterMap)
53 // Construct
54 _CellSize = 0.f;
55 _Width = 0;
56 _Height = 0;
60 // *********************************************************************************************************
61 void CWaterMap::init(const NLMISC::CVector2f &minCorner, const NLMISC::CVector2f &maxCorner, float cellSize /*=20.f*/)
63 H_AUTO_USE(RZ_WaterMap)
64 release();
65 if (cellSize <= 0.f)
67 nlwarning("Invalid cell size");
68 return;
70 _MinCorner = minCorner;
71 _MaxCorner = maxCorner;
72 if (_MinCorner.y > _MaxCorner.y) std::swap(_MinCorner.y, _MaxCorner.y);
73 if (_MinCorner.y > _MaxCorner.y) std::swap(_MinCorner.y, _MaxCorner.y);
74 _CellSize = cellSize;
75 _Width = (uint) ceilf((_MaxCorner.x - _MinCorner.x) / _CellSize);
76 _Height = (uint) ceilf((_MaxCorner.y - _MinCorner.y) / _CellSize);
77 _Grid.resize(_Width * _Height, 0);
78 // init index 0 as an empty list of water surface
79 _WaterInfoVect.push_back(CWaterInfo());
80 _WaterInfoVect.back().Height = 0.f;
81 _WaterInfoVect.back().SplashEnabled = false;
82 _WaterInfoIndexVectVect.push_back(TWaterInfoIndexVect(1, 0));
83 Scene->setWaterCallback(this);
86 // *********************************************************************************************************
87 void CWaterMap::release()
89 H_AUTO_USE(RZ_WaterMap)
90 NLMISC::contReset(_Grid);
91 NLMISC::contReset(_WaterInfoVect);
92 NLMISC::contReset(_WaterInfoIndexVectVect);
93 _CellSize = 0.f;
94 _Width = 0;
95 _Height = 0;
96 Scene->setWaterCallback(NULL);
99 // *********************************************************************************************************
100 bool CWaterMap::getWaterHeight(const NLMISC::CVector2f &pos, float &height, bool &splashEnabled)
102 H_AUTO_USE(RZ_WaterMap)
103 if (_Grid.empty()) return false;
104 float x = (pos.x - _MinCorner.x) / _CellSize;
105 float y = (pos.y - _MinCorner.y) / _CellSize;
106 sint ix = (sint) x;
107 sint iy = (sint) y;
108 if (ix < 0 || ix >= _Width) return false;
109 if (iy < 0 || iy >= _Height) return false;
110 const TWaterInfoIndexVect &wiiv = _WaterInfoIndexVectVect[_Grid[ix + iy * _Width]];
111 if (wiiv.empty()) return false;
112 // search if there's an intersection with one of the water surfaces
113 for(uint k = 0; k < wiiv.size(); ++k)
115 const CWaterInfo &wi = _WaterInfoVect[wiiv[k]];
116 if (wi.Shape.Vertices.empty()) continue;
117 // test intersection with each surface, and pick the first match
118 if (wi.Shape.contains(NLMISC::CVector2f(x, y)))
120 height = wi.Height;
121 splashEnabled = wi.SplashEnabled;
122 return true;
125 return false;
128 // *********************************************************************************************************
129 void CWaterMap::waterSurfaceAdded(const NLMISC::CPolygon2D &shape, const NLMISC::CMatrix &worldMatrix, bool splashEnabled, bool usesSceneWaterEnvMap)
131 if (ClientCfg.R2EDEnabled)
133 R2::getEditor().getIslandCollision().waterSurfaceAdded(shape, worldMatrix);
135 H_AUTO_USE(RZ_WaterMap)
136 if (usesSceneWaterEnvMap) ++WaterEnvMapRefCount;
137 if (_Grid.empty()) return;
138 if (_CellSize == 0.f) return;
139 float height = worldMatrix.getPos().z;
140 // transform the water shape in grid coordinates
141 CWaterInfo wi;
142 uint numVerts = (uint)shape.Vertices.size();
143 wi.Shape.Vertices.resize(numVerts);
144 wi.SplashEnabled = splashEnabled;
145 NLMISC::CMatrix toGridMatrix;
146 toGridMatrix.scale(NLMISC::CVector(1.f / _CellSize, 1.f / _CellSize, 1.f));
147 toGridMatrix.translate(- _MinCorner);
148 toGridMatrix = toGridMatrix * worldMatrix;
149 for(uint k = 0; k < numVerts; ++k)
151 wi.Shape.Vertices[k] = toGridMatrix * shape.Vertices[k];
153 // see if water surface already added
154 for(uint k = 0; k < _WaterInfoVect.size(); ++k)
156 if (_WaterInfoVect[k].Height == height && _WaterInfoVect[k].Shape.Vertices.size() == shape.Vertices.size())
158 if (std::equal(wi.Shape.Vertices.begin(), wi.Shape.Vertices.end(), _WaterInfoVect[k].Shape.Vertices.begin()))
160 // already inserted -> do nothing
161 return;
165 // insert new CWaterInfo in the list
166 wi.Height = height;
167 _WaterInfoVect.push_back(wi);
168 // build rasters
169 NLMISC::CPolygon2D::TRasterVect rasters;
170 sint minY;
171 //wi.Shape.computeBordersLarge(rasters, minY);
172 wi.Shape.computeOuterBorders(rasters, minY);
173 if (!rasters.empty())
175 sint numRasters = (sint) rasters.size();
176 for(sint y = 0; y < numRasters; ++y)
178 if ((y + minY) < 0 || (y + minY) >= _Height) continue; // outisde of map
179 sint lastX = rasters[y].second + 1;
180 for (sint x = rasters[y].first; x < lastX; ++x)
182 if (x < 0 || x >= _Width) continue; // outside of map
183 const TWaterInfoIndexVect *currWI = &_WaterInfoIndexVectVect[_Grid[x + (y + minY) * _Width]];
184 // see if there's already a list of water surfaces that match the list for that grid cell
185 // such a list should contains the surfaces already found for that grid cell + the new one
186 sint goodList = -1;
187 for(uint k = 0; k < _WaterInfoIndexVectVect.size(); ++k)
189 if (_WaterInfoIndexVectVect[k].size() == currWI->size() + 1)
191 if (std::equal(_WaterInfoIndexVectVect[k].begin(), _WaterInfoIndexVectVect[k].begin() + currWI->size(), currWI->begin()) &&
192 _WaterInfoIndexVectVect[k].back() == _WaterInfoVect.size() - 1)
194 // this list match what we want
195 goodList = k;
196 break;
200 if (goodList == -1)
202 // must create a new list
203 _WaterInfoIndexVectVect.push_back(TWaterInfoIndexVect());
204 // vector has grown up, so currWI pointer becomes invalid -> rebuild it
205 currWI = &_WaterInfoIndexVectVect[_Grid[x + (y + minY) * _Width]];
206 _WaterInfoIndexVectVect.back().resize(currWI->size() + 1);
207 std::copy(currWI->begin(), currWI->end(), _WaterInfoIndexVectVect.back().begin());
208 _WaterInfoIndexVectVect.back().back() = (uint16)_WaterInfoVect.size() - 1;
209 goodList = (sint)_WaterInfoIndexVectVect.size() - 1;
211 _Grid[x + (y + minY) * _Width] = goodList; // reassign new list
217 // *********************************************************************************************************
218 void CWaterMap::waterSurfaceRemoved(bool usesSceneWaterEnvMap)
220 H_AUTO_USE(RZ_WaterMap)
221 if (usesSceneWaterEnvMap) --WaterEnvMapRefCount;
226 static const NLMISC::CRGBA DebugCols[] =
228 NLMISC::CRGBA(255, 32, 32),
229 NLMISC::CRGBA(32, 255, 32),
230 NLMISC::CRGBA(255, 255, 32),
231 NLMISC::CRGBA(32, 255, 255),
232 NLMISC::CRGBA(255, 32, 255),
233 NLMISC::CRGBA(255, 127, 32),
234 NLMISC::CRGBA(255, 255, 255)
236 static const uint NumDebugCols = sizeof(DebugCols) / sizeof(DebugCols[0]);
239 // *********************************************************************************************************
240 void CWaterMap::dump(const std::string &filename)
242 H_AUTO_USE(RZ_WaterMap)
243 if (_Grid.empty())
245 nlwarning("Grid not built");
246 return;
248 NLMISC::CBitmap bm;
249 bm.resize(_Width, _Height, NLMISC::CBitmap::RGBA);
250 NLMISC::CRGBA *pix = (NLMISC::CRGBA *) bm.getPixels(0).getPtr();
251 for(uint x = 0; x < _Width; ++x)
253 for(uint y = 0; y < _Height; ++y)
255 if (_Grid[x + y *_Width] == 0)
257 pix[x + y * _Width] = CRGBA(127, 127, 127);
259 else
261 pix[x + y * _Width] = DebugCols[_Grid[x + y *_Width] % NumDebugCols];
265 // merge with world map if present
266 if (ContinentMngr.cur())
268 if (!ContinentMngr.cur()->WorldMap.empty())
270 std::string path = CPath::lookup(ContinentMngr.cur()->WorldMap, false);
271 if (!path.empty())
273 CIFile stream;
274 if (stream.open(path))
276 CBitmap worldMap;
277 if (worldMap.load(stream))
279 worldMap.flipV();
280 worldMap.convertToType(CBitmap::RGBA);
281 worldMap.resample(bm.getWidth(), bm.getHeight());
282 bm.blend(bm, worldMap, 127, true);
289 drawDisc(bm, ((float) UserEntity->pos().x - _MinCorner.x) / _CellSize, ((float) UserEntity->pos().y - _MinCorner.y) / _CellSize, 2.f, CRGBA::Magenta);
291 NLMISC::COFile f;
292 if (!f.open(filename))
294 nlwarning("Can't open %s for writing", filename.c_str());
295 return;
297 bm.writeTGA(f, 24, true);
298 f.close();
301 // ******************************************************************************************************************
302 void CWaterMap::render(const NLMISC::CVector2f &camPos, float maxDist /*=100.f*/)
304 H_AUTO_USE(RZ_WaterMap)
305 if (_Grid.empty()) return;
306 Driver->setViewMatrix(Scene->getCam().getMatrix().inverted());
307 NL3D::CFrustum fr;
308 Scene->getCam().getFrustum(fr.Left, fr.Right, fr.Bottom, fr.Top, fr.Near, fr.Far);
309 fr.Perspective = true;
310 Driver->setFrustum(fr);
311 Driver->setModelMatrix(NLMISC::CMatrix::Identity);
312 float userZ = UserEntity ? (float) UserEntity->pos().z : 0.f;
313 for (uint x = 0; x < _Width; ++x)
315 for (uint y = 0; y < _Height; ++y)
317 uint16 index = _Grid[x + _Width * y];
318 if (index == 0) continue;
319 const TWaterInfoIndexVect &wii = _WaterInfoIndexVectVect[index];
320 // see if cell not too far
321 NLMISC::CVector2f pos(x * _CellSize + _MinCorner.x, y * _CellSize + _MinCorner.y);
322 if ((camPos - pos).norm() > maxDist) continue; // too far, don't display
323 // display box for each primitive type
324 NLMISC::CVector cornerMin(pos.x, pos.y, userZ - 5.f);
325 NLMISC::CVector cornerMax(pos.x + _CellSize, pos.y + _CellSize, userZ + 5.f);
326 for(uint l = 0; l < wii.size(); ++l)
328 // add a bias each time to see when several primitives are overlapped
329 NLMISC::CVector bias = (float) wii[l] * NLMISC::CVector(0.01f, 0.f, 0.1f);
330 drawBox(cornerMin + bias, cornerMax + bias, DebugCols[wii[l] % NumDebugCols]);
336 #if !FINAL_VERSION
338 // ******************************************************************************************************************
339 // dump water map in a tga file
340 NLMISC_COMMAND(dumpWaterMap, "dump water map", "<filename>")
342 if (args.size() != 1) return false;
343 if (!ContinentMngr.cur()) return false;
344 ContinentMngr.cur()->WaterMap.dump(args[0]);
345 return true;
348 // ******************************************************************************************************************
349 // display the water map
350 NLMISC_COMMAND(displayWaterMap, "dump water map", "<0 = on / 1 = off>")
352 if (args.size() != 1) return false;
353 fromString(args[0], DisplayWaterMap);
354 return true;
357 #endif