#include "precompiled.h" #include #include #include #include "res/tex.h" #include "Renderer.h" #include "PatchRData.h" #include "AlphaMapCalculator.h" #include "ps/CLogger.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 } }; inline int clamp(int x,int min,int max) { if (xmax) return max; else return x; } static SColor4ub ConvertColor(const RGBColor& src) { SColor4ub result; result.R=clamp(int(src.X*255),0,255); result.G=clamp(int(src.Y*255),0,255); result.B=clamp(int(src.Z*255),0,255); result.A=0xff; return result; } /////////////////////////////////////////////////////////////////// // CPatchRData constructor CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_Vertices(0), m_VBBase(0), m_VBBlends(0) { 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 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) { u32 count=(u32)neighbourTextures.size(); // sort textures from lowest to highest priority std::sort(neighbourTextures.begin(),neighbourTextures.end()); // for each of the neighbouring textures .. for (uint k=0;k<(uint)neighbourTextures.size();++k) { // now build the grid of blends dependent on whether the tile adjacent to the current tile // uses the current neighbour texture BlendShape8 shape; for (int m=0;m<8;m++) { int ox=gx+BlendOffsets[m][1]; int oz=gz+BlendOffsets[m][0]; // get texture on adjacent tile Handle atex=GetTerrainTileTexture(terrain,ox,oz); // fill 0/1 into shape array shape[m]=(atex==neighbourTextures[k].m_Handle) ? 0 : 1; } // calculate the required alphamap and the required rotation of the alphamap from blendshape unsigned int alphamapflags; int alphamap=CAlphaMapCalculator::Calculate(shape,alphamapflags); // now actually render the blend tile (if we need one) if (alphamap!=-1) { float u0=g_Renderer.m_AlphaMapCoords[alphamap].u0; float u1=g_Renderer.m_AlphaMapCoords[alphamap].u1; float v0=g_Renderer.m_AlphaMapCoords[alphamap].v0; float v1=g_Renderer.m_AlphaMapCoords[alphamap].v1; if (alphamapflags & BLENDMAP_FLIPU) { // flip u float t=u0; u0=u1; u1=t; } if (alphamapflags & BLENDMAP_FLIPV) { // flip v float t=v0; v0=v1; v1=t; } int base=0; if (alphamapflags & BLENDMAP_ROTATE90) { // rotate 1 base=1; } else if (alphamapflags & BLENDMAP_ROTATE180) { // rotate 2 base=2; } else if (alphamapflags & BLENDMAP_ROTATE270) { // rotate 3 base=3; } SBlendVertex vtx[4]; vtx[(base+0)%4].m_AlphaUVs[0]=u0; vtx[(base+0)%4].m_AlphaUVs[1]=v0; vtx[(base+1)%4].m_AlphaUVs[0]=u1; vtx[(base+1)%4].m_AlphaUVs[1]=v0; vtx[(base+2)%4].m_AlphaUVs[0]=u1; vtx[(base+2)%4].m_AlphaUVs[1]=v1; vtx[(base+3)%4].m_AlphaUVs[0]=u0; vtx[(base+3)%4].m_AlphaUVs[1]=v1; int vsize=PATCH_SIZE+1; SBlendVertex dst; int vindex=(int)m_BlendVertices.size(); const SBaseVertex& vtx0=m_Vertices[(j*vsize)+i]; dst.m_UVs[0]=i*0.125f; dst.m_UVs[1]=j*0.125f; dst.m_AlphaUVs[0]=vtx[0].m_AlphaUVs[0]; dst.m_AlphaUVs[1]=vtx[0].m_AlphaUVs[1]; dst.m_Color=vtx0.m_Color; dst.m_Position=vtx0.m_Position; m_BlendVertices.push_back(dst); const SBaseVertex& vtx1=m_Vertices[(j*vsize)+i+1]; dst.m_UVs[0]=(i+1)*0.125f; dst.m_UVs[1]=j*0.125f; dst.m_AlphaUVs[0]=vtx[1].m_AlphaUVs[0]; dst.m_AlphaUVs[1]=vtx[1].m_AlphaUVs[1]; dst.m_Color=vtx1.m_Color; dst.m_Position=vtx1.m_Position; m_BlendVertices.push_back(dst); const SBaseVertex& vtx2=m_Vertices[((j+1)*vsize)+i+1]; dst.m_UVs[0]=(i+1)*0.125f; dst.m_UVs[1]=(j+1)*0.125f; dst.m_AlphaUVs[0]=vtx[2].m_AlphaUVs[0]; dst.m_AlphaUVs[1]=vtx[2].m_AlphaUVs[1]; dst.m_Color=vtx2.m_Color; dst.m_Position=vtx2.m_Position; m_BlendVertices.push_back(dst); const SBaseVertex& vtx3=m_Vertices[((j+1)*vsize)+i]; dst.m_UVs[0]=i*0.125f; dst.m_UVs[1]=(j+1)*0.125f; dst.m_AlphaUVs[0]=vtx[3].m_AlphaUVs[0]; dst.m_AlphaUVs[1]=vtx[3].m_AlphaUVs[1]; dst.m_Color=vtx3.m_Color; dst.m_Position=vtx3.m_Position; m_BlendVertices.push_back(dst); // build a splat for this quad STmpSplat splat; splat.m_Texture=neighbourTextures[k].m_Handle; splat.m_Indices[0]=vindex; splat.m_Indices[1]=vindex+1; splat.m_Indices[2]=vindex+2; splat.m_Indices[3]=vindex+3; splats.push_back(splat); // add this texture to set of unique splat textures splatTextures.insert(splat.m_Texture); } } } } } // build vertex data if (m_VBBlends) { // release existing vertex buffer chunk g_VBMan.Release(m_VBBlends); } m_VBBlends=g_VBMan.Allocate(sizeof(SBlendVertex),m_BlendVertices.size(),false); m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]); // now build outgoing splats m_BlendSplats.resize(splatTextures.size()); int splatCount=0; u32 base=(u32)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 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_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; u32 mapSize=terrain->GetVerticesPerSide(); // build vertices for (int j=0;jCalcPosition(ix,iz,vertices[v].m_Position); terrain->CalcNormal(ix,iz,normal); g_Renderer.m_SHCoeffsTerrain.Evaluate(normal,c); 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; } } void CPatchRData::RenderBase() { 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]; g_Renderer.BindTexture(0,tex_id(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) { 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() { 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)); glClientActiveTexture(GL_TEXTURE0_ARB); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0])); glClientActiveTexture(GL_TEXTURE1_ARB); 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]; g_Renderer.BindTexture(0,tex_id(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::RenderOutline() { // TODO, RC - fixme, only works for PATCH_SIZE = 16 const u16 EdgeIndices[PATCH_SIZE*4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 33, 50, 67, 84, 101, 118, 135, 152, 169, 186, 203, 220, 237, 254, 271, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 255, 238, 221, 204, 187, 170, 153, 136, 119, 102, 85, 68, 51, 34, 17, 0 }; u8* base=m_VBBase->m_Owner->Bind(); // setup data pointers glVertexPointer(3,GL_FLOAT,sizeof(SBaseVertex),base+offsetof(SBaseVertex,m_Position)); // render outline as line loop u32 numIndices=sizeof(EdgeIndices)/sizeof(u16); glDrawElements(GL_LINE_LOOP,numIndices,GL_UNSIGNED_SHORT,EdgeIndices); g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_TerrainTris+=numIndices/2; } ///////////////////////////////////////////////////////////////////////////////////////////////////// // SubmitBaseBatches: submit base batches for this patch to the vertex buffer void CPatchRData::SubmitBaseBatches() { assert(m_VBBase); for (uint 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"); glActiveTexture(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); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_ZERO); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA); #if 1 // submit base batches for each patch to the vertex buffer MICROLOG(L"submitting %d patches", m_Patches.size()); for (i=0;iGetRenderData(); patchdata->SubmitBaseBatches(); } // render base passes for each patch MICROLOG(L"get buffer list"); const std::list& buffers=g_VBMan.GetBufferList(); std::list::const_iterator iter; for (iter=buffers.begin();iter!=buffers.end();++iter) { MICROLOG(L"bu"); CVertexBuffer* buffer=*iter; // any batches in this VB? const std::vector& batches=buffer->GetBatches(); if (batches.size()>0) { MICROLOG(L"bind"); u8* base=buffer->Bind(); // setup data pointers u32 stride=sizeof(SBaseVertex); MICROLOG(L"data pointers"); 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 MICROLOG(L"render batches"); for (i=0;im_IndexData.size()>0) { g_Renderer.BindTexture(0,tex_id(batch->m_Texture)); for (uint j=0;jm_IndexData.size();j++) { MICROLOG(L"el"); glDrawElements(GL_QUADS,(GLsizei)batch->m_IndexData[j].first,GL_UNSIGNED_SHORT,batch->m_IndexData[j].second); MICROLOG(L"."); 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 glClientActiveTexture(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE0); // switch on the composite alpha map texture g_Renderer.BindTexture(1,g_Renderer.m_CompositeAlphaMap); // 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)); glClientActiveTexture(GL_TEXTURE0); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0])); glClientActiveTexture(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) { g_Renderer.BindTexture(0,tex_id(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 texture unit 1, make unit 0 active texture g_Renderer.BindTexture(1,0); glActiveTexture(GL_TEXTURE0); // tidy up client states glDisableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE0_ARB); } ///////////////////////////////////////////////////////////////////////////////////////////// // 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; // glEnable(GL_ALPHA_TEST); // glAlphaFunc(GL_GREATER,0.0f); 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_ALPHA_TEST); glDisable(GL_BLEND); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); }