Curso completo de DirectX 9 com C\C++
Gameprog - Escola de programação de jogos digitais
Contato: gameprog.br@gmail.com
Fase 01-3
01.3 Criando o dispositivo
1.1 Visão geral
1.2 Estrutura principal da aplicação
Arquivo: entrada.cpp
WinMain()
criação da classe da janela
registro da classe da janela
criação da janela de acordo com a classe definida
chama initGfx() para inicializar motor gráfico
mostra a janela
estabelecimento do laço de mensagens
finaliza a aplicação
processaJanela()
tratamento das mensagens
Verifica teclado - finaliza aplicação na tecla Escape
chama Limpar() na finalização da aplicação
chama Renderizar() para desenhar os gráficos
Arquivo: motor.cpp
initGfx()
Inicializa objeto Direct3d
Inicializa dispositivo renderizador
Renderizar()
Limpa a tela
Desenha a cena
Apresenta a cena
Limpar()
Libera dispositivo renderizador
Libera objeto Direct3d
A aplicação desse tópico mostra como inicializar o Direct3d e o
dispositivo gráfico que produz a imagem final da cena. A aplicação
mostra a configuração básica da função Renderizar() e mostra como se
faz o importante trabalho de 'limpeza' no qual todos os objetos do
DirectX utilizados são liberados. A tecla Escape finaliza a aplicação.
Como se pode deduzir da leitura da estrutura da aplicação, o arquivo
entrada.cpp contém o código de criação da janela e outros aspectos
básicos de uma aplicação Windows e faz chamadas às funções contidas
em motor.cpp responsável por inicializar e finalizar os objetos do
directx bem como renderizar a cena.
2.1.1 Aspectos globais - Arquivo: motor.h
A listagem abaixo mostra o conteúdo do arquivo motor.h que traz os
protótipos das funções presentes em motor.cpp. A partir do próximo
tópico a função processaJanela() será transferida para estes arquivos
pois ela se constitui numa roda ativa da aplicação com o arquivo
entrada.cpp ficando apenas responsável por criar a janela e outros
aspectos primários.
// Projeto: prj_Dispositivo - Arquivo: motor.h
#ifndef motor_h
#define motor_h
// Esta função inicializa o Direct3D
HRESULT initGfx (HWND hJanela);
// Essa função libera os objetos utilizados
void Limpar();
// Essa função desenha a cena
void Renderizar();
#endif
// Projeto: prj_Dispositivo - Arquivo: motor.h
#ifndef motor_h
#define motor_h
// (...) conteúdo do arquivo
#endif
Esse bloco de código é clássico de qualquer arquivo de cabeçalho. Ele
define uma macro ( motor_h ) que controla a inclusão do conteúdo do
arquivo evitando incluir em duplicidade o conteúdo se a macro já
estiver definida.
// Esta função inicializa o Direct3D
HRESULT initGfx (HWND hJanela);
Essa função inicializa o objeto Direct3d que é responsável por criar
o dispositivo renderizador. Através de HRESULT a função retorna o
status de falha ou sucesso na criação destes dispositivos. A função
conecta o dispositivo renderizador com a janela apontada por hJanela.
// Essa função libera os objetos utilizados
void Limpar();
Todo objeto do Directx utilizado deve ser liberado. Esse trabalho de
limpeza ou liberação é feito na função Limpar().
// Essa função desenha a cena
void Renderizar();
O backbuffer é uma superfície retangular que duplica a configuração
da tela imediata. A imagem é composta no backbuffer e virada
rapidamente para a tela. O trabalho de limpeza do backbuffer e outras
superfícies conectadas com ele ocorre nessa função que também realiza
o trabalho de montar a cena final ( renderização ) e apresentá-la na
tela imediata.
2.1.2 Aspectos globais - Arquivo: motor.cpp
// -----------------------------------------------------------------------------
// Projeto: prj_Dispositivo - arquivo: motor.cpp
// Este programa ilustra como inicializar o Direct3D e
// limpar a tela. Produzido por www.gameprog.com.br
// -----------------------------------------------------------------------------
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
// Inclui as bibliotecas do Direct3D
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
// Variáveis globais
// Representa o dispositivo D3D
LPDIRECT3D9 g_Direct3d = NULL;
// Representa o dispositivo Renderizador
LPDIRECT3DDEVICE9 g_device = NULL;
// Essa variável recebe informação de erro do Directx
HRESULT g_hr = 0;
#include <d3d9.h>
#include <d3dx9.h>
Estes dois arquivos de cabeçalho contém as funcionalidades principais
do Direct3d.
// Inclui as bibliotecas do Direct3D
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
A diretiva #pragma utilizada dessa maneira informa ao linker durante
o processo de montagem do executável final as bibliotecas de origem
das funções do Direct3d que devem ser ligadas à aplicação final.
// Variáveis globais
// Representa o dispositivo D3D
LPDIRECT3D9 g_Direct3d = NULL;
Essa é a forma clássica de declarar um ponteiro para qualquer
interface do directx que será utilizada. Nesse caso g_Direct3d é um
ponteiro que aponta para a interface IDirect3D9 que é responsável
por criar o dispositivo renderizador e verificar capacidades do
adaptador gráfico. O prefixo LP indica que será criado um ponteiro da
interface indicada no restante da nomenclatura.
// Representa o dispositivo Renderizador
LPDIRECT3DDEVICE9 g_device = NULL;
Essa interface é o centro das maiores atenções do directx pois ela é
o motor de produção gráfica que processa a geometria, texturas e
outros aspectos para produzir a imagem final da cena. O dispositivo
renderizador é solicitado em muitas funções portanto é interessante
que a variável desse ponteiro seja global. Ao invés de utilizar as
macros que começam com LP também é possível inicializar os ponteiros
de qualquer interface do directx conforme o exemplo abaixo:
IDirect3DDevice9* g_device = NULL;
Isso é exatamente equivalente a LPDIRECT3DDEVICE9 g_device = NULL;
// Essa variável recebe informação de erro do Directx
HRESULT g_hr = 0;
A grande maioria das funções do directx retorna o status do seu
trabalho pelo tipo HRESULT que indica sucesso ou o tipo de erro
ocorrido. Esta variável foi declarada como global pois é muito
solicitada. Como é global, cada uso particular por uma função vai
apagar o valor anterior, entretanto vai se prestar ao processo de
verificação do momento.
2.2 Inicializando o motor gráfico
// initGfx() - Inicializa o Direct3D
HRESULT initGfx( HWND hJanela )
{
// Cria o objeto D3D que é necessário para criar o dispositivo gráfico
g_Direct3d = Direct3DCreate9( D3D_SDK_VERSION );
// Verifica se objeto Direct3D foi criado
if(g_Direct3d == NULL)
{
MessageBox (NULL,
"Falha na inialização do Direct3D", "InitGfx()", MB_OK);
return E_FAIL;
} // endif
// Declara a variável para os parâmetros de apresentação
D3DPRESENT_PARAMETERS pps;
// Limpa a estrutura
ZeroMemory( &pps, sizeof(pps) );
// Configura os parâmetros de apresentação
// A aplicação vai ter janela
pps.Windowed = TRUE;
// Esse método vira rapidamente o backbuffer para a tela imediata
pps.SwapEffect = D3DSWAPEFFECT_DISCARD;
// Esse formato vai procurar se encaixar no modo de video corrente
pps.BackBufferFormat = D3DFMT_UNKNOWN;
// Configuração do renderizador a ser criado
// Adaptador default (0)
int nAdaptador = D3DADAPTER_DEFAULT;
// Tipo de dispositivo Hardware ou emulador de referência (software)
D3DDEVTYPE dispositivo_tipo = D3DDEVTYPE_HAL;
// Flags de configuração do dispositivo
DWORD create_flags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
// Criamos aqui o dispositivo renderizador
g_hr = g_Direct3d->CreateDevice( nAdaptador, dispositivo_tipo,
hJanela, create_flags, &pps, &g_device );
// Verifica se houve falha no processo
if( FAILED( g_hr ) ) {
MessageBox (NULL, "Falha na criação: g_device", "initGfx()", MB_OK);
return E_FAIL;
} // endif
return S_OK;
} // initGfx().fim
// Cria o objeto D3D que é necessário para criar o dispositivo gráfico
g_Direct3d = Direct3DCreate9( D3D_SDK_VERSION );
A função Direct3DCreate9() cria o objeto Direct3d. O único argumento
é uma macro para garantir que os dispositivos criados vão corresponder
com as dll's instaladas ou com o runtime(*) como se fala. (*) O termo
runtime refere-se às bibliotecas que estão operando em tempo de
execução do programa ou à própria aplicação em tempo de execução.
// Verifica se objeto Direct3D foi criado
if(g_Direct3d == NULL)
{
MessageBox (NULL,
"Falha na inialização do Direct3D", "InitGfx()", MB_OK);
return E_FAIL;
} // endif
Esse bloco de código verifica se o dispositivo Direct3d foi criado
com sucesso. Ainda que o código esteja correto é importante fazer
essa verificação na criação de qualquer coisa pois se o sistema
Windows estiver com problemas nenhum código correto funciona!
// Declara a variável para os parâmetros de apresentação
D3DPRESENT_PARAMETERS pps;
Esta linha cria a estrutura pps do tipo D3DPRESENT_PARAMETERS para
configurar os parâmetros de apresentação que definem aspectos de
elaboração da apresentação da imagem final. Por exemplo, a aplicação
vai ter janela? Qual o tamanho e quantos backbuffer's vão haver?
Como vai ocorrer a troca de dados entre o backbuffer e a tela? Então,
os parâmetros de apresentação definem as respostas para questões
como estas e outras afins.
// Limpa a estrutura
ZeroMemory( &pps, sizeof(pps) );
A estrutura é limpa com esta linha.
// Configura os parâmetros de apresentação
// A aplicação vai ter janela
pps.Windowed = TRUE;
Esta linha indica que a aplicação vai ser janelada. Para aplicações
em tela cheia o valor desse argumento deve ser FALSE junto com outras
configurações adicionais que vão ser esclarecidas mais adiante neste
curso.
// Esse formato vai procurar se encaixar no modo de video corrente
pps.BackBufferFormat = D3DFMT_UNKNOWN;
Aqui o formato do backbuffer é selecionado. O formato do backbuffer
é baseado em como a cor é expressa pela aplicação, por exemplo, o
popular RGB no qual cada componente de cor tem 8 bits é visto assim
do ponto de vista de formatação do backbuffer: R8G8B8, que totaliza
24 bits para cada pixel. Isso é um exemplo hipotético apenas para
ilustrar neste momento o conceito de formato de uma superfície
desenhável como o backbuffer, a textura ou a própria tela. Voltando
ao código D3DFMT_UNKNOWN deixa o directx selecionar o formato que é
compatível com a janela. Mais adiante no curso vamos conhecer outros
formatos possíveis.
// Esse método vira rapidamente o backbuffer para a tela imediata
pps.SwapEffect = D3DSWAPEFFECT_DISCARD;
Por questões de qualidade e performance a renderização nunca ocorre
diretamente na tela imediata. A imagem é composta gradualmente nos
bastidores de uma área de memória que duplica a configuração da
tela, o famoso backbuffer, e depois de pronta esta imagem é
copiada ou virada rapidamente para a tela. Pode haver uma coleção de
backbuffers trabalhando circularmente e neste caso dizemos que existe
uma corrente de trocas ou uma swap chain em inglês. A imagem pode
ser sequencialmente copiada de backbuffer em backbuffer até chegar na
tela imediata ou pode ser virada que é uma operação que apenas
modifica o endereço do ponteiro que aponta para o próximo backbuffer
a ser renderizado. A opção D3DSWAPEFFECT_DISCARD deixa o directx
escolher a forma de troca de imagens na corrente de trocas com a
melhor performance para a aplicação que pode ser virada ou copiada.
Acabamos com a explanação de alguns itens dos parâmetros de
apresentação e na sequência temos outros argumentos que vão aparecer
na função que cria o dispositivo renderizador.
// Configuração do renderizador a ser criado
// Adaptador default (0)
int nAdaptador = D3DADAPTER_DEFAULT;
O adaptador indica qual a placa de vídeo ou saída de vídeo que será
usada. O dispositivo primário ou adaptador default tem valor zero(0).
// Tipo de dispositivo Hardware ou emulador de referência (software)
D3DDEVTYPE dispositivo_tipo = D3DDEVTYPE_HAL;
Essa linha indica o tipo de dispositivo que será usado que pode ser
a placa aceleradora (D3DDEVTYPE_HAL) ou o lento emulador de referência
indicado pelo valor D3DDEVTYPE_REF definido na enumeração D3DDEVTYPE.
O emulador de referência implementa todas as funcionalidades do
directx porém ele é muito lento e serve apenas para testes.
// Flags de configuração do dispositivo
DWORD create_flags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
Esta linha configura os flags de criação cujas opções podem ser
concatenadas com um ou binário (|). A configuração acima indica que o
processamento de vértices vai ser feita pelo directx. Na configuração
ideal o processamento de vértices é feito puramente e totalmente no
hardware. A configuração ideal é expressa por essa combinação:
D3DCREATE_PUREDEVICE | D3DCREATE_HARDWARE_VERTEXPROCESSING
Depois de tudo configurado a função CreateDevice() cria o dispositivo
e é prudente depois fazer a verificação do resultado dessa operação:
// Criamos aqui o dispositivo renderizador
g_hr = g_Direct3d->CreateDevice( nAdaptador, dispositivo_tipo,
hJanela, create_flags, &pps, &g_device );
Note que os parâmetros de apresentação (&pps) e o ponteiro do
dispositivo (&g_device) são passados como referência. Isso é rotina
na criação de objetos do directx.
// Verifica se houve falha no processo
if( FAILED( g_hr ) ) {
MessageBox (NULL, "Falha na criação: g_device", "initGfx()", MB_OK);
return E_FAIL;
} // endif
return S_OK;
Estando tudo ok a função initGfx() retorna o sinal de sucesso das
operações do directx: S_OK.
2.3 Renderizando a cena
// -----------------------------------------------------------------------------
// Renderizar() - Desenha a cena
// -----------------------------------------------------------------------------
VOID Renderizar()
{
// Retorne se o dispositivo estiver nulo
if( g_device == NULL) return;
// Cor de fundo da janela
const D3DCOLOR branco = D3DCOLOR_XRGB( 255, 255, 255 );
const D3DCOLOR cor_fundo = branco;
// Superfícies que serão limpas
DWORD superficies = D3DCLEAR_TARGET;
// Configuração padrão do buffer de profundidade
float zBuffer = 1.0f;
// Configuração padrão do stencil
DWORD sBuffer = 0;
// Quantidade de retângulos a serem limpos
DWORD nPartes = 0;
// Array de retângulos que serão limpos
const D3DRECT* pPartes = NULL;
// Limpa o backbuffer com uma cor branca
g_device->Clear( nPartes, pPartes, superficies, cor_fundo,
zBuffer, sBuffer);
// Começa a cena
if( SUCCEEDED( g_device->BeginScene() ) )
{
// Todo código de desenhar ocorre aqui dentro
// Finalizando a cena
g_device->EndScene();
} // endif
// Apresenta o conteúdo do backbuffer na tela
// Retangulo fonte e destino
const RECT* rFonte = NULL;
const RECT* rDestino = NULL;
// Janela alternativa para apresentar os dados visuais
HWND hOutraJanela = NULL;
g_device->Present( rFonte, rDestino, hOutraJanela, NULL);
} // Renderizar().fim
// Retorne se o dispositivo estiver nulo
if( g_device == NULL) return;
O primeiro cuidado da função Renderizar() é sair caso o dispositivo
esteja NULL. Esse cuidado é importante pois a aplicação pode perder o
dispositivo para outras aplicações que estejam rodando simultaneamente
para as quais o usuário pode alternar o seu foco de atenção.
// Cor de fundo da janela
const D3DCOLOR branco = D3DCOLOR_XRGB( 255, 255, 255 );
const D3DCOLOR cor_fundo = branco;
A cor branco vai ser a cor de fundo ou de limpeza da janela. O padrão
de cor do directx é o ARGB de 32 bits com um canal alpha responsável
pela taxa de transparência da cor. A cor totalmente opaca tem este
valor configurado como 255 ou 0xFF em hexadecimal. A macro
D3DCOLOR_XRGB() recebe uma cor RGB mas retorna uma cor ARGB com o
canal alpha configurado para 255.
// Superfícies que serão limpas
DWORD superficies = D3DCLEAR_TARGET;
O termo superfície define uma área retangular de memória para imagens,
textura, o backbuffer ou ainda a tela imediata. Além do backbuffer
existe uma área retangular chamada zBuffer que indica qual pixel está
na frente do outro no caso de haver dois pixels querendo ocupar uma
mesma posição na tela. Outra superfície retangular é o stencil que
indica qual ponto que deve ser desenhado e como deve ser desenhado no
caso de haver dois pixels disputando a mesma posição na tela. O
zBuffer ou buffer de profundidade como é normalmente conhecido é mais
usado para correção da visualização da cena enquanto que o stencil é
usado para efeitos especiais de espelhamento, sombra, mistura de cor
ou cortes propositais na visualização da cena. O argumento aqui em
questão vai limpar a superfície alvo ( D3DCLEAR_TARGET ) que neste
caso é o backbuffer que por tabela vai limpar a tela pois o backbuffer
é despejado sobre ela. Este flag pode receber outras configurações
para limpar o stencil e o buffer de profundidade. Geralmente o buffer
de profundidade e o stencil habitam na mesma superfície com cada um
ocupando alguma porção do mesmo tipo de dado e a superfície como um
todo tendo uma correspondência com o pixel final desenhado sobre a
tela.
O estudo do stencil será feito no nosso curso avançado de DirectX 9.
// Configuração padrão do buffer de profundidade
float zBuffer = 1.0f;
A configuração padrão do buffer de profundidade é 1.0f.
// Configuração padrão do stencil
DWORD sBuffer = 0;
A configuração padrão do stencil é zero (0).
// Quantidade de retângulos a serem limpos
DWORD nPartes = 0;
Geralmente a superfície toda é limpa. Entretanto é possível escolher
partes quadriculadas para a ocorrência da limpeza. Esta variável
indica quantos retangulos estão presentes na array de retangulos que
indicam as porções da tela que devem ser limpas. Para a tela toda
esse argumento é configurado como zero (0) e a array que lhe
corresponde é configurada como NULL.
// Array de retângulos que serão limpos
const D3DRECT* pPartes = NULL;
Esta aqui é a array de retangulos que indicam as áreas para serem
limpas. Deixe configurado como NULL para limpar a tela toda.
// Limpa o backbuffer com uma cor branca
g_device->Clear( nPartes, pPartes, superficies, cor_fundo,
zBuffer, sBuffer);
Esta função realiza a limpeza das superfícies indicadas e resseta o
buffer de profundidade e o stencil para valores padrões.
// Começa a cena
if( SUCCEEDED( g_device->BeginScene() ) )
{
// Todo código de desenhar ocorre aqui dentro
// Finalizando a cena
g_device->EndScene();
} // endif
O processo de desenhar qualquer objeto 3d ou texto deve ocorrer entre
esse par de funções BeginScene() e EndScene() do dispositivo
renderizador ( g_device ). Geralmente, de dentro desse bloco de
código você vai chamar outras funções que desenham alguma geometria
ou texto.
// Apresenta o conteúdo do backbuffer na tela
// Retangulo fonte e destino
const RECT* rFonte = NULL;
const RECT* rDestino = NULL;
Depois de EndScene() o backbuffer está pronto para ser transferido
para a tela. A função de apresentar a cena também permite através dos
argumentos rFonte e rDestino que apenas parte do backbuffer seja
transferido para parte da tela. Mas quase sempre queremos copiar todo
o backbuffer para toda a tela e para isso esses parâmetros devem ser
configurados como NULL.
// Janela alternativa para apresentar os dados visuais
HWND hOutraJanela = NULL;
Alternativamente a função que apresenta a cena permite apontar para
outra janela diferente da que foi pré-configurada para o dispositivo.
Para manter a janela original que deu muito trabalho para ser
confeccionada o valor desse argumento deve ser NULL.
g_device->Present( rFonte, rDestino, hOutraJanela, NULL);
Esta é a função que apresenta a cena finalizando todo o trabalho de
renderização. O último argumento que está definido como NULL é um
retangulo que define a região mínima da tela que deve ser atualizada;
este valor NULL indica que queremos que toda a tela seja atualizada.
Reforçamos que todos os quatro argumentos de Present() devem ser
configurados como NULL para transferência total da área do backbuffer
para a área total da tela. Se a aplicação vai trabalhar com áreas
parciais como permitem os argumentos de Clear() e Present(), a
propriedade SwapEffect dos parâmetros de apresentação deve ser
configurada como D3DSWAPEFFECT_COPY.
2.4 Limpando o ambiente
// Limpar() - Libera todos os objetos previamente inicializados
// -----------------------------------------------------------------------------
VOID Limpar()
{
// Libera o dispositivo gráfico
if( g_device != NULL) g_device->Release();
// Libera o motor do Direct3D
if( g_Direct3d != NULL) g_Direct3d->Release();
} // Limpar().fim
// Libera o dispositivo gráfico
if( g_device != NULL) g_device->Release();
// Libera o motor do Direct3D
if( g_Direct3d != NULL) g_Direct3d->Release();
Este bloco de código ilustra como se deve liberar com segurança os
objetos criados. A função Limpar() é chamada no processo de
finalização da aplicação no evento de WM_DESTROY e no pressionamento
da tecla Escape no evento WM_KEYDOWN. Os objetos subordinados ao
Direct3d devem ser liberados primeiro e por último o Direct3d
( g_Direct3d ).
2.5 Aspectos globais de inicialização da aplicação
//-----------------------------------------------------------------------------
// Projeto: prj_Dispositivo - arquivo: entrada.cpp
// Este programa ilustra como inicializar o Direct3D e
// limpar a tela. Produzido por www.gameprog.com.br
//-----------------------------------------------------------------------------
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include "motor.h"
// Declaração da função que atende as mensagens da janela
LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem,
WPARAM wParam, LPARAM lParam);
// Variável global da classe da janela
char sclasseJanela[ ] = "cls_directx";
// Dimensões da janela
int g_xtela = 320;
int g_ytela = 240;
#include <d3d9.h>
#include <d3dx9.h>
Estes arquivos foram inseridos no arquivo entrada.cpp para o
aproveitamento do elenco de valores de retorno do directx através de
HRESULT
#include "motor.h"
Este arquivo foi inserido em entrada.cpp porque este módulo precisa
conhecer a assinatura de initGfx(). Nos próximos tópicos, este
arquivo vai trazer também a assinatura de processaJanela() que vai
ser transferida e vai compor um peça ativa do 'motor' das nossas
aplicações.
// Dimensões da janela
int g_xtela = 320; int g_ytela = 240;
Estas variáveis são globais e serão aproveitadas em outras partes de
aplicações futuras neste curso.
2.6 Criando a janela da aplicação
int WINAPI WinMain (HINSTANCE app_instancia, HINSTANCE app_anterior,
LPSTR sComando,int nExibir) {
// alça da janela
HWND hJanela;
// Estrutura de recepção das mensagens
MSG mensagem;
// Estrutura de descrição da janela
WNDCLASSEX wcls;
// Estrutura que descreve a janela
wcls.hInstance = app_instancia;
wcls.lpszClassName = sclasseJanela;
wcls.lpfnWndProc = processaJanela;
wcls.style = CS_HREDRAW | CS_VREDRAW;
wcls.cbSize = sizeof (WNDCLASSEX);
// O cursor e os ícones da aplicação são default
wcls.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wcls.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wcls.hCursor = LoadCursor (NULL, IDC_ARROW);
// Aplicação sem menu
wcls.lpszMenuName = NULL;
// Nada de espaço extra atrelado a classe da janela (wcls)
wcls.cbClsExtra = 0;
// Nada de espaço extra atrelado a janela
wcls.cbWndExtra = 0;
// Cor default da janela
wcls.hbrBackground = ( HBRUSH) COLOR_BACKGROUND;
// Registra a janela e retorna se esta operação falhar
int status = RegisterClassEx (&wcls);
if(status == 0) {
MessageBox (NULL, "Registro falhou!", "prj_Dispositivo", MB_OK);
return 0;
} // endif
// Com a classe criada pode-se criar a janela
DWORD estiloExtra = 0;
const char janelaTitulo[] = "prj_Dispositivo";
DWORD controleEstilo = WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX;
int xpos = 160;
int ypos = 120;
HWND hjanelaPai = HWND_DESKTOP;
HMENU sem_menu = NULL;
LPVOID dadoExtra = NULL;
// Cria a janela
hJanela = CreateWindowEx(estiloExtra, sclasseJanela, janelaTitulo,
controleEstilo, xpos, xpos, g_xtela, g_ytela, hjanelaPai, sem_menu,
app_instancia, dadoExtra );
// Verifica se janela foi criada
if(hJanela == NULL) {
MessageBox (NULL, "Falha na criação da janela!", "prj_Dispositivo", MB_OK);
return 0;
} // endif
// Essa variável recebe informação de erro do Directx
HRESULT hr;
// Inicia o Direct3D
hr = initGfx ( hJanela);
// Encerre a aplicação se houve falha
if(FAILED (hr) ) {
MessageBox (hJanela,
"Direct3D: falha na inicialização", "prj_Dispositivo", MB_OK);
UnregisterClass( sclasseJanela, wcls.hInstance);
return E_FAIL;
} // endif
// Mostra a janela
ShowWindow (hJanela, nExibir);
UpdateWindow (hJanela);
// Rode a bombeamento de mensagens até GetMessage() retornar 0
while (GetMessage (&mensagem, NULL, 0, 0) )
{
// Traduz mensagem de tecla virtual em mensagem de caracteres
TranslateMessage(&mensagem);
// Despacha a mensagem para a função processaJanela */
DispatchMessage(&mensagem);
} // endwhile
// O valor de retorno é zero(0) passado por PostQuitMessage()
UnregisterClass( sclasseJanela, wcls.hInstance);
return mensagem.wParam;
} // WinMain().fim
DWORD controleEstilo = WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX;
Aqui é interessante notar como foi inserido o botão de minimizar na
janela ( WS_MINIMIZEBOX ).
// Cria a janela
hJanela = CreateWindowEx( estiloExtra, sclasseJanela, janelaTitulo,
controleEstilo, xpos, xpos, g_xtela, g_ytela, hjanelaPai, sem_menu,
app_instancia, dadoExtra );
// Essa variável recebe informação de erro do Directx
HRESULT hr;
// Inicia o Direct3D
hr = initGfx ( hJanela);
A função initGfx() passa a janela ( hJanela ) para a frente na qual
os gráficos serão renderizados. A variável hr recebe o resultado do
processo de inicialização dos dispositivos gráficos. Na sequência,
este resultado é verificado no código abaixo para encerrar a aplicação
caso tenha ocorrido falha.
// Encerre a aplicação se houve falha
if( FAILED (hr) ) {
MessageBox ( hJanela,
"Direct3D: falha na inicialização", "prj_Dispositivo", MB_OK);
UnregisterClass( sclasseJanela, wcls.hInstance);
return E_FAIL;
} // endif
2.7 Processamento de mensagens
// Esta função é chamada por DispatchMessage()
LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem,
WPARAM wParam, LPARAM lParam)
{
switch (mensagem)
{
case WM_DESTROY:
// Coloca uma mensagem WM_QUIT na fila de mensagem
Limpar();
PostQuitMessage (0);
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
{
Limpar();
PostQuitMessage( 0);
} // endif
break;
// Essa mensagem vai ocorrer a todo momento
case WM_PAINT:
// Renderiza a cena
Renderizar();
// Invalida a tela para chamar WM_PAINT novamente
InvalidateRect( hJanela, NULL, false);
break;
// Processamento default de mensagens não tratada pela aplicação
default:
return DefWindowProc (hJanela, mensagem, wParam, lParam);
} // endswitch
return 0;
} // processaJanela().fim
case WM_DESTROY:
// Coloca uma mensagem WM_QUIT na fila de mensagem
Limpar();
PostQuitMessage (0);
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
{
Limpar();
PostQuitMessage( 0);
} // endif
break;
Aqui nestes case's do switch, a função Limpar() é chamada um pouco
antes da finalização da aplicação para liberar os objetos do directx
utilizados.
// Essa mensagem vai ocorrer a todo momento
case WM_PAINT:
// Renderiza a cena
Renderizar();
// Invalida a tela para chamar WM_PAINT novamente
InvalidateRect( hJanela, NULL, false);
break;
A mensagem WM_PAINT ocorre quando a janela precisa ter o seu conteúdo
desenhado. Esse evento ocorre quando a janela foi coberta parcialmente
por outra aplicação e precisa ter parte do seu retangulo ou área
cliente restaurada. Geralmente o Windows informa qual parte da janela
deve ser restaurada passando essa parte em um chamado retangulo
inválido. Mas em nosso caso queremos desenhar continuamente em toda a
janela e não apenas para consertar parcialmente seu conteúdo visual.
Então, chamamos InvalidateRect() dentro de WM_PAINT para gerar outro
WM_PAINT e assim chamar a função Renderizar() continuamente. O NULL
dentro desta função indica que toda a janela deve ser atualizada,
e o false impede que essa função apague o background já que isso é
feito por g_device->Clear().
3. Código fonte do projeto de exemplo: prj_Dispositivo
// Projeto: prj_Dispositivo - Arquivo: motor.h
#ifndef motor_h
#define motor_h
// Esta função inicializa o Direct3D
HRESULT initGfx (
HWND hJanela);
// Essa função libera os objetos utilizados
void Limpar();
// Essa função desenha a cena
void Renderizar();
#endif
// -----------------------------------------------------------------------------
// Projeto: prj_Dispositivo - arquivo: motor.cpp
// Este programa ilustra como inicializar o Direct3D e
// limpar a tela. Produzido por www.gameprog.com.br
// -----------------------------------------------------------------------------
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
// Inclui as bibliotecas do Direct3D
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
// Variáveis globais
// Representa o dispositivo D3D
LPDIRECT3D9 g_Direct3d = NULL;
// Representa o dispositivo Renderizador
LPDIRECT3DDEVICE9 g_device = NULL;
// Essa variável recebe informação de erro do Directx
HRESULT g_hr = 0;
// initGfx() - Inicializa o Direct3D
HRESULT initGfx( HWND hJanela )
{
// Cria o objeto D3D que é necessário para criar o dispositivo gráfico
g_Direct3d = Direct3DCreate9( D3D_SDK_VERSION);
// Verifica se objeto Direct3D foi criado
if(g_Direct3d == NULL)
{
MessageBox (NULL,
"Falha na inialização do Direct3D", "InitGfx()", MB_OK);
return E_FAIL;
} // endif
// Declara a variável para os parâmetros de apresentação
D3DPRESENT_PARAMETERS pps;
// Limpa a estrutura
ZeroMemory( &pps, sizeof(pps) );
// Configura os parâmetros de apresentação
// A aplicação vai ter janela
pps.Windowed = TRUE;
// Esse método vira rapidamente o backbuffer para a tela imediata
pps.SwapEffect = D3DSWAPEFFECT_DISCARD;
// Esse formato vai procurar se encaixar no modo de video corrente
pps.BackBufferFormat = D3DFMT_UNKNOWN;
// Configuração do renderizador a ser criado
// Adaptador default (0)
int nAdaptador = D3DADAPTER_DEFAULT;
// Tipo de dispositivo Hardware ou emulador de referência (software)
D3DDEVTYPE dispositivo_tipo = D3DDEVTYPE_HAL;
// Flags de configuração do dispositivo
DWORD create_flags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
// Criamos aqui o dispositivo renderizador
g_hr = g_Direct3d->CreateDevice( nAdaptador, dispositivo_tipo,
hJanela, create_flags, &pps, &g_device );
// Verifica se houve falha no processo
if( FAILED( g_hr ) ) {
MessageBox (NULL, "Falha na criação: g_device", "initGfx()", MB_OK);
return E_FAIL;
} // endif
return S_OK;
} // initGfx().fim
// Limpar() - Libera todos os objetos previamente inicializados
// -----------------------------------------------------------------------------
VOID Limpar()
{
// Libera o dispositivo gráfico
if( g_device != NULL) g_device->Release();
// Libera o motor do Direct3D
if( g_Direct3d != NULL) g_Direct3d->Release();
} // Limpar().fim
// -----------------------------------------------------------------------------
// Renderizar() - Desenha a cena
// -----------------------------------------------------------------------------
VOID Renderizar()
{
// Retorne se o dispositivo estiver nulo
if( g_device == NULL) return;
// Cor de fundo da janela
const D3DCOLOR branco = D3DCOLOR_XRGB( 255, 255, 255 );
const D3DCOLOR cor_fundo = branco;
// Superfícies que serão limpas
DWORD superficies = D3DCLEAR_TARGET;
// Configuração padrão do buffer de profundidade
float zBuffer = 1.0f;
// Configuração padrão do stencil
DWORD sBuffer = 0;
// Quantidade de retângulos a serem limpos
DWORD nPartes = 0;
// Array de retângulos que serão limpos
const D3DRECT* pPartes = NULL;
// Limpa o backbuffer com uma cor branca
g_device->Clear( nPartes, pPartes, superficies, cor_fundo,
zBuffer, sBuffer);
// Começa a cena
if( SUCCEEDED( g_device->BeginScene() ) )
{
// Todo código de desenhar ocorre aqui dentro
// Finalizando a cena
g_device->EndScene();
} // endif
// Apresenta o conteúdo do backbuffer na tela
// Retangulo fonte e destino
const RECT* rFonte = NULL;
const RECT* rDestino = NULL;
// Janela alternativa para apresentar os dados visuais
HWND hOutraJanela = NULL;
g_device->Present( rFonte, rDestino, hOutraJanela, NULL);
} // Renderizar().fim
//-----------------------------------------------------------------------------
// Projeto: prj_Dispositivo - arquivo: entrada.cpp
// Este programa ilustra como inicializar o Direct3D e
// limpar a tela. Produzido por www.gameprog.com.br
//-----------------------------------------------------------------------------
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include "motor.h"
// Declaração da função que atende as mensagens da janela
LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem,
WPARAM wParam, LPARAM lParam);
// Variável global da classe da janela
char sclasseJanela[ ] = "cls_directx";
// Dimensões da janela
int g_xtela = 320;
int g_ytela = 240;
int WINAPI WinMain (HINSTANCE app_instancia, HINSTANCE app_anterior,
LPSTR sComando,int nExibir) {
// alça da janela
HWND hJanela;
// Estrutura de recepção das mensagens
MSG mensagem;
// Estrutura de descrição da janela
WNDCLASSEX wcls;
// Estrutura que descreve a janela
wcls.hInstance = app_instancia;
wcls.lpszClassName = sclasseJanela;
wcls.lpfnWndProc = processaJanela;
wcls.style = CS_HREDRAW | CS_VREDRAW;
wcls.cbSize = sizeof (WNDCLASSEX);
// O cursor e os ícones da aplicação são default
wcls.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wcls.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wcls.hCursor = LoadCursor (NULL, IDC_ARROW);
// Aplicação sem menu
wcls.lpszMenuName = NULL;
// Nada de espaço extra atrelado a classe da janela (wcls)
wcls.cbClsExtra = 0;
// Nada de espaço extra atrelado a janela
wcls.cbWndExtra = 0;
// Cor default da janela
wcls.hbrBackground = ( HBRUSH) COLOR_BACKGROUND;
// Registra a janela e retorna se esta operação falhar
int status = RegisterClassEx (&wcls);
if(status == 0) {
MessageBox (NULL, "Registro falhou!", "prj_Dispositivo", MB_OK);
return 0;
} // endif
// Com a classe criada pode-se criar a janela
DWORD estiloExtra = 0;
const char janelaTitulo[] = "prj_Dispositivo";
DWORD controleEstilo = WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX;
int xpos = 160;
int ypos = 120;
HWND hjanelaPai = HWND_DESKTOP;
HMENU sem_menu = NULL;
LPVOID dadoExtra = NULL;
// Cria a janela
hJanela = CreateWindowEx(estiloExtra, sclasseJanela, janelaTitulo,
controleEstilo, xpos, xpos, g_xtela, g_ytela, hjanelaPai, sem_menu,
app_instancia, dadoExtra );
// Verifica se janela foi criada
if(hJanela == NULL) {
MessageBox (NULL, "Falha na criação da janela!", "prj_Dispositivo", MB_OK);
return 0;
} // endif
// Essa variável recebe informação de erro do Directx
HRESULT hr;
// Inicia o Direct3D
hr = initGfx ( hJanela);
// Encerre a aplicação se houve falha
if(FAILED (hr) ) {
MessageBox (hJanela,
"Direct3D: falha na inicialização", "prj_Dispositivo", MB_OK);
UnregisterClass( sclasseJanela, wcls.hInstance);
return E_FAIL;
} // endif
// Mostra a janela
ShowWindow (hJanela, nExibir);
UpdateWindow (hJanela);
// Rode a bombeamento de mensagens até GetMessage() retornar 0
while (GetMessage (&mensagem, NULL, 0, 0) )
{
// Traduz mensagem de tecla virtual em mensagem de caracteres
TranslateMessage(&mensagem);
// Despacha a mensagem para a função processaJanela */
DispatchMessage(&mensagem);
} // endwhile
// O valor de retorno é zero(0) passado por PostQuitMessage()
UnregisterClass( sclasseJanela, wcls.hInstance);
return mensagem.wParam;
} // WinMain().fim
// Esta função é chamada por DispatchMessage()
LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem,
WPARAM wParam, LPARAM lParam)
{
switch (mensagem)
{
case WM_DESTROY:
// Coloca uma mensagem WM_QUIT na fila de mensagem
Limpar();
PostQuitMessage (0);
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
{
Limpar();
PostQuitMessage( 0);
} // endif
break;
// Essa mensagem vai ocorrer a todo momento
case WM_PAINT:
// Renderiza a cena
Renderizar();
// Invalida a tela para chamar WM_PAINT novamente
InvalidateRect( hJanela, NULL, false);
break;
// Processamento default de mensagens não tratada pela aplicação
default:
return DefWindowProc (hJanela, mensagem, wParam, lParam);
} // endswitch
return 0;
} // processaJanela().fim