UMODELO ORIENTADO A OBJETOS PARA JOGOS EM …lvalente/docs/2002_monografia_final.pdf · propósito...

116
UNIVERSIDADE FEDERAL FLUMINENSE UM MODELO ORIENTADO A OBJETOS PARA JOGOS EM COMPUTADOR POR LUIS VALENTE Monografia apresentada ao Departamento de Ciência da Computação da Universidade Federal Fluminense como parte dos requisitos para obtenção do Grau de Bacharel em Ciência da Computação Orientador: Prof. Luiz Carlos Castro Guedes – D.Sc. PUC-RJ Departamento de Ciência da Computação Niterói, Agosto de 2002

Transcript of UMODELO ORIENTADO A OBJETOS PARA JOGOS EM …lvalente/docs/2002_monografia_final.pdf · propósito...

UNIVERSIDADE FEDERAL FLUMINENSE

UM MODELO ORIENTADO A OBJETOS PARA

JOGOS EM COMPUTADOR

POR LUIS VALENTE

Monografia apresentada ao Departamento de Ciência daComputação da Universidade Federal Fluminense comoparte dos requisitos para obtenção do Grau de Bacharel emCiência da Computação

Orientador:Prof. Luiz Carlos Castro Guedes – D.Sc. PUC-RJ

Departamento de Ciência da ComputaçãoNiterói, Agosto de 2002

UM MODELO ORIENTADO A OBJETOS

PARA JOGOS EM COMPUTADOR

LUIS VALENTE

Monografia apresentada ao Departamento de Ciência da Computaçãoda Universidade Federal Fluminense como parte dos requisitos para

obtenção do Grau de Bacharel em Ciência da Computação

Avaliada em Agosto de 2002

Banca Examinadora

________________________________________Prof. Luiz Carlos Castro Guedes

D.Sc. PUC-RJ

________________________________________Prof ª. Aura Conci

D.Sc. PUC-RJ

________________________________________Prof. Luiz Carlos Montez Monte

D.Sc. COPPE-UFRJ

Niterói, 2002

i

Resumo

Programação de Jogos é uma área da Ciência da Computação que abrange uma grandequantidade de conhecimento, como Computação Gráfica, Programação de dispositi vos de entrada,Programação de hardware para som, Redes de Computadores, Inteligência Artificial, Engenharia deSoftware e Concorrência (threads). O desenvolvedor de jogos precisa conhecer (senão dominar) boaparte dessas áreas, além de utili zar tecnologias que estão em constante evolução.

Este projeto propõe uma ferramenta, conhecida como Máquina de Jogos (engine), compropósito de abreviar o tempo de desenvolvimento ao oferecer recursos diversos de alto nível, queenglobem algumas dessas tecnologias necessárias.

A Máquina de Jogos, que aplica paradigmas como Orientação a Objetos e Gestão Automatizadade Recursos, é destinada ao desenvolvimento de aplicações para o Sistema Operacional Windows eemprega tecnologias como OpenGL e DirectX.

ii

Lista de FigurasFigura 4.1: Diagrama de classes para um Estado...................................................................................15Figura 4.2: Associação entre Estado e Máquina de Estados..................................................................17Figura 4.3: Oráculo e suas especializações............................................................................................21Figura 4.4: Diagrama de classes para manipulação de janelas..............................................................24Figura 4.5: Diagrama de classes relacionadas à biblioteca COM..........................................................25Figura 4.6: Diagrama de classes relacionadas a recursos do Sistema Gráfico.......................................28Figura 4.7: Diagrama de classes para uma câmera.................................................................................29Figura 4.8: Diagrama de classes para representação de uma textura.....................................................31Figura 4.9: Diagrama de classes relacionadas a modelos tridimensionais.............................................34Figura 4.10: Diagrama de classes para uma entidade.............................................................................38Figura 4.11: Diagrama de classes para o Sistema de Dispositi vos de Entrada......................................41Figura 4.12: Classe para representação de efeitos sonoros....................................................................43Figura 4.13: Diagrama de Relacionamentos para a Máquina de Estados..............................................44Figura 4.14: Diagrama de Relacionamentos para objetos móveis, modelos 3D e texturas..................45Figura 4.15: Aplicação em execução......................................................................................................46Figura 5.1: Visualização da cena............................................................................................................54Figura 5.2: Cena com todos os efeitos gráficos aplicados.....................................................................55

iii

Índ ice1. Introdução.............................................................................................................................................12. Desenvolvimento de Jogos...................................................................................................................2

2.1. Módulo Gráfico............................................................................................................................22.2. Módulo de Sons............................................................................................................................32.3. Módulo de Dispositi vos de Entrada.............................................................................................32.4. Módulo de Física..........................................................................................................................42.5. Módulo de Inteligência Artificial.................................................................................................42.6. Módulo de Conectividade.............................................................................................................5

3. Máquinas de Jogos................................................................................................................................63.1. Exemplos de Máquinas de Jogos..................................................................................................6

3.1.1. T3DLib.................................................................................................................................63.1.2. Ack-3D.................................................................................................................................63.1.3. OGRE...................................................................................................................................73.1.4. Fly3D....................................................................................................................................8

3.2. Conclusões....................................................................................................................................93.2.1. Vantagens.............................................................................................................................93.2.2. Desvantagens......................................................................................................................10

4. Uma Máquina de Jogos......................................................................................................................114.1. Gestão Automatizada de Recursos.............................................................................................11

4.1.1. Objetos Automáticos..........................................................................................................114.1.1.1. Exemplo 1..................................................................................................................124.1.1.2. Exemplo 2..................................................................................................................12

4.2. Máquina de Estados....................................................................................................................144.2.1. Fluxo de Execução de Jogos..............................................................................................144.2.2. Definição de Estado...........................................................................................................154.2.3. Associação entre Estados e Máquina de Estados...............................................................164.2.4. Funcionamento da Máquina de Estados.............................................................................184.2.5. Comunicação entre Estados e Ambiente Exterior..............................................................20

4.3. Ferramentas Utili zadas...............................................................................................................234.4. Ferramentas Disponíveis............................................................................................................23

4.4.1. Sistema Windows...............................................................................................................234.4.1.1. Criação de Janelas......................................................................................................244.4.1.2. Biblioteca COM.........................................................................................................25

4.4.2. Sistema Gráfico..................................................................................................................284.4.2.1. Câmeras......................................................................................................................294.4.2.2. Texturas......................................................................................................................304.4.2.3. Modelos 3D................................................................................................................33

4.4.3. Sistema de Entidades..........................................................................................................384.4.4. Sistema de Dispositi vos de Entrada...................................................................................404.4.5. Sistema de Som..................................................................................................................42

4.5. Diagrama de Relacionamentos da Máquina de Jogos................................................................444.6. Exemplo......................................................................................................................................45

4.6.1. State.h.................................................................................................................................464.6.2. State.cpp.............................................................................................................................47

5. Resultados Experimentais...................................................................................................................535.1. Métricas de Desempenho...........................................................................................................535.2. Ambiente de Execução...............................................................................................................535.3. Propriedades da Simulação........................................................................................................53

5.3.1. Orientação da Cena............................................................................................................545.3.2. Efeitos Especiais................................................................................................................545.3.3. Cenário...............................................................................................................................555.3.4. Câmeras..............................................................................................................................555.3.5. Projéteis..............................................................................................................................565.3.6. Jogador...............................................................................................................................56

iv

5.3.7. Inimigos..............................................................................................................................565.3.8. Efeitos Sonoros..................................................................................................................56

5.4. Dados Experimentais..................................................................................................................575.5. Resultados Obtidos.....................................................................................................................575.6. Implementação do Estado...........................................................................................................58

5.6.1. State.h.................................................................................................................................585.6.2. State.cpp.............................................................................................................................61

6. Conclusões..........................................................................................................................................776.1. Trabalhos Futuros.......................................................................................................................77

7. Anexo A..............................................................................................................................................787.1. State.h.........................................................................................................................................787.2. State.cpp.....................................................................................................................................797.3. Cage.h.........................................................................................................................................847.4. InputHandler.h............................................................................................................................847.5. InputHandler.cpp........................................................................................................................857.6. ActionMap.h...............................................................................................................................867.7. ActionMap.cpp...........................................................................................................................877.8. Main.cpp.....................................................................................................................................88

8. Anexo B: Gestão Automatizada de Recursos.....................................................................................918.1. Funcionalidades Existentes em Algumas Linguagens de Programação....................................91

8.1.1. Java.....................................................................................................................................918.1.2. C++.....................................................................................................................................91

8.2. Fundamentos...............................................................................................................................918.2.1. Objetos Automáticos..........................................................................................................92

8.2.1.1. Construtores...............................................................................................................928.2.1.2. Destrutores.................................................................................................................928.2.1.3. Escopo........................................................................................................................928.2.1.4. Construtor + Destrutor + Escopo = Objeto Automático............................................92

8.2.2. Regra Primeira de Aquisição..............................................................................................938.2.3. Propriedade Sobre Recursos..............................................................................................93

8.3. Estudo de Caso: Memória..........................................................................................................938.3.1. Transferência de Recurso...................................................................................................94

8.4. Estudo de Caso: Contadores de Referências..............................................................................958.5. Conclusões..................................................................................................................................97

9. Anexo C: Termos Relacionados.........................................................................................................9810. Referências Bibliográficas..............................................................................................................110

1

1. Introdução

Este trabalho discute tópicos em Programação de Jogos e apresenta ferramentas paradesenvolvimento de aplicações do gênero.

Programação de Jogos é uma área da Ciência da Computação que abrange uma grandequantidade de conhecimento, como Computação Gráfica, Programação de dispositi vos de entrada,Programação de hardware para som, Redes de Computadores, Inteligência Artificial, Engenharia deSoftware e Concorrência (threads). O desenvolvedor de jogos precisa conhecer essas áreas (entreoutras) e as várias tecnologias utili zadas que são relacionadas a elas.

As ferramentas implementadas aplicam paradigmas como Orientação de Objetos e GestãoAutomatizada de Recursos para oferecer ao desenvolvedor interfaces de alto nível sobre algumas dastecnologias necessárias, de modoa abreviar o tempo de desenvolvimento. A Gestão Automatizada deRecursos tem como propósito minimizar erros de programação e aumentar a confiabili dade dasaplicações. São empregadas tecnologias como OpenGL e DirectX, para o desenvolvimento deaplicações em Windows.

No capítulo 2 deste trabalho, são relatados alguns problemas encontrados na área deProgramação de Jogos para computadores.

No capítulo 3, são descritos exemplos de Máquinas de Jogos e os problemas resolvidos porelas.

No capítulo 4, é apresentada uma proposta de Máquina de Jogos. Os conceitos utili zados e aimplementação são detalhados. No final do capítulo é descrito um exemplo simples de uso daMáquina de Jogos especificada.

No capítulo 5, são relatados os resultados obtidos pela execução de uma aplicaçãodemonstrativa que utili za todos os serviços oferecidos pela Máquina de Jogos apresentada. Aimplementação do estado correspondente encontra-se no anexo A.

O anexo B apresenta Gestão Automatizada de Recursos com mais detalhes, enquanto o anexo Cdescreve um glossário de termos relacionados ao projeto.

As conclusões sobre o projeto são apresentadas no capítulo 6.

2

2. Desenvolvimento de Jogos

Este capítulo discute alguns tópicos existentes em desenvolvimento de jogos, sob o ponto devista do programador. Existem vários gêneros de jogos, ao mesmo tempo com alguns problemascomuns e outros bastante diferentes entre si.

Segundo André LaMothe [ALA’1999], um jogo deve possuir alguns dos seguintes módulos:Gráficos (o que se vê), Sons (o que se ouve), Dispositi vos de entrada (Input) (como interagir com ojogo), Física (interação entre os objetos do jogo e o ambiente aproximando o que ocorre no mundoreal, conforme as Leis da Física) e Inteligência Artificial (IA) (como os objetos controlados pelo jogose comportam). Pode ser acrescentado também o módulo de Conectividade (conexão em rede entredois ou mais jogadores).

2.1. Módulo Gráfico

O desenvolvedor pode optar por utili zar gráficos em duas (2D) ou três dimensões (3D).Atualmente, devido à evolução do hardware de vídeo (para PC), é comum o uso de gráficos 3D. Ostipos de cenas representados estão diretamente ligados aos gêneros dos jogos. Qualquer que seja ogênero do jogo, é possível classificá-los em duas categorias:

� Primeira Pessoa: São jogos em que o ponto de vista utili zado é o do usuário. Os ambientescaracterizados são totalmente tridimensionais, contribuindo para a imersão do usuário nocenário;

� Terceira Pessoa: O usuário acompanha a ação à distância. Jogos deste tipo podem fazer usode gráficos 2D ou 3D.

Os gêneros de jogos, podem se enquadrar em uma das classificações ou em ambas ao mesmotempo, podem ser divididos nas seguintes categorias, segundo [ALA’1999] e [BSA’1996]:

� Tiro em primeira pessoa: Estilo popularmente conhecido como First Person Shooter (FPS),simula cenas tridimensionais em grandes ambientes internos (ou labirintos), geralmente.Como o próprio nome sugere, o ponto de vista utili zado é o de primeira pessoa, para geraruma sensação de imersão no ambiente. Exemplos: Quake, Unreal;

� Esportes: Utili zam gráficos 2D ou 3D e podem oferecer vários pontos de vista. O cenáriolimita-se àquele onde é praticada a modalidade (uma quadra, por exemplo). As animaçõespodem ser avançadas para acomodar os possíveis movimentos de um atleta. Exemplos:FIFA Soccer, NBA Live;

� Luta: Ambientam-se em um cenário restrito ao local da luta. No caso de jogos 2D, a ação évisualizada lateralmente. Caso sejam usados gráficos 3D, podem ser oferecidos váriospontos de vista. Podem ser utili zadas animações ou modelos tridimensionais pararepresentar os personagens. Exemplos: Mortal Kombat, Street Fighter, Tekken;

� Side Scroller: Tradicionalmente são utili zados gráficos 2D, com visão lateral. Possuem umgrande cenário que é visualizado por partes. O usuário controla um objeto que percorre estecenário, por rolamento (scrolling). É possível, entretanto, utili zar gráficos 3D pararepresentar o cenário e ao mesmo tempo manter as características do gênero. Exemplos:Super Mario Bros, Sonic The Hedgehog;

� Arcade: Utili zam gráficos 2D. Correspondem aos primeiros jogos existentes, em que a açãoé visualizada lateralmente ou por cima. O cenário é fixo, não possuindo rolamento(scrolling). Exemplos: Pac-Man, Asteroids;

� Simulação mecânica: Abrange simulações de corrida, vôo, et cetera. Utili zam gráficos 3D,com visão em primeira ou terceira pessoa. Exemplos: Microsoft Flight Simulator, The Needfor Speed;

3

� Estratégia/RPG1/Guerra: Utili zam gráficos 2D ou 3D. Os cenários geralmente possuemgrande extensão, podendorepresentar ambientes externos ou internos. O ponto de vista podeser localizado bem acima do local onde estão os objetos, de modo que o usuário possa teruma ampla visão da áreafocalizada. O usuário participa de simulação como um espectadorque tem o poder de interferir na simulação, ou seja, ele não controla um objetoespecificamente. Exemplos: Diablo, Final Fantasy, SimCity, The Sims;

� Quebra-cabeças/Tabuleiro/Cartas: Geralmente são os estilos de jogos mais simples e usamgráficos 2D. Entretanto, podem utili zar gráficos 3D para criar alguns efeitos gráficos, mas aessência do gênero é mantida. Exemplos: Tetris, Blockout, Monopoly, Solitaire;

� Shooter: Correspondem aos jogos em que o usuário controla algum tipo de aeronave epercorre um cenário por rolamento (scrolling). A visão é em terceira pessoa e a direção dorolamento do cenário pode ser horizontal, vertical ou diagonal (isométrica), em 2D ou 3D.Exemplos: Galaga, Zaxxon.

2.2. Módulo de Sons

Sons são utili zados em jogos para dois fins:

� Efeitos sonoros;� Música.

Efeito sonoro é definido como um som de curta duração, que é reproduzido em resposta aalgum evento. Música pode ser definida como uma longa melodia de fundo.

Para representar efeitos sonoros, podem ser utili zados arquivos de som que definem umformato de onda. Um dos formatos mais populares é aquele utili zado em sistemas Windows, cujoarquivo possui extensão .wav [WLR’1995].

Para representar músicas, podem ser utili zados arquivos MIDI, trilhas de CD ou outrosformatos de áudio como MP3. É interessante observar que arquivos de formato de onda tambémpoderia ser utili zados para representar sons, mas isto não acontecena prática porque estes arquivosocupam muito espaço em disco e memória. Os arquivos de tipo MIDI [WLR’1995] são utili zados paraproduzir músicasintetizada, pois eles contêm apenas uma descrição do som (como uma partitura) queé enviada para que um sintetizador efetivamente o reproduza. De acordo com o Fraunhofer IIS[MP3’2001], MP3 é um formato de áudio que utili za uma alta taxa de compressão, ao mesmo tempomantendouma qualidade comparável a existente em trilhas de CD. Desta forma, o uso deste formatode arquivo para músicas de fundo é viável. A única desvantagem é que o arquivo tem que serdescomprimido em tempo real, e isto pode gerar problemas de desempenho para a aplicação.

2.3. Módulo de Dispositivos de Entrada

Computadores pessoais possuem pelo menos dois tipos de dispositi vos padrões: um teclado eum mouse. Jogos para computador podem, entretando, fazer uso de outros dispositi vos como controlesde jogo (joysticks). Um controle de jogo pode ser definido como qualquer dispositi vo que não sejateclado nem mouse [ALA’1999].

A escolha de qual dispositi vo é utili zado é dividida entre o desenvolvedor e o usuário. Algunsjogos podem requerer que algum tipo específico de dispositi vo esteja disponível para tornar asimulação mais realista ou simplesmente porque o determinado tipo de dispositi vo é mais adequado àsituação. Por exemplo, simuladores de vôo podem utili zar manches e jogos de corrida podem utili zar

1 Role Playing Games

4

volantes e pedais.Existem, ainda, dispositi vos que são capazes de reagir a certos eventos. Estes dispositi vos são

conhecidos como dispositi vos force-feedback. Eles possuem motores em seu interior que são capazesde produzir forças como resistência ou “puxões” , conforme forem programados.

2.4. Módulo de Física

Neste módulo são feitos todos os cálculos para a simulação de Física da interação entre osobjetos do jogo e o ambiente onde eles são posicionados. É comum a utili zação da FísicaNewtonianapara modelar o comportamento de todas estas entidades.

A complexidade dos cálculos depende do quão próximo da realidade se quer que o jogo esteja.Algumas aplicações para Física em jogos:

� Simulação de gravidade;� Simulação de movimento de objetos, atrito, amortecimento, et cetera;� Sistemas de partículas (simulação de fogo, fumaça, explosões, et cetera);� Detecção e resposta a colisões.

2.5. Módulo de Inteligência Artificial

A função da IA é produzir um comportamento para os objetos controlados pela aplicação demodo que seja convincente ao usuário e que torne o jogo interessante. Como exempli ficado em[ALA’1999] e [BSA’1996], IA pode ser utili zada em jogos para alguns destes fins:

� Oponentes inteligentes: Objetos que representam algum desafio ao jogador, ou seja, ousuário precisa derrotá-los de alguma maneira para prosseguir no jogo;

� Objetos inteligentes não controlados pelo jogador: Objetos que cooperam com o jogador ouque apenas estão presentes no mesmo ambiente deste, alheios à ação (mas com vidaprópria);

� Ambientes ou sistemas inteligentes: Ambientes que reagem a eventos executados pelojogador ou conjunto de objetos que se comunicam entre si para executar algum tipo deestratégia.

Entre algumas técnicas de IA existentes que podem ser aplicadas, podem ser citadas:

� Algoritmos determinísticos (comportamentos pré-determinados ou pré-programados);� Aplicação de padrões (comportamentos determinados por uma série de padrões, que podem

ser implementados através de roteiros ou scripts);� Planejamento e árvores de decisão;� Redes neurais;� Lógica Fuzzy;� Algoritmos genéticos;� Aprendizado e memória;� Cálculo de rotas (Path-finding);� Sistemas Especialistas.

5

2.6. Módulo de Conectividade

SegundoBen Sawyer [BSA’1996], uma sessão de jogo em que dois ou mais jogadores estãoconectados remotamente pode ser classificada em uma das seguintes categorias:

� Conexão modem a modem : Duas pessoas com duas máquinas diferentes conectam-sediretamente uma à outra com uso de modems ;

� Conexão em rede local (LAN): Duas ou mais pessoas utili zam uma rede local paracompartilhar uma sessão de jogo. É utili zado algum protocolo de rede como IPX ou NetBios;

� Conexão a servidor remoto: Nesta configuração, existe um servidor e vários programasclientes. O servidor tem a função de receber os dados dos clientes e repassá-los aos outros.Geralmente, estas sessões de jogos ocorrem através da Internet, utili zando-se o protocoloTCP/IP.

� Web Games: Correspondem a jogos que são executados através de um programa navegadorpra Internet (web browser). Podem ser implementados em Java (applets) ou comtecnologias tais como Macromedia Flash e Macromedia Shockwave.

6

3. Máquinas de Jogos

Máquinas de jogos (engines) são coleções de uma ou mais bibliotecas de software queoferecem serviços e/ou entidades de alto nível (abstrações), com o objetivo de automatizar o máximopossível de tarefas para que o desenvolvedor possa se concentrar no conteúdoou lógicada aplicação.Uma questão pertinente: o que deve ser automatizado ?

Um jogo é uma aplicação que utili za alguns módulos bem definidos (Gráficos, Sons,Dispositi vos de entrada, Física, Inteligência Artificial ou Conectividade). Uma Máquina de Jogosdeve implementar alguns desses módulos e oferecê-los como um serviço para o desenvolvedor.

Os estilos de jogos determinam quais módulos são necessários e o quão complexos eles são.Sendoassim, o grau de especialização de uma engine depende do comprometimento com um estilo ouestilos para os quais ela foi escrita (se for o caso).

As engines podem também prover certas ferramentas para a manipulação de suas abstrações.Por exemplo, uma engine poderia propor um modelo para as cenas que ela exibe (ex: um mapa).Nesse caso, ela poderia oferecer uma ferramenta de construção de cenas, de forma que o usuáriopossa lidar com estas abstrações sem se preocupar com a sua real implementação.

3.1. Exemplos de Máquinas de Jogos

A seguir são apresentados alguns exemplos de Máquinas de Jogos, para ajudar na compreensãode suas aplicações.

3.1.1. T3DLib

Desenvolvida por André LaMothe [ALA’1999], é uma engine simples, escrita com C++ sobreDirectX, usada para o desenvolvimento de aplicações para sistemas Windows. Ela provê finascamadas de abstração (conhecidas como wrappers) para os módulos Gráfico, Física, Dispositi vos deEntrada e Som. Esta engine é destinada para jogos 2D.

Podem-se destacar os seguintes itens:

� Possibilit a o uso de arquivos Bitmap do Windows (BMP);� Implementação de sprites, chamados de BOBs (Blitter objects) pela engine. Estes objetos

podem ter uma ou mais sequências de animações. Os quadros (frames) das animações sãoorganizados em um arquivo do tipo Bitmap, e as sequências de animações são simplesmenteíndices sequenciais para os quadros. São oferecidos algoritmos para detecção de colisãoentre BOBs;

� Contém uma bibliotecapara lidar com linhas e polígonos em 2D. Existem operações básicassobre polígonos (translação, rotação, escala) e para desenho (sólido ou wireframe);

� Leitura de dados do teclado, mouse e joystick, através do DirectInput;� Reprodução e leitura de arquivos de som do tipo formato de onda do Windows (arquivos que

possuem normalmente extensão .wav) e músicas MIDI.

3.1.2. Ack-3D

Esta engine foi desenvolvida por Larry Myers [LLM’1995] para jogos 3D do tipo POV (Pointof view) em mente. Neste tipo de simulação, o cenário é desenhado utili zando-se técnicas de raycasting, e o resultado final é que o usuário visualiza as cenas em primeira pessoa. (Exemplo de jogosdo gêneros: Doom e Wolfenstein 3D).

Embora esta engine tenha “3D” no nome, comumente ela não seria classificada como tal[JDG’1996]. O uso da técnicade ray casting resulta em cenas que simulam 3D a partir de princípios

7

2D, e não 3D real. Por esta razão, engines que usam essa implementação são ditas “2.5D” por nãoserem totalmente 3D. Existem implementações para DOS (C) e Windows (C++). Sua funcionalidadepermite o uso dos módulos Gráfico, Física e Dispositi vos de Entrada.

Entre os serviços oferecidos, pode-se destacar:

� Implementação de algoritmos para ray casting exclusivamente por software (não é utili zadaaceleração por hardware, até porque na épocade desenvolvimento da engine esses recursosainda não estavam disponíveis);

� Utili zação de arquivos Bitmap (PCX, BBM2 e LBM3 );� Movimentação do ponto de vista (câmera) e detecção de colisão com o ambiente;� Renderização ou construção de cenas a partir de um ponto de vista. Estas cenas (que podem

conter paredes, portas e outros objetos definidos pelo desenvolvedor), são representadas porarquivos de mapas. Elas podem ser desenhadas com sombreamento e texturas;

� Leitura de dados do teclado e mouse4;� Implementação de objetos (sprites) estáticos e dinâmicos com animação (chamadas de

object sequences na engine) e detecção de colisão;� Editor de mapas.

3.1.3. OGRE

OGRE (acrônimo para Object Oriented Graphics Rendering Engine) é uma engine orientada acenas desenvolvida em C++ sob o modelo open source, desenvolvida por OGRE Team [OGR’2002].

Uma das premissas no desenvolvimento de OGRE é a qualidade do código e projeto. Para osdesenvolvedores desta engine, a API oferecida ao usuário deve ser a mais simples e intuiti va possível,deixando otimizações e “ truques” para quando forem realmente necessários. Conceitos encontradosem sistemas comerciais (design patterns, análise e projeto com UML, uso de outras bibliotecas desoftware para agili zar o desenvolvimento) são amplamente utili zados.

Sua implementação faz uso de técnicas de programação orientadas a objeto (encapsulamento,overloading, herança, et cetera).

O projeto da engine não foi desenvolvido para um tipo específico de aplicação; sua interfacegenérica pode ser extendida dinamicamente através do uso de plugins (dlls). Esses pluginsimplementam gerentes de cena (scene managers) que se comunicam com o cerne da engine, que nãoprecisa ser modificado. OGRE possui apenas o módulo Gráfico.

Esta engine oferece os seguintes serviços (entre outros):

� Interfaceorientada a objetos construída com independência de API 3D (i.e. poderiam serusados Direct3D, OpenGL, et cetera). Existem implementações para Windows e Linux;

� Importação de modelos do 3D Studio (possui também arquivo de descrição de modelos comformato próprio);

� Uso de diversos tipos de arquivos de imagem (TGA, PNG e JPEG) para texturas, comgeração automática de mipmaps. Alguns efeitos sobre texturas (environment mapping,multitexturing em um ou mais passos, entre outros) podem ser aplicados;

� Implementação de objetos móveis (chamados de entidades). Não oferecedetecção de colisãoentre estes objetos;

2 Formato usado pelo programa Deluxe Paint II. [LLM’1995]3 Formato usado pelo programa Deluxe Paint II. [LLM’1995]4 Na versão para DOS, são implementados algoritmos para lidar com esses dispositivos diretamente por

hardware. Já a versão para Windows, usa as ferramentas padrões já disponíveis para programas Windows.Não é possível utili zar joysticks em nenhuma versão.

8

� Tipos diversos de luzes (spot lights, point lights);� Sistemas de partículas (particle systems), inclusive com definição por scripts de texto;� Billboarding para representar sprites;� Definição dos materiais dos objetos através de scripts de texto;� Framework (i.e. template) para novos programas clientes.

3.1.4. Fly3D

Fly3D5 é uma engine desenvolvida em C++, orientada a plugins desenvolvida por ParaleloComputação [FLY’2002]. Os plugins têm papel fundamental na concepção da engine, pois qualquerextensão se faz a partir deles. Uma extensão da engine podem ser classes utilit árias, um frontend ouaté uma nova aplicação. O objetivo disso é prover uma interface generalizada e deixar asespecializações a cargo dos plugins, ao mesmo tempo evitando-se recompilar todoo cerne da enginequando se quiser adicionar uma nova funcionalidade.

A parte central da engine (chamada de backend) é formada por quatro módulos: DirectX ,renderização, matemática e a engine propriamente dita.

O módulo de renderização guarda toda a configuração da parte gráfica, cuida de estruturas dedados afins e oferececomandos de alto nível (desenhar texto na tela, alterar modode vídeo, et cetera)que lidam com a API 3D nativa, que é OpenGL.

O módulo de matemática oferecevárias classes que representam entidades matemáticas comovetores, matrizes e quaternions, que são usadas ao longo do processamento na engine.

O módulo DirectX oferece leitura de dados de dispositi vos de entrada (teclado e mouse),carregamento, gestão e reprodução de arquivos de som (Windows .wav) e conectividade.

O módulo da engine propriamente dita é aquele que integra os outros três e que supervisiona acomunicação entre eles e os plugins.

Fly3D também provê várias ferramentas adicionais (que são os frontends), como editores decenas, servidores para jogos em rede , extensões para o uso na Web (ActiveX), entre outras.

Fly3D oferecesoluções para os módulos Gráfico, Dispositi vos de Entrada, Som, Física, Redese Inteligência Artificial.

Podem-se ainda destacar algumas características desta engine :

� Utili za árvores BSP como estrutura principal no processamento de cenas;� Possui uma série de formatos de arquivo próprios, usados para armazenar estruturas como

árvores BSP, lightmaps, geometria para objetos estáticos ou animados, entre outros. Paratrabalhar com esses tipos de arquivos, são oferecidas ferramentas (frontends) e plugins paraprogramas de modelagem 3D;

� Possui vários plugins que oferecem uma série de objetos ou entidades de alto nível e efeitosespeciais. Como exemplos, encontram-se nesse pacote sistemas de partículas, explosões,projéteis, câmeras, objetos controlados pela IA (chamados de robôs pela engine), abstraçãopara jogador (chamado de “pessoa”), entre vários outros;

� Leitura de dados do teclado e mouse, através do DirectInput;� Implementação de detecção e tratamento de colisão entre objetos e o ambiente;� Possui algoritmos de baixo nível para implementação de Inteligência Artificial;� Implementa jogos em rede em um esquema cliente/servidor;� Todas as aplicações criadas com a engine são executadas através de um frontend especial.

Esse frontend possui algumas opções para alterar o comportamento da renderização e paracarregar e visualizar arquivos de cenas.

5 http://www.fly3d.com.br

9

3.2. Conclusões

Os exemplos apresentados são apenas uma pequena amostra da grande quantidade de enginesencontradas hoje em dia (basta fazer uma busca na Web para encontrar vários outros exemplos).Podem-se encontrar engines das mais variadas complexidades, desde algumas que apenas encapsulamalguma API nativa ou partes desta (DirectX, por exemplo), quanto àquelas que provêm soluçõesaltamentes especializadas como renderização de terrenos com níveis de detalhe variados,implementação de Inteligência Artificial através de roteiros (scripts) e outros problemas avançados.

Entre os exemplos citados, pode-se observar uma variedade de tecnologias. Na parte gráfica, asmais recentes utili zam aceleração por hardware, através de APIs como Direct3D, DirectDraw eOpenGL. A aceleração por hardware em placas de vídeo é comum hoje em dia, seja para 2D ou 3D. Oresultado disso é que os desenvolvedores podem acessar as funções disponíveis em hardware anexo eaproveitar o processador central para implementar outras tarefas.

Na parte de som e dispositi vos de entrada, a implementação é a mínima necessária para queesses módulos funcionem, não existindo maiores abstrações.

Finalmente, o próprio conceito de engine pode ser confundido com o conceito de bibliotecadesoftware, mas as diferenças são sutis. Uma máquina de jogos que não apresente um comprometimentocom um determinado estilo de jogo poderia ser confundida com uma bibliotecaporque ela apenasoferece ferramentas para o programa (i.e. a aplicação), ou seja, o programa usa os serviços damáquina mas esta não possui uma autonomia (i.e. a máquina em si é apenas um conjunto de serviços eobjetos que são acessíveis a um programa). Por outro lado, uma máquina altamente especializadapoderia ser configurada através de parâmetros (um programa que executasse essa máquina, roteiros ouscripts, et cetera) para que um novo jogo pudesse ser produzido. Isso quer dizer que a máquinafuncionaria como um molde (ou template), com uma certa automonia (ou seja, a máquina aceitariaparâmetros e funcionaria por si própria).

Embora existam engines tão diferentes entre si, pode-se citar algumas vantagens e desvantagensem relação ao uso:

3.2.1. Vantagens� Uma engine com alto nível de especialização pode proporcionar uma plataforma de

desenvolvimento rápido de aplicações, sendo necessário apenas alterar os parâmetros defuncionamento.

� O desenvolvedor não precisa se preocupar em criar/testar código para lidar com problemasde nível mais baixo, porque isto já foi feito antes.

� O código pode ser reutili zado em vários projetos diferentes.� O reuso em vários projetos diferentes proporciona oportunidades para novos testes, o que

contribui para a robustez da engine.� O desenvolvedor (cliente) não necessariamente precisa ter todo o conhecimento necessário

para implementar (ou entender a implementação) da engine. Esta pode apresentar umainterfacede nível mais alto de modoque se possa utili zá-la sem se preocupar com detalhesirrelevantes para a aplicação em desenvolvimento. Além do mais, se a engine for projetadacom orientação a objetos em mente, ela pode apresentar uma interface comum para umdeterminado tipo de tarefa e deixar as especializações definirem a implementação. Nessecaso, o desenvolvedor (cliente) será beneficiado porque o tempo de aprendizado para usar aengine será menor (porque usa uma interface comum para realizar várias tarefas).

� Pode proporcionar independência de API e sistema operacional. A engine poderia serdesenvolvida utili zando objetos ou entidades de nível de abstração mais alto (ex: efeitosonoro, polígono, sistema gráfico, gerente de cenas, et cetera). Desta forma, a enginepoderia ter a implementação modificada, se assim fosse necessário, e as aplicações clientesnão sofreriam alterações significativas. Pelo mesmo motivo, o uso da aplicação em sistemasoperacionais diferentes é facilit ado.

10

� Economizam-se recursos (tempo, dinheiro) ao pular etapas de desenvolvimento.

3.2.2. Desvantagens� Nenhuma engine resolve todos os problemas. Nesse caso, o desenvolvedor tem duas

escolhas: usar outra engine ou tentar extender a atual para adequá-la ao seu caso.� Engines desenvolvidas para um único tipo de jogo possuem uma estrutura rígida e sua

flexibili dade é pequena; isto é uma desvantagem se o desenvolvedor trabalha com váriosestilos de jogos. Por outro lado, pode ser uma vantagem significativa se o desenvolvedor sóse preocupa com o estilo em questão, pois a implementação da engine pode ser bastanteotimizada (e/ou mais completa) ao assumir que somente um estilo de jogo será usado. Aquestão aqui é o confronto entre flexibili dade e otimização.

� Engines não são muito diferentes de outros sistemas de software. Com o tempo, novosrecursos podem ser adicionados, aumentando a complexidade geral. Sem estratégias dedesenvolvimento e manutenção adequadas, a engine pode passar de solução a problema.

11

4. Uma Máquina de Jogos

Neste capítulo é apresentada uma máquina de jogos (Glib) com os seguintes objetivos:

� Prover uma funcionalidade mínima para a criação de jogos e programas de demonstraçãomultimídia;

� Facili dade de uso;� Criação de camadas de alto nível sobre as APIs nativas, com a finalidade de automatizar

tarefas repetiti vas (como por exemplo, inicialização de sistemas) e agili zar odesenvolvimento dos aplicativos;

� Gestão automatizada de recursos.

Esta máquina de jogos é implementada em C++ e utili za tecnologias como OpenGL e DirectXpara realizar as suas tarefas. É destinada para o desenvolvimento de aplicações em ambienteWindows.

4.1. Gestão Automatizada de Recursos

Um recurso pode ser definido como algo que deve ser requisitado ao Sistema Operacional,quando necessário, e que deve ser devolvido a este após o uso. Esta definição é abstrata o bastantepara englobar exemplos como memória, arquivos, sockets, et cetera.

O principal problema em relação a recursos é que o programador deve explicitamente devolvê-los ao Sistema Operacional quando não são mais necessários. Quando isto não é feito, uma série deproblemas podem se manifestar, como perda de recursos (resource leaks), deadlocks, bugs“aleatórios” (um defeito que é difícil de se reproduzir), acessos inválidos à memória, exaustão doSistema Operacional (i.e. os recursos do Sistema Operacional estão esgotados), entre outros,dependendo do tipo do recurso.

Linguagens de programação6 como C++ não possuem funcionalidade padrão para tratar doproblema. Entretanto, a gestão de recursos pode ser automatizada utili zando-se a estrutura nativadesta linguagem.

4.1.1. Objetos Automáticos

Toda classe em C++ possui duas funções especiais: um construtor e um destrutor. O construtoré chamado quandoo objeto é criado,e o destrutor é chamado quandoo objeto sai do escopoonde elefoi definido,sempre. Desta forma, é possível encapsular recursos utili zando-se classes. Uma instânciada classe passa a ser proprietária do recurso (porque passa a ser responsável por sua devolução); esteé alocado no construtor e devolvido ao Sistema Operacional através do destrutor.

É importante notar que todos os recursos encapsulados dessa forma são automaticamentedevolvidos ao Sistema Operacional, se acontecer algum desses casos:

� Um objeto declarado em um bloco de código (uma função, por exemplo) será criado (i.e.terá o seu construtor executado) quando o fluxo de programa entrar no escopo do bloco.Quando o fluxo de programa deixar o bloco7, o objeto será destruído (i.e. terá o seu destrutorexecutado);

6 Linguagens como Java fornecem coletores de lixo para gestão “automática” de memória. A coleta dememória não utili zada é feita periodicamente pela Máquina Virtual Java, processo que em determinadascircunstâncias pode ocorrer somente ao término da aplicação [BEC’2000].

7 Saída normal (break, goto , return) ou por exceção (throw).

12

� Um objeto A é declarado como campo de um outro objeto B. Nesse caso, o objeto A seráconstruído quando o objeto B for contruído. Da mesma forma, o destrutor de A seráexecutado pelo destrutor de B, quando este for destruído.

Em ambos os casos, o escopo de uso do objeto é bem definido, e por isso o compilador podeautomatizar todo o processo8.

Existe, porém, o caso em que o recurso é alocado dinamicamente (ex: memória). O escopo deuso deste recurso não é bem definido, e a linguagem não cuida da devolução deste ao SistemaOperacional de maneira automática. A solução para o problema é utili zar um objeto que tenha umescopo bem definido para encapsular o recurso. Este objeto passará a ser o proprietário do recursoencapsulado. Essa cadeia de objetos que encapsulam recursos ou outros objetos acaba no programaprincipal, que é o nível mais alto.

Estas idéias, acrescidas da noção de propriedade sobre recursos, formam a base para oparadigma de Gestão de Recursos (Resource Management), apresentado por Bartosz Milewsky[BMI’2001]. Este paradigma define uma única regra, chamada Regra Primeira de Aquisição (FirstRule of Acquisition), que diz: “Obtenha recursos em construtores e devolva-os usando os destrutorescorrespondentes”. Como exemplos ilustrativos, podem-se citar a alocação de memória e sincronizaçãoentre threads através de semáforos.

4.1.1.1. Exemplo 1

A STL9 ofereceuma classe utilit ária para cuidar de memória alocada dinamicamente, chamadaauto_ptr. Uma instância desta classe, ao ser criada, recebe o ponteiro para a memória obtida dosistema. Quando a instância é destruída, ela devolve a memória ao Sistema Operacional,automaticamente :

int main (int argc, char* argv[]){ std::auto_ptr <int> p (new int);

return 0;}

No exemplo acima, o objeto auto_ptr passa a ser proprietário do recurso obtido e irádevolvê-lo ao Sistema Operacional quando o programa se encerrar.

4.1.1.2. Exemplo 2

Sincronização entre threads em Windows é feita utili zando-se regiões críticas ou exclusivas(Critical Sections). Para informar ao Sistema Operacional que vai “entrar” em uma região exclusiva,uma thread deve chamar a função EnterCriticalSection()10, e para “sair”, ela deve chamarLeaveCriticalSection()11. O problema é que a saída da thread da região exclusiva (para queoutras threads possam utili zá-la) não é garantida, porque deve-se chamar explicitamente uma funçãopara que isto aconteça.

Uma solução simples para esse problema é fornecida a seguir:

8 A exceção que pode desquali ficar a regra é o uso das funções exit() e abort(), que provocam términoabrupto do programa. Quando essas funções são usadas, destrutores de objetos locais não são executados.

9 Standard Template Library.10 API do Windows.11 API do Windows.

13

class CriticalSection{ friend class Lock;

private:

CRITICAL_SECTION _criticalSection;

void Enter () { ::EnterCriticalSection (&_criticalSection); }

void Leave () { ::LeaveCriticalSection (&_criticalSection); }

public:

CriticalSection () { ::InitializeCriticalSection (&_criticalSection); }

~CriticalSection () { ::DeleteCriticalSection (&_criticalSection); }

};

A classe Criti calSection apenas encapsula a estrutura de dados usada pela API do Windows.Esta classe, por si só, não garante que todo objeto que entrar em uma região exclusiva saia delaautomaticamente. Por isso, seus métodos são acessíveis apenas a uma classe em especial e invisíveispara todas as outras. Esta classe privilegiada (Lock) será a proprietária do recurso “estado da regiãoexclusiva”, e irá garantir que todo cliente que entrar em uma região exclusiva irá deixá-laautomaticamente:

class Lock{

private:

CriticalSection & _zone;

public:

Lock (CriticalSection & criticalSection) : _zone (criticalSection)

{ _zone.Enter(); }

~Lock () { _zone.Leave(); }

};

Um exemplo de uso de ambas:

14

class Shared{

private:

CriticalSection _criticalSection;

public:

void Update () { Lock guard (_criticalSection);

// todo o processamento aqui

}

};

É possível observar que clientes que entrarem na região exclusiva sairão dela no término deexecução da função. Mais detalhes sobre a metodologia de Gestão de Recursos (como noções detransferência de propriedade) podem ser encontrados em [BMI’2001].

4.2. Máquina de Estados

O modelo proposto de Máquina de Jogos utili za uma Máquina de Estados que possui osseguintes objetivos:

� Prover uma estrutura autônoma para todas as aplicações. Estrutura autônoma aqui significaque a Máquina funcionará com o uso de parâmetros, e que ela não terá que sofrermodificações para que uma novo programa seja feito;

� Isolar partes da aplicação que são comuns e implementá-las como parte da Máquina. Entreestas partes comuns, encontram-se tarefas como configurar sistemas diversos (SistemaGráfico, por exemplo). Com isso, o desenvolvedor ganhará tempo ao pular estas etapas.

4.2.1. Fluxo de Execução de Jogos

Um fluxo de execução típico de um jogo, segue a seguinte ordem [ALA’1999]:

1. Inicialização da Aplicação12;

2. Ciclo Principal;

3. Finalização da Aplicação13.

O Ciclo Principal divide-se em:

A) Leitura de dados dos dispositi vos de entrada;

12 Inicialização de estruturas de dados, obtenção de recursos do Sistema Operacional, et cetera.13 Devolução de recursos usados ao Sistema Operacional , restauração de variáveis anteriores dos subsistemas

(como modo de vídeo, por exemplo), et cetera .

15

B) Atualização da aplicação e os objetos do jogo14;

C) Renderização de todos os objetos do jogo.

O Ciclo Principal é repetido até que se deseje terminar a aplicação.

4.2.2. Definição de Estado

Como o interesse é que a Máquina de Estados seja genérica e independente das aplicações, épreciso definir uma parametrização para o fluxo de execução.

Esta parametrização pode ser representada como um Estado da aplicação, ou seja, um Estado éuma entidade que representa o Ciclo Principal do fluxo de execução de um jogo. Como cada etapa doCiclo Principal é bem definida, podem-se usar técnicas de orientação a objetos para construir umaabstração para o Ciclo e assim, oferecer uma interface única para os programas clientes.

A classe para um estado da aplicação pode ser definida como a seguir:

// classe pertencente ao namespace Glib class State {

public: virtual void Init (...) = 0; virtual void ShutDown () = 0; virtual void GetInput (...) = 0; virtual void Update (float time) = 0; virtual void Render () = 0; ...15 std::string GetName() const { return _name; }

14 Inclui cálculos de Física (ex: movimentação, detecção de colisão, et cetera) para o jogador e outros objetosdo jogo, processamento de Inteligência Artificial e reprodução de sons (músicas, efeitos sonoros), porexemplo.

15 Representam elementos omitidos, por questões de clareza.

Figura 4.1: Diagrama de classes para um Estado.

16

protected: State (const std::string & name);

private: std::string _name; ... };

Todos os estados possuem um nome que vai ser usado para identificá-los em outras operaçõesda Máquina de Estados. Os métodos relacionados desta classe têm os seguintes objetivos:

� void Init (...): Requisitar que o Estado execute qualquer inicialização específica aele. Parâmetros relacionados ao ambiente de execução são enviados para o Estado, para queeste possa modificá-lo conforme suas necessidades;

� void ShutDown(): Requisitar que o Estado execute qualquer finalização específica a ele;� void GetInput (...): Requisitar que o Estado recupere dados dos dispositi vos de

entrada. Corresponde ao passo A do Ciclo Principal. Parâmetros relacionados ao sistema dedispositi vos de entrada são enviados para o Estado;

� void Update (float time): Requisitar que o Estado façaquaisquer atualizações deobjetos controlados por ele. Um parâmetro que informa o tempo decorrido desde a execuçãodo ciclo anterior é enviado ao Estado. Corresponde ao passo B do Ciclo Principal;

� void Render(): Requisitar que o Estado execute quaisquer operações relacionadas adesenho. Corresponde ao passo C do Ciclo Principal.

A Máquina de Estados trabalha apenas com referências a objetos dessa classe base. Destaforma, ela usará polimorfismo para executar os métodos corretos das classes especializadas.

4.2.3. Associação entre Estados e Máquina de Estados

A associação entre Estados e a Máquina de Estados é feita automaticamente pela classe base deEstados. O objetivo é reforçar o contrato de que todoEstado deve estar associado a uma Máquina deEstados.

Para que isso seja possível, é necessário que se possa determinar alguma Máquina de Estadosno contexto de criação de um Estado. Isto é feito da seguinte forma: Como a Máquina de Estados é aentidade central da Máquina de Jogos, é razoável impor que exista apenas uma instância da Máquinade Estados em qualquer aplicação.

Sendo assim, a Máquina de Estados é implementada como um singleton :

17

// classe pertencente ao namespace Glib class StateMachine {

... friend class State;

public: static StateMachine * GetInstance (); ...

private: StateMachine (); StateMachine (const StateMachine & obj); void operator = (StateMachine & obj); void Add (State * state); ...

private:

State * _pCurrentState; ... };

Figura 4.2: Associação entre Estado e Máquina de Estados.

18

A única maneira de obter uma instãncia da Máquina de Estados é através do método públicoGetInstance()(Todos os outros métodos de criação são invisíveis ao mundo exterior à classe).Este método é implementado como a seguir:

Glib::StateMachine * Glib::StateMachine::GetInstance () {

using namespace Glib;

static std::auto_ptr <StateMachine> instance (new StateMachine);

return instance.get(); }

Desta forma, a instância será criada sob demanda e será destruída automaticamente ao términoda aplicação.

A classe base de Estado associa-se à Máquina de Estados automaticamente em seu construtor,da seguinte forma:

Glib::State::State (const std::string & name) : _name (name) {

Glib::StateMachine::GetInstance ()->Add (this); }

É importante notar que o métodoAdd() é privado,sendoacessível aState porque a classe deMáquina de Estados é amiga (friend) da classe de Estado.Como a relação de classesfriend não éherdada por classes derivadas, pode-se construir um modelo de registro automático de estados semcomprometer a Máquina de Estados. As especializações de Estado não precisam ter (e não têm) idéiasobre nada disso.

4.2.4. Funcionamento da Máquina de Estados

Como toda Máquina de Estados deve ter um Estado inicial, é preciso prover alguma maneira dedeterminá-lo. A determinação do Estado inicial pode ser feita de duas maneiras:

� O primeiro Estado a ser registrado passa a ser o Estado inicial;� Utili zando-se um métodopúblico da Máquina de Estados chamadoSetInitialState().

Este método recebe como parâmetro um nome de Estado qualquer. Se não existir nenhumEstado registrado com o nome especificado, uma exceção é gerada para sinalizar o erro.

A mudança de Estado é controlada pelos próprios Estados. É importante observar que asespecializações de Estado não possuem acesso direto à Máquina de Estados. Desta forma, interaçõesentre Estados e Máquina de Estados serão feitas através de um objeto intermediário (Oráculo). OsEstados efetuam requisições de mudançade Estado ao Oráculo, enviandopara ele o nome do Estadopara o qual se deseja mudar (mais detalhes sobre o Oráculo na próxima seção).

A principal função da Máquina de Estados é executar o fluxo de jogo; por este motivo, elaguarda uma referência (um ponteiro) para o Estado corrente. A implementação desta tarefa é feitaassim:

19

void Glib::StateMachine::Run (HINSTANCE appInstance) {

if (_isRunning) return;

Init (appInstance);

MainLoop ();

}

O método Run() é público e portanto, acessível à aplicação. Existe um guarda16 para impedirque vários fluxos sejam executados em paralelo. Os métodos referenciados espelham os passos dofluxo de execução de jogo. É interessante notar que o passo 3 do fluxo (encerramento da aplicação)não é executado explicitamente porque os objetos controlados pela Máquina de Estados fazem uso deGestão Automatizada de Recursos.

O método Init() corresponde à inicialização da aplicação (passo 1 do fluxo de execução). Nocontexto da Máquina de Estados, a função inicializa todos os sistemas e estruturas de dadosnecessários para a operação dos Estados.

O métodoMainLoop() corresponde ao Ciclo Principal (passo 2 do fluxo de execução). Nestafase, as funções do Estado atual são executadas, na ordem definida pelo Ciclo Principal. Este métodoexecuta algumas instruções específicas (e necessárias) para programas Windows e em seguida executaos passos do Ciclo Principal, conforme a seguir:

void Glib::StateMachine::MainLoop () {

MSG msg;

while (WM_QUIT != msg.message) { if (::PeekMessage (& msg, NULL, 0, 0, PM_REMOVE) ) { ::TranslateMessage (& msg); ::DispatchMessage (& msg); } else ProcessState (); } }

O método ProcessState() , que executa todas as operações relacionadas ao Estado, possuibasicamente três passos: Verificar se é necessário mudar o estado atual, calcular o tempo decorridodeste o ciclo anterior e executar os passos do Ciclo Principal:

void Glib::StateMachine::ProcessState () { se houve uma requisição de mudança de estado { mudar o estado } tempo decorrido = CalcularTempoDecorridoEntreCiclos();

_currentState->GetInput (...); _currentState->Update (tempo decorrido);

16 Variável, função ou trecho de código, utili zado para garantir que uma determinada condição seja satisfeita.

20

_currentState->Render ();

atualizar o vídeo (i.e. double buffering) }

É importante ressaltar que uma mudança de estados não ocorre no momento da requisição. Arequisição é armazenada, sendoavaliada somente no início de um novo ciclo. A razão disto é que osEstados podem implementar alguma inicialização ou finalização específicaa eles, e este modelo evitapossíveis inconsistências e erros. Como um exemplo problemático simples, tem-se a seguintesituação:

� Um estado A (inicial) e um estado B utili zam uma estrutura de dados ED qualquer paraoperações de desenho.Estas estruturas são alocadas dinamicamente, durante a inicializaçõesde A e B. O processamento da Máquina de Estados começapela inicialização de A, e aseguir executa o Ciclo Principal. Por alguma decisão na lógica da função Update() de A, éfeita uma requisição de mudança para o estado B. Se a mudança fosse efetuadaimediatamente, o Estado atual passaria a ser B. Logo a seguir, a função Render() , de B, éexecutada. O Estado B não teve chancede adquirir recursos do Sistema Operacional, e suasreferências para as estruturas de dados ED utili zadas são inválidas. A aplicação executa umaoperação ilegal e a Máquina de Estados pára. Existe ainda o risco de recursos obtidos por Anunca serem devolvidos ao Sistema Operacional.

4.2.5. Comunicação entre Estados e Ambiente Exterior

As especializações de Estado não possuem acesso direto à Máquina de Estados e, portanto, nãopodem se comunicar li vremente com os diversos sistemas utili zados pela aplicação.

Isto é feito para proporcionar ao desenvolvedor uma abstração em relação aos sistemasdisponíveis. Como o desenvolvedor não tem acesso direto aos sistemas, ele não se preocupa com ofuncionamento deles, e pode concentrar-se somente na implementação dos métodos de um Estado.

Entretando, pode ser necessário em algum momento fazer alguma chamada a algum dossistemas envolvidos. Uma questão importante é como se requisita uma mudançade Estado se não setem acesso à Máquina de Estados.

Para resolver estes problemas, sem comprometer a Máquina de Estados, são oferecidasabstrações definidas como Oráculos. Os Oráculos possuem acesso direto à Máquina de Estados(friend), mas expõem apenas alguns métodos potencialmente úteis aos Estados. Com isso, osOráculos funcionam como filt ros de acesso à Máquina de Estados.

21

A classe base para estados (Glib::State) possui uma instância da classe Glib::Oracle,para que as classes derivadas possam ao menos requisitar mudança de estados e encerrar a aplicação.

A classe Glib::Oracle é definida como a seguir:

// classe pertencente ao namespace Glib class Oracle { ... friend class State; public: virtual ~Oracle() {} void RequestStateChange (const std::string & newStateName); void TerminateApplication(); ...

private: void operator = (const Oracle & obj); Oracle (const Oracle & obj);

protected:

Oracle (); };

Figura 4.3: Oráculo e suas especializações

22

As especializações de Glib::Oracle que estão disponíveis são fornecidas pela Máquina deJogos ao desenvolvedor apenas em partes específicas da aplicação.

O ResourceOracle é acessível apenas no momento de inicialização17 do estado. Este objetoé responsável pela aquisição de recursos que dependam de algum status da Máquina de Estados (porexemplo, algum objeto cuja criação dependa de variáveis internas da Máquina, e que só possa serfeita diretamente por ela).

O InputOracle, que tem como função lidar com o Sistema de Dispositi vos de Entrada, éacessível na inicialização do estado e no momento de requisição18 de dados dos dispositi vos deentrada.

Estas especializações são implementadas da seguinte forma:

// classes pertencente ao namespace Glib class InputOracle : public Oracle {

...

friend class StateMachine;

public: virtual ~InputOracle() {} void ConfigureDevices(); void GetInput (...); ...

private: InputOracle () {} ...

};

class ResourceOracle : public Oracle {

friend class StateMachine;

public: virtual ~ResourceOracle() {} ...19 LoadSoundBuffer (...);

private: ResourceOracle () {} };

17 Método Init() da classe de estados.18 Método GetInput() da classe de estados.19 Representa um conjunto de efeitos sonoros, cuja descrição encontra-se na seção sobre o Sistema de Som.

23

4.3. Ferramentas Utili zadas

A Máquina de Jogos usa serviços de várias bibliotecas para implementar as suas ferramentas. Amotivação para a utili zação de bibliotecas anexas é agili zar o desenvolvimento e aumentar amodularidade da Máquina de Jogos. As seguintes soluções são empregadas:

� OpenGL20: É uma API utili zada para Computação Gráfica, em três dimensões. Desenvolvidainicialmente por Sili con Graphics, Inc (SGI), possui alto desempenhoe portabili dade, sendoimplementada para trabalhar em conjunto com hardware que seja otimizado e projetadopara a exibição de gráficos tridimensionais. Sua especificação é controlada pela ARB21, umgrupo de fabricantes de hardware para gráficos tridimensionais.

� DirectX22: Família de APIs criada por Microsoft Corporation para o desenvolvimento deaplicações multimídia de alto desempenho em Windows. Possui soluções para gráficosbidimensionais e tridimensionais (DirectDraw e Direct3D), dispositi vos de entrada(DirectInput), áudio (DirectSounde DirectMusic), conectividade (DirectPlay), instalação deaplicativos (DirectSetup) e fluxo (streaming) de mídia (DirectShow).

� Boost23: É um conjunto de várias bibliotecas desenvolvidas por colaboradores em todo omundo,que foi criada originalmente por membros do Comitê de Padronização do C++ . Umde seus objetivos é estabelecer referências para o desenvolvimento de bibliotecas, de modoque seja possível integrá-las à implementação padrão do C++. Provê soluções para Gestãode Memória, Programação Concorrente, Entrada e Saída, Programação Genérica,Matemática, entre outras. Sua distribuição é gratuita.

� DevIL24: Desenvolvida por Denton Woods, a DevIL (Developer’s Image Library) é utili zadapara manipulação de imagens, sendocapaz de trabalhar com diversos formatos de arquivos.Sua distribuição é gratuita e está disponível em diversas plataformas.

� Lib3ds25: Destina-se a manipulação de arquivos (formato 3DS) gerados pelo software 3DStudio, foi desenvolvida por J. E. Hoffman. Está disponível gratuitamente e suaimplementação é portável.

4.4. Ferramentas Disponíveis

Além da Máquina de Estados, são oferecidas outras ferramentas que encapsulam diversosmódulos implementados pela Máquina de Jogos. Entre esses módulos, estão Sistema Windows,Sistema Gráfico, Sistema de Entidades, Sistema de Dispositi vos de Entrada e Sistema de Som.

4.4.1. Sistema Windows

As aplicações que utili zam a Máquina de jogos possuem uma única janela, que é associada avários sistemas:

� Sistema Gráfico: Utili za a janela para operações de desenho;� Sistema de Dispositi vos de Entrada: Utili za a janela para determinar o modode cooperação

da aplicação em relação aos dispositi vos (ou seja, como a aplicação irá repartir o uso dosdispositi vos com outras aplicações);

20 http://www.opengl.org21 Architecture Review Board22 http://www.microsoft.com/directx23 http://www.boost.org24 http://www.imagelib.org25 http://lib3ds.sourceforge.net

24

� Sistema de Som: Utili za a janela para determinar como a aplicação irá dividir o usohardware de som com as outras aplicações.

A bibli otecaCOM é outra parte integrante do Sistema Operacional Windows que é usada pelaMáquina de Jogos. São oferecidas classes que encapsulam o funcionamento geral desta bibloteca, queé base para a implementação da API DirectX.

4.4.1.1. Criação de Janelas

A tarefa de se criar janelas em Windows é feita em duas partes: Primeiro, uma classe (é ummolde para janelas, não confundir com classes de linguagens orientadas a objetos) para a janela deveser definida e registrada com o Windows. Em seguida, criam-se as instâncias desejadas utili zando-sechamadas da API. Para automatizar estas tarefas, estão disponíveis objetos que registram classes dejanelas e criam instâncias delas, de maneira independente.

� Glib::Window::Class: Responsável por configurar e registrar a classe de janela;� Glib::Window::Maker: Responsável por criar instâncias de janelas. Uma instância pode

ser configurada de maneira independente das demais.

Uma observação importante é que OpenGL requer que a classe de janela da aplicação que outili za possua alguns atributos específicos. Para resolver este problema, é disponibili zada umaespecialização (Glib::Ogl::Window::Class) para registrar classes de janela específicas parauso com OpenGL.

Adicionalmente, existe uma especialização da classe criadora de instâncias de janelas(Glib::Ogl::Window::Maker) que configura os parâmetros da janela, baseando-se naconfiguração26 desejada para o OpenGL.

26 A classe Glib::Ogl::Config é apresentada na seção sobre o Sistema Gráfico.

Figura 4.4: Diagrama de classes para manipulação de janelas.

25

4.4.1.2. Biblioteca COM

O estado de uso da biblioteca COM é visto como um recurso e consequentemente éencapsulado como um objeto automático. Assim sendo, é oferecida uma classe(Glib::Com::Initializer) que inicializa e finaliza a biblioteca sem intervenção dodesenvolvedor.

Em relação a objetos e interfaces COM, são oferecidas classes (Glib::Com::Object,Glib::Com::SmartObject e Glib::Com::SmartInterface) que automaticamente cuidamda gestão dos contadores de referências utili zadas por elementos dessa biblioteca. As classes são ditasinteligentes porque são capazes de detectar quando um objeto não será mais utili zado e,consequentemente, devolvem os recursos utili zados ao Sistema Operacional.

// classes pertencentes ao namespace Glib::Com class Object {

public: virtual void * AcquireInterface (const IID & interfaceId) = 0; };

template <class I, const IID * iid> class SmartObject : public Object {

public: SmartObject (const CLSID & classId); virtual ~SmartObject(); SmartObject (const SmartObject & obj); void operator = (const SmartObject & obj);

...

Figura 4.5: Diagrama de classes relacionadas à biblioteca COM.

26

protected:

I * _interface;

private: void * AcquireInterface ( const IID & interfaceId); };

template <class I, const IID * iid> class SmartInterface {

public: SmartInterface (Object & obj); virtual ~SmartInterface(); SmartInterface (Object & obj); void operator = ( const SmartInterface & obj); ...

protected: I * _interface; };

Essas classes encapsulam os dois tipos de objetos COM disponíveis para uso: aqueles quepodem ser criados diretamente, através de uma função da API, e aqueles que só podem ser criadosindiretamente, a partir de um outro objeto. Ao primeiro, corresponde a classe SmartObject ,enquanto ao último corresponde a classe SmartInterface . A outra classe citada (Object ) énecessária para que se possa utili zar objetos do tipo SmartObject em SmartInterface , semespecificar parâmetros de especialização para SmartObject .

Um objeto do tipo SmartObject é construído e destruído da seguinte forma:

template <class I, const IID * iid> Glib::Com::SmartObject <I, iid>::SmartObject ( const CLSID & classId)

: _interface (0) {

HRESULT hr = S_OK; hr = ::CoCreateInstance (...); if (FAILED (hr)) // gera exceção }

template <class I, const IID * iid> Glib::Com::SmartObject <I, iid>::~SmartObject () {

if (_interface) _interface->Release ();

_interface = 0; }

27

Como todo objeto COM possui um contador de referências (utili zado para determinar a vidaútil do objeto) é necessário impor que as operações de cópia tradicionais sejam proibidas. Seja oseguinte exemplo:

Glib::Com::SmartObject <...> obj1; Glib::Com::SmartObject <...> obj2; obj1 = obj2; Glib::Com::SmartObject <...> obj3 (obj2);

Neste exemplo, obj1 e obj2 passam a fazem referência a um mesmo recurso. O recursoreferenciado anteriormente por obj1 está perdido e o contador de referências do recurso estáinválido.O objeto obj3 , ao ser construído,passa a fazer referência ao recurso encapsulado por obj1e obj2 . Existem três objetos referenciandoum único recurso, e o contador de referência deste não foiatualizado para refletir a situação.

Para evitar este tipo de problema, as operações de cópias são transformadas em operações dedivisão do uso de recurso:27

SmartObject::SmartObject (SmartObject & obj) 28 : _interface (obj_.interface) {

_inteface->AddRef (); }

void SmartObject:: operator = (SmartObject & obj) {

obj._interface->AddRef ();

if (_interface) _interface->Release ();

_interface = obj._interface; }

A classe SmartObject implementa um métodopara recuperar instâncias de outras interfaceschamado AcquireInterface() . Este método é usado pela classe SmartInterface pararecuperar instâncias de objetos COM encapsulados por ela:

template <class I, const IID * iid> void * Glib::Com::SmartObject <I, iid>::AcquireInterface ( const IID &interfaceId) { void * p = NULL;

HRESULT hr = _interface->QueryInterface (interfaceId, & p); if ( FAILED (hr) ) // gera exceção return p; }

27 Em outros contextos, esta operação poderia ser considerada uma transferência de recurso.28 Parâmetros de especialização omitidos por questões de clareza.

28

template <class I, const IID * iid> Glib::Com::SmartInterface::SmartInterface (Glib::Com::Object & obj) : _interface (0) {

_interface = reinterpret_cast <I*> (obj.AcquireInterface (*iid)); }

Analogamente à classeSmartObject, a classeSmartInterface proíbe operações de cópiatradicionais. A implementação destas classes é baseada em [WIN’2001].

4.4.2. Sistema Gráfico

Existem vários tipos de recursos em OpenGL, assim sendo, várias classes são oferecidas comfim de automatizar o processo de aquisição e devolução desses recursos.

� Glib::Ogl::Initializer: Responsável por preparar o sistema gráfico para uso, criauma instância do contexto de renderização e configura o OpenGL de acordo com osparâmetros desejados. Quando uma instância sai de escopo, o modo gráfico e recursosusados pela classe são devolvidos ao Sistema Operacional;

� Glib::Ogl::RenderingContext: Representa um contexto de desenho do OpenGL.Estes contextos são obtidos junto ao Sistema Operacional na preparação inicial para o usode OpenGL. Quando a aplicação é encerrada, todos os contextos devem ser devolvidos aoSistema Operacional. Estas tarefas são realizadas automaticamente pela classe;

� Glib::Ogl::DisplayList: Representa uma lista de execução de comandos doOpenGL. Toda lista de execução possui um identificador, que é obtido por uma função daAPI OpenGL. Uma instância da classe obtém um identificador na sua criação e devolve oidentificador quando é destruída;

� Glib::Ogl::Texture::TextureObject: Representa um objeto de textura, que é umaabstração para um conjunto de informações sobre uma textura (como o próprio conteúdodatextura e filt ros utili zados). Um objeto de textura possui um identificador, que éautomaticamente criado e destruído nas instâncias da classe.

Figura 4.6: Diagrama de classes relacionadas a recursos do Sistema Gráfico.

29

É possível, ainda, dividir instâncias das classes Glib::Ogl::DisplayList eGlib::Ogl::Texture::TextureObject entre vários objetos utili zando-se classes queimplementam contadores de referência, que são fornecidas pela biblioteca Boost:

typedef boost::shared_ptr <Glib::Ogl::Texture::TextureObject> Glib::Ogl::Texture::TextureObjectSharedPtr; typedef boost::shared_ptr <Glib::Ogl::DisplayList> Glib::Ogl::DisplayListSharedPtr;

Adicionalmente, são definidos alguns sinônimos para a conveniência do desenvolvedor:

typedef std::auto_ptr <Glib::Ogl::Initializer>Glib::Ogl::InitializerAutoPtr; typedef std::auto_ptr <Glib::Ogl::RenderingContext> Glib::Ogl::RcAutoPtr;

4.4.2.1. Câmeras

A abstração para câmeras em perspectiva pode ser representada de acordo com o seguintediagrama:

Esta classe possui propriedades básicas como posição, campo de visão (field of view) e planosdelimitadores (near e far clipping planes), sendo declarada como:

// classe pertencente ao namespace Glib::Ogl class Camera { public: float fov; float nearPlane; float farPlane; Glib::Math::Vector3D position; Glib::Math::Vector3D upVector; void UpdateProjection (int width, int height);

Figura 4.7: Diagrama de classes para uma câmera.

30

void LookAt (const Glib::Math::Vector3D & where); ... };

A classeVector3D representa um vetor de três dimensões. O campoupVector indicaa qualdireção corresponde “para cima”. Este é utili zado no método que faz com que a câmera aponte paraum determinado ponto:

void Glib::Ogl::Camera::LookAt (const Glib::Math::Vector3D & where) {

::gluLookAt (position.x, position.y, position.z, where.x , where.y , where.z, upVector.x, upVector.y, upVector.z); }

A projeção definida pela câmera pode ser atualizada por UpdateProjection(). Geralmenteisto é feito quando a área de desenho (viewport) muda de tamanho:

void Glib::Ogl::Camera::UpdateProjection (int width, int height) { ::glPushAttrib (GL_TRANSFORM_BIT); ::glMatrixMode (GL_PROJECTION); ::glLoadIdentity (); if (height == 0) height = 1;

::gluPerspective (fov, static_cast <double> (width) / static_cast <double> (height), nearPlane, farPlane); ::glPopAttrib(); }

4.4.2.2. Texturas

É possível utili zar texturas de duas dimensões, cujos dados são lidos do disco através dabibliotecaDevIL. Consequentemente, diversos formatos de arquivos de imagem [DWO’2002] podemser usados.

31

A definição de Glib::Ogl::Texture é dada a seguir:

// classe pertencente ao namespace Glib::Ogl class Texture { public: Texture (const std::string & filename, const Parameters & parameters); void Bind() const { _textureObject->Bind(); } void ChangeParameters (const Parameters & parameters); ... private: void LoadImage (const std::string & filename); private: Parameters _textureParameters; TextureObjectSharedPtr _textureObject; int _width; int _height; int _bytesPerPixel; int _imageFormat; };

A classe possui alguns atributos de imagem como formato (RGB, RGBA, BGR, et cetera,conforme os tipos definidos pelo OpenGL), largura, altura e quantidade de bytes por pixel. Apenastexturas com 24 bits por pixel ou mais podem ser usadas.

É possível determinar a aparência da textura através de parâmetros que são definidos pelaclasse Parameters, que é uma classe interna à classe Texture:

Figura 4.8: Diagrama de classes para representação de uma textura.

32

// classe pertencente ao namespace Glib::Ogl class Texture { public: class Parameters { Parameters (); Parameters (GLenum minFilter, GLenum magFilter, GLenum wrapMode_S, GLenum wrapMode_T); GLenum minFilter; GLenum magFilter; GLenum wrapMode_S; GLenum wrapMode_T; }; ... };

Os parâmetros disponíveis são os filt ros de redução e ampliação para aplicação de imagens empolígonos e os modos para wrapping29 nos eixos U (wrapMode_S) e V (wrapMode_T) decoordenadas de textura. Os valores que podem ser utili zados são os mesmos definidos por OpenGL. Oconstrutor padrão apenas preenche os parâmetros com os valores GL_LINEAR, GL_LINEAR,GL_REPEAT e GL_REPEAT.

O objeto mais interessante guardado por Texture é _textureObject. O objeto original(Texture::TextureObject) encapsula o recurso “objeto textura do OpenGL”. O objeto usadopor Texture é uma classe que implementa gestão de contadores de referência. O resultado é quevários objetos Texture podem automaticamente dividir uma única instância de uma imagem.

As imagens são lidas do disco no construtor da classe.

class Glib::Ogl::Texture::Texture (const std::string & filename, const Texture::Parameters & parameters) : _textureObject (new Glib::Ogl::Texture::TextureObject), _textureParameters (parameters), _width (0), _height (0), _bytesPerPixel (0), _imageFormat (0) { LoadImage (filename, parameters); }

void Glib::Ogl::Texture::LoadImage (const std::string & filename, const Texture::Parameters & parameters)

{ Glib::DevIL::Image image (filename); ...

29 Coordenadas de textura variam normalmente entre 0.0 e 1.0, inclusive. Quando os valores encontram-se foradesta faixa, eles podem ser repetidos (GL_REPEAT) ou truncados (GL_CLAMP).

33

UploadImage (image);

GetPropetiesFromImage (image); }

O método LoadImage() utili za um objeto automático para ler a imagem em disco. Emseguida, faz a transferência dos dados da imagem para a placa de vídeo (que inclui a especificação dosparâmetros de textura) e recupera as suas propriedades (altura, largura, et cetera). A classe podegerar mipmaps para a textura automaticamente, de acordo com o filt ro especificado nos parâmetros.

Os parâmetros podem ser alterados a qualquer momento através do métodoChangeParameters(). É importante destacar que a geração de mimaps só é feita no momento daleitura da imagem do disco.

A textura pode ser selecionada para uso com o método Bind(). A especificação decoordenadas de textura deve ser feita pelo cliente da classe, utili zando as funções do OpenGL.

Para a conveniência do desenvolvedor, é oferecida uma implementação para uma agregação detexturas, cuja classe correspondente (Glib::Ogl::TextureAutoVector) é definida a seguir:

typedef Glib::Util::AutoVector <Glib::Ogl::Texture>Glib::Ogl::TextureAutoVector; typedef boost::shared_ptr <Glib::Ogl::TextureAutoVector>Glib::Ogl::TextureAutoVectorSharedPtr;

A classe Glib::Util::AutoVector é apenas um array dinâmico de objetosstd::auto_ptr. Desta forma, estas classes seguem o paradigma de Gestão Automatizada deRecursos.

4.4.2.3. Modelos 3D

Existem três classes para se lidar como modelos: uma que encapsula o recurso “arquivo demodelos” (Glib::Ogl::ModelFile), uma que representa o próprio modelo(Glib::Ogl::Model) e uma que é responsável por ler e interpretar os dados do modelo em disco(Glib::Ogl::ModelLoader).

34

O tipo de arquivo de modelos tridimensionais utili zado é aquele definido segundoo formato dearquivo 3DS, que é originário do software de modelagem 3D Studio. A leitura dos dados dos arquivosé feita através da bibliotecaLib3ds. A classe Glib::Ogl::ModelFile realiza a leitura dos dadosdo modelo em disco e devolve os recursos alocados quandoo fluxo de programa sai do escopo ondefoi definida.

// classe pertencente ao namespace Glib::Ogl class ModelFile { public: Lib3dsFile * GetData () const { return _3dsFile; } ModelFile (const std::string & filename) : _3dsFile (0) {

Figura 4.9: Diagrama de classes relacionadas a modelos tridimensionais.

35

_3dsFile = ::lib3ds_file_load (filename_.c_str() ); if (! _3dsFile) // gera exceção } ~ModelFile() { ::lib3ds_file_free (_3dsFile); } private: ModelFile ( const ModelFile & obj); void operator = ( const ModelFile & obj); private: Lib3dsFile * _3dsFile; };

Os modelos possuem um delimitador de volume, definido como o menor paralelepípedo queengloba todos os seus polígonos, é calculado após os dados do arquivo serem lidos do disco. Estesparalelepípedos, também conhecidos como bounding boxes, são representados pela classeGlib::Ogl::ModelBoundingBox .

O único requisito assumido em relação aos modelos 3D é que eles sejam construídos demaneira que estejam centralizados na origem do Sistema de Coordenadas Cartesiano 3D (0,0,0).Desta forma, o paralelepípedo delimitador pode ser representado com apenas um ponto, que deterninaas extensões nos sentidos positi vo e negativo dos eixos X, Y e Z.

Estes paralelepípedos são alinhados com os eixos X, Y e Z, configuração que é particularmenteconhecida pelo acrônimo AABB (Axis Aligned Bounding Boxes). Esta configuração provê umamaneira simples de se verificar se dois paralelepípedos se sobrepõem, embora exista a limitação deque eles não podem sofrer rotações em qualquer um dos eixos.

// classe pertencente ao namespace Glib::Ogl class ModelBoundingBox { public: ModelBoundingBox() : ext (0, 0, 0) {}

ModelBoundingBox (ModelFile & modelFile) { ComputeFrom (modelFile); } void ComputeFrom (ModelFile & modelFile); public: Glib::Math::Vector3D ext; };

A classe Glib::Ogl::ModelBuilder (que lê e interpreta os dados do modelo em disco)realiza as seguintes tarefas:

� Ler os dados do arquivo em disco, utili zando objetos do tipo ModelFile ;� Criar instâncias de todas as texturas utili zadas pelo modelo (se houver);

36

� Construir uma lista de execução (display list) com os dados do modelo, utili zandotriânguloscomo primitiva básica;

� Calcular o paralelepípedo delimitador (bounding box) do modelo.

Todas estas tarefas são realizadas pelo único construtor da classe, que recebe o nome doarquivo a ser lido e parâmetros a serem usados nas texturas (se existirem) do modelo. A classe éimplementada como um objeto automático:

// classe pertencente ao namespace Glib::Ogl class ModelLoader { public: ModelLoader ( const std::string & filename, const Texture::Parameters & textureParameters);

DisplayListSharedPtr GetDisplayList() const { return _displayList; } TextureAutoVectorSharedPtr GetTextureVector() const { return _textureVector; } ModelBoundingBox GetBoundingBox() const { return _boundingBox; }

private: void LoadTextures (ModelFile & modelFile); void ComputeBoundingBox (ModelFile & modelFile); void BuildDisplayList (ModelFile & modelFile); ...

private: ModelLoader ( const ModelLoader & obj); void operator = ( const ModelLoader & obj); private: // parâmetros de filtros para as texturas Texture::Parameters _textureParameters; // vector com todas as texturas TextureAutoVectorSharedPtr _textureVector; // display list do modelo DisplayListSharedPtr _displayList; // paralelepípedo delimitador ModelBoundingBox _boundingBox; ... };

37

Glib::Ogl::ModelLoader::ModelLoader ( const std::string & filename, const Texture::Parameters & textureParameters) : ... { // o arquivo é carregado do disco Glib::Ogl::ModelFile modelFile (filename);

// todas as texturas usadas pelo modelo são lidas do disco // (caso existam) LoadTextures (modelFile);

// constrói-se a lista de comandos OpenGL BuildDisplayList (modelFile); // calcula-se o paralelepípedo delimetador ComputeBoundingBox (modelFile); }

A classe que representa o modelo é definida como a seguir:

// classe pertencente ao namespace Glib::Ogl class Model { public: Model();

Model ( const std::string & filename, const Texture::Parameters & textureParameters); void Render() { _displayList->Execute(); } const ModelBoundingBox & GetBoundingBox() const { return _boundingBox; } private: void CreateDefaultModel();

private:

TextureAutoVectorSharedPtr _textureVector; DisplayListSharedPtr _displayList; ModelBoundingBox _boundingBox; };

A classe possui uma agregação de texturas, uma lista de execução e um paralelepípedodelimitador. É interessante observar que os dois primeiros campos são objetos que implementamcontadores de referência. Isto quer dizer que várias instâncias da classe podem dividir os dados de umúnico modelo 3D. Adicionalmente está disponível uma classe (Glib::Ogl::ModelSharedPtr )para divisão de uma instância da classe Glib::Ogl::Model através de contadores de referências:

38

typedef boost::shared_ptr <Glib::Ogl::Model> Glib::Ogl::ModelSharedPtr;

O construtor padrão cria um modelo 3D que representa um cubo,o que pode ser útil para finsde depuração. O construtor principal recebe o nome doarquivo a ser lidoe parâmetros a serem usadosnas texturas do modelo, se existirem:

Glib::Ogl::Model::Model (const std::string & filename, const Texture::Parameters & textureParameters) { Glib::Ogl::ModelLoader loader (filename, textureParameters); _textureVector = loader.GetTextureVector(); _displayList = loader.GetDisplayList(); _boundingBox = loader.GetBoundingBox(); }

4.4.3. Sistema de Entidades

Uma entidade é um objeto que faz parte da cena simulada.

A implementação de uma entidade (Glib::MovableObject) é definida como um objetomóvel, que possui propriedades como posição e velocidade. Graficamente, um objeto móvel érepresentado por um modelo tridimensional. Este modelo tridimensional é uma propriedade do objetomóvel, e como tal, pode ser alterada a qualquer momento. Esta classe é definida como a seguir:

// classe pertencente ao namespace Glib class MovableObject { public: MovableObject(); MovableObject (const Glib::Math::Vector3D & pos, const Glib::Math::Vector3D & vel); bool Overlapped (MovableObject & obj);

Figura 4.10: Diagrama de classes para uma entidade.

39

const Glib::Ogl::ModelBoundingBox & GetBoundingBox () const; public: virtual void Update (float time); virtual void Render (); virtual bool TestCollision (MovableObject & obj); ... protected: Glib::Math::Vector3D _position; Glib::Math::Vector3D _velocity; Glib::Ogl::ModelSharedPtr _model3d; bool _isActive; bool _isVisible; };

O construtor default cria um objeto posicionado na origem com velocidade igual a zero emtodos os eixos, além de um modelo padrão. É possível, também, construir um objeto com posição evelocidades determinadas. Neste caso, um modelo padrão também é construído.

Todas as propriedades podem ser alteradas ou consultadas através de métodos de acesso(omitidos por simplicidade).

Os métodos virtuais são aqueles que definem o comportamento do objeto, de modo que odesenvolvedor deve redefiní-los conforme as suas necessidades. Por conveniência, eles possuem umaimplementação padrão:

void Glib::MovableObject::Update (float time) { _position.x += _velocity.x * time; _position.y += _velocity.y * time; _position.z += _velocity.z * time; }

void Glib::MovableObject::Render () { ::glPushMatrix(); ::glTranslatef (_position.x, _position.y, _position.z); _model3d->Render(); ::glPopMatrix(); }

bool Glib::MovableObject::TestCollision (Glib::MovableObject & obj) { return Overlapped (obj); }

40

O objetivo da implementação padrão é apenas proporcionar o uso imediato de instâncias daclasse. O métodoUpdate() é responsável por atualizar o estado do objeto. O significado de estadode objeto depende da especialização da classe. O método Render() tem como função desenhar omodelo que representa o objeto.

O métodoTestCollision() realiza a verificação de existência de colisão entre objetos e otratamento da ocorrência, se houver. O métodopúblico Overlapped() indicase os paralelepípedosdelimitadores que envolvem os modelos em questão se sobrepõem:

bool Glib::MovableObject::Overlapped (MovableObject & obj) { return Glib::Physics::AABBOverlap (_position, _model3d->GetBoundingBox(), obj._position, obj._model3d->GetBoundingBox() ); }

// função pertencente ao namespace Glib::Physics inline bool AABBOverlap ( const Glib::Math::Vector3D & pos1, const Glib::Ogl::ModelBoundingBox & box1, const Glib::Math::Vector3D & pos2, const Glib::Ogl::ModelBoundingBox & box2) {

const Glib::Math::Vector3D p1p2 (pos1, pos2);

return Glib::Util::Abs (p1p2.x) <= (box1.ext.x + box2.ext.x) && Glib::Util::Abs (p1p2.y) <= (box1.ext.y + box2.ext.y) && Glib::Util::Abs (p1p2.z) <= (box1.ext.z + box2.ext.z); }

A diferença entre Overlapped() e TestCollision() é que o primeiro é destina-se àchecagem simples de interseção enquanto o último deve implementar a resposta ao evento de colisão.

4.4.4. Sistema de Dispositivos de Entrada

O sistema de dispositi vos de entrada é implementado através de duas abstrações: o Gerente dedispositi vos de entrada e o Interpretador de dados.

41

A idéia principal é isolar os dispositi vos e os dados. O Gerente tem como responsabili dadelidar diretamente com os dispositi vos. Isto inclui realizar uma preparação inicial deles para o uso daaplicação e a leitura de dados.

Apesar de o Gerente ser a entidade que efetivamente recupera os dados dos dispositi vos, elenão tem idéia do significado deles. Por esta razão, quando os dados são lidos, o Gerenteimediatamente delega a tarefa de interpretar os dados para um Interpretador.

// classes pertencentes ao namespace Glib::Input class Handler { public: virtual void OnInputLoopStart() {} virtual void OnInputLoopEnd () {} virtual void Handle (...) = 0; };

class Manager { ... public: void GetInput (Handler & inputHandler); ... };

O Gerente requer apenas que as especializações de Interpretadores implementem o métodoqueefetivamente interpreta os dados. Desta forma, o Gerente torna-se uma entidade independente daaplicação, podendo funcionar com qualquer especialização do Interpretador :

void Glib::Input::Manager::GetInput(Glib::Input::Handler & inputHandler) { ...

// sinaliza ao interpretador que o ciclo de leitura vai começar inputHandler.OnInputLoopStart (); for (iteração por todos os dispositivos ativos) { dados = recupera os dados do dispositivos atual // delega a interpretação dos dados

Figura 4.11: Diagrama de classes para o Sistema de Dispositivos de Entrada.

42

inputHandler.Handle (dados); } // sinaliza ao interpretador que o ciclo de leitura foi encerrado inputHandler.OnInputLoopEnd (); }

Uma questão pertinente é como isso poderia funcionar, se existem os mais variados tipos dedispositi vos, tão diferentes entre si. A resposta para esta questão é que os dispositi vos são tratadoscomo dispositi vos virtuais.

Na programação tradicional de dispositi vos, os dados lidos são específicos aos dispositi vos enão específicos à aplicação. Por exemplo, uma função que lê dados do teclado poderia responder se atecla A ou a tecla ESC foi pressionada, ou uma função que lê dados de um joystick poderia responderse o botão 1 foi ativado ou para qual sentido o eixo horizontal estava apontado.

Para lidar com diversos tipos de dispositi vos, é preciso existir uma maneira de padronizar todasas ações, de forma que os dados passem a ser específicos à aplicação. Isto pode ser feito utili zando-seo mapeamento de ações a dispositi vos (action mapping), disponível no DirectInput, a partir da versão8.0 [DX8’2001].

Basicamente, o DirectInput define uma série de gêneros (Ação em primeira pessoa, jogos deplataforma, CAD, et cetera) de aplicações. Todos esses gêneros possuem algumas ações pré-definidas,que podem ser mapeadas para valores definidos pelo desenvolvedor. Por exemplo: O desenvolvedorpossui em sua aplicação uma ação que faz com que o jogador pule. Ele poderia definir uma constantepara representar esta ação, como, INPUT_PULAR. Após essa associação ser feita, a leitura de dadosdos dispositi vos irá informar o valor do dado utili zando a constante fornecida, em vez de valoresespecíficos a dispositi vos. Um ponto importante a destacar é que não importa qual é o tipo dedispositi vo, todos eles vão responder da mesma forma.

Para que tudo isso funcione, o desenvolvedor precisa definir uma série de valores com os quaisele queira trabalhar e associá-los com um dos gêneros pré-definidos.

O Gerente possibilit a que o desenvolvedor determine que associação (chamadas de mapas deações, ou Action Maps) será usada. O desenvolvedor também pode proporcionar que o usuário de suaaplicação altere as configurações do mapa utili zado (i.e. que botões ou teclas correspondem a quaisações), através do Gerente. Esta edição é feita utili zando-se uma chamada ao sistema, que exibe umajanela para que o usuário efetue as modificações.

Como consequência natural, o Gerente pode trabalhar com qualquer dispositi vo que sejacompatível com o DirectInput. A decisão sobre quais dispositi vos serão efetivamente usados passa aser do usuário final da aplicação.

4.4.5. Sistema de Som

O sistema de som é representado pelo uso de efeitos sonoros (pequenos arquivos de som), que éoferecido através de uma classe (Glib::Sound::Buffer) que implementa um conjunto de efeitossonoros.

43

Para usar esta classe, o desenvolvedor precisa determinar um conjunto de nomes de arquivo (naverdade, um std::vector), que será utili zado para criar os objetos de som. O acesso a essesobjetos é feito utili zando-se apenas um identificador numérico, em correspondência 1:1 com ostd::vector que contém os nomes dos arquivos. A classe std::vector, que faz parte da STL,corresponde a um array dinâmico.

// classe pertencente ao namespace Glib::Sound class Buffer { public: Buffer (HWND windowHandle, const std::vector <std::string> filenames); void Play (size_t id, size_t flags); void Stop (size_t id); void PlayLoop (size_t id, size_t flags); bool IsPlaying (size_t id); ... private: ... };

É necessário informar uma janela que vai estar associada com a reprodução de sons, conformerequisitos da API (DirectX).

O parâmetro flags dePlay() ePlayLoop() (cujo valor corresponde aos valores utili zadospela API) regulam o comportamento da reprodução do objeto selecionado. Vários efeitos sonorospodem ser reproduzidos simultaneamente (dependendodo valor de flags). Para a conveniência dodesenvolvedor, é utili zado um parâmetro padrão que indica reprodução simultânea.

Internamente, a classe utili za objetos DirectMusic associados com objetos automáticos COMpara realizar as tarefas de reprodução de sons e leitura dos dados dos arquivos em disco.

Figura 4.12: Classe para representação de efeitos sonoros.

44

4.5. Diagrama de Relacionamentos da Máquina de Jogos

Nesta seção é apresentado um diagrama que representa as relações entre as classes definidasnas seções anteriores.

Figura 4.13: Diagrama de Relacionamentos para a Máquina de Estados.

45

4.6. Exemplo

Para demonstrar o uso da Máquina de Jogos, é utili zado um exemplo simples cuja descrição é aseguinte:

Figura 4.14: Diagrama de Relacionamentos para objetos móveis, modelos 3D e texturas.

46

� A cena é constituída por uma “caixa” (na verdade, um cubo), onde um objeto se movimenta.Este objeto possui velocidade inicial aleatória e altera o sentidode seu movimento ao colidiralguma das paredes do cubo.Os comandos do usuário são: Movimentação do ponto de vista(câmera) nas direções horizontal e vertical, configuração dos dispositi vos de entrada eencerramento da aplicação. O ponto central do cenário corresponde à origem.

A aplicação possui somente um estado, que é representado por uma classe (State ). Aimplementação completa da aplicação encontra-se em anexo.

4.6.1. State.h//---------------// INCLUDES//---------------#include <Glib/GlibState.h>#include <Glib/OglCamera.h>#include <Glib/GlibMovableObject.h>#include "InputHandler.h"#include "Cage.h"

//---------------// CLASSES//---------------

class State : public Glib::State{ public:

// métodos herdados da classe base void Init (Glib::ResourceOracle & resourceOracle, Glib::InputOracle & inputOracle);

void WindowResized ( int width, int height);

Figura 4.15: Aplicação em execução.

47

void GetInput (Glib::InputOracle & inputOracle);

void Render ();

void Update ( float time) ;

void ShutDown () {}

public:

State() : Glib::State ("State") {}

private:

// Determinação do estado inicial do OpenGL void SetUpRenderingContext();

// Preparar câmera para uso void SetUpCamera ();

// Preparar o action map da aplicação void SetActionFormat (Glib::InputOracle & inputOracle);

// Preparar o cenário void SetUpCage();

// Preparar o objeto móvel void SetUpMovableObject();

// Atualizar a câmera void UpdateCamera ( float time);

// Atualizar o objeto móvel void UpdateMovableObject ( float time);

private:

// Guarda o ponto para onde a câmera apontará Glib::Math::Vector3D _camWhereToLook;

// Representa uma câmera Glib::Ogl::Camera _camera;

// Representa um objeto móvel Glib::MovableObject _movableObject;

// Representa o interpretador de dados de entrada InputHandler _inputHandler;

// Representa o cenário (o cubo) Cage _cage;

};

4.6.2. State.cpp//---------------// INCLUDES//---------------#include <windows.h>

48

#include <GL/gl.h>

#include <Glib/GlibUtil.h>#include <Glib/GlibVector3D.h>

#include "State.h"#include "ActionMap.h"

//---------------// CLASSES//---------------

/** * Inicialização do estado. */void State::Init (Glib::ResourceOracle & resourceOracle, Glib::InputOracle & inputOracle){ SetActionFormat (inputOracle); SetUpCage(); SetUpCamera(); SetUpMovableObject(); SetUpRenderingContext();}

