Eis um exemplo de uso do OpenGL com SDL para sanar o problema citado acima.
O código fica mais complicado... mas, pelo menos, o OpenGL usa os recursos de hardware...
Espero que seja de alguma ajuda...
/* Esse código só mostra como usar SDL com OpenGL para
conseguir usar recursos de Hardware no Linux.
O código funciona em todas as plataformas (acho). */
#include <stdlib.h>
#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_opengl.h>
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define SCREEN_BPP 32
#define FRAMES_PER_SECOND 50
#define FRAME_TIME (Uint32)((1.0f / (float)FRAMES_PER_SECOND) * 1000.0f)
#define NEAR_PLANE 0.0f
#define FAR_PLANE 10.0f
Uint32 oldTicks;
GLint tex_id = 0;
/* --- Protótipos --- */
void InitializeSDL(void);
void InitializeOpenGL(void);
void MainLoop(void);
void LoadTextures();
void ProcessEvents(void);
void Render(void);
void Quit(void);
int main(int argc, char **argv)
{
/* Prefiro manter as rotinas separadas! */
InitializeSDL();
InitializeOpenGL();
LoadTextures();
MainLoop();
/* OBS: Nunca chegará aqui */
return 0;
}
/* Só é chamada na saída, se o SDL for inicializado corretamente */
void Quit(void)
{
/* Temos que lembrar de nos livrar da textura! */
if (tex_id != 0)
glDeleteTextures( 1, &tex_id );
SDL_Quit();
}
void InitializeSDL(void)
{
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER))
{
fprintf(stderr, "Erro ao tentar inicializar o SDL.\n");
exit(1);
}
atexit(Quit);
SDL_WM_SetCaption("Teste de sprites", NULL);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
/* Não precisa guardar o ponteiro para a surface pq não é
possível usá-la no OpenGL.
Só não inicializei em FULLSCREEN para ver a saída dos fprintf's
Adicione (usando o operador |) SDL_FULLSCREEN se quiser. */
if ( SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_OPENGL) == NULL )
{
fprintf(stderr, "Erro ao setar o modo de vídeo.\n");
exit(1);
}
}
void InitializeOpenGL(void)
{
/* Não precisamos de interpolação aqui. */
glShadeModel( GL_FLAT );
/* Não precisamos de iluminação e de teste de profundidade */
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
/* Precisamos de texturas e blending para usar o canal alpha nas texturas! */
glEnable( GL_TEXTURE_2D );
glEnable( GL_BLEND );
/* Essa função de blending é necessária para usar o alpha (transparência). */
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
/* Cor de fundo RGBA */
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
/* Vamos usar toda a área cliente */
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
/* Ajusta o sistema de coordenadas (projeção ortogonal) */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
/* glOrtho toma l,r,b.t,n,f como parâmetros.
O ajuste abaixo coloca a origem (0,0) no centro da tela
É possível colocar o sistema de coordendas da "tela" do mesmo modo que o SDL, basta
manipular essa incialização. Só que você terá que modar o translado lá em Render(). */
glOrtho(-SCREEN_WIDTH/2, SCREEN_WIDTH/2, SCREEN_HEIGHT/2, -SCREEN_HEIGHT/2, NEAR_PLANE, FAR_PLANE);
/* Precisa disso para inverter o sentido de Y, nas texturas. */
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef(1, -1, 1);
glMatrixMode(GL_MODELVIEW);
/* Isso é feito em Render()! */
//glLoadIdentity();
}
/* Nada que você já não tenha visto aqui. */
void MainLoop(void)
{
Uint32 newTicks, delay;
oldTicks = SDL_GetTicks();
for (;;)
{
ProcessEvents();
newTicks = SDL_GetTicks();
delay = newTicks - oldTicks;
if (delay >= FRAME_TIME)
{
Render();
oldTicks = newTicks;
}
}
}
/* Por enquanto carrego apenas 1 textura aqui! */
void LoadTextures(void)
{
GLenum fmt;
SDL_Surface *img, *tmp;
/* Vou usar PNG ao invés de GIF porque GIF não possui formato RGBA.
Ou seja, GIF só permite 254 cores... PNG permite todas e mais transparência! */
IMG_Init(IMG_INIT_PNG);
/* ATENÇÂO: A imagem tem que ter dimensões (width e height) que satisfaçam a equação:
y = 2^x (1)
Onde: y é o tamanho e x é um inteiro positivo.
Eu até tinha feito uma rotina para conversão, caso as dimensões não atendessem (1),
mas, lidar com transparência com SDL é meio chato!
Mas, se o seu alvo foi OpenGL 2.0 ou superior, pode desconsiderar essa restrição. */
img = IMG_Load("test.png");
if (img == NULL)
{
fprintf(stderr, "Não foi possível carregar a imagem.\n");
IMG_Quit();
exit(1);
}
/* OBS: Observei que tentar converter o formato - com SDL_DisplayFormatAlpha() - não é algo que
funcione muito bem... Pelo menos não para mim! */
#ifdef DEBUG
fprintf(stderr, "Imagem: Bytes por pixel=%d\n"
" Width=%d\n"
" Height=%d\n",
img->format->BytesPerPixel,
img->w,
img->h);
#endif
/* Ajusta o alinhamento dos componentes de um texel. Isso é usado por glTexImage2D(), abaixo */
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
/* Aloca espaço de nomes para uma textura */
glGenTextures( 1, &tex_id );
#ifdef DEBUG
fprintf(stderr, "Texture id = %d\n", tex_id);
#endif
/* Usa a textura alocada */
glBindTexture( GL_TEXTURE_2D, tex_id );
/* Ajusta parâmetros e ambiente de textura atual */
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexEnvi( GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE );
/* Provavelmente (é quase certo) que BytesPerPixel sempre será 4.
Mas eu testo assim mesmo! */
switch (img->format->BytesPerPixel)
{
case 3:
fmt = GL_RGB;
break;
case 4:
fmt = GL_RGBA;
break;
default:
fprintf(stderr, "Formato de imagem errado!\n");
SDL_FreeSurface(img);
IMG_Quit();
exit(0);
}
/* OBS:
Se a versão do OpenGL for inferior a 2.1, podemos ter problemas com as dimensões da
imagens. As versões inferiores a 2.1 não permitem tamanhos diferentes de 2^n.
Pode ser necessário verificar a versão do OpenGL e adicionar código aqui para
"aumentar" o tamanho da imagem. Podemos preencher a área adicional com alpha=0 (transparente). */
/* Carrega a textura para o OpenGL.
Uso o mesmo formato interno da imagem original */
glTexImage2D( GL_TEXTURE_2D, 0, fmt,
img->w, img->h, 0, fmt, GL_UNSIGNED_BYTE, img->pixels);
#ifdef DEBUG
GLint err = glGetError();
fprintf(stderr, "Texture Transfer is %sOK: %d\n", err != GL_NO_ERROR ? "NOT " : "", err);
#endif
/* Não precisamos mais da superfície e nem do SDL_image. */
SDL_FreeSurface(img);
IMG_Quit();
}
void ProcessEvents(void)
{
SDL_Event event;
/* Se tem um evento, processa-o */
if (SDL_PollEvent(&event))
{
/* É necessário processar SDL_QUIT em uma janela! */
if (event.type == SDL_QUIT)
exit(0);
/* QQ tecla gera um evento SDL_QUIT. */
if (event.type == SDL_KEYDOWN)
{
/* OK, eu poderia simplesmente usar exit(0), mas não vamos
sacanear com o sistema de mensagens do SDL, né?! */
event.type = SDL_QUIT;
SDL_PushEvent(&event);
}
}
}
void Render(void)
{
/* Limpa o backbuffer */
glClear( GL_COLOR_BUFFER_BIT );
/* Matriz GL_MODELVIEW já está ativa neste ponto! */
//glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* Afasta a "câmera" da origem */
glTranslatef( 0.0f, 0.0f, -10.0f);
/* Usaremos a nova textura!
No i945G não funciona (Open GL 1.4), veremos na nVidia (Open GL 3.1)
Nesse exemplo essa chamada é supérflua (pq temos apenas 1 textura e
glBindTexture() já foi chamada em LoadTextures()), mas eu faço assim mesmo
só para garantir que estamos lidando com a textura certa. */
glBindTexture( GL_TEXTURE_2D, tex_id );
/* Desenha um retângulo. */
glBegin( GL_QUADS );
/* A cor do retângulo não é importante. A textura foi definida como GL_REPLACE. */
//glColor4f(1.8f, 1.8f, 1.8f, 1.0f);
/* O vetor normal não é realmente necessário aqui! Mas, não faz mal informá-lo! */
glNormal3f( 0, 0, 1 );
/* Note que o retângulo é definido "counter-clockwise" */
/* As dimensões devem ser "proporcionais" à textura, para não distorcer! */
/* OBS: Ao invés de manipular a matriz de transformação de coordenadas de texturas, lá
na inicialização do OpenGL, poderíamos manipular as coordenadas aqui.
Eu "prefiro" usar coordenadas da esquerda para direita e de cima para baixo! */
glTexCoord2f( 0, 0 ); glVertex3f(-128, 128, 0);
glTexCoord2f( 0, 1 ); glVertex3f(-128, -128, 0);
glTexCoord2f( 1, 1 ); glVertex3f(128, -128, 0);
glTexCoord2f( 1, 0 ); glVertex3f(128, 128, 0);
glEnd();
/* Chamar glFlush() não é realmente necessário, já que SDL_GL_SwapBuffers() o faz (acho) */
//glFlush();
/* Depois de tudo desenhado... faz swap dos buffers */
SDL_GL_SwapBuffers();
}
[]s