17#ifndef CSCI441_MODEL_LOADER_HPP
18#define CSCI441_MODEL_LOADER_HPP
23#ifdef CSCI441_USE_GLEW
31#ifndef CSCI441_TEXTURE_UTILS_HPP
32 #include <stb_image.h>
64 [[maybe_unused]]
explicit ModelLoader(
const char* filename );
96 bool loadModelFile( std::
string filename,
bool INFO = true,
bool ERRORS = true );
105 [[maybe_unused]]
void setAttributeLocations(GLint positionLocation, GLint normalLocation = -1, GLint texCoordLocation = -1, GLint tangentLocation = -1) const;
117 [[maybe_unused]]
bool draw( GLuint shaderProgramHandle,
118 GLint matDiffLocation = -1, GLint matSpecLocation = -1, GLint matShinLocation = -1, GLint matAmbLocation = -1,
119 GLenum diffuseTexture = GL_TEXTURE0 ) const;
125 [[maybe_unused]] [[nodiscard]] GLuint getNumberOfVertices() const;
131 [[maybe_unused]] [[nodiscard]] GLfloat* getVertices() const;
137 [[maybe_unused]] [[nodiscard]] GLfloat* getNormals() const;
143 [[maybe_unused]] [[nodiscard]] GLfloat* getTangents() const;
149 [[maybe_unused]] [[nodiscard]] GLfloat* getTexCoords() const;
154 [[maybe_unused]] [[nodiscard]] GLuint getNumberOfIndices() const;
160 [[maybe_unused]] [[nodiscard]] GLuint* getIndices() const;
169 [[maybe_unused]] static
void enableAutoGenerateNormals();
176 [[maybe_unused]] static
void disableAutoGenerateNormals();
184 [[maybe_unused]] static
void enableAutoGenerateTangents();
191 [[maybe_unused]] static
void disableAutoGenerateTangents();
195 bool _loadMTLFile( const
char *mtlFilename,
bool INFO,
bool ERRORS );
196 bool _loadOBJFile(
bool INFO,
bool ERRORS );
197 bool _loadOFFFile(
bool INFO,
bool ERRORS );
198 bool _loadPLYFile(
bool INFO,
bool ERRORS );
199 bool _loadSTLFile(
bool INFO,
bool ERRORS );
200 static std::vector<std::
string> _tokenizeString( const std::
string& input, const std::
string& delimiters );
201 void _allocateAttributeArrays(GLuint numVertices, GLuint numIndices);
202 void _bufferData() const;
204 std::
string _filename;
205 CSCI441_INTERNAL::MODEL_TYPE _modelType;
208 GLuint _vbods[2] = {0};
210 glm::vec3* _vertices;
212 glm::vec4* _tangents;
213 glm::vec2* _texCoords;
218 std::map< std::string, CSCI441_INTERNAL::ModelMaterial* > _materials;
219 std::map< std::string, std::vector< std::pair< GLuint, GLuint > > > _materialIndexStartStop;
221 bool _hasVertexTexCoords;
222 bool _hasVertexNormals;
227 static bool sAUTO_GEN_NORMALS;
228 static bool sAUTO_GEN_TANGENTS;
235namespace CSCI441_INTERNAL {
236 unsigned char* createTransparentTexture(
const unsigned char *imageData,
const unsigned char *imageMask,
int texWidth,
int texHeight,
int texChannels,
int maskChannels );
237 [[maybe_unused]]
void flipImageY(
int texWidth,
int texHeight,
int textureChannels,
unsigned char *textureData );
240inline bool CSCI441::ModelLoader::sAUTO_GEN_NORMALS =
false;
241inline bool CSCI441::ModelLoader::sAUTO_GEN_TANGENTS =
false;
244 _modelType(CSCI441_INTERNAL::MODEL_TYPE::UNKNOWN),
253 _hasVertexTexCoords(false),
254 _hasVertexNormals(false)
261 _modelType(CSCI441_INTERNAL::MODEL_TYPE::UNKNOWN),
270 _hasVertexTexCoords(false),
271 _hasVertexNormals(false)
282 _modelType(CSCI441_INTERNAL::MODEL_TYPE::UNKNOWN),
291 _hasVertexTexCoords(
false),
292 _hasVertexNormals(
false)
305inline void CSCI441::ModelLoader::_init() {
306 glGenVertexArrays( 1, &_vaod );
307 glGenBuffers( 2, _vbods );
312 _filename = std::move(filename);
313 if( _filename.find(
".obj") != std::string::npos ) {
314 result = _loadOBJFile( INFO, ERRORS );
315 _modelType = CSCI441_INTERNAL::MODEL_TYPE::OBJ;
317 else if( _filename.find(
".off") != std::string::npos ) {
318 result = _loadOFFFile( INFO, ERRORS );
319 _modelType = CSCI441_INTERNAL::MODEL_TYPE::OFF;
321 else if( _filename.find(
".ply") != std::string::npos ) {
322 result = _loadPLYFile( INFO, ERRORS );
323 _modelType = CSCI441_INTERNAL::MODEL_TYPE::PLY;
325 else if( _filename.find(
".stl") != std::string::npos ) {
326 result = _loadSTLFile( INFO, ERRORS );
327 _modelType = CSCI441_INTERNAL::MODEL_TYPE::STL;
331 if (ERRORS) fprintf( stderr,
"[ERROR]: Unsupported file format for file: %s\n", _filename.c_str() );
339 glBindVertexArray( _vaod );
340 glBindBuffer( GL_ARRAY_BUFFER, _vbods[0] );
342 glEnableVertexAttribArray( positionLocation );
343 glVertexAttribPointer( positionLocation, 3, GL_FLOAT, GL_FALSE, 0, (
void*)
nullptr );
345 glEnableVertexAttribArray( normalLocation );
346 glVertexAttribPointer( normalLocation, 3, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex) );
348 glEnableVertexAttribArray( texCoordLocation );
349 glVertexAttribPointer( texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex * 2) );
351 glEnableVertexAttribArray( tangentLocation );
352 glVertexAttribPointer( tangentLocation, 4, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex * 2 +
sizeof(glm::vec2) * _uniqueIndex) );
357 const GLint matDiffLocation,
const GLint matSpecLocation,
const GLint matShinLocation,
const GLint matAmbLocation,
358 const GLenum diffuseTexture )
const {
359 glBindVertexArray( _vaod );
362 if( _modelType == CSCI441_INTERNAL::MODEL_TYPE::OBJ ) {
363 for(
const auto & materialIter : _materialIndexStartStop) {
364 auto materialName = materialIter.first;
365 auto indexStartStop = materialIter.second;
367 CSCI441_INTERNAL::ModelMaterial* material =
nullptr;
368 if( _materials.find( materialName ) != _materials.end() )
369 material = _materials.find( materialName )->second;
371 for(
const auto &[start, end] : indexStartStop) {
372 const GLsizei length =
static_cast<GLsizei
>(end - start) + 1;
376 if( material !=
nullptr ) {
377 glProgramUniform4fv( shaderProgramHandle, matAmbLocation, 1, &material->ambient[0] );
378 glProgramUniform4fv( shaderProgramHandle, matDiffLocation, 1, &material->diffuse[0] );
379 glProgramUniform4fv( shaderProgramHandle, matSpecLocation, 1, &material->specular[0] );
380 glProgramUniform1f( shaderProgramHandle, matShinLocation, material->shininess );
382 if( material->map_Kd != -1 ) {
383 glActiveTexture( diffuseTexture );
384 glBindTexture( GL_TEXTURE_2D, material->map_Kd );
388 glDrawElements( GL_TRIANGLES, length, GL_UNSIGNED_INT, (
void*)(
sizeof(GLuint)*start) );
392 glDrawElements( GL_TRIANGLES,
static_cast<GLint
>(_numIndices), GL_UNSIGNED_INT, (
void*)
nullptr );
407inline bool CSCI441::ModelLoader::_loadOBJFile(
const bool INFO,
const bool ERRORS ) {
411 if( _filename.find(
'/') != std::string::npos ) {
412 path = _filename.substr( 0, _filename.find_last_of(
'/')+1 );
417 if ( INFO ) fprintf( stdout,
"[.obj]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=- \n", _filename.c_str() );
422 std::ifstream in( _filename );
423 if( !in.is_open() ) {
424 if (ERRORS) fprintf( stderr,
"[.obj]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
425 if ( INFO ) fprintf( stdout,
"[.obj]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n", _filename.c_str() );
429 GLuint numObjects = 0, numGroups = 0;
430 GLuint numVertices = 0, numTexCoords = 0, numNormals = 0;
431 GLuint numFaces = 0, numTriangles = 0;
432 glm::vec3 minDimension = {999999.f, 999999.f, 999999.f};
433 glm::vec3 maxDimension = { -999999.f, -999999.f, -999999.f };
436 std::map<std::string, GLuint> uniqueCounts;
439 int progressCounter = 0;
441 while( getline( in, line ) ) {
442 if( line.length() > 1 && line.at(0) ==
'\t' )
443 line = line.substr( 1 );
444 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
446 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
447 if( tokens.empty() )
continue;
450 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
452 }
else if( tokens[0] ==
"o" ) {
454 }
else if( tokens[0] ==
"g" ) {
456 }
else if( tokens[0] ==
"mtllib" ) {
457 std::string mtlFilename = path + tokens[1];
458 _loadMTLFile( mtlFilename.c_str(), INFO, ERRORS );
459 }
else if( tokens[0] ==
"v" ) {
462 glm::vec3 pos = { strtof( tokens[1].c_str(),
nullptr ),
463 strtof( tokens[2].c_str(),
nullptr ),
464 strtof( tokens[3].c_str(),
nullptr ) };
466 if( pos.x < minDimension.x ) minDimension.x = pos.x;
467 if( pos.x > maxDimension.x ) maxDimension.x = pos.x;
468 if( pos.y < minDimension.y ) minDimension.y = pos.y;
469 if( pos.y > maxDimension.y ) maxDimension.y = pos.y;
470 if( pos.z < minDimension.z ) minDimension.z = pos.z;
471 if( pos.z > maxDimension.z ) maxDimension.z = pos.z;
472 }
else if( tokens[0] ==
"vn" ) {
474 }
else if( tokens[0] ==
"vt" ) {
476 }
else if( tokens[0] ==
"f" ) {
479 std::vector<std::string> faceTokens = _tokenizeString(line,
" ");
481 for (GLuint i = 1; i < faceTokens.size(); i++) {
483 std::vector<std::string> groupTokens = _tokenizeString(faceTokens[i],
"/");
485 for (
char j: faceTokens[i]) {
486 if (j ==
'/') numSlashes++;
489 std::stringstream currentFaceTokenStream;
491 auto signedVertexIndex =
static_cast<GLint
>(strtol(groupTokens[0].c_str(),
nullptr, 10));
492 GLuint vertexIndex = signedVertexIndex;
493 if (signedVertexIndex < 0) vertexIndex = numVertices + signedVertexIndex + 1;
495 currentFaceTokenStream << vertexIndex;
498 if (groupTokens.size() == 2 && numSlashes == 1) {
499 _hasVertexTexCoords =
true;
501 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(groupTokens[1].c_str(),
nullptr, 10));
502 GLuint texCoordIndex = signedTexCoordIndex;
503 if (signedTexCoordIndex < 0) texCoordIndex = numTexCoords + signedTexCoordIndex + 1;
505 currentFaceTokenStream <<
"/" << texCoordIndex;
506 }
else if (groupTokens.size() == 2 && numSlashes == 2) {
507 _hasVertexNormals =
true;
509 auto signedNormalIndex =
static_cast<GLint
>(strtol(groupTokens[1].c_str(),
nullptr, 10));
510 GLuint normalIndex = signedNormalIndex;
511 if (signedNormalIndex < 0) normalIndex = numNormals + signedNormalIndex + 1;
513 currentFaceTokenStream <<
"//" << normalIndex;
514 }
else if (groupTokens.size() == 3) {
515 _hasVertexTexCoords =
true;
516 _hasVertexNormals =
true;
518 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(groupTokens[1].c_str(),
nullptr, 10));
519 GLuint texCoordIndex = signedTexCoordIndex;
520 if (signedTexCoordIndex < 0) texCoordIndex = numTexCoords + signedTexCoordIndex + 1;
522 auto signedNormalIndex =
static_cast<GLint
>(strtol(groupTokens[2].c_str(),
nullptr, 10));
523 GLuint normalIndex = signedNormalIndex;
524 if (signedNormalIndex < 0) normalIndex = numNormals + signedNormalIndex + 1;
526 currentFaceTokenStream <<
"/" << texCoordIndex <<
"/" << normalIndex;
527 }
else if (groupTokens.size() != 1) {
528 if (ERRORS) fprintf(stderr,
"[.obj]: [ERROR]: Malformed OBJ file, %s.\n", _filename.c_str());
532 std::string processedFaceToken = currentFaceTokenStream.str();
533 if (uniqueCounts.find(processedFaceToken) == uniqueCounts.end()) {
534 uniqueCounts.insert(std::pair<std::string, long int>(processedFaceToken, _uniqueIndex));
539 numTriangles += (faceTokens.size() - 1 - 3 + 1);
542 }
else if( tokens[0] ==
"usemtl" ) {
545 if (INFO) printf(
"[.obj]: ignoring line: %s\n", line.c_str() );
550 if( progressCounter % 5000 == 0 ) {
552 switch( progressCounter ) {
553 case 5000: printf(
"[.obj]: scanning %s...\\", _filename.c_str());
break;
554 case 10000: printf(
"[.obj]: scanning %s...|", _filename.c_str());
break;
555 case 15000: printf(
"[.obj]: scanning %s.../", _filename.c_str());
break;
556 case 20000: printf(
"[.obj]: scanning %s...-", _filename.c_str());
break;
561 if( progressCounter == 20000 )
568 printf(
"\33[2K\r" );
569 printf(
"[.obj]: scanning %s...done!\n", _filename.c_str() );
570 printf(
"[.obj]: ------------\n" );
571 printf(
"[.obj]: Model Stats:\n" );
572 printf(
"[.obj]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, numNormals, numTexCoords );
573 printf(
"[.obj]: Unique Verts:\t%u\n", _uniqueIndex );
574 printf(
"[.obj]: Faces: \t%u\tTriangles:\t%u\n", numFaces, numTriangles );
575 printf(
"[.obj]: Objects: \t%u\tGroups: \t%u\n", numObjects, numGroups );
577 glm::vec3 sizeDimensions = maxDimension - minDimension;
578 printf(
"[.obj]: Dimensions:\t(%f, %f, %f)\n", sizeDimensions.x, sizeDimensions.y, sizeDimensions.z );
581 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
582 if (INFO && !_hasVertexNormals)
583 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" );
584 if (INFO && sAUTO_GEN_TANGENTS) fprintf( stdout,
"[.obj]: Vertex tangents will be autogenerated\n" );
585 _allocateAttributeArrays(_uniqueIndex, numTriangles*3);
587 if (INFO) printf(
"[.obj]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
588 if (INFO && sAUTO_GEN_TANGENTS) fprintf( stdout,
"[.obj]: Vertex tangents will be autogenerated\n" );
589 _allocateAttributeArrays(numTriangles * 3, numTriangles*3);
592 auto objVertices =
new glm::vec3[numVertices];
593 auto objNormals =
new glm::vec3[numNormals];
594 auto objTexCoords =
new glm::vec2[numTexCoords];
596 std::vector<glm::vec3> verticesTemps;
597 std::vector<glm::vec2> texCoordsTemp;
599 printf(
"[.obj]: ------------\n" );
601 uniqueCounts.clear();
605 in.open( _filename );
607 GLuint verticesSeen = 0, texCoordsSeen = 0, normalsSeen = 0, indicesSeen = 0;
608 GLuint uniqueNumVertices = 0;
610 std::string currentMaterial =
"default";
611 _materialIndexStartStop.insert( std::pair< std::string, std::vector< std::pair< GLuint, GLuint > > >( currentMaterial, std::vector< std::pair< GLuint, GLuint > >(1) ) );
612 _materialIndexStartStop.find( currentMaterial )->second.back().first = indicesSeen;
614 while( getline( in, line ) ) {
615 if( line.length() > 1 && line.at(0) ==
'\t' )
616 line = line.substr( 1 );
617 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
619 auto tokens = _tokenizeString( line,
" \t" );
620 if( tokens.empty() )
continue;
623 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0
626 || tokens[0] ==
"mtllib"
630 }
else if( tokens[0] ==
"usemtl" ) {
631 if( currentMaterial ==
"default" && indicesSeen == 0 ) {
632 _materialIndexStartStop.clear();
634 _materialIndexStartStop.find( currentMaterial )->second.back().second = indicesSeen - 1;
636 currentMaterial = tokens[1];
637 if( _materialIndexStartStop.find( currentMaterial ) == _materialIndexStartStop.end() ) {
638 _materialIndexStartStop.insert( std::pair< std::string, std::vector< std::pair< GLuint, GLuint > > >( currentMaterial, std::vector< std::pair< GLuint, GLuint > >(1) ) );
639 _materialIndexStartStop.find( currentMaterial )->second.back().first = indicesSeen;
641 _materialIndexStartStop.find( currentMaterial )->second.emplace_back( indicesSeen, -1 );
643 }
else if( tokens[0] ==
"v" ) {
644 objVertices[verticesSeen++] = glm::vec3(strtof(tokens[1].c_str(),
nullptr ),
645 strtof( tokens[2].c_str(),
nullptr ),
646 strtof( tokens[3].c_str(),
nullptr ) );
647 }
else if( tokens[0] ==
"vn" ) {
648 objNormals[normalsSeen++] = glm::vec3(strtof(tokens[1].c_str(),
nullptr ),
649 strtof( tokens[2].c_str(),
nullptr ),
650 strtof( tokens[3].c_str(),
nullptr ));
651 }
else if( tokens[0] ==
"vt" ) {
652 objTexCoords[texCoordsSeen++] = glm::vec2(strtof(tokens[1].c_str(),
nullptr ),
653 strtof( tokens[2].c_str(),
nullptr ));
654 }
else if( tokens[0] ==
"f" ) {
655 std::vector<std::string> processedFaceTokens;
657 bool faceHasVertexNormals =
false;
658 bool faceHasTextureCoordinates =
false;
660 for(GLuint i = 1; i < tokens.size(); i++) {
662 auto vertexAttributeTokens = _tokenizeString(tokens[i],
"/");
663 int numAttributeSlashes = 0;
664 for(
char j : tokens[i]) {
665 if(j ==
'/') numAttributeSlashes++;
668 std::stringstream currentFaceTokenStream;
670 auto signedVertexIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[0].c_str(),
nullptr, 10));
671 GLuint vertexIndex = signedVertexIndex;
672 if(signedVertexIndex < 0) vertexIndex = verticesSeen + signedVertexIndex + 1;
673 currentFaceTokenStream << vertexIndex;
675 GLuint texCoordIndex = 0, normalIndex = 0;
678 if(vertexAttributeTokens.size() == 2 && numAttributeSlashes == 1) {
680 _hasVertexTexCoords =
true;
681 faceHasTextureCoordinates =
true;
683 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10));
684 texCoordIndex = signedTexCoordIndex;
685 if(signedTexCoordIndex < 0) texCoordIndex = texCoordsSeen + signedTexCoordIndex + 1;
686 currentFaceTokenStream <<
"/" << texCoordIndex;
687 }
else if(vertexAttributeTokens.size() == 2 && numAttributeSlashes == 2) {
689 _hasVertexNormals =
true;
690 faceHasVertexNormals =
true;
692 auto signedNormalIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10));
693 normalIndex = signedNormalIndex;
694 if(signedNormalIndex < 0) normalIndex = normalsSeen + signedNormalIndex + 1;
695 currentFaceTokenStream <<
"//" << normalIndex;
696 }
else if(vertexAttributeTokens.size() == 3) {
698 _hasVertexTexCoords =
true;
699 faceHasTextureCoordinates =
true;
700 _hasVertexNormals =
true;
701 faceHasVertexNormals =
true;
703 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10));
704 texCoordIndex = signedTexCoordIndex;
705 if(signedTexCoordIndex < 0) texCoordIndex = texCoordsSeen + signedTexCoordIndex + 1;
707 auto signedNormalIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[2].c_str(),
nullptr, 10));
708 normalIndex = signedNormalIndex;
709 if(signedNormalIndex < 0) normalIndex = normalsSeen + signedNormalIndex + 1;
711 currentFaceTokenStream <<
"/" << texCoordIndex <<
"/" << normalIndex;
712 }
else if(vertexAttributeTokens.size() != 1) {
713 if (ERRORS) fprintf(stderr,
"[.obj]: [ERROR]: Malformed OBJ file, %s.\n", _filename.c_str());
717 auto processedFaceToken = currentFaceTokenStream.str();
718 processedFaceTokens.push_back(processedFaceToken);
721 if( uniqueCounts.find( processedFaceToken ) == uniqueCounts.end() ) {
723 uniqueCounts.insert( std::pair<std::string,GLuint>(processedFaceToken, uniqueNumVertices) );
725 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
726 _vertices[ _uniqueIndex ] = objVertices[ vertexIndex - 1 ];
727 if(faceHasTextureCoordinates && texCoordIndex != 0) { _texCoords[ _uniqueIndex ] = objTexCoords[ texCoordIndex - 1 ]; }
728 if(faceHasVertexNormals && normalIndex != 0) _normals[ _uniqueIndex ] = objNormals[ normalIndex - 1 ];
731 verticesTemps.push_back( objVertices[ vertexIndex - 1 ] );
732 if(faceHasTextureCoordinates && texCoordIndex != 0) { texCoordsTemp.push_back( objTexCoords[ texCoordIndex - 1 ] ); }
734 if( (vertexAttributeTokens.size() == 2 && numAttributeSlashes == 2)
735 || (vertexAttributeTokens.size() == 3) ) {
737 if (ERRORS) fprintf( stderr,
"[.obj]: [ERROR]: no vertex normals were specified, should not be trying to access values\n" );
744 for(GLuint i = 1; i < processedFaceTokens.size()-1; i++) {
745 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
746 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[0] )->second;
747 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[i] )->second;
748 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[i+1] )->second;
750 if (sAUTO_GEN_TANGENTS) {
751 glm::vec3 a = _vertices[ _indices[ indicesSeen - 3 ] ];
752 glm::vec3 b = _vertices[ _indices[ indicesSeen - 2 ] ];
753 glm::vec3 c = _vertices[ _indices[ indicesSeen - 1 ] ];
755 glm::vec3 ab = b - a;
756 glm::vec3 bc = c - b;
757 glm::vec3 ca = a - c;
759 _tangents[ _indices[ indicesSeen - 3 ] ] = glm::vec4(ab, 1.0f);
760 _tangents[ _indices[ indicesSeen - 2 ] ] = glm::vec4(bc, 1.0f);
761 _tangents[ _indices[ indicesSeen - 1 ] ] = glm::vec4(ca, 1.0f);
766 GLuint aI = uniqueCounts.find( processedFaceTokens[0] )->second;
767 GLuint bI = uniqueCounts.find( processedFaceTokens[i] )->second;
768 GLuint cI = uniqueCounts.find( processedFaceTokens[i+1] )->second;
770 glm::vec3 a = verticesTemps[aI];
771 glm::vec3 b = verticesTemps[bI];
772 glm::vec3 c = verticesTemps[cI];
774 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
775 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
776 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
778 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
779 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
780 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
782 _vertices[ _uniqueIndex ] = a;
783 _normals[ _uniqueIndex ] = aN;
784 if (sAUTO_GEN_TANGENTS) _tangents[ _uniqueIndex ] = glm::vec4(ab, 1.0f);
785 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ aI ]; }
786 _indices[ _numIndices++ ] = _uniqueIndex++;
790 _vertices[ _uniqueIndex ] = b;
791 _normals[ _uniqueIndex ] = bN;
792 if (sAUTO_GEN_TANGENTS) _tangents[ _uniqueIndex ] = glm::vec4(bc, 1.0f);
793 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ bI ]; }
794 _indices[ _numIndices++ ] = _uniqueIndex++;
798 _vertices[ _uniqueIndex ] = c;
799 _normals[ _uniqueIndex ] = cN;
800 if (sAUTO_GEN_TANGENTS) _tangents[ _uniqueIndex ] = glm::vec4(ca, 1.0f);
801 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ cI ]; }
802 _indices[ _numIndices++ ] = _uniqueIndex++;
811 if( progressCounter % 5000 == 0 ) {
813 switch( progressCounter ) {
814 case 5000: printf(
"[.obj]: parsing %s...\\", _filename.c_str());
break;
815 case 10000: printf(
"[.obj]: parsing %s...|", _filename.c_str());
break;
816 case 15000: printf(
"[.obj]: parsing %s.../", _filename.c_str());
break;
817 case 20000: printf(
"[.obj]: parsing %s...-", _filename.c_str());
break;
822 if( progressCounter == 20000 )
830 printf(
"\33[2K\r" );
831 printf(
"[.obj]: parsing %s...done!\n", _filename.c_str() );
834 _materialIndexStartStop.find( currentMaterial )->second.back().second = indicesSeen - 1;
839 double seconds = difftime( end, start );
842 printf(
"[.obj]: Completed in %.3fs\n", seconds );
843 printf(
"[.obj]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n\n", _filename.c_str() );
849inline bool CSCI441::ModelLoader::_loadMTLFile(
const char* mtlFilename,
const bool INFO,
const bool ERRORS ) {
852 if (INFO) printf(
"[.mtl]: -*-*-*-*-*-*-*- BEGIN %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
856 if( _filename.find(
'/') != std::string::npos ) {
857 path = _filename.substr( 0, _filename.find_last_of(
'/')+1 );
863 in.open( mtlFilename );
864 if( !in.is_open() ) {
865 in.open( mtlFilename );
866 if( !in.is_open() ) {
867 if (ERRORS) fprintf( stderr,
"[.mtl]: [ERROR]: could not open material file: %s\n", mtlFilename );
868 if ( INFO ) printf(
"[.mtl]: -*-*-*-*-*-*-*- END %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
873 CSCI441_INTERNAL::ModelMaterial* currentMaterial =
nullptr;
874 std::string materialName;
876 unsigned char *textureData =
nullptr;
877 unsigned char *maskData =
nullptr;
878 unsigned char *fullData;
879 int texWidth, texHeight, textureChannels = 1, maskChannels = 1;
880 GLuint textureHandle = 0;
882 std::map< std::string, GLuint > imageHandles;
884 int numMaterials = 0;
886 while( getline( in, line ) ) {
887 if( line.length() > 1 && line.at(0) ==
'\t' )
888 line = line.substr( 1 );
889 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
891 std::vector< std::string > tokens = _tokenizeString( line,
" /" );
892 if( tokens.empty() )
continue;
895 if( tokens[0] ==
"#" ) {
897 }
else if( tokens[0] ==
"newmtl" ) {
898 if (INFO) printf(
"[.mtl]: Parsing material %s properties\n", tokens[1].c_str() );
899 currentMaterial =
new CSCI441_INTERNAL::ModelMaterial();
900 materialName = tokens[1];
901 _materials.insert( std::pair<std::string, CSCI441_INTERNAL::ModelMaterial*>( materialName, currentMaterial ) );
904 textureData =
nullptr;
910 }
else if( tokens[0] ==
"Ka" ) {
911 currentMaterial->ambient[0] = strtof( tokens[1].c_str(),
nullptr );
912 currentMaterial->ambient[1] = strtof( tokens[2].c_str(),
nullptr );
913 currentMaterial->ambient[2] = strtof( tokens[3].c_str(),
nullptr );
914 }
else if( tokens[0] ==
"Kd" ) {
915 currentMaterial->diffuse[0] = strtof( tokens[1].c_str(),
nullptr );
916 currentMaterial->diffuse[1] = strtof( tokens[2].c_str(),
nullptr );
917 currentMaterial->diffuse[2] = strtof( tokens[3].c_str(),
nullptr );
918 }
else if( tokens[0] ==
"Ks" ) {
919 currentMaterial->specular[0] = strtof( tokens[1].c_str(),
nullptr );
920 currentMaterial->specular[1] = strtof( tokens[2].c_str(),
nullptr );
921 currentMaterial->specular[2] = strtof( tokens[3].c_str(),
nullptr );
922 }
else if( tokens[0] ==
"Ke" ) {
923 currentMaterial->emissive[0] = strtof( tokens[1].c_str(),
nullptr );
924 currentMaterial->emissive[1] = strtof( tokens[2].c_str(),
nullptr );
925 currentMaterial->emissive[2] = strtof( tokens[3].c_str(),
nullptr );
926 }
else if( tokens[0] ==
"Ns" ) {
927 currentMaterial->shininess = strtof( tokens[1].c_str(),
nullptr );
928 }
else if( tokens[0] ==
"Tr"
929 || tokens[0] ==
"d" ) {
930 currentMaterial->ambient[3] = strtof( tokens[1].c_str(),
nullptr );
931 currentMaterial->diffuse[3] = strtof( tokens[1].c_str(),
nullptr );
932 currentMaterial->specular[3] = strtof( tokens[1].c_str(),
nullptr );
933 }
else if( tokens[0] ==
"illum" ) {
935 }
else if( tokens[0] ==
"map_Kd" ) {
936 if( imageHandles.find( tokens[1] ) != imageHandles.end() ) {
938 currentMaterial->map_Kd = imageHandles.find( tokens[1] )->second;
940 stbi_set_flip_vertically_on_load(
true);
941 textureData = stbi_load( tokens[1].c_str(), &texWidth, &texHeight, &textureChannels, 0 );
943 std::string folderName = path + tokens[1];
944 textureData = stbi_load( folderName.c_str(), &texWidth, &texHeight, &textureChannels, 0 );
948 if (ERRORS) fprintf( stderr,
"[.mtl]: [ERROR]: File Not Found: %s\n", tokens[1].c_str() );
950 if (INFO) printf(
"[.mtl]: TextureMap:\t%s\tSize: %dx%d\tColors: %d\n", tokens[1].c_str(), texWidth, texHeight, textureChannels );
952 if( maskData ==
nullptr ) {
953 if( textureHandle == 0 ) {
954 glGenTextures( 1, &textureHandle );
955 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
958 glBindTexture( GL_TEXTURE_2D, textureHandle );
960 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
961 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
963 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
964 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
966 GLint colorSpace = GL_RGB;
967 if( textureChannels == 4 )
968 colorSpace = GL_RGBA;
969 glTexImage2D( GL_TEXTURE_2D, 0, colorSpace, texWidth, texHeight, 0, colorSpace, GL_UNSIGNED_BYTE, textureData );
971 currentMaterial->map_Kd = textureHandle;
973 fullData = CSCI441_INTERNAL::createTransparentTexture( textureData, maskData, texWidth, texHeight, textureChannels, maskChannels );
975 if( textureHandle == 0 ) {
976 glGenTextures( 1, &textureHandle );
977 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
980 glBindTexture( GL_TEXTURE_2D, textureHandle );
982 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
983 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
985 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
986 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
988 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullData );
992 currentMaterial->map_Kd = textureHandle;
996 }
else if( tokens[0] ==
"map_d" ) {
997 if( imageHandles.find( tokens[1] ) != imageHandles.end() ) {
999 currentMaterial->map_d = imageHandles.find( tokens[1] )->second;
1001 stbi_set_flip_vertically_on_load(
true);
1002 maskData = stbi_load( tokens[1].c_str(), &texWidth, &texHeight, &textureChannels, 0 );
1003 if( !textureData ) {
1004 std::string folderName = path + tokens[1];
1005 maskData = stbi_load( folderName.c_str(), &texWidth, &texHeight, &textureChannels, 0 );
1009 if (ERRORS) fprintf( stderr,
"[.mtl]: [ERROR]: File Not Found: %s\n", tokens[1].c_str() );
1011 if (INFO) printf(
"[.mtl]: AlphaMap: \t%s\tSize: %dx%d\tColors: %d\n", tokens[1].c_str(), texWidth, texHeight, maskChannels );
1013 if( textureData !=
nullptr ) {
1014 fullData = CSCI441_INTERNAL::createTransparentTexture( textureData, maskData, texWidth, texHeight, textureChannels, maskChannels );
1016 if( textureHandle == 0 ) {
1017 glGenTextures( 1, &textureHandle );
1018 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
1021 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1022 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1024 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1025 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1027 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullData );
1031 currentMaterial->map_Kd = textureHandle;
1035 }
else if( tokens[0] ==
"map_Ka" ) {
1037 }
else if( tokens[0] ==
"map_Ks" ) {
1039 }
else if( tokens[0] ==
"map_Ns" ) {
1041 }
else if( tokens[0] ==
"Ni" ) {
1043 }
else if( tokens[0] ==
"Tf" ) {
1045 }
else if( tokens[0] ==
"bump"
1046 || tokens[0] ==
"map_bump" ) {
1049 if (INFO) printf(
"[.mtl]: ignoring line: %s\n", line.c_str() );
1056 printf(
"[.mtl]: Materials:\t%d\n", numMaterials );
1057 printf(
"[.mtl]: -*-*-*-*-*-*-*- END %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
1063inline bool CSCI441::ModelLoader::_loadOFFFile(
const bool INFO,
const bool ERRORS ) {
1066 if (INFO ) printf(
"[.off]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1071 std::ifstream in( _filename );
1072 if( !in.is_open() ) {
1073 if (ERRORS) fprintf( stderr,
"[.off]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
1074 if ( INFO ) printf(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1078 GLuint numVertices = 0, numFaces = 0, numTriangles = 0;
1079 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1082 enum OFF_FILE_STATE { HEADER, VERTICES, FACES, DONE };
1084 OFF_FILE_STATE fileState = HEADER;
1086 GLuint vSeen = 0, fSeen = 0;
1088 while( getline( in, line ) ) {
1089 if( line.length() > 1 && line.at(0) ==
'\t' )
1090 line = line.substr( 1 );
1091 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1093 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1094 if( tokens.empty() )
continue;
1097 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
1098 }
else if( fileState == HEADER ) {
1099 if( tokens[0] ==
"OFF" ) {
1101 if( tokens.size() != 3 ) {
1102 if (ERRORS) fprintf( stderr,
"[.off]: [ERROR]: Malformed OFF file. # vertices, faces, edges not properly specified\n" );
1103 if ( INFO ) printf(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1108 numVertices =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1109 numFaces =
static_cast<GLuint
>(strtol(tokens[1].c_str(),
nullptr, 10));
1114 fileState = VERTICES;
1116 }
else if( fileState == VERTICES ) {
1118 GLfloat x = strtof( tokens[0].c_str(),
nullptr ),
1119 y = strtof( tokens[1].c_str(),
nullptr ),
1120 z = strtof( tokens[2].c_str(),
nullptr );
1122 if( x < minX ) minX = x;
1123 if( x > maxX ) maxX = x;
1124 if( y < minY ) minY = y;
1125 if( y > maxY ) maxY = y;
1126 if( z < minZ ) minZ = z;
1127 if( z > maxZ ) maxZ = z;
1130 if( vSeen == numVertices )
1132 }
else if( fileState == FACES ) {
1133 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1135 numTriangles += numberOfVerticesInFace - 3 + 1;
1137 if( fSeen == numFaces )
1140 if (INFO) printf(
"[.off]: unknown file state: %d\n", fileState );
1146 printf(
"\33[2K\r" );
1147 printf(
"[.off]: scanning %s...done!\n", _filename.c_str() );
1148 printf(
"[.off]: ------------\n" );
1149 printf(
"[.off]: Model Stats:\n" );
1150 printf(
"[.off]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, 0, 0 );
1151 printf(
"[.off]: Faces: \t%u\tTriangles: \t%u\n", numFaces, numTriangles );
1152 printf(
"[.off]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1155 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1156 if (INFO && !_hasVertexNormals)
1157 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" );
1158 _allocateAttributeArrays(numVertices, numTriangles*3);
1160 if (INFO) printf(
"[.off]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
1161 _allocateAttributeArrays(numTriangles*3, numTriangles*3);
1164 std::vector<glm::vec3> verticesTemp;
1166 if (INFO) printf(
"[.off]: ------------\n" );
1168 in.open( _filename );
1176 int progressCounter = 0;
1178 while( getline( in, line ) ) {
1179 if( line.length() > 1 && line.at(0) ==
'\t' )
1180 line = line.substr( 1 );
1181 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1183 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1184 if( tokens.empty() )
continue;
1187 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
1189 }
else if( fileState == HEADER ) {
1190 if( tokens[0] ==
"OFF" ) {
1194 fileState = VERTICES;
1196 }
else if( fileState == VERTICES ) {
1198 glm::vec3 pos(strtof( tokens[0].c_str(),
nullptr ),
1199 strtof( tokens[1].c_str(),
nullptr ),
1200 strtof( tokens[2].c_str(),
nullptr ) );
1203 if( tokens.size() == 6 || tokens.size() == 7 ) {
1211 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1212 _vertices[ _uniqueIndex ] = pos;
1215 verticesTemp.push_back(pos);
1219 if( _uniqueIndex == numVertices || vSeen == numVertices )
1221 }
else if( fileState == FACES ) {
1222 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1225 for(GLuint i = 2; i <= numberOfVerticesInFace - 1; i++) {
1226 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1227 auto fanRoot = strtol( tokens[1].c_str(),
nullptr, 10 );
1228 auto fanA = strtol( tokens[i].c_str(),
nullptr, 10 );
1229 auto fanB = strtol( tokens[i+1].c_str(),
nullptr, 10 );
1231 if( fanRoot < 0 ) fanRoot = numVertices + fanRoot + 1;
1232 if( fanA < 0 ) fanA = numVertices + fanA + 1;
1233 if( fanB < 0 ) fanB = numVertices + fanB + 1;
1236 _indices[ _numIndices++ ] = fanRoot;
1237 _indices[ _numIndices++ ] = fanA;
1238 _indices[ _numIndices++ ] = fanB;
1240 auto aI = strtol( tokens[1].c_str(),
nullptr, 10 );
1241 auto bI = strtol( tokens[i].c_str(),
nullptr, 10 );
1242 auto cI = strtol( tokens[i+1].c_str(),
nullptr, 10 );
1244 if( aI < 0 ) aI = numVertices + aI + 1;
1245 if( bI < 0 ) bI = numVertices + bI + 1;
1246 if( cI < 0 ) cI = numVertices + cI + 1;
1248 glm::vec3 a = verticesTemp[aI];
1249 glm::vec3 b = verticesTemp[bI];
1250 glm::vec3 c = verticesTemp[cI];
1252 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
1253 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
1254 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
1256 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
1257 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
1258 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
1260 _vertices[ _uniqueIndex ] = a;
1261 _normals[ _uniqueIndex ] = aN;
1262 _indices[ _numIndices++ ] = _uniqueIndex++;
1264 _vertices[ _uniqueIndex ] = b;
1265 _normals[ _uniqueIndex ] = bN;
1266 _indices[ _numIndices++ ] = _uniqueIndex++;
1268 _vertices[ _uniqueIndex ] = c;
1269 _normals[ _uniqueIndex ] = cN;
1270 _indices[ _numIndices++ ] = _uniqueIndex++;
1295 if( progressCounter % 5000 == 0 ) {
1297 switch( progressCounter ) {
1298 case 5000: printf(
"[.off]: parsing %s...\\", _filename.c_str());
break;
1299 case 10000: printf(
"[.off]: parsing %s...|", _filename.c_str());
break;
1300 case 15000: printf(
"[.off]: parsing %s.../", _filename.c_str());
break;
1301 case 20000: printf(
"[.off]: parsing %s...-", _filename.c_str());
break;
1306 if( progressCounter == 20000 )
1307 progressCounter = 0;
1315 double seconds = difftime( end, start );
1318 printf(
"\33[2K\r" );
1319 printf(
"[.off]: parsing %s...done! (Time: %.1fs)\n", _filename.c_str(), seconds );
1320 printf(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1327inline bool CSCI441::ModelLoader::_loadPLYFile(
const bool INFO,
const bool ERRORS ) {
1330 if (INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1335 std::ifstream in( _filename );
1336 if( !in.is_open() ) {
1337 if (ERRORS) fprintf( stderr,
"[.ply]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
1338 if ( INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1342 GLuint numVertices = 0, numFaces = 0, numTriangles = 0, numMaterials = 0;
1343 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1346 enum PLY_FILE_STATE { HEADER, VERTICES, FACES, MATERIALS };
1347 enum PLY_ELEMENT_TYPE { NONE, VERTEX, FACE, MATERIAL };
1349 PLY_FILE_STATE fileState = HEADER;
1350 PLY_ELEMENT_TYPE elemType = NONE;
1352 GLuint progressCounter = 0;
1355 while( getline( in, line ) ) {
1356 if( line.length() > 1 && line.at(0) ==
'\t' )
1357 line = line.substr( 1 );
1358 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1360 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1362 if( tokens.empty() )
continue;
1365 if( tokens[0] ==
"comment" ) {
1366 }
else if( fileState == HEADER ) {
1367 if( tokens[0] ==
"ply" ) {
1368 }
else if( tokens[0] ==
"format" ) {
1369 if( tokens[1] !=
"ascii" ) {
1370 if (ERRORS) fprintf( stderr,
"[.ply]: [ERROR]: File \"%s\" not ASCII format\n", _filename.c_str() );
1371 if ( INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1375 }
else if( tokens[0] ==
"element" ) {
1376 if( tokens[1] ==
"vertex" ) {
1377 numVertices =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1379 }
else if( tokens[1] ==
"face" ) {
1380 numFaces =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1382 }
else if( tokens[1] ==
"edge" ) {
1384 }
else if( tokens[1] ==
"material" ) {
1385 numMaterials =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1386 elemType = MATERIAL;
1390 }
else if( tokens[0] ==
"property" ) {
1391 if( elemType == VERTEX ) {
1393 }
else if( elemType == FACE ) {
1395 }
else if( elemType == MATERIAL ) {
1398 }
else if( tokens[0] ==
"end_header" ) {
1399 fileState = VERTICES;
1401 }
else if( fileState == VERTICES ) {
1403 auto x = (GLfloat) strtof( tokens[0].c_str(), nullptr ),
1404 y = (GLfloat) strtof( tokens[1].c_str(),
nullptr ),
1405 z = (GLfloat) strtof( tokens[2].c_str(), nullptr );
1407 if( x < minX ) minX = x;
1408 if( x > maxX ) maxX = x;
1409 if( y < minY ) minY = y;
1410 if( y > maxY ) maxY = y;
1411 if( z < minZ ) minZ = z;
1412 if( z > maxZ ) maxZ = z;
1416 if( vSeen == numVertices )
1418 }
else if( fileState == FACES ) {
1419 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1420 numTriangles += numberOfVerticesInFace - 3 + 1;
1422 if (INFO) printf(
"[.ply]: unknown file state: %d\n", fileState );
1427 if( progressCounter % 5000 == 0 ) {
1429 switch( progressCounter ) {
1430 case 5000: printf(
"[.ply]: scanning %s...\\", _filename.c_str());
break;
1431 case 10000: printf(
"[.ply]: scanning %s...|", _filename.c_str());
break;
1432 case 15000: printf(
"[.ply]: scanning %s.../", _filename.c_str());
break;
1433 case 20000: printf(
"[.ply]: scanning %s...-", _filename.c_str());
break;
1438 if( progressCounter == 20000 )
1439 progressCounter = 0;
1445 printf(
"\33[2K\r" );
1446 printf(
"[.ply]: scanning %s...done!\n", _filename.c_str() );
1447 printf(
"[.ply]: ------------\n" );
1448 printf(
"[.ply]: Model Stats:\n" );
1449 printf(
"[.ply]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, 0, 0 );
1450 printf(
"[.ply]: Faces: \t%u\tTriangles: \t%u\n", numFaces, numTriangles );
1451 printf(
"[.ply]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1454 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1455 if (INFO && !_hasVertexNormals)
1456 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" );
1457 _allocateAttributeArrays(numVertices, numTriangles*3);
1459 if (INFO) printf(
"[.ply]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
1460 _allocateAttributeArrays(numTriangles*3, numTriangles*3);
1463 if (INFO) printf(
"[.ply]: ------------\n" );
1465 std::vector<glm::vec3> verticesTemp;
1467 in.open( _filename );
1475 progressCounter = 0;
1478 while( getline( in, line ) ) {
1479 if( line.length() > 1 && line.at(0) ==
'\t' )
1480 line = line.substr( 1 );
1481 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1483 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1485 if( tokens.empty() )
continue;
1488 if( tokens[0] ==
"comment" ) {
1489 }
else if( fileState == HEADER ) {
1490 if( tokens[0] ==
"ply" ) {
1491 }
else if( tokens[0] ==
"format" ) {
1492 if( tokens[1] !=
"ascii" ) {
1493 if (ERRORS) fprintf( stderr,
"[.ply]: [ERROR]: File \"%s\" not ASCII format\n", _filename.c_str() );
1494 if ( INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1498 }
else if( tokens[0] ==
"element" ) {
1499 if( tokens[1] ==
"vertex" ) {
1500 numVertices =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1502 }
else if( tokens[1] ==
"face" ) {
1503 numFaces =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1505 }
else if( tokens[1] ==
"edge" ) {
1507 }
else if( tokens[1] ==
"material" ) {
1508 numMaterials =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1509 elemType = MATERIAL;
1513 }
else if( tokens[0] ==
"property" ) {
1514 if( elemType == VERTEX ) {
1516 }
else if( elemType == FACE ) {
1518 }
else if( elemType == MATERIAL ) {
1521 }
else if( tokens[0] ==
"end_header" ) {
1522 fileState = VERTICES;
1524 }
else if( fileState == VERTICES ) {
1526 glm::vec3 pos(strtof( tokens[0].c_str(),
nullptr ),
1527 strtof( tokens[1].c_str(),
nullptr ),
1528 strtof( tokens[2].c_str(),
nullptr ) );
1530 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1531 _vertices[ _uniqueIndex ] = pos;
1534 verticesTemp.push_back(pos );
1538 if( _uniqueIndex == numVertices || vSeen == numVertices )
1540 }
else if( fileState == FACES ) {
1541 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1543 for( GLuint i = 2; i <= numberOfVerticesInFace - 1; i++ ) {
1544 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1545 _indices[ _numIndices++ ] =
static_cast<GLuint
>(strtol(tokens[1].c_str(),
nullptr, 10));
1546 _indices[ _numIndices++ ] =
static_cast<GLuint
>(strtol(tokens[i].c_str(),
nullptr, 10));
1547 _indices[ _numIndices++ ] =
static_cast<GLuint
>(strtol(tokens[i + 1].c_str(),
nullptr, 10));
1549 auto aI =
static_cast<GLuint
>(strtol(tokens[1].c_str(),
nullptr, 10));
1550 auto bI =
static_cast<GLuint
>(strtol(tokens[i].c_str(),
nullptr, 10));
1551 auto cI =
static_cast<GLuint
>(strtol(tokens[i + 1].c_str(),
nullptr, 10));
1553 glm::vec3 a = verticesTemp[aI];
1554 glm::vec3 b = verticesTemp[bI];
1555 glm::vec3 c = verticesTemp[cI];
1557 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
1558 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
1559 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
1561 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
1562 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
1563 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
1565 _vertices[ _uniqueIndex ] = a;
1566 _normals[ _uniqueIndex ] = aN;
1567 _indices[ _numIndices++ ] = _uniqueIndex++;
1569 _vertices[ _uniqueIndex ] = b;
1570 _normals[ _uniqueIndex ] = bN;
1571 _indices[ _numIndices++ ] = _uniqueIndex++;
1573 _vertices[ _uniqueIndex ] = c;
1574 _normals[ _uniqueIndex ] = cN;
1575 _indices[ _numIndices++ ] = _uniqueIndex++;
1579 if (INFO) printf(
"[.ply]: unknown file state: %d\n", fileState );
1584 if( progressCounter % 5000 == 0 ) {
1586 switch( progressCounter ) {
1587 case 5000: printf(
"[.ply]: parsing %s...\\", _filename.c_str());
break;
1588 case 10000: printf(
"[.ply]: parsing %s...|", _filename.c_str());
break;
1589 case 15000: printf(
"[.ply]: parsing %s.../", _filename.c_str());
break;
1590 case 20000: printf(
"[.ply]: parsing %s...-", _filename.c_str());
break;
1595 if( progressCounter == 20000 )
1596 progressCounter = 0;
1604 double seconds = difftime( end, start );
1607 printf(
"\33[2K\r" );
1608 printf(
"[.ply]: parsing %s...done!\n[.ply]: Time to complete: %.3fs\n", _filename.c_str(), seconds );
1609 printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1615inline bool CSCI441::ModelLoader::_loadSTLFile(
const bool INFO,
const bool ERRORS ) {
1618 if (INFO) printf(
"[.stl]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1623 std::ifstream in( _filename );
1624 if( !in.is_open() ) {
1625 if (ERRORS) fprintf(stderr,
"[.stl]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
1626 if ( INFO ) printf(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1630 GLuint numVertices = 0, numNormals = 0, numFaces = 0, numTriangles = 0, numVertsInLoop = 0;
1631 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1634 int progressCounter = 0;
1635 glm::vec3 normalVector = {0.f,0.f,0.f};
1637 while( getline( in, line ) ) {
1638 if( line.length() > 1 && line.at(0) ==
'\t' )
1639 line = line.substr( 1 );
1640 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1642 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1644 if( tokens.empty() )
continue;
1646 if( tokens[0] ==
"solid" ) {
1648 }
else if( tokens[0] ==
"facet" ) {
1651 }
else if( tokens[0] ==
"outer" && tokens[1] ==
"loop" ) {
1654 }
else if( tokens[0] ==
"vertex" ) {
1655 GLfloat x = strtof( tokens[1].c_str(),
nullptr ),
1656 y = strtof( tokens[2].c_str(),
nullptr ),
1657 z = strtof( tokens[3].c_str(),
nullptr );
1659 if( x < minX ) minX = x;
1660 if( x > maxX ) maxX = x;
1661 if( y < minY ) minY = y;
1662 if( y > maxY ) maxY = y;
1663 if( z < minZ ) minZ = z;
1664 if( z > maxZ ) maxZ = z;
1668 }
else if( tokens[0] ==
"endloop" ) {
1670 numTriangles += numVertsInLoop - 3 + 1;
1671 }
else if( tokens[0] ==
"endfacet" ) {
1673 }
else if( tokens[0] ==
"endsolid" ) {
1677 if( memchr( line.c_str(),
'\0', line.length() ) !=
nullptr ) {
1678 if (ERRORS) fprintf( stderr,
"[.stl]: [ERROR]: Cannot read binary STL file \"%s\"\n", _filename.c_str() );
1679 if ( INFO ) printf(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1682 }
else if (INFO) printf(
"[.stl]: unknown line: %s\n", line.c_str() );
1687 if( progressCounter % 5000 == 0 ) {
1689 switch( progressCounter ) {
1690 case 5000: printf(
"[.stl]: scanning %s...\\", _filename.c_str());
break;
1691 case 10000: printf(
"[.stl]: scanning %s...|", _filename.c_str());
break;
1692 case 15000: printf(
"[.stl]: scanning %s.../", _filename.c_str());
break;
1693 case 20000: printf(
"[.stl]: scanning %s...-", _filename.c_str());
break;
1698 if( progressCounter == 20000 )
1699 progressCounter = 0;
1705 printf(
"\33[2K\r" );
1706 printf(
"[.stl]: scanning %s...done!\n", _filename.c_str() );
1707 printf(
"[.stl]: ------------\n" );
1708 printf(
"[.stl]: Model Stats:\n" );
1709 printf(
"[.stl]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, numNormals, 0 );
1710 printf(
"[.stl]: Faces: \t%u\tTriangles: \t%u\n", numFaces, numTriangles );
1711 printf(
"[.stl]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1714 _allocateAttributeArrays(numVertices, numTriangles*3);
1716 if (INFO) printf(
"[.stl]: ------------\n" );
1718 in.open( _filename );
1723 while( getline( in, line ) ) {
1724 if( line.length() > 1 && line.at(0) ==
'\t' )
1725 line = line.substr( 1 );
1726 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1728 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1730 if( tokens.empty() )
continue;
1732 if( tokens[0] ==
"solid" ) {
1734 }
else if( tokens[0] ==
"facet" ) {
1736 normalVector = glm::vec3( strtof( tokens[2].c_str(),
nullptr ),
1737 strtof( tokens[3].c_str(),
nullptr ),
1738 strtof( tokens[4].c_str(),
nullptr ) );
1739 }
else if( tokens[0] ==
"outer" && tokens[1] ==
"loop" ) {
1741 }
else if( tokens[0] ==
"vertex" ) {
1742 _vertices[ _uniqueIndex ] = glm::vec3(strtof( tokens[1].c_str(),
nullptr ),
1743 strtof( tokens[2].c_str(),
nullptr ),
1744 strtof( tokens[3].c_str(),
nullptr ) );
1745 _normals[ _uniqueIndex ] = normalVector;
1746 _indices[ _numIndices++ ] = _uniqueIndex++;
1747 }
else if( tokens[0] ==
"endloop" ) {
1749 }
else if( tokens[0] ==
"endfacet" ) {
1751 }
else if( tokens[0] ==
"endsolid" ) {
1760 if( progressCounter % 5000 == 0 ) {
1762 switch( progressCounter ) {
1763 case 5000: printf(
"[.stl]: parsing %s...\\", _filename.c_str());
break;
1764 case 10000: printf(
"[.stl]: parsing %s...|", _filename.c_str());
break;
1765 case 15000: printf(
"[.stl]: parsing %s.../", _filename.c_str());
break;
1766 case 20000: printf(
"[.stl]: parsing %s...-", _filename.c_str());
break;
1771 if( progressCounter == 20000 )
1772 progressCounter = 0;
1780 double seconds = difftime( end, start );
1784 printf(
"[.stl]: parsing %s...done!\n[.stl]: Time to complete: %.3fs\n", _filename.c_str(), seconds);
1785 printf(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1793 sAUTO_GEN_NORMALS =
true;
1798 sAUTO_GEN_NORMALS =
false;
1802 sAUTO_GEN_TANGENTS =
true;
1807 sAUTO_GEN_TANGENTS =
false;
1810inline void CSCI441::ModelLoader::_allocateAttributeArrays(
const GLuint numVertices,
const GLuint numIndices) {
1811 _vertices =
new glm::vec3[numVertices];
1812 _normals =
new glm::vec3[numVertices];
1813 _tangents =
new glm::vec4[numVertices];
1814 _texCoords =
new glm::vec2[numVertices];
1815 _indices =
new GLuint[numIndices];
1818inline void CSCI441::ModelLoader::_bufferData()
const {
1819 glBindVertexArray( _vaod );
1821 glBindBuffer( GL_ARRAY_BUFFER, _vbods[0] );
1822 glBufferData( GL_ARRAY_BUFFER,
static_cast<GLsizeiptr
>((
sizeof(glm::vec3)*2 +
sizeof(glm::vec2) +
sizeof(glm::vec4)) * _uniqueIndex),
nullptr, GL_STATIC_DRAW );
1823 glBufferSubData( GL_ARRAY_BUFFER, 0,
static_cast<GLsizeiptr
>(
sizeof(glm::vec3) * _uniqueIndex), _vertices );
1824 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex),
static_cast<GLsizeiptr
>(
sizeof(glm::vec3) * _uniqueIndex), _normals );
1825 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex * 2),
static_cast<GLsizeiptr
>(
sizeof(glm::vec2) * _uniqueIndex), _texCoords );
1826 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex * 2 +
sizeof(glm::vec2) * _uniqueIndex),
static_cast<GLsizeiptr
>(
sizeof(glm::vec4) * _uniqueIndex), _tangents );
1828 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, _vbods[1] );
1829 glBufferData( GL_ELEMENT_ARRAY_BUFFER,
static_cast<GLsizeiptr
>(
sizeof(GLuint) * _numIndices), _indices, GL_STATIC_DRAW );
1838inline std::vector<std::string> CSCI441::ModelLoader::_tokenizeString(
const std::string& input,
const std::string& delimiters) {
1842 auto retVec = std::vector<std::string>();
1843 size_t oldR = 0, r = 0;
1846 GLint lowerValidIndex = 0, upperValidIndex =
static_cast<GLint
>(input.size()) - 1;
1847 while(
static_cast<GLuint
>(lowerValidIndex) < input.size() && delimiters.find_first_of(input.at(lowerValidIndex), 0) != std::string::npos)
1850 while(upperValidIndex >= 0 && delimiters.find_first_of(input.at(upperValidIndex), 0) != std::string::npos)
1854 if(
static_cast<GLuint
>(lowerValidIndex) >= input.size() || upperValidIndex < 0 || lowerValidIndex > upperValidIndex)
1858 const std::string strippedInput = input.substr(lowerValidIndex, upperValidIndex-lowerValidIndex+1);
1862 while((r = strippedInput.find_first_of(delimiters, oldR)) != std::string::npos) {
1865 retVec.push_back(strippedInput.substr(oldR, r-oldR));
1870 retVec.push_back(strippedInput.substr(oldR, r-oldR));
1876inline void CSCI441::ModelLoader::_moveFromSrc(ModelLoader& src) {
1877 _hasVertexTexCoords = src._hasVertexTexCoords;
1878 src._hasVertexTexCoords =
false;
1880 _hasVertexNormals = src._hasVertexNormals;
1881 src._hasVertexNormals =
false;
1883 _vertices = src._vertices;
1884 src._vertices =
nullptr;
1886 _texCoords = src._texCoords;
1887 src._texCoords =
nullptr;
1889 _normals = src._normals;
1890 src._normals =
nullptr;
1892 _tangents = src._tangents;
1893 src._tangents =
nullptr;
1895 _indices = src._indices;
1896 src._indices =
nullptr;
1898 _uniqueIndex = src._uniqueIndex;
1899 src._uniqueIndex = 0;
1901 _numIndices = src._numIndices;
1902 src._numIndices = 0;
1904 _vbods[0] = src._vbods[0];
1905 _vbods[1] = src._vbods[1];
1912 _filename = std::move(src._filename);
1915 _modelType = src._modelType;
1917 _materials = std::move(src._materials);
1918 _materialIndexStartStop = std::move(src._materialIndexStartStop);
1921inline void CSCI441::ModelLoader::_cleanupSelf() {
1923 _vertices =
nullptr;
1929 _tangents =
nullptr;
1931 delete[] _texCoords;
1932 _texCoords =
nullptr;
1937 glDeleteBuffers( 2, _vbods );
1941 glDeleteVertexArrays( 1, &_vaod );
1944 _hasVertexTexCoords =
false;
1945 _hasVertexNormals =
false;
1950 for(
const auto& [name, material] : _materials ) {
1955 _materialIndexStartStop.clear();
1958inline unsigned char* CSCI441_INTERNAL::createTransparentTexture(
const unsigned char * imageData,
const unsigned char *imageMask,
const int texWidth,
const int texHeight,
const int texChannels,
const int maskChannels ) {
1960 auto *fullData =
new unsigned char[texWidth*texHeight*4];
1962 for(
int j = 0; j < texHeight; j++) {
1963 for(
int i = 0; i < texWidth; i++) {
1965 fullData[(j*texWidth+i)*4+0] = imageData[(j*texWidth+i)*texChannels+0];
1966 fullData[(j*texWidth+i)*4+1] = imageData[(j*texWidth+i)*texChannels+1];
1967 fullData[(j*texWidth+i)*4+2] = imageData[(j*texWidth+i)*texChannels+2];
1969 fullData[(j*texWidth+i)*4+0] = 1;
1970 fullData[(j*texWidth+i)*4+1] = 1;
1971 fullData[(j*texWidth+i)*4+2] = 1;
1975 fullData[(j*texWidth+i)*4+3] = imageMask[(j*texWidth+i)*maskChannels+0];
1977 fullData[(j*texWidth+i)*4+3] = 1;
1985inline void CSCI441_INTERNAL::flipImageY(
const int texWidth,
const int texHeight,
const int textureChannels,
unsigned char *textureData ) {
1986 for(
int j = 0; j < texHeight / 2; j++ ) {
1987 for(
int i = 0; i < texWidth; i++ ) {
1988 for(
int k = 0; k < textureChannels; k++ ) {
1989 const int top = (j*texWidth + i)*textureChannels + k;
1990 const int bot = ((texHeight-j-1)*texWidth + i)*textureChannels + k;
1992 const unsigned char t = textureData[top];
1993 textureData[top] = textureData[bot];
1994 textureData[bot] = t;
Loads object models from file and renders using VBOs/VAOs.
Definition: ModelLoader.hpp:54
ModelLoader(const ModelLoader &)=delete
do not allow models to be copied
static void enableAutoGenerateNormals()
Enable auto-generation of vertex normals.
Definition: ModelLoader.hpp:1792
ModelLoader & operator=(const ModelLoader &)=delete
do not allow models to be copied
static void enableAutoGenerateTangents()
Enable auto-generation of vertex tangents.
Definition: ModelLoader.hpp:1801
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:403
GLfloat * getVertices() const
Return the vertex array that makes up the model mesh.
Definition: ModelLoader.hpp:399
GLfloat * getTangents() const
Return the tangent array that corresponds to the model mesh.
Definition: ModelLoader.hpp:401
bool loadModelFile(std::string filename, bool INFO=true, bool ERRORS=true)
Loads a model from the given file.
Definition: ModelLoader.hpp:310
void setAttributeLocations(GLint positionLocation, GLint normalLocation=-1, GLint texCoordLocation=-1, GLint tangentLocation=-1) const
Enables VBO attribute array locations.
Definition: ModelLoader.hpp:338
GLfloat * getNormals() const
Return the normal array that corresponds to the model mesh.
Definition: ModelLoader.hpp:400
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:356
GLuint * getIndices() const
Return the index array that dictates the order to draw the model mesh.
Definition: ModelLoader.hpp:404
GLfloat * getTexCoords() const
Return the texture coordinates array that corresponds to the model mesh.
Definition: ModelLoader.hpp:402
static void disableAutoGenerateTangents()
Disable auto-generation of vertex tangents.
Definition: ModelLoader.hpp:1806
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:398
static void disableAutoGenerateNormals()
Disable auto-generation of vertex normals.
Definition: ModelLoader.hpp:1797
~ModelLoader()
Frees memory associated with model on both CPU and GPU.
Definition: ModelLoader.hpp:277
ModelLoader()
Creates an empty model.
Definition: ModelLoader.hpp:243
Internal material representation for *.mtl files.
CSCI441 Helper Functions for OpenGL.
Definition: ArcballCam.hpp:17