/** * Evento executado quando a janela da aplicação é redimensionada. */void State::WindowResized ( int width, int height){ // atualizar a viewport ::glViewport (0, 0, width, height);

// atualizar a projeção definida pela câmera _camera.UpdateProjection (width, height);}

/** * Renderização da cena. */void State::Render (){ ::glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

::glLoadIdentity();

// ajustar câmera _camera.LookAt (_camWhereToLook);

// desenhar os objetos da cena _cage.Render(); _movableObject.Render();

}

/** * Atualização de todos os objetos da cena. */void State::Update ( float time){ UpdateCamera (time); UpdateMovableObject (time);}

49

/** * Leitura de dados dos dispositivos de entrada. */void State::GetInput (Glib::InputOracle & inputOracle){ // recuperação de dados de entrada inputOracle.GetInput (_inputHandler);

// Interpretação if (_inputHandler.wantsToConfigDevices) { inputOracle.ConfigureDevices (); _inputHandler.wantsToConfigDevices = false; return; }

if (_inputHandler.wantsToQuitGame) { inputOracle.TerminateApplication(); return; }

}

/** * Preparar a câmera para uso. */void State::SetUpCamera(){ // A câmera é colocada na parte da frente do cubo, e olha inicialmente // diretamente para a parte de trás.

_camera.fov = 70.0f; _camera.nearPlane = 1.0f; _camera.farPlane = 100.0f;

_camera.position.x = 0.0f; _camera.position.y = 0.0f; _camera.position.z = _cage.GetFront();

_camWhereToLook.z = _cage.GetBack();

}

/** * Informar qual mapa de ações usar e requisitar ao sistema que ache todos * os dispositivos que sejam adequados a este mapa. */void State::SetActionFormat (Glib::InputOracle & inputOracle){ inputOracle.SetCurrentActionMap (g_actionMap); inputOracle.DistributeDevices ();}

/** * Preparar o objeto móvel para uso. */void State::SetUpMovableObject(){ // o objeto é posicionado na origem. _movableObject.SetPosition (0,0,0);

50

// o objeto possui velocidade inicial aleatória. _movableObject.SetVelocity (Glib::Math::RandomVector3D (-20.0f, 20.0f) );

// o objeto possui um modelo que o representa. const Glib::Ogl::ModelSharedPtr model ( new Glib::Ogl::Model ("data\\model.3ds") );

_movableObject.SetModel (model);

}

/** * Preparar o cenário para uso. */void State::SetUpCage(){ // o cenário possui um modelo que o representa. const Glib::Ogl::ModelSharedPtr model ( new Glib::Ogl::Model ("data\\cage.3ds") );

_cage.SetModel (model);

}

/** * Preparar o OpenGL para uso. */void State::SetUpRenderingContext(){ ::glClearColor (0.0f, 0.0f, 0.0f, 1.0f);

::glEnable (GL_DEPTH_TEST); ::glEnable (GL_COLOR_MATERIAL); ::glEnable (GL_TEXTURE_2D); ::glEnable (GL_CULL_FACE);

::glShadeModel (GL_SMOOTH); ::glFrontFace (GL_CCW); ::glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

::glMatrixMode (GL_MODELVIEW); ::glLoadIdentity();

}

/** * Atualizar a orientação da câmera de acordo com o comando do usuário. */void State::UpdateCamera ( float time){ const float cameraVelocity = 100.0f;

if (_inputHandler.goRight) { _camWhereToLook.x += cameraVelocity * time;

if (_camWhereToLook.x > _cage.GetRight() )

51

_camWhereToLook.x = _cage.GetRight();

}

if (_inputHandler.goLeft) { _camWhereToLook.x -= cameraVelocity * time;

if (_camWhereToLook.x < _cage.GetLeft() ) _camWhereToLook.x = _cage.GetLeft();

}

if (_inputHandler.goUp) { _camWhereToLook.y += cameraVelocity * time;

if (_camWhereToLook.y > _cage.GetTop() ) _camWhereToLook.y = _cage.GetTop();

}

if (_inputHandler.goDown) { _camWhereToLook.y -= cameraVelocity * time;

if (_camWhereToLook.y < _cage.GetBottom() ) _camWhereToLook.y = _cage.GetBottom();

}

}

/** * Atualizar o objeto móvel. */void State::UpdateMovableObject ( float time){ // Movimenta-se o objeto

_movableObject.Update (time);

// Verifica-se se houve colisão com alguma parede do cubo; caso exista // alguma colisão, a velocidade correspondente é invertida

const float boundsTop = _cage.GetTop() - _movableObject.GetBoundingBox().ext.y;

const float boundsBottom = _cage.GetBottom() + _movableObject.GetBoundingBox().ext.y;

const float boundsLeft = _cage.GetLeft() + _movableObject.GetBoundingBox().ext.x;

const float boundsRight = _cage.GetRight() - _movableObject.GetBoundingBox().ext.x;

const float boundsFront = _cage.GetFront() - _movableObject.GetBoundingBox().ext.z;

const float boundsBack = _cage.GetBack() + _movableObject.GetBoundingBox().ext.z;

// eixo X

52

if (_movableObject.GetX() <= boundsLeft) { _movableObject.SetX (boundsLeft); _movableObject.InvertVx (); }

if (_movableObject.GetX() >= boundsRight) { _movableObject.SetX (boundsRight); _movableObject.InvertVx (); }

// eixo Y if (_movableObject.GetY() <= boundsBottom) { _movableObject.SetY (boundsBottom); _movableObject.InvertVy (); }

if (_movableObject.GetY() >= boundsTop) { _movableObject.SetY (boundsTop); _movableObject.InvertVy (); }

// eixo Z if (_movableObject.GetZ() <= boundsBack) { _movableObject.SetZ (boundsBack); _movableObject.InvertVz (); }

if (_movableObject.GetZ() >= boundsFront) { _movableObject.SetZ (boundsFront); _movableObject.InvertVz (); }

}

53

5. Resultados Experimentais

Neste capítulo é apresentado o resultado da execução de um programa de demonstração queutili za a Máquina de Jogos definida anteriormente. Entre os tópicos abordados, encontram-se:

� Definição de métricas de desempenho;� Descrição do ambiente de execução da aplicação;� Descrição das atividades realizadas pela aplicação.� Descrição dos resultados obtidos.

5.1. Métricas de Desempenho

Para medição de desempenho, é utili zada a métrica de quadros (frames) por segundo (FPS). Umquadro corresponde a um ciclo completo de execução.

Se a aplicação for executada em modo janela, a quantidade de FPS atual é exibida na barra detítulo da janela.

Ao final da execução da aplicação é exibida a média de FPS da sessão, seja em modojanela outela cheia.

5.2. Ambiente de Execução

A máquina usada na avaliação do desempenho possui as seguintes características:

� Processador Intel Pentium 3 600E, com relógio de 600 Mhz, barramento externo de 100Mhz e 256 Kilobytes de memória cache principal acessada à mesma frequência doprocessador.

� Placa de vídeo Diamond Viper V770 (chipset Nvidia Riva TNT2) com 32 Megabytes dememória. É utili zado o driver de vídeo (Detonator) disponibili zado por Nvidia Corporation,versão 6.31. Esta placade vídeo utili za o barramento AGP, com velocidade ajustada para2X;

� 256 Megabytes de memória RAM;� Sistema Operacional Windows Millenium;� Teclado e mouse padrões;� Controles de jogo (joysticks) Microsoft SideWinder GamePad, Microsoft SideWinder

GamePad Pro (conexão USB) e Microsoft SideWinder Precision Racing Wheel (conexãoUSB).

� Placa de som Creative Labs SoundBlaster Live! (ligada ao barramento PCI).

5.3. Propriedades da Simulação

O programa de demonstração consiste em uma simulação de um jogo em que o usuário controlaum objeto (aeronave), através de um terreno definido por um vale. O usuário possui um determinadonúmero de projéteis, que podem ser utili zados contra os outros objetos que estão no cenário(inimigos). Os inimigos também possuem um determinado número de projéteis, que podem serutili zados contra o usuário.

54

5.3.1. Orientação da Cena

A cena simulada é totalmente tridimensional, e utili za o eixo Z para profundidade e o eixo Ypara representar as alturas.

5.3.2. Efeitos Especiais

São utili zados os seguintes efeitos especiais:

� Iluminação em tempo real;� Alpha Blending;� Fog.

Uma observação pertinente: É possível perceber, na figura 5.1, uma pequena descontinuidadeno horizonte, abaixo do nível da água. Isto ocorre porque todos os objetos que estão além do planodelimitador distante (far clipping plane) não são desenhados. Se efeito de transparência da água(alpha blending) for desligado, a descontinuidade não será notada.

Figura 5.1: Visualização da cena.

55

5.3.3. Cenário

O cenário consiste em um terreno,um domo para representar o céu e a água. O terreno é criadoa partir de um modelo do 3D Studio. Para simular um terreno infinito, o modelo do terreno éduplicado e as instâncias são desenhadas em sequência, de modo que a áreavisível seja coberta. Oterreno é desenhado com uma textura.

O domo também é criado a partir de um modelo do 3D Studio (possui uma textura) e gira comvelocidade fixa em torno do eixo Y.

A água é simulada utili zando-se dois quadrados simples que são desenhado em uma altura pré-determinada. A água possui um efeito de animação que é implementado alternando-se entre 32texturas. É utili zado o alpha blending na renderização da água.

5.3.4. Câmeras

Estão disponíveis três câmeras. Elas são posicionadas como a seguir:

� Atrás do jogador, na mesma altura deste;� Ligeiramente atrás do jogador, a uma altura um pouco maior que a dele;� Em primeira pessoa.

Figura 5.2: Cena com todos os efeitos gráficos aplicados.

56

5.3.5. Projéteis

Os projéteis, utili zados tanto pelo jogador quanto pelos inimigos, são representados pormodelos do 3D Studio (sem textura) Todos eles possuem um tempo de vida pré-determinado. Umprojetil é desativado (podendo assim ser reutili zado) se o seu tempo de vida acabar ou caso ocorrauma colisão entre ele e outro objeto, o que acontecer primeiro. Projéteis não se chocam contra outrosprojéteis.

É interessante ressaltar que todos os objetos que representam projéteis são alocados uma vezapenas pelos objetos que os detêm.

5.3.6. Jogador

O jogador é representado por um modelo tridimensional (3D Studio, com uma textura) e possuios seguintes comandos:

� Mover-se para a direita ou esquerda (eixo X);� Aumentar ou diminuir a sua velocidade em relação ao terreno;� Atirar;� Alterar a câmera atual;� Alternar entre modo de desenho de polígonos wireframe e sólido;� Ligar ou desligar a iluminação em tempo real;� Ligar ou desligar o efeito de fog;� Ligar ou desligar o efeito de alpha blending;� Ligar ou desligar efeitos sonoros;� Configurar os dispositi vos de entrada;� Pausar a execução da aplicação;� Encerrar a aplicação.

O jogador possui um número limitado de projéteis (sete), que podem ser usados para abater osinimigos. Caso o jogador esgote o seu estoque de projéteis, ele não poderá atirar até que algumprojetil seja desativado.

5.3.7. Inimigos

Existe um número fixo de inimigos, que é definido a priori (quinze). Estes objetos semovimentam no cenário com velocidade fixa no eixo Z. A velocidade no eixo X é determinada nomomento da ativação do objeto. Um inimigo é desativado se este sair da área visível da cena, foratingido por algum projetil ou chocar-se com o jogador. Um objeto desativado é disponibili zado parauso imediatamente, não ocorrendo realocação de recursos.

Os inimigos possuem um número limitado de projéteis (cinco), com as mesmas regras definidaspara o jogador. Eles atiram aleatoriamente, sendo que a probabili dade de atirar cresce à medida que osinimigos se aproximam do jogador.

Os inimigos são representados por modelos tridimensionais (3D Studio) e utili zam uma texturaapenas.

5.3.8. Efeitos Sonoros

Os efeitos sonoros são utili zados quando algum dos eventos ocorre:

57

� Um inimigo atira;� O jogador atira;� O jogador atira, mas não possui projéteis disponíveis;� Um inimigo é atingido por algum projetil;� O jogador é atingido por algum projetil;� O jogador e algum inimigo se chocam.

Os efeitos sonoros são representados por arquivos de áudio em formato de onda do Windows(.wav).

5.4. Dados Experimentais

É possível estimar a quantidade de triângulos desenhados a cada ciclo considerando-se onúmero máximo de objetos que podem estar presentes na cena ao mesmo tempo. A quantidade detriângulos por tipo de objeto é descrita a seguir:30

� Domo: 220;� Terreno: 522 (duas instâncias);� Inimigos: 1260 (quinze instâncias);� Jogador: 156;� Projéteis do jogador: 84 (sete instâncias);� Projéteis dos inimigos: 900 (setenta e cinco instâncias);� Dois quadrados para representar a água: 4

Têm-se, então, um limite superior de 3146 triângulos desenhados a cada ciclo. Todos osmodelos são desenhados utili zando-se triângulos (GL_TRIANGLE).

5.5. Resultados Obtidos

Para a avaliação do desempenho, foram utili zadas duas configurações para a aplicação:

� Modo janela, com janela de dimensões 640x480,desktop com resolução 1152x864,32 bitsde profundidade de cores, taxa de atualização vertical do monitor de 75 Hz, Z-Buffer de 16bits e modo double buffering. São utili zados todos os efeitos gráficos (fog, iluminação ealpha blending) e efeitos sonoros. Sincronismo com a taxa de atualização vertical domonitor habilit ado.

� Modo tela cheia, com resolução de 800x600 com 16 bits de profundidade de cores.Utili zação de Z-Buffer de 16 bits e modo double buffering. Uso de todos os efeitos gráficos eefeitos sonoros. Sincronismo com a taxa de atualização vertical do monitor habilit ado.

30 Os dados podem ser obtidos utili zando-se programas de modelagem tridimensional. No caso, foi utili zado oOpenFX.

58

Após a execução da aplicação, foi obtida uma média de 74.8FPS utili zando-se o modojanela e98.4 FPS utili zando-se o modo em tela cheia. A média é calculada pela Máquina de Jogos e aaplicação apenas exibe o valor calculado, no seu encerramento.

Com o sincronismo com a taxa de atualização vertical desligado, foram obtidas médias de 84.1FPS para o modo janela e 151.4 FPS para o modo em tela cheia.

5.6. Implementação do Estado

Segue aqui a implementação do Estado que é utili zado na aplicação.

5.6.1. State.h//---------------------------------------------------------------------------#ifndef StateH#define StateH//---------------------------------------------------------------------------

//---------------// INCLUDES//---------------#include <memory>#include <Glib/GlibState.h>#include <Glib/OglCamera.h>#include <Glib/GlibMovableObject.h>#include <Glib/GlibSoundBuffer.h>

#include <boost/scoped_array.hpp>

#include "InputHandler.h"#include "Player.h"#include "Enemy.h"#include "WorkVolume.h"#include "SkyDome.h"#include "Bullet.h"#include "Terrain.h"#include "Water.h"#include "Sounds.h"#include "AppConst.h"#include "Patch.h"

//---------------// CLASSES//---------------

class State : public Glib::State{ public:

void Init (Glib::ResourceOracle & resourceOracle, Glib::InputOracle & inputOracle);

void WindowResized (int width, int height);

void GetInput (Glib::InputOracle & inputOracle);

void Render ();

void Update (float time) ;

void ShutDown () {}

59

public:

State() : Glib::State ("State"), _currentCamera (0) {}

private:

void SetUpActionMap (Glib::InputOracle & inputOracle); void SetUpSoundEffects (Glib::ResourceOracle & resourceOracle); void SetUpWorkVolume (); void SetUpPlayer (); void SetUpPlayerBullets (); void SetUpEnemies (); void SetUpEnemiesBullets (); void SetUpSkyDome (); void SetUpCameras (); void SetUpLights (); void SetUpTerrain (); void SetUpWater (); void SetUpRenderingContext (); void SetUpPatch ();

void UpdatePlayer ( float time); void UpdateEnemies ( float time); void UpdateBullets ( float time);

void UpdateSkyDome ( float time); void UpdateTerrain ( float time); void UpdateWater ( float time);

void SpawnNewEnemy ( float time); void UpdateCamera (); void ChangeCamera ();

void CheckCollisionsBulletsVsEnemies (); void CheckCollisionsPlayerVsEnemies (); void CheckCollisionsBulletsVsPlayer ();

void RenderEnemies (); void RenderBullets (); void RenderSkyDome (); void RenderTerrain (); void RenderWater (); void RenderPatch (); void RenderPlayer ();

void PlaySounds ();

private:

// Representa o espaço de atuação dos objetos móveis da aplicação WorkVolume _workVolume;

// Representa o interpretador de dados InputHandler _inputHandler;

// Representa o jogador Player _player;

// Array que contém todos os inimigos boost::scoped_array <Enemy> _enemies;

// Total de inimigos

60

int _numEnemies;

// Lista de todos os projéteis ativos usados pelo jogador BulletPtrList _playerBulletList;

// Lista de todos os projéteis ativos usados pelos inimigos BulletPtrList _enemyBulletList;

// Câmeras usadas na aplicação Glib::Ogl::Camera _cameras [CAMERAS_TOTAL];

// Índice da câmera usada no momento unsigned int _currentCamera;

// Indica o ponto para onde a câmera atual aponta Glib::Math::Vector3D _camWhereToLook;

// Objeto que representa o céu SkyDome _skyDome;

// Objeto que representa o terreno TerrainAutoPtr _terrain;

// Objeto que representa a água WaterAutoPtr _water;

// Atuenuação para efeitos causados no horizonte por causa do uso de // transparência na água Patch _patch;

// Container de efeitos sonoros Glib::Sound::BufferAutoPtr _soundEffects;

// Indica os eventos relacionados a efeitos sonoros que foram acionados SoundTask _soundTask;

};

//---------------// INLINES//---------------

inline void State::RenderSkyDome (){ _skyDome.Render ();}

inline void State::RenderTerrain (){ _terrain->Render();}

inline void State::RenderPatch (){ _patch.Render ();}

inline void State::RenderPlayer (){ _player.Render();}

inline void State::RenderWater (){ _water->Render ();}

61

inline void State::UpdateSkyDome (float time){ _skyDome.Update (time);}

inline void State::UpdateTerrain (float time){ _terrain->Update (time);}

inline void State::UpdateWater (float time){ _water->Update (time);}

inline void State::ChangeCamera (){ if (++_currentCamera >= CAMERAS_TOTAL) _currentCamera = 0;

switch (_currentCamera) { case CAM0_ID: _camWhereToLook.y = _cameras [CAM0_ID].position.y;

break;

case CAM1_ID: _camWhereToLook.y = _cameras [CAM1_ID].position.y;

break;

case CAM2_ID: _camWhereToLook.y = _cameras [CAM2_ID].position.y;

break;

};

}

//---------------------------------------------------------------------------#endif

5.6.2. State.cpp//---------------// INCLUDES//---------------#include <windows.h>#include <GL/gl.h>

#include "State.h"#include "ActionMap.h"

//---------------// CLASSES//---------------

/** * */void State::Init (Glib::ResourceOracle & resourceOracle,

62

Glib::InputOracle & inputOracle){

SetUpActionMap (inputOracle); SetUpWorkVolume (); SetUpSoundEffects (resourceOracle);

SetUpPlayer (); SetUpPlayerBullets (); SetUpEnemies (); SetUpEnemiesBullets ();

SetUpTerrain (); SetUpSkyDome (); SetUpWater ();

SetUpPatch (); SetUpRenderingContext (); SetUpCameras (); SetUpLights ();}

/** * */void State::WindowResized (int width, int height){ ::glViewport (0, 0, width, height);

_cameras [_currentCamera].UpdateProjection (width, height);}

/** * */void State::Render (){ ::glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

::glLoadIdentity();

_cameras [_currentCamera].LookAt (_camWhereToLook);

RenderPlayer (); RenderEnemies (); RenderBullets ();

RenderPatch (); RenderSkyDome (); RenderTerrain (); RenderWater ();}

/** * */void State::Update (float time){ UpdateSkyDome (time); UpdateTerrain (time); UpdateWater (time); UpdatePlayer (time);

SpawnNewEnemy (time);

63

UpdateEnemies (time); UpdateBullets (time);

UpdateCamera ();

CheckCollisionsBulletsVsEnemies (); CheckCollisionsPlayerVsEnemies (); CheckCollisionsBulletsVsPlayer ();

PlaySounds ();}

/** * */void State::GetInput (Glib::InputOracle & inputOracle){ static bool useFog = true; static bool useAlpha = true; static bool useLighting = true; static bool useWireframe = false; static float fogDensity = FOG_DENSITY; static float fogAmount = 0.000050f;

inputOracle.GetInput (_inputHandler);

if (_inputHandler.wantsToConfigDevices) { inputOracle.ConfigureDevices (); _inputHandler.wantsToConfigDevices = false; return; }

if (_inputHandler.wantsToQuitGame) } inputOracle.TerminateApplication(); return; }

if (_inputHandler.wantsToPause) { _inputHandler.wantsToPause = false;

if (inputOracle.IsSimulationPaused () ) inputOracle.ResumeSimulation (); else { inputOracle.PauseSimulation(); return; } }

if (_inputHandler.wantsToShoot) { Gun::Status status = _player.Shoot (_playerBulletList);

if (status == Gun::GUN_OK ) _soundTask.playerShoot = true; else if (status == Gun::GUN_EMPTY) _soundTask.playerEmptyGun = true;

}

64

if (_inputHandler.wantsToChangeCam) { _inputHandler.wantsToChangeCam = false; ChangeCamera(); }

if (_inputHandler.goLeft) _player.GoLeft(); else if (_inputHandler.goRight) _player.GoRight(); else _player.Stop();

if (_inputHandler.goUp) { _terrain->UseFastVelocity(); _water->UseFastVelocity(); } else if (_inputHandler.goDown) { _terrain->UseSlowVelocity(); _water->UseSlowVelocity(); } else { _terrain->UseNormalVelocity(); _water->UseNormalVelocity(); }

if (_inputHandler.wantsToToggleFog) { _inputHandler.wantsToToggleFog = false;

if (useFog) { useFog = false; ::glDisable (GL_FOG); } else { useFog = true; ::glEnable (GL_FOG); }

}

if (_inputHandler.wantsToToggleAlphaBlend) { _inputHandler.wantsToToggleAlphaBlend = false;

if (useAlpha) { useAlpha = false; _water->DisableBlend(); } else { useAlpha = true; _water->EnableBlend(); }

}

65

if (_inputHandler.wantsToToggleLighting) { _inputHandler.wantsToToggleLighting = false;

if (useLighting) { useLighting = false; ::glDisable (GL_LIGHTING); } else { useLighting = true; ::glEnable (GL_LIGHTING); } }

if (_inputHandler.wantsToToggleWireframe) { _inputHandler.wantsToToggleWireframe = false;

if (useWireframe) { useWireframe = false; ::glPolygonMode (GL_FRONT, GL_FILL); } else { useWireframe = true; ::glPolygonMode (GL_FRONT, GL_LINE); }

}

if (_inputHandler.wantsToIncreaseFog) { ::glPushAttrib (GL_ENABLE_BIT);

::glDisable (GL_FOG);

fogDensity += fogAmount; ::glFogf (GL_FOG_DENSITY, fogDensity);

::glPopAttrib(); }

if (_inputHandler.wantsToDecreaseFog) { ::glPushAttrib (GL_ENABLE_BIT);

::glDisable (GL_FOG); fogDensity -= fogAmount; ::glFogf (GL_FOG_DENSITY, fogDensity);

::glPopAttrib(); }

}

//--------------////--------------

66

/** * Atualizar a posição e ponto de referência da câmera atual. */void State::UpdateCamera (){

switch (_currentCamera) { case CAM0_ID: _cameras [CAM0_ID].position.x = _player.GetX(); _camWhereToLook.x = _player.GetX();

break;

case CAM1_ID: _cameras [CAM1_ID].position.x = _player.GetX(); _camWhereToLook.x = _player.GetX();

break;

case CAM2_ID: _cameras [CAM2_ID].position = _player.GetPosition(); _camWhereToLook.x = _player.GetX(); break;

};}

/** * Atualizar projéteis do jogador e dos inimigos. */void State::UpdateBullets ( float time){ const float boundsBack = _workVolume.GetBack(); const float boundsFront = _workVolume.GetFront(); const float boundsLeft = _workVolume.GetLeft(); const float boundsRight = _workVolume.GetRight();

for (BulletPtrList::iterator i = _playerBulletList.begin(); i != _playerBulletList.end(); ++i) {

Bullet * p = *i;

if (p->IsActive () ) { p->Update (time);

if (p->GetZ() < boundsBack) { p->Deactivate();

_playerBulletList.erase (i); --i; }

} else { _playerBulletList.erase (i); --i; }

}

for (BulletPtrList::iterator e = _enemyBulletList.begin(); e != _enemyBulletList.end(); ++e)

67

{ Bullet * p = *e;

if (p->IsActive() ) { p->Update (time);

if (p->GetZ() > boundsFront || p->GetX() < boundsLeft || p->GetX() > boundsRight) { p->Deactivate(); _enemyBulletList.erase (e); --e; }

} else { _enemyBulletList.erase (e); --e; }

}

}

/** * Atualizar o jogador. */void State::UpdatePlayer (float time){ _player.Update (time);

const float boundsLeft = _workVolume.GetLeft() + _player.GetBoundingBox().ext.x;

const float boundsRight = _workVolume.GetRight() - _player.GetBoundingBox().ext.x;

if (_player.GetX() >= boundsRight) _player.SetX (boundsRight);

if (_player.GetX() <= boundsLeft)_player.SetX (boundsLeft);

}

