2
0
Эх сурвалжийг харах

Merge branch 'master' into collada_animation_ticks_per_sec_issue_3162

Fabian Schmidt 5 жил өмнө
parent
commit
338c0b753c
82 өөрчлөгдсөн 577 нэмэгдсэн , 399 устгасан
  1. 2 2
      code/AssetLib/3DS/3DSLoader.cpp
  2. 2 2
      code/AssetLib/3MF/D3MFOpcPackage.cpp
  3. 1 1
      code/AssetLib/AC/ACLoader.cpp
  4. 10 10
      code/AssetLib/AMF/AMFImporter.cpp
  5. 1 1
      code/AssetLib/ASE/ASELoader.cpp
  6. 2 2
      code/AssetLib/B3D/B3DImporter.cpp
  7. 19 18
      code/AssetLib/BVH/BVHLoader.cpp
  8. 2 1
      code/AssetLib/BVH/BVHLoader.h
  9. 1 1
      code/AssetLib/Blender/BlenderCustomData.cpp
  10. 4 12
      code/AssetLib/Blender/BlenderDNA.cpp
  11. 4 3
      code/AssetLib/Blender/BlenderDNA.h
  12. 22 34
      code/AssetLib/Blender/BlenderDNA.inl
  13. 2 3
      code/AssetLib/Blender/BlenderLoader.cpp
  14. 1 1
      code/AssetLib/COB/COBLoader.cpp
  15. 1 1
      code/AssetLib/CSM/CSMLoader.cpp
  16. 1 1
      code/AssetLib/Collada/ColladaLoader.cpp
  17. 28 27
      code/AssetLib/Collada/ColladaParser.cpp
  18. 4 2
      code/AssetLib/Collada/ColladaParser.h
  19. 1 1
      code/AssetLib/DXF/DXFLoader.cpp
  20. 2 2
      code/AssetLib/FBX/FBXBinaryTokenizer.cpp
  21. 3 3
      code/AssetLib/FBX/FBXDocumentUtil.cpp
  22. 2 2
      code/AssetLib/FBX/FBXParser.cpp
  23. 1 1
      code/AssetLib/FBX/FBXTokenizer.cpp
  24. 9 11
      code/AssetLib/FBX/FBXUtil.cpp
  25. 7 14
      code/AssetLib/FBX/FBXUtil.h
  26. 3 3
      code/AssetLib/HMP/HMPLoader.cpp
  27. 1 1
      code/AssetLib/Irr/IRRLoader.cpp
  28. 1 1
      code/AssetLib/Irr/IRRMeshLoader.cpp
  29. 3 3
      code/AssetLib/LWO/LWOLoader.cpp
  30. 1 1
      code/AssetLib/LWS/LWSLoader.cpp
  31. 5 5
      code/AssetLib/M3D/M3DImporter.cpp
  32. 1 1
      code/AssetLib/MD2/MD2Loader.cpp
  33. 1 1
      code/AssetLib/MD3/MD3Loader.cpp
  34. 1 1
      code/AssetLib/MD5/MD5Loader.cpp
  35. 1 1
      code/AssetLib/MDC/MDCLoader.cpp
  36. 2 2
      code/AssetLib/MDL/HalfLife/HL1MDLLoader.h
  37. 3 3
      code/AssetLib/MDL/MDLLoader.cpp
  38. 2 2
      code/AssetLib/MMD/MMDImporter.cpp
  39. 1 1
      code/AssetLib/MMD/MMDPmxParser.cpp
  40. 1 1
      code/AssetLib/MS3D/MS3DLoader.cpp
  41. 1 1
      code/AssetLib/NFF/NFFLoader.cpp
  42. 1 1
      code/AssetLib/OFF/OFFLoader.cpp
  43. 1 1
      code/AssetLib/Obj/ObjFileImporter.cpp
  44. 9 9
      code/AssetLib/Ogre/OgreBinarySerializer.cpp
  45. 1 1
      code/AssetLib/Ogre/OgreImporter.cpp
  46. 5 5
      code/AssetLib/Ogre/OgreStructs.cpp
  47. 18 18
      code/AssetLib/Ogre/OgreXmlSerializer.cpp
  48. 1 1
      code/AssetLib/OpenGEX/OpenGEXImporter.cpp
  49. 2 2
      code/AssetLib/Ply/PlyLoader.cpp
  50. 1 1
      code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp
  51. 2 3
      code/AssetLib/Q3D/Q3DLoader.cpp
  52. 1 1
      code/AssetLib/Raw/RawLoader.cpp
  53. 1 1
      code/AssetLib/SIB/SIBImporter.cpp
  54. 1 1
      code/AssetLib/SMD/SMDLoader.cpp
  55. 2 2
      code/AssetLib/STL/STLLoader.cpp
  56. 1 1
      code/AssetLib/Terragen/TerragenLoader.cpp
  57. 1 1
      code/AssetLib/X/XFileImporter.cpp
  58. 14 13
      code/AssetLib/X/XFileParser.cpp
  59. 2 1
      code/AssetLib/X/XFileParser.h
  60. 7 7
      code/AssetLib/X3D/FIReader.cpp
  61. 30 30
      code/AssetLib/X3D/X3DImporter.cpp
  62. 11 11
      code/AssetLib/X3D/X3DImporter_Postprocess.cpp
  63. 1 1
      code/AssetLib/XGL/XGLLoader.cpp
  64. 29 29
      code/AssetLib/glTF/glTFAsset.inl
  65. 1 1
      code/AssetLib/glTF/glTFImporter.cpp
  66. 1 1
      code/AssetLib/glTF2/glTF2Asset.h
  67. 15 15
      code/AssetLib/glTF2/glTF2Asset.inl
  68. 1 1
      code/AssetLib/glTF2/glTF2Importer.cpp
  69. 1 0
      code/CMakeLists.txt
  70. 3 2
      code/Common/BaseImporter.cpp
  71. 52 0
      code/Common/Exceptional.cpp
  72. 12 2
      code/Common/Importer.cpp
  73. 23 25
      code/Common/Importer.h
  74. 1 1
      code/PostProcessing/ValidateDataStructure.cpp
  75. 1 1
      doc/dox.h
  76. 15 1
      include/assimp/BaseImporter.h
  77. 31 21
      include/assimp/Exceptional.h
  78. 9 0
      include/assimp/Importer.hpp
  79. 3 2
      include/assimp/LogAux.h
  80. 3 0
      include/assimp/TinyFormatter.h
  81. 1 1
      include/assimp/irrXMLWrapper.h
  82. 102 0
      test/unit/utImporter.cpp

+ 2 - 2
code/AssetLib/3DS/3DSLoader.cpp

@@ -147,7 +147,7 @@ void Discreet3DSImporter::InternReadFile(const std::string &pFile,
 
     // We should have at least one chunk
     if (theStream.GetRemainingSize() < 16) {
-        throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile);
+        throw DeadlyImportError("3DS file is either empty or corrupt: ", pFile);
     }
     this->stream = &theStream;
 
@@ -178,7 +178,7 @@ void Discreet3DSImporter::InternReadFile(const std::string &pFile,
     // file.
     for (auto &mesh : mScene->mMeshes) {
         if (mesh.mFaces.size() > 0 && mesh.mPositions.size() == 0) {
-            throw DeadlyImportError("3DS file contains faces but no vertices: " + pFile);
+            throw DeadlyImportError("3DS file contains faces but no vertices: ", pFile);
         }
         CheckIndices(mesh);
         MakeUnique(mesh);

+ 2 - 2
code/AssetLib/3MF/D3MFOpcPackage.cpp

@@ -118,7 +118,7 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
         mRootStream(nullptr), mZipArchive() {
     mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile));
     if (!mZipArchive->isOpen()) {
-        throw DeadlyImportError("Failed to open file " + rFile + ".");
+        throw DeadlyImportError("Failed to open file ", rFile, ".");
     }
 
     std::vector<std::string> fileList;
@@ -192,7 +192,7 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) {
     });
 
     if (itr == reader.m_relationShips.end()) {
-        throw DeadlyImportError("Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE);
+        throw DeadlyImportError("Cannot find ", XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE);
     }
 
     return (*itr)->target;

+ 1 - 1
code/AssetLib/AC/ACLoader.cpp

@@ -762,7 +762,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open AC3D file " + pFile + ".");
+        throw DeadlyImportError("Failed to open AC3D file ", pFile, ".");
     }
 
     // allocate storage and copy the contents of the file to a memory buffer

+ 10 - 10
code/AssetLib/AMF/AMFImporter.cpp

@@ -143,23 +143,23 @@ bool AMFImporter::Find_ConvertedMaterial(const std::string &pID, const SPP_Mater
 /*********************************************************************************************************************************************/
 
 void AMFImporter::Throw_CloseNotFound(const std::string &pNode) {
-    throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
+    throw DeadlyImportError("Close tag for node <", pNode, "> not found. Seems file is corrupt.");
 }
 
 void AMFImporter::Throw_IncorrectAttr(const std::string &pAttrName) {
-    throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
+    throw DeadlyImportError("Node <", mReader->getNodeName(), "> has incorrect attribute \"", pAttrName, "\".");
 }
 
 void AMFImporter::Throw_IncorrectAttrValue(const std::string &pAttrName) {
-    throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
+    throw DeadlyImportError("Attribute \"", pAttrName, "\" in node <", mReader->getNodeName(), "> has incorrect value.");
 }
 
 void AMFImporter::Throw_MoreThanOnceDefined(const std::string &pNodeType, const std::string &pDescription) {
-    throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
+    throw DeadlyImportError("\"", pNodeType, "\" node can be used only once in ", mReader->getNodeName(), ". Description: ", pDescription);
 }
 
 void AMFImporter::Throw_ID_NotFound(const std::string &pID) const {
-    throw DeadlyImportError("Not found node with name \"" + pID + "\".");
+    throw DeadlyImportError("Not found node with name \"", pID, "\".");
 }
 
 /*********************************************************************************************************************************************/
@@ -167,7 +167,7 @@ void AMFImporter::Throw_ID_NotFound(const std::string &pID) const {
 /*********************************************************************************************************************************************/
 
 void AMFImporter::XML_CheckNode_MustHaveChildren() {
-    if (mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
+    if (mReader->isEmptyElement()) throw DeadlyImportError("Node <", mReader->getNodeName(), "> must have children.");
 }
 
 void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string &pParentNodeName) {
@@ -202,7 +202,7 @@ void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string &pParentNodeNa
 
 casu_cres:
 
-    if (!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
+    if (!found) throw DeadlyImportError("Unknown node \"", nn, "\" in ", pParentNodeName, ".");
     if (!close_found) Throw_CloseNotFound(nn);
 
     if (!skipped_before[sk_idx]) {
@@ -227,7 +227,7 @@ bool AMFImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx) {
     else if ((val == "true") || (val == "1"))
         return true;
     else
-        throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"" + val + "\"");
+        throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"", val, "\"");
 }
 
 float AMFImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx) {
@@ -367,13 +367,13 @@ void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) {
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
+        throw DeadlyImportError("Failed to open AMF file ", pFile, ".");
     }
 
     // generate a XML reader for it
     std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
     mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
-    if (!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
+    if (!mReader) throw DeadlyImportError("Failed to create XML reader for file", pFile, ".");
     //
     // start reading
     // search for root tag <amf>

+ 1 - 1
code/AssetLib/ASE/ASELoader.cpp

@@ -137,7 +137,7 @@ void ASEImporter::InternReadFile(const std::string &pFile,
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open ASE file " + pFile + ".");
+        throw DeadlyImportError("Failed to open ASE file ", pFile, ".");
     }
 
     // Allocate storage and copy the contents of the file to a memory buffer

+ 2 - 2
code/AssetLib/B3D/B3DImporter.cpp

@@ -119,7 +119,7 @@ void B3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open B3D file " + pFile + ".");
+        throw DeadlyImportError("Failed to open B3D file ", pFile, ".");
     }
 
     // check whether the .b3d file is large enough to contain
@@ -147,7 +147,7 @@ AI_WONT_RETURN void B3DImporter::Fail(string str) {
 #ifdef DEBUG_B3D
     ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str);
 #endif
-    throw DeadlyImportError("B3D Importer - error in B3D file data: " + str);
+    throw DeadlyImportError("B3D Importer - error in B3D file data: ", str);
 }
 
 // ------------------------------------------------------------------------------------------------

+ 19 - 18
code/AssetLib/BVH/BVHLoader.cpp

@@ -71,6 +71,13 @@ static const aiImporterDesc desc = {
     "bvh"
 };
 
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+template<typename... T>
+AI_WONT_RETURN void BVHLoader::ThrowException(T&&... args) {
+    throw DeadlyImportError(mFileName, ":", mLine, " - ", args...);
+}
+
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 BVHLoader::BVHLoader() :
@@ -118,7 +125,7 @@ void BVHLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IOSyst
     // read file into memory
     std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open file " + pFile + ".");
+        throw DeadlyImportError("Failed to open file ", pFile, ".");
     }
 
     size_t fileSize = file->FileSize();
