Tutorial Qt – Capitulo 13 – OpenGL

Quando lidar com Qt e OpenGl, existe o problema de onde desenhar as linhas. Como o alvo desse tutorial é ser um tutorial de Qt, esse capítulo é baseado no tutorial de OpenGL do NeHe. Para aqueles de vocês que estiverem interessados em OpenGL, dê uma olhada nesse tutorial. Para aqueles que quiserem trabalhar com OpenGL dentro do Qt, continue nesse tutorial.

Esse tutorial é baseado nos primeiros doze capítulos do tutorial de OpenGL do NeHe. Mas primeiro, existe alguma teoria relacionada ao Qt que precisa ser vista. O Qt fornece um módulo OpenGL inteiro em quase todas as versões. Algumas versões comerciais não tem esse módulo. O módulo contém um conjunto de classes que encapsula as partes interessantes de OpenGL. Em muitas aplicações, a classe QGLWidget é a única classe necessária. Nesse capitulo, todo código será baseado em um classe derivada de QGLWidget chamada NeHeWidget. A declaração dessa classe pode ser vista abaixo:


class NeHeWidget : public QGLWidget
{
Q_OBJECT

public:
NeHeWidget( int timerInterval=0, QWidget *parent=0, char *name=0 );


protected:
virtual void initializeGL() = 0;
virtual void resizeGL( int width, int height ) = 0;
virtual void paintGL() = 0;

virtual void keyPressEvent( QKeyEvent *e );
virtual void timeOut();
protected slots:
virtual void timeOutSlot();
 
 
 
private:
QTimer *m_timer;
};

O widget não pode ser instanciado, pois contém métodos virtuais puros, mais sobre isso mais tarde. Em alguns capítulos do tutorial do NeHe, os gráficos são atualizados por um temporizador. Para facilitar essa parte, um QTimer é usado. Um QTimer é uma classe que pode ser configurada para ou chamar um slot após um tempo dado, ou chamar de um slot em um certo intervalo de tempo específico. Fornecendo um timerInterval diferente de zero ao construtor, o temporizador é configurado para chamar timeOutSlot com o intervalo dado. O método timeOutSlot então chama o métoso timeOut. A razão para essa invocação indireta de timeOut, é para evitar ter um slot adicional (incluindo a sobrecarga de timeOutSlot) para cada sub-classe.
O método keyPressEvent manipula a tecla Esc e simplesmente fecha a janela se ela for pressionada. Mas adiante, as sub-classes sobrecarregarão keyPressEvent novamente para fornecer mais funcionalidades, mas todas chamarão a implementação da classe base.

O exemplo abaixo mostra a função main que será usada para iniciar os exemplos. Cada capítulo então consistirá de uma implementação específica da classe NeHeWidget chamada NeHeChapterXX, onde XX indica o número do capítulo do tutorial do NeHe.


int main( int argc, char **argv )
{
QApplication a( argc, argv );

NeHeWidget *w = new NeHeChapterXX();
a.setMainWidget( w );
w->show();


 
return a.exec();
}

O primeiro capítulo do tutorial do NeHe mostra como configurar o OpenGL, procedimentos que são cuidados pelo Qt e pelo QGLWidget. O código base, que é frequentemente referenciado pelo tutorial, está encapsulado na classe NeHeWidget.

Capítulo 2

Quando estiver lendo o capítulo 2 dos tutorial do NeHe, é bom dar uma olhada também na descrição detalhada de QGLWidget da documentação oficial do Qt. O capítulo introduz os conceitos básicos de desenho usando OpenGL: vértices, triângulos e quadrados.

Figure 14-1
Figure 14-1

O método DrawGLScene do tutorial do NeHe corresponde ao paintGL do Qt. Da mesma forma, resizeGL e initializeGL tem métodos correspondentes dentre as classes bases do NeHe. O método paintGL da classe NeHeChapter2 é mostrado abaixo, e pode ser facilmente reconhecido da leitura do tutorial do NeHe.


void paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

glTranslatef(-1.5f,0.0f,-6.0f);

glBegin(GL_TRIANGLES);
glVertex3f( 0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
glEnd();

glTranslatef(3.0f,0.0f,0.0f);
 
 
 
glBegin(GL_QUADS);
glVertex3f(-1.0f, 1.0f, 0.0f);
glVertex3f( 1.0f, 1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glEnd();
}

Capítulo 3

O capítulo 3 introduz o modo de usar o método glColor3f para configurar a cor do objeto. Também mostra como configurar a cor e criar vários vértices dando as eles esse cor. A classe NeHeChapter3 é idêntica a classe do capítulo 2 exceto pela adição da chamado ao método glColor3f em paintGL.

Figure 14-2
Figure 14-2

Capítulo 4

No capitulo 4, movimento é introduzido. O exemplo abaixo mostra as partes interessantes da classe NeHeChapter4.

Figure 14-3
Figure 14-3


class NeHeChapter4 : public NeHeWidget
{
private:
GLfloat rtri, rquad;
public:
NeHeChapter4( QWidget *parent=0, char *name=0 ) : NeHeWidget( 50, parent, name )
{
rtri = rquad = 0.0f;
}

protected:
void paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();


glTranslatef(-1.5f,0.0f,-6.0f);
glRotatef(rtri,0.0f,1.0f,0.0f);

...
glLoadIdentity();
glTranslatef(1.5f,0.0f,-6.0f);
glRotatef(rquad,1.0f,0.0f,0.0f);

}
void timeOut()
{
rtri += 0.5f;
rquad -= 0.25f;
 
 
 
updateGL();
}
};

