update assimp lib

This commit is contained in:
marauder2k7 2024-12-09 20:22:47 +00:00
parent 03a348deb7
commit d3f8fee74e
1725 changed files with 196314 additions and 62009 deletions

View file

@ -59,9 +59,9 @@ namespace Assimp {
// ------------------------------------------------------------------------------------------------
// Worker function for exporting a scene to Wavefront OBJ. Prototyped and registered in Exporter.cpp
void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) {
void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* props) {
// invoke the exporter
ObjExporter exporter(pFile, pScene);
ObjExporter exporter(pFile, pScene, false, props);
if (exporter.mOutput.fail() || exporter.mOutputMat.fail()) {
throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
@ -86,9 +86,9 @@ void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene
// ------------------------------------------------------------------------------------------------
// Worker function for exporting a scene to Wavefront OBJ without the material file. Prototyped and registered in Exporter.cpp
void ExportSceneObjNoMtl(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* ) {
void ExportSceneObjNoMtl(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* props) {
// invoke the exporter
ObjExporter exporter(pFile, pScene, true);
ObjExporter exporter(pFile, pScene, true, props);
if (exporter.mOutput.fail() || exporter.mOutputMat.fail()) {
throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
@ -111,7 +111,7 @@ void ExportSceneObjNoMtl(const char* pFile,IOSystem* pIOSystem, const aiScene* p
static const std::string MaterialExt = ".mtl";
// ------------------------------------------------------------------------------------------------
ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMtl)
ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMtl, const ExportProperties* props)
: filename(_filename)
, pScene(pScene)
, vn()
@ -130,7 +130,10 @@ ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMt
mOutputMat.imbue(l);
mOutputMat.precision(ASSIMP_AI_REAL_TEXT_PRECISION);
WriteGeometryFile(noMtl);
WriteGeometryFile(
noMtl,
props == nullptr ? true : props->GetPropertyBool("bJoinIdenticalVertices", true)
);
if ( !noMtl ) {
WriteMaterialFile();
}
@ -171,9 +174,12 @@ void ObjExporter::WriteHeader(std::ostringstream& out) {
// ------------------------------------------------------------------------------------------------
std::string ObjExporter::GetMaterialName(unsigned int index) {
static const std::string EmptyStr;
if ( nullptr == pScene->mMaterials ) {
return EmptyStr;
}
const aiMaterial* const mat = pScene->mMaterials[index];
if ( nullptr == mat ) {
static const std::string EmptyStr;
return EmptyStr;
}
@ -255,14 +261,14 @@ void ObjExporter::WriteMaterialFile() {
}
}
void ObjExporter::WriteGeometryFile(bool noMtl) {
void ObjExporter::WriteGeometryFile(bool noMtl, bool merge_identical_vertices) {
WriteHeader(mOutput);
if (!noMtl)
mOutput << "mtllib " << GetMaterialLibName() << endl << endl;
// collect mesh geometry
aiMatrix4x4 mBase;
AddNode(pScene->mRootNode, mBase);
AddNode(pScene->mRootNode, mBase, merge_identical_vertices);
// write vertex positions with colors, if any
mVpMap.getKeys( vp );
@ -330,7 +336,7 @@ void ObjExporter::WriteGeometryFile(bool noMtl) {
}
// ------------------------------------------------------------------------------------------------
void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) {
void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat, bool merge_identical_vertices) {
mMeshes.emplace_back();
MeshInstance& mesh = mMeshes.back();
@ -362,13 +368,14 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
for(unsigned int a = 0; a < f.mNumIndices; ++a) {
const unsigned int idx = f.mIndices[a];
const unsigned int fi = merge_identical_vertices ? 0 : idx;
aiVector3D vert = mat * m->mVertices[idx];
if ( nullptr != m->mColors[ 0 ] ) {
aiColor4D col4 = m->mColors[ 0 ][ idx ];
face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(col4.r, col4.g, col4.b)});
face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(col4.r, col4.g, col4.b), fi});
} else {
face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(0,0,0)});
face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(0,0,0), fi});
}
if (m->mNormals) {
@ -388,21 +395,21 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4
}
// ------------------------------------------------------------------------------------------------
void ObjExporter::AddNode(const aiNode* nd, const aiMatrix4x4& mParent) {
void ObjExporter::AddNode(const aiNode* nd, const aiMatrix4x4& mParent, bool merge_identical_vertices) {
const aiMatrix4x4& mAbs = mParent * nd->mTransformation;
aiMesh *cm( nullptr );
for(unsigned int i = 0; i < nd->mNumMeshes; ++i) {
cm = pScene->mMeshes[nd->mMeshes[i]];
if (nullptr != cm) {
AddMesh(cm->mName, pScene->mMeshes[nd->mMeshes[i]], mAbs);
AddMesh(cm->mName, pScene->mMeshes[nd->mMeshes[i]], mAbs, merge_identical_vertices);
} else {
AddMesh(nd->mName, pScene->mMeshes[nd->mMeshes[i]], mAbs);
AddMesh(nd->mName, pScene->mMeshes[nd->mMeshes[i]], mAbs, merge_identical_vertices);
}
}
for(unsigned int i = 0; i < nd->mNumChildren; ++i) {
AddNode(nd->mChildren[i], mAbs);
AddNode(nd->mChildren[i], mAbs, merge_identical_vertices);
}
}

View file

@ -51,6 +51,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector>
#include <map>
#include <assimp/Exporter.hpp>
struct aiScene;
struct aiNode;
struct aiMesh;
@ -63,7 +65,7 @@ namespace Assimp {
class ObjExporter {
public:
/// Constructor for a specific scene to export
ObjExporter(const char* filename, const aiScene* pScene, bool noMtl=false);
ObjExporter(const char* filename, const aiScene* pScene, bool noMtl=false, const ExportProperties* props = nullptr);
~ObjExporter();
std::string GetMaterialLibName();
std::string GetMaterialLibFileName();
@ -97,10 +99,10 @@ private:
void WriteHeader(std::ostringstream& out);
void WriteMaterialFile();
void WriteGeometryFile(bool noMtl=false);
void WriteGeometryFile(bool noMtl=false, bool merge_identical_vertices = false);
std::string GetMaterialName(unsigned int index);
void AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat);
void AddNode(const aiNode* nd, const aiMatrix4x4& mParent);
void AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat, bool merge_identical_vertices);
void AddNode(const aiNode* nd, const aiMatrix4x4& mParent, bool merge_identical_vertices);
private:
std::string filename;
@ -109,6 +111,7 @@ private:
struct vertexData {
aiVector3D vp;
aiColor3D vc; // OBJ does not support 4D color
uint32_t index = 0;
};
std::vector<aiVector3D> vn, vt;
@ -133,7 +136,7 @@ private:
if (a.vc.g > b.vc.g) return false;
if (a.vc.b < b.vc.b) return true;
if (a.vc.b > b.vc.b) return false;
return false;
return a.index < b.index;
}
};

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -199,12 +199,12 @@ struct Material {
//! Constructor
Material() :
diffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)),
diffuse(0.6f, 0.6f, 0.6f),
alpha(ai_real(1.0)),
shineness(ai_real(0.0)),
illumination_model(1),
ior(ai_real(1.0)),
transparent(ai_real(1.0), ai_real(1.0), ai_real(1.0)),
transparent(1.0f, 1.0, 1.0),
roughness(),
metallic(),
sheen(),
@ -239,8 +239,6 @@ struct Mesh {
unsigned int m_uiMaterialIndex;
/// True, if normals are stored.
bool m_hasNormals;
/// True, if vertex colors are stored.
bool m_hasVertexColors;
/// Constructor
explicit Mesh(const std::string &name) :

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2020, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -54,7 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/ObjMaterial.h>
#include <memory>
static const aiImporterDesc desc = {
static constexpr aiImporterDesc desc = {
"Wavefront Object Importer",
"",
"",
@ -67,7 +67,7 @@ static const aiImporterDesc desc = {
"obj"
};
static const unsigned int ObjMinSize = 16;
static constexpr unsigned int ObjMinSize = 16u;
namespace Assimp {
@ -78,13 +78,14 @@ using namespace std;
ObjFileImporter::ObjFileImporter() :
m_Buffer(),
m_pRootObject(nullptr),
m_strAbsPath(std::string(1, DefaultIOSystem().getOsSeparator())) {}
m_strAbsPath(std::string(1, DefaultIOSystem().getOsSeparator())) {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor.
ObjFileImporter::~ObjFileImporter() {
delete m_pRootObject;
m_pRootObject = nullptr;
}
// ------------------------------------------------------------------------------------------------
@ -102,13 +103,18 @@ const aiImporterDesc *ObjFileImporter::GetInfo() const {
// ------------------------------------------------------------------------------------------------
// Obj-file import implementation
void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
if (m_pRootObject != nullptr) {
delete m_pRootObject;
m_pRootObject = nullptr;
}
// Read file into memory
static const std::string mode = "rb";
static constexpr char mode[] = "rb";
auto streamCloser = [&](IOStream *pStream) {
pIOHandler->Close(pStream);
};
std::unique_ptr<IOStream, decltype(streamCloser)> fileStream(pIOHandler->Open(file, mode), streamCloser);
if (!fileStream.get()) {
if (!fileStream) {
throw DeadlyImportError("Failed to open file ", file, ".");
}
@ -157,7 +163,7 @@ void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, I
// ------------------------------------------------------------------------------------------------
// Create the data from parsed obj-file
void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene *pScene) {
if (nullptr == pModel) {
if (pModel == nullptr) {
return;
}
@ -172,7 +178,6 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene
}
if (!pModel->mObjects.empty()) {
unsigned int meshCount = 0;
unsigned int childCount = 0;
@ -187,7 +192,7 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene
pScene->mRootNode->mChildren = new aiNode *[childCount];
// Create nodes for the whole scene
std::vector<aiMesh *> MeshArray;
std::vector<std::unique_ptr<aiMesh>> MeshArray;
MeshArray.reserve(meshCount);
for (size_t index = 0; index < pModel->mObjects.size(); ++index) {
createNodes(pModel, pModel->mObjects[index], pScene->mRootNode, pScene, MeshArray);
@ -199,7 +204,7 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene
if (pScene->mNumMeshes > 0) {
pScene->mMeshes = new aiMesh *[MeshArray.size()];
for (size_t index = 0; index < MeshArray.size(); ++index) {
pScene->mMeshes[index] = MeshArray[index];
pScene->mMeshes[index] = MeshArray[index].release();
}
}
@ -251,9 +256,8 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene
// Creates all nodes of the model
aiNode *ObjFileImporter::createNodes(const ObjFile::Model *pModel, const ObjFile::Object *pObject,
aiNode *pParent, aiScene *pScene,
std::vector<aiMesh *> &MeshArray) {
ai_assert(nullptr != pModel);
if (nullptr == pObject) {
std::vector<std::unique_ptr<aiMesh>> &MeshArray) {
if (nullptr == pObject || pModel == nullptr) {
return nullptr;
}
@ -269,12 +273,10 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model *pModel, const ObjFile
for (size_t i = 0; i < pObject->m_Meshes.size(); ++i) {
unsigned int meshId = pObject->m_Meshes[i];
aiMesh *pMesh = createTopology(pModel, pObject, meshId);
if (pMesh) {
std::unique_ptr<aiMesh> pMesh = createTopology(pModel, pObject, meshId);
if (pMesh != nullptr) {
if (pMesh->mNumFaces > 0) {
MeshArray.push_back(pMesh);
} else {
delete pMesh;
MeshArray.push_back(std::move(pMesh));
}
}
}
@ -306,17 +308,14 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model *pModel, const ObjFile
// ------------------------------------------------------------------------------------------------
// Create topology data
aiMesh *ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjFile::Object *pData, unsigned int meshIndex) {
// Checking preconditions
ai_assert(nullptr != pModel);
if (nullptr == pData) {
std::unique_ptr<aiMesh> ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjFile::Object *pData, unsigned int meshIndex) {
if (nullptr == pData || pModel == nullptr) {
return nullptr;
}
// Create faces
ObjFile::Mesh *pObjMesh = pModel->mMeshes[meshIndex];
if (!pObjMesh) {
if (pObjMesh == nullptr) {
return nullptr;
}
@ -331,8 +330,10 @@ aiMesh *ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjF
for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) {
const ObjFile::Face *inp = pObjMesh->m_Faces[index];
//ai_assert(nullptr != inp);
if (inp == nullptr) {
continue;
}
if (inp->mPrimitiveType == aiPrimitiveType_LINE) {
pMesh->mNumFaces += static_cast<unsigned int>(inp->m_vertices.size() - 1);
pMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
@ -349,14 +350,14 @@ aiMesh *ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjF
}
}
unsigned int uiIdxCount(0u);
unsigned int uiIdxCount = 0u;
if (pMesh->mNumFaces > 0) {
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
if (pObjMesh->m_uiMaterialIndex != ObjFile::Mesh::NoMaterial) {
pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex;
}
unsigned int outIndex(0);
unsigned int outIndex = 0u;
// Copy all data from all stored meshes
for (auto &face : pObjMesh->m_Faces) {
@ -389,7 +390,7 @@ aiMesh *ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjF
// Create mesh vertices
createVertexArray(pModel, pData, meshIndex, pMesh.get(), uiIdxCount);
return pMesh.release();
return pMesh;
}
// ------------------------------------------------------------------------------------------------
@ -400,11 +401,14 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel,
aiMesh *pMesh,
unsigned int numIndices) {
// Checking preconditions
ai_assert(nullptr != pCurrentObject);
if (pCurrentObject == nullptr || pModel == nullptr || pMesh == nullptr) {
return;
}
// Break, if no faces are stored in object
if (pCurrentObject->m_Meshes.empty())
if (pCurrentObject->m_Meshes.empty()) {
return;
}
// Get current mesh
ObjFile::Mesh *pObjMesh = pModel->mMeshes[uiMeshIndex];
@ -500,6 +504,10 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel,
if (vertexIndex) {
if (!last) {
if (pMesh->mNumVertices <= newIndex + 1) {
throw DeadlyImportError("OBJ: bad vertex index");
}
pMesh->mVertices[newIndex + 1] = pMesh->mVertices[newIndex];
if (!sourceFace->m_normals.empty() && !pModel->mNormals.empty()) {
pMesh->mNormals[newIndex + 1] = pMesh->mNormals[newIndex];
@ -579,11 +587,12 @@ void ObjFileImporter::createMaterials(const ObjFile::Model *pModel, aiScene *pSc
it = pModel->mMaterialMap.find(pModel->mMaterialLib[matIndex]);
// No material found, use the default material
if (pModel->mMaterialMap.end() == it)
if (pModel->mMaterialMap.end() == it) {
continue;
}
aiMaterial *mat = new aiMaterial;
ObjFile::Material *pCurrentMaterial = (*it).second;
ObjFile::Material *pCurrentMaterial = it->second;
mat->AddProperty(&pCurrentMaterial->MaterialName, AI_MATKEY_NAME);
// convert illumination model
@ -770,8 +779,11 @@ void ObjFileImporter::createMaterials(const ObjFile::Model *pModel, aiScene *pSc
// Appends this node to the parent node
void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild) {
// Checking preconditions
ai_assert(nullptr != pParent);
ai_assert(nullptr != pChild);
if (pParent == nullptr || pChild == nullptr) {
ai_assert(nullptr != pParent);
ai_assert(nullptr != pChild);
return;
}
// Assign parent to child
pChild->mParent = pParent;

View file

@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/BaseImporter.h>
#include <assimp/material.h>
#include <memory>
#include <vector>
struct aiMesh;
@ -84,10 +85,10 @@ protected:
//! \brief Creates all nodes stored in imported content.
aiNode *createNodes(const ObjFile::Model *pModel, const ObjFile::Object *pData,
aiNode *pParent, aiScene *pScene, std::vector<aiMesh *> &MeshArray);
aiNode *pParent, aiScene *pScene, std::vector<std::unique_ptr<aiMesh>> &MeshArray);
//! \brief Creates topology data like faces and meshes for the geometry.
aiMesh *createTopology(const ObjFile::Model *pModel, const ObjFile::Object *pData,
std::unique_ptr<aiMesh> createTopology(const ObjFile::Model *pModel, const ObjFile::Object *pData,
unsigned int uiMeshIndex);
//! \brief Creates vertices from model.

View file

@ -53,38 +53,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
// Material specific token (case insensitive compare)
static const std::string DiffuseTexture = "map_Kd";
static const std::string AmbientTexture = "map_Ka";
static const std::string SpecularTexture = "map_Ks";
static const std::string OpacityTexture = "map_d";
static const std::string EmissiveTexture1 = "map_emissive";
static const std::string EmissiveTexture2 = "map_Ke";
static const std::string BumpTexture1 = "map_bump";
static const std::string BumpTexture2 = "bump";
static const std::string NormalTextureV1 = "map_Kn";
static const std::string NormalTextureV2 = "norm";
static const std::string ReflectionTexture = "refl";
static const std::string DisplacementTexture1 = "map_disp";
static const std::string DisplacementTexture2 = "disp";
static const std::string SpecularityTexture = "map_ns";
static const std::string RoughnessTexture = "map_Pr";
static const std::string MetallicTexture = "map_Pm";
static const std::string SheenTexture = "map_Ps";
static const std::string RMATexture = "map_Ps";
static constexpr char DiffuseTexture[] = "map_Kd";
static constexpr char AmbientTexture[] = "map_Ka";
static constexpr char SpecularTexture[] = "map_Ks";
static constexpr char OpacityTexture[] = "map_d";
static constexpr char EmissiveTexture1[] = "map_emissive";
static constexpr char EmissiveTexture2[] = "map_Ke";
static constexpr char BumpTexture1[] = "map_bump";
static constexpr char BumpTexture2[] = "bump";
static constexpr char NormalTextureV1[] = "map_Kn";
static constexpr char NormalTextureV2[] = "norm";
static constexpr char ReflectionTexture[] = "refl";
static constexpr char DisplacementTexture1[] = "map_disp";
static constexpr char DisplacementTexture2[] = "disp";
static constexpr char SpecularityTexture[] = "map_ns";
static constexpr char RoughnessTexture[] = "map_Pr";
static constexpr char MetallicTexture[] = "map_Pm";
static constexpr char SheenTexture[] = "map_Ps";
static constexpr char RMATexture[] = "map_Ps";
// texture option specific token
static const std::string BlendUOption = "-blendu";
static const std::string BlendVOption = "-blendv";
static const std::string BoostOption = "-boost";
static const std::string ModifyMapOption = "-mm";
static const std::string OffsetOption = "-o";
static const std::string ScaleOption = "-s";
static const std::string TurbulenceOption = "-t";
static const std::string ResolutionOption = "-texres";
static const std::string ClampOption = "-clamp";
static const std::string BumpOption = "-bm";
static const std::string ChannelOption = "-imfchan";
static const std::string TypeOption = "-type";
static constexpr char BlendUOption[] = "-blendu";
static constexpr char BlendVOption[] = "-blendv";
static constexpr char BoostOption[] = "-boost";
static constexpr char ModifyMapOption[] = "-mm";
static constexpr char OffsetOption[] = "-o";
static constexpr char ScaleOption[] = "-s";
static constexpr char TurbulenceOption[] = "-t";
static constexpr char ResolutionOption[] = "-texres";
static constexpr char ClampOption[] = "-clamp";
static constexpr char BumpOption[] = "-bm";
static constexpr char ChannelOption[] = "-imfchan";
static constexpr char TypeOption[] = "-type";
// -------------------------------------------------------------------
// Constructor
@ -252,9 +252,9 @@ void ObjFileMtlImporter::load() {
case 'a': // Anisotropy
{
++m_DataIt;
getFloatValue(m_pModel->mCurrentMaterial->anisotropy);
if (m_pModel->mCurrentMaterial != nullptr)
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
getFloatValue(m_pModel->mCurrentMaterial->anisotropy);
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
} break;
default: {
@ -282,6 +282,7 @@ void ObjFileMtlImporter::getColorRGBA(aiColor3D *pColor) {
pColor->b = b;
}
// -------------------------------------------------------------------
void ObjFileMtlImporter::getColorRGBA(Maybe<aiColor3D> &value) {
aiColor3D v;
getColorRGBA(&v);
@ -309,6 +310,7 @@ void ObjFileMtlImporter::getFloatValue(ai_real &value) {
value = (ai_real)fast_atof(&m_buffer[0]);
}
// -------------------------------------------------------------------
void ObjFileMtlImporter::getFloatValue(Maybe<ai_real> &value) {
m_DataIt = CopyNextWord<DataArrayIt>(m_DataIt, m_DataItEnd, &m_buffer[0], BUFFERSIZE);
size_t len = std::strlen(&m_buffer[0]);
@ -341,7 +343,7 @@ void ObjFileMtlImporter::createMaterial() {
}
}
name = trim_whitespaces(name);
name = ai_trim(name);
std::map<std::string, ObjFile::Material *>::iterator it = m_pModel->mMaterialMap.find(name);
if (m_pModel->mMaterialMap.end() == it) {
@ -356,73 +358,79 @@ void ObjFileMtlImporter::createMaterial() {
}
} else {
// Use older material
m_pModel->mCurrentMaterial = (*it).second;
m_pModel->mCurrentMaterial = it->second;
}
}
// -------------------------------------------------------------------
// Gets a texture name from data.
void ObjFileMtlImporter::getTexture() {
aiString *out(nullptr);
aiString *out = nullptr;
int clampIndex = -1;
if (m_pModel->mCurrentMaterial == nullptr) {
m_pModel->mCurrentMaterial = new ObjFile::Material();
m_pModel->mCurrentMaterial->MaterialName.Set("Empty_Material");
m_pModel->mMaterialMap["Empty_Material"] = m_pModel->mCurrentMaterial;
}
const char *pPtr(&(*m_DataIt));
if (!ASSIMP_strincmp(pPtr, DiffuseTexture.c_str(), static_cast<unsigned int>(DiffuseTexture.size()))) {
if (!ASSIMP_strincmp(pPtr, DiffuseTexture, static_cast<unsigned int>(strlen(DiffuseTexture)))) {
// Diffuse texture
out = &m_pModel->mCurrentMaterial->texture;
clampIndex = ObjFile::Material::TextureDiffuseType;
} else if (!ASSIMP_strincmp(pPtr, AmbientTexture.c_str(), static_cast<unsigned int>(AmbientTexture.size()))) {
} else if (!ASSIMP_strincmp(pPtr, AmbientTexture, static_cast<unsigned int>(strlen(AmbientTexture)))) {
// Ambient texture
out = &m_pModel->mCurrentMaterial->textureAmbient;
clampIndex = ObjFile::Material::TextureAmbientType;
} else if (!ASSIMP_strincmp(pPtr, SpecularTexture.c_str(), static_cast<unsigned int>(SpecularTexture.size()))) {
} else if (!ASSIMP_strincmp(pPtr, SpecularTexture, static_cast<unsigned int>(strlen(SpecularTexture)))) {
// Specular texture
out = &m_pModel->mCurrentMaterial->textureSpecular;
clampIndex = ObjFile::Material::TextureSpecularType;
} else if (!ASSIMP_strincmp(pPtr, DisplacementTexture1.c_str(), static_cast<unsigned int>(DisplacementTexture1.size())) ||
!ASSIMP_strincmp(pPtr, DisplacementTexture2.c_str(), static_cast<unsigned int>(DisplacementTexture2.size()))) {
} else if (!ASSIMP_strincmp(pPtr, DisplacementTexture1, static_cast<unsigned int>(strlen(DisplacementTexture1))) ||
!ASSIMP_strincmp(pPtr, DisplacementTexture2, static_cast<unsigned int>(strlen(DisplacementTexture2)))) {
// Displacement texture
out = &m_pModel->mCurrentMaterial->textureDisp;
clampIndex = ObjFile::Material::TextureDispType;
} else if (!ASSIMP_strincmp(pPtr, OpacityTexture.c_str(), static_cast<unsigned int>(OpacityTexture.size()))) {
} else if (!ASSIMP_strincmp(pPtr, OpacityTexture, static_cast<unsigned int>(strlen(OpacityTexture)))) {
// Opacity texture
out = &m_pModel->mCurrentMaterial->textureOpacity;
clampIndex = ObjFile::Material::TextureOpacityType;
} else if (!ASSIMP_strincmp(pPtr, EmissiveTexture1.c_str(), static_cast<unsigned int>(EmissiveTexture1.size())) ||
!ASSIMP_strincmp(pPtr, EmissiveTexture2.c_str(), static_cast<unsigned int>(EmissiveTexture2.size()))) {
} else if (!ASSIMP_strincmp(pPtr, EmissiveTexture1, static_cast<unsigned int>(strlen(EmissiveTexture1))) ||
!ASSIMP_strincmp(pPtr, EmissiveTexture2, static_cast<unsigned int>(strlen(EmissiveTexture2)))) {
// Emissive texture
out = &m_pModel->mCurrentMaterial->textureEmissive;
clampIndex = ObjFile::Material::TextureEmissiveType;
} else if (!ASSIMP_strincmp(pPtr, BumpTexture1.c_str(), static_cast<unsigned int>(BumpTexture1.size())) ||
!ASSIMP_strincmp(pPtr, BumpTexture2.c_str(), static_cast<unsigned int>(BumpTexture2.size()))) {
} else if (!ASSIMP_strincmp(pPtr, BumpTexture1, static_cast<unsigned int>(strlen(BumpTexture1))) ||
!ASSIMP_strincmp(pPtr, BumpTexture2, static_cast<unsigned int>(strlen(BumpTexture2)))) {
// Bump texture
out = &m_pModel->mCurrentMaterial->textureBump;
clampIndex = ObjFile::Material::TextureBumpType;
} else if (!ASSIMP_strincmp(pPtr, NormalTextureV1.c_str(), static_cast<unsigned int>(NormalTextureV1.size())) || !ASSIMP_strincmp(pPtr, NormalTextureV2.c_str(), static_cast<unsigned int>(NormalTextureV2.size()))) {
} else if (!ASSIMP_strincmp(pPtr, NormalTextureV1, static_cast<unsigned int>(strlen(NormalTextureV1))) || !ASSIMP_strincmp(pPtr, NormalTextureV2, static_cast<unsigned int>(strlen(NormalTextureV2)))) {
// Normal map
out = &m_pModel->mCurrentMaterial->textureNormal;
clampIndex = ObjFile::Material::TextureNormalType;
} else if (!ASSIMP_strincmp(pPtr, ReflectionTexture.c_str(), static_cast<unsigned int>(ReflectionTexture.size()))) {
} else if (!ASSIMP_strincmp(pPtr, ReflectionTexture, static_cast<unsigned int>(strlen(ReflectionTexture)))) {
// Reflection texture(s)
//Do nothing here
return;
} else if (!ASSIMP_strincmp(pPtr, SpecularityTexture.c_str(), static_cast<unsigned int>(SpecularityTexture.size()))) {
} else if (!ASSIMP_strincmp(pPtr, SpecularityTexture, static_cast<unsigned int>(strlen(SpecularityTexture)))) {
// Specularity scaling (glossiness)
out = &m_pModel->mCurrentMaterial->textureSpecularity;
clampIndex = ObjFile::Material::TextureSpecularityType;
} else if ( !ASSIMP_strincmp( pPtr, RoughnessTexture.c_str(), static_cast<unsigned int>(RoughnessTexture.size()))) {
} else if ( !ASSIMP_strincmp( pPtr, RoughnessTexture, static_cast<unsigned int>(strlen(RoughnessTexture)))) {
// PBR Roughness texture
out = & m_pModel->mCurrentMaterial->textureRoughness;
clampIndex = ObjFile::Material::TextureRoughnessType;
} else if ( !ASSIMP_strincmp( pPtr, MetallicTexture.c_str(), static_cast<unsigned int>(MetallicTexture.size()))) {
} else if ( !ASSIMP_strincmp( pPtr, MetallicTexture, static_cast<unsigned int>(strlen(MetallicTexture)))) {
// PBR Metallic texture
out = & m_pModel->mCurrentMaterial->textureMetallic;
clampIndex = ObjFile::Material::TextureMetallicType;
} else if (!ASSIMP_strincmp( pPtr, SheenTexture.c_str(), static_cast<unsigned int>(SheenTexture.size()))) {
} else if (!ASSIMP_strincmp( pPtr, SheenTexture, static_cast<unsigned int>(strlen(SheenTexture)))) {
// PBR Sheen (reflectance) texture
out = & m_pModel->mCurrentMaterial->textureSheen;
clampIndex = ObjFile::Material::TextureSheenType;
} else if (!ASSIMP_strincmp( pPtr, RMATexture.c_str(), static_cast<unsigned int>(RMATexture.size()))) {
} else if (!ASSIMP_strincmp( pPtr, RMATexture, static_cast<unsigned int>(strlen(RMATexture)))) {
// PBR Rough/Metal/AO texture
out = & m_pModel->mCurrentMaterial->textureRMA;
clampIndex = ObjFile::Material::TextureRMAType;
@ -466,7 +474,7 @@ void ObjFileMtlImporter::getTextureOption(bool &clamp, int &clampIndex, aiString
//skip option key and value
int skipToken = 1;
if (!ASSIMP_strincmp(pPtr, ClampOption.c_str(), static_cast<unsigned int>(ClampOption.size()))) {
if (!ASSIMP_strincmp(pPtr, ClampOption, static_cast<unsigned int>(strlen(ClampOption)))) {
DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
char value[3];
CopyNextWord(it, m_DataItEnd, value, sizeof(value) / sizeof(*value));
@ -475,7 +483,7 @@ void ObjFileMtlImporter::getTextureOption(bool &clamp, int &clampIndex, aiString
}
skipToken = 2;
} else if (!ASSIMP_strincmp(pPtr, TypeOption.c_str(), static_cast<unsigned int>(TypeOption.size()))) {
} else if (!ASSIMP_strincmp(pPtr, TypeOption, static_cast<unsigned int>(strlen(TypeOption)))) {
DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
char value[12];
CopyNextWord(it, m_DataItEnd, value, sizeof(value) / sizeof(*value));
@ -503,15 +511,21 @@ void ObjFileMtlImporter::getTextureOption(bool &clamp, int &clampIndex, aiString
}
skipToken = 2;
} else if (!ASSIMP_strincmp(pPtr, BumpOption.c_str(), static_cast<unsigned int>(BumpOption.size()))) {
} else if (!ASSIMP_strincmp(pPtr, BumpOption, static_cast<unsigned int>(strlen(BumpOption)))) {
DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
getFloat(it, m_DataItEnd, m_pModel->mCurrentMaterial->bump_multiplier);
skipToken = 2;
} else if (!ASSIMP_strincmp(pPtr, BlendUOption.c_str(), static_cast<unsigned int>(BlendUOption.size())) || !ASSIMP_strincmp(pPtr, BlendVOption.c_str(), static_cast<unsigned int>(BlendVOption.size())) || !ASSIMP_strincmp(pPtr, BoostOption.c_str(), static_cast<unsigned int>(BoostOption.size())) || !ASSIMP_strincmp(pPtr, ResolutionOption.c_str(), static_cast<unsigned int>(ResolutionOption.size())) || !ASSIMP_strincmp(pPtr, ChannelOption.c_str(), static_cast<unsigned int>(ChannelOption.size()))) {
} else if (!ASSIMP_strincmp(pPtr, BlendUOption, static_cast<unsigned int>(strlen(BlendUOption))) ||
!ASSIMP_strincmp(pPtr, BlendVOption, static_cast<unsigned int>(strlen(BlendVOption))) ||
!ASSIMP_strincmp(pPtr, BoostOption, static_cast<unsigned int>(strlen(BoostOption))) ||
!ASSIMP_strincmp(pPtr, ResolutionOption, static_cast<unsigned int>(strlen(ResolutionOption))) ||
!ASSIMP_strincmp(pPtr, ChannelOption, static_cast<unsigned int>(strlen(ChannelOption)))) {
skipToken = 2;
} else if (!ASSIMP_strincmp(pPtr, ModifyMapOption.c_str(), static_cast<unsigned int>(ModifyMapOption.size()))) {
} else if (!ASSIMP_strincmp(pPtr, ModifyMapOption, static_cast<unsigned int>(strlen(ModifyMapOption)))) {
skipToken = 3;
} else if (!ASSIMP_strincmp(pPtr, OffsetOption.c_str(), static_cast<unsigned int>(OffsetOption.size())) || !ASSIMP_strincmp(pPtr, ScaleOption.c_str(), static_cast<unsigned int>(ScaleOption.size())) || !ASSIMP_strincmp(pPtr, TurbulenceOption.c_str(), static_cast<unsigned int>(TurbulenceOption.size()))) {
} else if (!ASSIMP_strincmp(pPtr, OffsetOption, static_cast<unsigned int>(strlen(OffsetOption))) ||
!ASSIMP_strincmp(pPtr, ScaleOption, static_cast<unsigned int>(strlen(ScaleOption))) ||
!ASSIMP_strincmp(pPtr, TurbulenceOption, static_cast<unsigned int>(strlen(TurbulenceOption)))) {
skipToken = 4;
}

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -52,7 +52,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cstdlib>
#include <memory>
#include <utility>
#include <string_view>
namespace Assimp {
@ -64,6 +63,7 @@ ObjFileParser::ObjFileParser() :
m_pModel(nullptr),
m_uiLine(0),
m_buffer(),
mEnd(&m_buffer[Buffersize]),
m_pIO(nullptr),
m_progress(nullptr),
m_originalObjFileName() {
@ -97,8 +97,6 @@ ObjFileParser::ObjFileParser(IOStreamBuffer<char> &streamBuffer, const std::stri
parseFile(streamBuffer);
}
ObjFileParser::~ObjFileParser() = default;
void ObjFileParser::setBuffer(std::vector<char> &buffer) {
m_DataIt = buffer.begin();
m_DataItEnd = buffer.end();
@ -121,6 +119,7 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
while (streamBuffer.getNextDataLine(buffer, '\\')) {
m_DataIt = buffer.begin();
m_DataItEnd = buffer.end();
mEnd = &buffer[buffer.size() - 1] + 1;
// Handle progress reporting
const size_t filePos(streamBuffer.getFilePos());
@ -130,7 +129,7 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
m_progress->UpdateFileRead(processed, progressTotal);
}
// handle cstype section end (http://paulbourke.net/dataformats/obj/)
// handle c-stype section end (http://paulbourke.net/dataformats/obj/)
if (insideCstype) {
switch (*m_DataIt) {
case 'e': {
@ -156,9 +155,17 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
// read in vertex definition (homogeneous coords)
getHomogeneousVector3(m_pModel->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));
}
// read vertex and vertex-color
getTwoVectors3(m_pModel->mVertices, m_pModel->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));
}
} else if (*m_DataIt == 't') {
// read in texture coordinate ( 2D or 3D )
++m_DataIt;
@ -236,7 +243,7 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
getNameNoSpace(m_DataIt, m_DataItEnd, name);
insideCstype = name == "cstype";
goto pf_skip_line;
} break;
}
default: {
pf_skip_line:
@ -293,18 +300,19 @@ size_t ObjFileParser::getNumComponentsInDataDefinition() {
} else if (IsLineEnd(*tmp)) {
end_of_definition = true;
}
if (!SkipSpaces(&tmp)) {
if (!SkipSpaces(&tmp, mEnd)) {
break;
}
const bool isNum(IsNumeric(*tmp) || isNanOrInf(tmp));
SkipToken(tmp);
SkipToken(tmp, mEnd);
if (isNum) {
++numComponents;
}
if (!SkipSpaces(&tmp)) {
if (!SkipSpaces(&tmp, mEnd)) {
break;
}
}
return numComponents;
}
@ -440,7 +448,7 @@ void ObjFileParser::getFace(aiPrimitiveType type) {
const bool vt = (!m_pModel->mTextureCoord.empty());
const bool vn = (!m_pModel->mNormals.empty());
int iPos = 0;
while (m_DataIt != m_DataItEnd) {
while (m_DataIt < m_DataItEnd) {
int iStep = 1;
if (IsLineEnd(*m_DataIt)) {
@ -456,9 +464,20 @@ void ObjFileParser::getFace(aiPrimitiveType type) {
iPos = 0;
} else {
//OBJ USES 1 Base ARRAYS!!!!
const char *token = &(*m_DataIt);
const int iVal = ::atoi(token);
int iVal;
auto end = m_DataIt;
// find either the buffer end or the '\0'
while (end < m_DataItEnd && *end != '\0')
++end;
// avoid temporary string allocation if there is a zero
if (end != m_DataItEnd) {
iVal = ::atoi(&(*m_DataIt));
} else {
// otherwise make a zero terminated copy, which is safe to pass to atoi
std::string number(&(*m_DataIt), m_DataItEnd - m_DataIt);
iVal = ::atoi(number.c_str());
}
// increment iStep position based off of the sign and # of digits
int tmp = iVal;
if (iVal < 0) {
@ -468,8 +487,9 @@ void ObjFileParser::getFace(aiPrimitiveType type) {
++iStep;
}
if (iPos == 1 && !vt && vn)
if (iPos == 1 && !vt && vn) {
iPos = 2; // skip texture coords for normals if there are no tex coords
}
if (iVal > 0) {
// Store parsed index
@ -557,14 +577,17 @@ void ObjFileParser::getMaterialDesc() {
// Get name
std::string strName(pStart, &(*m_DataIt));
strName = trim_whitespaces(strName);
if (strName.empty())
strName = ai_trim(strName);
if (strName.empty()) {
skip = true;
}
// If the current mesh has the same material, we simply ignore that 'usemtl' command
// 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 (m_pModel->mCurrentMaterial && m_pModel->mCurrentMaterial->MaterialName == aiString(strName)) {
skip = true;
if (!skip) {
if (m_pModel->mCurrentMaterial && m_pModel->mCurrentMaterial->MaterialName == aiString(strName)) {
skip = true;
}
}
if (!skip) {
@ -586,7 +609,8 @@ void ObjFileParser::getMaterialDesc() {
}
if (needsNewMesh(strName)) {
createMesh(strName);
auto newMeshName = m_pModel->mActiveGroup.empty() ? strName : m_pModel->mActiveGroup;
createMesh(newMeshName);
}
m_pModel->mCurrentMesh->m_uiMaterialIndex = getMaterialIndex(strName);

View file

@ -41,6 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef OBJ_FILEPARSER_H_INC
#define OBJ_FILEPARSER_H_INC
#include "ObjFileData.h"
#include <assimp/IOStreamBuffer.h>
#include <assimp/material.h>
#include <assimp/mesh.h>
@ -53,14 +55,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
namespace ObjFile {
struct Model;
struct Object;
struct Material;
struct Point3;
struct Point2;
} // namespace ObjFile
class ObjFileImporter;
class IOSystem;
class ProgressHandler;
@ -79,7 +73,7 @@ public:
/// @brief Constructor with data array.
ObjFileParser(IOStreamBuffer<char> &streamBuffer, const std::string &modelName, IOSystem *io, ProgressHandler *progress, const std::string &originalObjFileName);
/// @brief Destructor
~ObjFileParser();
~ObjFileParser() = default;
/// @brief If you want to load in-core data.
void setBuffer(std::vector<char> &buffer);
/// @brief Model getter.
@ -149,6 +143,7 @@ private:
unsigned int m_uiLine;
//! Helper buffer
char m_buffer[Buffersize];
const char *mEnd;
/// Pointer to IO system instance.
IOSystem *m_pIO;
//! Pointer to progress handler

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2021, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -51,7 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
/**
/**
* @brief Returns true, if the last entry of the buffer is reached.
* @param[in] it Iterator of current position.
* @param[in] end Iterator with end of buffer.
@ -67,7 +67,7 @@ inline bool isEndOfBuffer(char_t it, char_t end) {
return (it == end);
}
/**
/**
* @brief Returns next word separated by a space
* @param[in] pBuffer Pointer to data buffer
* @param[in] pEnd Pointer to end of buffer
@ -85,7 +85,7 @@ inline Char_T getNextWord(Char_T pBuffer, Char_T pEnd) {
return pBuffer;
}
/**
/**
* @brief Returns pointer a next token
* @param[in] pBuffer Pointer to data buffer
* @param[in] pEnd Pointer to end of buffer
@ -102,7 +102,7 @@ inline Char_T getNextToken(Char_T pBuffer, Char_T pEnd) {
return getNextWord(pBuffer, pEnd);
}
/**
/**
* @brief Skips a line
* @param[in] it Iterator set to current position
* @param[in] end Iterator set to end of scratch buffer for readout
@ -111,6 +111,10 @@ inline Char_T getNextToken(Char_T pBuffer, Char_T pEnd) {
*/
template <class char_t>
inline char_t skipLine(char_t it, char_t end, unsigned int &uiLine) {
if (it >= end) {
return it;
}
while (!isEndOfBuffer(it, end) && !IsLineEnd(*it)) {
++it;
}
@ -127,9 +131,9 @@ inline char_t skipLine(char_t it, char_t end, unsigned int &uiLine) {
return it;
}
/**
/**
* @brief Get a name from the current line. Preserve space in the middle,
* but trim it at the end.
* but trim it at the end.
* @param[in] it set to current position
* @param[in] end set to end of scratch buffer for readout
* @param[out] name Separated name
@ -158,13 +162,13 @@ inline char_t getName(char_t it, char_t end, std::string &name) {
std::string strName(pStart, &(*it));
if (!strName.empty()) {
name = strName;
}
}
return it;
}
/**
/**
* @brief Get a name from the current line. Do not preserve space
* in the middle, but trim it at the end.
* @param it set to current position
@ -198,11 +202,11 @@ inline char_t getNameNoSpace(char_t it, char_t end, std::string &name) {
if (!strName.empty()) {
name = strName;
}
return it;
}
/**
/**
* @brief Get next word from given line
* @param[in] it set to current position
* @param[in] end set to end of scratch buffer for readout
@ -226,7 +230,7 @@ inline char_t CopyNextWord(char_t it, char_t end, char *pBuffer, size_t length)
return it;
}
/**
/**
* @brief Get next float from given line
* @param[in] it set to current position
* @param[in] end set to end of scratch buffer for readout
@ -243,22 +247,6 @@ inline char_t getFloat(char_t it, char_t end, ai_real &value) {
return it;
}
/**
* @brief Will remove white-spaces for a string.
* @param[in] str The string to clean
* @return The trimmed string.
*/
template <class string_type>
inline string_type trim_whitespaces(string_type str) {
while (!str.empty() && IsSpace(str[0])) {
str.erase(0);
}
while (!str.empty() && IsSpace(str[str.length() - 1])) {
str.erase(str.length() - 1);
}
return str;
}
/**
* @brief Checks for a line-end.
* @param[in] it Current iterator in string.