Aula Prática: Transformações Geométricas 2D
Disciplina: Computação Gráfica
Professores: Isabel Harb Manssour e Márcio Pinho

 

O objetivo desta aula é aprender a trabalhar com as transformações geométricas usando a biblioteca OpenGL. Para isto, será utilizada a API JOGL e a linguagem de programação Java, tendo como resultado um programa que pode ser executado tanto no ambiente Windows como no ambiente Linux.

Para implementação de aplicações com JOGL na linguagem de programação Java é necessário configurar um projeto para linkar as bibliotecas. Na primeira aula prática já foi explicado como configurar o projeto para o ambiente Eclipse. Portanto, basta criar um novo projeto, configurá-lo conforme explicado e incluir os programas fonte que serão usados nesta aula. Este programa tem a implementação dos exercícios da aula prática anterior.

Inicialmente, verifique o conteúdo do programa fonte fornecido para entender como foi implementado o zoom, o pan e a correção de aspecto, e quais são as teclas usadas para interação. Depois, compile e execute o programa que abrirá uma janela com fundo branco, uma casinha e duas linhas que representam os eixos cartesianos. Aplique zoom e pan e procure alterar o tamanho da janela várias vezes, aumentando e diminuindo bastante a sua largura e altura, de maneira intercalada. Observe atentamente o código para entender o seu funcionamento e observe os métodos implementados na classe Renderer2D (init, display, reshape e keyPressed).

Agora altere o código fonte conforme descrito a seguir para implementar as transformações geométricas para as várias instâncias da casinha separadamente. A cada alteração, compile e execute novamente o programa para verificar os resultados obtidos. Em caso de dúvida, consulte a professora. Para entender o funcionamento dos métodos OpenGL consulte o tuturial de introdução à OpenGL disponível em http://www.inf.pucrs.br/~manssour/OpenGL/Tutorial.html.

Crie uma classe chamada Instancia que contenha os atributos apresentados abaixo. Acrescente dois construtores nesta classe, um que recebe por parâmetro todos os valores para inicializar os atributos, e outro que não recebe parâmetros e inicializa ex e ey com 1 e os demais atributos com 0.

 
        private float tx, ty;
        private float ex, ey;
        private float angulo;

Além de acrescentar métodos get e set para todos os atributos a classe Instancia, acrescente também métodos para incrementar e decrementar cada atributo, conforme mostra o exemplo abaixo. Porém, considere que os atributos tx, ty, ex e ey devem ser incrementados e decrementados em apenas 0.1 unidade.

 
        public void incrementaAngulo() {
               this.angulo += 1;
        }
        public void decrementaAngulo() {
               this.angulo -= 1;
        };

