offline
- Srki_82
- Moderator foruma
- Srđan Tot
- Am I evil? I am man, yes I am.
- Pridružio: 12 Jul 2005
- Poruke: 2483
- Gde živiš: Ljubljana
|
Meni je opet doslo da piskaram malo osnove. Ovog puta sam pisao u 3 razlicita jezika kod koji bi trebalo da radi na skoro svim OS (testiram samo na Windows (Vista) i Linux (Ubuntu 8.04) jer samo njih imam).
Evo prvo jednog malog programcica, cisto da vidimo kako SDL i OGL rade zajedno.
Od kompajlera cu koristiti: GNU C++, Free Pascal, .NET/Mono kompajler.
IDE u kojima cu raditi: za C++ - Code Blocks, za Pascal - FreePascal IDE, za C# - Visual Studio Express za Windows i MonoDevelop za Linux.
Dakle, sve sto koristim je potpuno besplatno i moze se preuzeti sa interneta, ali vi mozete koristiti sta god zelite.
Kompajleri
GNU C++: http://gcc.gnu.org/
Free Pascal: http://www.freepascal.org/
.NET: http://msdn2.microsoft.com/en-us/netframework/default.aspx
Mono: http://www.mono-project.com/Main_Page
Razvojna okruzenja:
Code Blocks: http://www.codeblocks.org/
Free Pascal: http://www.freepascal.org/
Visual Studio Express: http://msdn2.microsoft.com/en-us/express/default.aspx
MonoDevelop: http://www.monodevelop.com/Main_Page
Pre kodiranja bice potrebno da imate instalirane potrebne biblioteke. Za ovu lekciju je potrebno da imate instaliran SDL i TAO.
SDL je biblioteka koja omogucava unificiran pristup ulaznim (tastatura, mis, dzojstik,...) i izlaznim (monitor, zvucnik) uredjajima na skoro svim platformama. Zbog toga je odlican izbor kada je potrebno pisati kod koji nije vezan samo za Windows.
SDL: http://www.libsdl.org/
SDL za Pascal: http://jedi-sdl.pascalgamedevelopment.com/
TAO Framework je skup .NET assembly fajlova koji omogucavaju koriscenje SDL i OpenGL u .Net jezicima.
TAO: http://www.taoframework.com/
Napomena: ovde necu objasnjavati kako se postavljaju opcije kompajlera/linkera, podrazumeva se da onaj ko cita tekst zna osnove C++, Pascal i C# jezika.
Sad kad imamo sve sto nam treba, mozemo krenuti sa pisanjem koda. Prvo cemo kreirati projekat za svaki jezik koji ce sadrzati samo praznu glavnu proceduru:
C++
int main ( int argc, char** argv )
{
return 0;
}
Pascal
program basic;
begin
end.
C#
using System;
namespace basic
{
class Basic
{
static void Main(string[] args)
{
}
}
}
Kad se malo bolje pogledaju ovi kodovi, vidi se da se ne razlikuju mnogo. Ok... idemo na inicijalizaciju SDL biblioteke. Ako inicijalizacija ne uspe, sve komande vezane za SDL nece moci da se izvrse pa je to prva komanda koju izvrsavamo. Na kraju programa moramo osloboditi resurse koje zauzima SDL. Najsigurniji nacin je da taj kod ubacimo u deo koji ce se uvek izvrsiti na kraju, bez obzira da li se neka greska desila u kodu:
C++
#include <SDL.h>
int main ( int argc, char** argv )
{
// initialize SDL video
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
return 1;
}
// make sure SDL cleans up before exit
atexit(SDL_Quit);
return 0;
}
Pascal
program basic;
uses
SDL;
var
OldExitProc: Pointer;
procedure SDLExitProc;
begin
ExitProc := OldExitProc;
SDL_Quit;
end;
begin
// initialize SDL video
if (SDL_Init(SDL_INIT_VIDEO) < 0) then
Halt(1);
// make sure SDL cleans up before exit
OldExitProc := ExitProc;
ExitProc := @SDLExitProc;
end.
C#
using System;
using Tao.Sdl;
namespace basic
{
class Basic
{
static void Main(string[] args)
{
try
{
// initialize SDL video
Sdl.SDL_Init(Sdl.SDL_INIT_VIDEO);
}
finally
{
// make sure SDL cleans up before exit
Sdl.SDL_Quit();
}
}
}
}
Imamo sve sto je potrebno za kreiranje prozora... ako nesto podje naopako, SDL_Quit ce biti pozvan tako da ne moramo mnogo da brinemo:
C++
// create a new window
SDL_Surface* surface = SDL_SetVideoMode(width, height, bpp, flags);
if (!surface)
{
return 1;
}
Pascal
// create a new window
Surface := SDL_SetVideoMode(Width, Height, BPP, flags);
if (Surface = nil) then
Halt(1);
C#
// create a new window
IntPtr surface = Sdl.SDL_SetVideoMode(width, height, bpp, flags);
Eeee... imamo prozorce Da ne bi nestalo tkao brzo, sledece na redu je glavna petlja programa. U njoj cemo obradjivati poruke i kazati programu kad da se zatvori:
C++
// program main loop
bool done = false;
while (!done)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
// check for messages
switch (event.type)
{
// exit if the window is closed
case SDL_QUIT:
{
done = true;
break;
}
// check for keypresses
case SDL_KEYDOWN:
{
// exit if ESCAPE is pressed
if (event.key.keysym.sym == SDLK_ESCAPE)
done = true;
break;
}
}
}
}
Pascal
// program main loop
while not Done do
begin
while SDL_PollEvent(@Event) <> 0 do
begin
// check for messages
case Event.type_ of
// exit if the window is closed
SDL_QUITEV:
Done := True;
// check for keypresses
SDL_KEYDOWN:
// exit if ESCAPE is pressed
if Event.key.keysym.sym = SDLK_ESCAPE then
Done := True;
end;
end;
end;
C#
bool done = false;
while (!done)
{
Sdl.SDL_Event event_;
while (Sdl.SDL_PollEvent(out event_) != 0)
{
// check for messages
switch (event_.type)
{
// exit if the window is closed
case Sdl.SDL_QUIT:
{
done = true;
break;
}
// check for keypresses
case Sdl.SDL_KEYDOWN:
{
// exit if ESCAPE is pressed
if (event_.key.keysym.sym == Sdl.SDLK_ESCAPE)
done = true;
break;
}
}
}
}
Ovo prozorce ne brise pozadinu, ne crta nista... idemo to da sredimo. Pre nego sto nesto nacrtamo, prvo cemo postaviti neke vrednosti za OpenGL (boju brisanja, velicinu ekrana i slicno):
C++
void InitGL()
{
// initialize OpenGL
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}
void ResizeWindow(int width, int height)
{
if (height == 0)
height = 1;
// setup viewport
glViewport(0, 0, width, height);
// setup matrices
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (float)width / (float)height, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Pascal
procedure InitGL;
begin
// initialize OpenGL
glShadeModel(GL_SMOOTH);
glClearColor(0, 0, 0, 0);
glClearDepth(1);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
end;
procedure ResizeWindow(Width: Integer; Height: Integer);
begin
if (Height = 0) then
Height := 1;
// setup viewport
glViewport(0, 0, Width, Height);
// setup matrices
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
gluPerspective(45, Width/Height, 0.1, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
end;
C#
static void InitGL()
{
// initialize OpenGL
Gl.glShadeModel(Gl.GL_SMOOTH);
Gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
Gl.glClearDepth(1.0f);
Gl.glEnable(Gl.GL_DEPTH_TEST);
Gl.glDepthFunc(Gl.GL_LEQUAL);
Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST);
}
static void ResizeWindow(int width, int height)
{
if (height == 0)
height = 1;
// setup viewport
Gl.glViewport(0, 0, width, height);
// setup matrices
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Glu.gluPerspective(45.0f, (float)width / (float)height, 0.1f, 100.0f);
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glLoadIdentity();
}
Ove funkcije pozivamo odmah posle kreiranja prozora kako bi svebilo spremno za crtanje. Na kraju funkcija koja ce da iscrta trouglic na ekranu:
C++
void DrawScene()
{
// empty buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(0.0f, 0.0f, -5.0f);
glRotatef(SDL_GetTicks() / 50.0f, 0.0f, 0.0f, 1.0f);
// draw scene
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 0.0f);
glEnd();
glPopMatrix();
// show scene
SDL_GL_SwapBuffers();
}
Pascal
procedure DrawScene;
begin
// empty buffers
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glPushMatrix;
glTranslatef(0, 0, -5);
glRotatef(SDL_GetTicks / 50, 0, 0, 1);
// draw scene
glBegin(GL_TRIANGLES);
glColor3f(1, 0, 0);
glVertex3f(0, 1, 0);
glColor3f(0, 1, 0);
glVertex3f(1, -1, 0);
glColor3f(0, 0, 1);
glVertex3f(-1, -1, 0);
glEnd;
glPopMatrix;
// show scene
SDL_GL_SwapBuffers;
end;
C#
static void DrawScene()
{
// empty buffers
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
Gl.glPushMatrix();
Gl.glTranslatef(0.0f, 0.0f, -5.0f);
Gl.glRotatef(Sdl.SDL_GetTicks() / 50.0f, 0.0f, 0.0f, 1.0f);
// draw scene
Gl.glBegin(Gl.GL_TRIANGLES);
Gl.glColor3f(1.0f, 0.0f, 0.0f);
Gl.glVertex3f(0.0f, 1.0f, 0.0f);
Gl.glColor3f(0.0f, 1.0f, 0.0f);
Gl.glVertex3f(1.0f, -1.0f, 0.0f);
Gl.glColor3f(0.0f, 0.0f, 1.0f);
Gl.glVertex3f(-1.0f, -1.0f, 0.0f);
Gl.glEnd();
Gl.glPopMatrix();
// show scene
Sdl.SDL_GL_SwapBuffers();
}
Ova funkcija se poziva u glavnoj petlji posle obrada poruka... i to je to
https://www.mycity.rs/must-login.png
Dopuna: 20 Maj 2008 21:05
Da bi uopste mogli da koristimo SDL, moramo ga prvo inicijalizovati. Funkcija za inicijalizaciju je definisana ovako:
C++
int SDL_Init(Uint32 flags);
Pascal
function SDL_Init( flags : UInt32 ) : Integer;
C#
public static int SDL_Init(int flags)
Kao parametar uzima vrednost koja oznacava koji deo SDL-a zelimo da inicijalizujemo. Moguce vrednosti su:
SDL_INIT_TIMER
SDL_INIT_AUDIO
SDL_INIT_VIDEO
SDL_INIT_CDROM
SDL_INIT_JOYSTICK
SDL_INIT_EVERYTHING
Na kraju programa je potrebno osloboditi resurse koje SDL zauzima. To se vrsi komandom:
C++
void SDL_Quit(void);
Pascal
procedure SDL_Quit;
C#
public static extern void SDL_Quit();
Program koji inicijalizuje i oslobadja memoriju i nije bas zanimljiv, zato cemo da pogledamo funkciju koja kreira SDL prozor:
C++
SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);
Pascal
function SDL_SetVideoMode(width, height, bpp: Integer; flags: UInt32): PSDL_Surface;
C#
public static extern IntPtr SDL_SetVideoMode(int width, int height, int bpp, int flags);
Ulazni parametri su sirina, visina, broj bitova po pikselu i osobine prozora. Kao rezultat dobijamo pokazivac na kreiran prozor.
Moguce osobine prozora su:
SDL_SWSURFACE: memorija za sliku se zauzima u sistemskoj memoriji
SDL_HWSURFACE: memorija za sliku se zauzima u memoriji graficke kartice
SDL_ASYNCBLIT: asinhrono prebacivanje slike u memoriju
SDL_ANYFORMAT: broj bitova po pixelu nije bitan
SDL_HWPALETTE: SDL ima eksluzivno pravo nad paletom
SDL_DOUBLEBUF: Ukljucivanje hardverskog "double buffer-a"
SDL_FULLSCREEN: Prozor preko celog ekrana
SDL_OPENGL: OpenGL moze da crta po prozoru
SDL_OPENGLBLIT: OpenGL i obicne 2D funkcije mogu crtati po prozoru (nije preporucljivo)
SDL_RESIZABLE: Prozor moze da se siri i skuplja
SDL_NOFRAME: Prozor bez ivica (samo slika)
Posto SDL_Quit komanda zatvara sve kreirane prozore, nece nam trebati funkcija koja zatvara glavni prozor jer on treba da bude otvoren do samog kraja programa.
Od osnovnih funkcija, treba da spomenemo jos jednu:
C++
int SDL_PollEvent(SDL_Event *event);
Pascal
function SDL_PollEvent(event: PSDL_Event): Integer;
C#
public static extern int SDL_PollEvent(out SDL_Event sdlEvent);
Ova funkcija cita sta se dogadja s prozorom i daje nam informaciju o tome. Na osnovu te informacije mi znamo da li je korisnik pritisnuo taster, da li je pomerio misa, da li je zatvorio prozor, itd. Uzima strukturu u koju ce nam upisati sta se desilo, a vraca 0 ako nema novih poruka ili neki drugi broj ako ih ima.
Struktura koja sadrzi primljenu poruku izgleda ovako:
C++
typedef union{
Uint8 type;
SDL_ActiveEvent active;
SDL_KeyboardEvent key;
SDL_MouseMotionEvent motion;
SDL_MouseButtonEvent button;
SDL_JoyAxisEvent jaxis;
SDL_JoyBallEvent jball;
SDL_JoyHatEvent jhat;
SDL_JoyButtonEvent jbutton;
SDL_ResizeEvent resize;
SDL_ExposeEvent expose;
SDL_QuitEvent quit;
SDL_UserEvent user;
SDL_SywWMEvent syswm;
} SDL_Event;
Pascal
TSDL_Event = record
case UInt8 of
SDL_NOEVENT: (type_: byte);
SDL_ACTIVEEVENT: (active: TSDL_ActiveEvent);
SDL_KEYDOWN, SDL_KEYUP: (key: TSDL_KeyboardEvent);
SDL_MOUSEMOTION: (motion: TSDL_MouseMotionEvent);
SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP: (button: TSDL_MouseButtonEvent );
SDL_JOYAXISMOTION: (jaxis: TSDL_JoyAxisEvent );
SDL_JOYBALLMOTION: (jball: TSDL_JoyBallEvent );
SDL_JOYHATMOTION: (jhat: TSDL_JoyHatEvent );
SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP: (jbutton: TSDL_JoyButtonEvent );
SDL_VIDEORESIZE: (resize: TSDL_ResizeEvent );
SDL_QUITEV: (quit: TSDL_QuitEvent );
SDL_USEREVENT : ( user : TSDL_UserEvent );
SDL_SYSWMEVENT: (syswm: TSDL_SysWMEvent );
end;
C#
public struct SDL_Event
{
// Fields
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_ActiveEvent active;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_MouseButtonEvent button;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_ExposeEvent expose;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_JoyAxisEvent jaxis;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_JoyBallEvent jball;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_JoyButtonEvent jbutton;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_JoyHatEvent jhat;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_KeyboardEvent key;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_MouseMotionEvent motion;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_QuitEvent quit;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_ResizeEvent resize;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_SysWMEvent syswm;
[FieldOffset(0)]
public byte type;
[FieldOffset(0)]
public Tao.Sdl.Sdl.SDL_UserEvent user;
}
Kao sto vidite, dogadjaji su razvrstani tako da mozemo dobiti tacno one podatke koje su nam potrebni za svaki tip.
To su neke od najosnovnijih funkcija koje cemo koristiti. Sledece na redu je kreiranje jednostavne klase koja ce nam olaksati rad sa SDL prozorom.
Da ne bi morali da se zezamo svaki put sa kreiranjem prozora, podesavanjem OpenGL-a i ostalim koliko-toliko standardnim stvarima, kreiracemo jednu klasu koja ce nam omoguciti lako kreiranje prozora, crtanje po njemu i obradjivanje poruka. Ovu klasu cemo kasnije nadogradjivati i koristiti u svim projektima.
Necemo koristiti nista sto do sad nismo, a objektno orientisano programiranje svi znaju Ovo ce biti lako
Klasa ce za pocetak izgledati ovako:
C++
class CSDLWindow {
protected:
SDL_Surface* surface;
bool initialized;
int width, height, bpp;
bool fullscreen;
virtual bool EventHandler(SDL_Event Event);
virtual bool InitGL();
virtual bool Resize(int Width, int Height);
virtual bool Draw(float AppTime, float DeltaTime);
public:
CSDLWindow(int Width, int Height, int BPP, bool FullScreen);
virtual ~CSDLWindow();
int Run();
};
Pascal
TSDLWindow = class
protected
FSurface: PSDL_Surface;
FInitialized: Boolean;
FWidth, FHeight, FBPP: Integer;
FFullScreen: Boolean;
function EventHandler(Event: TSDL_Event): Boolean; virtual;
function InitGL: Boolean; virtual;
function Resize(Width, Height: Integer): Boolean; virtual;
function Draw(AppTime, DeltaTime: Double): Boolean; virtual;
public
constructor Create(AWidth, AHeight, ABPP: Integer;
AFullScreen: Boolean); virtual;
destructor Destroy; override;
function Run: Integer;
end;
C#
class SDLWindow
{
protected IntPtr surface;
protected bool initialized;
protected int width, height, bpp;
protected bool fullscreen;
protected virtual bool EventHandler(Sdl.SDL_Event Event);
protected virtual bool InitGL();
protected virtual bool Resize(int Width, int Height);
protected virtual bool Draw(float AppTime, float DeltaTime);
public SDLWindow(int Width, int Height, int BPP, bool FullScreen);
~SDLWindow();
public int Run();
}
Posto se u klasi pozivaju funkcije koje su vec objasnjene (inicijalizacija SDL, kreiranje prozora, citanje poruka), necu postaviti kod svake funkcije u klasi nego cu samo objasniti kako se koristi. OpenGL funkcije koriscene u ovoj klasi ce biti objasnjene u sledecem postu.
Klasa se jednostavno koristi. Kreira se instanca klase, pozove se Run metoda i, na kraju, se objekat oslobodi. To je sve. U kodu bi to ovako izgledalo:
C++
int main ( int argc, char** argv )
{
CSDLWindow* sdlwin = new CSDLWindow(800, 600, 24, false);
sdlwin->Run();
delete sdlwin;
return 0;
}
Pascal
var
SDLWin: TSDLWindow;
begin
SDLWin := TSDLWindow.Create(800, 600, 24, False);
SDLWin.Run;
SDLWin.Free;
end.
C#
static void Main(string[] args)
{
SDLWindow sdlwin = new SDLWindow(800, 600, 24, false);
sdlwin.Run();
// C# sam oslobodi kreirane objekte
}
Prva dva parametra kod kreiranja instance klase su sirina i visina prozora, zatim ide broj bitova po pikselu i, na kraju, da li je prozor preko celog ekrana ili ne.
E, sad... kreiranje klase koja nasledjuje ovu je isto lako. Dovoljno je da promenimo Draw metod i scena ce se drugacije iscrtavati, ako promenimo InitGL metod, OpenGL ce se drugacije inicijalizovati, ako promenimo EventHandler, mozemo poruke da obradjujemo kako hocemo, itd... Evo jednog malog primera u kojem cemo promeniti samo Draw metod:
C++
class CMySDLWindow : public CSDLWindow {
protected:
virtual bool Draw(float AppTime, float DeltaTime)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(0.0f, 0.0f, -5.0f);
glRotatef(AppTime * 90.0f, 0.0f, 0.0f, 1.0f);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 0.0f);
glEnd();
glPopMatrix();
SDL_GL_SwapBuffers();
return true;
}
public:
CMySDLWindow(int Width, int Height, int BPP, bool FullScreen) :
CSDLWindow(Width, Height, BPP, FullScreen) {}
};
int main ( int argc, char** argv )
{
CSDLWindow* sdlwin = new CMySDLWindow(800, 600, 24, false);
sdlwin->Run();
delete sdlwin;
return 0;
}
Pascal
type
{ TMySDLWindow }
TMySDLWindow = class(TSDLWindow)
protected
function Draw(AppTime, DeltaTime: Double): Boolean; override;
end;
{ TMySDLWindow }
function TMySDLWindow.Draw(AppTime, DeltaTime: Double): Boolean;
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glPushMatrix;
glTranslatef(0, 0, -5);
glRotatef(AppTime * 90, 0, 0, 1);
glBegin(GL_TRIANGLES);
glColor3f(1, 0, 0);
glVertex3f(0, 1, 0);
glColor3f(0, 1, 0);
glVertex3f(1, -1, 0);
glColor3f(0, 0, 1);
glVertex3f(-1, -1, 0);
glEnd;
glPopMatrix;
SDL_GL_SwapBuffers;
Result := True;
end;
var
SDLWin: TSDLWindow;
begin
SDLWin := TMySDLWindow.Create(800, 600, 24, False);
SDLWin.Run;
SDLWin.Free;
end.
C#
class MySDLWindow : SDLWindow
{
protected override bool Draw(float AppTime, float DeltaTime)
{
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
Gl.glPushMatrix();
Gl.glTranslatef(0.0f, 0.0f, -5.0f);
Gl.glRotatef(AppTime * 90.0f, 0.0f, 0.0f, 1.0f);
Gl.glBegin(Gl.GL_TRIANGLES);
Gl.glColor3f(1.0f, 0.0f, 0.0f);
Gl.glVertex3f(0.0f, 1.0f, 0.0f);
Gl.glColor3f(0.0f, 1.0f, 0.0f);
Gl.glVertex3f(1.0f, -1.0f, 0.0f);
Gl.glColor3f(0.0f, 0.0f, 1.0f);
Gl.glVertex3f(-1.0f, -1.0f, 0.0f);
Gl.glEnd();
Gl.glPopMatrix();
Sdl.SDL_GL_SwapBuffers();
return true;
}
public MySDLWindow(int Width, int Height, int BPP, bool FullScreen) : base(Width, Height, BPP, FullScreen) { }
}
class OOSDL
{
static void Main(string[] args)
{
SDLWindow sdlwin = new MySDLWindow(800, 600, 24, false);
sdlwin.Run();
}
}
To je to za sad... klasu cemo prisirivati/popravljati kad kod vidimo da je to potrebno. Na kraju bi trebalo da dobijemo finu klasicu koja ce pomoci svakom ko se prvi put sretne sa SDL + OpenGL.
https://www.mycity.rs/must-login.png
Dopuna: 23 Maj 2008 20:15
Ovog puta necu pisati nov kod, samo cu objasniti neke od OpenGL funkcija koje smo do sad koristili. OpenGL radi kao jedna velika state masina. Kada se neko stanje postavi ono vazi sve dok se ponovo ne promeni. Zbog toga se na poceku programa, kod nas u InitGL, postavljaju neke vrednosti za koje mislimo da ih necemo menjati. U poslednjem primeru smo u InitGL imali kod koji:
1. postavlja nacin sencenja objekata
2. postavlja boju kojom ce se pozadina brisati
3. ukljucuje depth buffer (z-buffer)
4. postavlja nacin racunanja za prevodjenje 3d koordinate u 2d koordinatu
Objekti u OpenGL mogu da se sence na dva nacina. Prvi nacin se zove FLAT. Taj nacin sencenja oboji ceo ovjekat jednom bojom. Na osnovu boja verteksa i svetla se racuna srednja vrednost boje koja se tada iscrtava. Drugi nacin se zove SMOOTH. Kada je ovaj nacin aktiviran, boje izmedju vertexa se interpoliraju i time se dobija fin prelaz.
Podatak o ovom stanju se postavlja funkcijom glShadeModel koja kao parametar uzima konstantu GL_SMOOTH ili GL_FLAT.
Ove slike prikazuju isti objekat u oba rezima sencenja
FLAT
SMOOTH
Boja pozadine se postavlja funkcijom glClearColor. Ona uzima cetiri parametra koji predstavljaju kolicinu crvene, zelene i plave boje, kao i providnost. Providnost kod ove boje nema znacenje jer ona brise sve sto je bilo nacrtano i nema niceg sto bi se videlo kroz pozadinu. Vrednost za svaki od ova cetiri parametra moze da bude od 0 do 1.
Depth buffer ili, tzv., Z-buffer se koristi da ne bi morali da crtamo prvo najdalje objekta pa onda one blize. Kad ne bi bilo Z-buffer-a, Z koordinata bi sluzila samo da bi se na onovu nje odredilo koliko objekat treba da bude mali (sto je dalji, to je manji) i onda bi moglo da se desi da nacrtamo objekat koji se nalazi jako blizu kamere, a posle njega da nacrtamo objekat u daljini i da se taj objekat u daljini vidi preko prvog objekat jer smo njega poslednjeg nacrtali. Z-buffer resava bas taj problem.
Sta se desava u Z-bufferu... kada iscrtamo neki objekat, u Z-buffer-u se upise na kojoj daljini se on nalazi, sledeci objekat koji se crta prvo mora da prodje test. Taj test se svodi na uporedjivanje njegove Z koordinate sa koordinatom upisanom u Z-buffer. Ako objekat prodje test, bice iscrtan i njegova vrednost ce biti upisana u Z-buffer, a ako ne prodje, OpenGL ga nece iscrtati, a Z-bufer ce ostati nepromenjen. Vrednosti koje se upisiju u Z-buffer idu od 0 (najblize kameri) do 1 (najdalje).
Da bi podesili Z-buffer treba da kazemo kojom vrednoscu zelimo da brisemo Z-buffer kada crtamo nov frejm i koju funkciju zelimo za uporedjivanje sadrzaja Z-buffer-a sa objektima koji se crtaju.
Funkcija za uporedjivanje se uglavnom postavlja na GL_LEQUAL, sto znaci da ce objekat proci test ako je njegova Z koordinata manja ili jednaka onoj upisanoj u Z-buffer-u, sto znaci da ce objekat biti iscrtan ako se nalazi na istoj Z koordinati ili blize od poslednjeg iscrtanog objekata na toj poziciji.
Postavljanje test funkcije se vrsi pozivom glDepthFunc. glDepthFunc prihvata samo jedan parametar koji moze biti:
GL_NEVER: objekat nikad ne prolazi test
GL_LESS: objekat prolazi test ako je blizi
GL_EQUAL: objekat prolazi test ako je na istoj daljini kao prethodni iscrtan na toj poziciji
GL_LEQUAL: objekat prolazi test ako je blizi ili na istoj daljini kao prethodni
GL_GREATER: objekat prolazi test ako je dalji
GL_NOTEQUAL: objekat prolazi test ako je blizi ili dalji
GL_GEQUAL: objekat prolazi test ako je dalji ili na istoj daljini kao prethodni
GL_ALWAYS: objekat uvek prolazi test
Brisanje Z-buffer-a se skoro uvek vrsi vrednoscu 1. To je najdalja moguca vrednost pa ce objekat proci test ako nista nije iscrtano ispred njega.
Postavljanje ove vrednosti se vrsi funkcijom glClearDepth.
Za kraj ostaje jos ukljucivanje Z-buffer-a. Ukljucivanje raznih mogucnosti se vrsi komandom glEnable, a iskljucivanje glDisabled. Za ukljuciavnje Z-buffer-a se prosledjuje parametar GL_DEPTH_TEST.
Na kraju inicijalizacije kazemo OpenGL-u kako zelimo da prevodi 3d koordinate u 2d. Ako ste igrali neke stare igre, verovatno ste primetili kako se teksture blizu kamere pomalo deformisu i ne izgledaju kako bi trebale. Takve stvari se sad mogu izbeci postavljanjem nacina na koji ce OpenGL da racuna neke parametre. Funkcija koja se koristi je glHint, a sve moguce parametre mozete pronaci na ovoj adresi: http://www.opengl.org/documentation/specs/man_page...../hint.html
Posle inicijalizacije OpenGL-a, u programu se poziva funkcija koja podesava opcije koje su vezane za velicinu prozora. Tu podesavamo:
1. Deo prozora na kojem OpenGL crta
2. Osnovnu matricu projekcije
3. Osnovnu matricu polozaja modela
Deo prozora po kojem OpenGL crta se definise funkcijom glViewport. Ona uzima 4 parametra koji definisu pravougaonik na prozoru koji je rezervisan za OpenGL. Za igre je normalno da se cela povrsina koristi za iscrtavanje.
Matrica projekcije sluzi da se odredi kako se 3d tacke konvertuju u 2d tacke. Ucili ste iz tehnickog da postoji perspektiva, ortogonalna projekcija, izometrija i slicno... to su sve nacini na koji moguze 3d tacka da se prevede u 2d tacku. Za pocetak cemo koristiti perspektivu jer ona daje najrealniji prikaz objekata... kako budemo napredovali spomenucemo i ostale vrste ako nam budu zatrebale.
Da bismo postavili neku matricu prvo moramo da je izaberemo. Biranje matrice koju zelimo da menjamo se vrsi funkcijom glMatrixMode. Ona uzima samo jedan parametra koji kaze koju matricu zelimo: GL_MODELVIEW, GL_PROJECTION ili GL_TEXTURE.
Za postavljanje matrice projekcije imamo pomocne funkcije. Pomocna funkcija za perspektivu se zove gluPerspective. Prva dva parametra odredjuju koliko siroko kamera vidi. Prvi parametar je sirina pogleda po Y pravcu, dok se je drugi parametar odnos izmedju sirine i visine ekrana koji OpenGL koristi za racunanje sirine pogleda po X pravcu. Druga dva parametra predstavljaju najmanju i najvecu udaljenost koju kamera moze da vidi. To znaci da se objekti koji su blizi od najblize ili dalji od najdalje udaljenosti nece prikazivati.
Matrica polozaja modela odredjuje gde ce se 3d tacke pomeriti pre nego sto budu iscrtane. To nam omogucava da, recimo, svaki objekat definisemo oko koordinatnog pocetka i da ga zatim ovom matricom pomerimo gde zelimo da bude u 3d svetu. U primeru ovu matricu samo resetujemo na matricu koja nece nista menjati. To se radi funkcijom glLoadIdentity.
Imajte samo na umu da OpenGL pamti koju ste zadnju matricu izabrali za menjanje i svaka funkcija koja menja matricu ce raditi nad tom izabranom. O matricama cemo govoriti jos malo kasnije.
Kada je sve podeseno, moze se preci na crtanje. Sada cu objasniti samo funkcije za brisanje ekrana pre svakog frejma i za prikaz scene na ekran.
Brisanje ekrana se vrsi funkcijom glClear. glClear funkcija ustvari brise buffer-e koje koristi OpenGL. Ona uzima samo jedan parametar koji kaze koje buffer-e treba obrisati. Za sad je dovoljno da znate da moze da brise sliku i Z-buffer sto i radimo u svakom primeru.
Funkcija koja prikazuje sliku iz OpenGL na ekran se zove SDL_GL_SwapBuffers. Ona je deo SDL biblioteke. Kada se zavrsi crtanje frjma, dovoljno je da pozovemo ovu funkciju i slika ce se pojaviti na ekranu.
To je to za sad, sledece na redu je crtanje jednostavnih oblika, ako jos ima zainteresovanih
Dopuna: 23 Maj 2008 20:17
Sad malo da objasnimo osnovne funkcije za crtanje objekata. Imajte na umu da su ove funkcije jako spore i da se uglavnom koriste kada je potrebno nesto na brzinu nacrtati i videti kako to izgleda.
Ovaj kod smo videli u ranijim primerima (ne pisem za svaki jezik posebno jer ce svako ko je pratio dosadasnje primere prepoznati deo koda):
...
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
gluPerspective(45, Width / Height, 0.1, 1000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
...
glMatrixMode funkcija sluzi da bismo izabrali matricu koju cemo menjati. OpenGL radi sa 3 matrice:
1. GL_MODELVIEW: matrica koja vertex (vertex je tacka u 3D prostoru) iz koordinatnog sistema objekta u koordinatni sitem cele scene.
2. GL_PROJECTION: ona sluzi da vertex iz koordinatnog sistema scene pretvori u pixel (pixel je tacka u 2D) u koordinatnom sistemu ekrana.
3. GL_TEXTURE: ova matrica sluzi za manipulaciju koordinatnog sistema tekstura koje se crtanu na objektima.
Kada se matrica izabere uz pomoc glMatrixMode funkcije, svaki poziv funkciji koja menja matricu ce vaziti za tu selektovanu.
glLoadIdentity funkcija ce resetovati trenutno izabranu matricu tako da ne modifikuje poziciju tacke. Posto skoro sve funkcije za promenu matrica rade tako sto na trenutnu verdnost dodaju jos i svoju, ponekad je potrebno isprazniti matricu, i bas tome sluzi ova funkcija.
Za postavljanje matrice projekcije imamo jednu pomocnu funkciju koja se zove gluPerspective. Ona uzima 4 parametra, ugao gledanja po Y osi, odnos izmedju ugla gledanja po X i Y osi, minimalna i maksimalna udaljenost objekata koji ce se videti.
Model matricu cemo za sad ostaviti... idemo da crtamo pa cemo je tamo objasniti
...
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glLoadIdentity;
glTranslatef(0, 0, -5);
glBegin(GL_TRIANGLES);
glColor3f(1, 0, 0);
glVertex3f(-1, -1, 0);
glColor3f(0, 1, 0);
glVertex3f(1, -1, 0);
glColor3f(0, 0, 1);
glVertex3f(0, 1, 0);
glEnd;
...
Brisanje pozadine smo vec spomenuli... sledece dve linije cemo za sd da preskocimo i idemo direktno na deo za crtanje.
glBegin oznacava pocetak bloka za crtanje. Posle ove funkcije je dozvoljeno pozovati druge koje vrse iscrtavanje. Ona uzima samo jedan parametar od kojeg zavisi kako ce se vertexi koje budemo crtali spajati u objekat.
GL_POINTS: Svaki vertex predstavlja jednu tacku.
GL_LINES: Svaki par vertexa definise jednu liniju.
GL_LINE_STRIP: Crta liniju koja povezuje vertexe od prvog do poslednjeg.
GL_LINE_LOOP: Isto kao predhodno samo sto na kraju spoji i poslednju i prvu tacku.
GL_TRIANGLES: Svaka 3 vertexa definisu jedan trougao.
GL_TRIANGLE_STRIP: Crta listu trouglova. Jedan trougao je definisan za svaku tacku posle prve dve. Za neparno n, trougao definisu tacke n, n+1 i n+2. Za parno n, trougao definisu tacke n+1, n, n+2.
GL_TRIANGLE_FAN: Crta listu trouglova. Jedan trougao je definisan za svaku tacku posle prve dve. Trougao je definisan tackama 1, n+1, n+2.
GL_QUADS: Svaka 4 vertexa definisu jedan pravougaonik.
GL_QUAD_STRIP: Crta listu pravougaonika. Pravougaonik je definisan svakim parom verteksa posle prvog para. Vertexi 2n-1, 2n, 2n+2 i 2n+1 definisu pravougaonik n.
GL_POLYGON: Crta jedan konveksan poligon od svih tacaka.
U gore pokazanom kodu se koristi GL_TRIANGLES sto znaci da ce od tri vertexa koja su unesena biti nacrtan jedan trougao. Najjednostavniji nacin unosenja vertexa je komanda glVertex3f. Postoje vise vrsta glVertex* komandi i one se razlikuju u broju i tipu promenljivih koje uzimaju. Npr. glVertex2i uzima 2 parametra (x i y, z postavlja na 0) i tip podatka je ceo broj. U najvecem broju slucajeva glVertex3f ce vrsiti posao kako treba.
Vertex koji se crta uzima boju, materijal, texturu, itd, koja je bila podesena pre poziva glVertex* funkcije. Zbog jednostavnosti primera do sada je menjana samo boja. Boja se menja funkcijama glColor*. Opet ce glColor3f skoro uvek biti sasvim dovoljna (ona definise r, g i b vrednosti).
Ako pogledate kod, videcete da se za sve vertexe Z postavlja na 0, a pre toga je matrica proekcije bila podesena da ne prikacuje objekte koji su blizi od 0,1. To znaci da trougao nece biti vidljiv. Zato imamo one dve funkcije odmah posle brisanja buffera. Vec znamo da glLoadIdentity resetuje matricu. glTranslatef je funkcija koja u trenutnu matricu ubacuje matricu za pomeranje po X, Y i Z osi. U ovom primeru cemo X i Y ostaviti na 0, a Z na -5... to znaci da ce svaka koordinata vertexa biti pomerena po Z osi za -5 (u OpenGL, -Z znaci da je objekat dalji od kamere... -Z ulazi u ekran). To znaci da ce prvi pizel biti nacrtan na -1, -1, -5 i bice vidljiv. glTranslatef je samo jedna od funkcija za rad sa matricama... kasnije cemo spomenuti jos neke.
Na kraju crtanja pozivamo glEnd funkciju. Posle nje je moguce poceti nov blok glBegin komandom (recimo prvo hocemo da crtamo trougolove, pa kvadrate, pa linije,...).
Toliko za sada... sad znate osnovne komande za crtanje, pa probajte malo da se poigrate s njima
Next... textures...
|