Archive for category SoftGel

SoftGeL Lesson 10: Lighting and Bumpmapping

Source Code
Lesson10.cpp
coreSDL2.cpp

Header Files
cSGL_Shapes.h
cSGL_Color2.h
cSGL_Perlin.h
cSGL_Texture2.h
cSGL_Font.h
cSGL_2X2Matrix.h
cSGL_Light.h
SoftGL.j.h

Libraries to Link
sdl.lib
sdlmain.lib

You will additionally need fontdef.txt, font2.bmp, font2.mask.bmp, test.bmp and test.bump.bmp.

test.bmp is pure while and the bump is just a grey scale image. You can see from the screenshot how bumpmapping and lighting can turn a perfectly flat texture into something that’s very bumpy. In this tutorial we’ll cover how this effect is achieved.

No Comments

SoftGeL Lesson 9: 2D Translation and Rotation

SoftGeL Lesson 9: 2D Translation and Rotation

Source Code
Lesson9.cpp
coreSDL2.cpp

Header Files
cSGL_Shapes.h
cSGL_Color2.h
cSGL_Perlin.h
cSGL_Texture.h
cSGL_Font.h
cSGL_2X2Matrix.h
SoftGL.i.h

Libraries to Link
sdl.lib
sdlmain.lib

You will additionally need fontdef.txt, font2.bmp and font2.mask.bmp.

The first change is to the cSGL_Color header file. The SGL_RECT and SGL_TRIANGLE classes have been move out of it into the cSGL_Shapes header file. We’ve also added an SGL_POINT class. The new SGL_RECT and SGL_TRIANGLE class then use the SGL_POINT class rather than just using x,y pairs as done in previous lessons. Using operator overloading, we’re able to then peform mathematical operations using these classes which results in easy to follow code since it’s more math based than method based as are other graphics APIs such as OpenGL and DirectX. For example in the SGL_TRIANGLE class:

1
2
3
4
5
6
7
8
9
10
11
	void operator += (SGL_POINT src)
	{
		p1 += src;
		p2 += src;
		p3 += src;
	}
 
	SGL_TRIANGLE operator + (SGL_POINT src)
	{
		return SGL_TRIANGLE(p1 + src, p2 + src, p3 + src);
	}

Now rather than supplying a method such as SGL_TRIANGLE.Move(SGL_POINT) we can simply write SGL_TRIANGLE += SGL_POINT or SGL_TRIANGLE = SGL_TRIANGLE + SGL_POINT.

Aside from simplifying the shape based classes we’ve now also added a matrix class which handles doing 2D transformations. The SGL_2X2Matrix class also uses operator overloading in order to keep the mathematical look of what is going on.

1
2
3
4
5
6
7
	tri2 = tri;	//set temp triangle to original
	trans.SetRotation(deg); //set angle of rotation
	tri2 = trans * tri2; //rotate temp triangle around own axis
	tri2 += SGL_POINT(60,60); //move temp triangle
	trans.SetRotation((360.0f-deg)/2.0f); //set angle of rotation
	tri2 = trans * tri2; //rotate around new axis
	tri2 += SGL_POINT(160,120); //move to final position

tri is the original triangle shape. We don’t want to modify that. Instead we set tri2 equal to tri and then modify tri2. The only transformation that the matrix class does is the rotation. So we set the rotation in degrees and then multiply the transformation matrix by the triangle matrix in order to rotate the triangle. We then add a point to the triangle in order to translate the triangle. Order of operations matters. tri2 * trans is mathematically invalid and won’t work in this code either. Translation before rotation also produces a different output than rotation before translation.

The other changes were done to the SGL_Interface class. Now that we have a triangle class we don’t need to pass x,y pairs into the triangle rendering functions.

1
2
3
4
5
6
7
	void SGL_DrawTriangleSolidFill(SGL_TRIANGLE tri,SGL_Color c)
	void SGL_DrawTriangleGradFill(	SGL_TRIANGLE tri,
							SGL_Color c1,
							SGL_Color c2,
							SGL_Color c3)
	void SGL_DrawTriangleTexture(SGL_TRIANGLE tri, SGL_TRIANGLE tex,
					SGL_Texture * texture,  SGL_Texture * mask = 0)

And that’s it. There’s so much new information that I’m not going to cover it all here. This lesson simply introduces the changes and how to use the end result rather than details on how it all works on the low level. Rather, I’ll be using The Programmer’s Wiki to post the detailed workings of each part.

