Desenho de personagem de desenho animado

Descrição gerada automaticamente com confiança baixa

Logotipo

Descrição gerada automaticamente

Ícone

Descrição gerada automaticamente


Computação Gráfica
Prof. Márcio Sarroglia Pinho

Transformações Geométricas Hierárquicas em OpenGL


AULA PRÁTICA

Diagrama

Descrição gerada automaticamente


 

RESUMO

O objetivo deste trabalho é criar um Grafo de Cena.

Os fontes para o desenvolvimento do trabalho em C++ e Python estão neste link.

No caso de C++, no Windows e no Linux, abra o projeto OpenGL.cbp, no Code::Blocks. No MacOS, abra o projeto OpenGL.xcodeproj, no XCode. Para construir seu próprio projeto em C++, insira os seguintes fontes: HierarquiaV2.cpp, Desenhos.cpp, ListaDeCoresRGB.cpp, Objeto.cpp, Ponto.cpp, Tools.cpp.

Os fontes para o desenvolvimento do trabalho em Python, estão neste link. Para rodar o programa, execute o script HierarquiaV2.py.

Para obter instruções sobre como criar um projeto OpenGL acesse a página https://tiny.cc/PinhoOpenGL.

Compile o programa e controle os objetos na tela com as seguintes teclas:

·      0, 1, 2, ....: definem qual é objeto ativo;

·      teclas de seta LEFT, RIGHT: movem o objeto ativo para frente/trás.

·      teclas de seta UP, DOWN: giram o objeto ativo para esquerda/direita.

·      barra de espaço: imprime no console as coordenadas do objeto ativo.


DESCRIÇÃO DOS FONTES

Tools.cpp e Tools.h

Contém funções auxiliares de cálculos matemáticos. Analise o Tools.cpp para ver a descrição das funções.

void InverteMatriz( float a[4][4], float b[4][4]);
void MultiplicaMatriz(float m1[4][4], float m2[4][4], float m[4][4]);
float calculaDistancia(float P1[3], float P2[3]);

void criaIdentidade(float m1[4][4]);


Desenhos.cpp e Desenhos.h


Contém as funções de desenhos dos objetos em coordenadas do sistema de referência do objeto, ou seja, SEM as transformações geométricas que definem a posição, a rotação e a escala de uma instância.

Normalmente, é neste fonte que devem ser colocadas os comandos OpenGL que fazem os desenhos.

void DesenhaContornoCirculo(GLfloat raio, GLfloat centrox, GLfloat centroy);
void DesenhaCirculo(GLfloat raio, GLfloat centrox, GLfloat centroy);
void DesenhaTexto(char *string, GLfloat posX, GLfloat posY);
void DesenhaCarro();
void DesenhaHelicoptero();


Objeto.cpp e Objeto.h


Contém a estrutura capaz de armazenar uma instância de um objeto. Os métodos que aplicam as transformações geométricas estão nestes arquivos.

A classe que define esta instância é a seguinte:

class Objeto {

    int tipo;

    int id;

    int nroDeFilhos;

    bool eh_filho;

    Objeto *Filho;

   Objeto *Pai;

    GLfloat M[4][4];

 

public:

    Objeto();

    void setID(int i);

    void setTipo(int t);

   void Init();

   void Rotate(float ang, float x, float y, float z);

   void Translate(float x, float y, float z);

   void Scale(float x, float y, float z);

   void Draw();

   void ImprimeMatriz(const char *msg);

   void InstanciaPonto(float PontoIN[3], float PontoOUT[3]);

   void AdicionaFilho(Objeto *filho);

   void RemoveFilho();

 

};

 


Os atributos têm o seguinte significado:

    int tipo; // define se o objeto é um helicoptero(0) ou um carro(1)
    int nroDeFilhos; //define quantos filhos tem o objeto no momento
    bool eh_filho; // informa se o objeto é filho de algum outro objeto do cenário
    Objeto *Filhos; // vetor com referências para os filhos do objeto

    GLfloat M[4][4]; // matriz de transformação do objeto
    Objeto *Pai; // Referência para o pai do objeto


Analise o fonte Objeto.cpp  e veja a descrição de cada método.

HierarquiaV2.cpp

Este arquivo é módulo principal do programa. Ele controla a exibição do cenário.

O programa trabalha com o conceito de objeto ativo, que define sobre qual objeto as transformações comandadas pelo teclado serão aplicadas. Para ativar um objeto pressione 0(helicóptero), 1(carro) ou 2(carro).


As instâncias dos objetos estão armazenadas no vetor

Objeto Cenario[10];


As principais funções do módulo são:

Função Init():

Faz as inicializações de OpenGL, definindo a cor de fundo e os limites da área de desenho através das variáveis Min e Max.

A função também chama a função CarregaModelos que cria as instâncias que compõem o cenário.

 

Função CarregaModelos ():

Define quais instâncias fazem parte do cenário.

O trecho de código a seguir inicializa as instâncias. No caso, há 3 instâncias. A ZERO é um helicóptero e as demais são carros.