@@ -176,12 +183,12 @@ aiNode *BVHLoader::ReadNode() {
     // first token is name
     std::string nodeName = GetNextToken();
     if (nodeName.empty() || nodeName == "{")
-        ThrowException(format() << "Expected node name, but found \"" << nodeName << "\".");
+        ThrowException("Expected node name, but found \"", nodeName, "\".");
 
     // then an opening brace should follow
     std::string openBrace = GetNextToken();
     if (openBrace != "{")
-        ThrowException(format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
+        ThrowException("Expected opening brace \"{\", but found \"", openBrace, "\".");
 
     // Create a node
     aiNode *node = new aiNode(nodeName);
@@ -211,7 +218,7 @@ aiNode *BVHLoader::ReadNode() {
             siteToken.clear();
             siteToken = GetNextToken();
             if (siteToken != "Site")
-                ThrowException(format() << "Expected \"End Site\" keyword, but found \"" << token << " " << siteToken << "\".");
+                ThrowException("Expected \"End Site\" keyword, but found \"", token, " ", siteToken, "\".");
 
             aiNode *child = ReadEndSite(nodeName);
             child->mParent = node;
@@ -221,7 +228,7 @@ aiNode *BVHLoader::ReadNode() {
             break;
         } else {
             // everything else is a parse error
-            ThrowException(format() << "Unknown keyword \"" << token << "\".");
+            ThrowException("Unknown keyword \"", token, "\".");
         }
     }
 
@@ -242,7 +249,7 @@ aiNode *BVHLoader::ReadEndSite(const std::string &pParentName) {
     // check opening brace
     std::string openBrace = GetNextToken();
     if (openBrace != "{")
-        ThrowException(format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
+        ThrowException("Expected opening brace \"{\", but found \"", openBrace, "\".");
 
     // Create a node
     aiNode *node = new aiNode("EndSite_" + pParentName);
@@ -261,7 +268,7 @@ aiNode *BVHLoader::ReadEndSite(const std::string &pParentName) {
             break;
         } else {
             // everything else is a parse error
-            ThrowException(format() << "Unknown keyword \"" << token << "\".");
+            ThrowException("Unknown keyword \"", token, "\".");
         }
     }
 
@@ -307,7 +314,7 @@ void BVHLoader::ReadNodeChannels(BVHLoader::Node &pNode) {
         else if (channelToken == "Zrotation")
             pNode.mChannels.push_back(Channel_RotationZ);
         else
-            ThrowException(format() << "Invalid channel specifier \"" << channelToken << "\".");
+            ThrowException("Invalid channel specifier \"", channelToken, "\".");
     }
 }
 
@@ -317,7 +324,7 @@ void BVHLoader::ReadMotion(aiScene * /*pScene*/) {
     // Read number of frames
     std::string tokenFrames = GetNextToken();
     if (tokenFrames != "Frames:")
-        ThrowException(format() << "Expected frame count \"Frames:\", but found \"" << tokenFrames << "\".");
+        ThrowException("Expected frame count \"Frames:\", but found \"", tokenFrames, "\".");
 
     float numFramesFloat = GetNextTokenAsFloat();
     mAnimNumFrames = (unsigned int)numFramesFloat;
@@ -326,7 +333,7 @@ void BVHLoader::ReadMotion(aiScene * /*pScene*/) {
     std::string tokenDuration1 = GetNextToken();
     std::string tokenDuration2 = GetNextToken();
     if (tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
-        ThrowException(format() << "Expected frame duration \"Frame Time:\", but found \"" << tokenDuration1 << " " << tokenDuration2 << "\".");
+        ThrowException("Expected frame duration \"Frame Time:\", but found \"", tokenDuration1, " ", tokenDuration2, "\".");
 
     mAnimTickDuration = GetNextTokenAsFloat();
 
@@ -393,17 +400,11 @@ float BVHLoader::GetNextTokenAsFloat() {
     ctoken = fast_atoreal_move<float>(ctoken, result);
 
     if (ctoken != token.c_str() + token.length())
-        ThrowException(format() << "Expected a floating point number, but found \"" << token << "\".");
+        ThrowException("Expected a floating point number, but found \"", token, "\".");
 
     return result;
 }
 
-// ------------------------------------------------------------------------------------------------
-// Aborts the file reading with an exception
-AI_WONT_RETURN void BVHLoader::ThrowException(const std::string &pError) {
-    throw DeadlyImportError(format() << mFileName << ":" << mLine << " - " << pError);
-}
-
 // ------------------------------------------------------------------------------------------------
 // Constructs an animation for the motion data and stores it in the given scene
 void BVHLoader::CreateAnimation(aiScene *pScene) {
@@ -453,7 +454,7 @@ void BVHLoader::CreateAnimation(aiScene *pScene) {
                     std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
 
                     if (mapIter == channelMap.end())
-                        throw DeadlyImportError("Missing position channel in node " + nodeName);
+                        throw DeadlyImportError("Missing position channel in node ", nodeName);
                     else {
                         int channelIdx = mapIter->second;
                         switch (channel) {

+ 2 - 1
code/AssetLib/BVH/BVHLoader.h

@@ -134,7 +134,8 @@ protected:
     float GetNextTokenAsFloat();
 
     /** Aborts the file reading with an exception */
-    AI_WONT_RETURN void ThrowException(const std::string &pError) AI_WONT_RETURN_SUFFIX;
+    template<typename... T>
+    AI_WONT_RETURN void ThrowException(T&&... args) AI_WONT_RETURN_SUFFIX;
 
     /** Constructs an animation for the motion data and stores it in the given scene */
     void CreateAnimation(aiScene *pScene);

+ 1 - 1
code/AssetLib/Blender/BlenderCustomData.cpp

@@ -149,7 +149,7 @@ bool isValidCustomDataType(const int cdtype) {
 
 bool readCustomData(std::shared_ptr<ElemBase> &out, const int cdtype, const size_t cnt, const FileDatabase &db) {
     if (!isValidCustomDataType(cdtype)) {
-        throw Error((Formatter::format(), "CustomData.type ", cdtype, " out of index"));
+        throw Error("CustomData.type ", cdtype, " out of index");
     }
 
     const CustomDataTypeDescription cdtd = customDataTypeDescriptions[cdtype];

+ 4 - 12
code/AssetLib/Blender/BlenderDNA.cpp

@@ -130,9 +130,7 @@ void DNAParser::Parse() {
 
         uint16_t n = stream.GetI2();
         if (n >= types.size()) {
-            throw DeadlyImportError((format(),
-                    "BlenderDNA: Invalid type index in structure name", n,
-                    " (there are only ", types.size(), " entries)"));
+            throw DeadlyImportError("BlenderDNA: Invalid type index in structure name", n, " (there are only ", types.size(), " entries)");
         }
 
         // maintain separate indexes
@@ -151,9 +149,7 @@ void DNAParser::Parse() {
 
             uint16_t j = stream.GetI2();
             if (j >= types.size()) {
-                throw DeadlyImportError((format(),
-                        "BlenderDNA: Invalid type index in structure field ", j,
-                        " (there are only ", types.size(), " entries)"));
+                throw DeadlyImportError("BlenderDNA: Invalid type index in structure field ", j, " (there are only ", types.size(), " entries)");
             }
             s.fields.push_back(Field());
             Field &f = s.fields.back();
@@ -164,9 +160,7 @@ void DNAParser::Parse() {
 
             j = stream.GetI2();
             if (j >= names.size()) {
-                throw DeadlyImportError((format(),
-                        "BlenderDNA: Invalid name index in structure field ", j,
-                        " (there are only ", names.size(), " entries)"));
+                throw DeadlyImportError("BlenderDNA: Invalid name index in structure field ", j, " (there are only ", names.size(), " entries)");
             }
 
             f.name = names[j];
@@ -188,9 +182,7 @@ void DNAParser::Parse() {
             if (*f.name.rbegin() == ']') {
                 const std::string::size_type rb = f.name.find('[');
                 if (rb == std::string::npos) {
-                    throw DeadlyImportError((format(),
-                            "BlenderDNA: Encountered invalid array declaration ",
-                            f.name));
+                    throw DeadlyImportError("BlenderDNA: Encountered invalid array declaration ", f.name);
                 }
 
                 f.flags |= FieldFlag_Array;

+ 4 - 3
code/AssetLib/Blender/BlenderDNA.h

@@ -83,9 +83,10 @@ class ObjectCache;
  *  ancestry. */
 // -------------------------------------------------------------------------------
 struct Error : DeadlyImportError {
-    Error(const std::string &s) :
-            DeadlyImportError(s) {
-        // empty
+    template<typename... T>
+    explicit Error(T&&... args)
+        : DeadlyImportError(args...)
+    {
     }
 };
 

+ 22 - 34
code/AssetLib/Blender/BlenderDNA.inl

@@ -57,9 +57,7 @@ const Field& Structure :: operator [] (const std::string& ss) const
 {
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
     if (it == indices.end()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`"
-            ));
+        throw Error("BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`");
     }
 
     return fields[(*it).second];
@@ -76,9 +74,7 @@ const Field* Structure :: Get (const std::string& ss) const
 const Field& Structure :: operator [] (const size_t i) const
 {
     if (i >= fields.size()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: There is no field with index `",i,"` in structure `",name,"`"
-            ));
+        throw Error("BlendDNA: There is no field with index `",i,"` in structure `",name,"`");
     }
 
     return fields[i];
@@ -109,9 +105,7 @@ void Structure :: ReadFieldArray(T (& out)[M], const char* name, const FileDatab
 
         // is the input actually an array?
         if (!(f.flags & FieldFlag_Array)) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
-                this->name,"` ought to be an array of size ",M
-                ));
+            throw Error("Field `",name,"` of structure `",this->name,"` ought to be an array of size ",M);
         }
 
         db.reader->IncPtr(f.offset);
@@ -148,9 +142,9 @@ void Structure :: ReadFieldArray2(T (& out)[M][N], const char* name, const FileD
 
         // is the input actually an array?
         if (!(f.flags & FieldFlag_Array)) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
+            throw Error("Field `",name,"` of structure `",
                 this->name,"` ought to be an array of size ",M,"*",N
-                ));
+                );
         }
 
         db.reader->IncPtr(f.offset);
@@ -195,8 +189,8 @@ bool Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabas
 
         // sanity check, should never happen if the genblenddna script is right
         if (!(f->flags & FieldFlag_Pointer)) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
-                this->name,"` ought to be a pointer"));
+            throw Error("Field `",name,"` of structure `",
+                this->name,"` ought to be a pointer");
         }
 
         db.reader->IncPtr(f->offset);
@@ -241,8 +235,8 @@ bool Structure :: ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
 #ifdef _DEBUG
         // sanity check, should never happen if the genblenddna script is right
         if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
-                this->name,"` ought to be a pointer AND an array"));
+            throw Error("Field `",name,"` of structure `",
+                this->name,"` ought to be a pointer AND an array");
         }
 #endif // _DEBUG
 
@@ -322,8 +316,8 @@ bool Structure::ReadCustomDataPtr(std::shared_ptr<ElemBase>&out, int cdtype, con
 
 		// sanity check, should never happen if the genblenddna script is right
 		if (!(f->flags & FieldFlag_Pointer)) {
-			throw Error((Formatter::format(), "Field `", name, "` of structure `",
-				this->name, "` ought to be a pointer"));
+			throw Error("Field `", name, "` of structure `",
+				this->name, "` ought to be a pointer");
 		}
 
 		db.reader->IncPtr(f->offset);
@@ -369,8 +363,8 @@ bool Structure::ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const
 
 		// sanity check, should never happen if the genblenddna script is right
 		if (!(f->flags & FieldFlag_Pointer)) {
-			throw Error((Formatter::format(), "Field `", name, "` of structure `",
-				this->name, "` ought to be a pointer"));
+			throw Error("Field `", name, "` of structure `",
+				this->name, "` ought to be a pointer");
 		}
 
 		db.reader->IncPtr(f->offset);
@@ -428,9 +422,9 @@ bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const Fil
     // and check if it matches the type which we expect.
     const Structure& ss = db.dna[block->dna_index];
     if (ss != s) {
-        throw Error((Formatter::format(),"Expected target to be of type `",s.name,
+        throw Error("Expected target to be of type `",s.name,
             "` but seemingly it is a `",ss.name,"` instead"
-            ));
+            );
     }
 
     // try to retrieve the object from the cache
@@ -614,16 +608,14 @@ const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrv
     if (it == db.entries.end()) {
         // this is crucial, pointers may not be invalid.
         // this is either a corrupted file or an attempted attack.
-        throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
-            std::hex,ptrval.val,", no file block falls into this address range"
-            ));
+        throw DeadlyImportError("Failure resolving pointer 0x",
+            std::hex,ptrval.val,", no file block falls into this address range");
     }
     if (ptrval.val >= (*it).address.val + (*it).size) {
-        throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
+        throw DeadlyImportError("Failure resolving pointer 0x",
             std::hex,ptrval.val,", nearest file block starting at 0x",
             (*it).address.val," ends at 0x",
-            (*it).address.val + (*it).size
-            ));
+            (*it).address.val + (*it).size);
     }
     return &*it;
 }
@@ -676,7 +668,7 @@ template <typename T> inline void ConvertDispatcher(T& out, const Structure& in,
         out = static_cast<T>(db.reader->GetF8());
     }
     else {
-        throw DeadlyImportError("Unknown source for conversion to primitive data type: "+in.name);
+        throw DeadlyImportError("Unknown source for conversion to primitive data type: ", in.name);
     }
 }
 
@@ -784,9 +776,7 @@ const Structure& DNA :: operator [] (const std::string& ss) const
 {
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
     if (it == indices.end()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: Did not find a structure named `",ss,"`"
-            ));
+        throw Error("BlendDNA: Did not find a structure named `",ss,"`");
     }
 
     return structures[(*it).second];
@@ -803,9 +793,7 @@ const Structure* DNA :: Get (const std::string& ss) const
 const Structure& DNA :: operator [] (const size_t i) const
 {
     if (i >= structures.size()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: There is no structure with index `",i,"`"
-            ));
+        throw Error("BlendDNA: There is no structure with index `",i,"`");
     }
 
     return structures[i];

+ 2 - 3
code/AssetLib/Blender/BlenderLoader.cpp

@@ -748,9 +748,8 @@ void BlenderImporter::BuildMaterials(ConversionData &conv_data) {
 void BlenderImporter::CheckActualType(const ElemBase *dt, const char *check) {
     ai_assert(dt);
     if (strcmp(dt->dna_type, check)) {
-        ThrowException((format(),
-                "Expected object at ", std::hex, dt, " to be of type `", check,
-                "`, but it claims to be a `", dt->dna_type, "`instead"));
+        ThrowException("Expected object at ", std::hex, dt, " to be of type `", check,
+                "`, but it claims to be a `", dt->dna_type, "`instead");
     }
 }
 

+ 1 - 1
code/AssetLib/COB/COBLoader.cpp

@@ -125,7 +125,7 @@ void COBImporter::SetupProperties(const Importer * /*pImp*/) {
 
 // ------------------------------------------------------------------------------------------------
 /*static*/ AI_WONT_RETURN void COBImporter::ThrowException(const std::string &msg) {
-    throw DeadlyImportError("COB: " + msg);
+    throw DeadlyImportError("COB: ", msg);
 }
 
 // ------------------------------------------------------------------------------------------------

+ 1 - 1
code/AssetLib/CSM/CSMLoader.cpp

@@ -128,7 +128,7 @@ void CSMImporter::InternReadFile( const std::string& pFile,
 
     // Check whether we can read from the file
     if( file.get() == nullptr) {
-        throw DeadlyImportError( "Failed to open CSM file " + pFile + ".");
+        throw DeadlyImportError( "Failed to open CSM file ", pFile, ".");
     }
 
     // allocate storage and copy the contents of the file to a memory buffer

+ 1 - 1
code/AssetLib/Collada/ColladaLoader.cpp

@@ -1251,7 +1251,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
 
             // time count and value count must match
             if (e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
-                throw DeadlyImportError(format() << "Time count / value count mismatch in animation channel \"" << e.mChannel->mTarget << "\".");
+                throw DeadlyImportError("Time count / value count mismatch in animation channel \"", e.mChannel->mTarget, "\".");
 
             if (e.mTimeAccessor->mCount > 0) {
                 // find bounding times

+ 28 - 27
code/AssetLib/Collada/ColladaParser.cpp

@@ -66,6 +66,13 @@ using namespace Assimp;
 using namespace Assimp::Collada;
 using namespace Assimp::Formatter;
 
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+template<typename... T>
+AI_WONT_RETURN void ColladaParser::ThrowException(T&&... args) const {
+    throw DeadlyImportError("Collada: ", mFileName, " - ", args...);
+}
+
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) :
@@ -116,7 +123,7 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) :
         // attempt to open the file directly
         daefile.reset(pIOHandler->Open(pFile));
         if (daefile.get() == nullptr) {
-            throw DeadlyImportError("Failed to open file '" + pFile + "'.");
+            throw DeadlyImportError("Failed to open file '", pFile, "'.");
         }
     }
 
@@ -853,7 +860,7 @@ void ColladaParser::ReadControllerJoints(Collada::Controller &pController) {
 
                 // local URLS always start with a '#'. We don't support global URLs
                 if (attrSource[0] != '#')
-                    ThrowException(format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <joints> data <input> element");
+                    ThrowException("Unsupported URL format in \"", attrSource, "\" in source attribute of <joints> data <input> element");
                 attrSource++;
 
                 // parse source URL to corresponding source
@@ -862,7 +869,7 @@ void ColladaParser::ReadControllerJoints(Collada::Controller &pController) {
                 else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0)
                     pController.mJointOffsetMatrixSource = attrSource;
                 else
-                    ThrowException(format() << "Unknown semantic \"" << attrSemantic << "\" in <joints> data <input> element");
+                    ThrowException("Unknown semantic \"", attrSemantic, "\" in <joints> data <input> element");
 
                 // skip inner data, if present
                 if (!mReader->isEmptyElement())
@@ -904,7 +911,7 @@ void ColladaParser::ReadControllerWeights(Collada::Controller &pController) {
 
                 // local URLS always start with a '#'. We don't support global URLs
                 if (attrSource[0] != '#')
-                    ThrowException(format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <vertex_weights> data <input> element");
+                    ThrowException("Unsupported URL format in \"", attrSource, "\" in source attribute of <vertex_weights> data <input> element");
                 channel.mAccessor = attrSource + 1;
 
                 // parse source URL to corresponding source
@@ -913,7 +920,7 @@ void ColladaParser::ReadControllerWeights(Collada::Controller &pController) {
                 else if (strcmp(attrSemantic, "WEIGHT") == 0)
                     pController.mWeightInputWeights = channel;
                 else
-                    ThrowException(format() << "Unknown semantic \"" << attrSemantic << "\" in <vertex_weights> data <input> element");
+                    ThrowException("Unknown semantic \"", attrSemantic, "\" in <vertex_weights> data <input> element");
 
                 // skip inner data, if present
                 if (!mReader->isEmptyElement())
@@ -1901,7 +1908,7 @@ void ColladaParser::ReadAccessor(const std::string &pID) {
     int attrSource = GetAttribute("source");
     const char *source = mReader->getAttributeValue(attrSource);
     if (source[0] != '#')
-        ThrowException(format() << "Unknown reference format in url \"" << source << "\" in source attribute of <accessor> element.");
+        ThrowException("Unknown reference format in url \"", source, "\" in source attribute of <accessor> element.");
     int attrCount = GetAttribute("count");
     unsigned int count = (unsigned int)mReader->getAttributeValueAsInt(attrCount);
     int attrOffset = TestAttribute("offset");
@@ -1968,7 +1975,7 @@ void ColladaParser::ReadAccessor(const std::string &pID) {
                     else if (name == "V")
                         acc.mSubOffset[1] = acc.mParams.size();
                     //else
-                    //  DefaultLogger::get()->warn( format() << "Unknown accessor parameter \"" << name << "\". Ignoring data channel." );
+                    //  DefaultLogger::get()->warn( "Unknown accessor parameter \"", name, "\". Ignoring data channel." );
                 }
 
                 // read data type
@@ -1989,7 +1996,7 @@ void ColladaParser::ReadAccessor(const std::string &pID) {
                 // skip remaining stuff of this element, if any
                 SkipElement();
             } else {
-                ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <accessor>");
+                ThrowException("Unexpected sub element <", mReader->getNodeName(), "> in tag <accessor>");
             }
         } else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
             if (strcmp(mReader->getNodeName(), "accessor") != 0)
@@ -2012,7 +2019,7 @@ void ColladaParser::ReadVertexData(Mesh &pMesh) {
             if (IsElement("input")) {
                 ReadInputChannel(pMesh.mPerVertexData);
             } else {
-                ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <vertices>");
+                ThrowException("Unexpected sub element <", mReader->getNodeName(), "> in tag <vertices>");
             }
         } else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
             if (strcmp(mReader->getNodeName(), "vertices") != 0)
@@ -2096,11 +2103,11 @@ void ColladaParser::ReadIndexData(Mesh &pMesh) {
             } else if (IsElement("ph")) {
                 SkipElement("ph");
             } else {
-                ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <" << elementName << ">");
+                ThrowException("Unexpected sub element <", mReader->getNodeName(), "> in tag <", elementName, ">");
             }
         } else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
             if (mReader->getNodeName() != elementName)
-                ThrowException(format() << "Expected end of <" << elementName << "> element.");
+                ThrowException("Expected end of <", elementName, "> element.");
 
             break;
         }
@@ -2132,7 +2139,7 @@ void ColladaParser::ReadInputChannel(std::vector<InputChannel> &poChannels) {
     int attrSource = GetAttribute("source");
     const char *source = mReader->getAttributeValue(attrSource);
     if (source[0] != '#')
-        ThrowException(format() << "Unknown reference format in url \"" << source << "\" in source attribute of <input> element.");
+        ThrowException("Unknown reference format in url \"", source, "\" in source attribute of <input> element.");
     channel.mAccessor = source + 1; // skipping the leading #, hopefully the remaining text is the accessor ID only
 
     // read index offset, if per-index <input>
@@ -2146,7 +2153,7 @@ void ColladaParser::ReadInputChannel(std::vector<InputChannel> &poChannels) {
         if (attrSet > -1) {
             attrSet = mReader->getAttributeValueAsInt(attrSet);
             if (attrSet < 0)
-                ThrowException(format() << "Invalid index \"" << (attrSet) << "\" in set attribute of <input> element");
+                ThrowException("Invalid index \"", (attrSet), "\" in set attribute of <input> element");
 
             channel.mIndex = attrSet;
         }
@@ -2369,7 +2376,7 @@ void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, siz
 
     const Accessor &acc = *pInput.mResolved;
     if (pLocalIndex >= acc.mCount)
-        ThrowException(format() << "Invalid data index (" << pLocalIndex << "/" << acc.mCount << ") in primitive specification");
+        ThrowException("Invalid data index (", pLocalIndex, "/", acc.mCount, ") in primitive specification");
 
     // get a pointer to the start of the data object referred to by the accessor and the local index
     const ai_real *dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex * acc.mStride;
@@ -2781,12 +2788,6 @@ void ColladaParser::ReadScene() {
     }
 }
 
-// ------------------------------------------------------------------------------------------------
-// Aborts the file reading with an exception
-AI_WONT_RETURN void ColladaParser::ThrowException(const std::string &pError) const {
-    throw DeadlyImportError(format() << "Collada: " << mFileName << " - " << pError);
-}
-
 void ColladaParser::ReportWarning(const char *msg, ...) {
     ai_assert(nullptr != msg);
 
@@ -2833,17 +2834,17 @@ void ColladaParser::SkipElement(const char *pElement) {
 void ColladaParser::TestOpening(const char *pName) {
     // read element start
     if (!mReader->read()) {
-        ThrowException(format() << "Unexpected end of file while beginning of <" << pName << "> element.");
+        ThrowException("Unexpected end of file while beginning of <", pName, "> element.");
     }
     // whitespace in front is ok, just read again if found
     if (mReader->getNodeType() == irr::io::EXN_TEXT) {
         if (!mReader->read()) {
-            ThrowException(format() << "Unexpected end of file while reading beginning of <" << pName << "> element.");
+            ThrowException("Unexpected end of file while reading beginning of <", pName, "> element.");
         }
     }
 
     if (mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp(mReader->getNodeName(), pName) != 0) {
-        ThrowException(format() << "Expected start of <" << pName << "> element.");
+        ThrowException("Expected start of <", pName, "> element.");
     }
 }
 
@@ -2862,18 +2863,18 @@ void ColladaParser::TestClosing(const char *pName) {
 
     // if not, read some more
     if (!mReader->read()) {
-        ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element.");
+        ThrowException("Unexpected end of file while reading end of <", pName, "> element.");
     }
     // whitespace in front is ok, just read again if found
     if (mReader->getNodeType() == irr::io::EXN_TEXT) {
         if (!mReader->read()) {
-            ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element.");
+            ThrowException("Unexpected end of file while reading end of <", pName, "> element.");
         }
     }
 
     // but this has the be the closing tag, or we're lost
     if (mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp(mReader->getNodeName(), pName) != 0) {
-        ThrowException(format() << "Expected end of <" << pName << "> element.");
+        ThrowException("Expected end of <", pName, "> element.");
     }
 }
 
@@ -2882,7 +2883,7 @@ void ColladaParser::TestClosing(const char *pName) {
 int ColladaParser::GetAttribute(const char *pAttr) const {
     int index = TestAttribute(pAttr);
     if (index == -1) {
-        ThrowException(format() << "Expected attribute \"" << pAttr << "\" for element <" << mReader->getNodeName() << ">.");
+        ThrowException("Expected attribute \"", pAttr, "\" for element <", mReader->getNodeName(), ">.");
     }
 
     // attribute not found -> throw an exception

+ 4 - 2
code/AssetLib/Collada/ColladaParser.h

@@ -242,7 +242,9 @@ protected:
 
 protected:
     /** Aborts the file reading with an exception */
-    AI_WONT_RETURN void ThrowException(const std::string &pError) const AI_WONT_RETURN_SUFFIX;
+    template<typename... T>
+    AI_WONT_RETURN void ThrowException(T&&... args) const AI_WONT_RETURN_SUFFIX;
+
     void ReportWarning(const char *msg, ...);
 
     /** Skips all data until the end node of the current element */
@@ -383,7 +385,7 @@ template <typename Type>
 const Type &ColladaParser::ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const {
     typename std::map<std::string, Type>::const_iterator it = pLibrary.find(pURL);
     if (it == pLibrary.end())
-        ThrowException(Formatter::format() << "Unable to resolve library reference \"" << pURL << "\".");
+        ThrowException("Unable to resolve library reference \"", pURL, "\".");
     return it->second;
 }
 

+ 1 - 1
code/AssetLib/DXF/DXFLoader.cpp

@@ -152,7 +152,7 @@ void DXFImporter::InternReadFile( const std::string& filename, aiScene* pScene,
 
     // Check whether we can read the file
     if( file.get() == nullptr ) {
-        throw DeadlyImportError( "Failed to open DXF file " + filename + "");
+        throw DeadlyImportError( "Failed to open DXF file ", filename, "");
     }
 
     // Check whether this is a binary DXF file - we can't read binary DXF files :-(

+ 2 - 2
code/AssetLib/FBX/FBXBinaryTokenizer.cpp

@@ -127,7 +127,7 @@ namespace {
 AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset)
 {
-    throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset));
+    throw DeadlyImportError("FBX-Tokenize", Util::GetOffsetText(offset), message);
 }
 
 
@@ -468,7 +468,7 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
     catch (const DeadlyImportError& e)
     {
         if (!is64bits && (length > std::numeric_limits<std::uint32_t>::max())) {
-            throw DeadlyImportError("The FBX file is invalid. This may be because the content is too big for this older version (" + to_string(version) + ") of the FBX format. (" + e.what() + ")");
+            throw DeadlyImportError("The FBX file is invalid. This may be because the content is too big for this older version (", to_string(version), ") of the FBX format. (", e.what(), ")");
         }
         throw;
     }

+ 3 - 3
code/AssetLib/FBX/FBXDocumentUtil.cpp

@@ -61,7 +61,7 @@ namespace Util {
 // signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError.
 void DOMError(const std::string& message, const Token& token)
 {
-    throw DeadlyImportError(Util::AddTokenText("FBX-DOM",message,&token));
+    throw DeadlyImportError("FBX-DOM", Util::GetTokenText(&token), message);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -70,7 +70,7 @@ void DOMError(const std::string& message, const Element* element /*= nullptr*/)
     if(element) {
         DOMError(message,element->KeyToken());
     }
-    throw DeadlyImportError("FBX-DOM " + message);
+    throw DeadlyImportError("FBX-DOM ", message);
 }
 
 
@@ -79,7 +79,7 @@ void DOMError(const std::string& message, const Element* element /*= nullptr*/)
 void DOMWarning(const std::string& message, const Token& token)
 {
     if(DefaultLogger::get()) {
-        ASSIMP_LOG_WARN(Util::AddTokenText("FBX-DOM",message,&token));
+        ASSIMP_LOG_WARN_F("FBX-DOM", Util::GetTokenText(&token), message);
     }
 }
 

+ 2 - 2
code/AssetLib/FBX/FBXParser.cpp

@@ -73,7 +73,7 @@ namespace {
     AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX;
     AI_WONT_RETURN void ParseError(const std::string& message, const Token& token)
     {
-        throw DeadlyImportError(Util::AddTokenText("FBX-Parser",message,&token));
+        throw DeadlyImportError("FBX-Parser", Util::GetTokenText(&token), message);
     }
 
     // ------------------------------------------------------------------------------------------------
@@ -83,7 +83,7 @@ namespace {
         if(element) {
             ParseError(message,element->KeyToken());
         }
-        throw DeadlyImportError("FBX-Parser " + message);
+        throw DeadlyImportError("FBX-Parser ", message);
     }
 
 

+ 1 - 1
code/AssetLib/FBX/FBXTokenizer.cpp

@@ -90,7 +90,7 @@ namespace {
 AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column)
 {
-    throw DeadlyImportError(Util::AddLineAndColumn("FBX-Tokenize",message,line,column));
+    throw DeadlyImportError("FBX-Tokenize", Util::GetLineAndColumnText(line,column), message);
 }
 
 

+ 9 - 11
code/AssetLib/FBX/FBXUtil.cpp

@@ -86,32 +86,30 @@ const char* TokenTypeString(TokenType t)
 
 
 // ------------------------------------------------------------------------------------------------
-std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset)
+std::string GetOffsetText(size_t offset)
 {
-    return static_cast<std::string>( (Formatter::format() << prefix << " (offset 0x" << std::hex << offset << ") " << text) );
+    return static_cast<std::string>( Formatter::format() << " (offset 0x" << std::hex << offset << ") " );
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column)
+std::string GetLineAndColumnText(unsigned int line, unsigned int column)
 {
-    return static_cast<std::string>( (Formatter::format() << prefix << " (line " << line << " <<  col " << column << ") " << text) );
+    return static_cast<std::string>( Formatter::format() << " (line " << line << " <<  col " << column << ") " );
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok)
+std::string GetTokenText(const Token* tok)
 {
     if(tok->IsBinary()) {
-        return static_cast<std::string>( (Formatter::format() << prefix <<
+        return static_cast<std::string>( Formatter::format() << 
             " (" << TokenTypeString(tok->Type()) <<
-            ", offset 0x" << std::hex << tok->Offset() << ") " <<
-            text) );
+            ", offset 0x" << std::hex << tok->Offset() << ") " );
     }
 
-    return static_cast<std::string>( (Formatter::format() << prefix <<
+    return static_cast<std::string>( Formatter::format() <<
         " (" << TokenTypeString(tok->Type()) <<
         ", line " << tok->Line() <<
-        ", col " << tok->Column() << ") " <<
-        text) );
+        ", col " << tok->Column() << ") " );
 }
 
 // Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;

+ 7 - 14
code/AssetLib/FBX/FBXUtil.h

@@ -73,31 +73,24 @@ const char* TokenTypeString(TokenType t);
 
 /** Format log/error messages using a given offset in the source binary file
  *
- *  @param prefix Message prefix to be preprended to the location info.
- *  @param text Message text
- *  @param line Line index, 1-based
- *  @param column Column index, 1-based
- *  @return A string of the following format: {prefix} (offset 0x{offset}) {text}*/
-std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset);
+ *  @param offset offset within the file
+ *  @return A string of the following format: " (offset 0x{offset}) "*/
+std::string GetOffsetText(size_t offset);
 
 
 /** Format log/error messages using a given line location in the source file.
  *
- *  @param prefix Message prefix to be preprended to the location info.
- *  @param text Message text
  *  @param line Line index, 1-based
  *  @param column Column index, 1-based
- *  @return A string of the following format: {prefix} (line {line}, col {column}) {text}*/
-std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column);
+ *  @return A string of the following format: " (line {line}, col {column}) "*/
+std::string GetLineAndColumnText(unsigned int line, unsigned int column);
 
 
 /** Format log/error messages using a given cursor token.
  *
- *  @param prefix Message prefix to be preprended to the location info.
- *  @param text Message text
  *  @param tok Token where parsing/processing stopped
- *  @return A string of the following format: {prefix} ({token-type}, line {line}, col {column}) {text}*/
-std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok);
+ *  @return A string of the following format: " ({token-type}, line {line}, col {column}) "*/
+std::string GetTokenText(const Token* tok);
 
 /** Decode a single Base64-encoded character.
 *

+ 3 - 3
code/AssetLib/HMP/HMPLoader.cpp

@@ -115,7 +115,7 @@ void HMPImporter::InternReadFile(const std::string &pFile,
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open HMP file " + pFile + ".");
+        throw DeadlyImportError("Failed to open HMP file ", pFile, ".");
     }
 
     // Check whether the HMP file is large enough to contain
@@ -159,8 +159,8 @@ void HMPImporter::InternReadFile(const std::string &pFile,
         szBuffer[4] = '\0';
 
         // We're definitely unable to load this file
-        throw DeadlyImportError("Unknown HMP subformat " + pFile +
-                                ". Magic word (" + szBuffer + ") is not known");
+        throw DeadlyImportError("Unknown HMP subformat ", pFile,
+                                ". Magic word (", szBuffer, ") is not known");
     }
 
     // Set the AI_SCENE_FLAGS_TERRAIN bit

+ 1 - 1
code/AssetLib/Irr/IRRLoader.cpp

@@ -867,7 +867,7 @@ void IRRImporter::InternReadFile(const std::string &pFile,
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open IRR file " + pFile + "");
+        throw DeadlyImportError("Failed to open IRR file ", pFile, "");
     }
 
     // Construct the irrXML parser

+ 1 - 1
code/AssetLib/Irr/IRRMeshLoader.cpp

@@ -140,7 +140,7 @@ void IRRMeshImporter::InternReadFile( const std::string& pFile,
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open IRRMESH file " + pFile + ".");
+        throw DeadlyImportError("Failed to open IRRMESH file ", pFile, ".");
     }
 
     // Construct the irrXML parser

+ 3 - 3
code/AssetLib/LWO/LWOLoader.cpp

@@ -145,7 +145,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open LWO file " + pFile + ".");
+        throw DeadlyImportError("Failed to open LWO file ", pFile, ".");
     }
 
     if ((this->fileSize = (unsigned int)file->FileSize()) < 12) {
@@ -212,7 +212,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
         szBuff[2] = (char)(fileType >> 8u);
         szBuff[3] = (char)(fileType);
         szBuff[4] = '\0';
-        throw DeadlyImportError(std::string("Unknown LWO sub format: ") + szBuff);
+        throw DeadlyImportError("Unknown LWO sub format: ", szBuff);
     }
 
     if (AI_LWO_FOURCC_LWOB != fileType) {
@@ -232,7 +232,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
         }
 
         if (configLayerName.length() && !hasNamedLayer) {
-            throw DeadlyImportError("LWO2: Unable to find the requested layer: " + configLayerName);
+            throw DeadlyImportError("LWO2: Unable to find the requested layer: ", configLayerName);
         }
     }
 

+ 1 - 1
code/AssetLib/LWS/LWSLoader.cpp

@@ -502,7 +502,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open LWS file " + pFile + ".");
+        throw DeadlyImportError("Failed to open LWS file ", pFile, ".");
     }
 
     // Allocate storage and copy the contents of the file to a memory buffer

+ 5 - 5
code/AssetLib/M3D/M3DImporter.cpp

@@ -160,21 +160,21 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys
     // Read file into memory
     std::unique_ptr<IOStream> pStream(pIOHandler->Open(file, "rb"));
     if (!pStream.get()) {
-        throw DeadlyImportError("Failed to open file " + file + ".");
+        throw DeadlyImportError("Failed to open file ", file, ".");
     }
 
     // Get the file-size and validate it, throwing an exception when fails
     size_t fileSize = pStream->FileSize();
     if (fileSize < 8) {
-        throw DeadlyImportError("M3D-file " + file + " is too small.");
+        throw DeadlyImportError("M3D-file ", file, " is too small.");
     }
     std::vector<unsigned char> buffer(fileSize);
     if (fileSize != pStream->Read(buffer.data(), 1, fileSize)) {
-        throw DeadlyImportError("Failed to read the file " + file + ".");
+        throw DeadlyImportError("Failed to read the file ", file, ".");
     }
     // extra check for binary format's first 8 bytes. Not done for the ASCII variant
     if (!memcmp(buffer.data(), "3DMO", 4) && memcmp(buffer.data() + 4, &fileSize, 4)) {
-        throw DeadlyImportError("Bad binary header in file " + file + ".");
+        throw DeadlyImportError("Bad binary header in file ", file, ".");
     }
 #ifdef M3D_ASCII
     // make sure there's a terminator zero character, as input must be ASCIIZ
@@ -200,7 +200,7 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys
     M3DWrapper m3d(pIOHandler, buffer);
 
     if (!m3d) {
-        throw DeadlyImportError("Unable to parse " + file + " as M3D.");
+        throw DeadlyImportError("Unable to parse ", file, " as M3D.");
     }
 
     // create the root node

+ 1 - 1
code/AssetLib/MD2/MD2Loader.cpp

@@ -222,7 +222,7 @@ void MD2Importer::InternReadFile( const std::string& pFile,
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open MD2 file " + pFile + "");
+        throw DeadlyImportError("Failed to open MD2 file ", pFile, "");
     }
 
     // check whether the md3 file is large enough to contain

+ 1 - 1
code/AssetLib/MD3/MD3Loader.cpp

@@ -715,7 +715,7 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open MD3 file " + pFile + ".");
+        throw DeadlyImportError("Failed to open MD3 file ", pFile, ".");
     }
 
     // Check whether the md3 file is large enough to contain the header

+ 1 - 1
code/AssetLib/MD5/MD5Loader.cpp

@@ -675,7 +675,7 @@ void MD5Importer::LoadMD5CameraFile() {
 
     // Check whether we can read from the file
     if (!file.get() || !file->FileSize()) {
-        throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile);
+        throw DeadlyImportError("Failed to read MD5CAMERA file: ", pFile);
     }
     mHadMD5Camera = true;
     LoadFileIntoMemory(file.get());

+ 1 - 1
code/AssetLib/MDC/MDCLoader.cpp

@@ -219,7 +219,7 @@ void MDCImporter::InternReadFile(
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open MDC file " + pFile + ".");
+        throw DeadlyImportError("Failed to open MDC file ", pFile, ".");
     }
 
     // check whether the mdc file is large enough to contain the file header

+ 2 - 2
code/AssetLib/MDL/HalfLife/HL1MDLLoader.h

@@ -218,12 +218,12 @@ private:
 template <typename MDLFileHeader>
 void HL1MDLLoader::load_file_into_buffer(const std::string &file_path, unsigned char *&buffer) {
     if (!io_->Exists(file_path))
-        throw DeadlyImportError("Missing file " + DefaultIOSystem::fileName(file_path) + ".");
+        throw DeadlyImportError("Missing file ", DefaultIOSystem::fileName(file_path), ".");
 
     std::unique_ptr<IOStream> file(io_->Open(file_path));
 
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open MDL file " + DefaultIOSystem::fileName(file_path) + ".");
+        throw DeadlyImportError("Failed to open MDL file ", DefaultIOSystem::fileName(file_path), ".");
     }
 
     const size_t file_size = file->FileSize();

+ 3 - 3
code/AssetLib/MDL/MDLLoader.cpp

@@ -167,7 +167,7 @@ void MDLImporter::InternReadFile(const std::string &pFile,
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open MDL file " + pFile + ".");
+        throw DeadlyImportError("Failed to open MDL file ", pFile, ".");
     }
 
     // This should work for all other types of MDL files, too ...
@@ -251,8 +251,8 @@ void MDLImporter::InternReadFile(const std::string &pFile,
             }
         } else {
             // print the magic word to the log file
-            throw DeadlyImportError("Unknown MDL subformat " + pFile +
-                                    ". Magic word (" + std::string((char *)&iMagicWord, 4) + ") is not known");
+            throw DeadlyImportError("Unknown MDL subformat ", pFile,
+                                    ". Magic word (", std::string((char *)&iMagicWord, 4), ") is not known");
         }
 
         // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system

+ 2 - 2
code/AssetLib/MMD/MMDImporter.cpp

@@ -111,7 +111,7 @@ void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
     // Read file by istream
     std::filebuf fb;
     if (!fb.open(file, std::ios::in | std::ios::binary)) {
-        throw DeadlyImportError("Failed to open file " + file + ".");
+        throw DeadlyImportError("Failed to open file ", file, ".");
     }
 
     std::istream fileStream(&fb);
@@ -122,7 +122,7 @@ void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
     fileStream.seekg(0, fileStream.beg);
 
     if (fileSize < sizeof(pmx::PmxModel)) {
-        throw DeadlyImportError(file + " is too small.");
+        throw DeadlyImportError(file, " is too small.");
     }
 
     pmx::PmxModel model;

+ 1 - 1
code/AssetLib/MMD/MMDPmxParser.cpp

@@ -524,7 +524,7 @@ namespace pmx
 		if (version != 2.0f && version != 2.1f)
 		{
 			std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl;
-            throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but " + to_string(version));
+            throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but ", to_string(version));
     }
 		this->setting.Read(stream);
 

+ 1 - 1
code/AssetLib/MS3D/MS3DLoader.cpp

@@ -229,7 +229,7 @@ void MS3DImporter::InternReadFile( const std::string& pFile,
     stream.CopyAndAdvance(head,10);
     stream >> version;
     if (strncmp(head,"MS3D000000",10)) {
-        throw DeadlyImportError("Not a MS3D file, magic string MS3D000000 not found: "+pFile);
+        throw DeadlyImportError("Not a MS3D file, magic string MS3D000000 not found: ", pFile);
     }
 
     if (version != 4) {

+ 1 - 1
code/AssetLib/NFF/NFFLoader.cpp

@@ -214,7 +214,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
 
     // Check whether we can read from the file
     if (!file.get())
-        throw DeadlyImportError("Failed to open NFF file " + pFile + ".");
+        throw DeadlyImportError("Failed to open NFF file ", pFile, ".");
 
     // allocate storage and copy the contents of the file to a memory buffer
     // (terminate it with zero)

+ 1 - 1
code/AssetLib/OFF/OFFLoader.cpp

@@ -123,7 +123,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
 
     // Check whether we can read from the file
     if( file.get() == nullptr) {
-        throw DeadlyImportError( "Failed to open OFF file " + pFile + ".");
+        throw DeadlyImportError( "Failed to open OFF file ", pFile, ".");
     }
 
     // allocate storage and copy the contents of the file to a memory buffer

+ 1 - 1
code/AssetLib/Obj/ObjFileImporter.cpp

@@ -112,7 +112,7 @@ void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, I
     };
     std::unique_ptr<IOStream, decltype(streamCloser)> fileStream(pIOHandler->Open(file, mode), streamCloser);
     if (!fileStream.get()) {
-        throw DeadlyImportError("Failed to open file " + file + ".");
+        throw DeadlyImportError("Failed to open file ", file, ".");
     }
 
     // Get the file-size and validate it, throwing an exception when fails

+ 9 - 9
code/AssetLib/Ogre/OgreBinarySerializer.cpp

@@ -187,8 +187,8 @@ Mesh *OgreBinarySerializer::ImportMesh(MemoryStreamReader *stream) {
     /// @todo Check what we can actually support.
     std::string version = serializer.ReadLine();
     if (version != MESH_VERSION_1_8) {
-        throw DeadlyExportError(Formatter::format() << "Mesh version " << version << " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again."
-                                                    << " Supported versions: " << MESH_VERSION_1_8);
+        throw DeadlyExportError("Mesh version ", version, " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again.",
+                                                    " Supported versions: ", MESH_VERSION_1_8);
     }
 
     Mesh *mesh = new Mesh();
@@ -471,7 +471,7 @@ void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh) {
             uint16_t submeshIndex = Read<uint16_t>();
             SubMesh *submesh = mesh->GetSubMesh(submeshIndex);
             if (!submesh) {
-                throw DeadlyImportError(Formatter::format() << "Ogre Mesh does not include submesh " << submeshIndex << " referenced in M_SUBMESH_NAME_TABLE_ELEMENT. Invalid mesh file.");
+                throw DeadlyImportError("Ogre Mesh does not include submesh ", submeshIndex, " referenced in M_SUBMESH_NAME_TABLE_ELEMENT. Invalid mesh file.");
             }
 
             submesh->name = ReadLine();
@@ -788,7 +788,7 @@ MemoryStreamReaderPtr OgreBinarySerializer::OpenReader(Assimp::IOSystem *pIOHand
 
     IOStream *f = pIOHandler->Open(filename, "rb");
     if (!f) {
-        throw DeadlyImportError("Failed to open skeleton file " + filename);
+        throw DeadlyImportError("Failed to open skeleton file ", filename);
     }
 
     return MemoryStreamReaderPtr(new MemoryStreamReader(f));
@@ -803,8 +803,8 @@ void OgreBinarySerializer::ReadSkeleton(Skeleton *skeleton) {
     // This deserialization supports both versions of the skeleton spec
     std::string version = ReadLine();
     if (version != SKELETON_VERSION_1_8 && version != SKELETON_VERSION_1_1) {
-        throw DeadlyExportError(Formatter::format() << "Skeleton version " << version << " not supported by this importer."
-                                                    << " Supported versions: " << SKELETON_VERSION_1_8 << " and " << SKELETON_VERSION_1_1);
+        throw DeadlyExportError("Skeleton version ", version, " not supported by this importer.",
+                                                    " Supported versions: ", SKELETON_VERSION_1_8, " and ", SKELETON_VERSION_1_1);
     }
 
     ASSIMP_LOG_VERBOSE_DEBUG("Reading Skeleton");
@@ -871,7 +871,7 @@ void OgreBinarySerializer::ReadBone(Skeleton *skeleton) {
 
     // Bone indexes need to start from 0 and be contiguous
     if (bone->id != skeleton->bones.size()) {
-        throw DeadlyImportError(Formatter::format() << "Ogre Skeleton bone indexes not contiguous. Error at bone index " << bone->id);
+        throw DeadlyImportError("Ogre Skeleton bone indexes not contiguous. Error at bone index ", bone->id);
     }
 
     ASSIMP_LOG_VERBOSE_DEBUG_F("    ", bone->id, " ", bone->name);
@@ -889,7 +889,7 @@ void OgreBinarySerializer::ReadBoneParent(Skeleton *skeleton) {
     if (child && parent)
         parent->AddChild(child);
     else
-        throw DeadlyImportError(Formatter::format() << "Failed to find bones for parenting: Child id " << childId << " for parent id " << parentId);
+        throw DeadlyImportError("Failed to find bones for parenting: Child id ", childId, " for parent id ", parentId);
 }
 
 void OgreBinarySerializer::ReadSkeletonAnimation(Skeleton *skeleton) {
@@ -926,7 +926,7 @@ void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton * /*skeleton*/, A
     uint16_t boneId = Read<uint16_t>();
     Bone *bone = dest->parentSkeleton->BoneById(boneId);
     if (!bone) {
-        throw DeadlyImportError(Formatter::format() << "Cannot read animation track, target bone " << boneId << " not in target Skeleton");
+        throw DeadlyImportError("Cannot read animation track, target bone ", boneId, " not in target Skeleton");
     }
 
     VertexAnimationTrack track;

+ 1 - 1
code/AssetLib/Ogre/OgreImporter.cpp

@@ -100,7 +100,7 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
     // Open source file
     IOStream *f = pIOHandler->Open(pFile, "rb");
     if (!f) {
-        throw DeadlyImportError("Failed to open file " + pFile);
+        throw DeadlyImportError("Failed to open file ", pFile);
     }
 
     // Binary .mesh import

+ 5 - 5
code/AssetLib/Ogre/OgreStructs.cpp

@@ -476,7 +476,7 @@ void SubMesh::Reset(){
 
 aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent) {
     if (operationType != OT_TRIANGLE_LIST) {
-        throw DeadlyImportError(Formatter::format() << "Only mesh operation type OT_TRIANGLE_LIST is supported. Found " << operationType);
+        throw DeadlyImportError("Only mesh operation type OT_TRIANGLE_LIST is supported. Found ", operationType);
     }
 
     aiMesh *dest = new aiMesh();
@@ -944,7 +944,7 @@ void Bone::AddChild(Bone *bone) {
     if (!bone)
         return;
     if (bone->IsParented())
-        throw DeadlyImportError("Attaching child Bone that is already parented: " + bone->name);
+        throw DeadlyImportError("Attaching child Bone that is already parented: ", bone->name);
 
     bone->parent = this;
     bone->parentId = id;
@@ -963,7 +963,7 @@ void Bone::CalculateWorldMatrixAndDefaultPose(Skeleton *skeleton) {
     for (auto boneId : children) {
         Bone *child = skeleton->BoneById(boneId);
         if (!child) {
-            throw DeadlyImportError(Formatter::format() << "CalculateWorldMatrixAndDefaultPose: Failed to find child bone " << boneId << " for parent " << id << " " << name);
+            throw DeadlyImportError("CalculateWorldMatrixAndDefaultPose: Failed to find child bone ", boneId, " for parent ", id, " ", name);
         }
         child->CalculateWorldMatrixAndDefaultPose(skeleton);
     }
@@ -983,7 +983,7 @@ aiNode *Bone::ConvertToAssimpNode(Skeleton *skeleton, aiNode *parentNode) {
         for (size_t i = 0, len = children.size(); i < len; ++i) {
             Bone *child = skeleton->BoneById(children[i]);
             if (!child) {
-                throw DeadlyImportError(Formatter::format() << "ConvertToAssimpNode: Failed to find child bone " << children[i] << " for parent " << id << " " << name);
+                throw DeadlyImportError("ConvertToAssimpNode: Failed to find child bone ", children[i], " for parent ", id, " ", name);
             }
             node->mChildren[i] = child->ConvertToAssimpNode(skeleton, node);
         }
@@ -1022,7 +1022,7 @@ aiNodeAnim *VertexAnimationTrack::ConvertToAssimpAnimationNode(Skeleton *skeleto
 
     Bone *bone = skeleton->BoneByName(boneName);
     if (!bone) {
-        throw DeadlyImportError("VertexAnimationTrack::ConvertToAssimpAnimationNode: Failed to find bone " + boneName + " from parent Skeleton");
+        throw DeadlyImportError("VertexAnimationTrack::ConvertToAssimpAnimationNode: Failed to find bone ", boneName, " from parent Skeleton");
     }
 
     // Keyframes

+ 18 - 18
code/AssetLib/Ogre/OgreXmlSerializer.cpp

@@ -59,9 +59,9 @@ namespace Ogre {
 AI_WONT_RETURN void ThrowAttibuteError(const XmlReader *reader, const std::string &name, const std::string &error = "") AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void ThrowAttibuteError(const XmlReader *reader, const std::string &name, const std::string &error) {
     if (!error.empty()) {
-        throw DeadlyImportError(error + " in node '" + std::string(reader->getNodeName()) + "' and attribute '" + name + "'");
+        throw DeadlyImportError(error, " in node '", reader->getNodeName(), "' and attribute '", name, "'");
     } else {
-        throw DeadlyImportError("Attribute '" + name + "' does not exist in node '" + std::string(reader->getNodeName()) + "'");
+        throw DeadlyImportError("Attribute '", name, "' does not exist in node '", reader->getNodeName(), "'");
     }
 }
 
@@ -265,7 +265,7 @@ MeshXml *OgreXmlSerializer::ImportMesh(XmlReader *reader) {
 
 void OgreXmlSerializer::ReadMesh(MeshXml *mesh) {
     if (NextNode() != nnMesh) {
-        throw DeadlyImportError("Root node is <" + m_currentNodeName + "> expecting <mesh>");
+        throw DeadlyImportError("Root node is <", m_currentNodeName, "> expecting <mesh>");
     }
 
     ASSIMP_LOG_VERBOSE_DEBUG("Reading Mesh");
@@ -430,18 +430,18 @@ void OgreXmlSerializer::ReadGeometryVertexBuffer(VertexDataXml *dest) {
 
     // Sanity checks
     if (dest->positions.size() != dest->count) {
-        throw DeadlyImportError(Formatter::format() << "Read only " << dest->positions.size() << " positions when should have read " << dest->count);
+        throw DeadlyImportError("Read only ", dest->positions.size(), " positions when should have read ", dest->count);
     }
     if (normals && dest->normals.size() != dest->count) {
-        throw DeadlyImportError(Formatter::format() << "Read only " << dest->normals.size() << " normals when should have read " << dest->count);
+        throw DeadlyImportError("Read only ", dest->normals.size(), " normals when should have read ", dest->count);
     }
     if (tangents && dest->tangents.size() != dest->count) {
-        throw DeadlyImportError(Formatter::format() << "Read only " << dest->tangents.size() << " tangents when should have read " << dest->count);
+        throw DeadlyImportError("Read only ", dest->tangents.size(), " tangents when should have read ", dest->count);
     }
     for (unsigned int i = 0; i < dest->uvs.size(); ++i) {
         if (dest->uvs[i].size() != dest->count) {
-            throw DeadlyImportError(Formatter::format() << "Read only " << dest->uvs[i].size()
-                                                        << " uvs for uv index " << i << " when should have read " << dest->count);
+            throw DeadlyImportError("Read only ", dest->uvs[i].size(),
+                                                        " uvs for uv index ", i, " when should have read ", dest->count);
         }
     }
 }
@@ -507,7 +507,7 @@ void OgreXmlSerializer::ReadSubMesh(MeshXml *mesh) {
             if (submesh->indexData->faces.size() == submesh->indexData->faceCount) {
                 ASSIMP_LOG_VERBOSE_DEBUG_F("  - Faces ", submesh->indexData->faceCount);
             } else {
-                throw DeadlyImportError(Formatter::format() << "Read only " << submesh->indexData->faces.size() << " faces when should have read " << submesh->indexData->faceCount);
+                throw DeadlyImportError("Read only ", submesh->indexData->faces.size(), " faces when should have read ", submesh->indexData->faceCount);
             }
         } else if (m_currentNodeName == nnGeometry) {
             if (submesh->usesSharedVertexData) {
@@ -632,20 +632,20 @@ XmlReaderPtr OgreXmlSerializer::OpenReader(Assimp::IOSystem *pIOHandler, const s
 
     std::unique_ptr<IOStream> file(pIOHandler->Open(filename));
     if (!file.get()) {
-        throw DeadlyImportError("Failed to open skeleton file " + filename);
+        throw DeadlyImportError("Failed to open skeleton file ", filename);
     }
 
     std::unique_ptr<CIrrXML_IOStreamReader> stream(new CIrrXML_IOStreamReader(file.get()));
     XmlReaderPtr reader = XmlReaderPtr(irr::io::createIrrXMLReader(stream.get()));
     if (!reader.get()) {
-        throw DeadlyImportError("Failed to create XML reader for skeleton file " + filename);
+        throw DeadlyImportError("Failed to create XML reader for skeleton file ", filename);
     }
     return reader;
 }
 
 void OgreXmlSerializer::ReadSkeleton(Skeleton *skeleton) {
     if (NextNode() != nnSkeleton) {
-        throw DeadlyImportError("Root node is <" + m_currentNodeName + "> expecting <skeleton>");
+        throw DeadlyImportError("Root node is <", m_currentNodeName, "> expecting <skeleton>");
     }
 
     ASSIMP_LOG_VERBOSE_DEBUG("Reading Skeleton");
@@ -687,7 +687,7 @@ void OgreXmlSerializer::ReadAnimations(Skeleton *skeleton) {
         anim->length = ReadAttribute<float>("length");
 
         if (NextNode() != nnTracks) {
-            throw DeadlyImportError(Formatter::format() << "No <tracks> found in <animation> " << anim->name);
+            throw DeadlyImportError("No <tracks> found in <animation> ", anim->name);
         }
 
         ReadAnimationTracks(anim);
@@ -705,7 +705,7 @@ void OgreXmlSerializer::ReadAnimationTracks(Animation *dest) {
         track.boneName = ReadAttribute<std::string>("bone");
 
         if (NextNode() != nnKeyFrames) {
-            throw DeadlyImportError(Formatter::format() << "No <keyframes> found in <track> " << dest->name);
+            throw DeadlyImportError("No <keyframes> found in <track> ", dest->name);
         }
 
         ReadAnimationKeyFrames(dest, &track);
@@ -732,7 +732,7 @@ void OgreXmlSerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimationT
                 float angle = ReadAttribute<float>("angle");
 
                 if (NextNode() != nnAxis) {
-                    throw DeadlyImportError("No axis specified for keyframe rotation in animation " + anim->name);
+                    throw DeadlyImportError("No axis specified for keyframe rotation in animation ", anim->name);
                 }
 
                 aiVector3D axis;
@@ -774,7 +774,7 @@ void OgreXmlSerializer::ReadBoneHierarchy(Skeleton *skeleton) {
         if (bone && parent)
             parent->AddChild(bone);
         else
-            throw DeadlyImportError("Failed to find bones for parenting: Child " + name + " for parent " + parentName);
+            throw DeadlyImportError("Failed to find bones for parenting: Child ", name, " for parent ", parentName);
     }
 
     // Calculate bone matrices for root bones. Recursively calculates their children.
@@ -813,7 +813,7 @@ void OgreXmlSerializer::ReadBones(Skeleton *skeleton) {
                 float angle = ReadAttribute<float>("angle");
 
                 if (NextNode() != nnAxis) {
-                    throw DeadlyImportError(Formatter::format() << "No axis specified for bone rotation in bone " << bone->id);
+                    throw DeadlyImportError("No axis specified for bone rotation in bone ", bone->id);
                 }
 
                 aiVector3D axis;
@@ -854,7 +854,7 @@ void OgreXmlSerializer::ReadBones(Skeleton *skeleton) {
         ASSIMP_LOG_VERBOSE_DEBUG_F("    ", b->id, " ", b->name);
 
         if (b->id != static_cast<uint16_t>(i)) {
-            throw DeadlyImportError(Formatter::format() << "Bone ids are not in sequence starting from 0. Missing index " << i);
+            throw DeadlyImportError("Bone ids are not in sequence starting from 0. Missing index ", i);
         }
     }
 }

+ 1 - 1
code/AssetLib/OpenGEX/OpenGEXImporter.cpp

@@ -302,7 +302,7 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce
     // open source file
     IOStream *file = pIOHandler->Open( filename, "rb" );
     if( !file ) {
-        throw DeadlyImportError( "Failed to open file " + filename );
+        throw DeadlyImportError( "Failed to open file ", filename );
     }
 
     std::vector<char> buffer;

+ 2 - 2
code/AssetLib/Ply/PlyLoader.cpp

@@ -151,13 +151,13 @@ void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
     const std::string mode = "rb";
     std::unique_ptr<IOStream> fileStream(pIOHandler->Open(pFile, mode));
     if (!fileStream.get()) {
-        throw DeadlyImportError("Failed to open file " + pFile + ".");
+        throw DeadlyImportError("Failed to open file ", pFile, ".");
     }
 
     // Get the file-size
     const size_t fileSize(fileStream->FileSize());
     if (0 == fileSize) {
-        throw DeadlyImportError("File " + pFile + " is empty.");
+        throw DeadlyImportError("File ", pFile, " is empty.");
     }
 
     IOStreamBuffer<char> streamedBuffer(1024 * 1024);

+ 1 - 1
code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp

@@ -180,7 +180,7 @@ const aiImporterDesc *Q3BSPFileImporter::GetInfo() const {
 void Q3BSPFileImporter::InternReadFile(const std::string &rFile, aiScene *scene, IOSystem *ioHandler) {
     ZipArchiveIOSystem Archive(ioHandler, rFile);
     if (!Archive.isOpen()) {
-        throw DeadlyImportError("Failed to open file " + rFile + ".");
+        throw DeadlyImportError("Failed to open file ", rFile, ".");
     }
 
     std::string archiveName(""), mapName("");

+ 2 - 3
code/AssetLib/Q3D/Q3DLoader.cpp

@@ -110,13 +110,12 @@ void Q3DImporter::InternReadFile(const std::string &pFile,
 
     // The header is 22 bytes large
     if (stream.GetRemainingSize() < 22)
-        throw DeadlyImportError("File is either empty or corrupt: " + pFile);
+        throw DeadlyImportError("File is either empty or corrupt: ", pFile);
 
     // Check the file's signature
     if (ASSIMP_strincmp((const char *)stream.GetPtr(), "quick3Do", 8) &&
             ASSIMP_strincmp((const char *)stream.GetPtr(), "quick3Ds", 8)) {
-        throw DeadlyImportError("Not a Quick3D file. Signature string is: " +
-                                std::string((const char *)stream.GetPtr(), 8));
+        throw DeadlyImportError("Not a Quick3D file. Signature string is: ", std::string((const char *)stream.GetPtr(), 8));
     }
 
     // Print the file format version

+ 1 - 1
code/AssetLib/Raw/RawLoader.cpp

@@ -101,7 +101,7 @@ void RAWImporter::InternReadFile(const std::string &pFile,
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open RAW file " + pFile + ".");
+        throw DeadlyImportError("Failed to open RAW file ", pFile, ".");
     }
 
     // allocate storage and copy the contents of the file to a memory buffer

+ 1 - 1
code/AssetLib/SIB/SIBImporter.cpp

@@ -808,7 +808,7 @@ void SIBImporter::InternReadFile(const std::string &pFile,
 
     // We should have at least one chunk
     if (stream.GetRemainingSize() < 16)
-        throw DeadlyImportError("SIB file is either empty or corrupt: " + pFile);
+        throw DeadlyImportError("SIB file is either empty or corrupt: ", pFile);
 
     SIB sib;
 

+ 1 - 1
code/AssetLib/SMD/SMDLoader.cpp

@@ -695,7 +695,7 @@ void SMDImporter::ReadSmd(const std::string &pFile, IOSystem* pIOHandler) {
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open SMD/VTA file " + pFile + ".");
+        throw DeadlyImportError("Failed to open SMD/VTA file ", pFile, ".");
     }
 
     iFileSize = (unsigned int)file->FileSize();

+ 2 - 2
code/AssetLib/STL/STLLoader.cpp

@@ -181,7 +181,7 @@ void STLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
     // Check whether we can read from the file
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open STL file " + pFile + ".");
+        throw DeadlyImportError("Failed to open STL file ", pFile, ".");
     }
 
     mFileSize = (unsigned int)file->FileSize();
@@ -207,7 +207,7 @@ void STLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
     } else if (IsAsciiSTL(mBuffer, mFileSize)) {
         LoadASCIIFile(mScene->mRootNode);
     } else {
-        throw DeadlyImportError("Failed to determine STL storage representation for " + pFile + ".");
+        throw DeadlyImportError("Failed to determine STL storage representation for ", pFile, ".");
     }
 
     // create a single default material, using a white diffuse color for consistency with

+ 1 - 1
code/AssetLib/Terragen/TerragenLoader.cpp

@@ -121,7 +121,7 @@ void TerragenImporter::InternReadFile(const std::string &pFile,
 
     // Check whether we can read from the file
     if (file == nullptr)
-        throw DeadlyImportError("Failed to open TERRAGEN TERRAIN file " + pFile + ".");
+        throw DeadlyImportError("Failed to open TERRAGEN TERRAIN file ", pFile, ".");
 
     // Construct a stream reader to read all data in the correct endianness
     StreamReaderLE reader(file);

+ 1 - 1
code/AssetLib/X/XFileImporter.cpp

@@ -114,7 +114,7 @@ void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, I
     // read file into memory
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
     if ( file.get() == nullptr ) {
-        throw DeadlyImportError( "Failed to open file " + pFile + "." );
+        throw DeadlyImportError( "Failed to open file ", pFile, "." );
     }
 
     static const size_t MinSize = 16;

+ 14 - 13
code/AssetLib/X/XFileParser.cpp

@@ -82,6 +82,17 @@ static void dummy_free(void * /*opaque*/, void *address) {
 
 #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
 
+// ------------------------------------------------------------------------------------------------
+// Throws an exception with a line number and the given text.
+template<typename... T>
+AI_WONT_RETURN void XFileParser::ThrowException(T&&... args) {
+    if (mIsBinaryFormat) {
+        throw DeadlyImportError(args...);
+    } else {
+        throw DeadlyImportError("Line ", mLineNumber, ": ", args...);
+    }
+}
+
 // ------------------------------------------------------------------------------------------------
 // Constructor. Creates a data structure out of the XFile given in the memory block.
 XFileParser::XFileParser(const std::vector<char> &pBuffer) :
@@ -122,13 +133,13 @@ XFileParser::XFileParser(const std::vector<char> &pBuffer) :
         mIsBinaryFormat = true;
         compressed = true;
     } else
-        ThrowException(format() << "Unsupported xfile format '" << mP[8] << mP[9] << mP[10] << mP[11] << "'");
+        ThrowException("Unsupported xfile format '", mP[8], mP[9], mP[10], mP[11], "'");
 
     // float size
     mBinaryFloatSize = (unsigned int)(mP[12] - 48) * 1000 + (unsigned int)(mP[13] - 48) * 100 + (unsigned int)(mP[14] - 48) * 10 + (unsigned int)(mP[15] - 48);
 
     if (mBinaryFloatSize != 32 && mBinaryFloatSize != 64)
-        ThrowException(format() << "Unknown float size " << mBinaryFloatSize << " specified in xfile header.");
+        ThrowException("Unknown float size ", mBinaryFloatSize, " specified in xfile header.");
 
     // The x format specifies size in bits, but we work in bytes
     mBinaryFloatSize /= 8;
@@ -864,7 +875,7 @@ void XFileParser::ParseDataObjectAnimationKey(AnimBone *pAnimBone) {
         }
 
         default:
-            ThrowException(format() << "Unknown key type " << keyType << " in animation.");
+            ThrowException("Unknown key type ", keyType, " in animation.");
             break;
         } // end switch
 
@@ -1355,16 +1366,6 @@ aiColor3D XFileParser::ReadRGB() {
     return color;
 }
 
-// ------------------------------------------------------------------------------------------------
-// Throws an exception with a line number and the given text.
-AI_WONT_RETURN void XFileParser::ThrowException(const std::string &pText) {
-    if (mIsBinaryFormat) {
-        throw DeadlyImportError(pText);
-    } else {
-        throw DeadlyImportError(format() << "Line " << mLineNumber << ": " << pText);
-    }
-}
-
 // ------------------------------------------------------------------------------------------------
 // Filters the imported hierarchy for some degenerated cases that some exporters produce.
 void XFileParser::FilterHierarchy(XFile::Node *pNode) {

+ 2 - 1
code/AssetLib/X/XFileParser.h

@@ -133,7 +133,8 @@ protected:
     aiColor4D ReadRGBA();
 
     /** Throws an exception with a line number and the given text. */
-    AI_WONT_RETURN void ThrowException( const std::string& pText) AI_WONT_RETURN_SUFFIX;
+    template<typename... T>
+    AI_WONT_RETURN void ThrowException(T&&... args) AI_WONT_RETURN_SUFFIX;
 
     /**
       * @brief  Filters the imported hierarchy for some degenerated cases that some exporters produce.

+ 7 - 7
code/AssetLib/X3D/FIReader.cpp

@@ -997,18 +997,18 @@ private:
         if (index < 32) {
             FIDecoder *decoder = defaultDecoder[index];
             if (!decoder) {
-                throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
+                throw DeadlyImportError("Invalid encoding algorithm index ", to_string(index));
             }
             return decoder->decode(dataP, len);
         }
         else {
             if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) {
-                throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
+                throw DeadlyImportError("Invalid encoding algorithm index ", to_string(index));
             }
             std::string uri = vocabulary.encodingAlgorithmTable[index - 32];
             auto it = decoderMap.find(uri);
             if (it == decoderMap.end()) {
-                throw DeadlyImportError("Unsupported encoding algorithm " + uri);
+                throw DeadlyImportError("Unsupported encoding algorithm ", uri);
             }
             else {
                 return it->second->decode(dataP, len);
@@ -1027,12 +1027,12 @@ private:
                 alphabet = "0123456789-:TZ ";
                 break;
             default:
-                throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
+                throw DeadlyImportError("Invalid restricted alphabet index ", to_string(index));
             }
         }
         else {
             if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) {
-                throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
+                throw DeadlyImportError("Invalid restricted alphabet index ", to_string(index));
             }
             alphabet = vocabulary.restrictedAlphabetTable[index - 16];
         }
@@ -1040,7 +1040,7 @@ private:
         utf8::utf8to32(alphabet.begin(), alphabet.end(), back_inserter(alphabetUTF32));
         std::string::size_type alphabetLength = alphabetUTF32.size();
         if (alphabetLength < 2) {
-            throw DeadlyImportError("Invalid restricted alphabet length " + to_string(alphabetLength));
+            throw DeadlyImportError("Invalid restricted alphabet length ", to_string(alphabetLength));
         }
         std::string::size_type bitsPerCharacter = 1;
         while ((1ull << bitsPerCharacter) <= alphabetLength) {
@@ -1442,7 +1442,7 @@ private:
                 std::string uri = parseNonEmptyOctetString2();
                 auto it = vocabularyMap.find(uri);
                 if (it == vocabularyMap.end()) {
-                    throw DeadlyImportError("Unknown vocabulary " + uri);
+                    throw DeadlyImportError("Unknown vocabulary ", uri);
                 }
                 const FIVocabulary *externalVocabulary = it->second;
                 if (externalVocabulary->restrictedAlphabetTable) {

+ 30 - 30
code/AssetLib/X3D/X3DImporter.cpp

@@ -233,48 +233,48 @@ bool X3DImporter::FindNodeElement(const std::string& pID, const CX3DImporter_Nod
 
 void X3DImporter::Throw_ArgOutOfRange(const std::string& pArgument)
 {
-	throw DeadlyImportError("Argument value is out of range for: \"" + pArgument + "\".");
+	throw DeadlyImportError("Argument value is out of range for: \"", pArgument, "\".");
 }
 
 void X3DImporter::Throw_CloseNotFound(const std::string& pNode)
 {
-	throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
+	throw DeadlyImportError("Close tag for node <", pNode, "> not found. Seems file is corrupt.");
 }
 
 void X3DImporter::Throw_ConvertFail_Str2ArrF(const std::string& pAttrValue)
 {
-	throw DeadlyImportError("In <" + std::string(mReader->getNodeName()) + "> failed to convert attribute value \"" + pAttrValue +
+	throw DeadlyImportError("In <", mReader->getNodeName(), "> failed to convert attribute value \"", pAttrValue,
 							"\" from string to array of floats.");
 }
 
 void X3DImporter::Throw_DEF_And_USE()
 {
-	throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <" + std::string(mReader->getNodeName()) + ">.");
+	throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <", mReader->getNodeName(), ">.");
 }
 
 void X3DImporter::Throw_IncorrectAttr(const std::string& pAttrName)
 {
-	throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
+	throw DeadlyImportError("Node <", mReader->getNodeName(), "> has incorrect attribute \"", pAttrName, "\".");
 }
 
 void X3DImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
 {
-	throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
+	throw DeadlyImportError("Attribute \"", pAttrName, "\" in node <", mReader->getNodeName(), "> has incorrect value.");
 }
 
 void X3DImporter::Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription)
 {
-	throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
+	throw DeadlyImportError("\"", pNodeType, "\" node can be used only once in ", mReader->getNodeName(), ". Description: ", pDescription);
 }
 
 void X3DImporter::Throw_TagCountIncorrect(const std::string& pNode)
 {
-	throw DeadlyImportError("Count of open and close tags for node <" + pNode + "> are not equivalent. Seems file is corrupt.");
+	throw DeadlyImportError("Count of open and close tags for node <", pNode, "> are not equivalent. Seems file is corrupt.");
 }
 
 void X3DImporter::Throw_USE_NotFound(const std::string& pAttrValue)
 {
-	throw DeadlyImportError("Not found node with name \"" + pAttrValue + "\" in <" + std::string(mReader->getNodeName()) + ">.");
+	throw DeadlyImportError("Not found node with name \"", pAttrValue, "\" in <", mReader->getNodeName(), ">.");
 }
 
 /*********************************************************************************************************************************************/
@@ -283,7 +283,7 @@ void X3DImporter::Throw_USE_NotFound(const std::string& pAttrValue)
 
 void X3DImporter::XML_CheckNode_MustBeEmpty()
 {
-	if(!mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must be empty.");
+	if(!mReader->isEmptyElement()) throw DeadlyImportError("Node <", mReader->getNodeName(), "> must be empty.");
 }
 
 void X3DImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
@@ -395,7 +395,7 @@ void X3DImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeNa
 
 casu_cres:
 
-	if(!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
+	if(!found) throw DeadlyImportError("Unknown node \"", nn, "\" in ", pParentNodeName, ".");
 
 	if(close_found)
 		LogInfo("Skipping node \"" + nn + "\" in " + pParentNodeName + ".");
@@ -430,7 +430,7 @@ bool X3DImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
         else if(val == "true")
             return true;
         else
-            throw DeadlyImportError("Bool attribute value can contain \"false\" or \"true\" not the \"" + val + "\"");
+            throw DeadlyImportError("Bool attribute value can contain \"false\" or \"true\" not the \"", val, "\"");
     }
 }
 
@@ -971,8 +971,8 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D
 	{
 		if(pColors.size() < pMesh.mNumVertices)
 		{
-			throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + to_string(pColors.size()) + ") can not be less than Vertices count(" +
-									to_string(pMesh.mNumVertices) +  ").");
+			throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(", to_string(pColors.size()), ") can not be less than Vertices count(",
+									to_string(pMesh.mNumVertices), ").");
 		}
 
 		// copy colors to mesh
@@ -983,8 +983,8 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D
 	{
 		if(pColors.size() < pMesh.mNumFaces)
 		{
-			throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + to_string(pColors.size()) + ") can not be less than Faces count(" +
-									to_string(pMesh.mNumFaces) +  ").");
+			throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(", to_string(pColors.size()), ") can not be less than Faces count(",
+									to_string(pMesh.mNumFaces), ").");
 		}
 
 		// copy colors to mesh
@@ -1043,8 +1043,8 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t
 			// check indices array count.
 			if(pColorIdx.size() < pCoordIdx.size())
 			{
-				throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + to_string(pColorIdx.size()) +
-										") can not be less than Coords inidces count(" + to_string(pCoordIdx.size()) +  ").");
+				throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(", to_string(pColorIdx.size()),
+										") can not be less than Coords inidces count(", to_string(pCoordIdx.size()),  ").");
 			}
 			// create list with colors for every vertex.
 			col_tgt_arr.resize(pMesh.mNumVertices);
@@ -1072,8 +1072,8 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t
 			// check indices array count.
 			if(pColors.size() < pMesh.mNumVertices)
 			{
-				throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + to_string(pColors.size()) + ") can not be less than Vertices count(" +
-										to_string(pMesh.mNumVertices) +  ").");
+				throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(", to_string(pColors.size()), ") can not be less than Vertices count(",
+										to_string(pMesh.mNumVertices),  ").");
 			}
 			// create list with colors for every vertex.
 			col_tgt_arr.resize(pMesh.mNumVertices);
@@ -1090,8 +1090,8 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t
 			// check indices array count.
 			if(pColorIdx.size() < pMesh.mNumFaces)
 			{
-				throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + to_string(pColorIdx.size()) +
-										") can not be less than Faces count(" + to_string(pMesh.mNumFaces) +  ").");
+				throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(", to_string(pColorIdx.size()),
+										") can not be less than Faces count(", to_string(pMesh.mNumFaces),  ").");
 			}
 			// create list with colors for every vertex using faces indices.
 			col_tgt_arr.resize(pMesh.mNumFaces);
@@ -1110,8 +1110,8 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t
 			// check indices array count.
 			if(pColors.size() < pMesh.mNumFaces)
 			{
-				throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + to_string(pColors.size()) + ") can not be less than Faces count(" +
-										to_string(pMesh.mNumFaces) +  ").");
+				throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(", to_string(pColors.size()), ") can not be less than Faces count(",
+										to_string(pMesh.mNumFaces),  ").");
 			}
 			// create list with colors for every vertex using faces indices.
 			col_tgt_arr.resize(pMesh.mNumFaces);
@@ -1157,8 +1157,8 @@ void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::vector<int32_
 			for(size_t i = 0; (i < pMesh.mNumVertices) && (i < tind.size()); i++)
 			{
 				if(tind[i] >= norm_arr_copy.size())
-					throw DeadlyImportError("MeshGeometry_AddNormal. Normal index(" + to_string(tind[i]) +
-											") is out of range. Normals count: " + to_string(norm_arr_copy.size()) + ".");
+					throw DeadlyImportError("MeshGeometry_AddNormal. Normal index(", to_string(tind[i]),
+											") is out of range. Normals count: ", to_string(norm_arr_copy.size()), ".");
 
 				pMesh.mNormals[i] = norm_arr_copy[tind[i]];
 			}
@@ -1268,7 +1268,7 @@ void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::vector<int3
 	for(size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++)
 	{
 		if(pMesh.mFaces[fi].mNumIndices != faces.at(fi).mNumIndices)
-			throw DeadlyImportError("Number of indices in texture face and mesh face must be equal. Invalid face index: " + to_string(fi) + ".");
+			throw DeadlyImportError("Number of indices in texture face and mesh face must be equal. Invalid face index: ", to_string(fi), ".");
 
 		for(size_t ii = 0; ii < pMesh.mFaces[fi].mNumIndices; ii++)
 		{
@@ -1419,12 +1419,12 @@ void X3DImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
 	// Check whether we can read from the file
     if ( file.get() == nullptr )
     {
-        throw DeadlyImportError( "Failed to open X3D file " + pFile + "." );
+        throw DeadlyImportError( "Failed to open X3D file ", pFile, "." );
     }
 	mReader = FIReader::create(file.get());
     if ( !mReader )
     {
-        throw DeadlyImportError( "Failed to create XML reader for file" + pFile + "." );
+        throw DeadlyImportError( "Failed to create XML reader for file", pFile, "." );
     }
     mReader->registerVocabulary("urn:web3d:x3d:fi-vocabulary-3.2", &X3D_vocabulary_3_2);
     mReader->registerVocabulary("urn:web3d:x3d:fi-vocabulary-3.3", &X3D_vocabulary_3_3);
@@ -1519,7 +1519,7 @@ void X3DImporter::ParseNode_Scene()
     auto GroupCounter_Increase = [](size_t& pCounter, const char* pGroupName) -> void
     {
 	    pCounter++;
-	    if(pCounter == 0) throw DeadlyImportError("Group counter overflow. Too much groups with type: " + std::string(pGroupName) + ".");
+	    if(pCounter == 0) throw DeadlyImportError("Group counter overflow. Too much groups with type: ", pGroupName, ".");
 };
 
 auto GroupCounter_Decrease = [&](size_t& pCounter, const char* pGroupName) -> void

+ 11 - 11
code/AssetLib/X3D/X3DImporter_Postprocess.cpp

@@ -178,7 +178,7 @@ void X3DImporter::Postprocess_BuildLight(const CX3DImporter_NodeElement& pNodeEl
 
 			break;
 		default:
-			throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + to_string(pNodeElement.Type) + ".");
+			throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: ", to_string(pNodeElement.Type), ".");
 	}
 
 	pSceneLightList.push_back(new_light);
@@ -300,7 +300,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
 				MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
 			else
-				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + to_string((*ch_it)->Type) + ".");
+				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: ", to_string((*ch_it)->Type), ".");
 		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
@@ -337,7 +337,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
 				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
 			else
-				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + to_string((*ch_it)->Type) + ".");
+				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: ", to_string((*ch_it)->Type), ".");
 		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
@@ -368,7 +368,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 				{} // skip because already read when mesh created.
 			else
-				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + to_string((*ch_it)->Type) + ".");
+				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: ", to_string((*ch_it)->Type), ".");
 		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
@@ -458,7 +458,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 				{} // skip because already read when mesh created.
 			else
-				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + to_string((*ch_it)->Type) + ".");
+				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: ", to_string((*ch_it)->Type), ".");
 		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
@@ -488,7 +488,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 				{} // skip because already read when mesh created.
 			else
-				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + to_string((*ch_it)->Type) + ".");
+				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: ", to_string((*ch_it)->Type), ".");
 		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
@@ -525,7 +525,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
 				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
 			else
-				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + to_string((*ch_it)->Type) + ".");
+				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleFanSet: ", to_string((*ch_it)->Type), ".");
 		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
@@ -569,7 +569,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
 				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
 			else
-				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + to_string((*ch_it)->Type) + ".");
+				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: ", to_string((*ch_it)->Type), ".");
 		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
@@ -604,13 +604,13 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 			else if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
 				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
 			else
-				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + to_string((*ch_it)->Type) + ".");
+				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: ", to_string((*ch_it)->Type), ".");
 		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
 	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet)
 
-	throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + to_string(pNodeElement.Type) + ".");
+	throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: ", to_string(pNodeElement.Type), ".");
 }
 
 void X3DImporter::Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeElement, aiNode& pSceneNode, std::list<aiMesh*>& pSceneMeshList,
@@ -672,7 +672,7 @@ void X3DImporter::Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeEle
 		}
 		else if(!PostprocessHelper_ElementIsMetadata((*it)->Type))// skip metadata
 		{
-			throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + to_string((*it)->Type) + ".");
+			throw DeadlyImportError("Postprocess_BuildNode. Unknown type: ", to_string((*it)->Type), ".");
 		}
 	}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; it++)
 

+ 1 - 1
code/AssetLib/XGL/XGLLoader.cpp

@@ -144,7 +144,7 @@ void XGLImporter::InternReadFile(const std::string &pFile,
 
     // check whether we can read from the file
     if (stream.get() == nullptr) {
-        throw DeadlyImportError("Failed to open XGL/ZGL file " + pFile + "");
+        throw DeadlyImportError("Failed to open XGL/ZGL file ", pFile, "");
     }
 
     // see if its compressed, if so uncompress it

+ 29 - 29
code/AssetLib/glTF/glTFAsset.inl

@@ -235,15 +235,15 @@ Ref<T> LazyDict<T>::Get(const char *id) {
 
     // read it from the JSON object
     if (!mDict) {
-        throw DeadlyImportError("GLTF: Missing section \"" + std::string(mDictId) + "\"");
+        throw DeadlyImportError("GLTF: Missing section \"", mDictId, "\"");
     }
 
     Value::MemberIterator obj = mDict->FindMember(id);
     if (obj == mDict->MemberEnd()) {
-        throw DeadlyImportError("GLTF: Missing object with id \"" + std::string(id) + "\" in \"" + mDictId + "\"");
+        throw DeadlyImportError("GLTF: Missing object with id \"", id, "\" in \"", mDictId, "\"");
     }
     if (!obj->value.IsObject()) {
-        throw DeadlyImportError("GLTF: Object with id \"" + std::string(id) + "\" is not a JSON object");
+        throw DeadlyImportError("GLTF: Object with id \"", id, "\" is not a JSON object");
     }
 
     // create an instance of the given type
@@ -317,13 +317,13 @@ inline void Buffer::Read(Value &obj, Asset &r) {
             this->mData.reset(data, std::default_delete<uint8_t[]>());
 
             if (statedLength > 0 && this->byteLength != statedLength) {
-                throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
-                                        " bytes, but found " + to_string(dataURI.dataLength));
+                throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", to_string(statedLength),
+                                        " bytes, but found ", to_string(dataURI.dataLength));
             }
         } else { // assume raw data
             if (statedLength != dataURI.dataLength) {
-                throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
-                                        " bytes, but found " + to_string(dataURI.dataLength));
+                throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", to_string(statedLength),
+                                        " bytes, but found ", to_string(dataURI.dataLength));
             }
 
             this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>());
@@ -339,9 +339,9 @@ inline void Buffer::Read(Value &obj, Asset &r) {
                 delete file;
 
                 if (!ok)
-                    throw DeadlyImportError("GLTF: error while reading referenced file \"" + std::string(uri) + "\"");
+                    throw DeadlyImportError("GLTF: error while reading referenced file \"", uri, "\"");
             } else {
-                throw DeadlyImportError("GLTF: could not open referenced file \"" + std::string(uri) + "\"");
+                throw DeadlyImportError("GLTF: could not open referenced file \"", uri, "\"");
             }
         }
     }
@@ -373,7 +373,7 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod
         char val[val_size];
 
         ai_snprintf(val, val_size, AI_SIZEFMT, pOffset);
-        throw DeadlyImportError(std::string("GLTF: incorrect offset value (") + val + ") for marking encoded region.");
+        throw DeadlyImportError("GLTF: incorrect offset value (", val, ") for marking encoded region.");
     }
 
     // Check length
@@ -383,7 +383,7 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod
         char val[val_size];
 
         ai_snprintf(val, val_size, AI_SIZEFMT "/" AI_SIZEFMT, pOffset, pEncodedData_Length);
-        throw DeadlyImportError(std::string("GLTF: encoded region with offset/length (") + val + ") is out of range.");
+        throw DeadlyImportError("GLTF: encoded region with offset/length (", val, ") is out of range.");
     }
 
     // Add new region
@@ -403,7 +403,7 @@ inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) {
         }
     }
 
-    throw DeadlyImportError("GLTF: EncodedRegion with ID: \"" + pID + "\" not found.");
+    throw DeadlyImportError("GLTF: EncodedRegion with ID: \"", pID, "\" not found.");
 }
 
 inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) {
@@ -851,7 +851,7 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
 /************** Read data from JSON-document **************/
 #define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut)                                                  \
     if (!ReadMember(*comp_data, pFieldName, pOut)) {                                                       \
-        throw DeadlyImportError(std::string("GLTF: \"compressedData\" must has \"") + pFieldName + "\"."); \
+        throw DeadlyImportError("GLTF: \"compressedData\" must has \"", pFieldName, "\"."); \
     }
 
             const char *mode_str;
@@ -880,7 +880,7 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
             else if (strcmp(mode_str, "ascii") == 0)
                 ext_o3dgc->Binary = false;
             else
-                throw DeadlyImportError(std::string("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"") + mode_str + "\".");
+                throw DeadlyImportError("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"", mode_str, "\".");
 
             /************************ Decoding ************************/
             Decode_O3DGC(*ext_o3dgc, pAsset_Root);
@@ -888,7 +888,7 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
         } // if(it_memb->name.GetString() == "Open3DGC-compression")
         else
         {
-            throw DeadlyImportError(std::string("GLTF: Unknown mesh extension: \"") + it_memb->name.GetString() + "\".");
+            throw DeadlyImportError("GLTF: Unknown mesh extension: \"", it_memb->name.GetString(), "\".");
         }
     } // for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); json_extensions++)
 #endif
@@ -923,24 +923,24 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
     size_t size_coordindex = ifs.GetNCoordIndex() * 3; // See float attributes note.
 
     if (primitives[0].indices->count != size_coordindex)
-        throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (" + to_string(size_coordindex) +
-                                ") not equal to uncompressed (" + to_string(primitives[0].indices->count) + ").");
+        throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (", to_string(size_coordindex),
+                                ") not equal to uncompressed (", to_string(primitives[0].indices->count), ").");
 
     size_coordindex *= sizeof(IndicesType);
     // Coordinates
     size_t size_coord = ifs.GetNCoord(); // See float attributes note.
 
     if (primitives[0].attributes.position[0]->count != size_coord)
-        throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (" + to_string(size_coord) +
-                                ") not equal to uncompressed (" + to_string(primitives[0].attributes.position[0]->count) + ").");
+        throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (", to_string(size_coord),
+                                ") not equal to uncompressed (", to_string(primitives[0].attributes.position[0]->count), ").");
 
     size_coord *= 3 * sizeof(float);
     // Normals
     size_t size_normal = ifs.GetNNormal(); // See float attributes note.
 
     if (primitives[0].attributes.normal[0]->count != size_normal)
-        throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (" + to_string(size_normal) +
-                                ") not equal to uncompressed (" + to_string(primitives[0].attributes.normal[0]->count) + ").");
+        throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (", to_string(size_normal),
+                                ") not equal to uncompressed (", to_string(primitives[0].attributes.normal[0]->count), ").");
 
     size_normal *= 3 * sizeof(float);
     // Additional attributes.
@@ -961,8 +961,8 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
                 // Check situation when encoded data contain texture coordinates but primitive not.
                 if (idx_texcoord < primitives[0].attributes.texcoord.size()) {
                     if (primitives[0].attributes.texcoord[idx]->count != tval)
-                        throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (" + to_string(tval) +
-                                                ") not equal to uncompressed (" + to_string(primitives[0].attributes.texcoord[idx]->count) + ").");
+                        throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (", to_string(tval),
+                                                ") not equal to uncompressed (", to_string(primitives[0].attributes.texcoord[idx]->count), ").");
 
                     idx_texcoord++;
                 } else {
@@ -971,7 +971,7 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
 
                 break;
             default:
-                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
+                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: ", to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
         }
 
         tval *= ifs.GetFloatAttributeDim(static_cast<unsigned long>(idx)) * sizeof(o3dgc::Real); // After checking count of objects we can get size of array.
@@ -990,7 +990,7 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
                 break;
 
             default:
-                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
+                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: ", to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
         }
 
         tval *= ifs.GetIntAttributeDim(static_cast<unsigned long>(idx)) * sizeof(long); // See float attributes note.
@@ -1025,7 +1025,7 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
 
                 break;
             default:
-                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
+                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: ", to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
         }
     }
 
@@ -1039,7 +1039,7 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
 
             // ifs.SetIntAttribute(idx, (long* const)(decoded_data + get_buf_offset(primitives[0].attributes.joint)));
             default:
-                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
+                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: ", to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
         }
     }
 
@@ -1231,7 +1231,7 @@ inline void AssetMetadata::Read(Document &doc) {
     }
 
     if (version.empty() || version[0] != '1') {
-        throw DeadlyImportError("GLTF: Unsupported glTF version: " + version);
+        throw DeadlyImportError("GLTF: Unsupported glTF version: ", version);
     }
 }
 
@@ -1309,7 +1309,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) {
     if (doc.HasParseError()) {
         char buffer[32];
         ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset()));
-        throw DeadlyImportError(std::string("GLTF: JSON parse error, offset ") + buffer + ": " + GetParseError_En(doc.GetParseError()));
+        throw DeadlyImportError("GLTF: JSON parse error, offset ", buffer, ": ", GetParseError_En(doc.GetParseError()));
     }
 
     if (!doc.IsObject()) {

+ 1 - 1
code/AssetLib/glTF/glTFImporter.cpp

@@ -234,7 +234,7 @@ void glTFImporter::ImportMeshes(glTF::Asset &r) {
                     buf->EncodedRegion_SetCurrent(mesh.id);
                 } else
                 {
-                    throw DeadlyImportError("GLTF: Can not import mesh: unknown mesh extension (code: \"" + to_string(cur_ext->Type) +
+                    throw DeadlyImportError("GLTF: Can not import mesh: unknown mesh extension (code: \"", to_string(cur_ext->Type),
                                             "\"), only Open3DGC is supported.");
                 }
             }

+ 1 - 1
code/AssetLib/glTF2/glTF2Asset.h

@@ -202,7 +202,7 @@ inline unsigned int ComponentTypeSize(ComponentType t) {
     case ComponentType_UNSIGNED_BYTE:
         return 1;
     default:
-        throw DeadlyImportError("GLTF: Unsupported Component Type " + to_string(t));
+        throw DeadlyImportError("GLTF: Unsupported Component Type ", to_string(t));
     }
 }
 

+ 15 - 15
code/AssetLib/glTF2/glTF2Asset.inl

@@ -269,21 +269,21 @@ Ref<T> LazyDict<T>::Retrieve(unsigned int i) {
 
     // read it from the JSON object
     if (!mDict) {
-        throw DeadlyImportError("GLTF: Missing section \"" + std::string(mDictId) + "\"");
+        throw DeadlyImportError("GLTF: Missing section \"", mDictId, "\"");
     }
 
     if (!mDict->IsArray()) {
-        throw DeadlyImportError("GLTF: Field is not an array \"" + std::string(mDictId) + "\"");
+        throw DeadlyImportError("GLTF: Field is not an array \"", mDictId, "\"");
     }
 
     Value &obj = (*mDict)[i];
 
     if (!obj.IsObject()) {
-        throw DeadlyImportError("GLTF: Object at index \"" + to_string(i) + "\" is not a JSON object");
+        throw DeadlyImportError("GLTF: Object at index \"", to_string(i), "\" is not a JSON object");
     }
 
     if (mRecursiveReferenceCheck.find(i) != mRecursiveReferenceCheck.end()) {
-        throw DeadlyImportError("GLTF: Object at index \"" + to_string(i) + "\" has recursive reference to itself");
+        throw DeadlyImportError("GLTF: Object at index \"", to_string(i), "\" has recursive reference to itself");
     }
     mRecursiveReferenceCheck.insert(i);
 
@@ -381,13 +381,13 @@ inline void Buffer::Read(Value &obj, Asset &r) {
             this->mData.reset(data, std::default_delete<uint8_t[]>());
 
             if (statedLength > 0 && this->byteLength != statedLength) {
-                throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
-                                        " bytes, but found " + to_string(dataURI.dataLength));
+                throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", to_string(statedLength),
+                                        " bytes, but found ", to_string(dataURI.dataLength));
             }
         } else { // assume raw data
             if (statedLength != dataURI.dataLength) {
-                throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
-                                        " bytes, but found " + to_string(dataURI.dataLength));
+                throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", to_string(statedLength),
+                                        " bytes, but found ", to_string(dataURI.dataLength));
             }
 
             this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>());
@@ -403,9 +403,9 @@ inline void Buffer::Read(Value &obj, Asset &r) {
                 delete file;
 
                 if (!ok)
-                    throw DeadlyImportError("GLTF: error while reading referenced file \"" + std::string(uri) + "\"");
+                    throw DeadlyImportError("GLTF: error while reading referenced file \"", uri, "\"");
             } else {
-                throw DeadlyImportError("GLTF: could not open referenced file \"" + std::string(uri) + "\"");
+                throw DeadlyImportError("GLTF: could not open referenced file \"", uri, "\"");
             }
         }
     }
@@ -437,7 +437,7 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod
         char val[val_size];
 
         ai_snprintf(val, val_size, AI_SIZEFMT, pOffset);
-        throw DeadlyImportError(std::string("GLTF: incorrect offset value (") + val + ") for marking encoded region.");
+        throw DeadlyImportError("GLTF: incorrect offset value (", val, ") for marking encoded region.");
     }
 
     // Check length
@@ -447,7 +447,7 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod
         char val[val_size];
 
         ai_snprintf(val, val_size, AI_SIZEFMT "/" AI_SIZEFMT, pOffset, pEncodedData_Length);
-        throw DeadlyImportError(std::string("GLTF: encoded region with offset/length (") + val + ") is out of range.");
+        throw DeadlyImportError("GLTF: encoded region with offset/length (", val, ") is out of range.");
     }
 
     // Add new region
@@ -467,7 +467,7 @@ inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) {
         }
     }
 
-    throw DeadlyImportError("GLTF: EncodedRegion with ID: \"" + pID + "\" not found.");
+    throw DeadlyImportError("GLTF: EncodedRegion with ID: \"", pID, "\" not found.");
 }
 
 inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) {
@@ -1458,7 +1458,7 @@ inline void AssetMetadata::Read(Document &doc) {
     }
 
     if (version.empty() || version[0] != '2') {
-        throw DeadlyImportError("GLTF: Unsupported glTF version: " + version);
+        throw DeadlyImportError("GLTF: Unsupported glTF version: ", version);
     }
 }
 
@@ -1570,7 +1570,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) {
     if (doc.HasParseError()) {
         char buffer[32];
         ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset()));
-        throw DeadlyImportError(std::string("GLTF: JSON parse error, offset ") + buffer + ": " + GetParseError_En(doc.GetParseError()));
+        throw DeadlyImportError("GLTF: JSON parse error, offset ", buffer, ": ", GetParseError_En(doc.GetParseError()));
     }
 
     if (!doc.IsObject()) {

+ 1 - 1
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -668,7 +668,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 				}
 				if (actualNumFaces == 0)
 				{
-					throw DeadlyImportError(std::string("Mesh \"") + aim->mName.C_Str() + "\" has no faces");
+					throw DeadlyImportError("Mesh \"", aim->mName.C_Str(), "\" has no faces");
 				}
 				aim->mNumFaces = actualNumFaces;
 				ai_assert(CheckValidFacesIndices(faces, actualNumFaces, aim->mNumVertices));

+ 1 - 0
code/CMakeLists.txt

@@ -200,6 +200,7 @@ SET( Common_SRCS
   Common/simd.cpp
   Common/material.cpp
   Common/AssertHandler.cpp
+  Common/Exceptional.cpp
 )
 SOURCE_GROUP(Common FILES ${Common_SRCS})
 

+ 3 - 2
code/Common/BaseImporter.cpp

@@ -130,10 +130,11 @@ aiScene *BaseImporter::ReadFile(Importer *pImp, const std::string &pFile, IOSyst
         // passes scale into ScaleProcess
         UpdateImporterScale(pImp);
 
-    } catch (const std::exception &err) {
+    } catch( const std::exception &err ) {
         // extract error description
         m_ErrorText = err.what();
-        ASSIMP_LOG_ERROR(m_ErrorText);
+        ASSIMP_LOG_ERROR(err.what());
+        m_Exception = std::current_exception();
         return nullptr;
     }
 

+ 52 - 0
code/Common/Exceptional.cpp

@@ -0,0 +1,52 @@
+/*
+---------------------------------------------------------------------------
+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 Exceptional.cpp
+
+Implementations of the exception classes.
+
+*/
+
+#include <assimp/Exceptional.h>
+#include <assimp/TinyFormatter.h>
+
+DeadlyErrorBase::DeadlyErrorBase(Assimp::Formatter::format f) :
+        runtime_error(std::string(f)){}

+ 12 - 2
code/Common/Importer.cpp

@@ -387,6 +387,7 @@ void Importer::FreeScene( ) {
     pimpl->mScene = nullptr;
 
     pimpl->mErrorString = "";
+    pimpl->mException = std::exception_ptr();
     ASSIMP_END_EXCEPTION_REGION(void);
 }
 
@@ -399,6 +400,13 @@ const char* Importer::GetErrorString() const {
     return pimpl->mErrorString.c_str();
 }
 
+const std::exception_ptr& Importer::GetException() const {
+    ai_assert(nullptr != pimpl);
+    
+    // Must remain valid as long as ReadFile() or FreeFile() are not called
+    return pimpl->mException;
+}
+
 // ------------------------------------------------------------------------------------------------
 // Enable extra-verbose mode
 void Importer::SetExtraVerbose(bool bDo) {
@@ -426,6 +434,7 @@ aiScene* Importer::GetOrphanedScene() {
     pimpl->mScene = nullptr;
 
     pimpl->mErrorString = ""; // reset error string
+    pimpl->mException = std::exception_ptr();
     ASSIMP_END_EXCEPTION_REGION(aiScene*);
     
     return s;
@@ -502,7 +511,7 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
     ReadFile(fbuff,pFlags);
     SetIOHandler(io);
 
-    ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString);
+    ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString, pimpl->mException);
     return pimpl->mScene;
 }
 
@@ -709,6 +718,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) {
         // if failed, extract the error string
         else if( !pimpl->mScene) {
             pimpl->mErrorString = imp->GetErrorText();
+            pimpl->mException = imp->GetException();
         }
 
         // clear any data allocated by post-process steps
@@ -733,7 +743,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) {
 #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
 
     // either successful or failure - the pointer expresses it anyways
-    ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString);
+    ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString, pimpl->mException);
     
     return pimpl->mScene;
 }

+ 23 - 25
code/Common/Importer.h

@@ -4,7 +4,6 @@ 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,
@@ -97,9 +96,13 @@ public:
     /** The imported data, if ReadFile() was successful, nullptr otherwise. */
     aiScene* mScene;
 
-    /** The error description, if there was one. */
+    /** The error description, if there was one. In the case of an exception,
+     *  mException will carry the full details. */
     std::string mErrorString;
 
+    /** Any exception which occurred */
+    std::exception_ptr mException;
+
     /** List of integer properties */
     IntPropertyMap mIntProperties;
 
@@ -124,26 +127,26 @@ public:
 };
 
 inline
-ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT
-: mIOHandler( nullptr )
-, mIsDefaultHandler( false )
-, mProgressHandler( nullptr )
-, mIsDefaultProgressHandler( false )
-, mImporter()
-, mPostProcessingSteps()
-, mScene( nullptr )
-, mErrorString()
-, mIntProperties()
-, mFloatProperties()
-, mStringProperties()
-, mMatrixProperties()
-, bExtraVerbose( false )
-, mPPShared( nullptr ) {
+ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT :
+        mIOHandler( nullptr ),
+        mIsDefaultHandler( false ),
+        mProgressHandler( nullptr ),
+        mIsDefaultProgressHandler( false ),
+        mImporter(),
+        mPostProcessingSteps(),
+        mScene( nullptr ),
+        mErrorString(),
+        mException(),
+        mIntProperties(),
+        mFloatProperties(),
+        mStringProperties(),
+        mMatrixProperties(),
+        bExtraVerbose( false ),
+        mPPShared( nullptr ) {
     // empty
 }
 //! @endcond
 
-
 struct BatchData;
 
 // ---------------------------------------------------------------------------
@@ -154,17 +157,13 @@ struct BatchData;
  *  could, this has not yet been implemented at the moment).
  *
  *  @note The class may not be used by more than one thread*/
-class ASSIMP_API BatchLoader
-{
-    // friend of Importer
-
+class ASSIMP_API BatchLoader {
 public:
     //! @cond never
     // -------------------------------------------------------------------
     /** Wraps a full list of configuration properties for an importer.
      *  Properties can be set using SetGenericProperty */
-    struct PropertyMap
-    {
+    struct PropertyMap {
         ImporterPimpl::IntPropertyMap     ints;
         ImporterPimpl::FloatPropertyMap   floats;
         ImporterPimpl::StringPropertyMap  strings;
@@ -181,7 +180,6 @@ public:
     };
     //! @endcond
 
-public:
     // -------------------------------------------------------------------
     /** Construct a batch loader from a given IO system to be used
      *  to access external files 

+ 1 - 1
code/PostProcessing/ValidateDataStructure.cpp

@@ -85,7 +85,7 @@ AI_WONT_RETURN void ValidateDSProcess::ReportError(const char *msg, ...) {
 
     va_end(args);
 
-    throw DeadlyImportError("Validation failed: " + std::string(szBuffer, iLen));
+    throw DeadlyImportError("Validation failed: ", std::string(szBuffer, iLen));
 }
 // ------------------------------------------------------------------------------------------------
 void ValidateDSProcess::ReportWarning(const char *msg, ...) {

+ 1 - 1
doc/dox.h

@@ -1626,7 +1626,7 @@ void xxxxImporter::InternReadFile( const std::string& pFile,
 
 	// Check whether we can read from the file
 	if( file.get() == NULL) {
-		throw DeadlyImportError( "Failed to open xxxx file " + pFile + ".");
+		throw DeadlyImportError( "Failed to open xxxx file ", pFile, ".");
 	}
 
 	// Your task: fill pScene

+ 15 - 1
include/assimp/BaseImporter.h

@@ -143,6 +143,8 @@ public:
 
     // -------------------------------------------------------------------
     /** Returns the error description of the last error that occurred.
+     * If the error is due to a std::exception, this will return the message.
+     * Exceptions can also be accessed with GetException().
      * @return A description of the last error that occurred. An empty
      * string if there was no error.
      */
@@ -150,6 +152,16 @@ public:
         return m_ErrorText;
     }
 
+    // -------------------------------------------------------------------
+    /** Returns the exception of the last exception that occurred.
+     * Note: Exceptions are not the only source of error details, so GetErrorText
+     * should be consulted too.
+     * @return The last exception that occurred. 
+     */
+    const std::exception_ptr& GetException() const {
+        return m_Exception;
+    }
+
     // -------------------------------------------------------------------
     /** Called prior to ReadFile().
      * The function is a request to the importer to update its configuration
@@ -410,9 +422,11 @@ private:
     /* Pushes state into importer for the importer scale */
     virtual void UpdateImporterScale(Importer *pImp);
 
-    protected:
+protected:
     /// Error description in case there was one.
     std::string m_ErrorText;
+    /// The exception, in case there was one.
+    std::exception_ptr m_Exception;
     /// Currently set progress handler.
     ProgressHandler *m_progress;
 };

+ 31 - 21
include/assimp/Exceptional.h

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endif
 
 #include <assimp/DefaultIOStream.h>
+#include <assimp/TinyFormatter.h>
 #include <stdexcept>
 
 using std::runtime_error;
@@ -55,26 +56,33 @@ using std::runtime_error;
 #pragma warning(disable : 4275)
 #endif
 
+class ASSIMP_API DeadlyErrorBase : public runtime_error {
+protected:
+    DeadlyErrorBase(Assimp::Formatter::format f);
+    
+    template<typename... T, typename U>
+    DeadlyErrorBase(Assimp::Formatter::format f, U&& u, T&&... args) :
+            DeadlyErrorBase(std::move(f << std::forward<U>(u)), std::forward<T>(args)...) {}
+};
+
 // ---------------------------------------------------------------------------
 /** FOR IMPORTER PLUGINS ONLY: Simple exception class to be thrown if an
  *  unrecoverable error occurs while importing. Loading APIs return
  *  nullptr instead of a valid aiScene then.  */
-class DeadlyImportError : public runtime_error {
+class ASSIMP_API DeadlyImportError : public DeadlyErrorBase {
 public:
     /** Constructor with arguments */
-    explicit DeadlyImportError(const std::string &errorText) :
-            runtime_error(errorText) {
-        // empty
-    }
+    template<typename... T>
+    explicit DeadlyImportError(T&&... args) :
+            DeadlyErrorBase(Assimp::Formatter::format(), std::forward<T>(args)...) {}
 };
 
-class DeadlyExportError : public runtime_error {
+class ASSIMP_API DeadlyExportError : public DeadlyErrorBase {
 public:
     /** Constructor with arguments */
-    explicit DeadlyExportError(const std::string &errorText) :
-            runtime_error(errorText) {
-        // empty
-    }
+    template<typename... T>
+    explicit DeadlyExportError(T&&... args) :
+            DeadlyErrorBase(Assimp::Formatter::format(), std::forward<T>(args)...) {}
 };
 
 #ifdef _MSC_VER
@@ -123,17 +131,19 @@ struct ExceptionSwallower<void> {
     {                                   \
         try {
 
-#define ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(type, ASSIMP_END_EXCEPTION_REGION_errorString) \
-    }                                                                                                \
-    catch (const DeadlyImportError &e) {                                                             \
-        ASSIMP_END_EXCEPTION_REGION_errorString = e.what();                                          \
-        return ExceptionSwallower<type>()();                                                         \
-    }                                                                                                \
-    catch (...) {                                                                                    \
-        ASSIMP_END_EXCEPTION_REGION_errorString = "Unknown exception";                               \
-        return ExceptionSwallower<type>()();                                                         \
-    }                                                                                                \
-    }
+#define ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(type, ASSIMP_END_EXCEPTION_REGION_errorString, ASSIMP_END_EXCEPTION_REGION_exception)     \
+    }                                                                                                                                           \
+    catch (const DeadlyImportError &e) {                                                                                                        \
+        ASSIMP_END_EXCEPTION_REGION_errorString = e.what();                                                                                     \
+        ASSIMP_END_EXCEPTION_REGION_exception = std::current_exception();                                                                       \
+        return ExceptionSwallower<type>()();                                                                                                    \
+    }                                                                                                                                           \
+    catch (...) {                                                                                                                               \
+        ASSIMP_END_EXCEPTION_REGION_errorString = "Unknown exception";                                                                          \
+        ASSIMP_END_EXCEPTION_REGION_exception = std::current_exception();                                                                       \
+        return ExceptionSwallower<type>()();                                                                                                    \
+    }                                                                                                                                           \
+}
 
 #define ASSIMP_END_EXCEPTION_REGION(type)    \
     }                                        \

+ 9 - 0
include/assimp/Importer.hpp

@@ -495,6 +495,15 @@ public:
      * following methods is called: #ReadFile(), #FreeScene(). */
     const char *GetErrorString() const;
 
+    // -------------------------------------------------------------------
+    /** Returns an exception if one occurred during import.
+     *
+     * @return The last exception which occurred.
+     *
+     * @note The returned value remains valid until one of the
+     * following methods is called: #ReadFile(), #FreeScene(). */
+    const std::exception_ptr& GetException() const;
+
     // -------------------------------------------------------------------
     /** Returns the scene loaded by the last successful call to ReadFile()
      *

+ 3 - 2
include/assimp/LogAux.h

@@ -61,9 +61,10 @@ template<class TDeriving>
 class LogFunctions {
 public:
     // ------------------------------------------------------------------------------------------------
-    static void ThrowException(const std::string& msg)
+    template<typename... T>
+    static void ThrowException(T&&... args)
     {
-        throw DeadlyImportError(Prefix()+msg);
+        throw DeadlyImportError(Prefix(), args...);
     }
 
     // ------------------------------------------------------------------------------------------------

+ 3 - 0
include/assimp/TinyFormatter.h

@@ -88,6 +88,9 @@ public:
         underlying << sin;
     }
 
+    basic_formatter(basic_formatter&& other)
+        : underlying(std::move(other.underlying)) {
+    }
 
     // The problem described here:
     // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462

+ 1 - 1
include/assimp/irrXMLWrapper.h

@@ -64,7 +64,7 @@ namespace Assimp    {
  * // open the file
  * std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
  * if( file.get() == nullptr ) {
- *    throw DeadlyImportError( "Failed to open file " + pFile + ".");
+ *    throw DeadlyImportError( "Failed to open file ", pFile, ".");
  * }
  *
  * // generate a XML reader for it

+ 102 - 0
test/unit/utImporter.cpp

@@ -279,3 +279,105 @@ TEST_F(ImporterTest, SearchFileHeaderForTokenTest) {
     //DefaultIOSystem ioSystem;
     //    BaseImporter::SearchFileHeaderForToken( &ioSystem, assetPath, Token, 2 )
 }
+
+
+namespace
+{
+    // Description for an importer which fails in specific ways.
+    aiImporterDesc s_failingImporterDescription = {
+        "Failing importer",
+        "assimp team",
+        "",
+        "",
+        0,
+        1,
+        0,
+        1,
+        0,
+        "fail"
+    };
+
+    // This importer fails in specific ways.
+    class FailingImporter : public Assimp::BaseImporter {
+    public:
+        virtual ~FailingImporter() = default;
+        virtual bool CanRead( const std::string&, Assimp::IOSystem*, bool ) const override
+        {
+            return true;
+        }
+
+    protected:
+        virtual const aiImporterDesc* GetInfo() const override { return &s_failingImporterDescription; }
+
+        virtual void InternReadFile( const std::string& pFile, aiScene*, Assimp::IOSystem* ) override
+        {
+            if (pFile == "deadlyImportError.fail")
+            {
+                throw DeadlyImportError("Deadly import error test. Details: ", 42, " More Details: ", "Failure");
+            }
+            else if (pFile == "stdException.fail")
+            {
+                throw std::runtime_error("std::exception test");
+            }
+            else if (pFile == "unexpectedException.fail")
+            {
+                throw 5;
+            }
+        }
+    };
+}
+
+TEST_F(ImporterTest, deadlyImportError)
+{
+    pImp->RegisterLoader(new FailingImporter);
+    pImp->SetIOHandler(new TestIOSystem);
+    const aiScene* scene = pImp->ReadFile("deadlyImportError.fail", 0);
+    EXPECT_EQ(scene, nullptr);
+    EXPECT_STREQ(pImp->GetErrorString(), "Deadly import error test. Details: 42 More Details: Failure");
+    EXPECT_NE(pImp->GetException(), std::exception_ptr());
+}
+
+TEST_F(ImporterTest, stdException)
+{
+    pImp->RegisterLoader(new FailingImporter);
+    pImp->SetIOHandler(new TestIOSystem);
+    const aiScene* scene = pImp->ReadFile("stdException.fail", 0);
+    EXPECT_EQ(scene, nullptr);
+    EXPECT_STREQ(pImp->GetErrorString(), "std::exception test");
+    EXPECT_NE(pImp->GetException(), std::exception_ptr());
+    try
+    {
+        std::rethrow_exception(pImp->GetException());
+    }
+    catch(const std::exception& e)
+    {
+        EXPECT_STREQ(e.what(), "std::exception test");
+    }
+    catch(...)
+    {
+        EXPECT_TRUE(false);
+    }
+}
+
+TEST_F(ImporterTest, unexpectedException)
+{
+    pImp->RegisterLoader(new FailingImporter);
+    pImp->SetIOHandler(new TestIOSystem);
+    const aiScene* scene = pImp->ReadFile("unexpectedException.fail", 0);
+
+    EXPECT_EQ(scene, nullptr);
+    EXPECT_STREQ(pImp->GetErrorString(), "Unknown exception");
+    ASSERT_NE(pImp->GetException(), std::exception_ptr());
+    try
+    {
+        std::rethrow_exception(pImp->GetException());
+    }
+    catch(int x)
+    {
+        EXPECT_EQ(x, 5);
+    }
+    catch(...)
+    {
+        EXPECT_TRUE(false);
+    }
+}