1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
18 #include "water_map.h"
19 #include "continent_manager.h"
20 #include "user_entity.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"
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
)
60 // *********************************************************************************************************
61 void CWaterMap::init(const NLMISC::CVector2f
&minCorner
, const NLMISC::CVector2f
&maxCorner
, float cellSize
/*=20.f*/)
63 H_AUTO_USE(RZ_WaterMap
)
67 nlwarning("Invalid cell size");
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
);
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
);
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
;
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
)))
121 splashEnabled
= wi
.SplashEnabled
;
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
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
165 // insert new CWaterInfo in the list
167 _WaterInfoVect
.push_back(wi
);
169 NLMISC::CPolygon2D::TRasterVect rasters
;
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
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
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
)
245 nlwarning("Grid not built");
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);
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);
274 if (stream
.open(path
))
277 if (worldMap
.load(stream
))
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
);
292 if (!f
.open(filename
))
294 nlwarning("Can't open %s for writing", filename
.c_str());
297 bm
.writeTGA(f
, 24, true);
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());
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
]);
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]);
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
);