2×2 Matrix Math - learn how the math behind the rotations works.

Operator Overloading - Learn how operating overloading works.

Both of the above are key parts to how Lesson 9 works. You will need to understand matrix math and operator overloading to fully be able to understand what exactly is going on and be able to proceed as we move into more complicated math.

No Comments

SoftGeL Lesson 8: Displaying Text With a Mask

Source Code
Lesson8.cpp
coreSDL2.cpp

Header Files
cSGL_Color.h
cSGL_Perlin.h
cSGL_Texture.h
cSGL_Font.h
SoftGL.h.h

Libraries to Link
sdl.lib
sdlmain.lib

You will additionally need fontdef.txt, font2.bmp and font2.mask.bmp.

It’s okay for in progress programs to look ugly which this one definitly does. Both the font bitmap and the mask bitmap are low res which results in very blocky rendering. When we make our first real game, we’ll improve the graphics.

The size parameter of the Print() method now specifies the height in pixels the font will be rendered at. We’ve also added a mask parameter with a default value of 0. This allows the cSGL_Font.h header to work with prior tutorials even though the method for loading the font has been changed.

1
	font.LoadFont(textures[1],"fontdef.txt",textures[2]);

textures[2] is used as the mask. If the mask value is zero then the pixel is drawn, otherwise it’s not. The mask must also be the same size and aligned with the texture being rendered. It’s not possible to select a region on the mask different from the religion on the source texture.

1
2
3
4
5
6
7
8
9
10
11
12
13
	void SGL_BlitStretch(SGL_RECT dest, SGL_RECT src,
			SGL_Texture * texture, SGL_Texture * mask = 0)
	{
		...
			dx = (int)(src.x1+(float)(x*xsd)*xratio);
			dy = (int)(src.y1+(float)(y*ysd)*yratio);
 
			if(mask == 0 || mask->GetColorAt(dx,dy).black())
			SGL_PlotPixel((int)dest.x1+x*xdd,
					(int)dest.y1+y*ydd,
					texture->GetColorAt(dx,dy));
		...
	}

With the OR operator, conditions are only checked until a TRUE is found. So, if the mask pointer is zero then the pixel is drawn and no attempt is made to access the GetColorAt method at the mask memory location which would cause a crash. If the mask pointer is non zero then the the color is checked. If the color of the mask is black (0) then the pixel is drawn. The only change to the Print() method is the line which calls the BlitStretch() method. We now reference the mask pointer of the font class and pass it to the BlitStretch() method. We also default the mask pointer value to 0 in the font constructor method.

1
2
3
	...
	SGL_BlitStretch(dest,src,font->font,font->mask);
	...

And that’s it. Masking is actually a very simple thing to do. This logic was applied to the other blit function and the textured triangle function. We now have a nearly complete 2D graphics library. In our next lesson we’ll cover 2D translation and rotation. This will allow us more advanced sprite movement which will allow us a much greater range of game types this library can be used to create.

No Comments

SoftGeL Lesson 7: Displaying Text

Source Code
Lesson7.cpp
coreSDL2.cpp

Header Files
cSGL_Color.h
cSGL_Perlin.h
cSGL_Texture.h
cSGL_Font.h
SoftGL.g.h

Libraries to Link
sdl.lib
sdlmain.lib

The BlitStretch() function has had a bug correction made in SoftGL.g.h header file. This will need to be applied to previous tutorials.

1
2
3
4
5
	...
	texture->GetColorAt(
			(int)(src.x1+(float)(x*xsd)*xratio),
			(int)(src.y1+(float)(y*ysd)*yratio))
	...

In the original lesson the ratio was also being applied to the x1,y1 variables which is incorrect.

You will additionally need fontdef.txt and font.bmp.

Windows supplies functions which can be used to generate font textures at load time. But, since this is software rendering intended to be as universally applicable as possible, we won’t be using those functions. Instead, we’ll be doing it the hard way; with a paint program.

Standard APIs such as DirectX and OpenGL demand that textures be a power of two. We don’t have that limitation. So, instead of fitting the fonts into a power of 2 texture we’re going to put all the characters in one long texture that’s only as long as it needs to be. This allows us to set the top of the texture source to 0 and the bottom of the texture source to the height of the font texture. No vertical calculations needed. Since we’re inputing the numbers by hand this also saves time defining the texture location of each letter.

