// // Copyright (c) 2009-2010 Mikko Mononen memon@inside.org // // 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 "MeshLoaderObj.h" #include #include #include #define _USE_MATH_DEFINES #include rcMeshLoaderObj::rcMeshLoaderObj() : m_scale( 1.0f ), m_verts( 0 ), m_tris( 0 ), m_normals( 0 ), m_vertCount( 0 ), m_triCount( 0 ) { } rcMeshLoaderObj::~rcMeshLoaderObj() { delete[] m_verts; delete[] m_normals; delete[] m_tris; } void rcMeshLoaderObj::addVertex( float x, float y, float z, int& cap ) { if( m_vertCount + 1 > cap ) { cap = !cap ? 8 : cap * 2; float* nv = new float[cap * 3]; if( m_vertCount ) memcpy( nv, m_verts, m_vertCount * 3 * sizeof( float ) ); delete[] m_verts; m_verts = nv; } float* dst = &m_verts[ m_vertCount * 3 ]; *dst++ = x * m_scale; *dst++ = y * m_scale; *dst++ = z * m_scale; m_vertCount++; } void rcMeshLoaderObj::addTriangle( int a, int b, int c, int& cap ) { if( m_triCount + 1 > cap ) { cap = !cap ? 8 : cap * 2; int* nv = new int[cap * 3]; if( m_triCount ) memcpy( nv, m_tris, m_triCount * 3 * sizeof( int ) ); delete[] m_tris; m_tris = nv; } int* dst = &m_tris[ m_triCount * 3 ]; *dst++ = a; *dst++ = b; *dst++ = c; m_triCount++; } static char* parseRow( char* buf, char* bufEnd, char* row, int len ) { bool start = true; bool done = false; int n = 0; while( !done && buf < bufEnd ) { char c = *buf; buf++; // multirow switch( c ) { case '\\': break; case '\n': if( start ) break; done = true; break; case '\r': break; case '\t': case ' ': if( start ) break; // else falls through default: start = false; row[ n++ ] = c; if( n >= len - 1 ) done = true; break; } } row[ n ] = '\0'; return buf; } static int parseFace( char* row, int* data, int n, int vcnt ) { int j = 0; while( *row != '\0' ) { // Skip initial white space while( *row != '\0' && ( *row == ' ' || *row == '\t' ) ) row++; char* s = row; // Find vertex delimiter and terminated the string there for conversion. while( *row != '\0' && *row != ' ' && *row != '\t' ) { if( *row == '/' ) *row = '\0'; row++; } if( *s == '\0' ) continue; int vi = atoi( s ); data[ j++ ] = vi < 0 ? vi + vcnt : vi - 1; if( j >= n ) return j; } return j; } bool rcMeshLoaderObj::load( const std::string& filename ) { char* buf = 0; FILE* fp = fopen( filename.c_str(), "rb" ); if( !fp ) return false; if( fseek( fp, 0, SEEK_END ) != 0 ) { fclose( fp ); return false; } long bufSize = ftell( fp ); if( bufSize < 0 ) { fclose( fp ); return false; } if( fseek( fp, 0, SEEK_SET ) != 0 ) { fclose( fp ); return false; } buf = new char[bufSize]; if( !buf ) { fclose( fp ); return false; } size_t readLen = fread( buf, bufSize, 1, fp ); fclose( fp ); if( readLen != 1 ) { delete[] buf; return false; } char* src = buf; char* srcEnd = buf + bufSize; char row[512]; int face[32]; float x, y, z; int nv; int vcap = 0; int tcap = 0; while( src < srcEnd ) { // Parse one row row[ 0 ] = '\0'; src = parseRow( src, srcEnd, row, sizeof( row ) / sizeof( char ) ); // Skip comments if( row[ 0 ] == '#' ) continue; if( row[ 0 ] == 'v' && row[ 1 ] != 'n' && row[ 1 ] != 't' ) { // Vertex pos sscanf( row + 1, "%f %f %f", &x, &y, &z ); addVertex( x, y, z, vcap ); } if( row[ 0 ] == 'f' ) { // Faces nv = parseFace( row + 1, face, 32, m_vertCount ); for( int i = 2; i < nv; ++i ) { const int a = face[ 0 ]; const int b = face[ i - 1 ]; const int c = face[ i ]; if( a < 0 || a >= m_vertCount || b < 0 || b >= m_vertCount || c < 0 || c >= m_vertCount ) continue; addTriangle( a, b, c, tcap ); } } } delete[] buf; // Calculate normals. m_normals = new float[m_triCount * 3]; for( int i = 0; i < m_triCount * 3; i += 3 ) { const float* v0 = &m_verts[ m_tris[ i ] * 3 ]; const float* v1 = &m_verts[ m_tris[ i + 1 ] * 3 ]; const float* v2 = &m_verts[ m_tris[ i + 2 ] * 3 ]; float e0[3], e1[3]; for( int j = 0; j < 3; ++j ) { e0[ j ] = v1[ j ] - v0[ j ]; e1[ j ] = v2[ j ] - v0[ j ]; } float* n = &m_normals[ i ]; n[ 0 ] = e0[ 1 ] * e1[ 2 ] - e0[ 2 ] * e1[ 1 ]; n[ 1 ] = e0[ 2 ] * e1[ 0 ] - e0[ 0 ] * e1[ 2 ]; n[ 2 ] = e0[ 0 ] * e1[ 1 ] - e0[ 1 ] * e1[ 0 ]; float d = sqrtf( n[ 0 ] * n[ 0 ] + n[ 1 ] * n[ 1 ] + n[ 2 ] * n[ 2 ] ); if( d > 0 ) { d = 1.0f / d; n[ 0 ] *= d; n[ 1 ] *= d; n[ 2 ] *= d; } } m_filename = filename; return true; }