SmallVR - Virtual Reality Toolkit
Prof. Márcio Sarroglia Pinho

Navegação no Ambiente Virtual


1. Posicionamento Simples do Observador

Navegar um ambiente virtual significa deslocar o observador e seu ponto de interesse.
No caso da SmallVR, não há um comando específico para o controle do observador. Conforme já aparece nos exemplos anteriores, o controle do observador é feito a partir da chamada da rotina gluLookAt, da biblioteca GLU. No exemplo a seguir observa-se como isto é feito:
....
......
// ******************************
// User stuff
//     see "PosicUser" function
//  on how to use these variables
// ******************************
// User position
SmVR_CPoint    pt_user(0,1.6,2);
// Target position
SmVR_CPoint    pt_target(0,1.0,0);

// Root Object to represent the entire scene
SmVR_CGeometricObject *RootObject;

// Viewing angle in degrees
float ViewAngle = 90;



// **********************************************************************
//  void PosicUser()
//
//
// **********************************************************************
void PosicUser()
{
    // Set the clipping volume
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(ViewAngle,WindowRatio,0.01,200);

    // Set the user Position
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(pt_user.X,pt_user.Y, pt_user.Z,
              pt_target.X,pt_target.Y, pt_target.Z,
              0.0f,1.0f,0.0f);
}

Esta função deve então ser chamada a cada vez que a tela for ser redesenhada, conforme o exemplo abaixo.
// **********************************************************************
//  void display( void )
//  Exibe a cena
//
// **********************************************************************
void display( void )
{
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    PosicUser();
    glMatrixMode(GL_MODELVIEW);
    RootObject->Render();
    glutSwapBuffers();

}
A partir disto, para mover o observador, pode-se alterar os valores das variáveis pt_user e pt_target através de teclas, conforme o exemplo.
void keyboard ( unsigned char key, int x, int y )
{
 switch ( key )
 {
     case 27:        // Encerra o programa
          exit(0);
          break;
     case 'd' : // Move o OBSERVADOR e ALVO para a direita
               pt_user.X++;
               pt_target.X++;
               break;
     case 'e' : // Move o OBSERVADOR e ALVO para a esquerda
               pt_user.X--;
               pt_target.X--;
               break;
     case 't' : // Move o OBSERVADOR e ALVO para a frente
               pt_user.Z++;
               pt_target.Z++;
               break;
     case 'f' : // Move o OBSERVADOR e ALVO para a tras
               pt_user.Z--;
               pt_target.Z--;
               break;
  }
   glutPostRedisplay();// força o refresh da tela
}


Exercício

Altere este programa  de maneira a permitir que o usário mova-se no ambiente virtual. Altere a função void keyboard conforme o exemplo acima.


2. Andando para Frente e Olhando para os Lados

A forma de navegação apresentada acima não representa muito bem a maneira de deslocamento normalmente usada por uma pessoa em um ambiente real.
Em geral, se "anda para frente" ou "para trás". Para implementar isto diretamente em OpenGL, consulte este link http://www.inf.pucrs.br/~pinho/TCG/Apoio/Aulas/Navegacao/Navega.html da disciplina de Tópicos Especiais em Computação Gráfica da PUCRS.

Por outro lado, utilizando-se a SmallVR, primeiro devemos criar dois objetos que representarão o usuário e seu alvo. O usuário deverá ser filho do objeto raiz do grafo de cena e o alvo, filho do objeto que represnta o usuário.
O trecho de código a seguir realiza esta tarefa:

  // Inicializa a biblioteca
  SmVR_CGeometricObject *RootObject = SmVR_Init(NULL);
  // Cria o objeto "usuário"
  user = new SmVR_CGeometricObject("user");
  // Posiciona o usuário
  user->TranslateBy(0, 30, 0);
  // Cria o objeto "alvo"
  alvo = new SmVR_CGeometricObject("alvo");
  // Posiciona o alvo
  alvo->TranslateBy(0, 30, 200);
  //Faz o "alvo"ser filho do "usuário"
  user->AddChild(alvo);
  // Faz o "usuário" ser filho da "raiz" do grafo de cena
  RootObject->AddChild(user);
Esses objetos devem possuir preferencialmente apenas uma das coordenadas diferentes, para ter o vetor resultante sobre um dos eixos.

O "alvo" deve ser criado como filho de "user", para sofrer as mesmas transformações.

A partir disto, este objetos servem como base para a visualização do cenário. O posicionamento de fato continua a ser feito pela função gluLookAt como antes, porém, agora seu parâmentros são obtidos a partir das posições dos objetos user e da SmallVR.
A rotina PosicUser passa a ter o seguinte código.

//**********************************************************************
// void PosicUser()
//**********************************************************************
void PosicUser() {
    SmVR_CPoint Zero(0, 0, 0);

      glutSetWindow(janela);
      // Set the clipping volume
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective(angle, WindowRatio, val_near, val_far);

      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();

      // Obtém a posição do usuário no sistema de coordenadas da raiz do grafo de cena
      user->GetPointInOCS(Zero, &pt_user, RootObject);
      // Obtém a posição do alvo no sistema de coordenadas da raiz do grafo de cena
      alvo->GetPointInOCS(Zero, &pt_target, RootObject);
      gluLookAt(pt_user.X, pt_user.Y, pt_user.Z, // Posicao do observador
                pt_target.X, pt_target.Y, pt_target.Z, // Ponto que o observador esta olhando
                0.0f, 1.0f, 0.0f); // Define o lado de cima do cenario 3D
}

Deve-se tomar cuidado na hora de posicionar os objetos user e alvo, porque temos que colocá-los em uma posição que esteja dentro do cenário e que seja permitida a navegação.
Agora só falta capturar as teclas do teclado para fazer o usuário se locomover pelo ambiente virtual.

Para isso temos que adicionar no main a 'glutSpecialFunc(SpecialKeys)', para definir que quem trata as teclas especiais é a função 'void SpecialKeys(int key, int x, int y) {}'
Para as teclas normais usamos 'glutKeyboardFunc(keyboard)', para definir que as teclas normais chamam a função 'void keyboard(unsigned char key, int x, int y) {}' para tratamento.

Nessas funções devemos fazer as transformações em user para realizar a navegação.

Para "andar para frente", devemos mover o usuário na direção para onde este está olhando. Isto pode ser feito deslocando-se o objeto "user" no eixo Z do sistema de coordenadas de seu "alvo", desta forma:

 case GLUT_KEY_UP:  // Anda para frente
  // Desloca-se na direção do alvo
        user->TranslateByOnOBJCS(0, 0, -1, alvo);
        break;
Para permitir que o usuário "olhe" para o lado, basta rotacioná-lo em seu eixo Y, sendo que para as rotações é utilizado o sistema de coordenadas do próprio objeto, da seguinte maneira:
 case GLUT_KEY_LEFT:  // Gira o corpo para esquerda
        Angulo = 10;
        UserObject->RotateBy(Angulo, 0,1,0);
        break;


Exercício

Altere o programa do exercício anterior de maneira a permitir que o usário olhe para os lados e navegue para frente e para trás na direção em que está olhando.
Descompacte o arquivo e abra o projeto "Navegacao.dev".
Você deverá alterar as funções void SetupTheUser(), void PosicUser() e void specialFunc().