Background Images

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
AticAtac
Posts: 29
Joined: Mon Mar 22, 2004 2:46 pm
Location: Germany

Background Images

Post by AticAtac »

Hwo do you folks load and draw background images ? They are typically of sizes 800x600 or 1024x768. I know that textures need to be of power of 2 size (256x256, 512x512, 1024x1024). I also want to prevent to use 1024x1024 textures, because of compatibility on some "older" cards.
One hard and complicated way could be to break the background images in smaller pieces and store them in different 512x512 textures. I would like to know if there is a better to do that!
Guest

Post by Guest »

Actually in my old engine (direct3d) I could load a 512x512 image and stretch it to 800x600 in game and it worked fine (hardly any scaling artifacts). In irrlicht it seems harder or I am missing something - so I now use the large cut up textures as you mentioned which works but is not as space saving.

I used to make the 800x600 image - then resize it in the art package to 512x512 (it would look squished/out of ratio of course) but once in the engine again just draw it on a quad with co-ords that made the image go back to the correct size.. it should be technically possible to do that in irrlicht with one of the overloaded draw 2d image functions (or a modified one of your own) but not sure about the scaling quality.
AticAtac
Posts: 29
Joined: Mon Mar 22, 2004 2:46 pm
Location: Germany

Post by AticAtac »

Well, for drawing *exact" the same image rescaling is not an option.
Splitting the texture into smaller pieces could be "automated" in the engine or in an function which loads the texture into memory and creates itself textures (of size 512x512) on the fly and put the original imge into them.
I guess i will go for this method until someone here has a better method.
AticAtac
Posts: 29
Joined: Mon Mar 22, 2004 2:46 pm
Location: Germany

Here it is ...

Post by AticAtac »

Ok, i did implement the functionalities for drawing "exact" background-images of sizes 640x480, 800x600 and 1024x768 into a class 'cGuiBackground' which implementation you will find down this post.
The class can load a background-image of the above sizes and draw it *exactly* on the screen without having to rescale if image and screen sizes match.
If the screen size is different than the image size then rescaling will automatically happen.

The class "cuts" the backgroundimage into pieces and put them into 2-3 textures.
For 640x480 image there are 2 textures (1x512x512, 1x128x512), for 800x600 and 1024x768 3 textures (3x512x512), used.



Usage:

In your Init-Rotuine just declare an instance of cGuiBackground and call "LoadImage":

Code: Select all

...
cGuiBackground mybackground(ECF_A8R8G8B8);
mybackground.LoadImage("data\\back.bmp");
...
Remember that the texture-creation flag can be overriden by Irrlicht !
The loading will take "some time". The fastest option is to use 32-Bit textures and 32-Bit images.

Later in your main loop just between "beginScene(..)" and "endScene(...)" call the draw-method of cGuiBackground ( mybackground.Draw() ).
Thats it.
Let me know if you folks try this and get problems!

cGuiBackground.h

Code: Select all

#ifndef __CGUIBACKGROUND_H__
#define __CGUIBACKGROUND_H__


typedef struct
{
ITexture* tex;
rect<s32> rc_image[6];
rect<s32> rc_tex[6];
int max_pieces;
int tex_w, tex_h;
} tTex;

class cGuiBackground
{
public:
	cGuiBackground(ECOLOR_FORMAT texformat);

	bool LoadImage(const char* filename);		
	void Draw();

protected:
	void Destroy();
private:
	std::vector<tTex> m_texs;
	s32 m_w, m_h;
	ECOLOR_FORMAT m_texformat;
};

#endif
cGuiBackground.cpp

Code: Select all

#include "cGuiBackground.h"

extern IrrlichtDevice* g_device;
extern IVideoDriver* g_driver;

//------------------------------------------------------------------------------
cGuiBackground::cGuiBackground(ECOLOR_FORMAT texformat) :
m_texformat(texformat)
{
}


//------------------------------------------------------------------------------
void cGuiBackground::Destroy()
{
	for (unsigned int i=0; i<m_texs.size(); ++i)
	{
		if (m_texs[i].tex)
			g_driver->removeTexture(m_texs[i].tex);
	}
	m_texs.clear();
}

