I did receive a couple of requests for the pre-processor code that I use in the demo. It is written in my engine code, so it may not be the easiest to understand, but here is the function that I use to generate the new edge mesh from a given instance of a normal triangle mesh:
//--------------------------------------------------------------------------------CTriMesh* CTriMesh::generateFinMesh(){ CTriMesh* pMesh = NULL; if ( ( getElement( "POSITION" ) != NULL ) && ( getElement( "NORMAL" ) != NULL ) ) { // create storage for the new vertex elements CVertexElement* pNewPositions = new CVertexElement( 3, faceCount() * 3 * 2 ); pNewPositions->m_Name = "POSITION"; pNewPositions->m_Type = FLOAT3; pNewPositions->m_Method = DEFAULT; pNewPositions->m_Usage = POSITION; pNewPositions->m_UsageIndex = 0; CVertexElement* pNewNormals = new CVertexElement( 3, faceCount() * 3 * 2 ); pNewNormals->m_Name = "NORMAL"; pNewNormals->m_Type = FLOAT3; pNewNormals->m_Method = DEFAULT; pNewNormals->m_Usage = NORMAL; pNewNormals->m_UsageIndex = 0; CVertexElement* pNewTangents = new CVertexElement( 3, faceCount() * 3 * 2 ); pNewTangents->m_Name = "TANGENT"; pNewTangents->m_Type = FLOAT3; pNewTangents->m_Method = DEFAULT; pNewTangents->m_Usage = TANGENT; pNewTangents->m_UsageIndex = 0; CVertexElement* pNewTexcoords = new CVertexElement( 1, faceCount() * 3 * 2 ); pNewTexcoords->m_Name = "TEXCOORD0"; pNewTexcoords->m_Type = FLOAT1; pNewTexcoords->m_Method = DEFAULT; pNewTexcoords->m_Usage = TEXCOORD; pNewTexcoords->m_UsageIndex = 0; // get pointers to the new data elements CVector3f* pNewPos = (CVector3f*)((*pNewPositions)[0]); CVector3f* pNewNrm = (CVector3f*)((*pNewNormals)[0]); CVector3f* pNewTan = (CVector3f*)((*pNewTangents)[0]); float* pNewTex = (float*)((*pNewTexcoords)[0]); // get pointers to the original mesh elements CVertexElement* pPositions = getElement( "POSITION" ); CVertexElement* pNormals = getElement( "NORMAL" ); CVector3f* pPos = (CVector3f*)((*pPositions)[0]); CVector3f* pNrm = (CVector3f*)((*pNormals)[0]); CSegment line; // create the new mesh that will eventually be returned pMesh = new CTriMesh(); pMesh->addElement( pNewPositions ); pMesh->addElement( pNewNormals ); pMesh->addElement( pNewTangents ); pMesh->addElement( pNewTexcoords ); for ( int i = 0; i < faceCount(); i++ ) { pNewPos[6*i+0] = pPos[ m_faces.P1() ]; pNewPos[6*i+1] = pPos[ m_faces.P2() ]; pNewPos[6*i+2] = pPos[ m_faces.P2() ]; pNewPos[6*i+3] = pPos[ m_faces.P3() ]; pNewPos[6*i+4] = pPos[ m_faces.P3() ]; pNewPos[6*i+5] = pPos[ m_faces.P1() ]; pNewNrm[6*i+0] = pNrm[ m_faces.P1() ]; pNewNrm[6*i+1] = pNrm[ m_faces.P2() ]; pNewNrm[6*i+2] = pNrm[ m_faces.P2() ]; pNewNrm[6*i+3] = pNrm[ m_faces.P3() ]; pNewNrm[6*i+4] = pNrm[ m_faces.P3() ]; pNewNrm[6*i+5] = pNrm[ m_faces.P1() ]; pNewTan[6*i+0] = pNrm[ m_faces.P1() ]; pNewTan[6*i+1] = pNrm[ m_faces.P2() ]; pNewTan[6*i+2] = pNrm[ m_faces.P2() ]; pNewTan[6*i+3] = pNrm[ m_faces.P3() ]; pNewTan[6*i+4] = pNrm[ m_faces.P3() ]; pNewTan[6*i+5] = pNrm[ m_faces.P1() ]; line.P1() = 6*i+0; line.P2() = 6*i+1; pMesh->addLine( line ); line.P1() = 6*i+2; line.P2() = 6*i+3; pMesh->addLine( line ); line.P1() = 6*i+4; line.P2() = 6*i+5; pMesh->addLine( line ); } TArray<unsigned int>* aliases = NULL; aliases = new TArray<unsigned int>[pMesh->lineCount() * 2]; for ( int i = 0; i < pMesh->lineCount() * 2; i++ ) { for ( int j = 0; j < pMesh->lineCount() * 2; j++ ) { if ( i != j ) { if ( pNewPos == pNewPos[j] ) aliases.add( j ); } } } CTriangle face; for ( int i = 0; i < pMesh->lineCount(); i++ ) { for ( int j = 0; j < pMesh->lineCount(); j++ ) { if ( i != j ) { unsigned int p1 = pMesh->getLine(j)->P1(); unsigned int p2 = pMesh->getLine(j)->P2(); if ( aliases[2*i+0].contains( p1 ) && aliases[2*i+1].contains( p2 ) ) { pNewTan[2*i+0] = pNewNrm[p1]; pNewTan[2*i+1] = pNewNrm[p2]; face.P1() = 2*i+0; face.P2() = p1; face.P3() = 2*i+1; pMesh->addFace( face ); face.P1() = 2*i+1; face.P2() = p1; face.P3() = p2; pMesh->addFace( face ); } else { if ( aliases[2*i+0].contains( p2 ) && aliases[2*i+1].contains( p1 ) ) { pNewTan[2*i+0] = pNewNrm[p2]; pNewTan[2*i+1] = pNewNrm[p1]; face.P1() = 2*i+0; face.P2() = p1; face.P3() = 2*i+1; pMesh->addFace( face ); face.P1() = 2*i+1; face.P2() = p1; face.P3() = p2; pMesh->addFace( face ); } } } } } const float fThresRidge = cosf( 1.57f - 0.5f ); // the valley and ridges use the same angle to measure the threshold //const float fThresValley = cosf( 2.57f ); for ( int i = 0; i < pMesh->lineCount(); i++ ) { pNewNrm[2*i+0].Normalize(); pNewTan[2*i+0].Normalize(); if ( pNewNrm[2*i+0].dot( pNewTan[2*i+0] ) < fThresRidge ) pNewTex[2*i+0] = 1.0f; else pNewTex[2*i+0] = 0.0f; pNewNrm[2*i+1].Normalize(); pNewTan[2*i+1].Normalize(); if ( pNewNrm[2*i+1].dot( pNewTan[2*i+1] ) < fThresRidge ) pNewTex[2*i+1] = 1.0f; else pNewTex[2*i+1] = 0.0f; } pMesh->removeAllLines(); delete[] aliases; } return( pMesh );}//--------------------------------------------------------------------------------
If anyone has any feedback I would be happy to hear it!