Serious-Engine/Sources/Engine/Terrain/TerrainTile.cpp
2016-03-11 18:20:51 -06:00

1000 lines
33 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program 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 this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
#include "stdh.h"
#include <Engine/Terrain/TerrainTile.h>
#include <Engine/Terrain/Terrain.h>
#include <Engine/Terrain/TerrainRender.h>
#include <Engine/Terrain/TerrainMisc.h>
extern CTerrain *_ptrTerrain;
extern FLOAT3D _vViewerAbs;
#define BORDERTEST 0
extern CStaticStackArray<GFXVertex4> _avLerpedVerices;
CTerrainTile::CTerrainTile()
{
tt_iIndex = -1;
tt_iArrayIndex = -1;
tt_iLod = -1;
tt_iRequestedLod = 0;
tt_ulTileFlags = 0;
}
// Render tile
void CTerrainTile::Render(void)
{
ASSERT(FALSE);
}
CTerrainTile::~CTerrainTile()
{
Clear();
}
// Release tile
void CTerrainTile::Clear()
{
}
// TEMP!!!!!
__forceinline void CTerrainTile::LerpVertexPos(GFXVertex4 &vtx, INDEX iVxTarget, INDEX iVxFirst,INDEX iVxLast)
{
GFXVertex4 &vxFirst = GetVertices()[iVxFirst];
GFXVertex4 &vxLast = GetVertices()[iVxLast];
GFXVertex4 &vxTarget = GetVertices()[iVxTarget];
FLOAT fLerpMaxPosY = Lerp(vxFirst.y,vxLast.y,0.5f);
FLOAT fLerpResultY = Lerp(vxTarget.y,fLerpMaxPosY,tt_fLodLerpFactor);
vtx.x = vxTarget.x;
vtx.y = fLerpResultY;
vtx.z = vxTarget.z;
}
/*
* Tile memory alloc
*/
INDEX CTerrainTile::ChangeTileArrays(INDEX iRequestedArrayLod)
{
// if requested lod is same as current lod
if(iRequestedArrayLod==tt_iLod) {
// Just pop all arrays
GetVertices().PopAll();
GetTexCoords().PopAll();
GetShadowMapTC().PopAll();
GetIndices().PopAll();
// if tile is in highest lod
if(tt_iLod==0) {
// pop detail uvmap
GetDetailTC().PopAll();
// for each tile layer
INDEX cttl=GetTileLayers().Count();
for(INDEX itl=0;itl<cttl;itl++) {
// pop all arrays for this layer
TileLayer &tl = GetTileLayers()[itl];
tl.tl_acColors.PopAll();
tl.tl_atcTexCoords.PopAll();
tl.tl_auiIndices.PopAll();
tl.tl_avVertices.PopAll();
}
}
return tt_iLod;
}
// release current tile arrays
ReleaseTileArrays();
// Allocate new arrays for new lod
CArrayHolder &ah = _ptrTerrain->tr_aArrayHolders[iRequestedArrayLod];
ASSERT(tt_iArrayIndex==-1);
tt_iArrayIndex = ah.GetNewArrays();
// if this is first lod
if(iRequestedArrayLod==0) {
// Add tile layers
INDEX ctLayers = _ptrTerrain->tr_atlLayers.Count();
if(ctLayers>0) {
GetTileLayers().Push(ctLayers);
}
}
return iRequestedArrayLod;
}
void CTerrainTile::ReleaseTileArrays()
{
// if tile had some arrays
if(tt_iArrayIndex != -1) {
// Free them
CArrayHolder &ahOld = _ptrTerrain->tr_aArrayHolders[tt_iLod];
ahOld.FreeArrays(tt_iArrayIndex);
tt_iArrayIndex = -1;
}
}
void CTerrainTile::EmptyTileArrays()
{
ASSERT(tt_iArrayIndex != -1);
CArrayHolder &ahCurrent = _ptrTerrain->tr_aArrayHolders[tt_iLod];
ahCurrent.EmptyArrays(tt_iArrayIndex);
}
/*
* Tile generation (TEMP)
*/
inline void CTerrainTile::AddTriangle(INDEX iind1,INDEX iind2,INDEX iind3)
{
// is this tile in highest lod
if(tt_iLod==0) {
// Is this triangle visible
GFXVertex *pvx[3];
pvx[0] = &GetVertices()[iind1];
pvx[1] = &GetVertices()[iind2];
pvx[2] = &GetVertices()[iind3];
// check if all vertices all visible
SLONG slTriangleMask = pvx[0]->shade + pvx[1]->shade + pvx[2]->shade;
if(slTriangleMask!=255*3) {
return;
}
// Add one triangle
INDEX *pIndices = GetIndices().Push(3);
pIndices[0] = iind1;
pIndices[1] = iind2;
pIndices[2] = iind3;
// for each layer
INDEX cttl = GetTileLayers().Count();
for(INDEX itl=0;itl<cttl;itl++) {
TileLayer &ttl = GetTileLayers()[itl];
CTerrainLayer &tl = _ptrTerrain->tr_atlLayers[itl];
// if this is tile layer
if(tl.tl_ltType==LT_TILE) {
continue; // skip it
}
COLOR ul = ttl.tl_acColors[iind1].a + ttl.tl_acColors[iind2].a + ttl.tl_acColors[iind3].a;
if(ul>0) {
INDEX *pIndices = ttl.tl_auiIndices.Push(3);
pIndices[0] = iind1;
pIndices[1] = iind2;
pIndices[2] = iind3;
}
}
} else {
INDEX *pIndices = GetIndices().Push(3);
pIndices[0] = iind1;
pIndices[1] = iind2;
pIndices[2] = iind3;
}
}
// Returns a height in heightmap
inline FLOAT GetHeight(INDEX ic,INDEX ir,INDEX iTileIndex)
{
CTerrainTile &tt = _ptrTerrain->tr_attTiles[iTileIndex];
// Get Y position of vertex from heightmap
INDEX icHMap = ic + tt.tt_iOffsetX*_ptrTerrain->GetQuadsPerTileRow();
INDEX irHMap = ir + tt.tt_iOffsetZ*_ptrTerrain->GetQuadsPerTileRow();
INDEX ivx = icHMap+irHMap*_ptrTerrain->tr_pixHeightMapWidth;
return (FLOAT)_ptrTerrain->tr_auwHeightMap[ivx];
}
BYTE GetVertexAlpha(INDEX ic,INDEX ir,INDEX iTileIndex,INDEX iLayer)
{
CTerrainTile &tt = _ptrTerrain->tr_attTiles[iTileIndex];
INDEX icHMap = ic + tt.tt_iOffsetX*_ptrTerrain->GetQuadsPerTileRow();
INDEX irHMap = ir + tt.tt_iOffsetZ*_ptrTerrain->GetQuadsPerTileRow();
INDEX ivx = icHMap+irHMap*_ptrTerrain->tr_pixHeightMapWidth;
CTerrainLayer &tl = _ptrTerrain->tr_atlLayers[iLayer];
return tl.tl_aubColors[ivx];
}
// Returns vertex at specified position inside one tile
GFXVertex4 GetVertex(INDEX ic,INDEX ir,INDEX iTileIndex)
{
CTerrainTile &tt = _ptrTerrain->tr_attTiles[iTileIndex];
FLOAT fPosY = GetHeight(ic,ir,iTileIndex);
GFXVertex4 vx;
INDEX ix = ic + tt.tt_iOffsetX*_ptrTerrain->GetQuadsPerTileRow();
INDEX iz = ir + tt.tt_iOffsetZ*_ptrTerrain->GetQuadsPerTileRow();
vx.x = (FLOAT)(ix);
vx.z = (FLOAT)(iz);
vx.y = fPosY;
// Fill 'shade' with edge map value
INDEX iMask = ix + iz*_ptrTerrain->tr_pixHeightMapWidth;
vx.shade = _ptrTerrain->tr_aubEdgeMap[iMask];
return vx;
}
// Add vertex to array of vertices
void CTerrainTile::AddVertex(INDEX ic, INDEX ir)
{
GFXVertex4 &vxFinal = GetVertices().Push();
GFXTexCoord &tcShadow = GetShadowMapTC().Push();
GFXVertex4 &vx = GetVertex(ic,ir,tt_iIndex);
vxFinal.x = vx.x * _ptrTerrain->tr_vStretch(1);
vxFinal.y = vx.y * _ptrTerrain->tr_vStretch(2);
vxFinal.z = vx.z * _ptrTerrain->tr_vStretch(3);
vxFinal.shade = vx.shade;
// if this tile is in highest lod
if(tt_iLod==0) {
// for each layer
INDEX cttl = GetTileLayers().Count();
for(INDEX itl=0;itl<cttl;itl++) {
TileLayer &ttl = GetTileLayers()[itl];
CTerrainLayer &tl = _ptrTerrain->tr_atlLayers[itl];
// Set vertex color
GFXColor &col = ttl.tl_acColors.Push();
BYTE bAlpha = GetVertexAlpha(ic,ir,tt_iIndex,itl);
col.abgr = 0x00FFFFFF;
col.a = bAlpha;
// if this is normal layer
if(tl.tl_ltType == LT_NORMAL) {
// Set its texcoords
GFXTexCoord &tc = ttl.tl_atcTexCoords.Push();
tc.u = (FLOAT)ic;
tc.v = (FLOAT)ir;
}
}
GFXTexCoord &tcDetail = GetDetailTC().Push();
tcDetail.u = ic * 2;
tcDetail.v = ir * 2;
// if tile is in lowest lod
} else if(tt_iLod==_ptrTerrain->tr_iMaxTileLod) {
GFXTexCoord &tc = GetTexCoords().Push();
FLOAT fWidth = (_ptrTerrain->tr_pixHeightMapWidth-1);
FLOAT fHeight = (_ptrTerrain->tr_pixHeightMapHeight-1);
tc.u = vx.x / fWidth;
tc.v = vx.z / fHeight;
// tile is not in highest lod nor in lowest lod
} else {
GFXTexCoord &tc = GetTexCoords().Push();
tc.u = ((vx.x - tt_iOffsetX * _ptrTerrain->GetQuadsPerTileRow()) / (_ptrTerrain->GetQuadsPerTileRow()));
tc.v = ((vx.z - tt_iOffsetZ * _ptrTerrain->GetQuadsPerTileRow()) / (_ptrTerrain->GetQuadsPerTileRow()));
}
tcShadow.u = vx.x / (_ptrTerrain->tr_pixHeightMapWidth-1);
tcShadow.v = vx.z / (_ptrTerrain->tr_pixHeightMapHeight-1);
}
void CTerrainTile::ReGenerateTileLayer(INDEX iTileLayer)
{
FLOAT fStrX = _ptrTerrain->tr_vStretch(1);
FLOAT fStrY = _ptrTerrain->tr_vStretch(2);
FLOAT fStrZ = _ptrTerrain->tr_vStretch(3);
PIX pixHMWidth = _ptrTerrain->tr_pixHeightMapWidth;
PIX iOffsetX = tt_iOffsetX * _ptrTerrain->tr_ctQuadsInTileRow;
PIX iOffsetZ = tt_iOffsetZ * _ptrTerrain->tr_ctQuadsInTileRow;
INDEX ctQuadsPerRow = _ptrTerrain->tr_ctQuadsInTileRow;
CTerrainLayer &tl = _ptrTerrain->tr_atlLayers[iTileLayer];
TileLayer &ttl = GetTileLayers()[iTileLayer];
INDEX ctVertices = ctQuadsPerRow*ctQuadsPerRow*4; // four vertices per one quad
INDEX ctIndices = ctQuadsPerRow*ctQuadsPerRow*6; // six indices per one quad
ASSERT(ttl.tl_avVertices.Count()==0 && ttl.tl_atcTexCoords.Count()==0 && ttl.tl_auiIndices.Count()==0);
GFXVertex *pvtx = ttl.tl_avVertices.Push(ctVertices);
GFXTexCoord *ptc = ttl.tl_atcTexCoords.Push(ctVertices);
INDEX *pind = ttl.tl_auiIndices.Push(ctIndices);
UBYTE *pubMask = tl.tl_aubColors;
INDEX ivx = 0;
INDEX iind = 0;
BOOL bFacing = FALSE;
INDEX iTilesInRowLog2 = FastLog2(tl.tl_ctTilesInRow);
// for each quad in tile
for(INDEX iz=0;iz<ctQuadsPerRow;iz++) {
for(INDEX ix=0;ix<ctQuadsPerRow;ix++) {
PIX pix = ix+iOffsetX + (iz+iOffsetZ)*pixHMWidth;
// Add four vertices for this quad
pvtx[ivx ].x = (FLOAT)(iOffsetX+ix+0)*fStrX;
pvtx[ivx ].y = (FLOAT)_ptrTerrain->tr_auwHeightMap[pix] * fStrY;
pvtx[ivx ].z = (FLOAT)(iOffsetZ+iz+0)*fStrZ;
pvtx[ivx+1].x = (FLOAT)(iOffsetX+ix+1)*fStrX;
pvtx[ivx+1].y = (FLOAT)_ptrTerrain->tr_auwHeightMap[pix+1] * fStrY;
pvtx[ivx+1].z = (FLOAT)(iOffsetZ+iz+0)*fStrZ;
pvtx[ivx+2].x = (FLOAT)(iOffsetX+ix+0)*fStrX;
pvtx[ivx+2].y = (FLOAT)_ptrTerrain->tr_auwHeightMap[pix+pixHMWidth] * fStrY;
pvtx[ivx+2].z = (FLOAT)(iOffsetZ+iz+1)*fStrZ;
pvtx[ivx+3].x = (FLOAT)(iOffsetX+ix+1)*fStrX;
pvtx[ivx+3].y = (FLOAT)_ptrTerrain->tr_auwHeightMap[pix+pixHMWidth+1] * fStrY;
pvtx[ivx+3].z = (FLOAT)(iOffsetZ+iz+1)*fStrZ;
UBYTE ubMask = pubMask[pix];
INDEX iTile = ubMask&TL_TILE_INDEX; // First 4 bits
BOOL bFlipX = (ubMask&TL_FLIPX)>>TL_FLIPX_SHIFT;
BOOL bFlipY = (ubMask&TL_FLIPY)>>TL_FLIPY_SHIFT;
BOOL bSwapXY = (ubMask&TL_SWAPXY)>>TL_SWAPXY_SHIFT;
BOOL bVisible = (ubMask&TL_VISIBLE)>>TL_VISIBLE_SHIFT;
INDEX iTileX = iTile&(tl.tl_ctTilesInRow-1);
INDEX iTileY = iTile>>iTilesInRowLog2;
ASSERT(iTileX<tl.tl_ctTilesInRow);
ASSERT(iTileY<tl.tl_ctTilesInCol);
// Add four texcoords
ptc[ivx ].u = tl.tl_fTileU * (iTileX + bFlipX);
ptc[ivx ].v = tl.tl_fTileV * (iTileY + bFlipY);
ptc[ivx+1].u = tl.tl_fTileU * (iTileX + 1-bFlipX);
ptc[ivx+1].v = tl.tl_fTileV * (iTileY + bFlipY);
ptc[ivx+2].u = tl.tl_fTileU * (iTileX + bFlipX);
ptc[ivx+2].v = tl.tl_fTileV * (iTileY + 1-bFlipY);
ptc[ivx+3].u = tl.tl_fTileU * (iTileX + 1-bFlipX);
ptc[ivx+3].v = tl.tl_fTileV * (iTileY + 1-bFlipY);
if(bSwapXY) {
Swap(ptc[ivx+1].u,ptc[ivx+2].u);
Swap(ptc[ivx+1].v,ptc[ivx+2].v);
}
// if tile is visible
if(bVisible) {
// Add six indices
if(bFacing) {
pind[iind ] = ivx;
pind[iind+1] = ivx+2;
pind[iind+2] = ivx+1;
pind[iind+3] = ivx+1;
pind[iind+4] = ivx+2;
pind[iind+5] = ivx+3;
} else {
pind[iind ] = ivx+2;
pind[iind+1] = ivx+3;
pind[iind+2] = ivx;
pind[iind+3] = ivx;
pind[iind+4] = ivx+3;
pind[iind+5] = ivx+1;
}
iind+=6;
} else {
ctIndices-=6;
}
ivx+=4;
bFacing=!bFacing;
}
bFacing=!bFacing;
}
// discard triangles that arn't visible
if(ctIndices<ctQuadsPerRow*ctQuadsPerRow*6) {
ttl.tl_auiIndices.PopUntil(ctIndices);
}
// discart vertices that arn't visible
if(ctVertices<ctQuadsPerRow*ctQuadsPerRow*4) {
ttl.tl_avVertices.PopUntil(ctVertices);
ttl.tl_atcTexCoords.PopUntil(ctVertices);
}
ASSERT(ivx==ctVertices);
ASSERT(iind==ctIndices);
}
// Regenerate tile
void CTerrainTile::ReGenerate()
{
// remember lod before regen
INDEX iOldLod = tt_iLod;
// Allocate arrays for requested lod
tt_iLod = ChangeTileArrays(tt_iRequestedLod);
// for each vertex in row
INDEX iStep = 1<<tt_iLod;
INDEX ir=0;
for(;ir<tt_ctVtxY;ir+=iStep) {
// for each vertex in col
for(INDEX ic=0;ic<tt_ctVtxX;ic+=iStep) {
// add vertex in this row and col
AddVertex(ic,ir);
}
}
// Calculate number of quads for this lod
INDEX ctQuads = _ptrTerrain->GetQuadsPerTileRow()>>tt_iLod;
// Fill middle of tile with triangles
for(ir=1;ir<ctQuads-1;ir++) {
for(INDEX ic=1;ic<ctQuads-1;ic++) {
INDEX ivx = ic+ir*(ctQuads+1);
if(ivx%2) {
AddTriangle(ivx,ivx+ctQuads+1,ivx+1);
AddTriangle(ivx+1,ivx+ctQuads+1,ivx+ctQuads+2);
} else {
AddTriangle(ivx+ctQuads+1,ivx+ctQuads+2,ivx);
AddTriangle(ivx,ivx+ctQuads+2,ivx+1);
}
}
}
INDEX ctVtxBefore = GetVertices().Count();
INDEX ctTrisBefore = GetIndices().Count()/3;
// tt_ctNormalVertices = GetVertexCount();
// Generate borders for tile
ReGenerateTopBorder();
ReGenerateLeftBorder();
ReGenerateBottomBorder();
ReGenerateRightBorder();
// if this tile is in first lod
if(tt_iLod==0) {
// for each layer
INDEX cttl = _ptrTerrain->tr_atlLayers.Count();
for(INDEX itl=0;itl<cttl;itl++) {
CTerrainLayer &tl = _ptrTerrain->tr_atlLayers[itl];
// if this is tile layer
if(tl.tl_ltType == LT_TILE) {
// Regenerate it
ReGenerateTileLayer(itl);
}
}
}
BOOL bAllowTopMapRegen = !(GetFlags()&TT_NO_TOPMAP_REGEN);
// if top map is allowed to be regenerated
if(bAllowTopMapRegen) {
// if tile is not in highest nor in lowest lod
if(tt_iLod>0 && tt_iLod<_ptrTerrain->tr_iMaxTileLod) {
// if top map regen is forced or tile has changed lod
BOOL bForceTopMapRegen = (GetFlags()&TT_FORCE_TOPMAP_REGEN);
if(bForceTopMapRegen || iOldLod!=tt_iLod) {
// Update tile top map
_ptrTerrain->UpdateTopMap(tt_iIndex);
// remove flag that forced top map regen
RemoveFlag(TT_FORCE_TOPMAP_REGEN);
// allow terrain to regenerete top map
_ptrTerrain->AddFlag(TR_ALLOW_TOP_MAP_REGEN);
}
}
// if not
} else {
// regenerate it next time
RemoveFlag(TT_NO_TOPMAP_REGEN);
}
// if flag to resize quad tree node has been set
if(GetFlags()&TT_QUADTREENODE_REGEN) {
// update quad tree node
UpdateQuadTreeNode();
// node has been updated
RemoveFlag(TT_QUADTREENODE_REGEN);
}
INDEX ctBorderVertices = tt_ctBorderVertices[0] + tt_ctBorderVertices[1] +
tt_ctBorderVertices[2] + tt_ctBorderVertices[3];
// if tile is in lowest lod, has not lerp factor and no border vertices
if(tt_iLod==_ptrTerrain->tr_iMaxTileLod && tt_fLodLerpFactor==0.0f && ctBorderVertices == 0) {
// mark it as available for batch rendering
AddFlag(TT_IN_LOWEST_LOD);
} else {
RemoveFlag(TT_IN_LOWEST_LOD);
}
}
INDEX CTerrainTile::CalculateLOD(void)
{
QuadTreeNode &qtn = _ptrTerrain->tr_aqtnQuadTreeNodes[tt_iIndex];
FLOAT fDistance = (qtn.qtn_aabbox.Center() - _vViewerAbs).Length() - qtn.qtn_aabbox.Size().Length() / 2;
// if flag has been set for tile to regenerate without lod
if(GetFlags()&TT_NO_LODING) {
// set new lod at 0
fDistance = 0;
// if tile is in highest lod, no need to regenerate texture
// AddFlag(TT_NO_TOPMAP_REGEN);
// remove flag for no loding
RemoveFlag(TT_NO_LODING);
}
// Calculate new lod
INDEX iNewLod = Clamp((INDEX)(fDistance/_ptrTerrain->tr_fDistFactor),(INDEX)0,_ptrTerrain->tr_iMaxTileLod);
// if lod has changed
if(iNewLod!=tt_iLod) {
// add to regeneration queue
_ptrTerrain->AddTileToRegenQueue(tt_iIndex);
// for each neighbour
for(INDEX in=0;in<4;in++) {
INDEX ini = tt_aiNeighbours[in];
// if neighbour is valid
if(ini>=0) {
CTerrainTile &ttNeigbour = _ptrTerrain->tr_attTiles[ini];
// if neighbour is in higher lod
if(TRUE) { /*ttNeigbour.tt_iLod > tt.tt_iNewLod*/
// add neighbour to regen queue
_ptrTerrain->AddTileToRegenQueue(ini);
}
}
}
// Calculate num of vertices for row and col in current lod
tt_ctLodVtxX = (_ptrTerrain->GetQuadsPerTileRow() >> iNewLod) + 1;
tt_ctLodVtxY = (_ptrTerrain->GetQuadsPerTileRow() >> iNewLod) + 1;
}
// Calculate lerp factor
tt_fLodLerpFactor = Clamp(fDistance/_ptrTerrain->tr_fDistFactor - iNewLod,0.0f,1.0f);
// if tile is in lowest lod
if(iNewLod == _ptrTerrain->tr_iMaxTileLod) {
// no lerping for this tile
tt_fLodLerpFactor = 0.0f;
}
// return new lod
return iNewLod;
}
// Update quad tree node
void CTerrainTile::UpdateQuadTreeNode()
{
// resize aabox for this node
FLOATaabbox3D bboxNewBox;
GFXVertex4 *pavVertices;
INDEX *paiIndices;
INDEX ctVertices;
INDEX ctIndices;
QuadTreeNode &qtn = _ptrTerrain->tr_aqtnQuadTreeNodes[tt_iIndex];
// prepare box that will extract vertices (x and z of old box are allready valid)
bboxNewBox = qtn.qtn_aabbox;
bboxNewBox.minvect(2) = 0;
bboxNewBox.maxvect(2) = 65536 * _ptrTerrain->tr_vStretch(2);
// extract vertices in box
ExtractPolygonsInBox(_ptrTerrain,bboxNewBox,&pavVertices,&paiIndices,ctVertices,ctIndices);
// if some vertices exists
if(ctVertices>0) {
qtn.qtn_aabbox = FLOAT3D(pavVertices->x,pavVertices->y,pavVertices->z);
pavVertices++;
} else {
ASSERTALWAYS("Some vertices must exisits for tile bbox");
}
// for each vertex in box after first
for(INDEX ivx=1;ivx<ctVertices;ivx++) {
// add vertex to box
qtn.qtn_aabbox |= FLOAT3D(pavVertices->x,pavVertices->y,pavVertices->z);
pavVertices++;
}
// notify terrain that it needs to update higher levels of quad tree
_ptrTerrain->AddFlag(TR_REBUILD_QUADTREE);
}
// Regenerate top border
void CTerrainTile::ReGenerateTopBorder()
{
INDEX iTopTileIndex = tt_aiNeighbours[NB_TOP];
INDEX iTopBorderLod = tt_iLod;
// If top neighbour exists
if(iTopTileIndex!=(-1)) {
CTerrainTile &ttTop = _ptrTerrain->tr_attTiles[iTopTileIndex];
iTopBorderLod = ttTop.tt_iRequestedLod; // !!!! iLod 2 iRequested
}
#if BORDERTEST
iTopBorderLod = 0;
#endif
INDEX iStep = 1<<tt_iLod; // Tile step in vertices for this lod
INDEX iBorderStep = 1<<iTopBorderLod; // Border tile step for border lod
INDEX ctQuads = tt_ctVtxX-iStep; // Number of quads in this tile
INDEX ctVtxInsert = iStep-iBorderStep; // Number of vertices to insert in each quad
INDEX iLastVx = 0; // Last vertex inserted in quads
INDEX icVtx = 1; // Existing tile vertex index in column, using step one
INDEX iVtxRow = 0; // Row to add vertices in
INDEX iBaseFanVtx = (tt_ctVtxX-1) / iStep + 3; // Index of fan triangle to connect to
tt_iFirstBorderVertex[NB_TOP] = GetVertices().Count();
// Add half of topleft corner
INDEX icb=0;
for(;icb<ctVtxInsert;icb+=iBorderStep) {
AddVertex(icb+iBorderStep,0);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx-1,ivxAdded,iLastVx);
iLastVx = ivxAdded;
}
// Close top left fan
AddTriangle(iBaseFanVtx-1,icVtx,iLastVx);
// Insert aditional vertices into each quad in top row after first
for(INDEX ic=iStep;ic<ctQuads-iStep;ic+=iStep) {
iLastVx = icVtx;
if(icVtx%2) {
// for each vertex nedeed to be inserted in this quad
for(INDEX icb=0;icb<ctVtxInsert;icb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(ic+icb+iBorderStep,iVtxRow);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx-1,ivxAdded,iLastVx);
iLastVx = ivxAdded;
}
// Close fan
AddTriangle(iBaseFanVtx-1,icVtx+1,iLastVx);
// Add last fan triangle
AddTriangle(iBaseFanVtx,icVtx+1,iBaseFanVtx-1);
} else {
// Add first fan triangle
AddTriangle(iBaseFanVtx,icVtx,iBaseFanVtx-1);
// for each vertex nedeed to be inserted in this quad
for(INDEX icb=0;icb<ctVtxInsert;icb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(ic+icb+iBorderStep,iVtxRow);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx,ivxAdded,iLastVx);
iLastVx = ivxAdded;
}
// Close fan
AddTriangle(iBaseFanVtx,icVtx+1,iLastVx);
}
iBaseFanVtx++;
icVtx++;
}
iLastVx = icVtx;
// Add half of topright corner
for(icb=0;icb<ctVtxInsert;icb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(icb+iBorderStep+ctQuads-1,0);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx-1,ivxAdded,iLastVx);
iLastVx = ivxAdded;
}
// Close top right fan
AddTriangle(iBaseFanVtx-1,icVtx+1,iLastVx);
tt_ctBorderVertices[NB_TOP] = GetVertices().Count() - tt_iFirstBorderVertex[NB_TOP];
}
// Regenerate left border
void CTerrainTile::ReGenerateLeftBorder()
{
INDEX iLeftTileIndex = tt_aiNeighbours[NB_LEFT];
INDEX iLeftBorderLod = tt_iLod;
// If top neighbour exists
if(iLeftTileIndex!=(-1)) {
CTerrainTile &ttLeft = _ptrTerrain->tr_attTiles[iLeftTileIndex];
iLeftBorderLod = ttLeft.tt_iRequestedLod;
}
#if BORDERTEST
iLeftBorderLod = 0;
#endif
INDEX iStep = 1<<tt_iLod; // Tile step in vertices for this lod
INDEX iBorderStep = 1<<iLeftBorderLod; // Border tile step for border lod
INDEX ctQuads = tt_ctVtxX-iStep;// Number of quads in this tile
INDEX ctVtxInsert = iStep-iBorderStep; // Number of vertices to insert in each quad
INDEX iLastVx = 0; // Last vertex inserted in quads
INDEX iVtxCol = 0; // Col to add vertices in
INDEX iBaseStep = (tt_ctVtxX-1) / iStep + 1;
INDEX irVtx = iBaseStep; // Existing tile vertex index in row, using step one
INDEX iBaseFanVtx = iBaseStep + 1; // Index of fan triangle to connect to
tt_iFirstBorderVertex[NB_LEFT] = GetVertices().Count();
// Add half of topleft corner
INDEX irb=0;
for(;irb<ctVtxInsert;irb+=iBorderStep) {
AddVertex(iVtxCol,irb+iBorderStep);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx,iLastVx,ivxAdded);
iLastVx = ivxAdded;
}
// Close top left fan
AddTriangle(iBaseFanVtx,iLastVx,irVtx);
// Insert aditional vertices into each quad in top row after first
for(INDEX ir=iStep;ir<ctQuads-iStep;ir+=iStep) {
iLastVx = irVtx;
if(irVtx%2) {
// for each vertex nedeed to be inserted in this quad
for(INDEX irb=0;irb<ctVtxInsert;irb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(iVtxCol,ir+irb+iBorderStep);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx,iLastVx,ivxAdded);
iLastVx = ivxAdded;
}
// Close fan
AddTriangle(iBaseFanVtx,iLastVx,irVtx+iBaseStep);
// Add last fan triangle
AddTriangle(iBaseFanVtx+iBaseStep,iBaseFanVtx,irVtx+iBaseStep);
} else {
// Add first fan triangle
AddTriangle(iBaseFanVtx,iLastVx,iBaseFanVtx+iBaseStep);
// for each vertex nedeed to be inserted in this quad
for(INDEX irb=0;irb<ctVtxInsert;irb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(iVtxCol,ir+irb+iBorderStep);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx+iBaseStep,iLastVx,ivxAdded);
iLastVx = ivxAdded;
}
// Close fan
AddTriangle(iBaseFanVtx+iBaseStep,iLastVx,irVtx+iBaseStep);
}
iBaseFanVtx+=iBaseStep;
irVtx+=iBaseStep;
}
iLastVx = irVtx;
// Add half of bottomleft corner
for(irb=0;irb<ctVtxInsert;irb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(iVtxCol,irb+iBorderStep+ctQuads-1);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx,iLastVx,ivxAdded);
iLastVx = ivxAdded;
}
// Close top right fan
AddTriangle(iBaseFanVtx,iLastVx,irVtx+iBaseStep);
tt_ctBorderVertices[NB_LEFT] = GetVertices().Count() - tt_iFirstBorderVertex[NB_LEFT];
}
// Regenerate right border
void CTerrainTile::ReGenerateRightBorder()
{
INDEX iRightTileIndex = tt_aiNeighbours[NB_RIGHT];
INDEX iRightBorderLod = tt_iLod;
// If top neighbour exists
if(iRightTileIndex!=(-1)) {
CTerrainTile &ttRight = _ptrTerrain->tr_attTiles[iRightTileIndex];
iRightBorderLod = ttRight.tt_iRequestedLod;
}
#if BORDERTEST
iRightBorderLod = 0;
#endif
INDEX iStep = 1<<tt_iLod; // Tile step in vertices for this lod
INDEX iBorderStep = 1<<iRightBorderLod; // Border tile step for border lod
INDEX ctQuads = tt_ctVtxX-iStep;// Number of quads in this tile
INDEX ctVtxInsert = iStep-iBorderStep; // Number of vertices to insert in each quad
INDEX iLastVx = 0; // Last vertex inserted in quads
INDEX iVtxCol = tt_ctVtxX-1; // Col to add vertices in
INDEX iBaseStep = (tt_ctVtxX-1) / iStep + 1;
INDEX irVtx = iBaseStep-1; // Existing tile vertex index in row, using step one
INDEX iBaseFanVtx = iBaseStep - 2 +iBaseStep; // Index of fan triangle to connect to
tt_iFirstBorderVertex[NB_RIGHT] = GetVertices().Count();
iLastVx = irVtx;
// Add half of topleft corner
INDEX irb=0;
for(;irb<ctVtxInsert;irb+=iBorderStep) {
AddVertex(iVtxCol,irb+iBorderStep);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx,ivxAdded,iLastVx);
iLastVx = ivxAdded;
}
// Close top left fan
AddTriangle(iBaseFanVtx,irVtx+iBaseStep,iLastVx);
irVtx+=iBaseStep;
// Insert aditional vertices into each quad in top row after first
for(INDEX ir=iStep;ir<ctQuads-iStep;ir+=iStep) {
iLastVx = irVtx;
if(irVtx%2) {
// Add first fan triangle
AddTriangle(iBaseFanVtx,iBaseFanVtx+iBaseStep,iLastVx+iBaseStep);
// for each vertex nedeed to be inserted in this quad
for(INDEX irb=0;irb<ctVtxInsert;irb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(iVtxCol,ir+irb+iBorderStep);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx,ivxAdded,iLastVx);
iLastVx = ivxAdded;
}
// Close fan
AddTriangle(iBaseFanVtx,irVtx+iBaseStep,iLastVx);
} else {
// Add first fan triangle
AddTriangle(iBaseFanVtx,iBaseFanVtx+iBaseStep,iLastVx);
// for each vertex nedeed to be inserted in this quad
for(INDEX irb=0;irb<ctVtxInsert;irb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(iVtxCol,ir+irb+iBorderStep);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx+iBaseStep,ivxAdded,iLastVx);
iLastVx = ivxAdded;
}
// Close fan
AddTriangle(iBaseFanVtx+iBaseStep,irVtx+iBaseStep,iLastVx);
}
iBaseFanVtx+=iBaseStep;
irVtx+=iBaseStep;
}
iLastVx = irVtx;
// Add half of bottomleft corner
for(irb=0;irb<ctVtxInsert;irb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(iVtxCol,irb+iBorderStep+ctQuads-1);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx,ivxAdded,iLastVx);
iLastVx = ivxAdded;
}
// Close top right fan
AddTriangle(iBaseFanVtx,irVtx+iBaseStep,iLastVx);
tt_ctBorderVertices[NB_RIGHT] = GetVertices().Count() - tt_iFirstBorderVertex[NB_RIGHT];
}
// Regenerate bottom border
void CTerrainTile::ReGenerateBottomBorder()
{
INDEX iBottomTileIndex = tt_aiNeighbours[NB_BOTTOM];
INDEX iBottomBorderLod = tt_iRequestedLod;
// If bottom neighbour exists
if(iBottomTileIndex!=(-1)) {
CTerrainTile &ttBottom = _ptrTerrain->tr_attTiles[iBottomTileIndex];
iBottomBorderLod = ttBottom.tt_iRequestedLod;
}
#if BORDERTEST
iBottomBorderLod = 0;
#endif
INDEX iStep = 1<<tt_iLod; // Tile step in vertices for this lod
INDEX iBorderStep = 1<<iBottomBorderLod;// Border tile step for border lod
INDEX ctQuads = tt_ctVtxX-iStep;// Number of quads in this tile
INDEX ctVtxInsert = iStep-iBorderStep; // Number of vertices to insert in each quad
INDEX iLastVx = 0; // Last vertex inserted in quads
INDEX iBaseFanVtx = (((tt_ctVtxX-1) / iStep)*((tt_ctVtxX-1) / iStep)) + 1; // Index of fan triangle to connect to
INDEX iVtxRow = tt_ctVtxX-1; // Row to add vertices in
INDEX icVtx = iBaseFanVtx + (tt_ctVtxX-1)/ iStep; // Existing tile vertex index in column, using step one
tt_iFirstBorderVertex[NB_BOTTOM] = GetVertices().Count();
iLastVx = icVtx-1;
// Add half of bottom left corner
INDEX icb=0;
for(;icb<ctVtxInsert;icb+=iBorderStep) {
AddVertex(icb+iBorderStep,iVtxRow);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx-1,iLastVx,ivxAdded);
iLastVx = ivxAdded;
}
// Close bottom left fan
AddTriangle(iBaseFanVtx-1,iLastVx,icVtx);
// Insert aditional vertices into each quad in top row after first
for(INDEX ic=iStep;ic<ctQuads-iStep;ic+=iStep) {
iLastVx = icVtx;
if(icVtx%2) {
// Add first fan triangle
AddTriangle(iBaseFanVtx-1,icVtx+1,iBaseFanVtx);
// for each vertex nedeed to be inserted in this quad
for(INDEX icb=0;icb<ctVtxInsert;icb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(ic+icb+iBorderStep,iVtxRow);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx-1,iLastVx,ivxAdded);
iLastVx = ivxAdded;
}
// Close fan
AddTriangle(iBaseFanVtx-1,iLastVx,icVtx+1);
} else {
// Add first fan triangle
AddTriangle(iBaseFanVtx-1,icVtx,iBaseFanVtx);
// for each vertex nedeed to be inserted in this quad
for(INDEX icb=0;icb<ctVtxInsert;icb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(ic+icb+iBorderStep,iVtxRow);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx,iLastVx,ivxAdded);
iLastVx = ivxAdded;
}
// Close fan
AddTriangle(iBaseFanVtx,iLastVx,icVtx+1);
}
iBaseFanVtx++;
icVtx++;
}
iLastVx = icVtx;
// Add half of bottomright corner
for(icb=0;icb<ctVtxInsert;icb+=iBorderStep) {
// Insert vertex into quad and add one triangle
AddVertex(icb+iBorderStep+ctQuads-1,iVtxRow);
INDEX ivxAdded = GetVertices().Count()-1;
AddTriangle(iBaseFanVtx-1,iLastVx,ivxAdded);
iLastVx = ivxAdded;
}
// Close bottom right fan
AddTriangle(iBaseFanVtx-1,iLastVx,icVtx+1);
tt_ctBorderVertices[NB_BOTTOM] = GetVertices().Count() - tt_iFirstBorderVertex[NB_BOTTOM];
}
CStaticStackArray<GFXVertex4> &CTerrainTile::GetVertices() {
ASSERT(tt_iArrayIndex!=-1);
ASSERT(tt_iLod!=-1);
CArrayHolder &ah = _ptrTerrain->tr_aArrayHolders[tt_iLod];
TileArrays &ta = ah.ah_ataTileArrays[tt_iArrayIndex];
return ta.ta_avVertices;
}
CStaticStackArray<GFXTexCoord> &CTerrainTile::GetTexCoords() {
ASSERT(tt_iArrayIndex!=-1);
ASSERT(tt_iLod!=-1);
CArrayHolder &ah = _ptrTerrain->tr_aArrayHolders[tt_iLod];
TileArrays &ta = ah.ah_ataTileArrays[tt_iArrayIndex];
return ta.ta_auvTexCoords;
}
CStaticStackArray<GFXTexCoord> &CTerrainTile::GetShadowMapTC() {
ASSERT(tt_iArrayIndex!=-1);
ASSERT(tt_iLod!=-1);
CArrayHolder &ah = _ptrTerrain->tr_aArrayHolders[tt_iLod];
TileArrays &ta = ah.ah_ataTileArrays[tt_iArrayIndex];
return ta.ta_auvShadowMap;
}
CStaticStackArray<GFXTexCoord> &CTerrainTile::GetDetailTC() {
ASSERT(tt_iArrayIndex!=-1);
ASSERT(tt_iRequestedLod==0 || tt_iLod==0);
CArrayHolder &ah = _ptrTerrain->tr_aArrayHolders[tt_iRequestedLod];
TileArrays &ta = ah.ah_ataTileArrays[tt_iArrayIndex];
return ta.ta_auvDetailMap;
}
CStaticStackArray<INDEX> &CTerrainTile::GetIndices() {
ASSERT(tt_iArrayIndex!=-1);
ASSERT(tt_iLod!=-1);
CArrayHolder &ah = _ptrTerrain->tr_aArrayHolders[tt_iLod];
TileArrays &ta = ah.ah_ataTileArrays[tt_iArrayIndex];
return ta.ta_auiIndices;
}
CStaticStackArray<TileLayer> &CTerrainTile::GetTileLayers() {
ASSERT(tt_iArrayIndex!=-1);
ASSERT(tt_iRequestedLod==0 || tt_iLod==0);
CArrayHolder &ah = _ptrTerrain->tr_aArrayHolders[tt_iRequestedLod];
TileArrays &ta = ah.ah_ataTileArrays[tt_iArrayIndex];
return ta.ta_atlLayers;
}
CTextureData *CTerrainTile::GetTopMap()
{
ASSERT(tt_iArrayIndex!=-1);
ASSERT(tt_iLod!=-1);
ASSERT(tt_iLod!=0);
ASSERT(tt_iLod!=_ptrTerrain->tr_iMaxTileLod);
CArrayHolder &ah = _ptrTerrain->tr_aArrayHolders[tt_iRequestedLod];
TileArrays &ta = ah.ah_ataTileArrays[tt_iArrayIndex];
return ta.ta_ptdTopMap;
}
// Count used memory
SLONG CTerrainTile::GetUsedMemory(void)
{
SLONG slUsedMemory=0;
slUsedMemory += sizeof(CTerrainTile);
return slUsedMemory;
}