# Pictures paint a thousand dwords, Win32 graphics



## ctrain (Feb 26, 2011)

Graphics stuff is fun, rewriting some stuff for giggles after a reformat, here's a classic 2d starfield effect. A 3d starfield is next then a proper 3d engine, all software rendered for good measure.


Drawing is handled via DIB Sections which is the fastest way within GDI to draw. It's not structured at all, just to show off the effect.



```
#include <Windows.h>
#include <stdlib.h>
#include "types.h" //typedefs for u32 etc etc.

//////////////////////////////////////////////////////////////////////////

const u32	width		= 320;
const u32	height		= 180;

const u32	num_stars	= 48;
const u32	speed		= 4;
const u8	scale		= 255 / speed;

HWND	hwnd;
HDC		hdc;
HDC		hdcMem;
HBITMAP dib;
HBITMAP hbmOld;
void*	data;

void gfxCreate(u32 w, u32 h)
{
	s32 dong = (s32)h;

	BITMAPINFO bi			= { 0 };
	bi.bmiHeader.biSize     = sizeof(BITMAPINFO);
	bi.bmiHeader.biWidth    = w;
	bi.bmiHeader.biHeight   = -dong;		//top down
	bi.bmiHeader.biPlanes   = 1;
	bi.bmiHeader.biBitCount = 32;

	hwnd	= GetForegroundWindow();
	hdc		= GetDC(hwnd);
	hdcMem	= CreateCompatibleDC(hdc);
	dib		= CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, (void**)&data, 0, 0);
	hbmOld	= (HBITMAP)SelectObject(hdcMem, dib);
}

void gfxDestroy()
{
	SelectObject(hdcMem, hbmOld);	//have to restore hdcMem to default state
	DeleteObject(dib);				//bye
	ReleaseDC(hwnd, hdc);
	DeleteDC(hdcMem);
}

void gfxSetPixel(u32 x, u32 y, u32 c)
{
	if((x | y) >= 0 && x < width  && y < height)
	{
		u32* dst	= (u32*)data + (y * width + x);
		*dst		= c;
	}	
}

struct Star
{
	s32 x;
	s32 y;
	u32 speed;
};

int main()
{
	gfxCreate(width, height);

	Star*	stars	= (Star*)malloc(sizeof(Star) * num_stars);
	for(s32 i = 0; i < num_stars; i++)
	{
		stars[i].x		= rand() % width;
		stars[i].y		= rand() % height;
		stars[i].speed	= rand() % speed + 1;	//what fun is a star that doesn't move?
	}

	while(!GetAsyncKeyState(VK_ESCAPE))
	{
		for(u32 i = 0; i < num_stars; i++)
		{			
			u8	tint = (u8)(scale * stars[i].speed);	//slower stars are dimmer

			for(u32 j = 0; j < (stars[i].speed * 3); j++)//make a tail for the stars, multiply for longer tail
			{		
				u8	temp	= (u8)(tint / (j + 1));
				u32 color	= RGB(temp, temp, temp);	//hack-around because of no alpha blending yet
														//as such, overlapping stars block each other atm

				gfxSetPixel(stars[i].x + j, stars[i].y, color);		
			}

			if(stars[i].x <= 0)
				stars[i].x = width;
			else
				stars[i].x -= stars[i].speed;

		}

		Sleep(10);	//100 fps, not a good way to cap fps but ok for this
		BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
		memset(data, 0, (width * height) * 4);
	}

	free(stars);
	gfxDestroy();
	return 0;
}
```

http://dl.dropbox.com/u/10565193/sf.rar


----------



## streetfighter 2 (Feb 26, 2011)

_As the Chinese say, 1001 [d]words is worth more than a picture._ 

Pretty cool stuff there.  Since I don't have your "types.h" I just added:

```
#define u32    DWORD
#define u8     BYTE //unsigned char included in windows.h
#define s32    int
```

I compiled it with mingw (g++) and it runs, though awkwardly, with a file size around 28kB.  I think if I compiled it with a C compiler it'd be closer to the 2 byte binary in your rar.  I don't do much stuff with GDI so it's neat to see a novel usage like this.


----------



## ctrain (Feb 26, 2011)

Your typedefs are right, but here it is anyway.


```
typedef unsigned char		u8;
typedef unsigned short		u16;
typedef unsigned int		u32;
typedef unsigned __int64	u64;
typedef char			s8;
typedef short			s16;
typedef int			s32;
typedef __int64			s64;
typedef float			f32;
typedef double			f64;
```

I use MSVC as the compiler, so I'm not sure how it behaves elsewhere. The file size is the result of some rather questionable trickery that might not work elsewhere. If I remember right, the C-runtime usually comes along for the ride when you build as it does various things for you like helps set up before main() is actually started.

If you abandon the CRT you will probably get the linker complaining about main() among other things. 
The linker expects mainCRTStartup() as the real entry point so... you can define it yourself (just to return main()) and it will be happy and that initialization code will be gone. I'm not sure what kind of black magic actually goes on behind the scenes, but as far as I have ever seen, it's nothing that has ever caused a problem with not being there. You can cheese it even further by linking against msvcrt.lib, the CRT is a system file unlike the C++ RT. As a bonus your final compiled result may also set off some virus scanners because for whatever reason a couple seem to find it weird.


As for a 3d version of the starfield, I spent some time last night going over the math again, the transform can seem confusing but pretty simple in actuality. To go from 3d world space to 2d screen space, you can effectively simplify it to this if your camera is at the world origin (0, 0, 0):

The camera is stationary so it'll be taking advantage of this simplification.


```
x' = x / z;

... but since we want the center of the screen to function as 0, 0 it needs to be offset.

x' = (x / z) + half_screen_width;
```

The problem is that z needs to be in the range for 0.0 to 1.0 for this work, and since I kept z in line with the way I kept the other coordinates I have to scale it into that range, which is simple.


```
stars[i].z / max_depth (i used 256)
```
So given some random numbers for an example...


```
(-11 / (190 / 256)) + 64
```

Can be rearranged to trade a division for a multiplication which will be faster... and we can also get the reciprocal of stars_.z to turn that into a multiplication as well. Here it's easy to see that the further away it is from the max depth, the closer it is to the camera, and the further it will be scaled away from the center.



		Code:
	

(-11 * (256 / 190)) + 64




		Code:
	

rcp = 1 / 190;

(-11 * (256 * rcp)) + 64


I'll post the binary / source for the 3d version later.

Here's the binary. I decided to wait to post the source as I want to cover something else, alpha blending. The 3d effect is correct and there but it doesn't look very good. The stars seem to come out of nowhere which kills the illusion. I'll show a fast alpha blending method which can be used to blend against the background depending on depth so they will smoothly fade into existence. Also it will look better scaling the stars depending on how close they are.

http://dl.dropbox.com/u/10565193/3dsf.rar_


----------

