Copy Link
Add to Bookmark
Report
Xine - issue #5 - Phile 109
Ú-----------------------------¿
| 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 sinusodal 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 ?