void CarregaModelos () {

 

    QTD_OBJETOS = 3;

    ObjetoAtivo = 0;

 

    for(int i=1; i<QTD_OBJETOS; i++) {

        Cenario[i].Translate(i*600,i*300,0);

        Cenario[i].setTipo(2); // carro

        Cenario[i].setID(i);

    }

    Cenario[0].setTipo(1); // Helicoptero

    // Helicoptero.ImprimeMatriz("Apos o INIT");

 

    imprimeCoordenadas = false;

    tentaPegar = false;

 

}

Função Desenha():

Realiza o desenho do cenário. Os principais passos são:

·     Limpa a tecla;

·     Desenha os eixos coordenados;

·     Imprime mensagens de texto;

·     Desenha o cada objeto armazenado no vetor "Cenario", a partir do código a seguir.


// Desenha o cenario todo

   for(int i=0; i<3; i++) {

   Cenario[i].Draw();

   }


Também se pode, nesta função, imprimir as posições de cada objeto do cenário com o trecho apresentado a seguir. A variável
imprimeCoordenadas é que determina a chamada desta função e pode ser alterada pela tecla espaço.

void imprimeCoordenadasDosObjetos()

{

    GLfloat P1[] = {0,0,0};

    GLfloat P2[3];

 

    imprimeCoordenadas = false;

    printf("Posicoes do objetos:\n");

    for(int i=0; i<QTD_OBJETOS; i++) {

        printf("Objeto %d: ",i);

        Cenario[i].InstanciaPonto(P1,P2);

        for (int j=0; j<3; j++)

            printf("%7.2f ", P2[j]);

        printf("\n");

    }

    printf("======================================\n");

}
       


EXERCÍCIO

 

O objetivo deste exercício é permitir que o helicóptero possa ‘pegar’ um carro e transportá-lo pelo cenário até um ponto desejado, onde será 'largado'.

 

O comando de 'pegar' só deve funcionar quando o helicóptero for o objeto ativo. O carro a ser pego deve ser o mais próximo do helicóptero, que ainda não é filho do helicóptero.

 
O programa deve ser alterado de forma a desenhar mais carros, de forma a se ter 10(dez) carros no cenário.

 

Comandos de Pegar e Largar

O comando de pegar deve estar na função que trata as teclas void keyboard (unsigned char key, int x, int y). A versão do código que foi disponibilizada apenas adiciona o carro 1 no helicóptero, conforme mostrado a seguir.


    case 'p': // faz o helicoptero pegar um dos objetos do cenario

    case 'P':

    Cenario[0].AdicionaFilho(&Cenario[1]);

       break;

 

Este código, entretanto, precisa ser alterado para pegar o mais próximo. Recomenda-se que a função keyboard apenas altere uma variável que será testada na rotina de desenho void Desenha(void), com algo como :

    if (tentaPegar)
    {
        // acha o carro mais próximo do helicóptero, que nao eh filho.
        proximo = MaisProximo(Cenario[0]);
       
        // adiciona o carro mais próximo como filho do helicoptero
        Cenario[0].AdicionaFilho(&Cenario[proximo]);
    }
 

Para realizar o trabalho será preciso alterar a versão atual do método AdicionaFilho da classe Objeto de forma a permitir que um objeto passe a ser um 'filho' de outro um objeto. Ao ser adicionado, o objeto não deve se mover e, quando o pai for movido, ele deverá se mover junto. Veja os slides da aula para entender como fazer.

 

Para ativar o comando de largar, o usuário pressiona a tecla L. O carro a ser largado deve ser o último que foi pego.

 

Alteração de parâmetros de um objeto

 

Deve ser possível mover ou girar um objeto, independente do fato de que este objeto seja ou não filho do helicóptero.

 

Desenho dos Objetos

 

O processo de desenho dos objetos ocorre através da chamada do método Draw, da classe Objeto, apresentado a seguir. Note, entretanto, que, quando um objeto já é filho de outro, o pai deve ser desenhado antes dele e as transformações do pai devem estar ativas quando do desenho do filho.

 

Note que no código disponibilizado, ocorre apenas o desenho de um dos filhos. Isto precisa ser alterado.

 

void Objeto::Draw() {

   if (eh_filho) {

   printf("Metodo Draw: Voce esta tentando desenhar um FILHO antes de seu PAI\n");

        // Quem deve desenhar um objeto é

   return; // Nao desenha se o objeto eh filho de algum outro

   }

   glPushMatrix();

   glMultMatrixf(&M[0][0]);

   switch(tipo) {

   case 1:

   DesenhaHelicoptero();

   if (!Filho) // se não tiver filhos, entao termina

      break;

   // libera o filho para desenho

   Filho->eh_filho = false;

   // Ativa o desenho dos Filhos

   Filho->Draw(); // alterar para desenhar todos os filhos

   // Bloqueia o filho para desenho

   Filho->eh_filho = true;

   break;

 

   case 2:

      DesenhaCarro();

   break;

   }

   glPopMatrix();

}

 

 


FIM.