// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "screenshot_islands.h" #include <game_share/scenario_entry_points.h> #include <game_share/season.h> #include <game_share/dir_light_setup.h> #include <game_share/bmp4image.h> #include <nel/misc/path.h> #include <nel/misc/file.h> #include <nel/misc/config_file.h> #include <nel/misc/big_file.h> #include <nel/misc/i18n.h> #include <nel/misc/progress_callback.h> #include <nel/misc/random.h> #include <nel/misc/common.h> #include <nel/3d/u_material.h> #include <nel/3d/u_driver.h> #include <nel/3d/u_scene.h> #include <nel/3d/u_landscape.h> #include <nel/3d/u_camera.h> #include <nel/3d/landscapeig_manager.h> #include <nel/3d/u_material.h> #include <nel/georges/u_form_loader.h> #include <nel/georges/u_form.h> #include <nel/georges/u_form_elm.h> // AI share #include <ai_share/world_map.h> #include <nel/3d/material.h> #include <math.h> using namespace NLMISC; using namespace NL3D; using namespace std; using namespace EGSPD; using namespace NLGEORGES; using namespace RYAI_MAP_CRUNCH; // The 3d driver UDriver *driver = NULL; CLandscapeIGManager LandscapeIGManager; uint ScreenShotWidth; uint ScreenShotHeight; UMaterial sceneMaterial; namespace R2 { const TBufferEntry InteriorValue= (TBufferEntry)(~0u-1); const TBufferEntry ValueBorder= (TBufferEntry)(~0u-2); const uint32 BigValue= 15*5; const float limitValue = 200.0; //------------------------------------------------------------------------------------------------- CScreenshotIslands::CScreenshotIslands() { _BackColor = CRGBA(255, 255, 255, 255); } //------------------------------------------------------------------------------------------------- void CScreenshotIslands::init() { // Create a driver driver = UDriver::createDriver(0, true); nlassert(driver); sceneMaterial = driver->createMaterial(); sceneMaterial.getObjectPtr()->setLighting(true); sceneMaterial.getObjectPtr()->setSpecular(CRGBA(255, 255, 255, 255)); sceneMaterial.getObjectPtr()->setShininess(50); sceneMaterial.getObjectPtr()->setDiffuse(CRGBA(100, 100, 100, 255)); sceneMaterial.getObjectPtr()->setEmissive(CRGBA(25, 25, 25, 255)); // load and parse the configfile CConfigFile cf; cf.load("island_screenshots.cfg"); // get the value of searchPaths CConfigFile::CVar * searchPaths = cf.getVarPtr("SearchPaths"); if(searchPaths) { for(int i = 0; i < searchPaths->size(); i++) { CPath::addSearchPath(searchPaths->asString(i).c_str(), true, false); } } CPath::remapExtension("dds", "tga", true); CPath::remapExtension("dds", "png", true); // get the scenario entry points file CConfigFile::CVar * epFile = cf.getVarPtr("CompleteIslandsFile"); if(epFile) { _CompleteIslandsFile = epFile->asString(); } // get the out directory path CConfigFile::CVar * outDir = cf.getVarPtr("OutDir"); if(outDir) { _OutDirectory = outDir->asString(); } // get the vegetation option CConfigFile::CVar * veget = cf.getVarPtr("Vegetation"); if(veget) { _Vegetation = veget->asBool(); } // get the vegetation option CConfigFile::CVar * inverseZTest = cf.getVarPtr("InverseZTest"); if(inverseZTest) { _InverseZTest = inverseZTest->asBool(); } // get list of continents CConfigFile::CVar * continents = cf.getVarPtr("Continents"); vector<string> continentsName(continents->size()); for(int i = 0; i < continents->size(); i++) { continentsName[i] = continents->asString(i); } // get continents data (light, coarseMesh,...) UFormLoader * formLoader; for(uint i=0; i<continentsName.size(); i++) { string georgeFileName = continentsName[i]+".continent"; formLoader = UFormLoader::createLoader(); CSmartPtr<UForm> form = formLoader->loadForm(CPath::lookup(georgeFileName).c_str()); if(form) { CContinentData continentData; UFormElm &formRoot = form->getRootNode(); const UFormElm *elm; if(formRoot.getNodeByName(&elm, "LightLandscapeDay") && elm) { CDirLightSetup landscapeLightDay; landscapeLightDay.build(*elm); continentData.Ambiant = landscapeLightDay.Ambiant; continentData.Diffuse = landscapeLightDay.Diffuse; if(continentsName[i]=="r2_jungle" || continentsName[i]=="r2_forest" || continentsName[i]=="r2_roots") { continentData.Ambiant = CRGBA(255, 255, 255, 255); continentData.Diffuse = CRGBA(255, 255, 255, 255); } } formRoot.getValueByName(continentData.IGFile, "LandscapeIG"); string zoneMin, zoneMax; formRoot.getValueByName(zoneMin, "ZoneMin"); formRoot.getValueByName(zoneMax, "ZoneMax"); getPosFromZoneName(zoneMin, continentData.ZoneMin); getPosFromZoneName(zoneMax, continentData.ZoneMax); string filename; if(formRoot.getValueByName(filename, "Ecosystem")) { UFormLoader *formLoaderEco = UFormLoader::createLoader(); if(formLoaderEco) { // Load the form CSmartPtr<UForm> formEco = formLoaderEco->loadForm(filename.c_str()); if(formEco) { // Root node UFormElm &formRootEco = formEco->getRootNode(); // Small bank. formRootEco.getValueByName(continentData.SmallBank, "SmallBank"); // Far bank. formRootEco.getValueByName(continentData.FarBank, "FarBank"); // Coarse mesh texture. formRootEco.getValueByName(continentData.CoarseMeshMap, "CoarseMeshMap"); } else { nlwarning("CScreenshotIslands::init : Can't load form %s.", filename.c_str()); } UFormLoader::releaseLoader(formLoaderEco); } } _ContinentsData[continentsName[i]] = continentData; } UFormLoader::releaseLoader(formLoader); } // load islands loadIslands(); searchIslandsBorders(); // get seasons CConfigFile::CVar * seasonSuffixes = cf.getVarPtr("SeasonSuffixes"); if(seasonSuffixes) { for(uint i = 0; i < (uint)seasonSuffixes->size(); i++) { _SeasonSuffixes.push_back(seasonSuffixes->asString(i)); } } // get the meter size in pixels CConfigFile::CVar * meterSize = cf.getVarPtr("MeterPixelSize"); if(meterSize) { _MeterPixelSize = meterSize->asInt(); } } //------------------------------------------------------------------------------------------------- bool CScreenshotIslands::getPosFromZoneName(const std::string &name, NLMISC::CVector2f &dest) { if(name.empty()) { nlwarning ("getPosFromZoneName(): empty name, can't getPosFromZoneName"); return false; } static std::string zoneName; static string xStr, yStr; xStr.clear(); yStr.clear(); zoneName = CFile::getFilenameWithoutExtension(name); uint32 i = 0; while(zoneName[i] != '_') { if(!::isdigit(zoneName[i])) return false; yStr += zoneName[i]; ++i; if(i == zoneName.size()) return false; } ++i; while(i < zoneName.size()) { if(!::isalpha(zoneName[i])) return false; xStr += (char) ::toupper(zoneName[i]); ++i; } if(xStr.size() != 2) return false; dest.x = 160.f * ((xStr[0] - 'A') * 26 + (xStr[1] - 'A')); dest.y = 160.f * -atoi(yStr.c_str()); return true; } //------------------------------------------------------------------------------------------------- void CScreenshotIslands::searchIslandsBorders() { vector<string> filenames; list<string> zonelFiles; map< CVector2f, bool> islandsMap; TContinentsData::iterator itCont(_ContinentsData.begin()), lastCont(_ContinentsData.end()); for( ; itCont != lastCont ; ++itCont) { // for each continent we recover a map of zonel files whith position of // left/bottom point of each zone for keys filenames.clear(); zonelFiles.clear(); string bnpFileName = itCont->first + ".bnp"; CBigFile::getInstance().list(bnpFileName.c_str(), filenames); for(uint i=0; i<filenames.size(); i++) { if(CFile::getExtension(filenames[i]) == "zonel") { zonelFiles.push_back(filenames[i]); } } list<string>::iterator itZonel(zonelFiles.begin()), lastZonel(zonelFiles.end()); for( ; itZonel != lastZonel ; ++itZonel) { CVector2f position; getPosFromZoneName(*itZonel, position); islandsMap[position] = true; } // search for island borders CContinentData & continent = itCont->second; list< string >::const_iterator itIsland(continent.Islands.begin()), lastIsland(continent.Islands.end()); for( ; itIsland != lastIsland ; ++itIsland) { if(_IslandsData.find(itIsland->c_str()) != _IslandsData.end()) { const CProximityZone & islandData = _IslandsData[itIsland->c_str()]; sint32 xmin = islandData.getBoundXMin(); sint32 xmax = islandData.getBoundXMax(); sint32 ymin = islandData.getBoundYMin(); sint32 ymax = islandData.getBoundYMax(); sint32 width = xmax-xmin; sint32 height = ymax-ymin; sint32 zonelXMin = ((uint)(xmin/160)) * 160; sint32 zonelYMin = ((uint)(ymin/160) - 1) * 160; sint32 zonelXMax = ((uint)(xmax/160)) * 160; sint32 zonelYMax = ((uint)(ymax/160) - 1) * 160; list< CVector2f > leftBorders, rightBorders, bottomBorders, topBorders; // search for left and right borders on lines for(sint32 y = zonelYMin; y<=zonelYMax; y+=160 ) { sint32 x=zonelXMin; CVector2f vec((float)x, (float)y); bool lastZoneFull = (islandsMap.find(vec) != islandsMap.end()); bool currentZoneFull; while(x<=zonelXMax) { vec = CVector2f((float)x, (float)y); currentZoneFull = (islandsMap.find(vec) != islandsMap.end()); if(lastZoneFull && !currentZoneFull && vec.x-1 >= xmin) { rightBorders.push_back(CVector2f(vec.x-1, vec.y)); } else if(!lastZoneFull && currentZoneFull) { leftBorders.push_back(vec); } x += 160; lastZoneFull = currentZoneFull; } } // search for bottom and top borders on columns for(sint32 x = zonelXMin; x<=zonelXMax; x+=160 ) { sint32 y=zonelYMin; CVector2f vec((float)x, (float)y); bool lastZoneFull = (islandsMap.find(vec) != islandsMap.end()); bool currentZoneFull; while(y<=zonelYMax) { vec = CVector2f((float)x, (float)y); currentZoneFull = (islandsMap.find(vec) != islandsMap.end()); if(lastZoneFull && !currentZoneFull && vec.y-1 >= ymin) { topBorders.push_back(CVector2f(vec.x, vec.y-1)); } else if(!lastZoneFull && currentZoneFull) { bottomBorders.push_back(vec); } y += 160; lastZoneFull = currentZoneFull; } } _BorderIslands[*itIsland + "/right"] = rightBorders; _BorderIslands[*itIsland + "/left"] = leftBorders; _BorderIslands[*itIsland + "/bottom"] = bottomBorders; _BorderIslands[*itIsland + "/top"] = topBorders; } } } } //------------------------------------------------------------------------------------------------- void CScreenshotIslands::attenuateIslandBorders(const std::string & islandName, CBitmap & islandBitmap, const CProximityZone & islandData) { list< CVector2f > leftBorders, rightBorders, bottomBorders, topBorders; rightBorders = _BorderIslands[islandName + "/right"]; leftBorders = _BorderIslands[islandName + "/left"]; bottomBorders = _BorderIslands[islandName + "/bottom"]; topBorders = _BorderIslands[islandName + "/top"]; sint32 xmin = islandData.getBoundXMin(); sint32 xmax = islandData.getBoundXMax(); sint32 ymin = islandData.getBoundYMin(); sint32 ymax = islandData.getBoundYMax(); sint32 width = xmax-xmin; sint32 height = ymax-ymin; uint8 *dest = &(islandBitmap.getPixels(0)[0]); list< CVector2f >::iterator itBorder; for(itBorder=leftBorders.begin(); itBorder!=leftBorders.end(); itBorder++) { const CVector2f initPoint = *itBorder; sint32 x = (sint32)initPoint.x - xmin; sint32 y = (sint32)initPoint.y - ymin; sint32 maxBorder = 160; if(y<0) { maxBorder += y; y = 0; } sint32 inity = y; while(y<(inity+maxBorder) && y<(sint32)height) { double noiseValue = (1+cos(((y-inity)*2*Pi)/maxBorder))*5; double evalNoise = 10 + noiseValue; double diffAlpha = 255/evalNoise; CRGBA color = islandBitmap.getPixelColor(x, height-y-1); sint32 currentX = x-1; while((currentX>=x-evalNoise) && currentX>=0) { uint8 *pixel = &(islandBitmap.getPixels(0)[((height-y-1)*width + currentX)*4]); uint alpha = (uint)(255-diffAlpha*(x-currentX)); uint invAlpha = 255-alpha; *pixel = (uint8) (((invAlpha * *pixel) + (alpha * color.R)) >> 8); *(pixel + 1) = (uint8) (((invAlpha * *(pixel + 1)) + (alpha * color.G)) >> 8); *(pixel + 2) = (uint8) (((invAlpha * *(pixel + 2)) + (alpha * color.B)) >> 8); *(pixel + 3) = (uint8) 255; currentX--; } y++; } } for(itBorder=rightBorders.begin(); itBorder!=rightBorders.end(); itBorder++) { const CVector2f initPoint = *itBorder; sint32 x = (sint32)initPoint.x - xmin; sint32 y = (sint32)initPoint.y - ymin; sint32 maxBorder = 160; if(y<0) { maxBorder += y; y = 0; } sint32 inity = y; while(y<(inity+maxBorder) && y<(sint32)height) { double noiseValue = (1+cos(((y-inity)*2*Pi)/maxBorder))*5; double evalNoise = 10 + noiseValue; double diffAlpha = 255/evalNoise; CRGBA color = islandBitmap.getPixelColor(x, height-y-1); sint32 currentX = x+1; while((currentX<=x+evalNoise) && currentX<width) { uint8 *pixel = &(islandBitmap.getPixels(0)[((height-y-1)*width + currentX)*4]); uint alpha = (uint)(255-diffAlpha*(currentX-x)); uint invAlpha = 255-alpha; *pixel = (uint8) (((invAlpha * *pixel) + (alpha * color.R)) >> 8); *(pixel + 1) = (uint8) (((invAlpha * *(pixel + 1)) + (alpha * color.G)) >> 8); *(pixel + 2) = (uint8) (((invAlpha * *(pixel + 2)) + (alpha * color.B)) >> 8); *(pixel + 3) = (uint8) 255; currentX++; } y++; } } for(itBorder=bottomBorders.begin(); itBorder!=bottomBorders.end(); itBorder++) { const CVector2f initPoint = *itBorder; sint32 x = (sint32)initPoint.x - xmin; sint32 y = (sint32)initPoint.y - ymin; sint32 maxBorder = 160; if(x<0) { maxBorder += x; x = 0; } sint32 initx = x; while(x<(initx+maxBorder) && x<(sint32)width) { double noiseValue = (1+cos(((x-initx)*2*Pi)/maxBorder))*5; double evalNoise = 10 + noiseValue; double diffAlpha = 255/evalNoise; CRGBA color = islandBitmap.getPixelColor(x, height-y-1); sint32 currentY = y-1; while((currentY>=y-evalNoise) && currentY>=0) { uint8 *pixel = &(islandBitmap.getPixels(0)[((height-currentY-1)*width + x)*4]); uint alpha = (uint)(255-diffAlpha*(y-currentY)); uint invAlpha = 255-alpha; *pixel = (uint8) (((invAlpha * *pixel) + (alpha * color.R)) >> 8); *(pixel + 1) = (uint8) (((invAlpha * *(pixel + 1)) + (alpha * color.G)) >> 8); *(pixel + 2) = (uint8) (((invAlpha * *(pixel + 2)) + (alpha * color.B)) >> 8); *(pixel + 3) = (uint8) 255; currentY--; } x++; } } for(itBorder=topBorders.begin(); itBorder!=topBorders.end(); itBorder++) { const CVector2f initPoint = *itBorder; sint32 x = (sint32)initPoint.x - xmin; sint32 y = (sint32)initPoint.y - ymin; sint32 maxBorder = 160; if(x<0) { maxBorder += x; x = 0; } sint32 initx = x; while(x<(initx+maxBorder) && x<(sint32)width) { double noiseValue = (1+cos(((x-initx)*2*Pi)/maxBorder))*5; double evalNoise = 10 + noiseValue; double diffAlpha = 255/evalNoise; CRGBA color = islandBitmap.getPixelColor(x, height-y-1); sint32 currentY = y+1; while((currentY<=y+evalNoise) && currentY<height) { uint8 *pixel = &(islandBitmap.getPixels(0)[((height-currentY-1)*width + x)*4]); uint alpha = (uint)(255-diffAlpha*(currentY-y)); uint invAlpha = 255-alpha; *pixel = (uint8) (((invAlpha * *pixel) + (alpha * color.R)) >> 8); *(pixel + 1) = (uint8) (((invAlpha * *(pixel + 1)) + (alpha * color.G)) >> 8); *(pixel + 2) = (uint8) (((invAlpha * *(pixel + 2)) + (alpha * color.B)) >> 8); *(pixel + 3) = (uint8) 255; currentY++; } x++; } } } //------------------------------------------------------------------------------------------------- void CScreenshotIslands::buildScreenshots() { init(); buildIslandsTextures(); } //------------------------------------------------------------------------------------------------- void CScreenshotIslands::writeProximityBufferToTgaFile(const std::string& fileName,const TBuffer& buffer,uint32 scanWidth,uint32 scanHeight) { uint imageWidth = (scanWidth); // (scanWidth+15)&~15; uint imageHeight = (scanHeight); CTGAImageGrey tgaImage; tgaImage.setup((uint16)imageWidth, (uint16)imageHeight, fileName, 0, 0); for (uint32 y=0;y<scanHeight;++y) { for (uint32 x=0; x<scanWidth; ++x) { uint32 value= buffer[y*scanWidth+x]; tgaImage.set(x, (value>255*5)?255:value/5); } tgaImage.writeLine(); } } //------------------------------------------------------------------------------------------------- void CScreenshotIslands::processProximityBuffer(TBuffer & inputBuffer, uint32 lineLength, TBuffer& resultBuffer) { // a couple of constants to control the range over which our degressive filter is to be applied const uint32 smallValue= 2*5; float a = 5*((255.0 - limitValue)/(float(100-BigValue))); float b = (float)(limitValue*5 - a*BigValue); // determine numer of lines in the buffer... uint32 numLines= inputBuffer.size()/ lineLength; // clear out the result buffer and reset all values to 5*255, remembering that this is the correct value for the image edges resultBuffer.clear(); resultBuffer.resize(inputBuffer.size(),(TBufferEntry)5*255); for (uint32 y=1;y<numLines-1;++y) { uint32 lineOffset= y* lineLength; for (uint32 x=1;x<lineLength-1;++x) { uint32 offset= lineOffset+x; uint32 value=(uint32)inputBuffer[offset]; // apply a clip and cosine function /* if (value<smallValue) value=0; else if (value>BigValue) value=5*255; else value= (uint32)(((1.0-cos(Pi*(float(value-smallValue)/(float)(BigValue-smallValue))))/2.0)*float(5*255)); */ if (value==ValueBorder); else if ((value>=0) && (value<smallValue)) value=0; else if (value>BigValue) { if(value==InteriorValue) { value = (uint)(5*limitValue); } else { value = (uint)(a*value+b); if(value > 5*255) value = 5*255; } } else if((value>=smallValue) && (value<=BigValue)) { value= (uint32)(((1.0-cos(Pi*(float(value-smallValue)/(float)(BigValue-smallValue))))/2.0)*float(5*limitValue)); } // store the value into the result buffer resultBuffer[offset]= (TBufferEntry)value; } } // modify inputBuffer to store "bigValue" limit for (uint32 y=1;y<numLines-1;++y) { uint32 lineOffset= y* lineLength; for (uint32 x=1;x<lineLength-1;++x) { uint32 offset= lineOffset+x; uint32 value=(uint32)inputBuffer[offset]; if(value==BigValue) value = 255*5; else value = 0; inputBuffer[offset]= (TBufferEntry)value; } } /* // lines for (uint32 y=0;y<numLines;++y) { uint32 lineOffset= y* lineLength; for (uint32 x=0;x<lineLength;++x) { uint32 offset = lineOffset+x; uint16 value = resultBuffer[offset]; if(value==ValueBorder) { resultBuffer[offset]= (TBufferEntry)(5*limitValue); // increase x if(x+1<lineLength-1) { uint16 nextValue = resultBuffer[offset+1]; uint16 prevValue = (TBufferEntry)(5*limitValue); if(nextValue!=(TBufferEntry)(5*limitValue)) { uint32 count = x+1; while((count<x+7) && (count<lineLength-1) && ((nextValue=resultBuffer[lineOffset+count])!=0) && nextValue!=(TBufferEntry)(5*limitValue) && (nextValue!=ValueBorder)) { uint16 newValue = (TBufferEntry)(prevValue+5*7); if(newValue < nextValue) { resultBuffer[lineOffset+count] = newValue; } prevValue = newValue; count++; } x = count-1; break; } } // decrease x if(x-1>0) { uint16 nextValue = resultBuffer[offset-1]; uint16 prevValue = (TBufferEntry)(5*limitValue); if(nextValue!=(TBufferEntry)(5*limitValue)) { uint32 count = x-1; while((count>x-7) && (count>0) && ((nextValue=resultBuffer[lineOffset+count])!=0) && nextValue!=(TBufferEntry)(5*limitValue) && (nextValue!=ValueBorder)) { uint16 newValue = (TBufferEntry)(prevValue+5*7); if(newValue < nextValue) { resultBuffer[lineOffset+count] = newValue; } prevValue = newValue; count--; } } } } } } //columns for (uint32 x=0;x<lineLength;++x) { // setup start and end offsets marking first and last 'zero' value pixels in the column uint32 startOffset=x, endOffset=x+(numLines-1)*lineLength; for (uint32 offset=startOffset; offset<=endOffset; offset+=lineLength) { uint16 value = resultBuffer[offset]; if(value==ValueBorder) { resultBuffer[offset]= (TBufferEntry)(5*limitValue); // increase y if(offset+lineLength<=endOffset) { uint16 nextValue = resultBuffer[offset+lineLength]; uint16 prevValue = (TBufferEntry)(5*limitValue); if(nextValue!=(TBufferEntry)(5*limitValue)) { uint32 count = offset+lineLength; while((count<offset+7*lineLength) && (count<=endOffset) && ((nextValue=resultBuffer[count])!=0) && nextValue!=(TBufferEntry)(5*limitValue) && (nextValue!=ValueBorder)) { uint16 newValue = (TBufferEntry)(prevValue+5*7); if(newValue < nextValue) { resultBuffer[count] = newValue; } prevValue = newValue; count += lineLength; } offset = count-lineLength; break; } } // decrease y if(offset-lineLength>=startOffset) { uint16 nextValue = resultBuffer[offset-lineLength]; uint16 prevValue = (TBufferEntry)(5*limitValue); if(nextValue!=(TBufferEntry)(5*limitValue)) { uint32 count = offset-lineLength; while((count>offset-7*lineLength) && (count>=startOffset) && ((nextValue=resultBuffer[count])!=0) && nextValue!=(TBufferEntry)(5*limitValue) && (nextValue!=ValueBorder)) { uint16 newValue = (TBufferEntry)(prevValue+5*7); if(newValue < nextValue) { resultBuffer[count] = newValue; } prevValue = newValue; count -= lineLength; } } } } } } */ //----------------------------------------------------------------------------- // search for pixels of borders map< CVector2f, bool > bordersPixels; for (uint32 y=0;y<numLines;++y) { uint32 lineOffset= y* lineLength; for (uint32 x=0;x<lineLength;++x) { uint32 offset = lineOffset+x; uint16 value = resultBuffer[offset]; if(value==ValueBorder) { bordersPixels[CVector2f((float)x, (float)y)] = true; resultBuffer[offset] = (TBufferEntry)(5*limitValue); } } } //----------------------------------------------------------------------------- // search for global borders // search on lines for top and bottom borders map< CVector2f, uint32 > leftBorders, rightBorders, bottomBorders, topBorders; for (uint32 y=0;y<numLines;++y) { uint32 lineOffset= y* lineLength; bool lastValue = false; CVector2f firstPixelBorder; uint32 nbPixelsBorder = 0; for (uint32 x=0;x<lineLength;++x) { bool value = (bordersPixels.find(CVector2f((float)x, (float)y))!=bordersPixels.end()); if(value) { if(!lastValue) { firstPixelBorder = CVector2f((float)x, (float)y); } nbPixelsBorder ++; } else if(lastValue) { // store border line if(nbPixelsBorder !=1) //column instead of line { bool top=false, bottom=false; if(y>0) { for(uint32 xc=(uint32)firstPixelBorder.x; xc<(uint32)(firstPixelBorder.x+nbPixelsBorder); xc++) { if(resultBuffer[(y-1)*lineLength+xc]==(TBufferEntry)(5*255)) { bottom = true; break; } } } if(y+1<numLines) { for(uint32 xc=(uint32)firstPixelBorder.x; xc<(uint32)(firstPixelBorder.x+nbPixelsBorder); xc++) { if(resultBuffer[(y+1)*lineLength+xc]==(TBufferEntry)(5*255)) { top = true; break; } } } // TOP if(top) topBorders[firstPixelBorder] =nbPixelsBorder; // BOTTOM if(bottom) bottomBorders[firstPixelBorder] =nbPixelsBorder; } nbPixelsBorder =0; } lastValue = value; } } // search on columns for left and right borders for (uint32 x=0;x<lineLength;++x) { bool lastValue = false; CVector2f firstPixelBorder; uint32 nbPixelsBorder = 0; for(uint32 y=0; y<numLines; y++) { bool value = (bordersPixels.find(CVector2f((float)x, (float)y))!=bordersPixels.end()); if(value) { if(!lastValue) { firstPixelBorder = CVector2f((float)x, (float)y); } nbPixelsBorder ++; } else if(lastValue) { // store border line if(nbPixelsBorder !=1) //line instead of column { bool left = false; bool right = false; if(x>0) { for(uint32 yc=(uint32)firstPixelBorder.y; yc<(uint32)(firstPixelBorder.y+nbPixelsBorder); yc++) { if(resultBuffer[yc*lineLength+x-1]==(TBufferEntry)(5*255)) { left = true; break; } } } if(x+1<lineLength) { for(uint32 yc=(uint32)firstPixelBorder.y; yc<(uint32)(firstPixelBorder.y+nbPixelsBorder); yc++) { if(resultBuffer[yc*lineLength+x+1]==(TBufferEntry)(5*255)) { right = true; break; } } } // LEFT if(left) leftBorders[firstPixelBorder] =nbPixelsBorder; // RIGHT if(right) rightBorders[firstPixelBorder] =nbPixelsBorder; } nbPixelsBorder =0; } lastValue = value; } } //----------------------------------------------------------------------------- // attenuate borders // bottom and top borders (lines) for (uint32 y=0;y<numLines;++y) { uint32 lineOffset= y* lineLength; for (uint32 x=0;x<lineLength;++x) { uint32 offset = lineOffset+x; CVector2f firstPixelBorder((float)x, (float)y); // BOTTOM if(bottomBorders.find(CVector2f((float)x, (float)y))!=bottomBorders.end()) { uint32 nbPixelsBorder = bottomBorders[firstPixelBorder]; uint32 xc; for(xc=(uint32)firstPixelBorder.x; xc<(uint32)(firstPixelBorder.x+nbPixelsBorder); xc++) { uint nbZones = (uint)(nbPixelsBorder/160)+1; uint period = (uint)(nbPixelsBorder/(2*nbZones)); double noiseValue = (1-cos(((xc-firstPixelBorder.x)*Pi)/period))*5; double evalNoise = 7 + noiseValue; double diffAlpha = (255.0-limitValue)/evalNoise; uint32 yc = y-1; while((yc>=(uint32)(y-evalNoise)) && yc>=0) { uint16 newValue = (TBufferEntry)(5*(limitValue + diffAlpha*(y-yc))); uint16 currentValue = (TBufferEntry)resultBuffer[yc*lineLength+xc]; if(newValue<currentValue) resultBuffer[yc*lineLength+xc] = (TBufferEntry)(5*(limitValue + diffAlpha*(y-yc))); yc--; } } x=xc; } // TOP if(topBorders.find(CVector2f((float)x, (float)y))!=topBorders.end()) { uint32 nbPixelsBorder = topBorders[firstPixelBorder]; uint32 xc; for(xc=(uint32)firstPixelBorder.x; xc<=(uint32)(firstPixelBorder.x+nbPixelsBorder); xc++) { uint nbZones = (uint)(nbPixelsBorder/160)+1; uint period = (uint)(nbPixelsBorder/(2*nbZones)); double noiseValue = (1-cos(((xc-firstPixelBorder.x)*Pi)/period))*5; double evalNoise = 7 + noiseValue; double diffAlpha = (255.0-limitValue)/evalNoise; uint32 yc = y+1; while((yc<=(uint32)(y+evalNoise)) && yc<numLines) { uint16 newValue = (TBufferEntry)(5*(limitValue + diffAlpha*(yc-y))); uint16 currentValue = (TBufferEntry)resultBuffer[yc*lineLength+xc]; if(newValue<currentValue) resultBuffer[yc*lineLength+xc] = (TBufferEntry)(5*(limitValue + diffAlpha*(yc-y))); yc++; } } x=xc; } } } // left and right borders (columns) for (uint32 x=0;x<lineLength;++x) { for (uint32 y=0; y<numLines; y++) { CVector2f firstPixelBorder((float)x, (float)y); // LEFT if(leftBorders.find(CVector2f((float)x, (float)y))!=leftBorders.end()) { uint32 nbPixelsBorder = leftBorders[firstPixelBorder]; uint32 yc; for(yc=(uint32)firstPixelBorder.y; yc<(uint32)(firstPixelBorder.y+nbPixelsBorder); yc++) { uint nbZones = (uint)(nbPixelsBorder/160)+1; uint period = (uint)(nbPixelsBorder/(2*nbZones)); double noiseValue = (1-cos(((yc-firstPixelBorder.y)*Pi)/period))*5; double evalNoise = 7 + noiseValue; double diffAlpha = (255.0-limitValue)/evalNoise; uint32 xc = x-1; while((xc>=(uint32)(x-evalNoise)) && xc>=0) { uint16 newValue = (TBufferEntry)(5*(limitValue + diffAlpha*(x-xc))); uint16 currentValue = (TBufferEntry)resultBuffer[yc*lineLength+xc]; if(newValue<currentValue) resultBuffer[yc*lineLength+xc] = (TBufferEntry)(5*(limitValue + diffAlpha*(x-xc))); xc--; } } y=yc; } // RIGHT if(rightBorders.find(CVector2f((float)x, (float)y))!=rightBorders.end()) { uint32 nbPixelsBorder = rightBorders[firstPixelBorder]; uint32 yc; for(yc=(uint32)firstPixelBorder.y; yc<=(uint32)(firstPixelBorder.y+nbPixelsBorder); yc++) { uint nbZones = (uint)(nbPixelsBorder/160)+1; uint period = (uint)(nbPixelsBorder/(2*nbZones)); double noiseValue = (1-cos(((yc-firstPixelBorder.y)*Pi)/period))*5; double evalNoise = 7 + noiseValue; double diffAlpha = (255.0-limitValue)/evalNoise; uint32 xc = x+1; while((xc<=(uint32)(x+evalNoise)) && xc<lineLength) { uint16 newValue = (TBufferEntry)(5*(limitValue + diffAlpha*(xc-x))); uint16 currentValue = (TBufferEntry)resultBuffer[yc*lineLength+xc]; if(newValue<currentValue) resultBuffer[yc*lineLength+xc] = (TBufferEntry)(5*(limitValue + diffAlpha*(xc-x))); xc++; } } y=yc; } } } } //-------------------------------------------------------------------------------- void CScreenshotIslands::loadIslands() { // load entryPoints map< string, CVector2f > islands; CScenarioEntryPoints scenarioEntryPoints = CScenarioEntryPoints::getInstance(); scenarioEntryPoints.loadFromFile(); const CScenarioEntryPoints::TEntryPoints& entryPoints = scenarioEntryPoints.getEntryPoints(); CScenarioEntryPoints::TEntryPoints::const_iterator entry(entryPoints.begin()), entryPoint(entryPoints.end()); for( ; entry != entryPoint ; ++entry) { islands[entry->Island] = CVector2f((float)entry->X, (float)entry->Y); } // search islands of each continent map< string, CVector2f >::iterator itIsland(islands.begin()), lastIsland(islands.end()); for( ; itIsland != lastIsland ; ++itIsland) { const CVector2f & entryPoint = itIsland->second; // search continent of this island TContinentsData::iterator itCont(_ContinentsData.begin()), lastCont(_ContinentsData.end()); for( ; itCont != lastCont ; ++itCont) { CContinentData & continent = itCont->second; CVector2f zoneMax = continent.ZoneMax; CVector2f zoneMin = continent.ZoneMin; if((zoneMin.x <= entryPoint.x) && (entryPoint.x <= zoneMax.x) && (zoneMax.y <= entryPoint.y) && (entryPoint.y <= zoneMin.y)) { continent.Islands.push_back(itIsland->first); break; } } } // search data of each island TContinentsData::iterator itCont(_ContinentsData.begin()), lastCont(_ContinentsData.end()); for( ; itCont != lastCont ; ++itCont) { string aiFileName = itCont->first+"_0.cwmap2"; const CContinentData & continent = itCont->second; CProximityMapBuffer continentBuffer; continentBuffer.load(CPath::lookup(aiFileName)); CProximityMapBuffer::TZones zones; continentBuffer.calculateZones(zones); for (uint32 i=0;i<zones.size();++i) { TBuffer zoneBuffer; continentBuffer.generateZoneProximityMap(zones[i], zoneBuffer); TBuffer cleanBuffer; processProximityBuffer(zoneBuffer, zones[i].getZoneWidth(), cleanBuffer); string fileName = string(""); list< string >::const_iterator itIsland(continent.Islands.begin()), lastIsland(continent.Islands.end()); for( ; itIsland != lastIsland ; ++itIsland) { const CVector2f & entryPoint = islands[*itIsland]; sint32 xmin = zones[i].getBoundXMin(); sint32 xmax = zones[i].getBoundXMax(); sint32 ymin = zones[i].getBoundYMin(); sint32 ymax = zones[i].getBoundYMax(); if((xmin <= entryPoint.x) && (entryPoint.x <= xmax) && (ymin <= entryPoint.y) && (entryPoint.y <= ymax)) { fileName = _OutDirectory + "/" + *itIsland + "_prox.tga"; _IslandsData[*itIsland] = zones[i]; break; } } // write the processed proximity map to an output file if(fileName != "") { writeProximityBufferToTgaFile(fileName, cleanBuffer, zones[i].getZoneWidth(), zones[i].getZoneHeight()); _TempFileNames.push_back(fileName); fileName = _OutDirectory + "/" + *itIsland + "_limit.tga"; writeProximityBufferToTgaFile(fileName, zoneBuffer, zones[i].getZoneWidth(), zones[i].getZoneHeight()); _TempFileNames.push_back(fileName); } else { nlinfo("Zone of island not found, tga not build"); } } } CScenarioEntryPoints::TCompleteIslands completeIslands(entryPoints.size()); uint completeIslandsNb = 0; for(uint e=0; e<entryPoints.size(); e++) { const CScenarioEntryPoints::CEntryPoint & entry = entryPoints[e]; CScenarioEntryPoints::CCompleteIsland completeIsland; completeIsland.Island = entry.Island; completeIsland.Package = entry.Package; for(itCont=_ContinentsData.begin(); itCont!=_ContinentsData.end(); ++itCont) { list< string >::const_iterator itIsland(itCont->second.Islands.begin()), lastIsland(itCont->second.Islands.end()); for( ; itIsland != lastIsland ; ++itIsland) { if(*itIsland == entry.Island) { completeIsland.Continent = CSString(itCont->first); if(_IslandsData.find(entry.Island)!=_IslandsData.end()) { completeIsland.XMin = _IslandsData[entry.Island].getBoundXMin(); completeIsland.YMin = _IslandsData[entry.Island].getBoundYMin(); completeIsland.XMax = _IslandsData[entry.Island].getBoundXMax(); completeIsland.YMax = _IslandsData[entry.Island].getBoundYMax(); completeIslands[completeIslandsNb] = completeIsland; completeIslandsNb++; } break; } } } } completeIslands.resize(completeIslandsNb); CScenarioEntryPoints::getInstance().saveXMLFile(completeIslands, _CompleteIslandsFile); } //-------------------------------------------------------------------------------- void CScreenshotIslands::getBuffer(UScene * scene, ULandscape * landscape, CBitmap &btm) { // if (ScreenShotWidth && ScreenShotHeight) { UCamera camera = scene->getCam(); // Destination image CBitmap dest; btm.resize(ScreenShotWidth, ScreenShotHeight, CBitmap::RGBA); uint windowWidth = driver->getWindowWidth(); uint windowHeight = driver->getWindowHeight(); uint top; uint bottom = min(windowHeight, ScreenShotHeight); for (top=0; top<ScreenShotHeight; top+=windowHeight) { uint left; uint right = std::min(windowWidth, ScreenShotWidth); for(left=0; left<ScreenShotWidth; left+=windowWidth) { driver->clearBuffers(_BackColor); // store initial frustum and viewport CFrustum Frustum = scene->getCam().getFrustum(); CViewport Viewport = scene->getViewport(); // Build a new frustum CFrustum frustumPart; frustumPart.Left = Frustum.Left+(Frustum.Right-Frustum.Left)*((float)left/(float)ScreenShotWidth); frustumPart.Right = Frustum.Left+(Frustum.Right-Frustum.Left)*((float)right/(float)ScreenShotWidth); frustumPart.Top = ceil(Frustum.Top+(Frustum.Bottom-Frustum.Top)*((float)top/(float)ScreenShotHeight)); frustumPart.Bottom = ceil(Frustum.Top+(Frustum.Bottom-Frustum.Top)*((float)bottom/(float)ScreenShotHeight)); frustumPart.Near = Frustum.Near; frustumPart.Far = Frustum.Far; frustumPart.Perspective = Frustum.Perspective; // Build a new viewport CViewport viewport; viewport.init(0, 0, (float)(right-left)/windowWidth, (float)(bottom-top)/windowHeight); // Activate all this scene->getCam().setFrustum(frustumPart); scene->setViewport(viewport); scene->setMaxSkeletonsInNotCLodForm(1000000); scene->setPolygonBalancingMode(UScene::PolygonBalancingOff); if(_InverseZTest) { // render scene with inversed ZBuffer test (keep greater distances) driver->setColorMask(false, false, false, false); sceneMaterial.setZFunc(UMaterial::less); // initialize ZBuffer with leak value driver->setMatrixMode2D11(); CQuad quad; quad.V0 = CVector(0.0, 0.0, 0.0); quad.V1 = CVector(1.0, 0.0, 0.0); quad.V2 = CVector(1.0, 1.0, 0.0); quad.V3 = CVector(0.0, 1.0, 0.0); driver->drawQuad(quad, sceneMaterial); driver->setMatrixMode3D(camera); driver->setColorMask(true, true, true, true); scene->enableElementRender(UScene::FilterWater, false); } scene->render(); // display vegetables with normal ZBuffer test if(_InverseZTest && _Vegetation) { scene->enableElementRender(UScene::FilterWater, false); scene->enableElementRender(UScene::FilterLandscape, false); scene->enableElementRender(UScene::FilterWater, true); scene->render(); scene->enableElementRender(UScene::FilterLandscape, true); } // Get the bitmap driver->getBuffer(dest); btm.blit(dest, 0, windowHeight-(bottom-top), right-left, bottom-top, left, top); // restore camera scene->getCam().setFrustum(Frustum); scene->setViewport(Viewport); driver->flush(); driver->swapBuffers(); // Next right = std::min(right+windowWidth, ScreenShotWidth); } // Next bottom = std::min(bottom+windowHeight, ScreenShotHeight); } } else { driver->getBuffer(btm); } } void CScreenshotIslands::buildIslandsTextures() { int loop = 0; int maxLoop = 6; // Create the window with config file values driver->setDisplay(UDriver::CMode(512, 512, 32, true)); // Create a scene UScene * scene = driver->createScene(true); scene->animate(CTime::ticksToSecond(CTime::getPerformanceTime())); scene->setMaxSkeletonsInNotCLodForm(1000000); scene->setPolygonBalancingMode(UScene::PolygonBalancingOff); // Create a camera UCamera camera = scene->getCam(); camera.setTransformMode(UTransformable::DirectMatrix); // Create and load landscape ULandscape * landscape = scene->createLandscape(); landscape->setThreshold(0.0005); if(_InverseZTest) { landscape->setZFunc(UMaterial::greaterequal); } else { landscape->setZFunc(UMaterial::less); } //Iteration on seasons list< string >::iterator itSeason(_SeasonSuffixes.begin()), lastSeason(_SeasonSuffixes.end()); for( ; itSeason != lastSeason ; ++itSeason) { string seasonSuffix = *itSeason; int season; if(seasonSuffix=="_sp") season = CSeason::Spring; else if(seasonSuffix=="_su") season = CSeason::Summer; else if(seasonSuffix=="_au") season = CSeason::Autumn; else if(seasonSuffix=="_wi") season = CSeason::Winter; // Iterations on Continents TContinentsData::iterator itCont(_ContinentsData.begin()), lastCont(_ContinentsData.end()); for( ; itCont != lastCont ; ++itCont) { const CContinentData & continent = itCont->second; // Light init landscape->setupStaticLight(continent.Diffuse, continent.Ambiant, 1.0f); string coarseMeshFile = continent.CoarseMeshMap; string coarseMeshWithoutExt = CFile::getFilenameWithoutExtension(coarseMeshFile); string coarseMeshExt = CFile::getExtension(coarseMeshFile); coarseMeshFile = coarseMeshWithoutExt + seasonSuffix + "." + coarseMeshExt; nldebug("Coarse mesh texture: '%s'", coarseMeshFile.c_str()); scene->setCoarseMeshManagerTexture(coarseMeshFile.c_str()); // Load the landscape string farBank = continent.FarBank; string farBankWithoutExt = CFile::getFilenameWithoutExtension(farBank); string farBankExt = CFile::getExtension(farBank); farBank = farBankWithoutExt + seasonSuffix + "." + farBankExt; landscape->loadBankFiles(continent.SmallBank, farBank); // Load vegatables LandscapeIGManager.initIG(scene, continent.IGFile, driver, season, NULL); // Iterations on Islands list< string >::const_iterator itIsland(continent.Islands.begin()), lastIsland(continent.Islands.end()); for( ; itIsland != lastIsland ; ++itIsland) { loop = 0; if(_IslandsData.find(itIsland->c_str()) != _IslandsData.end()) { const CProximityZone & islandData = _IslandsData[itIsland->c_str()]; sint32 xmin = islandData.getBoundXMin(); sint32 xmax = islandData.getBoundXMax(); sint32 ymin = islandData.getBoundYMin(); sint32 ymax = islandData.getBoundYMax(); float width = (float)(xmax-xmin); float height = (float)(ymax-ymin); ScreenShotWidth = (uint)(width*_MeterPixelSize); ScreenShotHeight = (uint)(height*_MeterPixelSize); // position in island center float posx = ((float)(xmax+xmin))/2; float posy = ((float)(ymax+ymin))/2; CVector entryPos(posx, posy, 400); // Setup camera CMatrix startCamMatrix; startCamMatrix.setPos(entryPos); startCamMatrix.rotateX(-(float)Pi/2); camera.setMatrix(startCamMatrix); camera.setFrustum(width, height, -10000.0f, 10000.0f, false); // init lanscape landscape->postfixTileFilename(seasonSuffix.c_str()); while(loop<maxLoop) { // refresh lanscape vector<string> zonesAdded; vector<string> zonesRemoved; IProgressCallback progress; landscape->refreshAllZonesAround(camera.getMatrix().getPos(), 1000, zonesAdded, zonesRemoved, progress); if(_Vegetation) { LandscapeIGManager.unloadArrayZoneIG(zonesRemoved); LandscapeIGManager.loadArrayZoneIG(zonesAdded); vector<string>::iterator itName(zonesAdded.begin()), lastName(zonesAdded.end()); for( ; itName != lastName ; ++itName) { UInstanceGroup * instanceGr = LandscapeIGManager.getIG(*itName); if(instanceGr) { uint nbInst = instanceGr->getNumInstance(); for(uint i=0; i<instanceGr->getNumInstance(); i++) { instanceGr->setCoarseMeshDist(i, 100000); instanceGr->setDistMax(i, 100000); } } } } scene->animate(CTime::ticksToSecond(CTime::getPerformanceTime())); // Clear all buffers driver->clearBuffers(_BackColor); if(_InverseZTest) { // render scene with inversed ZBuffer test (keep greater distances) driver->setColorMask(false, false, false, false); sceneMaterial.setZFunc(UMaterial::less); // initialize ZBuffer with leak value driver->setMatrixMode2D11(); CQuad quad; quad.V0 = CVector(0.0, 0.0, 0.0); quad.V1 = CVector(1.0, 0.0, 0.0); quad.V2 = CVector(1.0, 1.0, 0.0); quad.V3 = CVector(0.0, 1.0, 0.0); driver->drawQuad(quad, sceneMaterial); driver->setMatrixMode3D(camera); driver->setColorMask(true, true, true, true); scene->enableElementRender(UScene::FilterWater, false); } scene->render(); // display vegetables with normal ZBuffer test if(_InverseZTest && _Vegetation) { scene->enableElementRender(UScene::FilterLandscape, false); scene->enableElementRender(UScene::FilterWater, true); scene->render(); scene->enableElementRender(UScene::FilterLandscape, true); } // Swap 3d buffers driver->flush(); driver->swapBuffers(); // Pump user input messages driver->EventServer.pump(); loop += 1; // Screenshot if(loop==maxLoop-1) { CBitmap islandBitmap; getBuffer(scene, landscape, islandBitmap); buildBackTextureHLS(*itIsland, islandBitmap); } if(loop==maxLoop) { // create srcennshot bitmap of full island CBitmap islandBitmap; getBuffer(scene, landscape, islandBitmap); attenuateIslandBorders(*itIsland, islandBitmap, islandData); // load proximity bitmap CBitmap proxBitmap; std::string proxFileName = _OutDirectory + "/" + *itIsland + "_prox.tga"; CIFile proxFS(proxFileName.c_str()); proxBitmap.load(proxFS); // resize proximity bitmap CBitmap tempBitmap; int newWidth = islandBitmap.getWidth(); int newHeight = islandBitmap.getHeight(); tempBitmap.resize(newWidth, newHeight, islandBitmap.PixelFormat); // blit src bitmap //tempBitmap.blit(proxBitmap, 0, 0, newWidth, newHeight, 0, 0); { const uint8 *prox = &(proxBitmap.getPixels(0)[0]); uint8 *temp = &(tempBitmap.getPixels(0)[0]); for (uint y = 0; y < newHeight; ++y) for (uint x = 0; x < newWidth; ++x) { uint ys = (y * proxBitmap.getHeight()) / newHeight; uint xs = (x * proxBitmap.getWidth()) / newWidth; uint addr = ((y * newWidth) + x) * 4; uint addrs = ((ys * proxBitmap.getWidth()) + xs) * 4; temp[addr] = prox[addrs]; temp[addr+1] = prox[addrs+1]; temp[addr+2] = prox[addrs+2]; temp[addr+3] = prox[addrs+3]; } } // swap them proxBitmap.resize(newWidth, newHeight, proxBitmap.PixelFormat); proxBitmap.swap(tempBitmap); //proxBitmap.resample(newWidth, newHeight); // create final bitmap CBitmap bitmapDest; bitmapDest.resize(islandBitmap.getWidth(), islandBitmap.getHeight(), islandBitmap.PixelFormat); // mix black and full island bitmaps with blend factor of proximity bitmap pixels uint numPix = islandBitmap.getWidth() * islandBitmap.getHeight(); const uint8 *alphaProx = &(proxBitmap.getPixels(0)[0]); const uint8 *srcIsland = &(islandBitmap.getPixels(0)[0]); uint8 *dest = &(bitmapDest.getPixels(0)[0]); const uint8 *srcBack = &(_BackBitmap.getPixels(0)[0]); uint8 *endDest = dest + (numPix << 2); do { uint invblendFact = (uint) alphaProx[0]; uint blendFact = 256 - invblendFact; // blend 4 component at each pass *dest = (uint8) (((blendFact * *srcIsland) + (invblendFact * *srcBack)) >> 8); *(dest + 1) = (uint8) (((blendFact * *(srcIsland + 1)) + (invblendFact * *(srcBack + 1))) >> 8); *(dest + 2) = (uint8) (((blendFact * *(srcIsland + 2)) + (invblendFact * *(srcBack + 2))) >> 8); *(dest + 3) = (uint8) 255; alphaProx = alphaProx + 4; srcIsland = srcIsland + 4; dest = dest + 4; srcBack = srcBack + 4; } while(dest != endDest); // create tga file of avoidable place in island string textureName = _OutDirectory + "/" + *itIsland + seasonSuffix + ".tga"; CBitmap bitmapLittle; bitmapLittle.resize(bitmapDest.getWidth(), bitmapDest.getHeight(), bitmapDest.PixelFormat); bitmapLittle = bitmapDest; if(!isPowerOf2(bitmapDest.getWidth()) || !isPowerOf2(bitmapDest.getHeight()) ) { uint pow2w = NLMISC::raiseToNextPowerOf2(bitmapDest.getWidth()); uint pow2h = NLMISC::raiseToNextPowerOf2(bitmapDest.getHeight()); CBitmap enlargedBitmap; enlargedBitmap.resize(pow2w, pow2h, bitmapDest.PixelFormat); // blit src bitmap enlargedBitmap.blit(&bitmapDest, 0, 0); // swap them bitmapDest.swap(enlargedBitmap); } COFile fsDest(textureName.c_str()); bitmapDest.writeTGA(fsDest,32); // little tga bitmapLittle.resample(bitmapLittle.getWidth()/10, bitmapLittle.getHeight()/10); if(!isPowerOf2(bitmapLittle.getWidth()) || !isPowerOf2(bitmapLittle.getHeight()) ) { uint pow2w = NLMISC::raiseToNextPowerOf2(bitmapLittle.getWidth()); uint pow2h = NLMISC::raiseToNextPowerOf2(bitmapLittle.getHeight()); CBitmap enlargedBitmap; enlargedBitmap.resize(pow2w, pow2h, bitmapLittle.PixelFormat); // blit src bitmap enlargedBitmap.blit(&bitmapLittle, 0, 0); // swap them bitmapLittle.swap(enlargedBitmap); } textureName = _OutDirectory + "/" + *itIsland + seasonSuffix + "_little.tga"; COFile fsLittle(textureName.c_str()); bitmapLittle.writeTGA(fsLittle,32); _BackColor = CRGBA(255, 255, 255, 255); } } } } LandscapeIGManager.reset(); landscape->removeAllZones(); } } // remove proximity tga list<string>::iterator itProx(_TempFileNames.begin()), lastProx(_TempFileNames.end()); for( ; itProx != lastProx ; ++itProx) { CFile::deleteFile(*itProx); }; } //-------------------------------------------------------------------------------- inline bool RGB2HSV(const CRGBA & rgba, uint & Hue, uint & Sat, uint & Val) { double Min_, Max_, Delta, H, S, V; H = 0.0; Min_ = min(min(rgba.R, rgba.G), rgba.B); Max_ = max(max(rgba.R, rgba.G), rgba.B); Delta = ( Max_ - Min_); V = Max_; if(Max_ != 0.0) { S = 255.0*Delta/Max_; } else { S = 0.0; H = -1; return false; } if(rgba.R == Max_) { H = (rgba.G - rgba.B) / Delta; } else if(rgba.G == Max_) { H = 2.0 + (rgba.B - rgba.R) / Delta; } else { H = 4.0 + (rgba.R - rgba.G) / Delta; } H = H * 60; if(H < 0.0) { H = H + 360.0; } Hue = (uint)H ; // Hue -> 0..360 Sat = (uint)S * 100 / 255; // Saturation -> 0..100 % Val = (uint)V * 100 / 255; // Value - > 0..100 % return true; } //------------------------------------------------------------------------------------------------- inline bool infHLS(const CRGBA & rgba1, const CRGBA & rgba2) { uint h1, s1, v1, h2, s2, v2; RGB2HSV(rgba1, h1, s1, v1); RGB2HSV(rgba2, h2, s2, v2); if(h1 != h2) { return (h1 < h2); } else if(s1 != s2) { return (s1 < s2); } else { return (v1 < v2); } } //------------------------------------------------------------------------------------------------- void CScreenshotIslands::buildBackTextureHLS(const std::string & islandName, const CBitmap & islandBitmap) { // load limit bitmap CBitmap limBitmap; std::string limFileName = _OutDirectory + "/" + islandName + "_limit.tga"; CIFile limFS(limFileName.c_str()); limBitmap.load(limFS); list< CRGBA > limitPixels; // search for colors of limit pixels for(uint x=0; x<islandBitmap.getWidth(); x++) { for(uint y=0; y<islandBitmap.getHeight(); y++) { CRGBA lim = limBitmap.getPixelColor(x, y); if((lim == CRGBA::White) && (islandBitmap.getPixelColor(x, y)!=CRGBA::White)) { limitPixels.push_back(islandBitmap.getPixelColor(x, y)); } } } // HLS order list< CRGBA > sortedHLS; list< CRGBA >::iterator itCol, itHLS; bool inserted = false; for(itCol=limitPixels.begin(); itCol!=limitPixels.end(); itCol++) { inserted = false; for(itHLS=sortedHLS.begin(); itHLS!=sortedHLS.end(); ++itHLS) { if(infHLS(*itCol, *itHLS)) { sortedHLS.insert(itHLS, *itCol); inserted = true; break; } } if(inserted==false) sortedHLS.push_back(*itCol); } // keep more filled eighth of circle nlassert(!sortedHLS.empty()); // If it crashes here, you may be missing .zonel's. itHLS = sortedHLS.begin(); uint h, s, v; RGB2HSV(*itHLS, h, s, v); list< CRGBA > currentList, maxList; for(uint i=0; i<8; i++) { while(itHLS!=sortedHLS.end() && h<i*45) { currentList.push_back(*itHLS); RGB2HSV(*itHLS, h, s, v); ++itHLS; } if(currentList.size() > maxList.size()) { maxList.clear(); maxList = currentList; currentList.clear(); } } vector< CRGBA > sortedColors(maxList.size()); uint colorsNb = 0; CRGBA lastColor(0, 0, 0, 0); CRGBA maxColor; uint maxColorNb = 0; uint currentColorNb = 0; for(itHLS=maxList.begin(); itHLS!=maxList.end(); ++itHLS) { if(lastColor==*itHLS) { currentColorNb++; } else { currentColorNb = 1; } if(currentColorNb>maxColorNb) { maxColorNb = currentColorNb; maxColor = *itHLS; } lastColor = *itHLS; RGB2HSV(*itHLS, h, s, v); if(v>25 && v<75 && s>25 && s<75) { sortedColors[colorsNb] = *itHLS; colorsNb++; } } if(colorsNb < 5) { colorsNb = 0; for(itHLS=maxList.begin(); itHLS!=maxList.end(); ++itHLS) { sortedColors[colorsNb] = *itHLS; colorsNb++; } } sortedColors.resize(colorsNb); _BackBitmap.resize(islandBitmap.getWidth(), islandBitmap.getHeight(), islandBitmap.PixelFormat); if(sortedColors.size()!=0) { _BackColor = maxColor; CRandom randomGenerator; uint8 * backPixels = &(_BackBitmap.getPixels(0)[0]); for(uint x=0; x<_BackBitmap.getWidth(); x++) { for(uint y=0; y<_BackBitmap.getHeight(); y++) { sint32 randomVal = randomGenerator.rand(colorsNb-1); const CRGBA & color = sortedColors[randomVal]; *backPixels = (uint8) color.R; *(backPixels + 1) = (uint8) color.G; *(backPixels + 2) = (uint8) color.B; *(backPixels + 3) = (uint8) 255; backPixels = backPixels+4; } } } /* //TEST CBitmap HLSBitmap; HLSBitmap.resize(640, sortedColors.size()*4, islandBitmap.PixelFormat); uint8 * hlsPixels = &(HLSBitmap.getPixels(0)[0]); for(uint i=0; i < sortedColors.size(); i++) { uint count = 0; while(count<640*4) { *hlsPixels = (uint8) sortedColors[i].R; *(hlsPixels + 1) = (uint8) sortedColors[i].G; *(hlsPixels + 2) = (uint8) sortedColors[i].B; *(hlsPixels + 3) = (uint8) sortedColors[i].A; hlsPixels = hlsPixels+4; count++; } } string textureName = _OutDirectory + "/" + islandName + "_HLS2.tga"; COFile fsHLS(textureName.c_str()); HLSBitmap.writeTGA(fsHLS,32); */ } //-------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- // methods CProximityMapBuffer //------------------------------------------------------------------------------------------------- void CProximityMapBuffer::load(const std::string& name) { // load the AI collision map file CWorldMap worldMap; CIFile f(name); f.serial(worldMap); // lookup the map bounds CMapPosition min, max; worldMap.getBounds(min, max); // calculate a handful of constants relating to the bounds of the image... _ScanWidth = max.x()-min.x(); _ScanHeight = max.y()-min.y(); _XOffset= min.x(); _YOffset= (sint16)min.y(); // redimension buffer to correct size _Buffer.resize(_ScanWidth*_ScanHeight); // setup a position variable to mark the start point of each line CMapPosition scanpos(min.x(),min.y()); // iterate over the scan area looking for points that are accessible for (uint32 y=0; y<_ScanHeight; ++y, scanpos = scanpos.getStepN()) { CMapPosition pos(scanpos); // scan a line of the map for (uint32 x=0; x<_ScanWidth; ++x, pos = pos.getStepE()) { bool isAccessible= false; // if the cell pointer is NULL it means that the 16x16 cell in question is inaccessible if (worldMap.getRootCellCst(pos) != NULL) { // run through the surfaces in the cell looking for a match for this position (may be as many as 3 surfaces per cell max) for (uint32 ns=0; ns<3; ++ns) { isAccessible |= worldMap.getSafeWorldPosition(pos, CSlot(ns)).isValid(); } } // setup the next pixel in the output buffers... _Buffer[y*_ScanWidth+x]= (isAccessible? 0: (TBufferEntry)~0u); } } } //------------------------------------------------------------------------------------------------- void CProximityMapBuffer::calculateZones(TZones& zones) { // clear out the result buffer before starting work zones.clear(); // setup a container to hold the accessible points within this buffer typedef std::set<uint32> TAccessiblePoints; TAccessiblePoints accessiblePoints; // start by building the set of all accessible points for (uint32 i=0;i<_Buffer.size();++i) { if (_Buffer[i]==0) accessiblePoints.insert(i); } // while there are still points remaining in the set we must have another zone to process while (!accessiblePoints.empty()) { // append a new zone to the zones vector and get a refference to it zones.push_back( CProximityZone(_ScanWidth,_ScanHeight,_XOffset,_YOffset) ); CProximityZone& theZone= zones.back(); // setup a todo list representing points that are part of the surface that we are dealing with // that haven't yet been treated to check for neighbours, etc std::vector<uint32> todo; // get hold of the first point in the accessilbe points set and push it onto the todo list todo.push_back(*accessiblePoints.begin()); accessiblePoints.erase(todo.back()); // while we have more points to deal with ... while (!todo.empty()) { // pop the next point off the todo list uint32 thePoint= todo.back(); todo.pop_back(); // add the point to the zone theZone.add(thePoint); // a little macro for the code to perform for each movement test... #define TEST_MOVE(xoffs,yoffs)\ {\ TAccessiblePoints::iterator it= accessiblePoints.find(thePoint+xoffs+_ScanWidth*yoffs);\ if (it!=accessiblePoints.end())\ {\ todo.push_back(*it);\ accessiblePoints.erase(it);\ }\ } // N, S, W, E moves TEST_MOVE( 0, 1); TEST_MOVE( 0,-1); TEST_MOVE( 1, 0); TEST_MOVE(-1, 0); // NW, NE, WS, SE moves TEST_MOVE( 1, 1); TEST_MOVE(-1, 1); TEST_MOVE( 1,-1); TEST_MOVE(-1,-1); #undef TEST_MOVE } } nlinfo("Found %u zones",zones.size()); } //------------------------------------------------------------------------------------------------- void CProximityMapBuffer::_prepareBufferForZoneProximityMap(const CProximityZone& zone,TBuffer& zoneBuffer,TOffsetsVector& accessiblePoints) { // the length of runs that we consider too short to deal with... const uint32 shortRunLength=5; // redimention and initialise the zone buffer uint32 zoneWidth= zone.getZoneWidth(); uint32 zoneHeight= zone.getZoneHeight(); zoneBuffer.clear(); zoneBuffer.resize(zoneWidth*zoneHeight,(TBufferEntry)~0u); // setup the buffer's accessible points and prime vects[0] with the set of accessible points in the zone buffer for (uint32 i=0;i<zone.getOffsets().size();++i) { // lookup the next offset in the zone's offsets vector and remap to a zone-relative offset uint32 zoneOffset= zone.remapOffset(zone.getOffsets()[i]); // flag the appropriate entry in the zoneBuffer as 'distance=0' zoneBuffer[zoneOffset]= 0; // add the zone offset to the accessible points vector accessiblePoints.push_back(zoneOffset); } // run through the zone buffer looking for points that are surrounded that we can just throw out... // start by flagging all points that belong to a short run in the y direction for (uint32 i=0;i<zoneWidth;++i) { // setup start and end offsets marking first and last 'zero' value pixels in the column uint32 startOffset=i, endOffset=i+(zoneHeight-1)*zoneWidth; for (; startOffset<endOffset && zoneBuffer[startOffset]!=0; startOffset+= zoneWidth) {} for (; endOffset>startOffset && zoneBuffer[endOffset]!=0; endOffset-= zoneWidth) {} for (uint32 offset=startOffset, marker=startOffset;offset<=endOffset;offset+=zoneWidth) { // see if this is an accessible position if (zoneBuffer[offset]!=0) { zoneBuffer[offset]= InteriorValue; if(offset-1>=startOffset && zoneBuffer[offset-1]==(TBufferEntry)~0u) { zoneBuffer[offset-1] = ValueBorder; } if(offset+1<=endOffset && zoneBuffer[offset+1]==(TBufferEntry)~0u) { zoneBuffer[offset+1] = ValueBorder; } } } } // continue by dealing with all points that belong to a short run in the x direction for (uint32 i=0;i<zoneHeight;++i) { // setup start and end offsets marking first and last 'zero' value pixels in the column uint32 startOffset= i*zoneWidth; uint32 endOffset= startOffset+zoneWidth-1; uint32 startOffsetInit = startOffset; uint32 endOffsetInit = endOffset; for (; startOffset<endOffset && zoneBuffer[startOffset]!=0; ++startOffset) {} for (; endOffset>startOffset && zoneBuffer[endOffset]!=0; --endOffset) {} for (uint32 offset=startOffset, marker=startOffset;offset<=endOffset;++offset) { // see if this is an accessible position if (zoneBuffer[offset]!=0) { zoneBuffer[offset]= InteriorValue; if(offset>zoneWidth && zoneBuffer[offset-zoneWidth]==(TBufferEntry)~0u) { zoneBuffer[offset-zoneWidth] = ValueBorder; } if(offset+zoneWidth<zoneHeight*zoneWidth && zoneBuffer[offset+zoneWidth]==(TBufferEntry)~0u) { zoneBuffer[offset+zoneWidth] = ValueBorder; } } } } } //------------------------------------------------------------------------------------------------- void CProximityMapBuffer::generateZoneProximityMap(const CProximityZone& zone,TBuffer& zoneBuffer) { // a set of vectors to hold sets of points that need to be treated TOffsetsVector vects[16]; // a counter that should always contain sum of all vects[i].size() uint32 entriesToTreat= 0; // setup the buffer's accessible points and prime vects[0] with the set of accessible points in the zone buffer _prepareBufferForZoneProximityMap(zone, zoneBuffer, vects[0]); entriesToTreat= vects[0].size(); // lookup the buffer dimentions uint32 zoneWidth= zone.getZoneWidth(); uint32 zoneHeight= zone.getZoneHeight(); // for dist=0 to ? treat points with distance 'dist' from centre, iterating until all vects are empty for (TBufferEntry dist=0; entriesToTreat!=0; ++dist) { // setup refference to the vector that we are supposed to be iterating over for this dist TOffsetsVector &vect= vects[dist&15]; // iterate over contents of points for this distance, treating NSWE neighbours for(TOffsetsVector::iterator it=vect.begin(); it!=vect.end(); ++it) { uint32 val=(*it); // deal with the case where this point has already been refferenced via a better route if (zoneBuffer[val]<dist || (zoneBuffer[val]==InteriorValue && dist > BigValue) || (zoneBuffer[val]==ValueBorder && dist > BigValue)) continue; // write the new distance into this buffer entry zoneBuffer[val]=dist; // decompose into x and y in order to manage identification of neighbour cells correctly uint32 x= val% zoneWidth; uint32 y= val/ zoneWidth; #define TEST_MOVE(xoffs,yoffs,newDist)\ {\ if (((uint32)(x+(xoffs))<zoneWidth) && ((uint32)(y+(yoffs))<zoneHeight))\ {\ uint32 newVal= val+(xoffs)+((yoffs)*zoneWidth);\ bool isInterior= ((zoneBuffer[newVal]==InteriorValue && newDist > BigValue) || (zoneBuffer[newVal]==ValueBorder && newDist > BigValue));\ if (zoneBuffer[newVal]>(newDist) && !isInterior)\ {\ zoneBuffer[newVal]=(newDist);\ vects[(newDist)&15].push_back(newVal);\ ++entriesToTreat;\ }\ }\ } // N, S, W, E moves TEST_MOVE( 0, 1,dist+5); TEST_MOVE( 0,-1,dist+5); TEST_MOVE( 1, 0,dist+5); TEST_MOVE(-1, 0,dist+5); // NW, NE, WS, SE moves TEST_MOVE( 1, 1,dist+7); TEST_MOVE(-1, 1,dist+7); TEST_MOVE( 1,-1,dist+7); TEST_MOVE(-1,-1,dist+7); // NNW, NNE, SSW, SSE moves TEST_MOVE( 1, 2,dist+11); TEST_MOVE(-1, 2,dist+11); TEST_MOVE( 1,-2,dist+11); TEST_MOVE(-1,-2,dist+11); // WNW, WSW, ENE, ESE moves TEST_MOVE( 2, 1,dist+11); TEST_MOVE(-2, 1,dist+11); TEST_MOVE( 2,-1,dist+11); TEST_MOVE(-2,-1,dist+11); #undef TEST_MOVE } // clear out the vector entriesToTreat-= vect.size(); vect.clear(); } } //------------------------------------------------------------------------------------------------- const TBuffer& CProximityMapBuffer::getBuffer() const { return _Buffer; } //------------------------------------------------------------------------------------------------- uint32 CProximityMapBuffer::getScanHeight() const { return _ScanHeight; } //------------------------------------------------------------------------------------------------- uint32 CProximityMapBuffer::getScanWidth() const { return _ScanWidth; } //----------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- // methods CProximityZone //------------------------------------------------------------------------------------------------- CProximityZone::CProximityZone(uint32 scanWidth,uint32 scanHeight,sint32 xOffset, sint32 yOffset) { _ScanWidth = scanWidth; _ScanHeight = scanHeight; _XOffset = xOffset; _YOffset = yOffset; _MaxOffset = scanWidth * scanHeight -1; _XMin = ~0u; _YMin = ~0u; _XMax = 0; _YMax = 0; _BorderPixels = 30; } //------------------------------------------------------------------------------------------------- bool CProximityZone::add(uint32 offset) { // make sure the requested point is in the zone if (offset>_MaxOffset) return false; // calculate the x and y coordinates of the point uint32 y= offset/ _ScanWidth; uint32 x= offset% _ScanWidth; // update the bounding coordinates for this zone if (x<_XMin) _XMin= x; if (x>_XMax) _XMax= x; if (y<_YMin) _YMin= y; if (y>_YMax) _YMax= y; // add the point to the vector of points _Offsets.push_back(offset); return true; } //------------------------------------------------------------------------------------------------- const CProximityZone::TOffsets& CProximityZone::getOffsets() const { return _Offsets; } //------------------------------------------------------------------------------------------------- uint32 CProximityZone::getZoneWidth() const { return getZoneXMax()- getZoneXMin() +1; } //------------------------------------------------------------------------------------------------- uint32 CProximityZone::getZoneHeight() const { return getZoneYMax()- getZoneYMin() +1; } //------------------------------------------------------------------------------------------------- sint32 CProximityZone::getZoneXMin() const { return _XMin-_BorderPixels; } //------------------------------------------------------------------------------------------------- sint32 CProximityZone::getZoneYMin() const { return _YMin-_BorderPixels; } //------------------------------------------------------------------------------------------------- uint32 CProximityZone::getZoneXMax() const { return _XMax+_BorderPixels; } //------------------------------------------------------------------------------------------------- uint32 CProximityZone::getZoneYMax() const { return _YMax+_BorderPixels; } //------------------------------------------------------------------------------------------------- uint32 CProximityZone::getBoundXMin() const { return _XMin+_XOffset-_BorderPixels; } //------------------------------------------------------------------------------------------------- uint32 CProximityZone::getBoundYMin() const { return _YMin+_YOffset-_BorderPixels; } //------------------------------------------------------------------------------------------------- uint32 CProximityZone::getBoundXMax() const { return _XMax+_XOffset+_BorderPixels; } //------------------------------------------------------------------------------------------------- uint32 CProximityZone::getBoundYMax() const { return _YMax+_YOffset+_BorderPixels; } //------------------------------------------------------------------------------------------------- uint32 CProximityZone::remapOffset(uint32 bufferOffset) const { // decompose input coordinates into x and y parts uint32 bufferX= bufferOffset% _ScanWidth; uint32 bufferY= bufferOffset/ _ScanWidth; // remap the offset from a _Buffer-relative offset to a zone-relative offset return bufferX-getZoneXMin()+ (bufferY-getZoneYMin())*getZoneWidth(); } }