Linux multi-monitor fullscreen support
[ryzomcore.git] / ryzom / tools / client / r2_islands_textures / screenshot_islands.cpp
blobae8595bf825403e9639a7b950b77a94a5f10f68f
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "screenshot_islands.h"
22 #include <game_share/scenario_entry_points.h>
24 #include <game_share/season.h>
25 #include <game_share/dir_light_setup.h>
26 #include <game_share/bmp4image.h>
28 #include <nel/misc/path.h>
29 #include <nel/misc/file.h>
30 #include <nel/misc/config_file.h>
31 #include <nel/misc/big_file.h>
32 #include <nel/misc/i18n.h>
33 #include <nel/misc/progress_callback.h>
34 #include <nel/misc/random.h>
35 #include <nel/misc/common.h>
36 #include <nel/misc/wang_hash.h>
37 #include <nel/3d/u_material.h>
39 #include <nel/3d/u_driver.h>
40 #include <nel/3d/u_scene.h>
41 #include <nel/3d/u_landscape.h>
42 #include <nel/3d/u_camera.h>
43 #include <nel/3d/landscapeig_manager.h>
44 #include <nel/3d/u_material.h>
46 #include <nel/georges/u_form_loader.h>
47 #include <nel/georges/u_form.h>
48 #include <nel/georges/u_form_elm.h>
50 // AI share
51 #include <ai_share/world_map.h>
53 #include <nel/3d/material.h>
55 #include <math.h>
56 #include <limits>
58 using namespace NLMISC;
59 using namespace NL3D;
60 using namespace std;
61 using namespace EGSPD;
62 using namespace NLGEORGES;
63 using namespace RYAI_MAP_CRUNCH;
66 // The 3d driver
67 UDriver *driver = NULL;
68 CLandscapeIGManager LandscapeIGManager;
70 uint ScreenShotWidth;
71 uint ScreenShotHeight;
73 UMaterial sceneMaterial;
75 namespace R2
78 const TBufferEntry InteriorValue = std::numeric_limits<TBufferEntry>::max()-1;
79 const TBufferEntry ValueBorder = std::numeric_limits<TBufferEntry>::max()-2;
80 const uint32 BigValue= 15*5;
81 const float limitValue = 200.0;
83 //-------------------------------------------------------------------------------------------------
84 CScreenshotIslands::CScreenshotIslands()
86 _BackColor = CRGBA(255, 255, 255, 255);
89 //-------------------------------------------------------------------------------------------------
90 void CScreenshotIslands::init()
92 // Create a driver
93 driver = UDriver::createDriver(0, true);
94 nlassert(driver);
96 sceneMaterial = driver->createMaterial();
97 sceneMaterial.getObjectPtr()->setLighting(true);
98 sceneMaterial.getObjectPtr()->setSpecular(CRGBA(255, 255, 255, 255));
99 sceneMaterial.getObjectPtr()->setShininess(50);
100 sceneMaterial.getObjectPtr()->setDiffuse(CRGBA(100, 100, 100, 255));
101 sceneMaterial.getObjectPtr()->setEmissive(CRGBA(25, 25, 25, 255));
103 // load and parse the configfile
104 CConfigFile cf;
105 cf.load("island_screenshots.cfg");
107 // get the value of searchPaths
108 CConfigFile::CVar * searchPaths = cf.getVarPtr("SearchPaths");
109 if(searchPaths)
111 for(int i = 0; i < searchPaths->size(); i++)
113 CPath::addSearchPath(searchPaths->asString(i).c_str(), true, false);
116 CPath::remapExtension("dds", "tga", true);
117 CPath::remapExtension("dds", "png", true);
119 // get the scenario entry points file
120 CConfigFile::CVar * epFile = cf.getVarPtr("CompleteIslandsFile");
121 if(epFile)
123 _CompleteIslandsFile = epFile->asString();
126 // get the out directory path
127 CConfigFile::CVar * outDir = cf.getVarPtr("OutDir");
128 if(outDir)
130 _OutDirectory = outDir->asString();
133 // get the vegetation option
134 CConfigFile::CVar * veget = cf.getVarPtr("Vegetation");
135 if(veget)
137 _Vegetation = veget->asBool();
140 // get the vegetation option
141 CConfigFile::CVar * inverseZTest = cf.getVarPtr("InverseZTest");
142 if(inverseZTest)
144 _InverseZTest = inverseZTest->asBool();
147 // get list of continents
148 CConfigFile::CVar * continents = cf.getVarPtr("Continents");
149 vector<string> continentsName(continents->size());
150 for(int i = 0; i < continents->size(); i++)
152 continentsName[i] = continents->asString(i);
155 // get continents data (light, coarseMesh,...)
156 UFormLoader * formLoader;
157 for(uint i=0; i<continentsName.size(); i++)
159 string georgeFileName = continentsName[i]+".continent";
161 formLoader = UFormLoader::createLoader();
162 CSmartPtr<UForm> form = formLoader->loadForm(CPath::lookup(georgeFileName).c_str());
163 if(form)
165 CContinentData continentData;
167 UFormElm &formRoot = form->getRootNode();
169 const UFormElm *elm;
170 if(formRoot.getNodeByName(&elm, "LightLandscapeDay") && elm)
172 CDirLightSetup landscapeLightDay;
173 landscapeLightDay.build(*elm);
174 continentData.Ambiant = landscapeLightDay.Ambiant;
175 continentData.Diffuse = landscapeLightDay.Diffuse;
177 if(continentsName[i]=="r2_jungle" || continentsName[i]=="r2_forest" || continentsName[i]=="r2_roots")
179 continentData.Ambiant = CRGBA(255, 255, 255, 255);
180 continentData.Diffuse = CRGBA(255, 255, 255, 255);
184 formRoot.getValueByName(continentData.IGFile, "LandscapeIG");
185 string zoneMin, zoneMax;
186 formRoot.getValueByName(zoneMin, "ZoneMin");
187 formRoot.getValueByName(zoneMax, "ZoneMax");
189 getPosFromZoneName(zoneMin, continentData.ZoneMin);
190 getPosFromZoneName(zoneMax, continentData.ZoneMax);
192 string filename;
193 if(formRoot.getValueByName(filename, "Ecosystem"))
195 UFormLoader *formLoaderEco = UFormLoader::createLoader();
196 if(formLoaderEco)
198 // Load the form
199 CSmartPtr<UForm> formEco = formLoaderEco->loadForm(filename.c_str());
201 if(formEco)
203 // Root node
204 UFormElm &formRootEco = formEco->getRootNode();
206 // Small bank.
207 formRootEco.getValueByName(continentData.SmallBank, "SmallBank");
209 // Far bank.
210 formRootEco.getValueByName(continentData.FarBank, "FarBank");
212 // Coarse mesh texture.
213 formRootEco.getValueByName(continentData.CoarseMeshMap, "CoarseMeshMap");
215 else
217 nlwarning("CScreenshotIslands::init : Can't load form %s.", filename.c_str());
219 UFormLoader::releaseLoader(formLoaderEco);
223 _ContinentsData[continentsName[i]] = continentData;
225 UFormLoader::releaseLoader(formLoader);
229 // load islands
230 loadIslands();
232 searchIslandsBorders();
234 // get seasons
235 CConfigFile::CVar * seasonSuffixes = cf.getVarPtr("SeasonSuffixes");
236 if(seasonSuffixes)
238 for(uint i = 0; i < (uint)seasonSuffixes->size(); i++)
240 _SeasonSuffixes.push_back(seasonSuffixes->asString(i));
244 // get the meter size in pixels
245 CConfigFile::CVar * meterSize = cf.getVarPtr("MeterPixelSize");
246 if(meterSize)
248 _MeterPixelSize = meterSize->asInt();
252 //-------------------------------------------------------------------------------------------------
253 bool CScreenshotIslands::getPosFromZoneName(const std::string &name, NLMISC::CVector2f &dest)
255 if(name.empty())
257 nlwarning ("getPosFromZoneName(): empty name, can't getPosFromZoneName");
258 return false;
261 static std::string zoneName;
262 static string xStr, yStr;
263 xStr.clear();
264 yStr.clear();
265 zoneName = CFile::getFilenameWithoutExtension(name);
266 uint32 i = 0;
267 while(zoneName[i] != '_')
269 if(!::isdigit(zoneName[i])) return false;
270 yStr += zoneName[i]; ++i;
271 if(i == zoneName.size())
272 return false;
274 ++i;
275 while(i < zoneName.size())
277 if(!::isalpha(zoneName[i])) return false;
278 xStr += (char) ::toupper(zoneName[i]); ++i;
280 if(xStr.size() != 2) return false;
281 dest.x = 160.f * ((xStr[0] - 'A') * 26 + (xStr[1] - 'A'));
282 dest.y = 160.f * -atoi(yStr.c_str());
283 return true;
286 //-------------------------------------------------------------------------------------------------
287 void CScreenshotIslands::searchIslandsBorders()
289 vector<string> filenames;
290 list<string> zonelFiles;
292 map< CVector2f, bool> islandsMap;
294 TContinentsData::iterator itCont(_ContinentsData.begin()), lastCont(_ContinentsData.end());
295 for( ; itCont != lastCont ; ++itCont)
297 // for each continent we recover a map of zonel files whith position of
298 // left/bottom point of each zone for keys
299 filenames.clear();
300 zonelFiles.clear();
302 string bnpFileName = itCont->first + ".bnp";
303 CBigFile::getInstance().list(bnpFileName.c_str(), filenames); // FIXME FIXME NOT READING FROM BNP!
305 for(uint i=0; i<filenames.size(); i++)
307 if(CFile::getExtension(filenames[i]) == "zonel")
309 zonelFiles.push_back(filenames[i]);
313 list<string>::iterator itZonel(zonelFiles.begin()), lastZonel(zonelFiles.end());
314 for( ; itZonel != lastZonel ; ++itZonel)
316 CVector2f position;
317 getPosFromZoneName(*itZonel, position);
319 islandsMap[position] = true;
322 // search for island borders
323 CContinentData & continent = itCont->second;
324 list< string >::const_iterator itIsland(continent.Islands.begin()), lastIsland(continent.Islands.end());
325 for( ; itIsland != lastIsland ; ++itIsland)
327 if(_IslandsData.find(itIsland->c_str()) != _IslandsData.end())
329 const CProximityZone & islandData = _IslandsData[itIsland->c_str()];
331 sint32 xmin = islandData.getBoundXMin();
332 sint32 xmax = islandData.getBoundXMax();
333 sint32 ymin = islandData.getBoundYMin();
334 sint32 ymax = islandData.getBoundYMax();
336 sint32 width = xmax-xmin;
337 sint32 height = ymax-ymin;
339 sint32 zonelXMin = ((uint)(xmin/160)) * 160;
340 sint32 zonelYMin = ((uint)(ymin/160) - 1) * 160;
341 sint32 zonelXMax = ((uint)(xmax/160)) * 160;
342 sint32 zonelYMax = ((uint)(ymax/160) - 1) * 160;
344 list< CVector2f > leftBorders, rightBorders, bottomBorders, topBorders;
346 // search for left and right borders on lines
347 for(sint32 y = zonelYMin; y<=zonelYMax; y+=160 )
349 sint32 x=zonelXMin;
350 CVector2f vec((float)x, (float)y);
351 bool lastZoneFull = (islandsMap.find(vec) != islandsMap.end());
352 bool currentZoneFull;
354 while(x<=zonelXMax)
356 vec = CVector2f((float)x, (float)y);
357 currentZoneFull = (islandsMap.find(vec) != islandsMap.end());
359 if(lastZoneFull && !currentZoneFull && vec.x-1 >= xmin)
361 rightBorders.push_back(CVector2f(vec.x-1, vec.y));
363 else if(!lastZoneFull && currentZoneFull)
365 leftBorders.push_back(vec);
367 x += 160;
368 lastZoneFull = currentZoneFull;
372 // search for bottom and top borders on columns
373 for(sint32 x = zonelXMin; x<=zonelXMax; x+=160 )
375 sint32 y=zonelYMin;
376 CVector2f vec((float)x, (float)y);
377 bool lastZoneFull = (islandsMap.find(vec) != islandsMap.end());
378 bool currentZoneFull;
380 while(y<=zonelYMax)
382 vec = CVector2f((float)x, (float)y);
383 currentZoneFull = (islandsMap.find(vec) != islandsMap.end());
385 if(lastZoneFull && !currentZoneFull && vec.y-1 >= ymin)
387 topBorders.push_back(CVector2f(vec.x, vec.y-1));
389 else if(!lastZoneFull && currentZoneFull)
391 bottomBorders.push_back(vec);
393 y += 160;
394 lastZoneFull = currentZoneFull;
398 _BorderIslands[*itIsland + "/right"] = rightBorders;
399 _BorderIslands[*itIsland + "/left"] = leftBorders;
400 _BorderIslands[*itIsland + "/bottom"] = bottomBorders;
401 _BorderIslands[*itIsland + "/top"] = topBorders;
407 //-------------------------------------------------------------------------------------------------
408 void CScreenshotIslands::attenuateIslandBorders(const std::string & islandName, CBitmap & islandBitmap,
409 const CProximityZone & islandData)
411 list< CVector2f > leftBorders, rightBorders, bottomBorders, topBorders;
412 rightBorders = _BorderIslands[islandName + "/right"];
413 leftBorders = _BorderIslands[islandName + "/left"];
414 bottomBorders = _BorderIslands[islandName + "/bottom"];
415 topBorders = _BorderIslands[islandName + "/top"];
417 sint32 xmin = islandData.getBoundXMin();
418 sint32 xmax = islandData.getBoundXMax();
419 sint32 ymin = islandData.getBoundYMin();
420 sint32 ymax = islandData.getBoundYMax();
422 sint32 width = xmax-xmin;
423 sint32 height = ymax-ymin;
424 uint8 *dest = &(islandBitmap.getPixels(0)[0]);
426 list< CVector2f >::iterator itBorder;
428 for(itBorder=leftBorders.begin(); itBorder!=leftBorders.end(); itBorder++)
430 const CVector2f initPoint = *itBorder;
431 sint32 x = (sint32)initPoint.x - xmin;
432 sint32 y = (sint32)initPoint.y - ymin;
433 sint32 maxBorder = 160;
434 if(y<0)
436 maxBorder += y;
437 y = 0;
439 sint32 inity = y;
441 while(y<(inity+maxBorder) && y<(sint32)height)
443 double noiseValue = (1+cos(((y-inity)*2*Pi)/maxBorder))*5;
444 double evalNoise = 10 + noiseValue;
445 double diffAlpha = 255/evalNoise;
447 CRGBA color = islandBitmap.getPixelColor(x, height-y-1);
448 sint32 currentX = x-1;
450 while((currentX>=x-evalNoise) && currentX>=0)
452 uint8 *pixel = &(islandBitmap.getPixels(0)[((height-y-1)*width + currentX)*4]);
453 uint alpha = (uint)(255-diffAlpha*(x-currentX));
454 uint invAlpha = 255-alpha;
456 *pixel = (uint8) (((invAlpha * *pixel) + (alpha * color.R)) >> 8);
457 *(pixel + 1) = (uint8) (((invAlpha * *(pixel + 1)) + (alpha * color.G)) >> 8);
458 *(pixel + 2) = (uint8) (((invAlpha * *(pixel + 2)) + (alpha * color.B)) >> 8);
459 *(pixel + 3) = (uint8) 255;
461 currentX--;
463 y++;
467 for(itBorder=rightBorders.begin(); itBorder!=rightBorders.end(); itBorder++)
469 const CVector2f initPoint = *itBorder;
470 sint32 x = (sint32)initPoint.x - xmin;
471 sint32 y = (sint32)initPoint.y - ymin;
472 sint32 maxBorder = 160;
473 if(y<0)
475 maxBorder += y;
476 y = 0;
478 sint32 inity = y;
480 while(y<(inity+maxBorder) && y<(sint32)height)
482 double noiseValue = (1+cos(((y-inity)*2*Pi)/maxBorder))*5;
483 double evalNoise = 10 + noiseValue;
484 double diffAlpha = 255/evalNoise;
486 CRGBA color = islandBitmap.getPixelColor(x, height-y-1);
487 sint32 currentX = x+1;
489 while((currentX<=x+evalNoise) && currentX<width)
491 uint8 *pixel = &(islandBitmap.getPixels(0)[((height-y-1)*width + currentX)*4]);
492 uint alpha = (uint)(255-diffAlpha*(currentX-x));
493 uint invAlpha = 255-alpha;
495 *pixel = (uint8) (((invAlpha * *pixel) + (alpha * color.R)) >> 8);
496 *(pixel + 1) = (uint8) (((invAlpha * *(pixel + 1)) + (alpha * color.G)) >> 8);
497 *(pixel + 2) = (uint8) (((invAlpha * *(pixel + 2)) + (alpha * color.B)) >> 8);
498 *(pixel + 3) = (uint8) 255;
500 currentX++;
503 y++;
507 for(itBorder=bottomBorders.begin(); itBorder!=bottomBorders.end(); itBorder++)
509 const CVector2f initPoint = *itBorder;
510 sint32 x = (sint32)initPoint.x - xmin;
511 sint32 y = (sint32)initPoint.y - ymin;
512 sint32 maxBorder = 160;
513 if(x<0)
515 maxBorder += x;
516 x = 0;
518 sint32 initx = x;
520 while(x<(initx+maxBorder) && x<(sint32)width)
522 double noiseValue = (1+cos(((x-initx)*2*Pi)/maxBorder))*5;
523 double evalNoise = 10 + noiseValue;
524 double diffAlpha = 255/evalNoise;
526 CRGBA color = islandBitmap.getPixelColor(x, height-y-1);
527 sint32 currentY = y-1;
529 while((currentY>=y-evalNoise) && currentY>=0)
531 uint8 *pixel = &(islandBitmap.getPixels(0)[((height-currentY-1)*width + x)*4]);
532 uint alpha = (uint)(255-diffAlpha*(y-currentY));
533 uint invAlpha = 255-alpha;
535 *pixel = (uint8) (((invAlpha * *pixel) + (alpha * color.R)) >> 8);
536 *(pixel + 1) = (uint8) (((invAlpha * *(pixel + 1)) + (alpha * color.G)) >> 8);
537 *(pixel + 2) = (uint8) (((invAlpha * *(pixel + 2)) + (alpha * color.B)) >> 8);
538 *(pixel + 3) = (uint8) 255;
540 currentY--;
543 x++;
547 for(itBorder=topBorders.begin(); itBorder!=topBorders.end(); itBorder++)
549 const CVector2f initPoint = *itBorder;
550 sint32 x = (sint32)initPoint.x - xmin;
551 sint32 y = (sint32)initPoint.y - ymin;
552 sint32 maxBorder = 160;
553 if(x<0)
555 maxBorder += x;
556 x = 0;
558 sint32 initx = x;
560 while(x<(initx+maxBorder) && x<(sint32)width)
562 double noiseValue = (1+cos(((x-initx)*2*Pi)/maxBorder))*5;
563 double evalNoise = 10 + noiseValue;
564 double diffAlpha = 255/evalNoise;
566 CRGBA color = islandBitmap.getPixelColor(x, height-y-1);
567 sint32 currentY = y+1;
569 while((currentY<=y+evalNoise) && currentY<height)
571 uint8 *pixel = &(islandBitmap.getPixels(0)[((height-currentY-1)*width + x)*4]);
572 uint alpha = (uint)(255-diffAlpha*(currentY-y));
573 uint invAlpha = 255-alpha;
575 *pixel = (uint8) (((invAlpha * *pixel) + (alpha * color.R)) >> 8);
576 *(pixel + 1) = (uint8) (((invAlpha * *(pixel + 1)) + (alpha * color.G)) >> 8);
577 *(pixel + 2) = (uint8) (((invAlpha * *(pixel + 2)) + (alpha * color.B)) >> 8);
578 *(pixel + 3) = (uint8) 255;
580 currentY++;
583 x++;
588 //-------------------------------------------------------------------------------------------------
589 void CScreenshotIslands::buildScreenshots()
591 init();
592 buildIslandsTextures();
595 //-------------------------------------------------------------------------------------------------
596 void CScreenshotIslands::writeProximityBufferToTgaFile(const std::string& fileName,const TBuffer& buffer,uint32 scanWidth,uint32 scanHeight)
598 uint imageWidth = (scanWidth); // (scanWidth+15)&~15;
599 uint imageHeight = (scanHeight);
601 CTGAImageGrey tgaImage;
602 tgaImage.setup((uint16)imageWidth, (uint16)imageHeight, fileName, 0, 0);
603 for (uint32 y=0;y<scanHeight;++y)
605 for (uint32 x=0; x<scanWidth; ++x)
607 uint32 value= buffer[y*scanWidth+x];
608 tgaImage.set(x, (value>255*5)?255:value/5);
610 tgaImage.writeLine();
614 //-------------------------------------------------------------------------------------------------
615 void CScreenshotIslands::processProximityBuffer(TBuffer & inputBuffer, uint32 lineLength, TBuffer& resultBuffer)
617 // a couple of constants to control the range over which our degressive filter is to be applied
618 const uint32 smallValue= 2*5;
620 float a = 5*((255.0 - limitValue)/(float(100-BigValue)));
621 float b = (float)(limitValue*5 - a*BigValue);
623 // determine numer of lines in the buffer...
624 uint32 numLines= inputBuffer.size()/ lineLength;
626 // clear out the result buffer and reset all values to 5*255, remembering that this is the correct value for the image edges
627 resultBuffer.clear();
628 resultBuffer.resize(inputBuffer.size(),(TBufferEntry)5*255);
630 for (uint32 y=1;y<numLines-1;++y)
632 uint32 lineOffset= y* lineLength;
633 for (uint32 x=1;x<lineLength-1;++x)
635 uint32 offset= lineOffset+x;
637 uint32 value=(uint32)inputBuffer[offset];
639 // apply a clip and cosine function
641 if (value<smallValue) value=0;
642 else if (value>BigValue) value=5*255;
643 else value= (uint32)(((1.0-cos(Pi*(float(value-smallValue)/(float)(BigValue-smallValue))))/2.0)*float(5*255));
646 if (value==ValueBorder);
647 else if ((value>=0) && (value<smallValue)) value=0;
648 else if (value>BigValue)
650 if(value==InteriorValue)
652 value = (uint)(5*limitValue);
654 else
656 value = (uint)(a*value+b);
657 if(value > 5*255) value = 5*255;
660 else if((value>=smallValue) && (value<=BigValue))
662 value= (uint32)(((1.0-cos(Pi*(float(value-smallValue)/(float)(BigValue-smallValue))))/2.0)*float(5*limitValue));
665 // store the value into the result buffer
666 resultBuffer[offset]= (TBufferEntry)value;
670 // modify inputBuffer to store "bigValue" limit
671 for (uint32 y=1;y<numLines-1;++y)
673 uint32 lineOffset= y* lineLength;
674 for (uint32 x=1;x<lineLength-1;++x)
676 uint32 offset= lineOffset+x;
678 uint32 value=(uint32)inputBuffer[offset];
680 if(value==BigValue) value = 255*5;
681 else value = 0;
683 inputBuffer[offset]= (TBufferEntry)value;
688 // lines
689 for (uint32 y=0;y<numLines;++y)
691 uint32 lineOffset= y* lineLength;
693 for (uint32 x=0;x<lineLength;++x)
695 uint32 offset = lineOffset+x;
696 uint16 value = resultBuffer[offset];
698 if(value==ValueBorder)
700 resultBuffer[offset]= (TBufferEntry)(5*limitValue);
702 // increase x
703 if(x+1<lineLength-1)
705 uint16 nextValue = resultBuffer[offset+1];
706 uint16 prevValue = (TBufferEntry)(5*limitValue);
707 if(nextValue!=(TBufferEntry)(5*limitValue))
709 uint32 count = x+1;
710 while((count<x+7) && (count<lineLength-1) && ((nextValue=resultBuffer[lineOffset+count])!=0)
711 && nextValue!=(TBufferEntry)(5*limitValue) && (nextValue!=ValueBorder))
713 uint16 newValue = (TBufferEntry)(prevValue+5*7);
714 if(newValue < nextValue)
716 resultBuffer[lineOffset+count] = newValue;
719 prevValue = newValue;
720 count++;
723 x = count-1;
724 break;
728 // decrease x
729 if(x-1>0)
731 uint16 nextValue = resultBuffer[offset-1];
732 uint16 prevValue = (TBufferEntry)(5*limitValue);
733 if(nextValue!=(TBufferEntry)(5*limitValue))
735 uint32 count = x-1;
736 while((count>x-7) && (count>0) && ((nextValue=resultBuffer[lineOffset+count])!=0)
737 && nextValue!=(TBufferEntry)(5*limitValue) && (nextValue!=ValueBorder))
739 uint16 newValue = (TBufferEntry)(prevValue+5*7);
740 if(newValue < nextValue)
742 resultBuffer[lineOffset+count] = newValue;
745 prevValue = newValue;
746 count--;
754 //columns
755 for (uint32 x=0;x<lineLength;++x)
757 // setup start and end offsets marking first and last 'zero' value pixels in the column
758 uint32 startOffset=x, endOffset=x+(numLines-1)*lineLength;
760 for (uint32 offset=startOffset; offset<=endOffset; offset+=lineLength)
762 uint16 value = resultBuffer[offset];
764 if(value==ValueBorder)
766 resultBuffer[offset]= (TBufferEntry)(5*limitValue);
768 // increase y
769 if(offset+lineLength<=endOffset)
771 uint16 nextValue = resultBuffer[offset+lineLength];
772 uint16 prevValue = (TBufferEntry)(5*limitValue);
773 if(nextValue!=(TBufferEntry)(5*limitValue))
775 uint32 count = offset+lineLength;
776 while((count<offset+7*lineLength) && (count<=endOffset) &&
777 ((nextValue=resultBuffer[count])!=0) && nextValue!=(TBufferEntry)(5*limitValue) && (nextValue!=ValueBorder))
779 uint16 newValue = (TBufferEntry)(prevValue+5*7);
780 if(newValue < nextValue)
782 resultBuffer[count] = newValue;
785 prevValue = newValue;
786 count += lineLength;
789 offset = count-lineLength;
790 break;
794 // decrease y
795 if(offset-lineLength>=startOffset)
797 uint16 nextValue = resultBuffer[offset-lineLength];
798 uint16 prevValue = (TBufferEntry)(5*limitValue);
799 if(nextValue!=(TBufferEntry)(5*limitValue))
801 uint32 count = offset-lineLength;
802 while((count>offset-7*lineLength) && (count>=startOffset) &&
803 ((nextValue=resultBuffer[count])!=0) && nextValue!=(TBufferEntry)(5*limitValue) && (nextValue!=ValueBorder))
805 uint16 newValue = (TBufferEntry)(prevValue+5*7);
806 if(newValue < nextValue)
808 resultBuffer[count] = newValue;
811 prevValue = newValue;
812 count -= lineLength;
821 //-----------------------------------------------------------------------------
822 // search for pixels of borders
823 map< CVector2f, bool > bordersPixels;
824 for (uint32 y=0;y<numLines;++y)
826 uint32 lineOffset= y* lineLength;
828 for (uint32 x=0;x<lineLength;++x)
830 uint32 offset = lineOffset+x;
831 uint16 value = resultBuffer[offset];
833 if(value==ValueBorder)
835 bordersPixels[CVector2f((float)x, (float)y)] = true;
836 resultBuffer[offset] = (TBufferEntry)(5*limitValue);
841 //-----------------------------------------------------------------------------
842 // search for global borders
843 // search on lines for top and bottom borders
844 map< CVector2f, uint32 > leftBorders, rightBorders, bottomBorders, topBorders;
845 for (uint32 y=0;y<numLines;++y)
847 uint32 lineOffset= y* lineLength;
849 bool lastValue = false;
850 CVector2f firstPixelBorder;
851 firstPixelBorder.set(0.f, 0.f);
852 uint32 nbPixelsBorder = 0;
854 for (uint32 x=0;x<lineLength;++x)
856 bool value = (bordersPixels.find(CVector2f((float)x, (float)y))!=bordersPixels.end());
858 if(value)
860 if(!lastValue)
862 firstPixelBorder = CVector2f((float)x, (float)y);
864 nbPixelsBorder ++;
866 else if(lastValue)
868 // store border line
869 if(nbPixelsBorder !=1) //column instead of line
871 bool top=false, bottom=false;
872 if(y>0)
874 for(uint32 xc=(uint32)firstPixelBorder.x; xc<(uint32)(firstPixelBorder.x+nbPixelsBorder); xc++)
876 if(resultBuffer[(y-1)*lineLength+xc]==(TBufferEntry)(5*255))
878 bottom = true;
879 break;
884 if(y+1<numLines)
886 for(uint32 xc=(uint32)firstPixelBorder.x; xc<(uint32)(firstPixelBorder.x+nbPixelsBorder); xc++)
888 if(resultBuffer[(y+1)*lineLength+xc]==(TBufferEntry)(5*255))
890 top = true;
891 break;
896 // TOP
897 if(top)
898 topBorders[firstPixelBorder] =nbPixelsBorder;
900 // BOTTOM
901 if(bottom)
902 bottomBorders[firstPixelBorder] =nbPixelsBorder;
904 nbPixelsBorder =0;
906 lastValue = value;
910 // search on columns for left and right borders
911 for (uint32 x=0;x<lineLength;++x)
913 bool lastValue = false;
914 CVector2f firstPixelBorder;
915 firstPixelBorder.set(0.f, 0.f);
916 uint32 nbPixelsBorder = 0;
918 for(uint32 y=0; y<numLines; y++)
920 bool value = (bordersPixels.find(CVector2f((float)x, (float)y))!=bordersPixels.end());
922 if(value)
924 if(!lastValue)
926 firstPixelBorder = CVector2f((float)x, (float)y);
928 nbPixelsBorder ++;
930 else if(lastValue)
932 // store border line
933 if(nbPixelsBorder !=1) //line instead of column
935 bool left = false;
936 bool right = false;
938 if(x>0)
940 for(uint32 yc=(uint32)firstPixelBorder.y; yc<(uint32)(firstPixelBorder.y+nbPixelsBorder); yc++)
942 if(resultBuffer[yc*lineLength+x-1]==(TBufferEntry)(5*255))
944 left = true;
945 break;
950 if(x+1<lineLength)
952 for(uint32 yc=(uint32)firstPixelBorder.y; yc<(uint32)(firstPixelBorder.y+nbPixelsBorder); yc++)
954 if(resultBuffer[yc*lineLength+x+1]==(TBufferEntry)(5*255))
956 right = true;
957 break;
962 // LEFT
963 if(left)
964 leftBorders[firstPixelBorder] =nbPixelsBorder;
966 // RIGHT
967 if(right)
968 rightBorders[firstPixelBorder] =nbPixelsBorder;
970 nbPixelsBorder =0;
972 lastValue = value;
976 //-----------------------------------------------------------------------------
977 // attenuate borders
978 // bottom and top borders (lines)
979 for (uint32 y=0;y<numLines;++y)
981 uint32 lineOffset= y* lineLength;
983 for (uint32 x=0;x<lineLength;++x)
985 uint32 offset = lineOffset+x;
986 CVector2f firstPixelBorder((float)x, (float)y);
988 // BOTTOM
989 if(bottomBorders.find(CVector2f((float)x, (float)y))!=bottomBorders.end())
991 uint32 nbPixelsBorder = bottomBorders[firstPixelBorder];
992 uint32 xc;
993 for(xc=(uint32)firstPixelBorder.x; xc<(uint32)(firstPixelBorder.x+nbPixelsBorder); xc++)
995 uint nbZones = (uint)(nbPixelsBorder/160)+1;
996 uint period = (uint)(nbPixelsBorder/(2*nbZones));
997 double noiseValue = (1-cos(((xc-firstPixelBorder.x)*Pi)/period))*5;
998 double evalNoise = 7 + noiseValue;
999 double diffAlpha = (255.0-limitValue)/evalNoise;
1001 uint32 yc = y-1;
1002 while((yc>=(uint32)(y-evalNoise)) && yc>=0)
1004 uint16 newValue = (TBufferEntry)(5*(limitValue + diffAlpha*(y-yc)));
1005 uint16 currentValue = (TBufferEntry)resultBuffer[yc*lineLength+xc];
1006 if(newValue<currentValue)
1007 resultBuffer[yc*lineLength+xc] = (TBufferEntry)(5*(limitValue + diffAlpha*(y-yc)));
1008 yc--;
1011 x=xc;
1014 // TOP
1015 if(topBorders.find(CVector2f((float)x, (float)y))!=topBorders.end())
1017 uint32 nbPixelsBorder = topBorders[firstPixelBorder];
1018 uint32 xc;
1019 for(xc=(uint32)firstPixelBorder.x; xc<=(uint32)(firstPixelBorder.x+nbPixelsBorder); xc++)
1021 uint nbZones = (uint)(nbPixelsBorder/160)+1;
1022 uint period = (uint)(nbPixelsBorder/(2*nbZones));
1023 double noiseValue = (1-cos(((xc-firstPixelBorder.x)*Pi)/period))*5;
1024 double evalNoise = 7 + noiseValue;
1025 double diffAlpha = (255.0-limitValue)/evalNoise;
1027 uint32 yc = y+1;
1028 while((yc<=(uint32)(y+evalNoise)) && yc<numLines)
1030 uint16 newValue = (TBufferEntry)(5*(limitValue + diffAlpha*(yc-y)));
1031 uint16 currentValue = (TBufferEntry)resultBuffer[yc*lineLength+xc];
1032 if(newValue<currentValue)
1033 resultBuffer[yc*lineLength+xc] = (TBufferEntry)(5*(limitValue + diffAlpha*(yc-y)));
1034 yc++;
1037 x=xc;
1042 // left and right borders (columns)
1043 for (uint32 x=0;x<lineLength;++x)
1045 for (uint32 y=0; y<numLines; y++)
1047 CVector2f firstPixelBorder((float)x, (float)y);
1049 // LEFT
1050 if(leftBorders.find(CVector2f((float)x, (float)y))!=leftBorders.end())
1052 uint32 nbPixelsBorder = leftBorders[firstPixelBorder];
1053 uint32 yc;
1054 for(yc=(uint32)firstPixelBorder.y; yc<(uint32)(firstPixelBorder.y+nbPixelsBorder); yc++)
1056 uint nbZones = (uint)(nbPixelsBorder/160)+1;
1057 uint period = (uint)(nbPixelsBorder/(2*nbZones));
1058 double noiseValue = (1-cos(((yc-firstPixelBorder.y)*Pi)/period))*5;
1059 double evalNoise = 7 + noiseValue;
1060 double diffAlpha = (255.0-limitValue)/evalNoise;
1062 uint32 xc = x-1;
1063 while((xc>=(uint32)(x-evalNoise)) && xc>=0)
1065 uint16 newValue = (TBufferEntry)(5*(limitValue + diffAlpha*(x-xc)));
1066 uint16 currentValue = (TBufferEntry)resultBuffer[yc*lineLength+xc];
1067 if(newValue<currentValue)
1068 resultBuffer[yc*lineLength+xc] = (TBufferEntry)(5*(limitValue + diffAlpha*(x-xc)));
1069 xc--;
1072 y=yc;
1075 // RIGHT
1076 if(rightBorders.find(CVector2f((float)x, (float)y))!=rightBorders.end())
1078 uint32 nbPixelsBorder = rightBorders[firstPixelBorder];
1079 uint32 yc;
1080 for(yc=(uint32)firstPixelBorder.y; yc<=(uint32)(firstPixelBorder.y+nbPixelsBorder); yc++)
1082 uint nbZones = (uint)(nbPixelsBorder/160)+1;
1083 uint period = (uint)(nbPixelsBorder/(2*nbZones));
1084 double noiseValue = (1-cos(((yc-firstPixelBorder.y)*Pi)/period))*5;
1085 double evalNoise = 7 + noiseValue;
1086 double diffAlpha = (255.0-limitValue)/evalNoise;
1088 uint32 xc = x+1;
1089 while((xc<=(uint32)(x+evalNoise)) && xc<lineLength)
1091 uint16 newValue = (TBufferEntry)(5*(limitValue + diffAlpha*(xc-x)));
1092 uint16 currentValue = (TBufferEntry)resultBuffer[yc*lineLength+xc];
1093 if(newValue<currentValue)
1094 resultBuffer[yc*lineLength+xc] = (TBufferEntry)(5*(limitValue + diffAlpha*(xc-x)));
1095 xc++;
1098 y=yc;
1104 //--------------------------------------------------------------------------------
1105 void CScreenshotIslands::loadIslands()
1107 // load entryPoints
1108 map< string, CVector2f > islands;
1109 CScenarioEntryPoints scenarioEntryPoints = CScenarioEntryPoints::getInstance();
1111 scenarioEntryPoints.loadFromFile();
1112 const CScenarioEntryPoints::TEntryPoints& entryPoints = scenarioEntryPoints.getEntryPoints();
1114 CScenarioEntryPoints::TEntryPoints::const_iterator entry(entryPoints.begin()), entryPoint(entryPoints.end());
1115 for( ; entry != entryPoint ; ++entry)
1117 islands[entry->Island] = CVector2f((float)entry->X, (float)entry->Y);
1120 // search islands of each continent
1121 map< string, CVector2f >::iterator itIsland(islands.begin()), lastIsland(islands.end());
1122 for( ; itIsland != lastIsland ; ++itIsland)
1124 const CVector2f & entryPoint = itIsland->second;
1126 // search continent of this island
1127 TContinentsData::iterator itCont(_ContinentsData.begin()), lastCont(_ContinentsData.end());
1128 for( ; itCont != lastCont ; ++itCont)
1130 CContinentData & continent = itCont->second;
1131 CVector2f zoneMax = continent.ZoneMax;
1132 CVector2f zoneMin = continent.ZoneMin;
1134 if((zoneMin.x <= entryPoint.x) && (entryPoint.x <= zoneMax.x)
1135 && (zoneMax.y <= entryPoint.y) && (entryPoint.y <= zoneMin.y))
1137 continent.Islands.push_back(itIsland->first);
1138 break;
1143 // search data of each island
1144 TContinentsData::iterator itCont(_ContinentsData.begin()), lastCont(_ContinentsData.end());
1145 for( ; itCont != lastCont ; ++itCont)
1147 string aiFileName = itCont->first+"_0.cwmap2";
1149 const CContinentData & continent = itCont->second;
1151 CProximityMapBuffer continentBuffer;
1152 continentBuffer.load(CPath::lookup(aiFileName));
1154 CProximityMapBuffer::TZones zones;
1155 continentBuffer.calculateZones(zones);
1157 for (uint32 i=0;i<zones.size();++i)
1159 TBuffer zoneBuffer;
1160 continentBuffer.generateZoneProximityMap(zones[i], zoneBuffer);
1162 TBuffer cleanBuffer;
1163 processProximityBuffer(zoneBuffer, zones[i].getZoneWidth(), cleanBuffer);
1165 string fileName;
1166 list< string >::const_iterator itIsland(continent.Islands.begin()), lastIsland(continent.Islands.end());
1167 for( ; itIsland != lastIsland ; ++itIsland)
1169 const CVector2f & entryPoint = islands[*itIsland];
1170 sint32 xmin = zones[i].getBoundXMin();
1171 sint32 xmax = zones[i].getBoundXMax();
1172 sint32 ymin = zones[i].getBoundYMin();
1173 sint32 ymax = zones[i].getBoundYMax();
1175 if((xmin <= entryPoint.x) && (entryPoint.x <= xmax)
1176 && (ymin <= entryPoint.y) && (entryPoint.y <= ymax))
1178 fileName = _OutDirectory + "/" + *itIsland + "_prox.tga";
1179 _IslandsData[*itIsland] = zones[i];
1180 break;
1184 // write the processed proximity map to an output file
1185 if(!fileName.empty())
1187 writeProximityBufferToTgaFile(fileName, cleanBuffer, zones[i].getZoneWidth(), zones[i].getZoneHeight());
1188 _TempFileNames.push_back(fileName);
1190 fileName = _OutDirectory + "/" + *itIsland + "_limit.tga";
1191 writeProximityBufferToTgaFile(fileName, zoneBuffer, zones[i].getZoneWidth(), zones[i].getZoneHeight());
1192 _TempFileNames.push_back(fileName);
1194 else
1196 nlinfo("Zone of island not found, tga not build");
1202 CScenarioEntryPoints::TCompleteIslands completeIslands(entryPoints.size());
1204 uint completeIslandsNb = 0;
1205 for(uint e=0; e<entryPoints.size(); e++)
1207 const CScenarioEntryPoints::CEntryPoint & entry = entryPoints[e];
1209 CScenarioEntryPoints::CCompleteIsland completeIsland;
1210 completeIsland.Island = entry.Island;
1211 completeIsland.Package = entry.Package;
1213 for(itCont=_ContinentsData.begin(); itCont!=_ContinentsData.end(); ++itCont)
1215 list< string >::const_iterator itIsland(itCont->second.Islands.begin()), lastIsland(itCont->second.Islands.end());
1216 for( ; itIsland != lastIsland ; ++itIsland)
1218 if(*itIsland == entry.Island)
1220 completeIsland.Continent = CSString(itCont->first);
1221 if(_IslandsData.find(entry.Island)!=_IslandsData.end())
1223 completeIsland.XMin = _IslandsData[entry.Island].getBoundXMin();
1224 completeIsland.YMin = _IslandsData[entry.Island].getBoundYMin();
1225 completeIsland.XMax = _IslandsData[entry.Island].getBoundXMax();
1226 completeIsland.YMax = _IslandsData[entry.Island].getBoundYMax();
1227 completeIslands[completeIslandsNb] = completeIsland;
1228 completeIslandsNb++;
1230 break;
1235 completeIslands.resize(completeIslandsNb);
1237 CScenarioEntryPoints::getInstance().saveXMLFile(completeIslands, _CompleteIslandsFile);
1240 //--------------------------------------------------------------------------------
1242 void CScreenshotIslands::getBuffer(UScene * scene, ULandscape * landscape, CBitmap &btm)
1245 if (ScreenShotWidth && ScreenShotHeight)
1247 UCamera camera = scene->getCam();
1249 // Destination image
1250 CBitmap dest;
1251 btm.resize(ScreenShotWidth, ScreenShotHeight, CBitmap::RGBA);
1253 uint windowWidth = driver->getWindowWidth();
1254 uint windowHeight = driver->getWindowHeight();
1256 uint top;
1257 uint bottom = min(windowHeight, ScreenShotHeight);
1258 for (top=0; top<ScreenShotHeight; top+=windowHeight)
1260 uint left;
1261 uint right = std::min(windowWidth, ScreenShotWidth);
1262 for(left=0; left<ScreenShotWidth; left+=windowWidth)
1264 driver->clearBuffers(_BackColor);
1266 // store initial frustum and viewport
1267 CFrustum Frustum = scene->getCam().getFrustum();
1268 CViewport Viewport = scene->getViewport();
1270 // Build a new frustum
1271 CFrustum frustumPart;
1272 frustumPart.Left = Frustum.Left+(Frustum.Right-Frustum.Left)*((float)left/(float)ScreenShotWidth);
1273 frustumPart.Right = Frustum.Left+(Frustum.Right-Frustum.Left)*((float)right/(float)ScreenShotWidth);
1274 frustumPart.Top = ceil(Frustum.Top+(Frustum.Bottom-Frustum.Top)*((float)top/(float)ScreenShotHeight));
1275 frustumPart.Bottom = ceil(Frustum.Top+(Frustum.Bottom-Frustum.Top)*((float)bottom/(float)ScreenShotHeight));
1276 frustumPart.Near = Frustum.Near;
1277 frustumPart.Far = Frustum.Far;
1278 frustumPart.Perspective = Frustum.Perspective;
1280 // Build a new viewport
1281 CViewport viewport;
1282 viewport.init(0, 0, (float)(right-left)/windowWidth,
1283 (float)(bottom-top)/windowHeight);
1285 // Activate all this
1286 scene->getCam().setFrustum(frustumPart);
1287 scene->setViewport(viewport);
1289 scene->setMaxSkeletonsInNotCLodForm(1000000);
1290 scene->setPolygonBalancingMode(UScene::PolygonBalancingOff);
1292 if(_InverseZTest)
1294 // render scene with inversed ZBuffer test (keep greater distances)
1295 driver->setColorMask(false, false, false, false);
1296 sceneMaterial.setZFunc(UMaterial::less);
1298 // initialize ZBuffer with leak value
1299 driver->setMatrixMode2D11();
1300 CQuad quad;
1301 quad.V0 = CVector(0.0, 0.0, 0.0);
1302 quad.V1 = CVector(1.0, 0.0, 0.0);
1303 quad.V2 = CVector(1.0, 1.0, 0.0);
1304 quad.V3 = CVector(0.0, 1.0, 0.0);
1306 driver->drawQuad(quad, sceneMaterial);
1307 driver->setMatrixMode3D(camera);
1308 driver->setColorMask(true, true, true, true);
1310 scene->enableElementRender(UScene::FilterWater, false);
1313 scene->render();
1315 // display vegetables with normal ZBuffer test
1316 if(_InverseZTest && _Vegetation)
1318 scene->enableElementRender(UScene::FilterWater, false);
1319 scene->enableElementRender(UScene::FilterLandscape, false);
1320 scene->enableElementRender(UScene::FilterWater, true);
1321 scene->render();
1322 scene->enableElementRender(UScene::FilterLandscape, true);
1325 // Get the bitmap
1326 driver->getBuffer(dest);
1327 btm.blit(dest, 0, windowHeight-(bottom-top), right-left, bottom-top, left, top);
1329 // restore camera
1330 scene->getCam().setFrustum(Frustum);
1331 scene->setViewport(Viewport);
1333 driver->flush();
1334 driver->swapBuffers();
1336 // Next
1337 right = std::min(right+windowWidth, ScreenShotWidth);
1340 // Next
1341 bottom = std::min(bottom+windowHeight, ScreenShotHeight);
1344 else
1346 driver->getBuffer(btm);
1352 void CScreenshotIslands::buildIslandsTextures()
1354 int loop = 0;
1355 int maxLoop = 6;
1357 // Create the window with config file values
1358 driver->setDisplay(UDriver::CMode(512, 512, 32, true));
1360 // Create a scene
1361 UScene * scene = driver->createScene(true);
1362 scene->animate(CTime::ticksToSecond(CTime::getPerformanceTime()));
1363 scene->setMaxSkeletonsInNotCLodForm(1000000);
1364 scene->setPolygonBalancingMode(UScene::PolygonBalancingOff);
1366 // Create a camera
1367 UCamera camera = scene->getCam();
1368 camera.setTransformMode(UTransformable::DirectMatrix);
1370 // Create and load landscape
1371 ULandscape * landscape = scene->createLandscape();
1372 landscape->setThreshold(0.0005);
1373 landscape->setTileNear(10000);
1374 if(_InverseZTest)
1376 landscape->setZFunc(UMaterial::greaterequal);
1378 else
1380 landscape->setZFunc(UMaterial::less);
1383 //Iteration on seasons
1384 list< string >::iterator itSeason(_SeasonSuffixes.begin()), lastSeason(_SeasonSuffixes.end());
1385 for( ; itSeason != lastSeason ; ++itSeason)
1387 string seasonSuffix = *itSeason;
1389 sint season = -1;
1391 if (seasonSuffix == "_sp") season = CSeason::Spring;
1392 else if (seasonSuffix == "_su") season = CSeason::Summer;
1393 else if (seasonSuffix == "_au") season = CSeason::Autumn;
1394 else if (seasonSuffix == "_wi") season = CSeason::Winter;
1396 if (season == -1)
1398 nlwarning("Unknown season suffix %s, skipping...", seasonSuffix.c_str());
1399 continue;
1402 // Iterations on Continents
1403 TContinentsData::iterator itCont(_ContinentsData.begin()), lastCont(_ContinentsData.end());
1404 for( ; itCont != lastCont ; ++itCont)
1406 const CContinentData & continent = itCont->second;
1408 // Light init
1409 landscape->setupStaticLight(continent.Diffuse, continent.Ambiant, 1.0f);
1411 string coarseMeshFile = continent.CoarseMeshMap;
1412 string coarseMeshWithoutExt = CFile::getFilenameWithoutExtension(coarseMeshFile);
1413 string coarseMeshExt = CFile::getExtension(coarseMeshFile);
1414 coarseMeshFile = coarseMeshWithoutExt + seasonSuffix + "." + coarseMeshExt;
1415 nldebug("Coarse mesh texture: '%s'", coarseMeshFile.c_str());
1416 scene->setCoarseMeshManagerTexture(coarseMeshFile.c_str());
1418 // Load the landscape
1419 string farBank = continent.FarBank;
1420 string farBankWithoutExt = CFile::getFilenameWithoutExtension(farBank);
1421 string farBankExt = CFile::getExtension(farBank);
1422 farBank = farBankWithoutExt + seasonSuffix + "." + farBankExt;
1423 landscape->loadBankFiles(continent.SmallBank, farBank);
1425 // Load vegatables
1426 LandscapeIGManager.initIG(scene, continent.IGFile, driver, season, NULL);
1428 // Iterations on Islands
1429 list< string >::const_iterator itIsland(continent.Islands.begin()), lastIsland(continent.Islands.end());
1430 for( ; itIsland != lastIsland ; ++itIsland)
1432 loop = 0;
1434 if(_IslandsData.find(itIsland->c_str()) != _IslandsData.end())
1436 const CProximityZone & islandData = _IslandsData[itIsland->c_str()];
1438 sint32 xmin = islandData.getBoundXMin();
1439 sint32 xmax = islandData.getBoundXMax();
1440 sint32 ymin = islandData.getBoundYMin();
1441 sint32 ymax = islandData.getBoundYMax();
1443 float width = (float)(xmax-xmin);
1444 float height = (float)(ymax-ymin);
1445 ScreenShotWidth = (uint)(width*_MeterPixelSize);
1446 ScreenShotHeight = (uint)(height*_MeterPixelSize);
1448 // position in island center
1449 float posx = ((float)(xmax+xmin))/2;
1450 float posy = ((float)(ymax+ymin))/2;
1451 CVector entryPos(posx, posy, 400);
1453 // Setup camera
1454 CMatrix startCamMatrix;
1455 startCamMatrix.setPos(entryPos);
1456 startCamMatrix.rotateX(-(float)Pi/2);
1457 camera.setMatrix(startCamMatrix);
1458 camera.setFrustum(width, height, -10000.0f, 10000.0f, false);
1460 // init lanscape
1461 landscape->postfixTileFilename(seasonSuffix.c_str());
1463 while(loop<maxLoop)
1465 // refresh lanscape
1466 vector<string> zonesAdded;
1467 vector<string> zonesRemoved;
1468 IProgressCallback progress;
1469 landscape->refreshAllZonesAround(camera.getMatrix().getPos(), 2000, zonesAdded, zonesRemoved, progress);
1470 if(_Vegetation)
1472 LandscapeIGManager.unloadArrayZoneIG(zonesRemoved);
1473 LandscapeIGManager.loadArrayZoneIG(zonesAdded);
1475 vector<string>::iterator itName(zonesAdded.begin()), lastName(zonesAdded.end());
1476 for( ; itName != lastName ; ++itName)
1478 UInstanceGroup * instanceGr = LandscapeIGManager.getIG(*itName);
1479 if(instanceGr)
1481 uint nbInst = instanceGr->getNumInstance();
1482 for(uint i=0; i<instanceGr->getNumInstance(); i++)
1484 instanceGr->setCoarseMeshDist(i, 100000);
1485 instanceGr->setDistMax(i, 100000);
1490 scene->animate(CTime::ticksToSecond(CTime::getPerformanceTime()));
1492 // Clear all buffers
1493 driver->clearBuffers(_BackColor);
1495 if(_InverseZTest)
1497 // render scene with inversed ZBuffer test (keep greater distances)
1498 driver->setColorMask(false, false, false, false);
1499 sceneMaterial.setZFunc(UMaterial::less);
1501 // initialize ZBuffer with leak value
1502 driver->setMatrixMode2D11();
1503 CQuad quad;
1504 quad.V0 = CVector(0.0, 0.0, 0.0);
1505 quad.V1 = CVector(1.0, 0.0, 0.0);
1506 quad.V2 = CVector(1.0, 1.0, 0.0);
1507 quad.V3 = CVector(0.0, 1.0, 0.0);
1509 driver->drawQuad(quad, sceneMaterial);
1510 driver->setMatrixMode3D(camera);
1511 driver->setColorMask(true, true, true, true);
1513 scene->enableElementRender(UScene::FilterWater, false);
1516 scene->render();
1518 // display vegetables with normal ZBuffer test
1519 if(_InverseZTest && _Vegetation)
1521 scene->enableElementRender(UScene::FilterLandscape, false);
1522 scene->enableElementRender(UScene::FilterWater, true);
1523 scene->render();
1524 scene->enableElementRender(UScene::FilterLandscape, true);
1527 // Swap 3d buffers
1528 driver->flush();
1529 driver->swapBuffers();
1531 // Pump user input messages
1532 driver->EventServer.pump();
1534 loop += 1;
1536 // Screenshot
1537 if(loop==maxLoop-1)
1539 CBitmap islandBitmap;
1540 getBuffer(scene, landscape, islandBitmap);
1542 buildBackTextureHLS(*itIsland, islandBitmap);
1544 if(loop==maxLoop)
1546 // create srcennshot bitmap of full island
1547 CBitmap islandBitmap;
1548 getBuffer(scene, landscape, islandBitmap);
1550 attenuateIslandBorders(*itIsland, islandBitmap, islandData);
1552 // load proximity bitmap
1553 CBitmap proxBitmap;
1554 std::string proxFileName = _OutDirectory + "/" + *itIsland + "_prox.tga";
1555 CIFile proxFS(proxFileName.c_str());
1556 proxBitmap.load(proxFS);
1558 // resize proximity bitmap
1559 CBitmap tempBitmap;
1560 int newWidth = islandBitmap.getWidth();
1561 int newHeight = islandBitmap.getHeight();
1562 tempBitmap.resize(newWidth, newHeight, islandBitmap.PixelFormat);
1563 // blit src bitmap
1564 //tempBitmap.blit(proxBitmap, 0, 0, newWidth, newHeight, 0, 0);
1566 const uint8 *prox = &(proxBitmap.getPixels(0)[0]);
1567 uint8 *temp = &(tempBitmap.getPixels(0)[0]);
1568 for (uint y = 0; y < newHeight; ++y)
1569 for (uint x = 0; x < newWidth; ++x)
1571 uint ys = (y * proxBitmap.getHeight()) / newHeight;
1572 uint xs = (x * proxBitmap.getWidth()) / newWidth;
1573 uint addr = ((y * newWidth) + x) * 4;
1574 uint addrs = ((ys * proxBitmap.getWidth()) + xs) * 4;
1575 temp[addr] = prox[addrs];
1576 temp[addr+1] = prox[addrs+1];
1577 temp[addr+2] = prox[addrs+2];
1578 temp[addr+3] = prox[addrs+3];
1582 // swap them
1583 proxBitmap.resize(newWidth, newHeight, proxBitmap.PixelFormat);
1584 proxBitmap.swap(tempBitmap);
1586 //proxBitmap.resample(newWidth, newHeight);
1589 // create final bitmap
1590 CBitmap bitmapDest;
1591 bitmapDest.resize(islandBitmap.getWidth(), islandBitmap.getHeight(), islandBitmap.PixelFormat);
1594 // mix black and full island bitmaps with blend factor of proximity bitmap pixels
1595 uint numPix = islandBitmap.getWidth() * islandBitmap.getHeight();
1597 const uint8 *alphaProx = &(proxBitmap.getPixels(0)[0]);
1598 const uint8 *srcIsland = &(islandBitmap.getPixels(0)[0]);
1599 uint8 *dest = &(bitmapDest.getPixels(0)[0]);
1602 const uint8 *srcBack = &(_BackBitmap.getPixels(0)[0]);
1604 uint8 *endDest = dest + (numPix << 2);
1608 uint invblendFact = (uint) alphaProx[0];
1609 uint blendFact = 256 - invblendFact;
1611 // blend 4 component at each pass
1612 *dest = (uint8) (((blendFact * *srcIsland) + (invblendFact * *srcBack)) >> 8);
1613 *(dest + 1) = (uint8) (((blendFact * *(srcIsland + 1)) + (invblendFact * *(srcBack + 1))) >> 8);
1614 *(dest + 2) = (uint8) (((blendFact * *(srcIsland + 2)) + (invblendFact * *(srcBack + 2))) >> 8);
1615 *(dest + 3) = (uint8) 255;
1617 alphaProx = alphaProx + 4;
1618 srcIsland = srcIsland + 4;
1619 dest = dest + 4;
1621 srcBack = srcBack + 4;
1623 while(dest != endDest);
1626 // create tga file of avoidable place in island
1627 string textureName = _OutDirectory + "/" + *itIsland + seasonSuffix + ".tga";
1629 CBitmap bitmapLittle;
1630 bitmapLittle.resize(bitmapDest.getWidth(), bitmapDest.getHeight(), bitmapDest.PixelFormat);
1631 bitmapLittle = bitmapDest;
1632 if(!isPowerOf2(bitmapDest.getWidth()) || !isPowerOf2(bitmapDest.getHeight()) )
1634 uint pow2w = NLMISC::raiseToNextPowerOf2(bitmapDest.getWidth());
1635 uint pow2h = NLMISC::raiseToNextPowerOf2(bitmapDest.getHeight());
1636 CBitmap enlargedBitmap;
1637 enlargedBitmap.resize(pow2w, pow2h, bitmapDest.PixelFormat);
1638 // blit src bitmap
1639 enlargedBitmap.blit(&bitmapDest, 0, 0);
1640 // swap them
1641 bitmapDest.swap(enlargedBitmap);
1644 COFile fsDest(textureName.c_str());
1645 bitmapDest.writeTGA(fsDest,32);
1648 // little tga
1649 bitmapLittle.resample(bitmapLittle.getWidth() / 20, bitmapLittle.getHeight() / 20);
1650 if (!isPowerOf2(bitmapLittle.getWidth()) || !isPowerOf2(bitmapLittle.getHeight()))
1652 uint pow2w = NLMISC::raiseToNextPowerOf2(bitmapLittle.getWidth());
1653 uint pow2h = NLMISC::raiseToNextPowerOf2(bitmapLittle.getHeight());
1654 CBitmap enlargedBitmap;
1655 enlargedBitmap.resize(pow2w, pow2h, bitmapLittle.PixelFormat);
1656 // blit src bitmap
1657 enlargedBitmap.blit(&bitmapLittle, 0, 0);
1658 // swap them
1659 bitmapLittle.swap(enlargedBitmap);
1662 textureName = _OutDirectory + "/" + *itIsland + seasonSuffix + "_little.tga";
1663 COFile fsLittle(textureName.c_str());
1664 bitmapLittle.writeTGA(fsLittle,32);
1666 _BackColor = CRGBA(255, 255, 255, 255);
1672 LandscapeIGManager.reset();
1673 landscape->removeAllZones();
1677 // remove proximity tga
1678 list<string>::iterator itProx(_TempFileNames.begin()), lastProx(_TempFileNames.end());
1679 for( ; itProx != lastProx ; ++itProx)
1681 CFile::deleteFile(*itProx);
1685 //--------------------------------------------------------------------------------
1686 inline bool RGB2HSV(const CRGBA & rgba, uint & Hue, uint & Sat, uint & Val)
1688 double Min_, Max_, Delta, H, S, V;
1690 Min_ = min(min(rgba.R, rgba.G), rgba.B);
1691 Max_ = max(max(rgba.R, rgba.G), rgba.B);
1692 Delta = ( Max_ - Min_);
1694 if(Max_ != 0.0)
1696 S = 255.0*Delta/Max_;
1698 else
1700 Hue = 0;
1701 Sat = 0;
1702 Val = 0;
1703 return false;
1706 H = 0.0;
1707 V = Max_;
1709 if(rgba.R == Max_)
1711 H = (rgba.G - rgba.B) / Delta;
1713 else if(rgba.G == Max_)
1715 H = 2.0 + (rgba.B - rgba.R) / Delta;
1717 else
1719 H = 4.0 + (rgba.R - rgba.G) / Delta;
1722 H = H * 60;
1723 if(H < 0.0)
1725 H = H + 360.0;
1728 Hue = (uint)H ; // Hue -> 0..360
1729 Sat = (uint)S * 100 / 255; // Saturation -> 0..100 %
1730 Val = (uint)V * 100 / 255; // Value - > 0..100 %
1732 return true;
1735 //-------------------------------------------------------------------------------------------------
1736 inline bool infHLS(const CRGBA & rgba1, const CRGBA & rgba2)
1738 uint h1, s1, v1, h2, s2, v2;
1739 RGB2HSV(rgba1, h1, s1, v1);
1740 RGB2HSV(rgba2, h2, s2, v2);
1742 if(h1 != h2)
1744 return (h1 < h2);
1746 else if(s1 != s2)
1748 return (s1 < s2);
1750 else
1752 return (v1 < v2);
1756 //-------------------------------------------------------------------------------------------------
1757 void CScreenshotIslands::buildBackTextureHLS(const std::string & islandName, const CBitmap & islandBitmap)
1759 // load limit bitmap
1760 CBitmap limBitmap;
1761 std::string limFileName = _OutDirectory + "/" + islandName + "_limit.tga";
1763 CIFile limFS(limFileName.c_str());
1764 limBitmap.load(limFS);
1766 list< CRGBA > limitPixels;
1768 // search for colors of limit pixels
1769 for(uint x=0; x<islandBitmap.getWidth(); x++)
1771 for(uint y=0; y<islandBitmap.getHeight(); y++)
1773 CRGBA lim = limBitmap.getPixelColor(x, y);
1774 if((lim == CRGBA::White) && (islandBitmap.getPixelColor(x, y)!=CRGBA::White))
1776 limitPixels.push_back(islandBitmap.getPixelColor(x, y));
1781 // HLS order
1782 list< CRGBA > sortedHLS;
1783 list< CRGBA >::iterator itCol, itHLS;
1784 bool inserted = false;
1785 for(itCol=limitPixels.begin(); itCol!=limitPixels.end(); itCol++)
1787 inserted = false;
1788 for(itHLS=sortedHLS.begin(); itHLS!=sortedHLS.end(); ++itHLS)
1790 if(infHLS(*itCol, *itHLS))
1792 sortedHLS.insert(itHLS, *itCol);
1793 inserted = true;
1794 break;
1797 if(inserted==false) sortedHLS.push_back(*itCol);
1801 // keep more filled eighth of circle
1802 nlassert(!sortedHLS.empty()); // If it crashes here, you may be missing .zonel's.
1803 itHLS = sortedHLS.begin();
1804 uint h, s, v;
1805 RGB2HSV(*itHLS, h, s, v);
1806 list< CRGBA > currentList, maxList;
1808 for(uint i=0; i<8; i++)
1810 while(itHLS!=sortedHLS.end() && h<i*45)
1812 currentList.push_back(*itHLS);
1813 RGB2HSV(*itHLS, h, s, v);
1814 ++itHLS;
1817 if(currentList.size() > maxList.size())
1819 maxList.clear();
1820 maxList = currentList;
1821 currentList.clear();
1825 vector< CRGBA > sortedColors(maxList.size());
1826 uint colorsNb = 0;
1827 CRGBA lastColor(0, 0, 0, 0);
1828 CRGBA maxColor;
1829 uint maxColorNb = 0;
1830 uint currentColorNb = 0;
1831 for(itHLS=maxList.begin(); itHLS!=maxList.end(); ++itHLS)
1833 if(lastColor==*itHLS)
1835 currentColorNb++;
1837 else
1839 currentColorNb = 1;
1842 if(currentColorNb>maxColorNb)
1844 maxColorNb = currentColorNb;
1845 maxColor = *itHLS;
1848 lastColor = *itHLS;
1850 RGB2HSV(*itHLS, h, s, v);
1851 if(v>25 && v<75 && s>25 && s<75)
1853 sortedColors[colorsNb] = *itHLS;
1854 colorsNb++;
1858 if(colorsNb < 5)
1860 colorsNb = 0;
1861 for(itHLS=maxList.begin(); itHLS!=maxList.end(); ++itHLS)
1863 sortedColors[colorsNb] = *itHLS;
1864 colorsNb++;
1868 sortedColors.resize(colorsNb);
1870 _BackBitmap.resize(islandBitmap.getWidth(), islandBitmap.getHeight(), islandBitmap.PixelFormat);
1872 if(sortedColors.size()!=0)
1874 _BackColor = maxColor;
1876 std::string islandNameLwr = toLowerAscii(islandName);
1877 uint32_t seed = 0;
1878 for (ptrdiff_t i = 0; i < (ptrdiff_t)islandNameLwr.size(); ++i)
1879 seed += wangHash(seed ^ islandNameLwr[i]);
1881 uint8 * backPixels = &(_BackBitmap.getPixels(0)[0]);
1883 uint i = 0;
1884 for(uint x=0; x<_BackBitmap.getWidth(); x++)
1886 for(uint y=0; y<_BackBitmap.getHeight(); y++, i++)
1888 sint32 randomVal = wangHash(seed ^ i) % colorsNb;
1889 const CRGBA & color = sortedColors[randomVal];
1891 *backPixels = (uint8) color.R;
1892 *(backPixels + 1) = (uint8) color.G;
1893 *(backPixels + 2) = (uint8) color.B;
1894 *(backPixels + 3) = (uint8) 255;
1896 backPixels = backPixels+4;
1902 //TEST
1903 CBitmap HLSBitmap;
1904 HLSBitmap.resize(640, sortedColors.size()*4, islandBitmap.PixelFormat);
1906 uint8 * hlsPixels = &(HLSBitmap.getPixels(0)[0]);
1908 for(uint i=0; i < sortedColors.size(); i++)
1910 uint count = 0;
1911 while(count<640*4)
1913 *hlsPixels = (uint8) sortedColors[i].R;
1914 *(hlsPixels + 1) = (uint8) sortedColors[i].G;
1915 *(hlsPixels + 2) = (uint8) sortedColors[i].B;
1916 *(hlsPixels + 3) = (uint8) sortedColors[i].A;
1918 hlsPixels = hlsPixels+4;
1919 count++;
1924 string textureName = _OutDirectory + "/" + islandName + "_HLS2.tga";
1925 COFile fsHLS(textureName.c_str());
1926 HLSBitmap.writeTGA(fsHLS,32);
1931 //--------------------------------------------------------------------------------------------------------------
1932 //--------------------------------------------------------------------------------------------------------------
1934 //-------------------------------------------------------------------------------------------------
1935 // methods CProximityMapBuffer
1936 //-------------------------------------------------------------------------------------------------
1938 void CProximityMapBuffer::load(const std::string& name)
1940 // load the AI collision map file
1941 CWorldMap worldMap;
1942 CIFile f(name);
1943 f.serial(worldMap);
1945 // lookup the map bounds
1946 CMapPosition min, max;
1947 worldMap.getBounds(min, max);
1949 // calculate a handful of constants relating to the bounds of the image...
1950 _ScanWidth = max.x()-min.x();
1951 _ScanHeight = max.y()-min.y();
1952 _XOffset= min.x();
1953 _YOffset= (sint16)min.y();
1955 // redimension buffer to correct size
1956 _Buffer.resize(_ScanWidth*_ScanHeight);
1958 // setup a position variable to mark the start point of each line
1959 CMapPosition scanpos(min.x(),min.y());
1961 // iterate over the scan area looking for points that are accessible
1962 for (uint32 y=0; y<_ScanHeight; ++y, scanpos = scanpos.getStepN())
1964 CMapPosition pos(scanpos);
1966 // scan a line of the map
1967 for (uint32 x=0; x<_ScanWidth; ++x, pos = pos.getStepE())
1969 bool isAccessible= false;
1970 // if the cell pointer is NULL it means that the 16x16 cell in question is inaccessible
1971 if (worldMap.getRootCellCst(pos) != NULL)
1973 // run through the surfaces in the cell looking for a match for this position (may be as many as 3 surfaces per cell max)
1974 for (uint32 ns=0; ns<3; ++ns)
1976 isAccessible |= worldMap.getSafeWorldPosition(pos, CSlot(ns)).isValid();
1979 // setup the next pixel in the output buffers...
1980 _Buffer[y*_ScanWidth+x]= (isAccessible? 0:std::numeric_limits<TBufferEntry>::max());
1985 //-------------------------------------------------------------------------------------------------
1986 void CProximityMapBuffer::calculateZones(TZones& zones)
1988 // clear out the result buffer before starting work
1989 zones.clear();
1991 // setup a container to hold the accessible points within this buffer
1992 typedef std::set<uint32> TAccessiblePoints;
1993 TAccessiblePoints accessiblePoints;
1995 // start by building the set of all accessible points
1996 for (uint32 i=0;i<_Buffer.size();++i)
1998 if (_Buffer[i]==0)
1999 accessiblePoints.insert(i);
2002 // while there are still points remaining in the set we must have another zone to process
2003 while (!accessiblePoints.empty())
2005 // append a new zone to the zones vector and get a refference to it
2006 zones.push_back( CProximityZone(_ScanWidth,_ScanHeight,_XOffset,_YOffset) );
2007 CProximityZone& theZone= zones.back();
2009 // setup a todo list representing points that are part of the surface that we are dealing with
2010 // that haven't yet been treated to check for neighbours, etc
2011 std::vector<uint32> todo;
2013 // get hold of the first point in the accessilbe points set and push it onto the todo list
2014 todo.push_back(*accessiblePoints.begin());
2015 accessiblePoints.erase(todo.back());
2017 // while we have more points to deal with ...
2018 while (!todo.empty())
2020 // pop the next point off the todo list
2021 uint32 thePoint= todo.back();
2022 todo.pop_back();
2024 // add the point to the zone
2025 theZone.add(thePoint);
2027 // a little macro for the code to perform for each movement test...
2028 #define TEST_MOVE(xoffs,yoffs)\
2030 TAccessiblePoints::iterator it= accessiblePoints.find(thePoint+xoffs+_ScanWidth*yoffs);\
2031 if (it!=accessiblePoints.end())\
2033 todo.push_back(*it);\
2034 accessiblePoints.erase(it);\
2038 // N, S, W, E moves
2039 TEST_MOVE( 0, 1);
2040 TEST_MOVE( 0,-1);
2041 TEST_MOVE( 1, 0);
2042 TEST_MOVE(-1, 0);
2044 // NW, NE, WS, SE moves
2045 TEST_MOVE( 1, 1);
2046 TEST_MOVE(-1, 1);
2047 TEST_MOVE( 1,-1);
2048 TEST_MOVE(-1,-1);
2050 #undef TEST_MOVE
2054 nlinfo("Found %u zones",zones.size());
2057 //-------------------------------------------------------------------------------------------------
2058 void CProximityMapBuffer::_prepareBufferForZoneProximityMap(const CProximityZone& zone,TBuffer& zoneBuffer,TOffsetsVector& accessiblePoints)
2060 // the length of runs that we consider too short to deal with...
2061 const uint32 shortRunLength=5;
2063 // redimention and initialise the zone buffer
2064 uint32 zoneWidth= zone.getZoneWidth();
2065 uint32 zoneHeight= zone.getZoneHeight();
2066 zoneBuffer.clear();
2067 zoneBuffer.resize(zoneWidth*zoneHeight, std::numeric_limits<TBufferEntry>::max());
2069 // setup the buffer's accessible points and prime vects[0] with the set of accessible points in the zone buffer
2070 for (uint32 i=0;i<zone.getOffsets().size();++i)
2072 // lookup the next offset in the zone's offsets vector and remap to a zone-relative offset
2073 uint32 zoneOffset= zone.remapOffset(zone.getOffsets()[i]);
2074 // flag the appropriate entry in the zoneBuffer as 'distance=0'
2075 zoneBuffer[zoneOffset]= 0;
2076 // add the zone offset to the accessible points vector
2077 accessiblePoints.push_back(zoneOffset);
2080 // run through the zone buffer looking for points that are surrounded that we can just throw out...
2081 // start by flagging all points that belong to a short run in the y direction
2082 for (uint32 i=0;i<zoneWidth;++i)
2084 // setup start and end offsets marking first and last 'zero' value pixels in the column
2085 uint32 startOffset=i, endOffset=i+(zoneHeight-1)*zoneWidth;
2086 for (; startOffset<endOffset && zoneBuffer[startOffset]!=0; startOffset+= zoneWidth) {}
2087 for (; endOffset>startOffset && zoneBuffer[endOffset]!=0; endOffset-= zoneWidth) {}
2089 for (uint32 offset=startOffset, marker=startOffset;offset<=endOffset;offset+=zoneWidth)
2091 // see if this is an accessible position
2092 if (zoneBuffer[offset]!=0)
2094 zoneBuffer[offset]= InteriorValue;
2096 if(offset-1>=startOffset && zoneBuffer[offset-1] == std::numeric_limits<TBufferEntry>::max())
2098 zoneBuffer[offset-1] = ValueBorder;
2100 if(offset+1<=endOffset && zoneBuffer[offset+1] == std::numeric_limits<TBufferEntry>::max())
2102 zoneBuffer[offset+1] = ValueBorder;
2108 // continue by dealing with all points that belong to a short run in the x direction
2109 for (uint32 i=0;i<zoneHeight;++i)
2111 // setup start and end offsets marking first and last 'zero' value pixels in the column
2112 uint32 startOffset= i*zoneWidth;
2113 uint32 endOffset= startOffset+zoneWidth-1;
2115 uint32 startOffsetInit = startOffset;
2116 uint32 endOffsetInit = endOffset;
2118 for (; startOffset<endOffset && zoneBuffer[startOffset]!=0; ++startOffset) {}
2119 for (; endOffset>startOffset && zoneBuffer[endOffset]!=0; --endOffset) {}
2121 for (uint32 offset=startOffset, marker=startOffset;offset<=endOffset;++offset)
2123 // see if this is an accessible position
2124 if (zoneBuffer[offset]!=0)
2126 zoneBuffer[offset]= InteriorValue;
2128 if(offset>zoneWidth && zoneBuffer[offset-zoneWidth] == std::numeric_limits<TBufferEntry>::max())
2130 zoneBuffer[offset-zoneWidth] = ValueBorder;
2132 if(offset+zoneWidth<zoneHeight*zoneWidth && zoneBuffer[offset+zoneWidth] == std::numeric_limits<TBufferEntry>::max())
2134 zoneBuffer[offset+zoneWidth] = ValueBorder;
2141 //-------------------------------------------------------------------------------------------------
2142 void CProximityMapBuffer::generateZoneProximityMap(const CProximityZone& zone,TBuffer& zoneBuffer)
2144 // a set of vectors to hold sets of points that need to be treated
2145 TOffsetsVector vects[16];
2146 // a counter that should always contain sum of all vects[i].size()
2147 uint32 entriesToTreat= 0;
2149 // setup the buffer's accessible points and prime vects[0] with the set of accessible points in the zone buffer
2150 _prepareBufferForZoneProximityMap(zone, zoneBuffer, vects[0]);
2151 entriesToTreat= vects[0].size();
2153 // lookup the buffer dimentions
2154 uint32 zoneWidth= zone.getZoneWidth();
2155 uint32 zoneHeight= zone.getZoneHeight();
2157 // for dist=0 to ? treat points with distance 'dist' from centre, iterating until all vects are empty
2158 for (TBufferEntry dist=0; entriesToTreat!=0; ++dist)
2160 // setup refference to the vector that we are supposed to be iterating over for this dist
2161 TOffsetsVector &vect= vects[dist&15];
2163 // iterate over contents of points for this distance, treating NSWE neighbours
2164 for(TOffsetsVector::iterator it=vect.begin(); it!=vect.end(); ++it)
2166 uint32 val=(*it);
2168 // deal with the case where this point has already been refferenced via a better route
2169 if (zoneBuffer[val]<dist || (zoneBuffer[val]==InteriorValue && dist > BigValue)
2170 || (zoneBuffer[val]==ValueBorder && dist > BigValue))
2171 continue;
2173 // write the new distance into this buffer entry
2174 zoneBuffer[val]=dist;
2176 // decompose into x and y in order to manage identification of neighbour cells correctly
2177 uint32 x= val % zoneWidth;
2178 uint32 y= val / zoneWidth;
2180 #define TEST_MOVE(xoffs,yoffs,newDist)\
2182 if (((uint32)(x+xoffs)<zoneWidth) && ((uint32)(y+yoffs)<zoneHeight))\
2184 uint32 newVal= val+xoffs+(yoffs*zoneWidth);\
2185 bool isInterior = ((zoneBuffer[newVal] == InteriorValue && newDist > BigValue) || (zoneBuffer[newVal] == ValueBorder && newDist > BigValue));\
2186 if (zoneBuffer[newVal] > newDist && !isInterior)\
2188 zoneBuffer[newVal] = newDist;\
2189 vects[newDist & 15].push_back(newVal);\
2190 ++entriesToTreat;\
2195 // N, S, W, E moves
2196 TEST_MOVE( 0, 1,dist+5);
2197 TEST_MOVE( 0,-1,dist+5);
2198 TEST_MOVE( 1, 0,dist+5);
2199 TEST_MOVE(-1, 0,dist+5);
2201 // NW, NE, WS, SE moves
2202 TEST_MOVE( 1, 1,dist+7);
2203 TEST_MOVE(-1, 1,dist+7);
2204 TEST_MOVE( 1,-1,dist+7);
2205 TEST_MOVE(-1,-1,dist+7);
2207 // NNW, NNE, SSW, SSE moves
2208 TEST_MOVE( 1, 2,dist+11);
2209 TEST_MOVE(-1, 2,dist+11);
2210 TEST_MOVE( 1,-2,dist+11);
2211 TEST_MOVE(-1,-2,dist+11);
2213 // WNW, WSW, ENE, ESE moves
2214 TEST_MOVE( 2, 1,dist+11);
2215 TEST_MOVE(-2, 1,dist+11);
2216 TEST_MOVE( 2,-1,dist+11);
2217 TEST_MOVE(-2,-1,dist+11);
2219 #undef TEST_MOVE
2222 // clear out the vector
2223 entriesToTreat-= vect.size();
2224 vect.clear();
2228 //-------------------------------------------------------------------------------------------------
2229 const TBuffer& CProximityMapBuffer::getBuffer() const
2231 return _Buffer;
2234 //-------------------------------------------------------------------------------------------------
2235 uint32 CProximityMapBuffer::getScanHeight() const
2237 return _ScanHeight;
2240 //-------------------------------------------------------------------------------------------------
2241 uint32 CProximityMapBuffer::getScanWidth() const
2243 return _ScanWidth;
2246 //-----------------------------------------------------------------------------------------
2247 //-----------------------------------------------------------------------------------------
2249 //-------------------------------------------------------------------------------------------------
2250 // methods CProximityZone
2251 //-------------------------------------------------------------------------------------------------
2253 CProximityZone::CProximityZone(uint32 scanWidth,uint32 scanHeight,sint32 xOffset, sint32 yOffset)
2255 _ScanWidth = scanWidth;
2256 _ScanHeight = scanHeight;
2257 _XOffset = xOffset;
2258 _YOffset = yOffset;
2260 _MaxOffset = scanWidth * scanHeight -1;
2262 _XMin = std::numeric_limits<uint32>::max();
2263 _YMin = std::numeric_limits<uint32>::max();
2264 _XMax = 0;
2265 _YMax = 0;
2267 _BorderPixels = 30;
2270 //-------------------------------------------------------------------------------------------------
2271 bool CProximityZone::add(uint32 offset)
2273 // make sure the requested point is in the zone
2274 if (offset>_MaxOffset)
2275 return false;
2277 // calculate the x and y coordinates of the point
2278 uint32 y= offset/ _ScanWidth;
2279 uint32 x= offset% _ScanWidth;
2281 // update the bounding coordinates for this zone
2282 if (x<_XMin) _XMin= x;
2283 if (x>_XMax) _XMax= x;
2284 if (y<_YMin) _YMin= y;
2285 if (y>_YMax) _YMax= y;
2287 // add the point to the vector of points
2288 _Offsets.push_back(offset);
2289 return true;
2292 //-------------------------------------------------------------------------------------------------
2293 const CProximityZone::TOffsets& CProximityZone::getOffsets() const
2295 return _Offsets;
2298 //-------------------------------------------------------------------------------------------------
2299 uint32 CProximityZone::getZoneWidth() const
2301 return getZoneXMax()- getZoneXMin() +1;
2304 //-------------------------------------------------------------------------------------------------
2305 uint32 CProximityZone::getZoneHeight() const
2307 return getZoneYMax()- getZoneYMin() +1;
2310 //-------------------------------------------------------------------------------------------------
2311 sint32 CProximityZone::getZoneXMin() const
2313 return _XMin-_BorderPixels;
2316 //-------------------------------------------------------------------------------------------------
2317 sint32 CProximityZone::getZoneYMin() const
2319 return _YMin-_BorderPixels;
2322 //-------------------------------------------------------------------------------------------------
2323 uint32 CProximityZone::getZoneXMax() const
2325 return _XMax+_BorderPixels;
2328 //-------------------------------------------------------------------------------------------------
2329 uint32 CProximityZone::getZoneYMax() const
2331 return _YMax+_BorderPixels;
2334 //-------------------------------------------------------------------------------------------------
2335 uint32 CProximityZone::getBoundXMin() const
2337 return _XMin+_XOffset-_BorderPixels;
2340 //-------------------------------------------------------------------------------------------------
2341 uint32 CProximityZone::getBoundYMin() const
2343 return _YMin+_YOffset-_BorderPixels;
2346 //-------------------------------------------------------------------------------------------------
2347 uint32 CProximityZone::getBoundXMax() const
2349 return _XMax+_XOffset+_BorderPixels;
2352 //-------------------------------------------------------------------------------------------------
2353 uint32 CProximityZone::getBoundYMax() const
2355 return _YMax+_YOffset+_BorderPixels;
2358 //-------------------------------------------------------------------------------------------------
2359 uint32 CProximityZone::remapOffset(uint32 bufferOffset) const
2361 // decompose input coordinates into x and y parts
2362 uint32 bufferX= bufferOffset% _ScanWidth;
2363 uint32 bufferY= bufferOffset/ _ScanWidth;
2365 // remap the offset from a _Buffer-relative offset to a zone-relative offset
2366 return bufferX-getZoneXMin()+ (bufferY-getZoneYMin())*getZoneWidth();