Tu tercer programa

<<< BAJATE EL CODIGO FUENTE AQUI >>>

Este es el tercer y ultimo programa de introduccion

Continuo haciendo modificaciones sobre el anterior, y como antes solo voy a comentar las partes que hayan cambiado o aquello que sea nuevo.

En este programa vamos a aprender a user

- Triangle List
- Index Buffer

 

Index Buffer

Lo primero que hacemos es declarar un Index Buffer en nuestra clase principal, y lo inicializamos en el constructor de la clase...

LPDIRECT3DINDEXBUFFER8 m_pIB;
DWORD m_dwSizeofIndices;
...
CMyD3DApplication::CMyD3DApplication() 
{ 
	m_strWindowTitle = _T("Basic"); 
	m_bUseDepthBuffer = FALSE; 
	m_pVB = NULL; m_pIB = NULL; 
	m_pFont = new CD3DFont( _T("Arial"), 12, D3DFONT_BOLD ); 
}
Pero antes de seguir... que es un Index Buffer? Porque lo usamos?

Sirve para almacenar la posicion de los vertices de nuestro triangulo dentro de nuestro vertex buffer.

>mmm... un buffer dentro de un buffer? esto como se come?

basicamente es un offset dentro del vertex buffer, esto es, un numero que se desplaza a lo largo del vertex buffer (recuerda, nuestro buffer de vertices).

el index buffer tiene mas sentido cuando introducimos el concepto de "Triangle List"...

 

Triangle List

Un Triangle List es como su nombre indica una lista de triangulos. No tienen porque estar conectados entre si (formando mallas) pero suelen estarlo.

veamos, suponiendo que tienes dos triangulos como en el dibujo. Si no estan conectados entre si has de manejar 6 vertices.

En cambio, si estan conectados entre si, solo tienes que manejar 4 vertices con la ayuda de un index buffer. En nuestro programa de ejemplo vamos a tratar con un vertex buffer con solo 4 vertices y usando un Triangle List.

Vamos a suponer que nos ponemos a dibujar los dos triangulos que estan enlazados entre si...

Para el primer triangulo:

- DrawPrimitive() empezaria por ejemplo por el vertice de la izquierda, dibujaria una linea desde ese vertice al de arriba del triangulo de la izquierda.
- Despues conectaria el vertice superior con el vertice de abajo a la derecha.
- Finalmente uniria con los dos vertices de abajo.

Si comenzase a dibujar el segundo:

- Cogeria el vertice superior izquierdo del segundo triangulo (almacenado en el vertex buffer con la ayuda de un index buffer, que guarda el desplazamiento a este vertuce desde el segundo triangulo.
- Tras dibujar el vertice de arriba a la izquierda pasaria al cuarto y ultimo vertice almacenado en el vertex buffer.

Esto en codigo quedaria asi:

{ 0.0f, 0.0f, 0.5f, 1.0f, 0.0f, 0.0f, }, // x, y, z, rhw, tu, tv
{ (float)vp.Width, 0.0f, 0.5f, 1.0f, 1.0f, 0.0f, },
{ (float)vp.Width, (float)vp.Height, 0.5f, 1.0f, 1.0f, 1.0f, },
{ 0.0f, (float)vp.Height, 0.5f, 1.0f, 0.0f, 1.0f },

Este es el vertex buffer que almacenaria los 4 vertices.

Ahora el index buffer almacena estos variables

WORD dwIndices[] = { 0, 1, 2, 0, 2, 3 };

Esto le dice a DrawIndexedPrimitive() que deberia usar el vertce 0,1 y 2 para el primer triangulo. Y usar a su vez 0,2 y 3 para el segundo triangulo.

 

RestoreDeviceObjects()

HRESULT CMyD3DApplication::RestoreDeviceObjects() 
{ 
	m_pFont->RestoreDeviceObjects();    // Set the transform matrices 
	D3DXVECTOR3 vEyePt = D3DXVECTOR3( 0.0f, 0.0f,    2.0f ); 
	D3DXVECTOR3 vLookatPt = D3DXVECTOR3( 0.0f, 0.0f, 1.0f ); 
	D3DXVECTOR3 vUpVec = D3DXVECTOR3( 0.0f, 1.0f, 0.0f ); 
	D3DXMATRIX matWorld, matView, matProj;    
	D3DXMatrixIdentity( &matWorld ); 
	D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec ); 
	FLOAT fAspect = m_d3dsdBackBuffer.Width / (FLOAT)m_d3dsdBackBuffer.Height;
	D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, fAspect, 1.0f, 500.0f ); 
	m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); 
	m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );    
	m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); // fill the vertex buffer with the new data 
	D3DVIEWPORT8 vp; m_pd3dDevice->GetViewport(&vp); // Initialize four vertices for rendering two triangles 
	CUSTOMVERTEX cvVertices[] = 
		{ { 0.0f, 0.0f, 0.5f, 1.0f, 0.0f, 0.0f, }, // x, y, z, rhw, tu, tv 
		{ (float)vp.Width, 0.0f, 0.5f, 1.0f, 1.0f, 0.0f, }, 
		{ (float)vp.Width, (float)vp.Height, 0.5f, 1.0f, 1.0f, 1.0f, }, 
		{ 0.0f, (float)vp.Height, 0.5f, 1.0f, 0.0f, 1.0f}, 
	}; m_dwSizeofVertices = sizeof(cvVertices); 

	// Create the vertex buffer 
	if( FAILED( m_pd3dDevice->CreateVertexBuffer( m_dwSizeofVertices, 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_MANAGED, &m_pVB ) ) ) return E_FAIL; 
	VOID* pVertices; 

	if( FAILED( m_pVB->Lock( 0, m_dwSizeofVertices, (BYTE**)&pVertices, 0 ) ) ) return E_FAIL; 
	memcpy( pVertices, cvVertices, m_dwSizeofVertices); m_pVB->Unlock(); // Initialize the Index buffer 
	WORD dwIndices[] = {0, 1, 2, 0, 2, 3}; 
	m_dwSizeofIndices = sizeof(dwIndices); // Create the index buffer 

	if( FAILED( m_pd3dDevice->CreateIndexBuffer( m_dwSizeofIndices, 0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &m_pIB ) ) ) return E_FAIL;    
	VOID* pIndices; 

	if( FAILED( m_pIB->Lock( 0, m_dwSizeofIndices, (BYTE**)&pIndices, 0 ) ) ) return E_FAIL; 
	memcpy( pIndices, dwIndices, sizeof(dwIndices) ); 
	m_pIB->Unlock();    
	return S_OK; 
}

