/* 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 #include #include #include #include #include #include #include #include extern CTerrain *_ptrTerrain; extern BOOL _bWorldEditorApp; // is this world edtior app static INDEX _iTerrainVersion = 9; // Current terrain version static INDEX ctGeneratedTopMaps = 0; // TEMP static INDEX ctGlobalTopMaps = 0; // TEMP INDEX _ctShadowMapUpdates = 0; // TEMP // TEMP INDEX _ctNodesVis = 0; INDEX _ctTris = 0; INDEX _ctDelayedNodes = 0; static void ShowTerrainInfo(CAnyProjection3D &apr, CDrawPort *pdp, CTerrain *ptrTerrain); // TEMP /* * Terrain initialization */ CTerrain::CTerrain() { tr_vStretch = FLOAT3D(1,0.05f,1); tr_fDistFactor = 32; tr_iMaxTileLod = 0; tr_iSelectedLayer = 0; tr_ctDriverChanges = -1; tr_pixHeightMapWidth = 256; tr_pixHeightMapHeight = 256; tr_pixTopMapWidth = 256; tr_pixTopMapHeight = 256; tr_pixFirstMipTopMapWidth = 256; tr_pixFirstMipTopMapHeight = 256; tr_ptdDetailMap = NULL; tr_auwHeightMap = NULL; tr_aubEdgeMap = NULL; tr_auwShadingMap = NULL; // TEMP try { tr_ptdDetailMap = _pTextureStock->Obtain_t((CTString)"Textures\\Detail\\Crumples04.tex"); } catch(char *) { } // Set size of shadow and shading maps SetShadowMapsSize(0,0); // Set terrain size SetTerrainSize(FLOAT3D(256,80,256)); // Set num of quads in one tile SetQuadsPerTileRow(32); SetFlags(TR_REGENERATE); } // Render visible terrain tiles void CTerrain::Render(CAnyProjection3D &apr, CDrawPort *pdp) { // prepare gfx stuff PrepareScene(apr, pdp, this); if(tr_ctDriverChanges!=_pGfx->gl_ctDriverChanges) { tr_ctDriverChanges = _pGfx->gl_ctDriverChanges; // RefreshTerrain(); } // if terrain is not frozen extern INDEX ter_bNoRegeneration; if(GetFlags()&TR_REGENERATE && !ter_bNoRegeneration) { // Regenerate tiles ReGenerate(); } // if shadow map must be regenerated if(GetFlags()&TR_UPDATE_SHADOWMAP) { UpdateShadowMap(); } // if top map regen is allowed if(GetFlags()&TR_ALLOW_TOP_MAP_REGEN) { // if top map regen is requested if(GetFlags()&TR_REGENERATE_TOP_MAP) { // update terrain top map UpdateTopMap(-1); // remove request for top map regen RemoveFlag(TR_REGENERATE_TOP_MAP); } // remove flag that allows terrain to regenerate top map RemoveFlag(TR_ALLOW_TOP_MAP_REGEN); } // show RenderTerrain(); // if flag show brush selection has been set if(GetFlags()&TR_SHOW_SELECTION) { ShowSelectionInternal(this); // remove show selection terrain flag RemoveFlag(TR_SHOW_SELECTION); } // if flag for quadtree rebuilding is set if(GetFlags()&TR_REBUILD_QUADTREE) { // Resize quadtree UpdateQuadTree(); // remove flag for quadtree rebuilding RemoveFlag(TR_REBUILD_QUADTREE); } RemoveFlag(TR_HAS_FOG); RemoveFlag(TR_HAS_HAZE); extern INDEX ter_bShowWireframe; // if wireframe mode forced if(ter_bShowWireframe) { COLOR colWire = 0xFFFFFFFF; RenderTerrainWire(colWire); } extern INDEX ter_bShowQuadTree; // if showing of quad tree is required if(ter_bShowQuadTree) { DrawQuadTree(); } extern INDEX ter_bShowInfo; if(ter_bShowInfo) { ShowTerrainInfo(apr, pdp, this); } _ptrTerrain = NULL; } void CTerrain::RenderWireFrame(CAnyProjection3D &apr, CDrawPort *pdp, COLOR &colEdges) { // prepare gfx stuff PrepareScene(apr, pdp, this); // Regenerate tiles if(tr_ctTiles>=0) { CTerrainTile &tt = tr_attTiles[0]; if(tt.tt_iLod == -1) { ReGenerate(); } } // show RenderTerrainWire(colEdges); } // Create empty terrain with given size void CTerrain::CreateEmptyTerrain_t(PIX pixWidth,PIX pixHeight) { _ptrTerrain = this; // Clear old terrain data if exists Clear(); ASSERT(tr_auwHeightMap==NULL); ASSERT(tr_aubEdgeMap==NULL); AllocateHeightMap(pixWidth,pixHeight); AddDefaultLayer_t(); // Rebuild terrain ReBuildTerrain(); _ptrTerrain = NULL; } // Import height map from targa file void CTerrain::ImportHeightMap_t(CTFileName fnHeightMap, BOOL bUse16b/*=TRUE*/) { _ptrTerrain = this; BOOL bResizeTerrain = FALSE; // Load targa file CImageInfo iiHeightMap; iiHeightMap.LoadAnyGfxFormat_t(fnHeightMap); // if new width and height are same if(tr_pixHeightMapWidth==iiHeightMap.ii_Width && tr_pixHeightMapHeight==iiHeightMap.ii_Height) { // Clear terrain data without removing layers bResizeTerrain = FALSE; } else { // Clear all terrain data bResizeTerrain = TRUE; } bResizeTerrain = TRUE; FLOAT fLogWidht = Log2(iiHeightMap.ii_Width-1); FLOAT fLogHeight = Log2(iiHeightMap.ii_Height-1); if(fLogWidht!=INDEX(fLogWidht) || fLogHeight!=INDEX(fLogHeight)) { ThrowF_t("Invalid terrain width or height"); } if(iiHeightMap.ii_Width!= iiHeightMap.ii_Height) { ThrowF_t("Only terrains with same width and height are supported in this version"); } // Reallocate memory for terrain with size ReAllocateHeightMap(iiHeightMap.ii_Width, iiHeightMap.ii_Height); INDEX iHeightMapSize = iiHeightMap.ii_Width * iiHeightMap.ii_Height; UBYTE *puwSrc = &iiHeightMap.ii_Picture[0]; UWORD *puwDst = &tr_auwHeightMap[0]; INDEX iBpp = iiHeightMap.ii_BitsPerPixel/8; // for each word in loaded image for(INDEX iw=0;iw>8); } pacolImage++; puwHeight++; } iiHeightMap.SaveTGA_t(fnHeightMap); iiHeightMap.Clear(); } // Rebuild all terrain void CTerrain::ReBuildTerrain(BOOL bDelayTileRegen/*=FALSE*/) { _ptrTerrain = this; ClearTopMaps(); ClearTiles(); ClearArrays(); ClearQuadTree(); // Make sure terrain is same size (in metars) SetTerrainSize(tr_vTerrainSize); // Build terrain data BuildTerrainData(); // Build terrain quadtree BuildQuadTree(); // Generate global top map GenerateTerrainTopMap(); // Clear current regen list ClearRegenList(); // Add all tiles to reqen queue AddAllTilesToRegenQueue(); // if not delaying tile regen if(!bDelayTileRegen) { // Regenerate tiles now ReGenerate(); // Update shadow map UpdateShadowMap(); } } // Refresh terrain void CTerrain::RefreshTerrain(void) { ReBuildTerrain(); } // Set terrain size void CTerrain::SetTerrainSize(FLOAT3D vSize) { tr_vStretch(1) = vSize(1) / (tr_pixHeightMapWidth-1); tr_vStretch(2) = vSize(2) / 65535.0f; tr_vStretch(3) = vSize(3) / (tr_pixHeightMapHeight-1); // remember new size tr_vTerrainSize = vSize; } template static void CropMap(INDEX iNewWidth, INDEX iNewHeight, INDEX iOldWidth, INDEX iOldHeight, Type *pNewData, Type *pOldData) { INDEX iWidth = Min(iOldWidth,iNewWidth); INDEX iHeight = Min(iOldHeight,iNewHeight); INDEX iNewStepX = ClampDn(iNewWidth-iOldWidth,0); INDEX iOldStepX = ClampDn(iOldWidth-iNewWidth,0); INDEX iNew = 0; INDEX iOld = 0; for(INDEX iy=0;iy static void StretchMap(INDEX iNewWidth, INDEX iNewHeight, INDEX iOldWidth, INDEX iOldHeight, Type *pNewData, Type *pOldData) { int a=0; CropMap(iNewWidth,iNewHeight,iOldWidth,iOldHeight,pNewData,pOldData); } template static void ShrinkMap(INDEX iNewWidth, INDEX iNewHeight, INDEX iOldWidth, INDEX iOldHeight, Type *pNewData, Type *pOldData) { FLOAT fWidth = iNewWidth; FLOAT fHeight = iNewHeight; FLOAT fDiffX = (FLOAT)iNewWidth / iOldWidth; FLOAT fDiffY = (FLOAT)iNewHeight / iOldHeight; ULONG *pulNewData = (ULONG*)AllocMemory(iNewWidth * iNewHeight * sizeof(ULONG)); memset(pulNewData,0,iNewWidth * iNewHeight * sizeof(ULONG)); INDEX iOldPix = 0; for(FLOAT fy=0;fy static void ResizeMap(INDEX iNewWidth, INDEX iNewHeight, INDEX iOldWidth, INDEX iOldHeight, Type *pNewData, Type *pOldData) { CropMap(iNewWidth,iNewHeight,iOldWidth,iOldHeight,pNewData,pOldData); /* if(iNewWidth>=iOldWidth && iNewHeight>=iOldHeight) { StretchMap(iNewWidth,iNewHeight,iOldWidth,iOldHeight,pNewData,pOldData); } else if(iNewWidth<=iOldWidth && iNewHeight<=iOldHeight) { ShrinkMap(iNewWidth,iNewHeight,iOldWidth,iOldHeight,pNewData,pOldData); } else { INDEX iTempWidth = Max(iNewWidth ,iOldWidth); INDEX iTempHeight = Max(iNewHeight,iOldHeight); INDEX iTempSize = iTempWidth*iTempHeight*sizeof(Type); Type *pTempData = (Type*)AllocMemory(iTempSize); memset(pTempData,0,iTempSize); StretchMap(iTempWidth,iTempHeight,iOldWidth,iOldHeight,pTempData,pOldData); ShrinkMap(iNewWidth,iNewHeight,iTempWidth,iTempHeight,pNewData, pTempData); FreeMemory(pTempData); } */ } void CTerrain::AllocateHeightMap(PIX pixWidth, PIX pixHeight) { ASSERT(tr_auwHeightMap==NULL); ASSERT(tr_aubEdgeMap==NULL); FLOAT fLogWidht = Log2(pixWidth-1); FLOAT fLogHeight = Log2(pixHeight-1); if(fLogWidht!=INDEX(fLogWidht) || fLogHeight!=INDEX(fLogHeight)) { ASSERTALWAYS("Invalid terrain width or height"); return; } if(pixWidth != pixHeight) { ASSERTALWAYS("Only terrains with same width and height are supported in this version"); return; } INDEX iSize = pixWidth * pixHeight * sizeof(UBYTE); // Allocate memory for maps tr_auwHeightMap = (UWORD*)AllocMemory(iSize*2); tr_aubEdgeMap = (UBYTE*)AllocMemory(iSize); memset(tr_auwHeightMap,0,iSize*2); memset(tr_aubEdgeMap,255,iSize); tr_pixHeightMapWidth = pixWidth; tr_pixHeightMapHeight = pixHeight; // Update shadow map size cos it depends on size of height map SetShadowMapsSize(tr_iShadowMapSizeAspect,tr_iShadingMapSizeAspect); } void CTerrain::ReAllocateHeightMap(PIX pixWidth, PIX pixHeight) { ASSERT(tr_auwHeightMap!=NULL); ASSERT(tr_aubEdgeMap!=NULL); FLOAT fLogWidht = Log2(pixWidth-1); FLOAT fLogHeight = Log2(pixHeight-1); if(fLogWidht!=INDEX(fLogWidht) || fLogHeight!=INDEX(fLogHeight)) { ASSERTALWAYS("Invalid terrain width or height"); return; } if(pixWidth != pixHeight) { ASSERTALWAYS("Only terrains with same width and height are supported in this version"); return; } INDEX iSize = pixWidth * pixHeight * sizeof(UBYTE); // Allocate memory for maps UWORD *auwHeightMap = (UWORD*)AllocMemory(iSize*2); UBYTE *aubEdgeMap = (UBYTE*)AllocMemory(iSize); // Resize height map memset(auwHeightMap,0,iSize*2); ResizeMap(pixWidth,pixHeight,tr_pixHeightMapWidth,tr_pixHeightMapHeight,auwHeightMap,tr_auwHeightMap); // Resize edge map memset(aubEdgeMap,255,iSize); ResizeMap(pixWidth,pixHeight,tr_pixHeightMapWidth,tr_pixHeightMapHeight,aubEdgeMap,tr_aubEdgeMap); // for each layer INDEX cttl = tr_atlLayers.Count(); for(INDEX itl=0;itl> Clamp dn SetShadowMapsSize") if(iShadingMapAspect<0) { iShadingMapAspect = 0; } ASSERT(iShadingMapAspect>=0); tr_iShadowMapSizeAspect = iShadowMapAspect; tr_iShadingMapSizeAspect = iShadingMapAspect; if(GetShadowMapWidth()<32 || GetShadingMapHeight()<32) { tr_iShadowMapSizeAspect = -(FastLog2(tr_pixHeightMapWidth-1)-5); } if(GetShadingMapWidth()<32 || GetShadingMapHeight()<32) { tr_iShadingMapSizeAspect = 0; } PIX pixShadowMapWidth = GetShadowMapWidth(); PIX pixShadowMapHeight = GetShadowMapHeight(); PIX pixShadingMapWidth = GetShadingMapWidth(); PIX pixShadingMapHeight = GetShadingMapHeight(); // Clear current shadow map ClearShadowMap(); ULONG ulShadowMapFlags = 0; // if current app is world editor app if(_bWorldEditorApp) { // force texture to be static ulShadowMapFlags = TEX_STATIC; } // Create new shadow map texture ASSERT(tr_tdShadowMap.td_pulFrames==NULL); CreateTexture(tr_tdShadowMap,pixShadowMapWidth,pixShadowMapHeight,ulShadowMapFlags); // Reset shadow map texture memset(&tr_tdShadowMap.td_pulFrames[0],0,sizeof(COLOR)*pixShadowMapWidth*pixShadowMapHeight); // Create new shading map ASSERT(tr_auwShadingMap==NULL); tr_auwShadingMap = (UWORD*)AllocMemory(pixShadingMapWidth*pixShadingMapHeight*sizeof(UWORD)); // Reset shading map memset(&tr_auwShadingMap[0],0,pixShadingMapWidth*pixShadingMapHeight*sizeof(UWORD)); } // Set size of terrain top map texture void CTerrain::SetGlobalTopMapSize(PIX pixTopMapSize) { FLOAT fLogSize = Log2(pixTopMapSize); if(fLogSize!=INDEX(fLogSize)) { ASSERTALWAYS("Invalid top map size"); return; } tr_pixTopMapWidth = pixTopMapSize; tr_pixTopMapHeight = pixTopMapSize; } // Set size of top map texture for tiles in lower lods void CTerrain::SetTileTopMapSize(PIX pixLodTopMapSize) { FLOAT fLogSize = Log2(pixLodTopMapSize); if(fLogSize!=INDEX(fLogSize)) { ASSERTALWAYS("Invalid top map size"); return; } tr_pixFirstMipTopMapWidth = pixLodTopMapSize; tr_pixFirstMipTopMapHeight = pixLodTopMapSize; } // Set lod distance factor void CTerrain::SetLodDistanceFactor(FLOAT fLodDistance) { tr_fDistFactor = ClampDn(fLodDistance,0.1f); } // Get shadow map size PIX CTerrain::GetShadowMapWidth(void) { if(tr_iShadowMapSizeAspect<0) { return (tr_pixHeightMapWidth-1)>>-tr_iShadowMapSizeAspect; } else { return (tr_pixHeightMapWidth-1)<>-tr_iShadowMapSizeAspect; } else { return (tr_pixHeightMapHeight-1)<=0); return GetShadowMapWidth()>>tr_iShadingMapSizeAspect; } PIX CTerrain::GetShadingMapHeight(void) { ASSERT(tr_iShadingMapSizeAspect>=0); return GetShadowMapHeight()>>tr_iShadingMapSizeAspect; } // Get reference to layer CTerrainLayer &CTerrain::GetLayer(INDEX iLayer) { INDEX cttl = tr_atlLayers.Count(); ASSERT(iLayer=0); return tr_atlLayers[iLayer]; } // Add new layer CTerrainLayer &CTerrain::AddLayer_t(CTFileName fnTexture, LayerType ltType/*=LT_NORMAL*/, BOOL bUpdateTerrain/*=TRUE*/) { CTerrainLayer &tl = tr_atlLayers.Push(); // Set layer properties tl.tl_ltType = ltType; tl.SetLayerSize(tr_pixHeightMapWidth, tr_pixHeightMapHeight); tl.SetLayerTexture_t(fnTexture); // if update terrain flag has been set if(bUpdateTerrain) { // Refresh whole terrain RefreshTerrain(); } return tl; } // Remove one layer void CTerrain::RemoveLayer(INDEX iLayer, BOOL bUpdateTerrain/*=TRUE*/) { CStaticStackArray atlLayers; INDEX cttl = tr_atlLayers.Count(); if(iLayer<0 || iLayer>=cttl) { ASSERTALWAYS("Invalid layer index"); return; } if(iLayer==0 && cttl==1) { ASSERTALWAYS("Can't remove last layer"); return; } // for each exisiting layer for(INDEX itl=0;itl atlLayers; INDEX cttl = tr_atlLayers.Count(); if(iLayer<0 || iLayer>=cttl) { ASSERTALWAYS("Invalid layer index"); return iLayer; } if(iLayer==0 && cttl==1) { ASSERTALWAYS("Can't move only layer"); return iLayer; } if(iLayer==iNewIndex) { ASSERTALWAYS("Old layer index is same as new one"); return iLayer; } CStaticStackArray &atlFrom = tr_atlLayers; CStaticStackArray &atlTo = atlLayers; atlTo.Push(cttl); INDEX iOld = iLayer; INDEX iNew = iNewIndex; for(INDEX iFrom=0; iFromiOld && iFrom>iNew)) { iTo = iFrom; } else if (iFrom==iOld) { iTo = iNew; } else { if (iNew>iOld) { iTo = iFrom-1; } else { iTo = iFrom+1; } } atlTo[iTo] = atlFrom[iFrom]; } ASSERT(atlLayers.Count() == cttl); // Clear old layers tr_atlLayers.Clear(); // Copy new layers insted of old one tr_atlLayers = atlLayers; // if update terrain flag has been set if(bUpdateTerrain) { // Refresh whole terrain RefreshTerrain(); } return iNewIndex; } // Add tile to reqen queue void CTerrain::AddTileToRegenQueue(INDEX iTileIndex) { INDEX &iRegenIndex = tr_auiRegenList.Push(); CTerrainTile &tt = tr_attTiles[iTileIndex]; iRegenIndex = iTileIndex; tt.AddFlag(TT_REGENERATE); } // Add all tiles to regen queue void CTerrain::AddAllTilesToRegenQueue() { // for each terrain tile for(INDEX itt=0;ittub.r = Lerp(pcolSrc->ub.r,pcolDst->ub.r,fMaskStrength); pcolSrc->ub.g = Lerp(pcolSrc->ub.g,pcolDst->ub.g,fMaskStrength); pcolSrc->ub.b = Lerp(pcolSrc->ub.b,pcolDst->ub.b,fMaskStrength); pcolSrc->ub.a = 255; } static INDEX _ctSavedTopMaps=0; static void SaveAsTga(CTextureData *ptdTex) { INDEX iSize = ptdTex->td_mexWidth * ptdTex->td_mexHeight * 4; CImageInfo iiHeightMap; iiHeightMap.ii_Width = ptdTex->td_mexWidth; iiHeightMap.ii_Height = ptdTex->td_mexHeight; iiHeightMap.ii_BitsPerPixel = 32; iiHeightMap.ii_Picture = (UBYTE*)AllocMemory(iSize); memcpy(&iiHeightMap.ii_Picture[0],&ptdTex->td_pulFrames[0],iSize); CTString strTopMap = CTString(0,"Temp\\Topmap%d.tga",++_ctSavedTopMaps); iiHeightMap.SaveTGA_t(strTopMap); iiHeightMap.Clear(); /* GFXColor *pacolImage = (GFXColor*)&iiHeightMap.ii_Picture[0]; UWORD *puwHeight = tr_auwHeightMap; for(INDEX ipix=0;ipix>8); } pacolImage++; puwHeight++; } */ } static void AddTileLayerToTopMap(CTerrain *ptrTerrain, INDEX iTileIndex, INDEX iLayer) { CTerrainLayer &tl = ptrTerrain->tr_atlLayers[iLayer]; CTextureData *ptdSrc = tl.tl_ptdTexture; CTextureData *ptdDst; INDEX ctQuadsPerTile = ptrTerrain->tr_ctQuadsInTileRow; INDEX iOffsetX = 0; INDEX iOffsetZ = 0; if(iTileIndex==(-1)) { ptdDst = &ptrTerrain->tr_tdTopMap; ctQuadsPerTile = ptrTerrain->tr_ctTilesX * ptrTerrain->tr_ctQuadsInTileRow; } else { CTerrainTile &tt = ptrTerrain->tr_attTiles[iTileIndex]; ptdDst = tt.GetTopMap(); iOffsetX = tt.tt_iOffsetX*ctQuadsPerTile; iOffsetZ = tt.tt_iOffsetZ*ctQuadsPerTile; } ULONG *pulFirstInTopMap = ptdDst->td_pulFrames; UBYTE *pubFirstInLayerMask = tl.tl_aubColors; // Calculate width and height of quad that will be draw in top map PIX pixDstQuadWidth = ptdDst->GetPixWidth() / ctQuadsPerTile; PIX pixSrcQuadWidth = tl.tl_pixTileWidth; // if dst quad is smaller then one pixel if(pixDstQuadWidth==0) { return; } ASSERT(tl.tl_ctTilesInRow==tl.tl_ctTilesInCol); INDEX iSrcMipmap = FastLog2((ptdSrc->GetPixWidth() / tl.tl_ctTilesInRow) / pixDstQuadWidth); INDEX iSrcMipMapOffset = GetMipmapOffset(iSrcMipmap,ptdSrc->GetPixWidth(),ptdSrc->GetPixHeight()); INDEX iSrcMipWidth = ptdSrc->GetPixWidth() >> iSrcMipmap; INDEX iSrcMipQuadWidth = pixSrcQuadWidth >> iSrcMipmap; INDEX iSrcMipQuadHeight = iSrcMipQuadWidth; ASSERT(pixDstQuadWidth==iSrcMipQuadWidth); ULONG *pulSrcMip = &ptdSrc->td_pulFrames[iSrcMipMapOffset]; INDEX iMaskIndex = iOffsetX + iOffsetZ*ptrTerrain->tr_pixHeightMapWidth; INDEX iMaskStepX = ptrTerrain->tr_pixHeightMapWidth - ctQuadsPerTile; // for each quad in tile for(INDEX iQuadY=0;iQuadY>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 iTile = ubMask&TL_TILE_INDEX; INDEX iTileX = iTile%tl.tl_ctTilesInRow; INDEX iTileY = iTile/tl.tl_ctTilesInRow; // if not visible if(!bVisible) { iMaskIndex++; continue; // skip it } ASSERT(iTileXGetPixWidth(); ULONG *pulDstPixel = &pulFirstInTopMap[iFirstDstQuadPixel]; PIX pixSrc = iTileX*iSrcMipQuadWidth + iTileY*iSrcMipQuadWidth*iSrcMipWidth; PIX pixDst = 0; PIX pixDstModulo = ptdDst->GetPixWidth() - pixDstQuadWidth; PIX pixSrcStepY = iSrcMipWidth; PIX pixSrcStepX = 1; if(bFlipY) { pixSrcStepY = -pixSrcStepY; pixSrc+=iSrcMipQuadHeight*iSrcMipWidth; } if(bFlipX) { pixSrcStepX = -pixSrcStepX; pixSrc+=iSrcMipQuadWidth; } if(bSwapXY) { Swap(pixSrcStepX, pixSrcStepY); } pixSrcStepY -= pixDstQuadWidth*pixSrcStepX; // for each pixel in this quad for(PIX pixY=0;pixY 0x80000000) { pulDstPixel[pixDst] = pulSrcMip[pixSrc]; } pixSrc+=pixSrcStepX; pixDst++; } pixDst+=pixDstModulo; pixSrc+=pixSrcStepY; } iMaskIndex++; } iMaskIndex+=iMaskStepX; } } void CTerrain::UpdateTopMap(INDEX iTileIndex, Rect *prcDest/*=NULL*/) { //ReGenerateTopMap(this, iTileIndex); if(iTileIndex==(-1)) { ctGlobalTopMaps++; } else { ctGeneratedTopMaps++; } FIX16_16 fiMaskDiv = 1; INDEX iFirstInMask = 0; INDEX iMaskWidth = tr_pixHeightMapWidth; INDEX iTiling = 1; INDEX iSrcMipWidth = 1; // destionation texture (must have set allocated memory) CTextureData *ptdDest; // if global top map if(iTileIndex==(-1)) { ptdDest = &tr_tdTopMap; // else tile top map } else { CTerrainTile &tt = tr_attTiles[iTileIndex]; ptdDest = tt.GetTopMap(); fiMaskDiv = tr_ctTilesX; iFirstInMask = iMaskWidth * tt.tt_iOffsetZ * (tr_ctVerticesInTileRow-1) + (tt.tt_iOffsetX * (tr_ctVerticesInTileRow-1)); } ASSERT(ptdDest->td_pulFrames==NULL); PrepareSharedTopMapMemory(ptdDest, iTileIndex); ASSERT(ptdDest!=NULL); ASSERT(ptdDest->td_pulFrames!=NULL); // ASSERT(ptdDest->GetPixWidth()>0 && ptdDest->GetPixHeight()>0 && ptdDest->GetPixWidth()==ptdDest->GetPixHeight()); // iTiling = ClampDn(iTiling,(INDEX)1); // INDEX iSrcMipWidth = ClampDn(ptdDest->GetWidth()/iTiling,(INDEX)1); INDEX ctLayers = tr_atlLayers.Count(); // for each layer for(INDEX itl=0;itlGetPixWidth()>0 && ptdDest->GetPixHeight()>0 && ptdDest->GetPixWidth()==ptdDest->GetPixHeight()); ASSERT(iTiling>=1); iTiling = ClampDn(iTiling,(INDEX)1); // get source texture CTextureData *ptdSrc = tl.tl_ptdTexture; INDEX iSrcMipWidth = ClampDn( ptdSrc->GetPixWidth() /iTiling, 1); INDEX iSrcMipHeight = ClampDn( ptdSrc->GetPixHeight()/iTiling, 1); // Get mipmap of source texture INDEX immW = FastLog2( ptdSrc->GetPixWidth() / iSrcMipWidth); INDEX immH = FastLog2( ptdSrc->GetPixHeight() / iSrcMipHeight); // get address of first byte in source mipmap INDEX imm = Max( immW, immH); INDEX iMipAdr = GetMipmapOffset(imm,ptdSrc->GetPixWidth(),ptdSrc->GetPixHeight()); // Mask thing // get first byte in layer mask UBYTE *ubFirstInMask = &tl.tl_aubColors[iFirstInMask]; // get first byte in edge map UBYTE *ubFirstInEdgeMap = &tr_aubEdgeMap[iFirstInMask]; FIX16_16 fiHMaskStep = FIX16_16(iMaskWidth-1) / FIX16_16(ptdDest->GetWidth()-1) / fiMaskDiv; FIX16_16 fiVMaskStep = FIX16_16(iMaskWidth-1) / FIX16_16(ptdDest->GetWidth()-1) / fiMaskDiv; SLONG xHMaskStep = fiHMaskStep.slHolder; SLONG xVMaskStep = fiVMaskStep.slHolder; SLONG xMaskVPos=0; // get first byte in destination texture ULONG *pulTexDst = (ULONG*)&ptdDest->td_pulFrames[0]; // get first byte in source texture ULONG *pulFirstInMipSrc = (ULONG*)&ptdSrc->td_pulFrames[iMipAdr]; // for each row for(UINT ir=0;irGetPixHeight();ir++) { // get first byte for src mip texture in this row ULONG *pulSrcRow = &pulFirstInMipSrc[(ir&(iSrcMipWidth-1))*iSrcMipWidth];//% INDEX iMaskVPos = (INDEX)(xMaskVPos>>16) * (iMaskWidth); UBYTE *pubMaskRow = &ubFirstInMask[iMaskVPos]; UBYTE *pubEdgeMaskRow = &ubFirstInEdgeMap[iMaskVPos]; SLONG xMaskHPos = 0; // for each column for(UINT ic=0;icGetPixWidth();ic++) { ULONG *ulSrc = &pulSrcRow[ic&(iSrcMipWidth-1)]; INDEX iMask = (INDEX)(xMaskHPos>>16); SLONG x1 = (SLONG)(pubMaskRow[iMask+0]) <<0; //NormByteToFixInt(pubMaskRow[iMask]); SLONG x2 = (SLONG)(pubMaskRow[iMask+1]) <<0; //NormByteToFixInt(pubMaskRow[iMask+1]); SLONG x3 = (SLONG)(pubMaskRow[iMask+iMaskWidth+0]) <<0;//NormByteToFixInt(pubMaskRow[iMask+iMaskWidth+0]); SLONG x4 = (SLONG)(pubMaskRow[iMask+iMaskWidth+1]) <<0;//NormByteToFixInt(pubMaskRow[iMask+iMaskWidth+1]); SLONG xFactH = xMaskHPos - (xMaskHPos&0xFFFF0000); SLONG xFactV = xMaskVPos - (xMaskVPos&0xFFFF0000); SLONG xStrengthX1 = (x1<<7) + (SLONG)(((x2-x1)*xFactH)>>9); //Lerp(fi1,fi2,fiFactH); SLONG xStrengthX2 = (x3<<7) + (SLONG)(((x4-x3)*xFactH)>>9); //Lerp(fi3,fi4,fiFactH); SLONG xStrength = (xStrengthX1<<1) + (SLONG)((((xStrengthX2>>0)-(xStrengthX1>>0))*xFactV)>>15); //Lerp(fiStrengthX1,fiStrengthX2,fiFactV); GFXColor *pcolSrc = (GFXColor*)pulTexDst; GFXColor *pcolDst = (GFXColor*)ulSrc; pcolSrc->ub.r = (BYTE)( (ULONG)pcolSrc->ub.r + ((((ULONG)pcolDst->ub.r - (ULONG)pcolSrc->ub.r) * xStrength)>>16)); pcolSrc->ub.g = (BYTE)( (ULONG)pcolSrc->ub.g + ((((ULONG)pcolDst->ub.g - (ULONG)pcolSrc->ub.g) * xStrength)>>16)); pcolSrc->ub.b = (BYTE)( (ULONG)pcolSrc->ub.b + ((((ULONG)pcolDst->ub.b - (ULONG)pcolSrc->ub.b) * xStrength)>>16)); pcolSrc->ub.a = pubEdgeMaskRow[iMask]; pulTexDst++; xMaskHPos += xHMaskStep; } xMaskVPos += xVMaskStep; } } // make mipmaps INDEX ctMipMaps = GetNoOfMipmaps(ptdDest->GetPixWidth(),ptdDest->GetPixHeight()); MakeMipmaps(ctMipMaps, ptdDest->td_pulFrames, ptdDest->GetPixWidth(), ptdDest->GetPixHeight()); #pragma message(">> Fix DitherMipmaps") INDEX iDithering = 4; DitherMipmaps(iDithering,ptdDest->td_pulFrames,ptdDest->td_pulFrames,ptdDest->GetPixWidth(),ptdDest->GetPixHeight()); // force topmap upload ptdDest->SetAsCurrent(0,TRUE); // Free shared memory FreeSharedTopMapMemory(ptdDest, iTileIndex); } void CTerrain::GetAllTerrainBBox(FLOATaabbox3D &bbox) { // Get last quad tree level INDEX ctqtl = tr_aqtlQuadTreeLevels.Count(); QuadTreeLevel &qtl = tr_aqtlQuadTreeLevels[ctqtl-1]; ASSERT(qtl.qtl_ctNodes==1); // Get quad tree node for last level QuadTreeNode &qtn = tr_aqtnQuadTreeNodes[qtl.qtl_iFirstNode]; bbox = qtn.qtn_aabbox; } // Get shading color from tex coords in shading map COLOR CTerrain::GetShadeColor(CShadingInfo *psi) { ASSERT(psi!=NULL); ASSERT(tr_auwShadingMap!=NULL); PIX pixShadowU = Clamp(psi->si_pixShadowU,0,GetShadingMapWidth()-2); PIX pixShadowV = Clamp(psi->si_pixShadowV,0,GetShadingMapHeight()-2); FLOAT fUDRatio = psi->si_fUDRatio; FLOAT fLRRatio = psi->si_fLRRatio; PIX pixWidth = GetShadingMapWidth(); PIX pixShadow = pixShadowU + pixShadowV*pixWidth; UWORD auwShade[4]; SLONG aslr[4], aslg[4], aslb[4]; auwShade[0] = tr_auwShadingMap[pixShadow]; auwShade[1] = tr_auwShadingMap[pixShadow+1]; auwShade[2] = tr_auwShadingMap[pixShadow+pixWidth+0]; auwShade[3] = tr_auwShadingMap[pixShadow+pixWidth+1]; for(INDEX ish=0;ish<4;ish++) { aslr[ish] = (auwShade[ish]&0x7C00)>>10; aslg[ish] = (auwShade[ish]&0x03E0)>> 5; aslb[ish] = (auwShade[ish]&0x001F)>> 0; aslr[ish] = (aslr[ish]<<3) | (aslr[ish]>>2); aslg[ish] = (aslg[ish]<<3) | (aslg[ish]>>2); aslb[ish] = (aslb[ish]<<3) | (aslb[ish]>>2); } SLONG slRed = Lerp( Lerp(aslr[0], aslr[1], fLRRatio), Lerp(aslr[2], aslr[3], fLRRatio), fUDRatio); SLONG slGreen = Lerp( Lerp(aslg[0], aslg[1], fLRRatio), Lerp(aslg[2], aslg[3], fLRRatio), fUDRatio); SLONG slBlue = Lerp( Lerp(aslb[0], aslb[1], fLRRatio), Lerp(aslb[2], aslb[3], fLRRatio), fUDRatio); ULONG ulPixel = ((slRed <<24)&0xFF000000) | ((slGreen<<16)&0x00FF0000) | ((slBlue << 8)&0x0000FF00) | 0xFF; return ulPixel; } // Get plane from given point FLOATplane3D CTerrain::GetPlaneFromPoint(FLOAT3D &vAbsPoint) { ASSERT(tr_penEntity!=NULL); FLOAT3D vRelPoint = (vAbsPoint-tr_penEntity->en_plPlacement.pl_PositionVector) * !tr_penEntity->en_mRotation; vRelPoint(1) /= tr_vStretch(1); vRelPoint(3) /= tr_vStretch(3); PIX pixX = (PIX) floor(vRelPoint(1)); PIX pixZ = (PIX) floor(vRelPoint(3)); PIX pixWidth = tr_pixHeightMapWidth; FLOAT fXRatio = vRelPoint(1) - pixX; FLOAT fZRatio = vRelPoint(3) - pixZ; INDEX iPix = pixX + pixZ*pixWidth; BOOL bFacing = (iPix)&1; FLOAT3D vx0 = FLOAT3D((pixX+0)*tr_vStretch(1),tr_auwHeightMap[iPix] * tr_vStretch(2) ,(pixZ+0)*tr_vStretch(3)); FLOAT3D vx1 = FLOAT3D((pixX+1)*tr_vStretch(1),tr_auwHeightMap[iPix+1] * tr_vStretch(2) ,(pixZ+0)*tr_vStretch(3)); FLOAT3D vx2 = FLOAT3D((pixX+0)*tr_vStretch(1),tr_auwHeightMap[iPix+pixWidth] * tr_vStretch(2) ,(pixZ+1)*tr_vStretch(3)); FLOAT3D vx3 = FLOAT3D((pixX+1)*tr_vStretch(1),tr_auwHeightMap[iPix+pixWidth+1]* tr_vStretch(2) ,(pixZ+1)*tr_vStretch(3)); vx0 = vx0 * tr_penEntity->en_mRotation + tr_penEntity->en_plPlacement.pl_PositionVector; vx1 = vx1 * tr_penEntity->en_mRotation + tr_penEntity->en_plPlacement.pl_PositionVector; vx2 = vx2 * tr_penEntity->en_mRotation + tr_penEntity->en_plPlacement.pl_PositionVector; vx3 = vx3 * tr_penEntity->en_mRotation + tr_penEntity->en_plPlacement.pl_PositionVector; if(bFacing) { if(fXRatio>=fZRatio) { return FLOATplane3D(vx0,vx2,vx1); } else { return FLOATplane3D(vx1,vx2,vx3); } } else { if(fXRatio>=fZRatio) { return FLOATplane3D(vx2,vx3,vx0); } else { return FLOATplane3D(vx0,vx3,vx1); } } } // Sets number of quads in row of one tile void CTerrain::SetQuadsPerTileRow(INDEX ctQuadsPerTileRow) { tr_ctQuadsInTileRow = Clamp(ctQuadsPerTileRow,(INDEX)4,(INDEX)(tr_pixHeightMapWidth-1)); if(tr_ctQuadsInTileRow!=ctQuadsPerTileRow) { CPrintF("Warning: Quads per tile has been changed from requested %d to %d\n",ctQuadsPerTileRow,tr_ctQuadsInTileRow); } // TODO: Assert that it is 2^n tr_ctVerticesInTileRow = tr_ctQuadsInTileRow+1; } // Set Terrain stretch void CTerrain::SetTerrainStretch(FLOAT3D vStretch) { tr_vStretch = vStretch; } // Build terrain data void CTerrain::BuildTerrainData() { // Allocate space for terrain tiles tr_ctTilesX = (tr_pixHeightMapWidth-1) / tr_ctQuadsInTileRow; tr_ctTilesY = (tr_pixHeightMapHeight-1) / tr_ctQuadsInTileRow; tr_ctTiles = tr_ctTilesX*tr_ctTilesY; tr_attTiles.New(tr_ctTiles); // Calculate max posible lod INDEX ctVtxInLod = tr_ctQuadsInTileRow; tr_iMaxTileLod = 0; while(ctVtxInLod>2) { tr_iMaxTileLod++; ctVtxInLod = ctVtxInLod>>1; } // Allocate memory for terrain tile arrays tr_aArrayHolders.New(tr_iMaxTileLod+1); INDEX ctah = tr_aArrayHolders.Count(); // for each array handler for(INDEX iah=0;iah0) tt.tt_aiNeighbours[NB_TOP] = iTileIndex-tr_ctTilesX; if(ix>0) tt.tt_aiNeighbours[NB_LEFT] = iTileIndex-1; if(iyGetPixWidth()>0 && ptdDest->GetPixHeight()>0 && ptdDest->GetPixWidth()==ptdDest->GetPixHeight()); INDEX iSrcMipWidth = ClampDn(ptdDest->GetWidth()/iTiling,(INDEX)1); INDEX ctLayers = tr_atlLayers.Count(); // for each layer for(INDEX itl=0;itlGetPixWidth()/iSrcMipWidth); // get address of first byte in source mipmap INDEX iMipAdr = GetMipmapOffset(imm,ptdSrc->GetPixWidth(),ptdSrc->GetPixHeight()); // Mask thing // get first byte in layer mask UBYTE *ubFirstInMask = &tl.tl_aubColors[iFirstInMask]; FIX16_16 fiHMaskStep = FIX16_16(iMaskWidth-1)/FIX16_16(ptdDest->GetWidth()-1)/fiMaskDiv; FIX16_16 fiVMaskStep = FIX16_16(iMaskWidth-1)/FIX16_16(ptdDest->GetWidth()-1)/fiMaskDiv; SLONG xHMaskStep = fiHMaskStep.slHolder; SLONG xVMaskStep = fiVMaskStep.slHolder; SLONG xMaskVPos=0; // get first byte in destination texture ULONG *pulTexDst = (ULONG*)&ptdDest->td_pulFrames[0]; // get first byte in source texture ULONG *pulFirstInMipSrc = (ULONG*)&ptdSrc->td_pulFrames[iMipAdr]; // for each row for(UINT ir=0;irGetHeight();ir++) { // get first byte for src mip texture in this row ULONG *pulSrcRow = &pulFirstInMipSrc[(ir&(iSrcMipWidth-1))*iSrcMipWidth];//% INDEX iMaskVPos = (INDEX)(xMaskVPos>>16) * (iMaskWidth); UBYTE *pubMaskRow = &ubFirstInMask[iMaskVPos]; SLONG xMaskHPos = 0; // for each column for(UINT ic=0;icGetWidth();ic++) { ULONG *ulSrc = &pulSrcRow[ic&(iSrcMipWidth-1)]; INDEX iMask = (INDEX)(xMaskHPos>>16); SLONG x1 = (SLONG)(pubMaskRow[iMask+0]) <<0; //NormByteToFixInt(pubMaskRow[iMask]); SLONG x2 = (SLONG)(pubMaskRow[iMask+1]) <<0; //NormByteToFixInt(pubMaskRow[iMask+1]); SLONG x3 = (SLONG)(pubMaskRow[iMask+iMaskWidth+0]) <<0;//NormByteToFixInt(pubMaskRow[iMask+iMaskWidth+0]); SLONG x4 = (SLONG)(pubMaskRow[iMask+iMaskWidth+1]) <<0;//NormByteToFixInt(pubMaskRow[iMask+iMaskWidth+1]); SLONG xFactH = xMaskHPos - (xMaskHPos&0xFFFF0000); SLONG xFactV = xMaskVPos - (xMaskVPos&0xFFFF0000); SLONG xStrengthX1 = (x1<<7) + (SLONG)(((x2-x1)*xFactH)>>9); //Lerp(fi1,fi2,fiFactH); SLONG xStrengthX2 = (x3<<7) + (SLONG)(((x4-x3)*xFactH)>>9); //Lerp(fi3,fi4,fiFactH); SLONG xStrength = (xStrengthX1<<1) + (SLONG)((((xStrengthX2>>0)-(xStrengthX1>>0))*xFactV)>>15); //Lerp(fiStrengthX1,fiStrengthX2,fiFactV); GFXColor *pcolSrc = (GFXColor*)pulTexDst; GFXColor *pcolDst = (GFXColor*)ulSrc; pcolSrc->r = (BYTE)( (ULONG)pcolSrc->r + ((((ULONG)pcolDst->r - (ULONG)pcolSrc->r) * xStrength)>>16)); pcolSrc->g = (BYTE)( (ULONG)pcolSrc->g + ((((ULONG)pcolDst->g - (ULONG)pcolSrc->g) * xStrength)>>16)); pcolSrc->b = (BYTE)( (ULONG)pcolSrc->b + ((((ULONG)pcolDst->b - (ULONG)pcolSrc->b) * xStrength)>>16)); pcolSrc->a = 255; pulTexDst++; xMaskHPos += xHMaskStep; } xMaskVPos += xVMaskStep; } } // make mipmaps MakeMipmaps(32, ptdDest->td_pulFrames, ptdDest->GetWidth(), ptdDest->GetHeight()); // force topmap upload ptdDest->SetAsCurrent(0,TRUE); } #else void CTerrain::GenerateTopMap(INDEX iTileIndex) { INDEX iMaskDiv = 1; INDEX iFirstInMask = 0; INDEX iMaskWidth = tr_pixHeightMapWidth; INDEX iTiling = (INDEX)(tr_ctTilesX*tr_ctQuadsInTileRow*tr_fTexStretch); // destionation texture (must have set required width and height) CTextureData *ptdDest; // if global top map if(iTileIndex==(-1)) { ptdDest = &tr_tdTopMap; // else tile top map } else { CTerrainTile &tt = tr_attTiles[iTileIndex]; ptdDest = &tt.GetTopMap(); iMaskDiv = tr_ctTilesX; iFirstInMask = iMaskWidth * tt.tt_iOffsetZ * (tr_ctVerticesInTileRow-1) + (tt.tt_iOffsetX * (tr_ctVerticesInTileRow-1)); iTiling = (INDEX)(tr_ctQuadsInTileRow*tr_fTexStretch); } ASSERT(ptdDest->GetPixWidth()>0 && ptdDest->GetPixHeight()>0 && ptdDest->GetPixWidth()==ptdDest->GetPixHeight()); INDEX iSrcMipWidth = ptdDest->GetWidth()/iTiling; INDEX ctLayers = tr_atlLayers.Count(); // for each layer for(INDEX ilr=0;ilrGetPixWidth()/iSrcMipWidth); // get address of first byte in source mipmap INDEX iMipAdr = GetMipmapOffset(imm,ptdSrc->GetPixWidth(),ptdSrc->GetPixHeight()); // Mask thing // get first byte in layer mask UBYTE *ubFirstInMask = &tl.tl_aubColors[iFirstInMask]; FLOAT fHMaskStep = FLOAT(iMaskWidth-1)/(ptdDest->GetWidth()-1)/iMaskDiv; FLOAT fVMaskStep = FLOAT(iMaskWidth-1)/(ptdDest->GetWidth()-1)/iMaskDiv; FLOAT fMaskVPos=0; // get first byte in destination texture ULONG *pulTexDst = (ULONG*)&ptdDest->td_pulFrames[0]; // get first byte in source texture ULONG *pulFirstInMipSrc = &ptdSrc->td_pulFrames[iMipAdr]; // for each row for(UINT ir=0;irGetWidth();ir++) { // get first byte for src mip texture in this row ULONG *pulSrcRow = &pulFirstInMipSrc[(ir&(iSrcMipWidth-1))*iSrcMipWidth];//% INDEX iMaskVPos = (INDEX)fMaskVPos * (iMaskWidth); UBYTE *pubMaskRow = &ubFirstInMask[iMaskVPos]; FLOAT fMaskHPos = 0; // for each column for(UINT ic=0;icGetWidth();ic++) { ULONG *ulSrc = &pulSrcRow[ic&(iSrcMipWidth-1)]; INDEX iMask = (INDEX)fMaskHPos; FLOAT f1 = NormByteToFloat(pubMaskRow[iMask]); FLOAT f2 = NormByteToFloat(pubMaskRow[iMask+1]); FLOAT f3 = NormByteToFloat(pubMaskRow[iMask+iMaskWidth+0]); FLOAT f4 = NormByteToFloat(pubMaskRow[iMask+iMaskWidth+1]); FLOAT fStrengthX1 = Lerp(f1,f2,fMaskHPos-(INDEX)fMaskHPos); FLOAT fStrengthX2 = Lerp(f3,f4,fMaskHPos-(INDEX)fMaskHPos); FLOAT fStrength = Lerp(fStrengthX1,fStrengthX2,fMaskVPos-(INDEX)fMaskVPos); CopyPixel(pulTexDst,ulSrc,fStrength); pulTexDst++; fMaskHPos+=fHMaskStep; } fMaskVPos+=fVMaskStep; } } // make mipmaps MakeMipmaps( 32, ptdDest->td_pulFrames, ptdDest->GetWidth(), ptdDest->GetHeight()); // force topmap upload ptdDest->SetAsCurrent(0,TRUE); } #endif */ void CTerrain::GenerateTerrainTopMap() { CreateTopMap(tr_tdTopMap,tr_pixTopMapWidth,tr_pixTopMapHeight); UpdateTopMap(-1); } // Add default layer void CTerrain::AddDefaultLayer_t(void) { // Add one layer using default texture, but do not refresh terrain CTerrainLayer &tl = AddLayer_t((CTString)"Textures\\Editor\\Default.TEX", LT_NORMAL, FALSE); // fill this layer tl.ResetLayerMask(255); } // Build quadtree for terrain void CTerrain::BuildQuadTree(void) { INDEX ctQuadNodeRows = tr_ctTilesX; INDEX ctQuadNodeCols = tr_ctTilesY; INDEX ctQuadNodes = 0; // Create quad tree levels while(TRUE) { QuadTreeLevel &qtl = tr_aqtlQuadTreeLevels.Push(); // Remember first node qtl.qtl_iFirstNode = ctQuadNodes; // Add nodes in this level to total node count ctQuadNodes += ClampDn(ctQuadNodeRows,(INDEX)1) * ClampDn(ctQuadNodeCols,(INDEX)1); // Count nodes in this level qtl.qtl_ctNodes = ctQuadNodes - qtl.qtl_iFirstNode; qtl.qtl_ctNodesCol = ctQuadNodeCols; qtl.qtl_ctNodesRow = ctQuadNodeRows; // if only one node is in this level if(qtl.qtl_ctNodes == 1) { // this is last level so exit loop break; } if(ctQuadNodeCols%2 == 1 && ctQuadNodeCols != 1) { ctQuadNodeCols = (ctQuadNodeCols+1)>>1; } else { ctQuadNodeCols = ctQuadNodeCols>>1; } if(ctQuadNodeRows%2 == 1 && ctQuadNodeRows != 1) { ctQuadNodeRows = (ctQuadNodeRows+1)>>1; } else { ctQuadNodeRows = ctQuadNodeRows>>1; } } QuadTreeLevel &qtlFirst = tr_aqtlQuadTreeLevels[0]; // Add quadtree nodes for first level tr_aqtnQuadTreeNodes.Push(qtlFirst.qtl_ctNodes); // for each quad tree node in first level for(INDEX iqn=0;iqnqtn_aabbox; qtn.qtn_iChild[0] = iqt; qtn.qtn_iChild[1] = -1; qtn.qtn_iChild[2] = -1; qtn.qtn_iChild[3] = -1; qtn.qtn_iTileIndex = -1; // If second child node exists if(ic+1 _avLerpedVerices; static void ShowTerrainInfo(CAnyProjection3D &apr, CDrawPort *pdp, CTerrain *ptrTerrain) { pdp->SetFont( _pfdConsoleFont); pdp->SetTextAspect( 1.0f); pdp->SetOrtho(); CTString strInfo; INDEX ctTopMaps = ptrTerrain->tr_atdTopMaps.Count() + 1; strInfo.PrintF("Tris = %d\nNodes = %d\nDelayed nodes = %d\nTop maps = %d\nTexgens = %d, %d\nShadowmap updates = %d\n", _ctTris,_ctNodesVis,_ctDelayedNodes,ctTopMaps,ctGeneratedTopMaps,ctGlobalTopMaps,_ctShadowMapUpdates); CStaticStackArray iaLodInfo; iaLodInfo.Push(ptrTerrain->tr_iMaxTileLod+1); memset(&iaLodInfo[0],0,sizeof(INDEX)*iaLodInfo.sa_Count); // build lod info for(INDEX it=0;ittr_ctTiles;it++) { CTerrainTile &tt = ptrTerrain->tr_attTiles[it]; INDEX &ili = iaLodInfo[tt.tt_iLod]; ili++; } // Show how many tiles are in witch lod CTString strTemp = "LodInfo:\n"; for(INDEX itti=0;ittitr_iMaxTileLod+1;itti++) { CTString str; CArrayHolder &ah = ptrTerrain->tr_aArrayHolders[itti]; str.PrintF("L%d = mem = %d KB, nodes = %d\n",itti, ah.GetUsedMemory()/1024, iaLodInfo[itti]); strTemp += str; } strTemp += "\n"; strInfo +=strTemp; // Show memory usage SLONG slUsedMemory=0; // Height map usage SLONG slHeightMap = ptrTerrain->tr_pixHeightMapWidth*ptrTerrain->tr_pixHeightMapHeight*sizeof(UWORD); // Edge map usage SLONG slEdgeMap = ptrTerrain->tr_pixHeightMapWidth*ptrTerrain->tr_pixHeightMapHeight*sizeof(UBYTE); // Shadow map usage SLONG slShadowMap = ptrTerrain->tr_tdShadowMap.GetUsedMemory(); // Quad tree usage SLONG slQTNodes = sizeof(QuadTreeNode)*ptrTerrain->tr_aqtnQuadTreeNodes.Count(); SLONG slQTLevels = sizeof(QuadTreeLevel)*ptrTerrain->tr_aqtlQuadTreeLevels.Count(); // Tiles usage SLONG slTiles = 0; INDEX cttt = ptrTerrain->tr_ctTiles; for(INDEX itt=0;itttr_attTiles[itt]; slTiles+=tt.GetUsedMemory(); } // Arrays holders usage SLONG slArrayHoldes=0; INDEX ctah=ptrTerrain->tr_aArrayHolders.Count(); for(INDEX iah=0;iahtr_aArrayHolders[iah]; slArrayHoldes+=ah.GetUsedMemory(); } SLONG slLayers=0; // Terrain layers usage INDEX cttl = ptrTerrain->tr_atlLayers.Count(); for(INDEX itl=0;itltr_atlLayers[itl]; slLayers+=tl.GetUsedMemory(); } SLONG slTopMaps=0; // Top maps usage INDEX cttm=ptrTerrain->tr_atdTopMaps.Count(); for(INDEX itm=0;itmtr_atdTopMaps[itm]; slTopMaps+=ptdTopMap->GetUsedMemory(); } SLONG slGlobalTopMap = ptrTerrain->tr_tdTopMap.GetUsedMemory(); SLONG slTileBatchingSize = GetUsedMemoryForTileBatching(); SLONG slVertexSmoothing = _avLerpedVerices.sa_Count * sizeof(GFXVertex4); extern SLONG _slSharedTopMapSize; // Shared top map size // Global top map usage SLONG slTotal = slHeightMap+slEdgeMap+slShadowMap+slQTNodes+slQTLevels+slTiles+slArrayHoldes+slLayers+ slTopMaps+slGlobalTopMap+slTileBatchingSize+slVertexSmoothing; CTString strMemoryUsed; strMemoryUsed.PrintF("Heightmap = %d KB\nEdgemap = %d KB\nShadowMap = %d KB\nQuadTree = %d KB\nTiles = %d KB\nArrays = %d KB\nLayers = %d KB\nTopMaps = %d KB\nGlobal TM = %d KB\nShared TM = %d KB\nVtx lerp = %d KB\nBatching = %d KB\nTotal = %d KB\n", slHeightMap/1024,slEdgeMap/1024,slShadowMap/1024,(slQTNodes+slQTLevels)/1024,slTiles/1024,slArrayHoldes/1024,slLayers/1024,slTopMaps/1024,slGlobalTopMap/1024,_slSharedTopMapSize/1024,slVertexSmoothing/1024,slTileBatchingSize/1024,slTotal/1024); strInfo += strMemoryUsed; extern FLOAT3D _vDirection; strInfo += CTString(0,"Shadow map size = %d,%d [%d]\nShading map size= %d,%d [%d]\n", ptrTerrain->GetShadowMapWidth(), ptrTerrain->GetShadowMapHeight(), ptrTerrain->tr_iShadowMapSizeAspect,ptrTerrain->GetShadingMapWidth(), ptrTerrain->GetShadingMapHeight(),ptrTerrain->tr_iShadingMapSizeAspect); pdp->PutText(strInfo,0,40); } static void ReadOldShadowMap(CTerrain *ptrTerrain, CTStream *istrFile) { // Read terrain shadow map // Read shadow map size INDEX pixShadowMapWidth; INDEX pixShadowMapHeight; (*istrFile)>>pixShadowMapWidth; (*istrFile)>>pixShadowMapHeight; BOOL bHaveShadowMap; (*istrFile)>>bHaveShadowMap; // is shadow map saved if(bHaveShadowMap) { // skip reading of first mip of shadow map istrFile->Seek_t(pixShadowMapWidth*pixShadowMapHeight*sizeof(GFXColor),CTStream::SD_CUR); //istrFile->Read_t(&tr_tdShadowMap.td_pulFrames[0],tr_pixShadowMapWidth*tr_pixShadowMapHeight*sizeof(GFXColor)); } // Set default shadow map size ptrTerrain->SetShadowMapsSize(0,0); } void CTerrain::ReadVersion_t( CTStream *istrFile, INDEX iSavedVersion) { // set current terrain _ptrTerrain = this; ASSERT(_CrtCheckMemory()); PIX pixWidth; PIX pixHeight; // read height map width and height (*istrFile)>>pixWidth; (*istrFile)>>pixHeight; // Reallocate memory for terrain with size AllocateHeightMap(pixWidth,pixHeight); // read terrain stretch (*istrFile)>>tr_vStretch; // read texture stretch if(iSavedVersion<6) { FLOAT fTemp; // Read temp stretch (*istrFile)>>fTemp; } // read lod distance factor (*istrFile)>>tr_fDistFactor; if(iSavedVersion>6) { // Read terrain size (*istrFile)>>tr_vTerrainSize; istrFile->ExpectID_t("TRSM"); // 'Terrain shadowmap' // if version is smaller than 8 if(iSavedVersion<8) { // read old shadow map format ReadOldShadowMap(this,istrFile); } else { INDEX iShadowMapAspect; INDEX iShadingMapAspect; (*istrFile)>>iShadowMapAspect; (*istrFile)>>iShadingMapAspect; SetShadowMapsSize(iShadowMapAspect,iShadingMapAspect); INDEX iShadowMapSize = GetShadowMapWidth() * GetShadowMapHeight(); INDEX iShadingMapSize = GetShadingMapWidth() * GetShadingMapHeight(); // Read shadow map ASSERT(tr_tdShadowMap.td_pulFrames!=NULL); for (INDEX i = 0; i < iShadowMapSize; i++) (*istrFile)>>tr_tdShadowMap.td_pulFrames[i]; // Read shading map ASSERT(tr_auwShadingMap!=NULL); for (INDEX i = 0; i < iShadingMapSize; i++) (*istrFile)>>tr_auwShadingMap[i]; } // Create shadow map mipmaps INDEX ctMipMaps = GetNoOfMipmaps(GetShadowMapWidth(),GetShadowMapHeight()); MakeMipmaps(ctMipMaps, tr_tdShadowMap.td_pulFrames, GetShadowMapWidth(), GetShadowMapHeight()); // Upload shadow map tr_tdShadowMap.SetAsCurrent(0,TRUE); istrFile->ExpectID_t("TSEN"); // 'Terrain shadowmap end' // if there is edge map saved if(istrFile->PeekID_t()==CChunkID("TREM")) { // 'Terrain edge map' // Read terrain edge map istrFile->ExpectID_t("TREM"); // 'Terrain edge map' // read edge map istrFile->Read_t(&tr_aubEdgeMap[0],tr_pixHeightMapWidth*tr_pixHeightMapHeight); istrFile->ExpectID_t("TEEN"); // 'Terrain edge map end' } } (*istrFile).ExpectID_t("TRHM"); // 'Terrain heightmap' // read height map for (ULONG i = 0; i < tr_pixHeightMapWidth*tr_pixHeightMapHeight; i++) (*istrFile)>>tr_auwHeightMap[i]; (*istrFile).ExpectID_t("THEN"); // 'Terrain heightmap end' // Terrain will be rebuild in entity.cpp _ptrTerrain = NULL; } // Read from stream. void CTerrain::Read_t( CTStream *istrFile) { (*istrFile).ExpectID_t("TERR"); // 'Terrain' // read the version number INDEX iSavedVersion; (*istrFile)>>iSavedVersion; // is this version 6 if(iSavedVersion>4) { ReadVersion_t(istrFile,iSavedVersion); // else unknown version } else { // report error ThrowF_t( TRANS("The terrain version on disk is %d.\n" "Current supported version is %d."), iSavedVersion, _iTerrainVersion); } // Read Terrain layers (*istrFile).ExpectID_t("TRLR"); // 'Terrain layers' // Read terrain layers INDEX cttl; (*istrFile)>>cttl; // Create layers tr_atlLayers.Push(cttl); // for each terrain layer for(INDEX itl=0;itlWriteID_t("TRSM"); // 'Terrain shadowmap' (*ostrFile)<Write_t(&tr_tdShadowMap.td_pulFrames[0],iShadowMapSize); // Write shading map ASSERT(tr_auwShadingMap!=NULL); ostrFile->Write_t(&tr_auwShadingMap[0],iShadingMapSize); ostrFile->WriteID_t("TSEN"); // 'Terrain shadowmap end' // if edge map exists if(tr_aubEdgeMap!=NULL) { ostrFile->WriteID_t("TREM"); // 'Terrain edge map' // Write edge map ostrFile->Write_t(&tr_aubEdgeMap[0],sizeof(UBYTE)*tr_pixHeightMapWidth*tr_pixHeightMapHeight); ostrFile->WriteID_t("TEEN"); // 'Terrain edge map end' } (*ostrFile).WriteID_t("TRHM"); // 'Terrain heightmap' // write height map (*ostrFile).Write_t(&tr_auwHeightMap[0],sizeof(UWORD)*tr_pixHeightMapWidth*tr_pixHeightMapHeight); (*ostrFile).WriteID_t("THEN"); // 'Terrain heightmap end' (*ostrFile).WriteID_t("TRLR"); // 'Terrain layers' // write terrain layers INDEX cttl = tr_atlLayers.Count(); (*ostrFile)<si_penEntity->en_ulFlags &= ~ENF_VALIDSHADINGINFO; itsi->si_lnInPolygon.Remove(); itsi->si_pbpoPolygon = NULL; } } // Clear height map void CTerrain::ClearHeightMap(void) { // if height map space was allocated if(tr_auwHeightMap!=NULL) { // release it FreeMemory(tr_auwHeightMap); tr_auwHeightMap = NULL; } } // Clear shadow map void CTerrain::ClearShadowMap(void) { // Clear current terrain shadow map tr_tdShadowMap.Clear(); // Also clear shading map if(tr_auwShadingMap!=NULL) { FreeMemory(tr_auwShadingMap); tr_auwShadingMap = NULL; } } void CTerrain::ClearEdgeMap(void) { // if space for edge map was allocated if(tr_aubEdgeMap!=NULL) { // release it FreeMemory(tr_aubEdgeMap); tr_aubEdgeMap = NULL; } } // Clear all topmaps void CTerrain::ClearTopMaps(void) { // for each topmap in terrain INDEX cttm = tr_atdTopMaps.Count(); for(INDEX itm=0;itmtd_pulFrames = NULL; // Clear tile topmap ptdTopMap->Clear(); // remove topmap from container tr_atdTopMaps.Remove(ptdTopMap); delete ptdTopMap; ptdTopMap = NULL; } tr_atdTopMaps.Clear(); ASSERT(_CrtCheckMemory()); // Remove memory pointer from global top map cos it is shared memory tr_tdTopMap.td_pulFrames = NULL; // Clear global topmap tr_tdTopMap.Clear(); } // Clear tiles void CTerrain::ClearTiles(void) { // for each tile for(INDEX itt=0;ittRelease(tr_ptdDetailMap); tr_ptdDetailMap = NULL; } }