Merge 'remotes/trunk'
[0ad.git] / source / renderer / WaterManager.cpp
blob38648cd41189e6a176876373e63b674065e77cb8
1 /* Copyright (C) 2017 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. 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 General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
19 * Water settings (speed, height) and texture management
22 #include "precompiled.h"
24 #include "graphics/Terrain.h"
25 #include "graphics/TextureManager.h"
26 #include "graphics/ShaderManager.h"
27 #include "graphics/ShaderProgram.h"
29 #include "lib/bits.h"
30 #include "lib/timer.h"
31 #include "lib/tex/tex.h"
32 #include "lib/res/graphics/ogl_tex.h"
34 #include "maths/MathUtil.h"
35 #include "maths/Vector2D.h"
37 #include "ps/CLogger.h"
38 #include "ps/Game.h"
39 #include "ps/World.h"
41 #include "renderer/WaterManager.h"
42 #include "renderer/Renderer.h"
44 #include "simulation2/Simulation2.h"
45 #include "simulation2/components/ICmpWaterManager.h"
46 #include "simulation2/components/ICmpRangeManager.h"
49 ///////////////////////////////////////////////////////////////////////////////////////////////
50 // WaterManager implementation
52 struct CoastalPoint
54 CoastalPoint(int idx, CVector2D pos) : index(idx), position(pos) {};
55 int index;
56 CVector2D position;
59 struct SWavesVertex {
60 // vertex position
61 CVector3D m_BasePosition;
62 CVector3D m_ApexPosition;
63 CVector3D m_SplashPosition;
64 CVector3D m_RetreatPosition;
66 CVector2D m_PerpVect;
67 u8 m_UV[3];
69 // pad to a power of two
70 u8 m_Padding[5];
72 cassert(sizeof(SWavesVertex) == 64);
74 struct WaveObject
76 CVertexBuffer::VBChunk* m_VBvertices;
77 CBoundingBoxAligned m_AABB;
78 size_t m_Width;
79 float m_TimeDiff;
82 ///////////////////////////////////////////////////////////////////
83 // Construction/Destruction
84 WaterManager::WaterManager()
86 // water
87 m_RenderWater = false; // disabled until textures are successfully loaded
88 m_WaterHeight = 5.0f;
90 m_WaterCurrentTex = 0;
92 m_ReflectionTexture = 0;
93 m_RefractionTexture = 0;
94 m_RefTextureSize = 0;
96 m_ReflectionFbo = 0;
97 m_RefractionFbo = 0;
98 m_FancyEffectsFBO = 0;
100 m_WaterTexTimer = 0.0;
102 m_WindAngle = 0.0f;
103 m_Waviness = 8.0f;
104 m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f);
105 m_WaterTint = CColor(0.28f, 0.3f, 0.59f, 1.0f);
106 m_Murkiness = 0.45f;
107 m_RepeatPeriod = 16.0f;
109 m_DistanceHeightmap = NULL;
110 m_BlurredNormalMap = NULL;
111 m_WindStrength = NULL;
113 m_ShoreWaves_VBIndices = NULL;
115 m_WaterEffects = true;
116 m_WaterFancyEffects = false;
117 m_WaterRealDepth = false;
118 m_WaterRefraction = false;
119 m_WaterReflection = false;
120 m_WaterShadows = false;
121 m_WaterType = L"ocean";
123 m_NeedsReloading = false;
124 m_NeedInfoUpdate = true;
126 m_depthTT = 0;
127 m_FancyTextureNormal = 0;
128 m_FancyTextureOther = 0;
129 m_FancyTextureDepth = 0;
130 m_ReflFboDepthTexture = 0;
131 m_RefrFboDepthTexture = 0;
133 m_MapSize = 0;
135 m_updatei0 = 0;
136 m_updatej0 = 0;
137 m_updatei1 = 0;
138 m_updatej1 = 0;
141 WaterManager::~WaterManager()
143 // Cleanup if the caller messed up
144 UnloadWaterTextures();
146 for (WaveObject* const& obj : m_ShoreWaves)
148 if (obj->m_VBvertices)
149 g_VBMan.Release(obj->m_VBvertices);
150 delete obj;
153 if (m_ShoreWaves_VBIndices)
154 g_VBMan.Release(m_ShoreWaves_VBIndices);
156 delete[] m_DistanceHeightmap;
157 delete[] m_BlurredNormalMap;
158 delete[] m_WindStrength;
160 if (!g_Renderer.GetCapabilities().m_PrettyWater)
161 return;
163 glDeleteTextures(1, &m_depthTT);
164 glDeleteTextures(1, &m_FancyTextureNormal);
165 glDeleteTextures(1, &m_FancyTextureOther);
166 glDeleteTextures(1, &m_FancyTextureDepth);
167 glDeleteTextures(1, &m_ReflFboDepthTexture);
168 glDeleteTextures(1, &m_RefrFboDepthTexture);
170 pglDeleteFramebuffersEXT(1, &m_FancyEffectsFBO);
171 pglDeleteFramebuffersEXT(1, &m_RefractionFbo);
172 pglDeleteFramebuffersEXT(1, &m_ReflectionFbo);
176 ///////////////////////////////////////////////////////////////////
177 // Progressive load of water textures
178 int WaterManager::LoadWaterTextures()
180 // TODO: this doesn't need to be progressive-loading any more
181 // (since texture loading is async now)
183 wchar_t pathname[PATH_MAX];
185 // Load diffuse grayscale images (for non-fancy water)
186 for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); ++i)
188 swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/default/diffuse%02d.dds", (int)i+1);
189 CTextureProperties textureProps(pathname);
190 textureProps.SetWrap(GL_REPEAT);
192 CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
193 texture->Prefetch();
194 m_WaterTexture[i] = texture;
197 if (!g_Renderer.GetCapabilities().m_PrettyWater)
199 // Enable rendering, now that we've succeeded this far
200 m_RenderWater = true;
201 return 0;
204 #if CONFIG2_GLES
205 #warning Fix WaterManager::LoadWaterTextures on GLES
206 #else
207 // Load normalmaps (for fancy water)
208 for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i)
210 swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", m_WaterType.c_str(), (int)i+1);
211 CTextureProperties textureProps(pathname);
212 textureProps.SetWrap(GL_REPEAT);
213 textureProps.SetMaxAnisotropy(4);
215 CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
216 texture->Prefetch();
217 m_NormalMap[i] = texture;
220 // Load CoastalWaves
222 CTextureProperties textureProps(L"art/textures/terrain/types/water/coastalWave.png");
223 textureProps.SetWrap(GL_REPEAT);
224 CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
225 texture->Prefetch();
226 m_WaveTex = texture;
229 // Load Foam
231 CTextureProperties textureProps(L"art/textures/terrain/types/water/foam.png");
232 textureProps.SetWrap(GL_REPEAT);
233 CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
234 texture->Prefetch();
235 m_FoamTex = texture;
238 // Use screen-sized textures for minimum artifacts.
239 m_RefTextureSize = g_Renderer.GetHeight();
241 m_RefTextureSize = round_up_to_pow2(m_RefTextureSize);
243 // Create reflection texture
244 glGenTextures(1, &m_ReflectionTexture);
245 glBindTexture(GL_TEXTURE_2D, m_ReflectionTexture);
246 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
247 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
248 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
249 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
250 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
252 // Create refraction texture
253 glGenTextures(1, &m_RefractionTexture);
254 glBindTexture(GL_TEXTURE_2D, m_RefractionTexture);
255 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
257 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
258 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
259 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
261 // Create depth textures
262 glGenTextures(1, &m_ReflFboDepthTexture);
263 glBindTexture(GL_TEXTURE_2D, m_ReflFboDepthTexture);
264 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
265 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
266 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
267 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
268 glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
270 glGenTextures(1, &m_RefrFboDepthTexture);
271 glBindTexture(GL_TEXTURE_2D, m_RefrFboDepthTexture);
272 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
273 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
274 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
275 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
276 glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
278 // Create the Fancy Effects texture
279 glGenTextures(1, &m_FancyTextureNormal);
280 glBindTexture(GL_TEXTURE_2D, m_FancyTextureNormal);
281 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
282 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
283 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
284 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
286 glGenTextures(1, &m_FancyTextureOther);
287 glBindTexture(GL_TEXTURE_2D, m_FancyTextureOther);
288 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
289 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
290 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
291 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
293 glGenTextures(1, &m_FancyTextureDepth);
294 glBindTexture(GL_TEXTURE_2D, m_FancyTextureDepth);
295 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
296 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
297 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
300 glBindTexture(GL_TEXTURE_2D, 0);
302 Resize();
304 // Create the water framebuffers
306 GLint currentFbo;
307 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &currentFbo);
309 m_ReflectionFbo = 0;
310 pglGenFramebuffersEXT(1, &m_ReflectionFbo);
311 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_ReflectionFbo);
312 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_ReflectionTexture, 0);
313 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_ReflFboDepthTexture, 0);
315 ogl_WarnIfError();
317 GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
318 if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
320 LOGWARNING("Reflection framebuffer object incomplete: 0x%04X", status);
321 g_Renderer.m_Options.m_WaterReflection = false;
324 m_RefractionFbo = 0;
325 pglGenFramebuffersEXT(1, &m_RefractionFbo);
326 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_RefractionFbo);
327 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_RefractionTexture, 0);
328 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_RefrFboDepthTexture, 0);
330 ogl_WarnIfError();
332 status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
333 if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
335 LOGWARNING("Refraction framebuffer object incomplete: 0x%04X", status);
336 g_Renderer.m_Options.m_WaterRefraction = false;
339 pglGenFramebuffersEXT(1, &m_FancyEffectsFBO);
340 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO);
341 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_FancyTextureNormal, 0);
342 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, m_FancyTextureOther, 0);
343 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_FancyTextureDepth, 0);
345 ogl_WarnIfError();
347 status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
348 if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
350 LOGWARNING("Fancy Effects framebuffer object incomplete: 0x%04X", status);
351 g_Renderer.m_Options.m_WaterRefraction = false;
354 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentFbo);
356 // Enable rendering, now that we've succeeded this far
357 m_RenderWater = true;
358 #endif
359 return 0;
363 ///////////////////////////////////////////////////////////////////
364 // Resize: Updates the fancy water textures.
365 void WaterManager::Resize()
367 glBindTexture(GL_TEXTURE_2D, m_FancyTextureNormal);
368 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_SHORT, NULL);
370 glBindTexture(GL_TEXTURE_2D, m_FancyTextureOther);
371 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_SHORT, NULL);
373 glBindTexture(GL_TEXTURE_2D, m_FancyTextureDepth);
374 glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
376 glBindTexture(GL_TEXTURE_2D, 0);
379 // This is for Atlas. TODO: this copies code from init, should reuse it.
380 void WaterManager::ReloadWaterNormalTextures()
382 wchar_t pathname[PATH_MAX];
383 // Load normalmaps (for fancy water)
384 for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i)
386 swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", m_WaterType.c_str(), (int)i+1);
387 CTextureProperties textureProps(pathname);
388 textureProps.SetWrap(GL_REPEAT);
389 textureProps.SetMaxAnisotropy(4);
391 CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
392 texture->Prefetch();
393 m_NormalMap[i] = texture;
397 ///////////////////////////////////////////////////////////////////
398 // Unload water textures
399 void WaterManager::UnloadWaterTextures()
401 for(size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
402 m_WaterTexture[i].reset();
404 if (!g_Renderer.GetCapabilities().m_PrettyWater)
405 return;
407 for(size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++)
408 m_NormalMap[i].reset();
410 glDeleteTextures(1, &m_ReflectionTexture);
411 glDeleteTextures(1, &m_RefractionTexture);
412 pglDeleteFramebuffersEXT(1, &m_RefractionFbo);
413 pglDeleteFramebuffersEXT(1, &m_ReflectionFbo);
416 ///////////////////////////////////////////////////////////////////
417 // Calculate our binary heightmap from the terrain heightmap.
418 void WaterManager::RecomputeDistanceHeightmap()
420 CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
421 if (!terrain || !terrain->GetHeightMap())
422 return;
424 size_t SideSize = m_MapSize*2;
425 if (m_DistanceHeightmap == NULL)
426 m_DistanceHeightmap = new float[SideSize*SideSize];
428 // Create a manhattan-distance heightmap.
429 // This is currently upsampled by a factor of 2 to get more precision
430 // This could be refined to only be done near the coast itself, but it's probably not necessary.
432 for (size_t z = 0; z < SideSize; ++z)
434 float level = SideSize;
435 for (size_t x = 0; x < SideSize; ++x)
436 m_DistanceHeightmap[z*SideSize + x] = terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight ? level = 0.f : ++level;
437 level = SideSize;
438 for (size_t x = SideSize-1; x != (size_t)-1; --x)
440 if (terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight)
441 level = 0.f;
442 else
444 ++level;
445 if (level < m_DistanceHeightmap[z*SideSize + x])
446 m_DistanceHeightmap[z*SideSize + x] = level;
450 for (size_t x = 0; x < SideSize; ++x)
452 float level = SideSize;
453 for (size_t z = 0; z < SideSize; ++z)
455 if (terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight)
456 level = 0.f;
457 else if (level > m_DistanceHeightmap[z*SideSize + x])
458 level = m_DistanceHeightmap[z*SideSize + x];
459 else
461 ++level;
462 if (level < m_DistanceHeightmap[z*SideSize + x])
463 m_DistanceHeightmap[z*SideSize + x] = level;
466 level = SideSize;
467 for (size_t z = SideSize-1; z != (size_t)-1; --z)
469 if (terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight)
470 level = 0.f;
471 else if (level > m_DistanceHeightmap[z*SideSize + x])
472 level = m_DistanceHeightmap[z*SideSize + x];
473 else
475 ++level;
476 if (level < m_DistanceHeightmap[z*SideSize + x])
477 m_DistanceHeightmap[z*SideSize + x] = level;
483 // This requires m_DistanceHeightmap to be defined properly.
484 void WaterManager::CreateWaveMeshes()
486 size_t SideSize = m_MapSize*2;
487 CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
488 if (!terrain || !terrain->GetHeightMap())
489 return;
491 for (WaveObject* const& obj : m_ShoreWaves)
493 if (obj->m_VBvertices)
494 g_VBMan.Release(obj->m_VBvertices);
495 delete obj;
497 m_ShoreWaves.clear();
499 if (m_ShoreWaves_VBIndices)
501 g_VBMan.Release(m_ShoreWaves_VBIndices);
502 m_ShoreWaves_VBIndices = NULL;
505 if (m_Waviness < 5.0f && m_WaterType != L"ocean")
506 return;
508 // First step: get the points near the coast.
509 std::set<int> CoastalPointsSet;
510 for (size_t z = 1; z < SideSize-1; ++z)
511 for (size_t x = 1; x < SideSize-1; ++x)
512 if (fabs(m_DistanceHeightmap[z*SideSize + x]-1.0f) < 0.2f)
513 CoastalPointsSet.insert(z*SideSize + x);
515 // Second step: create chains out of those coastal points.
516 static const int around[8][2] = { { -1,-1 }, { -1,0 }, { -1,1 }, { 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 } };
518 std::vector<std::deque<CoastalPoint> > CoastalPointsChains;
519 while (!CoastalPointsSet.empty())
521 int index = *(CoastalPointsSet.begin());
522 int x = index % SideSize;
523 int y = (index - x ) / SideSize;
525 std::deque<CoastalPoint> Chain;
527 Chain.push_front(CoastalPoint(index,CVector2D(x*2,y*2)));
529 // Erase us.
530 CoastalPointsSet.erase(CoastalPointsSet.begin());
532 // We're our starter points. At most we can have 2 points close to us.
533 // We'll pick the first one and look for its neighbors (he can only have one new)
534 // Up until we either reach the end of the chain, or ourselves.
535 // Then go down the other direction if there is any.
536 int neighbours[2] = { -1, -1 };
537 int nbNeighb = 0;
538 for (int i = 0; i < 8; ++i)
540 if (CoastalPointsSet.count(x + around[i][0] + (y + around[i][1])*SideSize))
542 if (nbNeighb < 2)
543 neighbours[nbNeighb] = x + around[i][0] + (y + around[i][1])*SideSize;
544 ++nbNeighb;
547 if (nbNeighb > 2)
548 continue;
550 for (int i = 0; i < 2; ++i)
552 if (neighbours[i] == -1)
553 continue;
554 // Move to our neighboring point
555 int xx = neighbours[i] % SideSize;
556 int yy = (neighbours[i] - xx ) / SideSize;
557 int indexx = xx + yy*SideSize;
558 int endedChain = false;
560 if (i == 0)
561 Chain.push_back(CoastalPoint(indexx,CVector2D(xx*2,yy*2)));
562 else
563 Chain.push_front(CoastalPoint(indexx,CVector2D(xx*2,yy*2)));
565 // If there's a loop we'll be the "other" neighboring point already so check for that.
566 // We'll readd at the end/front the other one to have full squares.
567 if (CoastalPointsSet.count(indexx) == 0)
568 break;
570 CoastalPointsSet.erase(indexx);
572 // Start checking from there.
573 while(!endedChain)
575 bool found = false;
576 nbNeighb = 0;
577 for (int p = 0; p < 8; ++p)
579 if (CoastalPointsSet.count(xx+around[p][0] + (yy + around[p][1])*SideSize))
581 if (nbNeighb >= 2)
583 CoastalPointsSet.erase(xx + yy*SideSize);
584 continue;
586 ++nbNeighb;
587 // We've found a new point around us.
588 // Move there
589 xx = xx + around[p][0];
590 yy = yy + around[p][1];
591 indexx = xx + yy*SideSize;
592 if (i == 0)
593 Chain.push_back(CoastalPoint(indexx,CVector2D(xx*2,yy*2)));
594 else
595 Chain.push_front(CoastalPoint(indexx,CVector2D(xx*2,yy*2)));
596 CoastalPointsSet.erase(xx + yy*SideSize);
597 found = true;
598 break;
601 if (!found)
602 endedChain = true;
605 if (Chain.size() > 10)
606 CoastalPointsChains.push_back(Chain);
609 // (optional) third step: Smooth chains out.
610 // This is also really dumb.
611 for (size_t i = 0; i < CoastalPointsChains.size(); ++i)
613 // Bump 1 for smoother.
614 for (int p = 0; p < 3; ++p)
616 for (size_t j = 1; j < CoastalPointsChains[i].size()-1; ++j)
618 CVector2D realPos = CoastalPointsChains[i][j-1].position + CoastalPointsChains[i][j+1].position;
620 CoastalPointsChains[i][j].position = (CoastalPointsChains[i][j].position + realPos/2.0f)/2.0f;
625 // Fourth step: create waves themselves, using those chains. We basically create subchains.
626 size_t waveSizes = 14; // maximal size in width.
628 // Construct indices buffer (we can afford one for all of them)
629 std::vector<GLushort> water_indices;
630 for (size_t a = 0; a < waveSizes-1;++a)
632 for (size_t rect = 0; rect < 7; ++rect)
634 water_indices.push_back(a*9 + rect);
635 water_indices.push_back(a*9 + 9 + rect);
636 water_indices.push_back(a*9 + 1 + rect);
637 water_indices.push_back(a*9 + 9 + rect);
638 water_indices.push_back(a*9 + 10 + rect);
639 water_indices.push_back(a*9 + 1 + rect);
642 // Generic indexes, max-length
643 m_ShoreWaves_VBIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
644 m_ShoreWaves_VBIndices->m_Owner->UpdateChunkVertices(m_ShoreWaves_VBIndices, &water_indices[0]);
646 float diff = (rand() % 50) / 5.0f;
648 for (size_t i = 0; i < CoastalPointsChains.size(); ++i)
650 for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j)
652 if (CoastalPointsChains[i].size()- 1 - j < waveSizes)
653 break;
655 size_t width = waveSizes;
657 // First pass to get some parameters out.
658 float outmost = 0.0f; // how far to move on the shore.
659 float avgDepth = 0.0f;
660 int sign = 1;
661 CVector2D firstPerp(0,0), perp(0,0), lastPerp(0,0);
662 for (size_t a = 0; a < waveSizes;++a)
664 lastPerp = perp;
665 perp = CVector2D(0,0);
666 int nb = 0;
667 CVector2D pos = CoastalPointsChains[i][j+a].position;
668 CVector2D posPlus;
669 CVector2D posMinus;
670 if (a > 0)
672 ++nb;
673 posMinus = CoastalPointsChains[i][j+a-1].position;
674 perp += pos-posMinus;
676 if (a < waveSizes-1)
678 ++nb;
679 posPlus = CoastalPointsChains[i][j+a+1].position;
680 perp += posPlus-pos;
682 perp /= nb;
683 perp = CVector2D(-perp.Y,perp.X).Normalized();
685 if (a == 0)
686 firstPerp = perp;
688 if ( a > 1 && perp.Dot(lastPerp) < 0.90f && perp.Dot(firstPerp) < 0.70f)
690 width = a+1;
691 break;
694 if (m_BlurredNormalMap[ (int)(pos.X/4) + (int)(pos.Y/4)*m_MapSize].Y < 0.9)
696 width = a-1;
697 break;
700 if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > m_WaterHeight)
701 sign = -1;
703 avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - m_WaterHeight;
705 float localOutmost = -2.0f;
706 while (localOutmost < 0.0f)
708 float depth = terrain->GetExactGroundLevel(pos.X+sign*perp.X*localOutmost, pos.Y+sign*perp.Y*localOutmost) - m_WaterHeight;
709 if (depth < 0.0f || depth > 0.6f)
710 localOutmost += 0.2f;
711 else
712 break;
715 outmost += localOutmost;
717 if (width < 5)
719 j += 6;
720 continue;
723 outmost /= width;
725 if (outmost > -0.5f)
727 j += 3;
728 continue;
730 outmost = -0.5f + outmost * m_Waviness/10.0f;
732 avgDepth /= width;
734 if (avgDepth > -1.3f)
736 j += 3;
737 continue;
739 // we passed the checks, we can create a wave of size "width".
741 WaveObject* shoreWave = new WaveObject;
742 std::vector<SWavesVertex> vertices;
744 shoreWave->m_Width = width;
745 shoreWave->m_TimeDiff = diff;
746 diff += (rand() % 100) / 25.0f + 4.0f;
748 for (size_t a = 0; a < width;++a)
750 CVector2D perp = CVector2D(0,0);
751 int nb = 0;
752 CVector2D pos = CoastalPointsChains[i][j+a].position;
753 CVector2D posPlus;
754 CVector2D posMinus;
755 if (a > 0)
757 ++nb;
758 posMinus = CoastalPointsChains[i][j+a-1].position;
759 perp += pos-posMinus;
761 if (a < waveSizes-1)
763 ++nb;
764 posPlus = CoastalPointsChains[i][j+a+1].position;
765 perp += posPlus-pos;
767 perp /= nb;
768 perp = CVector2D(-perp.Y,perp.X).Normalized();
770 SWavesVertex point[9];
772 float baseHeight = 0.04f;
774 float halfWidth = (width-1.0f)/2.0f;
775 float sideNess = sqrtf(clamp( (halfWidth - fabsf(a-halfWidth))/3.0f, 0.0f,1.0f));
777 point[0].m_UV[0] = a; point[0].m_UV[1] = 8;
778 point[1].m_UV[0] = a; point[1].m_UV[1] = 7;
779 point[2].m_UV[0] = a; point[2].m_UV[1] = 6;
780 point[3].m_UV[0] = a; point[3].m_UV[1] = 5;
781 point[4].m_UV[0] = a; point[4].m_UV[1] = 4;
782 point[5].m_UV[0] = a; point[5].m_UV[1] = 3;
783 point[6].m_UV[0] = a; point[6].m_UV[1] = 2;
784 point[7].m_UV[0] = a; point[7].m_UV[1] = 1;
785 point[8].m_UV[0] = a; point[8].m_UV[1] = 0;
787 point[0].m_PerpVect = perp;
788 point[1].m_PerpVect = perp;
789 point[2].m_PerpVect = perp;
790 point[3].m_PerpVect = perp;
791 point[4].m_PerpVect = perp;
792 point[5].m_PerpVect = perp;
793 point[6].m_PerpVect = perp;
794 point[7].m_PerpVect = perp;
795 point[8].m_PerpVect = perp;
797 static const float perpT1[9] = { 6.0f, 6.05f, 6.1f, 6.2f, 6.3f, 6.4f, 6.5f, 6.6f, 9.7f };
798 static const float perpT2[9] = { 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 3.0f, 3.3f, 3.6f, 9.5f };
799 static const float perpT3[9] = { 1.1f, 0.7f, -0.2f, 0.0f, 0.6f, 1.3f, 2.2f, 3.6f, 9.0f };
800 static const float perpT4[9] = { 2.0f, 2.1f, 1.2f, 1.5f, 1.7f, 1.9f, 2.7f, 3.8f, 9.0f };
802 static const float heightT1[9] = { 0.0f, 0.2f, 0.5f, 0.8f, 0.9f, 0.85f, 0.6f, 0.2f, 0.0 };
803 static const float heightT2[9] = { -0.8f, -0.4f, 0.0f, 0.1f, 0.1f, 0.03f, 0.0f, 0.0f, 0.0 };
804 static const float heightT3[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0 };
806 for (size_t t = 0; t < 9; ++t)
808 float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT1[t]+outmost),
809 pos.Y+sign*perp.Y*(perpT1[t]+outmost));
810 point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight),
811 pos.Y+sign*perp.Y*(perpT1[t]+outmost));
813 for (size_t t = 0; t < 9; ++t)
815 float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT2[t]+outmost),
816 pos.Y+sign*perp.Y*(perpT2[t]+outmost));
817 point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight),
818 pos.Y+sign*perp.Y*(perpT2[t]+outmost));
820 for (size_t t = 0; t < 9; ++t)
822 float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess),
823 pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess));
824 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));
826 for (size_t t = 0; t < 9; ++t)
828 float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT4[t]+outmost),
829 pos.Y+sign*perp.Y*(perpT4[t]+outmost));
830 point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(m_WaterHeight,terrHeight),
831 pos.Y+sign*perp.Y*(perpT4[t]+outmost));
834 vertices.push_back(point[8]);
835 vertices.push_back(point[7]);
836 vertices.push_back(point[6]);
837 vertices.push_back(point[5]);
838 vertices.push_back(point[4]);
839 vertices.push_back(point[3]);
840 vertices.push_back(point[2]);
841 vertices.push_back(point[1]);
842 vertices.push_back(point[0]);
844 shoreWave->m_AABB += point[8].m_SplashPosition;
845 shoreWave->m_AABB += point[8].m_BasePosition;
846 shoreWave->m_AABB += point[0].m_SplashPosition;
847 shoreWave->m_AABB += point[0].m_BasePosition;
848 shoreWave->m_AABB += point[4].m_ApexPosition;
851 if (sign == 1)
853 // Let's do some fancy reversing.
854 std::vector<SWavesVertex> reversed;
855 for (int a = width-1; a >= 0; --a)
857 for (size_t t = 0; t < 9; ++t)
858 reversed.push_back(vertices[a*9+t]);
860 vertices = reversed;
862 j += width/2-1;
864 shoreWave->m_VBvertices = g_VBMan.Allocate(sizeof(SWavesVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
865 shoreWave->m_VBvertices->m_Owner->UpdateChunkVertices(shoreWave->m_VBvertices, &vertices[0]);
867 m_ShoreWaves.push_back(shoreWave);
872 void WaterManager::RenderWaves(const CFrustum& frustrum)
874 #if CONFIG2_GLES
875 #warning Fix WaterManager::RenderWaves on GLES
876 #else
877 if (g_Renderer.m_SkipSubmit || !m_WaterFancyEffects)
878 return;
880 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO);
882 GLuint attachments[2] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT };
883 pglDrawBuffers(2, attachments);
885 glClearColor(0.0f,0.0f, 0.0f,0.0f);
886 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
888 glEnable(GL_BLEND);
889 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
890 glEnable(GL_DEPTH_TEST);
891 glDepthFunc(GL_ALWAYS);
893 CShaderDefines none;
894 CShaderProgramPtr shad = g_Renderer.GetShaderManager().LoadProgram("glsl/waves", none);
896 shad->Bind();
898 shad->BindTexture(str_waveTex, m_WaveTex);
899 shad->BindTexture(str_foamTex, m_FoamTex);
901 shad->Uniform(str_time, (float)m_WaterTexTimer);
902 shad->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
904 for (size_t a = 0; a < m_ShoreWaves.size(); ++a)
906 if (!frustrum.IsBoxVisible(m_ShoreWaves[a]->m_AABB))
907 continue;
909 CVertexBuffer::VBChunk* VBchunk = m_ShoreWaves[a]->m_VBvertices;
910 SWavesVertex* base = (SWavesVertex*)VBchunk->m_Owner->Bind();
912 // setup data pointers
913 GLsizei stride = sizeof(SWavesVertex);
914 shad->VertexPointer(3, GL_FLOAT, stride, &base[VBchunk->m_Index].m_BasePosition);
915 shad->TexCoordPointer(GL_TEXTURE0, 2, GL_UNSIGNED_BYTE, stride, &base[VBchunk->m_Index].m_UV);
916 // NormalPointer(gl_FLOAT, stride, &base[m_VBWater->m_Index].m_UV)
917 pglVertexAttribPointerARB(2, 2, GL_FLOAT, GL_TRUE, stride, &base[VBchunk->m_Index].m_PerpVect); // replaces commented above because my normal is vec2
918 shad->VertexAttribPointer(str_a_apexPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_ApexPosition);
919 shad->VertexAttribPointer(str_a_splashPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_SplashPosition);
920 shad->VertexAttribPointer(str_a_retreatPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_RetreatPosition);
922 shad->AssertPointersBound();
924 shad->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff);
925 shad->Uniform(str_width, (int)m_ShoreWaves[a]->m_Width);
927 u8* indexBase = m_ShoreWaves_VBIndices->m_Owner->Bind();
928 glDrawElements(GL_TRIANGLES, (GLsizei) (m_ShoreWaves[a]->m_Width-1)*(7*6),
929 GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_ShoreWaves_VBIndices->m_Index));
931 shad->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff + 6.0f);
933 // TODO: figure out why this doesn't work.
934 //g_Renderer.m_Stats.m_DrawCalls++;
935 //g_Renderer.m_Stats.m_WaterTris += m_ShoreWaves_VBIndices->m_Count / 3;
937 CVertexBuffer::Unbind();
939 shad->Unbind();
940 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
942 glDisable(GL_BLEND);
943 glDepthFunc(GL_LEQUAL);
944 #endif
947 ///////////////////////////////////////////////////////////////////
948 // Calculate The blurred normal map to get an idea of where water ought to go.
949 void WaterManager::RecomputeBlurredNormalMap()
951 CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
952 if (!terrain || !terrain->GetHeightMap())
953 return;
955 // used to cache terrain normals since otherwise we'd recalculate them a lot (I'm blurring the "normal" map).
956 // this might be updated to actually cache in the terrain manager but that's not for now.
957 if (m_BlurredNormalMap == NULL)
958 m_BlurredNormalMap = new CVector3D[m_MapSize*m_MapSize];
960 // It's really slow to calculate normals so cache them first.
961 CVector3D* normals = new CVector3D[m_MapSize*m_MapSize];
963 // Not the edges, we won't care about them.
964 float ii = 8.0f, jj = 8.0f;
965 for (size_t j = 2; j < m_MapSize-2; ++j, jj += 4.0f)
966 for (size_t i = 2; i < m_MapSize-2; ++i, ii += 4.0f)
968 CVector3D norm;
969 terrain->CalcNormal(i,j,norm);
970 normals[j*m_MapSize + i] = norm;
973 // We could be way fancier (and faster) for our blur but we probably don't need the complexity.
974 // Two pass filter, nothing complicated here.
975 CVector3D blurValue;
976 ii = 8.0f; jj = 8.0f;
977 size_t idx = 2;
978 for (size_t j = 2; j < m_MapSize-2; ++j, jj += 4.0f)
979 for (size_t i = 2; i < m_MapSize-2; ++i, ii += 4.0f,++idx)
981 blurValue = normals[idx-2];
982 blurValue += normals[idx-1];
983 blurValue += normals[idx];
984 blurValue += normals[idx+1];
985 blurValue += normals[idx+2];
986 m_BlurredNormalMap[idx] = blurValue * 0.2f;
988 // y direction, probably slower because of cache misses but I don't see an easy way around that.
989 ii = 8.0f; jj = 8.0f;
990 for (size_t i = 2; i < m_MapSize-2; ++i, ii += 4.0f)
992 for (size_t j = 2; j < m_MapSize-2; ++j, jj += 4.0f)
994 blurValue = normals[(j-2)*m_MapSize + i];
995 blurValue += normals[(j-1)*m_MapSize + i];
996 blurValue += normals[j*m_MapSize + i];
997 blurValue += normals[(j+1)*m_MapSize + i];
998 blurValue += normals[(j+2)*m_MapSize + i];
999 m_BlurredNormalMap[j*m_MapSize + i] = blurValue * 0.2f;
1003 delete[] normals;
1006 void WaterManager::RecomputeWaterData()
1008 if (!m_MapSize)
1009 return;
1011 RecomputeBlurredNormalMap();
1012 RecomputeDistanceHeightmap();
1013 RecomputeWindStrength();
1014 CreateWaveMeshes();
1017 ///////////////////////////////////////////////////////////////////
1018 // Calculate the strength of the wind at a given point on the map.
1019 // This is too slow and should support limited recomputation.
1020 void WaterManager::RecomputeWindStrength()
1022 if (m_WindStrength == NULL)
1023 m_WindStrength = new float[m_MapSize*m_MapSize];
1025 CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
1026 if (!terrain || !terrain->GetHeightMap())
1027 return;
1029 float waterLevel = m_WaterHeight;
1031 CVector2D windDir = CVector2D(cos(m_WindAngle),sin(m_WindAngle));
1032 CVector2D perp = CVector2D(-windDir.Y, windDir.X);
1034 // Our kernel will sample 5 points going towards the wind (generally).
1035 int kernel[5][2] = { {(int)windDir.X*2,(int)windDir.Y*2}, {(int)windDir.X*5,(int)windDir.Y*5}, {(int)windDir.X*9,(int)windDir.Y*9}, {(int)windDir.X*16,(int)windDir.Y*16}, {(int)windDir.X*25,(int)windDir.Y*25} };
1037 float* Temp = new float[m_MapSize*m_MapSize];
1038 std::fill(Temp, Temp + m_MapSize*m_MapSize, 1.0f);
1040 for (size_t j = 0; j < m_MapSize; ++j)
1041 for (size_t i = 0; i < m_MapSize; ++i)
1043 float curHeight = terrain->GetVertexGroundLevel(i,j);
1044 if (curHeight >= waterLevel)
1046 Temp[j*m_MapSize + i] = 0.3f; // blurs too strong otherwise
1047 continue;
1049 if (terrain->GetVertexGroundLevel(i + ceil(windDir.X),j + ceil(windDir.Y)) < waterLevel)
1050 continue;
1052 // Calculate how dampened our waves should be.
1053 float oldHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[4][0],j+kernel[4][1]));
1054 float currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[3][0],j+kernel[3][1]));
1055 float avgheight = oldHeight + currentHeight;
1056 float tendency = currentHeight - oldHeight;
1057 oldHeight = currentHeight;
1058 currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[2][0],j+kernel[2][1]));
1059 avgheight += currentHeight;
1060 tendency += currentHeight - oldHeight;
1061 oldHeight = currentHeight;
1062 currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[1][0],j+kernel[1][1]));
1063 avgheight += currentHeight;
1064 tendency += currentHeight - oldHeight;
1065 oldHeight = currentHeight;
1066 currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[0][0],j+kernel[0][1]));
1067 avgheight += currentHeight;
1068 tendency += currentHeight - oldHeight;
1070 float baseLevel = std::max(0.0f,1.0f - (avgheight/5.0f-waterLevel)/20.0f);
1071 baseLevel *= baseLevel;
1072 tendency /= 15.0f;
1073 baseLevel -= tendency; // if the terrain was sloping downwards, increase baselevel. Otherwise reduce.
1074 baseLevel = clamp(baseLevel,0.0f,1.0f);
1076 // Draw on map. This is pretty slow.
1077 float length = 35.0f * (1.0f-baseLevel/1.8f);
1078 for (float y = 0; y < length; y += 0.6f)
1080 int xx = clamp(i - y * windDir.X,0.0f,(float)(m_MapSize-1));
1081 int yy = clamp(j - y * windDir.Y,0.0f,(float)(m_MapSize-1));
1082 Temp[yy*m_MapSize + xx] = Temp[yy*m_MapSize + xx] < (0.0f+baseLevel/1.5f) * (1.0f-y/length) + y/length * 1.0f ?
1083 Temp[yy*m_MapSize + xx] : (0.0f+baseLevel/1.5f) * (1.0f-y/length) + y/length * 1.0f;
1087 int blurKernel[4][2] = { {(int)ceil(windDir.X),(int)ceil(windDir.Y)}, {(int)windDir.X*3,(int)windDir.Y*3}, {(int)ceil(perp.X),(int)ceil(perp.Y)}, {(int)-ceil(perp.X),(int)-ceil(perp.Y)} };
1088 float blurValue;
1089 for (size_t j = 2; j < m_MapSize-2; ++j)
1090 for (size_t i = 2; i < m_MapSize-2; ++i)
1092 blurValue = Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]];
1093 blurValue += Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]];
1094 blurValue += Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]];
1095 blurValue += Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]];
1096 m_WindStrength[j*m_MapSize + i] = blurValue * 0.25f;
1098 delete[] Temp;
1101 ////////////////////////////////////////////////////////////////////////
1102 // TODO: This will always recalculate for now
1103 void WaterManager::SetMapSize(size_t size)
1105 // TODO: Im' blindly trusting the user here.
1106 m_MapSize = size;
1107 m_NeedInfoUpdate = true;
1108 m_updatei0 = 0;
1109 m_updatei1 = size;
1110 m_updatej0 = 0;
1111 m_updatej1 = size;
1113 SAFE_ARRAY_DELETE(m_DistanceHeightmap);
1114 SAFE_ARRAY_DELETE(m_BlurredNormalMap);
1115 SAFE_ARRAY_DELETE(m_WindStrength);
1118 ////////////////////////////////////////////////////////////////////////
1119 // This will set the bools properly
1120 void WaterManager::UpdateQuality()
1122 if (g_Renderer.GetOptionBool(CRenderer::OPT_WATEREFFECTS) != m_WaterEffects)
1124 m_WaterEffects = g_Renderer.GetOptionBool(CRenderer::OPT_WATEREFFECTS);
1125 m_NeedsReloading = true;
1127 if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERFANCYEFFECTS) != m_WaterFancyEffects) {
1128 m_WaterFancyEffects = g_Renderer.GetOptionBool(CRenderer::OPT_WATERFANCYEFFECTS);
1129 m_NeedsReloading = true;
1131 if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREALDEPTH) != m_WaterRealDepth) {
1132 m_WaterRealDepth = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREALDEPTH);
1133 m_NeedsReloading = true;
1135 if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION) != m_WaterRefraction) {
1136 m_WaterRefraction = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION);
1137 m_NeedsReloading = true;
1139 if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFLECTION) != m_WaterReflection) {
1140 m_WaterReflection = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFLECTION);
1141 m_NeedsReloading = true;
1143 if (g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWSONWATER) != m_WaterShadows) {
1144 m_WaterShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWSONWATER);
1145 m_NeedsReloading = true;
1149 bool WaterManager::WillRenderFancyWater()
1151 return m_RenderWater && m_WaterEffects && g_Renderer.GetCapabilities().m_PrettyWater;