#include "precompiled.h" #include #include #include #include "res/tex.h" #include "Renderer.h" #include "PatchRData.h" #include "AlphaMapCalculator.h" const int BlendOffsets[8][2] = { { 0, -1 }, { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 } }; CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_Vertices(0), m_VBBase(0), m_VBBlends(0) { assert(patch); Build(); } CPatchRData::~CPatchRData() { delete[] m_Vertices; if (m_VBBase) glDeleteBuffersARB(1,(GLuint*) &m_VBBase); if (m_VBBlends) glDeleteBuffersARB(1,(GLuint*) &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=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::iterator iter=splatTextures.begin(); for (;iter!=splatTextures.end();++iter) { Handle tex=*iter; SSplat& splat=m_BlendSplats[splatCount]; splat.m_IndexStart=m_BlendIndices.size(); splat.m_Texture=tex; for (uint k=0;k 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()); for (uint i=0;imax) 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; } static void BuildHeightmapNormals(int size,u16 *heightmap,CVector3D* normals) { int x, y; int sm=size-1; for(y = 0;y < size; y++) for(x = 0; x < size; x++) { // Access current normalmap grid point CVector3D* N = &normals[y*size+x]; // Compute normal by using the height differential u16 h1=(x==sm) ? heightmap[y*size+x] : heightmap[y*size+x+1]; u16 h2=(y==sm) ? heightmap[y*size+x] : heightmap[(y+1)*size+x]; u16 h3=(x==0) ? heightmap[y*size+x] : heightmap[y*size+x-1]; u16 h4=(y==0) ? heightmap[y*size+x] : heightmap[(y-1)*size+x+1]; N->X = (h3-h1)*HEIGHT_SCALE; N->Y = CELL_SIZE; N->Z = (h4-h2)*HEIGHT_SCALE; // Normalize it float len=N->GetLength(); if (len>0) { (*N)*=1.0f/len; } else { *N=CVector3D(0,0,0); } } } void CPatchRData::BuildVertices() { // number of vertices in each direction in each patch int vsize=PATCH_SIZE+1; if (!m_Vertices) { m_Vertices=new SBaseVertex[vsize*vsize]; } SBaseVertex* vertices=m_Vertices; // get index of this patch u32 px=m_Patch->m_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,pos); terrain->CalcNormal(ix,iz,normal); RGBColor c; g_Renderer.m_SHCoeffsTerrain.Evaluate(normal,c); int v=(j*vsize)+i; vertices[v].m_UVs[0]=i*0.125f; vertices[v].m_UVs[1]=j*0.125f; vertices[v].m_Color=ConvertColor(c); vertices[v].m_Position=pos; } } if (g_Renderer.m_Caps.m_VBO) { if (!m_VBBase) { glGenBuffersARB(1,(GLuint*) &m_VBBase); glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VBBase); glBufferDataARB(GL_ARRAY_BUFFER_ARB,vsize*vsize*sizeof(SBaseVertex),0,GL_STATIC_DRAW_ARB); } glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VBBase); glBufferSubDataARB(GL_ARRAY_BUFFER_ARB,0,vsize*vsize*sizeof(SBaseVertex),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; if (g_Renderer.m_Caps.m_VBO) { glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VBBase); base=0; } else { base=(u8*) &m_Vertices[0]; } // 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