Ykkrosh
d39a4fac21
# Attempted to make compilation faster by including as little as possible in some .h files, and moving it into .cpp. Fixed BaseTechCollection memory leak. This was SVN commit r3992.
578 lines
16 KiB
C++
578 lines
16 KiB
C++
#include "precompiled.h"
|
|
|
|
|
|
#include <set>
|
|
#include <algorithm>
|
|
#include "ps/Pyrogenesis.h"
|
|
#include "lib/res/graphics/ogl_tex.h"
|
|
#include "graphics/LightEnv.h"
|
|
#include "Renderer.h"
|
|
#include "renderer/PatchRData.h"
|
|
#include "AlphaMapCalculator.h"
|
|
#include "ps/CLogger.h"
|
|
#include "ps/Profile.h"
|
|
#include "maths/MathUtil.h"
|
|
#include "simulation/LOSManager.h"
|
|
#include "graphics/Patch.h"
|
|
#include "graphics/Terrain.h"
|
|
|
|
const int 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 Handle GetTerrainTileTexture(CTerrain* terrain,int gx,int gz)
|
|
{
|
|
CMiniPatch* mp=terrain->GetTile(gx,gz);
|
|
return mp ? mp->Tex1 : 0;
|
|
}
|
|
|
|
const float uvFactor = 0.125f / sqrt(2.f);
|
|
static void CalculateUV(float uv[2], int x, int z)
|
|
{
|
|
// The UV axes are offset 45 degrees from XZ
|
|
uv[0] = ( x-z)*uvFactor;
|
|
uv[1] = (-x-z)*uvFactor;
|
|
}
|
|
|
|
struct STmpSplat {
|
|
Handle 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<STmpSplat> splats;
|
|
// set of textures used for splats
|
|
std::set<Handle> splatTextures;
|
|
|
|
// for each tile in patch ..
|
|
for (int j=0;j<PATCH_SIZE;j++) {
|
|
for (int i=0;i<PATCH_SIZE;i++) {
|
|
u32 gx,gz;
|
|
CMiniPatch* mp=&m_Patch->m_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<STex> 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->Tex1 != mp->Tex1) {
|
|
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;k<count;++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];
|
|
CalculateUV(dst.m_UVs, gx, gz);
|
|
dst.m_AlphaUVs[0]=vtx[0].m_AlphaUVs[0];
|
|
dst.m_AlphaUVs[1]=vtx[0].m_AlphaUVs[1];
|
|
dst.m_LOSColor=vtx0.m_LOSColor;
|
|
dst.m_Position=vtx0.m_Position;
|
|
m_BlendVertices.push_back(dst);
|
|
m_BlendVertexIndices.push_back((j*vsize)+i);
|
|
|
|
const SBaseVertex& vtx1=m_Vertices[(j*vsize)+i+1];
|
|
CalculateUV(dst.m_UVs, gx+1, gz);
|
|
dst.m_AlphaUVs[0]=vtx[1].m_AlphaUVs[0];
|
|
dst.m_AlphaUVs[1]=vtx[1].m_AlphaUVs[1];
|
|
dst.m_LOSColor=vtx1.m_LOSColor;
|
|
dst.m_Position=vtx1.m_Position;
|
|
m_BlendVertices.push_back(dst);
|
|
m_BlendVertexIndices.push_back((j*vsize)+i+1);
|
|
|
|
const SBaseVertex& vtx2=m_Vertices[((j+1)*vsize)+i+1];
|
|
CalculateUV(dst.m_UVs, gx+1, gz+1);
|
|
dst.m_AlphaUVs[0]=vtx[2].m_AlphaUVs[0];
|
|
dst.m_AlphaUVs[1]=vtx[2].m_AlphaUVs[1];
|
|
dst.m_LOSColor=vtx2.m_LOSColor;
|
|
dst.m_Position=vtx2.m_Position;
|
|
m_BlendVertices.push_back(dst);
|
|
m_BlendVertexIndices.push_back(((j+1)*vsize)+i+1);
|
|
|
|
const SBaseVertex& vtx3=m_Vertices[((j+1)*vsize)+i];
|
|
CalculateUV(dst.m_UVs, gx, gz+1);
|
|
dst.m_AlphaUVs[0]=vtx[3].m_AlphaUVs[0];
|
|
dst.m_AlphaUVs[1]=vtx[3].m_AlphaUVs[1];
|
|
dst.m_LOSColor=vtx3.m_LOSColor;
|
|
dst.m_Position=vtx3.m_Position;
|
|
m_BlendVertices.push_back(dst);
|
|
m_BlendVertexIndices.push_back(((j+1)*vsize)+i);
|
|
|
|
// build a splat for this quad
|
|
STmpSplat splat;
|
|
splat.m_Texture=neighbourTextures[k].m_Handle;
|
|
splat.m_Indices[0]=(u16)(vindex);
|
|
splat.m_Indices[1]=(u16)(vindex+1);
|
|
splat.m_Indices[2]=(u16)(vindex+2);
|
|
splat.m_Indices[3]=(u16)(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=0;
|
|
}
|
|
if (m_BlendVertices.size()) {
|
|
m_VBBlends=g_VBMan.Allocate(sizeof(SBlendVertex),m_BlendVertices.size(),true);
|
|
m_VBBlends->m_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<Handle>::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<Handle> textures;
|
|
Handle texgrid[PATCH_SIZE][PATCH_SIZE];
|
|
for (int j=0;j<PATCH_SIZE;j++) {
|
|
for (int i=0;i<PATCH_SIZE;i++) {
|
|
Handle h=m_Patch->m_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;j<PATCH_SIZE;j++) {
|
|
for (int i=0;i<PATCH_SIZE;i++) {
|
|
if (texgrid[j][i]==h){
|
|
m_Indices.push_back(((j+0)*vsize+(i+0))+base);
|
|
m_Indices.push_back(((j+0)*vsize+(i+1))+base);
|
|
m_Indices.push_back(((j+1)*vsize+(i+1))+base);
|
|
m_Indices.push_back(((j+1)*vsize+(i+0))+base);
|
|
}
|
|
}
|
|
}
|
|
splat.m_IndexCount=(u32)m_Indices.size()-splat.m_IndexStart;
|
|
}
|
|
|
|
// build indices for the shadow map pass
|
|
for (int j=0;j<PATCH_SIZE;j++) {
|
|
for (int i=0;i<PATCH_SIZE;i++) {
|
|
m_ShadowMapIndices.push_back(((j+0)*vsize+(i+0))+base);
|
|
m_ShadowMapIndices.push_back(((j+0)*vsize+(i+1))+base);
|
|
m_ShadowMapIndices.push_back(((j+1)*vsize+(i+1))+base);
|
|
m_ShadowMapIndices.push_back(((j+1)*vsize+(i+0))+base);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CPatchRData::BuildVertices()
|
|
{
|
|
// create both vertices and lighting colors
|
|
|
|
CVector3D normal;
|
|
|
|
// 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;
|
|
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
|
|
|
|
// build vertices
|
|
for (int j=0;j<vsize;j++) {
|
|
for (int i=0;i<vsize;i++) {
|
|
int ix=px*PATCH_SIZE+i;
|
|
int iz=pz*PATCH_SIZE+j;
|
|
int v=(j*vsize)+i;
|
|
|
|
// calculate vertex data
|
|
terrain->CalcPosition(ix,iz,vertices[v].m_Position);
|
|
*(uint32_t*)&vertices[v].m_LOSColor = 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);
|
|
*(u32*)&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
|
|
|
|
u32 px=m_Patch->m_X;
|
|
u32 pz=m_Patch->m_Z;
|
|
|
|
CTerrain* terrain=m_Patch->m_Parent;
|
|
int mapSize=terrain->GetVerticesPerSide();
|
|
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
|
|
int vsize=PATCH_SIZE+1;
|
|
|
|
// this is very similar to BuildVertices(), but just for color
|
|
for (int j=0;j<vsize;j++) {
|
|
for (int i=0;i<vsize;i++) {
|
|
int ix=px*PATCH_SIZE+i;
|
|
int iz=pz*PATCH_SIZE+j;
|
|
int v=(j*vsize)+i;
|
|
|
|
const int DX[] = {1,1,0,0};
|
|
const int DZ[] = {0,1,1,0};
|
|
SColor4ub losMod(255, 255, 255, 255);
|
|
|
|
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.R > 178)
|
|
losMod = SColor4ub(178, 178, 178, 255);
|
|
else if(s==LOS_UNEXPLORED && losMod.R > 0)
|
|
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(uint i=0; i<m_BlendVertices.size(); i++)
|
|
{
|
|
m_BlendVertices[i].m_LOSColor = m_Vertices[m_BlendVertexIndices[i]].m_LOSColor;
|
|
}
|
|
|
|
// upload blend vertices into their vertex buffer too
|
|
if(m_BlendVertices.size())
|
|
{
|
|
m_VBBlends->m_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
|
|
u32 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 (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, bool losColor)
|
|
{
|
|
debug_assert(m_UpdateFlags==0);
|
|
|
|
SBaseVertex* base=(SBaseVertex *)m_VBBase->m_Owner->Bind();
|
|
|
|
// setup data pointers
|
|
u32 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
|
|
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_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 (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::RenderOutline()
|
|
{
|
|
int i;
|
|
uint vsize=PATCH_SIZE+1;
|
|
|
|
glBegin(GL_LINES);
|
|
for (i=0;i<PATCH_SIZE;i++) {
|
|
glVertex3fv(&m_Vertices[i].m_Position.X);
|
|
glVertex3fv(&m_Vertices[i+1].m_Position.X);
|
|
}
|
|
glEnd();
|
|
glBegin(GL_LINES);
|
|
for (i=0;i<PATCH_SIZE;i++) {
|
|
glVertex3fv(&m_Vertices[PATCH_SIZE+(i*(PATCH_SIZE+1))].m_Position.X);
|
|
glVertex3fv(&m_Vertices[PATCH_SIZE+((i+1)*(PATCH_SIZE+1))].m_Position.X);
|
|
}
|
|
glEnd();
|
|
glBegin(GL_LINES);
|
|
for (i=1;i<PATCH_SIZE;i++) {
|
|
glVertex3fv(&m_Vertices[(vsize*vsize)-i].m_Position.X);
|
|
glVertex3fv(&m_Vertices[(vsize*vsize)-(i+1)].m_Position.X);
|
|
}
|
|
glEnd();
|
|
glBegin(GL_LINES);
|
|
for (i=1;i<PATCH_SIZE;i++) {
|
|
glVertex3fv(&m_Vertices[(vsize*(vsize-1))-(i*vsize)].m_Position.X);
|
|
glVertex3fv(&m_Vertices[(vsize*(vsize-1))-((i+1)*vsize)].m_Position.X);
|
|
}
|
|
glEnd();
|
|
}
|
|
|
|
|