Autor Tópico: Diferenças na execução do loop e exibição de blits em SDL (Linux-Windows)  (Lida 5355 vezes)

Offline fpissarra

  • Usuário Ubuntu
  • *
  • Mensagens: 246
    • Ver perfil
    • Lost in the e-Jungle
Re: Diferenças na execução do loop e exibição de blits em SDL (Linux-Windows)
« Resposta #15 Online: 01 de Dezembro de 2010, 20:34 »
Para quem estiver lendo essa thread:

As linhas comentadas no tratamento de eventos:

Código: [Selecionar]
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.

Offline fpissarra

  • Usuário Ubuntu
  • *
  • Mensagens: 246
    • Ver perfil
    • Lost in the e-Jungle
Re: Diferenças na execução do loop e exibição de blits em SDL (Linux-Windows)
« Resposta #16 Online: 03 de Dezembro de 2010, 15:21 »
Achei isso: Uma excelente lista de dicas sobre SDL e SDL+OpenGL...

http://osdl.esperide.com/main/documentation/rendering/SDL-hints.html

[]s

Offline mhkgalvez

  • Usuário Ubuntu
  • *
  • Mensagens: 289
  • Não temas: A Esperança VIVE!!!
    • Ver perfil
    • The Century Truth - Conhecendo a Verdade deste Século.
Re: Diferenças na execução do loop e exibição de blits em SDL (Linux-Windows)
« Resposta #17 Online: 03 de Dezembro de 2010, 18:51 »
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."

Offline fpissarra

  • Usuário Ubuntu
  • *
  • Mensagens: 246
    • Ver perfil
    • Lost in the e-Jungle
Re: Diferenças na execução do loop e exibição de blits em SDL (Linux-Windows)
« Resposta #18 Online: 04 de Dezembro de 2010, 09:58 »
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
« Última modificação: 04 de Dezembro de 2010, 10:04 por fpissarra »

Offline fpissarra

  • Usuário Ubuntu
  • *
  • Mensagens: 246
    • Ver perfil
    • Lost in the e-Jungle
Re: Diferenças na execução do loop e exibição de blits em SDL (Linux-Windows)
« Resposta #19 Online: 09 de Dezembro de 2010, 11:36 »
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...

Código: [Selecionar]
/* 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