Terrain - Risk style territories

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
monkeycracks
Posts: 1029
Joined: Thu Apr 06, 2006 12:45 am
Location: Tennesee, USA
Contact:

Terrain - Risk style territories

Post by monkeycracks »

Demonstration of what I'm trying to achieve :
http://www.clanlib.org/gfx/screenshots/ ... icewar.png

What would be the best way to implement something like the colored terrain? The color is the players color, and it shows that they control the territory. I'm quite lost in how to draw a color over the terrain texture and define the territories.
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Post by Virion »

this is rather hardcore to me :(
Nox
Posts: 304
Joined: Wed Jan 14, 2009 6:23 pm

Post by Nox »

Well you just have to maniplulate the texture which is quiet easy. Here one example how to generate from one colormap and one texture m-textures (m = number of colors in the colormap except black). This code snippet makes a lookup in the colormap and if the color of the pixel matches replacecolor[0][m] the new pixel for the m-texture will be a mitxure of the original-texture pixel and the replacecolor[1][m]. If the color doesnt math the new pixel will be a mixture of the original-texture pixel and the replacecolor[2][m]:

Code: Select all

size_t FindColorInArray(unsigned int Color, unsigned int* Array[3], size_t numColors)
{
	size_t min			= 0;
	size_t max			= numColors - 1;

	while(true)
	{
		size_t index = (max + min) / 2;

		if(max - min <= 1)
		{
			if(Array[0][min] == Color)
				return min;
			else if(Array[0][max] == Color)
				return max;
			else
				return -1;

		}
		else if(Color < Array[0][index])
			max = index;
		else if(Color > Array[0][index])
			min = index;
		else
			return index;
	}
}

unsigned int InterpolateByAlpha(unsigned int c1, unsigned int c2)
{
	unsigned int a1 = ((c1 & 0xFF000000) >> 24);
	unsigned int r1 = ((c1 & 0x00FF0000) >> 16) * a1;
	unsigned int g1 = ((c1 & 0x0000FF00) >> 8) * a1;
	unsigned int b1 = (c1 & 0x000000FF) * a1;

	unsigned int a2 = ((c2 & 0xFF000000) >> 24);
	unsigned int r2 = ((c2 & 0x00FF0000) >> 16) * a2;
	unsigned int g2 = ((c2 & 0x0000FF00) >> 8) * a2;
	unsigned int b2 = (c2 & 0x000000FF) * a2;

	if(a1 == 0) return c2;
	if(a2 == 0) return c1;


	unsigned int a = a1 + a2;

	return 0xFF000000 | (((r1 + r2) / a) << 16) | (((g1 + g2) / a) << 8) | ((b1 + b2) / a);
}

std::list<ITexture*> ManipulateTexture(IVideoDriver* driver, IImage* texture, size_t numColors,  unsigned int *ReplaceColors[3], IImage* secTexture)
{

	std::list<ITexture*> NewTextures;
	dimension2du size = texture->getDimension();

	unsigned int** bigBuffer = new unsigned int*[numColors];

	for(size_t counter = 0; counter < numColors; counter++)
		bigBuffer[counter] = new unsigned int[size.Height * size.Width];

	for(u32 y = 0; y < size.Height; y++)
	{
		for(u32 x = 0; x < size.Width; x++)
		{
			unsigned int Color	= texture->getPixel(x, y).color | 0xFF000000;
			size_t offset		= y * size.Width + x;
			size_t endindex		= FindColorInArray(Color, ReplaceColors, numColors);

			if(endindex != (size_t)-1)
			{
				unsigned int color1 = (secTexture ? secTexture->getPixel(x, y).color : 0);

				for(size_t counter = 0; counter < numColors; counter++)
				{
					if(endindex == counter)
						bigBuffer[counter][offset] = InterpolateByAlpha(color1, ReplaceColors[1][endindex]);
					else
						bigBuffer[counter][offset] = InterpolateByAlpha(color1, ReplaceColors[2][endindex]);
				}
			}
			else
			{

				if(secTexture)
				{
					unsigned int color1 = secTexture->getPixel(x, y).color | 0xFF00000;
					Color = InterpolateByAlpha(color1, Color);
				}

				for(size_t counter = 0; counter < numColors; counter++)
					bigBuffer[counter][offset] = Color;
			}
		}
	}

	for(size_t counter = 0; counter < numColors; counter++)
	{
		IImage* i = driver->createImageFromData(ECF_A8R8G8B8, size, bigBuffer[counter], true, false);
		NewTextures.push_back(driver->addTexture("image", i));
		i->drop();
		delete[] bigBuffer[counter];
	}
	delete[] bigBuffer;


	return NewTextures;
}
This can be used like this (not working just as an example):

Code: Select all

	Colormap	= Main->GetDriver()->createImageFromFile("../grafic/planet/GalaxyOverlayTest.png");
	Textur		= Main->GetDriver()->createImageFromFile("../grafic/planet/Galaxy.png");
std::list<unsigned int> KnownColors = CountTextureColors(Colormap);
		NumColors = KnownColors.size();

		SDaten = new SectorDaten[NumColors];

		unsigned int *ReplaceColor[3];
		ReplaceColor[0] = new unsigned int[NumColors];
		ReplaceColor[1] = new unsigned int[NumColors];
		ReplaceColor[2] = new unsigned int[NumColors];

		std::list<unsigned int>::iterator iter = KnownColors.begin();
		for(unsigned int counter = 0; counter < NumColors; counter++, ++iter)
		{
			SDaten[counter].Color		= *iter;
			ReplaceColor[0][counter]	= *iter;

			if(counter == 0 || counter > count)
			{
				ReplaceColor[1][counter] = 0x00000000;
				ReplaceColor[2][counter] = 0x00000000;
			}
			else if(Galaxy->GetGSectorByNum(counter))
			{
				ReplaceColor[1][counter] = 0xFF119911;
				ReplaceColor[2][counter] = 0xFF006600;
			}
			else
			{
				ReplaceColor[1][counter] = 0xFF999999;
				ReplaceColor[2][counter] = 0xFF666666;
			}
		}
		std::list<ITexture*> ImgList = ManipulateTexture(Main->GetDriver(), Colormap, NumColors, ReplaceColor, Textur);

		size_t counter = 0;
		for(std::list<ITexture*>::iterator it = ImgList.begin(); it != ImgList.end(); ++it)
		{
			SDaten[counter].Textur = *it;
			counter++;
		}
The files:
http://www.7bitfaster.de/downloads/Galaxy.png
http://www.7bitfaster.de/downloads/Gala ... ayTest.png
monkeycracks
Posts: 1029
Joined: Thu Apr 06, 2006 12:45 am
Location: Tennesee, USA
Contact:

Post by monkeycracks »

Thanks! With your code I was able to lay out territory borders.

Unfortunately, I'm still unsure of how to fill in each territory with the color of whoever owns it... A floodfill method on the 2d image perhaps? Not sure how fast/slow that would be for real-time rendering.


Edit: Also, could you give me some sample data for what should be in

std::list<unsigned int> KnownColors;
Nox
Posts: 304
Joined: Wed Jan 14, 2009 6:23 pm

Post by Nox »

O_o well...that code is for filling with colors.

Another usageexample. This snippet is used to fill the different provinces of a planet with different colors for enemy/allied/own:

Code: Select all

		IVideoDriver* driver = Main->GetDriver();

		//Altes Zeug weghauen
		delete[] PFrames;
		PFrames	= NULL;

		std::list<unsigned int> KnownColors = CountTextureColors(ProvincenKey);
		NumSelectMaps = KnownColors.size();

		//Stimmt die Anzahl der Provincen mit der Anzahl der Farben überein?
		if(NumSelectMaps - 1 != planet->GetProvincenCount())
		{
			std::wcout<<L"Die Anzahl der Parzellen auf der Colormap entspricht nicht der angegeben Anzahl des Planeten. Betroffene Textur: "<< planet->GetTextur() << std::endl;
			return;//error
		}

		//Die dazu gehörige Province und den Besitzer feststellen
		unsigned int *ReplaceColor[3];
		{
			ReplaceColor[0] = new unsigned int[NumSelectMaps];
			ReplaceColor[1] = new unsigned int[NumSelectMaps];
			ReplaceColor[2] = new unsigned int[NumSelectMaps];
			PFrames = new SelectMap[NumSelectMaps];

			std::list<unsigned int>::iterator	iter = KnownColors.begin();
			std::list<Province*>::iterator		temp = planet->GetFirstProvince();
			PFrames[0].SelectTexture = NULL;
			PFrames[0].ColorKey = *iter;
			PFrames[0].Prov = NULL;
			PFrames[0].OldOwner = NULL;

			ReplaceColor[0][0] = *iter;
			ReplaceColor[1][0] = 0x00000000;
			ReplaceColor[2][0] = 0x00000000;

			iter++;
			for(size_t counter = 1; counter < NumSelectMaps; iter++, counter++, temp++)
			{
				PFrames[counter].SelectTexture = NULL;
				PFrames[counter].ColorKey = *iter;
				PFrames[counter].Prov = (*temp);
				PFrames[counter].OldOwner = (*temp)->GetOwner();

				ReplaceColor[0][counter] = *iter;
				if(!PFrames[counter].OldOwner)	//Kein Besitzer
				{
					ReplaceColor[1][counter] = 0xFFFFFFFF;
					ReplaceColor[2][counter] = 0xFF999999;
				}
				else if(planet->GetVerwaltung()->GetPlayer() == PFrames[counter].OldOwner)	//Der Spieler ist der Besitzer
				{
					ReplaceColor[1][counter] = 0xFF00FF00;
					ReplaceColor[2][counter] = 0xFF009900;
				}
				else if(planet->GetVerwaltung()->GetPlayer()->GetFraction() == PFrames[counter].OldOwner->GetFraction()) // Der Besitzer ist in der selben Fraktion wie der Spieler
				{
					ReplaceColor[1][counter] = 0xFF0000FF;
					ReplaceColor[2][counter] = 0xFF000099;
				}
				else	//Der Spieler ist mit dem anderen Verfeindet
				{
					ReplaceColor[1][counter] = 0xFFFF0000;
					ReplaceColor[2][counter] = 0xFF990000;
				}
			}
		}
		//Auf Basis der oben gesammelten Informationen, werden nun die einzelnen Provincen auf der Textur eingefärbt
		std::list<ITexture*> ImgList = ManipulateTexture(driver, ProvincenKey, NumSelectMaps, ReplaceColor, PlanetTextur);
		size_t counter = 0;
		for(std::list<ITexture*>::iterator it = ImgList.begin(); it != ImgList.end(); ++it)
		{
			PFrames[counter].SelectTexture = *it;
			counter++;
		}

		delete[] ReplaceColor[0];
		delete[] ReplaceColor[1];
		delete[] ReplaceColor[2];
EDIT: i guess you cant use this code directly for your propose because it generates m-textures (one texture for every possible selectionstate) but you only need one texture. So you have to modify it.
monkeycracks
Posts: 1029
Joined: Thu Apr 06, 2006 12:45 am
Location: Tennesee, USA
Contact:

Post by monkeycracks »

Nox wrote:EDIT: i guess you cant use this code directly for your propose because it generates m-textures (one texture for every possible selectionstate) but you only need one texture. So you have to modify it.
I'm hoping to support a four player hotseat, so the amount of textures stored would be pretty massive (I think?). I'm rather sleep deprived at the moment, but my texture that's being generated is getting a full +255 of red added to every color on terrain-texture2.png.

Code: Select all

IImage* ColorMap = Driver->createImageFromFile("terrain-texture.jpg");
	IImage* Texture = Driver->createImageFromFile("terrain-texture2.png");

	std::list<unsigned int> KnownColors;
	KnownColors.push_back(1);
	unsigned int NumColors = KnownColors.size();

	unsigned int *ReplaceColor[3]; 
	ReplaceColor[0] = new unsigned int[NumColors];
	ReplaceColor[1] = new unsigned int[NumColors];
	ReplaceColor[2] = new unsigned int[NumColors]; 

	ITexture* p;

	std::list<ITexture*> ImgList = ManipulateTexture(Driver, ColorMap, NumColors, ReplaceColor, Texture);
	for(std::list<ITexture*>::iterator it = ImgList.begin(); it != ImgList.end(); ++it)
		p = (*it);

	ITerrainSceneNode* Terrain = SceneMgr->addTerrainSceneNode("terrain-heightmap.bmp");
	Terrain->setScale(vector3df(40.0f, 4.4f, 40.0f));
	Terrain->setMaterialTexture(0, p);
//	Terrain->setMaterialTexture(1, Driver->getTexture("detailmap3.jpg"));
//	Terrain->setMaterialType(EMT_DETAIL_MAP);
	Terrain->scaleTexture(1.0f, 20.0f);
	Terrain->setMaterialFlag(EMF_LIGHTING, false);
std::list<unsigned int> KnownColors = CountTextureColors(ProvincenKey);

I'm guess it has something to do with that line (Not sure what exactly CountTextureColors does, other than count texture colors of course).

My Texture:
http://img29.imageshack.us/i/terraintexture2.png/

My Colormap:
http://img85.imageshack.us/i/terraintexture.png/

Rendered:
http://img29.imageshack.us/i/ingamev.png/
Nox
Posts: 304
Joined: Wed Jan 14, 2009 6:23 pm

Post by Nox »

Well give me some time. I try to make this algorithm a bit more suitable for you. But i guess you didnt get the idea of this algorithm :? . For your case one texture would be quiet enough. And you need only one colormap. Your "Texture" could be a normal terraintexture. The black strips can be made with the colormap too.
monkeycracks
Posts: 1029
Joined: Thu Apr 06, 2006 12:45 am
Location: Tennesee, USA
Contact:

Post by monkeycracks »

I suppose I didn't :D. Thanks again for your help. I'm pretty much God awful with anything concerning textures or meshes.
Nox
Posts: 304
Joined: Wed Jan 14, 2009 6:23 pm

Post by Nox »

Code: Select all

ITexture* ManipulateTexture2(IVideoDriver* driver, IImage* texture, size_t numColors, unsigned int *ReplaceColors[3], size_t selected_Color, IImage* secTexture)
{
	unsigned int t1 = PortableGetTime();
	ITexture* NewTexture;
	dimension2du size = texture->getDimension();

	unsigned int* bigBuffer = new unsigned int[size.Height * size.Width];

	for(u32 y = 0; y < size.Height; y++)
	{
		for(u32 x = 0; x < size.Width; x++)
		{
			unsigned Color		= texture->getPixel(x, y).color | 0xFF000000;
			size_t offset		= y * size.Width + x;
			size_t endindex		= FindColorInArray(Color, ReplaceColors, numColors);

			if(endindex != (size_t)-1)
			{
				unsigned int color1 = (secTexture ? secTexture->getPixel(x, y).color : 0);

				if(endindex == selected_Color)
					bigBuffer[offset] = InterpolateByAlpha(color1, ReplaceColors[1][endindex]);
				else
					bigBuffer[offset] = InterpolateByAlpha(color1, ReplaceColors[2][endindex]);
			}
			else
			{

				if(secTexture)
				{
					unsigned int color1 = secTexture->getPixel(x, y).color | 0xFF00000;
					Color = InterpolateByAlpha(color1, Color);
				}

				bigBuffer[offset] = Color;
			}
		}
	}

	IImage* i = driver->createImageFromData(ECF_A8R8G8B8, size, bigBuffer, true, false);
	NewTexture = driver->addTexture("image", i);
	i->drop();
	delete[] bigBuffer;

	unsigned int t2 = PortableGetTime() - t1;
	std::cout<<"Zum Manipulieren benötigte Zeit in MS: "<<t2<<std::endl;
	return NewTexture;
}
How to use:

Code: Select all

		std::list<unsigned int>::iterator iter = KnownColors.begin();
		for(unsigned int counter = 0; counter < NumColors; counter++, ++iter)
		{
			ReplaceColor[0][counter] = *iter;

			if(*iter == 0xFFFFFFFF) //white is a borderline
			{
				ReplaceColor[1][counter] = DOESNT_MATTER;
				ReplaceColor[2][counter] = 0xFF000000;
			}
			else 			{
				ReplaceColor[1][counter] = GetOwnerOfFieldWithTheNumber(counter).GetColor();
				ReplaceColor[2][counter] = DOESNT_MATTER;
			}
		}
	}

	ITexture* newtex = ManipulateTexture2(Main->GetDriver(), Colormap, NumColors, ReplaceColor, NumColors, Textur);//last color is selected, the last color should be white!!
