Mega Code Archive

 
Categories / Delphi / Graphic
 

OpenGL I Hello World

Title: OpenGL I: Hello World Question: 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: oglMain unit: --------------------------------------------- //---------------------------------------------------------// // // // 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_DC0) 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_Wnd0) 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. that's it... you can download the whole thing here: keep it up salu2 EberSys