//------------------------------------------------------------------------------
bool cGuiBackground::LoadImage(const char* filename)
{
	io::IReadFile* file=NULL;
	IImage* image = NULL;
	
	try
	{
		file = g_device->getFileSystem()->createAndOpenFile(filename);
		if (!file) throw "FileError";
		// Load the image into an IImage
		image = g_driver->createImageFromFile(file);	
		if (!image) throw "ImageError";
		// suppported sizes : 640x480, 800x600, 1024x768
		if (!(	(image->getDimension().Width==640 && image->getDimension().Height==480) ||
				(image->getDimension().Width==800 && image->getDimension().Height==600) ||
				(image->getDimension().Width==1024 && image->getDimension().Height==768)
				))
				throw "ImageSizeNotSupported";

		Destroy();

		// set up the tiles
		tTex tex;
		tex.tex = NULL;
		if (image->getDimension().Width==640 && image->getDimension().Height==480)
		{
			tex.max_pieces = 1;
			tex.rc_image[0] = rect<s32>(0,0,512,480);
			tex.rc_tex[0] = rect<s32>(0,0,512,480);
			tex.tex_w = 512;
			tex.tex_h = 512;
			m_texs.push_back(tex);
			tex.rc_image[0] = rect<s32>(512,0,512+128,480);
			tex.rc_tex[0] = rect<s32>(0,0,128,480);
			tex.tex_w = 128;
			tex.tex_h = 512;
			m_texs.push_back(tex);
			m_w = 640;
			m_h = 480;
		} else if (image->getDimension().Width==800 && image->getDimension().Height==600)
		{
			tex.max_pieces = 1;
			tex.rc_image[0] = rect<s32>(0, 0, 512, 512);
			tex.rc_tex[0] = rect<s32>(0, 0, 512, 512);
			tex.tex_w = 512;
			tex.tex_h = 512;
			m_texs.push_back(tex);
			tex.max_pieces = 6;
			tex.rc_image[0] = rect<s32>(0, 512, 512, 512+88);
			tex.rc_tex[0] = rect<s32>(0, 0, 512, 88);			
			tex.rc_image[1] = rect<s32>(512, 0, 512+288, 424);
			tex.rc_tex[1] = rect<s32>(0, 88, 288, 88+424);
			tex.rc_image[2] = rect<s32>(512, 424, 512+144, 424+88);
			tex.rc_tex[2] = rect<s32>(288, 88, 288+144, 88+88);
			tex.rc_image[3] = rect<s32>(656, 424, 656+144, 424+88);
			tex.rc_tex[3] = rect<s32>(288, 176, 288+144, 176+88);
			tex.rc_image[4] = rect<s32>(512, 512, 512+144, 512+88);
			tex.rc_tex[4] = rect<s32>(288, 264, 288+144, 264+88);
			tex.rc_image[5] = rect<s32>(656, 512, 656+144, 512+88);
			tex.rc_tex[5] = rect<s32>(288, 352, 288+144, 352+88);
			m_texs.push_back(tex);
			m_w = 800;
			m_h = 600;
		} else if (image->getDimension().Width==1024 && image->getDimension().Height==768)
		{
			tex.max_pieces = 1;
			tex.rc_image[0] = rect<s32>(0, 0, 512, 512);
			tex.rc_tex[0] = rect<s32>(0, 0, 512, 512);
			tex.tex_w = 512;
			tex.tex_h = 512;
			m_texs.push_back(tex);			
			tex.rc_image[0] = rect<s32>(512, 0, 512+512, 512);
			tex.rc_tex[0] = rect<s32>(0, 0, 512, 512);
			tex.tex_w = 512;
			tex.tex_h = 512;
			m_texs.push_back(tex);
			tex.max_pieces = 2;
			tex.rc_image[0] = rect<s32>(0, 512, 512, 512+256);
			tex.rc_tex[0] = rect<s32>(0, 0, 512, 256);
			tex.rc_image[1] = rect<s32>(512, 512, 512+512, 512+256);
			tex.rc_tex[1] = rect<s32>(0, 256, 512, 256+256);
			tex.tex_w = 512;
			tex.tex_h = 512;
			m_texs.push_back(tex);
			m_w = 1024;
			m_h = 768;
		}

		// turn off mipmapping
		bool bmipmap = g_driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
		g_driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);

		// create textures and put tiles into them
		s32* imgpix = (s32*)image->lock();
		char texname[8];
		for (unsigned int i=0; i<m_texs.size(); ++i)
		{
			sprintf(texname, "BTEX%d", i);
			m_texs[i].tex = g_driver->addTexture(dimension2d<s32>(m_texs[i].tex_w, m_texs[i].tex_h), texname, m_texformat);
			s32* texpix = (s32*)m_texs[i].tex->lock();
			for (int j=0; j<m_texs[i].max_pieces; ++j)
			{				
				s32 texofs = m_texs[i].rc_tex[j].UpperLeftCorner.Y * m_texs[i].tex_w + m_texs[i].rc_tex[j].UpperLeftCorner.X;
				s32 pixels = m_texs[i].rc_tex[j].getWidth()+1;
				s32 imgofs = m_texs[i].rc_image[j].UpperLeftCorner.Y * image->getDimension().Width;
												
				// 32-Bit Image -> 32-Bit Texture (fast)
				if (image->getColorFormat() == ECF_A8R8G8B8 && m_texs[i].tex->getColorFormat() == ECF_A8R8G8B8)
				{					
					for (int y=m_texs[i].rc_tex[j].UpperLeftCorner.Y; y<=m_texs[i].rc_tex[j].LowerRightCorner.Y; ++y)
					{
						memcpy(texpix+texofs, imgpix+imgofs, pixels);
						texofs += m_texs[i].tex_w;
						imgofs += image->getDimension().Width;
					}				
				} else if (m_texs[i].tex->getColorFormat() ==  ECF_A8R8G8B8) // Image -> 32-Bit Texture (slow)
				{
					for (int y=0; y<m_texs[i].rc_tex[j].getHeight()+1; ++y)
					{
						for (int x=0; x<m_texs[i].rc_tex[j].getWidth()+1; ++x)
						{
							texpix[texofs+x] = image->getPixel(	m_texs[i].rc_image[j].UpperLeftCorner.X+x,
																m_texs[i].rc_image[j].UpperLeftCorner.Y+y).color;
						}
						texofs += m_texs[i].tex_w;
					}
				} else if (m_texs[i].tex->getColorFormat() ==  ECF_A1R5G5B5) // Image -> 16-Bit Texture (slow)
				{
					s16* texpix16 = (s16*)texpix;
					for (int y=0; y<m_texs[i].rc_tex[j].getHeight()+1; ++y)
					{
						for (int x=0; x<m_texs[i].rc_tex[j].getWidth()+1; ++x)
						{
							texpix16[texofs+x] = image->getPixel(	m_texs[i].rc_image[j].UpperLeftCorner.X+x,
																m_texs[i].rc_image[j].UpperLeftCorner.Y+y).toA1R5G5B5();
						}
						texofs += m_texs[i].tex_w;
					}

				}

			}
			m_texs[i].tex->unlock();
		}

		// restore mipmapping-state
		g_driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, bmipmap);

		image->unlock();
		file->drop();
	} catch (const char*)
	{
		if (image) image->drop();
		if (file) file->drop();
		return false;
	}
	return true;
}

