/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** 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, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: W3DShroud.cpp ///////////////////////////////////////////////////////////////////////////// // Created: Mark Wilczynski, Jan 2002 // Desc: Code to support rendering of shrouded units/terrain. /////////////////////////////////////////////////////////////////////////////////////////////////// #include "Lib/BaseType.h" #include "camera.h" #include "simplevec.h" #include "dx8wrapper.h" #include "common/MapObject.h" #include "common/PerfTimer.h" #include "W3DDevice/GameClient/HeightMap.h" #include "W3DDevice/GameClient/W3DPoly.h" #include "W3DDevice/GameClient/W3DShaderManager.h" #include "assetmgr.h" #include "W3DDevice/GameClient/W3DShroud.h" #include "WW3D2/textureloader.h" #include "common/GlobalData.h" #include "GameLogic/PartitionManager.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif //----------------------------------------------------------------------------- // In Global Data now //#define SHROUD_COLOR 0x00ffffff //temporary test of gray shroud instead of pure black. //#define MIN_SHROUD_LEVEL 0 //for gray fog //Int SHROUD_COLOR=0x00808080; //temporary test of gray shroud instead of pure black. //Int MIN_SHROUD_LEVEL=50; //for gray fog //#define SHROUD_COLOR 0x00eeeebff //temporary test of gray shroud instead of pure black. //#define MIN_SHROUD_LEVEL 254 //for gray fog //#define SHROUD_COLOR 0x00bbbbbb //temporary test of gray shroud instead of pure black. //#define SHROUD_COLOR 0x00004080 //temporary test of blue shroud instead of pure black. //#define MIN_SHROUD_LEVEL 100 //for black fog //#define MAX_MAP_SHROUDSIZE 1024 //maximum number of shroud cells across entire map. //#define MAX_VISIBLE_SHROUDSIZE (MAX_MAP_SHROUDSIZE+1) //maximum number of shroud vertices visible at any given time #define DEFAULT_SHROUD_CELL_SIZE MAP_XY_FACTOR //assume shroud at same resolution as terrain cells. #define DEFAULT_TERRAIN_SIZE 1024 //assumed size of largest terrain possible (in vertices) #define DEFAULT_VISIBLE_TERRAIN 96 //assumed size of visible terrain cells. //----------------------------------------------------------------------------- W3DShroud::W3DShroud(void) { m_finalFogData=NULL; m_currentFogData=NULL; m_pSrcTexture=NULL; m_pDstTexture=NULL; m_srcTextureData=NULL; m_srcTexturePitch=NULL; m_dstTextureWidth=m_numMaxVisibleCellsX=0; m_dstTextureHeight=m_numMaxVisibleCellsY=0; m_boderShroudLevel = (W3DShroudLevel)TheGlobalData->m_shroudAlpha; //assume border is black m_clearDstTexture = TRUE; //force clearing of destination texture; m_cellWidth=DEFAULT_SHROUD_CELL_SIZE; m_cellHeight=DEFAULT_SHROUD_CELL_SIZE; m_numCellsX=0; m_numCellsY=0; m_shroudFilter=TextureClass::FILTER_TYPE_DEFAULT; } //----------------------------------------------------------------------------- W3DShroud::~W3DShroud(void) { ReleaseResources(); if (m_pSrcTexture) m_pSrcTexture->Release(); m_pSrcTexture=NULL; if (m_finalFogData) delete [] m_finalFogData; if (m_currentFogData) delete [] m_currentFogData; m_drawFogOfWar=FALSE; } //----------------------------------------------------------------------------- /**Called to initialize a new shroud for a new map. Should be done after the map is loaded into the terrain object. worldCellSize is the world-space dimensions of each shroud cell. The system will generate enough cells to cover the full map. */ void W3DShroud::init(WorldHeightMap *pMap, Real worldCellSizeX, Real worldCellSizeY) { DEBUG_ASSERTCRASH( m_pSrcTexture == NULL, ("ReAcquire of existing shroud textures")); DEBUG_ASSERTCRASH( pMap != NULL, ("Shroud init with NULL WorldHeightMap")); Int dstTextureWidth=0; Int dstTextureHeight=0; m_cellWidth=worldCellSizeX; m_cellHeight=worldCellSizeY; //Precompute a bounding box for entire shroud layer if (pMap) { m_numCellsX = REAL_TO_INT_CEIL((Real)(pMap->getXExtent() - 1 - pMap->getBorderSize()*2)*MAP_XY_FACTOR/m_cellWidth); m_numCellsY = REAL_TO_INT_CEIL((Real)(pMap->getYExtent() - 1 - pMap->getBorderSize()*2)*MAP_XY_FACTOR/m_cellHeight); //Maximum visible cells will depend on maximum drawable terrain size plus 1 for partial cells (since //shroud cells are larger than terrain cells). dstTextureWidth=m_numMaxVisibleCellsX=REAL_TO_INT_FLOOR((Real)(pMap->getDrawWidth()-1)*MAP_XY_FACTOR/m_cellWidth)+1; dstTextureHeight=m_numMaxVisibleCellsY=REAL_TO_INT_FLOOR((Real)(pMap->getDrawHeight()-1)*MAP_XY_FACTOR/m_cellHeight)+1; dstTextureWidth += 2; //enlarge by 2 pixels so we can have a border color all the way around. dstTextureHeight += 2; //enlarge by 2 pixels so we can have border color all the way around. TextureLoader::Validate_Texture_Size((unsigned int &)dstTextureWidth,(unsigned int &)dstTextureHeight); } UnsignedInt srcWidth,srcHeight; srcWidth=m_numCellsX; //vertical size is larger by 1 pixel so that we have some unused pixels to use in clearing the video texture. //To clear the video texture, I will copy pixels from this unused area. There is no other way to clear a video //memory texture to a known value because you can't lock it - only copy into it. srcHeight=m_numCellsY; srcHeight += 1; #ifdef DO_FOG_INTERPOLATION m_finalFogData = new W3DShroudLevel[srcWidth*srcHeight]; m_currentFogData = new W3DShroudLevel[srcWidth*srcHeight]; //Clear the fog to black memset(m_currentFogData,0,srcWidth*srcHeight); memset(m_finalFogData,0,srcWidth*srcHeight); #endif #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData && TheGlobalData->m_fogOfWarOn) m_pSrcTexture = DX8Wrapper::_Create_DX8_Surface(srcWidth,srcHeight, WW3D_FORMAT_A4R4G4B4); else #endif m_pSrcTexture = DX8Wrapper::_Create_DX8_Surface(srcWidth,srcHeight, WW3D_FORMAT_R5G6B5); DEBUG_ASSERTCRASH( m_pSrcTexture != NULL, ("Failed to Allocate Shroud Src Surface")); D3DLOCKED_RECT rect; //Get a pointer to source surface pixels. HRESULT res = m_pSrcTexture->LockRect(&rect,NULL,D3DLOCK_NO_DIRTY_UPDATE); m_pSrcTexture->UnlockRect(); DEBUG_ASSERTCRASH( res == D3D_OK, ("Failed to lock shroud src surface")); res = 0;// just to avoid compiler warnings m_srcTextureData=rect.pBits; m_srcTexturePitch=rect.Pitch; //clear entire texture to black memset(m_srcTextureData,0,m_srcTexturePitch*srcHeight); #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData && TheGlobalData->m_fogOfWarOn) fillShroudData(TheGlobalData->m_shroudAlpha); //initialize shroud to a known value #endif if (dstTextureWidth != m_dstTextureWidth || dstTextureHeight != m_dstTextureHeight ) ///@todo: Check if size has changed - probably never ReleaseResources(); //need a new sized shroud if (!m_pDstTexture ) { m_dstTextureWidth = dstTextureWidth; m_dstTextureHeight = dstTextureHeight; ReAcquireResources(); //allocate video memory surface } //Force a refresh of shroud data since we just created a new source texture. if (ThePartitionManager) ThePartitionManager->refreshShroudForLocalPlayer(); } //----------------------------------------------------------------------------- ///Called on map reset. void W3DShroud::reset() { //Free old shroud data since it may no longer fit new map. if (m_pSrcTexture) m_pSrcTexture->Release(); m_pSrcTexture=NULL; if (m_finalFogData) delete [] m_finalFogData; m_finalFogData=NULL; if (m_currentFogData) delete [] m_currentFogData; m_currentFogData=NULL; m_clearDstTexture = TRUE; //always refill the destination texture after a reset } //----------------------------------------------------------------------------- ///Release any resources that can't survive a D3D device reset. void W3DShroud::ReleaseResources(void) { REF_PTR_RELEASE (m_pDstTexture); } //----------------------------------------------------------------------------- ///Restore resources that are lost on D3D device reset. Bool W3DShroud::ReAcquireResources(void) { if (!m_dstTextureWidth) return TRUE; //nothing to reaquire since shroud was never initialized with valid data DEBUG_ASSERTCRASH( m_pDstTexture == NULL, ("ReAcquire of existing shroud texture")); // Create destination texture (stored in video memory). // Since we control the video memory copy, we can do partial updates more efficiently. Or do shift blits. #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData && TheGlobalData->m_fogOfWarOn) m_pDstTexture = MSGNEW("TextureClass") TextureClass(m_dstTextureWidth,m_dstTextureHeight,WW3D_FORMAT_A4R4G4B4,TextureClass::MIP_LEVELS_1, TextureClass::POOL_DEFAULT); else #endif m_pDstTexture = MSGNEW("TextureClass") TextureClass(m_dstTextureWidth,m_dstTextureHeight,WW3D_FORMAT_R5G6B5,TextureClass::MIP_LEVELS_1, TextureClass::POOL_DEFAULT); DEBUG_ASSERTCRASH( m_pDstTexture != NULL, ("Failed ReAcquire of shroud texture")); if (!m_pDstTexture) { //could not create a valid texture m_dstTextureWidth = 0; m_dstTextureHeight = 0; return FALSE; } m_pDstTexture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); m_pDstTexture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); m_pDstTexture->Set_Mip_Mapping(TextureClass::FILTER_TYPE_NONE); m_clearDstTexture = TRUE; //force clearing of destination texture first time it's used. return TRUE; } //----------------------------------------------------------------------------- W3DShroudLevel W3DShroud::getShroudLevel(Int x, Int y) { DEBUG_ASSERTCRASH( m_pSrcTexture != NULL, ("Reading empty shroud")); if (x < m_numCellsX && y < m_numCellsY) { UnsignedShort pixel=*(UnsignedShort *)((Byte *)m_srcTextureData + x*2 + y*m_srcTexturePitch); #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData && TheGlobalData->m_fogOfWarOn) //in this mode, alpha channel holds intensity return (W3DShroudLevel)((1.0f-((Real)(pixel >> 12)/15.0f))*255.0f); else #endif //in this mode, green has the best precision at 6 bits. return (W3DShroudLevel)((Real)((pixel >> 5)&0x3f)/63.0f*255.0f); } return 0; } //----------------------------------------------------------------------------- void W3DShroud::setShroudLevel(Int x, Int y, W3DShroudLevel level, Bool textureOnly) { DEBUG_ASSERTCRASH( m_pSrcTexture != NULL, ("Writing empty shroud. Usually means that map failed to load.")); if (!m_pSrcTexture) return; if (x < m_numCellsX && y < m_numCellsY) { if (level < TheGlobalData->m_shroudAlpha) level = TheGlobalData->m_shroudAlpha; #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData && TheGlobalData->m_fogOfWarOn) { ///@todo: optimize this case if we end up using fog shroud. Int redVal = TheGlobalData->m_shroudColor.red; Int greenVal = TheGlobalData->m_shroudColor.green; Int blueVal = TheGlobalData->m_shroudColor.blue; // Int redVal = (SHROUD_COLOR >> 16) & 0xff; // Int greenVal = (SHROUD_COLOR >> 8) & 0xff; // Int blueVal = SHROUD_COLOR & 0xff; Int alphaVal = 255 - level; UnsignedShort pixel=((blueVal>>4)&0xf) | (((greenVal>>4)&0xf)<<4) | (((redVal>>4)&0xf)<<8) | (((alphaVal>>4)&0xf)<<12); *(UnsignedShort *)((Byte *)m_srcTextureData + x*2 + y*m_srcTexturePitch)=pixel; } else #endif { #ifdef DO_FOG_INTERPOLATION if (!textureOnly) m_finalFogData[x+y*m_numCellsX]=level; #endif UnsignedInt bluepixel = (UnsignedInt)((Real)level*((Real)(TheGlobalData->m_shroudColor.getAsInt()&0xff)/255.0f)); UnsignedInt greenpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff00)>>8)/255.0f)); UnsignedInt redpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff0000)>>16)/255.0f)); // UnsignedInt bluepixel = (UnsignedInt)((Real)level*((Real)(SHROUD_COLOR&0xff)/255.0f)); // UnsignedInt greenpixel = (UnsignedInt)((Real)level*((Real)((SHROUD_COLOR&0xff00)>>8)/255.0f)); // UnsignedInt redpixel = (UnsignedInt)((Real)level*((Real)((SHROUD_COLOR&0xff0000)>>16)/255.0f)); if (level == 255) { //unshrouded pixels should be fully lit redpixel = 255; greenpixel = 255; bluepixel = 255; } UnsignedShort *texel = (UnsignedShort *)((Byte *)m_srcTextureData + x*2 + y*m_srcTexturePitch); // For those interested, MLorenzen has this bock commented out until he gets back on Mon, Sept. 30 2002 // If this code is still here by mid october, nuke it! // UnsignedInt texelRed = ((*texel >> 8 ) & 0xf8); // UnsignedInt texelGreen =((*texel >> 3 ) & 0xfc); // UnsignedInt texelBlue = ((*texel << 3 ) & 0xf8); // if (texelRed < texelGreen && texelGreen > texelBlue) // { // bluepixel += redpixel; // bluepixel -= redpixel; // } *texel = ( ((bluepixel&0xf8) >> 3) | ((greenpixel&0xfc)<<3) | ((redpixel&0xf8)<<8)); } return; } } //----------------------------------------------------------------------------- ///Quickly sets the shroud level of entire map to a single value void W3DShroud::fillShroudData(W3DShroudLevel level) { Int x,y; UnsignedShort pixel; if (level < TheGlobalData->m_shroudAlpha) level = TheGlobalData->m_shroudAlpha; #if defined(_DEBUG) || defined(_INTERNAL) //convert value to pixel format if (TheGlobalData && TheGlobalData->m_fogOfWarOn) { Int redVal = TheGlobalData->m_shroudColor.red; Int greenVal = TheGlobalData->m_shroudColor.green; Int blueVal = TheGlobalData->m_shroudColor.blue; // Int redVal = (SHROUD_COLOR >> 16) & 0xff; // Int greenVal = (SHROUD_COLOR >> 8) & 0xff; // Int blueVal = SHROUD_COLOR & 0xff; Int alphaVal = 255 - level; pixel=((blueVal>>4)&0xf) | (((greenVal>>4)&0xf)<<4) | (((redVal>>4)&0xf)<<8) | (((alphaVal>>4)&0xf)<<12); } else #endif { UnsignedInt bluepixel = (UnsignedInt)((Real)level*((Real)(TheGlobalData->m_shroudColor.getAsInt()&0xff)/255.0f)); UnsignedInt greenpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff00)>>8)/255.0f)); UnsignedInt redpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff0000)>>16)/255.0f)); // UnsignedInt bluepixel = (UnsignedInt)((Real)level*((Real)(SHROUD_COLOR&0xff)/255.0f)); // UnsignedInt greenpixel = (UnsignedInt)((Real)level*((Real)((SHROUD_COLOR&0xff00)>>8)/255.0f)); // UnsignedInt redpixel = (UnsignedInt)((Real)level*((Real)((SHROUD_COLOR&0xff0000)>>16)/255.0f)); if (level == 255) { //unshrouded pixels should be fully lit redpixel = 255; greenpixel = 255; bluepixel = 255; } pixel=( ((bluepixel&0xf8) >> 3) | ((greenpixel&0xfc)<<3) | ((redpixel&0xf8)<<8)); } UnsignedShort *ptr=(UnsignedShort *)m_srcTextureData; Int pitch = m_srcTexturePitch >> 1; //2 bytes per pointer increment for (y=0; ym_shroudAlpha) level = TheGlobalData->m_shroudAlpha; #if defined(_DEBUG) || defined(_INTERNAL) //convert value to pixel format if (TheGlobalData && TheGlobalData->m_fogOfWarOn) { Int redVal = TheGlobalData->m_shroudColor.red; Int greenVal = TheGlobalData->m_shroudColor.green; Int blueVal = TheGlobalData->m_shroudColor.blue; Int alphaVal = 255 - level; pixel=((blueVal>>4)&0xf) | (((greenVal>>4)&0xf)<<4) | (((redVal>>4)&0xf)<<8) | (((alphaVal>>4)&0xf)<<12); } else #endif { UnsignedInt bluepixel = (UnsignedInt)((Real)level*((Real)(TheGlobalData->m_shroudColor.getAsInt()&0xff)/255.0f)); UnsignedInt greenpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff00)>>8)/255.0f)); UnsignedInt redpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff0000)>>16)/255.0f)); if (level == 255) { //unshrouded pixels should be fully lit redpixel = 255; greenpixel = 255; bluepixel = 255; } pixel=( ((bluepixel&0xf8) >> 3) | ((greenpixel&0xfc)<<3) | ((redpixel&0xf8)<<8)); } //Skip to unused texels within the shroud data UnsignedShort *ptr=(UnsignedShort *)m_srcTextureData + m_numCellsY*(m_srcTexturePitch >> 1); //Fill unused texels with border color for (x=0; xPeek_D3D_Surface(), &dstPoint); } if (numExtraPixels) { Int oldVal=srcRect.right; dstPoint.x = numFullCopies * oldVal; srcRect.right = numExtraPixels; DX8Wrapper::_Copy_DX8_Rects( m_pSrcTexture, &srcRect, 1, pDestSurface->Peek_D3D_Surface(), &dstPoint); srcRect.right = oldVal; } } } /**Set the shroud color within the border area of the map*/ void W3DShroud::setBorderShroudLevel(W3DShroudLevel level) { m_boderShroudLevel = level; m_clearDstTexture = TRUE; } //----------------------------------------------------------------------------- ///@todo: remove this TextureClass *DummyTexture=NULL; //#define LOAD_DUMMY_SHROUD //----------------------------------------------------------------------------- //DECLARE_PERF_TIMER(shroudCopy) //----------------------------------------------------------------------------- /** Updates video memory surface with currently visible shroud data */ void W3DShroud::render(CameraClass *cam) { if (!m_pSrcTexture) return; //nothing to update from. Must be in reset state. if (DX8Wrapper::_Get_D3D_Device8() && (DX8Wrapper::_Get_D3D_Device8()->TestCooperativeLevel()) != D3D_OK) return; //device not ready to render anything #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData && TheGlobalData->m_fogOfWarOn != m_drawFogOfWar) { //fog state has changed since last time shroud system was initialized reset(); ReleaseResources(); init(TheTerrainRenderObject->getMap(),m_cellWidth,m_cellHeight); ThePartitionManager->refreshShroudForLocalPlayer(); m_drawFogOfWar=TheGlobalData->m_fogOfWarOn; m_clearDstTexture=TRUE; } #endif DEBUG_ASSERTCRASH( m_pSrcTexture != NULL, ("Updating unallocated shroud texture")); #ifdef LOAD_DUMMY_SHROUD static doInit=1; if (doInit) { //some temporary code here to debug the shroud. ///@todo: remove this debug buffer fill. fillShroudData(1.0f); //force to all shrouded Short *src=(Short *)m_srcTextureData; //fill with some dummy values src[0]=(char)0xff; src[1]=(char)0xff; src[m_numCellsX]=(char)0xff; src[m_numCellsX+1]=(char)0xff; src[m_numCellsX*2]=(char)0xff; src[m_numCellsX*9+8]=(char)0xff; src[m_numCellsX*8+8]=(char)0xff; src[m_numCellsX*7+8]=(char)0xff; src[m_numCellsX*8+9]=(char)0xff; src[m_numCellsX*8+7]=(char)0xff; DummyTexture=WW3DAssetManager::Get_Instance()->Get_Texture("shroud1024.tga"); Short *dataDest=(Short *)((char *)m_srcTextureData); //offset to correct row of full sysmem shroud Int pitchDest = m_srcTexturePitch >> 1; //2 bytes per pixel so divide byte count by 2. //Copy the dummy shroud into our game shroud. SurfaceClass *pSurface=DummyTexture->Get_Surface_Level(0); Int pitch; Int *dataSrc=(Int *)((char *)pSurface->Lock(&pitch)); //offset to correct row of full sysmem shroud pitch >>= 2; //4 bytes per pixel so divide byte count by 4. SurfaceClass::SurfaceDescription desc; pSurface->Get_Description(desc); //Check if source data is larger than our current shroud desc.Width = __min(desc.Width,m_numCellsX); desc.Height = __min(desc.Height,m_numCellsY); for (Int y=0; ym_fogOfWarOn) { dataDest[x]=((TheGlobalData->m_shroudColor.getAsInt()>>4)&0xf) | (((TheGlobalData->m_shroudColor.getAsInt()>>12)&0xf)<<4) | (((TheGlobalData->m_shroudColor.getAsInt()>>20)&0xf)<<8) | ((((255-(dataSrc[x] & 0xff))>>4)&0xf)<<12); // dataDest[x]=((SHROUD_COLOR>>4)&0xf) | (((SHROUD_COLOR>>12)&0xf)<<4) | (((SHROUD_COLOR>>20)&0xf)<<8) | ((((255-(dataSrc[x] & 0xff))>>4)&0xf)<<12); } else #endif { dataDest[x]=((dataSrc[x]>>3)&0x1f) | (((dataSrc[x]>>10)&0x3f)<<5) | (((dataSrc[x]>>19)&0x1f)<<11); } } dataDest += pitchDest; //skip to next row. dataSrc += pitch; //skip to next row of shroud } pSurface->Unlock(); REF_PTR_RELEASE (pSurface); REF_PTR_RELEASE (DummyTexture); doInit=0; } #endif //LOAD_DUMMY_SHROUD WorldHeightMap *hm=TheTerrainRenderObject->getMap(); Int visStartX=REAL_TO_INT_FLOOR((Real)(hm->getDrawOrgX()-hm->getBorderSize())*MAP_XY_FACTOR/m_cellWidth); //start of rendered heightmap rectangle if (visStartX < 0) visStartX = 0; //no shroud is applied in border area so it always starts at > 0 Int visStartY=REAL_TO_INT_FLOOR((Real)(hm->getDrawOrgY()-hm->getBorderSize())*MAP_XY_FACTOR/m_cellHeight); if (visStartY < 0) visStartY = 0; //no shroud is applied in border area so it always starts at > 0 Int visEndX=visStartX+REAL_TO_INT_FLOOR((Real)(hm->getDrawWidth()-1)*MAP_XY_FACTOR/m_cellWidth)+1; //size of rendered heightmap rectangle Int visEndY=visStartY+REAL_TO_INT_FLOOR((Real)(hm->getDrawHeight()-1)*MAP_XY_FACTOR/m_cellHeight)+1; if (visEndX > m_numCellsX) { visStartX -= visEndX - m_numCellsX; //shift visible rectangle to fall within terrain bounds if (visStartX < 0) visStartX = 0; visEndX = m_numCellsX; } if (visEndY > m_numCellsY) { visStartY -= visEndY - m_numCellsY; //shift visible rectangle to fall within terrain bounds if (visStartY < 0) visStartY = 0; visEndY = m_numCellsY; } m_drawOriginX = (Real)visStartX * m_cellWidth; m_drawOriginY = (Real)visStartY * m_cellHeight; //If system memory usage becomes too large, we should store shroud as Bytes. Update //a system memory texture with data. Then copy this to video memory. For now we're //holding shroud data directly in a texture to avoid the extra copy. /* pSurface=m_pSrcTexture->Get_Surface_Level(0); data=(Short *)((char *)pSurface->Lock(&pitch) + visStartY*pitch); //offset to correct row of full sysmem shroud pitch >>= 1; //we have 2 bytes per pixel, so divide pitch by 2 Byte *sd=shroudData+visStartY*MAX_MAP_SHROUDSIZE; //pointer to first shroud row for (Int y=visStartY; y<(visStartY+visSizeY); y++) { for (Int x=visStartX; x<(visStartX+visSizeX); x++) { data[x]=sd[x]|(sd[x]<<8); } data += pitch; //skip to next row. sd += MAX_MAP_SHROUDSIZE; //skip to next row of shroud } pSurface->Unlock(); */ if (m_pDstTexture->Get_Mag_Filter() != m_shroudFilter) { m_pDstTexture->Set_Mag_Filter(m_shroudFilter); m_pDstTexture->Set_Min_Filter(m_shroudFilter); } //Update video memory texture with sysmem copy SurfaceClass* pDestSurface; { pDestSurface=m_pDstTexture->Get_Surface_Level(0); } RECT srcRect; POINT dstPoint={1,1}; //first row/column is reserved for border. srcRect.left=visStartX; srcRect.top=visStartY; srcRect.right=visEndX; srcRect.bottom=visEndY; #ifdef DO_FOG_INTERPOLATION //interpolate current shroud state to the final one interpolateFogLevels(&srcRect); #endif if (m_clearDstTexture) { //we need to clear unused parts of the destination texture to a known //color in order to keep map border in the state we want. m_clearDstTexture=FALSE; fillBorderShroudData(m_boderShroudLevel, pDestSurface); } { //USE_PERF_TIMER(shroudCopy) DX8Wrapper::_Copy_DX8_Rects( m_pSrcTexture, &srcRect, 1, pDestSurface->Peek_D3D_Surface(), &dstPoint); } REF_PTR_RELEASE (pDestSurface); } #define FOG_INTERPOLATION_RATE (255.0f/1000.0f) //take one second to go from black to fully lit. //----------------------------------------------------------------------------- void W3DShroud::interpolateFogLevels(RECT *rect) { static UnsignedInt prevTime = timeGetTime(); UnsignedInt timeDiff=timeGetTime()-prevTime; if (!timeDiff) return; //no time has elapsed prevTime +=timeDiff; //update for next frame Int maxFogChange=FOG_INTERPOLATION_RATE * (Real)timeDiff; //maximum amount of fog change allowed in frame. if (maxFogChange > 255) maxFogChange = 255; W3DShroudLevel levelDelta = maxFogChange; W3DShroudLevel *startLevel=m_currentFogData; W3DShroudLevel *finalLevel=m_finalFogData; for (Int j=0; j *startLevel) { if ((*finalLevel - *startLevel) < levelDelta) *startLevel = *finalLevel; //change too large so clamp to final value. else *startLevel += levelDelta; } setShroudLevel(i,j,*startLevel,TRUE); } } } //----------------------------------------------------------------------------- void W3DShroud::setShroudFilter(Bool enable) { if (enable) m_shroudFilter=TextureClass::FILTER_TYPE_DEFAULT; else m_shroudFilter=TextureClass::FILTER_TYPE_NONE; } //----------------------------------------------------------------------------- ///Set render states required to draw shroud pass. void W3DShroudMaterialPassClass::Install_Materials(void) const { if (TheTerrainRenderObject->getShroud()) { W3DShaderManager::setTexture(0,TheTerrainRenderObject->getShroud()->getShroudTexture()); W3DShaderManager::setShader(W3DShaderManager::ST_SHROUD_TEXTURE, 0); } } //----------------------------------------------------------------------------- ///Restore render states that W3D doesn't know about. void W3DShroudMaterialPassClass::UnInstall_Materials(void) const { W3DShaderManager::resetShader(W3DShaderManager::ST_SHROUD_TEXTURE); } //----------------------------------------------------------------------------- ///Set render states required to draw shroud pass. void W3DMaskMaterialPassClass::Install_Materials(void) const { W3DShaderManager::setShader(W3DShaderManager::ST_MASK_TEXTURE, 0); } //----------------------------------------------------------------------------- ///Restore render states that W3D doesn't know about. void W3DMaskMaterialPassClass::UnInstall_Materials(void) const { if (m_allowUninstall) W3DShaderManager::resetShader(W3DShaderManager::ST_MASK_TEXTURE); }