17#ifndef CSCI441_MODEL_LOADER_HPP
18#define CSCI441_MODEL_LOADER_HPP
21#include "LogUtils.hpp"
24#ifdef CSCI441_USE_GLEW
32#ifndef CSCI441_TEXTURE_UTILS_HPP
33 #include <stb_image.h>
65 [[maybe_unused]]
explicit ModelLoader(
const char* filename );
97 bool loadModelFile( std::
string filename,
bool INFO = true,
bool ERRORS = true );
106 [[maybe_unused]]
void setAttributeLocations(GLint positionLocation, GLint normalLocation = -1, GLint texCoordLocation = -1, GLint tangentLocation = -1) const;
118 [[maybe_unused]]
bool draw( GLuint shaderProgramHandle,
119 GLint matDiffLocation = -1, GLint matSpecLocation = -1, GLint matShinLocation = -1, GLint matAmbLocation = -1,
120 GLenum diffuseTexture = GL_TEXTURE0 ) const;
126 [[maybe_unused]] [[nodiscard]] GLuint getNumberOfVertices() const;
132 [[maybe_unused]] [[nodiscard]] GLfloat* getVertices() const;
138 [[maybe_unused]] [[nodiscard]] GLfloat* getNormals() const;
144 [[maybe_unused]] [[nodiscard]] GLfloat* getTangents() const;
150 [[maybe_unused]] [[nodiscard]] GLfloat* getTexCoords() const;
155 [[maybe_unused]] [[nodiscard]] GLuint getNumberOfIndices() const;
161 [[maybe_unused]] [[nodiscard]] GLuint* getIndices() const;
170 [[maybe_unused]] static
void enableAutoGenerateNormals();
177 [[maybe_unused]] static
void disableAutoGenerateNormals();
185 [[maybe_unused]] static
void enableAutoGenerateTangents();
192 [[maybe_unused]] static
void disableAutoGenerateTangents();
196 bool _loadMTLFile( const
char *mtlFilename,
bool INFO,
bool ERRORS );
197 bool _loadOBJFile(
bool INFO,
bool ERRORS );
198 bool _loadOFFFile(
bool INFO,
bool ERRORS );
199 bool _loadPLYFile(
bool INFO,
bool ERRORS );
200 bool _loadSTLFile(
bool INFO,
bool ERRORS );
201 static std::vector<std::
string> _tokenizeString( const std::
string& input, const std::
string& delimiters );
202 void _allocateAttributeArrays(GLuint numVertices, GLuint numIndices);
203 void _bufferData() const;
205 std::
string _filename;
206 CSCI441_INTERNAL::MODEL_TYPE _modelType;
209 GLuint _vbods[2] = {0};
211 glm::vec3* _vertices;
213 glm::vec4* _tangents;
214 glm::vec2* _texCoords;
219 std::map< std::string, CSCI441_INTERNAL::ModelMaterial* > _materials;
220 std::map< std::string, std::vector< std::pair< GLuint, GLuint > > > _materialIndexStartStop;
222 bool _hasVertexTexCoords;
223 bool _hasVertexNormals;
228 static bool sAUTO_GEN_NORMALS;
229 static bool sAUTO_GEN_TANGENTS;
236namespace CSCI441_INTERNAL {
237 unsigned char* createTransparentTexture(
const unsigned char *imageData,
const unsigned char *imageMask,
int texWidth,
int texHeight,
int texChannels,
int maskChannels );
238 [[maybe_unused]]
void flipImageY(
int texWidth,
int texHeight,
int textureChannels,
unsigned char *textureData );
241inline bool CSCI441::ModelLoader::sAUTO_GEN_NORMALS =
false;
242inline bool CSCI441::ModelLoader::sAUTO_GEN_TANGENTS =
false;
245 _modelType(CSCI441_INTERNAL::MODEL_TYPE::UNKNOWN),
254 _hasVertexTexCoords(false),
255 _hasVertexNormals(false)
262 _modelType(CSCI441_INTERNAL::MODEL_TYPE::UNKNOWN),
271 _hasVertexTexCoords(false),
272 _hasVertexNormals(false)
283 _modelType(CSCI441_INTERNAL::MODEL_TYPE::UNKNOWN),
292 _hasVertexTexCoords(
false),
293 _hasVertexNormals(
false)
306inline void CSCI441::ModelLoader::_init() {
307 glGenVertexArrays( 1, &_vaod );
308 glGenBuffers( 2, _vbods );
313 _filename = std::move(filename);
314 if( _filename.find(
".obj") != std::string::npos ) {
315 result = _loadOBJFile( INFO, ERRORS );
316 _modelType = CSCI441_INTERNAL::MODEL_TYPE::OBJ;
318 else if( _filename.find(
".off") != std::string::npos ) {
319 result = _loadOFFFile( INFO, ERRORS );
320 _modelType = CSCI441_INTERNAL::MODEL_TYPE::OFF;
322 else if( _filename.find(
".ply") != std::string::npos ) {
323 result = _loadPLYFile( INFO, ERRORS );
324 _modelType = CSCI441_INTERNAL::MODEL_TYPE::PLY;
326 else if( _filename.find(
".stl") != std::string::npos ) {
327 result = _loadSTLFile( INFO, ERRORS );
328 _modelType = CSCI441_INTERNAL::MODEL_TYPE::STL;
340 glBindVertexArray( _vaod );
341 glBindBuffer( GL_ARRAY_BUFFER, _vbods[0] );
343 if (positionLocation >= 0) {
344 glEnableVertexAttribArray( positionLocation );
345 glVertexAttribPointer( positionLocation, 3, GL_FLOAT, GL_FALSE, 0, (
void*)
nullptr );
348 if (normalLocation >= 0) {
349 glEnableVertexAttribArray( normalLocation );
350 glVertexAttribPointer( normalLocation, 3, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex) );
353 if (texCoordLocation >= 0) {
354 glEnableVertexAttribArray( texCoordLocation );
355 glVertexAttribPointer( texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex * 2) );
358 if (tangentLocation >= 0) {
359 glEnableVertexAttribArray( tangentLocation );
360 glVertexAttribPointer( tangentLocation, 4, GL_FLOAT, GL_FALSE, 0, (
void*)(
sizeof(glm::vec3) * _uniqueIndex * 2 +
sizeof(glm::vec2) * _uniqueIndex) );
366 const GLint matDiffLocation,
const GLint matSpecLocation,
const GLint matShinLocation,
const GLint matAmbLocation,
367 const GLenum diffuseTexture )
const {
368 glBindVertexArray( _vaod );
371 if( _modelType == CSCI441_INTERNAL::MODEL_TYPE::OBJ ) {
372 for(
const auto & materialIter : _materialIndexStartStop) {
373 auto materialName = materialIter.first;
374 auto indexStartStop = materialIter.second;
376 CSCI441_INTERNAL::ModelMaterial* material =
nullptr;
377 if( _materials.find( materialName ) != _materials.end() )
378 material = _materials.find( materialName )->second;
380 for(
const auto &[start, end] : indexStartStop) {
381 const GLsizei length =
static_cast<GLsizei
>(end - start) + 1;
385 if( material !=
nullptr ) {
386 glProgramUniform4fv( shaderProgramHandle, matAmbLocation, 1, &material->ambient[0] );
387 glProgramUniform4fv( shaderProgramHandle, matDiffLocation, 1, &material->diffuse[0] );
388 glProgramUniform4fv( shaderProgramHandle, matSpecLocation, 1, &material->specular[0] );
389 glProgramUniform1f( shaderProgramHandle, matShinLocation, material->shininess );
391 if( material->map_Kd != -1 ) {
392 glActiveTexture( diffuseTexture );
393 glBindTexture( GL_TEXTURE_2D, material->map_Kd );
397 glDrawElements( GL_TRIANGLES, length, GL_UNSIGNED_INT, (
void*)(
sizeof(GLuint)*start) );
401 glDrawElements( GL_TRIANGLES,
static_cast<GLint
>(_numIndices), GL_UNSIGNED_INT, (
void*)
nullptr );
416inline bool CSCI441::ModelLoader::_loadOBJFile(
const bool INFO,
const bool ERRORS ) {
420 if( _filename.find(
'/') != std::string::npos ) {
421 path = _filename.substr( 0, _filename.find_last_of(
'/')+1 );
426 if ( INFO )
CSCI441::LogUtils::log(
"[.obj]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=- \n", _filename.c_str() );
431 std::ifstream in( _filename );
432 if( !in.is_open() ) {
434 if ( INFO )
CSCI441::LogUtils::log(
"[.obj]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=- \n", _filename.c_str() );
438 GLuint numObjects = 0, numGroups = 0;
439 GLuint numVertices = 0, numTexCoords = 0, numNormals = 0;
440 GLuint numFaces = 0, numTriangles = 0;
441 glm::vec3 minDimension = {999999.f, 999999.f, 999999.f};
442 glm::vec3 maxDimension = { -999999.f, -999999.f, -999999.f };
445 std::map<std::string, GLuint> uniqueCounts;
448 int progressCounter = 0;
450 while( getline( in, line ) ) {
451 if( line.length() > 1 && line.at(0) ==
'\t' )
452 line = line.substr( 1 );
453 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
455 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
456 if( tokens.empty() )
continue;
459 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
461 }
else if( tokens[0] ==
"o" ) {
463 }
else if( tokens[0] ==
"g" ) {
465 }
else if( tokens[0] ==
"mtllib" ) {
466 std::string mtlFilename = path + tokens[1];
467 _loadMTLFile( mtlFilename.c_str(), INFO, ERRORS );
468 }
else if( tokens[0] ==
"v" ) {
471 glm::vec3 pos = { strtof( tokens[1].c_str(),
nullptr ),
472 strtof( tokens[2].c_str(),
nullptr ),
473 strtof( tokens[3].c_str(),
nullptr ) };
475 if( pos.x < minDimension.x ) minDimension.x = pos.x;
476 if( pos.x > maxDimension.x ) maxDimension.x = pos.x;
477 if( pos.y < minDimension.y ) minDimension.y = pos.y;
478 if( pos.y > maxDimension.y ) maxDimension.y = pos.y;
479 if( pos.z < minDimension.z ) minDimension.z = pos.z;
480 if( pos.z > maxDimension.z ) maxDimension.z = pos.z;
481 }
else if( tokens[0] ==
"vn" ) {
483 }
else if( tokens[0] ==
"vt" ) {
485 }
else if( tokens[0] ==
"f" ) {
488 std::vector<std::string> faceTokens = _tokenizeString(line,
" ");
490 for (GLuint i = 1; i < faceTokens.size(); i++) {
492 std::vector<std::string> groupTokens = _tokenizeString(faceTokens[i],
"/");
494 for (
char j: faceTokens[i]) {
495 if (j ==
'/') numSlashes++;
498 std::stringstream currentFaceTokenStream;
500 auto signedVertexIndex =
static_cast<GLint
>(strtol(groupTokens[0].c_str(),
nullptr, 10));
501 GLuint vertexIndex = signedVertexIndex;
502 if (signedVertexIndex < 0) vertexIndex = numVertices + signedVertexIndex + 1;
504 currentFaceTokenStream << vertexIndex;
507 if (groupTokens.size() == 2 && numSlashes == 1) {
508 _hasVertexTexCoords =
true;
510 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(groupTokens[1].c_str(),
nullptr, 10));
511 GLuint texCoordIndex = signedTexCoordIndex;
512 if (signedTexCoordIndex < 0) texCoordIndex = numTexCoords + signedTexCoordIndex + 1;
514 currentFaceTokenStream <<
"/" << texCoordIndex;
515 }
else if (groupTokens.size() == 2 && numSlashes == 2) {
516 _hasVertexNormals =
true;
518 auto signedNormalIndex =
static_cast<GLint
>(strtol(groupTokens[1].c_str(),
nullptr, 10));
519 GLuint normalIndex = signedNormalIndex;
520 if (signedNormalIndex < 0) normalIndex = numNormals + signedNormalIndex + 1;
522 currentFaceTokenStream <<
"//" << normalIndex;
523 }
else if (groupTokens.size() == 3) {
524 _hasVertexTexCoords =
true;
525 _hasVertexNormals =
true;
527 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(groupTokens[1].c_str(),
nullptr, 10));
528 GLuint texCoordIndex = signedTexCoordIndex;
529 if (signedTexCoordIndex < 0) texCoordIndex = numTexCoords + signedTexCoordIndex + 1;
531 auto signedNormalIndex =
static_cast<GLint
>(strtol(groupTokens[2].c_str(),
nullptr, 10));
532 GLuint normalIndex = signedNormalIndex;
533 if (signedNormalIndex < 0) normalIndex = numNormals + signedNormalIndex + 1;
535 currentFaceTokenStream <<
"/" << texCoordIndex <<
"/" << normalIndex;
536 }
else if (groupTokens.size() != 1) {
541 std::string processedFaceToken = currentFaceTokenStream.str();
542 if (uniqueCounts.find(processedFaceToken) == uniqueCounts.end()) {
543 uniqueCounts.insert(std::pair<std::string, long int>(processedFaceToken, _uniqueIndex));
548 numTriangles += (faceTokens.size() - 1 - 3 + 1);
551 }
else if( tokens[0] ==
"usemtl" ) {
559 if( progressCounter % 5000 == 0 ) {
561 switch( progressCounter ) {
570 if( progressCounter == 20000 )
581 CSCI441::LogUtils::log(
"[.obj]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, numNormals, numTexCoords );
586 glm::vec3 sizeDimensions = maxDimension - minDimension;
587 CSCI441::LogUtils::log(
"[.obj]: Dimensions:\t(%f, %f, %f)\n", sizeDimensions.x, sizeDimensions.y, sizeDimensions.z );
590 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
591 if (INFO && !_hasVertexNormals)
592 CSCI441::LogUtils::log(
"[.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" );
593 if (INFO && sAUTO_GEN_TANGENTS)
CSCI441::LogUtils::log(
"[.obj]: Vertex tangents will be autogenerated\n" );
594 _allocateAttributeArrays(_uniqueIndex, numTriangles*3);
596 if (INFO)
CSCI441::LogUtils::log(
"[.obj]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
597 if (INFO && sAUTO_GEN_TANGENTS)
CSCI441::LogUtils::log(
"[.obj]: Vertex tangents will be autogenerated\n" );
598 _allocateAttributeArrays(numTriangles * 3, numTriangles*3);
601 auto objVertices =
new glm::vec3[numVertices];
602 auto objNormals =
new glm::vec3[numNormals];
603 auto objTexCoords =
new glm::vec2[numTexCoords];
605 std::vector<glm::vec3> verticesTemps;
606 std::vector<glm::vec2> texCoordsTemp;
610 uniqueCounts.clear();
614 in.open( _filename );
616 GLuint verticesSeen = 0, texCoordsSeen = 0, normalsSeen = 0, indicesSeen = 0;
617 GLuint uniqueNumVertices = 0;
619 std::string currentMaterial =
"default";
620 _materialIndexStartStop.insert( std::pair< std::string, std::vector< std::pair< GLuint, GLuint > > >( currentMaterial, std::vector< std::pair< GLuint, GLuint > >(1) ) );
621 _materialIndexStartStop.find( currentMaterial )->second.back().first = indicesSeen;
623 while( getline( in, line ) ) {
624 if( line.length() > 1 && line.at(0) ==
'\t' )
625 line = line.substr( 1 );
626 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
628 auto tokens = _tokenizeString( line,
" \t" );
629 if( tokens.empty() )
continue;
632 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0
635 || tokens[0] ==
"mtllib"
639 }
else if( tokens[0] ==
"usemtl" ) {
640 if( currentMaterial ==
"default" && indicesSeen == 0 ) {
641 _materialIndexStartStop.clear();
643 _materialIndexStartStop.find( currentMaterial )->second.back().second = indicesSeen - 1;
645 currentMaterial = tokens[1];
646 if( _materialIndexStartStop.find( currentMaterial ) == _materialIndexStartStop.end() ) {
647 _materialIndexStartStop.insert( std::pair< std::string, std::vector< std::pair< GLuint, GLuint > > >( currentMaterial, std::vector< std::pair< GLuint, GLuint > >(1) ) );
648 _materialIndexStartStop.find( currentMaterial )->second.back().first = indicesSeen;
650 _materialIndexStartStop.find( currentMaterial )->second.emplace_back( indicesSeen, -1 );
652 }
else if( tokens[0] ==
"v" ) {
653 objVertices[verticesSeen++] = glm::vec3(strtof(tokens[1].c_str(),
nullptr ),
654 strtof( tokens[2].c_str(),
nullptr ),
655 strtof( tokens[3].c_str(),
nullptr ) );
656 }
else if( tokens[0] ==
"vn" ) {
657 objNormals[normalsSeen++] = glm::vec3(strtof(tokens[1].c_str(),
nullptr ),
658 strtof( tokens[2].c_str(),
nullptr ),
659 strtof( tokens[3].c_str(),
nullptr ));
660 }
else if( tokens[0] ==
"vt" ) {
661 objTexCoords[texCoordsSeen++] = glm::vec2(strtof(tokens[1].c_str(),
nullptr ),
662 strtof( tokens[2].c_str(),
nullptr ));
663 }
else if( tokens[0] ==
"f" ) {
664 std::vector<std::string> processedFaceTokens;
666 bool faceHasVertexNormals =
false;
667 bool faceHasTextureCoordinates =
false;
669 for(GLuint i = 1; i < tokens.size(); i++) {
671 auto vertexAttributeTokens = _tokenizeString(tokens[i],
"/");
672 int numAttributeSlashes = 0;
673 for(
char j : tokens[i]) {
674 if(j ==
'/') numAttributeSlashes++;
677 std::stringstream currentFaceTokenStream;
679 auto signedVertexIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[0].c_str(),
nullptr, 10));
680 GLuint vertexIndex = signedVertexIndex;
681 if(signedVertexIndex < 0) vertexIndex = verticesSeen + signedVertexIndex + 1;
682 currentFaceTokenStream << vertexIndex;
684 GLuint texCoordIndex = 0, normalIndex = 0;
687 if(vertexAttributeTokens.size() == 2 && numAttributeSlashes == 1) {
689 _hasVertexTexCoords =
true;
690 faceHasTextureCoordinates =
true;
692 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10));
693 texCoordIndex = signedTexCoordIndex;
694 if(signedTexCoordIndex < 0) texCoordIndex = texCoordsSeen + signedTexCoordIndex + 1;
695 currentFaceTokenStream <<
"/" << texCoordIndex;
696 }
else if(vertexAttributeTokens.size() == 2 && numAttributeSlashes == 2) {
698 _hasVertexNormals =
true;
699 faceHasVertexNormals =
true;
701 auto signedNormalIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10));
702 normalIndex = signedNormalIndex;
703 if(signedNormalIndex < 0) normalIndex = normalsSeen + signedNormalIndex + 1;
704 currentFaceTokenStream <<
"//" << normalIndex;
705 }
else if(vertexAttributeTokens.size() == 3) {
707 _hasVertexTexCoords =
true;
708 faceHasTextureCoordinates =
true;
709 _hasVertexNormals =
true;
710 faceHasVertexNormals =
true;
712 auto signedTexCoordIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[1].c_str(),
nullptr, 10));
713 texCoordIndex = signedTexCoordIndex;
714 if(signedTexCoordIndex < 0) texCoordIndex = texCoordsSeen + signedTexCoordIndex + 1;
716 auto signedNormalIndex =
static_cast<GLint
>(strtol(vertexAttributeTokens[2].c_str(),
nullptr, 10));
717 normalIndex = signedNormalIndex;
718 if(signedNormalIndex < 0) normalIndex = normalsSeen + signedNormalIndex + 1;
720 currentFaceTokenStream <<
"/" << texCoordIndex <<
"/" << normalIndex;
721 }
else if(vertexAttributeTokens.size() != 1) {
726 auto processedFaceToken = currentFaceTokenStream.str();
727 processedFaceTokens.push_back(processedFaceToken);
730 if( uniqueCounts.find( processedFaceToken ) == uniqueCounts.end() ) {
732 uniqueCounts.insert( std::pair<std::string,GLuint>(processedFaceToken, uniqueNumVertices) );
734 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
735 _vertices[ _uniqueIndex ] = objVertices[ vertexIndex - 1 ];
736 if(faceHasTextureCoordinates && texCoordIndex != 0) { _texCoords[ _uniqueIndex ] = objTexCoords[ texCoordIndex - 1 ]; }
737 if(faceHasVertexNormals && normalIndex != 0) _normals[ _uniqueIndex ] = objNormals[ normalIndex - 1 ];
740 verticesTemps.push_back( objVertices[ vertexIndex - 1 ] );
741 if(faceHasTextureCoordinates && texCoordIndex != 0) { texCoordsTemp.push_back( objTexCoords[ texCoordIndex - 1 ] ); }
743 if( (vertexAttributeTokens.size() == 2 && numAttributeSlashes == 2)
744 || (vertexAttributeTokens.size() == 3) ) {
746 if (ERRORS)
CSCI441::LogUtils::logError(
"[.obj]: [ERROR]: no vertex normals were specified, should not be trying to access values\n" );
753 for(GLuint i = 1; i < processedFaceTokens.size()-1; i++) {
754 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
755 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[0] )->second;
756 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[i] )->second;
757 _indices[ indicesSeen++ ] = uniqueCounts.find( processedFaceTokens[i+1] )->second;
759 if (sAUTO_GEN_TANGENTS) {
760 glm::vec3 a = _vertices[ _indices[ indicesSeen - 3 ] ];
761 glm::vec3 b = _vertices[ _indices[ indicesSeen - 2 ] ];
762 glm::vec3 c = _vertices[ _indices[ indicesSeen - 1 ] ];
764 glm::vec3 ab = b - a;
765 glm::vec3 bc = c - b;
766 glm::vec3 ca = a - c;
768 _tangents[ _indices[ indicesSeen - 3 ] ] = glm::vec4(ab, 1.0f);
769 _tangents[ _indices[ indicesSeen - 2 ] ] = glm::vec4(bc, 1.0f);
770 _tangents[ _indices[ indicesSeen - 1 ] ] = glm::vec4(ca, 1.0f);
775 GLuint aI = uniqueCounts.find( processedFaceTokens[0] )->second;
776 GLuint bI = uniqueCounts.find( processedFaceTokens[i] )->second;
777 GLuint cI = uniqueCounts.find( processedFaceTokens[i+1] )->second;
779 glm::vec3 a = verticesTemps[aI];
780 glm::vec3 b = verticesTemps[bI];
781 glm::vec3 c = verticesTemps[cI];
783 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
784 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
785 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
787 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
788 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
789 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
791 _vertices[ _uniqueIndex ] = a;
792 _normals[ _uniqueIndex ] = aN;
793 if (sAUTO_GEN_TANGENTS) _tangents[ _uniqueIndex ] = glm::vec4(ab, 1.0f);
794 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ aI ]; }
795 _indices[ _numIndices++ ] = _uniqueIndex++;
799 _vertices[ _uniqueIndex ] = b;
800 _normals[ _uniqueIndex ] = bN;
801 if (sAUTO_GEN_TANGENTS) _tangents[ _uniqueIndex ] = glm::vec4(bc, 1.0f);
802 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ bI ]; }
803 _indices[ _numIndices++ ] = _uniqueIndex++;
807 _vertices[ _uniqueIndex ] = c;
808 _normals[ _uniqueIndex ] = cN;
809 if (sAUTO_GEN_TANGENTS) _tangents[ _uniqueIndex ] = glm::vec4(ca, 1.0f);
810 if( faceHasTextureCoordinates && _hasVertexTexCoords ) { _texCoords[ _uniqueIndex ] = texCoordsTemp[ cI ]; }
811 _indices[ _numIndices++ ] = _uniqueIndex++;
820 if( progressCounter % 5000 == 0 ) {
822 switch( progressCounter ) {
831 if( progressCounter == 20000 )
843 _materialIndexStartStop.find( currentMaterial )->second.back().second = indicesSeen - 1;
848 double seconds = difftime( end, start );
858inline bool CSCI441::ModelLoader::_loadMTLFile(
const char* mtlFilename,
const bool INFO,
const bool ERRORS ) {
861 if (INFO)
CSCI441::LogUtils::log(
"[.mtl]: -*-*-*-*-*-*-*- BEGIN %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
865 if( _filename.find(
'/') != std::string::npos ) {
866 path = _filename.substr( 0, _filename.find_last_of(
'/')+1 );
872 in.open( mtlFilename );
873 if( !in.is_open() ) {
874 in.open( mtlFilename );
875 if( !in.is_open() ) {
877 if ( INFO )
CSCI441::LogUtils::log(
"[.mtl]: -*-*-*-*-*-*-*- END %s Info -*-*-*-*-*-*-*-\n", mtlFilename );
882 CSCI441_INTERNAL::ModelMaterial* currentMaterial =
nullptr;
883 std::string materialName;
885 unsigned char *textureData =
nullptr;
886 unsigned char *maskData =
nullptr;
887 unsigned char *fullData;
888 int texWidth, texHeight, textureChannels = 1, maskChannels = 1;
889 GLuint textureHandle = 0;
891 std::map< std::string, GLuint > imageHandles;
893 int numMaterials = 0;
895 while( getline( in, line ) ) {
896 if( line.length() > 1 && line.at(0) ==
'\t' )
897 line = line.substr( 1 );
898 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
900 std::vector< std::string > tokens = _tokenizeString( line,
" /" );
901 if( tokens.empty() )
continue;
904 if( tokens[0] ==
"#" ) {
906 }
else if( tokens[0] ==
"newmtl" ) {
908 currentMaterial =
new CSCI441_INTERNAL::ModelMaterial();
909 materialName = tokens[1];
910 _materials.insert( std::pair<std::string, CSCI441_INTERNAL::ModelMaterial*>( materialName, currentMaterial ) );
913 textureData =
nullptr;
919 }
else if( tokens[0] ==
"Ka" ) {
920 currentMaterial->ambient[0] = strtof( tokens[1].c_str(),
nullptr );
921 currentMaterial->ambient[1] = strtof( tokens[2].c_str(),
nullptr );
922 currentMaterial->ambient[2] = strtof( tokens[3].c_str(),
nullptr );
923 }
else if( tokens[0] ==
"Kd" ) {
924 currentMaterial->diffuse[0] = strtof( tokens[1].c_str(),
nullptr );
925 currentMaterial->diffuse[1] = strtof( tokens[2].c_str(),
nullptr );
926 currentMaterial->diffuse[2] = strtof( tokens[3].c_str(),
nullptr );
927 }
else if( tokens[0] ==
"Ks" ) {
928 currentMaterial->specular[0] = strtof( tokens[1].c_str(),
nullptr );
929 currentMaterial->specular[1] = strtof( tokens[2].c_str(),
nullptr );
930 currentMaterial->specular[2] = strtof( tokens[3].c_str(),
nullptr );
931 }
else if( tokens[0] ==
"Ke" ) {
932 currentMaterial->emissive[0] = strtof( tokens[1].c_str(),
nullptr );
933 currentMaterial->emissive[1] = strtof( tokens[2].c_str(),
nullptr );
934 currentMaterial->emissive[2] = strtof( tokens[3].c_str(),
nullptr );
935 }
else if( tokens[0] ==
"Ns" ) {
936 currentMaterial->shininess = strtof( tokens[1].c_str(),
nullptr );
937 }
else if( tokens[0] ==
"Tr"
938 || tokens[0] ==
"d" ) {
939 currentMaterial->ambient[3] = strtof( tokens[1].c_str(),
nullptr );
940 currentMaterial->diffuse[3] = strtof( tokens[1].c_str(),
nullptr );
941 currentMaterial->specular[3] = strtof( tokens[1].c_str(),
nullptr );
942 }
else if( tokens[0] ==
"illum" ) {
944 }
else if( tokens[0] ==
"map_Kd" ) {
945 if( imageHandles.find( tokens[1] ) != imageHandles.end() ) {
947 currentMaterial->map_Kd = imageHandles.find( tokens[1] )->second;
949 stbi_set_flip_vertically_on_load(
true);
950 textureData = stbi_load( tokens[1].c_str(), &texWidth, &texHeight, &textureChannels, 0 );
952 std::string folderName = path + tokens[1];
953 textureData = stbi_load( folderName.c_str(), &texWidth, &texHeight, &textureChannels, 0 );
959 if (INFO)
CSCI441::LogUtils::log(
"[.mtl]: TextureMap:\t%s\tSize: %dx%d\tColors: %d\n", tokens[1].c_str(), texWidth, texHeight, textureChannels );
961 if( maskData ==
nullptr ) {
962 if( textureHandle == 0 ) {
963 glGenTextures( 1, &textureHandle );
964 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
967 glBindTexture( GL_TEXTURE_2D, textureHandle );
969 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
970 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
972 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
973 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
975 GLint colorSpace = GL_RGB;
976 if( textureChannels == 4 )
977 colorSpace = GL_RGBA;
978 glTexImage2D( GL_TEXTURE_2D, 0, colorSpace, texWidth, texHeight, 0, colorSpace, GL_UNSIGNED_BYTE, textureData );
980 currentMaterial->map_Kd = textureHandle;
982 fullData = CSCI441_INTERNAL::createTransparentTexture( textureData, maskData, texWidth, texHeight, textureChannels, maskChannels );
984 if( textureHandle == 0 ) {
985 glGenTextures( 1, &textureHandle );
986 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
989 glBindTexture( GL_TEXTURE_2D, textureHandle );
991 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
992 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
994 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
995 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
997 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullData );
1001 currentMaterial->map_Kd = textureHandle;
1005 }
else if( tokens[0] ==
"map_d" ) {
1006 if( imageHandles.find( tokens[1] ) != imageHandles.end() ) {
1008 currentMaterial->map_d = imageHandles.find( tokens[1] )->second;
1010 stbi_set_flip_vertically_on_load(
true);
1011 maskData = stbi_load( tokens[1].c_str(), &texWidth, &texHeight, &textureChannels, 0 );
1012 if( !textureData ) {
1013 std::string folderName = path + tokens[1];
1014 maskData = stbi_load( folderName.c_str(), &texWidth, &texHeight, &textureChannels, 0 );
1020 if (INFO)
CSCI441::LogUtils::log(
"[.mtl]: AlphaMap: \t%s\tSize: %dx%d\tColors: %d\n", tokens[1].c_str(), texWidth, texHeight, maskChannels );
1022 if( textureData !=
nullptr ) {
1023 fullData = CSCI441_INTERNAL::createTransparentTexture( textureData, maskData, texWidth, texHeight, textureChannels, maskChannels );
1025 if( textureHandle == 0 ) {
1026 glGenTextures( 1, &textureHandle );
1027 imageHandles.insert( std::pair<std::string, GLuint>( tokens[1], textureHandle ) );
1030 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1031 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1033 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1034 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1036 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullData );
1040 currentMaterial->map_Kd = textureHandle;
1044 }
else if( tokens[0] ==
"map_Ka" ) {
1046 }
else if( tokens[0] ==
"map_Ks" ) {
1048 }
else if( tokens[0] ==
"map_Ns" ) {
1050 }
else if( tokens[0] ==
"Ni" ) {
1052 }
else if( tokens[0] ==
"Tf" ) {
1054 }
else if( tokens[0] ==
"bump"
1055 || tokens[0] ==
"map_bump" ) {
1072inline bool CSCI441::ModelLoader::_loadOFFFile(
const bool INFO,
const bool ERRORS ) {
1075 if (INFO )
CSCI441::LogUtils::log(
"[.off]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1080 std::ifstream in( _filename );
1081 if( !in.is_open() ) {
1083 if ( INFO )
CSCI441::LogUtils::log(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1087 GLuint numVertices = 0, numFaces = 0, numTriangles = 0;
1088 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1091 enum OFF_FILE_STATE { HEADER, VERTICES, FACES, DONE };
1093 OFF_FILE_STATE fileState = HEADER;
1095 GLuint vSeen = 0, fSeen = 0;
1097 while( getline( in, line ) ) {
1098 if( line.length() > 1 && line.at(0) ==
'\t' )
1099 line = line.substr( 1 );
1100 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1102 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1103 if( tokens.empty() )
continue;
1106 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
1107 }
else if( fileState == HEADER ) {
1108 if( tokens[0] ==
"OFF" ) {
1110 if( tokens.size() != 3 ) {
1111 if (ERRORS)
CSCI441::LogUtils::logError(
"[.off]: [ERROR]: Malformed OFF file. # vertices, faces, edges not properly specified\n" );
1112 if ( INFO )
CSCI441::LogUtils::log(
"[.off]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1117 numVertices =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1118 numFaces =
static_cast<GLuint
>(strtol(tokens[1].c_str(),
nullptr, 10));
1123 fileState = VERTICES;
1125 }
else if( fileState == VERTICES ) {
1127 GLfloat x = strtof( tokens[0].c_str(),
nullptr ),
1128 y = strtof( tokens[1].c_str(),
nullptr ),
1129 z = strtof( tokens[2].c_str(),
nullptr );
1131 if( x < minX ) minX = x;
1132 if( x > maxX ) maxX = x;
1133 if( y < minY ) minY = y;
1134 if( y > maxY ) maxY = y;
1135 if( z < minZ ) minZ = z;
1136 if( z > maxZ ) maxZ = z;
1139 if( vSeen == numVertices )
1141 }
else if( fileState == FACES ) {
1142 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1144 numTriangles += numberOfVerticesInFace - 3 + 1;
1146 if( fSeen == numFaces )
1161 CSCI441::LogUtils::log(
"[.off]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1164 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1165 if (INFO && !_hasVertexNormals)
1166 CSCI441::LogUtils::log(
"[.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" );
1167 _allocateAttributeArrays(numVertices, numTriangles*3);
1169 if (INFO)
CSCI441::LogUtils::log(
"[.off]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
1170 _allocateAttributeArrays(numTriangles*3, numTriangles*3);
1173 std::vector<glm::vec3> verticesTemp;
1177 in.open( _filename );
1185 int progressCounter = 0;
1187 while( getline( in, line ) ) {
1188 if( line.length() > 1 && line.at(0) ==
'\t' )
1189 line = line.substr( 1 );
1190 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1192 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1193 if( tokens.empty() )
continue;
1196 if( tokens[0] ==
"#" || tokens[0].find_first_of(
'#') == 0 ) {
1198 }
else if( fileState == HEADER ) {
1199 if( tokens[0] ==
"OFF" ) {
1203 fileState = VERTICES;
1205 }
else if( fileState == VERTICES ) {
1207 glm::vec3 pos(strtof( tokens[0].c_str(),
nullptr ),
1208 strtof( tokens[1].c_str(),
nullptr ),
1209 strtof( tokens[2].c_str(),
nullptr ) );
1212 if( tokens.size() == 6 || tokens.size() == 7 ) {
1220 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1221 _vertices[ _uniqueIndex ] = pos;
1224 verticesTemp.push_back(pos);
1228 if( _uniqueIndex == numVertices || vSeen == numVertices )
1230 }
else if( fileState == FACES ) {
1231 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1234 for(GLuint i = 2; i <= numberOfVerticesInFace - 1; i++) {
1235 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1236 auto fanRoot = strtol( tokens[1].c_str(),
nullptr, 10 );
1237 auto fanA = strtol( tokens[i].c_str(),
nullptr, 10 );
1238 auto fanB = strtol( tokens[i+1].c_str(),
nullptr, 10 );
1240 if( fanRoot < 0 ) fanRoot = numVertices + fanRoot + 1;
1241 if( fanA < 0 ) fanA = numVertices + fanA + 1;
1242 if( fanB < 0 ) fanB = numVertices + fanB + 1;
1245 _indices[ _numIndices++ ] = fanRoot;
1246 _indices[ _numIndices++ ] = fanA;
1247 _indices[ _numIndices++ ] = fanB;
1249 auto aI = strtol( tokens[1].c_str(),
nullptr, 10 );
1250 auto bI = strtol( tokens[i].c_str(),
nullptr, 10 );
1251 auto cI = strtol( tokens[i+1].c_str(),
nullptr, 10 );
1253 if( aI < 0 ) aI = numVertices + aI + 1;
1254 if( bI < 0 ) bI = numVertices + bI + 1;
1255 if( cI < 0 ) cI = numVertices + cI + 1;
1257 glm::vec3 a = verticesTemp[aI];
1258 glm::vec3 b = verticesTemp[bI];
1259 glm::vec3 c = verticesTemp[cI];
1261 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
1262 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
1263 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
1265 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
1266 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
1267 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
1269 _vertices[ _uniqueIndex ] = a;
1270 _normals[ _uniqueIndex ] = aN;
1271 _indices[ _numIndices++ ] = _uniqueIndex++;
1273 _vertices[ _uniqueIndex ] = b;
1274 _normals[ _uniqueIndex ] = bN;
1275 _indices[ _numIndices++ ] = _uniqueIndex++;
1277 _vertices[ _uniqueIndex ] = c;
1278 _normals[ _uniqueIndex ] = cN;
1279 _indices[ _numIndices++ ] = _uniqueIndex++;
1304 if( progressCounter % 5000 == 0 ) {
1306 switch( progressCounter ) {
1315 if( progressCounter == 20000 )
1316 progressCounter = 0;
1324 double seconds = difftime( end, start );
1336inline bool CSCI441::ModelLoader::_loadPLYFile(
const bool INFO,
const bool ERRORS ) {
1339 if (INFO )
CSCI441::LogUtils::log(
"[.ply]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1344 std::ifstream in( _filename );
1345 if( !in.is_open() ) {
1347 if ( INFO )
CSCI441::LogUtils::log(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1351 GLuint numVertices = 0, numFaces = 0, numTriangles = 0, numMaterials = 0;
1352 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1355 enum PLY_FILE_STATE { HEADER, VERTICES, FACES, MATERIALS };
1356 enum PLY_ELEMENT_TYPE { NONE, VERTEX, FACE, MATERIAL };
1358 PLY_FILE_STATE fileState = HEADER;
1359 PLY_ELEMENT_TYPE elemType = NONE;
1361 GLuint progressCounter = 0;
1364 while( getline( in, line ) ) {
1365 if( line.length() > 1 && line.at(0) ==
'\t' )
1366 line = line.substr( 1 );
1367 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1369 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1371 if( tokens.empty() )
continue;
1374 if( tokens[0] ==
"comment" ) {
1375 }
else if( fileState == HEADER ) {
1376 if( tokens[0] ==
"ply" ) {
1377 }
else if( tokens[0] ==
"format" ) {
1378 if( tokens[1] !=
"ascii" ) {
1380 if ( INFO )
CSCI441::LogUtils::log(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1384 }
else if( tokens[0] ==
"element" ) {
1385 if( tokens[1] ==
"vertex" ) {
1386 numVertices =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1388 }
else if( tokens[1] ==
"face" ) {
1389 numFaces =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1391 }
else if( tokens[1] ==
"edge" ) {
1393 }
else if( tokens[1] ==
"material" ) {
1394 numMaterials =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1395 elemType = MATERIAL;
1399 }
else if( tokens[0] ==
"property" ) {
1400 if( elemType == VERTEX ) {
1402 }
else if( elemType == FACE ) {
1404 }
else if( elemType == MATERIAL ) {
1407 }
else if( tokens[0] ==
"end_header" ) {
1408 fileState = VERTICES;
1410 }
else if( fileState == VERTICES ) {
1412 auto x = (GLfloat) strtof( tokens[0].c_str(), nullptr ),
1413 y = (GLfloat) strtof( tokens[1].c_str(),
nullptr ),
1414 z = (GLfloat) strtof( tokens[2].c_str(), nullptr );
1416 if( x < minX ) minX = x;
1417 if( x > maxX ) maxX = x;
1418 if( y < minY ) minY = y;
1419 if( y > maxY ) maxY = y;
1420 if( z < minZ ) minZ = z;
1421 if( z > maxZ ) maxZ = z;
1425 if( vSeen == numVertices )
1427 }
else if( fileState == FACES ) {
1428 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1429 numTriangles += numberOfVerticesInFace - 3 + 1;
1436 if( progressCounter % 5000 == 0 ) {
1438 switch( progressCounter ) {
1447 if( progressCounter == 20000 )
1448 progressCounter = 0;
1460 CSCI441::LogUtils::log(
"[.ply]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1463 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1464 if (INFO && !_hasVertexNormals)
1465 CSCI441::LogUtils::log(
"[.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" );
1466 _allocateAttributeArrays(numVertices, numTriangles*3);
1468 if (INFO)
CSCI441::LogUtils::log(
"[.ply]: No vertex normals exist on model, vertex normals will be autogenerated\n" );
1469 _allocateAttributeArrays(numTriangles*3, numTriangles*3);
1474 std::vector<glm::vec3> verticesTemp;
1476 in.open( _filename );
1484 progressCounter = 0;
1487 while( getline( in, line ) ) {
1488 if( line.length() > 1 && line.at(0) ==
'\t' )
1489 line = line.substr( 1 );
1490 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1492 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1494 if( tokens.empty() )
continue;
1497 if( tokens[0] ==
"comment" ) {
1498 }
else if( fileState == HEADER ) {
1499 if( tokens[0] ==
"ply" ) {
1500 }
else if( tokens[0] ==
"format" ) {
1501 if( tokens[1] !=
"ascii" ) {
1503 if ( INFO )
CSCI441::LogUtils::log(
"[.ply]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1507 }
else if( tokens[0] ==
"element" ) {
1508 if( tokens[1] ==
"vertex" ) {
1509 numVertices =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1511 }
else if( tokens[1] ==
"face" ) {
1512 numFaces =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1514 }
else if( tokens[1] ==
"edge" ) {
1516 }
else if( tokens[1] ==
"material" ) {
1517 numMaterials =
static_cast<GLuint
>(strtol(tokens[2].c_str(),
nullptr, 10));
1518 elemType = MATERIAL;
1522 }
else if( tokens[0] ==
"property" ) {
1523 if( elemType == VERTEX ) {
1525 }
else if( elemType == FACE ) {
1527 }
else if( elemType == MATERIAL ) {
1530 }
else if( tokens[0] ==
"end_header" ) {
1531 fileState = VERTICES;
1533 }
else if( fileState == VERTICES ) {
1535 glm::vec3 pos(strtof( tokens[0].c_str(),
nullptr ),
1536 strtof( tokens[1].c_str(),
nullptr ),
1537 strtof( tokens[2].c_str(),
nullptr ) );
1539 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1540 _vertices[ _uniqueIndex ] = pos;
1543 verticesTemp.push_back(pos );
1547 if( _uniqueIndex == numVertices || vSeen == numVertices )
1549 }
else if( fileState == FACES ) {
1550 auto numberOfVerticesInFace =
static_cast<GLuint
>(strtol(tokens[0].c_str(),
nullptr, 10));
1552 for( GLuint i = 2; i <= numberOfVerticesInFace - 1; i++ ) {
1553 if( _hasVertexNormals || !sAUTO_GEN_NORMALS ) {
1554 _indices[ _numIndices++ ] =
static_cast<GLuint
>(strtol(tokens[1].c_str(),
nullptr, 10));
1555 _indices[ _numIndices++ ] =
static_cast<GLuint
>(strtol(tokens[i].c_str(),
nullptr, 10));
1556 _indices[ _numIndices++ ] =
static_cast<GLuint
>(strtol(tokens[i + 1].c_str(),
nullptr, 10));
1558 auto aI =
static_cast<GLuint
>(strtol(tokens[1].c_str(),
nullptr, 10));
1559 auto bI =
static_cast<GLuint
>(strtol(tokens[i].c_str(),
nullptr, 10));
1560 auto cI =
static_cast<GLuint
>(strtol(tokens[i + 1].c_str(),
nullptr, 10));
1562 glm::vec3 a = verticesTemp[aI];
1563 glm::vec3 b = verticesTemp[bI];
1564 glm::vec3 c = verticesTemp[cI];
1566 glm::vec3 ab = b - a; glm::vec3 ac = c - a;
1567 glm::vec3 ba = a - b; glm::vec3 bc = c - b;
1568 glm::vec3 ca = a - c; glm::vec3 cb = b - c;
1570 glm::vec3 aN = glm::normalize( glm::cross( ab, ac ) );
1571 glm::vec3 bN = glm::normalize( glm::cross( bc, ba ) );
1572 glm::vec3 cN = glm::normalize( glm::cross( ca, cb ) );
1574 _vertices[ _uniqueIndex ] = a;
1575 _normals[ _uniqueIndex ] = aN;
1576 _indices[ _numIndices++ ] = _uniqueIndex++;
1578 _vertices[ _uniqueIndex ] = b;
1579 _normals[ _uniqueIndex ] = bN;
1580 _indices[ _numIndices++ ] = _uniqueIndex++;
1582 _vertices[ _uniqueIndex ] = c;
1583 _normals[ _uniqueIndex ] = cN;
1584 _indices[ _numIndices++ ] = _uniqueIndex++;
1593 if( progressCounter % 5000 == 0 ) {
1595 switch( progressCounter ) {
1604 if( progressCounter == 20000 )
1605 progressCounter = 0;
1613 double seconds = difftime( end, start );
1617 CSCI441::LogUtils::log(
"[.ply]: parsing %s...done!\n[.ply]: Time to complete: %.3fs\n", _filename.c_str(), seconds );
1624inline bool CSCI441::ModelLoader::_loadSTLFile(
const bool INFO,
const bool ERRORS ) {
1627 if (INFO)
CSCI441::LogUtils::log(
"[.stl]: -=-=-=-=-=-=-=- BEGIN %s Info -=-=-=-=-=-=-=-\n", _filename.c_str() );
1632 std::ifstream in( _filename );
1633 if( !in.is_open() ) {
1635 if ( INFO )
CSCI441::LogUtils::log(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1639 GLuint numVertices = 0, numNormals = 0, numFaces = 0, numTriangles = 0, numVertsInLoop = 0;
1640 GLfloat minX = 999999.0f, maxX = -999999.0f, minY = 999999.0f, maxY = -999999.0f, minZ = 999999.0f, maxZ = -999999.0f;
1643 int progressCounter = 0;
1644 glm::vec3 normalVector = {0.f,0.f,0.f};
1646 while( getline( in, line ) ) {
1647 if( line.length() > 1 && line.at(0) ==
'\t' )
1648 line = line.substr( 1 );
1649 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1651 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1653 if( tokens.empty() )
continue;
1655 if( tokens[0] ==
"solid" ) {
1657 }
else if( tokens[0] ==
"facet" ) {
1660 }
else if( tokens[0] ==
"outer" && tokens[1] ==
"loop" ) {
1663 }
else if( tokens[0] ==
"vertex" ) {
1664 GLfloat x = strtof( tokens[1].c_str(),
nullptr ),
1665 y = strtof( tokens[2].c_str(),
nullptr ),
1666 z = strtof( tokens[3].c_str(),
nullptr );
1668 if( x < minX ) minX = x;
1669 if( x > maxX ) maxX = x;
1670 if( y < minY ) minY = y;
1671 if( y > maxY ) maxY = y;
1672 if( z < minZ ) minZ = z;
1673 if( z > maxZ ) maxZ = z;
1677 }
else if( tokens[0] ==
"endloop" ) {
1679 numTriangles += numVertsInLoop - 3 + 1;
1680 }
else if( tokens[0] ==
"endfacet" ) {
1682 }
else if( tokens[0] ==
"endsolid" ) {
1686 if( memchr( line.c_str(),
'\0', line.length() ) !=
nullptr ) {
1688 if ( INFO )
CSCI441::LogUtils::log(
"[.stl]: -=-=-=-=-=-=-=- END %s Info -=-=-=-=-=-=-=-\n\n", _filename.c_str() );
1696 if( progressCounter % 5000 == 0 ) {
1698 switch( progressCounter ) {
1707 if( progressCounter == 20000 )
1708 progressCounter = 0;
1718 CSCI441::LogUtils::log(
"[.stl]: Vertices: \t%u\tNormals: \t%u\tTex Coords:\t%u\n", numVertices, numNormals, 0 );
1720 CSCI441::LogUtils::log(
"[.stl]: Dimensions:\t(%f, %f, %f)\n", (maxX - minX), (maxY - minY), (maxZ - minZ) );
1723 _allocateAttributeArrays(numVertices, numTriangles*3);
1727 in.open( _filename );
1732 while( getline( in, line ) ) {
1733 if( line.length() > 1 && line.at(0) ==
'\t' )
1734 line = line.substr( 1 );
1735 line.erase( line.find_last_not_of(
" \n\r\t" ) + 1 );
1737 std::vector< std::string > tokens = _tokenizeString( line,
" \t" );
1739 if( tokens.empty() )
continue;
1741 if( tokens[0] ==
"solid" ) {
1743 }
else if( tokens[0] ==
"facet" ) {
1745 normalVector = glm::vec3( strtof( tokens[2].c_str(),
nullptr ),
1746 strtof( tokens[3].c_str(),
nullptr ),
1747 strtof( tokens[4].c_str(),
nullptr ) );
1748 }
else if( tokens[0] ==
"outer" && tokens[1] ==
"loop" ) {
1750 }
else if( tokens[0] ==
"vertex" ) {
1751 _vertices[ _uniqueIndex ] = glm::vec3(strtof( tokens[1].c_str(),
nullptr ),
1752 strtof( tokens[2].c_str(),
nullptr ),
1753 strtof( tokens[3].c_str(),
nullptr ) );
1754 _normals[ _uniqueIndex ] = normalVector;
1755 _indices[ _numIndices++ ] = _uniqueIndex++;
1756 }
else if( tokens[0] ==
"endloop" ) {
1758 }
else if( tokens[0] ==
"endfacet" ) {
1760 }
else if( tokens[0] ==
"endsolid" ) {
1769 if( progressCounter % 5000 == 0 ) {
1771 switch( progressCounter ) {
1780 if( progressCounter == 20000 )
1781 progressCounter = 0;
1789 double seconds = difftime( end, start );
1793 CSCI441::LogUtils::log(
"[.stl]: parsing %s...done!\n[.stl]: Time to complete: %.3fs\n", _filename.c_str(), seconds);
1802 sAUTO_GEN_NORMALS =
true;
1807 sAUTO_GEN_NORMALS =
false;
1811 sAUTO_GEN_TANGENTS =
true;
1816 sAUTO_GEN_TANGENTS =
false;
1819inline void CSCI441::ModelLoader::_allocateAttributeArrays(
const GLuint numVertices,
const GLuint numIndices) {
1820 _vertices =
new glm::vec3[numVertices];
1821 _normals =
new glm::vec3[numVertices];
1822 _tangents =
new glm::vec4[numVertices];
1823 _texCoords =
new glm::vec2[numVertices];
1824 _indices =
new GLuint[numIndices];
1827inline void CSCI441::ModelLoader::_bufferData()
const {
1828 glBindVertexArray( _vaod );
1830 glBindBuffer( GL_ARRAY_BUFFER, _vbods[0] );
1831 glBufferData( GL_ARRAY_BUFFER,
static_cast<GLsizeiptr
>((
sizeof(glm::vec3)*2 +
sizeof(glm::vec2) +
sizeof(glm::vec4)) * _uniqueIndex),
nullptr, GL_STATIC_DRAW );
1832 glBufferSubData( GL_ARRAY_BUFFER, 0,
static_cast<GLsizeiptr
>(
sizeof(glm::vec3) * _uniqueIndex), _vertices );
1833 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex),
static_cast<GLsizeiptr
>(
sizeof(glm::vec3) * _uniqueIndex), _normals );
1834 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex * 2),
static_cast<GLsizeiptr
>(
sizeof(glm::vec2) * _uniqueIndex), _texCoords );
1835 glBufferSubData( GL_ARRAY_BUFFER,
static_cast<GLintptr
>(
sizeof(glm::vec3) * _uniqueIndex * 2 +
sizeof(glm::vec2) * _uniqueIndex),
static_cast<GLsizeiptr
>(
sizeof(glm::vec4) * _uniqueIndex), _tangents );
1837 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, _vbods[1] );
1838 glBufferData( GL_ELEMENT_ARRAY_BUFFER,
static_cast<GLsizeiptr
>(
sizeof(GLuint) * _numIndices), _indices, GL_STATIC_DRAW );
1847inline std::vector<std::string> CSCI441::ModelLoader::_tokenizeString(
const std::string& input,
const std::string& delimiters) {
1851 auto retVec = std::vector<std::string>();
1852 size_t oldR = 0, r = 0;
1855 GLint lowerValidIndex = 0, upperValidIndex =
static_cast<GLint
>(input.size()) - 1;
1856 while(
static_cast<GLuint
>(lowerValidIndex) < input.size() && delimiters.find_first_of(input.at(lowerValidIndex), 0) != std::string::npos)
1859 while(upperValidIndex >= 0 && delimiters.find_first_of(input.at(upperValidIndex), 0) != std::string::npos)
1863 if(
static_cast<GLuint
>(lowerValidIndex) >= input.size() || upperValidIndex < 0 || lowerValidIndex > upperValidIndex)
1867 const std::string strippedInput = input.substr(lowerValidIndex, upperValidIndex-lowerValidIndex+1);
1871 while((r = strippedInput.find_first_of(delimiters, oldR)) != std::string::npos) {
1874 retVec.push_back(strippedInput.substr(oldR, r-oldR));
1879 retVec.push_back(strippedInput.substr(oldR, r-oldR));
1885inline void CSCI441::ModelLoader::_moveFromSrc(ModelLoader& src) {
1886 _hasVertexTexCoords = src._hasVertexTexCoords;
1887 src._hasVertexTexCoords =
false;
1889 _hasVertexNormals = src._hasVertexNormals;
1890 src._hasVertexNormals =
false;
1892 _vertices = src._vertices;
1893 src._vertices =
nullptr;
1895 _texCoords = src._texCoords;
1896 src._texCoords =
nullptr;
1898 _normals = src._normals;
1899 src._normals =
nullptr;
1901 _tangents = src._tangents;
1902 src._tangents =
nullptr;
1904 _indices = src._indices;
1905 src._indices =
nullptr;
1907 _uniqueIndex = src._uniqueIndex;
1908 src._uniqueIndex = 0;
1910 _numIndices = src._numIndices;
1911 src._numIndices = 0;
1913 _vbods[0] = src._vbods[0];
1914 _vbods[1] = src._vbods[1];
1921 _filename = std::move(src._filename);
1924 _modelType = src._modelType;
1926 _materials = std::move(src._materials);
1927 _materialIndexStartStop = std::move(src._materialIndexStartStop);
1930inline void CSCI441::ModelLoader::_cleanupSelf() {
1932 _vertices =
nullptr;
1938 _tangents =
nullptr;
1940 delete[] _texCoords;
1941 _texCoords =
nullptr;
1946 glDeleteBuffers( 2, _vbods );
1950 glDeleteVertexArrays( 1, &_vaod );
1953 _hasVertexTexCoords =
false;
1954 _hasVertexNormals =
false;
1959 for(
const auto& [name, material] : _materials ) {
1964 _materialIndexStartStop.clear();
1967inline 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 ) {
1969 auto *fullData =
new unsigned char[texWidth*texHeight*4];
1971 for(
int j = 0; j < texHeight; j++) {
1972 for(
int i = 0; i < texWidth; i++) {
1974 fullData[(j*texWidth+i)*4+0] = imageData[(j*texWidth+i)*texChannels+0];
1975 fullData[(j*texWidth+i)*4+1] = imageData[(j*texWidth+i)*texChannels+1];
1976 fullData[(j*texWidth+i)*4+2] = imageData[(j*texWidth+i)*texChannels+2];
1978 fullData[(j*texWidth+i)*4+0] = 1;
1979 fullData[(j*texWidth+i)*4+1] = 1;
1980 fullData[(j*texWidth+i)*4+2] = 1;
1984 fullData[(j*texWidth+i)*4+3] = imageMask[(j*texWidth+i)*maskChannels+0];
1986 fullData[(j*texWidth+i)*4+3] = 1;
1994inline void CSCI441_INTERNAL::flipImageY(
const int texWidth,
const int texHeight,
const int textureChannels,
unsigned char *textureData ) {
1995 for(
int j = 0; j < texHeight / 2; j++ ) {
1996 for(
int i = 0; i < texWidth; i++ ) {
1997 for(
int k = 0; k < textureChannels; k++ ) {
1998 const int top = (j*texWidth + i)*textureChannels + k;
1999 const int bot = ((texHeight-j-1)*texWidth + i)*textureChannels + k;
2001 const unsigned char t = textureData[top];
2002 textureData[top] = textureData[bot];
2003 textureData[bot] = t;
Loads object models from file and renders using VBOs/VAOs.
Definition: ModelLoader.hpp:55
ModelLoader(const ModelLoader &)=delete
do not allow models to be copied
static void enableAutoGenerateNormals()
Enable auto-generation of vertex normals.
Definition: ModelLoader.hpp:1801
ModelLoader & operator=(const ModelLoader &)=delete
do not allow models to be copied
static void enableAutoGenerateTangents()
Enable auto-generation of vertex tangents.
Definition: ModelLoader.hpp:1810
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:412
GLfloat * getVertices() const
Return the vertex array that makes up the model mesh.
Definition: ModelLoader.hpp:408
GLfloat * getTangents() const
Return the tangent array that corresponds to the model mesh.
Definition: ModelLoader.hpp:410
bool loadModelFile(std::string filename, bool INFO=true, bool ERRORS=true)
Loads a model from the given file.
Definition: ModelLoader.hpp:311
void setAttributeLocations(GLint positionLocation, GLint normalLocation=-1, GLint texCoordLocation=-1, GLint tangentLocation=-1) const
Enables VBO attribute array locations.
Definition: ModelLoader.hpp:339
GLfloat * getNormals() const
Return the normal array that corresponds to the model mesh.
Definition: ModelLoader.hpp:409
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:365
GLuint * getIndices() const
Return the index array that dictates the order to draw the model mesh.
Definition: ModelLoader.hpp:413
GLfloat * getTexCoords() const
Return the texture coordinates array that corresponds to the model mesh.
Definition: ModelLoader.hpp:411
static void disableAutoGenerateTangents()
Disable auto-generation of vertex tangents.
Definition: ModelLoader.hpp:1815
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:407
static void disableAutoGenerateNormals()
Disable auto-generation of vertex normals.
Definition: ModelLoader.hpp:1806
~ModelLoader()
Frees memory associated with model on both CPU and GPU.
Definition: ModelLoader.hpp:278
ModelLoader()
Creates an empty model.
Definition: ModelLoader.hpp:244
Internal material representation for *.mtl files.
void log(const char *MSG,...)
log a message to both the standard output stream and file
Definition: LogUtils.hpp:116
void logError(const char *MSG,...)
log a message to both the standard error stream and file
Definition: LogUtils.hpp:128
CSCI441 Helper Functions for OpenGL.
Definition: ArcballCam.hpp:17