/** * Atualizar todos os inimigos. */void State::UpdateEnemies (float time){

static const float boundsLeft = _workVolume.GetLeft() + _enemies [0].GetBoundingBox().ext.x;

static const float boundsRight = _workVolume.GetRight() - _enemies [0].GetBoundingBox().ext.x;

static const float inZ = _workVolume.GetBack() + _enemies [0].GetBoundingBox().ext.z;

static const float outZ = _workVolume.GetFront() + _enemies [0].GetBoundingBox().ext.z;

68

for ( int i = 0; i < _numEnemies; ++i) { if (_enemies [i].IsActive () == false) continue;

_enemies [i].Update (time);

/* X */

if (_enemies [i].GetX() >= boundsRight) { _enemies [i].SetX (boundsRight); _enemies [i].InvertVx (); }

if (_enemies [i].GetX() <= boundsLeft) { _enemies [i].SetX (boundsLeft); _enemies [i].InvertVx (); }

/* Z */

if (_enemies [i].GetZ() >= outZ) { _enemies [i].Deactivate (); }

/* Ações */

if (_enemies [i].IsActive() ) { if (_enemies [i].Shoot (_enemyBulletList, _player.GetPosition() ) == Gun::GUN_OK) _soundTask.enemyShoot = true; }

}

}

/** * Criar um novo objeto inimigo. */void State::SpawnNewEnemy ( float time){ static const float sampleVz = _enemies [0].GetVz(); static const float sampleExtZ = _enemies [0].GetBoundingBox().ext.z;

static const float threshold = _workVolume.GetSizeZ() / (ENEMY_MAX_INSTANCES_AT_ONCE * sampleVz);

static const float inZ = _workVolume.GetBack() + sampleExtZ;

static float timeCounter = threshold;

// verificamos se o tempo necessário entre duas aparições já passou timeCounter += time;

69

if (timeCounter >= threshold) { for ( int i = 0; i < _numEnemies; ++i) {

if (_enemies [i].IsActive () == false) { _enemies [i].Activate();

_enemies [i].SetX (Glib::Util::RandomFloat ( _workVolume.GetLeft(), _workVolume.GetRight() ) );

_enemies [i].SetZ (inZ);

_enemies [i].SetVx ( Glib::Util::RandomFloat ( ENEMY_MIN_VELOCITY_X, ENEMY_MAX_VELOCITY_X) );

timeCounter = 0.0f; break; } }

}

}

/** * Checar a colisão entre projéteis do jogador contra os inimigos */void State::CheckCollisionsBulletsVsEnemies (){ for (BulletPtrList::iterator bi = _playerBulletList.begin(); bi != _playerBulletList.end(); ++bi) { if ( (*bi)->IsActive() == false) continue;

for ( int i = 0; i < _numEnemies; ++i) { if (_enemies [i].IsActive() == false) continue;

if (_enemies [i].TestCollision ( *(*bi) ) == true) { _soundTask.explode = true;

_enemies [i].Deactivate ();

(*bi)->Deactivate();

_playerBulletList.erase (bi);

--bi;

break; }

}

}

}

70

/** * Checar a colisão entre do jogador e os inimigos */void State::CheckCollisionsPlayerVsEnemies (){ for ( int i = 0; i < _numEnemies; ++i) { if (_enemies [i].IsActive () == false) continue;

if (_enemies [i].TestCollision (_player) == true) { _soundTask.playerHit = true;

_enemies [i].Deactivate(); }

}

}

/** * Checar a colisão entre projéteis inimigos e o jogador. */void State::CheckCollisionsBulletsVsPlayer (){ for (BulletPtrList::iterator bi = _enemyBulletList.begin(); bi != _enemyBulletList.end(); ++bi) { if ( (*bi)->IsActive () == false) continue;

if (_player.TestCollision (*(*bi) ) == true) { _soundTask.playerHit = true;

(*bi)->Deactivate();

_enemyBulletList.erase (bi);

--bi; }

}

}

/** * Desenhar os inimigos. */void State::RenderEnemies (){ for ( int i = 0; i < _numEnemies; ++i)

{ if (_enemies [i].IsActive() ) _enemies [i].Render ();

}

}

71

/** * Desenhar todos os projéteis. */void State::RenderBullets (){ ::glPushAttrib (GL_ENABLE_BIT);

::glDisable (GL_LIGHTING);

for (BulletPtrList::iterator i = _playerBulletList.begin(); i != _playerBulletList.end(); ++i)

{ if ( (*i)->IsActive ()) (*i)->Render ();

}

for (BulletPtrList::iterator e = _enemyBulletList.begin(); e != _enemyBulletList.end(); ++e)

{ if ( (*e)->IsActive ()) (*e)->Render ();

}

::glPopAttrib ();

}

/** * Reproduzir todos os efeitos sonoros. */void State::PlaySounds(){

static const DWORD flags = DMUS_SEGF_SECONDARY;

if (_soundTask.explode){

_soundTask.explode = false;

if (! _soundEffects->IsPlaying (SFX_EXPLODE) ) _soundEffects->Play (SFX_EXPLODE, flags);

}

if (_soundTask.playerShoot){

_soundTask.playerShoot = false;

if (! _soundEffects->IsPlaying (SFX_PLAYER_SHOOT) ) _soundEffects->Play (SFX_PLAYER_SHOOT, flags);

}

if (_soundTask.playerEmptyGun){

_soundTask.playerEmptyGun = false;

if (! _soundEffects->IsPlaying (SFX_PLAYER_EMPTY_GUN) ) _soundEffects->Play (SFX_PLAYER_EMPTY_GUN, flags);

}

if (_soundTask.playerHit)

72

{ _soundTask.playerHit = false;

if (! _soundEffects->IsPlaying (SFX_PLAYER_HIT) ) _soundEffects->Play (SFX_PLAYER_HIT, flags);

}

if (_soundTask.enemyShoot){

_soundTask.enemyShoot = false;

if (! _soundEffects->IsPlaying (SFX_ENEMY_SHOOT) ) _soundEffects->Play (SFX_ENEMY_SHOOT, flags);

}

}

//---------------// //---------------

/** * Preparar o mapa de ações para uso. */void State:: SetUpActionMap (Glib::InputOracle & inputOracle){

inputOracle.SetCurrentActionMap (g_actionMap);inputOracle.DistributeDevices ();

}

/** * Preparar a área de atuação dos objetos móveis para uso. */void State::SetUpWorkVolume (){

_workVolume.ext = WORKVOLUME_EXTENTS;_workVolume.pos = WORKVOLUME_POSITION;

}

/** * Carregar os efeitos sonoros. */void State::SetUpSoundEffects (Glib::ResourceOracle & resourceOracle){

_soundEffects = resourceOracle.LoadSoundBuffer (OBJECTS_SFX_NAMES);}

/** * Preparar o objeto que representa o jogador, para uso. */void State::SetUpPlayer (){

const Glib::Ogl::ModelSharedPtr model ( new Glib::Ogl::Model (PLAYER_MODEL_NAME) );

_player.SetVelocity (PLAYER_VELOCITY);_player.SetPosition (PLAYER_POSITION);_player.SetShootRate (PLAYER_SHOOT_RATE);_player.SetModel (model);

}

73

/** * Preparar o projéteis do jogador, para uso. */void State:: SetUpPlayerBullets (){

const Glib::Ogl::ModelSharedPtr bulletModel ( new Glib::Ogl::Model (PLAYER_BULLET_MODEL_NAME) );

Bullet bullet;

bullet.SetTimeToLive (PLAYER_BULLET_TIMETOLIVE);bullet.SetAcceleration (PLAYER_BULLET_ACCELERATION);bullet.SetVelocity (PLAYER_BULLET_VELOCITY);bullet.SetModel (bulletModel);

_player.SetBulletObj (bullet, PLAYER_NUM_BULLETS);}

/** * Preparar os inimigos para uso */void State::SetUpEnemies (){

Glib::Ogl::Texture::Parameters enemyTexParams (GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR, GL_REPEAT, GL_REPEAT);

const Glib::Ogl::ModelSharedPtr model ( new Glib::Ogl::Model (ENEMY_MODEL_NAME, enemyTexParams) );

_numEnemies = ENEMY_MAX_INSTANCES_AT_ONCE;

_enemies.reset ( new Enemy [_numEnemies]);

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

_enemies [i].SetModel (model); _enemies [i].SetShootRate (ENEMY_SHOOT_RATE); _enemies [i].SetVz (ENEMY_VELOCITY_Z); _enemies [i].SetY (_player.GetY() ); _enemies [i].SetWorkVolume (_workVolume); _enemies [i].Deactivate ()

}

}

/** * Preparar os projéteis dos inimigos para uso. */void State::SetUpEnemiesBullets (){

const Glib::Ogl::ModelSharedPtr bulletModel ( new Glib::Ogl::Model (ENEMY_BULLET_MODEL_NAME) );

Bullet bullet;

bullet.SetTimeToLive (ENEMY_BULLET_TIMETOLIVE);bullet.SetAcceleration (ENEMY_BULLET_ACCELERATION);bullet.SetVelocity (ENEMY_BULLET_VELOCITY);bullet.SetModel (bulletModel);

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

74

{ _enemies [j].SetBulletObj (bullet, ENEMY_NUM_BULLETS);

}}

/** * Preparar o domo que representa o céu para uso. */void State::SetUpSkyDome (){ const Glib::Ogl::ModelSharedPtr model ( new Glib::Ogl::Model (SKYDOME_MODEL_NAME) );

_skyDome.SetModel (model);_skyDome.SetAngularVelocity (SKYDOME_VELOCITY);

}

/** * Preparar as câmeras para uso. */void State::SetUpCameras (){ _currentCamera = 0;

for ( unsigned int c = 0; c < CAMERAS_TOTAL; ++c){

_cameras [c].fov = CAMX_FOV;_cameras [c].nearPlane = CAMX_NEAR_PLANE;_cameras [c].farPlane = CAMX_FAR_PLANE;

}

_cameras [CAM0_ID].position = CAM0_POSITION; _cameras [CAM1_ID].position = CAM1_POSITION;

// câmera 2 é em primeira pessoa}

/** * Determinar o modelo de iluminação da aplicação. */void State::SetUpLights (){

float lightPosition [] = {0.0f, _skyDome.GetRadius(), _skyDome.GetRadius(), 1.0f};

::glLightfv (GL_LIGHT0, GL_POSITION, lightPosition);::glLightfv (GL_LIGHT0, GL_DIFFUSE, LIGHT_AMBIENT);::glLightfv (GL_LIGHT0, GL_DIFFUSE, LIGHT_DIFFUSE);

::glEnable (GL_LIGHT0);}

/** * Preparar o terreno para uso. */void State::SetUpTerrain (){

Glib::Ogl::Texture::Parameters terrainTexParams (GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,

75

GL_REPEAT, GL_REPEAT);

_terrain.reset ( new Terrain (TERRAIN_MODEL_NAME, terrainTexParams , _workVolume) );

_terrain->SetVelocity (TERRAIN_VELOCITY_Z_NORMAL, TERRAIN_VELOCITY_Z_FAST, TERRAIN_VELOCITY_Z_SLOW);}

/** * Preparar o objeto que representa a água para uso */void State::SetUpWater (){

WaterParameters waterParameters;

waterParameters.baseDirForTextures = WATER_BASE_DIR_TEXTURES;waterParameters.baseTextureName = WATER_BASE_TEXTURE_NAME;waterParameters.baseTextureNameExt = WATER_BASE_TEXTURE_NAME_EXT;

waterParameters.animFrameRate = WATER_ANIM_FPS;waterParameters.numTextures = WATER_NUM_TEXTURES;waterParameters.tile = WATER_TILE;waterParameters.textureParameters =

Glib::Ogl::Texture::Parameters (GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);

_water.reset ( new Water (*_terrain, waterParameters) );

_water->SetY (WATER_Y);_water->SetColor (WATER_COLOR);

_water->SetVelocity (WATER_VELOCITY_Z_NORMAL, WATER_VELOCITY_Z_FAST, WATER_VELOCITY_Z_SLOW);}

/** * Determinar a configuração do rendering context. */void State::SetUpRenderingContext (){

::glClearColor (0.0f, 0.0f, 0.0f, 1.0f);

::glEnable (GL_DEPTH_TEST);::glEnable (GL_COLOR_MATERIAL);::glEnable (GL_TEXTURE_2D);::glEnable (GL_CULL_FACE);::glEnable (GL_LIGHTING);::glEnable (GL_FOG);

::glShadeModel (GL_SMOOTH);::glFrontFace (GL_CCW);::glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE);::glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

::glFogi (GL_FOG_MODE, GL_EXP2);::glFogf (GL_FOG_DENSITY, FOG_DENSITY);::glFogfv (GL_FOG_COLOR, FOG_COLOR);

76

::glFogf (GL_FOG_START, _workVolume.GetFront() );::glFogf (GL_FOG_END, _workVolume.GetBack());

::glMatrixMode (GL_MODELVIEW); ::glLoadIdentity();}

/** * */void State::SetUpPatch (){ _patch.SetParameters (*_terrain, - _skyDome.GetRadius() );}

77

6. Conclusões

Jogos de computador são aplicações que empregam várias tecnologias diferentes, consideradasdo estado da arte, que estão em constante evolução. Como jogos são aplicações interativas em temporeal, a questão desempenho é crucial no desenvolvimento desses programas.

A Máquina de Jogos (Glib) apresentada ofereceferramentas básicas para a criação de jogos.Ela não foi escrita para um gênero de jogo específico; o desenvolvedor, ao usá-la, deve implementar afuncionalidade desejada. Ela possui soluções para os módulos Gráfico, Som, Dispositi vos de Entradae Física.

Em comparação com as outras Máquinas de Jogos estudadas, não possui ferramentas tãoavançadas para o Módulo Gráfico como as encontradas em OGRE e Fly3D. O nível de complexidadedo Módulo de Física é comparável ao encontrado em T3DLib. O Módulo de Som de Glib possui ainterface de nível mais alto entre as Máquinas de Jogos analisadas, embora só ofereça efeitos sonoros.

O Módulo de Dispositi vos de Entrada de Glib é o mais avançado entre as Máquinas de Jogosabordadas, pois oferece abstrações conhecidas como dispositi vos de entrada virtuais. Na prática, o usodestas abstrações simpli fica a leitura de dados e a gestão dos dispositi vos reais.

Entre outros aspectos a destacar, podem ser citados:

� O uso de gestão automatizada de recursos minimiza erros de programação (bugs)relacionados a recursos, além de contribuir para uma maior robustez da aplicação;

� As etapas de execução são bem definidas ao se utili zar o conceito de Estado,o que contribuipara reforçar o contrato da aplicação. Adicionalmente, a manutenção do programa éfacilit ada;

� A utili zação de objetos do tipo “Oráculo” em partes bem definidas da aplicação expõe aodesenvolvedor apenas o que é necessário em uma determinada etapa. A Máquina de Estadosnão precisa ser exposta diretamente à aplicação, o que contribui para a estabili dade deambas.

6.1. Trabalhos Futuros

Glib ainda é uma Máquina de Jogos básicae, em um futuro próximo, poderiam ser estudadas aspossibili dades dos seguintes itens serem acrescentados:

� Gerente de cenas: Uma alternativa para a gestão de grandes cenários;� Animação de modelos tridimensionais: Programas de modelagem tridimensional podem

criar modelos com animações (skeletal animation). Atualmente, somente modelos estáticospodem ser usados;

� Sistemas de partículas;� Músicas de fundo: Utili zação de músicas em formato MIDI ou MP3;� Métodos mais avançados de detecção e tratamento de colisões;� Abstração para a definição do mapa de ações para os dispositi vos de entrada: Atualmente, o

desenvolvedor precisa conhecer algumas estruturas de dados do DirectInput;� Criação de sessões de jogo atráves de algum tipo de rede.

78

7. Anexo A

Implementação completa do exemplo discutido no Capítulo 4.

7.1. State.h//---------------------------------------------------------------------------#ifndef StateH#define StateH//---------------------------------------------------------------------------

//---------------// INCLUDES//---------------#include <Glib/GlibState.h>#include <Glib/OglCamera.h>#include <Glib/GlibMovableObject.h>#include "InputHandler.h"#include "Cage.h"

//---------------// CLASSES//---------------

class State : public Glib::State{ public:

// métodos herdados da classe base void Init (Glib::ResourceOracle & resourceOracle, Glib::InputOracle & inputOracle);

void WindowResized ( int width, int height);

void GetInput (Glib::InputOracle & inputOracle);

void Render ();

void Update ( float time) ;

void ShutDown () {}

public:

State() : Glib::State ("State") {}

private:

// Determinação do estado inicial do OpenGL void SetUpRenderingContext();

// Preparar câmera para uso void SetUpCamera ();

// Preparar o action map da aplicação void SetActionFormat (Glib::InputOracle & inputOracle);

// Preparar o cenário void SetUpCage();

79

// Preparar o objeto móvel void SetUpMovableObject();

// Atualizar a câmera void UpdateCamera ( float time);

// Atualizar o objeto móvel void UpdateMovableObject ( float time);

private:

// Guarda o ponto para onde a câmera apontará Glib::Math::Vector3D _camWhereToLook;

// Representa uma câmera Glib::Ogl::Camera _camera;

// Representa um objeto móvel Glib::MovableObject _movableObject;

// Representa o interpretador de dados de entrada InputHandler _inputHandler;

// Representa o cenário (o cubo) Cage _cage;

};

//---------------------------------------------------------------------------#endif

7.2. State.cpp//---------------// INCLUDES//---------------#include <windows.h>#include <GL/gl.h>

#include <Glib/GlibUtil.h>#include <Glib/GlibVector3D.h>

#include "State.h"#include "ActionMap.h"

//---------------// CLASSES//---------------

/** * Inicialização do estado. */void State::Init (Glib::ResourceOracle & resourceOracle, Glib::InputOracle & inputOracle){ SetActionFormat (inputOracle); SetUpCage(); SetUpCamera(); SetUpMovableObject(); SetUpRenderingContext();}

80

/** * Evento executado quando a janela da aplicação é redimensionada. */void State::WindowResized ( int width, int height){ // atualizar a viewport ::glViewport (0, 0, width, height);

// atualizar a projeção definida pela câmera _camera.UpdateProjection (width, height);}

/** * Renderização da cena. */void State::Render (){ ::glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

::glLoadIdentity();

// ajustar câmera _camera.LookAt (_camWhereToLook);

// desenhar os objetos da cena _cage.Render(); _movableObject.Render();

}

/** * Atualização de todos os objetos da cena. */void State::Update ( float time){ UpdateCamera (time); UpdateMovableObject (time);}

/** * Leitura de dados dos dispositivos de entrada. */void State::GetInput (Glib::InputOracle & inputOracle){ // recuperação de dados de entrada inputOracle.GetInput (_inputHandler);

// Interpretação if (_inputHandler.wantsToConfigDevices) { inputOracle.ConfigureDevices (); _inputHandler.wantsToConfigDevices = false; return; }

if (_inputHandler.wantsToQuitGame) { inputOracle.TerminateApplication();

81

return; }

}

/** * Preparar a câmera para uso. */void State::SetUpCamera(){ // A câmera é colocada na parte da frente do cubo, e olha inicialmente // diretamente para a parte de trás.

_camera.fov = 70.0f; _camera.nearPlane = 1.0f; _camera.farPlane = 100.0f;

_camera.position.x = 0.0f; _camera.position.y = 0.0f; _camera.position.z = _cage.GetFront();

_camWhereToLook.z = _cage.GetBack();

}

/** * Informar qual mapa de ações usar e requisitar ao sistema que ache todos * os dispositivos que sejam adequados a este mapa. */void State::SetActionFormat (Glib::InputOracle & inputOracle){ inputOracle.SetCurrentActionMap (g_actionMap); inputOracle.DistributeDevices ();}

/** * Preparar o objeto móvel para uso. */void State::SetUpMovableObject(){ // o objeto é posicionado na origem. _movableObject.SetPosition (0,0,0);

// o objeto possui velocidade inicial aleatória. _movableObject.SetVelocity (Glib::Math::RandomVector3D (-20.0f, 20.0f) );

// o objeto possui um modelo que o representa. const Glib::Ogl::ModelSharedPtr model ( new Glib::Ogl::Model ("data\\model.3ds") );

_movableObject.SetModel (model);

}

/** * Preparar o cenário para uso.

82

*/void State::SetUpCage(){ // o cenário possui um modelo que o representa. const Glib::Ogl::ModelSharedPtr model ( new Glib::Ogl::Model ("data\\cage.3ds") );

_cage.SetModel (model);

}

/** * Preparar o OpenGL para uso. */void State::SetUpRenderingContext(){ ::glClearColor (0.0f, 0.0f, 0.0f, 1.0f);

::glEnable (GL_DEPTH_TEST); ::glEnable (GL_COLOR_MATERIAL); ::glEnable (GL_TEXTURE_2D); ::glEnable (GL_CULL_FACE);

::glShadeModel (GL_SMOOTH); ::glFrontFace (GL_CCW); ::glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

::glMatrixMode (GL_MODELVIEW); ::glLoadIdentity();

}

/** * Atualizar a orientação da câmera de acordo com o comando do usuário. */void State::UpdateCamera ( float time){ const float cameraVelocity = 100.0f;

if (_inputHandler.goRight) { _camWhereToLook.x += cameraVelocity * time;

if (_camWhereToLook.x > _cage.GetRight() ) _camWhereToLook.x = _cage.GetRight();

}

if (_inputHandler.goLeft) { _camWhereToLook.x -= cameraVelocity * time;

if (_camWhereToLook.x < _cage.GetLeft() ) _camWhereToLook.x = _cage.GetLeft();

}

if (_inputHandler.goUp) { _camWhereToLook.y += cameraVelocity * time;

83

if (_camWhereToLook.y > _cage.GetTop() ) _camWhereToLook.y = _cage.GetTop();

}

if (_inputHandler.goDown) { _camWhereToLook.y -= cameraVelocity * time;

if (_camWhereToLook.y < _cage.GetBottom() ) _camWhereToLook.y = _cage.GetBottom();

}

}

/** * Atualizar o objeto móvel. */void State::UpdateMovableObject ( float time){ // Movimenta-se o objeto

_movableObject.Update (time);

// Verifica-se se houve colisão com alguma parede do cubo; caso exista // alguma colisão, a velocidade correspondente é invertida

const float boundsTop = _cage.GetTop() - _movableObject.GetBoundingBox().ext.y;

const float boundsBottom = _cage.GetBottom() + _movableObject.GetBoundingBox().ext.y;

const float boundsLeft = _cage.GetLeft() + _movableObject.GetBoundingBox().ext.x;

const float boundsRight = _cage.GetRight() - _movableObject.GetBoundingBox().ext.x;

const float boundsFront = _cage.GetFront() - _movableObject.GetBoundingBox().ext.z;

const float boundsBack = _cage.GetBack() + _movableObject.GetBoundingBox().ext.z;

// eixo X if (_movableObject.GetX() <= boundsLeft || _movableObject.GetX() >= boundsRight) _movableObject.InvertVx ();

// eixo Y if (_movableObject.GetY() <= boundsBottom || _movableObject.GetY() >= boundsTop) _movableObject.InvertVy ();

// eixo Z if (_movableObject.GetZ() <= boundsBack || _movableObject.GetZ() >= boundsFront) _movableObject.InvertVz ();

84

}

7.3. Cage.h//---------------------------------------------------------------------------#ifndef CageH#define CageH//---------------------------------------------------------------------------

//---------------// INCLUDES//---------------#include <Glib/GlibMovableObject.h>

//---------------// CLASSES//---------------

class Cage : public Glib::MovableObject{

public:

Cage () {}

void Update (float time) {}

public:

float GetLeft() { return _position.x - _model3d->GetBoundingBox().ext.x;}

float GetRight() { return _position.x + _model3d->GetBoundingBox().ext.x; }

float GetTop() { return _position.y + _model3d->GetBoundingBox().ext.y; }

float GetBottom() { return _position.y - _model3d->GetBoundingBox().ext.y; }

float GetFront() { return _position.z + _model3d->GetBoundingBox().ext.z; }

float GetBack() { return _position.z - _model3d->GetBoundingBox().ext.z; }

};

//---------------------------------------------------------------------------#endif

7.4. InputHandler.h//---------------------------------------------------------------------------#ifndef InputHandlerH#define InputHandlerH//---------------------------------------------------------------------------

//---------------// INCLUDES//---------------#include <Glib/GlibInputManager.h>

85

//---------------// CLASSES//---------------

class InputHandler : public Glib::Input::Handler{ public:

// Informa se o usuário deseja movimentar-se para a esquerda bool goLeft;

// Informa se o usuário deseja movimentar-se para a direita bool goRight;

// Informa se o usuário deseja movimentar-se para cima bool goUp;

// Informa se o usuário deseja movimentar-se para baixo bool goDown;

// Informa se o usuário deseja configurar os dispositivos de entrada bool wantsToConfigDevices; // Informa se o usuário deseja encerrar a aplicação bool wantsToQuitGame;

public:

InputHandler() { Clear(); }

void Clear() { goLeft = goRight = goUp = goDown = false; wantsToConfigDevices = wantsToQuitGame = false; }

public:

// método herdado da classe base void Handle (size_t deviceIndex, const DIDEVICEOBJECTDATA & deviceData);

};

//---------------------------------------------------------------------------#endif

7.5. InputHandler.cpp//---------------// INCLUDES//---------------#include "ActionMap.h"#include "InputHandler.h"

//---------------// CLASSES//---------------

/** * Tratar o dado retornado pelo dispositivo de índice deviceIndex. * */

void InputHandler::Handle (size_t deviceIndex, const DIDEVICEOBJECTDATA & deviceData){

86

// os dados para botões são apresentados como `true` se o botão // foi pressionado ou `false` se o botão foi solto

bool data = deviceData.dwData != 0 ? true : false;

// verificamos qual foi a constante definida por nós que é retornada switch (deviceData.uAppData) { case INPUT_LEFT : goLeft = data; break; case INPUT_RIGHT : goRight = data; break; case INPUT_UP : goUp = data; break; case INPUT_DOWN : goDown = data; break;

case INPUT_CONFIG_DEVICES : wantsToConfigDevices = data; break; case INPUT_QUIT_GAME : wantsToQuitGame = data; break;

default: break;

}

}

