summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/Obj/ObjFileParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/assimp/code/AssetLib/Obj/ObjFileParser.cpp')
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjFileParser.cpp838
1 files changed, 838 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/Obj/ObjFileParser.cpp b/libs/assimp/code/AssetLib/Obj/ObjFileParser.cpp
new file mode 100644
index 0000000..2e998a8
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Obj/ObjFileParser.cpp
@@ -0,0 +1,838 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
+
+#include "ObjFileParser.h"
+#include "ObjFileData.h"
+#include "ObjFileMtlImporter.h"
+#include "ObjTools.h"
+#include <assimp/BaseImporter.h>
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/ParsingUtils.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Importer.hpp>
+#include <cstdlib>
+#include <memory>
+#include <utility>
+
+namespace Assimp {
+
+constexpr char ObjFileParser::DEFAULT_MATERIAL[];
+
+ObjFileParser::ObjFileParser() :
+ m_DataIt(),
+ m_DataItEnd(),
+ m_pModel(nullptr),
+ m_uiLine(0),
+ m_buffer(),
+ m_pIO(nullptr),
+ m_progress(nullptr),
+ m_originalObjFileName() {
+ std::fill_n(m_buffer, Buffersize, '\0');
+}
+
+ObjFileParser::ObjFileParser(IOStreamBuffer<char> &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');
+
+ // Create the model instance to store all the data
+ m_pModel.reset(new ObjFile::Model());
+ m_pModel->m_ModelName = modelName;
+
+ // create default material and store it
+ m_pModel->m_pDefaultMaterial = new ObjFile::Material;
+ m_pModel->m_pDefaultMaterial->MaterialName.Set(DEFAULT_MATERIAL);
+ m_pModel->m_MaterialLib.push_back(DEFAULT_MATERIAL);
+ m_pModel->m_MaterialMap[DEFAULT_MATERIAL] = m_pModel->m_pDefaultMaterial;
+
+ // Start parsing the file
+ parseFile(streamBuffer);
+}
+
+ObjFileParser::~ObjFileParser() {
+}
+
+void ObjFileParser::setBuffer(std::vector<char> &buffer) {
+ m_DataIt = buffer.begin();
+ m_DataItEnd = buffer.end();
+}
+
+ObjFile::Model *ObjFileParser::GetModel() const {
+ return m_pModel.get();
+}
+
+void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
+ // only update every 100KB or it'll be too slow
+ //const unsigned int updateProgressEveryBytes = 100 * 1024;
+ unsigned int progressCounter = 0;
+ const unsigned int bytesToProcess = static_cast<unsigned int>(streamBuffer.size());
+ const unsigned int progressTotal = bytesToProcess;
+ unsigned int processed = 0;
+ size_t lastFilePos(0);
+
+ std::vector<char> buffer;
+ while (streamBuffer.getNextDataLine(buffer, '\\')) {
+ m_DataIt = buffer.begin();
+ m_DataItEnd = buffer.end();
+
+ // Handle progress reporting
+ const size_t filePos(streamBuffer.getFilePos());
+ if (lastFilePos < filePos) {
+ processed = static_cast<unsigned int>(filePos);
+ lastFilePos = filePos;
+ progressCounter++;
+ m_progress->UpdateFileRead(processed, progressTotal);
+ }
+
+ // parse line
+ switch (*m_DataIt) {
+ case 'v': // Parse a vertex texture coordinate
+ {
+ ++m_DataIt;
+ if (*m_DataIt == ' ' || *m_DataIt == '\t') {
+ size_t numComponents = getNumComponentsInDataDefinition();
+ if (numComponents == 3) {
+ // read in vertex definition
+ getVector3(m_pModel->m_Vertices);
+ } else if (numComponents == 4) {
+ // read in vertex definition (homogeneous coords)
+ getHomogeneousVector3(m_pModel->m_Vertices);
+ } else if (numComponents == 6) {
+ // read vertex and vertex-color
+ getTwoVectors3(m_pModel->m_Vertices, m_pModel->m_VertexColors);
+ }
+ } else if (*m_DataIt == 't') {
+ // read in texture coordinate ( 2D or 3D )
+ ++m_DataIt;
+ size_t dim = getTexCoordVector(m_pModel->m_TextureCoord);
+ m_pModel->m_TextureCoordDim = std::max(m_pModel->m_TextureCoordDim, (unsigned int)dim);
+ } else if (*m_DataIt == 'n') {
+ // Read in normal vector definition
+ ++m_DataIt;
+ getVector3(m_pModel->m_Normals);
+ }
+ } 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));
+ } break;
+
+ case '#': // Parse a comment
+ {
+ getComment();
+ } break;
+
+ case 'u': // Parse a material desc. setter
+ {
+ std::string name;
+
+ getNameNoSpace(m_DataIt, m_DataItEnd, name);
+
+ size_t nextSpace = name.find(' ');
+ if (nextSpace != std::string::npos)
+ name = name.substr(0, nextSpace);
+
+ if (name == "usemtl") {
+ getMaterialDesc();
+ }
+ } break;
+
+ case 'm': // Parse a material library or merging group ('mg')
+ {
+ std::string name;
+
+ getNameNoSpace(m_DataIt, m_DataItEnd, name);
+
+ size_t nextSpace = name.find(' ');
+ if (nextSpace != std::string::npos)
+ name = name.substr(0, nextSpace);
+
+ if (name == "mg")
+ getGroupNumberAndResolution();
+ else if (name == "mtllib")
+ getMaterialLib();
+ else
+ goto pf_skip_line;
+ } break;
+
+ case 'g': // Parse group name
+ {
+ getGroupName();
+ } break;
+
+ case 's': // Parse group number
+ {
+ getGroupNumber();
+ } break;
+
+ case 'o': // Parse object name
+ {
+ getObjectName();
+ } break;
+
+ default: {
+ pf_skip_line:
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ } break;
+ }
+ }
+}
+
+void ObjFileParser::copyNextWord(char *pBuffer, size_t length) {
+ size_t index = 0;
+ m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (*m_DataIt == '\\') {
+ ++m_DataIt;
+ ++m_DataIt;
+ m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+ }
+ while (m_DataIt != m_DataItEnd && !IsSpaceOrNewLine(*m_DataIt)) {
+ pBuffer[index] = *m_DataIt;
+ index++;
+ if (index == length - 1) {
+ break;
+ }
+ ++m_DataIt;
+ }
+
+ 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]);
+ bool end_of_definition = false;
+ while (!end_of_definition) {
+ if (isDataDefinitionEnd(tmp)) {
+ tmp += 2;
+ } else if (IsLineEnd(*tmp)) {
+ end_of_definition = true;
+ }
+ if (!SkipSpaces(&tmp)) {
+ break;
+ }
+ const bool isNum(IsNumeric(*tmp) || isNanOrInf(tmp));
+ SkipToken(tmp);
+ if (isNum) {
+ ++numComponents;
+ }
+ if (!SkipSpaces(&tmp)) {
+ break;
+ }
+ }
+ return numComponents;
+}
+
+size_t ObjFileParser::getTexCoordVector(std::vector<aiVector3D> &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(m_buffer, Buffersize);
+ y = (ai_real)fast_atof(m_buffer);
+ z = 0.0;
+ } else if (3 == numComponents) {
+ copyNextWord(m_buffer, Buffersize);
+ x = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ y = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ z = (ai_real)fast_atof(m_buffer);
+ } else {
+ throw DeadlyImportError("OBJ: Invalid number of components");
+ }
+
+ // Coerce nan and inf to 0 as is the OBJ default value
+ if (!std::isfinite(x))
+ x = 0;
+
+ if (!std::isfinite(y))
+ y = 0;
+
+ if (!std::isfinite(z))
+ z = 0;
+
+ point3d_array.emplace_back(x, y, z);
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ return numComponents;
+}
+
+void ObjFileParser::getVector3(std::vector<aiVector3D> &point3d_array) {
+ ai_real x, y, z;
+ copyNextWord(m_buffer, Buffersize);
+ x = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ y = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ z = (ai_real)fast_atof(m_buffer);
+
+ point3d_array.emplace_back(x, y, z);
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+void ObjFileParser::getHomogeneousVector3(std::vector<aiVector3D> &point3d_array) {
+ ai_real x, y, z, w;
+ copyNextWord(m_buffer, Buffersize);
+ x = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ y = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ z = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ w = (ai_real)fast_atof(m_buffer);
+
+ 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<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+void ObjFileParser::getTwoVectors3(std::vector<aiVector3D> &point3d_array_a, std::vector<aiVector3D> &point3d_array_b) {
+ ai_real x, y, z;
+ copyNextWord(m_buffer, Buffersize);
+ x = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ y = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ z = (ai_real)fast_atof(m_buffer);
+
+ point3d_array_a.emplace_back(x, y, z);
+
+ copyNextWord(m_buffer, Buffersize);
+ x = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ y = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ z = (ai_real)fast_atof(m_buffer);
+
+ point3d_array_b.emplace_back(x, y, z);
+
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+void ObjFileParser::getVector2(std::vector<aiVector2D> &point2d_array) {
+ ai_real x, y;
+ copyNextWord(m_buffer, Buffersize);
+ x = (ai_real)fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, Buffersize);
+ y = (ai_real)fast_atof(m_buffer);
+
+ point2d_array.emplace_back(x, y);
+
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+static const std::string DefaultObjName = "defaultobject";
+
+void ObjFileParser::getFace(aiPrimitiveType type) {
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd || *m_DataIt == '\0') {
+ return;
+ }
+
+ ObjFile::Face *face = new ObjFile::Face(type);
+ bool hasNormal = false;
+
+ const int vSize = static_cast<unsigned int>(m_pModel->m_Vertices.size());
+ const int vtSize = static_cast<unsigned int>(m_pModel->m_TextureCoord.size());
+ const int vnSize = static_cast<unsigned int>(m_pModel->m_Normals.size());
+
+ const bool vt = (!m_pModel->m_TextureCoord.empty());
+ const bool vn = (!m_pModel->m_Normals.empty());
+ int iPos = 0;
+ while (m_DataIt != m_DataItEnd) {
+ int iStep = 1;
+
+ if (IsLineEnd(*m_DataIt)) {
+ break;
+ }
+
+ if (*m_DataIt == '/') {
+ if (type == aiPrimitiveType_POINT) {
+ ASSIMP_LOG_ERROR("Obj: Separator unexpected in point statement");
+ }
+ iPos++;
+ } else if (IsSpaceOrNewLine(*m_DataIt)) {
+ iPos = 0;
+ } else {
+ //OBJ USES 1 Base ARRAYS!!!!
+ const int iVal(::atoi(&(*m_DataIt)));
+
+ // increment iStep position based off of the sign and # of digits
+ int tmp = iVal;
+ if (iVal < 0) {
+ ++iStep;
+ }
+ while ((tmp = tmp / 10) != 0) {
+ ++iStep;
+ }
+
+ if (iPos == 1 && !vt && vn)
+ iPos = 2; // skip texture coords for normals if there are no tex coords
+
+ if (iVal > 0) {
+ // Store parsed index
+ if (0 == iPos) {
+ face->m_vertices.push_back(iVal - 1);
+ } else if (1 == iPos) {
+ face->m_texturCoords.push_back(iVal - 1);
+ } else if (2 == iPos) {
+ face->m_normals.push_back(iVal - 1);
+ hasNormal = true;
+ } else {
+ reportErrorTokenInFace();
+ }
+ } else if (iVal < 0) {
+ // Store relatively index
+ if (0 == iPos) {
+ face->m_vertices.push_back(vSize + iVal);
+ } else if (1 == iPos) {
+ face->m_texturCoords.push_back(vtSize + iVal);
+ } else if (2 == iPos) {
+ face->m_normals.push_back(vnSize + iVal);
+ hasNormal = true;
+ } else {
+ reportErrorTokenInFace();
+ }
+ } else {
+ //On error, std::atoi will return 0 which is not a valid value
+ delete face;
+ throw DeadlyImportError("OBJ: Invalid face indice");
+ }
+ }
+ m_DataIt += iStep;
+ }
+
+ if (face->m_vertices.empty()) {
+ ASSIMP_LOG_ERROR("Obj: Ignoring empty face");
+ // skip line and clean up
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ delete face;
+ return;
+ }
+
+ // Set active material, if one set
+ if (nullptr != m_pModel->m_pCurrentMaterial) {
+ face->m_pMaterial = m_pModel->m_pCurrentMaterial;
+ } else {
+ face->m_pMaterial = m_pModel->m_pDefaultMaterial;
+ }
+
+ // Create a default object, if nothing is there
+ if (nullptr == m_pModel->m_pCurrent) {
+ createObject(DefaultObjName);
+ }
+
+ // Assign face to mesh
+ if (nullptr == m_pModel->m_pCurrentMesh) {
+ createMesh(DefaultObjName);
+ }
+
+ // Store the face
+ m_pModel->m_pCurrentMesh->m_Faces.push_back(face);
+ m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_vertices.size();
+ m_pModel->m_pCurrentMesh->m_uiUVCoordinates[0] += (unsigned int)face->m_texturCoords.size();
+ if (!m_pModel->m_pCurrentMesh->m_hasNormals && hasNormal) {
+ m_pModel->m_pCurrentMesh->m_hasNormals = true;
+ }
+ // Skip the rest of the line
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+void ObjFileParser::getMaterialDesc() {
+ // Get next data for material data
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd) {
+ return;
+ }
+
+ char *pStart = &(*m_DataIt);
+ while (m_DataIt != m_DataItEnd && !IsLineEnd(*m_DataIt)) {
+ ++m_DataIt;
+ }
+
+ // 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));
+ strName = trim_whitespaces(strName);
+ if (strName.empty())
+ skip = true;
+
+ // If the current mesh has the same material, we simply ignore that 'usemtl' command
+ // There is no need to create another object or even mesh here
+ if (m_pModel->m_pCurrentMaterial && m_pModel->m_pCurrentMaterial->MaterialName == aiString(strName)) {
+ skip = true;
+ }
+
+ if (!skip) {
+ // Search for material
+ std::map<std::string, ObjFile::Material *>::iterator it = m_pModel->m_MaterialMap.find(strName);
+ if (it == m_pModel->m_MaterialMap.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->m_pCurrentMaterial = new ObjFile::Material();
+ m_pModel->m_pCurrentMaterial->MaterialName.Set(strName);
+ m_pModel->m_MaterialLib.push_back(strName);
+ m_pModel->m_MaterialMap[strName] = m_pModel->m_pCurrentMaterial;
+ } else {
+ // Found, using detected material
+ m_pModel->m_pCurrentMaterial = (*it).second;
+ }
+
+ if (needsNewMesh(strName)) {
+ createMesh(strName);
+ }
+
+ m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strName);
+ }
+
+ // Skip rest of line
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+// -------------------------------------------------------------------
+// Get a comment, values will be skipped
+void ObjFileParser::getComment() {
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+// -------------------------------------------------------------------
+// Get material library from file.
+void ObjFileParser::getMaterialLib() {
+ // Translate tuple
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd) {
+ return;
+ }
+
+ char *pStart = &(*m_DataIt);
+ while (m_DataIt != m_DataItEnd && !IsLineEnd(*m_DataIt)) {
+ ++m_DataIt;
+ }
+
+ // Check for existence
+ const std::string strMatName(pStart, &(*m_DataIt));
+ std::string absName;
+
+ // Check if directive is valid.
+ if (0 == strMatName.length()) {
+ ASSIMP_LOG_WARN("OBJ: no name for material library specified.");
+ return;
+ }
+
+ if (m_pIO->StackSize() > 0) {
+ std::string path = m_pIO->CurrentDirectory();
+ if ('/' != *path.rbegin()) {
+ path += '/';
+ }
+ absName += path;
+ absName += strMatName;
+ } else {
+ absName = strMatName;
+ }
+
+ IOStream *pFile = m_pIO->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";
+ ASSIMP_LOG_INFO("OBJ: Opening fallback material file ", strMatFallbackName);
+ pFile = m_pIO->Open(strMatFallbackName);
+ if (!pFile) {
+ ASSIMP_LOG_ERROR("OBJ: Unable to locate fallback material file ", strMatFallbackName);
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ return;
+ }
+ }
+
+ // Import material library data from file.
+ // Some exporters (e.g. Silo) will happily write out empty
+ // material files if the model doesn't use any materials, so we
+ // allow that.
+ std::vector<char> buffer;
+ BaseImporter::TextFileToBuffer(pFile, buffer, BaseImporter::ALLOW_EMPTY);
+ m_pIO->Close(pFile);
+
+ // Importing the material library
+ ObjFileMtlImporter mtlImporter(buffer, strMatName, m_pModel.get());
+}
+
+// -------------------------------------------------------------------
+// Set a new material definition as the current material.
+void ObjFileParser::getNewMaterial() {
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd) {
+ return;
+ }
+
+ char *pStart = &(*m_DataIt);
+ std::string strMat(pStart, *m_DataIt);
+ while (m_DataIt != m_DataItEnd && IsSpaceOrNewLine(*m_DataIt)) {
+ ++m_DataIt;
+ }
+ std::map<std::string, ObjFile::Material *>::iterator it = m_pModel->m_MaterialMap.find(strMat);
+ if (it == m_pModel->m_MaterialMap.end()) {
+ // Show a warning, if material was not found
+ ASSIMP_LOG_WARN("OBJ: Unsupported material requested: ", strMat);
+ m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
+ } else {
+ // Set new material
+ if (needsNewMesh(strMat)) {
+ createMesh(strMat);
+ }
+ m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strMat);
+ }
+
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+// -------------------------------------------------------------------
+int ObjFileParser::getMaterialIndex(const std::string &strMaterialName) {
+ int mat_index = -1;
+ if (strMaterialName.empty()) {
+ return mat_index;
+ }
+ for (size_t index = 0; index < m_pModel->m_MaterialLib.size(); ++index) {
+ if (strMaterialName == m_pModel->m_MaterialLib[index]) {
+ mat_index = (int)index;
+ break;
+ }
+ }
+ return mat_index;
+}
+
+// -------------------------------------------------------------------
+// Getter for a group name.
+void ObjFileParser::getGroupName() {
+ std::string groupName;
+
+ // here we skip 'g ' from line
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ m_DataIt = getName<DataArrayIt>(m_DataIt, m_DataItEnd, groupName);
+ if (isEndOfBuffer(m_DataIt, m_DataItEnd)) {
+ return;
+ }
+
+ // Change active group, if necessary
+ if (m_pModel->m_strActiveGroup != groupName) {
+ // Search for already existing entry
+ ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(groupName);
+
+ // We are mapping groups into the object structure
+ createObject(groupName);
+
+ // New group name, creating a new entry
+ if (it == m_pModel->m_Groups.end()) {
+ std::vector<unsigned int> *pFaceIDArray = new std::vector<unsigned int>;
+ m_pModel->m_Groups[groupName] = pFaceIDArray;
+ m_pModel->m_pGroupFaceIDs = (pFaceIDArray);
+ } else {
+ m_pModel->m_pGroupFaceIDs = (*it).second;
+ }
+ m_pModel->m_strActiveGroup = groupName;
+ }
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+// -------------------------------------------------------------------
+// Not supported
+void ObjFileParser::getGroupNumber() {
+ // Not used
+
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+// -------------------------------------------------------------------
+// Not supported
+void ObjFileParser::getGroupNumberAndResolution() {
+ // Not used
+
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+
+// -------------------------------------------------------------------
+// Stores values for a new object instance, name will be used to
+// identify it.
+void ObjFileParser::getObjectName() {
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd) {
+ return;
+ }
+ char *pStart = &(*m_DataIt);
+ while (m_DataIt != m_DataItEnd && !IsSpaceOrNewLine(*m_DataIt)) {
+ ++m_DataIt;
+ }
+
+ std::string strObjectName(pStart, &(*m_DataIt));
+ if (!strObjectName.empty()) {
+ // Reset current object
+ m_pModel->m_pCurrent = nullptr;
+
+ // Search for actual object
+ for (std::vector<ObjFile::Object *>::const_iterator it = m_pModel->m_Objects.begin();
+ it != m_pModel->m_Objects.end();
+ ++it) {
+ if ((*it)->m_strObjName == strObjectName) {
+ m_pModel->m_pCurrent = *it;
+ break;
+ }
+ }
+
+ // Allocate a new object, if current one was not found before
+ if (nullptr == m_pModel->m_pCurrent) {
+ createObject(strObjectName);
+ }
+ }
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+}
+// -------------------------------------------------------------------
+// Creates a new object instance
+void ObjFileParser::createObject(const std::string &objName) {
+ ai_assert(nullptr != m_pModel);
+
+ m_pModel->m_pCurrent = new ObjFile::Object;
+ m_pModel->m_pCurrent->m_strObjName = objName;
+ m_pModel->m_Objects.push_back(m_pModel->m_pCurrent);
+
+ createMesh(objName);
+
+ if (m_pModel->m_pCurrentMaterial) {
+ m_pModel->m_pCurrentMesh->m_uiMaterialIndex =
+ getMaterialIndex(m_pModel->m_pCurrentMaterial->MaterialName.data);
+ m_pModel->m_pCurrentMesh->m_pMaterial = m_pModel->m_pCurrentMaterial;
+ }
+}
+// -------------------------------------------------------------------
+// Creates a new mesh
+void ObjFileParser::createMesh(const std::string &meshName) {
+ ai_assert(nullptr != m_pModel);
+
+ m_pModel->m_pCurrentMesh = new ObjFile::Mesh(meshName);
+ m_pModel->m_Meshes.push_back(m_pModel->m_pCurrentMesh);
+ unsigned int meshId = static_cast<unsigned int>(m_pModel->m_Meshes.size() - 1);
+ if (nullptr != m_pModel->m_pCurrent) {
+ m_pModel->m_pCurrent->m_Meshes.push_back(meshId);
+ } else {
+ ASSIMP_LOG_ERROR("OBJ: No object detected to attach a new mesh instance.");
+ }
+}
+
+// -------------------------------------------------------------------
+// Returns true, if a new mesh must be created.
+bool ObjFileParser::needsNewMesh(const std::string &materialName) {
+ // If no mesh data yet
+ if (m_pModel->m_pCurrentMesh == nullptr) {
+ return true;
+ }
+ bool newMat = false;
+ int matIdx = getMaterialIndex(materialName);
+ int curMatIdx = m_pModel->m_pCurrentMesh->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->m_pCurrentMesh->m_Faces.empty()) {
+ // New material -> only one material per mesh, so we need to create a new
+ // material
+ newMat = true;
+ }
+ return newMat;
+}
+
+// -------------------------------------------------------------------
+// Shows an error in parsing process.
+void ObjFileParser::reportErrorTokenInFace() {
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ ASSIMP_LOG_ERROR("OBJ: Not supported token in face description detected");
+}
+
+// -------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER