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"
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"
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
54 CoastalPoint(int idx
, CVector2D pos
) : index(idx
), position(pos
) {};
61 CVector3D m_BasePosition
;
62 CVector3D m_ApexPosition
;
63 CVector3D m_SplashPosition
;
64 CVector3D m_RetreatPosition
;
69 // pad to a power of two
72 cassert(sizeof(SWavesVertex
) == 64);
76 CVertexBuffer::VBChunk
* m_VBvertices
;
77 CBoundingBoxAligned m_AABB
;
82 ///////////////////////////////////////////////////////////////////
83 // Construction/Destruction
84 WaterManager::WaterManager()
87 m_RenderWater
= false; // disabled until textures are successfully loaded
90 m_WaterCurrentTex
= 0;
92 m_ReflectionTexture
= 0;
93 m_RefractionTexture
= 0;
98 m_FancyEffectsFBO
= 0;
100 m_WaterTexTimer
= 0.0;
104 m_WaterColor
= CColor(0.3f
, 0.35f
, 0.7f
, 1.0f
);
105 m_WaterTint
= CColor(0.28f
, 0.3f
, 0.59f
, 1.0f
);
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;
127 m_FancyTextureNormal
= 0;
128 m_FancyTextureOther
= 0;
129 m_FancyTextureDepth
= 0;
130 m_ReflFboDepthTexture
= 0;
131 m_RefrFboDepthTexture
= 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
);
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
)
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
);
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;
205 #warning Fix WaterManager::LoadWaterTextures on GLES
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
);
217 m_NormalMap
[i
] = texture
;
222 CTextureProperties
textureProps(L
"art/textures/terrain/types/water/coastalWave.png");
223 textureProps
.SetWrap(GL_REPEAT
);
224 CTexturePtr texture
= g_Renderer
.GetTextureManager().CreateTexture(textureProps
);
231 CTextureProperties
textureProps(L
"art/textures/terrain/types/water/foam.png");
232 textureProps
.SetWrap(GL_REPEAT
);
233 CTexturePtr texture
= g_Renderer
.GetTextureManager().CreateTexture(textureProps
);
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);
304 // Create the water framebuffers
307 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT
, ¤tFbo
);
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);
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;
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);
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);
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;
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
);
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
)
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())
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
;
438 for (size_t x
= SideSize
-1; x
!= (size_t)-1; --x
)
440 if (terrain
->GetExactGroundLevel(x
*2, z
*2) >= m_WaterHeight
)
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
)
457 else if (level
> m_DistanceHeightmap
[z
*SideSize
+ x
])
458 level
= m_DistanceHeightmap
[z
*SideSize
+ x
];
462 if (level
< m_DistanceHeightmap
[z
*SideSize
+ x
])
463 m_DistanceHeightmap
[z
*SideSize
+ x
] = level
;
467 for (size_t z
= SideSize
-1; z
!= (size_t)-1; --z
)
469 if (terrain
->GetExactGroundLevel(x
*2, z
*2) >= m_WaterHeight
)
471 else if (level
> m_DistanceHeightmap
[z
*SideSize
+ x
])
472 level
= m_DistanceHeightmap
[z
*SideSize
+ x
];
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())
491 for (WaveObject
* const& obj
: m_ShoreWaves
)
493 if (obj
->m_VBvertices
)
494 g_VBMan
.Release(obj
->m_VBvertices
);
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")
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)));
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 };
538 for (int i
= 0; i
< 8; ++i
)
540 if (CoastalPointsSet
.count(x
+ around
[i
][0] + (y
+ around
[i
][1])*SideSize
))
543 neighbours
[nbNeighb
] = x
+ around
[i
][0] + (y
+ around
[i
][1])*SideSize
;
550 for (int i
= 0; i
< 2; ++i
)
552 if (neighbours
[i
] == -1)
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;
561 Chain
.push_back(CoastalPoint(indexx
,CVector2D(xx
*2,yy
*2)));
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)
570 CoastalPointsSet
.erase(indexx
);
572 // Start checking from there.
577 for (int p
= 0; p
< 8; ++p
)
579 if (CoastalPointsSet
.count(xx
+around
[p
][0] + (yy
+ around
[p
][1])*SideSize
))
583 CoastalPointsSet
.erase(xx
+ yy
*SideSize
);
587 // We've found a new point around us.
589 xx
= xx
+ around
[p
][0];
590 yy
= yy
+ around
[p
][1];
591 indexx
= xx
+ yy
*SideSize
;
593 Chain
.push_back(CoastalPoint(indexx
,CVector2D(xx
*2,yy
*2)));
595 Chain
.push_front(CoastalPoint(indexx
,CVector2D(xx
*2,yy
*2)));
596 CoastalPointsSet
.erase(xx
+ yy
*SideSize
);
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
)
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
;
661 CVector2D
firstPerp(0,0), perp(0,0), lastPerp(0,0);
662 for (size_t a
= 0; a
< waveSizes
;++a
)
665 perp
= CVector2D(0,0);
667 CVector2D pos
= CoastalPointsChains
[i
][j
+a
].position
;
673 posMinus
= CoastalPointsChains
[i
][j
+a
-1].position
;
674 perp
+= pos
-posMinus
;
679 posPlus
= CoastalPointsChains
[i
][j
+a
+1].position
;
683 perp
= CVector2D(-perp
.Y
,perp
.X
).Normalized();
688 if ( a
> 1 && perp
.Dot(lastPerp
) < 0.90f
&& perp
.Dot(firstPerp
) < 0.70f
)
694 if (m_BlurredNormalMap
[ (int)(pos
.X
/4) + (int)(pos
.Y
/4)*m_MapSize
].Y
< 0.9)
700 if (terrain
->GetExactGroundLevel(pos
.X
+perp
.X
*1.5f
, pos
.Y
+perp
.Y
*1.5f
) > m_WaterHeight
)
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
;
715 outmost
+= localOutmost
;
730 outmost
= -0.5f
+ outmost
* m_Waviness
/10.0f
;
734 if (avgDepth
> -1.3f
)
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);
752 CVector2D pos
= CoastalPointsChains
[i
][j
+a
].position
;
758 posMinus
= CoastalPointsChains
[i
][j
+a
-1].position
;
759 perp
+= pos
-posMinus
;
764 posPlus
= CoastalPointsChains
[i
][j
+a
+1].position
;
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
;
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
]);
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
)
875 #warning Fix WaterManager::RenderWaves on GLES
877 if (g_Renderer
.m_SkipSubmit
|| !m_WaterFancyEffects
)
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
);
889 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
890 glEnable(GL_DEPTH_TEST
);
891 glDepthFunc(GL_ALWAYS
);
894 CShaderProgramPtr shad
= g_Renderer
.GetShaderManager().LoadProgram("glsl/waves", none
);
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
))
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();
940 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, 0);
943 glDepthFunc(GL_LEQUAL
);
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())
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
)
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.
976 ii
= 8.0f
; jj
= 8.0f
;
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
;
1006 void WaterManager::RecomputeWaterData()
1011 RecomputeBlurredNormalMap();
1012 RecomputeDistanceHeightmap();
1013 RecomputeWindStrength();
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())
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
1049 if (terrain
->GetVertexGroundLevel(i
+ ceil(windDir
.X
),j
+ ceil(windDir
.Y
)) < waterLevel
)
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
;
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
)} };
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
;
1101 ////////////////////////////////////////////////////////////////////////
1102 // TODO: This will always recalculate for now
1103 void WaterManager::SetMapSize(size_t size
)
1105 // TODO: Im' blindly trusting the user here.
1107 m_NeedInfoUpdate
= true;
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
;