7.6. ActionMap.h//---------------------------------------------------------------------------#ifndef ActionMapH#define ActionMapH//---------------------------------------------------------------------------

//---------------// INCLUDES//---------------#include <windows.h>#include <dinput.h>

//---------------// CONST//---------------

enum { // ações possíveis INPUT_LEFT, INPUT_RIGHT, INPUT_UP, INPUT_DOWN,

INPUT_CONFIG_DEVICES, INPUT_QUIT_GAME

};

//---------------// GLOBALS//---------------

// Objeto ActionMap do DirectInputextern DIACTIONFORMAT g_actionMap;

//---------------------------------------------------------------------------

87

#endif

7.7. ActionMap.cpp//---------------// INCLUDES//---------------#include "ActionMap.h"

//---------------// FUNCTIONS//---------------

// Função reponsável por preencher alguns campos requeridos da estruturaDIACTIONFORMAT & SetUpGlobalActionMap();

//---------------// CONST//---------------

// Um GUID (Globally Unique Identifier) é requerido pelo DirectInput// para que o sistema possa identificar o mapa de ações e poder// associá-lo à mesma aplicação em outras instâncias de execução.

static const GUID APP_GUID = {0xd45b538c, 0x6329, 0x4279, { 0x83, 0xfc, 0x46, 0x22, 0x1f, 0x4f, 0x26, 0x86 } };

//---------------// GLOBALS//---------------

// Definição do mapa de ações global

DIACTIONFORMAT g_actionMap = SetUpGlobalActionMap();

// Ações da aplicação; no caso foi escolhido o gênero DIVIRTUAL_CAD_FLYBY,// que correponde a CAD – 3D Navigation.//// O mapeamento é feito da seguinte forma:// - Escolhe-se uma ação existente do gênero desejado, e associa-se com// uma constante definida anteriormente. É possível no caso do teclado// e do mouse, associar a constante com alguma tecla ou botão// existente. O terceiro parâmetro indica se o mapeamento é fixo ou não// (significa que o usuário não poderá alterá-lo), enquanto o último// corresponde ao texto que é exibido na janela de configuração de// dispositivos.//

DIACTION DEMO_ACTIONS [] ={

{ INPUT_LEFT , DIKEYBOARD_LEFT , 0, "Esquerda" }, { INPUT_RIGHT , DIKEYBOARD_RIGHT , 0, "Direita" }, { INPUT_UP , DIKEYBOARD_UP , 0, "Cima" }, { INPUT_DOWN , DIKEYBOARD_DOWN , 0, "Baixo" },

{ INPUT_CONFIG_DEVICES, DIKEYBOARD_C, DIA_APPFIXED, "Configurar dispositivos" }, { INPUT_QUIT_GAME, DIKEYBOARD_ESCAPE, DIA_APPFIXED,"Finalizar aplicação" }

};

//---------------// FUNCTIONS//---------------

88

DIACTIONFORMAT & SetUpGlobalActionMap (){ // É preciso preencher alguns campos da estrutura de mapa de ações, // antes de enviá-la ao DirectInput.

static DIACTIONFORMAT actionMap;

::memset (& actionMap, 0, sizeof (DIACTIONFORMAT) );

DWORD numActions = sizeof (DEMO_ACTIONS) / sizeof (DIACTION);

actionMap.dwSize = sizeof (DIACTIONFORMAT); actionMap.dwActionSize = sizeof (DIACTION); actionMap.dwDataSize = numActions * sizeof (DWORD); actionMap.dwNumActions = numActions; actionMap.guidActionMap = APP_GUID; actionMap.dwGenre = DIVIRTUAL_CAD_FLYBY; actionMap.rgoAction = DEMO_ACTIONS;

// tamanho do buffer interno dos dispositivos actionMap.dwBufferSize = 16;

// Valor mínimo desejado para movimentação dos eixos actionMap.lAxisMin = - 10;

// Valor máximo desejado para movimentação dos eixos actionMap.lAxisMax = 10;

::strcpy (actionMap.tszActionMap, "ControlesDemo01" );

return actionMap;}

7.8. Main.cpp//---------------// INCLUDES//---------------#include <stdexcept>#include <new.h>#include <windows.h>

#include <Glib/GlibStateMachine.h>

#include "State.h"

//---------------// FUNCTIONS//---------------

// Determinar a configuração do OpenGL.void SetUpGraphicsConfig ();

// Handler para o operador 'new'void NewHandler ();int NewHandler (size_t size);

//---------------

89

// DEFINES//---------------

#ifdef _MSC_VER #define set_new_handler(p) _set_new_handler(p)

#endif

//---------------// GLOBALS//---------------

Glib::Ogl::Config g_oglConfig;

//---------------// FUNCTIONS//---------------

/** * Função principal de programas Windows. * */int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){

::set_ new_handler (NewHandler);

try {

SetUpGraphicsConfig ();

State state;

Glib::StateMachine::GetInstance()->SetOglConfig (g_oglConfig); Glib::StateMachine::GetInstance()->SetName ("Demo01");

Glib::StateMachine::GetInstance()->Run (hInstance);

return 0;

}

catch (std::exception & e) { ::MessageBox (0, e.what(), "Demo01", MB_ICONERROR);

return 0; }

catch (...) { ::MessageBox (0, "unknown error !", "Demo01", MB_ICONERROR); return 0; }

}

90

/** * Determinar a configuração de vídeo. */void SetUpGraphicsConfig(){

g_oglConfig.colorDepth = 16; g_oglConfig.depthBuffer = 16; g_oglConfig.screenHeight = 600; g_oglConfig.screenWidth = 800; g_oglConfig.useDoubleBuffering = true;

int result = ::MessageBox (NULL, "Fullscreen ?", "?", MB_YESNO | MB_ICONQUESTION);

if (result == IDYES) g_oglConfig.useFullScreen = true; else g_oglConfig.useFullScreen = false;

}

void NewHandler (){ throw std::runtime_error ("out of memory"); }

int NewHandler (size_t size){ throw std::runtime_error ("out of memory"); return 0;}

91

8. Anexo B: Gestão Automatizada de Recursos

Um recurso pode ser definido como algo que deve ser requisitado ao Sistema Operacional,quando necessário, e que deve ser devolvido a este após o uso. Esta definição é abstrata o bastantepara quali ficar exemplos como memória, arquivos, sockets, threads, et cetera.

O principal problema em relação a recursos é que o programador deve explicitamente devolvê-los ao Sistema Operacional quando não são mais necessários. Quando isto não é feito, uma série deproblemas podem se manifestar, como perda de recursos (resource leaks), deadlocks, bugs“aleatórios” (um defeito que é difícil de se reproduzir), acessos inválidos à memória, exaustão doSistema Operacional (i.e. os recursos do Sistema Operacional estão esgotados), entre outros,dependendo do tipo do recurso. As linguagens de programação, em geral, não possuem funcionalidadepadrão para tratar deste problema.

Neste anexo, é discutida uma solução para este problema utili zando-se a linguagem C++.

8.1. Funcionalidades Existentes em Algumas Linguagens de Programação

A seguir é feita uma comparação entre duas linguagens de programação, Java e C++, comrelação a funcionalidades oferecidas para tratar do problema de Gestão de Recursos.

8.1.1. Java

Java oferece um coletor de li xo para gestão “automática” de memória. A coleta de memória nãoutili zada é feita periodicamente pela Máquina Virtual Java (JVM). Alguns pontos devem serressaltados:

� Memória é o único tipo de recurso administrado pela Máquina Virtual Java;� A periodicidade da execução do coletor de lixo é indeterminada, ou seja, o coletor de lixo

entra em ação quando a quantidade de memória disponível para a aplicação não é maissuficiente [BEC’2000]. Isto quer dizer que se houver grande quantidade de memóriadisponível, o coletor de lixo pode não ser executado (neste caso, toda a memória utili zada édevolvida ao Sistema Operacional no encerramento da aplicação).

8.1.2. C++

C++ não ofereceferramentas para Gestão Automatizada de Recursos. Os recursos devem serrequisitados ao Sistema Operacional e devolvidos explicitamente. Como exemplo, memória pode serobtida utili zando-se o operador new e deve ser devolvida utili zando-se o operador delete. Oprogramador é o responsável pela gestão; ele deve garantir que a devolução do recurso será feita,possivelmente examinandotodos os caminhos possíveis para o fluxo de execução do programa desdea aquisição do recurso.

8.2. Fundamentos

Aqui são introduzidas terminologias e conceitos empregados em Gestão Automática deRecursos. Outros fundamentos serão apresentados ao longo do estudo de casos.

8.2.1. Objetos Automáticos

Um objeto automático é um objeto que é capaz de obter e devolver recursos ao SistemaOperacional, sem intervenção direta do programador.

É possível implementar objetos automáticos em C++ utili zando-se construtores, destrutores e adefinição de escopo.

92

8.2.1.1. Construtores

Construtores são métodos especiais de uma classe que são executados no momento de criaçãode instâncias. Estes métodos podem ser utili zados para aquisição de recursos, pois sua execução égarantida. O desenvolvedor só poderá usar uma instância após o construtor de sua classe serexecutado.

8.2.1.2. Destrutores

Destrutores são métodos especiais de uma classe que são executados no momento de destruiçãode instâncias, automaticamente. O desenvolvedor não pode fazer uma chamada a este tipo de métododiretamente.

8.2.1.3. Escopo

Um escopo de um identificador é a parte do programa onde o identificador pode ser utili zado,ou seja, onde ele é visível. Um escopo, em C++, pode ser enquadrado nas seguintes categorias[BCB’1999]:

� Bloco: Um bloco, em C++, é um trecho do programa que é delimitado pelos caracteres { e }.O escopo de um identificador em um bloco (ou escopo local) inicia-se no momento dadeclaração do identificador e termina no fim deste bloco.

� Função: Corresponde ao corpo da função, ou seja, ao trecho de código que define a função(que é um bloco de código).

� Cabeçalho de função: Corresponde ao trecho definido pela declaração dos parâmetros deuma função. O escopo termina no fim da declaração dos parâmetros.

� Arquivo: Identificadores declarados em arquivos, são conhecidos como indentificadoresglobais porque são acessíveis a todas as funções, classes e blocos de código, embora nãopertençam a nenhum deles. Este escopoinicia-se na declaração do identificador e termina nofim do arquivo.

� Classe: Este escopo corresponde ao bloco que define uma classe.� Condicional: Corresponde a blocos de código definidos por if, while e switch. No

caso, identificadores podem ser declarados com as expressões condicionais. O escopotermina no fim do bloco definido.

� Namespace: Corresponde ao trecho de código definido pelo uso da palavra reservadanamespace. Este tipo de escopo é utili zado para evitar a colisão de nomes deidentificadores.

8.2.1.4. Construtor + Destrutor + Escopo = Objeto Automático

Ao combinar as idéias de construtores, destrutores e escopo, têm-se os seguintes casos:

� Um objeto declarado em um bloco de código será criado (i.e. terá o seu construtorexecutado) quando o fluxo de programa entrar no escopo do bloco. Quando o fluxo deprograma deixar o bloco31, o objeto será destruído (i.e. terá o seu destrutor executado);

31 Saída normal (break, goto , return) ou por exceção (throw).

93

� Um objeto A é declarado como campo de um outro objeto B. Neste caso, o objeto A seráconstruído quando o objeto B for contruído. Da mesma forma, o destrutor de A seráexecutado pelo destrutor de B, quando este for destruído;

� Um objeto é declarado como uma variável ou constante global. A instância do objeto serácriada no início da execução do programa e será destruída ao término da aplicação.

Em ambos os casos, o escopo de uso do objeto é bem definido, e por isso o compilador podeautomatizar todo o processo de criação e destruição de instâncias. Entretanto, existe uma exceção:

� O uso de funções como exit() e abort() provoca o término abrupto do programa aoporque estas funções fazem chamadas a interrupções de hardware. Quando funções destetipo são utili zadas, destrutores de objetos locais não são executados [BCB’1999].

8.2.2. Regra Primeira de Aquisição

A Regra Primeira de Aquisição (First Rule of Acquisition), definida por Bartosz Milewski[BMI’2001], diz: “Obtenha recursos em construtores e devolva-os usando os destrutorescorrespondentes”.

8.2.3. Propriedade Sobre Recursos

Um objeto é proprietário de um recurso se este objeto é responsável por devolver o recurso aoSistema Operacional.

Geralmente, um recurso pode ter apenas um único proprietário. É possível, entretanto,implementar algum tipo de propriedade coletiva, com uso de técnicas como contagem de referências.Neste caso, o recurso a ser administrado passa a ser o contador de referências.

8.3. Estudo de Caso: Memória

Memória é um dos tipos mais comuns de recurso, sendoobtida em tempo de execução atravésdo operador new e devolvida ao sistema utili zando-se o operador delete. É possível criar umaclasse parametrizada para a gestão deste tipo de recurso:

template <typename T>class SmartPointer{

public:~SmartPointer ()

{ delete _p; }

SmartPointer (T * p): _p (p) {}

private:T * p;

};

O construtor da classe recebe um ponteiro para memória obtida do sistema. É possívelargumentar que esta implementação infringe a Regra Primeira de Aquisição, pois o recurso não éalocado no construtor. Entretanto, para obedecer rigidamente à regra definida, seria necessário criarespecializações da classe para cada tipo, pois o seguinte trecho de código não é válido:

94

template <typename T>SmartPointer::SmartPointer (){

_p = new T;}

Este trecho de código não é válido porque o compilador não sabe como executar o construtor daclasse representada por T32, porque o tipo não foi definido.

O que é feito, então, é uma transferência direta de recurso:

SmartPointer <int> sp (new int);

Ao utili zar o operador new somente para transferência direta de recurso, é obtido o mesmoresultado que é conseguido ao seguir a Regra Primeira de Aquisição.

É possível perceber que esta implementação não é suficiente para garantir a GestãoAutomatizada de Recursos. Há de se considerar o seguinte exemplo:

SmartPointer <int> p1 (new int (1) );SmartPointer <int> p2 (new int (2) );

p1 = p2;

A propriedade sobre os recursos está indefinida. O operador de atribuição, em suaimplementação padrão (feita pelo compilador), realiza uma cópia superficial33 (shallow copy). Nestecaso, os objetos p1 e p2 passam a referenciar o mesmo objeto (o ponteiro originalmente obtido peloobjeto p2). O recurso obtido pelo objeto p1, após a operação de atribuição, está perdido.

A solução é implementar o conceito de transferência de recurso.

8.3.1. Transferência de Recurso

A operação de atribuição original tem semântica de cópia; quando é feita uma operação destetipo, o objeto alvo recebe uma cópia do objeto fonte, que é feita bit a bit (bitwise). Como visto noexemplo, esta não é a solução adequada.

Para reforçar o conceito de Propriedade de Recursos, é necessário redefinir a operação deatribuição:

template <typename T>class SmartPointer{

public:

~SmartPointer (){ delete _p; }

SmartPointer (T * p): _p (p) {}

void operator = (SmartPointer <T> & obj){

if (_p != obj._p){

delete _p;_p = obj.Release();

32 T não necessariamente é uma classe; poderia ser um tipo simples, como int.33 A cópia é dita superficial porque o conteúdo da memória referenciada pelo ponteiro não é copiado.

95

}}

private:

T * Release(){

T * tmp = _p;_p = 0;return tmp;

}

private:

T * _p;

};

A operação de atribuição, inicialmente, verificase os dois objetos envolvidos são iguais. Casonão sejam iguais, o recurso armazenado é devolvido ao Sistema Operacional e o recurso do objetofonte é transferido.

Existe, ainda, uma outra operação que deve ser redefinida: o construtor para cópias. Caso oprogramador não implemente um construtor para cópias, o compilador irá criar um. A implementaçãodo compilador realiza uma cópia superficial do objeto fonte. É preciso, então, redefiní-lo:

template <typename T>SmartPointer::SmartPointer (SmartPointer <T> & obj){

_p = obj.Release();}

Ao redefinir o construtor para cópias e a operação de atribuição é implementada uma semânticade transferência de recursos, que substitui a semântica original de cópia destas operações.

8.4. Estudo de Caso: Contadores de Referências

O exemplo anterior ilustra o caso em que um recurso tem somente um proprietário,obrigatoriamente. Pode ser necessário, entretanto, utili zar algum tipo de propriedade coletiva. Apropriedade coletiva pode ser implementada com o uso de contadores de referências. O recurso a seradministrado, nesse caso, é o próprio contador de referências.

Inicialmente, é definida uma classe para representar o contador de referências:

class RefCounter{

public:

RefCounter () : _counter (1) {}

void Increase (){ ++_counter; }

void Decrease (){ --_counter; }

int GetCount() const { return _counter; }

96

private:int _counter;

};

Logo a seguir, é definida uma classe para administrar o contador de referências, junto com umponteiro para o recurso original:

template <typename T>class SharedPtr{

public:

~SharedPtr (){ Release (); }

SharedPtr (T * p) : _p (p)

{ _refCounter = new RefCounter; }

SharedPtr (SharedPtr <T> & obj){ AddReferenceTo (obj); }

void operator = (SharedPtr <T> & obj){

if (_p != obj._p){

Release ();AddReferenceTo (obj);

}}

T * operator -> (){ return _p; }

private:

void Release () {

_refCounter->Decrease();

if (_refCounter->GetCount() == 0){

delete _p;delete _refCounter;

}}

void AddReferenceTo (SharedPtr <T> & obj){

_refCounter = obj._refCounter;_p = obj._p;

_refCounter->Increase();}

private:

97

RefCounter * _refCounter;T * _p;

};

É importante observar que tanto o construtor para cópias quanto a operação de atribuição sãoredefinidos para que o contador de referências seja atualizado corretamente.

Quando uma instância qualquer dessa classe é destruída, é verificado se ainda existemreferências. Caso não existam referências, os recursos são devolvidos ao Sistema Operacional.

8.5. Conclusões

Gestão Automatizada de Recursos é uma metodologia tão poderosa quanto simples; suautili zação aumenta a robustez e confiabili dade das aplicações que a aplicam.

Como a administração de recursos é feita internamente em classes específicas, sem intervençãodireta do desenvolvedor, defeitos de programação (bugs) relacionados a recursos podem diminuirbastante. Isto facilit a a manutenção e a implementação das aplicações.

A STL oferece uma classe, chamada auto_ptr, com a mesma funcionalidade deSmartPointer. A implementação de auto_ptr, entretanto, é bem mais completa que a deSmartPointer, como poderia se esperar.

A implementação apresentada para gestão de contadores de referência pode não ser a maissegura ou eficiente, porque apenas serve como exemplo ilustrativo. Outros exemplos mais completospodem ser encontrados em bibliotecas anexas, como Boost, que foi utili zada neste projeto.

98

9. Anexo C: Termos Relacionados

AABBAcrônimo para Axis Aligned Bounding Box. Representa um paralelepípedo delimitador (BoundingBox) que está alinhado com os eixos X, Y e Z. Vide Bounding Box.

Action MappingMapeamento das ações pertinentes do usuário de uma aplicação para os botões e eixos dedispositi vos de entrada.

ActiveXNome registrado por Microsoft Corporation para as suas tecnologias que tornam possíveis asinterações entre objetos que utili zam COM.

AGP (Accelerated Graphics Port)Corresponde a um barramento que é utili zado exclusivamente por hardware de vídeo.

Alpha, Componente Representa o quarto componente de uma cor, utili zado para determinar o seu nível de opacidade.Em OpenGL, a faixa de valores possíveis está entre 0.0 (completamente transparente) e 1.0(opaco).

Alpha BlendingTécnica que utili za o componente alpha de uma cor para simular efeitos gráficos, comotransparência. Duas cores são combinadas por um algoritmo pré-determinado (que está disponívelna API), produzindo a cor final.

AlgoritmoConjunto de regras ou procedimentos que devem ser seguidos para a resolução de um determinadoproblema.

API (Application Programming Interface)Conjunto de rotinas utili zadas por uma aplicação para acessar serviços de um Sistema Operacionalou hardware. Como um exemplo, a API do Windows oferecefunções para a gestão de janelas,ícones, menus, arquivos, entre muitas outras.

AppletsAplicativos escritos em Java que podem ser anexados a páginas da Web, sendoexecutados por umprograma navegador (web browser).

ARBArchitecture Review Board. Representa um grupo de fabricantes de hardware para gráficostridimensionais, que controla a especificação do OpenGL e promove este padrão.

ArrayAgregração sequencial de vários objetos de mesmo tipo.

