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