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 if (positionLocation >= 0) {
343 glEnableVertexAttribArray( positionLocation );
344 glVertexAttribPointer( positionLocation, 3, GL_FLOAT, GL_FALSE, 0, (
void*)
nullptr );
347 if (normalLocation >= 0) {
348 glEnableVertexAttribArray( normalLocation );
349 glVertexAttribPointer( normalLocation, 3, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex) );
352 if (texCoordLocation >= 0) {
353 glEnableVertexAttribArray( texCoordLocation );
354 glVertexAttribPointer( texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex * 2) );
357 if (tangentLocation >= 0) {
358 glEnableVertexAttribArray( tangentLocation );
359 glVertexAttribPointer( tangentLocation, 4, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex * 2 +
sizeof(glm::vec2) * _uniqueIndex) );
365 const GLint matDiffLocation,
const GLint matSpecLocation,
const GLint matShinLocation,
const GLint matAmbLocation,
366 const GLenum diffuseTexture )
const {
367 glBindVertexArray( _vaod );
370 if( _modelType == CSCI441_INTERNAL::MODEL_TYPE::OBJ ) {
371 for(
const auto & materialIter : _materialIndexStartStop) {
372 auto materialName = materialIter.first;
373 auto indexStartStop = materialIter.second;
375 CSCI441_INTERNAL::ModelMaterial* material =
nullptr;
376 if( _materials.find( materialName ) != _materials.end() )
377 material = _materials.find( materialName )->second;
379 for(
const auto &[start, end] : indexStartStop) {
380 const GLsizei length =
static_cast<GLsizei
>(end - start) + 1;
384 if( material !=
nullptr ) {
385 glProgramUniform4fv( shaderProgramHandle, matAmbLocation, 1, &material->ambient[0] );
386 glProgramUniform4fv( shaderProgramHandle, matDiffLocation, 1, &material->diffuse[0] );
387 glProgramUniform4fv( shaderProgramHandle, matSpecLocation, 1, &material->specular[0] );
388 glProgramUniform1f( shaderProgramHandle, matShinLocation, material->shininess );
390 if( material->map_Kd != -1 ) {
391 glActiveTexture( diffuseTexture );
392 glBindTexture( GL_TEXTURE_2D, material->map_Kd );
396 glDrawElements( GL_TRIANGLES, length, GL_UNSIGNED_INT, (
void*)(
sizeof(GLuint)*start) );
400 glDrawElements( GL_TRIANGLES,
static_cast<GLint
>(_numIndices), GL_UNSIGNED_INT, (
void*)
nullptr );
415inline bool CSCI441::ModelLoader::_loadOBJFile(
const bool INFO,
const bool ERRORS ) {
419 if( _filename.find(
'/') != std::string::npos ) {
420 path = _filename.substr( 0, _filename.find_last_of(
'/')+1 );
425 if ( INFO ) fprintf( stdout,
"[.obj]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=- \n", _filename.c_str() );
430 std::ifstream in( _filename );
431 if( !in.is_open() ) {
432 if (ERRORS) fprintf( stderr,
"[.obj]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
433 if ( INFO ) fprintf( stdout,
"[.obj]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n", _filename.c_str() );
437 GLuint numObjects = 0, numGroups = 0;
438 GLuint numVertices = 0, numTexCoords = 0, numNormals = 0;
439 GLuint numFaces = 0, numTriangles = 0;
440 glm::vec3 minDimension = {999999.f, 999999.f, 999999.f};
441 glm::vec3 maxDimension = { -999999.f, -999999.f, -999999.f };
444 std::map<std::string, GLuint> uniqueCounts;
447 int progressCounter = 0;
449 while( getline( in, line ) ) {
450 if( line.length() > 1 && line.at(0) ==
'\t' )
451 line = line.substr( 1 );
452 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
454 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
455 if( tokens.empty() )
continue;
458 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
460 }
else if( tokens[0] ==
"o" ) {
462 }
else if( tokens[0] ==
"g" ) {
464 }
else if( tokens[0] ==
"mtllib" ) {
465 std::string mtlFilename = path + tokens[1];
466 _loadMTLFile( mtlFilename.c_str(), INFO, ERRORS );
467 }
else if( tokens[0] ==
"v" ) {
470 glm::vec3 pos = { strtof( tokens[1].c_str(),
nullptr ),
471 strtof( tokens[2].c_str(),
nullptr ),
472 strtof( tokens[3].c_str(),
nullptr ) };
474 if( pos.x < minDimension.x ) minDimension.x = pos.x;
475 if( pos.x > maxDimension.x ) maxDimension.x = pos.x;
476 if( pos.y < minDimension.y ) minDimension.y = pos.y;
477 if( pos.y > maxDimension.y ) maxDimension.y = pos.y;
478 if( pos.z < minDimension.z ) minDimension.z = pos.z;
479 if( pos.z > maxDimension.z ) maxDimension.z = pos.z;
480 }
else if( tokens[0] ==
"vn" ) {
482 }
else if( tokens[0] ==
"vt" ) {
484 }
else if( tokens[0] ==
"f" ) {
487 std::vector<std::string> faceTokens = _tokenizeString(line,
" ");
489 for (GLuint i = 1; i < faceTokens.size(); i++) {
491 std::vector<std::string> groupTokens = _tokenizeString(faceTokens[i],
"/");
493 for (
char j: faceTokens[i]) {
494 if (j ==
'/') numSlashes++;
497 std::stringstream currentFaceTokenStream;
499 auto signedVertexIndex =
static_cast<GLint
>(strtol(groupTokens[0].c_str(),
nullptr, 10));
500 GLuint vertexIndex = signedVertexIndex;
501 if (signedVertexIndex < 0) vertexIndex = numVertices + signedVertexIndex + 1;
503 currentFaceTokenStream << vertexIndex;
506 if (groupTokens.size() == 2 && numSlashes == 1) {
507 _hasVertexTexCoords =
true;
509 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(groupTokens[1].c_str(),
nullptr, 10));
510 GLuint texCoordIndex = signedTexCoordIndex;
511 if (signedTexCoordIndex < 0) texCoordIndex = numTexCoords + signedTexCoordIndex + 1;
513 currentFaceTokenStream <<
"/" << texCoordIndex;
514 }
else if (groupTokens.size() == 2 && numSlashes == 2) {
515 _hasVertexNormals =
true;
517 auto signedNormalIndex =
static_cast<GLint
>(strtol(groupTokens[1].c_str(),
nullptr, 10));
518 GLuint normalIndex = signedNormalIndex;
519 if (signedNormalIndex < 0) normalIndex = numNormals + signedNormalIndex + 1;
521 currentFaceTokenStream <<
"//" << normalIndex;
522 }
else if (groupTokens.size() == 3) {
523 _hasVertexTexCoords =
true;
524 _hasVertexNormals =
true;
526 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(groupTokens[1].c_str(),
nullptr, 10));
527 GLuint texCoordIndex = signedTexCoordIndex;
528 if (signedTexCoordIndex < 0) texCoordIndex = numTexCoords + signedTexCoordIndex + 1;
530 auto signedNormalIndex =
static_cast<GLint
>(strtol(groupTokens[2].c_str(),
nullptr, 10));
531 GLuint normalIndex = signedNormalIndex;
532 if (signedNormalIndex < 0) normalIndex = numNormals + signedNormalIndex + 1;
534 currentFaceTokenStream <<
"/" << texCoordIndex <<
"/" << normalIndex;
535 }
else if (groupTokens.size() != 1) {
536 if (ERRORS) fprintf(stderr,
"[.obj]: [ERROR]: Malformed OBJ file, %s.\n", _filename.c_str());
540 std::string processedFaceToken = currentFaceTokenStream.str();
541 if (uniqueCounts.find(processedFaceToken) == uniqueCounts.end()) {
542 uniqueCounts.insert(std::pair<std::string, long int>(processedFaceToken, _uniqueIndex));
547 numTriangles += (faceTokens.size() - 1 - 3 + 1);
550 }
else if( tokens[0] ==
"usemtl" ) {
553 if (INFO) printf(
"[.obj]: ignoring line: %s\n", line.c_str() );
558 if( progressCounter % 5000 == 0 ) {
560 switch( progressCounter ) {
561 case 5000: printf(
"[.obj]: scanning %s...\\", _filename.c_str());
break;
562 case 10000: printf(
"[.obj]: scanning %s...|", _filename.c_str());
break;
563 case 15000: printf(
"[.obj]: scanning %s.../", _filename.c_str());
break;
564 case 20000: printf(
"[.obj]: scanning %s...-", _filename.c_str());
break;
569 if( progressCounter == 20000 )
576 printf(
"\33[2K\r" );
577 printf(
"[.obj]: scanning %s...done!\n", _filename.c_str() );
578 printf(
"[.obj]: ------------\n" );
579 printf(
"[.obj]: Model Stats:\n" );
580 printf(
"[.obj]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, numNormals, numTexCoords );
581 printf(
"[.obj]: Unique Verts:\t%u\n", _uniqueIndex );
582 printf(
"[.obj]: Faces: \t%u\tTriangles:\t%u\n", numFaces, numTriangles );
583 printf(
"[.obj]: Objects: \t%u\tGroups: \t%u\n", numObjects, numGroups );
585 glm::vec3 sizeDimensions = maxDimension - minDimension;
586 printf(
"[.obj]: Dimensions:\t(%f, %f, %f)\n", sizeDimensions.x, sizeDimensions.y, sizeDimensions.z );
589 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
590 if (INFO && !_hasVertexNormals)
591 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" );
592 if (INFO && sAUTO_GEN_TANGENTS) fprintf( stdout,
"[.obj]: Vertex tangents will be autogenerated\n" );
593 _allocateAttributeArrays(_uniqueIndex, numTriangles*3);
595 if (INFO) printf(
"[.obj]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
596 if (INFO && sAUTO_GEN_TANGENTS) fprintf( stdout,
"[.obj]: Vertex tangents will be autogenerated\n" );
597 _allocateAttributeArrays(numTriangles * 3, numTriangles*3);
600 auto objVertices =
new glm::vec3[numVertices];
601 auto objNormals =
new glm::vec3[numNormals];
602 auto objTexCoords =
new glm::vec2[numTexCoords];
604 std::vector<glm::vec3> verticesTemps;
605 std::vector<glm::vec2> texCoordsTemp;
607 printf(
"[.obj]: ------------\n" );
609 uniqueCounts.clear();
613 in.open( _filename );
615 GLuint verticesSeen = 0, texCoordsSeen = 0, normalsSeen = 0, indicesSeen = 0;
616 GLuint uniqueNumVertices = 0;
618 std::string currentMaterial =
"default";
619 _materialIndexStartStop.insert( std::pair< std::string, std::vector< std::pair< GLuint, GLuint > > >( currentMaterial, std::vector< std::pair< GLuint, GLuint > >(1) ) );
620 _materialIndexStartStop.find( currentMaterial )->second.back().first = indicesSeen;
622 while( getline( in, line ) ) {
623 if( line.length() > 1 && line.at(0) ==
'\t' )
624 line = line.substr( 1 );
625 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
627 auto tokens = _tokenizeString( line,
" \t" );
628 if( tokens.empty() )
continue;
631 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0
634 || tokens[0] ==
"mtllib"
638 }
else if( tokens[0] ==
"usemtl" ) {
639 if( currentMaterial ==
"default" && indicesSeen == 0 ) {
640 _materialIndexStartStop.clear();
642 _materialIndexStartStop.find( currentMaterial )->second.back().second = indicesSeen - 1;
644 currentMaterial = tokens[1];
645 if( _materialIndexStartStop.find( currentMaterial ) == _materialIndexStartStop.end() ) {
646 _materialIndexStartStop.insert( std::pair< std::string, std::vector< std::pair< GLuint, GLuint > > >( currentMaterial, std::vector< std::pair< GLuint, GLuint > >(1) ) );
647 _materialIndexStartStop.find( currentMaterial )->second.back().first = indicesSeen;
649 _materialIndexStartStop.find( currentMaterial )->second.emplace_back( indicesSeen, -1 );
651 }
else if( tokens[0] ==
"v" ) {
652 objVertices[verticesSeen++] = glm::vec3(strtof(tokens[1].c_str(),
nullptr ),
653 strtof( tokens[2].c_str(),
nullptr ),
654 strtof( tokens[3].c_str(),
nullptr ) );
655 }
else if( tokens[0] ==
"vn" ) {
656 objNormals[normalsSeen++] = glm::vec3(strtof(tokens[1].c_str(),
nullptr ),
657 strtof( tokens[2].c_str(),
nullptr ),
658 strtof( tokens[3].c_str(),
nullptr ));
659 }
else if( tokens[0] ==
"vt" ) {
660 objTexCoords[texCoordsSeen++] = glm::vec2(strtof(tokens[1].c_str(),
nullptr ),
661 strtof( tokens[2].c_str(),
nullptr ));
662 }
else if( tokens[0] ==
"f" ) {
663 std::vector<std::string> processedFaceTokens;
665 bool faceHasVertexNormals =
false;
666 bool faceHasTextureCoordinates =
false;
668 for(GLuint i = 1; i < tokens.size(); i++) {
670 auto vertexAttributeTokens = _tokenizeString(tokens[i],
"/");
671 int numAttributeSlashes = 0;
672 for(
char j : tokens[i]) {
673 if(j ==
'/') numAttributeSlashes++;
676 std::stringstream currentFaceTokenStream;
678 auto signedVertexIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[0].c_str(),
nullptr, 10));
679 GLuint vertexIndex = signedVertexIndex;
680 if(signedVertexIndex < 0) vertexIndex = verticesSeen + signedVertexIndex + 1;
681 currentFaceTokenStream << vertexIndex;
683 GLuint texCoordIndex = 0, normalIndex = 0;
686 if(vertexAttributeTokens.size() == 2 && numAttributeSlashes == 1) {
688 _hasVertexTexCoords =
true;
689 faceHasTextureCoordinates =
true;
691 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10));
692 texCoordIndex = signedTexCoordIndex;
693 if(signedTexCoordIndex < 0) texCoordIndex = texCoordsSeen + signedTexCoordIndex + 1;
694 currentFaceTokenStream <<
"/" << texCoordIndex;
695 }
else if(vertexAttributeTokens.size() == 2 && numAttributeSlashes == 2) {
697 _hasVertexNormals =
true;
698 faceHasVertexNormals =
true;
700 auto signedNormalIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10));
701 normalIndex = signedNormalIndex;
702 if(signedNormalIndex < 0) normalIndex = normalsSeen + signedNormalIndex + 1;
703 currentFaceTokenStream <<
"//" << normalIndex;
704 }
else if(vertexAttributeTokens.size() == 3) {
706 _hasVertexTexCoords =
true;
707 faceHasTextureCoordinates =
true;
708 _hasVertexNormals =
true;
709 faceHasVertexNormals =
true;
711 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10));
712 texCoordIndex = signedTexCoordIndex;
713 if(signedTexCoordIndex < 0) texCoordIndex = texCoordsSeen + signedTexCoordIndex + 1;
715 auto signedNormalIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[2].c_str(),
nullptr, 10));
716 normalIndex = signedNormalIndex;
717 if(signedNormalIndex < 0) normalIndex = normalsSeen + signedNormalIndex + 1;
719 currentFaceTokenStream <<
"/" << texCoordIndex <<
"/" << normalIndex;
720 }
else if(vertexAttributeTokens.size() != 1) {
721 if (ERRORS) fprintf(stderr,
"[.obj]: [ERROR]: Malformed OBJ file, %s.\n", _filename.c_str());
725 auto processedFaceToken = currentFaceTokenStream.str();
726 processedFaceTokens.push_back(processedFaceToken);
729 if( uniqueCounts.find( processedFaceToken ) == uniqueCounts.end() ) {
731 uniqueCounts.insert( std::pair<std::string,GLuint>(processedFaceToken, uniqueNumVertices) );
733 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
734 _vertices[ _uniqueIndex ] = objVertices[ vertexIndex - 1 ];
735 if(faceHasTextureCoordinates && texCoordIndex != 0) { _texCoords[ _uniqueIndex ] = objTexCoords[ texCoordIndex - 1 ]; }
736 if(faceHasVertexNormals && normalIndex != 0) _normals[ _uniqueIndex ] = objNormals[ normalIndex - 1 ];
739 verticesTemps.push_back( objVertices[ vertexIndex - 1 ] );
740 if(faceHasTextureCoordinates && texCoordIndex != 0) { texCoordsTemp.push_back( objTexCoords[ texCoordIndex - 1 ] ); }
742 if( (vertexAttributeTokens.size() == 2 && numAttributeSlashes == 2)
743 || (vertexAttributeTokens.size() == 3) ) {
745 if (ERRORS) fprintf( stderr,
"[.obj]: [ERROR]: no vertex normals were specified, should not be trying to access values\n" );
752 for(GLuint i = 1; i < processedFaceTokens.size()-1; i++) {
753 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
754 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[0] )->second;
755 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[i] )->second;
756 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[i+1] )->second;
758 if (sAUTO_GEN_TANGENTS) {
759 glm::vec3 a = _vertices[ _indices[ indicesSeen - 3 ] ];
760 glm::vec3 b = _vertices[ _indices[ indicesSeen - 2 ] ];
761 glm::vec3 c = _vertices[ _indices[ indicesSeen - 1 ] ];
763 glm::vec3 ab = b - a;
764 glm::vec3 bc = c - b;
765 glm::vec3 ca = a - c;
767 _tangents[ _indices[ indicesSeen - 3 ] ] = glm::vec4(ab, 1.0f);
768 _tangents[ _indices[ indicesSeen - 2 ] ] = glm::vec4(bc, 1.0f);
769 _tangents[ _indices[ indicesSeen - 1 ] ] = glm::vec4(ca, 1.0f);
774 GLuint aI = uniqueCounts.find( processedFaceTokens[0] )->second;
775 GLuint bI = uniqueCounts.find( processedFaceTokens[i] )->second;
776 GLuint cI = uniqueCounts.find( processedFaceTokens[i+1] )->second;
778 glm::vec3 a = verticesTemps[aI];
779 glm::vec3 b = verticesTemps[bI];
780 glm::vec3 c = verticesTemps[cI];
782 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
783 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
784 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
786 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
787 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
788 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
790 _vertices[ _uniqueIndex ] = a;
791 _normals[ _uniqueIndex ] = aN;
792 if (sAUTO_GEN_TANGENTS) _tangents[ _uniqueIndex ] = glm::vec4(ab, 1.0f);
793 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ aI ]; }
794 _indices[ _numIndices++ ] = _uniqueIndex++;
798 _vertices[ _uniqueIndex ] = b;
799 _normals[ _uniqueIndex ] = bN;
800 if (sAUTO_GEN_TANGENTS) _tangents[ _uniqueIndex ] = glm::vec4(bc, 1.0f);
801 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ bI ]; }
802 _indices[ _numIndices++ ] = _uniqueIndex++;
806 _vertices[ _uniqueIndex ] = c;
807 _normals[ _uniqueIndex ] = cN;
808 if (sAUTO_GEN_TANGENTS) _tangents[ _uniqueIndex ] = glm::vec4(ca, 1.0f);
809 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ cI ]; }
810 _indices[ _numIndices++ ] = _uniqueIndex++;
819 if( progressCounter % 5000 == 0 ) {
821 switch( progressCounter ) {
822 case 5000: printf(
"[.obj]: parsing %s...\\", _filename.c_str());
break;
823 case 10000: printf(
"[.obj]: parsing %s...|", _filename.c_str());
break;
824 case 15000: printf(
"[.obj]: parsing %s.../", _filename.c_str());
break;
825 case 20000: printf(
"[.obj]: parsing %s...-", _filename.c_str());
break;
830 if( progressCounter == 20000 )
838 printf(
"\33[2K\r" );
839 printf(
"[.obj]: parsing %s...done!\n", _filename.c_str() );
842 _materialIndexStartStop.find( currentMaterial )->second.back().second = indicesSeen - 1;
847 double seconds = difftime( end, start );
850 printf(
"[.obj]: Completed in %.3fs\n", seconds );
851 printf(
"[.obj]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n\n", _filename.c_str() );
857inline bool CSCI441::ModelLoader::_loadMTLFile(
const char* mtlFilename,
const bool INFO,
const bool ERRORS ) {
860 if (INFO) printf(
"[.mtl]: -*-*-*-*-*-*-*- BEGIN %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
864 if( _filename.find(
'/') != std::string::npos ) {
865 path = _filename.substr( 0, _filename.find_last_of(
'/')+1 );
871 in.open( mtlFilename );
872 if( !in.is_open() ) {
873 in.open( mtlFilename );
874 if( !in.is_open() ) {
875 if (ERRORS) fprintf( stderr,
"[.mtl]: [ERROR]: could not open material file: %s\n", mtlFilename );
876 if ( INFO ) printf(
"[.mtl]: -*-*-*-*-*-*-*- END %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
881 CSCI441_INTERNAL::ModelMaterial* currentMaterial =
nullptr;
882 std::string materialName;
884 unsigned char *textureData =
nullptr;
885 unsigned char *maskData =
nullptr;
886 unsigned char *fullData;
887 int texWidth, texHeight, textureChannels = 1, maskChannels = 1;
888 GLuint textureHandle = 0;
890 std::map< std::string, GLuint > imageHandles;
892 int numMaterials = 0;
894 while( getline( in, line ) ) {
895 if( line.length() > 1 && line.at(0) ==
'\t' )
896 line = line.substr( 1 );
897 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
899 std::vector< std::string > tokens = _tokenizeString( line,
" /" );
900 if( tokens.empty() )
continue;
903 if( tokens[0] ==
"#" ) {
905 }
else if( tokens[0] ==
"newmtl" ) {
906 if (INFO) printf(
"[.mtl]: Parsing material %s properties\n", tokens[1].c_str() );
907 currentMaterial =
new CSCI441_INTERNAL::ModelMaterial();
908 materialName = tokens[1];
909 _materials.insert( std::pair<std::string, CSCI441_INTERNAL::ModelMaterial*>( materialName, currentMaterial ) );
912 textureData =
nullptr;
918 }
else if( tokens[0] ==
"Ka" ) {
919 currentMaterial->ambient[0] = strtof( tokens[1].c_str(),
nullptr );
920 currentMaterial->ambient[1] = strtof( tokens[2].c_str(),
nullptr );
921 currentMaterial->ambient[2] = strtof( tokens[3].c_str(),
nullptr );
922 }
else if( tokens[0] ==
"Kd" ) {
923 currentMaterial->diffuse[0] = strtof( tokens[1].c_str(),
nullptr );
924 currentMaterial->diffuse[1] = strtof( tokens[2].c_str(),
nullptr );
925 currentMaterial->diffuse[2] = strtof( tokens[3].c_str(),
nullptr );
926 }
else if( tokens[0] ==
"Ks" ) {
927 currentMaterial->specular[0] = strtof( tokens[1].c_str(),
nullptr );
928 currentMaterial->specular[1] = strtof( tokens[2].c_str(),
nullptr );
929 currentMaterial->specular[2] = strtof( tokens[3].c_str(),
nullptr );
930 }
else if( tokens[0] ==
"Ke" ) {
931 currentMaterial->emissive[0] = strtof( tokens[1].c_str(),
nullptr );
932 currentMaterial->emissive[1] = strtof( tokens[2].c_str(),
nullptr );
933 currentMaterial->emissive[2] = strtof( tokens[3].c_str(),
nullptr );
934 }
else if( tokens[0] ==
"Ns" ) {
935 currentMaterial->shininess = strtof( tokens[1].c_str(),
nullptr );
936 }
else if( tokens[0] ==
"Tr"
937 || tokens[0] ==
"d" ) {
938 currentMaterial->ambient[3] = strtof( tokens[1].c_str(),
nullptr );
939 currentMaterial->diffuse[3] = strtof( tokens[1].c_str(),
nullptr );
940 currentMaterial->specular[3] = strtof( tokens[1].c_str(),
nullptr );
941 }
else if( tokens[0] ==
"illum" ) {
943 }
else if( tokens[0] ==
"map_Kd" ) {
944 if( imageHandles.find( tokens[1] ) != imageHandles.end() ) {
946 currentMaterial->map_Kd = imageHandles.find( tokens[1] )->second;
948 stbi_set_flip_vertically_on_load(
true);
949 textureData = stbi_load( tokens[1].c_str(), &texWidth, &texHeight, &textureChannels, 0 );
951 std::string folderName = path + tokens[1];
952 textureData = stbi_load( folderName.c_str(), &texWidth, &texHeight, &textureChannels, 0 );
956 if (ERRORS) fprintf( stderr,
"[.mtl]: [ERROR]: File Not Found: %s\n", tokens[1].c_str() );
958 if (INFO) printf(
"[.mtl]: TextureMap:\t%s\tSize: %dx%d\tColors: %d\n", tokens[1].c_str(), texWidth, texHeight, textureChannels );
960 if( maskData ==
nullptr ) {
961 if( textureHandle == 0 ) {
962 glGenTextures( 1, &textureHandle );
963 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
966 glBindTexture( GL_TEXTURE_2D, textureHandle );
968 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
969 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
971 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
972 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
974 GLint colorSpace = GL_RGB;
975 if( textureChannels == 4 )
976 colorSpace = GL_RGBA;
977 glTexImage2D( GL_TEXTURE_2D, 0, colorSpace, texWidth, texHeight, 0, colorSpace, GL_UNSIGNED_BYTE, textureData );
979 currentMaterial->map_Kd = textureHandle;
981 fullData = CSCI441_INTERNAL::createTransparentTexture( textureData, maskData, texWidth, texHeight, textureChannels, maskChannels );
983 if( textureHandle == 0 ) {
984 glGenTextures( 1, &textureHandle );
985 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
988 glBindTexture( GL_TEXTURE_2D, textureHandle );
990 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
991 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
993 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
994 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
996 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullData );
1000 currentMaterial->map_Kd = textureHandle;
1004 }
else if( tokens[0] ==
"map_d" ) {
1005 if( imageHandles.find( tokens[1] ) != imageHandles.end() ) {
1007 currentMaterial->map_d = imageHandles.find( tokens[1] )->second;
1009 stbi_set_flip_vertically_on_load(
true);
1010 maskData = stbi_load( tokens[1].c_str(), &texWidth, &texHeight, &textureChannels, 0 );
1011 if( !textureData ) {
1012 std::string folderName = path + tokens[1];
1013 maskData = stbi_load( folderName.c_str(), &texWidth, &texHeight, &textureChannels, 0 );
1017 if (ERRORS) fprintf( stderr,
"[.mtl]: [ERROR]: File Not Found: %s\n", tokens[1].c_str() );
1019 if (INFO) printf(
"[.mtl]: AlphaMap: \t%s\tSize: %dx%d\tColors: %d\n", tokens[1].c_str(), texWidth, texHeight, maskChannels );
1021 if( textureData !=
nullptr ) {
1022 fullData = CSCI441_INTERNAL::createTransparentTexture( textureData, maskData, texWidth, texHeight, textureChannels, maskChannels );
1024 if( textureHandle == 0 ) {
1025 glGenTextures( 1, &textureHandle );
1026 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
1029 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1030 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1032 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1033 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1035 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullData );
1039 currentMaterial->map_Kd = textureHandle;
1043 }
else if( tokens[0] ==
"map_Ka" ) {
1045 }
else if( tokens[0] ==
"map_Ks" ) {
1047 }
else if( tokens[0] ==
"map_Ns" ) {
1049 }
else if( tokens[0] ==
"Ni" ) {
1051 }
else if( tokens[0] ==
"Tf" ) {
1053 }
else if( tokens[0] ==
"bump"
1054 || tokens[0] ==
"map_bump" ) {
1057 if (INFO) printf(
"[.mtl]: ignoring line: %s\n", line.c_str() );
1064 printf(
"[.mtl]: Materials:\t%d\n", numMaterials );
1065 printf(
"[.mtl]: -*-*-*-*-*-*-*- END %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
1071inline bool CSCI441::ModelLoader::_loadOFFFile(
const bool INFO,
const bool ERRORS ) {
1074 if (INFO ) printf(
"[.off]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1079 std::ifstream in( _filename );
1080 if( !in.is_open() ) {
1081 if (ERRORS) fprintf( stderr,
"[.off]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
1082 if ( INFO ) printf(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1086 GLuint numVertices = 0, numFaces = 0, numTriangles = 0;
1087 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1090 enum OFF_FILE_STATE { HEADER, VERTICES, FACES, DONE };
1092 OFF_FILE_STATE fileState = HEADER;
1094 GLuint vSeen = 0, fSeen = 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 ) {
1106 }
else if( fileState == HEADER ) {
1107 if( tokens[0] ==
"OFF" ) {
1109 if( tokens.size() != 3 ) {
1110 if (ERRORS) fprintf( stderr,
"[.off]: [ERROR]: Malformed OFF file. # vertices, faces, edges not properly specified\n" );
1111 if ( INFO ) printf(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1116 numVertices =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1117 numFaces =
static_cast<GLuint
>(strtol(tokens[1].c_str(),
nullptr, 10));
1122 fileState = VERTICES;
1124 }
else if( fileState == VERTICES ) {
1126 GLfloat x = strtof( tokens[0].c_str(),
nullptr ),
1127 y = strtof( tokens[1].c_str(),
nullptr ),
1128 z = strtof( tokens[2].c_str(),
nullptr );
1130 if( x < minX ) minX = x;
1131 if( x > maxX ) maxX = x;
1132 if( y < minY ) minY = y;
1133 if( y > maxY ) maxY = y;
1134 if( z < minZ ) minZ = z;
1135 if( z > maxZ ) maxZ = z;
1138 if( vSeen == numVertices )
1140 }
else if( fileState == FACES ) {
1141 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1143 numTriangles += numberOfVerticesInFace - 3 + 1;
1145 if( fSeen == numFaces )
1148 if (INFO) printf(
"[.off]: unknown file state: %d\n", fileState );
1154 printf(
"\33[2K\r" );
1155 printf(
"[.off]: scanning %s...done!\n", _filename.c_str() );
1156 printf(
"[.off]: ------------\n" );
1157 printf(
"[.off]: Model Stats:\n" );
1158 printf(
"[.off]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, 0, 0 );
1159 printf(
"[.off]: Faces: \t%u\tTriangles: \t%u\n", numFaces, numTriangles );
1160 printf(
"[.off]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1163 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1164 if (INFO && !_hasVertexNormals)
1165 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" );
1166 _allocateAttributeArrays(numVertices, numTriangles*3);
1168 if (INFO) printf(
"[.off]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
1169 _allocateAttributeArrays(numTriangles*3, numTriangles*3);
1172 std::vector<glm::vec3> verticesTemp;
1174 if (INFO) printf(
"[.off]: ------------\n" );
1176 in.open( _filename );
1184 int progressCounter = 0;
1186 while( getline( in, line ) ) {
1187 if( line.length() > 1 && line.at(0) ==
'\t' )
1188 line = line.substr( 1 );
1189 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1191 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1192 if( tokens.empty() )
continue;
1195 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
1197 }
else if( fileState == HEADER ) {
1198 if( tokens[0] ==
"OFF" ) {
1202 fileState = VERTICES;
1204 }
else if( fileState == VERTICES ) {
1206 glm::vec3 pos(strtof( tokens[0].c_str(),
nullptr ),
1207 strtof( tokens[1].c_str(),
nullptr ),
1208 strtof( tokens[2].c_str(),
nullptr ) );
1211 if( tokens.size() == 6 || tokens.size() == 7 ) {
1219 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1220 _vertices[ _uniqueIndex ] = pos;
1223 verticesTemp.push_back(pos);
1227 if( _uniqueIndex == numVertices || vSeen == numVertices )
1229 }
else if( fileState == FACES ) {
1230 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1233 for(GLuint i = 2; i <= numberOfVerticesInFace - 1; i++) {
1234 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1235 auto fanRoot = strtol( tokens[1].c_str(),
nullptr, 10 );
1236 auto fanA = strtol( tokens[i].c_str(),
nullptr, 10 );
1237 auto fanB = strtol( tokens[i+1].c_str(),
nullptr, 10 );
1239 if( fanRoot < 0 ) fanRoot = numVertices + fanRoot + 1;
1240 if( fanA < 0 ) fanA = numVertices + fanA + 1;
1241 if( fanB < 0 ) fanB = numVertices + fanB + 1;
1244 _indices[ _numIndices++ ] = fanRoot;
1245 _indices[ _numIndices++ ] = fanA;
1246 _indices[ _numIndices++ ] = fanB;
1248 auto aI = strtol( tokens[1].c_str(),
nullptr, 10 );
1249 auto bI = strtol( tokens[i].c_str(),
nullptr, 10 );
1250 auto cI = strtol( tokens[i+1].c_str(),
nullptr, 10 );
1252 if( aI < 0 ) aI = numVertices + aI + 1;
1253 if( bI < 0 ) bI = numVertices + bI + 1;
1254 if( cI < 0 ) cI = numVertices + cI + 1;
1256 glm::vec3 a = verticesTemp[aI];
1257 glm::vec3 b = verticesTemp[bI];
1258 glm::vec3 c = verticesTemp[cI];
1260 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
1261 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
1262 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
1264 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
1265 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
1266 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
1268 _vertices[ _uniqueIndex ] = a;
1269 _normals[ _uniqueIndex ] = aN;
1270 _indices[ _numIndices++ ] = _uniqueIndex++;
1272 _vertices[ _uniqueIndex ] = b;
1273 _normals[ _uniqueIndex ] = bN;
1274 _indices[ _numIndices++ ] = _uniqueIndex++;
1276 _vertices[ _uniqueIndex ] = c;
1277 _normals[ _uniqueIndex ] = cN;
1278 _indices[ _numIndices++ ] = _uniqueIndex++;
1303 if( progressCounter % 5000 == 0 ) {
1305 switch( progressCounter ) {
1306 case 5000: printf(
"[.off]: parsing %s...\\", _filename.c_str());
break;
1307 case 10000: printf(
"[.off]: parsing %s...|", _filename.c_str());
break;
1308 case 15000: printf(
"[.off]: parsing %s.../", _filename.c_str());
break;
1309 case 20000: printf(
"[.off]: parsing %s...-", _filename.c_str());
break;
1314 if( progressCounter == 20000 )
1315 progressCounter = 0;
1323 double seconds = difftime( end, start );
1326 printf(
"\33[2K\r" );
1327 printf(
"[.off]: parsing %s...done! (Time: %.1fs)\n", _filename.c_str(), seconds );
1328 printf(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1335inline bool CSCI441::ModelLoader::_loadPLYFile(
const bool INFO,
const bool ERRORS ) {
1338 if (INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1343 std::ifstream in( _filename );
1344 if( !in.is_open() ) {
1345 if (ERRORS) fprintf( stderr,
"[.ply]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
1346 if ( INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1350 GLuint numVertices = 0, numFaces = 0, numTriangles = 0, numMaterials = 0;
1351 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1354 enum PLY_FILE_STATE { HEADER, VERTICES, FACES, MATERIALS };
1355 enum PLY_ELEMENT_TYPE { NONE, VERTEX, FACE, MATERIAL };
1357 PLY_FILE_STATE fileState = HEADER;
1358 PLY_ELEMENT_TYPE elemType = NONE;
1360 GLuint progressCounter = 0;
1363 while( getline( in, line ) ) {
1364 if( line.length() > 1 && line.at(0) ==
'\t' )
1365 line = line.substr( 1 );
1366 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1368 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1370 if( tokens.empty() )
continue;
1373 if( tokens[0] ==
"comment" ) {
1374 }
else if( fileState == HEADER ) {
1375 if( tokens[0] ==
"ply" ) {
1376 }
else if( tokens[0] ==
"format" ) {
1377 if( tokens[1] !=
"ascii" ) {
1378 if (ERRORS) fprintf( stderr,
"[.ply]: [ERROR]: File \"%s\" not ASCII format\n", _filename.c_str() );
1379 if ( INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1383 }
else if( tokens[0] ==
"element" ) {
1384 if( tokens[1] ==
"vertex" ) {
1385 numVertices =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1387 }
else if( tokens[1] ==
"face" ) {
1388 numFaces =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1390 }
else if( tokens[1] ==
"edge" ) {
1392 }
else if( tokens[1] ==
"material" ) {
1393 numMaterials =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1394 elemType = MATERIAL;
1398 }
else if( tokens[0] ==
"property" ) {
1399 if( elemType == VERTEX ) {
1401 }
else if( elemType == FACE ) {
1403 }
else if( elemType == MATERIAL ) {
1406 }
else if( tokens[0] ==
"end_header" ) {
1407 fileState = VERTICES;
1409 }
else if( fileState == VERTICES ) {
1411 auto x = (GLfloat) strtof( tokens[0].c_str(), nullptr ),
1412 y = (GLfloat) strtof( tokens[1].c_str(),
nullptr ),
1413 z = (GLfloat) strtof( tokens[2].c_str(), nullptr );
1415 if( x < minX ) minX = x;
1416 if( x > maxX ) maxX = x;
1417 if( y < minY ) minY = y;
1418 if( y > maxY ) maxY = y;
1419 if( z < minZ ) minZ = z;
1420 if( z > maxZ ) maxZ = z;
1424 if( vSeen == numVertices )
1426 }
else if( fileState == FACES ) {
1427 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1428 numTriangles += numberOfVerticesInFace - 3 + 1;
1430 if (INFO) printf(
"[.ply]: unknown file state: %d\n", fileState );
1435 if( progressCounter % 5000 == 0 ) {
1437 switch( progressCounter ) {
1438 case 5000: printf(
"[.ply]: scanning %s...\\", _filename.c_str());
break;
1439 case 10000: printf(
"[.ply]: scanning %s...|", _filename.c_str());
break;
1440 case 15000: printf(
"[.ply]: scanning %s.../", _filename.c_str());
break;
1441 case 20000: printf(
"[.ply]: scanning %s...-", _filename.c_str());
break;
1446 if( progressCounter == 20000 )
1447 progressCounter = 0;
1453 printf(
"\33[2K\r" );
1454 printf(
"[.ply]: scanning %s...done!\n", _filename.c_str() );
1455 printf(
"[.ply]: ------------\n" );
1456 printf(
"[.ply]: Model Stats:\n" );
1457 printf(
"[.ply]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, 0, 0 );
1458 printf(
"[.ply]: Faces: \t%u\tTriangles: \t%u\n", numFaces, numTriangles );
1459 printf(
"[.ply]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1462 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1463 if (INFO && !_hasVertexNormals)
1464 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" );
1465 _allocateAttributeArrays(numVertices, numTriangles*3);
1467 if (INFO) printf(
"[.ply]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
1468 _allocateAttributeArrays(numTriangles*3, numTriangles*3);
1471 if (INFO) printf(
"[.ply]: ------------\n" );
1473 std::vector<glm::vec3> verticesTemp;
1475 in.open( _filename );
1483 progressCounter = 0;
1486 while( getline( in, line ) ) {
1487 if( line.length() > 1 && line.at(0) ==
'\t' )
1488 line = line.substr( 1 );
1489 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1491 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1493 if( tokens.empty() )
continue;
1496 if( tokens[0] ==
"comment" ) {
1497 }
else if( fileState == HEADER ) {
1498 if( tokens[0] ==
"ply" ) {
1499 }
else if( tokens[0] ==
"format" ) {
1500 if( tokens[1] !=
"ascii" ) {
1501 if (ERRORS) fprintf( stderr,
"[.ply]: [ERROR]: File \"%s\" not ASCII format\n", _filename.c_str() );
1502 if ( INFO ) printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1506 }
else if( tokens[0] ==
"element" ) {
1507 if( tokens[1] ==
"vertex" ) {
1508 numVertices =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1510 }
else if( tokens[1] ==
"face" ) {
1511 numFaces =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1513 }
else if( tokens[1] ==
"edge" ) {
1515 }
else if( tokens[1] ==
"material" ) {
1516 numMaterials =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1517 elemType = MATERIAL;
1521 }
else if( tokens[0] ==
"property" ) {
1522 if( elemType == VERTEX ) {
1524 }
else if( elemType == FACE ) {
1526 }
else if( elemType == MATERIAL ) {
1529 }
else if( tokens[0] ==
"end_header" ) {
1530 fileState = VERTICES;
1532 }
else if( fileState == VERTICES ) {
1534 glm::vec3 pos(strtof( tokens[0].c_str(),
nullptr ),
1535 strtof( tokens[1].c_str(),
nullptr ),
1536 strtof( tokens[2].c_str(),
nullptr ) );
1538 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1539 _vertices[ _uniqueIndex ] = pos;
1542 verticesTemp.push_back(pos );
1546 if( _uniqueIndex == numVertices || vSeen == numVertices )
1548 }
else if( fileState == FACES ) {
1549 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1551 for( GLuint i = 2; i <= numberOfVerticesInFace - 1; i++ ) {
1552 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1553 _indices[ _numIndices++ ] =
static_cast<GLuint
>(strtol(tokens[1].c_str(),
nullptr, 10));
1554 _indices[ _numIndices++ ] =
static_cast<GLuint
>(strtol(tokens[i].c_str(),
nullptr, 10));
1555 _indices[ _numIndices++ ] =
static_cast<GLuint
>(strtol(tokens[i + 1].c_str(),
nullptr, 10));
1557 auto aI =
static_cast<GLuint
>(strtol(tokens[1].c_str(),
nullptr, 10));
1558 auto bI =
static_cast<GLuint
>(strtol(tokens[i].c_str(),
nullptr, 10));
1559 auto cI =
static_cast<GLuint
>(strtol(tokens[i + 1].c_str(),
nullptr, 10));
1561 glm::vec3 a = verticesTemp[aI];
1562 glm::vec3 b = verticesTemp[bI];
1563 glm::vec3 c = verticesTemp[cI];
1565 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
1566 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
1567 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
1569 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
1570 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
1571 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
1573 _vertices[ _uniqueIndex ] = a;
1574 _normals[ _uniqueIndex ] = aN;
1575 _indices[ _numIndices++ ] = _uniqueIndex++;
1577 _vertices[ _uniqueIndex ] = b;
1578 _normals[ _uniqueIndex ] = bN;
1579 _indices[ _numIndices++ ] = _uniqueIndex++;
1581 _vertices[ _uniqueIndex ] = c;
1582 _normals[ _uniqueIndex ] = cN;
1583 _indices[ _numIndices++ ] = _uniqueIndex++;
1587 if (INFO) printf(
"[.ply]: unknown file state: %d\n", fileState );
1592 if( progressCounter % 5000 == 0 ) {
1594 switch( progressCounter ) {
1595 case 5000: printf(
"[.ply]: parsing %s...\\", _filename.c_str());
break;
1596 case 10000: printf(
"[.ply]: parsing %s...|", _filename.c_str());
break;
1597 case 15000: printf(
"[.ply]: parsing %s.../", _filename.c_str());
break;
1598 case 20000: printf(
"[.ply]: parsing %s...-", _filename.c_str());
break;
1603 if( progressCounter == 20000 )
1604 progressCounter = 0;
1612 double seconds = difftime( end, start );
1615 printf(
"\33[2K\r" );
1616 printf(
"[.ply]: parsing %s...done!\n[.ply]: Time to complete: %.3fs\n", _filename.c_str(), seconds );
1617 printf(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1623inline bool CSCI441::ModelLoader::_loadSTLFile(
const bool INFO,
const bool ERRORS ) {
1626 if (INFO) printf(
"[.stl]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1631 std::ifstream in( _filename );
1632 if( !in.is_open() ) {
1633 if (ERRORS) fprintf(stderr,
"[.stl]: [ERROR]: Could not open \"%s\"\n", _filename.c_str() );
1634 if ( INFO ) printf(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1638 GLuint numVertices = 0, numNormals = 0, numFaces = 0, numTriangles = 0, numVertsInLoop = 0;
1639 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1642 int progressCounter = 0;
1643 glm::vec3 normalVector = {0.f,0.f,0.f};
1645 while( getline( in, line ) ) {
1646 if( line.length() > 1 && line.at(0) ==
'\t' )
1647 line = line.substr( 1 );
1648 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1650 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1652 if( tokens.empty() )
continue;
1654 if( tokens[0] ==
"solid" ) {
1656 }
else if( tokens[0] ==
"facet" ) {
1659 }
else if( tokens[0] ==
"outer" && tokens[1] ==
"loop" ) {
1662 }
else if( tokens[0] ==
"vertex" ) {
1663 GLfloat x = strtof( tokens[1].c_str(),
nullptr ),
1664 y = strtof( tokens[2].c_str(),
nullptr ),
1665 z = strtof( tokens[3].c_str(),
nullptr );
1667 if( x < minX ) minX = x;
1668 if( x > maxX ) maxX = x;
1669 if( y < minY ) minY = y;
1670 if( y > maxY ) maxY = y;
1671 if( z < minZ ) minZ = z;
1672 if( z > maxZ ) maxZ = z;
1676 }
else if( tokens[0] ==
"endloop" ) {
1678 numTriangles += numVertsInLoop - 3 + 1;
1679 }
else if( tokens[0] ==
"endfacet" ) {
1681 }
else if( tokens[0] ==
"endsolid" ) {
1685 if( memchr( line.c_str(),
'\0', line.length() ) !=
nullptr ) {
1686 if (ERRORS) fprintf( stderr,
"[.stl]: [ERROR]: Cannot read binary STL file \"%s\"\n", _filename.c_str() );
1687 if ( INFO ) printf(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1690 }
else if (INFO) printf(
"[.stl]: unknown line: %s\n", line.c_str() );
1695 if( progressCounter % 5000 == 0 ) {
1697 switch( progressCounter ) {
1698 case 5000: printf(
"[.stl]: scanning %s...\\", _filename.c_str());
break;
1699 case 10000: printf(
"[.stl]: scanning %s...|", _filename.c_str());
break;
1700 case 15000: printf(
"[.stl]: scanning %s.../", _filename.c_str());
break;
1701 case 20000: printf(
"[.stl]: scanning %s...-", _filename.c_str());
break;
1706 if( progressCounter == 20000 )
1707 progressCounter = 0;
1713 printf(
"\33[2K\r" );
1714 printf(
"[.stl]: scanning %s...done!\n", _filename.c_str() );
1715 printf(
"[.stl]: ------------\n" );
1716 printf(
"[.stl]: Model Stats:\n" );
1717 printf(
"[.stl]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, numNormals, 0 );
1718 printf(
"[.stl]: Faces: \t%u\tTriangles: \t%u\n", numFaces, numTriangles );
1719 printf(
"[.stl]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1722 _allocateAttributeArrays(numVertices, numTriangles*3);
1724 if (INFO) printf(
"[.stl]: ------------\n" );
1726 in.open( _filename );
1731 while( getline( in, line ) ) {
1732 if( line.length() > 1 && line.at(0) ==
'\t' )
1733 line = line.substr( 1 );
1734 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1736 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1738 if( tokens.empty() )
continue;
1740 if( tokens[0] ==
"solid" ) {
1742 }
else if( tokens[0] ==
"facet" ) {
1744 normalVector = glm::vec3( strtof( tokens[2].c_str(),
nullptr ),
1745 strtof( tokens[3].c_str(),
nullptr ),
1746 strtof( tokens[4].c_str(),
nullptr ) );
1747 }
else if( tokens[0] ==
"outer" && tokens[1] ==
"loop" ) {
1749 }
else if( tokens[0] ==
"vertex" ) {
1750 _vertices[ _uniqueIndex ] = glm::vec3(strtof( tokens[1].c_str(),
nullptr ),
1751 strtof( tokens[2].c_str(),
nullptr ),
1752 strtof( tokens[3].c_str(),
nullptr ) );
1753 _normals[ _uniqueIndex ] = normalVector;
1754 _indices[ _numIndices++ ] = _uniqueIndex++;
1755 }
else if( tokens[0] ==
"endloop" ) {
1757 }
else if( tokens[0] ==
"endfacet" ) {
1759 }
else if( tokens[0] ==
"endsolid" ) {
1768 if( progressCounter % 5000 == 0 ) {
1770 switch( progressCounter ) {
1771 case 5000: printf(
"[.stl]: parsing %s...\\", _filename.c_str());
break;
1772 case 10000: printf(
"[.stl]: parsing %s...|", _filename.c_str());
break;
1773 case 15000: printf(
"[.stl]: parsing %s.../", _filename.c_str());
break;
1774 case 20000: printf(
"[.stl]: parsing %s...-", _filename.c_str());
break;
1779 if( progressCounter == 20000 )
1780 progressCounter = 0;
1788 double seconds = difftime( end, start );
1792 printf(
"[.stl]: parsing %s...done!\n[.stl]: Time to complete: %.3fs\n", _filename.c_str(), seconds);
1793 printf(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1801 sAUTO_GEN_NORMALS =
true;
1806 sAUTO_GEN_NORMALS =
false;
1810 sAUTO_GEN_TANGENTS =
true;
1815 sAUTO_GEN_TANGENTS =
false;
1818inline void CSCI441::ModelLoader::_allocateAttributeArrays(
const GLuint numVertices,
const GLuint numIndices) {
1819 _vertices =
new glm::vec3[numVertices];
1820 _normals =
new glm::vec3[numVertices];
1821 _tangents =
new glm::vec4[numVertices];
1822 _texCoords =
new glm::vec2[numVertices];
1823 _indices =
new GLuint[numIndices];
1826inline void CSCI441::ModelLoader::_bufferData()
const {
1827 glBindVertexArray( _vaod );
1829 glBindBuffer( GL_ARRAY_BUFFER, _vbods[0] );
1830 glBufferData( GL_ARRAY_BUFFER,
static_cast<GLsizeiptr
>((
sizeof(glm::vec3)*2 +
sizeof(glm::vec2) +
sizeof(glm::vec4)) * _uniqueIndex),
nullptr, GL_STATIC_DRAW );
1831 glBufferSubData( GL_ARRAY_BUFFER, 0,
static_cast<GLsizeiptr
>(
sizeof(glm::vec3) * _uniqueIndex), _vertices );
1832 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex),
static_cast<GLsizeiptr
>(
sizeof(glm::vec3) * _uniqueIndex), _normals );
1833 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex * 2),
static_cast<GLsizeiptr
>(
sizeof(glm::vec2) * _uniqueIndex), _texCoords );
1834 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex * 2 +
sizeof(glm::vec2) * _uniqueIndex),
static_cast<GLsizeiptr
>(
sizeof(glm::vec4) * _uniqueIndex), _tangents );
1836 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, _vbods[1] );
1837 glBufferData( GL_ELEMENT_ARRAY_BUFFER,
static_cast<GLsizeiptr
>(
sizeof(GLuint) * _numIndices), _indices, GL_STATIC_DRAW );
1846inline std::vector<std::string> CSCI441::ModelLoader::_tokenizeString(
const std::string& input,
const std::string& delimiters) {
1850 auto retVec = std::vector<std::string>();
1851 size_t oldR = 0, r = 0;
1854 GLint lowerValidIndex = 0, upperValidIndex =
static_cast<GLint
>(input.size()) - 1;
1855 while(
static_cast<GLuint
>(lowerValidIndex) < input.size() && delimiters.find_first_of(input.at(lowerValidIndex), 0) != std::string::npos)
1858 while(upperValidIndex >= 0 && delimiters.find_first_of(input.at(upperValidIndex), 0) != std::string::npos)
1862 if(
static_cast<GLuint
>(lowerValidIndex) >= input.size() || upperValidIndex < 0 || lowerValidIndex > upperValidIndex)
1866 const std::string strippedInput = input.substr(lowerValidIndex, upperValidIndex-lowerValidIndex+1);
1870 while((r = strippedInput.find_first_of(delimiters, oldR)) != std::string::npos) {
1873 retVec.push_back(strippedInput.substr(oldR, r-oldR));
1878 retVec.push_back(strippedInput.substr(oldR, r-oldR));
1884inline void CSCI441::ModelLoader::_moveFromSrc(ModelLoader& src) {
1885 _hasVertexTexCoords = src._hasVertexTexCoords;
1886 src._hasVertexTexCoords =
false;
1888 _hasVertexNormals = src._hasVertexNormals;
1889 src._hasVertexNormals =
false;
1891 _vertices = src._vertices;
1892 src._vertices =
nullptr;
1894 _texCoords = src._texCoords;
1895 src._texCoords =
nullptr;
1897 _normals = src._normals;
1898 src._normals =
nullptr;
1900 _tangents = src._tangents;
1901 src._tangents =
nullptr;
1903 _indices = src._indices;
1904 src._indices =
nullptr;
1906 _uniqueIndex = src._uniqueIndex;
1907 src._uniqueIndex = 0;
1909 _numIndices = src._numIndices;
1910 src._numIndices = 0;
1912 _vbods[0] = src._vbods[0];
1913 _vbods[1] = src._vbods[1];
1920 _filename = std::move(src._filename);
1923 _modelType = src._modelType;
1925 _materials = std::move(src._materials);
1926 _materialIndexStartStop = std::move(src._materialIndexStartStop);
1929inline void CSCI441::ModelLoader::_cleanupSelf() {
1931 _vertices =
nullptr;
1937 _tangents =
nullptr;
1939 delete[] _texCoords;
1940 _texCoords =
nullptr;
1945 glDeleteBuffers( 2, _vbods );
1949 glDeleteVertexArrays( 1, &_vaod );
1952 _hasVertexTexCoords =
false;
1953 _hasVertexNormals =
false;
1958 for(
const auto& [name, material] : _materials ) {
1963 _materialIndexStartStop.clear();
1966inline 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 ) {
1968 auto *fullData =
new unsigned char[texWidth*texHeight*4];
1970 for(
int j = 0; j < texHeight; j++) {
1971 for(
int i = 0; i < texWidth; i++) {
1973 fullData[(j*texWidth+i)*4+0] = imageData[(j*texWidth+i)*texChannels+0];
1974 fullData[(j*texWidth+i)*4+1] = imageData[(j*texWidth+i)*texChannels+1];
1975 fullData[(j*texWidth+i)*4+2] = imageData[(j*texWidth+i)*texChannels+2];
1977 fullData[(j*texWidth+i)*4+0] = 1;
1978 fullData[(j*texWidth+i)*4+1] = 1;
1979 fullData[(j*texWidth+i)*4+2] = 1;
1983 fullData[(j*texWidth+i)*4+3] = imageMask[(j*texWidth+i)*maskChannels+0];
1985 fullData[(j*texWidth+i)*4+3] = 1;
1993inline void CSCI441_INTERNAL::flipImageY(
const int texWidth,
const int texHeight,
const int textureChannels,
unsigned char *textureData ) {
1994 for(
int j = 0; j < texHeight / 2; j++ ) {
1995 for(
int i = 0; i < texWidth; i++ ) {
1996 for(
int k = 0; k < textureChannels; k++ ) {
1997 const int top = (j*texWidth + i)*textureChannels + k;
1998 const int bot = ((texHeight-j-1)*texWidth + i)*textureChannels + k;
2000 const unsigned char t = textureData[top];
2001 textureData[top] = textureData[bot];
2002 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:1800
ModelLoader & operator=(const ModelLoader &)=delete
do not allow models to be copied
static void enableAutoGenerateTangents()
Enable auto-generation of vertex tangents.
Definition: ModelLoader.hpp:1809
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:411
GLfloat * getVertices() const
Return the vertex array that makes up the model mesh.
Definition: ModelLoader.hpp:407
GLfloat * getTangents() const
Return the tangent array that corresponds to the model mesh.
Definition: ModelLoader.hpp:409
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:408
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:364
GLuint * getIndices() const
Return the index array that dictates the order to draw the model mesh.
Definition: ModelLoader.hpp:412
GLfloat * getTexCoords() const
Return the texture coordinates array that corresponds to the model mesh.
Definition: ModelLoader.hpp:410
static void disableAutoGenerateTangents()
Disable auto-generation of vertex tangents.
Definition: ModelLoader.hpp:1814
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:406
static void disableAutoGenerateNormals()
Disable auto-generation of vertex normals.
Definition: ModelLoader.hpp:1805
~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