From db81b925d776103326128bf629cbdda576a223e7 Mon Sep 17 00:00:00 2001 From: sanine Date: Sat, 16 Apr 2022 11:55:09 -0500 Subject: move 3rd-party librarys into libs/ and add built-in honeysuckle --- libs/assimp/code/AssetLib/Obj/ObjExporter.cpp | 414 ++++++++++ libs/assimp/code/AssetLib/Obj/ObjExporter.h | 190 +++++ libs/assimp/code/AssetLib/Obj/ObjFileData.h | 344 +++++++++ libs/assimp/code/AssetLib/Obj/ObjFileImporter.cpp | 783 +++++++++++++++++++ libs/assimp/code/AssetLib/Obj/ObjFileImporter.h | 122 +++ .../code/AssetLib/Obj/ObjFileMtlImporter.cpp | 497 ++++++++++++ libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.h | 113 +++ libs/assimp/code/AssetLib/Obj/ObjFileParser.cpp | 838 +++++++++++++++++++++ libs/assimp/code/AssetLib/Obj/ObjFileParser.h | 165 ++++ libs/assimp/code/AssetLib/Obj/ObjTools.h | 284 +++++++ 10 files changed, 3750 insertions(+) create mode 100644 libs/assimp/code/AssetLib/Obj/ObjExporter.cpp create mode 100644 libs/assimp/code/AssetLib/Obj/ObjExporter.h create mode 100644 libs/assimp/code/AssetLib/Obj/ObjFileData.h create mode 100644 libs/assimp/code/AssetLib/Obj/ObjFileImporter.cpp create mode 100644 libs/assimp/code/AssetLib/Obj/ObjFileImporter.h create mode 100644 libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.cpp create mode 100644 libs/assimp/code/AssetLib/Obj/ObjFileMtlImporter.h create mode 100644 libs/assimp/code/AssetLib/Obj/ObjFileParser.cpp create mode 100644 libs/assimp/code/AssetLib/Obj/ObjFileParser.h create mode 100644 libs/assimp/code/AssetLib/Obj/ObjTools.h (limited to 'libs/assimp/code/AssetLib/Obj') 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 +#include +#include +#include +#include +#include +#include +#include + +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 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(exporter.mOutput.tellp()),1); + } + { + std::unique_ptr 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(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 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(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 +#include +#include +#include + +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 indices; + }; + + struct MeshInstance { + std::string name, matname; + std::vector 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 vn, vt; + std::vector vc; + std::vector 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 indexMap { + int mNextIndex; + typedef std::map 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& keys ) { + keys.resize(vecMap.size()); + for(typename dataType::iterator it = vecMap.begin(); it != vecMap.end(); ++it){ + keys[it->second-1] = it->first; + } + }; + }; + + indexMap mVnMap, mVtMap; + indexMap mVpMap; + std::vector 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 +#include +#include +#include + +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; + + //! 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 m_SubObjects; + /// Assigned meshes + std::vector m_Meshes; + + //! \brief Default constructor + Object() = default; + + //! \brief Destructor + ~Object() { + for (std::vector::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(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 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::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 *>; + using GroupMapIt = std::map *>::iterator; + using ConstGroupMapIt = std::map *>::const_iterator; + + //! Model name + std::string m_ModelName; + //! List ob assigned objects + std::vector 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 m_MaterialLib; + //! Vector with all generated vertices + std::vector m_Vertices; + //! vector with all generated normals + std::vector m_Normals; + //! vector with all vertex colors + std::vector m_VertexColors; + //! Group map + GroupMap m_Groups; + //! Group to face id assignment + std::vector *m_pGroupFaceIDs; + //! Active group + std::string m_strActiveGroup; + //! Vector with generated texture coordinates + std::vector m_TextureCoord; + //! Maximum dimension of texture coordinates + unsigned int m_TextureCoordDim; + //! Current mesh instance + Mesh *m_pCurrentMesh; + //! Vector with stored meshes + std::vector m_Meshes; + //! Material map + std::map 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 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 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 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 &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(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(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 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(inp->m_vertices.size() - 1); + pMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + } else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) { + pMesh->mNumFaces += static_cast(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 &rObjects, int &iNumMeshes) { + iNumMeshes = 0; + if (rObjects.empty()) + return; + + iNumMeshes += static_cast(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(&clampMode, 1, AI_MATKEY_MAPPINGMODE_U(type, index)); + mat->AddProperty(&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::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(&sm, 1, AI_MATKEY_SHADING_MODEL); + + // Preserve the original illum value + mat->AddProperty(&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 +#include +#include + +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 &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 &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 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 +#include +#include +#include +#include + +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 &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(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(1.0) - d; + } + m_DataIt = skipLine(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(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(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(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(m_DataIt, m_DataItEnd, m_uiLine); + } break; + + case 'i': // Illumination model + { + m_DataIt = getNextToken(m_DataIt, m_DataItEnd); + getIlluminationModel(m_pModel->m_pCurrentMaterial->illumination_model); + m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + } break; + + case 'a': // Anisotropy + { + getFloatValue(m_pModel->m_pCurrentMaterial->anisotropy); + m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); + } break; + + default: { + m_DataIt = skipLine(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(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(m_DataIt, m_DataItEnd, g); + m_DataIt = getFloat(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(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(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 token; + const unsigned int numToken = tokenize(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::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(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(DiffuseTexture.size()))) { + // Diffuse texture + out = &m_pModel->m_pCurrentMaterial->texture; + clampIndex = ObjFile::Material::TextureDiffuseType; + } else if (!ASSIMP_strincmp(pPtr, AmbientTexture.c_str(), static_cast(AmbientTexture.size()))) { + // Ambient texture + out = &m_pModel->m_pCurrentMaterial->textureAmbient; + clampIndex = ObjFile::Material::TextureAmbientType; + } else if (!ASSIMP_strincmp(pPtr, SpecularTexture.c_str(), static_cast(SpecularTexture.size()))) { + // Specular texture + out = &m_pModel->m_pCurrentMaterial->textureSpecular; + clampIndex = ObjFile::Material::TextureSpecularType; + } else if (!ASSIMP_strincmp(pPtr, DisplacementTexture1.c_str(), static_cast(DisplacementTexture1.size())) || + !ASSIMP_strincmp(pPtr, DisplacementTexture2.c_str(), static_cast(DisplacementTexture2.size()))) { + // Displacement texture + out = &m_pModel->m_pCurrentMaterial->textureDisp; + clampIndex = ObjFile::Material::TextureDispType; + } else if (!ASSIMP_strincmp(pPtr, OpacityTexture.c_str(), static_cast(OpacityTexture.size()))) { + // Opacity texture + out = &m_pModel->m_pCurrentMaterial->textureOpacity; + clampIndex = ObjFile::Material::TextureOpacityType; + } else if (!ASSIMP_strincmp(pPtr, EmissiveTexture1.c_str(), static_cast(EmissiveTexture1.size())) || + !ASSIMP_strincmp(pPtr, EmissiveTexture2.c_str(), static_cast(EmissiveTexture2.size()))) { + // Emissive texture + out = &m_pModel->m_pCurrentMaterial->textureEmissive; + clampIndex = ObjFile::Material::TextureEmissiveType; + } else if (!ASSIMP_strincmp(pPtr, BumpTexture1.c_str(), static_cast(BumpTexture1.size())) || + !ASSIMP_strincmp(pPtr, BumpTexture2.c_str(), static_cast(BumpTexture2.size()))) { + // Bump texture + out = &m_pModel->m_pCurrentMaterial->textureBump; + clampIndex = ObjFile::Material::TextureBumpType; + } else if (!ASSIMP_strincmp(pPtr, NormalTextureV1.c_str(), static_cast(NormalTextureV1.size())) || !ASSIMP_strincmp(pPtr, NormalTextureV2.c_str(), static_cast(NormalTextureV2.size()))) { + // Normal map + out = &m_pModel->m_pCurrentMaterial->textureNormal; + clampIndex = ObjFile::Material::TextureNormalType; + } else if (!ASSIMP_strincmp(pPtr, ReflectionTexture.c_str(), static_cast(ReflectionTexture.size()))) { + // Reflection texture(s) + //Do nothing here + return; + } else if (!ASSIMP_strincmp(pPtr, SpecularityTexture.c_str(), static_cast(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(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(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(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(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(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(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(ClampOption.size()))) { + DataArrayIt it = getNextToken(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(TypeOption.size()))) { + DataArrayIt it = getNextToken(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(BumpOption.size()))) { + DataArrayIt it = getNextToken(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(BlendUOption.size())) || !ASSIMP_strincmp(pPtr, BlendVOption.c_str(), static_cast(BlendVOption.size())) || !ASSIMP_strincmp(pPtr, BoostOption.c_str(), static_cast(BoostOption.size())) || !ASSIMP_strincmp(pPtr, ResolutionOption.c_str(), static_cast(ResolutionOption.size())) || !ASSIMP_strincmp(pPtr, ChannelOption.c_str(), static_cast(ChannelOption.size()))) { + skipToken = 2; + } else if (!ASSIMP_strincmp(pPtr, ModifyMapOption.c_str(), static_cast(ModifyMapOption.size()))) { + skipToken = 3; + } else if (!ASSIMP_strincmp(pPtr, OffsetOption.c_str(), static_cast(OffsetOption.size())) || !ASSIMP_strincmp(pPtr, ScaleOption.c_str(), static_cast(ScaleOption.size())) || !ASSIMP_strincmp(pPtr, TurbulenceOption.c_str(), static_cast(TurbulenceOption.size()))) { + skipToken = 4; + } + + for (int i = 0; i < skipToken; ++i) { + m_DataIt = getNextToken(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 +#include +#include + +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; + using DataArrayIt = std::vector::iterator; + using ConstDataArrayIt = std::vector::const_iterator; + + //! \brief The class default constructor + ObjFileMtlImporter(std::vector &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 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 +#include +#include +#include +#include +#include +#include +#include + +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 &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 &buffer) { + m_DataIt = buffer.begin(); + m_DataItEnd = buffer.end(); +} + +ObjFile::Model *ObjFileParser::GetModel() const { + return m_pModel.get(); +} + +void ObjFileParser::parseFile(IOStreamBuffer &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(streamBuffer.size()); + const unsigned int progressTotal = bytesToProcess; + unsigned int processed = 0; + size_t lastFilePos(0); + + std::vector 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(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(m_DataIt, m_DataItEnd, m_uiLine); + } break; + } + } +} + +void ObjFileParser::copyNextWord(char *pBuffer, size_t length) { + size_t index = 0; + m_DataIt = getNextWord(m_DataIt, m_DataItEnd); + if (*m_DataIt == '\\') { + ++m_DataIt; + ++m_DataIt; + m_DataIt = getNextWord(m_DataIt, m_DataItEnd); + } + 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 &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(m_DataIt, m_DataItEnd, m_uiLine); + return numComponents; +} + +void ObjFileParser::getVector3(std::vector &point3d_array) { + ai_real x, y, z; + copyNextWord(m_buffer, Buffersize); + x = (ai_real)fast_atof(m_buffer); + + copyNextWord(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(m_DataIt, m_DataItEnd, m_uiLine); +} + +void ObjFileParser::getHomogeneousVector3(std::vector &point3d_array) { + ai_real x, y, z, w; + copyNextWord(m_buffer, Buffersize); + x = (ai_real)fast_atof(m_buffer); + + copyNextWord(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(m_DataIt, m_DataItEnd, m_uiLine); +} + +void ObjFileParser::getTwoVectors3(std::vector &point3d_array_a, std::vector &point3d_array_b) { + ai_real x, y, z; + copyNextWord(m_buffer, Buffersize); + x = (ai_real)fast_atof(m_buffer); + + copyNextWord(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(m_DataIt, m_DataItEnd, m_uiLine); +} + +void ObjFileParser::getVector2(std::vector &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(m_DataIt, m_DataItEnd, m_uiLine); +} + +static const std::string DefaultObjName = "defaultobject"; + +void ObjFileParser::getFace(aiPrimitiveType type) { + m_DataIt = getNextToken(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(m_pModel->m_Vertices.size()); + const int vtSize = static_cast(m_pModel->m_TextureCoord.size()); + const int vnSize = static_cast(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(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(m_DataIt, m_DataItEnd, m_uiLine); +} + +void ObjFileParser::getMaterialDesc() { + // Get next data for material data + m_DataIt = getNextToken(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::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(m_DataIt, m_DataItEnd, m_uiLine); +} + +// ------------------------------------------------------------------- +// Get a comment, values will be skipped +void ObjFileParser::getComment() { + m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); +} + +// ------------------------------------------------------------------- +// Get material library from file. +void ObjFileParser::getMaterialLib() { + // Translate tuple + m_DataIt = getNextToken(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(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 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(m_DataIt, m_DataItEnd); + m_DataIt = getNextWord(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::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(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(m_DataIt, m_DataItEnd); + m_DataIt = getName(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 *pFaceIDArray = new std::vector; + 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(m_DataIt, m_DataItEnd, m_uiLine); +} + +// ------------------------------------------------------------------- +// Not supported +void ObjFileParser::getGroupNumber() { + // Not used + + m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); +} + +// ------------------------------------------------------------------- +// Not supported +void ObjFileParser::getGroupNumberAndResolution() { + // Not used + + m_DataIt = skipLine(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(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::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(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(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(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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 DataArray; + typedef std::vector::iterator DataArrayIt; + typedef std::vector::const_iterator ConstDataArrayIt; + + /// @brief The default constructor. + ObjFileParser(); + /// @brief Constructor with data array. + ObjFileParser(IOStreamBuffer &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 &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 &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 &point3d_array); + /// Stores the following 3d vector. + void getVector3(std::vector &point3d_array); + /// Stores the following homogeneous vector as a 3D vector + void getHomogeneousVector3(std::vector &point3d_array); + /// Stores the following two 3d vectors on the line. + void getTwoVectors3(std::vector &point3d_array_a, std::vector &point3d_array_b); + /// Stores the following 3d vector. + void getVector2(std::vector &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 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 +#include +#include + +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 +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 +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 +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 +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 +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 +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 +inline char_t CopyNextWord(char_t it, char_t end, char *pBuffer, size_t length) { + size_t index = 0; + it = getNextWord(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 +inline char_t getFloat(char_t it, char_t end, ai_real &value) { + static const size_t BUFFERSIZE = 1024; + char buffer[BUFFERSIZE]; + it = CopyNextWord(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 +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 +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 -- cgit v1.2.1