Serious-Engine/Sources/Engine/Terrain/TerrainRender.cpp

1412 lines
49 KiB
C++
Executable File

/* 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 "Engine/StdH.h"
#include <Engine/Terrain/Terrain.h>
#include <Engine/Terrain/TerrainRender.h>
#include <Engine/Terrain/TerrainEditing.h>
#include <Engine/Math/Projection.h>
#include <Engine/Math/OBBox.h>
#include <Engine/Graphics/DrawPort.h>
#include <Engine/Graphics/Fog_internal.h>
#include <Engine/Rendering/Render.h>
#include <Engine/Entities/Entity.h>
static CAnyProjection3D _aprProjection; // Current projection
static CDrawPort *_pdp = NULL; // Current drawport
CTerrain *_ptrTerrain; // Current terrain
FLOAT3D _vViewerAbs; // Position of viewer
static COLOR _colTerrainEdges; // Color of terrain edges
static FLOATmatrix3D _mObjectToView;
static FLOAT3D _vObjectToView;
static FLOAT3D _vViewer;
static FLOAT3D _vViewerObj;
extern INDEX _ctNodesVis; // visible quad nodes
extern INDEX _ctTris; // tris rendered
extern INDEX _ctDelayedNodes; // DelayedNodes
extern INDEX ter_bLerpVertices; // prepare smoth vertices before rendering
// Vertex array for calculating smoth vertices
CStaticStackArray<GFXVertex4> _avLerpedVerices;
CStaticStackArray<GFXVertex4> _avLerpedTileLayerVertices;
// Arrays for batch rendering of tiles is lowest mip
static CStaticStackArray<GFXVertex4> _avDelayedVertices;
static CStaticStackArray<INDEX_T> _aiDelayedIndices;
static CStaticStackArray<GFXTexCoord> _auvDelayedTexCoords;
static CStaticStackArray<GFXTexCoord> _auvDelayedShadowMapTC;
typedef FLOAT Matrix16[16];
typedef FLOAT Matrix12[12];
static void RenderFogLayer(INDEX itt);
static void RenderHazeLayer(INDEX itt);
FLOATaabbox3D _bboxDrawNextFrame; // TEMP
SLONG GetUsedMemoryForTileBatching(void)
{
SLONG slUsedMemory = 0;
slUsedMemory += _avDelayedVertices.sa_Count * sizeof(GFXVertex4);
slUsedMemory += _aiDelayedIndices.sa_Count * sizeof(INDEX);
slUsedMemory += _auvDelayedTexCoords.sa_Count * sizeof(GFXTexCoord);
slUsedMemory += _auvDelayedShadowMapTC.sa_Count * sizeof(GFXTexCoord);
return slUsedMemory;
}
CStaticStackArray<GFXColor> _acolVtxConstColors;
static void FillConstColorArray(INDEX ctVertices)
{
INDEX ctColors=_acolVtxConstColors.Count();
_acolVtxConstColors.PopAll();
_acolVtxConstColors.Push(ctVertices);
// if requested array is larger then existing one
if(ctVertices>ctColors) {
memset(&_acolVtxConstColors[ctColors],255,(ctVertices-ctColors)*sizeof(GFXColor));
}
}
// Regenerate one tile
void ReGenerateTile(INDEX itt)
{
ASSERT(_ptrTerrain!=NULL);
CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
tt.ReGenerate();
}
// Convert matrix12 to
void CreateOpenGLMatrix(Matrix12 &m12,Matrix16 &mgl16)
{
mgl16[ 0] = m12[ 0]; mgl16[ 1] = m12[ 4]; mgl16[ 2] = m12[ 8]; mgl16[ 3] = 0;
mgl16[ 4] = m12[ 1]; mgl16[ 5] = m12[ 5]; mgl16[ 6] = m12[ 9]; mgl16[ 7] = 0;
mgl16[ 8] = m12[ 2]; mgl16[ 9] = m12[ 6]; mgl16[10] = m12[10]; mgl16[11] = 0;
mgl16[12] = m12[ 3]; mgl16[13] = m12[ 7]; mgl16[14] = m12[11]; mgl16[15] = 1;
}
// set given matrix as identity matrix
inline static void SetMatrixDiagonal(Matrix12 &mat,FLOAT fValue)
{
memset(&mat,0,sizeof(mat));
mat[0] = fValue;
mat[5] = fValue;
mat[10] = fValue;
}
// Set texture matrix
static inline void gfxSetTextureMatrix2(Matrix12 *pMatrix)
{
pglMatrixMode( GL_TEXTURE);
if(pMatrix==NULL) {
pglLoadIdentity();
} else {
Matrix16 mrot16;
Matrix16 mtra16;
CreateOpenGLMatrix(*pMatrix,mrot16);
Matrix12 mtr12;
SetMatrixDiagonal(mtr12,1);
CreateOpenGLMatrix(mtr12,mtra16);
pglLoadMatrixf(mtra16);
pglMultMatrixf(mrot16);
}
pglMatrixMode(GL_MODELVIEW);
}
/*
* Render
*/
// Prepare scene for terrain rendering
void PrepareScene(CAnyProjection3D &apr, CDrawPort *pdp, CTerrain *ptrTerrain)
{
ASSERT(ptrTerrain!=NULL);
ASSERT(ptrTerrain->tr_penEntity!=NULL);
// Set current terrain
_ptrTerrain = ptrTerrain;
// Set drawport
_pdp = pdp;
// Prepare and set the projection
apr->ObjectPlacementL() = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0));
apr->Prepare();
_aprProjection = apr;
_pdp->SetProjection( _aprProjection);
CEntity *pen = ptrTerrain->tr_penEntity;
// calculate projection of viewer in absolute space
const FLOATmatrix3D &mViewer = _aprProjection->pr_ViewerRotationMatrix;
_vViewer(1) = -mViewer(3,1);
_vViewer(2) = -mViewer(3,2);
_vViewer(3) = -mViewer(3,3);
// calculate projection of viewer in object space
_vViewerObj = _vViewer * !pen->en_mRotation;
const CPlacement3D &plTerrain = pen->GetLerpedPlacement();
_mObjectToView = mViewer * pen->en_mRotation;
_vObjectToView = (plTerrain.pl_PositionVector - _aprProjection->pr_vViewerPosition) * mViewer;
// make transform matrix
const FLOATmatrix3D &m = _mObjectToView;
const FLOAT3D &v = _vObjectToView;
FLOAT glm[16];
glm[0] = m(1,1); glm[4] = m(1,2); glm[ 8] = m(1,3); glm[12] = v(1);
glm[1] = m(2,1); glm[5] = m(2,2); glm[ 9] = m(2,3); glm[13] = v(2);
glm[2] = m(3,1); glm[6] = m(3,2); glm[10] = m(3,3); glm[14] = v(3);
glm[3] = 0; glm[7] = 0; glm[11] = 0; glm[15] = 1;
gfxSetViewMatrix(glm);
// Get viewer in absolute space
_vViewerAbs = (_aprProjection->ViewerPlacementR().pl_PositionVector -
pen->en_plPlacement.pl_PositionVector) * !pen->en_mRotation;
gfxDisableBlend();
gfxDisableTexture();
gfxDisableAlphaTest();
gfxEnableDepthTest();
gfxEnableDepthWrite();
gfxCullFace(GFX_BACK);
}
__forceinline void Lerp(GFXVertex &vResult, const GFXVertex &vOriginal, const GFXVertex &v1, const GFXVertex &v2, const FLOAT &fFactor)
{
FLOAT fHalfPosY = Lerp(v1.y,v2.y,0.5f);
vResult.x = vOriginal.x;
vResult.y = Lerp(vOriginal.y, fHalfPosY, fFactor);
vResult.z = vOriginal.z;
}
void PrepareSmothVertices(INDEX itt)
{
CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
const INDEX ctVertices = tt.GetVertices().Count();
const FLOAT &fLerpFactor = tt.tt_fLodLerpFactor;
// Allocate memory for all vertices
_avLerpedVerices.PopAll();
_avLerpedVerices.Push(ctVertices);
// Get pointers to src and dst vertex arrays
GFXVertex *pavSrcFirst = &tt.GetVertices()[0];
GFXVertex *pavDstFirst = &_avLerpedVerices[0];
GFXVertex *pavSrc = &pavSrcFirst[0];
GFXVertex *pavDst = &pavDstFirst[0];
INDEX iFacing=0;
// for each vertex column
for(INDEX iy=0;iy<tt.tt_ctLodVtxY;iy++) {
// for each vertex in row in even column step by 2
for(INDEX ix=0;ix<tt.tt_ctLodVtxX-2;ix+=2) {
// Copy first vertex
pavDst[0] = pavSrc[0];
// Second vertex is lerped between left and right vertices
Lerp(pavDst[1],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
// Increment vertex pointers
pavDst+=2;
pavSrc+=2;
}
// Copy last vertex in row
pavDst[0] = pavSrc[0];
// Increment vertex pointers and go to odd column
pavDst++;
pavSrc++;
iy++;
// if this is not last row
if(iy<tt.tt_ctLodVtxY) {
// for each vertex in row in odd column step by 2
for(INDEX ix=0;ix<tt.tt_ctLodVtxX-2;ix+=2) {
// First vertex is lerped between top and bottom vertices
Lerp(pavDst[0],pavSrc[0],pavSrc[-tt.tt_ctLodVtxX],pavSrc[tt.tt_ctLodVtxX],fLerpFactor);
// is this odd vertex in row
#pragma message(">> Fix this")
if(((ix+iy)/2)%2) {
// if(iFacing&1)
// Second vertex (diagonal one) is lerped between topright and bottom left vertices
Lerp(pavDst[1],pavSrc[1],pavSrc[-tt.tt_ctLodVtxX+2],pavSrc[tt.tt_ctLodVtxX],fLerpFactor);
} else {
// Second vertex (diagonal one) is lerped between topleft and bottom right vertices
Lerp(pavDst[1],pavSrc[1],pavSrc[-tt.tt_ctLodVtxX],pavSrc[tt.tt_ctLodVtxX+2],fLerpFactor);
}
iFacing++;
// Increment vertex pointers
pavDst+=2;
pavSrc+=2;
}
// Last vertex in row is lerped between top and bottom vertices (same as first in row)
Lerp(pavDst[0],pavSrc[0],pavSrc[-tt.tt_ctLodVtxX],pavSrc[tt.tt_ctLodVtxX],fLerpFactor);
}
// Increment vertex pointers
pavDst++;
pavSrc++;
}
pavDst--;
pavSrc--;
/*
// Copy border vertices
GFXVertex *pvBorderDst = pavDst;
GFXVertex *pvBorderSrc = pavSrc;
for(INDEX ivx=tt.tt_ctNonBorderVertices;ivx<ctVertices;ivx++) {
// *pavDst++ = *pavSrc++;
pvBorderDst[0] = pvBorderSrc[0];
pvBorderDst++;
pvBorderSrc++;
}
*/
// Lerp top border vertices
const INDEX &iTopNeigbour = tt.tt_aiNeighbours[NB_TOP];
// if top neighbour exists
if(iTopNeigbour>=0) {
CTerrainTile &ttTop = _ptrTerrain->tr_attTiles[iTopNeigbour];
const FLOAT &fLerpFactor = ttTop.tt_fLodLerpFactor;
// Get source vertex pointer in top neighbour (vertex in bottom left corner of top neighbour)
const INDEX iSrcVtx = ttTop.tt_ctLodVtxX * (ttTop.tt_ctLodVtxY-1);
GFXVertex *pavSrc = &ttTop.GetVertices()[iSrcVtx];
// Calculate num of vertices that needs to be lerped
const INDEX ctLerps = (ttTop.tt_ctLodVtxX-1)/2;
// is top tile in same lod as this tile and has smaller or equal lerp factor
if(tt.tt_iLod==ttTop.tt_iLod && fLerpFactor<=tt.tt_fLodLerpFactor) {
// Get destination vertex pointer in this tile (first vertex in top left corner of this tile - first vertex in array)
const INDEX iDstVtx = 0;
GFXVertex *pavDst = &pavDstFirst[iDstVtx];
// for each vertex in bottom row of top tile that needs to be lerped
for(INDEX ivx=0;ivx<ctLerps;ivx++) {
// First vertex is same as in top tile
pavDst[0] = pavSrc[0];
// Second vertex is lerped between left and right vertices
Lerp(pavDst[1],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
pavDst+=2;
pavSrc+=2;
}
// is top tile in higher lod
} else if(tt.tt_iLod>ttTop.tt_iLod) {
const INDEX iVtxDiff = (ttTop.tt_ctLodVtxX-1) / (tt.tt_ctLodVtxX-1);
// Get destination vertex pointer to copy vertices from top neighbour (first vertex in top left corner of this tile - first vertex in array)
// Get destination vertex pointer to lerp vertices from top neighbour (first vertex added as additional top border vertex)
const INDEX iDstCopyVtx = 0;
const INDEX iDstLerpVtx = tt.tt_iFirstBorderVertex[NB_TOP];
GFXVertex *pavDstCopy = &pavDstFirst[iDstCopyVtx];
GFXVertex *pavDstLerp = &pavDstFirst[iDstLerpVtx];
// if diference is in one lod
if(iVtxDiff==2) {
// for each vertex in bottom row of top tile that needs to be lerped
for(INDEX ivx=0;ivx<ctLerps;ivx++) {
// Copy src vertex in normal dst vertex array
pavDstCopy[0] = pavSrc[0];
// Lerp left and right src vertices in border dst vertex
Lerp(pavDstLerp[0],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
pavDstLerp++;
pavDstCopy++;
pavSrc+=2;
}
// diference is more than one lod
} else {
INDEX ctbv = tt.tt_ctBorderVertices[NB_TOP];
INDEX ivxInQuad = 2; // This is 2 cos first and last non border vertex
// for each border vertex
for(INDEX ivx=0;ivx<ctbv;ivx+=2) {
// Lerp left and right src vertices in border dst vertex
Lerp(pavDstLerp[0],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
// if this border vertex is not last in quad
if(ivxInQuad!=iVtxDiff) {
// Copy second border vertex
pavDstLerp[1] = pavSrc[2];
pavDstLerp+=2;
ivxInQuad+=2;
// this is last border vertex
} else {
// Copy second non border vertex
pavDstCopy[1] = pavSrc[2];
pavDstCopy++;
// since this wasn't border vertex, fix border vertex loop counter
ctbv++;
pavDstLerp++;
ivxInQuad=2;
}
pavSrc+=2;
}
}
}
}
// Lerp bottom border vertices
const INDEX &iBottomNeigbour = tt.tt_aiNeighbours[NB_BOTTOM];
// if bottom neighbour exists
if(iBottomNeigbour>=0) {
CTerrainTile &ttBottom = _ptrTerrain->tr_attTiles[iBottomNeigbour];
const FLOAT &fLerpFactor = ttBottom.tt_fLodLerpFactor;
// Get source vertex pointer in bottom neighbour (vertex in top left corner of bottom neighbour - first vertex in array)
const INDEX iSrcVtx = 0;
GFXVertex *pavSrc = &ttBottom.GetVertices()[iSrcVtx];
// Calculate num of vertices that needs to be lerped
const INDEX ctLerps = (ttBottom.tt_ctLodVtxX-1)/2;
// is bottom tile in same lod as this tile and has smaller lerp factor
if(tt.tt_iLod==ttBottom.tt_iLod && fLerpFactor<tt.tt_fLodLerpFactor) {
// Get destination vertex pointer in this tile (first vertex in bottom left corner of this tile)
const INDEX iDstVtx = tt.tt_ctLodVtxX * (tt.tt_ctLodVtxY-1);
GFXVertex *pavDst = &pavDstFirst[iDstVtx];
// for each vertex in top row of bottom tile that needs to be lerped
for(INDEX ivx=0;ivx<ctLerps;ivx++) {
// First vertex is same as in bottom tile
pavDst[0] = pavSrc[0];
// Second vertex is lerped between left and right vertices
Lerp(pavDst[1],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
pavDst+=2;
pavSrc+=2;
}
// is bottom tile in higher lod
} else if(tt.tt_iLod>ttBottom.tt_iLod) {
const INDEX iVtxDiff = (ttBottom.tt_ctLodVtxX-1) / (tt.tt_ctLodVtxX-1);
// Get destination vertex pointer to copy vertices from bottom neighbour (first vertex in bottom left corner of this tile)
// Get destination vertex pointer to lerp vertices from bottom neighbour (first vertex added as additional bottom border vertex)
const INDEX iDstCopyVtx = tt.tt_ctLodVtxX * (tt.tt_ctLodVtxY-1);
const INDEX iDstLerpVtx = tt.tt_iFirstBorderVertex[NB_BOTTOM];
GFXVertex *pavDstCopy = &pavDstFirst[iDstCopyVtx];
GFXVertex *pavDstLerp = &pavDstFirst[iDstLerpVtx];
// if diference is in one lod
if(iVtxDiff==2) {
// for each vertex in top row of bottom tile that needs to be lerped
for(INDEX ivx=0;ivx<ctLerps;ivx++) {
// Copy src vertex in normal dst vertex array
pavDstCopy[0] = pavSrc[0];
// Lerp left and right src vertices in border dst vertex
Lerp(pavDstLerp[0],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
pavDstLerp++;
pavDstCopy++;
pavSrc+=2;
}
// diference is more than one lod
} else {
INDEX ctbv = tt.tt_ctBorderVertices[NB_BOTTOM];
INDEX ivxInQuad = 2; // This is 2 cos first and last non border vertex
// for each border vertex
for(INDEX ivx=0;ivx<ctbv;ivx+=2) {
// Lerp left and right src vertices in border dst vertex
Lerp(pavDstLerp[0],pavSrc[1],pavSrc[0],pavSrc[2],fLerpFactor);
// if this border vertex is not last in quad
if(ivxInQuad!=iVtxDiff) {
// Copy second border vertex
pavDstLerp[1] = pavSrc[2];
pavDstLerp+=2;
ivxInQuad+=2;
// this is last border vertex
} else {
// Copy second non border vertex
pavDstCopy[1] = pavSrc[2];
pavDstCopy++;
// since this wasn't border vertex, fix border vertex loop counter
ctbv++;
pavDstLerp++;
ivxInQuad=2;
}
pavSrc+=2;
}
}
}
}
// Lerp left border vertices
const INDEX &iLeftNeigbour = tt.tt_aiNeighbours[NB_LEFT];
// if left neighbour exists
if(iLeftNeigbour>=0) {
CTerrainTile &ttLeft = _ptrTerrain->tr_attTiles[iLeftNeigbour];
const FLOAT &fLerpFactor = ttLeft.tt_fLodLerpFactor;
// Get source vertex pointer in left neighbour (vertex in top right corner of left neighbour)
const INDEX iSrcVtx = ttLeft.tt_ctLodVtxX-1;
const INDEX iSrcStep = ttLeft.tt_ctLodVtxX;
GFXVertex *pavSrc = &ttLeft.GetVertices()[iSrcVtx];
// Calculate num of vertices that needs to be lerped
const INDEX ctLerps = (ttLeft.tt_ctLodVtxX-1)/2;
// is left tile in same lod as this tile and has smaller or equal lerp factor
if(tt.tt_iLod==ttLeft.tt_iLod && fLerpFactor<=tt.tt_fLodLerpFactor) {
// Get destination vertex pointer in this tile (first vertex in top left corner of this tile - first vertex in array)
const INDEX iDstVtx = 0;
const INDEX iDstStep = tt.tt_ctLodVtxX;
GFXVertex *pavDst = &pavDstFirst[iDstVtx];
// for each vertex in last column of left tile that needs to be lerped
for(INDEX ivx=0;ivx<ctLerps;ivx++) {
// First vertex is same as in left tile
pavDst[0] = pavSrc[0];
// Second vertex is lerped between top and bottom vertices
Lerp(pavDst[iDstStep],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
pavDst+=iDstStep*2;
pavSrc+=iSrcStep*2;
}
// is left tile in higher lod
} else if(tt.tt_iLod>ttLeft.tt_iLod) {
const INDEX iVtxDiff = (ttLeft.tt_ctLodVtxX-1) / (tt.tt_ctLodVtxX-1);
// Get destination vertex pointer to copy vertices from left neighbour (first vertex in top left corner of this tile - first vertex in array)
// Get destination vertex pointer to lerp vertices from left neighbour (first vertex added as additional left border vertex)
const INDEX iDstCopyVtx = 0;
const INDEX iDstLerpVtx = tt.tt_iFirstBorderVertex[NB_LEFT];
const INDEX iDstStep = tt.tt_ctLodVtxX;
GFXVertex *pavDstCopy = &pavDstFirst[iDstCopyVtx];
GFXVertex *pavDstLerp = &pavDstFirst[iDstLerpVtx];
// if diference is in one lod
if(iVtxDiff==2) {
// for each vertex in last column of left tile that needs to be lerped
for(INDEX ivx=0;ivx<ctLerps;ivx++) {
// Copy src vertex in normal dst vertex array
pavDstCopy[0] = pavSrc[0];
// Lerp left and right src vertices in border dst vertex
Lerp(pavDstLerp[0],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
pavDstLerp++;
pavDstCopy+=iDstStep;
pavSrc+=iSrcStep*2;
}
// diference is more than one lod
} else {
INDEX ctbv = tt.tt_ctBorderVertices[NB_LEFT];
INDEX ivxInQuad = 2; // This is 2 cos first and last non border vertex
// for each border vertex
for(INDEX ivx=0;ivx<ctbv;ivx+=2) {
// Lerp left and right src vertices in border dst vertex
Lerp(pavDstLerp[0],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
// if this border vertex is not last in quad
if(ivxInQuad!=iVtxDiff) {
// Copy second border vertex
pavDstLerp[1] = pavSrc[iSrcStep*2];
pavDstLerp+=2;
ivxInQuad+=2;
// this is last border vertex
} else {
// Copy second non border vertex
pavDstCopy[iDstStep] = pavSrc[iSrcStep*2];
pavDstCopy+=iDstStep;
// since this wasn't border vertex, fix border vertex loop counter
ctbv++;
pavDstLerp++;
ivxInQuad=2;
}
pavSrc+=iSrcStep*2;
}
}
}
}
// Lerp right border vertices
const INDEX &iRightNeigbour = tt.tt_aiNeighbours[NB_RIGHT];
// if right neighbour exists
if(iRightNeigbour>=0) {
CTerrainTile &ttRight = _ptrTerrain->tr_attTiles[iRightNeigbour];
const FLOAT &fLerpFactor = ttRight.tt_fLodLerpFactor;
// Get source vertex pointer in right neighbour (vertex in top left corner of left neighbour - first vertex in array)
const INDEX iSrcVtx = 0;
const INDEX iSrcStep = ttRight.tt_ctLodVtxX;
GFXVertex *pavSrc = &ttRight.GetVertices()[iSrcVtx];
// Calculate num of vertices that needs to be lerped
const INDEX ctLerps = (ttRight.tt_ctLodVtxX-1)/2;
// is right tile in same lod as this tile and has smaller lerp factor
if(tt.tt_iLod==ttRight.tt_iLod && fLerpFactor<tt.tt_fLodLerpFactor) {
// Get destination vertex pointer in this tile (first vertex in top right corner of this tile)
INDEX iDstVtx = tt.tt_ctLodVtxX-1;
INDEX iDstStep = tt.tt_ctLodVtxX;
GFXVertex *pavDst = &pavDstFirst[iDstVtx];
// for each vertex in first column of right tile that needs to be lerped
for(INDEX ivx=0;ivx<ctLerps;ivx++) {
// First vertex is same as in right tile
pavDst[0] = pavSrc[0];
// Second vertex is lerped between top and bottom vertices
Lerp(pavDst[iDstStep],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
pavDst+=iDstStep*2;
pavSrc+=iSrcStep*2;
}
// is right tile in higher lod
} else if(tt.tt_iLod>ttRight.tt_iLod) {
const INDEX iVtxDiff = (ttRight.tt_ctLodVtxX-1) / (tt.tt_ctLodVtxX-1);
// Get destination vertex pointer to copy vertices from right neighbour (first vertex in top right corner of this tile)
// Get destination vertex pointer to lerp vertices from right neighbour (first vertex added as additional right border vertex)
const INDEX iDstCopyVtx = tt.tt_ctLodVtxX-1;
const INDEX iDstLerpVtx = tt.tt_iFirstBorderVertex[NB_RIGHT];
const INDEX iDstStep = tt.tt_ctLodVtxX;
GFXVertex *pavDstCopy = &pavDstFirst[iDstCopyVtx];
GFXVertex *pavDstLerp = &pavDstFirst[iDstLerpVtx];
// if diference is in one lod
if(iVtxDiff==2) {
// for each vertex in first column of right tile that needs to be lerped
for(INDEX ivx=0;ivx<ctLerps;ivx++) {
// Copy src vertex in normal dst vertex array
pavDstCopy[0] = pavSrc[0];
// Lerp left and right src vertices in border dst vertex
Lerp(pavDstLerp[0],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
pavDstLerp++;
pavDstCopy+=iDstStep;
pavSrc+=iSrcStep*2;
}
// diference is more than one lod
} else {
INDEX ctbv = tt.tt_ctBorderVertices[NB_RIGHT];
INDEX ivxInQuad = 2; // This is 2 cos first and last non border vertex
// for each border vertex
for(INDEX ivx=0;ivx<ctbv;ivx+=2) {
// Lerp left and right src vertices in border dst vertex
Lerp(pavDstLerp[0],pavSrc[iSrcStep],pavSrc[0],pavSrc[iSrcStep*2],fLerpFactor);
// if this border vertex is not last in quad
if(ivxInQuad!=iVtxDiff) {
// Copy second border vertex
pavDstLerp[1] = pavSrc[iSrcStep*2];
pavDstLerp+=2;
ivxInQuad+=2;
// this is last border vertex
} else {
// Copy second non border vertex
pavDstCopy[iDstStep] = pavSrc[iSrcStep*2];
pavDstCopy+=iDstStep;
// since this wasn't border vertex, fix border vertex loop counter
ctbv++;
pavDstLerp++;
ivxInQuad=2;
}
pavSrc+=iSrcStep*2;
}
}
}
}
}
void PrepareSmothVerticesOnTileLayer(INDEX iTerrainTile, INDEX iTileLayer)
{
CTerrainTile &tt = _ptrTerrain->tr_attTiles[iTerrainTile];
//CTerrainLayer &tl = _ptrTerrain->tr_atlLayers[iTileLayer];
TileLayer &ttl = tt.GetTileLayers()[iTileLayer];
ASSERT(tt.tt_iLod==0);
const INDEX ctVertices = ttl.tl_avVertices.Count();
const FLOAT &fLerpFactor = tt.tt_fLodLerpFactor;
// Allocate memory for all vertices
_avLerpedTileLayerVertices.PopAll();
_avLerpedTileLayerVertices.Push(ctVertices);
// Get pointers to src and dst vertex arrays
GFXVertex *pavSrcFirst = &ttl.tl_avVertices[0];
GFXVertex *pavDstFirst = &_avLerpedTileLayerVertices[0];
GFXVertex *pavSrc = &pavSrcFirst[0];
GFXVertex *pavDst = &pavDstFirst[0];
INDEX ctQuadsPerRow = _ptrTerrain->tr_ctQuadsInTileRow;
INDEX ctVerticesInRow = _ptrTerrain->tr_ctQuadsInTileRow*2;
INDEX iFacing = 1;
// Minimize popping on vertices using 4 quads, 2 from current row and 2 from next row in same tile
for(INDEX iz=0;iz<ctQuadsPerRow;iz+=2) {
for(INDEX ix=0;ix<ctQuadsPerRow;ix+=2) {
// Get pointer for quads in next row
GFXVertex *pavNRSrc = &pavSrc[ctVerticesInRow*2];
GFXVertex *pavNRDst = &pavDst[ctVerticesInRow*2];
pavDst[0] = pavSrc[0];
Lerp(pavDst[1],pavSrc[1],pavSrc[0],pavSrc[5],fLerpFactor);
Lerp(pavDst[2],pavSrc[2],pavSrc[0],pavNRSrc[2],fLerpFactor);
if(iFacing&1) {
Lerp(pavDst[3],pavSrc[3],pavSrc[0],pavNRSrc[7],fLerpFactor);
} else {
Lerp(pavDst[3],pavSrc[3],pavSrc[5],pavNRSrc[2],fLerpFactor);
}
pavDst[4] = pavDst[1];
pavDst[5] = pavSrc[5];
pavDst[6] = pavDst[3];
Lerp(pavDst[7],pavSrc[7],pavSrc[5],pavNRSrc[7],fLerpFactor);
pavNRDst[0] = pavDst[2];
pavNRDst[1] = pavDst[3];
pavNRDst[2] = pavNRSrc[2];
Lerp(pavNRDst[3],pavNRSrc[3],pavNRSrc[2],pavNRSrc[7],fLerpFactor);
pavNRDst[4] = pavDst[3];
pavNRDst[5] = pavDst[7];
pavNRDst[6] = pavNRDst[3];
pavNRDst[7] = pavNRSrc[7];
// Increment vertex pointers
pavSrc+=8;
pavDst+=8;
iFacing++;
}
iFacing++;
pavSrc+=ctVerticesInRow*2;
pavDst+=ctVerticesInRow*2;
}
// Lerp top border
INDEX iTopNeighbour = tt.tt_aiNeighbours[NB_TOP];
// if top border exists
if(iTopNeighbour>=0) {
CTerrainTile &ttTop = _ptrTerrain->tr_attTiles[iTopNeighbour];
const FLOAT fTopLerpFactor = ttTop.tt_fLodLerpFactor;
// is top tile in highest lod and has smaller or equal lerp factor
if(ttTop.tt_iLod==0 && fTopLerpFactor<=fLerpFactor) {
TileLayer &ttl = ttTop.GetTileLayers()[iTileLayer];
INDEX iFirstVertex = ctVerticesInRow*(ctVerticesInRow-2);
GFXVertex *pavSrc = &ttl.tl_avVertices[iFirstVertex];
GFXVertex *pavDst = &_avLerpedTileLayerVertices[0];
// for each quad
for(INDEX ix=0;ix<ctQuadsPerRow;ix+=2) {
Lerp(pavDst[1],pavSrc[6],pavSrc[2],pavSrc[7],fTopLerpFactor);
pavDst[4] = pavDst[1];
pavSrc+=8;
pavDst+=8;
}
}
}
// Lerp bottom border
INDEX iBottomNeighbour = tt.tt_aiNeighbours[NB_BOTTOM];
// if bottom border exists
if(iBottomNeighbour>=0) {
CTerrainTile &ttBottom = _ptrTerrain->tr_attTiles[iBottomNeighbour];
const FLOAT fBottomLerpFactor = ttBottom.tt_fLodLerpFactor;
// is bottom tile in highest lod and has smaller lerp factor
if(ttBottom.tt_iLod==0 && fBottomLerpFactor<fLerpFactor) {
TileLayer &ttl = ttBottom.GetTileLayers()[iTileLayer];
INDEX iFirstVertex = ctVerticesInRow*(ctVerticesInRow-2);
GFXVertex *pavSrc = &ttl.tl_avVertices[0];
GFXVertex *pavDst = &_avLerpedTileLayerVertices[iFirstVertex];
// for each quad
for(INDEX ix=0;ix<ctQuadsPerRow;ix+=2) {
Lerp(pavDst[3],pavSrc[1],pavSrc[0],pavSrc[5],fBottomLerpFactor);
pavDst[6] = pavDst[3];
pavSrc+=8;
pavDst+=8;
}
}
}
// Lerp left border
INDEX iLeftNeighbour = tt.tt_aiNeighbours[NB_LEFT];
// if left neightbour exits
if(iLeftNeighbour>=0) {
CTerrainTile &ttLeft = _ptrTerrain->tr_attTiles[iLeftNeighbour];
const FLOAT fLeftLerpFactor = ttLeft.tt_fLodLerpFactor;
// is left tile in highest lod and has smaller or equal left factor
if(ttLeft.tt_iLod==0 && fLeftLerpFactor<=fLerpFactor) {
TileLayer &ttl = ttLeft.GetTileLayers()[iTileLayer];
INDEX iFirstVertex = ctVerticesInRow*2-8;
GFXVertex *pavSrc = &ttl.tl_avVertices[iFirstVertex];
GFXVertex *pavDst = &_avLerpedTileLayerVertices[0];
// for each quad
for(INDEX ix=0;ix<ctQuadsPerRow;ix+=2) {
GFXVertex *pavNRSrc = &pavSrc[ctVerticesInRow*2];
GFXVertex *pavNRDst = &pavDst[ctVerticesInRow*2];
Lerp(pavDst[2],pavSrc[7],pavSrc[5],pavNRSrc[7],fLeftLerpFactor);
pavNRDst[0] = pavDst[2];
pavSrc+=ctVerticesInRow*4;
pavDst+=ctVerticesInRow*4;
}
}
}
// Lerp right border
INDEX iRightNeighbour = tt.tt_aiNeighbours[NB_RIGHT];
// if right neightbour exits
if(iRightNeighbour>=0) {
CTerrainTile &ttRight = _ptrTerrain->tr_attTiles[iRightNeighbour];
const FLOAT fRightLerpFactor = ttRight.tt_fLodLerpFactor;
// is right tile in highest lod and has smaller left factor
if(ttRight.tt_iLod==0 && fRightLerpFactor<fLerpFactor) {
TileLayer &ttl = ttRight.GetTileLayers()[iTileLayer];
INDEX iFirstVertex = ctVerticesInRow*2-8;
GFXVertex *pavSrc = &ttl.tl_avVertices[0];
GFXVertex *pavDst = &_avLerpedTileLayerVertices[iFirstVertex];
// for each quad
for(INDEX ix=0;ix<ctQuadsPerRow;ix+=2) {
GFXVertex *pavNRSrc = &pavSrc[ctVerticesInRow*2];
GFXVertex *pavNRDst = &pavDst[ctVerticesInRow*2];
Lerp(pavDst[7],pavSrc[2],pavSrc[0],pavNRSrc[2],fRightLerpFactor);
pavNRDst[5] = pavDst[7];
pavSrc+=ctVerticesInRow*4;
pavDst+=ctVerticesInRow*4;
}
}
}
}
// Draw all tiles that are in lowest lod
static void RenderBatchedTiles(void)
{
// Set texture wrapping
gfxSetTextureWrapping(GFX_CLAMP,GFX_CLAMP);
// Use terrains global top map as texture
_ptrTerrain->tr_tdTopMap.SetAsCurrent();
GFXVertex4 *pavVertices = &_avDelayedVertices[0];
GFXTexCoord *pauvTexCoords = &_auvDelayedTexCoords[0];
GFXTexCoord *pauvShadowMapTC = &_auvDelayedShadowMapTC[0];
INDEX_T *paiIndices = &_aiDelayedIndices[0];
INDEX ctVertices = _avDelayedVertices.Count();
INDEX ctIndices = _aiDelayedIndices.Count();
// Prepare white color array
FillConstColorArray(ctVertices);
GFXColor *pacolColors = &_acolVtxConstColors[0];
gfxEnableAlphaTest();
gfxDisableBlend();
gfxSetVertexArray(pavVertices,ctVertices);
gfxSetTexCoordArray(pauvTexCoords, FALSE);
gfxSetColorArray(pacolColors);
gfxLockArrays();
gfxDrawElements(ctIndices,paiIndices);
gfxDisableAlphaTest();
_ctTris +=ctIndices/2;
// if shadows are visible
if(_wrpWorldRenderPrefs.wrp_shtShadows!=CWorldRenderPrefs::SHT_NONE) {
gfxDepthFunc(GFX_EQUAL);
gfxBlendFunc(GFX_DST_COLOR,GFX_SRC_COLOR);
gfxEnableBlend();
gfxSetTexCoordArray(pauvShadowMapTC, FALSE);
_ptrTerrain->tr_tdShadowMap.SetAsCurrent();
gfxDrawElements(ctIndices,paiIndices);
gfxDepthFunc(GFX_LESS_EQUAL);
}
if(_ptrTerrain->GetFlags()&TR_HAS_FOG) {
RenderFogLayer(-1);
}
if(_ptrTerrain->GetFlags()&TR_HAS_HAZE) {
RenderHazeLayer(-1);
}
gfxUnlockArrays();
// Popall delayed arrays
_avDelayedVertices.PopAll();
_auvDelayedTexCoords.PopAll();
_auvDelayedShadowMapTC.PopAll();
_aiDelayedIndices.PopAll();
}
static void BatchTile(INDEX itt)
{
CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
ASSERT(tt.GetVertices().Count()==9);
ASSERT(tt.GetIndices().Count()==24);
INDEX ctDelayedVertices = _avDelayedVertices.Count();
GFXVertex4 *pavVertices = &tt.GetVertices()[0];
GFXTexCoord *pauvTexCoords = &tt.GetTexCoords()[0];
GFXTexCoord *pauvShadowMapTC = &tt.GetShadowMapTC()[0];
INDEX_T *paiIndices = &tt.GetIndices()[0];
GFXVertex4 *pavDelVertices = _avDelayedVertices.Push(9);
GFXTexCoord *pauvDelTexCoords = _auvDelayedTexCoords.Push(9);
GFXTexCoord *pauvDelShadowMapTC = _auvDelayedShadowMapTC.Push(9);
INDEX_T *paiDelIndices = _aiDelayedIndices.Push(24);
// for each vertex in tile
for(INDEX ivx=0;ivx<9;ivx++) {
// copy vertex, texcoord & shadow map texcoord to delayed array
pavDelVertices[ivx] = pavVertices[ivx];
pauvDelTexCoords[ivx] = pauvTexCoords[ivx];
pauvDelShadowMapTC[ivx] = pauvShadowMapTC[ivx];
}
// for each index in tile
for(INDEX iind=0;iind<24;iind++) {
// reindex indice for new arrays
paiDelIndices[iind] = paiIndices[iind] + ctDelayedVertices;
}
_ctDelayedNodes++;
}
// returns haze/fog value in vertex
static FLOAT3D _vFViewerObj, _vHDirObj;
static FLOAT _fFogAddZ, _fFogAddH;
static FLOAT _fHazeAdd;
// check vertex against haze
#pragma message(">> no asm in GetHazeMapInVertex and GetFogMapInVertex")
static void GetHazeMapInVertex( GFXVertex4 &vtx, GFXTexCoord &txHaze)
{
const FLOAT fD = vtx.x*_vViewerObj(1) + vtx.y*_vViewerObj(2) + vtx.z*_vViewerObj(3);
txHaze.uv.u = (fD+_fHazeAdd) * _haze_fMul;
txHaze.uv.v = 0.0f;
}
static void GetFogMapInVertex( GFXVertex4 &vtx, GFXTexCoord &tex)
{
const FLOAT fD = vtx.x*_vFViewerObj(1) + vtx.y*_vFViewerObj(2) + vtx.z*_vFViewerObj(3);
const FLOAT fH = vtx.x*_vHDirObj(1) + vtx.y*_vHDirObj(2) + vtx.z*_vHDirObj(3);
tex.uv.u = (fD+_fFogAddZ) * _fog_fMulZ;
tex.uv.v = (fH+_fFogAddH) * _fog_fMulH;
}
static CStaticStackArray<GFXTexCoord> _atcHaze;
static CStaticStackArray<GFXColor> _acolHaze;
static void RenderFogLayer(INDEX itt)
{
FLOATmatrix3D &mViewer = _aprProjection->pr_ViewerRotationMatrix;
FLOAT3D vObjPosition = _ptrTerrain->tr_penEntity->en_plPlacement.pl_PositionVector;
// get viewer -z in object space
_vFViewerObj = FLOAT3D(0,0,-1) * !_mObjectToView;
// get fog direction in object space
_vHDirObj = _fog_vHDirAbs * !(!mViewer*_mObjectToView);
// get viewer offset
_fFogAddZ = _vViewer(1) * (vObjPosition(1) - _aprProjection->pr_vViewerPosition(1));
_fFogAddZ += _vViewer(2) * (vObjPosition(2) - _aprProjection->pr_vViewerPosition(2));
_fFogAddZ += _vViewer(3) * (vObjPosition(3) - _aprProjection->pr_vViewerPosition(3));
// get fog offset
_fFogAddH = (_fog_vHDirAbs % vObjPosition) + _fog_fp.fp_fH3;
GFXVertex *pvVtx;
INDEX_T *piIndices;
INDEX ctVertices;
INDEX ctIndices;
// if this is tile
if(itt>=0) {
CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
pvVtx = &tt.GetVertices()[0];
piIndices = &tt.GetIndices()[0];
ctVertices = tt.GetVertices().Count();
ctIndices = tt.GetIndices().Count();
// else this are batched tiles
} else {
pvVtx = &_avDelayedVertices[0];
piIndices = &_aiDelayedIndices[0];
ctVertices = _avDelayedVertices.Count();
ctIndices = _aiDelayedIndices.Count();
}
GFXTexCoord *pfFogTC = _atcHaze.Push(ctVertices);
GFXColor *pcolFog = _acolHaze.Push(ctVertices);
const COLOR colF = AdjustColor( _fog_fp.fp_colColor, _slTexHueShift, _slTexSaturation);
GFXColor colFog(colF);
// for each vertex in tile
for(INDEX ivx=0;ivx<ctVertices;ivx++) {
GetFogMapInVertex(pvVtx[ivx],pfFogTC[ivx]);
pcolFog[ivx] = colFog;
}
// render fog layer
gfxDepthFunc(GFX_EQUAL);
gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
gfxSetTexture( _fog_ulTexture, _fog_tpLocal);
gfxSetTexCoordArray(pfFogTC, FALSE);
gfxSetColorArray(pcolFog);
gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
gfxEnableBlend();
gfxDisableAlphaTest();
gfxDrawElements(ctIndices,piIndices);
gfxDepthFunc(GFX_LESS_EQUAL);
_atcHaze.PopAll();
_acolHaze.PopAll();
}
static void RenderHazeLayer(INDEX itt)
{
FLOAT3D vObjPosition = _ptrTerrain->tr_penEntity->en_plPlacement.pl_PositionVector;
_fHazeAdd = -_haze_hp.hp_fNear;
_fHazeAdd += _vViewer(1) * (vObjPosition(1) - _aprProjection->pr_vViewerPosition(1));
_fHazeAdd += _vViewer(2) * (vObjPosition(2) - _aprProjection->pr_vViewerPosition(2));
_fHazeAdd += _vViewer(3) * (vObjPosition(3) - _aprProjection->pr_vViewerPosition(3));
GFXVertex *pvVtx;
INDEX_T *piIndices;
INDEX ctVertices;
INDEX ctIndices;
// if this is tile
if(itt>=0) {
CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
pvVtx = &tt.GetVertices()[0];
piIndices = &tt.GetIndices()[0];
ctVertices = tt.GetVertices().Count();
ctIndices = tt.GetIndices().Count();
// else this are batched tiles
} else {
pvVtx = &_avDelayedVertices[0];
piIndices = &_aiDelayedIndices[0];
ctVertices = _avDelayedVertices.Count();
ctIndices = _aiDelayedIndices.Count();
}
GFXTexCoord *pfHazeTC = _atcHaze.Push(ctVertices);
GFXColor *pcolHaze = _acolHaze.Push(ctVertices);
const COLOR colH = AdjustColor( _haze_hp.hp_colColor, _slTexHueShift, _slTexSaturation);
GFXColor colHaze(colH);
// for each vertex in tile
for(INDEX ivx=0;ivx<ctVertices;ivx++) {
GetHazeMapInVertex(pvVtx[ivx],pfHazeTC[ivx]);
pcolHaze[ivx] = colHaze;
}
// render haze layer
gfxDepthFunc(GFX_EQUAL);
gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
gfxSetTexture( _haze_ulTexture, _haze_tpLocal);
gfxSetTexCoordArray(pfHazeTC, FALSE);
gfxSetColorArray(pcolHaze);
gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
gfxEnableBlend();
gfxDrawElements(ctIndices,piIndices);
gfxDepthFunc(GFX_LESS_EQUAL);
_atcHaze.PopAll();
_acolHaze.PopAll();
}
// Render one tile
static void RenderTile(INDEX itt)
{
ASSERT(_ptrTerrain!=NULL);
CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
INDEX ctVertices = tt.GetVertices().Count();
extern INDEX ter_bOptimizeRendering;
// if tile is in posible lowest lod and doesn't have any border vertices
if(ter_bOptimizeRendering && tt.GetFlags()&TT_IN_LOWEST_LOD) {
// delay tile rendering
BatchTile(itt);
return;
}
GFXVertex4 *pavVertices;
// if vertex lerping is requested
if(ter_bLerpVertices==1) {
// Prepare smoth vertices
PrepareSmothVertices(itt);
pavVertices = &_avLerpedVerices[0];
} else {
// use non smoth vertices
pavVertices = &tt.GetVertices()[0];
}
// if tile is in highest lod
if(tt.tt_iLod==0) {
gfxBlendFunc(GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
gfxSetVertexArray(pavVertices,ctVertices);
gfxLockArrays();
// for each tile layer
INDEX cttl= tt.GetTileLayers().Count();
for(INDEX itl=0;itl<cttl;itl++) {
CTerrainLayer &tl = _ptrTerrain->tr_atlLayers[itl];
// if layer isn't visible
if(!tl.tl_bVisible) {
continue; // skip it
}
TileLayer &ttl = tt.GetTileLayers()[itl];
// Set tile stretch
Matrix12 m12;
SetMatrixDiagonal(m12,tl.tl_fStretchX);
gfxSetTextureMatrix2(&m12);
// Set tile blend mode
if(tl.tl_fSmoothness==0) {
gfxDisableBlend();
gfxEnableAlphaTest();
} else {
gfxEnableBlend();
gfxDisableAlphaTest();
}
// if this tile has any polygons in this layer
INDEX ctIndices = ttl.tl_auiIndices.Count();
if(ctIndices>0) {
gfxSetTextureWrapping(GFX_REPEAT,GFX_REPEAT);
tl.tl_ptdTexture->SetAsCurrent();
// if this is tile layer
if(tl.tl_ltType==LT_TILE) {
gfxUnlockArrays();
GFXVertex4 *pavLayerVertices;
if(ter_bLerpVertices==1) {
PrepareSmothVerticesOnTileLayer(itt,itl);
pavLayerVertices = &_avLerpedTileLayerVertices[0];
} else {
pavLayerVertices = &ttl.tl_avVertices[0];
}
gfxSetVertexArray(pavLayerVertices,ttl.tl_avVertices.Count());
gfxLockArrays();
// gfxSetColorArray(&ttl.tl_acColors[0]);
gfxSetTexCoordArray(&ttl.tl_atcTexCoords[0], FALSE);
// set wireframe mode
/*
gfxEnableDepthBias();
gfxPolygonMode(GFX_LINE);
gfxDisableTexture();*/
gfxSetConstantColor(0xFFFFFFFF);
// Draw tiled layer
gfxDrawElements(ttl.tl_auiIndices.Count(),&ttl.tl_auiIndices[0]);
_ctTris +=ttl.tl_auiIndices.Count()/2;
/*
// set fill mode
gfxDisableDepthBias();
gfxPolygonMode(GFX_FILL);*/
// Set old vertex array
gfxUnlockArrays();
gfxSetVertexArray(pavVertices,ctVertices);
gfxLockArrays();
// if this is normal layer
} else {
// render layer
gfxSetColorArray(&ttl.tl_acColors[0]);
gfxSetTexCoordArray(&ttl.tl_atcTexCoords[0], FALSE);
gfxDrawElements(ctIndices,&ttl.tl_auiIndices[0]);
_ctTris +=ctIndices/2;
}
}
}
gfxSetTextureMatrix2(NULL);
INDEX ctIndices = tt.GetIndices().Count();
if(ctIndices>0) {
INDEX_T *paiIndices = &tt.GetIndices()[0];
// if detail map exists
if(_ptrTerrain->tr_ptdDetailMap!=NULL) {
gfxSetTextureWrapping(GFX_REPEAT,GFX_REPEAT);
gfxDisableAlphaTest();
shaBlendFunc( GFX_DST_COLOR, GFX_SRC_COLOR);
gfxEnableBlend();
gfxSetTexCoordArray(&tt.GetDetailTC()[0], FALSE);
_ptrTerrain->tr_ptdDetailMap->SetAsCurrent();
gfxDrawElements(ctIndices,paiIndices);
}
// if shadows are visible
if(_wrpWorldRenderPrefs.wrp_shtShadows!=CWorldRenderPrefs::SHT_NONE) {
gfxDisableAlphaTest();
shaBlendFunc( GFX_DST_COLOR, GFX_SRC_COLOR);
gfxEnableBlend();
gfxSetTextureWrapping(GFX_CLAMP,GFX_CLAMP);
gfxSetTexCoordArray(&tt.GetShadowMapTC()[0], FALSE);
_ptrTerrain->tr_tdShadowMap.SetAsCurrent();
gfxDrawElements(ctIndices,paiIndices);
}
}
// if tile is not in highest lod
} else {
gfxSetTextureWrapping(GFX_CLAMP,GFX_CLAMP);
// if tile is in lowest lod
if(tt.tt_iLod == _ptrTerrain->tr_iMaxTileLod) {
// use terrains global top map
_ptrTerrain->tr_tdTopMap.SetAsCurrent();
// else tile is in some midle lod
} else {
// use its own topmap
tt.GetTopMap()->SetAsCurrent();
}
// Render tile
INDEX ctIndices = tt.GetIndices().Count();
gfxEnableAlphaTest();
gfxDisableBlend();
gfxSetVertexArray(pavVertices,ctVertices);
gfxSetTexCoordArray(&tt.GetTexCoords()[0], FALSE);
FillConstColorArray(ctVertices);
gfxSetColorArray(&_acolVtxConstColors[0]);
gfxLockArrays();
gfxDrawElements(ctIndices,&tt.GetIndices()[0]);
_ctTris +=ctIndices/2;
gfxDisableAlphaTest();
// if shadows are visible
if(_wrpWorldRenderPrefs.wrp_shtShadows!=CWorldRenderPrefs::SHT_NONE) {
gfxDepthFunc(GFX_EQUAL);
INDEX ctIndices = tt.GetIndices().Count();
INDEX_T *paiIndices = &tt.GetIndices()[0];
gfxSetTextureWrapping(GFX_CLAMP,GFX_CLAMP);
gfxBlendFunc(GFX_DST_COLOR,GFX_SRC_COLOR);
gfxEnableBlend();
gfxSetTexCoordArray(&tt.GetShadowMapTC()[0], FALSE);
_ptrTerrain->tr_tdShadowMap.SetAsCurrent();
gfxDrawElements(ctIndices,paiIndices);
gfxDepthFunc(GFX_LESS_EQUAL);
}
}
if(_ptrTerrain->GetFlags()&TR_HAS_FOG) {
RenderFogLayer(itt);
}
if(_ptrTerrain->GetFlags()&TR_HAS_HAZE) {
RenderHazeLayer(itt);
}
gfxUnlockArrays();
}
// Draw one quad tree node ( draws terrain tile if leaf node )
static void DrawQuadTreeNode(INDEX iqtn)
{
ASSERT(_ptrTerrain!=NULL);
CEntity *pen = _ptrTerrain->tr_penEntity;
QuadTreeNode &qtn = _ptrTerrain->tr_aqtnQuadTreeNodes[iqtn];
FLOATmatrix3D &mAbsToView = _aprProjection->pr_ViewerRotationMatrix;
FLOATobbox3D obbox = FLOATobbox3D( qtn.qtn_aabbox,
(pen->en_plPlacement.pl_PositionVector-_aprProjection->pr_vViewerPosition)*mAbsToView, mAbsToView*pen->en_mRotation);
INDEX iFrustumTest = _aprProjection->TestBoxToFrustum(obbox);
if(iFrustumTest!=(-1)) {
// is this leaf node
if(qtn.qtn_iTileIndex != -1) {
_ctNodesVis++;
// draw terrain tile for this node
RenderTile(qtn.qtn_iTileIndex);
// this node has some children
} else {
for(INDEX iqc=0;iqc<4;iqc++) {
INDEX iChildNode = qtn.qtn_iChild[iqc];
// if child node exists
if(iChildNode != -1) {
// draw child node
DrawQuadTreeNode(qtn.qtn_iChild[iqc]);
}
}
}
}
}
// Render one terrain
void RenderTerrain(void)
{
ASSERT(_ptrTerrain!=NULL);
ASSERT(_ptrTerrain->tr_penEntity!=NULL);
_ctNodesVis = 0;
_ctTris = 0;
_ctDelayedNodes = 0;
// draw node from last level
INDEX ctqtl = _ptrTerrain->tr_aqtlQuadTreeLevels.Count();
QuadTreeLevel &qtl = _ptrTerrain->tr_aqtlQuadTreeLevels[ctqtl-1];
DrawQuadTreeNode(qtl.qtl_iFirstNode);
// if any delayed tiles
if(_ctDelayedNodes>0) {
// Draw delayed tiles
RenderBatchedTiles();
}
//CEntity *pen = _ptrTerrain->tr_penEntity;
extern void ShowRayPath(CDrawPort *pdp);
ShowRayPath(_pdp);
/*
extern CStaticStackArray<GFXVertex> _avExtVertices;
extern CStaticStackArray<INDEX> _aiExtIndices;
extern FLOATaabbox3D _bboxDrawOne;
extern FLOATaabbox3D _bboxDrawTwo;
#pragma message(">> Remove gfxDrawWireBox")
FLOATaabbox3D bboxAllTerrain;
extern FLOAT3D _vHitBegin;
extern FLOAT3D _vHitEnd;
extern FLOAT3D _vHitExact;
_ptrTerrain->GetAllTerrainBBox(bboxAllTerrain);
gfxDrawWireBox(bboxAllTerrain,0xFFFF00FF);
gfxEnableDepthBias();
gfxDisableDepthTest();
_pdp->DrawPoint3D(_vHitBegin,0x00FF00FF,8);
_pdp->DrawPoint3D(_vHitEnd,0xFF0000FF,8);
_pdp->DrawPoint3D(_vHitExact,0x00FFFF,8);
_pdp->DrawLine3D(_vHitBegin,FLOAT3D(_vHitEnd(1),_vHitBegin(2),_vHitEnd(3)),0x00FF00FF);
_pdp->DrawLine3D(FLOAT3D(_vHitBegin(1),_vHitEnd(2),_vHitBegin(3)),_vHitEnd,0xFF0000FF);
_pdp->DrawLine3D(_vHitBegin,_vHitEnd,0xFFFF00FF);
gfxEnableDepthTest();
gfxDisableDepthBias();
*/
//gfxDrawWireBox(_bboxDrawOne,0xFF0000FF);
//gfxDrawWireBox(_bboxDrawTwo,0x0000FFFF);
//gfxDrawWireBox(_bboxDrawNextFrame,0xFFFFFFFF);
}
// Render one tile in wireframe mode
static void RenderWireTile(INDEX itt)
{
ASSERT(_ptrTerrain!=NULL);
CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
INDEX ctVertices = tt.GetVertices().Count();
GFXVertex4 *pavVertices;
if(ter_bLerpVertices) {
PrepareSmothVertices(itt);
pavVertices = &_avLerpedVerices[0];
} else {
pavVertices = &tt.GetVertices()[0];
}
INDEX ctIndices = tt.GetIndices().Count();
if(ctIndices>0) {
gfxDisableBlend();
gfxDisableTexture();
gfxSetConstantColor(_colTerrainEdges);
gfxSetVertexArray(pavVertices,ctVertices);
gfxLockArrays();
gfxDrawElements(ctIndices,&tt.GetIndices()[0]);
gfxUnlockArrays();
}
}
// Draw one quad tree node ( draws terrain tile in wireframe mode if leaf node )
static void DrawWireQuadTreeNode(INDEX iqtn)
{
ASSERT(_ptrTerrain!=NULL);
CEntity *pen = _ptrTerrain->tr_penEntity;
QuadTreeNode &qtn = _ptrTerrain->tr_aqtnQuadTreeNodes[iqtn];
FLOATmatrix3D &mAbsToView = _aprProjection->pr_ViewerRotationMatrix;
FLOATobbox3D obbox = FLOATobbox3D( qtn.qtn_aabbox,
(pen->en_plPlacement.pl_PositionVector-_aprProjection->pr_vViewerPosition)*mAbsToView, mAbsToView*pen->en_mRotation);
INDEX iFrustumTest = _aprProjection->TestBoxToFrustum(obbox);
if(iFrustumTest!=(-1)) {
// is this leaf node
if(qtn.qtn_iTileIndex != -1) {
_ctNodesVis++;
// draw terrain tile for this node
RenderWireTile(qtn.qtn_iTileIndex);
// this node has some children
} else {
for(INDEX iqc=0;iqc<4;iqc++) {
INDEX iChildNode = qtn.qtn_iChild[iqc];
// if child node exists
if(iChildNode != -1) {
// draw child node
DrawWireQuadTreeNode(qtn.qtn_iChild[iqc]);
}
}
}
}
}
// Render one terrain in wireframe mode
void RenderTerrainWire(COLOR &colEdges)
{
// set wireframe mode
gfxEnableDepthBias();
gfxPolygonMode(GFX_LINE);
// remember edges color
_colTerrainEdges = colEdges;
ASSERT(_ptrTerrain!=NULL);
// draw last node
INDEX ctqtl = _ptrTerrain->tr_aqtlQuadTreeLevels.Count();
QuadTreeLevel &qtl = _ptrTerrain->tr_aqtlQuadTreeLevels[ctqtl-1];
DrawWireQuadTreeNode(qtl.qtl_iFirstNode);
// set fill mode
gfxDisableDepthBias();
gfxPolygonMode(GFX_FILL);
}
// Draw terrain quad tree
void DrawQuadTree(void)
{
ASSERT(_ptrTerrain!=NULL);
QuadTreeLevel &qtl = _ptrTerrain->tr_aqtlQuadTreeLevels[0];
gfxDisableTexture();
// for each quad tree node
for(INDEX iqtn=qtl.qtl_iFirstNode;iqtn<qtl.qtl_iFirstNode+qtl.qtl_ctNodes;iqtn++) {
// draw node
QuadTreeNode &qtn = _ptrTerrain->tr_aqtnQuadTreeNodes[iqtn];
gfxDrawWireBox(qtn.qtn_aabbox,0x00FF00FF);
}
}
void DrawSelectedVertices(GFXVertex *pavVertices, GFXColor *pacolColors, INDEX ctVertices)
{
gfxEnableDepthBias();
// for each vertex
for(INDEX ivx=0;ivx<ctVertices;ivx++) {
GFXVertex &vtx = pavVertices[ivx];
GFXColor &col = pacolColors[ivx];
// draw vertex
_pdp->DrawPoint3D(FLOAT3D(vtx.x,vtx.y,vtx.z),ByteSwap(col.ul.abgr),3);
}
gfxDisableDepthBias();
}
// TEMP - Draw one AABBox
void gfxDrawWireBox(FLOATaabbox3D &bbox, COLOR col)
{
FLOAT3D vMinVtx = bbox.Min();
FLOAT3D vMaxVtx = bbox.Max();
// fill vertex array so it represents bounding box
FLOAT3D vBoxVtxs[8];
vBoxVtxs[0] = FLOAT3D( vMinVtx(1), vMinVtx(2), vMinVtx(3));
vBoxVtxs[1] = FLOAT3D( vMaxVtx(1), vMinVtx(2), vMinVtx(3));
vBoxVtxs[2] = FLOAT3D( vMaxVtx(1), vMinVtx(2), vMaxVtx(3));
vBoxVtxs[3] = FLOAT3D( vMinVtx(1), vMinVtx(2), vMaxVtx(3));
vBoxVtxs[4] = FLOAT3D( vMinVtx(1), vMaxVtx(2), vMinVtx(3));
vBoxVtxs[5] = FLOAT3D( vMaxVtx(1), vMaxVtx(2), vMinVtx(3));
vBoxVtxs[6] = FLOAT3D( vMaxVtx(1), vMaxVtx(2), vMaxVtx(3));
vBoxVtxs[7] = FLOAT3D( vMinVtx(1), vMaxVtx(2), vMaxVtx(3));
// connect vertices into lines of bounding box
INDEX iBoxLines[12][2];
iBoxLines[ 0][0] = 0; iBoxLines[ 0][1] = 1; iBoxLines[ 1][0] = 1; iBoxLines[ 1][1] = 2;
iBoxLines[ 2][0] = 2; iBoxLines[ 2][1] = 3; iBoxLines[ 3][0] = 3; iBoxLines[ 3][1] = 0;
iBoxLines[ 4][0] = 0; iBoxLines[ 4][1] = 4; iBoxLines[ 5][0] = 1; iBoxLines[ 5][1] = 5;
iBoxLines[ 6][0] = 2; iBoxLines[ 6][1] = 6; iBoxLines[ 7][0] = 3; iBoxLines[ 7][1] = 7;
iBoxLines[ 8][0] = 4; iBoxLines[ 8][1] = 5; iBoxLines[ 9][0] = 5; iBoxLines[ 9][1] = 6;
iBoxLines[10][0] = 6; iBoxLines[10][1] = 7; iBoxLines[11][0] = 7; iBoxLines[11][1] = 4;
// for all vertices in bounding box
for( INDEX i=0; i<12; i++) {
// get starting and ending vertices of one line
FLOAT3D &v0 = vBoxVtxs[iBoxLines[i][0]];
FLOAT3D &v1 = vBoxVtxs[iBoxLines[i][1]];
_pdp->DrawLine3D(v0,v1,col);
}
}