Árvores BSPBSP é um acrônimo para Binary Space Partition. Esta estrutura de dados, que é implementadacomo uma árvore binária, tem como objetivo dividir um mapa em partes menores. Um mapacorresponde à geometria que define o mundo virtual retratado pela simulação.

99

BackendAplicação ou partes de uma aplicação que lidam diretamente com subsistemas disponíveis.

BGRAcrônimo para Blue-Green-Red. Vide RGB.

BibliotecaConjunto de estruturas de dados e/ou algoritmos afins que são utili zados para resolver umdeterminado tipo de problema.

Bill boardingÉ uma técnica utili zada para simular um objeto tridimensional em uma cena. Uma primitiva,tipicamente retangular, é inserida na cena e orientada de modoque esteja sempre de frente para oobservador. Uma ou várias texturas são aplicadas, formando um objeto animado.

Bitmap, Arquivos de ImagemArquivos Bitmap reproduzem a imagem original com todos os seus detalhes, ou seja, todos ospixels que compõem a imagem são armazenados no arquivo.

Boost, BibliotecaÉ um conjunto de várias bibliotecas desenvolvidas por colaboradores em todo o mundo,que foicriada originalmente por membros do Comitê de Padronização do C++. Um de seus objetivos éestabelecer referências para o desenvolvimento de bibliotecas, de modo que seja possível integrá-las à implementação padrão do C++. Provê soluções para Gestão de Memória, ProgramaçãoConcorrente, Entrada e Saída, Programação Genérica, Matemática, entre outras. Sua distribuição égratuita.

Bounding BoxMenor paralelepípedo que envolve um polígono ou grupo de polígonos.

BugErro de programação ou de lógica em um algoritmo.

CADAcrônimo para Computer Aided Design; as aplicações que encontram-se nesta categoria sãoutili zadas para modelagem de objetos bidimensionais ou tridimensionais.

CâmeraAbstração utili zada para definir a posição e a orientação do observador em uma cena.

CenaConjunto de objetos que compõem o mundo virtual.

ClasseVide Programação Orientada a Objetos.

Classe AbstrataUma classe que é utili zada como padrão ou molde para especializações. Não é possível criarinstâncias de classes abstratas porque este tipo de classe não implementa nenhuma funcionalidade,ou seja, somente especifica uma interface. Vide Programação Orientada a Objetos, Interface.

100

Classe EspecializadaVide Subclasse.

Cliente/ServidorMetodologia de desenvolvimento em que existe uma aplicação dedicada que oferece algunsserviços determinados (definida como aplicação servidora) e uma aplicação (ou várias) que fazrequisições a esses serviços.

ClippingEliminação de parte de uma primitiva ou grupo de primitivas. Todos os elementos que estão forada região definida pelo volume de visão não são desenhados.

CLSID (Class Identifier)Identificador Universal Único (UUID) utili zado para a identificação de tipos de objetos COM.Todo objeto COM possui um CLSID para que possam ser usados por diversas aplicações.

COM (Component Object Model)Representa uma arquitetura para o desenvolvimento de aplicações com modelo cliente/servidor,baseada em componentes. É baseada em tecnologia orientada a objetos, tendo sido definida porDigital Equipment Corporation e Microsoft Corporation. Todos os objetos COM são derivados deuma interface base (IUnknown), que é semelhante a uma classe abstrata.

ConstrutorFunção especial de uma classe que é executada no momento da criação de uma instância. Em C++e Java, esta função possui o mesmo nome da classe. Uma classe pode oferecer vários construtores.

Contador de ReferênciaUm valor que indica o total de referências para um determinado objeto. Pode ser utili zado para adivisão de recursos, ou seja, em vez de serem criadas várias instâncias de um determinado recurso,apenas uma é alocada e dividida entre os objetos interessados. Quando a última referência édescartada, o recurso é devolvido ao Sistema Operacional.

DeadlockErro de lógicade programação concorrente, classicamente caracterizadopelo exemplo em que umathread A permanecebloqueada indefinidamente porque está a esperar uma resposta em relação aoprocessamento realizado por uma thread B, enquanto a thread B está bloqueada indefinidamenteporque espera por uma resposta do processamento realizado pela thread A.

Detecção de ColisãoTécnica utili zada para decidir se um objeto colidiu com algum outro objeto ou com o cenário doambiente virtual.

Device ContextÉ uma estrutura da GDI que define um conjunto de objetos gráficos e seus atributos. É incluídatambém a definição sobre o modo gráfico.

DevIL, BibliotecaDesenvolvida por Denton Woods, a DevIL (Developer’s Image Library) é utili zada paramanipulação de imagens, sendo capaz de trabalhar com diversos formatos de arquivos. Suadistribuição é gratuita e está disponível em diversas plataformas.

101

DestrutorFunção especial de uma classe que é executada no momento da destruição de uma instância. EmC++, esta função possui o mesmo nome da classe acrescido do prefixo ’~’ (exemplo:~MinhaClasse), sendo que só é permitida a existência de uma função deste tipo por classe.Classes em Java não possuem destrutores.

DirectXFamília de APIs criada por Microsoft Corporation para o desenvolvimento de aplicaçõesmultimídia de alto desempenho em Windows. Possui soluções para gráficos bidimensionais etridimensionais (DirectDraw e Direct3D), dispositi vos de entrada (DirectInput), áudio(DirectSound e DirectMusic), conectividade (DirectPlay), instalação de aplicativos (DirectSetup)e fluxo (streaming) de mídia (DirectShow).

Display List Representa uma lista compilada de comandos do OpenGL. A compilação ocorre durante aexecução da aplicação, após uma chamada a uma função da API para esse propósito. Possui umidentificador, que é utili zado para executar todos os comandos da li sta.

DomoSuperfície geométrica com formato esférico, por exemplo, a metade de uma esfera.

Double BufferingTécnica de animação onde a imagem a ser exibida é construída em memória anexa, sendo entãotransferida para a memória de vídeo principal. Como o conteúdo da memória anexa não é visívelno vídeo, o resultado é uma animação rápida e suave.

EncapsulamentoEm Programação Orientada a Objetos, representa a técnicade se esconder a implementação real dealguma operação com o oferecimento de métodos.

EngineEm tradução literal, significa “motor”. Em Ciência da Computação, pode significar algumaferramenta que automatiza algum processo de criação de software.

EntidadeSer; o que constitui a essência de uma determinada coisa; indivíduo.

Environment MappingTécnica utili zada para produzir objetos que refletem as cores do ambiente que os envolvem.

Especialização de Classe Vide Subclasse.

FrameworkEstrutura reusável para desenvolvimento de software. Esta estrutura implementa características oufuncionalidades comuns a todas as aplicações de um determinado gênero, de modoque é possíveliniciar o desenvolvimento a partir de etapas mais avançadas.

Force Feedback, Dispositi vosRepresentam a geração de dispositi vos de entrada que é capaz de produzir respostas a eventos pré-programados. Estas respostas são representadas por tipos variados de forças, que são produzidaspor motores localizados no interior do dispositi vo.

102

Formato de Onda, Arquivos de SomEste tipo de arquivo é o equivalente em som aos arquivos de imagem Bitmap, ou seja, estesarquivos armazenam todos os detalhes do som descrito.

FrontendAplicação ou partes de uma aplicação com a qual o usuário interage diretamente. Um frontendpode, por exemplo, ter como objetivo oferecer uma interfaceamigável para operações internas deum programa.

GDI (Graphics Device Interface)É um subsistema do Windows responsável pela exibição de texto e imagens, em dispositi vos devídeo ou impressoras. Este subsistema intercepta todas as chamadas da aplicação relacionadas aosistema gráfico do Windows e as repassa para algum driver de vídeo, que então irá gerar a saídadesejada.

GUID (Globally Unique Identifier)Identificador global único. Ver UUID.

HWNDIdentificador para uma janela do Windows.

HDCIdentificador para um device context da GDI.

HGLRCIdentificador para um rendering context do OpenGL.

II D (Interface Identifier)Representa uma constante utili zada para identificar uma determinada interfaceCOM, sinônimo deGUID.

Instância, ClassesRepresenta um objeto de uma classe em uso, ou seja, a classe é um padrão ou molde e as instânciassão os objetos que contêm os dados reais.

InterfaceInterfacegráfica ou textual utili zada pelo usuário para interação com alguma aplicação; conjuntode funções ou métodos usados para acessar algum serviço de uma aplicação, classe, bibliotecaouhardware.

Isométrico, Ponto de VistaPonto de vista que é eqüidistante em relação a um ponto de referência, em duas ou três dimensões.

JoystickControle de jogo; o termo é utili zado para identificar dispositi vos de entrada que não sejam tecladoou mouse. Estes dispositi vos podem ter uma quantidade variável de botões ou eixos, em diversosformatos. Exemplos: Manches, Volantes e gamepads (controles semelhantes aos encontrados emaparelhos de videogames).

JVMAcrônimo para Java Virtual Machine; Máquina Virtual Java.

103

Lib3ds, BibliotecaDestina-se a manipulação de arquivos (formato 3DS) gerados pelo software 3D Studio, foidesenvolvida por J. E. Hoffman. Está disponível gratuitamente e sua implementação é portável.

LightmapsSão texturas ou grupo de texturas que representam informações sobre iluminação de uma cena. Sãocombinadas com texturas tradicionais para criar a ilusão da existência de uma fonte de luz.

MétodoNo contexto de Programação Orientada a Objetos, representa uma operação que pode serexecutada por uma classe.

MI DIAcrônimo para Musical Instrument Digital Interface, é um padrão para a conexão de instrumentoseletrônicos e acessórios. Basicamente, MIDI é ao mesmo tempo hardware (um tipo de porta serial)e software (um protocolo para a transferência de comandos através dessa porta).

MP3Acrônimo para MPEG Audio Layer 3, representa um padrão para compactação de áudiodesenvolvido por Fraunhofer Institut Integrierte Schaltungen, Alemanha.

MPEGAcrônimo para Motion Picture Experts Group, que é um grupo industrial responsável pelamanutenção de um padrão para compactação de vídeo de mesmo nome.

MipmapSequência de texturas de resoluções variadas, derivadas de uma mesma imagem. As resoluçõessão calculadas progressivamente, desde o tamanho original até o menor tamanho possível. Umaimagem de maior resolução é utili zada em um objeto quando este está próximo ao observador;conforme o objeto se afasta do observador, imagens de resolução menor são usadas.

Modelo TridimensionalÉ um conjunto de polígonos que representam uma forma geométrica em três dimensões.

Modo de CoresCorresponde à quantidade de cores diferentes que podem ser exibidas no vídeo simultaneamente. Émedida em bits, sendoque o número total de cores pode ser calculado elevando-se dois ao total debits. Por exemplo, o modo de cor de 8 bits pode ter 256 cores diferentes exibidas simultaneamente.

MultitexturingRenderização repetida em um ou mais passos de alguma primitiva com o uso de uma ou maistexturas diferentes. Esta técnicapode ser combinada com outras (como alpha blending) para gerardiversos efeitos gráficos.

MutexMutual exclusive; exclusão mútua ou mutualmente exclusivo.

Objeto AutomáticoObjeto que é implementado de acordo com o paradigma de Gestão Automatizada de Recursos.

104

Objeto LocalObjeto que é declarado em um bloco de código e, consequentemente, possui escopobem definido.Um bloco de código pode ser uma função, por exemplo.

OpenGLÉ uma API utili zada para Computação Gráfica, em três dimensões. Desenvolvida por SiliconGraphics, Inc (SGI), possui alto desempenho e portabili dade, sendo implementada para trabalharem conjunto com hardware que seja otimizado e projetado para a exibição de gráficostridimensionais.

OráculoMetáfora para uma pessoa cujas opiniões ou conselhos são absolutamente confiáveis; entidade queconhece as repostas para todas as perguntas.

ParalelepípedoSólido limitado por seis parelelogramos, dos quais são os opostos são iguais e paralelos.

Paralelepípedo DelimitadorVide Bounding Box.

ParalelogramoQuadrilátero cujos lados iguais são opostos e paralelos.

PerspectivaModo de desenho em que objetos distantes do observador aparentam ser menores do que objetosque estão localizados próximo ao observador.

PixelAbreviação para Picture Element, é a menor divisão visual presente em uma tela de computador.Pixels são organizados em linhas e colunas, de modo que cores são atribuídas a eles para acomposição de alguma imagem.

Plano Delimitador DistanteLimite superior do volume de visão (viewing frustum) , além do qual objetos não são renderizados.

Plano Delimitador PróximoLimite inferior do volume de visão (viewing frustum), antes do qual objetos não são renderizados.

PluginÉ um módulo de programa extra que pode ser acoplado a uma aplicação, ou seja, novasfuncionalidades podem ser implementadas e anexadas a programas de maneira independente, semnecessidade de alteração ou recompilação da aplicação original.

Point LightFonte de luz que irradia em todas as direções, igualmente.

PolígonoSuperfície plana, limitada por várias retas e que apresenta vários lados e vários ângulos.

105

PolimorfismoExecução de uma variedade de operações através de uma mesma interface. Uma classe base ousuperclasse é utili zada para definir a interface. As subclasses ou especializações podem, então,redefinir as operações, e o cliente poderá utili zar essas operações através da interface comumdefinida anteriormente.

Portabili dadeQualidade do que é portável.

PortávelRefere-se à possibili dade de utili zação de uma aplicação ou partes desta em vários SistemasOperacionais ou hardware diferentes, sem alterações na implementação.

POVAcrônimo para Point of View; ponto de vista do observador de uma cena.

PrimitivaPrimitivas são tipos básicos de formas bidimensionais (como triângulos e quadrados) que podemser utili zados para a construção de outros objetos. Adicionalmente, linhas e pontos também sãoconsiderados primitivas (em outros contextos, sólidos tridimensionais como cubos ou esferaspodem também ser considerados primitivas).

ProcessoEm Programação Concorrente, representa uma instância de um programa em execução.

Profundidade de CoresVide Modo de Cores.

Programação ConcorrenteParadigma de programação que utili za vários processos e/ou threads em separado, que atuam demaneira cooperativa ou competiti va.

Programação GenéricaProgramação com utili zação de templates.

Programação Orientada a ObjetosParadigma de programação, define que uma aplicação é composta por objetos (classes), os quaispossuem certas propriedades e métodos (funções) que agem sobre estes dados. Os objetos, queoferecem serviços às aplicações, são reutili záveis. A implementação desses serviços é oculta paraclientes dos objetos, ou seja, camadas de alto nível de abstração são criadas.

ProjeçãoTransformação das coordenadas tridimensionais de pontos, linhas e polígonos em coordenadas datela (bidimensionais).

Ray CastingTécnicaderivada de ray tracing, em que raios são traçados a partir do ponto de vista na direção dovolume de visão. Esta técnica é utili zada geralmente em casos especiais, onde a geometria domundo virtual é simpli ficada, para que possa ser utili zada em tempo real.

106

Ray TracingTécnica utili zada para a renderização de cenas tridimensionais ao modelar a interação das fontesde luz com a geometria do mundo virtual retratado pela cena. Esta técnica normalmente não éadequada para renderização em tempo real.

RecursoRepresenta algum objeto que precisa ser requisitado ao Sistema Operacional antes do uso edevolvido a ele quando não é mais necessário. Pode representar também estados de sistemas(exemplo: estado de uso de bibliotecas, ou seja, uma bibliotecaque necessita ser inicializada parauso e que deve ser finalizada ao término da aplicação).

Região CríticaObjeto utili zado para a controlar a sincronização de threads em uma aplicação. Tambémconhecido como semáforo ou mutex. É comum estabelecer a regra de que apenas uma thread temacesso a uma região crítica, por vez.

Rendering ContextO contexto de renderização é um intermediário entre o OpenGL e o Sistema Operacional. Esteobjeto armazena informações que associam estados e recursos do OpenGL com alguma janela (i.e.onde as operações têm efeito). Todos os comandos do OpenGL são executados através do contextode renderização.

RenderizaçãoProcesso de conversão de primitivas em uma imagem na área de memória relacionada ao vídeo.

Resource LeakPerda de recursos; ocorre quando um recurso não é devolvido ao sistema e, consequentemente,está perdido.

RGBAcrônimo para Red-Green-Blue; especificaa ordem das cores primitivas utili zadas em um formatode cores.

RGBAAcrônimo para Red-Green-Blue-Alpha. Vide RGB.

Role Playing GamesEstilo de jogo em que o usuário assume um papel, ou seja, o usuário atua como se fosse opersonagem. Os personagens possuem diversas características bem definidas, que são utili zadasdistinguir os papéis de cada personagem. Geralmente possuem enredos bem elaborados, sendocomum a utili zação de estórias místicas ou fantásticas, retratadas em épocas diversas.

ScriptUm programa que consiste em um conjunto de instruções para uma aplicação ou programautilit ário. Estas instruções utili zam regras e procedimentos definidos por alguma linguagem parascripts.

Script, LinguagemÉ uma linguagem de programação simples destinada a realizar operações específicas ou tarefaslimitadas, que oferecer comandos de alto nível de abstração.

107

Scrolli ngTécnica de animação 2D utili zada quando o usuário se move em um cenário que é maior que atela, ou seja, a tela cobre apenas uma pequena área do universo virtual. A tela (ou janela) émovimentada para refletir a posição do usuário, como um rolamento, conforme comandos dousuário.

SingletonÉ um padrão para modelagem de classes que indica que somente uma única instância de umadeterminada classe pode existir em uma aplicação.

Sistemas de PartículasSão modelos físicos utili zados para a modelagem de um conjunto de pequenas partículas. Podemser usados para modelar efeitos como fogo, fumaça, explosões, et cetera.

SocketÉ um objeto que representa o último elo de comunicação entre dois processos em uma conexão emrede.

Spot LightsFonte de luz que emite um cone de luz. Objetos que encontram-se fora do volume definido pelocone não são iluminados.

SpritesSão objetos bidimensionais que se movem em uma cena. Podem ser representados em cenastridimensionais com uso de técnicas como billboarding.

STL (Standard Template Library)Bibliotecabaseada em templates, é parte integrante do padrão definido para a linguagem C++.Possui algoritmos genéricos e classes utilit árias como listas encadeadas, conjuntos, arraysassociativos, entre várias outras.

SubclasseClasse derivada ou especialização de classe; é uma classe que se relaciona com outra classe (asuperclasse) por herança, ou seja, a subclasse herda todas as propriedades e métodos da classebase. Os métodos herdados podem ser redefinidos para a criação de novas funcionalidades.

SubsistemaParte especializada de um sistema maior, ou seja, um sistema é formado por vários subsistemas.Exemplo: Sistema Gráfico, Sistema de Janelas, et cetera.

SuperclasseClasse base; é a classe que representa o primeiro nível (ou raiz) de uma hieraquia de classes; classeque define uma interface cujas operações podem ser redefinidas por classes derivadas ouespecializações da classe.

TemplateRepresenta um padrão ou padronização; em C++, é a ferramenta da linguagem que possibilit a ouso de programação genérica.

TexelAbreviação para texture element, representa o menor elemento que compõe uma textura.

108

TexturaRepresenta dados de uma imagem, que podem ser aplicados em polígonos.

Textura, CoordenadasSão valores que determinam qual elemento de textura (texel) é associado a um determinadovértice. A faixa de valores padrão varia entre 0.0 e 1.0, inclusive.

Textura, MapeamentoÉ o processo de aplicação de uma imagem em polígonos.

Texture WrappingÉ o processo utili zado para determinar como tratar o mapeamento de textura quando ascoordenadas estão fora da faixa padrão. Em OpenGL, existem dois modos possíveis: repetição datextura (GL_REPEAT) e truncamento de coordenadas de acordo com a faixa padrão (GL_CLAMP).

ThreadRepresenta uma unidade de execução em um programa. Uma aplicação pode conter várias threads,que são executadas em paralelo. Esse paralelismo pode ser real (se existirem vários processadoresdisponíveis) ou simulado (o tempo de execução é dividido entre as threads do programa).

UUID (Universally Unique Identifier)Corresponde a um valor de 128 bits utili zado para identificar de maneira única objetos como ospertencentes à biblioteca COM. Sinônimo para GUID (Globally Unique Identifier).

Viewing FrustumUm volume tridimensional (volume de visão), relacionado com a projeção definida por umacâmera. Todos os objetos que encontram-se no interior do volume são visíveis. Para projeção emperspectiva, este volume corresponde a uma pirâmide imaginária limitada pelos planosdelimitadores próximo e distante, que correspondem a distâncias em relação ao observador.

ViewportUm retângulo que define a área, de uma janela, utili zada para desenhar os objetos da cena.

WebNome curto para World Wide Web ou WWW.

Web BrowserAplicativo utili zado para a visualização de páginas da Web.

World Wide WebÉ o serviço mais popular da Internet, que permite acessar informações com conteúdo multimídiaem qualquer parte do mundo.

WrapperUm envoltório para algum objeto ou objetos.

WireframeRepresentação de um objeto sólido com o uso de linhas, somente.

109

Z-BufferUma áreade memória (buffer) que armazena dados sobre a profundidade para cada pixel na cena.Pixels com valores de profundidade pequenos sobrescrevem pixels com valores de profundidadegrandes. É usado para determinar quais pontos estão próximos ou distantes do observador.

110

10. Referências Bibliográficas

[ALA’1999] LaMothe, André – “Tricks of the Windows Game Programming Gurus”. Indianapolis:Sams, 1999.

[WLR’1995] Rosch, Winn L. – “The Winn L. Rosch Multimedia Bible”. Indianapolis: Sams, 1995.[LLM’1995] Myers, Larry L. – “Amazing 3–D Games Adventure Set”. Scottsdale: Coriolis Group

Books, 1995.[JDG’1996] De Goes, John – “Cutting Edge 3D Game Programming with C++”, páginas 3-9.

Scottsdale: Coriolis Group Books, 1995.[BSA’1996] Sawyer, Ben – “The Ultimate Game Developer’s Sourcebook”, capítulos 8 e 23.

Scottsdale: Coriolis Group Books, 1996.[ALA’1995] LaMothe, André – “Black Art of 3D Game Programming”, Corte Madera: Waite

Group Press, 1995.[RSW’1999] Wright, Richard S.; Sweet, Michael – “OpenGL SuperBible Second Edition”,

Indianapolis: Waite Group Press, 1999.[FLY’2002] Fly3D 2.0 SDK Documentation – Paralelo Computação, 2002.[BMI’2001] Milewsky, Bartosz – “C++ in Action. Industrial Strength Programming Techniques”,

Seção “Techniques – Code Review 5: Resource Management”. Edição para Internet.http://www.relisoft.com/book/index.html, 2001.

[BEC’2000] Eckel, Bruce – “Thinking in Java Second Edition”, capítulo 4.http://www.mindview.net/Books/DownloadSites, 2000

[MSD’1998] MSDN Library – Microsoft Corporation, 1998.[DX8’2001] DirectX 8.0 SDK Documentation – Microsoft Corporation, 2001.[DWO’2002] Woods, Denton – “DevIL Documentation”. http://www.imagelib.org/docs/index.php,

2002.[WIN’2001] Windows API Tutorials – Reliable Sofware.

http://www.relisoft.com/Win32/index.htm, 2001.[MP3’2001] MPEG Audio Layer-3 – Fraunhofer Institut Integrierte Schaltungen.

http://www.iis.fhg.de/amm/techinf/layer3/index.html, 2001.[OGR’2002] OGRE – http://ogre.sourceforge.net/wtf.php, 2002.[BCB’1999] Borland C++ Builder 4.0 Documentation – Inprise Corporation, 1999