diff --git a/source/ps/Loader.h b/source/ps/Loader.h index 9e4f605287..be1dcf50e8 100644 --- a/source/ps/Loader.h +++ b/source/ps/Loader.h @@ -158,7 +158,7 @@ extern LibError LDR_NonprogressiveLoad(); #define LDR_CHECK_TIMEOUT(completed_jobs, total_jobs)\ if(get_time() > end_time)\ {\ - int progress_percent = (completed_jobs*100 / total_jobs);\ + int progress_percent = ((completed_jobs)*100 / (total_jobs));\ /* 0 means "finished", so don't return that! */\ if(progress_percent == 0)\ progress_percent = 1;\ diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index 43ba445bd5..459daee802 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -1469,6 +1469,21 @@ void CRenderer::JSI_SetSky(JSContext* ctx, jsval newval) m->skyManager.SetSkySet(skySet); } +jsval CRenderer::JSI_GetHorizonHeight(JSContext*) +{ + return ToJSVal(m->skyManager.m_HorizonHeight); +} + +void CRenderer::JSI_SetHorizonHeight(JSContext* ctx, jsval newval) +{ + float value; + + if (!ToPrimitive(ctx, newval, value)) + return; + + m->skyManager.m_HorizonHeight = value; +} + void CRenderer::ScriptingInit() { AddProperty(L"fastPlayerColor", &CRenderer::JSI_GetFastPlayerColor, &CRenderer::JSI_SetFastPlayerColor); @@ -1481,6 +1496,7 @@ void CRenderer::ScriptingInit() AddProperty(L"disableCopyShadow", &CRenderer::m_DisableCopyShadow); AddProperty(L"depthTextureBits", &CRenderer::JSI_GetDepthTextureBits, &CRenderer::JSI_SetDepthTextureBits); AddProperty(L"skySet", &CRenderer::JSI_GetSky, &CRenderer::JSI_SetSky); + AddProperty(L"horizonHeight", &CRenderer::JSI_GetHorizonHeight, &CRenderer::JSI_SetHorizonHeight); CJSObject::ScriptingInit("Renderer"); } diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h index 3132fb17ed..5b7ae3db9c 100644 --- a/source/renderer/Renderer.h +++ b/source/renderer/Renderer.h @@ -331,6 +331,8 @@ protected: void JSI_SetDepthTextureBits(JSContext* ctx, jsval newval); jsval JSI_GetSky(JSContext*); void JSI_SetSky(JSContext* ctx, jsval newval); + jsval JSI_GetHorizonHeight(JSContext*); + void JSI_SetHorizonHeight(JSContext* ctx, jsval newval); static void ScriptingInit(); // patch rendering stuff diff --git a/source/renderer/SkyManager.cpp b/source/renderer/SkyManager.cpp index f3f20418d6..9960eee085 100644 --- a/source/renderer/SkyManager.cpp +++ b/source/renderer/SkyManager.cpp @@ -15,6 +15,8 @@ #include "lib/res/graphics/tex.h" #include "lib/res/graphics/ogl_tex.h" +#include "maths/MathUtil.h" + #include "ps/CLogger.h" #include "ps/Loader.h" @@ -22,6 +24,7 @@ #include "renderer/Renderer.h" #include "graphics/Camera.h" +#include "graphics/LightEnv.h" #define LOG_CATEGORY "graphics" @@ -50,6 +53,8 @@ SkyManager::SkyManager() // TODO: add a way to set the initial skyset before progressive load m_SkySet = L"default"; + m_HorizonHeight = 0; + for (uint i = 0; i < ARRAY_SIZE(m_SkyTexture); i++) m_SkyTexture[i] = 0; @@ -155,11 +160,15 @@ void SkyManager::RenderSky() glMatrixMode(GL_MODELVIEW); glPushMatrix(); - // Translate so we are at the camera in the X and Z directions, - // but put the horizon at Y=0 so it looks right when the camera is higher + // Translate so we are at the camera in the X and Z directions, but + // put the horizon at a fixed height regardless of camera Y const CCamera& camera = g_Renderer.GetViewCamera(); CVector3D pos = camera.m_Orientation.GetTranslation(); - glTranslatef( pos.X, 0.0f, pos.Z ); + glTranslatef( pos.X, m_HorizonHeight, pos.Z ); + + // Rotate so that the "left" face, which contains the brightest part of each + // skymap, is in the direction of the sun from our light environment + glRotatef( 90.0f + g_Renderer.GetLightEnv().GetRotation()*180.0f/M_PI, 0.0f, 1.0f, 0.0f ); // Distance to draw the faces at const float D = 2000.0; diff --git a/source/renderer/SkyManager.h b/source/renderer/SkyManager.h index c1fed40762..efa8dbb7aa 100644 --- a/source/renderer/SkyManager.h +++ b/source/renderer/SkyManager.h @@ -20,6 +20,7 @@ class SkyManager { public: bool m_RenderSky; + float m_HorizonHeight; public: SkyManager(); diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp index b72dff3cc1..0ee2b5c44f 100644 --- a/source/renderer/TerrainRenderer.cpp +++ b/source/renderer/TerrainRenderer.cpp @@ -30,6 +30,8 @@ #include "renderer/TerrainRenderer.h" #include "renderer/WaterManager.h" +#include "lib/res/graphics/ogl_shader.h" + /////////////////////////////////////////////////////////////////////////////////////////////// // TerrainRenderer implementation @@ -59,6 +61,9 @@ struct TerrainRendererInternals * @todo Merge this list with CPatchRData list */ std::vector visiblePatches; + + /// Fancy water shader + Handle fancyWaterShader; }; @@ -69,10 +74,15 @@ TerrainRenderer::TerrainRenderer() { m = new TerrainRendererInternals(); m->phase = Phase_Submit; + m->fancyWaterShader = 0; } TerrainRenderer::~TerrainRenderer() { + if( m->fancyWaterShader ) + { + ogl_program_free( m->fancyWaterShader ); + } delete m; } @@ -375,6 +385,14 @@ void TerrainRenderer::RenderWater() { PROFILE( "render water" ); + bool fancy = g_Renderer.m_Options.m_FancyWater; + + // If we're using fancy water, make sure its shader is loaded + if(fancy && !m->fancyWaterShader) + { + m->fancyWaterShader = ogl_program_load( "shaders/water_high.xml" ); + //debug_printf("Loaded the water shader!!\n"); + } //(Crappy) fresnel effect CCamera* Camera=g_Game->GetView()->GetCamera(); @@ -384,15 +402,11 @@ void TerrainRenderer::RenderWater() //Invert and set boundaries FresnelScalar = (1 - FresnelScalar) * 0.4f + 0.6f; - const int DX[] = {1,1,0,0}; - const int DZ[] = {0,1,1,0}; - CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); int mapSize = terrain->GetVerticesPerSide(); CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager(); WaterManager* WaterMgr = g_Renderer.GetWaterManager(); - glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glDepthMask(GL_FALSE); @@ -401,27 +415,90 @@ void TerrainRenderer::RenderWater() double period = 1.6; int curTex = (int)(time*60/period) % 60; - ogl_tex_bind(WaterMgr->m_WaterTexture[curTex], 0); - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - float tx = -fmod(time, 20.0)/20.0; - float ty = fmod(time, 35.0)/35.0; - glTranslatef(tx, ty, 0); + if(fancy) + { + ogl_tex_bind(WaterMgr->m_NormalMap[curTex], 0); + } + else + { + ogl_tex_bind(WaterMgr->m_WaterTexture[curTex], 0); + } - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + if(!fancy) + { + // Shift the texture coordinates to make it "flow" + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + float tx = -fmod(time, 20.0)/20.0; + float ty = fmod(time, 35.0)/35.0; + glTranslatef(tx, ty, 0); + } + + if(!fancy) + { + // Set up texture environment to multiply vertex RGB by texture RGB and use vertex alpha + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + } + else + { + // Temp stuff for testing + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + } // Set the proper LOD bias glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias); + // Some offsets used to go around counterclockwise while keeping code concise + const int DX[] = {1,1,0,0}; + const int DZ[] = {0,1,1,0}; + + GLint vertexDepth = 0; // water depth attribute, if using fancy water + + if(fancy) + { + ogl_program_use( m->fancyWaterShader ); + + const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); + //SetAmbient(lightenv.m_TerrainAmbientColor); + //SetSunDir(lightenv.GetSunDir()); + //SetSunColor(lightenv.m_SunColor); + + GLint ambient = ogl_program_get_uniform_location( m->fancyWaterShader, "ambient" ); + GLint sunDir = ogl_program_get_uniform_location( m->fancyWaterShader, "sunDir" ); + GLint sunColor = ogl_program_get_uniform_location( m->fancyWaterShader, "sunColor" ); + GLint shininess = ogl_program_get_uniform_location( m->fancyWaterShader, "shininess" ); + GLint cameraPos = ogl_program_get_uniform_location( m->fancyWaterShader, "cameraPos" ); + GLint normalMap = ogl_program_get_uniform_location( m->fancyWaterShader, "normalMap" ); + + pglUniform3fvARB( ambient, 1, &lightEnv.m_TerrainAmbientColor.X ); + pglUniform3fvARB( sunDir, 1, &lightEnv.GetSunDir().X ); + pglUniform3fvARB( sunColor, 1, &lightEnv.m_SunColor.X ); + pglUniform1fARB( shininess, 200.0f ); + pglUniform1iARB( normalMap, 0 ); // texture unit 0 + + const CCamera& camera = g_Renderer.GetViewCamera(); + CVector3D camPos = camera.m_Orientation.GetTranslation(); + pglUniform3fvARB( cameraPos, 1, &camPos.X ); + + vertexDepth = ogl_program_get_attrib_location( m->fancyWaterShader, "vertexDepth" ); + } + + float repeatFreq = (fancy ? 10.0f : 16.0f); + glBegin(GL_QUADS); for(size_t i=0; ivisiblePatches.size(); i++) @@ -481,8 +558,13 @@ void TerrainRenderer::RenderWater() } } + if(fancy) + { + pglVertexAttrib1fARB( vertexDepth, WaterMgr->m_WaterHeight - terrainHeight ); + } + glColor4f(WaterMgr->m_WaterColor.r*losMod, WaterMgr->m_WaterColor.g*losMod, WaterMgr->m_WaterColor.b*losMod, alpha * FresnelScalar); - pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/16.0f, vertZ/16.0f); + pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/repeatFreq, vertZ/repeatFreq); glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ); } } //end of x loop @@ -490,6 +572,11 @@ void TerrainRenderer::RenderWater() } glEnd(); + if(fancy) + { + ogl_program_use( 0 ); + } + glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDepthMask(GL_TRUE); diff --git a/source/renderer/WaterManager.cpp b/source/renderer/WaterManager.cpp index 9468dc5bd8..2f721e8cb6 100644 --- a/source/renderer/WaterManager.cpp +++ b/source/renderer/WaterManager.cpp @@ -50,7 +50,11 @@ WaterManager::WaterManager() for (uint i = 0; i < ARRAY_SIZE(m_WaterTexture); i++) m_WaterTexture[i] = 0; + for (uint i = 0; i < ARRAY_SIZE(m_NormalMap); i++) + m_NormalMap[i] = 0; + cur_loading_water_tex = 0; + cur_loading_normal_map = 0; } WaterManager::~WaterManager() @@ -65,29 +69,48 @@ WaterManager::~WaterManager() int WaterManager::LoadWaterTextures() { const uint num_textures = ARRAY_SIZE(m_WaterTexture); + const uint num_normal_maps = ARRAY_SIZE(m_NormalMap); + + // TODO: add a member variable and setter for this. (can't make this + // a parameter because this function is called via delay-load code) + static const char* const water_type = "default"; // yield after this time is reached. balances increased progress bar // smoothness vs. slowing down loading. const double end_time = get_time() + 100e-3; + char filename[PATH_MAX]; + while (cur_loading_water_tex < num_textures) { - char waterName[PATH_MAX]; - // TODO: add a member variable and setter for this. (can't make this - // a parameter because this function is called via delay-load code) - static const char* const water_type = "animation2"; - snprintf(waterName, ARRAY_SIZE(waterName), "art/textures/terrain/types/water/%s/water%02d.dds", water_type, cur_loading_water_tex+1); - Handle ht = ogl_tex_load(waterName); + snprintf(filename, ARRAY_SIZE(filename), "art/textures/animated/water/%s/diffuse%02d.dds", + water_type, cur_loading_water_tex+1); + Handle ht = ogl_tex_load(filename); if (ht <= 0) { - LOG(ERROR, LOG_CATEGORY, "LoadWaterTextures failed on \"%s\"", waterName); + LOG(ERROR, LOG_CATEGORY, "LoadWaterTextures failed on \"%s\"", filename); return ht; } m_WaterTexture[cur_loading_water_tex] = ht; RETURN_ERR(ogl_tex_upload(ht)); - cur_loading_water_tex++; - LDR_CHECK_TIMEOUT(cur_loading_water_tex, num_textures); + LDR_CHECK_TIMEOUT(cur_loading_water_tex, num_textures + num_normal_maps); + } + + while (cur_loading_normal_map < num_normal_maps) + { + snprintf(filename, ARRAY_SIZE(filename), "art/textures/animated/water/%s/normal%02d.dds", + water_type, cur_loading_normal_map+1); + Handle ht = ogl_tex_load(filename); + if (ht <= 0) + { + LOG(ERROR, LOG_CATEGORY, "LoadWaterTextures failed on \"%s\"", filename); + return ht; + } + m_NormalMap[cur_loading_normal_map] = ht; + RETURN_ERR(ogl_tex_upload(ht)); + cur_loading_normal_map++; + LDR_CHECK_TIMEOUT(num_textures + cur_loading_normal_map, num_textures + num_normal_maps); } return 0; @@ -103,5 +126,14 @@ void WaterManager::UnloadWaterTextures() ogl_tex_free(m_WaterTexture[i]); m_WaterTexture[i] = 0; } + + + for(uint i = 0; i < ARRAY_SIZE(m_NormalMap); i++) + { + ogl_tex_free(m_NormalMap[i]); + m_NormalMap[i] = 0; + } + cur_loading_water_tex = 0; // so they will be reloaded if LoadWaterTextures is called again + cur_loading_normal_map = 0; } diff --git a/source/renderer/WaterManager.h b/source/renderer/WaterManager.h index 7ccabb024f..d3e6158db0 100644 --- a/source/renderer/WaterManager.h +++ b/source/renderer/WaterManager.h @@ -23,6 +23,7 @@ class WaterManager { public: Handle m_WaterTexture[60]; + Handle m_NormalMap[60]; int m_WaterCurrentTex; CColor m_WaterColor; bool m_RenderWater; @@ -62,6 +63,7 @@ public: private: /// State of progressive loading (in # of loaded textures) uint cur_loading_water_tex; + uint cur_loading_normal_map; };