EDIT: just use your terraintexture.png as the colormap and a normal terraintexture as terraintexture2.png
Nox
Posts: 304
Joined: Wed Jan 14, 2009 6:23 pm

Post by Nox »

Simplified final version (maybe someone will need this later):

Code: Select all

#include<list>
#include "irrlicht.h"

using namespace irr;
using namespace core;
using namespace video;
using namespace scene;

#pragma comment(lib, "irrlicht.lib")

#define ALPHA 0xFF000000
#define WHITE 0xFFFFFFFF
#define RED   0xFFFF0000
#define GREEN 0xFF00FF00
#define BLUE  0xFF0000FF
#define BLACK 0xFF000000


std::list<unsigned int> CountTextureColors(IImage* texture)
{

	std::list<unsigned int>	KnownColors;
	unsigned int			StoredColors[200];

	dimension2du	size = texture->getDimension();
	size_t			NumStoredColors = 0;

	for(u32 y = 0; y < size.Height; y++)
	{
		for(u32 x = 0; x < size.Width; x++)
		{
			unsigned int Color = texture->getPixel(x, y).color | 0xFF000000;

			size_t min = 0;
			size_t max = NumStoredColors - 1;

			if(NumStoredColors == 0)
			{
				StoredColors[0] = Color;
				NumStoredColors = 1;
			}
			else for(bool IsInside = false; IsInside == false;)
			{
				if(max - min <= 1)
				{
					size_t newpos;
					if(Color < StoredColors[min])
						newpos = min;
					else if(Color == StoredColors[min])
						newpos = -1;
					else if(Color < StoredColors[max])
						newpos = max;
					else if(Color == StoredColors[max])
						newpos = -1;
					else
						newpos = max + 1;

					//wir fügen das neue Element ein
					if(newpos != (size_t)-1)
					{
						_ASSERT(NumStoredColors * sizeof(unsigned int) != sizeof(StoredColors));
						
						if(newpos < NumStoredColors)
							memmove(StoredColors + newpos + 1, StoredColors + newpos, sizeof(unsigned int) * (NumStoredColors - newpos));
						StoredColors[newpos] = Color;
						NumStoredColors++;
					}
					break;
				}

				size_t index = (max + min) / 2;

				if(Color < StoredColors[index])
					max = index;
				else if(Color > StoredColors[index])
					min = index;
				else
					IsInside = true;
			}
		}
	}
	for(size_t counter = 0; counter < NumStoredColors; counter++)
		KnownColors.push_back(StoredColors[counter]);

	return KnownColors;
}

