May I be lucky enough to pick my ass up and put it back onto my chair, then also say thanks for the well documented and detailed example!
I myself have toyed with OpenGL via perl before, but have never thought of combining it with Win32::GUI myself. Seeing as I'm a maintainer for a game, and some people have asked for updated tools with animations from said game.. I may someday put this example to a very literal use. Thanks again, Jason P On Thu, May 21, 2009 at 11:18 PM, Kevin Marshall <kejoh...@hotmail.com>wrote: > Hey, > > This lengthy post is about how to use the Win32::GUI module together with > the Perl OpenGL module (POGL). > > I would first like to thank the developers of Perl-Win32-GUI module. I have > been using this module for a while now, and prefer using this module than > coding the whole thing in C/C++. Thanks. I have decided to give back to the > Win32::GUI community by way of sharing a solution for using OpenGL in > conjunction with the Win32::GUI module. I hope that someone out there will > find this useful. > > After reading the book OpenGL Game Programming (Premier Press), I decided > to port some of the code examples to Perl. Since the Win32::GUI module makes > the creation of windows easy, it was simply a matter of converting the > relevant C/C++ code into Perl. A basic knowledge of OpenGL is needed in > order to understand the example that I have provided. The example contains > comments that should explain what each section does, but I'll provide a > brief overview of the process. I'll only be discussing the Windows+OpenGL > specific code, so if you don't have a good grasp of OpenGL, I recommend > finding a good book or website about OpenGL programming before going any > further. Detailed information regarding Windows API functions, and OpenGL > functions can be found in the Windows Software Development Kit > Documentation. > > OpenGL is a powerful, low-level rendering and modelling software library. > OpenGL does not provide higher functionality, such as windowing, in order to > remain platform independent. OpenGL uses a rendering context to store OpenGL > commands and settings. This means that each platform must provide > functionality to specify a rendering context for OpenGL. For UNIX systems, > this is provided by GLX. For Windows this is provided by a set of functions > affectionately known as 'wiggle' functions, since most of these start with > 'wgl'. There are a number of other Win32 API functions that are also needed. > These 'wiggle' and Win32 API functions relate Windows Device Contexts with > the OpenGL rendering contexts. The Win32::API module is needed to import > these functions for use in the program. > > Example Program Description: > > Lines 21-51 show some setup required for the functions that are to be > imported. This includes creating the structure PIXELFORMATDESCRIPTOR used > by the SetPixelFormat() function and creating a typedef for the > wglMakeCurrent() function. Lines 52-63 show the functions that need to be > imported. Info on these functions can be found in the SDK docs. > > Lines 64-100 creates the main window and sets its icon. This should be > fairly basic for anyone experienced with the Win32::GUI module, so I'll just > cover the important parts. Line 68 sets the -left of the window to > CW_USEDEFAULT. This specifies that the system should position the window. > Lines 69-71 add the window styles WS_CLIPCHILDREN and WS_CLIPSIBLINGS. > These affect how the window will be painted. For more info about these > styles, see the SDK docs. Lines 72-76 setup an -onTerminate event handler. > This will be called when the window is destroyed. At this point the > rendering context is deselected from the main window Device Context and is > then deleted. More on these functions later. Just know that each takes a > handle to a device or rendering context. The sub returns -1 to exit the main > loop. For those of you familiar with C/C++ Windows programming, this > function roughly corresponds to the WM_CLOSE message. Lines 77-90 setup an > -onResize event handler. This is called whenever the window is resized and > corresponds to the WM_SIZE message. The functions in the sub are OpenGL > specific and basically just reset the viewport to the new dimensions and > resets the perspective. Refer to the SDK docs or a good OpenGL resource for > info about these functions. Lines 91-95 setup a -onKeyDown event handler > which exits the program when the ESC key is pressed. > > Lines 101-106 are where the device and rendering contexts are setup. Line > 101 gets the device context of the main window and stores it in a global > variable. Lines 102-104 calls the SetupPixelFormat() function, passing it > the handle to the main window device context. If this sub fails the program > exits. Lines 116-157 show the SetupPixelFormat() function. Line 119 creates > a new PIXELFORMATDESCRIPTOR structure and lines 120-147 fills the structure > with appropriate data. See the SDK docs for more info about this structure. > Lines 148-151 calls the ChoosePixelFormat() function passing the handle to > the DC and the PIXELFORMATDESCRIPTOR structure. This function chooses the > best matching pixel format for the DC from the data specified in the > PIXELFORMATDESCRIPTOR structure. The function returns 0 if not pixel format > can be found. Lines 152-155 set the pixel format of the DC to the format > returned from the ChoosePixelFormat() function and returns 0 if it fails. > The sub returns 1 to show that it succeeded. Line 105 creates an OpenGL > rendering context from the specified DC using the wglCreateContext() > function. The function is passed the handle to the main window DC and > returns the handle to a rendering context. Line 106 selects the rendering > context into the device context using the wglMakeCurrent() function. This > functions is passed the handle to the main window DC and the handle to the > rendering context created with wglCreateContext(). Passing 0 as the > rendering context causes the rendering context to be deselected, such as in > the -onTerminate event handler above. The wglDeleteContext() function is > used to delete a rendering context and should be used after the rendering > context has been deselected. The deselection and deletion of a rendering > context should be performed when a window is destroyed, which is why this is > done in the -onTerminate event handler above. Lines 101-106 could be thought > of as equivalent to the WM_CREATE message (you could even use the Hook() > method to implement this). > > Lines 107-111 show some basic setup of OpenGL before any rendering is done. > These are OpenGL specific, so if you are unsure of what these do, refer to > the docs. > > Lines 113-115 setup the main message loop. Win32::GUI::Dialog() can't be > used here because the Render() function has to be called every frame. This > loop is essentially the same, but a few differences are present which may > affect applications with multiple windows, since the Win32::GUI::Dialog() > functions does more behind the scenes. It would be nice if the > Win32::GUI::Dialog() function accepted a sub ref which could be called every > frame, but I'm not sure how easy that would be to create. Anyway, this does > the job fine, but there are probably better methods. > > Lines 158-172 and 173-188 are the DrawCube() and Render() functions, > respectively. These are used to draw the cube every frame and are OpenGL > specific. Refer to the docs about what the OpenGL functions do if you are > unsure. > > Line 189-193 creates a Handle() method in the Win32::GUI::DC package. This > method returns the handle of the object passed in. Putting it in the DC > package allows both windows and DCs to access it. This method is used when > the OpenGL/Win32API function requires a handle. Since the objects store in > handle internally, it makes it really easy to pass this value to the > functions that need it. > > Here is my code example: > > > ################################################################################ > # > # Win32::GUI + OpenGL example > # > # This program demonstrates a basic example of using the Perl Win32::GUI > # module in conjunction with the Perl OpenGL module to render a spinning > # cube of points in the window. > # > # Requirements: > # Perl, > # Win32::GUI, > # Win32::GUI::Carp, > # Win32::API, and > # OpenGL > # > # This program was written using ActiveState Perl 5.8.8 Build 820 running > on > # Windows XP and using Win32::GUI v1.06, Win32::GUI::Carp v1.01, > # Win32::API v0.46, and OpenGL v0.56 > # > # Parts of this program are based on example code from the book OpenGL Game > # Programming (Premier Press, 2004) from the Premier Press Game > # Development Series. I recommend this book for anyone interested in using > # OpenGL for developing games on Windows. The book is written for the > # development of games written in C/C++ and assumes an advanced knowledge > # of the language but provides an in-depth look at OpenGL programming on > # Windows platforms, as well as a look at using DirectInput and > # DirectX Audio. > # > > ################################################################################ > > 1: use strict; > 2: use warnings; > #these constants are needed for SetPixelFormat() but aren't defined in > Win32::GUI > 3: use constant { > 4: PFD_TYPE_RGBA => 0, > 5: PFD_DOUBLEBUFFER => 0x00000001, > 6: PFD_DRAW_TO_WINDOW => 0x00000004, > 7: PFD_SUPPORT_OPENGL => 0x00000020, > 8: PFD_MAIN_PLANE => 0, > 9: }; > > 10: use OpenGL qw(:glfunctions :glconstants :glufunctions); > 11: use Win32::API; > 12: use Win32::GUI qw(); > 13: use Win32::GUI::Carp qw(warningsToDialog fatalsToDialog > immediateWarnings winwarn windie); > 14: use Win32::GUI::Constants qw(IDI_APPLICATION WS_CLIPCHILDREN > WS_CLIPSIBLINGS > 15: WM_CREATE WM_SIZE WM_CLOSE VK_ESCAPE CW_USEDEFAULT MB_OK > MB_ICONEXCLAMATION); > > 16: my $g_HDC; #global handle to device context > 17: my $hRC; #handle to rendering context > > #keep track of rotation of cube > 18: my $objectXRot = 0.0; > 19: my $objectYRot = 0.0; > 20: my $objectZRot = 0.0; > > #define PIXELFORMATDESCRIPTOR struct used for the SetPixelFormat function > #refer to the Windows SDK documentation for more info about this structure > 21: Win32::API::Struct->typedef( > 22: PIXELFORMATDESCRIPTOR => qw( > 23: WORD nSize; > 24: WORD nVersion; > 25: DWORD dwFlags; > 26: BYTE iPixelType; > 27: BYTE cColorBits; > 28: BYTE cRedBits; > 29: BYTE cRedShift; > 30: BYTE cGreenBits; > 31: BYTE cGreenShift; > 32: BYTE cBlueBits; > 33: BYTE cBlueShift; > 34: BYTE cAlphaBits; > 35: BYTE cAlphaShift; > 36: BYTE cAccumBits; > 37: BYTE cAccumRedBits; > 38: BYTE cAccumGreenBits; > 39: BYTE cAccumBlueBits; > 40: BYTE cAccumAlphaBits; > 41: BYTE cDepthBits; > 42: BYTE cStencilBits; > 43: BYTE cAuxBuffers; > 44: BYTE iLayerType; > 45: BYTE bReserved; > 46: DWORD dwLayerMask; > 47: DWORD dwVisibleMask; > 48: DWORD dwDamageMask; > 49: ) > 50: ); > > #needed for the wglMakeCurrent functions > 51: Win32::API::Type->typedef('HGLRC', 'HANDLE'); > > #import some extra functions > #more info can be found in the Windows SDK documentation > > #exchanges the front and back buffers of the current pixel format > 52: Win32::API->Import('gdi32', 'BOOL SwapBuffers(HDC hdc);') > 53: or windie "Win32::API->Import(SwapBuffers): $^E"; > > #attempts to match an appropriate pixel format supported by a device > context to > # a given pixel format specification. > 54: Win32::API->Import('gdi32', 'int ChoosePixelFormat(HDC hdc, > PIXELFORMATDESCRIPTOR * ppfd);') > 55: or windie "Win32::API->Import(ChoosePixelFormat): $^E"; > > #sets the pixel format of the specified device context to the format > specified > # by the iPixelFormat index returned from ChoosePixelFormat(). > 56: Win32::API->Import('gdi32', 'BOOL SetPixelFormat(HDC hdc, int > iPixelFormat, PIXELFORMATDESCRIPTOR * ppfd);') > 57: or windie "Win32::API->Import(SetPixelFormat): $^E"; > > #creates a new OpenGL rendering context, which is suitable for drawing on > the > # device referenced by hdc. > 58: Win32::API->Import('opengl32', 'HGLRC wglCreateContext(HDC hdc);') > 59: or windie "Win32::API->Import(wglCreateContext): $^E"; > > #makes a specified OpenGL rendering context the calling thread's current > # rendering context. > 60: Win32::API->Import('opengl32', 'BOOL wglMakeCurrent(HDC hdc, HGLRC > hglrc);') > 61: or windie "Win32::API->Import(wglMakeCurrent): $^E"; > > #deletes a specified OpenGL rendering context. > 62: Win32::API->Import('opengl32', 'BOOL wglDeleteContext(HGLRC hglrc);') > 63: or windie "Win32::API->Import(wglDeleteContext): $^E"; > > #create main window > 64: my $main = Win32::GUI::Window->new( > 65: -name => "main", > 66: -text => "OpenGL Example: Colour Cube", > 67: -size => [640,480], > 68: -left => CW_USEDEFAULT, #let system position window > 69: -pushstyle => > #Excludes the area occupied by child windows when drawing occurs > # within the parent window. > 70: WS_CLIPCHILDREN | > #When a particular child window needs to be painted, all other > overlapping > # child windows are clipped out of the region of the child window to be > updated. > 71: WS_CLIPSIBLINGS, > 72: -onTerminate => sub { #WM_CLOSE > 73: wglMakeCurrent($g_HDC->Handle(), 0); #deselect rendering context from > $hDC > 74: wglDeleteContext($hRC); #delete rendering context $hRC > 75: return -1; #exit main loop > 76: }, > 77: -onResize => sub { #WM_SIZE > 78: my $self = shift; > 79: return 0 unless $self; > 80: my $height = $self->Height(); #get height and width > 81: my $width = $self->Width(); > 82: $height = 1 if $height == 0; #don't divide by 0 > 83: glViewport(0,0,$width,$height); #set viewport to new dimensions > 84: glMatrixMode(GL_PROJECTION); #set matrix mode to projection matrix > 85: glLoadIdentity(); #reset projection matrix > 86: gluPerspective(54.0, $width / $height, 1.0, 1000.0); #calculate > aspect ratio of window > 87: glMatrixMode(GL_MODELVIEW); #set modelview matrix > 88: glLoadIdentity(); #reset modelview matrix > 89: return 1; > 90: }, > 91: -onKeyDown => sub { #WM_KEYDOWN > 92: my ($self, $flags, $key) = @_; > 93: return -1 if $key == VK_ESCAPE; #exit if escape key pressed > 94: return 1; > 95: }, > 96: ); > 97: unless($main){ > 98: windie("Cannot create window: $^E"); > 99: } > 100: $main->SetIcon(Win32::GUI::Icon->new(IDI_APPLICATION)); #set window > icon > > #WM_CREATE > 101: $g_HDC = $main->GetDC(); #set global device context to device > context of main window > 102: unless(SetupPixelFormat($g_HDC->Handle())){ #setup pixel format for > device context > 103: exit 1; #exit if setup fails > 104: } > 105: $hRC = wglCreateContext($g_HDC->Handle()); #create rendering context > used by OpenGL to draw > 106: wglMakeCurrent($g_HDC->Handle(), $hRC); #select rendering context > $hRC into $g_HDC > > #Initialize OpenGL > 107: glShadeModel(GL_SMOOTH); #set shading to smooth > 108: glEnable(GL_DEPTH_TEST); #do depth comparisons and update the > depth buffer > 109: glEnable(GL_CULL_FACE); #cull polygons based on their winding in > window coordinates > 110: glFrontFace(GL_CCW); #The orientation of front-facing polygons > 111: glClearColor(0.0, 0.0, 0.0, 0.0); #values that glClear uses to > clear the colour buffers > > 112: $main->Show(); #show window > > #message event > #This is necessary because Render() needs to be called every frame. > #This can produce interesting results in more complex applications (with > more > # than one window) since the Win32::GUI::Dialog() function does more than > just > # check if a sub has returned -1 (perhaps the ability to call a code block > every > # iteration of Win32::GUI::Dialog() would be useful) > 113: while(Win32::GUI::DoEvents() != -1){ > 114: Render(); > 115: } > > #This function is used to set the pixel format for the device context. > # Accepts a handle to the device context of the window and returns true if > succeeds > # or false if fails. > #Adapted from code from OpenGL Game Programming (Premier Press, 2004) > 116: sub SetupPixelFormat { > 117: my $hDC = shift; #is a handle to DC > 118: my $nPixelFormat; > 119: my $pfd = Win32::API::Struct->new('PIXELFORMATDESCRIPTOR'); #create > new structure > 120: $pfd->{nSize} = $pfd->sizeof(); #return sizeof structure > 121: $pfd->{nVersion} = 1; #default version > 122: $pfd->{dwFlags} = PFD_DRAW_TO_WINDOW | #window drawing support > 123: PFD_SUPPORT_OPENGL | #OpenGL support > 124: PFD_DOUBLEBUFFER; #double buffering support > 125: $pfd->{iPixelType} = PFD_TYPE_RGBA; #rgba colour mode > 126: $pfd->{cColorBits} = 32; #32 bit colour mode > 127: $pfd->{cRedBits} = 0; #ignore colour bits > 128: $pfd->{cRedShift} = 0; # > 129: $pfd->{cGreenBits} = 0; # > 130: $pfd->{cGreenShift} = 0; # > 131: $pfd->{cBlueBits} = 0; # > 132: $pfd->{cBlueShift} = 0; # > 133: $pfd->{cAlphaBits} = 0; #not alpha buffer > 134: $pfd->{cAlphaShift} = 0; #ignore alpha shift bit > 135: $pfd->{cAccumBits} = 0; #no accumulation buffer > 136: $pfd->{cAccumRedBits} = 0; #ignore accumulation bits > 137: $pfd->{cAccumGreenBits} = 0; # > 138: $pfd->{cAccumBlueBits} = 0; # > 139: $pfd->{cAccumAlphaBits} = 0; # > 140: $pfd->{cDepthBits} = 16; #16 bit z-buffer size > 141: $pfd->{cStencilBits} = 0; #no stencil buffer > 142: $pfd->{cAuxBuffers} = 0; #no auxiliary buffer > 143: $pfd->{iLayerType} = PFD_MAIN_PLANE; #main drawing plane > 144: $pfd->{bReserved} = 0; #reserved > 145: $pfd->{dwLayerMask} = 0; #layer masks ignored > 146: $pfd->{dwVisibleMask} = 0; # > 147: $pfd->{dwDamageMask} = 0; # > # choose best matching pixel format > 148: unless( $nPixelFormat = ChoosePixelFormat($hDC, $pfd) ){ > 149: winwarn("Can't find an appropriate pixel format"); > 150: return 0; > 151: } > # set pixel format to device context > 152: unless( SetPixelFormat($hDC, $nPixelFormat, $pfd) ){ > 153: winwarn("Unable to set pixel format"); > 154: return 0; > 155: } > 156: return 1; > 157: } > > #This function draws the cube of points. The colour of each point is based > on its position > #Adapted from code from OpenGL Game Programming (Premier Press, 2004) > 158: sub DrawCube { > 159: glPointSize(2.0); #set size of points drawn > 160: glPushMatrix(); > 161: glBegin(GL_POINTS); > 162: for(my $x = 0.0; $x <= 1.0; $x += 0.1){ > 163: for(my $y = 0.0; $y <= 1.0; $y += 0.1){ > 164: for(my $z = 0.0; $z <= 1.0; $z += 0.1){ > 165: glColor3f($x,$y,$z); > #move and scale vertexes so that cube rotates about centre > 166: glVertex3f($x*50-25,$y*50-25,$z*50-25); > 167: } > 168: } > 169: } > 170: glEnd(); > 171: glPopMatrix(); > 172: } > > #This function is used to draw the cube and is called every frame > #Adapted from code from OpenGL Game Programming (Premier Press, 2004) > 173: sub Render { > 174: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); #clear color and > depth buffer > 175: glLoadIdentity(); #replaces the current matrix with the > identity matrix. > 176: glTranslatef(0.0, 0.0, -150.0); #move to 0,0,-150 > 177: glPushMatrix(); #save current matrix > 178: glRotatef($objectXRot, 1.0, 0.0, 0.0); #rotate around x axis > 179: glRotatef($objectYRot, 0.0, 1.0, 0.0); #rotate around y axis > 180: glRotatef($objectZRot, 0.0, 0.0, 1.0); #rotate around z axis > 181: DrawCube(); #draw cube > 182: glPopMatrix(); #restore matrix > 183: glFlush(); #clear buffers > 184: SwapBuffers($g_HDC->Handle()); #exchange front and back buffers > of device context > 185: $objectXRot += 0.01; #increment rotation > 186: $objectYRot += 0.02; > 187: $objectZRot += 0.01; > 188: } > > #Conveniently, the Windows specific functions for setup of OpenGL accept > and return > # handles to windows or contexts, which are just numbers, and the handles > to > # these are stored in the Window and DC objects created by Win32::GUI. This > method > # provides an easy access to this value. Placing the method in the > Win32::GUI::DC > # package allows both Windows and DCs to use it. > 189: package Win32::GUI::DC; > 190: sub Handle { > 191: return shift->{-handle}; > 192: } > 193: __END__ > > Here are some tips and tricks regarding using Win32::GUI module and the > OpenGL module that I have come across on my travels: > > Win32::GUI: > > > - It is possible to create child windows that are rendered to using > OpenGL instead of rendering to the parent window, but requires a lot more > setup. One of the tricks needed is to specify the right styles for the > window. I have yet to perfect this technique, but perhaps when I get it > working correctly I'll post an example. > > > OpenGL: > > - try to use the *_p variants of functions, if they exist. The function > has been tweaked to behave more like a regular Perl function, such as the > ability to accept and return arrays. This is a lot more convenient than > having to pack() and unpack() your a arguments. Some functions don't have a > *_p variant, so the next best thing is to use the *_c variant, which > accept OpenGL::Array objects. The use of OpenGL::Array is not documented in > the module, but docs can be found on the website for the module (just > search > the web for POGL). I haven't included an example of this here, since it > requires more knowledge of OpenGL, but experienced OpenGL programmers > should > have no problems using them. > > > As an alternative to creating windows using Win32::GUI, windows can be > created using the GLUT(OpenGL Utility Toolkit) functions supplied with > OpenGL. These can create windows that a platform-independent, as well as a > lot of other stuff. A lot of the examples supplied for the OpenGL module use > the GLUT, making them more portable, but OpenGL needs to be compiled with > support for GLUT, requiring the GLUT libraries. Since I can't seem to get > XS-implemented modules to compile properly on my machine (I use PPM > instead), I just stick with Win32::GUI. Its all about personal preference. > > Well, that's it for my Win32::GUI+OpenGL example. I hope someone finds it > useful. I'm no expert at OpenGL or the Win32 API, so there is probably a > better way of doing this. So far this model has worked for basic > implementations but don't expect to be able to make anything to big, such as > games, but you never know. I'd love to hear any questions or comments about > this example, as well as any examples of anything anyone else has done. > > As a side note, I'm new to posting messages on the mailing lists. I was > wondering whether I can send pictures attached (I was hoping to show a > screen shot of my program). Also, how do I post a reply to an existing > thread. Any help would be much appreciated. > > Sorry about any typos in advance. Contact me if you find any errors with > this post (such as with the sample program). > > Thanks. > > Kevin Marshall > > (kejohm88 AT hotmail DOT com) > > > > ------------------------------ > Click Here View photos of singles in your > area<http://a.ninemsn.com.au/b.aspx?URL=http%3A%2F%2Fdating%2Eninemsn%2Ecom%2Eau%2Fsearch%2Fsearch%2Easpx%3Fexec%3Dgo%26tp%3Dq%26gc%3D2%26tr%3D1%26lage%3D18%26uage%3D55%26cl%3D14%26sl%3D0%26dist%3D50%26po%3D1%26do%3D2%26trackingid%3D1046138%26r2s%3D1&_t=773166090&_r=Hotmail_Endtext&_m=EXT> > > > ------------------------------------------------------------------------------ > Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT > is a gathering of tech-side developers & brand creativity professionals. > Meet > the minds behind Google Creative Lab, Visual Complexity, Processing, & > iPhoneDevCamp asthey present alongside digital heavyweights like Barbarian > Group, R/GA, & Big Spaceship. http://www.creativitycat.com > _______________________________________________ > Perl-Win32-GUI-Users mailing list > Perl-Win32-GUI-Users@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/perl-win32-gui-users > http://perl-win32-gui.sourceforge.net/ > -- Maximus* WarheadsSE MaxSource
------------------------------------------------------------------------------ Register Now for Creativity and Technology (CaT), June 3rd, NYC. CaT is a gathering of tech-side developers & brand creativity professionals. Meet the minds behind Google Creative Lab, Visual Complexity, Processing, & iPhoneDevCamp asthey present alongside digital heavyweights like Barbarian Group, R/GA, & Big Spaceship. http://www.creativitycat.com
_______________________________________________ Perl-Win32-GUI-Users mailing list Perl-Win32-GUI-Users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/perl-win32-gui-users http://perl-win32-gui.sourceforge.net/