ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Xine - issue #5 - Phile 109 ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ A little Introduction to the opengl +-----------------------------------+ Hey pals, I imagine as me you grew with these doom like and opengl demos, wacthing on your little screen the polygons moving, while launching rockets and such. Also, you are a dickhead at math and you don't want to build all these mathematical sinuso‹dal routines. Well, opengl is a cool library wich permit you to define a 3D universe very easily, to move it and apply a series of effect with the simple concept of an api. Today, there's two main opengl coding, depending on wich system you are, the normal way is using glut library in order of defining the animation, wich is compatible linux, win32, sgx stations etc etc... the other way is the pure win32 one. The last one use the win32 client call back in order to switch to next image. Anyway, the result is the same. But you know on wich platform we are building these little viruses. So, for using opengl, you need to init the window proc 1 The Window proc +----------------------+ the window proc is basically a call back wich handle all system/machine events attached to the current (displayed or not) window. The principe work great for GUI and diminuish the amount of eaten machine ressource. 1.1 The window class +------------------------+ For initialising a window proc, you need to define a class, the class is a little structure defining various information, but specially the address of the window procedure. It's very easy to initialise, an api call mov edx,esp ; save stack push offset classname push 0 ; window menu push 0 ; background color (black) push 0 ; our cursor push 0 ; our icon push edx ; save stack push 0 call GetModuleHandleA ; load current program pop edx push eax ; equal hinstance push 0 ; suplementary allocated memory push 0 ; additional size to this struct push offset Window_proc ; address of the window_proc push 0 ; we don't care of style mov eax,esp push edx push eax call RegisterClassA ; register the class pop esp ; restore the stack Once you have your little class registered you need to create your own window procedure. 1.2 Creating the window +-----------------------+ it's easy, just an api call push 0 call GetModuleHandleA push eax push 0 push 0 push 480 push 640 push 0 push 0 push 090000000h ; WS_VISIBLE push offset windowname push offset classname push 0400000h ; WS_EX_CLIENTEDGE call CreateWindowExA This is correct, but if you call that only like that, it may crash, why ? coz we have not yet created the window proc 1.3 The window proc +-----------------------+ The window proc is a basic procedure wich receive 4 arguments. The first argument is the window handle. The second argument is the message. The third is the low param and the fourth is the high param. As windows work in a c like environment, argument are dropped on stack. So, it's easy to access them. Also you need to ret 16. You need also to keep care of some registers, do not modify ebp as exemple or you will crash. Your window proc need to intercept some message, message are part of the win32 constants. let have a look on what look like a window proc. lParam equ 16 wParam equ 12 uMsg equ 8 hWnd equ 4 window_proc: cmp dword ptr [esp+uMsg], 0Fh ; WM_PAINT jne not_event1 ... put here WM_PAINT code not_event1: ... put here other events you want not_event99: cmp dword ptr [esp+uMsg], WM_CLOSE jne not_quiting push dword ptr [esp+lParam] push dword ptr [esp+4+wParam] push dword ptr [esp+4+4+uMsg] push dword ptr [esp+4+4+4+hWnd] call DefWindowProc ret 16 1.4 forking the message +---------------------------+ For some reason, some message are received and some messages have to be sent, For this, you need to dedicate the current thread for forking messages, wich cost a very low ressource in fact. So don't panick. This code is very generic in fact, it may vary from a programmer to an other, but it's all the time the same snippet. mov edx,esp sub esp,44 mov ebx,esp push ebx push eax loopit: push ebx push 0 push 0 push eax push ebx call GetMessageA pop ebx cmp eax,0 je goodbye push ebx push ebx call TranslateMessage call DispatchMessageA pop eax pop ebx push ebx push eax jmp loopit goodbye: program_error: add esp,44+4+4 push 0 call ExitProcess Once your window proc initialized, oh joy ! nothing, that's normal, you need to sync the window proc with the opengl 2 The opengl +------------------+ Now, as a standard window gui, we have a register class/window, and the tiny window proc wich will permit us many thing. But now, we need the help of the opengl in order to init the system, the graphic viewpoint and a lot things. There's mainly two things to do: at creating, and when the screen is refreshed. The method described here is only mine, acquired from various tutorial, documents. Keep a critical view point of it. 2.1 At window creation time +--------------------------------+ First of all, you need to initialize the pixel format, this pixel format handle for use various graphical technique, as a double graphical buffer. For that, you need the use of a PIXELFORMATDESCRIPTOR, it's a long structure boring, where only a few information need to be filled. let see mov ecx,40 sub esp,ecx mov edi,esp push edi xor eax,eax repz stosb pop edi mov word ptr [edi], 40 ; sizeof the structure mov word ptr [edi+2], 1 ; fill version mov dword ptr [edi+4], 37 ; PFD_SUPPORT_OPENGL ; PFD_DRAW_TO_WINDOW ; PFD_DOUBLE_BUFFER mov byte ptr [edi+9], 16 ; color bit mov byte ptr [edi+17], 16 ; depth bit push edi push dword ptr [currentDC] ; load the DC call ChoosePixelFormat if the return equal 0, the api failed, and the user can't display the resolution. Else, you can directly set the pixel format. push edi push eax push dword ptr [currentDC] ; load the DC call SetPixelFormat then it's equal the pixel format. To get the CurrentDC, it's very easy: push dword ptr [esp+hWnd] call GetDC and then eax equal the current DC. We also need to create an opengl context from this DC. push eax call wglCreateContext if eax = 0, then we have failed, and the best to do is to send a wm_close wich will close in good form the application. after, we need to synchronize both context push eax ; opengl contect push ebx ; currend DC call wglMakeCurrent that done, the opengl has been initialized, cool, now you can apply all a series of effect, but before applying them, you need to enable them. I let you choose from documents wich one you would like, (but try these ones: GL_DEPTH_TEST, GL_LIGHTING), active them this way : push 0B57h ; CL_COLOR_MATERIAL call glEnable ; damn easy isn't ? 2.2 Once the window is being resized +-------------------------------------+ when windows has been done the initializing, windows send a WM_SIZE message, this message mean that it's time to fix all a series of graphical things. The screen resolution has been sent in lParam. So first, set the viewport mov ecx,dword ptr [esp+lParam] movzx edx,cx shr ecx,16 push ecx push edx ; lenght push 0 push 0 ; start call glViewport ; define viewport After, we need to define the matrix model, I will not make a math lesson about matrix and transformations, but as usual, it's very easy push 1701h ; GL_PROJECTION call glMatrixMode after, we take the Indentity matrix wich will make corresponding to X Y Z the X Y Z coordinate :) call glLoadIdentity then, we will call a gl effect library to add perspective to the viewport, well you could recode it yourself, but it's a bit long and may ask you some skill you have not, a special api is dedicated but this api accept only double floating point precision. The compiler can init double via double1 dq 1.0f a standard double push is pushdl macro double1 fld qword ptr [&double1&] sub esp,8 fstp qword ptr [esp] endm if you macroize that, you can then pass to the viewport. pushdl farclip pushdl nearclip pushdl XYration pushdl FovAngle call gluPerpective The perspective will give us a nicest aspect to our 3D world. Set it as you like, you can give a camera like vision or an alien extended or a 16/9 vision of the scene. And optionnaly, you can set the shade model, for complex 3D, set it to GL_FLAT, it may speed up previewving of gouraud or other heavy image rendering. push 01D01h call glShadeModel and that's all for now. 3 It's time for your democoder skills +-------------------------------------------+ Windows tell the window proc that it's time to refresh the current image via the WM_PAINT message. The very first thing to do is to clear a few variables push 04100h ; GL_COLOR_BUFFER_BIT or call glClear ; GL_DEPTH_BUFFER_BIT push GL_MODEL_VIEW call glMatrixMode call glLoadIdentity now, it's clear for use and we can set the viewport, it's like a camera. This camera have 9 operands. The 3 first are the start of the camera The 3 after are the destination of the camera The 3 last are the coordinate of the up vector of the camera, the rotation of it. these operand are double again, then use pushdl in order to use it. pushf... pushf... call gluLookAt Now, it's time for you to draw the environment, you can do many things, set lingths, shadow, transparency, distorsion, etc etc... let's see an easy thing: push GL_POINTS call glBegin push 0.5 push 0.5 push 0.5 call glVertex3f call glEnd glColor3d and glVertex3i exist under many format, in fact, the last letter design wich type of variable you want to use. glVertex3d mean you use double as argument, capisco when the environment is build, you can finish by: push DC call SwapBuffers and the swapped buffer will be flushed to screen. 4 Little Appendix +-----------------------+ Often, ppl wonder why their production appears strange on screen, the reason is simple, opengl dont take care of other elements if they exist, so, it may overwrite them on screen, boring isn't ? You can set a depth test at creation time push 0B71h ; DEPTH_TEST call glEnable ; Some ppl would like to have a full screen resolution, this can be done in two steps, fix the display setting: mov ecx,148 mov esp,ecx mov edi,esp xor eax,eax repz stosb mov dword ptr [esp+36],148 ; dmSize mov dword ptr [esp+104],16 ; dmBitsPerPixel mov dword ptr [esp+108],640 ; dmWidth mov dword ptr [esp+112], 480 ; dmHeight mov dword ptr [esp+40], 1C0000h ; DM_BITSPERPEL DM_PELSWIDTH DM_PELSHEIGHT mov edx,esp push 4 ; CDS_FULLSCREEN push edx ; dmScreenSettings call ChangeDisplay add esp,148 it's easy as bonjour isn't ?