Tunnel: C/C++ Version
/****************************************************************************\
* *
* Tunnel.c *
* *
* This program draws a Gouraud-shaded tunnel and allows the viewer to move *
* through the tunnel using keyboard controls. *
* *
\****************************************************************************/
#include
#define vbWidth 640
#define vbHeight 480
typedef struct point3d
{
double x;
double y;
double z;
} POINT3D;
// four sides of a 20x40x100 tunnel, defined in 3D world coordinates
POINT3D Floor[] = {
{-10.0, 0.0,100.0},
{-10.0, 0.0,200.0},
{ 10.0, 0.0,200.0},
{ 10.0, 0.0,100.0}
};
POINT3D WestSide[] = {
{-10.0, 0.0,100.0},
{-10.0, 40.0,100.0},
{-10.0, 40.0,200.0},
{-10.0, 0.0,200.0}
};
POINT3D EastSide[] = {
{ 10.0, 0.0,100.0},
{ 10.0, 0.0,200.0},
{ 10.0, 40.0,200.0},
{ 10.0, 40.0,100.0}
};
POINT3D Ceiling[] = {
{-10.0, 40.0,100.0},
{ 10.0, 40.0,100.0},
{ 10.0, 40.0,200.0},
{-10.0, 40.0,200.0}
};
// RGB color values at each vertex of each side
BYTE FloorRGB[] = {192,192,192, 64, 64, 64, 64, 64, 64, 192,192,192};
BYTE WestSideRGB[] = { 32, 32,255, 32, 32,255, 32, 32, 96, 32, 32, 96};
BYTE EastSideRGB[] = { 32, 32,255, 32, 32, 96, 32, 32, 96, 32, 32,255};
BYTE CeilingRGB[] = {192,192,192, 192,192,192, 64, 64, 64, 64, 64, 64};
// for convenience, an array of pointers to each of the four sides
POINT3D *Faces[] = {Floor,WestSide,EastSide,Ceiling};
// a similar array of pointers to each side's RGB values
BYTE *FacesRGB[] = {FloorRGB,WestSideRGB,EastSideRGB,CeilingRGB};
LRESULT CALLBACK WindowProc(HWND,UINT,WPARAM,LPARAM);
void CheckForMovement(void);
void DrawTunnel(void);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdParam, int iCmdShow)
{
static char szAppName[] = "FGtunnel";
HWND hWnd;
MSG msg;
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wndclass.lpfnWndProc = WindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
RegisterClassEx(&wndclass);
hWnd = CreateWindow(szAppName, // window class name
"Gouraud-Shaded Tunnel", // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hWnd,iCmdShow);
UpdateWindow(hWnd);
// The message loop processes entries placed in the message queue.
// When no message is ready, call CheckForMovement() to check if
// we want to perform movement.
while (TRUE)
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
CheckForMovement();
}
return msg.wParam;
}
/****************************************************************************\
* *
* WindowProc() *
* *
\****************************************************************************/
HDC hDC;
HPALETTE hPal;
int hVB;
UINT cxClient, cyClient;
int hZB;
LRESULT CALLBACK WindowProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
int vbDepth;
switch (iMsg)
{
case WM_CREATE:
hDC = GetDC(hWnd);
fg_setdc(hDC);
hPal = fg_defpal();
fg_realize(hPal);
fg_vbinit();
vbDepth = fg_colors();
if (vbDepth < 16) vbDepth = 16;
fg_vbdepth(vbDepth);
hVB = fg_vballoc(vbWidth,vbHeight);
fg_vbopen(hVB);
fg_vbcolors();
hZB = fg_zballoc(vbWidth,vbHeight);
fg_zbopen(hZB);
fg_setcolor(-1);
fg_fillpage();
fg_3Dviewport(0,vbWidth-1,0,vbHeight-1,1.0);
fg_3Drenderstate(FG_ZBUFFER | FG_ZCLIP);
fg_3Dlookat(0.0,10.0,50.0,0.0,10.0,100.0);
return 0;
case WM_PAINT:
BeginPaint(hWnd,&ps);
fg_vbscale(0,vbWidth-1,0,vbHeight-1,0,cxClient-1,0,cyClient-1);
EndPaint(hWnd,&ps);
return 0;
case WM_SETFOCUS:
fg_realize(hPal);
InvalidateRect(hWnd,NULL,TRUE);
return 0;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0;
case WM_DESTROY:
fg_vbclose();
fg_zbfree(hZB);
fg_vbfree(hVB);
fg_vbfin();
DeleteObject(hPal);
ReleaseDC(hWnd,hDC);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,iMsg,wParam,lParam);
}
/****************************************************************************\
* *
* CheckForMovement() *
* *
* The CheckForMovement() function checks for key presses that control the *
* user's movement, and if required redraws the tunnel viewed from the new *
* camera position. It is called from the WinMain() message loop when there *
* are no messages waiting. *
* *
\****************************************************************************/
void CheckForMovement()
{
static BOOL Redraw = TRUE;
// up arrow moves viewer forward
if (fg_kbtest(72))
{
fg_3Dmoveforward(2.0);
Redraw = TRUE;
}
// down arrow moves viewer backward
else if (fg_kbtest(80))
{
fg_3Dmoveforward(-2.0);
Redraw = TRUE;
}
// right arrow turns viewer to the right
else if (fg_kbtest(77))
{
fg_3Drotateright(6*10);
Redraw = TRUE;
}
// left arrow turns viewer to the left
else if (fg_kbtest(75))
{
fg_3Drotateright(-6*10);
Redraw = TRUE;
}
// if the viewer's position or rotation changed, redraw the tunnel
if (Redraw)
{
// prepare the z-buffer for the next frame
fg_zbframe();
// erase the previous frame from the virtual buffer
fg_setcolor(-1);
fg_fillpage();
// draw the tunnel
DrawTunnel();
// display what we just drew
fg_vbscale(0,vbWidth-1,0,vbHeight-1,0,cxClient-1,0,cyClient-1);
Redraw = FALSE;
}
}
/****************************************************************************\
* *
* DrawTunnel() *
* *
* Draws each of the tunnel's four sides in 3D world space. *
* *
\****************************************************************************/
void DrawTunnel()
{
register int i;
for (i = 0; i < 4; i++)
{
fg_3Dshade((double *)Faces[i],(char *)FacesRGB[i],4);
}
}
|