Curso completo de DirectX 9 com C\C++
Gameprog - Escola de programação de jogos digitais
Contato: gameprog.br@gmail.com
Fase 01-3

index << >>


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

index << >>

Produzido por Gameprog: Jair Pereira - Agosto/2014 © gameprog.br@gmail.com http://www.gameprog.com.br