In order to simplify things further the character locations are stored in a plain text, tab delimited file. The characters don’t need to be in any particular order as the first number is the character number. The above definition and BMP file defines the upper and lower case letters. That’s enough to get you started. It’s hard to miss how blocky the characters look when rendered. This is because 16 pixel characters are being stretched to five times the size. You will need to supply your own high resolution character set. One will most likely not be provided here.

The SGL_Font class is quite simple to use and implement.

1
2
3
4
5
6
	class SGL_Font
	{
	public:
		SGL_Texture * font;
		uint32 charWidth[256],charStart[256];
	...

All we need is a pointer to a texture that will hold the characters (remember that the handler actually loads the textures and we don’t want to intialize a whole texture handler just for this) and then two variables to hold the width of each character and the starting pixel position. The standard ascii table contains 256 characters so that’s what we allow for. This also elimiates the need for bounds checking which speeds things up a bit. The constructor for this class initializes everything to zero. There is no need for a constructor since the texture will be cleared out by the handler outside of this class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	void LoadFont(SGL_Texture * tex, char * def)
	{
		font = tex;
		FILE * defs = fopen(def,"r");
 
		if(!defs)
			return;
 
		int j,k,l;
		while(!feof(defs))
		{
			fscanf(defs,"%i\t%i\t%i",&j,&k,&l);
			if(j<256 && j>=0)
			{
				charStart[j] = k;
				charWidth[j] = l;
			}
		}
		fclose(defs);
	}

To use a texture we simply pass in the pointer to the texture we will be using for the characters. We then pass in the name of the file which defines the location of each of the characters. This allows us to change the font without recompiling the code.

FILE and fscanf are used to read in the formated strings in the definition file. For simplicity we just read in all the file values to integers even though the j variable can only be 0-255. j is the character number, k is the starting x pixel position of the character and l is the width in pixels of the character.

Next we have a series of helper functions to get access to the variables of the font class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
	SGL_Texture * GetTexture()
	{
		return font;
	}
 
	uint32 Height()
	{
		return font->height;
	}
 
	uint32 Width(uint8 letter)
	{
		return charWidth[letter];
	}
 
	uint32 Start(uint8 letter)
	{
		return charStart[letter];
	}

GetTexture() returns the pointer to the texture being used. Height() gives us the stored height of the font. Width() gives us the pixel width of the character and Start() gives us the starting pixel of the character. Finally we go to the SGL_Interface class to actually render the text.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
	void SGL_Print(SGL_Font * font, char * text, float size, SGL_RECT box)
	{
		uint32 j = strlen(text);
		float h = (float)font->Height() * size;
 
		SGL_RECT src, dest;
 
		dest.x1 = box.x1;
		dest.y1 = box.y1;
 
		for(uint32 k = 0; k<j;k++)
		{
			src.x1 = (float)font->Start(text[k]);
			src.x2 = src.x1+(float)font->Width(text[k]);
			src.y1 = 0.0f;
			src.y2 = (float)font->Height();
 
			dest.x2=dest.x1 + (float)font->Width(text[k]) * size;
			dest.y2=dest.y1 + h;
 
			SGL_BlitStretch(dest,src,font->font);
			dest.x1+=(float)font->Width(text[k]) * size;
		}
	}

SGL_Print takes the font class, the text, the size to render and the box which tells the method where to start drawing. As you can see, there is nothing fancy here. All we’re doing is using the size variable to scale the text being printed. If the height of the font texture is 16 pixels and size is 2.0f then the text will be rendered 32 pixels high. This is actually “wrong.” Size should define the final render size and not a relative size based on the size of the texture. A high resolution font texture should render the same size as a low resolution font texture given the same “size” value into this method.

That change will be left as an exercise for the reader until a later tutorial. In future tutorials we’ll also cover handling special characters such as the carriage return and word wrap. We also need to introduce masking so that we can remove the background color of the font and make it transparent.

No Comments

SoftGeL Lesson 6: Textured Triangles

Source Code
Lesson6.cpp
coreSDL2.cpp

Header Files
cSGL_Color.h
cSGL_Perlin.h
cSGL_Texture.h
SoftGL.f.h

Libraries to Link
sdl.lib
sdlmain.lib

For this lesson we’ve simplified some things.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
	...
 
	SGL_TRIANGLE tri, tex, vel;
 
	...
 
	void Lesson_Render()
	{
		SGL.SGL_ClearScreen();
		SGL.SGL_DrawTriangleTexture(tri,tex,textures[0]);
 
		tri.x1 += vel.x1;
		tri.y1 += vel.y1;
		tri.x2 += vel.x2;
		tri.y2 += vel.y2;
		tri.x3 += vel.x3;
		tri.y3 += vel.y3;
 
		if(tri.x1>screenW || tri.x1<0)	vel.x1 = -vel.x1;
		if(tri.y1>screenH || tri.y1<0)	vel.y1 = -vel.y1;
		if(tri.x2>screenW || tri.x2<0)	vel.x2 = -vel.x2;
		if(tri.y2>screenH || tri.y2<0)	vel.y2 = -vel.y2;
		if(tri.x3>screenW || tri.x3<0)	vel.x3 = -vel.x3;
		if(tri.y3>screenH || tri.y3<0)	vel.y3 = -vel.y3;
	}

We’ve introduce the SGL_TRIANGLE class which holds three pairs of x,y values. We can then use that for vertices of a triangle and also our velocity values. Next, instead of explicity setting the velocity values as positive or negative with two if statements, we just negate the velocity value and use only one check.

The DrawTriangleTexture() method is identical to the DrawTriangleGradFill() method except we duplicate some of the math to calculate the texture coordinate.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
		float px1=tri.x1;
		float px2=tri.x3;
		float py1=tri.y1;
		float py2=tri.y3;
 
		float ptx1=tex.x1;
		float ptx2=tex.x3;
		float pty1=tex.y1;
		float pty2=tex.y3;
 
		float dx1 = (tri.x2-tri.x1)/h;
		float dx2 = (tri.x2-tri.x3)/h;
		float dy1 = (tri.y2-tri.y1)/h;
		float dy2 = (tri.y2-tri.y3)/h;
 
		float dtx1 = (tex.x2-tex.x1)/h;
		float dtx2 = (tex.x2-tex.x3)/h;
		float dty1 = (tex.y2-tex.y1)/h;
		float dty2 = (tex.y2-tex.y3)/h;

The extra t signifies the variable is for the texture coordinates. We then do the same thing when actually drawing the triangle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
		for(j=0;j<h;j++)
		{
			dist = sqrtf((px2-px1)*(px2-px1)+(py2-py1)*(py2-py1));
 
			if(dist>0)
			{
				px = px1;
				py = py1;
				ptx=ptx1;
				pty=pty1;
				dx = (px2-px1)/dist;
				dy = (py2-py1)/dist;
				dtx = (ptx2-ptx1)/dist;
				dty = (pty2-pty1)/dist;
 
				for(k=0;k<dist;k++)
				{
					c = texture->GetColorAt((int)ptx,(int)pty);
 
					SGL_PlotPixel((int)px,(int)py,c);
					px+=dx;
					py+=dy;
					ptx+=dtx;
					pty+=dty;
				}
 
				px1+=dx1;
				px2+=dx2;
				py1+=dy1;
				py2+=dy2;
 
				ptx1+=dtx1;
				ptx2+=dtx2;
				pty1+=dty1;
				pty2+=dty2;
			}
		}

And that’s all there is to it. All we’re doing is setting it so there are the same number of steps for the texture scanline as there are for the triangle scanline. It’s the same concept used for the BlitStretch() method.

In the next lesson we’ll finally cover how to create a font and display text to the user. It’s very hard to make games if you can’t display information.

No Comments

SoftGeL Lesson 5: Stretch Blitting

Source Code
Lesson5.cpp
coreSDL2.cpp

Header Files
cSGL_Color.h
cSGL_Perlin.h
cSGL_Texture.h
SoftGL.e.h

Libraries to Link
sdl.lib
sdlmain.lib

Previously xdir2 and ydir2 were used to specify the direction of the second texture. Now they’re used to specify the direction of the bottom right of the destination rectangle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
	void Lesson_Render()
	{
		SGL.SGL_ClearScreen();
		SGL.SGL_BlitStretch(dest,src,textures[0]);
 
		dest.x1+=xdir;
		dest.y1+=ydir;
		dest.x2+=xdir2;
		dest.y2+=ydir2;
		if(dest.x1>screenW)	xdir = -1;
		if(dest.x1<0)	xdir = 1;
		if(dest.y1>screenH)	ydir = -1;
		if(dest.y1<0)	ydir = 1;
 
		if(dest.x2>screenW)	xdir2 = -2;
		if(dest.x2<0)	xdir2 = 2;
		if(dest.y2>screenH)	ydir2 = -2;
		if(dest.y2<0)	ydir2 = 2;
	}

Our texture source rectangle always stays the same but it will be stretched to fit the destination rectangle. Next up is the SGL_BlitStretch method which does all the work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
	void SGL_BlitStretch(SGL_RECT dest, SGL_RECT src, SGL_Texture * texture)
	{
		int x,y;
		int xss = (int)fabs(src.x2-src.x1);
		int yss = (int)fabs(src.y2-src.y1);
		int xds = (int)fabs(dest.x2-dest.x1);
		int yds = (int)fabs(dest.y2-dest.y1);
 
		float yratio = (yds>0) ? (float)yss/(float)yds : 0;
		float xratio = (xds>0) ? (float)xss/(float)xds : 0;
 
		int xsd = (src.x2-src.x1) > 0 ? 1:-1;
		int ysd = (src.y2-src.y1) > 0 ? 1:-1;
		int xdd = (dest.x2-dest.x1) > 0 ? 1:-1;
		int ydd = (dest.y2-dest.y1) > 0 ? 1:-1;
 
		for(x=0;x<xds;x++)
			for(y=0;y<yds;y++)
				SGL_PlotPixel(
				(int)dest.x1+x*xdd,
				(int)dest.y1+y*ydd,
				texture->GetColorAt(
						(int)(((src.x1)+(float)(x*xsd))*xratio),
						(int)(((src.y1)+(float)(y*ysd))*yratio))
					);
	}

Previously we only checked to see what the size of the source rectangle was. Now we also check to see what the size of the destination rectangle is. Next, we find the ratio between the two. Finally we check the direction of both the source rectangle and the destination rectangle. In a regular blit the destination rectangle is used to define only a single point and so it had no direction.

And now we do the actual render. Previously the source retangle defined the size of the area we were drawing. Now the destination rectangle defines the size. The only changes to the PlotPixel call is that the direction is applied to the destination location and we multiply the texture pixel location by the ratio.

And that’s all there is to it. You can now blit any portion of a texture to any size rectangle. On a 1.2Ghz system this function can pump out over 100 rectangles per second.

If we left the 0 check out of the calculation of the y and x ratio this program would still run fine. However, not all OSes/CPUs may be so kind. Historically divide by 0 errors will crash a program. Any time you divide by a variable it’s important to make sure it’s not zero and supply a valid result for the 0 case.

No Comments

SoftGeL Lesson 4: First Blits

Source Code
Lesson4.cpp
coreSDL2.cpp

Header Files
cSGL_Color.h
cSGL_Perlin.h
cSGL_Texture.h
SoftGL.d.h

Libraries to Link
sdl.lib
sdlmain.lib

An additional bug in the SoftGL header has been fixed which wasn’t properly checking whether a pixel was on the screen or not. We’ve also broken the various classes into individual files. cSGL_Color.h also contains SGL_Rect which we will be using for blitting. In this lesson you’ll learn how to do a simple blit which doesn’t do any stretching. First though, we need to pull the texture loading class from other sections into this section. We’re also going to break it into two components: the texture class and then texture handler class.

The reason for this is that by using a handler we can greatly simplify loading and using textures. The handler performs all the texture creation logic while the texture class only holds the actual texture data. The texture handler also allows for any number of textures to be loaded while maintaining random access for the user. This is an advanced programming trick.

First let’s look at the texture class itself:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
	class SGL_Texture
	{
	public:
		int width, height;
		SGL_Color * texture;
		SGL_Texture * next;
 
		SGL_Texture()
		{
			width=height=0;
			texture=0;
			next = 0;
		}
 
		SGL_Texture(uint32 w, uint32 h)
		{
			width=w;
			height=h;
			if(w*h!=0)
				texture = new SGL_Color[width*height];
			else
				texture = 0;
			next = 0;
		}
 
		SGL_Color GetColorAtPer(float x, float y)
		{
			return GetColorAt((int)(x*(float)width),(int)(y*(float)height));
		}
 
		SGL_Color GetColorAt(int x, int y)
		{
			if(x<0 || y<0)
				return texture[0];
			if(x>=width || y>=height)
				return texture[0];
			return texture[x+y*width];
		}
 
		~SGL_Texture()
		{
			if(texture)
				delete [] texture;
			texture = 0;
		}
	};

The default constructor sets the texture to 0. An overloaded constructor allows us to create an empty texture of a given size. This will later be used in conjuction with texture generating functions such as the perlin and mandelbrot methods. You may also have noticed the SGL_Texture * next variable. This class is used by the handler as a texture node in a list of textures. Linked lists cannot be randomly accessed and this is where the trick comes in with the handler.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
	class SGL_Texture_Handler
	{
	private:
		SGL_Texture ** textures;
		SGL_Texture * head, * tail;
		int numTextures;
 
	public:
 
		SGL_Texture_Handler()
		{
			head = 0;
			textures = 0;
			numTextures = 0;
		}
 
		SGL_Texture * operator[](int i)
		{
			if(i<numTextures)
				return textures[i];
			return 0;
		}

SGL_Texture ** textures is used as a dynamic array of pointers to the textures in the linked list. We then overload the [] operator and we look at the array to return the requested texture rather than hunting through the linked list. This is how you can access any linked list with random access. The tradeoff is a small amount of memory to have an array of pointers pointing to the nodes in the list of pointers. And since the array is just a list of pointers, it’s very fast to delete and change the size. Compare this to having a dynamic array of textures. You’d have to copy over all the data of the existing textures just to expand the list by one texture.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
	int SGL_CreateEmptyTexture(uint32 width, uint32 height)
	{
		if(numTextures == 0)
		{
			head = new SGL_Texture(width,height);
			tail = head;
			textures = new SGL_Texture*[1];
			textures[0] = head;
			numTextures = 1;
		}
		else
		{
			numTextures++;
			delete [] textures;
			textures = new SGL_Texture*[numTextures];
 
			tail->next = new SGL_Texture(width,height);
			tail = tail->next;
 
			int j=0;
 
			SGL_Texture * temp = head;
			while(temp != 0)
			{
				textures[j]=temp;
				temp = temp->next;
				j++;
			}
		}
 
		return numTextures -1;
	}

Just in case you need to store the number of various textures, the handler will return the number of the texture it creates. Our CreateEmptyTexture() method handles adding a new texture to the end of the list and adjusts the size of array of pointers and recreates the list. And that’s all there is to it. We now have a linked list of textures with a supporting array of pointers to give us random access.

1
2
3
4
5
6
7
8
9
10
11
	int LoadBMP_To_Texture(char *filename)
	{
		int i = SGL_CreateEmptyTexture(0,0);
		LoadBMP_To_Texture(i,filename);
		return i;
	}
 
	void LoadBMP_To_Texture (uint32 texture, char *filename)
	{
		...
	}

The LoadBMP_To_Texture method has already been covered in prior sections so I won’t repeat it all here. The main thing is that we’ve now overloaded it. We can pass just a filename, in which case a new texture is created or we can pass an existing texture number and that texture will have the file loaded to it. Then in the lesson file, to load textures we simply use:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	#include <stdlib.h>
	#include <time.h>
	#include <math.h>
	#include <string.h>
	#include <stdio.h>
 
	#define uint8 unsigned char
	#define uint32 unsigned int
 
	#include "cSGL_Color.h"
	#include "cSGL_Perlin.h"
	#include "cSGL_Texture.h"
	#include "SoftGL.d.h"
 
	SGL_Interface SGL;
	SGL_Texture_Handler textures;
	...
	textures.LoadBMP_To_Texture("test.bmp");
	textures.LoadBMP_To_Texture("test2.bmp");
	...

Then, to render the texture or a portion of it, we simply use:

1
2
3
	SGL.SGL_ClearScreen();
	SGL.SGL_Blit(dest,src,textures[0]);
	SGL.SGL_Blit(dest2,src2,textures[1]);

Where the dest and src variables are of the type SGL_Rect. The numbers are in the order that the textures are loaded. The first texture loaded will be number 0 and so on. For the SGL_Blit() method does not stretch the texture. It only uses x1 and y1 of the dest rect. The src rect defines the area of the texture to render. If the area goes outside the bounds of the texture, then the color of the first pixel in the texture will be rendered at the out of bound portions. If x2 is less than x1 then the texture will be flipped horizontaly. The same is true for y1 and y2. This makes it unnecessary to provide flipped versions of all the tiles. You can flip them at render time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
	void SGL_Blit(SGL_RECT dest, SGL_RECT src, SGL_Texture * texture)
	{
		int x,y;
		int xs = (int)fabs(src.x2-src.x1);
		int ys = (int)fabs(src.y2-src.y1);
		int xsd = (src.x2-src.x1) > 0 ? 1:-1;
		int ysd = (src.y2-src.y1) > 0 ? 1:-1;
 
		for(x=0;x<xs;x++)
			for(y=0;y<ys;y++)
				SGL_PlotPixel(
				(int)dest.x1+x,
				(int)dest.y1+y,
				texture->GetColorAt(
						(int)(src.x1)+x*xsd,
						(int)(src.y1)+y*ysd)
					);
	}

ysd and xsd determine the direction the texture is facing. This is how we do the flipping. xs and ys are the absolute size of the source area. We then just use a double loop to render the appropriate pixels in the appropriate locations.

And that’s it. You now have a very fast simple blitter so you can put tiles on the screen and animate them. You almost have all the rendering tools you need to make a side scroller. The only thing missing is a font class so we can render text. Very important if you want to display messages to the user. We’ll cover that in the next lesson.

No Comments

SoftGeL Lesson 3: Fun with Perlin Noise

Source Code
Lesson3.cpp
coreSDL2.cpp

Header Files
SoftGL.c.h

Libraries to Link
sdl.lib
sdlmain.lib

Previous the SGL_Color class had the colors reversed to BGR instead of RGB. That has now been corrected.

1
2
3
4
	uint32 GetColor()
	{
		return (r<<16) + (g<<8) + b;
	}

The SGL_Color class also now contains the GetColor method which returns the color value in a 32 bit unsigned integer. This allows the composite value to be grabbed more easily. Using bitshifts instead of multiplying also helps speed up the code.

Tutorial on Perlin Noise. This is where I got the basis for the code used in this lesson. I’m not going to go into great detail on how perlin noise works but it’s actually pretty simple. Essentially a fake random number generator is used. Passing in the same x,y pair will always return the same floating point value between -1 and 1. The perlin noise code then simply smooths out the randomness which produces the wavey lines you see. The PerlinNoise_2D(x,y) method takes a fractional pixel location (in the SGL class we divide the x location by the width of the screen and the y location by the height of the screen) and returns a value from -1 to 1 which we then convert into a valid color value.

As you will see when you run it, this code is not very fast. We will be using Perlin Noise and Fractals in our texture generation code later. For now they’re just demonstrating some of the things you can do with low level graphics.

And that ends this lesson. Rather than regurgitate the information found on the above mentioned site, I’ll let you dig further into how perlin noise works on your own. In our next lesson we’ll start loading texture files and begin work on an advanced texture class.

No Comments

SoftGeL Lesson 2: Fun with Fractals

Source Code
Lesson2.cpp
coreSDL2.cpp

Header Files
SoftGL.b.h

Libraries to Link
sdl.lib
sdlmain.lib

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
	void SGL_MandelbrotFractal(double rMin, double rMax, double iMin, double iMax, int N)
	{
		//based on http://www.devx.com/amd/Article/21827/2046
		int i,j,q;
		double zReal,zImag,xReal,xImag,a,b,c;
		xReal = 0.0f;
		xImag = 0.0f;
		double delta = (rMax - rMin)/double(screenW);
		for(i=0,zReal = rMin;i<screenW;i++,zReal+=delta)
			for(j=0,zImag = iMin;j<screenH;j++,zImag+=delta)
			{
				xReal = zReal;
				xImag = zImag;
				for (q = 0; q < N; q++)
				{
					a = xReal * xReal;
					b = xImag * xImag;
					c = 2.0 * xReal * xImag;
					xReal = a - b + zReal;
					xImag = c + zImag;
					if((a + b) > 4.0)
					{
						SGL_PlotPixel(i,j,
			SGL_Color(0,(uint8)xImag,(uint8)xReal));
						q=N;
					}
				}
			}
	}

This is the simple function which actually renders our fractal with the given parameters. rMin/rMax and iMin define the area of the fractal we will be looking at. iMax doesn’t actually do anything. We could use it to stretch the area of the Mandelbrot to fit the view window. As it is, the Mandelbrot is scaled proportionaly. If you want to change how the fractal is colored, simply play with the SGL_PlotPixel line and change what variables are used and how to calculate the component colors.

In the main lesson file we initialize our starting point in the Main() function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
	...
 
	double r,i;
 
	void Lesson_Main()
	{
		screenW = 320;
		screenH = 240;
		SGL.SGL_Init(screenW,screenH);
		srand(time(0));
 
		r = 2.001;
		i = 2.795;
	}

Then in the Render() function we handle the zoom in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
	void Lesson_Render()
	{
		SGL.SGL_ClearScreen();
		SGL.SGL_MandelbrotFractal(-r, r, -i, i, 64);
		if(r>0.02)
		{
			r-=0.01;
			i-=0.01;
		}
		else
			if(r>0.001)
			{
				r-=0.0001;
				i-=0.0001;
			}
			else
				if(r>0.0001)
				{
					r-=0.00001;
					i-=0.00001;
				}
	}

As you can see, the farther we go the slower we zoom. By changing r and i the same amount we go straight down. If we were to make them different we would go down at an angle. Play around and see what you can do.

No Comments

SoftGeL Lesson 1: Getting Started

Source Code
Lesson1.cpp
coreSDL2.cpp

Header Files
SoftGL.a.h

Libraries to Link
sdl.lib
sdlmain.lib

The first lesson demonstrates the usage of the SoftGeL library. The lesson file itself does not define any graphics rendering routines. Those routines will all be contained in the SoftGL.*.h where * is the current version letter. In order to avoid needing to link the SoftGL library with the CoreSDL file, the lesson file must provide a minimum of functions to link the core with SoftGeL. This will allow you to not have to make any changes to the core file in order to compile various lessons with it.

1
2
3
4
5
6
7
8
	//defined in lesson file
	extern int screenW;
	extern int screenH;
 
	extern void Lesson_Main();
	extern void Lesson_Render();
	extern int Lesson_GetPixel(int x, int y);
	extern void Lesson_Quit();

screenW and screenH define the screen resolution. Since we are working in windowed mode, these can be anything. You are not restricted to standard resolutions such as 640×480 and 800×600. Main() initializes SoftGeL and any variables that the lesson file is using. Render() handles rendering the scene. GetPixel(x,y) retrieves the 24bit color value of the pixel at x,y. Quit() frees up any memory used by the lesson file and also cleans up SoftGeL. The following is the bare minimum a lesson file will need to be compatible with the CoreSDL2 and utilize SoftGeL.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	#include <stdlib.h>
	#include <time.h>
	#include "SoftGL.a.h"
 
	SGL_Interface SGL;
 
	int screenW;
	int screenH;
 
	void Lesson_Main()
	{
		screenW = 320;
		screenH = 240;
		SGL.SGL_Init(screenW,screenH);
		srand(time(0));
	}
 
	int Lesson_GetPixel(int x, int y)
	{
		return SGL.SGL_GetPixel(x, y);
	}
 
	void Lesson_Quit()
	{
		SGL.SGL_Quit();
	}
 
	void Lesson_Render()
	{
		...
	}

The currently defined SoftGeL graphics functions are as follows:

1
2
3
4
5
6
7
8
9
10
	SGL_PlotPixel(int x, int y, SGL_Color &v)
	SGL_EFLA_DrawLine(float x,float y,float x2,float y2,SGL_Color c)
	SGL_DrawLine(float x,float y,float x2,float y2,SGL_Color c)
	SGL_DrawTriangleSolidFill(float x1,float y1,
				float x2,float y2,
				float x3,float y3,
				SGL_Color c)
	SGL_DrawTriangleGradFill(float x1,float y1, SGL_Color c1,
						float x2,float y2, SGL_Color c2,
						float x3,float y3, SGL_Color c3)

How these functions work has already been covered in the javascript and PHP tutorials so I won’t repeat that information here. Rather, I’ll simply cover their usage here.

PlotPixel takes an x and y coordinate along with a color value and plots a single pixel of that color. EFLA_DrawLine stands for “Extremely Fast Line Algorithm” which may have been true years ago. It’s no longer true. The slow down for rendering a line is that you have to know how long the line is and that generally takes a square root. This is historically very slow but ever since the advent of MMX and 3DNow! the sqrt C function utilizes hardware rather than software to calculate the value. The net result is that you can draw a line using the sqrt function and not take a speed hit. DrawLine is the simplest method of rendering a line and uses the sqrtf function. On a 1.2Ghz Duron it can pump out over 100 triangles per second. Compare to modern graphics hardware which can pump out 10’s of thousands of triangles per second.

DrawTriangleSolidFill draws a triangle of a solid color using the DrawLine function. DrawTriangleGradFill draws a filled triangle will colored vertices using the PlotPixel function. The screenshot above shows the result of drawing hundreds of random triangles without clearing the screen.

And that completes this introductory tutorial. The next lesson will continue translating the javascript and PHP tutorials to continue building up the SoftGeL library with the basics before moving onto more advanced topics.

No Comments