

                                             ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
                                             ³ 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 ?


