17#ifndef CSCI441_MODEL_LOADER_HPP
18#define CSCI441_MODEL_LOADER_HPP
22#ifdef CSCI441_USE_GLEW
60 [[maybe_unused]]
explicit ModelLoader(
const char* filename );
92 bool loadModelFile( std::
string filename,
bool INFO = true,
bool ERRORS = true );
100 [[maybe_unused]]
void setAttributeLocations(GLint positionLocation, GLint normalLocation = -1, GLint texCoordLocation = -1) const;
112 [[maybe_unused]]
bool draw( GLuint shaderProgramHandle,
113 GLint matDiffLocation = -1, GLint matSpecLocation = -1, GLint matShinLocation = -1, GLint matAmbLocation = -1,
114 GLenum diffuseTexture = GL_TEXTURE0 ) const;
120 [[maybe_unused]] [[nodiscard]] GLuint getNumberOfVertices() const;
126 [[maybe_unused]] [[nodiscard]] GLfloat* getVertices() const;
132 [[maybe_unused]] [[nodiscard]] GLfloat* getNormals() const;
138 [[maybe_unused]] [[nodiscard]] GLfloat* getTexCoords() const;
143 [[maybe_unused]] [[nodiscard]] GLuint getNumberOfIndices() const;
149 [[maybe_unused]] [[nodiscard]] GLuint* getIndices() const;
158 [[maybe_unused]] static
void enableAutoGenerateNormals();
165 [[maybe_unused]] static
void disableAutoGenerateNormals();
169 bool _loadMTLFile( const
char *mtlFilename,
bool INFO,
bool ERRORS );
170 bool _loadOBJFile(
bool INFO,
bool ERRORS );
171 bool _loadOFFFile(
bool INFO,
bool ERRORS );
172 bool _loadPLYFile(
bool INFO,
bool ERRORS );
173 bool _loadSTLFile(
bool INFO,
bool ERRORS );
174 static std::vector<std::
string> _tokenizeString( std::
string input, const std::
string& delimiters );
175 void _allocateAttributeArrays(GLuint numVertices, GLuint numIndices);
178 std::
string _filename;
179 CSCI441_INTERNAL::MODEL_TYPE _modelType;
184 glm::vec3* _vertices;
186 glm::vec2* _texCoords;
191 std::map< std::
string, CSCI441_INTERNAL::ModelMaterial* > _materials;
192 std::map< std::
string, std::vector< std::pair< GLuint, GLuint > > > _materialIndexStartStop;
194 bool _hasVertexTexCoords;
195 bool _hasVertexNormals;
200 static
bool sAUTO_GEN_NORMALS;
207namespace CSCI441_INTERNAL {
208 unsigned char* createTransparentTexture(
const unsigned char *imageData,
const unsigned char *imageMask,
int texWidth,
int texHeight,
int texChannels,
int maskChannels );
209 [[maybe_unused]]
void flipImageY(
int texWidth,
int texHeight,
int textureChannels,
unsigned char *textureData );
212inline bool CSCI441::ModelLoader::sAUTO_GEN_NORMALS =
false;
221 loadModelFile( filename );
241inline void CSCI441::ModelLoader::_init() {
242 _hasVertexTexCoords =
false;
243 _hasVertexNormals =
false;
246 _texCoords =
nullptr;
253 glGenVertexArrays( 1, &_vaod );
254 glGenBuffers( 2, _vbods );
259 _filename = std::move(filename);
260 if( _filename.find(
".obj") != std::string::npos ) {
261 result = _loadOBJFile( INFO, ERRORS );
262 _modelType = CSCI441_INTERNAL::MODEL_TYPE::OBJ;
264 else if( _filename.find(
".off") != std::string::npos ) {
265 result = _loadOFFFile( INFO, ERRORS );
266 _modelType = CSCI441_INTERNAL::MODEL_TYPE::OFF;
268 else if( _filename.find(
".ply") != std::string::npos ) {
269 result = _loadPLYFile( INFO, ERRORS );
270 _modelType = CSCI441_INTERNAL::MODEL_TYPE::PLY;
272 else if( _filename.find(
".stl") != std::string::npos ) {
273 result = _loadSTLFile( INFO, ERRORS );
274 _modelType = CSCI441_INTERNAL::MODEL_TYPE::STL;
278 if (ERRORS) fprintf( stderr,
"[ERROR]: Unsupported file format for file: %s\n", _filename.c_str() );
286 glBindVertexArray( _vaod );
287 glBindBuffer( GL_ARRAY_BUFFER, _vbods[0] );
289 glEnableVertexAttribArray( positionLocation );
290 glVertexAttribPointer( positionLocation, 3, GL_FLOAT, GL_FALSE, 0, (
void*)
nullptr );
292 glEnableVertexAttribArray( normalLocation );
293 glVertexAttribPointer( normalLocation, 3, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex) );
295 glEnableVertexAttribArray( texCoordLocation );
296 glVertexAttribPointer( texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex * 2) );
301 GLint matDiffLocation, GLint matSpecLocation, GLint matShinLocation, GLint matAmbLocation,
302 GLenum diffuseTexture )
const {
303 glBindVertexArray( _vaod );
306 if( _modelType == CSCI441_INTERNAL::MODEL_TYPE::OBJ ) {
307 for(
const auto & materialIter : _materialIndexStartStop) {
308 auto materialName = materialIter.first;
309 auto indexStartStop = materialIter.second;
311 CSCI441_INTERNAL::ModelMaterial* material =
nullptr;
312 if( _materials.find( materialName ) != _materials.end() )
313 material = _materials.find( materialName )->second;
315 for(
auto & idxIter : indexStartStop) {
316 auto start = idxIter.first;
317 auto end = idxIter.second;
318 GLsizei length = (GLsizei)(end - start) + 1;
322 if( material !=
nullptr ) {
323 glProgramUniform4fv( shaderProgramHandle, matAmbLocation, 1, &material->ambient[0] );
324 glProgramUniform4fv( shaderProgramHandle, matDiffLocation, 1, &material->diffuse[0] );
325 glProgramUniform4fv( shaderProgramHandle, matSpecLocation, 1, &material->specular[0] );
326 glProgramUniform1f( shaderProgramHandle, matShinLocation, material->shininess );
328 if( material->map_Kd != -1 ) {
329 glActiveTexture( diffuseTexture );
330 glBindTexture( GL_TEXTURE_2D, material->map_Kd );
334 glDrawElements( GL_TRIANGLES, length, GL_UNSIGNED_INT, (
void*)(
sizeof(GLuint)*start) );
338 glDrawElements( GL_TRIANGLES,
static_cast<GLint
>(_numIndices), GL_UNSIGNED_INT, (
void*)
nullptr );
352inline bool CSCI441::ModelLoader::_loadOBJFile(
bool INFO,
bool ERRORS ) {
355 if ( INFO ) fprintf( stdout,
"[.obj]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=- \n", _filename.c_str() );
360 std::ifstream in( _filename );
361 if( !in.is_open() ) {
362 if (ERRORS) fprintf( stderr,
"[.obj]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
363 if ( INFO ) fprintf( stdout,
"[.obj]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n", _filename.c_str() );
367 GLuint numObjects = 0, numGroups = 0;
368 GLuint numVertices = 0, numTexCoords = 0, numNormals = 0;
369 GLuint numFaces = 0, numTriangles = 0;
370 glm::vec3 minDimension = {999999.f, 999999.f, 999999.f};
371 glm::vec3 maxDimension = { -999999.f, -999999.f, -999999.f };
374 std::map<std::string, GLuint> uniqueCounts;
377 int progressCounter = 0;
379 while( getline( in, line ) ) {
380 if( line.length() > 1 && line.at(0) ==
'\t' )
381 line = line.substr( 1 );
382 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
384 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
385 if( tokens.empty() )
continue;
388 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
390 }
else if( tokens[0] ==
"o" ) {
392 }
else if( tokens[0] ==
"g" ) {
394 }
else if( tokens[0] ==
"mtllib" ) {
395 _loadMTLFile( tokens[1].c_str(), INFO, ERRORS );
396 }
else if( tokens[0] ==
"v" ) {
399 glm::vec3 pos = { strtof( tokens[1].c_str(),
nullptr ),
400 strtof( tokens[2].c_str(),
nullptr ),
401 strtof( tokens[3].c_str(),
nullptr ) };
403 if( pos.x < minDimension.x ) minDimension.x = pos.x;
404 if( pos.x > maxDimension.x ) maxDimension.x = pos.x;
405 if( pos.y < minDimension.y ) minDimension.y = pos.y;
406 if( pos.y > maxDimension.y ) maxDimension.y = pos.y;
407 if( pos.z < minDimension.z ) minDimension.z = pos.z;
408 if( pos.z > maxDimension.z ) maxDimension.z = pos.z;
409 }
else if( tokens[0] ==
"vn" ) {
411 }
else if( tokens[0] ==
"vt" ) {
413 }
else if( tokens[0] ==
"f" ) {
416 std::vector<std::string> faceTokens = _tokenizeString(line,
" ");
418 for (GLuint i = 1; i < faceTokens.size(); i++) {
420 std::vector<std::string> groupTokens = _tokenizeString(faceTokens[i],
"/");
422 for (
char j: faceTokens[i]) {
423 if (j ==
'/') numSlashes++;
426 std::stringstream currentFaceTokenStream;
428 auto signedVertexIndex = (GLint) strtol(groupTokens[0].c_str(),
nullptr, 10);
429 GLuint vertexIndex = signedVertexIndex;
430 if (signedVertexIndex < 0) vertexIndex = numVertices + signedVertexIndex + 1;
432 currentFaceTokenStream << vertexIndex;
435 if (groupTokens.size() == 2 && numSlashes == 1) {
436 _hasVertexTexCoords =
true;
438 auto signedTexCoordIndex = (GLint) strtol(groupTokens[1].c_str(),
nullptr, 10);
439 GLuint texCoordIndex = signedTexCoordIndex;
440 if (signedTexCoordIndex < 0) texCoordIndex = numTexCoords + signedTexCoordIndex + 1;
442 currentFaceTokenStream <<
"/" << texCoordIndex;
443 }
else if (groupTokens.size() == 2 && numSlashes == 2) {
444 _hasVertexNormals =
true;
446 auto signedNormalIndex = (GLint) strtol(groupTokens[1].c_str(),
nullptr, 10);
447 GLuint normalIndex = signedNormalIndex;
448 if (signedNormalIndex < 0) normalIndex = numNormals + signedNormalIndex + 1;
450 currentFaceTokenStream <<
"//" << normalIndex;
451 }
else if (groupTokens.size() == 3) {
452 _hasVertexTexCoords =
true;
453 _hasVertexNormals =
true;
455 auto signedTexCoordIndex = (GLint) strtol(groupTokens[1].c_str(),
nullptr, 10);
456 GLuint texCoordIndex = signedTexCoordIndex;
457 if (signedTexCoordIndex < 0) texCoordIndex = numTexCoords + signedTexCoordIndex + 1;
459 auto signedNormalIndex = (GLint) strtol(groupTokens[2].c_str(),
nullptr, 10);
460 GLuint normalIndex = signedNormalIndex;
461 if (signedNormalIndex < 0) normalIndex = numNormals + signedNormalIndex + 1;
463 currentFaceTokenStream <<
"/" << texCoordIndex <<
"/" << normalIndex;
464 }
else if (groupTokens.size() != 1) {
465 if (ERRORS) fprintf(stderr,
"[.obj]: [ERROR]: Malformed OBJ file, %s.\n", _filename.c_str());
469 std::string processedFaceToken = currentFaceTokenStream.str();
470 if (uniqueCounts.find(processedFaceToken) == uniqueCounts.end()) {
471 uniqueCounts.insert(std::pair<std::string, long int>(processedFaceToken, _uniqueIndex));
476 numTriangles += (faceTokens.size() - 1 - 3 + 1);
479 }
else if( tokens[0] ==
"usemtl" ) {
482 if (INFO) printf(
"[.obj]: ignoring line: %s\n", line.c_str() );
487 if( progressCounter % 5000 == 0 ) {
489 switch( progressCounter ) {
490 case 5000: printf(
"[.obj]: scanning %s...\\", _filename.c_str());
break;
491 case 10000: printf(
"[.obj]: scanning %s...|", _filename.c_str());
break;
492 case 15000: printf(
"[.obj]: scanning %s.../", _filename.c_str());
break;
493 case 20000: printf(
"[.obj]: scanning %s...-", _filename.c_str());
break;
498 if( progressCounter == 20000 )
505 printf(
"\33[2K\r" );
506 printf(
"[.obj]: scanning %s...done!\n", _filename.c_str() );
507 printf(
"[.obj]: ------------\n" );
508 printf(
"[.obj]: Model Stats:\n" );
509 printf(
"[.obj]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, numNormals, numTexCoords );
510 printf(
"[.obj]: Unique Verts:\t%u\n", _uniqueIndex );
511 printf(
"[.obj]: Faces: \t%u\tTriangles:\t%u\n", numFaces, numTriangles );
512 printf(
"[.obj]: Objects: \t%u\tGroups: \t%u\n", numObjects, numGroups );
514 glm::vec3 sizeDimensions = maxDimension - minDimension;
515 printf(
"[.obj]: Dimensions:\t(%f, %f, %f)\n", sizeDimensions.x, sizeDimensions.y, sizeDimensions.z );
518 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
519 if (INFO && !_hasVertexNormals)
520 printf(
"[.obj]: [WARN]: No vertex normals exist on model. To autogenerate vertex\n\tnormals, call CSCI441::ModelLoader::enableAutoGenerateNormals()\n\tprior to loading the model file.\n" );
521 _allocateAttributeArrays(_uniqueIndex, numTriangles*3);
523 if (INFO) printf(
"[.obj]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
524 _allocateAttributeArrays(numTriangles * 3, numTriangles*3);
527 auto objVertices =
new glm::vec3[numVertices];
528 auto objNormals =
new glm::vec3[numNormals];
529 auto objTexCoords =
new glm::vec2[numTexCoords];
531 std::vector<glm::vec3> verticesTemps;
532 std::vector<glm::vec2> texCoordsTemp;
534 printf(
"[.obj]: ------------\n" );
536 uniqueCounts.clear();
540 in.open( _filename );
542 GLuint verticesSeen = 0, texCoordsSeen = 0, normalsSeen = 0, indicesSeen = 0;
543 GLuint uniqueNumVertices = 0;
545 std::string currentMaterial =
"default";
546 _materialIndexStartStop.insert( std::pair< std::string, std::vector< std::pair< GLuint, GLuint > > >( currentMaterial, std::vector< std::pair< GLuint, GLuint > >(1) ) );
547 _materialIndexStartStop.find( currentMaterial )->second.back().first = indicesSeen;
549 while( getline( in, line ) ) {
550 if( line.length() > 1 && line.at(0) ==
'\t' )
551 line = line.substr( 1 );
552 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
554 auto tokens = _tokenizeString( line,
" \t" );
555 if( tokens.empty() )
continue;
558 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0
561 || tokens[0] ==
"mtllib"
565 }
else if( tokens[0] ==
"usemtl" ) {
566 if( currentMaterial ==
"default" && indicesSeen == 0 ) {
567 _materialIndexStartStop.clear();
569 _materialIndexStartStop.find( currentMaterial )->second.back().second = indicesSeen - 1;
571 currentMaterial = tokens[1];
572 if( _materialIndexStartStop.find( currentMaterial ) == _materialIndexStartStop.end() ) {
573 _materialIndexStartStop.insert( std::pair< std::string, std::vector< std::pair< GLuint, GLuint > > >( currentMaterial, std::vector< std::pair< GLuint, GLuint > >(1) ) );
574 _materialIndexStartStop.find( currentMaterial )->second.back().first = indicesSeen;
576 _materialIndexStartStop.find( currentMaterial )->second.emplace_back( indicesSeen, -1 );
578 }
else if( tokens[0] ==
"v" ) {
579 objVertices[verticesSeen++] = glm::vec3(strtof(tokens[1].c_str(),
nullptr ),
580 strtof( tokens[2].c_str(),
nullptr ),
581 strtof( tokens[3].c_str(),
nullptr ) );
582 }
else if( tokens[0] ==
"vn" ) {
583 objNormals[normalsSeen++] = glm::vec3(strtof(tokens[1].c_str(),
nullptr ),
584 strtof( tokens[2].c_str(),
nullptr ),
585 strtof( tokens[3].c_str(),
nullptr ));
586 }
else if( tokens[0] ==
"vt" ) {
587 objTexCoords[texCoordsSeen++] = glm::vec2(strtof(tokens[1].c_str(),
nullptr ),
588 strtof( tokens[2].c_str(),
nullptr ));
589 }
else if( tokens[0] ==
"f" ) {
590 std::vector<std::string> processedFaceTokens;
592 bool faceHasVertexNormals =
false;
593 bool faceHasTextureCoordinates =
false;
595 for(GLuint i = 1; i < tokens.size(); i++) {
597 auto vertexAttributeTokens = _tokenizeString(tokens[i],
"/");
598 int numAttributeSlashes = 0;
599 for(
char j : tokens[i]) {
600 if(j ==
'/') numAttributeSlashes++;
603 GLuint vertexIndex = 0, texCoordIndex = 0, normalIndex = 0;
605 std::stringstream currentFaceTokenStream;
606 auto signedVertexIndex = (GLint)strtol(vertexAttributeTokens[0].c_str(),
nullptr, 10);
607 vertexIndex = signedVertexIndex;
608 if(signedVertexIndex < 0) vertexIndex = verticesSeen + signedVertexIndex + 1;
609 currentFaceTokenStream << vertexIndex;
612 if(vertexAttributeTokens.size() == 2 && numAttributeSlashes == 1) {
614 _hasVertexTexCoords =
true;
615 faceHasTextureCoordinates =
true;
617 auto signedTexCoordIndex = (GLint)strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10);
618 texCoordIndex = signedTexCoordIndex;
619 if(signedTexCoordIndex < 0) texCoordIndex = texCoordsSeen + signedTexCoordIndex + 1;
620 currentFaceTokenStream <<
"/" << texCoordIndex;
621 }
else if(vertexAttributeTokens.size() == 2 && numAttributeSlashes == 2) {
623 _hasVertexNormals =
true;
624 faceHasVertexNormals =
true;
626 auto signedNormalIndex = (GLint)strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10);
627 normalIndex = signedNormalIndex;
628 if(signedNormalIndex < 0) normalIndex = normalsSeen + signedNormalIndex + 1;
629 currentFaceTokenStream <<
"//" << normalIndex;
630 }
else if(vertexAttributeTokens.size() == 3) {
632 _hasVertexTexCoords =
true;
633 faceHasTextureCoordinates =
true;
634 _hasVertexNormals =
true;
635 faceHasVertexNormals =
true;
637 auto signedTexCoordIndex = (GLint)strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10);
638 texCoordIndex = signedTexCoordIndex;
639 if(signedTexCoordIndex < 0) texCoordIndex = texCoordsSeen + signedTexCoordIndex + 1;
641 auto signedNormalIndex = (GLint)strtol(vertexAttributeTokens[2].c_str(),
nullptr, 10);
642 normalIndex = signedNormalIndex;
643 if(signedNormalIndex < 0) normalIndex = normalsSeen + signedNormalIndex + 1;
645 currentFaceTokenStream <<
"/" << texCoordIndex <<
"/" << normalIndex;
646 }
else if(vertexAttributeTokens.size() != 1) {
647 if (ERRORS) fprintf(stderr,
"[.obj]: [ERROR]: Malformed OBJ file, %s.\n", _filename.c_str());
651 auto processedFaceToken = currentFaceTokenStream.str();
652 processedFaceTokens.push_back(processedFaceToken);
655 if( uniqueCounts.find( processedFaceToken ) == uniqueCounts.end() ) {
657 uniqueCounts.insert( std::pair<std::string,GLuint>(processedFaceToken, uniqueNumVertices) );
659 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
660 _vertices[ _uniqueIndex ] = objVertices[ vertexIndex - 1 ];
661 if(faceHasTextureCoordinates && texCoordIndex != 0) { _texCoords[ _uniqueIndex ] = objTexCoords[ texCoordIndex - 1 ]; }
662 if(faceHasVertexNormals && normalIndex != 0) _normals[ _uniqueIndex ] = objNormals[ normalIndex - 1 ];
665 verticesTemps.push_back( objVertices[ vertexIndex - 1 ] );
666 if(faceHasTextureCoordinates && texCoordIndex != 0) { texCoordsTemp.push_back( objTexCoords[ texCoordIndex - 1 ] ); }
668 if( (vertexAttributeTokens.size() == 2 && numAttributeSlashes == 2)
669 || (vertexAttributeTokens.size() == 3) ) {
671 if (ERRORS) fprintf( stderr,
"[.obj]: [ERROR]: no vertex normals were specified, should not be trying to access values\n" );
678 for(GLuint i = 1; i < processedFaceTokens.size()-1; i++) {
679 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
680 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[0] )->second;
681 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[i] )->second;
682 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[i+1] )->second;
686 GLuint aI = uniqueCounts.find( processedFaceTokens[0] )->second;
687 GLuint bI = uniqueCounts.find( processedFaceTokens[i] )->second;
688 GLuint cI = uniqueCounts.find( processedFaceTokens[i+1] )->second;
690 glm::vec3 a = verticesTemps[aI];
691 glm::vec3 b = verticesTemps[bI];
692 glm::vec3 c = verticesTemps[cI];
694 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
695 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
696 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
698 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
699 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
700 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
702 _vertices[ _uniqueIndex ] = a;
703 _normals[ _uniqueIndex ] = aN;
704 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ aI ]; }
705 _indices[ _numIndices++ ] = _uniqueIndex++;
709 _vertices[ _uniqueIndex ] = b;
710 _normals[ _uniqueIndex ] = bN;
711 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ bI ]; }
712 _indices[ _numIndices++ ] = _uniqueIndex++;
716 _vertices[ _uniqueIndex ] = c;
717 _normals[ _uniqueIndex ] = cN;
718 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ cI ]; }
719 _indices[ _numIndices++ ] = _uniqueIndex++;
728 if( progressCounter % 5000 == 0 ) {
730 switch( progressCounter ) {
731 case 5000: printf(
"[.obj]: parsing %s...\\", _filename.c_str());
break;
732 case 10000: printf(
"[.obj]: parsing %s...|", _filename.c_str());
break;
733 case 15000: printf(
"[.obj]: parsing %s.../", _filename.c_str());
break;
734 case 20000: printf(
"[.obj]: parsing %s...-", _filename.c_str());
break;
739 if( progressCounter == 20000 )
747 printf(
"\33[2K\r" );
748 printf(
"[.obj]: parsing %s...done!\n", _filename.c_str() );
751 _materialIndexStartStop.find( currentMaterial )->second.back().second = indicesSeen - 1;
756 double seconds = difftime( end, start );
759 printf(
"[.obj]: Completed in %.3fs\n", seconds );
760 printf(
"[.obj]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n\n", _filename.c_str() );
766inline bool CSCI441::ModelLoader::_loadMTLFile(
const char* mtlFilename,
bool INFO,
bool ERRORS ) {
769 if (INFO) printf(
"[.mtl]: -*-*-*-*-*-*-*- BEGIN %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
773 if( _filename.find(
'/') != std::string::npos ) {
774 path = _filename.substr( 0, _filename.find_last_of(
'/')+1 );
780 in.open( mtlFilename );
781 if( !in.is_open() ) {
782 std::string folderMtlFile = path + mtlFilename;
783 in.open( folderMtlFile.c_str() );
784 if( !in.is_open() ) {
785 if (ERRORS) fprintf( stderr,
"[.mtl]: [ERROR]: could not open material file: %s\n", mtlFilename );
786 if ( INFO ) printf(
"[.mtl]: -*-*-*-*-*-*-*- END %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
791 CSCI441_INTERNAL::ModelMaterial* currentMaterial =
nullptr;
792 std::string materialName;
794 unsigned char *textureData =
nullptr;
795 unsigned char *maskData =
nullptr;
796 unsigned char *fullData;
797 int texWidth, texHeight, textureChannels = 1, maskChannels = 1;
798 GLuint textureHandle = 0;
800 std::map< std::string, GLuint > imageHandles;
802 int numMaterials = 0;
804 while( getline( in, line ) ) {
805 if( line.length() > 1 && line.at(0) ==
'\t' )
806 line = line.substr( 1 );
807 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
809 std::vector< std::string > tokens = _tokenizeString( line,
" /" );
810 if( tokens.empty() )
continue;
813 if( tokens[0] ==
"#" ) {
815 }
else if( tokens[0] ==
"newmtl" ) {
816 if (INFO) printf(
"[.mtl]: Parsing material %s properties\n", tokens[1].c_str() );
817 currentMaterial =
new CSCI441_INTERNAL::ModelMaterial();
818 materialName = tokens[1];
819 _materials.insert( std::pair<std::string, CSCI441_INTERNAL::ModelMaterial*>( materialName, currentMaterial ) );
822 textureData =
nullptr;
828 }
else if( tokens[0] ==
"Ka" ) {
829 currentMaterial->ambient[0] = strtof( tokens[1].c_str(),
nullptr );
830 currentMaterial->ambient[1] = strtof( tokens[2].c_str(),
nullptr );
831 currentMaterial->ambient[2] = strtof( tokens[3].c_str(),
nullptr );
832 }
else if( tokens[0] ==
"Kd" ) {
833 currentMaterial->diffuse[0] = strtof( tokens[1].c_str(),
nullptr );
834 currentMaterial->diffuse[1] = strtof( tokens[2].c_str(),
nullptr );
835 currentMaterial->diffuse[2] = strtof( tokens[3].c_str(),
nullptr );
836 }
else if( tokens[0] ==
"Ks" ) {
837 currentMaterial->specular[0] = strtof( tokens[1].c_str(),
nullptr );
838 currentMaterial->specular[1] = strtof( tokens[2].c_str(),
nullptr );
839 currentMaterial->specular[2] = strtof( tokens[3].c_str(),
nullptr );
840 }
else if( tokens[0] ==
"Ke" ) {
841 currentMaterial->emissive[0] = strtof( tokens[1].c_str(),
nullptr );
842 currentMaterial->emissive[1] = strtof( tokens[2].c_str(),
nullptr );
843 currentMaterial->emissive[2] = strtof( tokens[3].c_str(),
nullptr );
844 }
else if( tokens[0] ==
"Ns" ) {
845 currentMaterial->shininess = strtof( tokens[1].c_str(),
nullptr );
846 }
else if( tokens[0] ==
"Tr"
847 || tokens[0] ==
"d" ) {
848 currentMaterial->ambient[3] = strtof( tokens[1].c_str(),
nullptr );
849 currentMaterial->diffuse[3] = strtof( tokens[1].c_str(),
nullptr );
850 currentMaterial->specular[3] = strtof( tokens[1].c_str(),
nullptr );
851 }
else if( tokens[0] ==
"illum" ) {
853 }
else if( tokens[0] ==
"map_Kd" ) {
854 if( imageHandles.find( tokens[1] ) != imageHandles.end() ) {
856 currentMaterial->map_Kd = imageHandles.find( tokens[1] )->second;
858 stbi_set_flip_vertically_on_load(
true);
859 textureData = stbi_load( tokens[1].c_str(), &texWidth, &texHeight, &textureChannels, 0 );
861 std::string folderName = path + tokens[1];
862 textureData = stbi_load( folderName.c_str(), &texWidth, &texHeight, &textureChannels, 0 );
866 if (ERRORS) fprintf( stderr,
"[.mtl]: [ERROR]: File Not Found: %s\n", tokens[1].c_str() );
868 if (INFO) printf(
"[.mtl]: TextureMap:\t%s\tSize: %dx%d\tColors: %d\n", tokens[1].c_str(), texWidth, texHeight, textureChannels );
870 if( maskData ==
nullptr ) {
871 if( textureHandle == 0 ) {
872 glGenTextures( 1, &textureHandle );
873 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
876 glBindTexture( GL_TEXTURE_2D, textureHandle );
878 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
879 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
881 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
882 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
884 GLint colorSpace = GL_RGB;
885 if( textureChannels == 4 )
886 colorSpace = GL_RGBA;
887 glTexImage2D( GL_TEXTURE_2D, 0, colorSpace, texWidth, texHeight, 0, colorSpace, GL_UNSIGNED_BYTE, textureData );
889 currentMaterial->map_Kd = textureHandle;
891 fullData = CSCI441_INTERNAL::createTransparentTexture( textureData, maskData, texWidth, texHeight, textureChannels, maskChannels );
893 if( textureHandle == 0 ) {
894 glGenTextures( 1, &textureHandle );
895 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
898 glBindTexture( GL_TEXTURE_2D, textureHandle );
900 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
901 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
903 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
904 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
906 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullData );
910 currentMaterial->map_Kd = textureHandle;
914 }
else if( tokens[0] ==
"map_d" ) {
915 if( imageHandles.find( tokens[1] ) != imageHandles.end() ) {
917 currentMaterial->map_d = imageHandles.find( tokens[1] )->second;
919 stbi_set_flip_vertically_on_load(
true);
920 maskData = stbi_load( tokens[1].c_str(), &texWidth, &texHeight, &textureChannels, 0 );
922 std::string folderName = path + tokens[1];
923 maskData = stbi_load( folderName.c_str(), &texWidth, &texHeight, &textureChannels, 0 );
927 if (ERRORS) fprintf( stderr,
"[.mtl]: [ERROR]: File Not Found: %s\n", tokens[1].c_str() );
929 if (INFO) printf(
"[.mtl]: AlphaMap: \t%s\tSize: %dx%d\tColors: %d\n", tokens[1].c_str(), texWidth, texHeight, maskChannels );
931 if( textureData !=
nullptr ) {
932 fullData = CSCI441_INTERNAL::createTransparentTexture( textureData, maskData, texWidth, texHeight, textureChannels, maskChannels );
934 if( textureHandle == 0 ) {
935 glGenTextures( 1, &textureHandle );
936 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
939 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
940 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
942 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
943 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
945 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullData );
949 currentMaterial->map_Kd = textureHandle;
953 }
else if( tokens[0] ==
"map_Ka" ) {
955 }
else if( tokens[0] ==
"map_Ks" ) {
957 }
else if( tokens[0] ==
"map_Ns" ) {
959 }
else if( tokens[0] ==
"Ni" ) {
961 }
else if( tokens[0] ==
"Tf" ) {
963 }
else if( tokens[0] ==
"bump"
964 || tokens[0] ==
"map_bump" ) {
967 if (INFO) printf(
"[.mtl]: ignoring line: %s\n", line.c_str() );
974 printf(
"[.mtl]: Materials:\t%d\n", numMaterials );
975 printf(
"[.mtl]: -*-*-*-*-*-*-*- END %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
981inline bool CSCI441::ModelLoader::_loadOFFFile(
bool INFO,
bool ERRORS ) {
984 if (INFO ) printf(
"[.off]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
989 std::ifstream in( _filename );
990 if( !in.is_open() ) {
991 if (ERRORS) fprintf( stderr,
"[.off]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
992 if ( INFO ) printf(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
996 GLuint numVertices = 0, numFaces = 0, numTriangles = 0;
997 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1000 enum OFF_FILE_STATE { HEADER, VERTICES, FACES, DONE };
1002 OFF_FILE_STATE fileState = HEADER;
1004 GLuint vSeen = 0, fSeen = 0;
1006 while( getline( in, line ) ) {
1007 if( line.length() > 1 && line.at(0) ==
'\t' )
1008 line = line.substr( 1 );
1009 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1011 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1012 if( tokens.empty() )
continue;
1015 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
1016 }
else if( fileState == HEADER ) {
1017 if( tokens[0] ==
"OFF" ) {
1019 if( tokens.size() != 3 ) {
1020 if (ERRORS) fprintf( stderr,
"[.off]: [ERROR]: Malformed OFF file. # vertices, faces, edges not properly specified\n" );
1021 if ( INFO ) printf(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1026 numVertices = (GLuint)strtol( tokens[0].c_str(),
nullptr, 10 );
1027 numFaces = (GLuint)strtol( tokens[1].c_str(),
nullptr, 10 );
1032 fileState = VERTICES;
1034 }
else if( fileState == VERTICES ) {
1036 GLfloat x = strtof( tokens[0].c_str(),
nullptr ),
1037 y = strtof( tokens[1].c_str(),
nullptr ),
1038 z = strtof( tokens[2].c_str(),
nullptr );
1040 if( x < minX ) minX = x;
1041 if( x > maxX ) maxX = x;
1042 if( y < minY ) minY = y;
1043 if( y > maxY ) maxY = y;
1044 if( z < minZ ) minZ = z;
1045 if( z > maxZ ) maxZ = z;
1048 if( vSeen == numVertices )
1050 }
else if( fileState == FACES ) {
1051 auto numberOfVerticesInFace = (GLuint)strtol( tokens[0].c_str(),
nullptr, 10 );
1053 numTriangles += numberOfVerticesInFace - 3 + 1;
1055 if( fSeen == numFaces )
1058 if (INFO) printf(
"[.off]: unknown file state: %d\n", fileState );
1064 printf(
"\33[2K\r" );
1065 printf(
"[.off]: scanning %s...done!\n", _filename.c_str() );
1066 printf(
"[.off]: ------------\n" );
1067 printf(
"[.off]: Model Stats:\n" );
1068 printf(
"[.off]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, 0, 0 );
1069 printf(
"[.off]: Faces: \t%u\tTriangles: \t%u\n", numFaces, numTriangles );
1070 printf(
"[.off]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1073 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1074 if (INFO && !_hasVertexNormals)
1075 printf(
"[.off]: [WARN]: No vertex normals exist on model. To autogenerate vertex\n\tnormals, call CSCI441::ModelLoader::enableAutoGenerateNormals()\n\tprior to loading the model file.\n" );
1076 _allocateAttributeArrays(numVertices, numTriangles*3);
1078 if (INFO) printf(
"[.off]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
1079 _allocateAttributeArrays(numTriangles*3, numTriangles*3);
1082 std::vector<glm::vec3> verticesTemp;
1084 if (INFO) printf(
"[.off]: ------------\n" );
1086 in.open( _filename );
1094 int progressCounter = 0;
1096 while( getline( in, line ) ) {
1097 if( line.length() > 1 && line.at(0) ==
'\t' )
1098 line = line.substr( 1 );
1099 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1101 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1102 if( tokens.empty() )
continue;
1105 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
1107 }
else if( fileState == HEADER ) {
1108 if( tokens[0] ==
"OFF" ) {
1112 fileState = VERTICES;
1114 }
else if( fileState == VERTICES ) {
1116 glm::vec3 pos(strtof( tokens[0].c_str(),
nullptr ),
1117 strtof( tokens[1].c_str(),
nullptr ),
1118 strtof( tokens[2].c_str(),
nullptr ) );
1121 if( tokens.size() == 6 || tokens.size() == 7 ) {
1129 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1130 _vertices[ _uniqueIndex ] = pos;
1133 verticesTemp.push_back(pos);
1137 if( _uniqueIndex == numVertices || vSeen == numVertices )
1139 }
else if( fileState == FACES ) {
1140 auto numberOfVerticesInFace = (GLuint)strtol( tokens[0].c_str(),
nullptr, 10 );
1143 for(GLuint i = 2; i <= numberOfVerticesInFace - 1; i++) {
1144 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1145 auto fanRoot = strtol( tokens[1].c_str(),
nullptr, 10 );
1146 auto fanA = strtol( tokens[i].c_str(),
nullptr, 10 );
1147 auto fanB = strtol( tokens[i+1].c_str(),
nullptr, 10 );
1149 if( fanRoot < 0 ) fanRoot = numVertices + fanRoot + 1;
1150 if( fanA < 0 ) fanA = numVertices + fanA + 1;
1151 if( fanB < 0 ) fanB = numVertices + fanB + 1;
1154 _indices[ _numIndices++ ] = fanRoot;
1155 _indices[ _numIndices++ ] = fanA;
1156 _indices[ _numIndices++ ] = fanB;
1158 auto aI = strtol( tokens[1].c_str(),
nullptr, 10 );
1159 auto bI = strtol( tokens[i].c_str(),
nullptr, 10 );
1160 auto cI = strtol( tokens[i+1].c_str(),
nullptr, 10 );
1162 if( aI < 0 ) aI = numVertices + aI + 1;
1163 if( bI < 0 ) bI = numVertices + bI + 1;
1164 if( cI < 0 ) cI = numVertices + cI + 1;
1166 glm::vec3 a = verticesTemp[aI];
1167 glm::vec3 b = verticesTemp[bI];
1168 glm::vec3 c = verticesTemp[cI];
1170 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
1171 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
1172 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
1174 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
1175 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
1176 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
1178 _vertices[ _uniqueIndex ] = a;
1179 _normals[ _uniqueIndex ] = aN;
1180 _indices[ _numIndices++ ] = _uniqueIndex++;
1182 _vertices[ _uniqueIndex ] = b;
1183 _normals[ _uniqueIndex ] = bN;
1184 _indices[ _numIndices++ ] = _uniqueIndex++;
1186 _vertices[ _uniqueIndex ] = c;
1187 _normals[ _uniqueIndex ] = cN;
1188 _indices[ _numIndices++ ] = _uniqueIndex++;
1213 if( progressCounter % 5000 == 0 ) {
1215 switch( progressCounter ) {
1216 case 5000: printf(
"[.off]: parsing %s...\\", _filename.c_str());
break;
1217 case 10000: printf(
"[.off]: parsing %s...|", _filename.c_str());
break;
1218 case 15000: printf(
"[.off]: parsing %s.../", _filename.c_str());
break;
1219 case 20000: printf(
"[.off]: parsing %s...-", _filename.c_str());
break;
1224 if( progressCounter == 20000 )
1225 progressCounter = 0;
1233 double seconds = difftime( end, start );
1236 printf(
"\33[2K\r" );
1237 printf(
"[.off]: parsing %s...done! (Time: %.1fs)\n", _filename.c_str(), seconds );
1238 printf(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1245inline bool CSCI441::ModelLoader::_loadPLYFile(
bool INFO,
bool ERRORS ) {
1248 if (INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1253 std::ifstream in( _filename );
1254 if( !in.is_open() ) {
1255 if (ERRORS) fprintf( stderr,
"[.ply]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
1256 if ( INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1260 GLuint numVertices = 0, numFaces = 0, numTriangles = 0, numMaterials = 0;
1261 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1264 enum PLY_FILE_STATE { HEADER, VERTICES, FACES, MATERIALS };
1265 enum PLY_ELEMENT_TYPE { NONE, VERTEX, FACE, MATERIAL };
1267 PLY_FILE_STATE fileState = HEADER;
1268 PLY_ELEMENT_TYPE elemType = NONE;
1270 GLuint progressCounter = 0;
1273 while( getline( in, line ) ) {
1274 if( line.length() > 1 && line.at(0) ==
'\t' )
1275 line = line.substr( 1 );
1276 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1278 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1280 if( tokens.empty() )
continue;
1283 if( tokens[0] ==
"comment" ) {
1284 }
else if( fileState == HEADER ) {
1285 if( tokens[0] ==
"ply" ) {
1286 }
else if( tokens[0] ==
"format" ) {
1287 if( tokens[1] !=
"ascii" ) {
1288 if (ERRORS) fprintf( stderr,
"[.ply]: [ERROR]: File \"%s\" not ASCII format\n", _filename.c_str() );
1289 if ( INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1293 }
else if( tokens[0] ==
"element" ) {
1294 if( tokens[1] ==
"vertex" ) {
1295 numVertices = (GLuint)strtol( tokens[2].c_str(),
nullptr, 10 );
1297 }
else if( tokens[1] ==
"face" ) {
1298 numFaces = (GLuint)strtol( tokens[2].c_str(),
nullptr, 10 );
1300 }
else if( tokens[1] ==
"edge" ) {
1302 }
else if( tokens[1] ==
"material" ) {
1303 numMaterials = (GLuint)strtol( tokens[2].c_str(),
nullptr, 10 );
1304 elemType = MATERIAL;
1308 }
else if( tokens[0] ==
"property" ) {
1309 if( elemType == VERTEX ) {
1311 }
else if( elemType == FACE ) {
1313 }
else if( elemType == MATERIAL ) {
1316 }
else if( tokens[0] ==
"end_header" ) {
1317 fileState = VERTICES;
1319 }
else if( fileState == VERTICES ) {
1321 auto x = (GLfloat) strtof( tokens[0].c_str(), nullptr ),
1322 y = (GLfloat) strtof( tokens[1].c_str(),
nullptr ),
1323 z = (GLfloat) strtof( tokens[2].c_str(), nullptr );
1325 if( x < minX ) minX = x;
1326 if( x > maxX ) maxX = x;
1327 if( y < minY ) minY = y;
1328 if( y > maxY ) maxY = y;
1329 if( z < minZ ) minZ = z;
1330 if( z > maxZ ) maxZ = z;
1334 if( vSeen == numVertices )
1336 }
else if( fileState == FACES ) {
1337 auto numberOfVerticesInFace = (GLuint)strtol( tokens[0].c_str(),
nullptr, 10);
1338 numTriangles += numberOfVerticesInFace - 3 + 1;
1340 if (INFO) printf(
"[.ply]: unknown file state: %d\n", fileState );
1345 if( progressCounter % 5000 == 0 ) {
1347 switch( progressCounter ) {
1348 case 5000: printf(
"[.ply]: scanning %s...\\", _filename.c_str());
break;
1349 case 10000: printf(
"[.ply]: scanning %s...|", _filename.c_str());
break;
1350 case 15000: printf(
"[.ply]: scanning %s.../", _filename.c_str());
break;
1351 case 20000: printf(
"[.ply]: scanning %s...-", _filename.c_str());
break;
1356 if( progressCounter == 20000 )
1357 progressCounter = 0;
1363 printf(
"\33[2K\r" );
1364 printf(
"[.ply]: scanning %s...done!\n", _filename.c_str() );
1365 printf(
"[.ply]: ------------\n" );
1366 printf(
"[.ply]: Model Stats:\n" );
1367 printf(
"[.ply]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, 0, 0 );
1368 printf(
"[.ply]: Faces: \t%u\tTriangles: \t%u\n", numFaces, numTriangles );
1369 printf(
"[.ply]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1372 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1373 if (INFO && !_hasVertexNormals)
1374 printf(
"[.ply]: [WARN]: No vertex normals exist on model. To autogenerate vertex\n\tnormals, call CSCI441::ModelLoader::enableAutoGenerateNormals()\n\tprior to loading the model file.\n" );
1375 _allocateAttributeArrays(numVertices, numTriangles*3);
1377 if (INFO) printf(
"[.ply]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
1378 _allocateAttributeArrays(numTriangles*3, numTriangles*3);
1381 if (INFO) printf(
"[.ply]: ------------\n" );
1383 std::vector<glm::vec3> verticesTemp;
1385 in.open( _filename );
1393 progressCounter = 0;
1396 while( getline( in, line ) ) {
1397 if( line.length() > 1 && line.at(0) ==
'\t' )
1398 line = line.substr( 1 );
1399 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1401 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1403 if( tokens.empty() )
continue;
1406 if( tokens[0] ==
"comment" ) {
1407 }
else if( fileState == HEADER ) {
1408 if( tokens[0] ==
"ply" ) {
1409 }
else if( tokens[0] ==
"format" ) {
1410 if( tokens[1] !=
"ascii" ) {
1411 if (ERRORS) fprintf( stderr,
"[.ply]: [ERROR]: File \"%s\" not ASCII format\n", _filename.c_str() );
1412 if ( INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1416 }
else if( tokens[0] ==
"element" ) {
1417 if( tokens[1] ==
"vertex" ) {
1418 numVertices = (GLuint)strtol( tokens[2].c_str(),
nullptr, 10 );
1420 }
else if( tokens[1] ==
"face" ) {
1421 numFaces = (GLuint)strtol( tokens[2].c_str(),
nullptr, 10 );
1423 }
else if( tokens[1] ==
"edge" ) {
1425 }
else if( tokens[1] ==
"material" ) {
1426 numMaterials = (GLuint)strtol( tokens[2].c_str(),
nullptr, 10 );
1427 elemType = MATERIAL;
1431 }
else if( tokens[0] ==
"property" ) {
1432 if( elemType == VERTEX ) {
1434 }
else if( elemType == FACE ) {
1436 }
else if( elemType == MATERIAL ) {
1439 }
else if( tokens[0] ==
"end_header" ) {
1440 fileState = VERTICES;
1442 }
else if( fileState == VERTICES ) {
1444 glm::vec3 pos(strtof( tokens[0].c_str(),
nullptr ),
1445 strtof( tokens[1].c_str(),
nullptr ),
1446 strtof( tokens[2].c_str(),
nullptr ) );
1448 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1449 _vertices[ _uniqueIndex ] = pos;
1452 verticesTemp.push_back(pos );
1456 if( _uniqueIndex == numVertices || vSeen == numVertices )
1458 }
else if( fileState == FACES ) {
1459 auto numberOfVerticesInFace = (GLuint)strtol( tokens[0].c_str(),
nullptr, 10 );
1461 for( GLuint i = 2; i <= numberOfVerticesInFace - 1; i++ ) {
1462 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1463 _indices[ _numIndices++ ] = (GLuint)strtol( tokens[1].c_str(),
nullptr, 10 );
1464 _indices[ _numIndices++ ] = (GLuint)strtol( tokens[i].c_str(),
nullptr, 10 );
1465 _indices[ _numIndices++ ] = (GLuint)strtol( tokens[i+1].c_str(),
nullptr, 10 );
1467 auto aI = (GLuint)strtol( tokens[1].c_str(),
nullptr, 10 );
1468 auto bI = (GLuint)strtol( tokens[i].c_str(),
nullptr, 10 );
1469 auto cI = (GLuint)strtol( tokens[i+1].c_str(),
nullptr, 10 );
1471 glm::vec3 a = verticesTemp[aI];
1472 glm::vec3 b = verticesTemp[bI];
1473 glm::vec3 c = verticesTemp[cI];
1475 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
1476 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
1477 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
1479 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
1480 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
1481 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
1483 _vertices[ _uniqueIndex ] = a;
1484 _normals[ _uniqueIndex ] = aN;
1485 _indices[ _numIndices++ ] = _uniqueIndex++;
1487 _vertices[ _uniqueIndex ] = b;
1488 _normals[ _uniqueIndex ] = bN;
1489 _indices[ _numIndices++ ] = _uniqueIndex++;
1491 _vertices[ _uniqueIndex ] = c;
1492 _normals[ _uniqueIndex ] = cN;
1493 _indices[ _numIndices++ ] = _uniqueIndex++;
1497 if (INFO) printf(
"[.ply]: unknown file state: %d\n", fileState );
1502 if( progressCounter % 5000 == 0 ) {
1504 switch( progressCounter ) {
1505 case 5000: printf(
"[.ply]: parsing %s...\\", _filename.c_str());
break;
1506 case 10000: printf(
"[.ply]: parsing %s...|", _filename.c_str());
break;
1507 case 15000: printf(
"[.ply]: parsing %s.../", _filename.c_str());
break;
1508 case 20000: printf(
"[.ply]: parsing %s...-", _filename.c_str());
break;
1513 if( progressCounter == 20000 )
1514 progressCounter = 0;
1522 double seconds = difftime( end, start );
1525 printf(
"\33[2K\r" );
1526 printf(
"[.ply]: parsing %s...done!\n[.ply]: Time to complete: %.3fs\n", _filename.c_str(), seconds );
1527 printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1533inline bool CSCI441::ModelLoader::_loadSTLFile(
bool INFO,
bool ERRORS ) {
1536 if (INFO) printf(
"[.stl]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1541 std::ifstream in( _filename );
1542 if( !in.is_open() ) {
1543 if (ERRORS) fprintf(stderr,
"[.stl]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
1544 if ( INFO ) printf(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1548 GLuint numVertices = 0, numNormals = 0, numFaces = 0, numTriangles = 0, numVertsInLoop = 0;
1549 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1552 int progressCounter = 0;
1553 glm::vec3 normalVector = {0.f,0.f,0.f};
1555 while( getline( in, line ) ) {
1556 if( line.length() > 1 && line.at(0) ==
'\t' )
1557 line = line.substr( 1 );
1558 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1560 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1562 if( tokens.empty() )
continue;
1564 if( tokens[0] ==
"solid" ) {
1566 }
else if( tokens[0] ==
"facet" ) {
1569 }
else if( tokens[0] ==
"outer" && tokens[1] ==
"loop" ) {
1572 }
else if( tokens[0] ==
"vertex" ) {
1573 GLfloat x = strtof( tokens[1].c_str(),
nullptr ),
1574 y = strtof( tokens[2].c_str(),
nullptr ),
1575 z = strtof( tokens[3].c_str(),
nullptr );
1577 if( x < minX ) minX = x;
1578 if( x > maxX ) maxX = x;
1579 if( y < minY ) minY = y;
1580 if( y > maxY ) maxY = y;
1581 if( z < minZ ) minZ = z;
1582 if( z > maxZ ) maxZ = z;
1586 }
else if( tokens[0] ==
"endloop" ) {
1588 numTriangles += numVertsInLoop - 3 + 1;
1589 }
else if( tokens[0] ==
"endfacet" ) {
1591 }
else if( tokens[0] ==
"endsolid" ) {
1595 if( memchr( line.c_str(),
'\0', line.length() ) !=
nullptr ) {
1596 if (ERRORS) fprintf( stderr,
"[.stl]: [ERROR]: Cannot read binary STL file \"%s\"\n", _filename.c_str() );
1597 if ( INFO ) printf(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1600 }
else if (INFO) printf(
"[.stl]: unknown line: %s\n", line.c_str() );
1605 if( progressCounter % 5000 == 0 ) {
1607 switch( progressCounter ) {
1608 case 5000: printf(
"[.stl]: scanning %s...\\", _filename.c_str());
break;
1609 case 10000: printf(
"[.stl]: scanning %s...|", _filename.c_str());
break;
1610 case 15000: printf(
"[.stl]: scanning %s.../", _filename.c_str());
break;
1611 case 20000: printf(
"[.stl]: scanning %s...-", _filename.c_str());
break;
1616 if( progressCounter == 20000 )
1617 progressCounter = 0;
1623 printf(
"\33[2K\r" );
1624 printf(
"[.stl]: scanning %s...done!\n", _filename.c_str() );
1625 printf(
"[.stl]: ------------\n" );
1626 printf(
"[.stl]: Model Stats:\n" );
1627 printf(
"[.stl]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, numNormals, 0 );
1628 printf(
"[.stl]: Faces: \t%u\tTriangles: \t%u\n", numFaces, numTriangles );
1629 printf(
"[.stl]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1632 _allocateAttributeArrays(numVertices, numTriangles*3);
1634 if (INFO) printf(
"[.stl]: ------------\n" );
1636 in.open( _filename );
1641 while( getline( in, line ) ) {
1642 if( line.length() > 1 && line.at(0) ==
'\t' )
1643 line = line.substr( 1 );
1644 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1646 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1648 if( tokens.empty() )
continue;
1650 if( tokens[0] ==
"solid" ) {
1652 }
else if( tokens[0] ==
"facet" ) {
1654 normalVector = glm::vec3( strtof( tokens[2].c_str(),
nullptr ),
1655 strtof( tokens[3].c_str(),
nullptr ),
1656 strtof( tokens[4].c_str(),
nullptr ) );
1657 }
else if( tokens[0] ==
"outer" && tokens[1] ==
"loop" ) {
1659 }
else if( tokens[0] ==
"vertex" ) {
1660 _vertices[ _uniqueIndex ] = glm::vec3(strtof( tokens[1].c_str(),
nullptr ),
1661 strtof( tokens[2].c_str(),
nullptr ),
1662 strtof( tokens[3].c_str(),
nullptr ) );
1663 _normals[ _uniqueIndex ] = normalVector;
1664 _indices[ _numIndices++ ] = _uniqueIndex++;
1665 }
else if( tokens[0] ==
"endloop" ) {
1667 }
else if( tokens[0] ==
"endfacet" ) {
1669 }
else if( tokens[0] ==
"endsolid" ) {
1678 if( progressCounter % 5000 == 0 ) {
1680 switch( progressCounter ) {
1681 case 5000: printf(
"[.stl]: parsing %s...\\", _filename.c_str());
break;
1682 case 10000: printf(
"[.stl]: parsing %s...|", _filename.c_str());
break;
1683 case 15000: printf(
"[.stl]: parsing %s.../", _filename.c_str());
break;
1684 case 20000: printf(
"[.stl]: parsing %s...-", _filename.c_str());
break;
1689 if( progressCounter == 20000 )
1690 progressCounter = 0;
1698 double seconds = difftime( end, start );
1702 printf(
"[.stl]: parsing %s...done!\n[.stl]: Time to complete: %.3fs\n", _filename.c_str(), seconds);
1703 printf(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1711 sAUTO_GEN_NORMALS =
true;
1716 sAUTO_GEN_NORMALS =
false;
1719inline void CSCI441::ModelLoader::_allocateAttributeArrays(
const GLuint numVertices,
const GLuint numIndices) {
1720 _vertices =
new glm::vec3[numVertices];
1721 _normals =
new glm::vec3[numVertices];
1722 _texCoords =
new glm::vec2[numVertices];
1723 _indices =
new GLuint[numIndices];
1726inline void CSCI441::ModelLoader::_bufferData() {
1727 glBindVertexArray( _vaod );
1729 glBindBuffer( GL_ARRAY_BUFFER, _vbods[0] );
1730 glBufferData( GL_ARRAY_BUFFER,
static_cast<GLsizeiptr
>((
sizeof(glm::vec3)*2 +
sizeof(glm::vec2)) * _uniqueIndex),
nullptr, GL_STATIC_DRAW );
1731 glBufferSubData( GL_ARRAY_BUFFER, 0,
static_cast<GLsizeiptr
>(
sizeof(glm::vec3) * _uniqueIndex), _vertices );
1732 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex),
static_cast<GLsizeiptr
>(
sizeof(glm::vec3) * _uniqueIndex), _normals );
1733 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex * 2),
static_cast<GLsizeiptr
>(
sizeof(glm::vec2) * _uniqueIndex), _texCoords );
1735 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, _vbods[1] );
1736 glBufferData( GL_ELEMENT_ARRAY_BUFFER,
static_cast<GLsizeiptr
>(
sizeof(GLuint) * _numIndices), _indices, GL_STATIC_DRAW );
1745inline std::vector<std::string> CSCI441::ModelLoader::_tokenizeString(std::string input,
const std::string& delimiters) {
1749 std::vector<std::string> retVec = std::vector<std::string>();
1750 size_t oldR = 0, r = 0;
1753 std::string strippedInput;
1754 GLint lowerValidIndex = 0, upperValidIndex = (GLint)input.size() - 1;
1755 while((GLuint)lowerValidIndex < input.size() && delimiters.find_first_of(input.at(lowerValidIndex), 0) != std::string::npos)
1758 while(upperValidIndex >= 0 && delimiters.find_first_of(input.at(upperValidIndex), 0) != std::string::npos)
1762 if((GLuint)lowerValidIndex >= input.size() || upperValidIndex < 0 || lowerValidIndex > upperValidIndex)
1766 strippedInput = input.substr(lowerValidIndex, upperValidIndex-lowerValidIndex+1);
1770 while((r = strippedInput.find_first_of(delimiters, oldR)) != std::string::npos)
1773 retVec.push_back(strippedInput.substr(oldR, r-oldR));
1777 retVec.push_back(strippedInput.substr(oldR, r-oldR));
1782inline void CSCI441::ModelLoader::_moveFromSrc(ModelLoader& src) {
1783 _hasVertexTexCoords = src._hasVertexTexCoords;
1784 src._hasVertexTexCoords =
false;
1786 _hasVertexNormals = src._hasVertexNormals;
1787 src._hasVertexNormals =
false;
1789 _vertices = src._vertices;
1790 src._vertices =
nullptr;
1792 _texCoords = src._texCoords;
1793 src._texCoords =
nullptr;
1795 _normals = src._normals;
1796 src._normals =
nullptr;
1798 _indices = src._indices;
1799 src._indices =
nullptr;
1801 _uniqueIndex = src._uniqueIndex;
1802 src._uniqueIndex = 0;
1804 _numIndices = src._numIndices;
1805 src._numIndices = 0;
1807 _vbods[0] = src._vbods[0];
1808 _vbods[1] = src._vbods[1];
1815 _filename = std::move(src._filename);
1818 _modelType = src._modelType;
1820 _materials = std::move(src._materials);
1821 _materialIndexStartStop = std::move(src._materialIndexStartStop);
1824inline void CSCI441::ModelLoader::_cleanupSelf() {
1826 _vertices =
nullptr;
1831 delete[] _texCoords;
1832 _texCoords =
nullptr;
1837 glDeleteBuffers( 2, _vbods );
1841 glDeleteVertexArrays( 1, &_vaod );
1844 _hasVertexTexCoords =
false;
1845 _hasVertexNormals =
false;
1850 for(
auto [name, material] : _materials ) {
1855 _materialIndexStartStop.clear();
1858inline unsigned char* CSCI441_INTERNAL::createTransparentTexture(
const unsigned char * imageData,
const unsigned char *imageMask,
int texWidth,
int texHeight,
int texChannels,
int maskChannels ) {
1860 auto *fullData =
new unsigned char[texWidth*texHeight*4];
1862 for(
int j = 0; j < texHeight; j++) {
1863 for(
int i = 0; i < texWidth; i++) {
1865 fullData[(j*texWidth+i)*4+0] = imageData[(j*texWidth+i)*texChannels+0];
1866 fullData[(j*texWidth+i)*4+1] = imageData[(j*texWidth+i)*texChannels+1];
1867 fullData[(j*texWidth+i)*4+2] = imageData[(j*texWidth+i)*texChannels+2];
1869 fullData[(j*texWidth+i)*4+0] = 1;
1870 fullData[(j*texWidth+i)*4+1] = 1;
1871 fullData[(j*texWidth+i)*4+2] = 1;
1875 fullData[(j*texWidth+i)*4+3] = imageMask[(j*texWidth+i)*maskChannels+0];
1877 fullData[(j*texWidth+i)*4+3] = 1;
1885inline void CSCI441_INTERNAL::flipImageY(
int texWidth,
int texHeight,
int textureChannels,
unsigned char *textureData ) {
1886 for(
int j = 0; j < texHeight / 2; j++ ) {
1887 for(
int i = 0; i < texWidth; i++ ) {
1888 for(
int k = 0; k < textureChannels; k++ ) {
1889 int top = (j*texWidth + i)*textureChannels + k;
1890 int bot = ((texHeight-j-1)*texWidth + i)*textureChannels + k;
1892 unsigned char t = textureData[top];
1893 textureData[top] = textureData[bot];
1894 textureData[bot] = t;
Loads object models from file and renders using VBOs/VAOs.
Definition: ModelLoader.hpp:50
ModelLoader(const ModelLoader &)=delete
do not allow models to be copied
static void enableAutoGenerateNormals()
Enable auto-generation of vertex normals.
Definition: ModelLoader.hpp:1710
void setAttributeLocations(GLint positionLocation, GLint normalLocation=-1, GLint texCoordLocation=-1) const
Enables VBO attribute array locations.
Definition: ModelLoader.hpp:285
ModelLoader & operator=(const ModelLoader &)=delete
do not allow models to be copied
GLuint getNumberOfIndices() const
Return the number of indices to draw the model. This value corresponds to the size of the Indices arr...
Definition: ModelLoader.hpp:348
GLfloat * getVertices() const
Return the vertex array that makes up the model mesh.
Definition: ModelLoader.hpp:345
bool loadModelFile(std::string filename, bool INFO=true, bool ERRORS=true)
Loads a model from the given file.
Definition: ModelLoader.hpp:257
GLfloat * getNormals() const
Return the normal array that corresponds to the model mesh.
Definition: ModelLoader.hpp:346
bool draw(GLuint shaderProgramHandle, GLint matDiffLocation=-1, GLint matSpecLocation=-1, GLint matShinLocation=-1, GLint matAmbLocation=-1, GLenum diffuseTexture=GL_TEXTURE0) const
Renders a model.
Definition: ModelLoader.hpp:300
GLuint * getIndices() const
Return the index array that dictates the order to draw the model mesh.
Definition: ModelLoader.hpp:349
GLfloat * getTexCoords() const
Return the texture coordinates array that corresponds to the model mesh.
Definition: ModelLoader.hpp:347
GLuint getNumberOfVertices() const
Return the number of vertices the model is made up of. This value corresponds to the size of the Vert...
Definition: ModelLoader.hpp:344
static void disableAutoGenerateNormals()
Disable auto-generation of vertex normals.
Definition: ModelLoader.hpp:1715
~ModelLoader()
Frees memory associated with model on both CPU and GPU.
Definition: ModelLoader.hpp:224
ModelLoader()
Creates an empty model.
Definition: ModelLoader.hpp:214
Internal material representation for *.mtl files.
CSCI441 Helper Functions for OpenGL.
Definition: ArcballCam.hpp:17