/* 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 #include #include #include #include #include #include #include #include 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 _avLerpedVerices; CStaticStackArray _avLerpedTileLayerVertices; // Arrays for batch rendering of tiles is lowest mip static CStaticStackArray _avDelayedVertices; static CStaticStackArray _aiDelayedIndices; static CStaticStackArray _auvDelayedTexCoords; static CStaticStackArray _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 _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> 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=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;ivxttTop.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=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 && fLerpFactorttBottom.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=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;ivxttLeft.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=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 && fLerpFactorttRight.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;ivxtr_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=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=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=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=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 && fRightLerpFactortr_tdTopMap.SetAsCurrent(); GFXVertex4 *pavVertices = &_avDelayedVertices[0]; GFXTexCoord *pauvTexCoords = &_auvDelayedTexCoords[0]; GFXTexCoord *pauvShadowMapTC = &_auvDelayedShadowMapTC[0]; INDEX *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 *paiIndices = &tt.GetIndices()[0]; GFXVertex4 *pavDelVertices = _avDelayedVertices.Push(9); GFXTexCoord *pauvDelTexCoords = _auvDelayedTexCoords.Push(9); GFXTexCoord *pauvDelShadowMapTC = _auvDelayedShadowMapTC.Push(9); INDEX *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 _atcHaze; static CStaticStackArray _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 *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;ivxtr_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 *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;ivxtr_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;itltr_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 *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 *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 _avExtVertices; extern CStaticStackArray _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;iqtntr_aqtnQuadTreeNodes[iqtn]; gfxDrawWireBox(qtn.qtn_aabbox,0x00FF00FF); } } void DrawSelectedVertices(GFXVertex *pavVertices, GFXColor *pacolColors, INDEX ctVertices) { gfxEnableDepthBias(); // for each vertex for(INDEX ivx=0;ivxDrawPoint3D(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); } }