2007. január 8., hétfő
OpenGL I: Hello World
Problem/Question/Abstract:
Most people know Delphi as a RAD tool to create database applications, Delphi programmers know that with Delphi you can do EVERYTHING
Answer:
There's quite some people out there doing great efforts to promote OpenGL and DirectX technologies with Delphi;
I will mention this, as I got the base code (and fixed it a little bit) from here:
http://nehe.gamedev.net/
In this article I will show you the base code to create fast and small Delphi-OpenGL applications
I would like to comment that graphics programming is not easy, you will need some knowledge about math and a lot of reading, is like learning a new languaje (a hard one)
First, we will be using no forms (to make application small) and therefore obviously no components (we're going to do this as real programmers =o) )
Our application will consist of the "project source" and one unit In our unit we are just going to create a record to hold some of the application values and some simple constants that are explained in detail here's the "header" of our unit (sorry, no classes or objects):
type
TGLWindow = record
Active: Boolean;
//Window Active Flag (False is minimized, so we don't draw stuff when minimized)
ExitGame: Boolean; //The main loop is based on this variable
Keys: array[0..255] of Bool; //Array Used For The Keyboard Routine
Fullscreen: Boolean; //Fullscreen Flag
MouseLButton: Integer;
MouseRButton: Integer; //Left or right buttons pressed? (0 or 1)
MouseX: Integer;
MouseY: Integer;
MouseZ: Integer;
//Used when right button is pressed (up and down move in and out in the Z axis)
end;
{ All User Variables Here }
var
GS: TGLWindow;
const
POS_X = 100; //Position of window (only when NOT in fullscren mode)
POS_Y = 100;
RES_X = 640; //Resolution
RES_Y = 480;
RES_BITS = 16; //16 bits resolution
WIN_TITLE = 'My Game'; //Title for our window
Then from our unit we need to export this function:
function WinMain(hInstance: HINST; hPrevInstance: HINST; lpCmdLine: PChar; nCmdShow:
Integer): integer; stdcall;
and we will also need these variables (private to our unit, so after implementation):
var
h_RC: HGLRC; //Permanent Rendering Context
h_DC: HDC; //Private GDI Device Context
h_Wnd: HWND; //Holds Our Window Handle
This function basically does everything, initializes the window, draws our stuff, process messages, and when you're done destroys the window. Ok, now we need all the procedures to initialize, process messages, etc...
Here are the functions and some explanations (I'll just list the functions and later will put the actual implementation of each one)
function DrawGLScene(): Bool; { All Rendering Done Here }
procedure ReSizeGLScene(const Width: GLsizei; Height: GLsizei);
{ Resize and Initialize The GL Window }
function InitGL(const Width, Height: Glsizei): Bool;
{ All Setup For OpenGL Goes Here }
//WndProc handles all the messages coming to our window
function WndProc(hWnd: HWND; //Handle For The Window
message: UINT; //Message For This Window
wParam: WPARAM; //Additional Message Information
lParam: LPARAM): //Additional Message Information
LRESULT; stdcall;
{in the CreateWindow we do:
- Register the class window
- Create the window
- Get a Device Context (DC)
- Create a Rendering Context (RC) }
function CreateGLWindow(Title: PChar; PosX, PosY: Integer; const Width,
Height, Bits: Integer; const FullScreenFlag: Bool): Bool; stdcall;
{In the KillWindow we do (obviously the opposite of the CreateWindow and in reverse order):
- Restore the display settings (we need to do this even if something else fails)
- Delete the Rendering Context (RC)
- Release the Device Context (DC)
- Destroy the Window
- Unregister the class window }
procedure KillGLWindow; { Properly Kill the Window }
//WinMain is the actual Main Program (gets called from the actual Main.dpr)
function WinMain(hInstance: HINST; hPrevInstance: HINST; lpCmdLine: PChar;
nCmdShow: Integer): integer; stdcall;
That's all you will need to get started, here's the implementation of these procedures/functions, take a good look at WinMain and WndProc, they show some good stuff even for not graphic applications, you could use them to create small programs that do not require windows...
oh... and one last thing... the "hello world" program in OpenGL won't even show the words "Hello World" (WHAAAT??)
the thing is that outputing text to the screen in OpenGL is a little advanced and I will show it to you in later articles (if you are interested that is). The purpose of this article is to show you the base code and hopefully you will get to understand it and then from there we can concentrate in the OpenGL stuff, so... I will just show you how to create a simple rectangle =o (,but don't be dissapointed, as I say OpenGL is not easy and you won't be creating the next QUAKE in the next 3 months, first you need to create a rectangle, a triangle... a circle ...and theeeen... eventually you will get there (if you really persist)
with no more preambule, here's the code:
//---------------------------------------------------------//
// //
// Original Copyrights: Daniel Vivas //
// daniel@vivas.com.br //
// //
// Main Game Unit //
// Ported To Delhi By: Bryce TeBeest //
// Assistance Provided By: JP Krauss //
// //
// Taken from Jeff Molofi (NEHE) WebSite //
// http://nehe.gamedev.net //
// //
// Some fixes and comments by: EberSys //
//---------------------------------------------------------//
unit oglMain;
interface
uses
Classes,
Messages,
Windows,
OpenGL;
type
TGLWindow = record
Active: Boolean;
//Window Active Flag (False is minimized, so we don't draw stuff when minimized)
ExitGame: Boolean; //The main loop is based on this variable
Keys: array[0..255] of Bool; //Array Used For The Keyboard Routine
Fullscreen: Boolean; //Fullscreen Flag
MouseLButton: Integer;
MouseRButton: Integer; //Left or right buttons pressed? (0 or 1)
MouseX: Integer;
MouseY: Integer;
MouseZ: Integer;
//Used when right button is pressed (up and down move in and out in the Z axis)
end;
{ All User Variables Here }
var
GS: TGLWindow;
const
POS_X = 100; //Position of window (only when NOT in fullscren mode)
POS_Y = 100;
RES_X = 640; //Resolution
RES_Y = 480;
RES_BITS = 16; //16 bits resolution
WIN_TITLE = 'My Game'; //Title for our window
{-----------------------------------------------------------}
{ Public Procedures: }
function WinMain(hInstance: HINST; hPrevInstance: HINST; lpCmdLine: PChar; nCmdShow:
Integer): integer; stdcall;
{-----------------------------------------------------------}
implementation
var
h_RC: HGLRC; //Permanent Rendering Context
h_DC: HDC; //Private GDI Device Context
h_Wnd: HWND; //Holds Our Window Handle
{-----------------------------------------------------------}
function DrawGLScene(): Bool; { All Rendering Done Here }
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); //Clear Screen and Depth Buffer
glLoadIdentity(); //Reset The View (move to 0, 0, 0)
glTranslatef(0.0, 0.0, -6.0); // Move Right 1.5 Units And Into The Screen 6.0
glColor3f(0.0, 0.5, 0.5);
glBegin(GL_QUADS); // Draw A Quad
glVertex3f(-0.5, 0.5, 0.0); // Top Left
glVertex3f(0.5, 0.5, 0.0); // Top Right
glVertex3f(0.5, -0.5, 0.0); // Bottom Right
glVertex3f(-0.5, -0.5, 0.0); // Bottom Left
glEnd();
DrawGLScene := True
end;
procedure ReSizeGLScene(const Width: GLsizei; Height: GLsizei);
{ Resize and Initialize The GL Window }
var
fWidth, fHeight: GLFloat;
begin
if (Height = 0) then //Prevent Divide by Zero
Height := 1; //Be Setting Height at One
glViewport(0, 0, Width, Height);
//Reset The Current Viewport and Perspective Transformation
glMatrixMode(GL_PROJECTION); //Select The Projection Matrix
glLoadIdentity(); //Reset The Project Matrix
fWidth := Width;
fHeight := Height;
gluPerspective(45.0, fWidth / fHeight, 0.1, 100);
//Calculate the Aspect Ratio of the Window
glMatrixMode(GL_MODELVIEW); //Select The ModelView Matrix
end;
{ All Setup For OpenGL Goes Here }
function InitGL(const Width, Height: Glsizei): Bool;
var
fWidth, fHeight: GLfloat;
begin
glClearColor(0.0, 0.0, 0.0, 0.0); //Black Background
glClearDepth(1.0); //Depth Buffer Setup
glDepthFunc(GL_LESS); //Text
glEnable(GL_DEPTH_TEST); //Enables Depth Testing
glShadeModel(GL_SMOOTH); //Enables Smooth Color Shading
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); //reset the View (move to 0, 0, 0)
fWidth := Width;
fHeight := Height;
gluPerspective(45.0, fWidth / fHeight, 0.1, 100);
//Calculate Aspect Ratio Of The Window
glMatrixMode(GL_MODELVIEW)
end;
//WndProc handles all the messages coming to our window
function WndProc(hWnd: HWND; //Handle For The Window
message: UINT; //Message For This Window
wParam: WPARAM; //Additional Message Information
lParam: LPARAM): //Additional Message Information
LRESULT; stdcall;
begin
if message = WM_SYSCOMMAND then
case wParam of //Check System Calls
SC_SCREENSAVE, SC_MONITORPOWER:
//Screensaver Trying To Start, Monitor Trying To Enter Powersave
begin
Result := 0;
exit
end
end;
case message of //Tells Windows We Want To Check Message
WM_ACTIVATE:
begin
if (Hiword(wParam) = 0) then //Check Minimization State
GS.Active := True
else
GS.Active := False; //when Active is False we don't draw anything
Result := 0;
end;
WM_CLOSE: //Did we get a close message?
begin
PostQuitMessage(0); //Send A Quit Message
Result := 0; //Return To The Message Loop
end;
WM_KEYDOWN: //Is A Key Being Held Down?
begin
GS.Keys[wParam] := True;
Result := 0; //Return To The Message Loop
end;
WM_KEYUP: //Is A Key Being Released?
begin
GS.Keys[wParam] := False;
Result := 0;
end;
WM_SIZE: //Resize scene
begin
ReSizeGLScene(LOWORD(lParam), HIWORD(lParam)); //LoWord=Width, HighWord=Height
Result := 0;
end;
WM_LBUTTONDOWN: //(mouse) Left button pressed
begin
ReleaseCapture(); //Need Them Here, Because If Mouse Moves Off
SetCapture(h_Wnd); //Window and Returns, It Needs To Reset Status
GS.MouseLButton := 1;
GS.MouseX := LOWORD(lParam);
GS.MouseY := HIWORD(lParam);
end;
WM_LBUTTONUP: //(mouse) Left button released
begin
ReleaseCapture();
GS.MouseLButton := 0;
GS.MouseX := 0;
GS.MouseY := 0;
Result := 0;
end;
WM_RBUTTONDOWN: //(mouse) Right button pressed
begin
ReleaseCapture();
SetCapture(h_Wnd);
GS.MouseRButton := 1;
GS.MouseZ := HIWORD(lParam);
Result := 0;
end;
WM_RBUTTONUP: //(mouse) Right button released
begin
ReleaseCapture();
GS.MouseRButton := 0;
Result := 0
end
else
{ Pass All Unhandled Messages TO DefWinProc }
Result := DefWindowProc(hWnd, message, wParam, lParam)
end //case message of
end;
{In the KillWindow we do (obviously the opposite of the CreateWindow and in reverse order):
- Restore the display settings (we need to do this even if something else fails)
- Delete the Rendering Context (RC)
- Release the Device Context (DC)
- Destroy the Window
- Unregister the class window }
procedure KillGLWindow; { Properly Kill the Window }
begin
if (GS.FullScreen) then
begin //Are We In FullScreen Mode?
ChangeDisplaySettings(devmode(nil^), 0); //Switch Back To The Desktop
ShowCursor(True); //Show The Mouse Pointer
end;
if (h_RC <> 0) and not (wglDeleteContext(h_RC)) then //Are We Able To Delete The Rc?
begin
MessageBox(0, 'Release of Rendering Context failed.', ' Shutdown Error', MB_OK or
MB_ICONERROR);
h_RC := 0 //Set Rendering Context To Null
end;
if (h_DC <> 0) and (releaseDC(h_Wnd, h_DC) = 0) then
//Are We Able To Release The Device Context?
begin
MessageBox(0, 'Release of Device Context failed.', ' Shutdown Error', MB_OK or
MB_ICONERROR);
h_Dc := 0; //Set Dc To Null
end;
if (h_Wnd <> 0) and not (destroywindow(h_Wnd)) then
//Are We Able To Destroy The Window?
begin
MessageBox(0, 'Could not release hWnd.', ' Shutdown Error', MB_OK or
MB_ICONERROR);
h_Wnd := 0; //Set hWnd To Null
end;
UnregisterClass('OpenGL', hInstance)
end;
{in the CreateWindow we do:
- Register the class window
- Create the window
- Get a Device Context (DC)
- Create a Rendering Context (RC) }
function CreateGLWindow(Title: PChar; PosX, PosY: Integer; const Width, Height, Bits:
Integer; const FullScreenFlag: Bool): Bool; stdcall;
var
PixelFormat: GLUint; //Holds The Result After Searching For A Match
WC: TWndClass; //Windows Class Structure
dwExStyle: DWord; //Extended Windows Style
dwStyle: DWord; //Window Style
PFD: PixelFormatDescriptor; //Tells Windows How We Want Things To Be
dmScreenSettings: DevMode; //Device Mode
h_Instance: hInst; //Holds The Instance Of The Application
begin
h_Instance := GetModuleHandle(nil); //Grab An Instance For Our Window
GS.Fullscreen := FullScreenFlag; //Set The Global FullScreen Flag
with WC do //can't use parentesis on "with" when using packed records
begin
Style := CS_HREDRAW or CS_VREDRAW or CS_OWNDC;
//ReDraw On Size -- Own DC For Window
lpfnWndProc := @WndProc; //WndProc Handles The Messages
cbClsExtra := 0; //No Extra Window Data
cbWndExtra := 0; //No Extra Window Data
hInstance := h_Instance; //Set The Instance
hIcon := LoadIcon(0, IDI_WINLOGO); //Load The Default Icon
hCursor := LoadCursor(0, IDC_ARROW); //Load The Arrow Pointer
hbrBackground := 0; //No BackGround Required For OpenGL
lpszMenuName := nil; //We Don't Want A Menu
lpszClassname := 'OpenGL'; //Set The Class Name
end;
if (RegisterClass(WC) = 0) then //Attempt To Register The Class Window
begin
MessageBox(0, 'Failed To Register The Window Class.', 'Error', MB_OK or
MB_ICONERROR);
CreateGLWindow := False;
exit
end;
if (GS.FullScreen) then
begin
ZeroMemory(@dmScreenSettings, SizeOf(dmScreenSettings));
//Make Sure Memory's Availiable
with dmScreenSettings do //don't use parentesis on "with" when using packed records
begin
dmSize := SizeOf(dmScreenSettings); //Size Of The DevMode Structure
dmPelsWidth := Width; //Selected Screen Width
dmPelsHeight := Height; //Selected Screen Height
dmBitsPerPel := Bits; //Selected Bits Per Pixel
dmFields := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
//Try to Set Selected Mode
end;
if (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN) <>
DISP_CHANGE_SUCCESSFUL) then
if (MessageBox(0,
'This Fullscreen Mode Is Not Supported. Use Windowed Mode Instead?',
WIN_TITLE,
MB_YESNO or MB_ICONEXCLAMATION) = IDYES) then
GS.Fullscreen := False //Select Windowed Mode
else
begin
{ Show Message Box To Let User Know Program Is Ending }
MessageBox(0, 'Program Will Now Close.', 'Error', MB_OK or MB_ICONERROR);
CreateGLWindow := False; //Return False
Exit
end
end;
if (GS.Fullscreen) then //If Still In FullScreen Mode
begin
dwExStyle := WS_EX_APPWINDOW; //Entended Window Style
dwStyle := WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN; //Window Style
ShowCursor(False);
PosX := 0; //reset these to zero
PosY := 0
end
else
begin
dwExStyle := WS_EX_APPWINDOW or WS_EX_WINDOWEDGE; //Extended Window Style
dwStyle := WS_OVERLAPPEDWINDOW or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
//Windows Style
end;
h_Wnd := CreateWindowEx(dwExStyle, //Extends Style For The Window
'OpenGL', //Class Name
Title, //Window Title
dwStyle, //Window Style
PosX, PosY, //Window Position
Width, Height, //Selected Width and Height
0, //No Parent Window
0, //No Menu
hInstance, //Instance
nil); //Don't Pass Anything To WM_CREATE
if (h_Wnd = 0) then
begin //If The Window Creation Failed
KillGLWindow(); //Reset The Display
MessageBox(0, 'Window Creation Error.', 'Error', MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow := False;
exit;
end;
with PFD do //don't use parentesis on "with" when using packed records
begin //Tells Window How We Want Things To Be
nSize := SizeOf(PIXELFORMATDESCRIPTOR); //Size Of This Pixel Format Descriptor
nVersion := 1; //Version Number (?)
dwFlags := PFD_DRAW_TO_WINDOW //Format Must Support Window
or PFD_SUPPORT_OPENGL //Format Must Support OpenGL
or PFD_DOUBLEBUFFER; //Must Support Double Buffering
iPixelType := PFD_TYPE_RGBA; //Request An RGBA Format
cColorBits := Bits; //Select Our Color Depth
cRedBits := 0; //Color Bits Ignored
cRedShift := 0;
cGreenBits := 0;
cGreenShift := 0;
cBlueBits := 0;
cBlueShift := 0;
cAlphaBits := 0; //No Alpha Buffer
cAlphaShift := 0; //Shift Bit Ignored
cAccumBits := 0; //No Accumulation Buffer
cAccumRedBits := 0; //Accumulation Bits Ignored
cAccumGreenBits := 0;
cAccumBlueBits := 0;
cAccumAlphaBits := 0;
cDepthBits := 16; //16 Bit Z-Buffer (Depth Buffer)
cStencilBits := 0; //No Stencil Buffer
cAuxBuffers := 0; //No Auxilary Buffer
iLayerType := PFD_MAIN_PLANE; //Main Drawing Layer
bReserved := 0; //Reserved
dwLayerMask := 0; //Layer Masks Ignored
dwVisibleMask := 0;
dwDamageMask := 0;
end;
h_DC := GetDC(h_Wnd); //Try Getting a Device Context
if (h_DC = 0) then // Did We Get Device Context For The Window?
begin
KillGLWindow(); //Reset The Display
MessageBox(0, 'Cant''t create a GL device context.', 'Error', MB_OK or
MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
PixelFormat := ChoosePixelFormat(h_Dc, @pfd);
// Finds The Closest Match To The Pixel Format We Set Above
if (PixelFormat = 0) then //Did We Find A Matching Pixelformat?
begin
KillGLWindow(); //Reset The Display
MessageBox(0, 'Cant''t Find A Suitable PixelFormat.', 'Error', MB_OK or
MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
if not (SetPixelFormat(h_Dc, PixelFormat, @pfd)) then
begin //Are We Able To Set The Pixelformat?
KillGLWindow(); //Reset The Display
MessageBox(0, 'Cant''t set PixelFormat.', 'Error', MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
h_RC := wglCreateContext(h_DC); //Are We Able To create a Rendering Context?
if (h_RC = 0) then
begin
KillGLWindow(); //Reset The Display
MessageBox(0, 'Cant''t create a GL rendering context.', 'Error', MB_OK or
MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
if not (wglMakeCurrent(h_DC, h_RC)) then
//Are We Able To Activate The Rendering Context?
begin
KillGLWindow(); //Reset The Display
MessageBox(0, 'Cant''t activate the GL rendering context.', 'Error', MB_OK or
MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
ShowWindow(h_Wnd, SW_SHOW); //Show The Window
SetForegroundWindow(h_Wnd); //Slightly Higher Priority
SetFocus(h_Wnd); //Set Keyboard Focus To The Window
ReSizeGLScene(Width, Height); //Set Up Our Perspective Gl Screen
if not (InitGl(Width, Height)) then
//Do all the initialization here (load textures, etc)
begin
KillGLWindow(); //Reset The Display
MessageBox(0, 'Initialization Failed.', 'Error', MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow := False; //Return False
exit;
end;
CreateGLWindow := True //Succes
end;
//WinMain is Main Program (gets called from the actual Main.dpr)
function WinMain(hInstance: HINST; hPrevInstance: HINST; lpCmdLine: PChar; nCmdShow:
Integer): integer; stdcall;
var
msg: TMsg;
begin
if MessageBox(0, 'Would You Like To Run In FullScreen Mode?', 'Start FullScreen',
MB_YESNO or MB_ICONQUESTION) = idNo then
GS.Fullscreen := False
else
GS.Fullscreen := True;
if not (CreateGLWindow(WIN_TITLE, POS_X, POS_Y, RES_X, RES_Y, RES_BITS,
GS.Fullscreen)) then
begin //Could We Create The OpenGL Window?
Result := 0;
exit
end;
while not (GS.ExitGame) do //Main Game Loop
begin
if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then //Is There A Message?
begin
if (msg.message = WM_QUIT) then //Have We Received A Quit Message?
GS.ExitGame := True
else
begin
TranslateMessage(msg); //Translate Message
DispatchMessage(msg); //Dispatch the Message
end
end
else
{//No messages, so keep rendering our game} if (GS.Active) and not (DrawGLScene())
then //here's where all the fun happens
GS.ExitGame := True
else
SwapBuffers(h_DC); //Not Time To Quit Yet
//Check for keyboard input here
if (GS.Keys[VK_ESCAPE]) then //Time To Quit
begin
GS.Keys[VK_ESCAPE] := False;
GS.ExitGame := True;
end
else if (GS.Keys[VK_F1]) then //Toggle FullScreen Mode
begin
GS.Keys[VK_F1] := False;
KillGLWindow(); //Kill Our Current Window
GS.Fullscreen := not GS.Fullscreen; //Toggle Our Fullscreen Flag
//Recreate Our Window
if not CreateGLWindow(WIN_TITLE, POS_X, POS_Y, RES_X, RES_Y, RES_BITS,
GS.Fullscreen) then
Result := 0;
end
end; //While not GS.ExitGame
{ End of the Game }
KillGLWindow(); //Shutdown
Result := msg.wParam
end;
end.
{and here's the code for the "project source"}
program prjShell;
uses
oglMain in 'oglMain.pas';
begin
GS.Active := True;
WinMain(hInstance, hPrevInst, CmdLine, CmdShow);
end.
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése