IA725 - Computação Gráfica

Nomes:

    Honório Gomes  RA: 991880 ( hgomes@fee.unicamp.br )
    Leonardo Rocha RA: 00
6092 ( leorocha@dca.fee.unicamp.br )

Professora: Wu Shin Ting ( ting@dca.fee.unicamp.br )(DCA/ FEEC/ UNICAMP).

Este trabalho faz parte das atividades práticas da disciplina "Computação Gráfica" (IA725) lecionada no curso de pos-graduação da Faculdade de Engenharia Elétrica e de Computação da Universidade Estadual de Campinas (UNICAMP).

O objetivo é a construção de um jogo de "Batalha Naval" renderizado em uma cena tridimensional tridimensional utilizando a linguagem " OpenGL".
Este projeto é composto de três etapas.

  1. "versão em wiraframe (com 3 tipos distintos de navios e posicionamentos arbitrários de câmera). A entrada das posições dos navios será por teclado (x,y). Em resposta a cada tentativa do jogador, é adicionada na cena 3D uma figura da ação correspondente."

 

  1. "versão com tonalização de Gouraud e a possibilidade de chaveamento do cenário entre o diurno e o noturno (quando navios terão luzes acesas) "

 

  1. "Cenas texturizadas e o jogador seleciona a posição através do cursor ."

Esta proposta foi desenvolvida da seguinte forma.

1° ETAPA (Modelos em wireframe)


Devido a grande dificuldade em reproduzir modelos próximos aos reais , decidimos utilizar um programa para converter modelos escritos em programas de desenho vetorial em uma lista de vértices. Estes vértices (bem como outras informações úteis, normal, cor, ...) então foram inseridos em estruturas de dados, onde poderiam ser facilmente manipulados.
Buscando por modelos para utilizar no projeto, encontramos um tutorial em GameDev.Net que ensinava a tratuzir modelos escritos no 3D Studio (*.3ds) em uma descrição OpenGL. Este tutorial incluia um programa exemplo( que utilizamos como base para o desenvolvimento de nosso jogo) escrito em C
++ e utilizando a bibloteca SDL.

Foram escolhidos 4 modelos para serem utilizados: bote salva vidas; submarino; porta-aviões; destroier.

O ponto chave desta tradução pode ser visto no main.h, onde temos definidas as classes t3DObjetc, t3DModel, que irão armazenar as informações contidas em um único modelo.
Observando o tutorial que está contido nestes arquivos, podemos perceber que os modelos são "montados" com a junção de faces triangulares, sendo cada seqüência de três vértices formando uma destas faces.

A grande tarefa de traduzir este modelo e inserí-lo em objetos destas classes é feita pelos métodos da classe CLoad3DS (definida em 3ds.h). Esta classe possui diversos métodos para executar o tradução do modelo, bem como interpretar informções de cor e textura.

Uma vez interpretado e carregado, torna-se simples a maniplulação dos vértices contidos do modelo contido em um objeto t3DModel, pois dentro dele (considerando que um que esta classe tem diversos objetos t3DObject) eles podem ser lidos como o conteúdo de um vetor, que cada tres posições simbolizam uma face triangular.

assim para renderizar o modelo por completo, bastaria utilizar o seguinte loop:

for(cada modelo){

    //Transformações geométricas para o posicionamento.

    glBegin(GL_TRIANGLES);

        for(número de objetos){
              for(número de faces do objeto){
                    for(3 vezes o número de faces){
                             glNormal3f(normal_no_ponto);
                             glVertex4f(ponto , escala);
                        }
                }
            }
  
    glEnd();

}

    Além disso, pensando em um jogo tradicional de batalha naval os barcos ficam dispostos na posição vertical ou horizontal em um tabuleiro. Por isso é renderizado um tabuleiro com células quadradas em um plano paralelo ao plano que futuramente serão "desenhado" os barcos.

Como resultado podemos ter a seguinte cena:


 


    Uma vez posicionados os barcos, é necessário construir estruturas ara controlar o posicionamento da câmera.

    Como o programa base utilizado para construir este jogo utilizava a biblioteca SDL para construir um ambiente onde os comandos OpenGL pudessem ser utilizados nativamente, decidimos utilizar a estruturas estruturas de tratamento de evento disponíveis nesta biblioteca.

    Então uma maneira eficiente de reposicionar a câmera em relação a cena formada seria fazer com que ela acompanhasse o movimento do mouse (evento SDL_MOUSEMOTION). Na captura deste evento obtemos duas informações importantes, as variações relativas nos eixos "x" e "y" (xrel e yrel) representadas pro números inteiros de 16 bits.
    Além do movimento da câmera devemos definir qual a sua distância em relação a cena. Por isso definimos o lugar geométrico das posições que a câmera pode assumir um espaço esférico em torno do tabuleiro.
    Para facilitar a implementação fizemos com que a câmera apontasse para o centro do tabuleiro, assim sua posição poderia ser determinada por coordenadas esféricas (r, theta, phi) em que o zoom (tamanho do raio) pode ser controlado através das teclas "+" e "-" do teclado alfanumérico (evento SDL_KEYDOWN).  Então a cada atualização da posição da câmera deve-se chamar o comando:

gluLookAt(XCentro + r*cos(theta)*sin(phi), YCentro + r*sin(theta)*sin(phi),  r*cos(phi) , 0, 0,0 , vetor_normal );

 



    Por último deveria ser implementada a interface em que o jogador escolheria a posição onde atirar.
    Nesta primeira etapa estes dados deveriam ser obitdos através do teclado, o que inicialmente esta obrigação gerava um problema, pois toda vez que o programa esperava o jogador escolher a posição do tabuleiro que qeria atacar, a cena deixava de ser renderizada.
    Isto foi resolvido dividindo em funções separadas a renderização e a ação do jogador e fazendo com que o programa principal os chamasse em "threads" diferentes que compartilham as variáveis de posiconamento na tela, sendo estas acessadas com um esquema de exclusão mútua.

    Por fim, restava sinalizar ao jogador quando sua tentativa tinha exito. Para isto será utilizado uma seqüência de imagens (bitmaps) para simular uma explosão na célula que contém uma parte de algum dos barcos. Esta seqüência seria:


    Assim uma possível cena durante o jogo seria:



2° ETAPA (Mudança de Cenário e Tonalização)

 

Nesta etapa, os modelos do jogo e o cenário são  tonalizados com  o modelo  Gouraud. Além da tonalização, vários modelos receberam texturas.  Em alguns casos o número  de  triângulos foi reduzido para aumentar  o desempenho.  Sob o grid  foi  desenhado outro grid com a textura  da água ,  e esta mapeada em cada quadrado de forma que simula  o  movimento  das  ondas.  Para aumentar o realismo, o  grid  da água  também  faz um movimento ondulatório, mostrando o  movimento de subida  e  descida  das ondas.

grid


A simulação da iluminação do dia e da noite é feita através da alteração das propriedades da fonte de luz. Os parâmetros GL_AMBIENT, GL_DIFFUSE e GL_SPECULAR são os seguintes para o cenário de dia, sob luz do sol:


dia

e para a noite, sob luz da lua:



noite


O chaveamento entre dia e noite é feito pelo botão direito do mouse. A iluminação das janelas dos modelos é feita através da mudança da propriedade GL_EMISSION dos objetos com nome WINDOW ou LIGHT de cada modelo. As propriedades de (simulação) de emissão luz das janelas e luzes dos modelos são alteradas de acordo com a iluminação diurna/noturna.

3° ETAPA (Texturização)


Os modelos já possuem textura desda a 2a etapa (madeira do chao do navio, ondas sob o grid, etc). A principal mudança foi na interface, que é feita através do mouse, ao invés da linha de comando. O quadrado sob o mouse é preenchido de vermelho semi-transparente através do movimento do mouse, indicando as posiç~oes v álidas. Quando o botão esquerdo do mouse é pressionado é dado um tiro na posição correspondente.  A seleção da posição no tabuleiro é feita através da funcao  gluPickMatrix, que define uma regiao de seleção através da mudaça da matrix de projeção:


void gluPickMatrix(GLdouble x,
                   GLdouble y,
                   GLdouble delX,
                   GLdouble delY,
                   GLint *viewport )


Para usar esta funcão corretamente, primeiro muda-se temporariamente o modo de rasterização para GL_SELECT com


GLint glRenderMode( GLenum mode )


Para determinar qual a posição selecionada, cada quadrado do tabuleiro é rotulado com um índice, que corresponde à sua posição no vetor de pontos, através das funções:

 
void glInitNames( void )
void glPushName( GLuint name )
void glLoadName( GLuint name )


O resultado da seleção é colocado em um buffer especificado pela função:

void glSelectBuffer( GLsizei size,
                      GLuint *buffer )



Figuras:


overview
Figura 1 - mouse sobre grid



shooting
Figura 2 - tiro com mouse


another angle
Figura 3 - mudanca de angulo de visao


Download do código completo:  bship_v3.tar.gz