DirectX10 Rendering Engine – Meshbuilder.cpp

 

#include "meshbuilder.h"

// The vertex input layout
D3D10_INPUT_ELEMENT_DESC VertexLayout[] =
{
	{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	{ "BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 36, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 48, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

// Calculate the number of elements in the layout array
UINT NumLayoutElements = (sizeof(VertexLayout) / sizeof(VertexLayout[0]));

CMeshBuilder::CMeshBuilder()
{

}

CMeshBuilder::~CMeshBuilder( void )
{

}

bool CMeshBuilder::Build( MeshDesc &meshDesc )
{
	BuildNBTFrames();

	// Create the vertex input layout
	bool bSuccess = CreateInputLayout( meshDesc.pMaterial->GetEffect()->GetTechnique(), VertexLayout, NumLayoutElements, &meshDesc.pLayout );

	if (!bSuccess)
		return false;

	HRESULT hr = D3DX10CreateMesh( Engine()->GetDevice(),
		VertexLayout,
		NumLayoutElements,
		VertexLayout[0].SemanticName,
		m_VertexList.size(),
		m_IndexList.size() / 3,
		D3DX10_MESH_32_BIT,
		meshDesc.ppMesh );

	if(FAILED(hr))
	{
		return false;
	}

	(*meshDesc.ppMesh)->SetVertexData( 0, (void*)m_VertexList.data() );
	m_VertexList.empty();

	(*meshDesc.ppMesh)->SetIndexData( (void*)m_IndexList.data(), m_IndexList.size() );
	m_IndexList.empty();

	// Set the attribute data
	(*meshDesc.ppMesh)->SetAttributeData( (UINT*)m_Attributes.data() );
	m_Attributes.empty();

	// Reorder the vertices according to subset and optimize the mesh for this graphics
	// card's vertex cache. When rendering the mesh's triangle list the vertices will
	// m_pMesh hit more often so it won't have to re-execute the vertex shader.

	// DX10 LEAK!!!

	(*meshDesc.ppMesh)->GenerateAdjacencyAndPointReps( 1e-6f );
	(*meshDesc.ppMesh)->Optimize( D3DX10_MESHOPT_ATTR_SORT | D3DX10_MESHOPT_VERTEX_CACHE, NULL, NULL );

	if (m_Attributes.size() > 0)
	{
		(*meshDesc.ppMesh)->GetAttributeTable( NULL, &meshDesc.nAttribTableEntries );
		meshDesc.pAttribTable = new D3DX10_ATTRIBUTE_RANGE[meshDesc.nAttribTableEntries];
		(*meshDesc.ppMesh)->GetAttributeTable( meshDesc.pAttribTable, &meshDesc.nAttribTableEntries );
	}
	else
	{
		meshDesc.nAttribTableEntries = 1;
		meshDesc.pAttribTable = NULL;
	}

	(*meshDesc.ppMesh)->CommitToDevice();

	return true;
}

void CMeshBuilder::BuildNBTFrames( void )
{
	// calculate normals, tangents, and binormals.  Must be done in this order!

	// we need to declare a temporary tangent array here. we use a D3DXVECTOR4
	// as the xyz component will contain the tangent, but the w component will
	// contain the handedness.  we can use that when calculating our binormals
	// to ensure they are correct.
	D3DXVECTOR4* pTangents = new D3DXVECTOR4[ m_IndexList.size() ];
	ZeroMemory( pTangents, m_VertexList.size() * sizeof(D3DXVECTOR4) );

	CalculateNormals();
	CalculateTangents( pTangents );
	CalculateBinormals( pTangents );

	SAFE_DELETE_ARRAY( pTangents );
}

/*******************************************************************
* CalculateNormals
* Calculates normal vectors for all vertices.  Must be called before
* CalculateTangents and CalculateBinormals
* Inputs - void
* Outputs - void
*******************************************************************/
void CMeshBuilder::CalculateNormals( void )
{
	// iterate through all of our vertices by index and calculate normals
	for ( UINT i = 0; i < m_IndexList.size(); i+=3 )
	{
		D3DXVECTOR3 v0 = m_VertexList[ m_IndexList[i] ].Pos;
		D3DXVECTOR3 v1 = m_VertexList[ m_IndexList[i+1] ].Pos;
		D3DXVECTOR3 v2 = m_VertexList[ m_IndexList[i+2] ].Pos;

		D3DXVECTOR3 vNormal, vCross;

		D3DXVec3Cross( &vCross, &D3DXVECTOR3(v2 - v0), &D3DXVECTOR3(v1 - v0) );
		D3DXVec3Normalize( &vNormal, &vCross );

		// assign normals
		m_VertexList[ m_IndexList[i] ].Normal = vNormal;
		m_VertexList[ m_IndexList[i+1] ].Normal = vNormal;
		m_VertexList[ m_IndexList[i+2] ].Normal = vNormal;
	}
}

/*******************************************************************
* CalculateTangents
* Calculates tangent vectors for all vertices.  Must be called after
* CalculateNormals but before CalculateBinormals
* Inputs - Tangent array - D3DXVECTOR4*
* Outputs - void
*
* Algorithm based on implementation located at
* http://www.terathon.com/code/tangent.html
*******************************************************************/
void CMeshBuilder::CalculateTangents( D3DXVECTOR4* pTangents )
{
	if ( !pTangents )
		return;

	D3DXVECTOR3* tanu = new D3DXVECTOR3[ m_IndexList.size() ];
	D3DXVECTOR3* tanv = new D3DXVECTOR3[ m_IndexList.size() ];
	ZeroMemory( tanu, m_IndexList.size() * sizeof(D3DXVECTOR3) );
	ZeroMemory( tanv, m_IndexList.size() * sizeof(D3DXVECTOR3) );

	// iterate through indices to get UV vectors
	for ( UINT i = 0; i < m_IndexList.size(); i+=3 )
	{
		// get indices
		int index1 = m_IndexList[i];
		int index2 = m_IndexList[i+1];
		int index3 = m_IndexList[i+2];

		// get vertices
		D3DXVECTOR3 vec1 = m_VertexList[ index1 ].Pos;
		D3DXVECTOR3 vec2 = m_VertexList[ index2 ].Pos;
		D3DXVECTOR3 vec3 = m_VertexList[ index3 ].Pos;

		// get tex coords
		D3DXVECTOR2 uv1 = m_VertexList[ index1 ].Tex;
		D3DXVECTOR2 uv2 = m_VertexList[ index2 ].Tex;
		D3DXVECTOR2 uv3 = m_VertexList[ index3 ].Tex;

		float x1 = vec2.x - vec1.x;
		float x2 = vec3.x - vec1.x;
		float y1 = vec2.y - vec1.y;
		float y2 = vec3.y - vec1.y;
		float z1 = vec2.z - vec1.z;
		float z2 = vec3.z - vec1.z;

		float u1 = uv2.x - uv1.x;
		float u2 = uv3.x - uv1.x;
		float v1 = uv2.y - uv1.y;
		float v2 = uv3.y - uv1.y;

		float r = (1.0f / (u1 * v2 - u2 * v1) );

		D3DXVECTOR3 udir( (v2 * x1 - v1 * x2) * r, (v2 * y1 - v1 * y2) * r, (v2 * z1 - v1 * z2) * r );
		D3DXVECTOR3 vdir( (u1 * x2 - u2 * x1) * r, (u1 * y2 - u2 * y1) * r, (u1 * z2 - u2 * z1) * r );

		// store all u and v vectors
		tanu[i] += udir;
		tanu[i+1] += udir;  // not needed but storing just in case
		tanu[i+2] += udir;  // not needed but storing just in case

		tanv[i] += vdir;
		tanv[i+1] += vdir;  // not needed but storing just in case
		tanv[i+2] += vdir;  // not needed but storing just in case
	}

	// iterate through the vertices and set their tangents
	for ( UINT i = 0; i < m_IndexList.size(); i+=3 )
	{
		// get indices
		int index1 = m_IndexList[i];
		int index2 = m_IndexList[i+1];
		int index3 = m_IndexList[i+2];

		// get the normal and the previous u and v vectors based on our index
		// since these values will be equal for all 3 vertices in each face,
		// we only need to get the values pertaining to the first index
		D3DXVECTOR3 vNormal = m_VertexList[ index1 ].Normal;
		D3DXVECTOR3 vTanU( tanu[i] );
		D3DXVECTOR3 vTanV( tanv[i] );

		// Gram-Schmidt orthogonalize
		float fTanDot = D3DXVec3Dot( &vNormal, &vTanU );
		D3DXVECTOR3 vTanPreNormal( vTanU - vNormal * fTanDot);
		D3DXVECTOR3 vTangent;
		D3DXVec3Normalize( &vTangent, &vTanPreNormal );

		// set vertex tangents
		m_VertexList[ index1 ].Tangent = vTangent;
		m_VertexList[ index2 ].Tangent = vTangent;
		m_VertexList[ index3 ].Tangent = vTangent;

		// fill xyz component of tangent array element
		pTangents[i] = D3DXVECTOR4( vTangent.x, vTangent.y, vTangent.z, 1.0f );
		pTangents[i+1] = D3DXVECTOR4( vTangent.x, vTangent.y, vTangent.z, 1.0f );
		pTangents[i+2] = D3DXVECTOR4( vTangent.x, vTangent.y, vTangent.z, 1.0f );

		// Calculate handedness and fill w component
		D3DXVECTOR3 vHandedCross;
		D3DXVec3Cross( &vHandedCross, &vNormal, &vTanU );
		float fHandedness = ( D3DXVec3Dot( &vHandedCross, &vTanV ) < 0.0f ) ? -1.0f : 1.0f;

		pTangents[i].w = fHandedness;
		pTangents[i+1].w = fHandedness;
		pTangents[i+2].w = fHandedness;
	}

	delete[] tanu;
	delete[] tanv;
}

/*******************************************************************
* CalculateBinormals
* Calculates binormal vectors for all vertices.  Must be called after
* CalculateNormals and CalculateTangents
* Inputs - Filled Tangent array, needed for handedness - D3DXVECTOR4*
* Outputs - void
*******************************************************************/
void CMeshBuilder::CalculateBinormals( D3DXVECTOR4* pTangents )
{
	if ( !pTangents )
		return;

	// iterate through all of our vertices by index and calculate binormals
	for ( UINT i = 0; i < m_IndexList.size(); i+=3 )
	{
		D3DXVECTOR3 vNormal = m_VertexList[ m_IndexList[i] ].Normal;
		D3DXVECTOR3 vTangent = m_VertexList[ m_IndexList[i] ].Tangent;

		D3DXVECTOR3 vBinormalPreNormal, vBinormal;

		D3DXVec3Cross( &vBinormalPreNormal, &vNormal, &vTangent );
		D3DXVec3Normalize( &vBinormal, &vBinormalPreNormal );

		// assign binormals and use w element of our tangent array
		// to ensure correct handedness
		m_VertexList[ m_IndexList[i] ].Binormal = vBinormal * pTangents[i].w;
		m_VertexList[ m_IndexList[i+1] ].Binormal = vBinormal * pTangents[i+1].w;
		m_VertexList[ m_IndexList[i+2] ].Binormal = vBinormal * pTangents[i+2].w;
	}
}