size_t FindColorInArray(unsigned int Color, unsigned* Array[2], size_t numColors) 
{ 
	size_t min         = 0; 
	size_t max         = numColors - 1; 

	while(true) 
	{ 
		size_t index = (max + min) / 2; 

		if(max - min <= 1) 
		{ 
			if(Array[0][min] == Color) 
				return min; 
			else if(Array[0][max] == Color) 
				return max; 
			else 
				return -1; 

		} 
		else if(Color < Array[0][index]) 
			max = index; 
		else if(Color > Array[0][index]) 
			min = index; 
		else 
			return index; 
	} 
} 

unsigned int InterpolateByAlpha(unsigned int c1, unsigned int c2) 
{ 
	unsigned int a1 = ((c1 & 0xFF000000) >> 24); 
	unsigned int r1 = ((c1 & 0x00FF0000) >> 16) * a1; 
	unsigned int g1 = ((c1 & 0x0000FF00) >> 8) * a1; 
	unsigned int b1 = (c1 & 0x000000FF) * a1; 

	unsigned int a2 = ((c2 & 0xFF000000) >> 24); 
	unsigned int r2 = ((c2 & 0x00FF0000) >> 16) * a2; 
	unsigned int g2 = ((c2 & 0x0000FF00) >> 8) * a2; 
	unsigned int b2 = (c2 & 0x000000FF) * a2; 

	if(a1 == 0) return c2; 
	if(a2 == 0) return c1; 


	unsigned int a = a1 + a2; 

	return 0xFF000000 | (((r1 + r2) / a) << 16) | (((g1 + g2) / a) << 8) | ((b1 + b2) / a); 
} 

