// // Copyright (c) 2010-2011 Matthew Jack and Doug Binks // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. #include "AURenMesh.h" #ifndef _WIN32 #define NO_ASSIMP //Currently not adding assimp support to other platforms #endif #include "../Common/AUVec3f.inl" //for cross product used in calculateing normals // Windows Requirements #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #include #endif //_WIN32 #ifdef __MACH__ #include #else // OpenGL requirements #include #endif //__MACH__ #ifndef NO_ASSIMP #include #include #include #endif //NO_ASSIMP #ifndef _WIN32 #include int _stricmp( const char* pS1, const char* pS2 ) { return strcasecmp( pS1, pS2 ); } #endif #include #include AURenMesh::AURenMesh() : m_pafVertexCoordinates( NULL ), m_pafTextureCoordinates( NULL ), m_pafNormals( NULL ), m_pausTriangleIndices( NULL ), m_uiNumVertices( 0 ), m_uiNumTriangles( 0 ) { } AURenMesh::~AURenMesh() { Clear(); } void AURenMesh::Clear() { delete[] m_pafNormals; delete[] m_pafVertexCoordinates; delete[] m_pafTextureCoordinates; delete[] m_pausTriangleIndices; } bool AURenMesh::LoadFromFile( const std::string& strFilename ) { // Safely delete any existing data before loading new mesh Clear(); int index = (int)strFilename.size() - 3; std::string extension = index >= 0 ? strFilename.substr(index, 3) : ""; if (!_stricmp(extension.c_str(), "aml")) { return LoadFromFileAML(strFilename); } else { return LoadFromFileImport(strFilename); } } bool AURenMesh::SaveToFile( const std::string& strFilename ) { std::ofstream outFile; outFile.open(strFilename.c_str(), std::ios::binary); if( !outFile ) { return false; } outFile << "AML Aurora File" << std::endl; outFile << 1 << std::endl; // version outFile << m_uiNumVertices << std::endl; outFile << m_uiNumTriangles << std::endl; outFile.write( reinterpret_cast( m_pafVertexCoordinates ), 3 * m_uiNumVertices * sizeof( float ) ); outFile.write( reinterpret_cast( m_pafTextureCoordinates ), 2 * m_uiNumVertices * sizeof( float ) ); outFile.write( reinterpret_cast( m_pafNormals ), 3 * m_uiNumVertices * sizeof( float ) ); outFile.write( reinterpret_cast( m_pausTriangleIndices ), 3 * m_uiNumTriangles * sizeof( unsigned short ) ); outFile.close(); return true; } bool AURenMesh::LoadFromFileAML( const std::string& strFilename_ ) { std::ifstream inFile; inFile.open(strFilename_.c_str(), std::ios::binary); if( !inFile ) { return false; } inFile.ignore(10000,'\n'); //ignore first line int iVersion; inFile >> iVersion; //currently ignore version number inFile >> m_uiNumVertices; inFile >> m_uiNumTriangles; //now discard end of line inFile.ignore(10000,'\n'); m_pafVertexCoordinates = new float[ 3 * m_uiNumVertices ]; m_pafNormals = new float[ 3 * m_uiNumVertices ]; m_pafTextureCoordinates = new float[ 2 * m_uiNumVertices ]; m_pausTriangleIndices = new unsigned short[ 3 * m_uiNumTriangles ]; inFile.read( reinterpret_cast( m_pafVertexCoordinates ), 3 * m_uiNumVertices * sizeof( float ) ); inFile.read( reinterpret_cast( m_pafTextureCoordinates ), 2 * m_uiNumVertices * sizeof( float ) ); inFile.read( reinterpret_cast( m_pafNormals ), 3 * m_uiNumVertices * sizeof( float ) ); inFile.read( reinterpret_cast( m_pausTriangleIndices ), 3 * m_uiNumTriangles * sizeof( unsigned short ) ); return true; } bool AURenMesh::LoadFromFileImport( const std::string& strFilename ) { #ifndef NO_ASSIMP Assimp::Importer importer; const aiScene* pScene = importer.ReadFile( strFilename, aiProcessPreset_TargetRealtime_Fast ); if (!pScene || pScene->mNumMeshes == 0) { return false; } ProcessScene(pScene); return true; #else assert( false ); return false; #endif } void AURenMesh::ProcessScene( const aiScene* pScene ) { #ifndef NO_ASSIMP // Calculate total number of verts and tris across all meshes in scene m_uiNumVertices = 0; m_uiNumTriangles = 0; for (unsigned int i=0; imNumMeshes; ++i) { aiMesh* pMesh = pScene->mMeshes[i]; m_uiNumVertices += pMesh->mNumVertices; m_uiNumTriangles += pMesh->mNumFaces; } // Allocate sufficent space for all data m_pafVertexCoordinates = new float[ 3 * m_uiNumVertices ]; m_pafNormals = new float[ 3 * m_uiNumVertices ]; m_pafTextureCoordinates = new float[ 2 * m_uiNumVertices ]; m_pausTriangleIndices = new unsigned short[ 3 * m_uiNumTriangles ]; // Iterate through all meshes and load data int vertIndex = 0; int normalIndex = 0; int texIndex = 0; int triIndex = 0; for (unsigned int i=0; imNumMeshes; ++i) { aiMesh* pMesh = pScene->mMeshes[i]; // Load Verts for (unsigned int j=0; jmNumVertices; ++j) { const aiVector3D& vec = pMesh->mVertices[j]; m_pafVertexCoordinates[vertIndex] = vec.x; m_pafVertexCoordinates[vertIndex+1] = vec.y; m_pafVertexCoordinates[vertIndex+2] = vec.z; vertIndex += 3; } // Load Normals for (unsigned int j=0; jmNumVertices; ++j) { const aiVector3D& vec = pMesh->mNormals[j]; m_pafNormals[normalIndex] = vec.x; m_pafNormals[normalIndex+1] = vec.y; m_pafNormals[normalIndex+2] = vec.z; normalIndex += 3; } // Load Tex Coords if (pMesh->HasTextureCoords(0)) { for (unsigned int j=0; jmNumVertices; ++j) { const aiVector3D& vec = pMesh->mTextureCoords[0][j]; m_pafTextureCoordinates[texIndex] = vec.x; m_pafTextureCoordinates[texIndex+1] = vec.y; texIndex += 2; } } // Load Tris for (unsigned int j=0; jmNumFaces; ++j) { const aiFace& tri = pMesh->mFaces[j]; m_pausTriangleIndices[triIndex] = tri.mIndices[0]; m_pausTriangleIndices[triIndex+1] = tri.mIndices[1]; m_pausTriangleIndices[triIndex+2] = tri.mIndices[2]; triIndex += 3; } } #endif } ////////////////////////////////////////////////////////////////////////////////////////// // Function: AURenMesh::AURenMesh // // Last Modified by: Douglas John Binks (DJB) // // Last Modified: 21 June 2000 // // Purpose: Normalises the size and position of the object to be // centered around the origin and of the correct BCube extent. // // Inputs: fBCubeHalfWidth_ : float giving the half width of the // bounding cube. // // Outputs: None. // // Returns: None. // ////////////////////////////////////////////////////////////////////////////////////////// void AURenMesh::NormaliseToBCubeHalfWidth( float fBCubeHalfWidth_ ) { //set up min and max variablse for each axis and use a real value from the //array to initialise (as a `made up' value may be wrong unless we use //floatmax for min etc.). float fMinX = m_pafVertexCoordinates[0]; float fMaxX = m_pafVertexCoordinates[0]; float fMinY = m_pafVertexCoordinates[1]; float fMaxY = m_pafVertexCoordinates[1]; float fMinZ = m_pafVertexCoordinates[2]; float fMaxZ = m_pafVertexCoordinates[2]; //Go through array of vertices to find the real min and max unsigned int uiCountCoords; for( uiCountCoords = 0; uiCountCoords < 3 * m_uiNumVertices; uiCountCoords += 3 ) { if( fMinX > m_pafVertexCoordinates[ uiCountCoords ] ) { fMinX = m_pafVertexCoordinates[ uiCountCoords ]; } else { if( fMaxX < m_pafVertexCoordinates[ uiCountCoords ] ) { fMaxX = m_pafVertexCoordinates[ uiCountCoords ]; } } if( fMinY > m_pafVertexCoordinates[ uiCountCoords + 1 ] ) { fMinY = m_pafVertexCoordinates[ uiCountCoords + 1 ]; } else { if( fMaxY < m_pafVertexCoordinates[ uiCountCoords + 1 ] ) { fMaxY = m_pafVertexCoordinates[ uiCountCoords + 1 ]; } } if( fMinZ > m_pafVertexCoordinates[ uiCountCoords + 2 ] ) { fMinZ = m_pafVertexCoordinates[ uiCountCoords + 2 ]; } else { if( fMaxZ < m_pafVertexCoordinates[ uiCountCoords + 2 ] ) { fMaxZ = m_pafVertexCoordinates[ uiCountCoords + 2 ]; } } } //calulate the current center float fCenterX = ( fMaxX + fMinX )/2.0f; float fCenterY = ( fMaxY + fMinY )/2.0f; float fCenterZ = ( fMaxZ + fMinZ )/2.0f; //calculate the largest distance^2 from the center float fMaxDistance2 = 0.0f; float fDistance2; float fX2, fY2, fZ2; for( uiCountCoords = 0; uiCountCoords < 3 * m_uiNumVertices; uiCountCoords += 3 ) { fX2 = m_pafVertexCoordinates[ uiCountCoords ] - fCenterX; fX2 *= fX2; fY2 = m_pafVertexCoordinates[ uiCountCoords + 1 ] - fCenterY; fY2 *= fY2; fZ2 = m_pafVertexCoordinates[ uiCountCoords + 2 ] - fCenterZ; fZ2 *= fZ2; fDistance2 = fX2 + fY2 + fZ2; if( fDistance2 > fMaxDistance2 ) { fMaxDistance2 = fDistance2; } } //calculate normalising coefficient such that Xnew = cf * Xold float fCoefficient = fBCubeHalfWidth_ / sqrt( fMaxDistance2 ); //now do normalisation ( use seperate calcs for X,Y,Z so as to use possible //parallel floating point units). for( uiCountCoords = 0; uiCountCoords < 3 * m_uiNumVertices; uiCountCoords += 3 ) { m_pafVertexCoordinates[ uiCountCoords ] = fCoefficient * ( m_pafVertexCoordinates[ uiCountCoords ] - fCenterX ); m_pafVertexCoordinates[ uiCountCoords + 1 ] = fCoefficient * ( m_pafVertexCoordinates[ uiCountCoords + 1 ] - fCenterY ); m_pafVertexCoordinates[ uiCountCoords + 2 ] = fCoefficient * ( m_pafVertexCoordinates[ uiCountCoords + 2 ] - fCenterZ ); } } ////////////////////////////////////////////////////////////////////////////////////////// // Function: AURenMesh::Render // // Last Modified by: Douglas John Binks (DJB) // // Last Modified: 25 July 2000 // // Purpose: Draws the mesh. // // Inputs: None. // // Outputs: None. // // Returns: None. // ////////////////////////////////////////////////////////////////////////////////////////// void AURenMesh::Render(const AUColor* pCol ) const { if( 0 == m_uiNumVertices ) { return; } const GLfloat pafDiffuseColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; const GLfloat pafSpecularColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; const GLfloat fShininess = 40.0f; if( 0 == pCol ) { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pafDiffuseColor); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pafDiffuseColor); } else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pCol->m_Color.rgba ); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pCol->m_Color.rgba); } glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pafSpecularColor); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, fShininess); const GLint iNumCoordinatesPerVertex = 3; const GLsizei iStride = 0; //set up vertex arrays glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_NORMAL_ARRAY ); glVertexPointer( iNumCoordinatesPerVertex, GL_FLOAT, iStride, (const GLvoid*)m_pafVertexCoordinates ); glNormalPointer( GL_FLOAT, iStride, (const GLvoid*)m_pafNormals ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glTexCoordPointer( 2, GL_FLOAT, iStride, (const GLvoid*)m_pafTextureCoordinates ); //do actual drawing glDrawElements( GL_TRIANGLES, 3 * m_uiNumTriangles, GL_UNSIGNED_SHORT, m_pausTriangleIndices ); //unset vertex arrays glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_NORMAL_ARRAY ); }