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

index << >>


07.1 Modificação de textura

1.1 Visão geral
De um ponto de vista prático a textura é apenas um recipiente retangular de cores. A modificação da textura envolve conseguir acesso a sua área retangular que pode ser pensada como uma tabela quadriculada de linhas e colunas onde cada célula tem uma cor. A modificação efetiva da textura vai consistir, em último grau, na reconfiguração da cor de cada ponto. Aliás essa visão simplista da textura enquadra-se naturalmente bem à tela e ao backubuffer. Em última análise estas superfícies retangulares são apenas recipientes de cores. Esse projeto é baseado no projeto prj_Modelo3D do tópico 4.3 desse curso. Fizemos pequenas modificações nesse projeto e acrescentamos duas funções altamente semelhantes que modificam parcialmente ou completamente a área da textura do modelo 3d com tons de cinza. A capacidade de modificar a textura em tempo de execução permite o efeito de texturas animadas. Um exemplo de uso de textura animada consiste em usá-la na simulação de 'letreiros eletrônicos' de informação ou o interior de naves espaciais com luzes que piscam. Ainda, a edição de texturas permite a construção do seu próprio editor de imagens visto que o directx dá um suporte fácil a gravação da imagem no disco. 1.2 Estrutura principal da aplicação
Arquivo: motor.cpp
modificar_Textura() Declara variáveis rgb para receber a cor da textura Declara a variável para montar a cor temporária Declara retângulo que receberá dados de cor da textura Preenche retângulo com dados de cor da textura Faz o cast dos dados recebidos para formato de cor acessível Faz leitura da cor da textura Produz tom de cinza com a média dos canais rgb da cor lida Configura cor temporária com tom de cinza produzido Escreve cor temporária na textura Libera textura ao directx modificar_TexturaEx() Obtém descrição da textura Declara retângulo que receberá dados de cor da textura Declara retângulo-seleção que selecionará região de dados da textura Configura retângulo-seleção com dados da descrição de textura Preenche retângulo com dados de cor da textura com área limitada por retângulo-seleção Faz o cast dos dados recebidos para formato de cor acessível Cálcula tamanho da área da textura com dados da descrição de textura Produz tom de cinza com seleção aleatória Configura cor temporária com tom de cinza produzido Escreve cor temporária na textura Libera textura ao directx CarregarModeloTiny() Declaração do nome completo do arquivo do modelo Declarar um buffer para receber o material Declaração do nome completo do arquivo da textura do modelo Carregar modelo do disco com D3DXLoadMeshFromX() Alocar memória para receber o material Obter material do buffer genérico Colocar material na variável global Carregar textura Liberar o buffer genérico. desenharObjeto() Esta função vai renderizar na tela o objeto da interface ID3DXMesh. Isso envolve a configuração das matrizes parciais de rotação, posição e escala; produzir a matriz final com as matrizes parciais; jogar a matriz final no dispositivo renderizador; configurar o material configurar textura conforme o valor de g_texturaFlag renderizar o subset zero(0). initGfx() chama configurar_cenaEstados() para preparação inicial da cena. chama inicializar_Camera() para a configuração inicial da câmera chama CarregarModeloTiny() para carregar o modelo 3d. Renderizar() Limpa a tela Desliga filtros de textura chama desenharObjeto() para renderizar o modelo 3d. Liga filtros de textura chama desenharObjeto() para renderizar o modelo 3d. Apresenta a cena Limpar() Libera a interface de textura utilizada Libera a memória alocada para o material Libera o objeto da interface ID3DXMesh Libera dispositivo renderizador Libera objeto Direct3d
2.1.1 Aspectos globais - Arquivo: motor.h
//----------------------------------------------------------------------------- // Projeto: prj_ModificarTextura - Arquivo: motor.h // Esta aplicação mostra como modificar uma textura em tempo de execução // By www.gameprog.com.br //----------------------------------------------------------------------------- #include <d3d9.h> #include <d3dx9.h> // Inclui as bibliotecas do Direct3D #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3dx9.lib") // Desliga aviso de 'função insegura' devido ao uso das // funções de string ( strcat etc... ) #pragma warning( disable:4996 ) #ifndef motor_h #define motor_h // Estrutura para receber dados de cor da textura struct ARGBCOR { unsigned char a, r, g, b; }; // Estrutura para controle de rotação e posição do objeto struct Propriedades3d { D3DXVECTOR3 pos; D3DXVECTOR3 rot; }; // 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(); // Monta a câmera da aplicação void inicializar_Camera(void); // Desenha o modelo 3d void desenharObjeto ( ID3DXMesh *obj3d, Propriedades3d *props ); // Carrega o modelo tiny.x void CarregarModeloTiny(); // Configura alguns estados da cena void configurar_cenaEstados(void); // Modifica parcialmente a textura para tons de cinza void modificar_Textura (IDirect3DTexture9 *textura); // Modifica completamente a textura para tons de cinza void modificar_TexturaEx (IDirect3DTexture9 *textura); // Declaração da função que atende as mensagens da janela LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem, WPARAM wParam, LPARAM lParam); #endif
#pragma warning( disable:4996 ) De acordo com os padrões atuais (2014) as antigas funções de string da linguagem C tal como strcat() e outras não são consideradas seguras pois não verificam a violação do espaço de buffer durante a operação de seus processos. Ao usar estas funções padrões o compilador da Microsoft pipoca avisos de que são inseguras; a diretiva #pragma da forma como está configurada acima desliga estes avisos que são inconvenientes para nossas pequenas aplicações. A Microsoft provê para cada função string padrão da linguagem C uma versão segura equivalente mas que preferimos não usar porque estraga a legibilidade e a simplicidade das aplicações de exemplo. struct ARGBCOR { unsigned char a, r, g, b; }; A interface de textura quando libera acesso aos dados de cor libera os dados com esse layout com a cor em formato argb. Esta estrutura é utilizada então para receber os dados de cor liberados pela interface de textura com essa formatação. void Renderizar(); A função renderizar coordena a renderização da cena. Nesta função testamos mais um vez os filtros de texturas que diferenciam o aspecto visual do mesmo modelo renderizado duas vezes. Note que o modelo renderizado à sua direita está com um melhor aspecto visual. void desenharObjeto ( ID3DXMesh *obj3d, Propriedades3d *props ); Essa função vai renderizar o modelo 3d aplicando de acordo com o o valor do flag de controle a textura original (g_texturaFlag = 1) ou a alternativa ( g_texturaFlag = -1 ) criada. void CarregarModeloTiny(); Essa função é uma versão simplificada da função CarregarModelo() que foi adaptada para carregar o modelo tiny.x aonde sabe-se que ele tem apenas um subset, um material e uma textura. void modificar_Textura (IDirect3DTexture9 *textura); Essa função é a primeira demonstração de como modificar a textura.Ela modifica metade da textura lendo a cor de cada texel e produzindo um tom de cinza com o valor médio dos canais r,g,b do texel. Lembramos que o texel é simplesmente cada ponto de cor da textura. void modificar_TexturaEx (IDirect3DTexture9 *textura); Essa função modifica a textura alternativa criada produzindo variados tons da cor cinza que por final produz o efeito de 'tv fora do ar'. LRESULT CALLBACK processaJanela ( // (...) ); Essa função no tratamento do pressionamento da tecla espaço inverte o estado do flag de controle de texturização. 2.1.2 Aspectos globais - Arquivo: motor.cpp
// Recipientes de materiais e texturas do mesh D3DMATERIAL9 *g_meshMtl = NULL; IDirect3DTexture9 *g_meshTex = NULL; IDirect3DTexture9 *g_texturaAlternativa = NULL; // Controle de seleção da textura int g_texturaFlag = 1; // Interface do objeto 3d ID3DXMesh *g_objeto3d = NULL;
D3DMATERIAL9 *g_meshMtl = NULL; Esta variável vai receber o material do mesh. IDirect3DTexture9 *g_meshTex = NULL; Essa variável vai receber a textura original do modelo 3d. IDirect3DTexture9 *g_texturaAlternativa = NULL; Essa variável vai ser utilizada para criar uma textura alternativa que vai ser modificada em cada frame antes da renderização do modelo 3d. int g_texturaFlag = 1; O valor desse flag vai controlar a textura usada no modelo 3d selecionando a textura original (1) ou a textura alternativa (-1). A barra de espaço dispara uma multiplicação por -1 neste flag e assim inverte o seu estado. ID3DXMesh *g_objeto3d = NULL; Esta interface vai servir de recipiente do modelo 3d. 2.2 Carregando o modelo tiny.x
void CarregarModeloTiny() { // Nome final do arquivo do modelo 3d char arquivo_x[] = "\\gameprog\\gdkmedia\\Modelos\\Tiny\\tiny.x"; // Buffer para o pacote de materiais e texturas do modelo 3d ID3DXBuffer *mtlPack; // Carrega modelo 3d com suas texturas e materiais g_hr = D3DXLoadMeshFromX(arquivo_x, D3DXMESH_SYSTEMMEM, g_device, NULL, &mtlPack, NULL, &g_mtlQtd, &g_objeto3d ); // Verifica falha no carregamento do modelo 3d if(FAILED (g_hr) ) { MessageBox (NULL, "Falha no carregamento do modelo", "CarregarModeloTiny()", MB_OK); return; } // endif // Pacote de materiais da biblioteca auxiliar d3dx D3DXMATERIAL *xMtl = (D3DXMATERIAL *) mtlPack->GetBufferPointer(); // Inicializa a array de materiais g_meshMtl = new D3DMATERIAL9[1]; // Copia o ponteiro do material g_meshMtl[0] = xMtl[0].MatD3D; // Configura a cor ambiente do material g_meshMtl[0].Ambient = g_meshMtl[0].Diffuse; // Nome completo da textura do modelo 3d char *arquivo_textura = "\\gameprog\\gdkmedia\\Modelos\\Tiny\\Tiny_skin.bmp"; // Carrega a textura do disco g_hr = D3DXCreateTextureFromFile(g_device, arquivo_textura, &g_meshTex); // Criação da textura alternativa g_hr = D3DXCreateTexture(g_device, 256, 256, 0, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &g_texturaAlternativa); // Verifica falha no carregamento ou criação das texturas if(FAILED (g_hr) ) { MessageBox (NULL, "Falha na criação da textura", "CarregarModeloTiny()", MB_OK); mtlPack->Release(); return; } // endif // Modifica a textura original modificar_Textura (g_meshTex); // Libera o buffer do pacote de materiais mtlPack->Release(); } // CarregarModeloTiny().fim
g_hr = D3DXCreateTexture(g_device, 256, 256, 0, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &g_texturaAlternativa); Esta função cria uma textura compatível com a textura original do modelo 3d. O tamanho da textura é 256x256. O valor zero(0) indica que não haverá níveis superiores de mipmap. Na sequência é encontrado a declaração do uso ( D3DUSAGE_DYNAMIC ), o formato ( D3DFMT_X8R8G8B8 ) e a classe ( D3DPOOL_DEFAULT ) de alojamento na memória da textura criada. modificar_Textura (g_meshTex); Aqui a textura original é modificada parcialmente em torno de 50% assumindo tons de cinza mais especialmente na parte que cobre a parte superior do modelo 3d. 2.3.1 Modificação parcial da textura - modificar_Textura()
void modificar_Textura (IDirect3DTexture9 *textura) { // Componentes rgb para montar depois a cor final unsigned char r, g, b; // Cor temporária ARGBCOR tempcor; // Obtém a área de dados da textura D3DLOCKED_RECT texData; // Acessa os dados da textura textura->LockRect(0, &texData, 0, 0); // Acessa dados de cor ARGBCOR *cores = (ARGBCOR *) texData.pBits; // Escreve a média dos canais de cor na metade da textura for (UINT linha = 0; linha < 32768; linha++) { // Leitura dos componentes rgb r = cores->r; g = cores->g; b = cores->b; // Média para deixar em preto e branco unsigned char media = ( r + g + b ) / 3; // Configura cor tempcor.a = media; tempcor.r = media; tempcor.g = media; tempcor.b = media; // Escreve a cor na área de dados da textura *cores = tempcor; // Avança o ponteiro cores++; } // endfor linha // Libera a textura para o sistema\placa de vídeo textura->UnlockRect (0); } // ModificarTextura().fim
unsigned char r, g, b; Estas variáveis temporárias são usadas para receber os valores dos canais rgb da cor que está na textura. ARGBCOR tempcor; Esta variável é usada para montar a cor final que vai ser escrita na superfície da textura. D3DLOCKED_RECT texData; Esta variável texData é utilizada para receber os dados da textura que são passados para a aplicação através do elemento pBits presente nesta estrutura D3DLOCKED_RECT; pBits é um ponteiro do tipo void* e por conta disso esse ponteiro recebe um cast para se conformar ao formato de dados particular da superfície da textura. textura->LockRect(0, &texData, 0, 0); Esta linha com esta configuração recebe acesso ao total dos dados da textura. ARGBCOR *cores = (ARGBCOR *) texData.pBits; Aqui de fato a aplicação tem acesso aos dados dentro do formato de cor adequado para a leitura e escrita da superfície da textura. for (UINT linha = 0; linha < 32768; linha++) { Aqui montamos um laço para perpassar metade dos dados da textura. O tamanho da textura original do modelo 3d carregado é 256x256 dando uma área total de 65536 pontos de cor no formato argb. r = cores->r; g = cores->g; b = cores->b; Aqui é feito a leitura da cor do ponto sendo examinado. unsigned char media = ( r + g + b ) / 3; Aqui calculamos a média dos componentes rgb para produzir um tom de cinza. tempcor.a = media; tempcor.r = media; tempcor.g = media; tempcor.b = media; Aqui configuramos a cor temporária. *cores = tempcor; Aqui copiamos a cor temporária para um ponto na superfície da textura. cores++; } Aqui avançamos o ponteiro para o próximo ponto e fechamos o laço for. textura->UnlockRect (0); Aqui liberamos de volta ao directx o acesso à textura que foi modificada. 2.3.2 Modificando completamente a textura - modificar_TexturaEx()
void modificar_TexturaEx (IDirect3DTexture9 *textura) { // Obtém a descrição da textura D3DSURFACE_DESC desc; g_hr = textura->GetLevelDesc (0, &desc); // Retângulo para receber os dados de cor da textura D3DLOCKED_RECT texData; // Seleção da região a ser modificada da textura (toda a textura) RECT texSelecao = { 0, 0, 0, 0 }; texSelecao.right = desc.Width; texSelecao.bottom = desc.Height; // Nível de mipmap UINT mipNivel = 0; // Flags de modo de acesso DWORD tranca_flags = 0; // Obtém os dados da textura textura->LockRect(mipNivel, &texData, &texSelecao, tranca_flags); // Faz o cast dos dados obtidos para o formato de cor argb ARGBCOR *cores = (ARGBCOR *) texData.pBits; // Cálcula o total de pontos de cor da textura UINT nTotal = desc.Width * desc.Height; // Escreve uma cor aleatória em cada ponto da textura for (UINT ncx = 0; ncx < nTotal; ncx++) { // Seleciona um valor aleatório unsigned char nval = (unsigned char) rand() % 255; // Configura o valor nos canais de uma cor temporária ARGBCOR tempcor; tempcor.a = nval; tempcor.r = nval; tempcor.g = nval; tempcor.b = nval; // Escreve a cor na área de dados da textura *cores = tempcor; // Avança o ponteiro pra frente cores++; } // endfor linha // Libera a textura para o sistema\placa de vídeo textura->UnlockRect (0); } // ModificarTexturaEx().fim
D3DSURFACE_DESC desc; g_hr = textura->GetLevelDesc (0, &desc); Estas linhas exemplificam a forma de obter informações sobre um determinado nível de mipmap(0) da textura. Vamos obter dessa descrição a largura e altura da textura. Nossa textura só tem o mipmap de nível zero(0) sendo então este nível que representa a superfície principal da textura. D3DLOCKED_RECT texData; Esta variável texData é utilizada para receber os dados da textura que são passados para a aplicação através do elemento pBits presente nesta estrutura D3DLOCKED_RECT; pBits é um ponteiro do tipo void* e por conta disso esse ponteiro recebe um cast para se conformar ao formato de dados particular da superfície da textura. RECT texSelecao = { 0, 0, 0, 0 }; Este retângulo representa a seleção de uma região da textura. texSelecao.right = desc.Width; texSelecao.bottom = desc.Height; Jogamos no retângulo que seleciona a região que queremos da textura a descrição coletada. Com a configuração acima indicamos que queremos a textura toda. UINT mipNivel = 0; DWORD tranca_flags = 0; Aqui estão os outros dois argumentos do método LockRect() da interface de textura. Foram nomeados aqui o nível de mipmap (mipNivel) e a forma de acessar os dados ( tranca_flags ). textura->LockRect(mipNivel, &texData, &texSelecao, tranca_flags); Esta linha com esta configuração que foi montada acima recebe acesso ao total dos dados da textura. ARGBCOR *cores = (ARGBCOR *) texData.pBits; Aqui de fato a aplicação tem acesso aos dados dentro do formato de cor adequado para a leitura e escrita da superfície da textura. UINT nTotal = desc.Width * desc.Height; for (UINT ncx = 0; ncx < nTotal; ncx++) { Aqui calculamos o total de pontos da textura e montamos adequadamente o laço for com esta informação. unsigned char nval = (unsigned char) rand() % 255; Aqui calculamos um valor aleatório para um tom de cinza qualquer. ARGBCOR tempcor; tempcor.a = nval; tempcor.r = nval; tempcor.g = nval; tempcor.b = nval; Aqui configuramos a cor temporária com o tom de cinza ( nval ). *cores = tempcor; Aqui copiamos a cor temporária para um ponto na superfície da textura. cores++; } Aqui avançamos o ponteiro para o próximo ponto e fechamos o laço for. textura->UnlockRect (0); Aqui liberamos de volta ao directx o acesso à textura que foi modificada. 2.4 Renderização da cena
VOID Renderizar() { // Retorne se o dispositivo estiver nulo if( g_device == NULL) return; // Limpa o backbuffer com uma cor branca g_device->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, branco, 1.0f, 0); // Começa a cena if( SUCCEEDED( g_device->BeginScene() ) ) { // Vamos renderizar o modelo da esquerda sem filtros de textura g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_NONE); g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_NONE); g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE); // Configura posição e renderiza g_props.pos = D3DXVECTOR3 (100.0f, 0.2f, -20.0f); g_props.rot = D3DXVECTOR3 (0.0f, 0.0f, 0.0f); desenharObjeto( g_objeto3d, &g_props); // Filtros para suavizar a textura do modelo da direita g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); // Salienta mais a visão das faces g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); // Configura posição e renderiza g_props.pos = D3DXVECTOR3 (-150.0f, 40.0f, -20.0f); desenharObjeto( g_objeto3d, &g_props); // Finalizando a cena g_device->EndScene(); } // endif // Apresenta o conteúdo do backbuffer na tela g_device->Present( NULL, NULL, NULL, NULL); // Isso melhora a performance do sistema Windows Sleep(0); } // Renderizar().fim
g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_NONE); g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_NONE); g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE); g_props.pos = D3DXVECTOR3 (100.0f, 0.2f, -20.0f); g_props.rot = D3DXVECTOR3 (0.0f, 0.0f, 0.0f); desenharObjeto( g_objeto3d, &g_props);
Esse primeiro bloco desliga os filtros de textura e renderiza uma instância do modelo 3d no lado esquerdo da tela.
g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); // Salienta mais a visão das faces g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); // Configura posição e renderiza g_props.pos = D3DXVECTOR3 (-150.0f, 40.0f, -20.0f); desenharObjeto( g_objeto3d, &g_props);
Esse bloco liga os filtros de textura e renderiza uma instância do modelo 3d no lado direito da tela. Repare que a textura fica mais suave e o mesh mantém um contorno mais nítido de sua forma. Sleep(0); O Sleep() permite adicionar uma pausa temporária no programa. O Sleep(0) não pausa mas permite que o Windows aplique um fragmento de atenção aos outros processos que já estavam rodando no sistema e assim mantém a suavidade do sistema ainda que rodando uma aplicação 'pesada' como um jogo que devora muitos recursos. 2.5 Renderização do modelo 3d
void desenharObjeto ( ID3DXMesh *obj3d, Propriedades3d *props) { // Matrizes para controlar posição e rotação do objeto 3d D3DXMATRIX obj_rot; D3DXMATRIX obj_pos; // Matriz para combinar todas as transformações do objeto 3d D3DXMATRIX mtxCombinada; // Vamos inicializar as matrizes para um valor neutro D3DXMatrixIdentity( &obj_rot ); D3DXMatrixIdentity( &obj_pos ); D3DXMatrixIdentity( &mtxCombinada ); // Atualiza ângulo de rotação g_angulo += 0.01f; // Configura rotação do objeto 3d D3DXMatrixRotationYawPitchRoll(&obj_rot, props->rot.y, props->rot.x + g_angulo, props->rot.z); // Ajusta posição do objeto 3d; D3DXMatrixTranslation(&obj_pos, props->pos.x, props->pos.y, props->pos.z); // Tranfere posição e rotação para o mundo D3DXMatrixMultiply (&mtxCombinada, &obj_rot, &obj_pos); // Configura matriz mundo para o dispositivo renderizador g_device->SetTransform( D3DTS_WORLD, &mtxCombinada ); // Renderiza o mesh g_device->SetMaterial(&g_meshMtl[0] ); // Seleciona a textura de acordo com o flag g_texturaFlag // Renderiza com textura original modificada if (g_texturaFlag == 1) g_device->SetTexture(0, g_meshTex); // Renderiza com textura alternativa else { modificar_TexturaEx ( g_texturaAlternativa ); g_device->SetTexture(0, g_texturaAlternativa ); } // endif // Renderiza o mesh obj3d->DrawSubset(0); } // desenharObjeto().fim
if (g_texturaFlag == 1) g_device->SetTexture(0, g_meshTex); else { modificar_TexturaEx ( g_texturaAlternativa ); g_device->SetTexture(0, g_texturaAlternativa ); } // endif obj3d->DrawSubset(0);
Esse bloco renderiza finalmente o modelo aplicando a textura original ou a textura alternativa conforme o valor presente em g_texturaFlag. Note que a textura é modificada por modificar_TexturaEx() antes de ser aplicada no dispositivo renderizador. 3. Código fonte do projeto de exemplo: prj_ModificarTextura
//----------------------------------------------------------------------------- // Projeto: prj_ModificarTextura - Arquivo: motor.h // Esta aplicação mostra como modificar uma textura em tempo de execução // By www.gameprog.com.br //----------------------------------------------------------------------------- #include <d3d9.h> #include <d3dx9.h> // Inclui as bibliotecas do Direct3D #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3dx9.lib") // Desliga aviso de 'função insegura' devido ao uso das // funções de string ( strcat etc... ) #pragma warning( disable:4996 ) #ifndef motor_h #define motor_h // Estrutura para receber dados de cor da textura struct ARGBCOR { unsigned char a, r, g, b; }; // Estrutura para controle de rotação e posição do objeto struct Propriedades3d { D3DXVECTOR3 pos; D3DXVECTOR3 rot; }; // 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(); // Monta a câmera da aplicação void inicializar_Camera(void); // Desenha o modelo 3d void desenharObjeto ( ID3DXMesh *obj3d, Propriedades3d *props ); // Carrega o modelo tiny.x void CarregarModeloTiny(); // Configura alguns estados da cena void configurar_cenaEstados(void); // Modifica parcialmente a textura para tons de cinza void modificar_Textura (IDirect3DTexture9 *textura); // Modifica completamente a textura para tons de cinza void modificar_TexturaEx (IDirect3DTexture9 *textura); // Declaração da função que atende as mensagens da janela LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem, WPARAM wParam, LPARAM lParam); #endif
// ----------------------------------------------------------------------------- // Projeto: prj_ModificarTextura - arquivo: motor.cpp // Esta aplicação mostra como modificar uma textura em tempo de execução // By www.gameprog.com.br // ----------------------------------------------------------------------------- #include <windows.h> #include <d3d9.h> #include <d3dx9.h> #include <time.h> #include <string.h> #include <stdlib.h> #include "motor.h" // Variáveis globais // Representa o dispositivo Direct3D LPDIRECT3D9 g_Direct3d = NULL; // Representa o dispositivo Renderizador IDirect3DDevice9* g_device = NULL; // Recipiente de materiais e texturas do mesh D3DMATERIAL9 *g_meshMtl = NULL; IDirect3DTexture9 *g_meshTex = NULL; IDirect3DTexture9 *g_texturaAlternativa = NULL; // Controle de seleção da textura int g_texturaFlag = 1; // Interface do objeto 3d ID3DXMesh *g_objeto3d = NULL; // Quantidade de materiais no objeto 3d DWORD g_mtlQtd = 0; // Recipientes de cor, rotação e posição do objeto Propriedades3d g_props; // Controla o ângulo de rotação do objeto 3d float g_angulo = 0.0f; // Essa variável recebe informação de erro do Directx HRESULT g_hr = 0; // Tamanho da janela extern int g_xtela; extern int g_ytela; extern HWND hJanela; // Constante para cores const DWORD branco = 0xFFFFFFFF; // Matrizes de configuração da câmera D3DXMATRIX g_mtxMundo; D3DXMATRIX g_mtxVisao; D3DXMATRIX g_mtxProj; void CarregarModeloTiny() { // Nome final do arquivo do modelo 3d char arquivo_x[] = "\\gameprog\\gdkmedia\\Modelos\\Tiny\\tiny.x"; // Buffer para o pacote de materiais e texturas do modelo 3d ID3DXBuffer *mtlPack; // Carrega modelo 3d com suas texturas e materiais g_hr = D3DXLoadMeshFromX(arquivo_x, D3DXMESH_SYSTEMMEM, g_device, NULL, &mtlPack, NULL, &g_mtlQtd, &g_objeto3d ); // Verifica falha no carregamento do modelo 3d if(FAILED (g_hr) ) { MessageBox (NULL, "Falha no carregamento do modelo", "CarregarModeloTiny()", MB_OK); return; } // endif // Pacote de materiais da biblioteca auxiliar d3dx D3DXMATERIAL *xMtl = (D3DXMATERIAL *) mtlPack->GetBufferPointer(); // Inicializa a array de materiais g_meshMtl = new D3DMATERIAL9[1]; // Copia o ponteiro do material g_meshMtl[0] = xMtl[0].MatD3D; // Configura a cor ambiente do material g_meshMtl[0].Ambient = g_meshMtl[0].Diffuse; // Nome completo da textura do modelo 3d char *arquivo_textura = "\\gameprog\\gdkmedia\\Modelos\\Tiny\\Tiny_skin.bmp"; // Carrega a textura do disco g_hr = D3DXCreateTextureFromFile(g_device, arquivo_textura, &g_meshTex); // Criação da textura alternativa g_hr = D3DXCreateTexture(g_device, 256, 256, 0, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &g_texturaAlternativa); // Verifica falha no carregamento ou criação das texturas if(FAILED (g_hr) ) { MessageBox (NULL, "Falha na criação da textura", "CarregarModeloTiny()", MB_OK); mtlPack->Release(); return; } // endif // Modifica a textura original modificar_Textura (g_meshTex); // Libera o buffer do pacote de materiais mtlPack->Release(); } // CarregarModeloTiny().fim void modificar_Textura (IDirect3DTexture9 *textura) { // Componentes rgb para montar depois a cor final unsigned char r, g, b; // Cor temporária ARGBCOR tempcor; // Obtém a área de dados da textura D3DLOCKED_RECT texData; // Acessa os dados da textura textura->LockRect(0, &texData, 0, 0); // Acessa dados de cor ARGBCOR *cores = (ARGBCOR *) texData.pBits; // Escreve a média dos canais de cor na metade da textura for (UINT linha = 0; linha < 32768; linha++) { // Leitura dos componentes rgb r = cores->r; g = cores->g; b = cores->b; // Média para deixar em preto e branco unsigned char media = ( r + g + b ) / 3; // Configura cor tempcor.a = media; tempcor.r = media; tempcor.g = media; tempcor.b = media; // Escreve a cor na área de dados da textura *cores = tempcor; // Avança o ponteiro cores++; } // endfor linha // Libera a textura para o sistema\placa de vídeo textura->UnlockRect (0); } // ModificarTextura().fim void modificar_TexturaEx (IDirect3DTexture9 *textura) { // Obtém a descrição da textura D3DSURFACE_DESC desc; g_hr = textura->GetLevelDesc (0, &desc); // Retângulo para receber os dados de cor da textura D3DLOCKED_RECT texData; // Seleção da região a ser modificada da textura (toda a textura) RECT texSelecao = { 0, 0, 0, 0 }; texSelecao.right = desc.Width; texSelecao.bottom = desc.Height; // Nível de mipmap UINT mipNivel = 0; // Flags de modo de acesso DWORD tranca_flags = 0; // Obtém os dados da textura textura->LockRect(mipNivel, &texData, &texSelecao, tranca_flags); // Faz o cast dos dados obtidos para o formato de cor argb ARGBCOR *cores = (ARGBCOR *) texData.pBits; // Cálcula o total de pontos de cor da textura UINT nTotal = desc.Width * desc.Height; // Escreve uma cor aleatória em cada ponto da textura for (UINT ncx = 0; ncx < nTotal; ncx++) { // Seleciona um valor aleatório unsigned char nval = (unsigned char) rand() % 255; // Configura o valor nos canais de uma cor temporária ARGBCOR tempcor; tempcor.a = nval; tempcor.r = nval; tempcor.g = nval; tempcor.b = nval; // Escreve a cor na área de dados da textura *cores = tempcor; // Avança o ponteiro pra frente cores++; } // endfor linha // Libera a textura para o sistema\placa de vídeo textura->UnlockRect (0); } // ModificarTexturaEx().fim // initGfx() - Inicializa o Direct3D HRESULT initGfx( HWND hJanela ) { // Cria o objeto Direct3D que é necessário para criar o dispositivo gráfico g_Direct3d = Direct3DCreate9( D3D_SDK_VERSION); // Verifica se o 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) ); // Vamos ativar o buffer de profundidade pps.EnableAutoDepthStencil = true; pps.AutoDepthStencilFormat = D3DFMT_D16; // Configura os parâmetros de apresentação // A aplicação vai ter janela pps.Windowed = TRUE; // Esse método transfere 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 // Inicializa o gerador de números aleatórios srand( clock() ); // Configura uma câmera para a aplicação inicializar_Camera(); // Configura alguns estados da cena configurar_cenaEstados(); // Carrega o modelo tiny.x CarregarModeloTiny(); return S_OK; } // initGfx().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: // Inverte a textura aplicada if (wParam == VK_SPACE) g_texturaFlag *= -1; // Encerra a aplicação 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 // Limpar() - Libera todos os objetos previamente inicializados // ----------------------------------------------------------------------------- VOID Limpar() { // Libera a memória de materiais e textura if (g_meshTex != NULL) g_meshTex->Release(); if (g_meshMtl != NULL) delete g_meshMtl; if (g_texturaAlternativa != NULL) g_texturaAlternativa->Release(); // Libera o objeto 3d, o renderizador e o Direct3d if( g_objeto3d != NULL) g_objeto3d->Release(); if( g_device != NULL) g_device->Release(); if( g_Direct3d != NULL) g_Direct3d->Release(); // Anula os ponteiros g_objeto3d = NULL; g_device = NULL; g_Direct3d = NULL; g_meshTex = NULL; g_meshMtl = NULL; g_texturaAlternativa = NULL; } // Limpar().fim // ----------------------------------------------------------------------------- // Renderizar() - Desenha a cena // ----------------------------------------------------------------------------- VOID Renderizar() { // Retorne se o dispositivo estiver nulo if( g_device == NULL) return; // Limpa o backbuffer com uma cor branca g_device->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, branco, 1.0f, 0); // Começa a cena if( SUCCEEDED( g_device->BeginScene() ) ) { // Vamos renderizar o modelo da esquerda sem filtros de textura g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_NONE); g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_NONE); g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE); // Configura posição e renderiza g_props.pos = D3DXVECTOR3 (100.0f, 0.2f, -20.0f); g_props.rot = D3DXVECTOR3 (0.0f, 0.0f, 0.0f); desenharObjeto( g_objeto3d, &g_props); // Filtros para suavizar a textura do modelo da direita g_device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); g_device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); // Salienta mais a visão das faces g_device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); // Configura posição e renderiza g_props.pos = D3DXVECTOR3 (-150.0f, 40.0f, -20.0f); desenharObjeto( g_objeto3d, &g_props); // Finalizando a cena g_device->EndScene(); } // endif // Apresenta o conteúdo do backbuffer na tela g_device->Present( NULL, NULL, NULL, NULL); // Isso melhora a performance do sistema Windows Sleep(0); } // Renderizar().fim void inicializar_Camera(void) { // *************************************************************************** // Inicializa todas as matrizes para elemento neutro D3DXMatrixIdentity( &g_mtxMundo ); D3DXMatrixIdentity( &g_mtxVisao ); D3DXMatrixIdentity( &g_mtxProj ); // *************************************************************************** // Configura a matriz de mundo no dispositivo renderizador g_device->SetTransform( D3DTS_WORLD, &g_mtxMundo ); // *************************************************************************** // Dados para a configuração da matriz de visualização // Aonde está a câmera? - posição da câmera D3DXVECTOR3 cam_pos (0.0f, 0.0f, 700.0f); // Para aonde a câmera está apontada ou olhando? Alvo da câmera D3DXVECTOR3 cam_alvo (0.0f, 0.0f, 0); // A câmera está de cabeça pra baixo? - orientação da câmera D3DXVECTOR3 cam_vetorcima (0.0f, 1.0f, 0.0f); // Configura a matriz de visão D3DXMatrixLookAtLH( &g_mtxVisao, &cam_pos, &cam_alvo, &cam_vetorcima ); // Configura a matriz de visão no dispositivo renderizador g_device->SetTransform( D3DTS_VIEW, &g_mtxVisao ); // *************************************************************************** // Argumentos de configuração da matriz de projeção // Pega o tamanho da área cliente RECT area_cliente; GetWindowRect (hJanela, &area_cliente); g_xtela = area_cliente.right; g_ytela = area_cliente.bottom; // aspecto dos gráficos float aspecto = (float) g_xtela / g_ytela; // campo de visão float campo_visao = D3DX_PI / 4; // Trapézio de visualização da câmera ( Frustrum ) float corte_perto = 1.0f; float corte_longe = 1000.0f; // Configura a matriz de projeção D3DXMatrixPerspectiveFovLH( &g_mtxProj, campo_visao, aspecto, corte_perto, corte_longe); // Configura a matriz de projeção no dispositivo renderizador g_device->SetTransform( D3DTS_PROJECTION, &g_mtxProj ); } // inicializar_Camera().fim void desenharObjeto ( ID3DXMesh *obj3d, Propriedades3d *props) { // Matrizes para controlar posição e rotação do objeto 3d D3DXMATRIX obj_rot; D3DXMATRIX obj_pos; // Matriz para combinar todas as transformações do objeto 3d D3DXMATRIX mtxCombinada; // Vamos inicializar as matrizes para um valor neutro D3DXMatrixIdentity( &obj_rot ); D3DXMatrixIdentity( &obj_pos ); D3DXMatrixIdentity( &mtxCombinada ); // Atualiza ângulo de rotação g_angulo += 0.01f; // Configura rotação do objeto 3d D3DXMatrixRotationYawPitchRoll(&obj_rot, props->rot.y, props->rot.x + g_angulo, props->rot.z); // Ajusta posição do objeto 3d; D3DXMatrixTranslation(&obj_pos, props->pos.x, props->pos.y, props->pos.z); // Tranfere posição e rotação para o mundo D3DXMatrixMultiply (&mtxCombinada, &obj_rot, &obj_pos); // Configura matriz mundo para o dispositivo renderizador g_device->SetTransform( D3DTS_WORLD, &mtxCombinada ); // Renderiza o mesh g_device->SetMaterial(&g_meshMtl[0] ); // Seleciona a textura de acordo com o flag g_texturaFlag // Renderiza com textura original modificada if (g_texturaFlag == 1) g_device->SetTexture(0, g_meshTex); // Renderiza com textura alternativa else { modificar_TexturaEx ( g_texturaAlternativa ); g_device->SetTexture(0, g_texturaAlternativa ); } // endif // Renderiza o mesh obj3d->DrawSubset(0); } // desenharObjeto().fim void configurar_cenaEstados(void) { // Configuração de iluminação g_device->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255) ); g_device->SetRenderState (D3DRS_LIGHTING, true); // Configura modo de shading g_device->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD); // Habilita uso de buffer de profundidade g_device->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE); // Desliga o culling g_device->SetRenderState (D3DRS_CULLMODE, D3DCULL_NONE); } // configurar_cenaEstados().fim
//----------------------------------------------------------------------------- // Projeto: prj_ModificarTextura - arquivo: entrada.cpp // Esta aplicação mostra como modificar uma textura em tempo de execução // By www.gameprog.com.br //----------------------------------------------------------------------------- #include <windows.h> #include <d3d9.h> #include <d3dx9.h> #include "motor.h" // Variável global da classe da janela char sclasseJanela[ ] = "cls_directx"; // Dimensões da janela int g_xtela = 640; int g_ytela = 480; // alça da janela HWND hJanela; int WINAPI WinMain (HINSTANCE app_instancia, HINSTANCE app_anterior, LPSTR sComando,int nExibir) { // 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!", "WinMain()", MB_OK); return 0; } // endif // Com a classe criada pode-se criar a janela DWORD estiloExtra = 0; const char janelaTitulo[] = "prj_ModificarTextura - Pressione a barra de espaços"; 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!", "WinMain()", 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", "WinMain()", 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

index << >>

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