Diferenças na execução do loop e exibição de blits em SDL (Linux-Windows)

Iniciado por mhkgalvez, 25 de Novembro de 2010, 18:35

tópico anterior - próximo tópico

fpissarra

Para quem estiver lendo essa thread:

As linhas comentadas no tratamento de eventos:

if (SDL_PollEvent(&event))
  {
    /* Pra que isso se SDLK_ESCAPE é tratado abaixo? */
    //if (event.type == SDL_QUIT)
    //  return 0;


Devem ser "descomentadas" caso não se use o modo SDL_FULLSCREEN. Senão o botão de fechar da janela não funcionará. Dica do mhkgalvez.


mhkgalvez

Erro maluco. Uma das coisas que só acontecem comigo. Pode ver que tem pouco material disso na internet. Mas estamos perseverando. A todos que puderem opinar, peço que o façam, por favor.
"A quem vencer, eu o farei coluna no templo do meu Deus, e dele nunca sairá; e escreverei sobre ele o nome do meu Deus, e o nome da cidade do meu Deus, a nova Jerusalém, que desce do céu, do meu Deus, e também o meu novo nome."

fpissarra

Já que o pessoal tem demonstrado algum interesse nesse tópico. Eis uma coisa que descobri recentemete:

O X11 (xorg) não dá suporte a Hardware Surfaces ou aceleração gráfica no SDL. Ou seja, se você pretende desenvolver jogos ou um "demo" em 2D, pode encontrar sérios problemas como o do mhkgalvez, por exemplo.

A solução para isso é usar OpenGL inclusive em aplicações 2D. Usar SDL com OpenGL é fácil e essa integração existe para, dentre outras coisas, facilitar o set up do framebuffer para uso do OpenGL.

Na minha opinião existem duas táticas que podem ser tentadas:

1. Criar uma surface RGB na memória (SDL_SWSURFACE), do tamanho da tela e usá-la como buffer terciário. Assim, os frames serão desenhados primeiro nessa superfície e depois, no tempo certo, ela será usada como textura num quadrado (GL_QUADS) numa projeção Ortogonal no espaço 3D do OpemGL;

2. OU... Cada "retângulo" é desenhado independentemente em quadrados (GL_QUADS), em produndidades diferentes de acordo com a necessidade, no espaço 3D do OpenGL (também numa projeção ortogonal). Cada um desses "quadrados" é texturizado de acordo com a imagem.

Obs: O que eu chamo de "quadrados" ai em cima são retângulos... só disse "quadrados" para explicitar o uso de glBegin(GL_QUADS) ou o equivalente em Vertex Buffers (ou Objects). Quem sabe um pouco de OpenGL entendeu!

Eu opto pela segunda opção, já que podemos aproveitar o canal Alpha das texturas, poupar tempo e não usar um buffer terciário e, de lambuja, ganhamos uma dimensão (Z) para trabalhar sobreposições... No exemplo do código que temos postado por aqui, cada uma das imagens teria um Z diferente. O texto (surface press, no código) ficaria mais à frente; a imagem "base" ficaria mais ao fundo...

mhkgalvez, estou fazendo alguns testes aqui para tentar facilitar sua (e a minha) vida... depois eu posto o código final (pode demorar um cadinho!)...

[]s

fpissarra

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