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

Representando Transformações Geométricas por Matrizes

As transformações geométricas aplicadas usando comandos OpenGL do tipo glTranslate, glRotate ou glScale, são sempre armazenadas em uma matriz chamada MODELVIEW.
A cada chamada de uma destas funções OpenGL cria uma matriz de transformação específica para a função e a seguir multiplica esta matrix pela matriz MODELVIEW atual.

Por exemplo, na chamada da função glTranslatef(-3, 2, -8)é criada a seguinte matriz:

   1.00   0.00   0.00   0.00
   0.00   1.00   0.00   0.00
   0.00   0.00   1.00   0.00
  -3.00   2.00  -8.00   1.00

Note que na última linha da matriz são colocados os valores passados como parâmetros para a função.

No momento de exibir um objeto o que OpenGL faz é a multiplicação desta matriz pelo vértices do objeto, obtendo assim a posição final do objeto. Por exemplo, no caso de exibir o ponto (10, 15, 20) seria obtido o ponto (7,17, 12) da seguinte forma:

 
 

Caso seja desejável ou necessário, é possivel setar "à mão" esta matriz de transformação. Esta operação manual pode ser feita de tres formas:

- usando a função glLoadIdentity(): com esta função a matriz de transformação converte-se na matriz identidade;
- usando a função glLoadMatrixf(matrix): com esta função é possível definir uma nova matriz de transformação destruindo-se a matrix anterior.
- usando a função glMultMatrixf(matrix): com esta função é possível multiplicar a matriz de transformação atual por uma segunda matriz.

Aplicando novas Matrizes de Transformação

Caso se tenha uma nova transformação (também representada em forma de matriz) é possivel aplicá-la a matriz atual, bastando multiplicar a matriz atual pela nova matriz.
Por exemplo, caso tenhamos a matriz de transformação para uma translação (5, 7, 2) a equação a seguir calcula a nova matriz.

Obtendo a Matriz de Transformação Corrente

Para  obter a matriz de transformação geométrica atual em OpenGL usa-se a função  glGetFloatv da seguinte forma:

GLfloat Matriz[4][4];
glGetFloatv(GL_MODELVIEW_MATRIX, &Matriz[0][0]);
 

Armazenando as Transformações de um objeto em uma matriz

Para  armazenar as transformações geométricas de um objeto em uma matriz deve-se chamar a função glGetFloatv imediatamente após terem sido setadas todas estas transformações do objeto.

Por exemplo:

// **********************************************************************
//  void RecalculaMatrizObjeto()
// **********************************************************************
void RecalculaMatrizObjeto()
{
 glPushMatrix();
      glLoadIdentity();
      glTranslatef (PosObj.X, PosObj.Y, PosObj.Z);
      // guarda a matriz de transformação do objeto para um futuro uso
      glGetFloatv(GL_MODELVIEW_MATRIX, &MatObj[0][0]);
 glPopMatrix();
}
 

Alterando a Matriz de Transformação antes de desenhar um objeto

Uma vez que se tem a matriz de transformações de um objeto é possível aplicar estas transformações multiplicando-se esta matriz pela matriz atual. Isto é feito da seguunte forma:

// **********************************************************************
//  void DesenhaObjeto()
// **********************************************************************
void DesenhaObjeto()
{
 glColor3f(0.0f,0.0f,0.6f);
 glPushMatrix();
      glMultMatrixf(&MatObj[0][0]);
      DesenhaCubo();
 glPopMatrix();
}
 
 

Montando Transformações Hierárquicas

A montagem de uma hierarquia de trnasfomações serve para que se possa definir um objeto como filho de outro.
Neste caso, o objeto filho sofrerá todas as transformações geométrica aplicadas a seu pai. Para tanto deve-se apenas desenhar o objeto logo após o desenho de seu. O exemplo a seguir ilustra este procediemnto.

// **********************************************************************
//  void DesenhaPAI()
// **********************************************************************
void DesenhaPAI()
{
 glColor3f(0.7f,0.7f,0.0f);

 glPushMatrix();
      glMultMatrixf(&Mat_Pai[0][0]); // aplica as transformações do pai
      DesenhaCubo();
      if (TemFilho)
          DesenhaObjeto();  // desenha Filho
 glPopMatrix();
}

Neste caso, entretato, no instante em que se torna um objeto filho de outro, este filho irá sofrer as transformações geométricas de seu pai adicionadas às suas. Por exemplo, se o pai tem uma translação em X igual a 40 e o filho outra igual a 30, então o objeto filho irá ser transladado de 70 unidades. Isto irá ocorrer no momento em que o objeto filho for desenhado pela primeira vez após o pai, causando uma translação indesejada.

Montando o vínculo de hierarquia

Para contornar este efeito colateral é ncessário que no momento do estabelecimento da relação pai-filho, a matriz do filho desfaça as transformações existentes no pai naquele momento. Para tanto deve-se multiplicar a matriz do filho pela inversa da matriz do pai, conforme o exemplo a seguir.

// **********************************************************************
//  void CriaVinculoPai_Filho()
// **********************************************************************
void CriaVinculoPai_Filho()
{
    GLfloat MatInv[4][4];

        TemFilho = TRUE;

    // calcula a inversa da matriz do Pai
    CopyMatrix(MatInv_Pai, Mat_Pai);
    M_invert(MatInv_Pai);
    // Multiplica a Matriz do filho pela inversa da matriz do pai
    M_mult(MatObj, MatInv_Pai); // MatObj = MatObj * MatInv_Pai
}
 

Note que isto deve ser feito somente no instante em que se estabelece o vínculo entre pai e filho. Isto porque só deve-se desfazer as transformações do pai que existem no momento do estabelecimento do vínculo. A partir deste momento, se uma nova transformaçõa for aplicada ao pai, ela deverá ser também aplicada ao filho.
 

Desfazendo o vínculo de hierarquia

Para desfazer o vínculo de  hierarquia e deixar o objeto no mesmo lugar deve-se multiplicar sua matriz pela matriz atual de seu pai. Se isto não for feito ocorrerá uma translação indesejada no objeto filho pois este deixará de sofrer as translações do pai (após o término do vínculo).

// **********************************************************************
//  void DesfazVinculoPai_Filho()
// **********************************************************************
void DesfazVinculoPai_Filho()
{
        TemFilho = FALSE;
    M_mult(MatObj, MatPai); // MatObj = MatObj * Mat_Pai
}
 

A partir deste momento o objeto filho não mais deverá ser desenhado após o pai.
 
 

Exemplos