//------------------------------------------------------------------------------
void cGuiBackground::Draw()
{
	int sw = g_driver->getScreenSize().Width;
	int sh = g_driver->getScreenSize().Height;
	float scale_w = 1.0f;
	float scale_h = 1.0f;
	bool scale = false;
	if (m_w != sw) 
	{
		scale_w = float(sw) / float(m_w);
		scale = true;
	}
	if (m_h != sh) 
	{
		scale_h = float(sh) / float(m_h);
		scale = true;
	}

	for (unsigned int i=0; i<m_texs.size(); ++i)
	{
		for (int j=0; j<m_texs[i].max_pieces; ++j)
		{						
			if (scale)
			{
				rect<s32> irc = m_texs[i].rc_image[j];
				if (scale_w != 1.0f)
					irc = rect<s32>( int(float(irc.UpperLeftCorner.X)*scale_w),
									irc.UpperLeftCorner.Y,
									int(float(irc.LowerRightCorner.X)*scale_w),
									irc.LowerRightCorner.Y);
				if (scale_h != 1.0f)
					irc = rect<s32>( irc.UpperLeftCorner.X,
									int(float(irc.UpperLeftCorner.Y)*scale_h),
									irc.LowerRightCorner.X,
									int(float(irc.LowerRightCorner.Y)*scale_h));
				rect<s32> trc = m_texs[i].rc_tex[j];
				trc.LowerRightCorner.X--;
				trc.LowerRightCorner.Y--;
				g_driver->draw2DImage(m_texs[i].tex, irc, trc, 0, 0, false);
			} else
			{
				g_driver->draw2DImage(	m_texs[i].tex, 
										m_texs[i].rc_image[j].UpperLeftCorner,
										m_texs[i].rc_tex[j],
										0,
										SColor(255,255,255,255),
										false);

			}
			
		}
	}
}


bearSoft
Posts: 165
Joined: Fri Apr 01, 2005 9:55 pm
Location: Denmark

Post by bearSoft »

thank you for sharing ;)
Perhaps add it to "FAQs, Howtos, and tool lists"?
Regards.
Tech: win98se| 320mb ram| abitbe6| 433mhzceleron| atiRadeon7000.64mb| soundblaster125| dx9.0b | devCPP | IRR 0.12.0 |
Emil_halim
Posts: 518
Joined: Tue Mar 29, 2005 9:02 pm
Location: Alex,Egypt
Contact:

Post by Emil_halim »

with my Magic Library you can do what you want,and more for mixing 2D with 3D.

check it here



http://www.freewebs.com/bcxgl/index.htm
AticAtac
Posts: 29
Joined: Mon Mar 22, 2004 2:46 pm
Location: Germany

Post by AticAtac »

I will have a look at your lib ;)
But for now i want to stick with just Irrlicht and "squeeze" the hell out of it 8)
There are some bugs left in the engine which should be fixed and we all should help Nik to get a stable and "bug-free" version.
Emil_halim
Posts: 518
Joined: Tue Mar 29, 2005 9:02 pm
Location: Alex,Egypt
Contact:

Post by Emil_halim »

ok , so i am so sorry if i am bothering you.just want to help.
Post Reply