diff --git a/code/AssetLib/Obj/ObjFileParser.cpp b/code/AssetLib/Obj/ObjFileParser.cpp index cc843468d..8d51b15ec 100644 --- a/code/AssetLib/Obj/ObjFileParser.cpp +++ b/code/AssetLib/Obj/ObjFileParser.cpp @@ -57,58 +57,73 @@ namespace Assimp { constexpr const char ObjFileParser::DEFAULT_MATERIAL[]; -ObjFileParser::ObjFileParser() : - m_DataIt(), - m_DataItEnd(), - m_pModel(nullptr), - m_uiLine(0), - m_buffer(), - mEnd(&m_buffer[Buffersize]), - m_pIO(nullptr), - m_progress(nullptr), - m_originalObjFileName() { - std::fill_n(m_buffer, Buffersize, '\0'); +// ------------------------------------------------------------------- +static bool isDataDefinitionEnd(const char *tmp) { + ai_assert(tmp != nullptr); + + if (*tmp == '\\') { + ++tmp; + if (IsLineEnd(*tmp)) { + return true; + } + } + return false; } +// ------------------------------------------------------------------- +static bool isNanOrInf(const char *in) { + ai_assert(in != nullptr); + + // Look for "nan" or "inf", case insensitive + return ((in[0] == 'N' || in[0] == 'n') && ASSIMP_strincmp(in, "nan", 3) == 0) || + ((in[0] == 'I' || in[0] == 'i') && ASSIMP_strincmp(in, "inf", 3) == 0); +} + +// ------------------------------------------------------------------- +ObjFileParser::ObjFileParser() : mEnd(&mBuffer[Buffersize-1]+1) { + std::fill_n(mBuffer, Buffersize, '\0'); +} + +// ------------------------------------------------------------------- ObjFileParser::ObjFileParser(IOStreamBuffer &streamBuffer, const std::string &modelName, - IOSystem *io, ProgressHandler *progress, - const std::string &originalObjFileName) : - m_DataIt(), - m_DataItEnd(), - m_pModel(nullptr), - m_uiLine(0), - m_buffer(), - m_pIO(io), - m_progress(progress), - m_originalObjFileName(originalObjFileName) { - std::fill_n(m_buffer, Buffersize, '\0'); + IOSystem *io, ProgressHandler *progress, const std::string &originalObjFileName) : + mIO(io), + mProgress(progress), + mOriginalObjFileName(originalObjFileName) { + std::fill_n(mBuffer, Buffersize, '\0'); // Create the model instance to store all the data - m_pModel.reset(new ObjFile::Model()); - m_pModel->mModelName = modelName; + mModel.reset(new ObjFile::Model()); + mModel->mModelName = modelName; // create default material and store it - m_pModel->mDefaultMaterial = new ObjFile::Material; - m_pModel->mDefaultMaterial->MaterialName.Set(DEFAULT_MATERIAL); - m_pModel->mMaterialLib.emplace_back(DEFAULT_MATERIAL); - m_pModel->mMaterialMap[DEFAULT_MATERIAL] = m_pModel->mDefaultMaterial; + mModel->mDefaultMaterial = new ObjFile::Material; + mModel->mDefaultMaterial->MaterialName.Set(DEFAULT_MATERIAL); + mModel->mMaterialLib.emplace_back(DEFAULT_MATERIAL); + mModel->mMaterialMap[DEFAULT_MATERIAL] = mModel->mDefaultMaterial; // Start parsing the file parseFile(streamBuffer); } void ObjFileParser::setBuffer(std::vector &buffer) { - m_DataIt = buffer.begin(); - m_DataItEnd = buffer.end(); + mDataIt = buffer.begin(); + mDataItEnd = buffer.end(); + ai_assert(mDataIt < mDataItEnd); + if (!buffer.empty()) { + mEnd = &buffer[buffer.size() - 1] + 1; + } } +// ------------------------------------------------------------------- ObjFile::Model *ObjFileParser::GetModel() const { - return m_pModel.get(); + return mModel.get(); } +// ------------------------------------------------------------------- void ObjFileParser::parseFile(IOStreamBuffer &streamBuffer) { // only update every 100KB or it'll be too slow - //const unsigned int updateProgressEveryBytes = 100 * 1024; + // const unsigned int updateProgressEveryBytes = 100 * 1024; const unsigned int bytesToProcess = static_cast(streamBuffer.size()); const unsigned int progressTotal = bytesToProcess; unsigned int processed = 0u; @@ -117,90 +132,93 @@ void ObjFileParser::parseFile(IOStreamBuffer &streamBuffer) { bool insideCstype = false; std::vector buffer; while (streamBuffer.getNextDataLine(buffer, '\\')) { - m_DataIt = buffer.begin(); - m_DataItEnd = buffer.end(); + mDataIt = buffer.begin(); + mDataItEnd = buffer.end(); mEnd = &buffer[buffer.size() - 1] + 1; - if (processed == 0 && std::distance(m_DataIt, m_DataItEnd) >= 3 && - static_cast(*m_DataIt) == 0xEF && - static_cast(*(m_DataIt + 1)) == 0xBB && - static_cast(*(m_DataIt + 2)) == 0xBF) { - m_DataIt += 3; // skip BOM + if (processed == 0 && std::distance(mDataIt, mDataItEnd) >= 3 && + static_cast(*mDataIt) == 0xEF && + static_cast(*(mDataIt + 1)) == 0xBB && + static_cast(*(mDataIt + 2)) == 0xBF) { + mDataIt += 3; // skip BOM } // Handle progress reporting - const size_t filePos(streamBuffer.getFilePos()); + const size_t filePos = streamBuffer.getFilePos(); if (lastFilePos < filePos) { processed = static_cast(filePos); lastFilePos = filePos; - m_progress->UpdateFileRead(processed, progressTotal); + if (mProgress != nullptr) { + mProgress->UpdateFileRead(processed, progressTotal); + } } // handle c-stype section end (http://paulbourke.net/dataformats/obj/) if (insideCstype) { - switch (*m_DataIt) { - case 'e': { - std::string name; - getNameNoSpace(m_DataIt, m_DataItEnd, name); - insideCstype = name != "end"; - } break; + switch (*mDataIt) { + case 'e': { + std::string name; + getNameNoSpace(mDataIt, mDataItEnd, name); + insideCstype = name != "end"; + } break; + default: + break; } goto pf_skip_line; } // parse line - switch (*m_DataIt) { + switch (*mDataIt) { case 'v': // Parse a vertex texture coordinate { - ++m_DataIt; - if (*m_DataIt == ' ' || *m_DataIt == '\t') { + ++mDataIt; + if (*mDataIt == ' ' || *mDataIt == '\t') { size_t numComponents = getNumComponentsInDataDefinition(); if (numComponents == 3) { // read in vertex definition - getVector3(m_pModel->mVertices); + getVector3(mModel->mVertices); } else if (numComponents == 4) { // read in vertex definition (homogeneous coords) - getHomogeneousVector3(m_pModel->mVertices); + getHomogeneousVector3(mModel->mVertices); } else if (numComponents == 6) { // fill previous omitted vertex-colors by default - if (m_pModel->mVertexColors.size() < m_pModel->mVertices.size()) { - m_pModel->mVertexColors.resize(m_pModel->mVertices.size(), aiVector3D(0, 0, 0)); + if (mModel->mVertexColors.size() < mModel->mVertices.size()) { + mModel->mVertexColors.resize(mModel->mVertices.size(), aiVector3D(0, 0, 0)); } // read vertex and vertex-color - getTwoVectors3(m_pModel->mVertices, m_pModel->mVertexColors); + getTwoVectors3(mModel->mVertices, mModel->mVertexColors); } // append omitted vertex-colors as default for the end if any vertex-color exists - if (!m_pModel->mVertexColors.empty() && m_pModel->mVertexColors.size() < m_pModel->mVertices.size()) { - m_pModel->mVertexColors.resize(m_pModel->mVertices.size(), aiVector3D(0, 0, 0)); + if (!mModel->mVertexColors.empty() && mModel->mVertexColors.size() < mModel->mVertices.size()) { + mModel->mVertexColors.resize(mModel->mVertices.size(), aiVector3D(0, 0, 0)); } - } else if (*m_DataIt == 't') { + } else if (*mDataIt == 't') { // read in texture coordinate ( 2D or 3D ) - ++m_DataIt; - size_t dim = getTexCoordVector(m_pModel->mTextureCoord); - m_pModel->mTextureCoordDim = std::max(m_pModel->mTextureCoordDim, (unsigned int)dim); - } else if (*m_DataIt == 'n') { + ++mDataIt; + size_t dim = getTexCoordVector(mModel->mTextureCoord); + mModel->mTextureCoordDim = std::max(mModel->mTextureCoordDim, (unsigned int)dim); + } else if (*mDataIt == 'n') { // Read in normal vector definition - ++m_DataIt; - getVector3(m_pModel->mNormals); + ++mDataIt; + getVector3(mModel->mNormals); } } break; case 'p': // Parse a face, line or point statement case 'l': case 'f': { - getFace(*m_DataIt == 'f' ? aiPrimitiveType_POLYGON : (*m_DataIt == 'l' ? aiPrimitiveType_LINE : aiPrimitiveType_POINT)); + getFace(*mDataIt == 'f' ? aiPrimitiveType_POLYGON : (*mDataIt == 'l' ? aiPrimitiveType_LINE : aiPrimitiveType_POINT)); } break; case '#': // Parse a comment { - getComment(); + skipComment(); } break; case 'u': // Parse a material desc. setter { std::string name; - - getNameNoSpace(m_DataIt, m_DataItEnd, name); + getNameNoSpace(mDataIt, mDataItEnd, name); size_t nextSpace = name.find(' '); if (nextSpace != std::string::npos) @@ -215,14 +233,14 @@ void ObjFileParser::parseFile(IOStreamBuffer &streamBuffer) { { std::string name; - getNameNoSpace(m_DataIt, m_DataItEnd, name); + getNameNoSpace(mDataIt, mDataItEnd, name); size_t nextSpace = name.find(' '); if (nextSpace != std::string::npos) name = name.substr(0, nextSpace); if (name == "mg") - getGroupNumberAndResolution(); + skipGroupNumberAndResolution(); else if (name == "mtllib") getMaterialLib(); else @@ -236,7 +254,7 @@ void ObjFileParser::parseFile(IOStreamBuffer &streamBuffer) { case 's': // Parse group number { - getGroupNumber(); + skipGroupNumber(); } break; case 'o': // Parse object name @@ -247,59 +265,45 @@ void ObjFileParser::parseFile(IOStreamBuffer &streamBuffer) { case 'c': // handle cstype section start { std::string name; - getNameNoSpace(m_DataIt, m_DataItEnd, name); + getNameNoSpace(mDataIt, mDataItEnd, name); insideCstype = name == "cstype"; goto pf_skip_line; } default: { pf_skip_line: - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } break; } } } +// ------------------------------------------------------------------- void ObjFileParser::copyNextWord(char *pBuffer, size_t length) { size_t index = 0; - m_DataIt = getNextWord(m_DataIt, m_DataItEnd); - if (*m_DataIt == '\\') { - ++m_DataIt; - ++m_DataIt; - m_DataIt = getNextWord(m_DataIt, m_DataItEnd); + mDataIt = getNextWord(mDataIt, mDataItEnd); + if (*mDataIt == '\\') { + ++mDataIt; + ++mDataIt; + mDataIt = getNextWord(mDataIt, mDataItEnd); } - while (m_DataIt != m_DataItEnd && !IsSpaceOrNewLine(*m_DataIt)) { - pBuffer[index] = *m_DataIt; + while (mDataIt != mDataItEnd && !IsSpaceOrNewLine(*mDataIt)) { + pBuffer[index] = *mDataIt; index++; if (index == length - 1) { break; } - ++m_DataIt; + ++mDataIt; } ai_assert(index < length); pBuffer[index] = '\0'; } -static bool isDataDefinitionEnd(const char *tmp) { - if (*tmp == '\\') { - tmp++; - if (IsLineEnd(*tmp)) { - return true; - } - } - return false; -} - -static bool isNanOrInf(const char *in) { - // Look for "nan" or "inf", case insensitive - return ((in[0] == 'N' || in[0] == 'n') && ASSIMP_strincmp(in, "nan", 3) == 0) || - ((in[0] == 'I' || in[0] == 'i') && ASSIMP_strincmp(in, "inf", 3) == 0); -} - +// ------------------------------------------------------------------- size_t ObjFileParser::getNumComponentsInDataDefinition() { size_t numComponents(0); - const char *tmp(&m_DataIt[0]); + const char *tmp = &mDataIt[0]; bool end_of_definition = false; while (!end_of_definition) { if (isDataDefinitionEnd(tmp)) { @@ -323,25 +327,26 @@ size_t ObjFileParser::getNumComponentsInDataDefinition() { return numComponents; } +// ------------------------------------------------------------------- size_t ObjFileParser::getTexCoordVector(std::vector &point3d_array) { size_t numComponents = getNumComponentsInDataDefinition(); ai_real x, y, z; if (2 == numComponents) { - copyNextWord(m_buffer, Buffersize); - x = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + x = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - y = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + y = fast_atof(mBuffer); z = 0.0; } else if (3 == numComponents) { - copyNextWord(m_buffer, Buffersize); - x = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + x = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - y = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + y = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - z = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + z = fast_atof(mBuffer); } else { throw DeadlyImportError("OBJ: Invalid number of components"); } @@ -357,121 +362,126 @@ size_t ObjFileParser::getTexCoordVector(std::vector &point3d_array) z = 0; point3d_array.emplace_back(x, y, z); - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); return numComponents; } +// ------------------------------------------------------------------- void ObjFileParser::getVector3(std::vector &point3d_array) { ai_real x, y, z; - copyNextWord(m_buffer, Buffersize); - x = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + x = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - y = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + y = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - z = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + z = fast_atof(mBuffer); point3d_array.emplace_back(x, y, z); - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } +// ------------------------------------------------------------------- void ObjFileParser::getHomogeneousVector3(std::vector &point3d_array) { ai_real x, y, z, w; - copyNextWord(m_buffer, Buffersize); - x = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + x = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - y = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + y = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - z = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + z = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - w = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + w = fast_atof(mBuffer); if (w == 0) throw DeadlyImportError("OBJ: Invalid component in homogeneous vector (Division by zero)"); point3d_array.emplace_back(x / w, y / w, z / w); - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } +// ------------------------------------------------------------------- void ObjFileParser::getTwoVectors3(std::vector &point3d_array_a, std::vector &point3d_array_b) { ai_real x, y, z; - copyNextWord(m_buffer, Buffersize); - x = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + x = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - y = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + y = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - z = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + z = fast_atof(mBuffer); point3d_array_a.emplace_back(x, y, z); - copyNextWord(m_buffer, Buffersize); - x = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + x = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - y = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + y = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - z = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + z = fast_atof(mBuffer); point3d_array_b.emplace_back(x, y, z); - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } +// ------------------------------------------------------------------- void ObjFileParser::getVector2(std::vector &point2d_array) { ai_real x, y; - copyNextWord(m_buffer, Buffersize); - x = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + x = fast_atof(mBuffer); - copyNextWord(m_buffer, Buffersize); - y = (ai_real)fast_atof(m_buffer); + copyNextWord(mBuffer, Buffersize); + y = fast_atof(mBuffer); point2d_array.emplace_back(x, y); - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } static constexpr char DefaultObjName[] = "defaultobject"; +// ------------------------------------------------------------------- void ObjFileParser::getFace(aiPrimitiveType type) { - m_DataIt = getNextToken(m_DataIt, m_DataItEnd); - if (m_DataIt == m_DataItEnd || *m_DataIt == '\0') { + mDataIt = getNextToken(mDataIt, mDataItEnd); + if (mDataIt == mDataItEnd || *mDataIt == '\0') { return; } ObjFile::Face *face = new ObjFile::Face(type); bool hasNormal = false; - const int vSize = static_cast(m_pModel->mVertices.size()); - const int vtSize = static_cast(m_pModel->mTextureCoord.size()); - const int vnSize = static_cast(m_pModel->mNormals.size()); + const int vSize = static_cast(mModel->mVertices.size()); + const int vtSize = static_cast(mModel->mTextureCoord.size()); + const int vnSize = static_cast(mModel->mNormals.size()); - const bool vt = (!m_pModel->mTextureCoord.empty()); - const bool vn = (!m_pModel->mNormals.empty()); + const bool vt = (!mModel->mTextureCoord.empty()); + const bool vn = (!mModel->mNormals.empty()); int iPos = 0; - while (m_DataIt < m_DataItEnd) { + while (mDataIt < mDataItEnd) { int iStep = 1; - if (IsLineEnd(*m_DataIt) || *m_DataIt == '#') { + if (IsLineEnd(*mDataIt) || *mDataIt == '#') { break; } - if (*m_DataIt == '/') { + if (*mDataIt == '/') { if (type == aiPrimitiveType_POINT) { ASSIMP_LOG_ERROR("Obj: Separator unexpected in point statement"); } - iPos++; - } else if (IsSpaceOrNewLine(*m_DataIt) || *m_DataIt == '\v') { + ++iPos; + } else if (IsSpaceOrNewLine(*mDataIt) || *mDataIt == '\v') { iPos = 0; } else { //OBJ USES 1 Base ARRAYS!!!! - const int iVal = ::atoi(&(*m_DataIt)); + const int iVal = ::atoi(&(*mDataIt)); // increment iStep position based off of the sign and # of digits int tmp = iVal; @@ -516,62 +526,63 @@ void ObjFileParser::getFace(aiPrimitiveType type) { throw DeadlyImportError("OBJ: Invalid face index."); } } - m_DataIt += iStep; + mDataIt += iStep; } if (face->m_vertices.empty()) { ASSIMP_LOG_ERROR("Obj: Ignoring empty face"); // skip line and clean up - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); delete face; return; } // Set active material, if one set - if (nullptr != m_pModel->mCurrentMaterial) { - face->m_pMaterial = m_pModel->mCurrentMaterial; + if (nullptr != mModel->mCurrentMaterial) { + face->m_pMaterial = mModel->mCurrentMaterial; } else { - face->m_pMaterial = m_pModel->mDefaultMaterial; + face->m_pMaterial = mModel->mDefaultMaterial; } // Create a default object, if nothing is there - if (nullptr == m_pModel->mCurrentObject) { + if (nullptr == mModel->mCurrentObject) { createObject(DefaultObjName); } // Assign face to mesh - if (nullptr == m_pModel->mCurrentMesh) { + if (nullptr == mModel->mCurrentMesh) { createMesh(DefaultObjName); } // Store the face - m_pModel->mCurrentMesh->m_Faces.emplace_back(face); - m_pModel->mCurrentMesh->m_uiNumIndices += static_cast(face->m_vertices.size()); - m_pModel->mCurrentMesh->m_uiUVCoordinates[0] += static_cast(face->m_texturCoords.size()); - if (!m_pModel->mCurrentMesh->m_hasNormals && hasNormal) { - m_pModel->mCurrentMesh->m_hasNormals = true; + mModel->mCurrentMesh->m_Faces.emplace_back(face); + mModel->mCurrentMesh->m_uiNumIndices += static_cast(face->m_vertices.size()); + mModel->mCurrentMesh->m_uiUVCoordinates[0] += static_cast(face->m_texturCoords.size()); + if (!mModel->mCurrentMesh->m_hasNormals && hasNormal) { + mModel->mCurrentMesh->m_hasNormals = true; } // Skip the rest of the line - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } +// ------------------------------------------------------------------- void ObjFileParser::getMaterialDesc() { // Get next data for material data - m_DataIt = getNextToken(m_DataIt, m_DataItEnd); - if (m_DataIt == m_DataItEnd) { + mDataIt = getNextToken(mDataIt, mDataItEnd); + if (mDataIt == mDataItEnd) { return; } - char *pStart = &(*m_DataIt); - while (m_DataIt != m_DataItEnd && !IsLineEnd(*m_DataIt)) { - ++m_DataIt; + char *pStart = &(*mDataIt); + while (mDataIt != mDataItEnd && !IsLineEnd(*mDataIt)) { + ++mDataIt; } // In some cases we should ignore this 'usemtl' command, this variable helps us to do so bool skip = false; // Get name - std::string strName(pStart, &(*m_DataIt)); + std::string strName(pStart, &(*mDataIt)); strName = ai_trim(strName); if (strName.empty()) { skip = true; @@ -580,63 +591,62 @@ void ObjFileParser::getMaterialDesc() { // If the current mesh has the same material, we will ignore that 'usemtl' command // There is no need to create another object or even mesh here if (!skip) { - if (m_pModel->mCurrentMaterial && m_pModel->mCurrentMaterial->MaterialName == aiString(strName)) { + if (mModel->mCurrentMaterial && mModel->mCurrentMaterial->MaterialName == aiString(strName)) { skip = true; } } if (!skip) { // Search for material - std::map::iterator it = m_pModel->mMaterialMap.find(strName); - if (it == m_pModel->mMaterialMap.end()) { + std::map::iterator it = mModel->mMaterialMap.find(strName); + if (it == mModel->mMaterialMap.end()) { // Not found, so we don't know anything about the material except for its name. // This may be the case if the material library is missing. We don't want to lose all // materials if that happens, so create a new named material instead of discarding it // completely. ASSIMP_LOG_ERROR("OBJ: failed to locate material ", strName, ", creating new material"); - m_pModel->mCurrentMaterial = new ObjFile::Material(); - m_pModel->mCurrentMaterial->MaterialName.Set(strName); - m_pModel->mMaterialLib.push_back(strName); - m_pModel->mMaterialMap[strName] = m_pModel->mCurrentMaterial; + mModel->mCurrentMaterial = new ObjFile::Material(); + mModel->mCurrentMaterial->MaterialName.Set(strName); + mModel->mMaterialLib.push_back(strName); + mModel->mMaterialMap[strName] = mModel->mCurrentMaterial; } else { // Found, using detected material - m_pModel->mCurrentMaterial = (*it).second; + mModel->mCurrentMaterial = it->second; } if (needsNewMesh(strName)) { - auto newMeshName = m_pModel->mActiveGroup.empty() ? strName : m_pModel->mActiveGroup; + auto newMeshName = mModel->mActiveGroup.empty() ? strName : mModel->mActiveGroup; createMesh(newMeshName); } - m_pModel->mCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strName); + mModel->mCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strName); } // Skip rest of line - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } // ------------------------------------------------------------------- // Get a comment, values will be skipped -void ObjFileParser::getComment() { - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); +void ObjFileParser::skipComment() { + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } // ------------------------------------------------------------------- -// Get material library from file. void ObjFileParser::getMaterialLib() { // Translate tuple - m_DataIt = getNextToken(m_DataIt, m_DataItEnd); - if (m_DataIt == m_DataItEnd) { + mDataIt = getNextToken(mDataIt, mDataItEnd); + if (mDataIt == mDataItEnd) { return; } - char *pStart = &(*m_DataIt); - while (m_DataIt != m_DataItEnd && !IsLineEnd(*m_DataIt)) { - ++m_DataIt; + char *pStart = &(*mDataIt); + while (mDataIt != mDataItEnd && !IsLineEnd(*mDataIt)) { + ++mDataIt; } // Check for existence - const std::string strMatName(pStart, &(*m_DataIt)); + const std::string strMatName(pStart, &(*mDataIt)); std::string absName; // Check if directive is valid. @@ -645,8 +655,8 @@ void ObjFileParser::getMaterialLib() { return; } - if (m_pIO->StackSize() > 0) { - std::string path = m_pIO->CurrentDirectory(); + if (mIO->StackSize() > 0) { + std::string path = mIO->CurrentDirectory(); if ('/' != *path.rbegin()) { path += '/'; } @@ -656,15 +666,15 @@ void ObjFileParser::getMaterialLib() { absName = strMatName; } - std::unique_ptr pFile(m_pIO->Open(absName)); + std::unique_ptr pFile(mIO->Open(absName)); if (nullptr == pFile) { ASSIMP_LOG_ERROR("OBJ: Unable to locate material file ", strMatName); - std::string strMatFallbackName = m_originalObjFileName.substr(0, m_originalObjFileName.length() - 3) + "mtl"; + std::string strMatFallbackName = mOriginalObjFileName.substr(0, mOriginalObjFileName.length() - 3) + "mtl"; ASSIMP_LOG_INFO("OBJ: Opening fallback material file ", strMatFallbackName); - pFile.reset(m_pIO->Open(strMatFallbackName)); + pFile.reset(mIO->Open(strMatFallbackName)); if (!pFile) { ASSIMP_LOG_ERROR("OBJ: Unable to locate fallback material file ", strMatFallbackName); - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); return; } } @@ -678,47 +688,48 @@ void ObjFileParser::getMaterialLib() { //m_pIO->Close(pFile); // Importing the material library - ObjFileMtlImporter mtlImporter(buffer, strMatName, m_pModel.get()); + ObjFileMtlImporter mtlImporter(buffer, strMatName, mModel.get()); } // ------------------------------------------------------------------- -// Set a new material definition as the current material. void ObjFileParser::getNewMaterial() { - m_DataIt = getNextToken(m_DataIt, m_DataItEnd); - m_DataIt = getNextWord(m_DataIt, m_DataItEnd); - if (m_DataIt == m_DataItEnd) { + mDataIt = getNextToken(mDataIt, mDataItEnd); + mDataIt = getNextWord(mDataIt, mDataItEnd); + if (mDataIt == mDataItEnd) { return; } - char *pStart = &(*m_DataIt); - std::string strMat(pStart, *m_DataIt); - while (m_DataIt != m_DataItEnd && IsSpaceOrNewLine(*m_DataIt)) { - ++m_DataIt; + char *pStart = &(*mDataIt); + std::string strMat(pStart, *mDataIt); + while (mDataIt != mDataItEnd && IsSpaceOrNewLine(*mDataIt)) { + ++mDataIt; } - std::map::iterator it = m_pModel->mMaterialMap.find(strMat); - if (it == m_pModel->mMaterialMap.end()) { + auto it = mModel->mMaterialMap.find(strMat); + if (it == mModel->mMaterialMap.end()) { // Show a warning, if material was not found ASSIMP_LOG_WARN("OBJ: Unsupported material requested: ", strMat); - m_pModel->mCurrentMaterial = m_pModel->mDefaultMaterial; + mModel->mCurrentMaterial = mModel->mDefaultMaterial; } else { // Set new material if (needsNewMesh(strMat)) { createMesh(strMat); } - m_pModel->mCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strMat); + mModel->mCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strMat); } - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } +static constexpr int InvalidMaterialIndex = -1; + // ------------------------------------------------------------------- int ObjFileParser::getMaterialIndex(const std::string &strMaterialName) { - int mat_index = -1; + int mat_index = InvalidMaterialIndex; if (strMaterialName.empty()) { return mat_index; } - for (size_t index = 0; index < m_pModel->mMaterialLib.size(); ++index) { - if (strMaterialName == m_pModel->mMaterialLib[index]) { + for (size_t index = 0; index < mModel->mMaterialLib.size(); ++index) { + if (strMaterialName == mModel->mMaterialLib[index]) { mat_index = (int)index; break; } @@ -732,111 +743,108 @@ void ObjFileParser::getGroupName() { std::string groupName; // here we skip 'g ' from line - m_DataIt = getNextToken(m_DataIt, m_DataItEnd); - m_DataIt = getName(m_DataIt, m_DataItEnd, groupName); - if (isEndOfBuffer(m_DataIt, m_DataItEnd)) { + mDataIt = getNextToken(mDataIt, mDataItEnd); + mDataIt = getName(mDataIt, mDataItEnd, groupName); + if (isEndOfBuffer(mDataIt, mDataItEnd)) { return; } // Change active group, if necessary - if (m_pModel->mActiveGroup != groupName) { + if (mModel->mActiveGroup != groupName) { // Search for already existing entry - ObjFile::Model::ConstGroupMapIt it = m_pModel->mGroups.find(groupName); + ObjFile::Model::ConstGroupMapIt it = mModel->mGroups.find(groupName); // We are mapping groups into the object structure createObject(groupName); // New group name, creating a new entry - if (it == m_pModel->mGroups.end()) { + if (it == mModel->mGroups.end()) { std::vector *pFaceIDArray = new std::vector; - m_pModel->mGroups[groupName] = pFaceIDArray; - m_pModel->mGroupFaceIDs = (pFaceIDArray); + mModel->mGroups[groupName] = pFaceIDArray; + mModel->mGroupFaceIDs = (pFaceIDArray); } else { - m_pModel->mGroupFaceIDs = (*it).second; + mModel->mGroupFaceIDs = (*it).second; } - m_pModel->mActiveGroup = groupName; + mModel->mActiveGroup = groupName; } - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } // ------------------------------------------------------------------- -// Not supported -void ObjFileParser::getGroupNumber() { +void ObjFileParser::skipGroupNumber() { // Not used - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } // ------------------------------------------------------------------- -// Not supported -void ObjFileParser::getGroupNumberAndResolution() { +void ObjFileParser::skipGroupNumberAndResolution() { // Not used - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } // ------------------------------------------------------------------- // Stores values for a new object instance, name will be used to // identify it. void ObjFileParser::getObjectName() { - m_DataIt = getNextToken(m_DataIt, m_DataItEnd); - if (m_DataIt == m_DataItEnd) { + mDataIt = getNextToken(mDataIt, mDataItEnd); + if (mDataIt == mDataItEnd) { return; } - char *pStart = &(*m_DataIt); - while (m_DataIt != m_DataItEnd && !IsSpaceOrNewLine(*m_DataIt)) { - ++m_DataIt; + char *pStart = &(*mDataIt); + while (mDataIt != mDataItEnd && !IsSpaceOrNewLine(*mDataIt)) { + ++mDataIt; } - std::string strObjectName(pStart, &(*m_DataIt)); + std::string strObjectName(pStart, &(*mDataIt)); if (!strObjectName.empty()) { // Reset current object - m_pModel->mCurrentObject = nullptr; + mModel->mCurrentObject = nullptr; // Search for actual object - for (std::vector::const_iterator it = m_pModel->mObjects.begin(); - it != m_pModel->mObjects.end(); - ++it) { + for (auto it = mModel->mObjects.begin(); it != mModel->mObjects.end(); ++it) { if ((*it)->m_strObjName == strObjectName) { - m_pModel->mCurrentObject = *it; + mModel->mCurrentObject = *it; break; } } // Allocate a new object, if current one was not found before - if (nullptr == m_pModel->mCurrentObject) { + if (mModel->mCurrentObject == nullptr) { createObject(strObjectName); } } - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); } + // ------------------------------------------------------------------- // Creates a new object instance void ObjFileParser::createObject(const std::string &objName) { - ai_assert(nullptr != m_pModel); + ai_assert(nullptr != mModel); - m_pModel->mCurrentObject = new ObjFile::Object; - m_pModel->mCurrentObject->m_strObjName = objName; - m_pModel->mObjects.push_back(m_pModel->mCurrentObject); + mModel->mCurrentObject = new ObjFile::Object; + mModel->mCurrentObject->m_strObjName = objName; + mModel->mObjects.push_back(mModel->mCurrentObject); createMesh(objName); - if (m_pModel->mCurrentMaterial) { - m_pModel->mCurrentMesh->m_uiMaterialIndex = - getMaterialIndex(m_pModel->mCurrentMaterial->MaterialName.data); - m_pModel->mCurrentMesh->m_pMaterial = m_pModel->mCurrentMaterial; + if (mModel->mCurrentMaterial) { + mModel->mCurrentMesh->m_uiMaterialIndex = + getMaterialIndex(mModel->mCurrentMaterial->MaterialName.data); + mModel->mCurrentMesh->m_pMaterial = mModel->mCurrentMaterial; } } // ------------------------------------------------------------------- // Creates a new mesh void ObjFileParser::createMesh(const std::string &meshName) { - ai_assert(nullptr != m_pModel); + ai_assert(nullptr != mModel); - m_pModel->mCurrentMesh = new ObjFile::Mesh(meshName); - m_pModel->mMeshes.push_back(m_pModel->mCurrentMesh); - unsigned int meshId = static_cast(m_pModel->mMeshes.size() - 1); - if (nullptr != m_pModel->mCurrentObject) { - m_pModel->mCurrentObject->m_Meshes.push_back(meshId); + mModel->mCurrentMesh = new ObjFile::Mesh(meshName); + mModel->mMeshes.push_back(mModel->mCurrentMesh); + auto meshId = static_cast(mModel->mMeshes.size() - 1); + if (mModel->mCurrentObject != nullptr) { + mModel->mCurrentObject->m_Meshes.push_back(meshId); } else { ASSIMP_LOG_ERROR("OBJ: No object detected to attach a new mesh instance."); } @@ -846,16 +854,16 @@ void ObjFileParser::createMesh(const std::string &meshName) { // Returns true, if a new mesh must be created. bool ObjFileParser::needsNewMesh(const std::string &materialName) { // If no mesh data yet - if (m_pModel->mCurrentMesh == nullptr) { + if (mModel->mCurrentMesh == nullptr) { return true; } bool newMat = false; int matIdx = getMaterialIndex(materialName); - int curMatIdx = m_pModel->mCurrentMesh->m_uiMaterialIndex; + int curMatIdx = mModel->mCurrentMesh->m_uiMaterialIndex; if (curMatIdx != int(ObjFile::Mesh::NoMaterial) && curMatIdx != matIdx // no need create a new mesh if no faces in current // lets say 'usemtl' goes straight after 'g' - && !m_pModel->mCurrentMesh->m_Faces.empty()) { + && !mModel->mCurrentMesh->m_Faces.empty()) { // New material -> only one material per mesh, so we need to create a new // material newMat = true; @@ -866,7 +874,7 @@ bool ObjFileParser::needsNewMesh(const std::string &materialName) { // ------------------------------------------------------------------- // Shows an error in parsing process. void ObjFileParser::reportErrorTokenInFace() { - m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + mDataIt = skipLine(mDataIt, mDataItEnd, mLine); ASSIMP_LOG_ERROR("OBJ: Not supported token in face description detected"); } diff --git a/code/AssetLib/Obj/ObjFileParser.h b/code/AssetLib/Obj/ObjFileParser.h index 7a0779bf2..731e8c19f 100644 --- a/code/AssetLib/Obj/ObjFileParser.h +++ b/code/AssetLib/Obj/ObjFileParser.h @@ -106,8 +106,8 @@ protected: void getFace(aiPrimitiveType type); /// Reads the material description. void getMaterialDesc(); - /// Gets a comment. - void getComment(); + /// Skip a comment. + void skipComment(); /// Gets a a material library. void getMaterialLib(); /// Creates a new material. @@ -115,9 +115,9 @@ protected: /// Gets the group name from file. void getGroupName(); /// Gets the group number from file. - void getGroupNumber(); + void skipGroupNumber(); /// Gets the group number and resolution from file. - void getGroupNumberAndResolution(); + void skipGroupNumberAndResolution(); /// Returns the index of the material. Is -1 if not material was found. int getMaterialIndex(const std::string &strMaterialName); /// Parse object name @@ -135,23 +135,23 @@ private: /// Default material name static constexpr const char DEFAULT_MATERIAL[] = AI_DEFAULT_MATERIAL_NAME; //! Iterator to current position in buffer - DataArrayIt m_DataIt; + DataArrayIt mDataIt{}; //! Iterator to end position of buffer - DataArrayIt m_DataItEnd; + DataArrayIt mDataItEnd{}; //! Pointer to model instance - std::unique_ptr m_pModel; + std::unique_ptr mModel{}; //! Current line (for debugging) - unsigned int m_uiLine; + unsigned int mLine{ 0 }; //! Helper buffer - char m_buffer[Buffersize]; + char mBuffer[Buffersize]; /// End of buffer - const char *mEnd; + const char *mEnd{ nullptr }; /// Pointer to IO system instance. - IOSystem *m_pIO; + IOSystem *mIO{ nullptr }; //! Pointer to progress handler - ProgressHandler *m_progress; + ProgressHandler *mProgress{ nullptr }; /// Path to the current model, name of the obj file where the buffer comes from - const std::string m_originalObjFileName; + const std::string mOriginalObjFileName{}; }; } // Namespace Assimp diff --git a/include/assimp/LineSplitter.h b/include/assimp/LineSplitter.h index 25fc3c08a..5dbc84fe0 100644 --- a/include/assimp/LineSplitter.h +++ b/include/assimp/LineSplitter.h @@ -166,7 +166,7 @@ AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_emp mSkip_empty_lines(skip_empty_lines), mTrim(trim) { mCur.reserve(1024); - mEnd = mCur.c_str() + 1024; + mEnd = mCur.c_str() + mCur.capacity(); operator++(); mIdx = 0; } @@ -183,6 +183,7 @@ AI_FORCE_INLINE LineSplitter& LineSplitter::operator++() { char s; mCur.clear(); + mEnd = mCur.c_str() + mCur.capacity(); while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) { if (s == '\n' || s == '\r') { if (mSkip_empty_lines) { @@ -205,6 +206,7 @@ AI_FORCE_INLINE LineSplitter& LineSplitter::operator++() { break; } mCur += s; + mEnd = mCur.c_str() + mCur.capacity(); } ++mIdx; diff --git a/include/assimp/ParsingUtils.h b/include/assimp/ParsingUtils.h index 17e992849..9c1a59f97 100644 --- a/include/assimp/ParsingUtils.h +++ b/include/assimp/ParsingUtils.h @@ -124,11 +124,11 @@ AI_FORCE_INLINE bool IsSpaceOrNewLine(char_t in) { /// @return true if valid. template AI_FORCE_INLINE bool SkipSpaces(const char_t *in, const char_t **out, const char_t *end) { - while ((*in == (char_t)' ' || *in == (char_t)'\t') && in != end) { + while (in < end && (*in == (char_t)' ' || *in == (char_t)'\t')) { ++in; } *out = in; - return !IsLineEnd(*in); + return ((in < end) && !IsLineEnd(*in)); } // --------------------------------------------------------------------------------- @@ -154,11 +154,11 @@ AI_FORCE_INLINE bool SkipLine(const char_t *in, const char_t **out, const char_t } // files are opened in binary mode. Ergo there are both NL and CR - while ((*in == (char_t)'\r' || *in == (char_t)'\n') && in != end) { + while (in < end && (*in == (char_t)'\r' || *in == (char_t)'\n')) { ++in; } *out = in; - return *in != (char_t)'\0'; + return in < end && *in != (char_t)'\0'; } // --------------------------------------------------------------------------------- @@ -178,11 +178,11 @@ AI_FORCE_INLINE bool SkipLine(const char_t **inout, const char_t *end) { /// @return true if valid. template AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t *in, const char_t **out, const char_t *end) { - while ((*in == (char_t)' ' || *in == (char_t)'\t' || *in == (char_t)'\r' || *in == (char_t)'\n') && in != end) { + while (in < end && (*in == (char_t)' ' || *in == (char_t)'\t' || *in == (char_t)'\r' || *in == (char_t)'\n')) { ++in; } *out = in; - return *in != '\0'; + return in < end && *in != '\0'; } // ---------------------------------------------------------------------------------