/** * ========================================================================= * File : TerrainRenderer.cpp * Project : Pyrogenesis * Description : Terrain rendering (everything related to patches and * : water) is encapsulated in TerrainRenderer * * @author Nicolai Hähnle * ========================================================================= */ #include "precompiled.h" #include "graphics/Camera.h" #include "graphics/Patch.h" #include "graphics/Terrain.h" #include "maths/MathUtil.h" #include "ps/Game.h" #include "ps/Profile.h" #include "simulation/LOSManager.h" #include "renderer/PatchRData.h" #include "renderer/Renderer.h" #include "renderer/TerrainRenderer.h" #include "renderer/WaterManager.h" /////////////////////////////////////////////////////////////////////////////////////////////// // TerrainRenderer implementation /** * TerrainRenderer keeps track of which phase it is in, to detect * when Submit, PrepareForRendering etc. are called in the wrong order. */ enum Phase { Phase_Submit, Phase_Render }; /** * Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class. */ struct TerrainRendererInternals { /// Which phase (submitting or rendering patches) are we in right now? Phase phase; /** * VisiblePatches: Patches that were submitted for this frame * * @todo Merge this list with CPatchRData list */ std::vector visiblePatches; }; /////////////////////////////////////////////////////////////////// // Construction/Destruction TerrainRenderer::TerrainRenderer() { m = new TerrainRendererInternals(); m->phase = Phase_Submit; } TerrainRenderer::~TerrainRenderer() { delete m; } /////////////////////////////////////////////////////////////////// // Submit a patch for rendering void TerrainRenderer::Submit(CPatch* patch) { debug_assert(m->phase == Phase_Submit); CPatchRData* data=(CPatchRData*) patch->GetRenderData(); if (data == 0) { // no renderdata for patch, create it now data = new CPatchRData(patch); patch->SetRenderData(data); } data->Update(); m->visiblePatches.push_back(patch); } /////////////////////////////////////////////////////////////////// // Prepare for rendering void TerrainRenderer::PrepareForRendering() { debug_assert(m->phase == Phase_Submit); m->phase = Phase_Render; } /////////////////////////////////////////////////////////////////// // Clear submissions lists void TerrainRenderer::EndFrame() { debug_assert(m->phase == Phase_Render); m->visiblePatches.clear(); m->phase = Phase_Submit; } /////////////////////////////////////////////////////////////////// // Query if patches have been submitted this frame bool TerrainRenderer::HaveSubmissions() { return m->visiblePatches.size() > 0; } /////////////////////////////////////////////////////////////////// // Full-featured terrain rendering with blending and everything void TerrainRenderer::RenderTerrain() { debug_assert(m->phase == Phase_Render); // switch on required client states glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); // render everything // set up texture environment for base pass MICROLOG(L"base splat textures"); pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); 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); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); // Set alpha to 1.0 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); float one[4] = { 1.f, 1.f, 1.f, 1.f }; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one); for(uint i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); patchdata->RenderBase(); } // render blends // switch on the composite alpha map texture (void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1); // switch on second uv set pglClientActiveTextureARB(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); // setup additional texenv required by blend pass pglActiveTextureARB(GL_TEXTURE1); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); 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_ONE_MINUS_SRC_ALPHA); // switch on blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // no need to write to the depth buffer a second time glDepthMask(0); // render blend passes for each patch for(uint i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); patchdata->RenderBlends(); } // restore OpenGL state glDepthMask(1); glDisable(GL_BLEND); pglClientActiveTextureARB(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); g_Renderer.BindTexture(1,0); pglClientActiveTextureARB(GL_TEXTURE0); pglActiveTextureARB(GL_TEXTURE0); // switch off all client states glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); } /////////////////////////////////////////////////////////////////// // Render un-textured patches as polygons void TerrainRenderer::RenderPatches() { debug_assert(m->phase == Phase_Render); glEnableClientState(GL_VERTEX_ARRAY); for(uint i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); patchdata->RenderStreams(STREAM_POS); } glDisableClientState(GL_VERTEX_ARRAY); } /////////////////////////////////////////////////////////////////// // Render outlines of submitted patches as lines void TerrainRenderer::RenderOutlines() { glEnableClientState(GL_VERTEX_ARRAY); for(uint i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); patchdata->RenderOutline(); } glDisableClientState(GL_VERTEX_ARRAY); } /////////////////////////////////////////////////////////////////// // Render water that is part of the terrain void TerrainRenderer::RenderWater() { PROFILE(" render water "); //Fresnel effect CCamera* Camera=g_Game->GetView()->GetCamera(); CVector3D CamFace=Camera->m_Orientation.GetIn(); CamFace.Normalize(); float FresnelScalar = CamFace.Dot( CVector3D(0.0f, -1.0f, 0.0f) ); //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); double time = get_time(); 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); 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); // Set the proper LOD bias glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias); glBegin(GL_QUADS); for(size_t i=0; ivisiblePatches.size(); i++) { CPatch* patch = m->visiblePatches[i]; for(int dx=0; dxm_X*PATCH_SIZE + dx); int z = (patch->m_Z*PATCH_SIZE + dz); // is any corner of the tile below the water height? if not, no point rendering it bool shouldRender = false; for(int j=0; j<4; j++) { float terrainHeight = terrain->getVertexGroundLevel(x + DX[j], z + DZ[j]); if(terrainHeight < WaterMgr->m_WaterHeight) { shouldRender = true; break; } } if(!shouldRender) { continue; } for(int j=0; j<4; j++) { int ix = x + DX[j]; int iz = z + DZ[j]; float vertX = ix * CELL_SIZE; float vertZ = iz * CELL_SIZE; float terrainHeight = terrain->getVertexGroundLevel(ix, iz); float alpha = clamp( (WaterMgr->m_WaterHeight - terrainHeight) / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset, -100.0f, WaterMgr->m_WaterMaxAlpha); float losMod = 1.0f; for(int k=0; k<4; k++) { int tx = ix - DX[k]; int tz = iz - DZ[k]; if(tx >= 0 && tz >= 0 && tx <= mapSize-2 && tz <= mapSize-2) { ELOSStatus s = losMgr->GetStatus(tx, tz, g_Game->GetLocalPlayer()); if(s == LOS_EXPLORED && losMod > 0.7f) losMod = 0.7f; else if(s==LOS_UNEXPLORED && losMod > 0.0f) losMod = 0.0f; } } 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); glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ); } } //end of x loop } //end of z loop } glEnd(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDepthMask(GL_TRUE); glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); } /////////////////////////////////////////////////////////////////// // void TerrainRenderer::ApplyShadowMap(GLuint shadowmaphandle) { debug_assert(m->phase == Phase_Render); g_Renderer.BindTexture(0, shadowmaphandle); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, 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); glColor3f(1,1,1); glEnable(GL_BLEND); glBlendFunc(GL_DST_COLOR,GL_ZERO); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); for (uint i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); patchdata->RenderStreams(STREAM_POS|STREAM_POSTOUV0); } glDisable(GL_BLEND); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); }