/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include #include #include "ps/Pyrogenesis.h" #include "graphics/LightEnv.h" #include "Renderer.h" #include "renderer/PatchRData.h" #include "AlphaMapCalculator.h" #include "ps/CLogger.h" #include "ps/Profile.h" #include "ps/Game.h" #include "ps/World.h" #include "maths/MathUtil.h" #include "graphics/Patch.h" #include "graphics/Terrain.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpRangeManager.h" const ssize_t BlendOffsets[8][2] = { { 0, -1 }, { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 } }; /////////////////////////////////////////////////////////////////// // 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 CTerrainTextureEntry* GetTerrainTileTexture(CTerrain* terrain, ssize_t gx, ssize_t gz) { CMiniPatch* mp = terrain->GetTile(gx, gz); if (!mp) return 0; return mp->GetTextureEntry(); } const float uvFactor = 0.125f / sqrt(2.f); static void CalculateUV(float uv[2], ssize_t x, ssize_t z) { // The UV axes are offset 45 degrees from XZ uv[0] = ( x-z)*uvFactor; uv[1] = (-x-z)*uvFactor; } struct STmpSplat { CTerrainTextureEntry* m_Texture; u16 m_Indices[4]; }; void CPatchRData::BuildBlends() { m_BlendIndices.clear(); m_BlendSplats.clear(); m_BlendVertices.clear(); m_BlendVertexIndices.clear(); 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 (ssize_t j=0;jm_MiniPatches[j][i]; ssize_t gx = m_Patch->m_X * PATCH_SIZE + i; ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j; // 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 && nmp->GetTextureEntry() != mp->GetTextureEntry()) { if (nmp->GetPriority() > mp->GetPriority() || (nmp->GetPriority() == mp->GetPriority() && nmp->GetTextureEntry() > mp->GetTextureEntry())) { STex tex; tex.m_Texture=nmp->GetTextureEntry(); tex.m_Priority=nmp->GetPriority(); 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 .. size_t count=neighbourTextures.size(); for (size_t k=0;km_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]); // now build outgoing splats m_BlendSplats.resize(splatTextures.size()); size_t 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) { CTerrainTextureEntry* tex=*iter; SSplat& splat=m_BlendSplats[splatCount]; splat.m_IndexStart=m_BlendIndices.size(); splat.m_Texture=tex; for (size_t k=0;k textures; CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE]; for (ssize_t j=0;jm_MiniPatches[j][i].GetTextureEntry(); texgrid[j][i]=tex; if (std::find(textures.begin(),textures.end(),tex)==textures.end()) { textures.push_back(tex); } } } // now build base splats from interior textures m_Splats.resize(textures.size()); // build indices for base splats size_t base=m_VBBase->m_Index; for (size_t i=0;im_X; ssize_t pz=m_Patch->m_Z; CTerrain* terrain=m_Patch->m_Parent; const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); // build vertices for (ssize_t j=0;jCalcPosition(ix,iz,vertices[v].m_Position); vertices[v].m_LOSColor = SColor4ub(0, 0, 0, 0); // will be set to the proper value in Update() CalculateUV(vertices[v].m_UVs, ix, iz); // Calculate diffuse lighting for this vertex // Ambient is added by the lighting pass (since ambient is the same // for all vertices, it need not be stored in the vertex structure) terrain->CalcNormal(ix,iz,normal); RGBColor diffuse; lightEnv.EvaluateDirect(normal, diffuse); vertices[v].m_DiffuseColor = ConvertRGBColorTo4ub(diffuse); } } // upload to vertex buffer if (!m_VBBase) { m_VBBase=g_VBMan.Allocate(sizeof(SBaseVertex),vsize*vsize,true); } 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; } // Update vertex colors, which are affected by LOS ssize_t px=m_Patch->m_X; ssize_t pz=m_Patch->m_Z; CTerrain* terrain=m_Patch->m_Parent; ssize_t vsize=PATCH_SIZE+1; SColor4ub baseColour = terrain->GetBaseColour(); CmpPtr cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); if (cmpRangeManager.null() || cmpRangeManager->GetLosRevealAll(g_Game->GetPlayerID())) { for (ssize_t j = 0; j < vsize; ++j) { for (ssize_t i = 0; i < vsize; ++i) { ssize_t v = (j*vsize)+i; m_Vertices[v].m_LOSColor = baseColour; } } } else { ICmpRangeManager::CLosQuerier los (cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID())); // this is very similar to BuildVertices(), but just for color for (ssize_t j = 0; j < vsize; j++) { for (ssize_t i = 0; i < vsize; i++) { ssize_t ix = px * PATCH_SIZE + i; ssize_t iz = pz * PATCH_SIZE + j; ssize_t v = (j * vsize) + i; SColor4ub losMod; if (los.IsVisible(ix, iz)) losMod = baseColour; else if (los.IsExplored(ix, iz)) losMod = SColor4ub(178, 178, 178, 255); else losMod = SColor4ub(0, 0, 0, 255); m_Vertices[v].m_LOSColor = losMod; } } } // upload base vertices into their vertex buffer m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices); // update blend colors by copying them from vertex colors for(size_t i=0; im_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]); } } void CPatchRData::RenderBase(bool losColor) { debug_assert(m_UpdateFlags==0); SBaseVertex *base=(SBaseVertex *)m_VBBase->m_Owner->Bind(); // setup data pointers GLsizei stride=sizeof(SBaseVertex); glVertexPointer(3,GL_FLOAT,stride,&base->m_Position[0]); glColorPointer(4,GL_UNSIGNED_BYTE,stride,losColor ? &base->m_LOSColor : &base->m_DiffuseColor); glTexCoordPointer(2,GL_FLOAT,stride,&base->m_UVs[0]); // render each splat for (size_t i=0;iGetTexture()->Bind(); else g_Renderer.GetTextureManager().GetErrorTexture()->Bind(); if (!g_Renderer.m_SkipSubmit) { glDrawElements(GL_QUADS, (GLsizei)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; } CVertexBuffer::Unbind(); } void CPatchRData::RenderStreams(int streamflags, bool losColor) { debug_assert(m_UpdateFlags==0); SBaseVertex* base=(SBaseVertex *)m_VBBase->m_Owner->Bind(); // setup data pointers GLsizei stride=sizeof(SBaseVertex); glVertexPointer(3, GL_FLOAT, stride, &base->m_Position); if (streamflags & STREAM_UV0) { glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs); } else if (streamflags & STREAM_POSTOUV0) { glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position); } if (streamflags & STREAM_COLOR) { glColorPointer(4,GL_UNSIGNED_BYTE,stride,losColor ? &base->m_LOSColor : &base->m_DiffuseColor); } // render all base splats at once if (!g_Renderer.m_SkipSubmit) { 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+=m_Indices.size()/2; CVertexBuffer::Unbind(); } void CPatchRData::RenderBlends() { debug_assert(m_UpdateFlags==0); if (m_BlendVertices.size()==0) return; u8* base=m_VBBlends->m_Owner->Bind(); // setup data pointers GLsizei stride=sizeof(SBlendVertex); // ((GCC warns about offsetof: SBlendVertex contains a CVector3D which has // a constructor, and so is not a POD type, and so offsetof is theoretically // invalid - see http://gcc.gnu.org/ml/gcc/2003-11/msg00281.html - but it // doesn't seem to be worth changing this code since it works anyway.)) glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_Position)); glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBlendVertex,m_LOSColor)); pglClientActiveTextureARB(GL_TEXTURE0); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0])); pglClientActiveTextureARB(GL_TEXTURE1); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_AlphaUVs[0])); for (size_t i=0;iGetTexture()->Bind(); else g_Renderer.GetTextureManager().GetErrorTexture()->Bind(); if (!g_Renderer.m_SkipSubmit) { glDrawElements(GL_QUADS, (GLsizei)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; } CVertexBuffer::Unbind(); } void CPatchRData::RenderOutline() { size_t vsize=PATCH_SIZE+1; glBegin(GL_LINES); for (ssize_t i=0;i