ITexture* ManipulateTexture2(IVideoDriver* driver, IImage* texture, size_t numColors, unsigned int *ReplaceColors[2], IImage* secTexture) 
{ 
	ITexture* NewTexture; 
	dimension2du size = texture->getDimension(); 

	unsigned int* bigBuffer = new unsigned int[size.Height * size.Width]; 

	for(u32 y = 0; y < size.Height; y++) 
	{ 
		for(u32 x = 0; x < size.Width; x++) 
		{ 
			unsigned Color      = texture->getPixel(x, y).color | 0xFF000000; 
			size_t offset      = y * size.Width + x; 
			size_t endindex      = FindColorInArray(Color, ReplaceColors, numColors); 

			if(endindex != (size_t)-1) 
			{ 
				unsigned int color1 = (secTexture ? secTexture->getPixel(x, y).color : 0); 

				bigBuffer[offset] = InterpolateByAlpha(color1, ReplaceColors[1][endindex]); 
			} 
			else 
			{ 

				if(secTexture) 
				{ 
					unsigned int color1 = secTexture->getPixel(x, y).color | 0xFF00000; 
					Color = InterpolateByAlpha(color1, Color); 
				} 

				bigBuffer[offset] = Color; 
			}
		} 
	} 

	IImage* i = driver->createImageFromData(ECF_A8R8G8B8, size, bigBuffer, true, false); 
	NewTexture = driver->addTexture("image", i); 
	i->drop(); 
	delete[] bigBuffer; 

	return NewTexture; 
} 