Em seguida, crie na classe Renderer2D um atributo ArrayList para armazenar objetos Instancia, chamado casas e instancie-o no método init:

 
        private ArrayList<Instancia> casas;
        ...   
        public void init(GLAutoDrawable drawable)
        {
               casas = new ArrayList<Instancia>();

Acrescente também na classe Renderer2D um novo atributo do tipo int chamado instanciaSelecionada e inicialize-o com 0 no método init. Como vamos trabalhar com várias instâncias da casinha, a idéia é que este atributo irá guardar qual é a instância selecionada em cada momento, se é a primeira (0), a segunda (1), a terceira (2), e assim por diante. Somente após uma instância ser selecionada é que será possível alterar as transformações geométricas aplicadas sobre ela.

Agora altere o método keyPressed dando o tratamento definido a seguir para cada uma das teclas listadas. Considere que apenas os parâmetros de instânciamento de uma instância deverão ser alterados em cada momento.

  • PAGE_DOWN: cada vez que esta tecla for pressionada, o atributo instanciaSelecionada deve ser decrementado, de maneira que a instância anterior à atual seja selecionada. Lembre que a instância selecionada representa o índice do ArrayList de instâncias, e, portanto, este número não pode ser negativo. Assim, caso seja menor que zero, faça instanciaSelecionada receber o valor da última posição ocupada do ArrayList.
  • PAGE_UP: cada vez que esta tecla for pressionada, o atributo instanciaSelecionada deve ser incrementado, de maneira que a instância posterior à atual seja selecionada. Lembre que a instância selecionada representa o índice do ArrayList de instâncias, e, portanto, este número não pode ser maior que o número de instâncias criadas e armazenadas no ArrayList. Assim, caso seja maior que o número de instâncias armazendas, faça instanciaSelecionada receber zero.
  • F1: quando esta tecla for pressionada, deverá ser criada uma nova instância, que deverá ser armazenada no ArrayList. Lembre de atualizar o atributo instanciaSelecionada para que ele "aponte" para esta última instância criada.
  • RIGHT: quando esta tecla for pressionada, a instância selecionada deverá sofrer uma translação para a direita.
  • LEFT: quando esta tecla for pressionada, a instância selecionada deverá sofrer uma translação para a esquerda.
  • UP: quando esta tecla for pressionada, a instância selecionada deverá sofrer uma translação para cima.
  • DOWN: quando esta tecla for pressionada, a instância selecionada deverá sofrer uma translação para baixo.
  • E: quando esta tecla for pressionada, a instância deverá ser rotacionada para a esquerda.
  • D: quando esta tecla for pressionada, a instância deverá ser rotacionada para a direita.
  • A: quando esta tecla for pressionada, a instância deverá aumentar de tamanho (aumente a escala em x e y proporcionalmente).
  • MINUS: quando esta tecla for pressionada, a instância deverá diminuir de tamanho (diminua a escala em x e y proporcionalmente).

Agora acrescente os comandos abaixo no método display, logo após a chamada para o método desenhaEixos() (elimine os comandos existentes após a chamada para o método desenhaEixos() e acrescente os listados abaixo):

 
               // Aplica transformações geométricas e exibe as instâncias da casinha
               for(Instancia i : casas)
               {
                       gl.glPushMatrix();
                       gl.glTranslatef(i.getTx(), i.getTy(), 0f);
                       gl.glScalef(i.getEx(), i.getEy(), 1);
                       gl.glRotatef(i.getAngulo(), 0, 0, 1);
                       desenhaCasinha();
                       gl.glPopMatrix();
               }

Experimente colocar em comentário os comandos gl.glPushMatrix(); e gl.glPopMatrix();, e execute o programa novamente. O que acontece? Lembre que OpenGL trabalha com uma matriz de transformação corrente que "acumula" as transformações geométricas. Portanto, as funções glPushMatrix e glPopMatrix são usadas para definir o escopo das transformações. A glPushMatrix é utilizada para guardar a matriz de transformação corrente na pilha, e a glPopMatrix para recuperar a matriz de transformação corrente da pilha.

O objetivo das alterações realizadas até aqui foi de permitir a criação de várias instâncias da casinha que podem ser manipuladas de forma independente através das transformações de escala, rotação e translação, como exemplificado na próxima figura. Por isso, foi necessário armazenar os parâmetros de instânciamento para guardar as transformações que devem ser aplicadas em cada instância da casinha.

Agora vamos ver como acrescentar caracteres no desenho. Para isto, será usada a classe TextRenderer da JOGL. Comece criando um novo atributo na classe Renderer2D e inicializando-o no método init, conforme exemplificado abaixo. Lembre de fazer os imports adequados (java.awt.Font e com.sun.opengl.util.j2d.TextRenderer).

 
        private TextRenderer text;
        public void init(GLAutoDrawable drawable)
        {
               ...
               text = new TextRenderer(new Font("SansSerif",Font.BOLD, 24), true,true);
        }

Agora inclua os comandos abaixo no final do método display. Depois execute a aplicação e veja o resultado. Experimente redimensionar a janela para ver o resultado.

 
        public void display(GLAutoDrawable drawable)
        {
               ...            
               // Desenha um texto
               text.beginRendering(largura, altura);
               text.setColor(1.0f, 0.0f, 0.0f, 1.0f);
               text.draw("CG 2D", largura/2-40, altura-40);
               text.endRendering();           
        }

Para finalizar, vamos "animar" este texto, de maneira que ele fique "andando" de um lado para outro da janela. Para isto, inicialmente, vamos criar dois atributos na classe Renderer2D que devem ser inicializados conforme mostra o código abaixo.

 
        private int posicaoTexto, incremento;
   
        public void init(GLAutoDrawable drawable)
        {
               ...
               posicaoTexto = 0;
               incremento = 1;
               ...

Ainda na classe Renderer2D altere o trecho de código para desenhar o texto no método display para que fique como apresentado a seguir. O objetivo desta alteração é fazer com que cada vez que o método display seja executado, o texto seja desenhado em outra posição, cuidando para que não ultrapasse os limites da janela.

 
               // Desenha um texto
               text.beginRendering(largura, altura);
               text.setColor(1.0f, 0.0f, 0.0f, 1.0f);
               text.draw("CG 2D", posicaoTexto, altura-40);
               text.endRendering();
               if(posicaoTexto > largura || posicaoTexto < 0)
                       incremento = -incremento;
               posicaoTexto += incremento;

Uma última alteração é necessária, desta vez na classe Janela: inclusão de um objeto FPSAnimator que fará com que o método display() seja colocado em um loop (para ser constantemente chamado). Para isto, basta acrescentar no final do método construtor da classe Janela os comandos listados a seguir.

 
               final FPSAnimator anim = new FPSAnimator(canvas, 20); 
 
               janela.addWindowListener(new WindowAdapter() {
                   public void windowClosing(WindowEvent e) {
                     anim.stop();
                     System.exit(0);
                   }
                 });           
               
            anim.start();

 ../../Imagens/emban15.png (1469 bytes)

../../Imagens/E-MAIL.JPG (3237 bytes)Comentários, dúvidas, sugestões, envie um e-mail para seu(sua) professor(a)

../../Imagens/emban15.png (1469 bytes)

 

Última alteração em 19 de março de 2010.