No código de exemplo do NeHe, os valores de rtri e rquad são atualizados logo após cada operação de desenho. Na versão Qt, o temporizador faz isso e depois chama o método updateGL para agendar o redesenho. Essa abordagem faz a animação ser executada no mesmo ritmo, independente do framerate

Capítulo 5

O capitulo 5 introduz objetos 3D de verdade. Nos capitulo anteriores, objetos planos no espaço 3D foram vistos, agora uma caixa e uma pirâmide são mostradas. O código é quase idêntico ao do capitulo 4.

Figure 14-4
Figure 14-4

Capítulo 6

No capítulo 6, coisas interessantes acontecem: texturização é introduzida. Aqui, existem algumas peculiaridades do Qt que precisam ser tratadas.

Figure 14-5
Figure 14-5

No exemplo abaixo, o código que carrega as imagens e liga elas na textura é mostrado. Observe que a classe QImage tem suporte a um formato amigável ao OpenGL. O método loadGLTexturers é chamado a partir do método initializeGL.


void NeHeChapter6::loadGLTextures()
{
QImage t;
QImage b;

if ( !b.load( "../images/nehe.bmp" ) )
{
b = QImage( 16, 16, 32 );
b.fill( Qt::green.rgb() );
}


 
t = QGLWidget::convertToGLFormat( b );
glGenTextures( 1, &texture[0] );
glBindTexture( GL_TEXTURE_2D, texture[0] );
glTexImage2D( GL_TEXTURE_2D, 0, 3, t.width(), t.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t.bits() );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}

Capítulo 7

O capitulo 7 introduz iluminação, filtros de texturas e movimentos controlador pelo teclado.

Figure 14-6
Figure 14-6

O código se parece muito como o código do capítulo 6, mas como algumas mudanças em loadGLTextures, e uma nova versão de keyPressEvent. Partes dessa nova versão são mostradas no exemplo abaixo:


void keyPressEvent( QKeyEvent *e )
{
switch( e->key() )
{
case Qt::Key_L:
light = !light;

if( light )
glEnable( GL_LIGHTING );
else
glDisable( GL_LIGHTING );


break;
case Qt::Key_F:
filter++;
if( filter > 2 )
filter = 0;

break;
case Qt::Key_Left:
droty -= 0.01f;
break;

 
 
 
default:
NeHeWidget::keyPressEvent( e );
}
}

Usando essa abordagem, estamos usando o mecanismo do Qt para manipular eventos do teclado, ao invés de confiar nos arrays de teclas usados no tutorial do NeHe.

Capítulo 8

No capitulo 8 o efeito de mistura é introduzido. O código é bem parecido com o exemplo anterior, mas uma flag adicional para ligar/desligar o processo de mistura pé adicionado.

Figure 14-7
Figure 14-7

Capítulo 9

O capitulo 9 inicia o uso de iluminação e mistura para construir efeitos mais complexos. Nesse caso, estrelas cintilantes em uma espiral rotativa.

Figure 14-8
Figure 14-8

A atualização da posição de cada estrela é manipulada pelo método timeOut. Tirando esse fato, o código é uma mistura da versão Qt do capítulo 8 e do código do NeHe do capítulo nove.

Capítulo 10

No capítulo 10 um modelo de um mundo é carregado e o usuário pode se mover nele em uma perspectiva em primeira pessoa.

Figure 14-0
Figure 14-0

O código desse capítulo é diferente do código dos capítulo anteriores. Não há temporizador envolvido, ao invés disso cada movimento dispara uma chamada a updateGL, que redesenha a cena.

A maior mudança da versão do NeHe é a manipulação do arquivo. O modelo do mundo é carregado em um método loadTriangles mostrado no exemplo do capítulo 8. Os métodos em C da versão do NeHe foram alterados pelos métodos correspondentes do Qt. Isso torna a manipulação do arquivo mais robusta.


void loadTriangles()
{
QFile f( "world.txt" );

if( f.open( IO_ReadOnly ) )
{
QTextStream ts( &f );


Vertex v[3];
int vcount = 0;
bool allok, ok;

while( !ts.atEnd() )
{
QStringList line = QStringList::split( " ", ts.readLine().simplifyWhiteSpace() );

if( line.count() == 5 )
{
allok = true;
v[vcount].x = line[0].toFloat( &ok );
allok &= ok;
v[vcount].y = line[1].toFloat( &ok );
allok &= ok;
v[vcount].z = line[2].toFloat( &ok );
allok &= ok;
v[vcount].u = line[3].toFloat( &ok );
allok &= ok;
v[vcount].v = line[4].toFloat( &ok );
allok &= ok;
if( allok )
vcount++;
if( vcount == 3 )
{
vcount = 0;
Triangle t;
t.vertex[0] = v[0];
t.vertex[1] = v[1];
t.vertex[2] = v[2];
triangles.append( t );
}
}
}
 
 
 
f.close();
}
}

Capítulo 11

O capitulo 11 implementa um efeito de bandeira. Uma imagem é mapeada em uma superfície que é “balança ao vento”. O código é bem direto e os pontos que constroem a superfície são atualizados pelo método timeOut.

Figure 14-10
Figure 14-10

Capítulo 12

No capitulo 12 listas de chamada são introduzidas. Essa é uma maneira de construir listas de chamadas OpenGL em uma lista. Essa lista pode ser invocada para voltar a realizar a lista de chamadas.

Figure 14-11
Figure 14-11

Esse foi o capítulo final desse tutorial. O código inteiro dos exemplos pode ser baixado aqui ex14.tar
Traduzido de http://www.digitalfanatics.org/projects/qt_tutorial/chapter14.html