int main()
{
	IrrlichtDevice* Device = createDevice(EDT_DIRECT3D9, dimension2du(1024, 768), 32, false, false, false, 0);
	IVideoDriver* Driver = Device->getVideoDriver();
	ISceneManager* SceneMgr = Device->getSceneManager();

	IImage* ColorMap = Driver->createImageFromFile("terrain texture.png");
	IImage* Texture = Driver->createImageFromFile("white.png");

	std::list<unsigned int> KnownColors = CountTextureColors(ColorMap);
	unsigned int NumColors = KnownColors.size();

	unsigned int *ReplaceColor[2]; 
	ReplaceColor[0] = new unsigned int[NumColors];
	ReplaceColor[1] = new unsigned int[NumColors];

	std::list<unsigned int>::iterator iter = KnownColors.begin(); 
	for(unsigned int counter = 0; counter < NumColors; counter++, ++iter) 
	{ 
		ReplaceColor[0][counter] = *iter;//this is a color which should be looked up 

		if(*iter == WHITE) //white is a borderline 
		{ 
			//this is the color which should be placed if the lookup color has be found for an pixel
			ReplaceColor[1][counter] = BLUE;
		} 
		else if(*iter == RED)
		{
			//this method has to be implemented by you to give back the color which should be placed for a sector depending on the owner
			ReplaceColor[1][counter] = GREEN;
		}
		else
			ReplaceColor[1][counter] = BLACK;
	} 

 
	ITexture* p = ManipulateTexture2(Driver, ColorMap, NumColors, ReplaceColor, Texture);

	ITerrainSceneNode* Terrain = SceneMgr->addTerrainSceneNode("terrain-heightmap.bmp");
	Terrain->setScale(vector3df(40.0f, 4.4f, 40.0f));
	Terrain->setMaterialTexture(0, p);
	Terrain->scaleTexture(1.0f, 20.0f);
	Terrain->setMaterialFlag(EMF_LIGHTING, false);

	ICameraSceneNode* Camera = SceneMgr->addCameraSceneNodeFPS(0, 100.0f, 5.0f);
	Camera->setFarValue(8000.0f);

	while(Device->run())
	{
		Driver->beginScene();
		SceneMgr->drawAll();
		
		stringw FPSCount("Terrain Territories - FPS: ");
		FPSCount += Driver->getFPS();
		Device->setWindowCaption(FPSCount.c_str());
		
		Driver->endScene();
	}
	
	ColorMap->drop();
	Texture->drop();

	delete[] ReplaceColor[0];
	delete[] ReplaceColor[1];
	Device->drop();
	return 0;
}
Post Reply