Changeset View
Changeset View
Standalone View
Standalone View
source/renderer/WaterRendering.cpp
- This file was moved from source/renderer/WaterManager.cpp.
Show All 15 Lines | |||||
*/ | */ | ||||
/* | /* | ||||
* Water settings (speed, height) and texture management | * Water settings (speed, height) and texture management | ||||
*/ | */ | ||||
#include "precompiled.h" | #include "precompiled.h" | ||||
#include "renderer/WaterRendering.h" | |||||
#include "graphics/Terrain.h" | #include "graphics/Terrain.h" | ||||
#include "graphics/TextureManager.h" | #include "graphics/TextureManager.h" | ||||
#include "graphics/ShaderManager.h" | #include "graphics/ShaderManager.h" | ||||
#include "graphics/ShaderProgram.h" | #include "graphics/WaterManager.h" | ||||
#include "lib/bits.h" | #include "lib/bits.h" | ||||
#include "lib/timer.h" | #include "maths/BoundingBoxAligned.h" | ||||
#include "lib/tex/tex.h" | |||||
#include "lib/res/graphics/ogl_tex.h" | |||||
#include "maths/MathUtil.h" | #include "maths/MathUtil.h" | ||||
#include "maths/Vector2D.h" | #include "maths/Vector2D.h" | ||||
#include "ps/CLogger.h" | |||||
#include "ps/Game.h" | #include "ps/Game.h" | ||||
#include "ps/World.h" | #include "ps/World.h" | ||||
#include "renderer/WaterManager.h" | |||||
#include "renderer/Renderer.h" | |||||
#include "renderer/RenderingOptions.h" | #include "renderer/RenderingOptions.h" | ||||
#include "simulation2/Simulation2.h" | #include "renderer/Renderer.h" | ||||
#include "simulation2/components/ICmpWaterManager.h" | #include "renderer/TimeManager.h" | ||||
#include "simulation2/components/ICmpRangeManager.h" | |||||
struct CoastalPoint | struct CoastalPoint | ||||
{ | { | ||||
CoastalPoint(int idx, CVector2D pos) : index(idx), position(pos) {}; | CoastalPoint(int idx, CVector2D pos) : index(idx), position(pos) {}; | ||||
int index; | int index; | ||||
CVector2D position; | CVector2D position; | ||||
}; | }; | ||||
Show All 15 Lines | |||||
struct WaveObject | struct WaveObject | ||||
{ | { | ||||
CVertexBuffer::VBChunk* m_VBvertices; | CVertexBuffer::VBChunk* m_VBvertices; | ||||
CBoundingBoxAligned m_AABB; | CBoundingBoxAligned m_AABB; | ||||
size_t m_Width; | size_t m_Width; | ||||
float m_TimeDiff; | float m_TimeDiff; | ||||
}; | }; | ||||
WaterManager::WaterManager() | WaterRendering::WaterRendering() | ||||
{ | { | ||||
// water | ENSURE(CRenderer::IsInitialised()); | ||||
m_RenderWater = false; // disabled until textures are successfully loaded | |||||
m_WaterHeight = 5.0f; | |||||
m_WaterCurrentTex = 0; | |||||
m_ReflectionTexture = 0; | |||||
m_RefractionTexture = 0; | |||||
m_RefTextureSize = 0; | |||||
m_ReflectionFbo = 0; | |||||
m_RefractionFbo = 0; | |||||
m_FancyEffectsFBO = 0; | |||||
m_WaterTexTimer = 0.0; | |||||
m_WindAngle = 0.0f; | |||||
m_Waviness = 8.0f; | |||||
m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f); | |||||
m_WaterTint = CColor(0.28f, 0.3f, 0.59f, 1.0f); | |||||
m_Murkiness = 0.45f; | |||||
m_RepeatPeriod = 16.0f; | |||||
m_DistanceHeightmap = NULL; | |||||
m_BlurredNormalMap = NULL; | |||||
m_WindStrength = NULL; | |||||
m_ShoreWaves_VBIndices = NULL; | |||||
m_WaterEffects = true; | |||||
m_WaterFancyEffects = false; | |||||
m_WaterRealDepth = false; | |||||
m_WaterRefraction = false; | |||||
m_WaterReflection = false; | |||||
m_WaterType = L"ocean"; | |||||
m_NeedsReloading = false; | |||||
m_NeedInfoUpdate = true; | |||||
m_FancyTexture = 0; | |||||
m_FancyTextureDepth = 0; | |||||
m_ReflFboDepthTexture = 0; | |||||
m_RefrFboDepthTexture = 0; | |||||
m_MapSize = 0; | |||||
m_updatei0 = 0; | |||||
m_updatej0 = 0; | |||||
m_updatei1 = 0; | |||||
m_updatej1 = 0; | |||||
} | } | ||||
WaterManager::~WaterManager() | WaterRendering::~WaterRendering() | ||||
{ | { | ||||
// Cleanup if the caller messed up | UnprepareTextures(); | ||||
UnloadWaterTextures(); | |||||
for (WaveObject* const& obj : m_ShoreWaves) | for (WaveObject* const& obj : m_ShoreWaves) | ||||
{ | { | ||||
if (obj->m_VBvertices) | if (obj->m_VBvertices) | ||||
g_VBMan.Release(obj->m_VBvertices); | g_VBMan.Release(obj->m_VBvertices); | ||||
delete obj; | delete obj; | ||||
} | } | ||||
m_ShoreWaves.clear(); | |||||
if (m_ShoreWaves_VBIndices) | if (m_ShoreWaves_VBIndices) | ||||
{ | |||||
g_VBMan.Release(m_ShoreWaves_VBIndices); | g_VBMan.Release(m_ShoreWaves_VBIndices); | ||||
m_ShoreWaves_VBIndices = NULL; | |||||
delete[] m_DistanceHeightmap; | } | ||||
delete[] m_BlurredNormalMap; | |||||
delete[] m_WindStrength; | |||||
if (!g_Renderer.GetCapabilities().m_PrettyWater) | |||||
return; | |||||
glDeleteTextures(1, &m_FancyTexture); | |||||
glDeleteTextures(1, &m_FancyTextureDepth); | |||||
glDeleteTextures(1, &m_ReflFboDepthTexture); | |||||
glDeleteTextures(1, &m_RefrFboDepthTexture); | |||||
pglDeleteFramebuffersEXT(1, &m_FancyEffectsFBO); | |||||
pglDeleteFramebuffersEXT(1, &m_RefractionFbo); | |||||
pglDeleteFramebuffersEXT(1, &m_ReflectionFbo); | |||||
} | } | ||||
void WaterRendering::PrepareTextures() | |||||
/////////////////////////////////////////////////////////////////// | |||||
// Progressive load of water textures | |||||
int WaterManager::LoadWaterTextures() | |||||
{ | { | ||||
// TODO: this doesn't need to be progressive-loading any more | UnprepareTextures(); | ||||
// (since texture loading is async now) | |||||
wchar_t pathname[PATH_MAX]; | wchar_t pathname[PATH_MAX]; | ||||
// Load diffuse grayscale images (for non-fancy water) | // Load diffuse grayscale images (for non-fancy water) | ||||
for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); ++i) | for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); ++i) | ||||
{ | { | ||||
swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/default/diffuse%02d.dds", (int)i+1); | swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/default/diffuse%02d.dds", (int)i+1); | ||||
CTextureProperties textureProps(pathname); | CTextureProperties textureProps(pathname); | ||||
textureProps.SetWrap(GL_REPEAT); | textureProps.SetWrap(GL_REPEAT); | ||||
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); | CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); | ||||
texture->Prefetch(); | texture->Prefetch(); | ||||
m_WaterTexture[i] = texture; | m_WaterTexture[i] = texture; | ||||
} | } | ||||
if (!g_Renderer.GetCapabilities().m_PrettyWater) | |||||
{ | |||||
// Enable rendering, now that we've succeeded this far | |||||
m_RenderWater = true; | |||||
return 0; | |||||
} | |||||
#if CONFIG2_GLES | |||||
#warning Fix WaterManager::LoadWaterTextures on GLES | |||||
#else | |||||
// Load normalmaps (for fancy water) | |||||
ReloadWaterNormalTextures(); | |||||
// Load CoastalWaves | // Load CoastalWaves | ||||
{ | { | ||||
CTextureProperties textureProps(L"art/textures/terrain/types/water/coastalWave.png"); | CTextureProperties textureProps(L"art/textures/terrain/types/water/coastalWave.png"); | ||||
textureProps.SetWrap(GL_REPEAT); | textureProps.SetWrap(GL_REPEAT); | ||||
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); | CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); | ||||
texture->Prefetch(); | texture->Prefetch(); | ||||
m_WaveTex = texture; | m_WaveTex = texture; | ||||
} | } | ||||
// Load Foam | // Load Foam | ||||
{ | { | ||||
CTextureProperties textureProps(L"art/textures/terrain/types/water/foam.png"); | CTextureProperties textureProps(L"art/textures/terrain/types/water/foam.png"); | ||||
textureProps.SetWrap(GL_REPEAT); | textureProps.SetWrap(GL_REPEAT); | ||||
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); | CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); | ||||
texture->Prefetch(); | texture->Prefetch(); | ||||
m_FoamTex = texture; | m_FoamTex = texture; | ||||
} | } | ||||
// Use screen-sized textures for minimum artifacts. | // Use screen-sized textures for minimum artifacts. | ||||
m_RefTextureSize = g_Renderer.GetHeight(); | m_RefTextureSize = g_Renderer.GetHeight(); | ||||
m_RefTextureSize = round_up_to_pow2(m_RefTextureSize); | m_RefTextureSize = round_up_to_pow2(m_RefTextureSize); | ||||
// Create reflection texture | // Create reflection texture | ||||
glGenTextures(1, &m_ReflectionTexture); | glGenTextures(1, &m_ReflectionTexture); | ||||
glBindTexture(GL_TEXTURE_2D, m_ReflectionTexture); | glBindTexture(GL_TEXTURE_2D, m_ReflectionTexture); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); | ||||
Show All 28 Lines | void WaterRendering::PrepareTextures() | ||||
// Create the Fancy Effects texture | // Create the Fancy Effects texture | ||||
glGenTextures(1, &m_FancyTexture); | glGenTextures(1, &m_FancyTexture); | ||||
glBindTexture(GL_TEXTURE_2D, m_FancyTexture); | glBindTexture(GL_TEXTURE_2D, m_FancyTexture); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); | ||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_SHORT, NULL); | |||||
glGenTextures(1, &m_FancyTextureDepth); | glGenTextures(1, &m_FancyTextureDepth); | ||||
glBindTexture(GL_TEXTURE_2D, m_FancyTextureDepth); | glBindTexture(GL_TEXTURE_2D, m_FancyTextureDepth); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | ||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); | ||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL); | |||||
glBindTexture(GL_TEXTURE_2D, 0); | glBindTexture(GL_TEXTURE_2D, 0); | ||||
Resize(); | |||||
// Create the water framebuffers | // Create the water framebuffers | ||||
GLint currentFbo; | GLint currentFbo; | ||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, ¤tFbo); | glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, ¤tFbo); | ||||
m_ReflectionFbo = 0; | m_ReflectionFbo = 0; | ||||
pglGenFramebuffersEXT(1, &m_ReflectionFbo); | pglGenFramebuffersEXT(1, &m_ReflectionFbo); | ||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_ReflectionFbo); | pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_ReflectionFbo); | ||||
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_ReflectionTexture, 0); | pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_ReflectionTexture, 0); | ||||
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_ReflFboDepthTexture, 0); | pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_ReflFboDepthTexture, 0); | ||||
ogl_WarnIfError(); | ogl_WarnIfError(); | ||||
GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); | GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); | ||||
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) | if (status != GL_FRAMEBUFFER_COMPLETE_EXT) | ||||
{ | { | ||||
LOGWARNING("Reflection framebuffer object incomplete: 0x%04X", status); | LOGWARNING("Reflection framebuffer object incomplete: 0x%04X", status); | ||||
g_RenderingOptions.SetWaterReflection(false); | g_RenderingOptions.SetWaterReflection(false); | ||||
UpdateQuality(); | m_ReflectionFbo = 0; | ||||
} | } | ||||
m_RefractionFbo = 0; | m_RefractionFbo = 0; | ||||
pglGenFramebuffersEXT(1, &m_RefractionFbo); | pglGenFramebuffersEXT(1, &m_RefractionFbo); | ||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_RefractionFbo); | pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_RefractionFbo); | ||||
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_RefractionTexture, 0); | pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_RefractionTexture, 0); | ||||
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_RefrFboDepthTexture, 0); | pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_RefrFboDepthTexture, 0); | ||||
ogl_WarnIfError(); | ogl_WarnIfError(); | ||||
status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); | status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); | ||||
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) | if (status != GL_FRAMEBUFFER_COMPLETE_EXT) | ||||
{ | { | ||||
LOGWARNING("Refraction framebuffer object incomplete: 0x%04X", status); | LOGWARNING("Refraction framebuffer object incomplete: 0x%04X", status); | ||||
g_RenderingOptions.SetWaterRefraction(false); | g_RenderingOptions.SetWaterRefraction(false); | ||||
UpdateQuality(); | m_RefractionFbo = 0; | ||||
} | } | ||||
m_FancyEffectsFBO = 0; | |||||
pglGenFramebuffersEXT(1, &m_FancyEffectsFBO); | pglGenFramebuffersEXT(1, &m_FancyEffectsFBO); | ||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO); | pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO); | ||||
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_FancyTexture, 0); | pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_FancyTexture, 0); | ||||
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_FancyTextureDepth, 0); | pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_FancyTextureDepth, 0); | ||||
ogl_WarnIfError(); | ogl_WarnIfError(); | ||||
status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); | status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); | ||||
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) | if (status != GL_FRAMEBUFFER_COMPLETE_EXT) | ||||
{ | { | ||||
LOGWARNING("Fancy Effects framebuffer object incomplete: 0x%04X", status); | LOGWARNING("Fancy Effects framebuffer object incomplete: 0x%04X", status); | ||||
g_RenderingOptions.SetWaterRefraction(false); | g_RenderingOptions.SetWaterRefraction(false); | ||||
UpdateQuality(); | m_FancyEffectsFBO = 0; | ||||
} | } | ||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentFbo); | pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentFbo); | ||||
// Enable rendering, now that we've succeeded this far | |||||
m_RenderWater = true; | |||||
#endif | |||||
return 0; | |||||
} | } | ||||
void WaterRendering::UnprepareTextures() | |||||
/////////////////////////////////////////////////////////////////// | |||||
// Resize: Updates the fancy water textures. | |||||
void WaterManager::Resize() | |||||
{ | { | ||||
glBindTexture(GL_TEXTURE_2D, m_FancyTexture); | for(size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++) | ||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_SHORT, NULL); | m_WaterTexture[i].reset(); | ||||
glBindTexture(GL_TEXTURE_2D, m_FancyTextureDepth); | for(size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++) | ||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL); | m_NormalMap[i].reset(); | ||||
glBindTexture(GL_TEXTURE_2D, 0); | m_WaveTex.reset(); | ||||
m_FoamTex.reset(); | |||||
glDeleteTextures(1, &m_ReflectionTexture); | |||||
glDeleteTextures(1, &m_RefractionTexture); | |||||
glDeleteTextures(1, &m_ReflFboDepthTexture); | |||||
glDeleteTextures(1, &m_RefrFboDepthTexture); | |||||
glDeleteTextures(1, &m_FancyTexture); | |||||
glDeleteTextures(1, &m_FancyTextureDepth); | |||||
} | } | ||||
void WaterManager::ReloadWaterNormalTextures() | void WaterRendering::LoadNormalTextures(const std::wstring &type) | ||||
{ | { | ||||
wchar_t pathname[PATH_MAX]; | wchar_t pathname[PATH_MAX]; | ||||
// Load normalmaps | |||||
for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i) | for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i) | ||||
{ | { | ||||
swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", m_WaterType.c_str(), static_cast<int>(i) + 1); | swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", type.c_str(), static_cast<int>(i) + 1); | ||||
CTextureProperties textureProps(pathname); | CTextureProperties textureProps(pathname); | ||||
textureProps.SetWrap(GL_REPEAT); | textureProps.SetWrap(GL_REPEAT); | ||||
textureProps.SetMaxAnisotropy(4); | textureProps.SetMaxAnisotropy(4); | ||||
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); | CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); | ||||
texture->Prefetch(); | texture->Prefetch(); | ||||
m_NormalMap[i] = texture; | m_NormalMap[i] = texture; | ||||
} | } | ||||
} | } | ||||
/////////////////////////////////////////////////////////////////// | void WaterRendering::OnRendererResize() | ||||
// Unload water textures | |||||
void WaterManager::UnloadWaterTextures() | |||||
{ | { | ||||
for(size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++) | if (!m_ReflectionTexture) | ||||
m_WaterTexture[i].reset(); | |||||
if (!g_Renderer.GetCapabilities().m_PrettyWater) | |||||
return; | return; | ||||
for(size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++) | // Use screen-sized textures for minimum artifacts. | ||||
m_NormalMap[i].reset(); | m_RefTextureSize = g_Renderer.GetHeight(); | ||||
m_RefTextureSize = round_up_to_pow2(m_RefTextureSize); | |||||
glDeleteTextures(1, &m_ReflectionTexture); | |||||
glDeleteTextures(1, &m_RefractionTexture); | |||||
pglDeleteFramebuffersEXT(1, &m_RefractionFbo); | |||||
pglDeleteFramebuffersEXT(1, &m_ReflectionFbo); | |||||
} | |||||
template<bool Transpose> | glBindTexture(GL_TEXTURE_2D, m_ReflectionTexture); | ||||
static inline void ComputeDirection(float* distanceMap, const u16* heightmap, float waterHeight, size_t SideSize, size_t maxLevel) | glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); | ||||
{ | |||||
#define ABOVEWATER(x, z) (HEIGHT_SCALE * heightmap[z*SideSize + x] >= waterHeight) | |||||
#define UPDATELOOKAHEAD \ | |||||
for (; lookahead <= id2+maxLevel && lookahead < SideSize && \ | |||||
((!Transpose && !ABOVEWATER(lookahead, id1)) || (Transpose && !ABOVEWATER(id1, lookahead))); ++lookahead) | |||||
// Algorithm: | |||||
// We want to know the distance to the closest shore point. Go through each line/column, | |||||
// keep track of when we encountered the last shore point and how far ahead the next one is. | |||||
for (size_t id1 = 0; id1 < SideSize; ++id1) | |||||
{ | |||||
size_t id2 = 0; | |||||
const size_t& x = Transpose ? id1 : id2; | |||||
const size_t& z = Transpose ? id2 : id1; | |||||
size_t level = ABOVEWATER(x, z) ? 0 : maxLevel; | glBindTexture(GL_TEXTURE_2D, m_RefractionTexture); | ||||
size_t lookahead = (size_t)(level > 0); | glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); | ||||
UPDATELOOKAHEAD; | glBindTexture(GL_TEXTURE_2D, m_ReflFboDepthTexture); | ||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL); | |||||
// start moving | glBindTexture(GL_TEXTURE_2D, m_RefrFboDepthTexture); | ||||
for (; id2 < SideSize; ++id2) | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL); | ||||
{ | |||||
// update current level | |||||
if (ABOVEWATER(x, z)) | |||||
level = 0; | |||||
else | |||||
level = std::min(level+1, maxLevel); | |||||
// move lookahead | glBindTexture(GL_TEXTURE_2D, m_FancyTexture); | ||||
if (lookahead == id2) | glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_SHORT, NULL); | ||||
++lookahead; | |||||
UPDATELOOKAHEAD; | |||||
// This is the important bit: set the distance to either: | glBindTexture(GL_TEXTURE_2D, m_FancyTextureDepth); | ||||
// - the distance to the previous shore point (level) | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL); | ||||
// - the distance to the next shore point (lookahead-id2) | |||||
distanceMap[z*SideSize + x] = std::min(distanceMap[z*SideSize + x], (float)std::min(lookahead-id2, level)); | |||||
} | |||||
} | |||||
#undef ABOVEWATER | |||||
#undef UPDATELOOKAHEAD | |||||
} | } | ||||
/////////////////////////////////////////////////////////////////// | CTexturePtr WaterRendering::GetTexture(float time, int advance) const | ||||
// Calculate our binary heightmap from the terrain heightmap. | |||||
void WaterManager::RecomputeDistanceHeightmap() | |||||
{ | { | ||||
CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); | if (g_RenderingOptions.GetWaterEffects()) | ||||
if (!terrain || !terrain->GetHeightMap()) | |||||
return; | |||||
size_t SideSize = m_MapSize; | |||||
// we want to look ahead some distance, but not too much (less efficient and not interesting). This is our lookahead. | |||||
const size_t maxLevel = 5; | |||||
if (m_DistanceHeightmap == NULL) | |||||
{ | { | ||||
m_DistanceHeightmap = new float[SideSize*SideSize]; | double period = 8; | ||||
std::fill(m_DistanceHeightmap, m_DistanceHeightmap + SideSize*SideSize, (float)maxLevel); | return m_NormalMap[((int)(time*60/period) + advance) % 60]; | ||||
} | } | ||||
double period = 1.6f; | |||||
// Create a manhattan-distance heightmap. | return m_WaterTexture[((int)(time*60/period) + advance) % 60]; | ||||
// This could be refined to only be done near the coast itself, but it's probably not necessary. | |||||
u16* heightmap = terrain->GetHeightMap(); | |||||
ComputeDirection<false>(m_DistanceHeightmap, heightmap, m_WaterHeight, SideSize, maxLevel); | |||||
ComputeDirection<true>(m_DistanceHeightmap, heightmap, m_WaterHeight, SideSize, maxLevel); | |||||
} | } | ||||
// This requires m_DistanceHeightmap to be defined properly. | void WaterRendering::CreateWaveMeshes() | ||||
void WaterManager::CreateWaveMeshes() | |||||
{ | { | ||||
if (m_MapSize == 0) | // Cleanup | ||||
return; | |||||
CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); | |||||
if (!terrain || !terrain->GetHeightMap()) | |||||
return; | |||||
for (WaveObject* const& obj : m_ShoreWaves) | for (WaveObject* const& obj : m_ShoreWaves) | ||||
{ | { | ||||
if (obj->m_VBvertices) | if (obj->m_VBvertices) | ||||
g_VBMan.Release(obj->m_VBvertices); | g_VBMan.Release(obj->m_VBvertices); | ||||
delete obj; | delete obj; | ||||
} | } | ||||
m_ShoreWaves.clear(); | m_ShoreWaves.clear(); | ||||
if (m_ShoreWaves_VBIndices) | if (m_ShoreWaves_VBIndices) | ||||
{ | { | ||||
g_VBMan.Release(m_ShoreWaves_VBIndices); | g_VBMan.Release(m_ShoreWaves_VBIndices); | ||||
m_ShoreWaves_VBIndices = NULL; | m_ShoreWaves_VBIndices = NULL; | ||||
} | } | ||||
if (m_Waviness < 5.0f && m_WaterType != L"ocean") | CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); | ||||
if (!terrain || !terrain->GetHeightMap()) | |||||
return; | return; | ||||
size_t SideSize = m_MapSize; | if (!m_WaterManager->m_DistanceHeightmap) | ||||
return; | |||||
if (m_WaterManager->m_Waviness < 5.0f && m_WaterManager->GetWaterType() != L"ocean") | |||||
return; | |||||
size_t SideSize = m_WaterManager->m_MapSize; | |||||
// First step: get the points near the coast. | // First step: get the points near the coast. | ||||
std::set<int> CoastalPointsSet; | std::set<int> CoastalPointsSet; | ||||
for (size_t z = 1; z < SideSize-1; ++z) | for (size_t z = 1; z < SideSize-1; ++z) | ||||
for (size_t x = 1; x < SideSize-1; ++x) | for (size_t x = 1; x < SideSize-1; ++x) | ||||
// get the points not on the shore but near it, ocean-side | // get the points not on the shore but near it, ocean-side | ||||
if (m_DistanceHeightmap[z*m_MapSize + x] > 0.5f && m_DistanceHeightmap[z*m_MapSize + x] < 1.5f) | if (m_WaterManager->m_DistanceHeightmap[z*SideSize + x] > 0.5f && m_WaterManager->m_DistanceHeightmap[z*SideSize + x] < 1.5f) | ||||
CoastalPointsSet.insert((z)*SideSize + x); | CoastalPointsSet.insert((z)*SideSize + x); | ||||
// Second step: create chains out of those coastal points. | // Second step: create chains out of those coastal points. | ||||
static const int around[8][2] = { { -1,-1 }, { -1,0 }, { -1,1 }, { 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 } }; | static const int around[8][2] = { { -1,-1 }, { -1,0 }, { -1,1 }, { 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 } }; | ||||
std::vector<std::deque<CoastalPoint> > CoastalPointsChains; | std::vector<std::deque<CoastalPoint> > CoastalPointsChains; | ||||
while (!CoastalPointsSet.empty()) | while (!CoastalPointsSet.empty()) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 165 Lines • ▼ Show 20 Lines | for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j) | ||||
firstPerp = perp; | firstPerp = perp; | ||||
if ( a > 1 && perp.Dot(lastPerp) < 0.90f && perp.Dot(firstPerp) < 0.70f) | if ( a > 1 && perp.Dot(lastPerp) < 0.90f && perp.Dot(firstPerp) < 0.70f) | ||||
{ | { | ||||
width = a+1; | width = a+1; | ||||
break; | break; | ||||
} | } | ||||
if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > m_WaterHeight) | if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > m_WaterManager->GetWaterHeight()) | ||||
sign = -1; | sign = -1; | ||||
avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - m_WaterHeight; | avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - m_WaterManager->GetWaterHeight(); | ||||
float localOutmost = -2.0f; | float localOutmost = -2.0f; | ||||
while (localOutmost < 0.0f) | while (localOutmost < 0.0f) | ||||
{ | { | ||||
float depth = terrain->GetExactGroundLevel(pos.X+sign*perp.X*localOutmost, pos.Y+sign*perp.Y*localOutmost) - m_WaterHeight; | float depth = terrain->GetExactGroundLevel(pos.X+sign*perp.X*localOutmost, pos.Y+sign*perp.Y*localOutmost) - m_WaterManager->GetWaterHeight(); | ||||
if (depth < 0.0f || depth > 0.6f) | if (depth < 0.0f || depth > 0.6f) | ||||
localOutmost += 0.2f; | localOutmost += 0.2f; | ||||
else | else | ||||
break; | break; | ||||
} | } | ||||
outmost += localOutmost; | outmost += localOutmost; | ||||
} | } | ||||
if (width < 5) | if (width < 5) | ||||
{ | { | ||||
j += 6; | j += 6; | ||||
continue; | continue; | ||||
} | } | ||||
outmost /= width; | outmost /= width; | ||||
if (outmost > -0.5f) | if (outmost > -0.5f) | ||||
{ | { | ||||
j += 3; | j += 3; | ||||
continue; | continue; | ||||
} | } | ||||
outmost = -2.5f + outmost * m_Waviness/10.0f; | outmost = -2.5f + outmost * m_WaterManager->m_Waviness/10.0f; | ||||
avgDepth /= width; | avgDepth /= width; | ||||
if (avgDepth > -1.3f) | if (avgDepth > -1.3f) | ||||
{ | { | ||||
j += 3; | j += 3; | ||||
continue; | continue; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j) | ||||
static const float heightT1[9] = { 0.0f, 0.2f, 0.5f, 0.8f, 0.9f, 0.85f, 0.6f, 0.2f, 0.0 }; | static const float heightT1[9] = { 0.0f, 0.2f, 0.5f, 0.8f, 0.9f, 0.85f, 0.6f, 0.2f, 0.0 }; | ||||
static const float heightT2[9] = { -0.8f, -0.4f, 0.0f, 0.1f, 0.1f, 0.03f, 0.0f, 0.0f, 0.0 }; | static const float heightT2[9] = { -0.8f, -0.4f, 0.0f, 0.1f, 0.1f, 0.03f, 0.0f, 0.0f, 0.0 }; | ||||
static const float heightT3[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0 }; | static const float heightT3[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0 }; | ||||
for (size_t t = 0; t < 9; ++t) | for (size_t t = 0; t < 9; ++t) | ||||
{ | { | ||||
float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT1[t]+outmost), | float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT1[t]+outmost), | ||||
pos.Y+sign*perp.Y*(perpT1[t]+outmost)); | pos.Y+sign*perp.Y*(perpT1[t]+outmost)); | ||||
point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight), | point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterManager->GetWaterHeight(),terrHeight), | ||||
pos.Y+sign*perp.Y*(perpT1[t]+outmost)); | pos.Y+sign*perp.Y*(perpT1[t]+outmost)); | ||||
} | } | ||||
for (size_t t = 0; t < 9; ++t) | for (size_t t = 0; t < 9; ++t) | ||||
{ | { | ||||
float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT2[t]+outmost), | float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT2[t]+outmost), | ||||
pos.Y+sign*perp.Y*(perpT2[t]+outmost)); | pos.Y+sign*perp.Y*(perpT2[t]+outmost)); | ||||
point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight), | point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterManager->GetWaterHeight(),terrHeight), | ||||
pos.Y+sign*perp.Y*(perpT2[t]+outmost)); | pos.Y+sign*perp.Y*(perpT2[t]+outmost)); | ||||
} | } | ||||
for (size_t t = 0; t < 9; ++t) | for (size_t t = 0; t < 9; ++t) | ||||
{ | { | ||||
float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), | float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), | ||||
pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); | pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); | ||||
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)); | point[t].m_SplashPosition = CVector3D(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), baseHeight + heightT2[t]*sideNess + std::max(m_WaterManager->GetWaterHeight(),terrHeight), pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); | ||||
} | } | ||||
for (size_t t = 0; t < 9; ++t) | for (size_t t = 0; t < 9; ++t) | ||||
{ | { | ||||
float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT4[t]+outmost), | float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT4[t]+outmost), | ||||
pos.Y+sign*perp.Y*(perpT4[t]+outmost)); | pos.Y+sign*perp.Y*(perpT4[t]+outmost)); | ||||
point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(m_WaterHeight,terrHeight), | point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(m_WaterManager->GetWaterHeight(),terrHeight), | ||||
pos.Y+sign*perp.Y*(perpT4[t]+outmost)); | pos.Y+sign*perp.Y*(perpT4[t]+outmost)); | ||||
} | } | ||||
vertices.push_back(point[8]); | vertices.push_back(point[8]); | ||||
vertices.push_back(point[7]); | vertices.push_back(point[7]); | ||||
vertices.push_back(point[6]); | vertices.push_back(point[6]); | ||||
vertices.push_back(point[5]); | vertices.push_back(point[5]); | ||||
vertices.push_back(point[4]); | vertices.push_back(point[4]); | ||||
Show All 26 Lines | for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j) | ||||
shoreWave->m_VBvertices = g_VBMan.Allocate(sizeof(SWavesVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); | shoreWave->m_VBvertices = g_VBMan.Allocate(sizeof(SWavesVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); | ||||
shoreWave->m_VBvertices->m_Owner->UpdateChunkVertices(shoreWave->m_VBvertices, &vertices[0]); | shoreWave->m_VBvertices->m_Owner->UpdateChunkVertices(shoreWave->m_VBvertices, &vertices[0]); | ||||
m_ShoreWaves.push_back(shoreWave); | m_ShoreWaves.push_back(shoreWave); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void WaterManager::RenderWaves(const CFrustum& frustrum) | void WaterRendering::RenderWaves(const CFrustum& frustrum) | ||||
{ | { | ||||
#if CONFIG2_GLES | #if CONFIG2_GLES | ||||
#warning Fix WaterManager::RenderWaves on GLES | #warning Fix WaterRendering::RenderWaves on GLES | ||||
#else | #else | ||||
if (g_Renderer.m_SkipSubmit || !m_WaterFancyEffects) | if (g_Renderer.m_SkipSubmit || g_RenderingOptions.GetWaterFancyEffects()) | ||||
return; | return; | ||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO); | pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO); | ||||
GLuint attachments[1] = { GL_COLOR_ATTACHMENT0_EXT }; | GLuint attachments[1] = { GL_COLOR_ATTACHMENT0_EXT }; | ||||
pglDrawBuffers(1, attachments); | pglDrawBuffers(1, attachments); | ||||
glClearColor(0.0f,0.0f, 0.0f,0.0f); | glClearColor(0.0f,0.0f, 0.0f,0.0f); | ||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | ||||
glEnable(GL_BLEND); | glEnable(GL_BLEND); | ||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||
glEnable(GL_DEPTH_TEST); | glEnable(GL_DEPTH_TEST); | ||||
glDepthFunc(GL_ALWAYS); | glDepthFunc(GL_ALWAYS); | ||||
CShaderDefines none; | CShaderDefines none; | ||||
CShaderProgramPtr shader = g_Renderer.GetShaderManager().LoadProgram("glsl/waves", none); | CShaderProgramPtr shader = g_Renderer.GetShaderManager().LoadProgram("glsl/waves", none); | ||||
shader->Bind(); | shader->Bind(); | ||||
shader->BindTexture(str_waveTex, m_WaveTex); | shader->BindTexture(str_waveTex, m_WaveTex); | ||||
shader->BindTexture(str_foamTex, m_FoamTex); | shader->BindTexture(str_foamTex, m_FoamTex); | ||||
shader->Uniform(str_time, (float)m_WaterTexTimer); | shader->Uniform(str_time, (float)g_Renderer.GetTimeManager().GetGlobalTime()); | ||||
shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); | shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); | ||||
for (size_t a = 0; a < m_ShoreWaves.size(); ++a) | for (size_t a = 0; a < m_ShoreWaves.size(); ++a) | ||||
{ | { | ||||
if (!frustrum.IsBoxVisible(m_ShoreWaves[a]->m_AABB)) | if (!frustrum.IsBoxVisible(m_ShoreWaves[a]->m_AABB)) | ||||
continue; | continue; | ||||
CVertexBuffer::VBChunk* VBchunk = m_ShoreWaves[a]->m_VBvertices; | CVertexBuffer::VBChunk* VBchunk = m_ShoreWaves[a]->m_VBvertices; | ||||
Show All 28 Lines | #else | ||||
} | } | ||||
shader->Unbind(); | shader->Unbind(); | ||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); | pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); | ||||
glDisable(GL_BLEND); | glDisable(GL_BLEND); | ||||
glDepthFunc(GL_LEQUAL); | glDepthFunc(GL_LEQUAL); | ||||
#endif | #endif | ||||
} | } | ||||
void WaterManager::RecomputeWaterData() | |||||
{ | |||||
if (!m_MapSize) | |||||
return; | |||||
RecomputeDistanceHeightmap(); | |||||
RecomputeWindStrength(); | |||||
CreateWaveMeshes(); | |||||
} | |||||
/////////////////////////////////////////////////////////////////// | |||||
// Calculate the strength of the wind at a given point on the map. | |||||
void WaterManager::RecomputeWindStrength() | |||||
{ | |||||
if (m_MapSize <= 0) | |||||
return; | |||||
if (m_WindStrength == nullptr) | |||||
m_WindStrength = new float[m_MapSize*m_MapSize]; | |||||
CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); | |||||
if (!terrain || !terrain->GetHeightMap()) | |||||
return; | |||||
CVector2D windDir = CVector2D(cos(m_WindAngle),sin(m_WindAngle)); | |||||
ssize_t windX = round(1.f / windDir.X); | |||||
ssize_t windY = round(1.f / windDir.Y); | |||||
struct SWindPoint { | |||||
SWindPoint(size_t x, size_t y, float strength) : X(x), Y(y), windStrength(strength) {} | |||||
ssize_t X; | |||||
ssize_t Y; | |||||
float windStrength; | |||||
}; | |||||
std::vector<SWindPoint> startingPoints; | |||||
std::vector<std::pair<int, int>> movement; // Every increment, move each starting point by all of these. | |||||
// Compute starting points (one or two edges of the map) and how much to move each computation increment. | |||||
if (fabs(windDir.X) < 0.01f) | |||||
{ | |||||
movement.emplace_back(0, windY); | |||||
startingPoints.reserve(m_MapSize); | |||||
size_t start = windY > 0 ? 0 : m_MapSize - 1; | |||||
for (size_t x = 0; x < m_MapSize; ++x) | |||||
startingPoints.emplace_back(x, start, 0.f); | |||||
} | |||||
else if (fabs(windDir.Y) < 0.01f) | |||||
{ | |||||
movement.emplace_back(windX, 0); | |||||
size_t start = windX > 0 ? 0 : m_MapSize - 1; | |||||
for (size_t z = 0; z < m_MapSize; ++z) | |||||
startingPoints.emplace_back(start, z, 0.f); | |||||
} | |||||
else | |||||
{ | |||||
startingPoints.reserve(m_MapSize * 2); | |||||
// Points along X. | |||||
size_t start = windY > 0 ? 0 : m_MapSize - 1; | |||||
for (size_t x = 0; x < m_MapSize; ++x) | |||||
startingPoints.emplace_back(x, start, 0.f); | |||||
// Points along Z, avoid repeating the corner point. | |||||
start = windX > 0 ? 0 : m_MapSize - 1; | |||||
if (windY > 0) | |||||
for (size_t z = 1; z < m_MapSize; ++z) | |||||
startingPoints.emplace_back(start, z, 0.f); | |||||
else | |||||
for (size_t z = 0; z < m_MapSize-1; ++z) | |||||
startingPoints.emplace_back(start, z, 0.f); | |||||
// Compute movement array. | |||||
movement.reserve(std::max(std::abs(windX),std::abs(windY))); | |||||
while (windX != 0 || windY != 0) | |||||
{ | |||||
std::pair<ssize_t, ssize_t> move = { | |||||
windX == 0 ? 0 : windX > 0 ? +1 : -1, | |||||
windY == 0 ? 0 : windY > 0 ? +1 : -1 | |||||
}; | |||||
windX -= move.first; | |||||
windY -= move.second; | |||||
movement.push_back(move); | |||||
} | |||||
} | |||||
// We have all starting points ready, move them all until the map is covered. | |||||
for (SWindPoint& point : startingPoints) | |||||
{ | |||||
// Starting velocity is 1.0 unless in shallow water. | |||||
m_WindStrength[point.Y * m_MapSize + point.X] = 1.f; | |||||
float depth = m_WaterHeight - terrain->GetVertexGroundLevel(point.X, point.Y); | |||||
if (depth > 0.f && depth < 2.f) | |||||
m_WindStrength[point.Y * m_MapSize + point.X] = depth / 2.f; | |||||
point.windStrength = m_WindStrength[point.Y * m_MapSize + point.X]; | |||||
bool onMap = true; | |||||
while (onMap) | |||||
for (size_t step = 0; step < movement.size(); ++step) | |||||
{ | |||||
// Move wind speed towards the mean. | |||||
point.windStrength = 0.15f + point.windStrength * 0.85f; | |||||
// Adjust speed based on height difference, a positive height difference slowly increases speed (simulate venturi effect) | |||||
// and a lower height reduces speed (wind protection from hills/...) | |||||
float heightDiff = std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X + movement[step].first, point.Y + movement[step].second)) - | |||||
std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X, point.Y)); | |||||
if (heightDiff > 0.f) | |||||
point.windStrength = std::min(2.f, point.windStrength + std::min(4.f, heightDiff) / 40.f); | |||||
else | |||||
point.windStrength = std::max(0.f, point.windStrength + std::max(-4.f, heightDiff) / 5.f); | |||||
point.X += movement[step].first; | |||||
point.Y += movement[step].second; | |||||
if (point.X < 0 || point.X >= static_cast<ssize_t>(m_MapSize) || point.Y < 0 || point.Y >= static_cast<ssize_t>(m_MapSize)) | |||||
{ | |||||
onMap = false; | |||||
break; | |||||
} | |||||
m_WindStrength[point.Y * m_MapSize + point.X] = point.windStrength; | |||||
} | |||||
} | |||||
// TODO: should perhaps blur a little, or change the above code to incorporate neighboring tiles a bit. | |||||
} | |||||
//////////////////////////////////////////////////////////////////////// | |||||
// TODO: This will always recalculate for now | |||||
void WaterManager::SetMapSize(size_t size) | |||||
{ | |||||
// TODO: Im' blindly trusting the user here. | |||||
m_MapSize = size; | |||||
m_NeedInfoUpdate = true; | |||||
m_updatei0 = 0; | |||||
m_updatei1 = size; | |||||
m_updatej0 = 0; | |||||
m_updatej1 = size; | |||||
SAFE_ARRAY_DELETE(m_DistanceHeightmap); | |||||
SAFE_ARRAY_DELETE(m_BlurredNormalMap); | |||||
SAFE_ARRAY_DELETE(m_WindStrength); | |||||
} | |||||
//////////////////////////////////////////////////////////////////////// | |||||
// This will set the bools properly | |||||
void WaterManager::UpdateQuality() | |||||
{ | |||||
if (g_RenderingOptions.GetWaterEffects() != m_WaterEffects) | |||||
{ | |||||
m_WaterEffects = g_RenderingOptions.GetWaterEffects(); | |||||
m_NeedsReloading = true; | |||||
} | |||||
if (g_RenderingOptions.GetWaterFancyEffects() != m_WaterFancyEffects) | |||||
{ | |||||
m_WaterFancyEffects = g_RenderingOptions.GetWaterFancyEffects(); | |||||
m_NeedsReloading = true; | |||||
} | |||||
if (g_RenderingOptions.GetWaterRealDepth() != m_WaterRealDepth) | |||||
{ | |||||
m_WaterRealDepth = g_RenderingOptions.GetWaterRealDepth(); | |||||
m_NeedsReloading = true; | |||||
} | |||||
if (g_RenderingOptions.GetWaterRefraction() != m_WaterRefraction) | |||||
{ | |||||
m_WaterRefraction = g_RenderingOptions.GetWaterRefraction(); | |||||
m_NeedsReloading = true; | |||||
} | |||||
if (g_RenderingOptions.GetWaterReflection() != m_WaterReflection) | |||||
{ | |||||
m_WaterReflection = g_RenderingOptions.GetWaterReflection(); | |||||
m_NeedsReloading = true; | |||||
} | |||||
} | |||||
bool WaterManager::WillRenderFancyWater() | |||||
{ | |||||
return m_RenderWater && g_RenderingOptions.GetWaterEffects() && g_Renderer.GetCapabilities().m_PrettyWater; | |||||
} |
Wildfire Games · Phabricator