summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/Obj
diff options
context:
space:
mode:
Diffstat (limited to 'libs/assimp/code/AssetLib/Obj')
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjExporter.cpp414
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjExporter.h190
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjFileData.h344
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjFileImporter.cpp783
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjFileImporter.h122
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.cpp497
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.h113
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjFileParser.cpp838
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjFileParser.h165
-rw-r--r--libs/assimp/code/AssetLib/Obj/ObjTools.h284
10 files changed, 3750 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/Obj/ObjExporter.cpp b/libs/assimp/code/AssetLib/Obj/ObjExporter.cpp
new file mode 100644
index 0000000..882f3a9
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Obj/ObjExporter.cpp
@@ -0,0 +1,414 @@
+/*
+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_EXPORT
+#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
+
+#include "ObjExporter.h"
+#include <assimp/Exceptional.h>
+#include <assimp/StringComparison.h>
+#include <assimp/version.h>
+#include <assimp/IOSystem.hpp>
+#include <assimp/Exporter.hpp>
+#include <assimp/material.h>
+#include <assimp/scene.h>
+#include <memory>
+
+using namespace Assimp;
+
+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*/) {
+ // invoke the exporter
+ ObjExporter exporter(pFile, pScene);
+
+ if (exporter.mOutput.fail() || exporter.mOutputMat.fail()) {
+ throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
+ }
+
+ // we're still here - export successfully completed. Write both the main OBJ file and the material script
+ {
+ std::unique_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt"));
+ if (outfile == nullptr) {
+ throw DeadlyExportError("could not open output .obj file: " + std::string(pFile));
+ }
+ outfile->Write( exporter.mOutput.str().c_str(), static_cast<size_t>(exporter.mOutput.tellp()),1);
+ }
+ {
+ std::unique_ptr<IOStream> outfile (pIOSystem->Open(exporter.GetMaterialLibFileName(),"wt"));
+ if (outfile == nullptr) {
+ throw DeadlyExportError("could not open output .mtl file: " + std::string(exporter.GetMaterialLibFileName()));
+ }
+ outfile->Write( exporter.mOutputMat.str().c_str(), static_cast<size_t>(exporter.mOutputMat.tellp()),1);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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* ) {
+ // invoke the exporter
+ ObjExporter exporter(pFile, pScene, true);
+
+ if (exporter.mOutput.fail() || exporter.mOutputMat.fail()) {
+ throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile));
+ }
+
+ // we're still here - export successfully completed. Write both the main OBJ file and the material script
+ {
+ std::unique_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt"));
+ if (outfile == nullptr) {
+ throw DeadlyExportError("could not open output .obj file: " + std::string(pFile));
+ }
+ outfile->Write( exporter.mOutput.str().c_str(), static_cast<size_t>(exporter.mOutput.tellp()),1);
+ }
+
+
+}
+
+} // end of namespace Assimp
+
+static const std::string MaterialExt = ".mtl";
+
+// ------------------------------------------------------------------------------------------------
+ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMtl)
+: filename(_filename)
+, pScene(pScene)
+, vn()
+, vt()
+, vp()
+, useVc(false)
+, mVnMap()
+, mVtMap()
+, mVpMap()
+, mMeshes()
+, endl("\n") {
+ // make sure that all formatting happens using the standard, C locale and not the user's current locale
+ const std::locale& l = std::locale("C");
+ mOutput.imbue(l);
+ mOutput.precision(ASSIMP_AI_REAL_TEXT_PRECISION);
+ mOutputMat.imbue(l);
+ mOutputMat.precision(ASSIMP_AI_REAL_TEXT_PRECISION);
+
+ WriteGeometryFile(noMtl);
+ if ( !noMtl ) {
+ WriteMaterialFile();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+ObjExporter::~ObjExporter() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string ObjExporter::GetMaterialLibName() {
+ // within the Obj file, we use just the relative file name with the path stripped
+ const std::string& s = GetMaterialLibFileName();
+ std::string::size_type il = s.find_last_of("/\\");
+ if (il != std::string::npos) {
+ return s.substr(il + 1);
+ }
+
+ return s;
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string ObjExporter::GetMaterialLibFileName() {
+ // Remove existing .obj file extension so that the final material file name will be fileName.mtl and not fileName.obj.mtl
+ size_t lastdot = filename.find_last_of('.');
+ if ( lastdot != std::string::npos ) {
+ return filename.substr( 0, lastdot ) + MaterialExt;
+ }
+
+ return filename + MaterialExt;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ObjExporter::WriteHeader(std::ostringstream& out) {
+ out << "# File produced by Open Asset Import Library (http://www.assimp.sf.net)" << endl;
+ out << "# (assimp v" << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.'
+ << aiGetVersionRevision() << ")" << endl << endl;
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string ObjExporter::GetMaterialName(unsigned int index) {
+ const aiMaterial* const mat = pScene->mMaterials[index];
+ if ( nullptr == mat ) {
+ static const std::string EmptyStr;
+ return EmptyStr;
+ }
+
+ aiString s;
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_NAME,s)) {
+ return std::string(s.data,s.length);
+ }
+
+ char number[ sizeof(unsigned int) * 3 + 1 ];
+ ASSIMP_itoa10(number,index);
+ return "$Material_" + std::string(number);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ObjExporter::WriteMaterialFile() {
+ WriteHeader(mOutputMat);
+
+ for(unsigned int i = 0; i < pScene->mNumMaterials; ++i) {
+ const aiMaterial* const mat = pScene->mMaterials[i];
+
+ int illum = 1;
+ mOutputMat << "newmtl " << GetMaterialName(i) << endl;
+
+ aiColor4D c;
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_DIFFUSE,c)) {
+ mOutputMat << "Kd " << c.r << " " << c.g << " " << c.b << endl;
+ }
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_AMBIENT,c)) {
+ mOutputMat << "Ka " << c.r << " " << c.g << " " << c.b << endl;
+ }
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_SPECULAR,c)) {
+ mOutputMat << "Ks " << c.r << " " << c.g << " " << c.b << endl;
+ }
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_EMISSIVE,c)) {
+ mOutputMat << "Ke " << c.r << " " << c.g << " " << c.b << endl;
+ }
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_TRANSPARENT,c)) {
+ mOutputMat << "Tf " << c.r << " " << c.g << " " << c.b << endl;
+ }
+
+ ai_real o;
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_OPACITY,o)) {
+ mOutputMat << "d " << o << endl;
+ }
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_REFRACTI,o)) {
+ mOutputMat << "Ni " << o << endl;
+ }
+
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_SHININESS,o) && o) {
+ mOutputMat << "Ns " << o << endl;
+ illum = 2;
+ }
+
+ mOutputMat << "illum " << illum << endl;
+
+ aiString s;
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_DIFFUSE(0),s)) {
+ mOutputMat << "map_Kd " << s.data << endl;
+ }
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_AMBIENT(0),s)) {
+ mOutputMat << "map_Ka " << s.data << endl;
+ }
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_SPECULAR(0),s)) {
+ mOutputMat << "map_Ks " << s.data << endl;
+ }
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_SHININESS(0),s)) {
+ mOutputMat << "map_Ns " << s.data << endl;
+ }
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_OPACITY(0),s)) {
+ mOutputMat << "map_d " << s.data << endl;
+ }
+ if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_HEIGHT(0),s) || AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_NORMALS(0),s)) {
+ // implementations seem to vary here, so write both variants
+ mOutputMat << "bump " << s.data << endl;
+ mOutputMat << "map_bump " << s.data << endl;
+ }
+
+ mOutputMat << endl;
+ }
+}
+
+void ObjExporter::WriteGeometryFile(bool noMtl) {
+ WriteHeader(mOutput);
+ if (!noMtl)
+ mOutput << "mtllib " << GetMaterialLibName() << endl << endl;
+
+ // collect mesh geometry
+ aiMatrix4x4 mBase;
+ AddNode(pScene->mRootNode, mBase);
+
+ // write vertex positions with colors, if any
+ mVpMap.getKeys( vp );
+ if ( !useVc ) {
+ mOutput << "# " << vp.size() << " vertex positions" << endl;
+ for ( const vertexData& v : vp ) {
+ mOutput << "v " << v.vp.x << " " << v.vp.y << " " << v.vp.z << endl;
+ }
+ } else {
+ mOutput << "# " << vp.size() << " vertex positions and colors" << endl;
+ for ( const vertexData& v : vp ) {
+ mOutput << "v " << v.vp.x << " " << v.vp.y << " " << v.vp.z << " " << v.vc.r << " " << v.vc.g << " " << v.vc.b << endl;
+ }
+ }
+ mOutput << endl;
+
+ // write uv coordinates
+ mVtMap.getKeys(vt);
+ mOutput << "# " << vt.size() << " UV coordinates" << endl;
+ for(const aiVector3D& v : vt) {
+ mOutput << "vt " << v.x << " " << v.y << " " << v.z << endl;
+ }
+ mOutput << endl;
+
+ // write vertex normals
+ mVnMap.getKeys(vn);
+ mOutput << "# " << vn.size() << " vertex normals" << endl;
+ for(const aiVector3D& v : vn) {
+ mOutput << "vn " << v.x << " " << v.y << " " << v.z << endl;
+ }
+ mOutput << endl;
+
+ // now write all mesh instances
+ for(const MeshInstance& m : mMeshes) {
+ mOutput << "# Mesh \'" << m.name << "\' with " << m.faces.size() << " faces" << endl;
+ if (!m.name.empty()) {
+ mOutput << "g " << m.name << endl;
+ }
+ if ( !noMtl ) {
+ mOutput << "usemtl " << m.matname << endl;
+ }
+
+ for(const Face& f : m.faces) {
+ mOutput << f.kind << ' ';
+ for(const FaceVertex& fv : f.indices) {
+ mOutput << ' ' << fv.vp;
+
+ if (f.kind != 'p') {
+ if (fv.vt || f.kind == 'f') {
+ mOutput << '/';
+ }
+ if (fv.vt) {
+ mOutput << fv.vt;
+ }
+ if (f.kind == 'f' && fv.vn) {
+ mOutput << '/' << fv.vn;
+ }
+ }
+ }
+
+ mOutput << endl;
+ }
+ mOutput << endl;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) {
+ mMeshes.push_back(MeshInstance() );
+ MeshInstance& mesh = mMeshes.back();
+
+ if ( nullptr != m->mColors[ 0 ] ) {
+ useVc = true;
+ }
+
+ mesh.name = std::string( name.data, name.length );
+ mesh.matname = GetMaterialName(m->mMaterialIndex);
+
+ mesh.faces.resize(m->mNumFaces);
+
+ for(unsigned int i = 0; i < m->mNumFaces; ++i) {
+ const aiFace& f = m->mFaces[i];
+
+ Face& face = mesh.faces[i];
+ switch (f.mNumIndices) {
+ case 1:
+ face.kind = 'p';
+ break;
+ case 2:
+ face.kind = 'l';
+ break;
+ default:
+ face.kind = 'f';
+ }
+ face.indices.resize(f.mNumIndices);
+
+ for(unsigned int a = 0; a < f.mNumIndices; ++a) {
+ const unsigned int idx = f.mIndices[a];
+
+ 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)});
+ } else {
+ face.indices[a].vp = mVpMap.getIndex({vert, aiColor3D(0,0,0)});
+ }
+
+ if (m->mNormals) {
+ aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx];
+ face.indices[a].vn = mVnMap.getIndex(norm);
+ } else {
+ face.indices[a].vn = 0;
+ }
+
+ if ( m->mTextureCoords[ 0 ] ) {
+ face.indices[a].vt = mVtMap.getIndex(m->mTextureCoords[0][idx]);
+ } else {
+ face.indices[a].vt = 0;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ObjExporter::AddNode(const aiNode* nd, const aiMatrix4x4& mParent) {
+ 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);
+ } else {
+ AddMesh(nd->mName, pScene->mMeshes[nd->mMeshes[i]], mAbs);
+ }
+ }
+
+ for(unsigned int i = 0; i < nd->mNumChildren; ++i) {
+ AddNode(nd->mChildren[i], mAbs);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+
+#endif // ASSIMP_BUILD_NO_OBJ_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT
diff --git a/libs/assimp/code/AssetLib/Obj/ObjExporter.h b/libs/assimp/code/AssetLib/Obj/ObjExporter.h
new file mode 100644
index 0000000..a64f38f
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Obj/ObjExporter.h
@@ -0,0 +1,190 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ObjExporter.h
+ * Declares the exporter class to write a scene to a Collada file
+ */
+#ifndef AI_OBJEXPORTER_H_INC
+#define AI_OBJEXPORTER_H_INC
+
+#include <assimp/types.h>
+#include <sstream>
+#include <vector>
+#include <map>
+
+struct aiScene;
+struct aiNode;
+struct aiMesh;
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------------------
+/** Helper class to export a given scene to an OBJ file. */
+// ------------------------------------------------------------------------------------------------
+class ObjExporter {
+public:
+ /// Constructor for a specific scene to export
+ ObjExporter(const char* filename, const aiScene* pScene, bool noMtl=false);
+ ~ObjExporter();
+ std::string GetMaterialLibName();
+ std::string GetMaterialLibFileName();
+
+ /// public string-streams to write all output into
+ std::ostringstream mOutput, mOutputMat;
+
+private:
+ // intermediate data structures
+ struct FaceVertex {
+ FaceVertex()
+ : vp()
+ , vn()
+ , vt() {
+ // empty
+ }
+
+ // one-based, 0 means: 'does not exist'
+ unsigned int vp, vn, vt;
+ };
+
+ struct Face {
+ char kind;
+ std::vector<FaceVertex> indices;
+ };
+
+ struct MeshInstance {
+ std::string name, matname;
+ std::vector<Face> faces;
+ };
+
+ void WriteHeader(std::ostringstream& out);
+ void WriteMaterialFile();
+ void WriteGeometryFile(bool noMtl=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);
+
+private:
+ std::string filename;
+ const aiScene* const pScene;
+
+ struct vertexData {
+ aiVector3D vp;
+ aiColor3D vc; // OBJ does not support 4D color
+ };
+
+ std::vector<aiVector3D> vn, vt;
+ std::vector<aiColor4D> vc;
+ std::vector<vertexData> vp;
+ bool useVc;
+
+ struct vertexDataCompare {
+ bool operator() ( const vertexData& a, const vertexData& b ) const {
+ // position
+ if (a.vp.x < b.vp.x) return true;
+ if (a.vp.x > b.vp.x) return false;
+ if (a.vp.y < b.vp.y) return true;
+ if (a.vp.y > b.vp.y) return false;
+ if (a.vp.z < b.vp.z) return true;
+ if (a.vp.z > b.vp.z) return false;
+
+ // color
+ if (a.vc.r < b.vc.r) return true;
+ if (a.vc.r > b.vc.r) return false;
+ if (a.vc.g < b.vc.g) return true;
+ 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;
+ }
+ };
+
+ struct aiVectorCompare {
+ bool operator() (const aiVector3D& a, const aiVector3D& b) const {
+ if(a.x < b.x) return true;
+ if(a.x > b.x) return false;
+ if(a.y < b.y) return true;
+ if(a.y > b.y) return false;
+ if(a.z < b.z) return true;
+ return false;
+ }
+ };
+
+ template <class T, class Compare = std::less<T>>
+ class indexMap {
+ int mNextIndex;
+ typedef std::map<T, int, Compare> dataType;
+ dataType vecMap;
+
+ public:
+ indexMap()
+ : mNextIndex(1) {
+ // empty
+ }
+
+ int getIndex(const T& key) {
+ typename dataType::iterator vertIt = vecMap.find(key);
+ // vertex already exists, so reference it
+ if(vertIt != vecMap.end()){
+ return vertIt->second;
+ }
+ return vecMap[key] = mNextIndex++;
+ };
+
+ void getKeys( std::vector<T>& keys ) {
+ keys.resize(vecMap.size());
+ for(typename dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){
+ keys[it->second-1] = it->first;
+ }
+ };
+ };
+
+ indexMap<aiVector3D, aiVectorCompare> mVnMap, mVtMap;
+ indexMap<vertexData, vertexDataCompare> mVpMap;
+ std::vector<MeshInstance> mMeshes;
+
+ // this endl() doesn't flush() the stream
+ const std::string endl;
+};
+
+}
+
+#endif
diff --git a/libs/assimp/code/AssetLib/Obj/ObjFileData.h b/libs/assimp/code/AssetLib/Obj/ObjFileData.h
new file mode 100644
index 0000000..3d504d0
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Obj/ObjFileData.h
@@ -0,0 +1,344 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+
+#pragma once
+#ifndef OBJ_FILEDATA_H_INC
+#define OBJ_FILEDATA_H_INC
+
+#include <assimp/mesh.h>
+#include <assimp/types.h>
+#include <map>
+#include <vector>
+
+namespace Assimp {
+namespace ObjFile {
+
+struct Object;
+struct Face;
+struct Material;
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Face
+//! \brief Data structure for a simple obj-face, describes discredit,l.ation and materials
+// ------------------------------------------------------------------------------------------------
+struct Face {
+ using IndexArray = std::vector<unsigned int>;
+
+ //! Primitive type
+ aiPrimitiveType m_PrimitiveType;
+ //! Vertex indices
+ IndexArray m_vertices;
+ //! Normal indices
+ IndexArray m_normals;
+ //! Texture coordinates indices
+ IndexArray m_texturCoords;
+ //! Pointer to assigned material
+ Material *m_pMaterial;
+
+ //! \brief Default constructor
+ Face(aiPrimitiveType pt = aiPrimitiveType_POLYGON) :
+ m_PrimitiveType(pt), m_vertices(), m_normals(), m_texturCoords(), m_pMaterial(0L) {
+ // empty
+ }
+
+ //! \brief Destructor
+ ~Face() {
+ // empty
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Object
+//! \brief Stores all objects of an obj-file object definition
+// ------------------------------------------------------------------------------------------------
+struct Object {
+ enum ObjectType {
+ ObjType,
+ GroupType
+ };
+
+ //! Object name
+ std::string m_strObjName;
+ //! Transformation matrix, stored in OpenGL format
+ aiMatrix4x4 m_Transformation;
+ //! All sub-objects referenced by this object
+ std::vector<Object *> m_SubObjects;
+ /// Assigned meshes
+ std::vector<unsigned int> m_Meshes;
+
+ //! \brief Default constructor
+ Object() = default;
+
+ //! \brief Destructor
+ ~Object() {
+ for (std::vector<Object *>::iterator it = m_SubObjects.begin(); it != m_SubObjects.end(); ++it) {
+ delete *it;
+ }
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Material
+//! \brief Data structure to store all material specific data
+// ------------------------------------------------------------------------------------------------
+struct Material {
+ //! Name of material description
+ aiString MaterialName;
+ //! Texture names
+ aiString texture;
+ aiString textureSpecular;
+ aiString textureAmbient;
+ aiString textureEmissive;
+ aiString textureBump;
+ aiString textureNormal;
+ aiString textureReflection[6];
+ aiString textureSpecularity;
+ aiString textureOpacity;
+ aiString textureDisp;
+ aiString textureRoughness;
+ aiString textureMetallic;
+ aiString textureSheen;
+ aiString textureRMA;
+
+ enum TextureType {
+ TextureDiffuseType = 0,
+ TextureSpecularType,
+ TextureAmbientType,
+ TextureEmissiveType,
+ TextureBumpType,
+ TextureNormalType,
+ TextureReflectionSphereType,
+ TextureReflectionCubeTopType,
+ TextureReflectionCubeBottomType,
+ TextureReflectionCubeFrontType,
+ TextureReflectionCubeBackType,
+ TextureReflectionCubeLeftType,
+ TextureReflectionCubeRightType,
+ TextureSpecularityType,
+ TextureOpacityType,
+ TextureDispType,
+ TextureRoughnessType,
+ TextureMetallicType,
+ TextureSheenType,
+ TextureRMAType,
+ TextureTypeCount
+ };
+ bool clamp[TextureTypeCount];
+
+ //! Ambient color
+ aiColor3D ambient;
+ //! Diffuse color
+ aiColor3D diffuse;
+ //! Specular color
+ aiColor3D specular;
+ //! Emissive color
+ aiColor3D emissive;
+ //! Alpha value
+ ai_real alpha;
+ //! Shineness factor
+ ai_real shineness;
+ //! Illumination model
+ int illumination_model;
+ //! Index of refraction
+ ai_real ior;
+ //! Transparency color
+ aiColor3D transparent;
+
+ //! PBR Roughness
+ ai_real roughness;
+ //! PBR Metallic
+ ai_real metallic;
+ //! PBR Metallic
+ aiColor3D sheen;
+ //! PBR Clearcoat Thickness
+ ai_real clearcoat_thickness;
+ //! PBR Clearcoat Rougness
+ ai_real clearcoat_roughness;
+ //! PBR Anisotropy
+ ai_real anisotropy;
+
+ //! bump map multipler (normal map scalar)(-bm)
+ ai_real bump_multiplier;
+
+ //! Constructor
+ Material() :
+ diffuse(ai_real(0.6), ai_real(0.6), ai_real(0.6)),
+ 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)),
+ roughness(ai_real(1.0)),
+ metallic(ai_real(0.0)),
+ sheen(ai_real(1.0), ai_real(1.0), ai_real(1.0)),
+ clearcoat_thickness(ai_real(0.0)),
+ clearcoat_roughness(ai_real(0.0)),
+ anisotropy(ai_real(0.0)),
+ bump_multiplier(ai_real(1.0)) {
+ std::fill_n(clamp, static_cast<unsigned int>(TextureTypeCount), false);
+ }
+
+ // Destructor
+ ~Material() = default;
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Mesh
+//! \brief Data structure to store a mesh
+// ------------------------------------------------------------------------------------------------
+struct Mesh {
+ static const unsigned int NoMaterial = ~0u;
+ /// The name for the mesh
+ std::string m_name;
+ /// Array with pointer to all stored faces
+ std::vector<Face *> m_Faces;
+ /// Assigned material
+ Material *m_pMaterial;
+ /// Number of stored indices.
+ unsigned int m_uiNumIndices;
+ /// Number of UV
+ unsigned int m_uiUVCoordinates[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ /// Material index.
+ 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) :
+ m_name(name),
+ m_pMaterial(nullptr),
+ m_uiNumIndices(0),
+ m_uiMaterialIndex(NoMaterial),
+ m_hasNormals(false) {
+ memset(m_uiUVCoordinates, 0, sizeof(unsigned int) * AI_MAX_NUMBER_OF_TEXTURECOORDS);
+ }
+
+ /// Destructor
+ ~Mesh() {
+ for (std::vector<Face *>::iterator it = m_Faces.begin();
+ it != m_Faces.end(); ++it) {
+ delete *it;
+ }
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Model
+//! \brief Data structure to store all obj-specific model data
+// ------------------------------------------------------------------------------------------------
+struct Model {
+ using GroupMap = std::map<std::string, std::vector<unsigned int> *>;
+ using GroupMapIt = std::map<std::string, std::vector<unsigned int> *>::iterator;
+ using ConstGroupMapIt = std::map<std::string, std::vector<unsigned int> *>::const_iterator;
+
+ //! Model name
+ std::string m_ModelName;
+ //! List ob assigned objects
+ std::vector<Object *> m_Objects;
+ //! Pointer to current object
+ ObjFile::Object *m_pCurrent;
+ //! Pointer to current material
+ ObjFile::Material *m_pCurrentMaterial;
+ //! Pointer to default material
+ ObjFile::Material *m_pDefaultMaterial;
+ //! Vector with all generated materials
+ std::vector<std::string> m_MaterialLib;
+ //! Vector with all generated vertices
+ std::vector<aiVector3D> m_Vertices;
+ //! vector with all generated normals
+ std::vector<aiVector3D> m_Normals;
+ //! vector with all vertex colors
+ std::vector<aiVector3D> m_VertexColors;
+ //! Group map
+ GroupMap m_Groups;
+ //! Group to face id assignment
+ std::vector<unsigned int> *m_pGroupFaceIDs;
+ //! Active group
+ std::string m_strActiveGroup;
+ //! Vector with generated texture coordinates
+ std::vector<aiVector3D> m_TextureCoord;
+ //! Maximum dimension of texture coordinates
+ unsigned int m_TextureCoordDim;
+ //! Current mesh instance
+ Mesh *m_pCurrentMesh;
+ //! Vector with stored meshes
+ std::vector<Mesh *> m_Meshes;
+ //! Material map
+ std::map<std::string, Material *> m_MaterialMap;
+
+ //! \brief The default class constructor
+ Model() :
+ m_ModelName(),
+ m_pCurrent(nullptr),
+ m_pCurrentMaterial(nullptr),
+ m_pDefaultMaterial(nullptr),
+ m_pGroupFaceIDs(nullptr),
+ m_strActiveGroup(),
+ m_TextureCoordDim(0),
+ m_pCurrentMesh(nullptr) {
+ // empty
+ }
+
+ //! \brief The class destructor
+ ~Model() {
+ for (auto & it : m_Objects) {
+ delete it;
+ }
+ for (auto & Meshe : m_Meshes) {
+ delete Meshe;
+ }
+ for (auto & Group : m_Groups) {
+ delete Group.second;
+ }
+ for (auto & it : m_MaterialMap) {
+ delete it.second;
+ }
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace ObjFile
+} // Namespace Assimp
+
+#endif // OBJ_FILEDATA_H_INC
diff --git a/libs/assimp/code/AssetLib/Obj/ObjFileImporter.cpp b/libs/assimp/code/AssetLib/Obj/ObjFileImporter.cpp
new file mode 100644
index 0000000..68fdb21
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Obj/ObjFileImporter.cpp
@@ -0,0 +1,783 @@
+/*
+---------------------------------------------------------------------------
+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 "ObjFileImporter.h"
+#include "ObjFileData.h"
+#include "ObjFileParser.h"
+#include <assimp/DefaultIOSystem.h>
+#include <assimp/IOStreamBuffer.h>
+#include <assimp/ai_assert.h>
+#include <assimp/importerdesc.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Importer.hpp>
+#include <assimp/ObjMaterial.h>
+#include <memory>
+
+static const aiImporterDesc desc = {
+ "Wavefront Object Importer",
+ "",
+ "",
+ "surfaces not supported",
+ aiImporterFlags_SupportTextFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "obj"
+};
+
+static const unsigned int ObjMinSize = 16;
+
+namespace Assimp {
+
+using namespace std;
+
+// ------------------------------------------------------------------------------------------------
+// Default constructor
+ObjFileImporter::ObjFileImporter() :
+ m_Buffer(),
+ m_pRootObject(nullptr),
+ m_strAbsPath(std::string(1, DefaultIOSystem().getOsSeparator())) {}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor.
+ObjFileImporter::~ObjFileImporter() {
+ delete m_pRootObject;
+ m_pRootObject = nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true if file is an obj file.
+bool ObjFileImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+ static const char *tokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " };
+ return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens), 200, false, true);
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiImporterDesc *ObjFileImporter::GetInfo() const {
+ return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Obj-file import implementation
+void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
+ // Read file into memory
+ static const std::string mode = "rb";
+ auto streamCloser = [&](IOStream *pStream) {
+ pIOHandler->Close(pStream);
+ };
+ std::unique_ptr<IOStream, decltype(streamCloser)> fileStream(pIOHandler->Open(file, mode), streamCloser);
+ if (!fileStream.get()) {
+ throw DeadlyImportError("Failed to open file ", file, ".");
+ }
+
+ // Get the file-size and validate it, throwing an exception when fails
+ size_t fileSize = fileStream->FileSize();
+ if (fileSize < ObjMinSize) {
+ throw DeadlyImportError("OBJ-file is too small.");
+ }
+
+ IOStreamBuffer<char> streamedBuffer;
+ streamedBuffer.open(fileStream.get());
+
+ // Allocate buffer and read file into it
+ //TextFileToBuffer( fileStream.get(),m_Buffer);
+
+ // Get the model name
+ std::string modelName, folderName;
+ std::string::size_type pos = file.find_last_of("\\/");
+ if (pos != std::string::npos) {
+ modelName = file.substr(pos + 1, file.size() - pos - 1);
+ folderName = file.substr(0, pos);
+ if (!folderName.empty()) {
+ pIOHandler->PushDirectory(folderName);
+ }
+ } else {
+ modelName = file;
+ }
+
+ // parse the file into a temporary representation
+ ObjFileParser parser(streamedBuffer, modelName, pIOHandler, m_progress, file);
+
+ // And create the proper return structures out of it
+ CreateDataFromImport(parser.GetModel(), pScene);
+
+ streamedBuffer.close();
+
+ // Clean up allocated storage for the next import
+ m_Buffer.clear();
+
+ // Pop directory stack
+ if (pIOHandler->StackSize() > 0) {
+ pIOHandler->PopDirectory();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Create the data from parsed obj-file
+void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene *pScene) {
+ if (nullptr == pModel) {
+ return;
+ }
+
+ // Create the root node of the scene
+ pScene->mRootNode = new aiNode;
+ if (!pModel->m_ModelName.empty()) {
+ // Set the name of the scene
+ pScene->mRootNode->mName.Set(pModel->m_ModelName);
+ } else {
+ // This is a fatal error, so break down the application
+ ai_assert(false);
+ }
+
+ if (!pModel->m_Objects.empty()) {
+
+ unsigned int meshCount = 0;
+ unsigned int childCount = 0;
+
+ for (auto object : pModel->m_Objects) {
+ if (object) {
+ ++childCount;
+ meshCount += (unsigned int)object->m_Meshes.size();
+ }
+ }
+
+ // Allocate space for the child nodes on the root node
+ pScene->mRootNode->mChildren = new aiNode *[childCount];
+
+ // Create nodes for the whole scene
+ std::vector<aiMesh *> MeshArray;
+ MeshArray.reserve(meshCount);
+ for (size_t index = 0; index < pModel->m_Objects.size(); ++index) {
+ createNodes(pModel, pModel->m_Objects[index], pScene->mRootNode, pScene, MeshArray);
+ }
+
+ ai_assert(pScene->mRootNode->mNumChildren == childCount);
+
+ // Create mesh pointer buffer for this scene
+ if (pScene->mNumMeshes > 0) {
+ pScene->mMeshes = new aiMesh *[MeshArray.size()];
+ for (size_t index = 0; index < MeshArray.size(); ++index) {
+ pScene->mMeshes[index] = MeshArray[index];
+ }
+ }
+
+ // Create all materials
+ createMaterials(pModel, pScene);
+ } else {
+ if (pModel->m_Vertices.empty()) {
+ return;
+ }
+
+ std::unique_ptr<aiMesh> mesh(new aiMesh);
+ mesh->mPrimitiveTypes = aiPrimitiveType_POINT;
+ unsigned int n = (unsigned int)pModel->m_Vertices.size();
+ mesh->mNumVertices = n;
+
+ mesh->mVertices = new aiVector3D[n];
+ memcpy(mesh->mVertices, pModel->m_Vertices.data(), n * sizeof(aiVector3D));
+
+ if (!pModel->m_Normals.empty()) {
+ mesh->mNormals = new aiVector3D[n];
+ if (pModel->m_Normals.size() < n) {
+ throw DeadlyImportError("OBJ: vertex normal index out of range");
+ }
+ memcpy(mesh->mNormals, pModel->m_Normals.data(), n * sizeof(aiVector3D));
+ }
+
+ if (!pModel->m_VertexColors.empty()) {
+ mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
+ for (unsigned int i = 0; i < n; ++i) {
+ if (i < pModel->m_VertexColors.size()) {
+ const aiVector3D &color = pModel->m_VertexColors[i];
+ mesh->mColors[0][i] = aiColor4D(color.x, color.y, color.z, 1.0);
+ } else {
+ throw DeadlyImportError("OBJ: vertex color index out of range");
+ }
+ }
+ }
+
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ pScene->mMeshes = new aiMesh *[1];
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes[0] = mesh.release();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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) {
+ return nullptr;
+ }
+
+ // Store older mesh size to be able to computes mesh offsets for new mesh instances
+ const size_t oldMeshSize = MeshArray.size();
+ aiNode *pNode = new aiNode;
+
+ pNode->mName = pObject->m_strObjName;
+
+ // If we have a parent node, store it
+ ai_assert(nullptr != pParent);
+ appendChildToParentNode(pParent, pNode);
+
+ 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) {
+ if (pMesh->mNumFaces > 0) {
+ MeshArray.push_back(pMesh);
+ } else {
+ delete pMesh;
+ }
+ }
+ }
+
+ // Create all nodes from the sub-objects stored in the current object
+ if (!pObject->m_SubObjects.empty()) {
+ size_t numChilds = pObject->m_SubObjects.size();
+ pNode->mNumChildren = static_cast<unsigned int>(numChilds);
+ pNode->mChildren = new aiNode *[numChilds];
+ pNode->mNumMeshes = 1;
+ pNode->mMeshes = new unsigned int[1];
+ }
+
+ // Set mesh instances into scene- and node-instances
+ const size_t meshSizeDiff = MeshArray.size() - oldMeshSize;
+ if (meshSizeDiff > 0) {
+ pNode->mMeshes = new unsigned int[meshSizeDiff];
+ pNode->mNumMeshes = static_cast<unsigned int>(meshSizeDiff);
+ size_t index = 0;
+ for (size_t i = oldMeshSize; i < MeshArray.size(); ++i) {
+ pNode->mMeshes[index] = pScene->mNumMeshes;
+ pScene->mNumMeshes++;
+ ++index;
+ }
+ }
+
+ return pNode;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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) {
+ return nullptr;
+ }
+
+ // Create faces
+ ObjFile::Mesh *pObjMesh = pModel->m_Meshes[meshIndex];
+ if (!pObjMesh) {
+ return nullptr;
+ }
+
+ if (pObjMesh->m_Faces.empty()) {
+ return nullptr;
+ }
+
+ std::unique_ptr<aiMesh> pMesh(new aiMesh);
+ if (!pObjMesh->m_name.empty()) {
+ pMesh->mName.Set(pObjMesh->m_name);
+ }
+
+ for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++) {
+ ObjFile::Face *const inp = pObjMesh->m_Faces[index];
+ ai_assert(nullptr != inp);
+
+ if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
+ pMesh->mNumFaces += static_cast<unsigned int>(inp->m_vertices.size() - 1);
+ pMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ } else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
+ pMesh->mNumFaces += static_cast<unsigned int>(inp->m_vertices.size());
+ pMesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ } else {
+ ++pMesh->mNumFaces;
+ if (inp->m_vertices.size() > 3) {
+ pMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ } else {
+ pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ }
+ }
+ }
+
+ 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);
+
+ // Copy all data from all stored meshes
+ for (auto &face : pObjMesh->m_Faces) {
+ ObjFile::Face *const inp = face;
+ if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
+ for (size_t i = 0; i < inp->m_vertices.size() - 1; ++i) {
+ aiFace &f = pMesh->mFaces[outIndex++];
+ uiIdxCount += f.mNumIndices = 2;
+ f.mIndices = new unsigned int[2];
+ }
+ continue;
+ } else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
+ for (size_t i = 0; i < inp->m_vertices.size(); ++i) {
+ aiFace &f = pMesh->mFaces[outIndex++];
+ uiIdxCount += f.mNumIndices = 1;
+ f.mIndices = new unsigned int[1];
+ }
+ continue;
+ }
+
+ aiFace *pFace = &pMesh->mFaces[outIndex++];
+ const unsigned int uiNumIndices = (unsigned int)face->m_vertices.size();
+ uiIdxCount += pFace->mNumIndices = (unsigned int)uiNumIndices;
+ if (pFace->mNumIndices > 0) {
+ pFace->mIndices = new unsigned int[uiNumIndices];
+ }
+ }
+ }
+
+ // Create mesh vertices
+ createVertexArray(pModel, pData, meshIndex, pMesh.get(), uiIdxCount);
+
+ return pMesh.release();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates a vertex array
+void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel,
+ const ObjFile::Object *pCurrentObject,
+ unsigned int uiMeshIndex,
+ aiMesh *pMesh,
+ unsigned int numIndices) {
+ // Checking preconditions
+ ai_assert(nullptr != pCurrentObject);
+
+ // Break, if no faces are stored in object
+ if (pCurrentObject->m_Meshes.empty())
+ return;
+
+ // Get current mesh
+ ObjFile::Mesh *pObjMesh = pModel->m_Meshes[uiMeshIndex];
+ if (nullptr == pObjMesh || pObjMesh->m_uiNumIndices < 1) {
+ return;
+ }
+
+ // Copy vertices of this mesh instance
+ pMesh->mNumVertices = numIndices;
+ if (pMesh->mNumVertices == 0) {
+ throw DeadlyImportError("OBJ: no vertices");
+ } else if (pMesh->mNumVertices > AI_MAX_VERTICES) {
+ throw DeadlyImportError("OBJ: Too many vertices");
+ }
+ pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+
+ // Allocate buffer for normal vectors
+ if (!pModel->m_Normals.empty() && pObjMesh->m_hasNormals)
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+
+ // Allocate buffer for vertex-color vectors
+ if (!pModel->m_VertexColors.empty())
+ pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
+
+ // Allocate buffer for texture coordinates
+ if (!pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0]) {
+ pMesh->mNumUVComponents[0] = pModel->m_TextureCoordDim;
+ pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
+ }
+
+ // Copy vertices, normals and textures into aiMesh instance
+ bool normalsok = true, uvok = true;
+ unsigned int newIndex = 0, outIndex = 0;
+ for (auto sourceFace : pObjMesh->m_Faces) {
+ // Copy all index arrays
+ for (size_t vertexIndex = 0, outVertexIndex = 0; vertexIndex < sourceFace->m_vertices.size(); vertexIndex++) {
+ const unsigned int vertex = sourceFace->m_vertices.at(vertexIndex);
+ if (vertex >= pModel->m_Vertices.size()) {
+ throw DeadlyImportError("OBJ: vertex index out of range");
+ }
+
+ if (pMesh->mNumVertices <= newIndex) {
+ throw DeadlyImportError("OBJ: bad vertex index");
+ }
+
+ pMesh->mVertices[newIndex] = pModel->m_Vertices[vertex];
+
+ // Copy all normals
+ if (normalsok && !pModel->m_Normals.empty() && vertexIndex < sourceFace->m_normals.size()) {
+ const unsigned int normal = sourceFace->m_normals.at(vertexIndex);
+ if (normal >= pModel->m_Normals.size()) {
+ normalsok = false;
+ } else {
+ pMesh->mNormals[newIndex] = pModel->m_Normals[normal];
+ }
+ }
+
+ // Copy all vertex colors
+ if (vertex < pModel->m_VertexColors.size()) {
+ const aiVector3D &color = pModel->m_VertexColors[vertex];
+ pMesh->mColors[0][newIndex] = aiColor4D(color.x, color.y, color.z, 1.0);
+ }
+
+ // Copy all texture coordinates
+ if (uvok && !pModel->m_TextureCoord.empty() && vertexIndex < sourceFace->m_texturCoords.size()) {
+ const unsigned int tex = sourceFace->m_texturCoords.at(vertexIndex);
+
+ if (tex >= pModel->m_TextureCoord.size()) {
+ uvok = false;
+ } else {
+ const aiVector3D &coord3d = pModel->m_TextureCoord[tex];
+ pMesh->mTextureCoords[0][newIndex] = aiVector3D(coord3d.x, coord3d.y, coord3d.z);
+ }
+ }
+
+ // Get destination face
+ aiFace *pDestFace = &pMesh->mFaces[outIndex];
+
+ const bool last = (vertexIndex == sourceFace->m_vertices.size() - 1);
+ if (sourceFace->m_PrimitiveType != aiPrimitiveType_LINE || !last) {
+ pDestFace->mIndices[outVertexIndex] = newIndex;
+ outVertexIndex++;
+ }
+
+ if (sourceFace->m_PrimitiveType == aiPrimitiveType_POINT) {
+ outIndex++;
+ outVertexIndex = 0;
+ } else if (sourceFace->m_PrimitiveType == aiPrimitiveType_LINE) {
+ outVertexIndex = 0;
+
+ if (!last)
+ outIndex++;
+
+ if (vertexIndex) {
+ if (!last) {
+ pMesh->mVertices[newIndex + 1] = pMesh->mVertices[newIndex];
+ if (!sourceFace->m_normals.empty() && !pModel->m_Normals.empty()) {
+ pMesh->mNormals[newIndex + 1] = pMesh->mNormals[newIndex];
+ }
+ if (!pModel->m_TextureCoord.empty()) {
+ for (size_t i = 0; i < pMesh->GetNumUVChannels(); i++) {
+ pMesh->mTextureCoords[i][newIndex + 1] = pMesh->mTextureCoords[i][newIndex];
+ }
+ }
+ ++newIndex;
+ }
+
+ pDestFace[-1].mIndices[1] = newIndex;
+ }
+ } else if (last) {
+ outIndex++;
+ }
+ ++newIndex;
+ }
+ }
+
+ if (!normalsok) {
+ delete[] pMesh->mNormals;
+ pMesh->mNormals = nullptr;
+ }
+
+ if (!uvok) {
+ delete[] pMesh->mTextureCoords[0];
+ pMesh->mTextureCoords[0] = nullptr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts all stored meshes
+void ObjFileImporter::countObjects(const std::vector<ObjFile::Object *> &rObjects, int &iNumMeshes) {
+ iNumMeshes = 0;
+ if (rObjects.empty())
+ return;
+
+ iNumMeshes += static_cast<unsigned int>(rObjects.size());
+ for (auto object : rObjects) {
+ if (!object->m_SubObjects.empty()) {
+ countObjects(object->m_SubObjects, iNumMeshes);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add clamp mode property to material if necessary
+void ObjFileImporter::addTextureMappingModeProperty(aiMaterial *mat, aiTextureType type, int clampMode, int index) {
+ if (nullptr == mat) {
+ return;
+ }
+
+ mat->AddProperty<int>(&clampMode, 1, AI_MATKEY_MAPPINGMODE_U(type, index));
+ mat->AddProperty<int>(&clampMode, 1, AI_MATKEY_MAPPINGMODE_V(type, index));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the material
+void ObjFileImporter::createMaterials(const ObjFile::Model *pModel, aiScene *pScene) {
+ if (nullptr == pScene) {
+ return;
+ }
+
+ const unsigned int numMaterials = (unsigned int)pModel->m_MaterialLib.size();
+ pScene->mNumMaterials = 0;
+ if (pModel->m_MaterialLib.empty()) {
+ ASSIMP_LOG_DEBUG("OBJ: no materials specified");
+ return;
+ }
+
+ pScene->mMaterials = new aiMaterial *[numMaterials];
+ for (unsigned int matIndex = 0; matIndex < numMaterials; matIndex++) {
+ // Store material name
+ std::map<std::string, ObjFile::Material *>::const_iterator it;
+ it = pModel->m_MaterialMap.find(pModel->m_MaterialLib[matIndex]);
+
+ // No material found, use the default material
+ if (pModel->m_MaterialMap.end() == it)
+ continue;
+
+ aiMaterial *mat = new aiMaterial;
+ ObjFile::Material *pCurrentMaterial = (*it).second;
+ mat->AddProperty(&pCurrentMaterial->MaterialName, AI_MATKEY_NAME);
+
+ // convert illumination model
+ int sm = 0;
+ switch (pCurrentMaterial->illumination_model) {
+ case 0:
+ sm = aiShadingMode_NoShading;
+ break;
+ case 1:
+ sm = aiShadingMode_Gouraud;
+ break;
+ case 2:
+ sm = aiShadingMode_Phong;
+ break;
+ default:
+ sm = aiShadingMode_Gouraud;
+ ASSIMP_LOG_ERROR("OBJ: unexpected illumination model (0-2 recognized)");
+ }
+
+ mat->AddProperty<int>(&sm, 1, AI_MATKEY_SHADING_MODEL);
+
+ // Preserve the original illum value
+ mat->AddProperty<int>(&pCurrentMaterial->illumination_model, 1, AI_MATKEY_OBJ_ILLUM);
+
+ // Adding material colors
+ mat->AddProperty(&pCurrentMaterial->ambient, 1, AI_MATKEY_COLOR_AMBIENT);
+ mat->AddProperty(&pCurrentMaterial->diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat->AddProperty(&pCurrentMaterial->specular, 1, AI_MATKEY_COLOR_SPECULAR);
+ mat->AddProperty(&pCurrentMaterial->emissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+ mat->AddProperty(&pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS);
+ mat->AddProperty(&pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY);
+ mat->AddProperty(&pCurrentMaterial->transparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
+ mat->AddProperty(&pCurrentMaterial->roughness, 1, AI_MATKEY_ROUGHNESS_FACTOR);
+ mat->AddProperty(&pCurrentMaterial->metallic, 1, AI_MATKEY_METALLIC_FACTOR);
+ mat->AddProperty(&pCurrentMaterial->sheen, 1, AI_MATKEY_SHEEN_COLOR_FACTOR);
+ mat->AddProperty(&pCurrentMaterial->clearcoat_thickness, 1, AI_MATKEY_CLEARCOAT_FACTOR);
+ mat->AddProperty(&pCurrentMaterial->clearcoat_roughness, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR);
+ mat->AddProperty(&pCurrentMaterial->anisotropy, 1, AI_MATKEY_ANISOTROPY_FACTOR);
+
+ // Adding refraction index
+ mat->AddProperty(&pCurrentMaterial->ior, 1, AI_MATKEY_REFRACTI);
+
+ // Adding textures
+ const int uvwIndex = 0;
+
+ if (0 != pCurrentMaterial->texture.length) {
+ mat->AddProperty(&pCurrentMaterial->texture, AI_MATKEY_TEXTURE_DIFFUSE(0));
+ mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_DIFFUSE(0));
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureDiffuseType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_DIFFUSE);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureAmbient.length) {
+ mat->AddProperty(&pCurrentMaterial->textureAmbient, AI_MATKEY_TEXTURE_AMBIENT(0));
+ mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_AMBIENT(0));
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureAmbientType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_AMBIENT);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureEmissive.length) {
+ mat->AddProperty(&pCurrentMaterial->textureEmissive, AI_MATKEY_TEXTURE_EMISSIVE(0));
+ mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_EMISSIVE(0));
+ }
+
+ if (0 != pCurrentMaterial->textureSpecular.length) {
+ mat->AddProperty(&pCurrentMaterial->textureSpecular, AI_MATKEY_TEXTURE_SPECULAR(0));
+ mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_SPECULAR(0));
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_SPECULAR);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureBump.length) {
+ mat->AddProperty(&pCurrentMaterial->textureBump, AI_MATKEY_TEXTURE_HEIGHT(0));
+ mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_HEIGHT(0));
+ if (pCurrentMaterial->bump_multiplier != 1.0) {
+ mat->AddProperty(&pCurrentMaterial->bump_multiplier, 1, AI_MATKEY_OBJ_BUMPMULT_HEIGHT(0));
+ }
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureBumpType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_HEIGHT);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureNormal.length) {
+ mat->AddProperty(&pCurrentMaterial->textureNormal, AI_MATKEY_TEXTURE_NORMALS(0));
+ mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_NORMALS(0));
+ if (pCurrentMaterial->bump_multiplier != 1.0) {
+ mat->AddProperty(&pCurrentMaterial->bump_multiplier, 1, AI_MATKEY_OBJ_BUMPMULT_NORMALS(0));
+ }
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureNormalType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_NORMALS);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureReflection[0].length) {
+ ObjFile::Material::TextureType type = 0 != pCurrentMaterial->textureReflection[1].length ?
+ ObjFile::Material::TextureReflectionCubeTopType :
+ ObjFile::Material::TextureReflectionSphereType;
+
+ unsigned count = type == ObjFile::Material::TextureReflectionSphereType ? 1 : 6;
+ for (unsigned i = 0; i < count; i++) {
+ mat->AddProperty(&pCurrentMaterial->textureReflection[i], AI_MATKEY_TEXTURE_REFLECTION(i));
+ mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_REFLECTION(i));
+
+ if (pCurrentMaterial->clamp[type])
+ addTextureMappingModeProperty(mat, aiTextureType_REFLECTION, 1, i);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureDisp.length) {
+ mat->AddProperty(&pCurrentMaterial->textureDisp, AI_MATKEY_TEXTURE_DISPLACEMENT(0));
+ mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_DISPLACEMENT(0));
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureDispType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_DISPLACEMENT);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureOpacity.length) {
+ mat->AddProperty(&pCurrentMaterial->textureOpacity, AI_MATKEY_TEXTURE_OPACITY(0));
+ mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_OPACITY(0));
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureOpacityType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_OPACITY);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureSpecularity.length) {
+ mat->AddProperty(&pCurrentMaterial->textureSpecularity, AI_MATKEY_TEXTURE_SHININESS(0));
+ mat->AddProperty(&uvwIndex, 1, AI_MATKEY_UVWSRC_SHININESS(0));
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureSpecularityType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_SHININESS);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureRoughness.length) {
+ mat->AddProperty(&pCurrentMaterial->textureRoughness, _AI_MATKEY_TEXTURE_BASE, aiTextureType_DIFFUSE_ROUGHNESS, 0);
+ mat->AddProperty(&uvwIndex, 1, _AI_MATKEY_UVWSRC_BASE, aiTextureType_DIFFUSE_ROUGHNESS, 0 );
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureRoughnessType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_DIFFUSE_ROUGHNESS);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureMetallic.length) {
+ mat->AddProperty(&pCurrentMaterial->textureMetallic, _AI_MATKEY_TEXTURE_BASE, aiTextureType_METALNESS, 0);
+ mat->AddProperty(&uvwIndex, 1, _AI_MATKEY_UVWSRC_BASE, aiTextureType_METALNESS, 0 );
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureMetallicType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_METALNESS);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureSheen.length) {
+ mat->AddProperty(&pCurrentMaterial->textureSheen, _AI_MATKEY_TEXTURE_BASE, aiTextureType_SHEEN, 0);
+ mat->AddProperty(&uvwIndex, 1, _AI_MATKEY_UVWSRC_BASE, aiTextureType_SHEEN, 0 );
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureSheenType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_SHEEN);
+ }
+ }
+
+ if (0 != pCurrentMaterial->textureRMA.length) {
+ // NOTE: glTF importer places Rough/Metal/AO texture in Unknown so doing the same here for consistency.
+ mat->AddProperty(&pCurrentMaterial->textureRMA, _AI_MATKEY_TEXTURE_BASE, aiTextureType_UNKNOWN, 0);
+ mat->AddProperty(&uvwIndex, 1, _AI_MATKEY_UVWSRC_BASE, aiTextureType_UNKNOWN, 0 );
+ if (pCurrentMaterial->clamp[ObjFile::Material::TextureRMAType]) {
+ addTextureMappingModeProperty(mat, aiTextureType_UNKNOWN);
+ }
+ }
+
+ // Store material property info in material array in scene
+ pScene->mMaterials[pScene->mNumMaterials] = mat;
+ pScene->mNumMaterials++;
+ }
+
+ // Test number of created materials.
+ ai_assert(pScene->mNumMaterials == numMaterials);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Appends this node to the parent node
+void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild) {
+ // Checking preconditions
+ ai_assert(nullptr != pParent);
+ ai_assert(nullptr != pChild);
+
+ // Assign parent to child
+ pChild->mParent = pParent;
+
+ // Copy node instances into parent node
+ pParent->mNumChildren++;
+ pParent->mChildren[pParent->mNumChildren - 1] = pChild;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
diff --git a/libs/assimp/code/AssetLib/Obj/ObjFileImporter.h b/libs/assimp/code/AssetLib/Obj/ObjFileImporter.h
new file mode 100644
index 0000000..e76c279
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Obj/ObjFileImporter.h
@@ -0,0 +1,122 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+#ifndef OBJ_FILE_IMPORTER_H_INC
+#define OBJ_FILE_IMPORTER_H_INC
+
+#include <assimp/BaseImporter.h>
+#include <assimp/material.h>
+#include <vector>
+
+struct aiMesh;
+struct aiNode;
+
+namespace Assimp {
+
+namespace ObjFile {
+struct Object;
+struct Model;
+} // namespace ObjFile
+
+// ------------------------------------------------------------------------------------------------
+/// \class ObjFileImporter
+/// \brief Imports a waveform obj file
+// ------------------------------------------------------------------------------------------------
+class ObjFileImporter : public BaseImporter {
+public:
+ /// \brief Default constructor
+ ObjFileImporter();
+
+ /// \brief Destructor
+ ~ObjFileImporter() override;
+
+ /// \brief Returns whether the class can handle the format of the given file.
+ /// \remark See BaseImporter::CanRead() for details.
+ bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
+
+protected:
+ //! \brief Appends the supported extension.
+ const aiImporterDesc *GetInfo() const override;
+
+ //! \brief File import implementation.
+ void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
+
+ //! \brief Create the data from imported content.
+ void CreateDataFromImport(const ObjFile::Model *pModel, aiScene *pScene);
+
+ //! \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);
+
+ //! \brief Creates topology data like faces and meshes for the geometry.
+ aiMesh *createTopology(const ObjFile::Model *pModel, const ObjFile::Object *pData,
+ unsigned int uiMeshIndex);
+
+ //! \brief Creates vertices from model.
+ void createVertexArray(const ObjFile::Model *pModel, const ObjFile::Object *pCurrentObject,
+ unsigned int uiMeshIndex, aiMesh *pMesh, unsigned int numIndices);
+
+ //! \brief Object counter helper method.
+ void countObjects(const std::vector<ObjFile::Object *> &rObjects, int &iNumMeshes);
+
+ //! \brief Material creation.
+ void createMaterials(const ObjFile::Model *pModel, aiScene *pScene);
+
+ /// @brief Adds special property for the used texture mapping mode of the model.
+ void addTextureMappingModeProperty(aiMaterial *mat, aiTextureType type, int clampMode = 1, int index = 0);
+
+ //! \brief Appends a child node to a parent node and updates the data structures.
+ void appendChildToParentNode(aiNode *pParent, aiNode *pChild);
+
+private:
+ //! Data buffer
+ std::vector<char> m_Buffer;
+ //! Pointer to root object instance
+ ObjFile::Object *m_pRootObject;
+ //! Absolute pathname of model in file system
+ std::string m_strAbsPath;
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif
diff --git a/libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.cpp b/libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.cpp
new file mode 100644
index 0000000..2441c17
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.cpp
@@ -0,0 +1,497 @@
+/*
+---------------------------------------------------------------------------
+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 "ObjFileMtlImporter.h"
+#include "ObjFileData.h"
+#include "ObjTools.h"
+#include <assimp/ParsingUtils.h>
+#include <assimp/fast_atof.h>
+#include <assimp/material.h>
+#include <stdlib.h>
+#include <assimp/DefaultLogger.hpp>
+
+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";
+
+// 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";
+
+// -------------------------------------------------------------------
+// Constructor
+ObjFileMtlImporter::ObjFileMtlImporter(std::vector<char> &buffer,
+ const std::string &,
+ ObjFile::Model *pModel) :
+ m_DataIt(buffer.begin()),
+ m_DataItEnd(buffer.end()),
+ m_pModel(pModel),
+ m_uiLine(0),
+ m_buffer() {
+ ai_assert(nullptr != m_pModel);
+ m_buffer.resize(BUFFERSIZE);
+ std::fill(m_buffer.begin(), m_buffer.end(), '\0');
+ if (nullptr == m_pModel->m_pDefaultMaterial) {
+ m_pModel->m_pDefaultMaterial = new ObjFile::Material;
+ m_pModel->m_pDefaultMaterial->MaterialName.Set("default");
+ }
+ load();
+}
+
+// -------------------------------------------------------------------
+// Destructor
+ObjFileMtlImporter::~ObjFileMtlImporter() {
+ // empty
+}
+
+// -------------------------------------------------------------------
+// Loads the material description
+void ObjFileMtlImporter::load() {
+ if (m_DataIt == m_DataItEnd)
+ return;
+
+ while (m_DataIt != m_DataItEnd) {
+ switch (*m_DataIt) {
+ case 'k':
+ case 'K': {
+ ++m_DataIt;
+ if (*m_DataIt == 'a') // Ambient color
+ {
+ ++m_DataIt;
+ getColorRGBA(&m_pModel->m_pCurrentMaterial->ambient);
+ } else if (*m_DataIt == 'd') {
+ // Diffuse color
+ ++m_DataIt;
+ getColorRGBA(&m_pModel->m_pCurrentMaterial->diffuse);
+ } else if (*m_DataIt == 's') {
+ ++m_DataIt;
+ getColorRGBA(&m_pModel->m_pCurrentMaterial->specular);
+ } else if (*m_DataIt == 'e') {
+ ++m_DataIt;
+ getColorRGBA(&m_pModel->m_pCurrentMaterial->emissive);
+ }
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ } break;
+ case 'T': {
+ ++m_DataIt;
+ // Material transmission color
+ if (*m_DataIt == 'f') {
+ ++m_DataIt;
+ getColorRGBA(&m_pModel->m_pCurrentMaterial->transparent);
+ } else if (*m_DataIt == 'r') {
+ // Material transmission alpha value
+ ++m_DataIt;
+ ai_real d;
+ getFloatValue(d);
+ m_pModel->m_pCurrentMaterial->alpha = static_cast<ai_real>(1.0) - d;
+ }
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ } break;
+ case 'd': {
+ if (*(m_DataIt + 1) == 'i' && *(m_DataIt + 2) == 's' && *(m_DataIt + 3) == 'p') {
+ // A displacement map
+ getTexture();
+ } else {
+ // Alpha value
+ ++m_DataIt;
+ getFloatValue(m_pModel->m_pCurrentMaterial->alpha);
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ }
+ } break;
+
+ case 'N':
+ case 'n': {
+ ++m_DataIt;
+ switch (*m_DataIt) {
+ case 's': // Specular exponent
+ ++m_DataIt;
+ getFloatValue(m_pModel->m_pCurrentMaterial->shineness);
+ break;
+ case 'i': // Index Of refraction
+ ++m_DataIt;
+ getFloatValue(m_pModel->m_pCurrentMaterial->ior);
+ break;
+ case 'e': // New material
+ createMaterial();
+ break;
+ case 'o': // Norm texture
+ --m_DataIt;
+ getTexture();
+ break;
+ }
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ } break;
+
+ case 'P':
+ {
+ ++m_DataIt;
+ switch(*m_DataIt)
+ {
+ case 'r':
+ ++m_DataIt;
+ getFloatValue(m_pModel->m_pCurrentMaterial->roughness);
+ break;
+ case 'm':
+ ++m_DataIt;
+ getFloatValue(m_pModel->m_pCurrentMaterial->metallic);
+ break;
+ case 's':
+ ++m_DataIt;
+ getColorRGBA(&m_pModel->m_pCurrentMaterial->sheen);
+ break;
+ case 'c':
+ ++m_DataIt;
+ if (*m_DataIt == 'r') {
+ ++m_DataIt;
+ getFloatValue(m_pModel->m_pCurrentMaterial->clearcoat_roughness);
+ } else {
+ getFloatValue(m_pModel->m_pCurrentMaterial->clearcoat_thickness);
+ }
+ break;
+ }
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ }
+ break;
+
+ case 'm': // Texture
+ case 'b': // quick'n'dirty - for 'bump' sections
+ case 'r': // quick'n'dirty - for 'refl' sections
+ {
+ getTexture();
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ } break;
+
+ case 'i': // Illumination model
+ {
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ getIlluminationModel(m_pModel->m_pCurrentMaterial->illumination_model);
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ } break;
+
+ case 'a': // Anisotropy
+ {
+ getFloatValue(m_pModel->m_pCurrentMaterial->anisotropy);
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ } break;
+
+ default: {
+ m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
+ } break;
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+// Loads a color definition
+void ObjFileMtlImporter::getColorRGBA(aiColor3D *pColor) {
+ ai_assert(nullptr != pColor);
+
+ ai_real r(0.0), g(0.0), b(0.0);
+ m_DataIt = getFloat<DataArrayIt>(m_DataIt, m_DataItEnd, r);
+ pColor->r = r;
+
+ // we have to check if color is default 0 with only one token
+ if (!IsLineEnd(*m_DataIt)) {
+ m_DataIt = getFloat<DataArrayIt>(m_DataIt, m_DataItEnd, g);
+ m_DataIt = getFloat<DataArrayIt>(m_DataIt, m_DataItEnd, b);
+ }
+ pColor->g = g;
+ pColor->b = b;
+}
+
+// -------------------------------------------------------------------
+// Loads the kind of illumination model.
+void ObjFileMtlImporter::getIlluminationModel(int &illum_model) {
+ m_DataIt = CopyNextWord<DataArrayIt>(m_DataIt, m_DataItEnd, &m_buffer[0], BUFFERSIZE);
+ illum_model = atoi(&m_buffer[0]);
+}
+
+// -------------------------------------------------------------------
+// Loads a single float value.
+void ObjFileMtlImporter::getFloatValue(ai_real &value) {
+ m_DataIt = CopyNextWord<DataArrayIt>(m_DataIt, m_DataItEnd, &m_buffer[0], BUFFERSIZE);
+ size_t len = std::strlen(&m_buffer[0]);
+ if (0 == len) {
+ value = 0.0f;
+ return;
+ }
+
+ value = (ai_real)fast_atof(&m_buffer[0]);
+}
+
+// -------------------------------------------------------------------
+// Creates a material from loaded data.
+void ObjFileMtlImporter::createMaterial() {
+ std::string line;
+ while (!IsLineEnd(*m_DataIt)) {
+ line += *m_DataIt;
+ ++m_DataIt;
+ }
+
+ std::vector<std::string> token;
+ const unsigned int numToken = tokenize<std::string>(line, token, " \t");
+ std::string name;
+ if (numToken == 1) {
+ name = AI_DEFAULT_MATERIAL_NAME;
+ } else {
+ // skip newmtl and all following white spaces
+ std::size_t first_ws_pos = line.find_first_of(" \t");
+ std::size_t first_non_ws_pos = line.find_first_not_of(" \t", first_ws_pos);
+ if (first_non_ws_pos != std::string::npos) {
+ name = line.substr(first_non_ws_pos);
+ }
+ }
+
+ name = trim_whitespaces(name);
+
+ std::map<std::string, ObjFile::Material *>::iterator it = m_pModel->m_MaterialMap.find(name);
+ if (m_pModel->m_MaterialMap.end() == it) {
+ // New Material created
+ m_pModel->m_pCurrentMaterial = new ObjFile::Material();
+ m_pModel->m_pCurrentMaterial->MaterialName.Set(name);
+ m_pModel->m_MaterialLib.push_back(name);
+ m_pModel->m_MaterialMap[name] = m_pModel->m_pCurrentMaterial;
+
+ if (m_pModel->m_pCurrentMesh) {
+ m_pModel->m_pCurrentMesh->m_uiMaterialIndex = static_cast<unsigned int>(m_pModel->m_MaterialLib.size() - 1);
+ }
+ } else {
+ // Use older material
+ m_pModel->m_pCurrentMaterial = (*it).second;
+ }
+}
+
+// -------------------------------------------------------------------
+// Gets a texture name from data.
+void ObjFileMtlImporter::getTexture() {
+ aiString *out(nullptr);
+ int clampIndex = -1;
+
+ const char *pPtr(&(*m_DataIt));
+ if (!ASSIMP_strincmp(pPtr, DiffuseTexture.c_str(), static_cast<unsigned int>(DiffuseTexture.size()))) {
+ // Diffuse texture
+ out = &m_pModel->m_pCurrentMaterial->texture;
+ clampIndex = ObjFile::Material::TextureDiffuseType;
+ } else if (!ASSIMP_strincmp(pPtr, AmbientTexture.c_str(), static_cast<unsigned int>(AmbientTexture.size()))) {
+ // Ambient texture
+ out = &m_pModel->m_pCurrentMaterial->textureAmbient;
+ clampIndex = ObjFile::Material::TextureAmbientType;
+ } else if (!ASSIMP_strincmp(pPtr, SpecularTexture.c_str(), static_cast<unsigned int>(SpecularTexture.size()))) {
+ // Specular texture
+ out = &m_pModel->m_pCurrentMaterial->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()))) {
+ // Displacement texture
+ out = &m_pModel->m_pCurrentMaterial->textureDisp;
+ clampIndex = ObjFile::Material::TextureDispType;
+ } else if (!ASSIMP_strincmp(pPtr, OpacityTexture.c_str(), static_cast<unsigned int>(OpacityTexture.size()))) {
+ // Opacity texture
+ out = &m_pModel->m_pCurrentMaterial->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()))) {
+ // Emissive texture
+ out = &m_pModel->m_pCurrentMaterial->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()))) {
+ // Bump texture
+ out = &m_pModel->m_pCurrentMaterial->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()))) {
+ // Normal map
+ out = &m_pModel->m_pCurrentMaterial->textureNormal;
+ clampIndex = ObjFile::Material::TextureNormalType;
+ } else if (!ASSIMP_strincmp(pPtr, ReflectionTexture.c_str(), static_cast<unsigned int>(ReflectionTexture.size()))) {
+ // Reflection texture(s)
+ //Do nothing here
+ return;
+ } else if (!ASSIMP_strincmp(pPtr, SpecularityTexture.c_str(), static_cast<unsigned int>(SpecularityTexture.size()))) {
+ // Specularity scaling (glossiness)
+ out = &m_pModel->m_pCurrentMaterial->textureSpecularity;
+ clampIndex = ObjFile::Material::TextureSpecularityType;
+ } else if ( !ASSIMP_strincmp( pPtr, RoughnessTexture.c_str(), static_cast<unsigned int>(RoughnessTexture.size()))) {
+ // PBR Roughness texture
+ out = & m_pModel->m_pCurrentMaterial->textureRoughness;
+ clampIndex = ObjFile::Material::TextureRoughnessType;
+ } else if ( !ASSIMP_strincmp( pPtr, MetallicTexture.c_str(), static_cast<unsigned int>(MetallicTexture.size()))) {
+ // PBR Metallic texture
+ out = & m_pModel->m_pCurrentMaterial->textureMetallic;
+ clampIndex = ObjFile::Material::TextureMetallicType;
+ } else if (!ASSIMP_strincmp( pPtr, SheenTexture.c_str(), static_cast<unsigned int>(SheenTexture.size()))) {
+ // PBR Sheen (reflectance) texture
+ out = & m_pModel->m_pCurrentMaterial->textureSheen;
+ clampIndex = ObjFile::Material::TextureSheenType;
+ } else if (!ASSIMP_strincmp( pPtr, RMATexture.c_str(), static_cast<unsigned int>(RMATexture.size()))) {
+ // PBR Rough/Metal/AO texture
+ out = & m_pModel->m_pCurrentMaterial->textureRMA;
+ clampIndex = ObjFile::Material::TextureRMAType;
+ } else {
+ ASSIMP_LOG_ERROR("OBJ/MTL: Encountered unknown texture type");
+ return;
+ }
+
+ bool clamp = false;
+ getTextureOption(clamp, clampIndex, out);
+ m_pModel->m_pCurrentMaterial->clamp[clampIndex] = clamp;
+
+ std::string texture;
+ m_DataIt = getName<DataArrayIt>(m_DataIt, m_DataItEnd, texture);
+ if (nullptr != out) {
+ out->Set(texture);
+ }
+}
+
+/* /////////////////////////////////////////////////////////////////////////////
+ * Texture Option
+ * /////////////////////////////////////////////////////////////////////////////
+ * According to http://en.wikipedia.org/wiki/Wavefront_.obj_file#Texture_options
+ * Texture map statement can contains various texture option, for example:
+ *
+ * map_Ka -o 1 1 1 some.png
+ * map_Kd -clamp on some.png
+ *
+ * So we need to parse and skip these options, and leave the last part which is
+ * the url of image, otherwise we will get a wrong url like "-clamp on some.png".
+ *
+ * Because aiMaterial supports clamp option, so we also want to return it
+ * /////////////////////////////////////////////////////////////////////////////
+ */
+void ObjFileMtlImporter::getTextureOption(bool &clamp, int &clampIndex, aiString *&out) {
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+
+ // If there is any more texture option
+ while (!isEndOfBuffer(m_DataIt, m_DataItEnd) && *m_DataIt == '-') {
+ const char *pPtr(&(*m_DataIt));
+ //skip option key and value
+ int skipToken = 1;
+
+ if (!ASSIMP_strincmp(pPtr, ClampOption.c_str(), static_cast<unsigned int>(ClampOption.size()))) {
+ DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ char value[3];
+ CopyNextWord(it, m_DataItEnd, value, sizeof(value) / sizeof(*value));
+ if (!ASSIMP_strincmp(value, "on", 2)) {
+ clamp = true;
+ }
+
+ skipToken = 2;
+ } else if (!ASSIMP_strincmp(pPtr, TypeOption.c_str(), static_cast<unsigned int>(TypeOption.size()))) {
+ DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ char value[12];
+ CopyNextWord(it, m_DataItEnd, value, sizeof(value) / sizeof(*value));
+ if (!ASSIMP_strincmp(value, "cube_top", 8)) {
+ clampIndex = ObjFile::Material::TextureReflectionCubeTopType;
+ out = &m_pModel->m_pCurrentMaterial->textureReflection[0];
+ } else if (!ASSIMP_strincmp(value, "cube_bottom", 11)) {
+ clampIndex = ObjFile::Material::TextureReflectionCubeBottomType;
+ out = &m_pModel->m_pCurrentMaterial->textureReflection[1];
+ } else if (!ASSIMP_strincmp(value, "cube_front", 10)) {
+ clampIndex = ObjFile::Material::TextureReflectionCubeFrontType;
+ out = &m_pModel->m_pCurrentMaterial->textureReflection[2];
+ } else if (!ASSIMP_strincmp(value, "cube_back", 9)) {
+ clampIndex = ObjFile::Material::TextureReflectionCubeBackType;
+ out = &m_pModel->m_pCurrentMaterial->textureReflection[3];
+ } else if (!ASSIMP_strincmp(value, "cube_left", 9)) {
+ clampIndex = ObjFile::Material::TextureReflectionCubeLeftType;
+ out = &m_pModel->m_pCurrentMaterial->textureReflection[4];
+ } else if (!ASSIMP_strincmp(value, "cube_right", 10)) {
+ clampIndex = ObjFile::Material::TextureReflectionCubeRightType;
+ out = &m_pModel->m_pCurrentMaterial->textureReflection[5];
+ } else if (!ASSIMP_strincmp(value, "sphere", 6)) {
+ clampIndex = ObjFile::Material::TextureReflectionSphereType;
+ out = &m_pModel->m_pCurrentMaterial->textureReflection[0];
+ }
+
+ skipToken = 2;
+ } else if (!ASSIMP_strincmp(pPtr, BumpOption.c_str(), static_cast<unsigned int>(BumpOption.size()))) {
+ DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ getFloat(it, m_DataItEnd, m_pModel->m_pCurrentMaterial->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()))) {
+ skipToken = 2;
+ } else if (!ASSIMP_strincmp(pPtr, ModifyMapOption.c_str(), static_cast<unsigned int>(ModifyMapOption.size()))) {
+ 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()))) {
+ skipToken = 4;
+ }
+
+ for (int i = 0; i < skipToken; ++i) {
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
diff --git a/libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.h b/libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.h
new file mode 100644
index 0000000..cda7415
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.h
@@ -0,0 +1,113 @@
+/*
+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 OBJFILEMTLIMPORTER_H_INC
+#define OBJFILEMTLIMPORTER_H_INC
+
+#include <assimp/defs.h>
+#include <string>
+#include <vector>
+
+struct aiColor3D;
+struct aiString;
+
+namespace Assimp {
+
+namespace ObjFile {
+struct Model;
+struct Material;
+} // namespace ObjFile
+
+/**
+ * @class ObjFileMtlImporter
+ * @brief Loads the material description from a mtl file.
+ */
+class ObjFileMtlImporter {
+public:
+ static const size_t BUFFERSIZE = 2048;
+ using DataArray = std::vector<char>;
+ using DataArrayIt = std::vector<char>::iterator;
+ using ConstDataArrayIt = std::vector<char>::const_iterator;
+
+ //! \brief The class default constructor
+ ObjFileMtlImporter(std::vector<char> &buffer, const std::string &strAbsPath,
+ ObjFile::Model *pModel);
+
+ //! \brief The class destructor
+ ~ObjFileMtlImporter();
+
+ ObjFileMtlImporter(const ObjFileMtlImporter &rOther) = delete;
+ ObjFileMtlImporter &operator=(const ObjFileMtlImporter &rOther) = delete;
+
+private:
+ /// Copy constructor, empty.
+ /// Load the whole material description
+ void load();
+ /// Get color data.
+ void getColorRGBA(aiColor3D *pColor);
+ /// Get illumination model from loaded data
+ void getIlluminationModel(int &illum_model);
+ /// Gets a float value from data.
+ void getFloatValue(ai_real &value);
+ /// Creates a new material from loaded data.
+ void createMaterial();
+ /// Get texture name from loaded data.
+ void getTexture();
+ void getTextureOption(bool &clamp, int &clampIndex, aiString *&out);
+
+private:
+ //! Absolute pathname
+ std::string m_strAbsPath;
+ //! Data iterator showing to the current position in data buffer
+ DataArrayIt m_DataIt;
+ //! Data iterator to end of buffer
+ DataArrayIt m_DataItEnd;
+ //! USed model instance
+ ObjFile::Model *m_pModel;
+ //! Current line in file
+ unsigned int m_uiLine;
+ //! Helper buffer
+ std::vector<char> m_buffer;
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // OBJFILEMTLIMPORTER_H_INC
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
diff --git a/libs/assimp/code/AssetLib/Obj/ObjFileParser.h b/libs/assimp/code/AssetLib/Obj/ObjFileParser.h
new file mode 100644
index 0000000..fbd2f2c
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Obj/ObjFileParser.h
@@ -0,0 +1,165 @@
+/*
+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 OBJ_FILEPARSER_H_INC
+#define OBJ_FILEPARSER_H_INC
+
+#include <assimp/IOStreamBuffer.h>
+#include <assimp/material.h>
+#include <assimp/mesh.h>
+#include <assimp/vector2.h>
+#include <assimp/vector3.h>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace Assimp {
+
+namespace ObjFile {
+struct Model;
+struct Object;
+struct Material;
+struct Point3;
+struct Point2;
+} // namespace ObjFile
+
+class ObjFileImporter;
+class IOSystem;
+class ProgressHandler;
+
+/// \class ObjFileParser
+/// \brief Parser for a obj waveform file
+class ASSIMP_API ObjFileParser {
+public:
+ static const size_t Buffersize = 4096;
+ typedef std::vector<char> DataArray;
+ typedef std::vector<char>::iterator DataArrayIt;
+ typedef std::vector<char>::const_iterator ConstDataArrayIt;
+
+ /// @brief The default constructor.
+ ObjFileParser();
+ /// @brief Constructor with data array.
+ ObjFileParser(IOStreamBuffer<char> &streamBuffer, const std::string &modelName, IOSystem *io, ProgressHandler *progress, const std::string &originalObjFileName);
+ /// @brief Destructor
+ ~ObjFileParser();
+ /// @brief If you want to load in-core data.
+ void setBuffer(std::vector<char> &buffer);
+ /// @brief Model getter.
+ ObjFile::Model *GetModel() const;
+
+ ObjFileParser(const ObjFileParser&) = delete;
+ ObjFileParser &operator=(const ObjFileParser& ) = delete;
+
+protected:
+ /// Parse the loaded file
+ void parseFile(IOStreamBuffer<char> &streamBuffer);
+ /// Method to copy the new delimited word in the current line.
+ void copyNextWord(char *pBuffer, size_t length);
+ /// Method to copy the new line.
+ // void copyNextLine(char *pBuffer, size_t length);
+ /// Get the number of components in a line.
+ size_t getNumComponentsInDataDefinition();
+ /// Stores the vector
+ size_t getTexCoordVector(std::vector<aiVector3D> &point3d_array);
+ /// Stores the following 3d vector.
+ void getVector3(std::vector<aiVector3D> &point3d_array);
+ /// Stores the following homogeneous vector as a 3D vector
+ void getHomogeneousVector3(std::vector<aiVector3D> &point3d_array);
+ /// Stores the following two 3d vectors on the line.
+ void getTwoVectors3(std::vector<aiVector3D> &point3d_array_a, std::vector<aiVector3D> &point3d_array_b);
+ /// Stores the following 3d vector.
+ void getVector2(std::vector<aiVector2D> &point2d_array);
+ /// Stores the following face.
+ void getFace(aiPrimitiveType type);
+ /// Reads the material description.
+ void getMaterialDesc();
+ /// Gets a comment.
+ void getComment();
+ /// Gets a a material library.
+ void getMaterialLib();
+ /// Creates a new material.
+ void getNewMaterial();
+ /// Gets the group name from file.
+ void getGroupName();
+ /// Gets the group number from file.
+ void getGroupNumber();
+ /// Gets the group number and resolution from file.
+ void getGroupNumberAndResolution();
+ /// Returns the index of the material. Is -1 if not material was found.
+ int getMaterialIndex(const std::string &strMaterialName);
+ /// Parse object name
+ void getObjectName();
+ /// Creates a new object.
+ void createObject(const std::string &strObjectName);
+ /// Creates a new mesh.
+ void createMesh(const std::string &meshName);
+ /// Returns true, if a new mesh instance must be created.
+ bool needsNewMesh(const std::string &rMaterialName);
+ /// Error report in token
+ void reportErrorTokenInFace();
+
+private:
+ // Copy and assignment constructor should be private
+ // because the class contains pointer to allocated memory
+
+ /// Default material name
+ static constexpr char DEFAULT_MATERIAL[] = AI_DEFAULT_MATERIAL_NAME;
+ //! Iterator to current position in buffer
+ DataArrayIt m_DataIt;
+ //! Iterator to end position of buffer
+ DataArrayIt m_DataItEnd;
+ //! Pointer to model instance
+ std::unique_ptr<ObjFile::Model> m_pModel;
+ //! Current line (for debugging)
+ unsigned int m_uiLine;
+ //! Helper buffer
+ char m_buffer[Buffersize];
+ /// Pointer to IO system instance.
+ IOSystem *m_pIO;
+ //! Pointer to progress handler
+ ProgressHandler *m_progress;
+ /// Path to the current model, name of the obj file where the buffer comes from
+ const std::string m_originalObjFileName;
+};
+
+} // Namespace Assimp
+
+#endif
diff --git a/libs/assimp/code/AssetLib/Obj/ObjTools.h b/libs/assimp/code/AssetLib/Obj/ObjTools.h
new file mode 100644
index 0000000..9e57a1c
--- /dev/null
+++ b/libs/assimp/code/AssetLib/Obj/ObjTools.h
@@ -0,0 +1,284 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2021, 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.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ObjTools.h
+ * @brief Some helpful templates for text parsing
+ */
+#ifndef OBJ_TOOLS_H_INC
+#define OBJ_TOOLS_H_INC
+
+#include <assimp/ParsingUtils.h>
+#include <assimp/fast_atof.h>
+#include <vector>
+
+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.
+ * @return true, if the end of the buffer is reached.
+ */
+template <class char_t>
+inline bool isEndOfBuffer(char_t it, char_t end) {
+ if (it == end) {
+ return true;
+ }
+ --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
+ * @return Pointer to next space
+ */
+template <class Char_T>
+inline Char_T getNextWord(Char_T pBuffer, Char_T pEnd) {
+ while (!isEndOfBuffer(pBuffer, pEnd)) {
+ if (!IsSpaceOrNewLine(*pBuffer) || IsLineEnd(*pBuffer)) {
+ break;
+ }
+ ++pBuffer;
+ }
+
+ return pBuffer;
+}
+
+/**
+ * @brief Returns pointer a next token
+ * @param[in] pBuffer Pointer to data buffer
+ * @param[in] pEnd Pointer to end of buffer
+ * @return Pointer to next token
+ */
+template <class Char_T>
+inline Char_T getNextToken(Char_T pBuffer, Char_T pEnd) {
+ while (!isEndOfBuffer(pBuffer, pEnd)) {
+ if (IsSpaceOrNewLine(*pBuffer)) {
+ break;
+ }
+ ++pBuffer;
+ }
+ 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
+ * @param[out] uiLine Current line number in format
+ * @return Current-iterator with new position
+ */
+template <class char_t>
+inline char_t skipLine(char_t it, char_t end, unsigned int &uiLine) {
+ while (!isEndOfBuffer(it, end) && !IsLineEnd(*it)) {
+ ++it;
+ }
+
+ if (it != end) {
+ ++it;
+ ++uiLine;
+ }
+ // fix .. from time to time there are spaces at the beginning of a material line
+ while (it != end && (*it == '\t' || *it == ' ')) {
+ ++it;
+ }
+
+ return it;
+}
+
+/**
+ * @brief Get a name from the current line. Preserve space in the middle,
+ * 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
+ * @return Current-iterator with new position
+ */
+template <class char_t>
+inline char_t getName(char_t it, char_t end, std::string &name) {
+ name = "";
+ if (isEndOfBuffer(it, end)) {
+ return end;
+ }
+
+ char *pStart = &(*it);
+ while (!isEndOfBuffer(it, end) && !IsLineEnd(*it)) {
+ ++it;
+ }
+
+ while (IsSpace(*it)) {
+ --it;
+ }
+ // Get name
+ // if there is no name, and the previous char is a separator, come back to start
+ while (&(*it) < pStart) {
+ ++it;
+ }
+ 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
+ * @param end set to end of scratch buffer for readout
+ * @param name Separated name
+ * @return Current-iterator with new position
+ */
+template <class char_t>
+inline char_t getNameNoSpace(char_t it, char_t end, std::string &name) {
+ name = "";
+ if (isEndOfBuffer(it, end)) {
+ return end;
+ }
+
+ char *pStart = &(*it);
+ while (!isEndOfBuffer(it, end) && !IsLineEnd(*it) && !IsSpaceOrNewLine(*it)) {
+ ++it;
+ }
+
+ while (isEndOfBuffer(it, end) || IsLineEnd(*it) || IsSpaceOrNewLine(*it)) {
+ --it;
+ }
+ ++it;
+
+ // Get name
+ // if there is no name, and the previous char is a separator, come back to start
+ while (&(*it) < pStart) {
+ ++it;
+ }
+ std::string strName(pStart, &(*it));
+ 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
+ * @param[in] pBuffer Buffer for next word
+ * @param[in] length Buffer length
+ * @return Current-iterator with new position
+ */
+template <class char_t>
+inline char_t CopyNextWord(char_t it, char_t end, char *pBuffer, size_t length) {
+ size_t index = 0;
+ it = getNextWord<char_t>(it, end);
+ while (!IsSpaceOrNewLine(*it) && !isEndOfBuffer(it, end)) {
+ pBuffer[index] = *it;
+ ++index;
+ if (index == length - 1) {
+ break;
+ }
+ ++it;
+ }
+ pBuffer[index] = '\0';
+ 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
+ * @param[out] value Separated float value.
+ * @return Current-iterator with new position
+ */
+template <class char_t>
+inline char_t getFloat(char_t it, char_t end, ai_real &value) {
+ static const size_t BUFFERSIZE = 1024;
+ char buffer[BUFFERSIZE];
+ it = CopyNextWord<char_t>(it, end, buffer, BUFFERSIZE);
+ value = (ai_real)fast_atof(buffer);
+
+ 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.
+ * @param[in] end End of the string.
+ * @return The trimmed string.
+ */
+template <class T>
+bool hasLineEnd(T it, T end) {
+ bool hasLineEnd = false;
+ while (!isEndOfBuffer(it, end)) {
+ ++it;
+ if (IsLineEnd(it)) {
+ hasLineEnd = true;
+ break;
+ }
+ }
+
+ return hasLineEnd;
+}
+
+} // Namespace Assimp
+
+#endif // OBJ_TOOLS_H_INC