#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;
}
}