1 /* Copyright (C) 2017 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
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.
8  *
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.
13  *
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/>.
16  */
17 
18 /*
19  * Water settings (speed, height) and texture management
20  */
21 
22 #include "precompiled.h"
23 
24 #include "graphics/Terrain.h"
25 #include "graphics/TextureManager.h"
26 #include "graphics/ShaderManager.h"
27 #include "graphics/ShaderProgram.h"
28 
29 #include "lib/bits.h"
30 #include "lib/timer.h"
31 #include "lib/tex/tex.h"
32 #include "lib/res/graphics/ogl_tex.h"
33 
34 #include "maths/MathUtil.h"
35 #include "maths/Vector2D.h"
36 
37 #include "ps/CLogger.h"
38 #include "ps/Game.h"
39 #include "ps/World.h"
40 
41 #include "renderer/WaterManager.h"
42 #include "renderer/Renderer.h"
43 
44 #include "simulation2/Simulation2.h"
45 #include "simulation2/components/ICmpWaterManager.h"
46 #include "simulation2/components/ICmpRangeManager.h"
47 
48 
49 ///////////////////////////////////////////////////////////////////////////////////////////////
50 // WaterManager implementation
51 
52 struct CoastalPoint
53 {
CoastalPointCoastalPoint54 	CoastalPoint(int idx, CVector2D pos) : index(idx), position(pos) {};
55 	int index;
56 	CVector2D position;
57 };
58 
59 struct SWavesVertex {
60 	// vertex position
61 	CVector3D m_BasePosition;
62 	CVector3D m_ApexPosition;
63 	CVector3D m_SplashPosition;
64 	CVector3D m_RetreatPosition;
65 
66 	CVector2D m_PerpVect;
67 	u8 m_UV[3];
68 
69 	// pad to a power of two
70 	u8 m_Padding[5];
71 };
72 cassert(sizeof(SWavesVertex) == 64);
73 
74 struct WaveObject
75 {
76 	CVertexBuffer::VBChunk* m_VBvertices;
77 	CBoundingBoxAligned m_AABB;
78 	size_t m_Width;
79 	float m_TimeDiff;
80 };
81 
82 ///////////////////////////////////////////////////////////////////
83 // Construction/Destruction
WaterManager()84 WaterManager::WaterManager()
85 {
86 	// water
87 	m_RenderWater = false; // disabled until textures are successfully loaded
88 	m_WaterHeight = 5.0f;
89 
90 	m_WaterCurrentTex = 0;
91 
92 	m_ReflectionTexture = 0;
93 	m_RefractionTexture = 0;
94 	m_RefTextureSize = 0;
95 
96 	m_ReflectionFbo = 0;
97 	m_RefractionFbo = 0;
98 	m_FancyEffectsFBO = 0;
99 
100 	m_WaterTexTimer = 0.0;
101 
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;
108 
109 	m_DistanceHeightmap = NULL;
110 	m_BlurredNormalMap = NULL;
111 	m_WindStrength = NULL;
112 
113 	m_ShoreWaves_VBIndices = NULL;
114 
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";
122 
123 	m_NeedsReloading = false;
124 	m_NeedInfoUpdate = true;
125 
126 	m_depthTT = 0;
127 	m_FancyTextureNormal = 0;
128 	m_FancyTextureOther = 0;
129 	m_FancyTextureDepth = 0;
130 	m_ReflFboDepthTexture = 0;
131 	m_RefrFboDepthTexture = 0;
132 
133 	m_MapSize = 0;
134 
135 	m_updatei0 = 0;
136 	m_updatej0 = 0;
137 	m_updatei1 = 0;
138 	m_updatej1 = 0;
139 }
140 
~WaterManager()141 WaterManager::~WaterManager()
142 {
143 	// Cleanup if the caller messed up
144 	UnloadWaterTextures();
145 
146 	for (WaveObject* const& obj : m_ShoreWaves)
147 	{
148 		if (obj->m_VBvertices)
149 			g_VBMan.Release(obj->m_VBvertices);
150 		delete obj;
151 	}
152 
153 	if (m_ShoreWaves_VBIndices)
154 		g_VBMan.Release(m_ShoreWaves_VBIndices);
155 
156 	delete[] m_DistanceHeightmap;
157 	delete[] m_BlurredNormalMap;
158 	delete[] m_WindStrength;
159 
160 	if (!g_Renderer.GetCapabilities().m_PrettyWater)
161 		return;
162 
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);
169 
170 	pglDeleteFramebuffersEXT(1, &m_FancyEffectsFBO);
171 	pglDeleteFramebuffersEXT(1, &m_RefractionFbo);
172 	pglDeleteFramebuffersEXT(1, &m_ReflectionFbo);
173 }
174 
175 
176 ///////////////////////////////////////////////////////////////////
177 // Progressive load of water textures
LoadWaterTextures()178 int WaterManager::LoadWaterTextures()
179 {
180 	// TODO: this doesn't need to be progressive-loading any more
181 	// (since texture loading is async now)
182 
183 	wchar_t pathname[PATH_MAX];
184 
185 	// Load diffuse grayscale images (for non-fancy water)
186 	for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); ++i)
187 	{
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);
191 
192 		CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
193 		texture->Prefetch();
194 		m_WaterTexture[i] = texture;
195 	}
196 
197 	if (!g_Renderer.GetCapabilities().m_PrettyWater)
198 	{
199 		// Enable rendering, now that we've succeeded this far
200 		m_RenderWater = true;
201 		return 0;
202 	}
203 
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)
209 	{
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);
214 
215 		CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
216 		texture->Prefetch();
217 		m_NormalMap[i] = texture;
218 	}
219 
220 	// Load CoastalWaves
221 	{
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;
227 	}
228 
229 	// Load Foam
230 	{
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;
236 	}
237 
238 	// Use screen-sized textures for minimum artifacts.
239 	m_RefTextureSize = g_Renderer.GetHeight();
240 
241 	m_RefTextureSize = round_up_to_pow2(m_RefTextureSize);
242 
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);
251 
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);
260 
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);
269 
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);
277 
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);
285 
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);
292 
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);
299 
300 	glBindTexture(GL_TEXTURE_2D, 0);
301 
302 	Resize();
303 
304 	// Create the water framebuffers
305 
306 	GLint currentFbo;
307 	glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &currentFbo);
308 
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);
314 
315 	ogl_WarnIfError();
316 
317 	GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
318 	if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
319 	{
320 		LOGWARNING("Reflection framebuffer object incomplete: 0x%04X", status);
321 		g_Renderer.m_Options.m_WaterReflection = false;
322 	}
323 
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);
329 
330 	ogl_WarnIfError();
331 
332 	status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
333 	if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
334 	{
335 		LOGWARNING("Refraction framebuffer object incomplete: 0x%04X", status);
336 		g_Renderer.m_Options.m_WaterRefraction = false;
337 	}
338 
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);
344 
345 	ogl_WarnIfError();
346 
347 	status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
348 	if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
349 	{
350 		LOGWARNING("Fancy Effects framebuffer object incomplete: 0x%04X", status);
351 		g_Renderer.m_Options.m_WaterRefraction = false;
352 	}
353 
354 	pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentFbo);
355 
356 	// Enable rendering, now that we've succeeded this far
357 	m_RenderWater = true;
358 #endif
359 	return 0;
360 }
361 
362 
363 ///////////////////////////////////////////////////////////////////
364 // Resize: Updates the fancy water textures.
Resize()365 void WaterManager::Resize()
366 {
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);
369 
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);
372 
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);
375 
376 	glBindTexture(GL_TEXTURE_2D, 0);
377 }
378 
379 // This is for Atlas. TODO: this copies code from init, should reuse it.
ReloadWaterNormalTextures()380 void WaterManager::ReloadWaterNormalTextures()
381 {
382 	wchar_t pathname[PATH_MAX];
383 	// Load normalmaps (for fancy water)
384 	for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i)
385 	{
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);
390 
391 		CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
392 		texture->Prefetch();
393 		m_NormalMap[i] = texture;
394 	}
395 }
396 
397 ///////////////////////////////////////////////////////////////////
398 // Unload water textures
UnloadWaterTextures()399 void WaterManager::UnloadWaterTextures()
400 {
401 	for(size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
402 		m_WaterTexture[i].reset();
403 
404 	if (!g_Renderer.GetCapabilities().m_PrettyWater)
405 		return;
406 
407 	for(size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++)
408 		m_NormalMap[i].reset();
409 
410 	glDeleteTextures(1, &m_ReflectionTexture);
411 	glDeleteTextures(1, &m_RefractionTexture);
412 	pglDeleteFramebuffersEXT(1, &m_RefractionFbo);
413 	pglDeleteFramebuffersEXT(1, &m_ReflectionFbo);
414 }
415 
416 ///////////////////////////////////////////////////////////////////
417 // Calculate our binary heightmap from the terrain heightmap.
RecomputeDistanceHeightmap()418 void WaterManager::RecomputeDistanceHeightmap()
419 {
420 	CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
421 	if (!terrain || !terrain->GetHeightMap())
422 		return;
423 
424 	size_t SideSize = m_MapSize*2;
425 	if (m_DistanceHeightmap == NULL)
426 		m_DistanceHeightmap = new float[SideSize*SideSize];
427 
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.
431 
432 	for (size_t z = 0; z < SideSize; ++z)
433 	{
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)
439 		{
440 			if (terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight)
441 				level = 0.f;
442 			else
443 			{
444 				++level;
445 				if (level < m_DistanceHeightmap[z*SideSize + x])
446 					m_DistanceHeightmap[z*SideSize + x] = level;
447 			}
448 		}
449 	}
450 	for (size_t x = 0; x < SideSize; ++x)
451 	{
452 		float level = SideSize;
453 		for (size_t z = 0; z < SideSize; ++z)
454 		{
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
460 			{
461 				++level;
462 				if (level < m_DistanceHeightmap[z*SideSize + x])
463 					m_DistanceHeightmap[z*SideSize + x] = level;
464 			}
465 		}
466 		level = SideSize;
467 		for (size_t z = SideSize-1; z != (size_t)-1; --z)
468 		{
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
474 			{
475 				++level;
476 				if (level < m_DistanceHeightmap[z*SideSize + x])
477 					m_DistanceHeightmap[z*SideSize + x] = level;
478 			}
479 		}
480 	}
481 }
482 
483 // This requires m_DistanceHeightmap to be defined properly.
CreateWaveMeshes()484 void WaterManager::CreateWaveMeshes()
485 {
486 	size_t SideSize = m_MapSize*2;
487 	CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
488 	if (!terrain || !terrain->GetHeightMap())
489 		return;
490 
491 	for (WaveObject* const& obj : m_ShoreWaves)
492 	{
493 		if (obj->m_VBvertices)
494 			g_VBMan.Release(obj->m_VBvertices);
495 		delete obj;
496 	}
497 	m_ShoreWaves.clear();
498 
499 	if (m_ShoreWaves_VBIndices)
500 	{
501 		g_VBMan.Release(m_ShoreWaves_VBIndices);
502 		m_ShoreWaves_VBIndices = NULL;
503 	}
504 
505 	if (m_Waviness < 5.0f && m_WaterType != L"ocean")
506 		return;
507 
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);
514 
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 } };
517 
518 	std::vector<std::deque<CoastalPoint> > CoastalPointsChains;
519 	while (!CoastalPointsSet.empty())
520 	{
521 		int index = *(CoastalPointsSet.begin());
522 		int x = index % SideSize;
523 		int y = (index - x ) / SideSize;
524 
525 		std::deque<CoastalPoint> Chain;
526 
527 		Chain.push_front(CoastalPoint(index,CVector2D(x*2,y*2)));
528 
529 		// Erase us.
530 		CoastalPointsSet.erase(CoastalPointsSet.begin());
531 
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)
539 		{
540 			if (CoastalPointsSet.count(x + around[i][0] + (y + around[i][1])*SideSize))
541 			{
542 				if (nbNeighb < 2)
543 					neighbours[nbNeighb] = x + around[i][0] + (y + around[i][1])*SideSize;
544 				++nbNeighb;
545 			}
546 		}
547 		if (nbNeighb > 2)
548 			continue;
549 
550 		for (int i = 0; i < 2; ++i)
551 		{
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;
559 
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)));
564 
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;
569 
570 			CoastalPointsSet.erase(indexx);
571 
572 			// Start checking from there.
573 			while(!endedChain)
574 			{
575 				bool found = false;
576 				nbNeighb = 0;
577 				for (int p = 0; p < 8; ++p)
578 				{
579 					if (CoastalPointsSet.count(xx+around[p][0] + (yy + around[p][1])*SideSize))
580 					{
581 						if (nbNeighb >= 2)
582 						{
583 							CoastalPointsSet.erase(xx + yy*SideSize);
584 							continue;
585 						}
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;
599 					}
600 				}
601 				if (!found)
602 					endedChain = true;
603 			}
604 		}
605 		if (Chain.size() > 10)
606 			CoastalPointsChains.push_back(Chain);
607 	}
608 
609 	// (optional) third step: Smooth chains out.
610 	// This is also really dumb.
611 	for (size_t i = 0; i < CoastalPointsChains.size(); ++i)
612 	{
613 		// Bump 1 for smoother.
614 		for (int p = 0; p < 3; ++p)
615 		{
616 			for (size_t j = 1; j < CoastalPointsChains[i].size()-1; ++j)
617 			{
618 				CVector2D realPos = CoastalPointsChains[i][j-1].position + CoastalPointsChains[i][j+1].position;
619 
620 				CoastalPointsChains[i][j].position = (CoastalPointsChains[i][j].position + realPos/2.0f)/2.0f;
621 			}
622 		}
623 	}
624 
625 	// Fourth step: create waves themselves, using those chains. We basically create subchains.
626 	size_t waveSizes = 14;	// maximal size in width.
627 
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)
631 	{
632 		for (size_t rect = 0; rect < 7; ++rect)
633 		{
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);
640 		}
641 	}
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]);
645 
646 	float diff = (rand() % 50) / 5.0f;
647 
648 	for (size_t i = 0; i < CoastalPointsChains.size(); ++i)
649 	{
650 		for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j)
651 		{
652 			if (CoastalPointsChains[i].size()- 1 - j < waveSizes)
653 				break;
654 
655 			size_t width = waveSizes;
656 
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)
663 			{
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)
671 				{
672 					++nb;
673 					posMinus = CoastalPointsChains[i][j+a-1].position;
674 					perp += pos-posMinus;
675 				}
676 				if (a < waveSizes-1)
677 				{
678 					++nb;
679 					posPlus = CoastalPointsChains[i][j+a+1].position;
680 					perp += posPlus-pos;
681 				}
682 				perp /= nb;
683 				perp = CVector2D(-perp.Y,perp.X).Normalized();
684 
685 				if (a == 0)
686 					firstPerp = perp;
687 
688 				if ( a > 1 && perp.Dot(lastPerp) < 0.90f && perp.Dot(firstPerp) < 0.70f)
689 				{
690 					width = a+1;
691 					break;
692 				}
693 
694 				if (m_BlurredNormalMap[ (int)(pos.X/4) + (int)(pos.Y/4)*m_MapSize].Y < 0.9)
695 				{
696 					width = a-1;
697 					break;
698 				}
699 
700 				if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > m_WaterHeight)
701 					sign = -1;
702 
703 				avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - m_WaterHeight;
704 
705 				float localOutmost = -2.0f;
706 				while (localOutmost < 0.0f)
707 				{
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;
713 				}
714 
715 				outmost += localOutmost;
716 			}
717 			if (width < 5)
718 			{
719 				j += 6;
720 				continue;
721 			}
722 
723 			outmost /= width;
724 
725 			if (outmost > -0.5f)
726 			{
727 				j += 3;
728 				continue;
729 			}
730 			outmost = -0.5f + outmost * m_Waviness/10.0f;
731 
732 			avgDepth /= width;
733 
734 			if (avgDepth > -1.3f)
735 			{
736 				j += 3;
737 				continue;
738 			}
739 			// we passed the checks, we can create a wave of size "width".
740 
741 			WaveObject* shoreWave = new WaveObject;
742 			std::vector<SWavesVertex> vertices;
743 
744 			shoreWave->m_Width = width;
745 			shoreWave->m_TimeDiff = diff;
746 			diff += (rand() % 100) / 25.0f + 4.0f;
747 
748 			for (size_t a = 0; a < width;++a)
749 			{
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)
756 				{
757 					++nb;
758 					posMinus = CoastalPointsChains[i][j+a-1].position;
759 					perp += pos-posMinus;
760 				}
761 				if (a < waveSizes-1)
762 				{
763 					++nb;
764 					posPlus = CoastalPointsChains[i][j+a+1].position;
765 					perp += posPlus-pos;
766 				}
767 				perp /= nb;
768 				perp = CVector2D(-perp.Y,perp.X).Normalized();
769 
770 				SWavesVertex point[9];
771 
772 				float baseHeight = 0.04f;
773 
774 				float halfWidth = (width-1.0f)/2.0f;
775 				float sideNess = sqrtf(clamp( (halfWidth - fabsf(a-halfWidth))/3.0f, 0.0f,1.0f));
776 
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;
786 
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;
796 
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 };
801 
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 };
805 
806 				for (size_t t = 0; t < 9; ++t)
807 				{
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));
812 				}
813 				for (size_t t = 0; t < 9; ++t)
814 				{
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));
819 				}
820 				for (size_t t = 0; t < 9; ++t)
821 				{
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));
825 				}
826 				for (size_t t = 0; t < 9; ++t)
827 				{
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));
832 				}
833 
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]);
843 
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;
849 			}
850 
851 			if (sign == 1)
852 			{
853 				// Let's do some fancy reversing.
854 				std::vector<SWavesVertex> reversed;
855 				for (int a = width-1; a >= 0; --a)
856 				{
857 					for (size_t t = 0; t < 9; ++t)
858 						reversed.push_back(vertices[a*9+t]);
859 				}
860 				vertices = reversed;
861 			}
862 			j += width/2-1;
863 
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]);
866 
867 			m_ShoreWaves.push_back(shoreWave);
868 		}
869 	}
870 }
871 
RenderWaves(const CFrustum & frustrum)872 void WaterManager::RenderWaves(const CFrustum& frustrum)
873 {
874 #if CONFIG2_GLES
875 #warning Fix WaterManager::RenderWaves on GLES
876 #else
877 	if (g_Renderer.m_SkipSubmit || !m_WaterFancyEffects)
878 		return;
879 
880 	pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO);
881 
882 	GLuint attachments[2] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT };
883 	pglDrawBuffers(2, attachments);
884 
885 	glClearColor(0.0f,0.0f, 0.0f,0.0f);
886 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
887 
888 	glEnable(GL_BLEND);
889 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
890 	glEnable(GL_DEPTH_TEST);
891 	glDepthFunc(GL_ALWAYS);
892 
893 	CShaderDefines none;
894 	CShaderProgramPtr shad = g_Renderer.GetShaderManager().LoadProgram("glsl/waves", none);
895 
896 	shad->Bind();
897 
898 	shad->BindTexture(str_waveTex, m_WaveTex);
899 	shad->BindTexture(str_foamTex, m_FoamTex);
900 
901 	shad->Uniform(str_time, (float)m_WaterTexTimer);
902 	shad->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
903 
904 	for (size_t a = 0; a < m_ShoreWaves.size(); ++a)
905 	{
906 		if (!frustrum.IsBoxVisible(m_ShoreWaves[a]->m_AABB))
907 			continue;
908 
909 		CVertexBuffer::VBChunk* VBchunk = m_ShoreWaves[a]->m_VBvertices;
910 		SWavesVertex* base = (SWavesVertex*)VBchunk->m_Owner->Bind();
911 
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);
921 
922 		shad->AssertPointersBound();
923 
924 		shad->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff);
925 		shad->Uniform(str_width, (int)m_ShoreWaves[a]->m_Width);
926 
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));
930 
931 		shad->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff + 6.0f);
932 
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;
936 
937 		CVertexBuffer::Unbind();
938 	}
939 	shad->Unbind();
940 	pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
941 
942 	glDisable(GL_BLEND);
943 	glDepthFunc(GL_LEQUAL);
944 #endif
945 }
946 
947 ///////////////////////////////////////////////////////////////////
948 // Calculate The blurred normal map to get an idea of where water ought to go.
RecomputeBlurredNormalMap()949 void WaterManager::RecomputeBlurredNormalMap()
950 {
951 	CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
952 	if (!terrain || !terrain->GetHeightMap())
953 		return;
954 
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];
959 
960 	// It's really slow to calculate normals so cache them first.
961 	CVector3D* normals = new CVector3D[m_MapSize*m_MapSize];
962 
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)
967 		{
968 			CVector3D norm;
969 			terrain->CalcNormal(i,j,norm);
970 			normals[j*m_MapSize + i] = norm;
971 		}
972 
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)
980 		{
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;
987 		}
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)
991 	{
992 		for (size_t j = 2; j < m_MapSize-2; ++j, jj += 4.0f)
993 		{
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;
1000 		}
1001 	}
1002 
1003 	delete[] normals;
1004 }
1005 
RecomputeWaterData()1006 void WaterManager::RecomputeWaterData()
1007 {
1008 	if (!m_MapSize)
1009 		return;
1010 
1011 	RecomputeBlurredNormalMap();
1012 	RecomputeDistanceHeightmap();
1013 	RecomputeWindStrength();
1014 	CreateWaveMeshes();
1015 }
1016 
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.
RecomputeWindStrength()1020 void WaterManager::RecomputeWindStrength()
1021 {
1022 	if (m_WindStrength == NULL)
1023 		m_WindStrength = new float[m_MapSize*m_MapSize];
1024 
1025 	CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
1026 	if (!terrain || !terrain->GetHeightMap())
1027 		return;
1028 
1029 	float waterLevel = m_WaterHeight;
1030 
1031 	CVector2D windDir = CVector2D(cos(m_WindAngle),sin(m_WindAngle));
1032 	CVector2D perp = CVector2D(-windDir.Y, windDir.X);
1033 
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} };
1036 
1037 	float* Temp = new float[m_MapSize*m_MapSize];
1038 	std::fill(Temp, Temp + m_MapSize*m_MapSize, 1.0f);
1039 
1040 	for (size_t j = 0; j < m_MapSize; ++j)
1041 		for (size_t i = 0; i < m_MapSize; ++i)
1042 		{
1043 			float curHeight = terrain->GetVertexGroundLevel(i,j);
1044 			if (curHeight >= waterLevel)
1045 			{
1046 				Temp[j*m_MapSize + i] = 0.3f;	// blurs too strong otherwise
1047 				continue;
1048 			}
1049 			if (terrain->GetVertexGroundLevel(i + ceil(windDir.X),j + ceil(windDir.Y)) < waterLevel)
1050 				continue;
1051 
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;
1069 
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);
1075 
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)
1079 				{
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;
1084 				}
1085 		}
1086 
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)
1091 		{
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;
1097 		}
1098 	delete[] Temp;
1099 }
1100 
1101 ////////////////////////////////////////////////////////////////////////
1102 // TODO: This will always recalculate for now
SetMapSize(size_t size)1103 void WaterManager::SetMapSize(size_t size)
1104 {
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;
1112 
1113 	SAFE_ARRAY_DELETE(m_DistanceHeightmap);
1114 	SAFE_ARRAY_DELETE(m_BlurredNormalMap);
1115 	SAFE_ARRAY_DELETE(m_WindStrength);
1116 }
1117 
1118 ////////////////////////////////////////////////////////////////////////
1119 // This will set the bools properly
UpdateQuality()1120 void WaterManager::UpdateQuality()
1121 {
1122 	if (g_Renderer.GetOptionBool(CRenderer::OPT_WATEREFFECTS) != m_WaterEffects)
1123 	{
1124 		m_WaterEffects = g_Renderer.GetOptionBool(CRenderer::OPT_WATEREFFECTS);
1125 		m_NeedsReloading = true;
1126 	}
1127 	if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERFANCYEFFECTS) != m_WaterFancyEffects) {
1128 		m_WaterFancyEffects = g_Renderer.GetOptionBool(CRenderer::OPT_WATERFANCYEFFECTS);
1129 		m_NeedsReloading = true;
1130 	}
1131 	if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREALDEPTH) != m_WaterRealDepth) {
1132 		m_WaterRealDepth = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREALDEPTH);
1133 		m_NeedsReloading = true;
1134 	}
1135 	if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION) != m_WaterRefraction) {
1136 		m_WaterRefraction = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION);
1137 		m_NeedsReloading = true;
1138 	}
1139 	if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFLECTION) != m_WaterReflection) {
1140 		m_WaterReflection = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFLECTION);
1141 		m_NeedsReloading = true;
1142 	}
1143 	if (g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWSONWATER) != m_WaterShadows) {
1144 		m_WaterShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWSONWATER);
1145 		m_NeedsReloading = true;
1146 	}
1147 }
1148 
WillRenderFancyWater()1149 bool WaterManager::WillRenderFancyWater()
1150 {
1151 	return m_RenderWater && m_WaterEffects && g_Renderer.GetCapabilities().m_PrettyWater;
1152 }
1153