Lo realmente nuevo es la creacion del index buffer y como lo rellenamos.

CreateIndexBuffer necesita la longitud del index buffer como primer parametro, y el formato del index buffer como tercer parametro:

HRESULT CreateIndexBuffer(
UINT Length,
DWORD Usage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DIndexBuffering *8 ppIndexBuffer
};

Puedes especificar el tamaño del index buffer a WORD o DWORD usando D3DFMT_INDEX16 o D3DFMT_INDEX32. segun el numero de vertices que necesites. No ponemos nada en el parametro Usage. Este paremetro es una estructura que contiene algunos flags generales de compatibilidad. Por ejemplo, D3DUSAGE_SOFTWAREPROCESSING indica que el index buffer va a ser usado mediante software (en vez de hardware). Usamos como POOL el valor D3DPOOL_MANAGED. Por ultimo, m_pIB es un puntero a la inter5face del index buffer.

 

Render()

HRESULT CMyD3DApplication::Render() 
{ 

	
	// Begin the scene 
	if( SUCCEEDED( m_pd3dDevice->BeginScene()) ) 
	{ 
		m_pd3dDevice->SetTexture( 0, m_pBackgroundTexture ); 
		m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); 
		m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); 
		m_pd3dDevice->SetStreamSource( 0, m_pVB, sizeof(CUSTOMVERTEX) ); 
		m_pd3dDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX );    
		m_pd3dDevice->SetIndices( m_pIB, 0 ); 
		m_pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 
			0, 4, // number of vertices 
			0, 2); // number of primitives 

		// Output statistics    
		m_pFont->DrawText( 2, 0, D3DCOLOR_ARGB(255,255,255,0), m_strFrameStats ); 
		m_pFont->DrawText( 2, 20, D3DCOLOR_ARGB(255,255,255,0), m_strDeviceStats ); 

		// End the scene. 
		m_pd3dDevice->EndScene();    
	} 

	return S_OK; 
}

Existen dos cambios fundamentales:

- Se llama a SetIndices(), que establece el actual array de indices
- Se llama a DrawIndexedPrimitive(), en lugar de DrawPrimitive()

El segundo parametro de SetIndices() especifica el valor base para los indices. Este es el punto de comienzo en el flujo de datos de los vertices, que se añade a todos los indices antes de hacer referencia en el flujo de datos de los vertices.

Esta es la funcion que sustituye a DrawPrimitive(), y que solo dibuja primitivas (triangulos en este caso) indexadas:

HRESULT DrawIndexedPrimitive(
3DPRIMITIVE Type,
UINT MinIndex,
UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount
};

- Como tipo de primitva hemos elegido el "Triangle List", que ya lo he explicado antes.
- El segundo parametro es el minimo indice que se puede usar para los vertices. MinIndex y todos los indices en el flujo de datos son relativos a BaseVertexIndex, que se ha configurado antes con SetIndices().
- El tercer parametro es el numero de indices que se van a usar en esta llamada, empezando desde BaseVertexIndex y MinIndex.
- El cuarto parametro es el array de indices desde donde se empiezan a leer estos indices
- El ultimo parametro es el numero de primitivas a dibujar. El ejemplo usa traingulos, asi que el numero de primitivas a dibujar es el numero de indices dividido por 3.

Utiliza esto para calcular el numero de primitivas (y usarlo como parametro PrimitiveCount):

D3DPT_POINTLIST: PrimitiveCount = n
D3DPT_LINELIST: PrimitiveCount = n/2
D3DPT_LINESTRIP: PrimitiveCount = n-1
D3DPT_TRIANGLELIST: PrimitiveCount = n/3
D3DPT_TRIANGLESTRIP: PrimitiveCount = n-2
D3DPT_TRIANGLEFAN: PrimitiveCount = n-2

 

InvalidateDeviceObjects()

HRESULT CMyD3DApplication::InvalidateDeviceObjects() 
{ 
	m_pFont->InvalidateDeviceObjects();    
	SAFE_RELEASE( m_pVB ); 
	SAFE_RELEASE( m_pIB ); 
	return S_OK; 
} 

tan solo hemos añadido que debemos librerar el IndexBuffer.