En este blog encontrarás información sobre tecnologías como XNA, DirectX, Gaming y otras cositas relacionadas con el mundo del desarrollo de video juegos. Bienvenid@!

11 de junio de 2007

Crear una aplicación Direct3D con Visual C++ 2005 (Parte 2)

En el post anterior vimos como inicializar una ventana para una aplicación usando la API de Windows y luego como adquirir los objetos IDirect3D9 e IDirect3DDevice9, los cuales son necesarios para dibujar escenas en 3D usando DirectX 9. Sin embargo, no vimos como usar dichos objetos para mostrar nuestra escena en la pantalla.

En esta ocasión quiero mostrar algunos de los conceptos básicos usados para trabajar en 3D y realizar un demo simple para dibujar vértices en la pantalla.

En mi concepto, la finalidad de una aplicación Direct3D es mostrar en una pantalla plana (2D) “la imagen de cómo se vería una escena en 3 dimensiones vista desde cierto punto y bajo ciertas condiciones de luz y otras”. Para lograr esto las aplicaciones Direct3D usan muchos vértices, los cuales son “puntos” de un objeto que al agruparse, generalmente formando triángulos, pueden dar forma a un objeto en 3 dimensiones.

Para hacerlo práctico y por cuestiones de simplicidad vamos a comenzar por dibujar un triángulo usando vértices transformados. Un vértice transformado en realidad está en coordenadas 2D de la ventana, lo cual quiere decir que el punto (0,0) está en la esquina superior izquierda de la ventana, el eje X positivo esta a la derecha y el eje Y positivo es hacia abajo. Este tipo de vértices no usan luces de Direct3D porque ellos proporcionan su propio color.

Lo primero que hacemos es definir un tipo de estructura en el que podamos almacenar los datos de nuestros vértices (El código de este ejemplo usa la información del post anterior, aunque dicha funcionalidad se encapsulo en las clases WinApplication y D3DApplication; el código de la estructura CUSTOMVERTEX se encuentra en el archivo MyD3DApplication.h donde se define la clase para este ejemplo).

/* Estructura para almacenar la informacion de los vértices */
struct CUSTOMVERTEX
{
  FLOAT x, y, z, rhw; // Posición transformada para el vértice
  DWORD color; // Color del vértice
};

Posteriormente debemos definir el FVF (Flexible Vertex Format) para nuestro tipo CUSTOMVERTEX, esto es un valor que indique al motor de Direct3D que información vamos a pasar para cada vértice en las operaciones de dibujado.

/* Formato de Vertice Flexible para CUSTOMVERTEX */
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)

Aquí D3DFVF_XYZRHW indica un punto transformado y D3DFVF_DIFFUSE indica el color del vértice.

Una vez que hemos definido el FVF podemos inicializar los valores para los tres (3) vértices del triángulo que queremos dibujar (Esto se hace en el método InitDirect3D en el archivo MyD3DApplication.cpp).

// Define los vertices para el triangulo
CUSTOMVERTEX vertices[] =
{
  { 320.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color
  { 490.0f, 350.0f, 0.5f, 1.0f, 0xff00ff00, },
  { 150.0f, 350.0f, 0.5f, 1.0f, 0xffff00ff, },
};

Esto define tres (3) vértices para nuestro triangulo en las posiciones de pantalla (320, 50), (490, 350) y (150, 350) y de colores rojo, verde y rojo-azul respectivamente. Luego de esto debemos llamar al método IDirect3DDevice9::CreateVertexBuffer para crear el buffer de vértices en el dispositivo.

// Crea el buffer de vértices
if (FAILED (m_pD3DDevice->CreateVertexBuffer (3*sizeof (CUSTOMVERTEX),
  0 /*Usage*/, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pVertexBuffer, NULL)))
  return 0;

Los parámetros de CreateVertexBuffer indican el tamaño del buffer, el uso (0 o una combinación de constantes D3DUSAGE), el formato de los vértices, la clase de memoria que va a almacenar el buffer y la dirección del puntero al buffer (LPDIRECT3DVERTEXBUFFER9, equivalente a IDirect3DVertexBuffer9 *). El último parámetro es reservado y debe fijarse a NULL. De aquí cabe destacar que D3DPOOL_DEFAULT indica que el buffer se alojará en la memoria más conveniente, generalmente memoria de video.

Después de crear el buffer, es necesario copiar en él los datos de los vértices. Para hacer esto hay que bloquear el buffer usando el método IDirect3DVertexBuffer9::Lock. Si se puede bloquear, éste método fija en un puntero la dirección de memoria del inicio del buffer, con lo cual se pueden copiar los datos y posteriormente desbloquearlo.


VOID* pVertices;

// Bloquea el buffer y obtiene un puntero a su memoria
if (FAILED (m_pVertexBuffer->Lock (0, sizeof (vertices), (void**)&pVertices, 0)))
  return E_FAIL;

// Copia los datos de los vértices a la memoria del buffer
memcpy (pVertices, vertices, sizeof (vertices));

// Desbloquea el buffer
m_pVertexBuffer->Unlock ();

Finalmente es hora de renderizar lo hecho. Para esto vamos al método MyD3DApplication::Render en donde se llaman las operaciones IDirect3DDevice9:: BeginScene y IDirect3DDevice9::EndScene entre las cuales se deben realizar todas las operaciones de dibujado. Lo primero es fijar la fuente de datos para el dispositivo llamando al método IDirect3DDevice9::SetStreamSource pasando el puntero a nuestro buffer de vértices.

/* Fijar la fuente para la operacion de dibujado */
m_pD3DDevice->SetStreamSource (0, m_pVertexBuffer, 0, sizeof (CUSTOMVERTEX));

Luego fijamos el formato de vértice flexible que usa el buffer y llamamos al método IDirect3DDevice9::DrawPrimitive para hacer que el dispositivo “dibuje” el triangulo pasándole el valor constante del tipo de primitiva D3DPT_TRIANGLELIST, el cual indica al dispositivo que se va a dibujar una lista de triángulos desde el buffer (Aunque en nuestro caso es solo un triángulo como se indica en el último parámetro).

/* Fijar el formato de vértices de la fuente */
m_pD3DDevice->SetFVF (D3DFVF_CUSTOMVERTEX);

/* Dibuja el triangulo desde el buffer */
m_pD3DDevice->DrawPrimitive (D3DPT_TRIANGLELIST, 0, 1);

Con esto terminamos los pasos necesarios para dibujar el triangulo en la pantalla como vemos en la siguiente imagen donde se ve que cada vértice representa una esquina y que cada una tiene un color diferente que se mezcla suavemente en el centro con el color de los demás vértices.



Este ejemplo ya hace uso del objeto IDirect3DDevice9 para dibujar objetos en la pantalla aunque en un modo transformado. En un siguiente post veremos cómo usar vértices no transformados y usar transformaciones para modificar la escena 3D.

El código de este ejemplo para Visual Studio 2005 lo puedes descargar desde aquí

Willy R.