Určitě se vám nelíbí mít všechny textury uložené v BMP souborech, které nejsou zrovna přátelské k místu na disku. Bohužel SDL žádný jiný formát přímo nepodporuje. Nicméně existuje malé rozšíření v podobě knihovničky SDL Image poskytující funkci IMG_Load(), která umí načíst většinu používaných grafických formátů.
Pokud tuto knihovnu nemáte, můžete si ji stáhnout ze známé adresy http://www.libsdl.org/. Začneme vložením hlavičkových souborů. Nezapomeňte kromě OpenGL a SDL přilinkovat i SDL_image.
#include <SDL.h>// Hlavní SDL knihovna
#include <SDL_opengl.h>// Vloží za nás OpenGL
#include <SDL_image.h>// Abychom mohli používat funkci IMG_Load()
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
Daklarujeme proměnnou OpenGL textury.
GLuint gl_texture;// Textura
Napíšeme funkci, která načte SDL\_Surface. Obrázek musíme současně upravit, aby byl ve složkách RGB a nebyl vzhůru nohama.
SDL_Surface* LoadBitmap(const char *filename)// Funkce pro načteni bitmapy
{
Uint8 *rowhi, *rowlo;// Ukazatele na prohazováni řádků
Uint8 *tmpbuf, tmpch;// Dočasná paměť
int i, j;// Řídící proměnné pro cykly
SDL_Surface *image;// Načítaný obrázek
image = IMG_Load(filename);// Načtení dat obrázku
if (image == NULL)// Ošetření chyby při načítání
{
fprintf(stderr, "Nepodarilo se nacist %s: %s\n", filename, SDL_GetError());
return(NULL);
}
GL surfaces jsou vzhůru nohama, takže budeme muset bitmapu převrátit. Alokujeme dynamickou paměť, do které odložíme právě přemisťovaný kousek. Její velikost se neurčí podle image->w, ale podle image->pitch, protože u SDL\_Surface se může stát, že kvůli zarovnávání v paměti SDL zabere více místa, než je skutečný rozměr obrázku.
Image->pitch udává šířku zabrané paměti, ale image->w udává šířku obrázku.
tmpbuf = (Uint8 *)malloc(image->pitch);// Alokace paměti
if (tmpbuf == NULL)// Ošetření chyby
{
fprintf(stderr, "Nedostatek pameti\n");
return NULL;
}
// Nastavení prvního a posledního řádku
rowhi = (Uint8 *)image->pixels;
rowlo = rowhi + (image->h * image->pitch) - image->pitch;
for (i = 0; i < image->h/2; i++)
{
// Převrácení BGR na RGB
if (image->format->Bshift == 0)
{
for (j = 0; j < image->w; j++)
{
tmpch = rowhi[j*3];
rowhi[j*3] = rowhi[j*3+2];
rowhi[j*3+2] = tmpch;
tmpch = rowlo[j*3];
rowlo[j*3] = rowlo[j*3+2];
rowlo[j*3+2] = tmpch;
}
}
// Prohození řádků
memcpy(tmpbuf, rowhi, image->pitch);
memcpy(rowhi, rowlo, image->pitch);
memcpy(rowlo, tmpbuf, image->pitch);
// Posun ukazatelů na řádky
rowhi += image->pitch;
rowlo -= image->pitch;
}
Zbývá už jen smazat dočasný odkládací prostor a vrátit načtenou bitmapu.
free(tmpbuf);// Úklid
return image;// Vrátí načtený obrázek
}
Teď, když máme načtený obrázek, vytvoříme funkci, která z něj vytvoří OpenGL texturu. Hned na začátku se pokusíme načíst obrázek právě napsanou funkcí LoadBitmap(). Pokud tato oprace selže, vrátíme nulu.
GLuint CreateTexture(const char* file, int min_filter, int mag_filter, bool mipmaps)// Vytvoří texturu
{
SDL_Surface *surface;// Obrázek
surface = LoadBitmap(file);// Načtení obrázku
if (surface == NULL)// Ošetření chyby
return 0;
Vytvoříme místo pro novou texturu a nastavíme filtry podle parametrů funkce.
GLuint texture;// OpenGL textura
glGenTextures(1, &texture);// Generování jedné textury
glBindTexture(GL_TEXTURE_2D, texture);// Nastavení textury
// Nastavení požadovaných filtrů
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
Vybereme podle přání, jestli se mají používat mipmapy nebo ne.
if (mipmaps)// Mipmapovaná textura
{
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, surface->w, surface->h, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);
}
else// Obyčejná textura
{
glTexImage2D(GL_TEXTURE_2D, 0, 3, surface->w, surface->h, 0, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);
}
Nakonec vše uklidíme. Pro mazání SDL\_Surface používejte zásadně SDL\_FreeSurface() a nikdy delete nebo free. Data bitmapy můžeme také smazat, protože nejsou potřeba. OpenGL si je nakopírovalo a my už se o ně nemusíme starat.
SDL_FreeSurface(surface);// Smazání SDL_Surface
surface = NULL;// Nastavení ukazatele na NULL
return texture;// Vrátí texturu
}
Funkce, která vykresluje OpenGL scénu. Obdoba DrawGLScene() z NeHe OpenGL Tutoriálů.
void RenderScene()// Vykreslí scénu
{
static float rott = 0.0f;// Statická proměnná pro úhel rotace
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Smaže obrazovku a hloubkový buffer
glLoadIdentity();// Reset matice
glBindTexture(GL_TEXTURE_2D, gl_texture);// Zvolí texturu
glTranslatef(0.0f, 0.0f, -6.0f);// Přesun do obrazovky
glRotatef(rott,-1.0, 0.0, 0.0);// Natočení scény
glRotatef(rott, 0.0, 1.0, 0.0);
glRotatef(rott, 0.0, 0.0,-1.0);
glBegin(GL_POLYGON);// Vykreslí obdélník
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 0.0f,-1.0);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 0.0f, 1.0);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 0.0f, 1.0);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 0.0f,-1.0);
glEnd();
rott += 0.1;// Zvětší rotaci
SDL_GL_SwapBuffers();// Prohození bufferů
}
Incializace SDL.
void InitSDL()// Inicializace SDL
{
// Inicializace SDL grafiky a SDL Timeru
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
fprintf(stderr, "Nepodarilo se inicializovat SDL: %s\n", SDL_GetError());
exit(1);
}
atexit(SDL_Quit);// Nastavení funkce při volání exit();
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);// Chceme doublebuffering s OpenGL
// Nastavení velikosti a stylu okna
unsigned int flags = SDL_OPENGL;// | SDL_FULLSCREEN; // Případně bitově orovat s fullscreen
if (SDL_SetVideoMode(640, 480, 0, flags) == NULL)// Vytvoří okno
{
fprintf(stderr, "Nepodarilo se vytvorit OpenGL okno : %s\n", SDL_GetError());
SDL_Quit();
exit(2);
}
SDL_WM_SetCaption("Lesson - SDL_image", NULL);// Nastavení titulku okna
}
Incializace OpenGL.
void InitOpenGL()// Nastavení základních parametrů OpenGL
{
glViewport(0, 0, 640, 480);
glEnable(GL_TEXTURE_2D);// Povolení textur
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);// Černé pozadí
glClearDepth(1.0);// Povolení mazání hloubkového bufferu
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);// Povolení testování hloubky
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHT0);// Povolení světel
glEnable(GL_COLOR_MATERIAL);// Povolení vybarvování materiálů
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (GLfloat)640 / (GLfloat)480, 0.1f, 1000.0f);
glMatrixMode(GL_MODELVIEW);
// Načtení textury
if ((gl_texture = CreateTexture("test.jpg", GL_LINEAR, GL_LINEAR, false)) == 0)
{
fprintf(stderr, "Nepodarilo se vytvorit texturu\n");
exit(1);
}
}
Při zavírání programu voláme ShutDownApp(), která smaže texturu a ukončí SDL.
void ShutDownApp()// Deinicializace
{
glDeleteTextures(1, &gl_texture);// Vymazání textury
SDL_Quit();// Ukončení SDL
}
Napíšeme funkci, zpracovávající zprávy, které programu posílá systém. Reagujeme na ukončení aplikace (SDL_QUIT), stisk klávesy (SDL_KEYDOWN) Esc (SDLK_ESCAPE) a překreslení okna SDL_VIDEOEXPOSE. Ostatní události nás nezajímají.
bool ProcessEvents()// Obsluha událostí
{
SDL_Event event;// Proměnná zprávy
while (SDL_PollEvent(&event))// Dokud přicházejí zprávy, zpracovávat je
{
switch (event.type)// Jaká přišla zpráva?
{
case SDL_QUIT:// Uživatel si přeje ukončit aplikaci
return false;
break;
case SDL_KEYDOWN:// Stisknutá klávesa
switch (event.key.keysym.sym)// Jaká klávesa?
{
case SDLK_ESCAPE:// Esc
return false;
break;
}
break;
case SDL_VIDEOEXPOSE:// Potřeba překreslit okno
RenderScene();// Vykreslí scénu
break;
}
}
return true;
}
Main() je první funkcí, která se volá po spuštění programu. Po jejím ukončení se ukončí i program. Na začátku inicializujeme SDL a OpenGL. V hlavní smyčce programu zpracováváme zprávy a pokud žádné nepřijdou, překreslíme scénu. Po příchodu zprázy o ukončení nebo pokud uživatel stiskl klávesu Esc, uvolníme zdroje, které program zabral a ukončíme aplikaci.
int main(int argc, char ** argv)// Hlavní funkce
{
InitSDL(); // Inicializace SDL
InitOpenGL();// Inicializace OpenGL
while (ProcessEvents())// Hlavní smyčka programu
{
RenderScene();// Pokud nepřišla zpráva překreslí scénu
}
ShutDownApp();// Deinicializace
return 0;// Konec main() a programu
}
Tak to je asi všechno. V archivu zdrojového kódu najdete pouze CPP soubor s JPG obrázkem, takže musíte program ještě přeložit. Pro kompilaci v Linuxu napište:
gcc `sdl-config --libs --cflags` -lSDL_image -L/usr/X11R6/lib -lGL -lGLU lessonSDL_image.cpp
Pracujete-li v jiném operačním systému, nezapomeňte kromě OpenGL a SDL přilinkovat i knihovnu SDL_image.
napsal: Bernard Lidický - Berny <2berny (zavináč) seznam.cz>