Preparando o Dispositivo
por :Murilo Maciel Curti
Este artigo tem como objetivo mostrar como dar o primeiro passo no desenvolvimento de Jogos e interfaces gráficas avançadas utilizando a linguagem C++ com a API gráfica DirectX.
Veremos aqui como preparar o nosso "Dispositivo"(Device) para receber a renderização de nossas cenas.
Utilizarei aqui o Visual C++ 2005, que atualmente se encontra dem versão Beta e pode sofrer algumas alterações até seu lançamento, mas acredito que nada que possa atrapalhar a utilização deste artigo.
Antes de mais nada é necessários prepararmos o nosso projeto Win32 para trabalhar com o DirectX nesta versão, pois esta versão vem com uma parâmetro que pode gerar muita dor de cabeça.
Nas propriedades do projeto faça:
Configuration Properties/General/Character Set -> Not Set
Podmos já deixar o projeto preparado para as possíveis dependências, em
Configuration Properties/Linker/Input -> d3dxof.lib dxguid.lib d3dx9d.lib d3d9.lib winmm.lib
Iniciamos com a inclusão das bibliotecas necessaries, para trabalharmos simplesmente com o Dispositivo, utilizamos o cabeçalho padrão do Direct 3D.
#include
#include
Estamos iniciando uma classe que precisa ter uma identificação perante o sistema operacional, podemos definir uma macro que carregue esta identificação para ser utilizada na inicialização e encerramento da janela.
#define SHARP_CLASS_NAME "SharpDeviceWindow"
A utilização desta macro facilita a leitura e manutenção do código, veremos mais à frente que ela é utilizada em alguns pontos essenciais do programa.
Agora preciamos começar a pensar no nosso Device, e para ele ser utilizado por toda o programa declaramos como globais um objeto do Direct3D que cuidará da renderização das cenas no nosso Device.
LPDIRECT3D9 g_pD3D = NULL; // Used to create the D3DDevice
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; // Our rendering device
Objetos prontos e “inicializados” precisamos crier uma função que se responsabilizará pela inicialização do Direct3D. O seu valor de retorno é HRESULT, onde devolveremos S_OK se conseguirmos inicializar o Direct3D com sucesso ou E_FAIL se ocorrer algum erro, assim podemos fazer um tratamento eficaz sobre o estado do nosso dispositivo.
Começamos iniciando o objeto responsável pela criação do nosso dispositivo, é necessário termos uma instância válida para que possamos passer os dados corretos sobre a compatibilidade de hardware para o dispositivo.
HRESULT InitD3D( HWND hWnd )
{
// Cria o objeto do Direct3D
if((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
return E_FAIL;
Obtendo êxito na criação do objeto, podemos declarar a estrutura D3DPRESENT_PARAMETERS, que é responsável pelas propriedades da tela. Com ela podemos definer a quantidade de cores, as configurações dos Buffers, formato de apresentação entre outros.
D3DPRESENT_PARAMETERS d3dpp;
Como alguns parâmetros não serão definidos, existe a necessidade de “zerá-los” para que não passem dados inválidos para o nosso dispositivo, a função ZeroMemory cuida disto, ela faz com que todos os bytes encontrados na região reservada para a estrutura sejam setados para 0, assim podemos trabalhar tranquilamente sem a preocupação com alguma anomalia inesperada.
ZeroMemory(&d3dpp, sizeof(d3dpp));
Passamos um ponteiro que aponta para a região da memória onde é iniciado o espaço de nossa estrutura e utilizamos a função sizeof para informar a quantidade de bytes que correspondem à declaração de nossta estrutura. Atentem para estas funções, elas serão amplamente utilizadas no desenvolvimento de jogos.
O primeiro parâmetro que nos interessa é o responsável pelo modo de apresentação da janela, neste exemplo queremos que ela seja apresentada da forma convencional, uma janela, com os botões padrões.
d3dpp.Windowed = TRUE;
Para este tipo de exibição devemos usar o SwapEffect em sua forma padrão, onde ele informa ao Direct3D que ele deve escolher o melhor metodo para fazer a troca entre back e front buffer. Caso queiramos uma janela em modo FullScreen, devemos tomar alguns cuidados, que serão abordados no próximo artigo.
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
Também Podemos, e devemos definir qual é o formato de cores do nosso Back Buffer. Assim podemos definer se serão utilizadas 16 ou 32 bits de cores. É preciso tomar cuidado com este parâmetro pois nem todos os formatos são compatíveis com as placas de videos, então deixar o Direct3D fazer este trabalho por nós é interessante, pelo menos para este exemplo.
D3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
Assim o Direct3D utilize o formato exibido na tela atualmente, porém este método só é válido para a janela vista como Windowed.
Para outras opções de cores utilize D3DFMT_R5G6B5 para 16 bits e D3DFMT_X8R8G8 para 32 bits.
Para finalizar a função agora vamos crier o nosso dispositivo, que vai ser criado baseando se nos dados da placa padrão D3DADAPTER_DEFAULT, geralmente encontramos apenas uma placa em cada máquina, mas se houver mais de uma não haverá problema algum.
Também instruimos o Direct3D a usar o máximo de processamento de Hardware possível, baseando se nas capacidades do sistema em qual está atuando, tendo assim processamento de software onde houver incompatibilidade e utilizar o máximo de hardware possível, fazendo a mixagem necessária para a rasterização, D3DDEVTYPE_HAL.
Também utilizamos aqui o processamento de vertices via Software, D3DCREATE_SOFTWARE_VERTEXPROCESSING, isto também para efeito de compatibilidade, porém o processamento de vertices via hardware, D3DCREATE_HARDWARE_VERTEXPROCESSING, consegue uma melhor performance, o que hoje em dia está se tornando muito comum devido às placas robustas que estão no Mercado.
Terminamos passando os ponteiros que apontam para nossos parâmetros de apresentação e para o dispositivo que está sendo criado, caso tudo corra bem nosso dispositivo estará criado e pronto para ser utilizado.
if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,&g_pd3dDevice)))
{
return E_FAIL;
}
return S_OK;
}
Antes de continuar a implementação, vamos preparar nossa classe para que ela possa ser totalmente limpa da memória em seu encerramento, no nosso caso, ao final do jogo.
Com a função Cleanup() liberamos os recursos dos nosso dispositivo e do Direct3D que foram utilizados para nossa aplicação, mas aqui podemos centralizer outros recursos a serem “disposados”, é questão de evoluir o projeto.
VOID Cleanup()
{
if(g_pd3dDevice != NULL)
g_pd3dDevice->Release();
if(g_pD3D != NULL)
g_pD3D->Release();
}
...