#include "precompiled.h" #include #include #include "Pyrogenesis.h" #include "lib/res/graphics/ogl_tex.h" #include "Renderer.h" #include "PatchRData.h" #include "AlphaMapCalculator.h" #include "ps/CLogger.h" #include "MathUtil.h" #include "LOSManager.h" /////////////////////////////////////////////////////////////////// // shared list of all submitted patches this frame std::vector CPatchRData::m_Patches; const int BlendOffsets[8][2] = { { 0, -1 }, { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 } }; static SColor4ub ConvertColor(const RGBColor& src) { SColor4ub result; result.R=(u8)clamp(int(src.X*255),0,255); result.G=(u8)clamp(int(src.Y*255),0,255); result.B=(u8)clamp(int(src.Z*255),0,255); result.A=0xff; return result; } /////////////////////////////////////////////////////////////////// // CPatchRData constructor CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_VBBase(0), m_VBBlends(0), m_Vertices(0) { debug_assert(patch); Build(); } /////////////////////////////////////////////////////////////////// // CPatchRData destructor CPatchRData::~CPatchRData() { // delete copy of vertex data delete[] m_Vertices; // release vertex buffer chunks if (m_VBBase) g_VBMan.Release(m_VBBase); if (m_VBBlends) g_VBMan.Release(m_VBBlends); } static Handle GetTerrainTileTexture(CTerrain* terrain,int gx,int gz) { CMiniPatch* mp=terrain->GetTile(gx,gz); return mp ? mp->Tex1 : 0; } bool QueryAdjacency(int x,int y,Handle h,Handle* texgrid) { for (int j=y-1;j<=y+1;j++) { for (int i=x-1;i<=x+1;i++) { if (i<0 || i>PATCH_SIZE+1 || j<0 || j>PATCH_SIZE+1) { continue; } if (texgrid[j*(PATCH_SIZE+2)+i]==h) { return true; } } } return false; } struct STmpSplat { Handle m_Texture; u16 m_Indices[4]; }; void CPatchRData::BuildBlends() { m_BlendIndices.clear(); m_BlendSplats.clear(); m_BlendVertices.clear(); // get index of this patch (unused) //int px=m_Patch->m_X; //int pz=m_Patch->m_Z; CTerrain* terrain=m_Patch->m_Parent; // temporary list of splats std::vector splats; // set of textures used for splats std::set splatTextures; // for each tile in patch .. for (int j=0;jm_MiniPatches[j][i]; mp->GetTileIndex(gx,gz); // build list of textures of higher priority than current tile that are used by neighbouring tiles std::vector neighbourTextures; for (int m=-1;m<=1;m++) { for (int k=-1;k<=1;k++) { CMiniPatch* nmp=terrain->GetTile(gx+k,gz+m); if (nmp) { if (nmp->Tex1Priority>mp->Tex1Priority || (nmp->Tex1Priority==mp->Tex1Priority && nmp->Tex1>mp->Tex1)) { STex tex; tex.m_Handle=nmp->Tex1; tex.m_Priority=nmp->Tex1Priority; if (std::find(neighbourTextures.begin(),neighbourTextures.end(),tex)==neighbourTextures.end()) { neighbourTextures.push_back(tex); } } } } } if (neighbourTextures.size()>0) { // sort textures from lowest to highest priority std::sort(neighbourTextures.begin(),neighbourTextures.end()); // for each of the neighbouring textures .. uint count=(uint)neighbourTextures.size(); for (uint k=0;km_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]); // now build outgoing splats m_BlendSplats.resize(splatTextures.size()); int splatCount=0; debug_assert(m_VBBlends->m_Index < 65536); unsigned short base = (unsigned short)m_VBBlends->m_Index; std::set::iterator iter=splatTextures.begin(); for (;iter!=splatTextures.end();++iter) { Handle tex=*iter; SSplat& splat=m_BlendSplats[splatCount]; splat.m_IndexStart=(u32)m_BlendIndices.size(); splat.m_Texture=tex; for (uint k=0;k<(uint)splats.size();k++) { if (splats[k].m_Texture==tex) { m_BlendIndices.push_back(splats[k].m_Indices[0]+base); m_BlendIndices.push_back(splats[k].m_Indices[1]+base); m_BlendIndices.push_back(splats[k].m_Indices[2]+base); m_BlendIndices.push_back(splats[k].m_Indices[3]+base); splat.m_IndexCount+=4; } } splatCount++; } } } void CPatchRData::BuildIndices() { // must have allocated some vertices before trying to build corresponding indices debug_assert(m_VBBase); // number of vertices in each direction in each patch int vsize=PATCH_SIZE+1; // release existing indices and bins m_Indices.clear(); m_ShadowMapIndices.clear(); m_Splats.clear(); // build grid of textures on this patch and boundaries of adjacent patches std::vector textures; Handle texgrid[PATCH_SIZE][PATCH_SIZE]; for (int j=0;jm_MiniPatches[j][i].Tex1; texgrid[j][i]=h; if (std::find(textures.begin(),textures.end(),h)==textures.end()) { textures.push_back(h); } } } // now build base splats from interior textures m_Splats.resize(textures.size()); // build indices for base splats u32 base=(u32)m_VBBase->m_Index; for (uint i=0;i<(uint)m_Splats.size();i++) { Handle h=textures[i]; SSplat& splat=m_Splats[i]; splat.m_Texture=h; splat.m_IndexStart=(u32)m_Indices.size(); for (int j=0;jm_X; u32 pz=m_Patch->m_Z; CTerrain* terrain=m_Patch->m_Parent; int mapSize=terrain->GetVerticesPerSide(); CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager(); // build vertices for (int j=0;jCalcPosition(ix,iz,vertices[v].m_Position); terrain->CalcNormal(ix,iz,normal); const int DX[] = {1,1,0,0}; const int DZ[] = {0,1,1,0}; 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; } } RGBColor losModColor(losMod, losMod, losMod); g_Renderer.m_SHCoeffsTerrain.Evaluate(normal, c, losModColor); vertices[v].m_Color=ConvertColor(c); vertices[v].m_UVs[0]=i*0.125f; vertices[v].m_UVs[1]=j*0.125f; } } if (!m_VBBase) { m_VBBase=g_VBMan.Allocate(sizeof(SBaseVertex),vsize*vsize,false); } m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices); } void CPatchRData::Build() { BuildVertices(); BuildIndices(); BuildBlends(); } void CPatchRData::Update() { if (m_UpdateFlags!=0) { // TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather // than everything; it's complicated slightly because the blends are dependent // on both vertex and index data //BuildVertices(); BuildIndices(); ///BuildBlends(); m_UpdateFlags=0; } // Always build vertices (due to LOS) BuildVertices(); BuildBlends(); } void CPatchRData::RenderBase() { debug_assert(m_UpdateFlags==0); u8* base=m_VBBase->m_Owner->Bind(); // setup data pointers u32 stride=sizeof(SBaseVertex); glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBaseVertex,m_Position)); glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBaseVertex,m_Color)); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBaseVertex,m_UVs[0])); // render each splat for (uint i=0;i<(uint)m_Splats.size();i++) { SSplat& splat=m_Splats[i]; ogl_tex_bind(splat.m_Texture); glDrawElements(GL_QUADS,splat.m_IndexCount,GL_UNSIGNED_SHORT,&m_Indices[splat.m_IndexStart]); // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2; } } void CPatchRData::RenderStreams(u32 streamflags) { debug_assert(m_UpdateFlags==0); u8* base=m_VBBase->m_Owner->Bind(); // setup data pointers glVertexPointer(3,GL_FLOAT,sizeof(SBaseVertex),base+offsetof(SBaseVertex,m_Position)); if (streamflags & STREAM_UV0) { glTexCoordPointer(2,GL_FLOAT,sizeof(SBaseVertex),base+offsetof(SBaseVertex,m_UVs)); } else if (streamflags & STREAM_POSTOUV0) { glTexCoordPointer(3,GL_FLOAT,sizeof(SBaseVertex),base+offsetof(SBaseVertex,m_Position)); } // render all base splats at once glDrawElements(GL_QUADS,(GLsizei)m_Indices.size(),GL_UNSIGNED_SHORT,&m_Indices[0]); // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_TerrainTris+=(u32)m_Indices.size()/2; } void CPatchRData::RenderBlends() { debug_assert(m_UpdateFlags==0); if (m_BlendVertices.size()==0) return; u8* base=m_VBBlends->m_Owner->Bind(); // setup data pointers u32 stride=sizeof(SBlendVertex); glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_Position)); glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBlendVertex,m_Color)); glClientActiveTextureARB(GL_TEXTURE0); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0])); glClientActiveTextureARB(GL_TEXTURE1); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_AlphaUVs[0])); for (uint i=0;i<(uint)m_BlendSplats.size();i++) { SSplat& splat=m_BlendSplats[i]; ogl_tex_bind(splat.m_Texture); glDrawElements(GL_QUADS,splat.m_IndexCount,GL_UNSIGNED_SHORT,&m_BlendIndices[splat.m_IndexStart]); // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_BlendSplats++; g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2; } } void CPatchRData::RenderOutlines() { for (uint i=0;iGetRenderData(); patchdata->RenderOutline(); } } void CPatchRData::RenderStreamsAll(u32 streamflags) { for (uint i=0;iGetRenderData(); patchdata->RenderStreams(streamflags); } } void CPatchRData::RenderOutline() { int i; uint vsize=PATCH_SIZE+1; u8* base=m_VBBase->m_Owner->Bind(); //TODO: this makes no sense, get rid of it UNUSED2(base); glBegin(GL_LINES); for (i=0;im_Owner->AppendBatch(m_VBBase,splat.m_Texture,splat.m_IndexCount,&m_Indices[splat.m_IndexStart]); } } ///////////////////////////////////////////////////////////////////////////////////////////////////// // SubmitBlendBatches: submit next set of blend batches for this patch to the vertex buffer; // return true if all blends on this patch have been submitted, else false bool CPatchRData::SubmitBlendBatches() { if (m_NextBlendSplatm_Owner->AppendBatch(m_VBBlends,splat.m_Texture,splat.m_IndexCount,&m_BlendIndices[splat.m_IndexStart]); } return true; } return true; } ///////////////////////////////////////////////////////////////////////////////////////////////////// // RenderBaseSplats: render all base passes of all patches; assumes vertex, texture and color // client states are enabled void CPatchRData::RenderBaseSplats() { uint i; // set up texture environment for base pass MICROLOG(L"base splat textures"); glActiveTextureARB(GL_TEXTURE0); glClientActiveTextureARB(GL_TEXTURE0); oglCheck(); 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); oglCheck(); // 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); oglCheck(); #if 1 // submit base batches for each patch to the vertex buffer for (i=0;iGetRenderData(); patchdata->SubmitBaseBatches(); } oglCheck(); // render base passes for each patch const std::list& buffers=g_VBMan.GetBufferList(); std::list::const_iterator iter; for (iter=buffers.begin();iter!=buffers.end();++iter) { CVertexBuffer* buffer=*iter; // any batches in this VB? const std::vector& batches=buffer->GetBatches(); if (batches.size()>0) { u8* base=buffer->Bind(); // setup data pointers u32 stride=sizeof(SBaseVertex); glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBaseVertex,m_Position)); glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBaseVertex,m_Color)); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBaseVertex,m_UVs[0])); // render each batch for (i=0;im_IndexData.size()>0) { ogl_tex_bind(batch->m_Texture); for (uint j=0;jm_IndexData.size();j++) { glDrawElements(GL_QUADS,(GLsizei)batch->m_IndexData[j].first,GL_UNSIGNED_SHORT,batch->m_IndexData[j].second); g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_TerrainTris+=(u32)batch->m_IndexData[j].first/2; } } } } } // everything rendered; empty out batch lists MICROLOG(L"clear"); g_VBMan.ClearBatchIndices(); #else for (i=0;iGetRenderData(); patchdata->RenderBase(); } #endif } ///////////////////////////////////////////////////////////////////////////////////////////////////// // RenderBlendSplats: render all blend passes of all patches; assumes vertex, texture and color // client states are enabled void CPatchRData::RenderBlendSplats() { uint i; // switch on second uv set glClientActiveTextureARB(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTextureARB(GL_TEXTURE0); // switch on the composite alpha map texture (void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1); // setup additional texenv required by blend pass 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); #if 1 // submit blend batches for each patch to the vertex buffer for (i=0;iGetRenderData(); patchdata->SetupBlendBatches(); } bool finished=true; do { for (i=0;iGetRenderData(); if (!patchdata->SubmitBlendBatches()) finished=false; } } while (!finished); // render blend passes for each patch const std::list& buffers=g_VBMan.GetBufferList(); std::list::const_iterator iter; for (iter=buffers.begin();iter!=buffers.end();++iter) { CVertexBuffer* buffer=*iter; // any batches in this VB? const std::vector& batches=buffer->GetBatches(); if (batches.size()>0) { u8* base=buffer->Bind(); // setup data pointers u32 stride=sizeof(SBlendVertex); glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_Position)); glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBlendVertex,m_Color)); glClientActiveTextureARB(GL_TEXTURE0); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0])); glClientActiveTextureARB(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_AlphaUVs[0])); // render each batch for (i=0;im_IndexData.size()>0) { ogl_tex_bind(batch->m_Texture); for (uint j=0;jm_IndexData.size();j++) { glDrawElements(GL_QUADS,(GLsizei)batch->m_IndexData[j].first,GL_UNSIGNED_SHORT,batch->m_IndexData[j].second); g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_TerrainTris+=(u32)batch->m_IndexData[j].first/2; } } } } } // everything rendered; empty out batch lists g_VBMan.ClearBatchIndices(); #else // render blend passes for each patch for (i=0;iGetRenderData(); patchdata->RenderBlends(); } #endif // restore depth writes glDepthMask(1); // restore default state: switch off blending glDisable(GL_BLEND); // switch off second uv set glClientActiveTextureARB(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTextureARB(GL_TEXTURE0); // switch off texture unit 1, make unit 0 active texture g_Renderer.BindTexture(1,0); glActiveTextureARB(GL_TEXTURE0); // tidy up client states glDisableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTextureARB(GL_TEXTURE0); } ///////////////////////////////////////////////////////////////////////////////////////////// // Submit: submit a patch to render this frame void CPatchRData::Submit(CPatch* patch) { CPatchRData* data=(CPatchRData*) patch->GetRenderData(); if (data==0) { // no renderdata for patch, create it now data=new CPatchRData(patch); patch->SetRenderData(data); } else { data->Update(); } m_Patches.push_back(patch); } ///////////////////////////////////////////////////////////////////////////////////////////// // ApplyShadowMap: apply given shadow map to all terrain patches; assume the texture matrix // has been correctly setup on unit 1 to handle the projection void CPatchRData::ApplyShadowMap(GLuint shadowmaphandle) { uint i; 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); #if 1 // submit base batches for each patch to the vertex buffer for (i=0;iGetRenderData(); patchdata->m_VBBase->m_Owner->AppendBatch(patchdata->m_VBBase,0,patchdata->m_ShadowMapIndices.size(), &patchdata->m_ShadowMapIndices[0]); } // render base passes for each patch const std::list& buffers=g_VBMan.GetBufferList(); std::list::const_iterator iter; for (iter=buffers.begin();iter!=buffers.end();++iter) { CVertexBuffer* buffer=*iter; // any batches in this VB? const std::vector& batches=buffer->GetBatches(); if (batches.size()>0) { u8* base=buffer->Bind(); // setup data pointers u32 stride=sizeof(SBaseVertex); glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBaseVertex,m_Position)); glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBaseVertex,m_Color)); glTexCoordPointer(3,GL_FLOAT,sizeof(SBaseVertex),base+offsetof(SBaseVertex,m_Position)); // render batch (can only be one per buffer, since all batches are flagged as using a null texture) const CVertexBuffer::Batch* batch=batches[0]; for (uint j=0;jm_IndexData.size();j++) { glDrawElements(GL_QUADS,(GLsizei)batch->m_IndexData[j].first,GL_UNSIGNED_SHORT,batch->m_IndexData[j].second); g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_TerrainTris+=(u32)batch->m_IndexData[j].first/2; } } } // everything rendered; empty out batch lists g_VBMan.ClearBatchIndices(); #else for (uint i=0;iGetRenderData();; patchdata->RenderStreams(STREAM_POS|STREAM_POSTOUV0); } #endif glDisable(GL_BLEND); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); }