فهرست منبع

Merge remote-tracking branch 'upstream/master' into fbxBlendshapes

Gordon Chapman 4 سال پیش
والد
کامیت
64da2a4315
47فایلهای تغییر یافته به همراه396 افزوده شده و 429 حذف شده
  1. 0 1
      .github/FUNDING.yml
  2. 3 0
      .gitignore
  3. 1 1
      CMakeLists.txt
  4. 2 35
      INSTALL
  5. 1 1
      LICENSE
  6. 2 8
      Readme.md
  7. 1 1
      code/AssetLib/3MF/D3MFOpcPackage.cpp
  8. 10 16
      code/AssetLib/AMF/AMFImporter.cpp
  9. 29 34
      code/AssetLib/AMF/AMFImporter_Geometry.cpp
  10. 0 164
      code/AssetLib/AMF/AMFImporter_Macro.hpp
  11. 69 72
      code/AssetLib/AMF/AMFImporter_Material.cpp
  12. 9 7
      code/AssetLib/AMF/AMFImporter_Postprocess.cpp
  13. 6 6
      code/AssetLib/FBX/FBXCompileConfig.h
  14. 6 0
      code/AssetLib/FBX/FBXMeshGeometry.cpp
  15. 4 1
      code/AssetLib/HMP/HMPLoader.cpp
  16. 3 3
      code/AssetLib/MDC/MDCLoader.cpp
  17. 5 5
      code/AssetLib/MS3D/MS3DLoader.cpp
  18. 4 4
      code/AssetLib/Ogre/OgreBinarySerializer.cpp
  19. 2 2
      code/AssetLib/Ogre/OgreMaterial.cpp
  20. 1 1
      code/AssetLib/Ogre/OgreParsingUtils.h
  21. 2 2
      code/AssetLib/Ogre/OgreXmlSerializer.cpp
  22. 1 1
      code/AssetLib/STEPParser/STEPFileReader.cpp
  23. 5 5
      code/AssetLib/Step/STEPFile.h
  24. 5 5
      code/AssetLib/Step/StepExporter.cpp
  25. 7 3
      code/AssetLib/glTF/glTFAsset.h
  26. 3 3
      code/AssetLib/glTF/glTFCommon.h
  27. 2 0
      code/AssetLib/glTF/glTFImporter.cpp
  28. 14 4
      code/AssetLib/glTF2/glTF2Asset.h
  29. 18 0
      code/AssetLib/glTF2/glTF2AssetWriter.inl
  30. 4 0
      code/AssetLib/glTF2/glTF2Exporter.cpp
  31. 36 22
      code/AssetLib/glTF2/glTF2Importer.cpp
  32. 0 1
      code/CMakeLists.txt
  33. 1 1
      code/Common/BaseImporter.cpp
  34. 1 1
      code/Common/Importer.cpp
  35. 1 1
      code/Common/Version.cpp
  36. 1 1
      code/PostProcessing/EmbedTexturesProcess.cpp
  37. 100 3
      code/PostProcessing/TriangulateProcess.cpp
  38. 2 2
      include/assimp/MathFunctions.h
  39. 1 1
      include/assimp/StringUtils.h
  40. 6 1
      include/assimp/XmlParser.h
  41. 18 0
      include/assimp/mesh.h
  42. 3 3
      port/iOS/build.sh
  43. 1 1
      test/unit/AssimpAPITest_aiMatrix4x4.cpp
  44. 3 3
      test/unit/AssimpAPITest_aiQuaternion.cpp
  45. 1 1
      test/unit/MathTest.cpp
  46. 1 1
      test/unit/utVersion.cpp
  47. 1 1
      tools/assimp_view/LogWindow.cpp

+ 0 - 1
.github/FUNDING.yml

@@ -1,2 +1 @@
-patreon: assimp
 open_collective: assimp

+ 3 - 0
.gitignore

@@ -116,3 +116,6 @@ tools/assimp_qt_viewer/moc_glview.cpp_parameters
 tools/assimp_qt_viewer/moc_mainwindow.cpp
 tools/assimp_qt_viewer/moc_mainwindow.cpp_parameters
 tools/assimp_qt_viewer/ui_mainwindow.h
+
+#Generated directory
+generated/*

+ 1 - 1
CMakeLists.txt

@@ -454,7 +454,7 @@ IF(ASSIMP_HUNTER_ENABLED)
 ELSE()
   # If the zlib is already found outside, add an export in case assimpTargets can't find it.
   IF( ZLIB_FOUND )
-    INSTALL( TARGETS zlib
+    INSTALL( TARGETS zlib zlibstatic
         EXPORT "${TARGETS_EXPORT_NAME}")
   ENDIF()
 

+ 2 - 35
INSTALL

@@ -8,43 +8,10 @@ Getting the documentation
 ------------------------------
 
 A regularly-updated copy is available at 
-http://assimp.sourceforge.net/lib_html/index.html
-
-A CHM file is included in the SVN repos: ./doc/AssimpDoc_Html/AssimpDoc.chm.
-To build the doxygen documentation on your own, follow these steps:
-
-a) download & install latest doxygen 
-b) make sure doxygen is in the executable search path
-c) navigate to ./doc
-d) and run 'doxygen'
-
-Open the generated HTML (AssimpDoc_Html/index.html) in the browser of your choice.
-Windows only: To generate the CHM doc, install 'Microsoft HTML Workshop'
-and configure the path to it in the DOXYFILE first. 
+https://assimp-docs.readthedocs.io/en/latest/
 
 ------------------------------
 Building Assimp 
 ------------------------------
 
-More detailed build instructions can be found in the documentation,
-this section is just for the inpatient among you.
-
-CMake is the preferred build system for Assimp. The minimum required version 
-is 2.6. If you don't have it yet, downloads for CMake can be found on
-http://www.cmake.org/. 
-
-For Unix:
-
-1. mkdir build && cd build
-2. cmake .. -G 'Unix Makefiles'
-3. make -j4
-
-For Windows:
-1. Open a command prompt
-2. mkdir build
-3. cd build
-4. cmake ..
-5. cmake --build .
-
-For iOS:
-Just check the following project, which deploys a compiler toolchain for different iOS-versions: https://github.com/assimp/assimp/tree/master/port/iOS
+Just check the build-instaructions which you can find here: https://github.com/assimp/assimp/blob/master/Build.md

+ 1 - 1
LICENSE

@@ -1,6 +1,6 @@
 Open Asset Import Library (assimp)
 
-Copyright (c) 2006-2020, assimp team
+Copyright (c) 2006-2021, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,

+ 2 - 8
Readme.md

@@ -8,10 +8,10 @@ A library to import and export various 3d-model-formats including scene-post-pro
   <img alt="Coverity Scan Build Status"
        src="https://scan.coverity.com/projects/5607/badge.svg"/>
 </a>
+[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9973693b7bdd4543b07084d5d9cf4745)](https://www.codacy.com/gh/assimp/assimp/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=assimp/assimp&amp;utm_campaign=Badge_Grade)
 [![Coverage Status](https://coveralls.io/repos/github/assimp/assimp/badge.svg?branch=master)](https://coveralls.io/github/assimp/assimp?branch=master)
 [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Average time to resolve an issue")
-[![Codacy Badge](https://api.codacy.com/project/badge/Grade/5be56faac64f46fc941ac890fb4febef)](https://www.codacy.com/app/kimkulling/assimp?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=assimp/assimp&amp;utm_campaign=Badge_Grade)
 [![Total alerts](https://img.shields.io/lgtm/alerts/g/assimp/assimp.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/assimp/assimp/alerts/)
 <br>
 
@@ -39,7 +39,7 @@ Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file.
 ### Ports ###
 * [Android](port/AndroidJNI/README.md)
 * [Python](port/PyAssimp/README.md)
-* [.NET](https://github.com/assimp/assimp-net)
+* [.NET](https://bitbucket.org/Starnick/assimpnet/src/master/)
 * [Pascal](port/AssimpPascal/Readme.md)
 * [Javascript (Alpha)](https://github.com/makc/assimp2json)
 * [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777)
@@ -76,9 +76,6 @@ For more information, visit [our website](http://assimp.org/). Or check out the
 
 If the docs don't solve your problem, ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest). If you think you found a bug, please open an issue on Github.
 
-For development discussions, there is also a (very low-volume) mailing list, _assimp-discussions_
-  [(subscribe here)]( https://lists.sourceforge.net/lists/listinfo/assimp-discussions)
-
 Open Asset Import Library is a library to load various 3d file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
 
 And we also have a Gitter-channel:Gitter [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)<br>
@@ -103,9 +100,6 @@ Become a financial contributor and help us sustain our community. [[Contribute](
 
 <a href="https://opencollective.com/assimp"><img src="https://opencollective.com/assimp/individuals.svg?width=890"></a>
 
-Monthly donations via Patreon:
-<br>[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/assimp)
-
 
 #### Organizations
 

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

@@ -156,7 +156,7 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
             mRootStream = mZipArchive->Open(rootFile.c_str());
             ai_assert(mRootStream != nullptr);
             if (nullptr == mRootStream) {
-                throw DeadlyExportError("Cannot open root-file in archive : " + rootFile);
+                throw DeadlyImportError("Cannot open root-file in archive : " + rootFile);
             }
 
         } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {

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

@@ -39,16 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-/// \file AMFImporter.cpp
-/// \brief AMF-format files importer for Assimp: main algorithm implementation.
-/// \date 2016
-/// \author [email protected]
-
 #ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
 
 // Header files, Assimp.
 #include "AMFImporter.hpp"
-#include "AMFImporter_Macro.hpp"
 
 #include <assimp/DefaultIOSystem.h>
 #include <assimp/fast_atof.h>
@@ -307,14 +301,14 @@ void AMFImporter::ParseNode_Root() {
         throw DeadlyImportError("Root node \"amf\" not found.");
     }
     XmlNode node = *root;
-    mUnit = ai_str_tolower(std::string(node.attribute("unit").as_string()));
+    mUnit = ai_tolower(std::string(node.attribute("unit").as_string()));
     
     mVersion = node.attribute("version").as_string();
 
     // Read attributes for node <amf>.
     // Check attributes
     if (!mUnit.empty()) {
-        if ((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) {
+        if ((mUnit != "inch") && (mUnit != "millimeters") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) {
             Throw_IncorrectAttrValue("unit", mUnit);
         }
     }
@@ -409,20 +403,20 @@ void AMFImporter::ParseNode_Instance(XmlNode &node) {
 
     if (!node.empty()) {
         ParseHelper_Node_Enter(ne);
-        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+        for (auto &currentNode : node.children()) {
             const std::string &currentName = currentNode.name();
             if (currentName == "deltax") {
-                als.Delta.x = (ai_real)std::atof(currentNode.value());
+                XmlParser::getValueAsFloat(currentNode, als.Delta.x);
             } else if (currentName == "deltay") {
-                als.Delta.y = (ai_real)std::atof(currentNode.value());
+                XmlParser::getValueAsFloat(currentNode, als.Delta.y);
             } else if (currentName == "deltaz") {
-                als.Delta.z = (ai_real)std::atof(currentNode.value());
+                XmlParser::getValueAsFloat(currentNode, als.Delta.z);
             } else if (currentName == "rx") {
-                als.Delta.x = (ai_real)std::atof(currentNode.value());
+                XmlParser::getValueAsFloat(currentNode, als.Delta.x);
             } else if (currentName == "ry") {
-                als.Delta.y = (ai_real)std::atof(currentNode.value());
+                XmlParser::getValueAsFloat(currentNode, als.Delta.y);
             } else if (currentName == "rz") {
-                als.Delta.z = (ai_real)std::atof(currentNode.value());
+                XmlParser::getValueAsFloat(currentNode, als.Delta.z);
             }
         }
         ParseHelper_Node_Exit();
@@ -458,7 +452,7 @@ void AMFImporter::ParseNode_Object(XmlNode &node) {
     // Check for child nodes
     if (!node.empty()) {
         ParseHelper_Node_Enter(ne);
-        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+        for (auto &currentNode : node.children()) {
             const std::string &currentName = currentNode.name();
             if (currentName == "color") {
                 ParseNode_Color(currentNode);

+ 29 - 34
code/AssetLib/AMF/AMFImporter_Geometry.cpp

@@ -39,16 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-/// \file AMFImporter_Geometry.cpp
-/// \brief Parsing data from geometry nodes.
-/// \date 2016
-/// \author [email protected]
-
 #ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
 
 #include "AMFImporter.hpp"
-#include "AMFImporter_Macro.hpp"
-
 #include <assimp/ParsingUtils.h>
 
 namespace Assimp {
@@ -103,18 +96,18 @@ void AMFImporter::ParseNode_Vertices(XmlNode &node) {
     // create new mesh object.
     ne = new AMFVertices(mNodeElement_Cur);
     // Check for child nodes
-    pugi::xml_node vertexNode = node.child("vertex");
-    if (!vertexNode.empty()) {
-        ParseHelper_Node_Enter(ne);
-
-        ParseNode_Vertex(vertexNode);
-
-        ParseHelper_Node_Exit();
-
-    } else {
+    if (node.empty()) {
         mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
-    } // if(!mReader->isEmptyElement()) else
-
+        return;
+    }
+    ParseHelper_Node_Enter(ne);
+    for (XmlNode &currentNode : node.children()) {
+        const std::string &currentName = currentNode.name();
+        if (currentName == "vertex") {
+            ParseNode_Vertex(currentNode);
+        }
+    }
+    ParseHelper_Node_Exit();
     mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
 }
 
@@ -166,27 +159,25 @@ void AMFImporter::ParseNode_Vertex(XmlNode &node) {
 //   X, Y, or Z coordinate, respectively, of a vertex position in space.
 void AMFImporter::ParseNode_Coordinates(XmlNode &node) {
     AMFNodeElementBase *ne = nullptr;
-
-    // create new color object.
-    ne = new AMFCoordinates(mNodeElement_Cur);
-
-    AMFCoordinates &als = *((AMFCoordinates *)ne); // alias for convenience
     if (!node.empty()) {
+        ne = new AMFCoordinates(mNodeElement_Cur);
         ParseHelper_Node_Enter(ne);
         for (XmlNode &currentNode : node.children()) {
-            const std::string &currentName = currentNode.name();
-            if (currentName == "X") {
+            // create new color object.
+            AMFCoordinates &als = *((AMFCoordinates *)ne); // alias for convenience
+            const std::string &currentName = ai_tolower(currentNode.name());
+            if (currentName == "x") {
                 XmlParser::getValueAsFloat(currentNode, als.Coordinate.x);
-            } else if (currentName == "Y") {
+            } else if (currentName == "y") {
                 XmlParser::getValueAsFloat(currentNode, als.Coordinate.y);
-            } else if (currentName == "Z") {
+            } else if (currentName == "z") {
                 XmlParser::getValueAsFloat(currentNode, als.Coordinate.z);
             }
         }
-
         ParseHelper_Node_Exit();
+
     } else {
-        mNodeElement_Cur->Child.push_back(ne);
+        mNodeElement_Cur->Child.push_back(new AMFCoordinates(mNodeElement_Cur));
     }
 
     mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
@@ -216,7 +207,7 @@ void AMFImporter::ParseNode_Volume(XmlNode &node) {
     bool col_read = false;
     if (!node.empty()) {
         ParseHelper_Node_Enter(ne);
-        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+        for (auto &currentNode : node.children()) {
             const std::string currentName = currentNode.name();
             if (currentName == "color") {
                 if (col_read) Throw_MoreThanOnceDefined(currentName, "color", "Only one color can be defined for <volume>.");
@@ -258,7 +249,8 @@ void AMFImporter::ParseNode_Triangle(XmlNode &node) {
     bool col_read = false;
     if (!node.empty()) {
         ParseHelper_Node_Enter(ne);
-        for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+        std::string v;
+        for (auto &currentNode : node.children()) {
             const std::string currentName = currentNode.name();
             if (currentName == "color") {
                 if (col_read) Throw_MoreThanOnceDefined(currentName, "color", "Only one color can be defined for <triangle>.");
@@ -269,11 +261,14 @@ void AMFImporter::ParseNode_Triangle(XmlNode &node) {
             } else if (currentName == "map") {
                 ParseNode_TexMap(currentNode, true);
             } else if (currentName == "v1") {
-                als.V[0] = std::atoi(currentNode.value());
+                XmlParser::getValueAsString(currentNode, v);
+                als.V[0] = std::atoi(v.c_str());
             } else if (currentName == "v2") {
-                als.V[1] = std::atoi(currentNode.value());
+                XmlParser::getValueAsString(currentNode, v);
+                als.V[1] = std::atoi(v.c_str());
             } else if (currentName == "v3") {
-                als.V[2] = std::atoi(currentNode.value());
+                XmlParser::getValueAsString(currentNode, v);
+                als.V[2] = std::atoi(v.c_str());
             }
         }
         ParseHelper_Node_Exit();

+ 0 - 164
code/AssetLib/AMF/AMFImporter_Macro.hpp

@@ -1,164 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2021, assimp team
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the following
-conditions are met:
-
-* Redistributions of source code must retain the above
-copyright notice, this list of conditions and the
-following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the
-following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-contributors may be used to endorse or promote products
-derived from this software without specific prior
-written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------
-*/
-
-/// \file AMFImporter_Macro.hpp
-/// \brief Useful macrodefines.
-/// \date 2016
-/// \author [email protected]
-
-#pragma once
-#ifndef AMFIMPORTER_MACRO_HPP_INCLUDED
-#define AMFIMPORTER_MACRO_HPP_INCLUDED
-
-/// \def MACRO_ATTRREAD_LOOPBEG
-/// Begin of loop that read attributes values.
-#define MACRO_ATTRREAD_LOOPBEG \
-	for(int idx = 0, idx_end = mReader->getAttributeCount(); idx < idx_end; idx++) \
-	{ \
-		std::string an(mReader->getAttributeName(idx));
-
-/// \def MACRO_ATTRREAD_LOOPEND
-/// End of loop that read attributes values.
-#define MACRO_ATTRREAD_LOOPEND \
-		Throw_IncorrectAttr(an); \
-	}
-
-/// \def MACRO_ATTRREAD_LOOPEND_WSKIP
-/// End of loop that read attributes values. Difference from \ref MACRO_ATTRREAD_LOOPEND in that: current macro skip unknown attributes, but
-/// \ref MACRO_ATTRREAD_LOOPEND throw an exception.
-#define MACRO_ATTRREAD_LOOPEND_WSKIP \
-		continue; \
-	}
-
-/// \def MACRO_ATTRREAD_CHECK_REF
-/// Check current attribute name and if it equal to requested then read value. Result write to output variable by reference. If result was read then
-/// "continue" will called.
-/// \param [in] pAttrName - attribute name.
-/// \param [out] pVarName - output variable name.
-/// \param [in] pFunction - function which read attribute value and write it to pVarName.
-#define MACRO_ATTRREAD_CHECK_REF(pAttrName, pVarName, pFunction) \
-	if(an == pAttrName) \
-	{ \
-		pFunction(idx, pVarName); \
-		continue; \
-	}
-
-/// \def MACRO_ATTRREAD_CHECK_RET
-/// Check current attribute name and if it equal to requested then read value. Result write to output variable using return value of \ref pFunction.
-/// If result was read then  "continue" will called.
-/// \param [in] pAttrName - attribute name.
-/// \param [out] pVarName - output variable name.
-/// \param [in] pFunction - function which read attribute value and write it to pVarName.
-#define MACRO_ATTRREAD_CHECK_RET(pAttrName, pVarName, pFunction) \
-	if(an == pAttrName) \
-	{ \
-		pVarName = pFunction(idx); \
-		continue; \
-	}
-
-/// \def MACRO_NODECHECK_LOOPBEGIN(pNodeName)
-/// Begin of loop of parsing child nodes. Do not add ';' at end.
-/// \param [in] pNodeName - current node name.
-#define MACRO_NODECHECK_LOOPBEGIN(pNodeName) \
-	do { \
-	bool close_found = false; \
-	 \
-	while(mReader->read()) \
-	{ \
-		if(mReader->getNodeType() == irr::io::EXN_ELEMENT) \
-		{
-
-/// \def MACRO_NODECHECK_LOOPEND(pNodeName)
-/// End of loop of parsing child nodes.
-/// \param [in] pNodeName - current node name.
-#define MACRO_NODECHECK_LOOPEND(pNodeName) \
-			XML_CheckNode_SkipUnsupported(pNodeName); \
-		}/* if(mReader->getNodeType() == irr::io::EXN_ELEMENT) */ \
-		else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) \
-		{ \
-			if(XML_CheckNode_NameEqual(pNodeName)) \
-			{ \
-				close_found = true; \
-	 \
-				break; \
-			} \
-		}/* else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) */ \
-	}/* while(mReader->read()) */ \
-	 \
-	if(!close_found) Throw_CloseNotFound(pNodeName); \
-	 \
-	} while(false)
-
-/// \def MACRO_NODECHECK_READCOMP_F
-/// Check current node name and if it equal to requested then read value. Result write to output variable of type "float".
-/// If result was read then  "continue" will called. Also check if node data already read then raise exception.
-/// \param [in] pNodeName - node name.
-/// \param [in, out] pReadFlag - read flag.
-/// \param [out] pVarName - output variable name.
-#define MACRO_NODECHECK_READCOMP_F(pNodeName, pReadFlag, pVarName) \
-	if(XML_CheckNode_NameEqual(pNodeName)) \
-	{ \
-		/* Check if field already read before. */ \
-		if(pReadFlag) Throw_MoreThanOnceDefined(pNodeName, "Only one component can be defined."); \
-		/* Read color component and assign it to object. */ \
-		pVarName = XML_ReadNode_GetVal_AsFloat(); \
-		pReadFlag = true; \
-		continue; \
-	}
-
-/// \def MACRO_NODECHECK_READCOMP_U32
-/// Check current node name and if it equal to requested then read value. Result write to output variable of type "uint32_t".
-/// If result was read then  "continue" will called. Also check if node data already read then raise exception.
-/// \param [in] pNodeName - node name.
-/// \param [in, out] pReadFlag - read flag.
-/// \param [out] pVarName - output variable name.
-#define MACRO_NODECHECK_READCOMP_U32(pNodeName, pReadFlag, pVarName) \
-	if(XML_CheckNode_NameEqual(pNodeName)) \
-	{ \
-		/* Check if field already read before. */ \
-		if(pReadFlag) Throw_MoreThanOnceDefined(pNodeName, "Only one component can be defined."); \
-		/* Read color component and assign it to object. */ \
-		pVarName = XML_ReadNode_GetVal_AsU32(); \
-		pReadFlag = true; \
-		continue; \
-	}
-
-#endif // AMFIMPORTER_MACRO_HPP_INCLUDED

+ 69 - 72
code/AssetLib/AMF/AMFImporter_Material.cpp

@@ -65,48 +65,45 @@ namespace Assimp {
 //   Red, Greed, Blue and Alpha (transparency) component of a color in sRGB space, values ranging from 0 to 1. The
 //   values can be specified as constants, or as a formula depending on the coordinates.
 void AMFImporter::ParseNode_Color(XmlNode &node) {
-    std::string profile = node.attribute("profile").as_string();
-    
-	// create new color object.
-	AMFNodeElementBase *ne = new AMFColor(mNodeElement_Cur);
-	AMFColor& als = *((AMFColor*)ne);// alias for convenience
+    if (node.empty()) {
+        return;
+    }
 
-	als.Profile = profile;
-	if (!node.empty()) {
-        ParseHelper_Node_Enter(ne);
-		bool read_flag[4] = { false, false, false, false };
-		for (pugi::xml_node &child : node.children()) {
-            std::string name = child.name();
-            if ( name == "r") {
-				read_flag[0] = true;
-                XmlParser::getValueAsFloat(child, als.Color.r);
-            } else if (name == "g") {
-				read_flag[1] = true;
-                XmlParser::getValueAsFloat(child, als.Color.g);
-            } else if (name == "b") {
-				read_flag[2] = true;
-                XmlParser::getValueAsFloat(child, als.Color.b);
-            } else if (name == "a") {
-			    read_flag[3] = true;
-                XmlParser::getValueAsFloat(child, als.Color.a);
-            }
-            ParseHelper_Node_Exit();
+    const std::string &profile = node.attribute("profile").as_string();
+    bool read_flag[4] = { false, false, false, false };
+    AMFNodeElementBase *ne = new AMFColor(mNodeElement_Cur);
+    AMFColor &als = *((AMFColor *)ne); // alias for convenience
+    ParseHelper_Node_Enter(ne);
+    for (pugi::xml_node &child : node.children()) {
+        // create new color object.
+        als.Profile = profile;
+
+        const std::string &name = child.name();
+        if ( name == "r") {
+			read_flag[0] = true;
+            XmlParser::getValueAsFloat(child, als.Color.r);
+        } else if (name == "g") {
+			read_flag[1] = true;
+            XmlParser::getValueAsFloat(child, als.Color.g);
+        } else if (name == "b") {
+			read_flag[2] = true;
+            XmlParser::getValueAsFloat(child, als.Color.b);
+        } else if (name == "a") {
+			read_flag[3] = true;
+            XmlParser::getValueAsFloat(child, als.Color.a);
         }
-		// check that all components was defined
-		if (!(read_flag[0] && read_flag[1] && read_flag[2])) {
-			throw DeadlyImportError("Not all color components are defined.");
-		}
-
-		// check if <a> is absent. Then manually add "a == 1".
-		if (!read_flag[3]) {
-			als.Color.a = 1;
-		}
-	} else {
-		mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+        // check if <a> is absent. Then manually add "a == 1".
+        if (!read_flag[3]) {
+            als.Color.a = 1;
+        }
+    }
+    als.Composed = false;
+    mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+    ParseHelper_Node_Exit();
+    // check that all components was defined
+	if (!(read_flag[0] && read_flag[1] && read_flag[2])) {
+		throw DeadlyImportError("Not all color components are defined.");
 	}
-
-	als.Composed = false;
-	mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
 }
 
 // <material
@@ -158,11 +155,11 @@ void AMFImporter::ParseNode_Material(XmlNode &node) {
 // Multi elements - Yes.
 // Parent element - <amf>.
 void AMFImporter::ParseNode_Texture(XmlNode &node) {
-    std::string id = node.attribute("id").as_string();
-	uint32_t width = node.attribute("width").as_uint();
-	uint32_t height = node.attribute("height").as_uint();
-	uint32_t depth = node.attribute("depth").as_uint();
-	std::string type = node.attribute("type").as_string();
+    const std::string id = node.attribute("id").as_string();
+	const uint32_t width = node.attribute("width").as_uint();
+    const uint32_t height = node.attribute("height").as_uint();
+    uint32_t depth = node.attribute("depth").as_uint();
+    const std::string type = node.attribute("type").as_string();
 	bool tiled = node.attribute("tiled").as_bool();
 
     if (node.empty()) {
@@ -174,22 +171,20 @@ void AMFImporter::ParseNode_Texture(XmlNode &node) {
 
 	AMFTexture& als = *((AMFTexture*)ne);// alias for convenience
 
-    std::string enc64_data = node.value();
-	// Check for child nodes
+    std::string enc64_data;
+    XmlParser::getValueAsString(node, enc64_data);
+    // Check for child nodes
 
 	// check that all components was defined
     if (id.empty()) {
 		throw DeadlyImportError("ID for texture must be defined.");
     }
     if (width < 1) {
-		throw DeadlyImportError("INvalid width for texture.");
+		throw DeadlyImportError("Invalid width for texture.");
     }
     if (height < 1) {
 		throw DeadlyImportError("Invalid height for texture.");
 	}
-    if (depth < 1) {
-		throw DeadlyImportError("Invalid depth for texture.");
-    }
     if (type != "grayscale") {
 		throw DeadlyImportError("Invalid type for texture.");
     }
@@ -203,7 +198,9 @@ void AMFImporter::ParseNode_Texture(XmlNode &node) {
 	als.Depth = depth;
 	als.Tiled = tiled;
 	ParseHelper_Decode_Base64(enc64_data, als.Data);
-
+    if (depth == 0) {
+        depth = (uint32_t)(als.Data.size() / (width * height));
+    }
     // check data size
     if ((width * height * depth) != als.Data.size()) {
         throw DeadlyImportError("Texture has incorrect data size.");
@@ -233,20 +230,18 @@ void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) {
     AMFTexMap &als = *((AMFTexMap *)ne); //
     std::string rtexid, gtexid, btexid, atexid;
     if (!node.empty()) {
-        ParseHelper_Node_Enter(ne);
-        for (XmlNode &currentNode : node.children()) {
-            const std::string &currentName = currentNode.name();
-            if (currentName == "rtexid") {
-                XmlParser::getValueAsString(node, rtexid);
-            } else if (currentName == "gtexid") {
-                XmlParser::getValueAsString(node, gtexid);
-            } else if (currentName == "btexid") {
-                XmlParser::getValueAsString(node, btexid);
-            } else if (currentName == "atexid") {
-                XmlParser::getValueAsString(node, atexid);
+        for (pugi::xml_attribute &attr : node.attributes()) {
+            const std::string &currentAttr = attr.name();
+            if (currentAttr == "rtexid") {
+                rtexid = attr.as_string();
+            } else if (currentAttr == "gtexid") {
+                gtexid = attr.as_string();
+            } else if (currentAttr == "btexid") {
+                btexid = attr.as_string();
+            } else if (currentAttr == "atexid") {
+                atexid = attr.as_string();
             }
         }
-        ParseHelper_Node_Exit();
     }
 
 	// create new texture coordinates object, alias for convenience
@@ -256,7 +251,6 @@ void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) {
 	}
 
 	// Check for children nodes
-	//XML_CheckNode_MustHaveChildren();
 	if (node.children().begin() == node.children().end()) {
 		throw DeadlyImportError("Invalid children definition.");
 	}
@@ -264,28 +258,31 @@ void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) {
 	bool read_flag[6] = { false, false, false, false, false, false };
 
 	if (!pUseOldName) {
-		for (pugi::xml_attribute &attr : node.attributes()) {
-            const std::string name = attr.name();
+        ParseHelper_Node_Enter(ne);
+        for ( XmlNode &currentNode : node.children()) {
+            const std::string &name = currentNode.name();
             if (name == "utex1") {
 				read_flag[0] = true;
-				als.TextureCoordinate[0].x = attr.as_float();
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[0].x);
             } else if (name == "utex2") {
 				read_flag[1] = true;
-				als.TextureCoordinate[1].x = attr.as_float();
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[1].x);
             } else if (name == "utex3") {
 				read_flag[2] = true;
-				als.TextureCoordinate[2].x = attr.as_float();
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[2].x);
             } else if (name == "vtex1") {
 				read_flag[3] = true;
-				als.TextureCoordinate[0].y = attr.as_float();
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[0].y);
             } else if (name == "vtex2") {
 				read_flag[4] = true;
-				als.TextureCoordinate[1].y = attr.as_float();
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[1].y);
             } else if (name == "vtex3") {
 				read_flag[5] = true;
-				als.TextureCoordinate[0].y = attr.as_float();
+                XmlParser::getValueAsFloat(node, als.TextureCoordinate[2].y);
 			}
 		}
+        ParseHelper_Node_Exit();
+
 	} else {
 		for (pugi::xml_attribute &attr : node.attributes()) {
             const std::string name = attr.name();

+ 9 - 7
code/AssetLib/AMF/AMFImporter_Postprocess.cpp

@@ -62,12 +62,14 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*
     // Check if stored data are supported.
     if (!Composition.empty()) {
         throw DeadlyImportError("IME. GetColor for composition");
-    } else if (Color->Composed) {
+    }
+
+    if (Color->Composed) {
         throw DeadlyImportError("IME. GetColor, composed color");
-    } else {
-        tcol = Color->Color;
     }
 
+    tcol = Color->Color;
+    
     // Check if default color must be used
     if ((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0)) {
         tcol.r = 0.5f;
@@ -79,13 +81,13 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*
     return tcol;
 }
 
-void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElement, std::vector<aiVector3D> &pVertexCoordinateArray,
+void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &nodeElement, std::vector<aiVector3D> &vertexCoordinateArray,
         std::vector<AMFColor *> &pVertexColorArray) const {
     AMFVertices  *vn = nullptr;
     size_t col_idx;
 
     // All data stored in "vertices", search for it.
-    for (AMFNodeElementBase *ne_child : pNodeElement.Child) {
+    for (AMFNodeElementBase *ne_child : nodeElement.Child) {
         if (ne_child->Type == AMFNodeElementBase::ENET_Vertices) {
             vn = (AMFVertices*)ne_child;
         }
@@ -97,7 +99,7 @@ void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElem
     }
 
     // all coordinates stored as child and we need to reserve space for future push_back's.
-    pVertexCoordinateArray.reserve(vn->Child.size()); 
+    vertexCoordinateArray.reserve(vn->Child.size()); 
 
     // colors count equal vertices count.
     pVertexColorArray.resize(vn->Child.size()); 
@@ -112,7 +114,7 @@ void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElem
 
             for (AMFNodeElementBase *vtx : vn_child->Child) {
                 if (vtx->Type == AMFNodeElementBase::ENET_Coordinates) {
-                    pVertexCoordinateArray.push_back(((AMFCoordinates *)vtx)->Coordinate);
+                    vertexCoordinateArray.push_back(((AMFCoordinates *)vtx)->Coordinate);
                     continue;
                 }
 

+ 6 - 6
code/AssetLib/FBX/FBXCompileConfig.h

@@ -62,16 +62,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP
 #   include <unordered_map>
 #   include <unordered_set>
-#   if _MSC_VER > 1600
-#       define fbx_unordered_map unordered_map
-#       define fbx_unordered_multimap unordered_multimap
-#       define fbx_unordered_set unordered_set
-#       define fbx_unordered_multiset unordered_multiset
-#   else
+#   if defined(_MSC_VER) && _MSC_VER <= 1600
 #       define fbx_unordered_map tr1::unordered_map
 #       define fbx_unordered_multimap tr1::unordered_multimap
 #       define fbx_unordered_set tr1::unordered_set
 #       define fbx_unordered_multiset tr1::unordered_multiset
+#   else
+#       define fbx_unordered_map unordered_map
+#       define fbx_unordered_multimap unordered_multimap
+#       define fbx_unordered_set unordered_set
+#       define fbx_unordered_multiset unordered_multiset
 #   endif
 #endif
 

+ 6 - 0
code/AssetLib/FBX/FBXMeshGeometry.cpp

@@ -508,6 +508,12 @@ void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
         std::vector<int> uvIndices;
         ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
 
+        if (uvIndices.size() > vertex_count) {
+            FBXImporter::LogWarn(Formatter::format("trimming length of input array for ByPolygonVertex mapping: ")
+                                          << uvIndices.size() << ", expected " << vertex_count);
+            uvIndices.resize(vertex_count);
+        }
+
         if (uvIndices.size() != vertex_count) {
             FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygonVertex mapping: ")
                                   << uvIndices.size() << ", expected " << vertex_count);

+ 4 - 1
code/AssetLib/HMP/HMPLoader.cpp

@@ -157,7 +157,10 @@ void HMPImporter::InternReadFile(const std::string &pFile,
         szBuffer[2] = ((char *)&iMagic)[2];
         szBuffer[3] = ((char *)&iMagic)[3];
         szBuffer[4] = '\0';
-
+    
+        delete[] mBuffer;
+        mBuffer = nullptr;
+        
         // We're definitely unable to load this file
         throw DeadlyImportError("Unknown HMP subformat ", pFile,
                                 ". Magic word (", szBuffer, ") is not known");

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

@@ -250,7 +250,7 @@ void MDCImporter::InternReadFile(
 
     // get the number of valid surfaces
     BE_NCONST MDC::Surface *pcSurface, *pcSurface2;
-    pcSurface = pcSurface2 = new (mBuffer + pcHeader->ulOffsetSurfaces) MDC::Surface;
+    pcSurface = pcSurface2 = reinterpret_cast<BE_NCONST MDC::Surface *>(mBuffer + pcHeader->ulOffsetSurfaces);
     unsigned int iNumShaders = 0;
     for (unsigned int i = 0; i < pcHeader->ulNumSurfaces; ++i) {
         // validate the surface header
@@ -260,7 +260,7 @@ void MDCImporter::InternReadFile(
             ++pScene->mNumMeshes;
         }
         iNumShaders += pcSurface2->ulNumShaders;
-        pcSurface2 = new ((int8_t *)pcSurface2 + pcSurface2->ulOffsetEnd) MDC::Surface;
+        pcSurface2 = reinterpret_cast<BE_NCONST MDC::Surface *>((BE_NCONST int8_t *)pcSurface2 + pcSurface2->ulOffsetEnd);
     }
     aszShaders.reserve(iNumShaders);
     pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
@@ -405,7 +405,7 @@ void MDCImporter::InternReadFile(
             pcFaceCur->mIndices[2] = iOutIndex + 0;
         }
 
-        pcSurface = new ((int8_t *)pcSurface + pcSurface->ulOffsetEnd) MDC::Surface;
+        pcSurface = reinterpret_cast<BE_NCONST MDC::Surface *>((BE_NCONST int8_t *)pcSurface + pcSurface->ulOffsetEnd);
     }
 
     // create a flat node graph with a root node and one child for each surface

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

@@ -500,7 +500,7 @@ void MS3DImporter::InternReadFile( const std::string& pFile,
                 throw DeadlyImportError("MS3D: Encountered invalid triangle index, file is malformed");
             }
 
-            TempTriangle& t = triangles[g.triangles[i]];
+            TempTriangle& t = triangles[g.triangles[j]];
             f.mIndices = new unsigned int[f.mNumIndices=3];
 
             for (unsigned int k = 0; k < 3; ++k,++n) {
@@ -508,7 +508,7 @@ void MS3DImporter::InternReadFile( const std::string& pFile,
                     throw DeadlyImportError("MS3D: Encountered invalid vertex index, file is malformed");
                 }
 
-                const TempVertex& v = vertices[t.indices[i]];
+                const TempVertex& v = vertices[t.indices[k]];
                 for(unsigned int a = 0; a < 4; ++a) {
                     if (v.bone_id[a] != UINT_MAX) {
                         if (v.bone_id[a] >= joints.size()) {
@@ -524,9 +524,9 @@ void MS3DImporter::InternReadFile( const std::string& pFile,
                 // collect vertex components
                 m->mVertices[n] = v.pos;
 
-                m->mNormals[n] = t.normals[i];
-                m->mTextureCoords[0][n] = aiVector3D(t.uv[i].x,1.f-t.uv[i].y,0.0);
-                f.mIndices[i] = n;
+                m->mNormals[n] = t.normals[k];
+                m->mTextureCoords[0][n] = aiVector3D(t.uv[k].x,1.f-t.uv[k].y,0.0);
+                f.mIndices[k] = n;
             }
         }
 

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

@@ -181,13 +181,13 @@ Mesh *OgreBinarySerializer::ImportMesh(MemoryStreamReader *stream) {
 
     uint16_t id = serializer.ReadHeader(false);
     if (id != HEADER_CHUNK_ID) {
-        throw DeadlyExportError("Invalid Ogre Mesh file header.");
+        throw DeadlyImportError("Invalid Ogre Mesh file header.");
     }
 
     /// @todo Check what we can actually support.
     std::string version = serializer.ReadLine();
     if (version != MESH_VERSION_1_8) {
-        throw DeadlyExportError("Mesh version ", version, " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again.",
+        throw DeadlyImportError("Mesh version ", version, " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again.",
                                                     " Supported versions: ", MESH_VERSION_1_8);
     }
 
@@ -797,13 +797,13 @@ MemoryStreamReaderPtr OgreBinarySerializer::OpenReader(Assimp::IOSystem *pIOHand
 void OgreBinarySerializer::ReadSkeleton(Skeleton *skeleton) {
     uint16_t id = ReadHeader(false);
     if (id != HEADER_CHUNK_ID) {
-        throw DeadlyExportError("Invalid Ogre Skeleton file header.");
+        throw DeadlyImportError("Invalid Ogre Skeleton file header.");
     }
 
     // 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("Skeleton version ", version, " not supported by this importer.",
+        throw DeadlyImportError("Skeleton version ", version, " not supported by this importer.",
                                                     " Supported versions: ", SKELETON_VERSION_1_8, " and ", SKELETON_VERSION_1_1);
     }
 

+ 2 - 2
code/AssetLib/Ogre/OgreMaterial.cpp

@@ -419,7 +419,7 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr
                 size_t posUnderscore = textureRef.find_last_of("_");
 
                 if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) {
-                    string identifier = ai_str_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore));
+                    string identifier = ai_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore));
                     ASSIMP_LOG_VERBOSE_DEBUG_F("Detecting texture type from filename postfix '", identifier, "'");
 
                     if (identifier == "_n" || identifier == "_nrm" || identifier == "_nrml" || identifier == "_normal" || identifier == "_normals" || identifier == "_normalmap") {
@@ -440,7 +440,7 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr
             // Detect from texture unit name. This cannot be too broad as
             // authors might give names like "LightSaber" or "NormalNinja".
             else {
-                string unitNameLower = ai_str_tolower(textureUnitName);
+                string unitNameLower = ai_tolower(textureUnitName);
                 if (unitNameLower.find("normalmap") != string::npos) {
                     textureType = aiTextureType_NORMALS;
                 } else if (unitNameLower.find("specularmap") != string::npos) {

+ 1 - 1
code/AssetLib/Ogre/OgreParsingUtils.h

@@ -64,7 +64,7 @@ static inline bool EndsWith(const std::string &s, const std::string &suffix, boo
     }
 
     if (!caseSensitive) {
-        return EndsWith(ai_str_tolower(s), ai_str_tolower(suffix), true);
+        return EndsWith(ai_tolower(s), ai_tolower(suffix), true);
     }
 
     size_t len = suffix.length();

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

@@ -120,7 +120,7 @@ std::string OgreXmlSerializer::ReadAttribute<std::string>(XmlNode &xmlNode, cons
 
 template <>
 bool OgreXmlSerializer::ReadAttribute<bool>(XmlNode &xmlNode, const char *name) const {
-    std::string value = ai_str_tolower(ReadAttribute<std::string>(xmlNode, name));
+    std::string value = ai_tolower(ReadAttribute<std::string>(xmlNode, name));
     if (ASSIMP_stricmp(value, "true") == 0) {
         return true;
     } else if (ASSIMP_stricmp(value, "false") == 0) {
@@ -545,7 +545,7 @@ void OgreXmlSerializer::ReadSkeleton(XmlNode &node, Skeleton *skeleton) {
 
     // Optional blend mode from root node
     if (XmlParser::hasAttribute(node, "blendmode")) {
-        skeleton->blendMode = (ai_str_tolower(ReadAttribute<std::string>(node, "blendmode")) == "cumulative" ? Skeleton::ANIMBLEND_CUMULATIVE : Skeleton::ANIMBLEND_AVERAGE);
+        skeleton->blendMode = (ai_tolower(ReadAttribute<std::string>(node, "blendmode")) == "cumulative" ? Skeleton::ANIMBLEND_CUMULATIVE : Skeleton::ANIMBLEND_AVERAGE);
     }
 
     for (XmlNode &currentNode : node.children()) {

+ 1 - 1
code/AssetLib/STEPParser/STEPFileReader.cpp

@@ -278,7 +278,7 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
             --ne;
         } while (IsSpace(s.at(ne)));
         std::string type = s.substr(ns, ne - ns + 1);
-        type = ai_str_tolower(type);
+        type = ai_tolower(type);
         const char* sz = scheme.GetStaticStringForToken(type);
         if(sz) {
             const std::string::size_type szLen = n2-n1+1;

+ 5 - 5
code/AssetLib/Step/STEPFile.h

@@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifdef _MSC_VER
 #    pragma warning(push)
 #    pragma warning(disable : 4127 4456 4245 4512 )
-#endif // _MSC_VER 
+#endif // _MSC_VER
 
 //
 #if _MSC_VER > 1500 || (defined __GNUC___)
@@ -69,12 +69,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifdef ASSIMP_STEP_USE_UNORDERED_MULTIMAP
 #    include <unordered_map>
-#    if _MSC_VER > 1600
-#        define step_unordered_map unordered_map
-#        define step_unordered_multimap unordered_multimap
-#    else
+#    if defined(_MSC_VER) && _MSC_VER <= 1600
 #        define step_unordered_map tr1::unordered_map
 #        define step_unordered_multimap tr1::unordered_multimap
+#    else
+#        define step_unordered_map unordered_map
+#        define step_unordered_multimap unordered_multimap
 #    endif
 #endif
 

+ 5 - 5
code/AssetLib/Step/StepExporter.cpp

@@ -75,12 +75,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifdef ASSIMP_STEP_USE_UNORDERED_MULTIMAP
 #   include <unordered_map>
-#   if _MSC_VER > 1600
-#       define step_unordered_map unordered_map
-#       define step_unordered_multimap unordered_multimap
-#   else
+#   if defined(_MSC_VER) && _MSC_VER <= 1600
 #       define step_unordered_map tr1::unordered_map
 #       define step_unordered_multimap tr1::unordered_multimap
+#   else
+#       define step_unordered_map unordered_map
+#       define step_unordered_multimap unordered_multimap
 #   endif
 #endif
 
@@ -302,7 +302,7 @@ void StepExporter::WriteFile()
             dv23.Normalize();
             dv31.Normalize();
             dv13.Normalize();
-            
+
             aiVector3D dvY = dv12;
             aiVector3D dvX = dvY ^ dv13;
             dvX.Normalize();

+ 7 - 3
code/AssetLib/glTF/glTFAsset.h

@@ -92,10 +92,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifdef ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
 #   include <unordered_map>
-#   if _MSC_VER > 1600
-#       define gltf_unordered_map unordered_map
-#   else
+#   if defined(_MSC_VER) && _MSC_VER <= 1600
 #       define gltf_unordered_map tr1::unordered_map
+#   else
+#       define gltf_unordered_map unordered_map
 #   endif
 #endif
 
@@ -954,7 +954,9 @@ namespace glTF
         virtual void AttachToDocument(Document& doc) = 0;
         virtual void DetachFromDocument() = 0;
 
+#if !defined(ASSIMP_BUILD_NO_EXPORT)
         virtual void WriteObjects(AssetWriter& writer) = 0;
+#endif
     };
 
 
@@ -986,8 +988,10 @@ namespace glTF
         void AttachToDocument(Document& doc);
         void DetachFromDocument();
 
+#if !defined(ASSIMP_BUILD_NO_EXPORT)
         void WriteObjects(AssetWriter& writer)
             { WriteLazyDict<T>(*this, writer); }
+#endif
 
         Ref<T> Add(T* obj);
 

+ 3 - 3
code/AssetLib/glTF/glTFCommon.h

@@ -74,10 +74,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifdef ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
 #include <unordered_map>
-#if _MSC_VER > 1600
-#define gltf_unordered_map unordered_map
-#else
+#if defined(_MSC_VER) && _MSC_VER <= 1600
 #define gltf_unordered_map tr1::unordered_map
+#else
+#define gltf_unordered_map unordered_map
 #endif
 #endif
 

+ 2 - 0
code/AssetLib/glTF/glTFImporter.cpp

@@ -43,7 +43,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "AssetLib/glTF/glTFImporter.h"
 #include "AssetLib/glTF/glTFAsset.h"
+#if !defined(ASSIMP_BUILD_NO_EXPORT)
 #include "AssetLib/glTF/glTFAssetWriter.h"
+#endif
 #include "PostProcessing/MakeVerboseFormat.h"
 
 #include <assimp/StringComparison.h>

+ 14 - 4
code/AssetLib/glTF2/glTF2Asset.h

@@ -98,12 +98,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifdef ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
 #include <unordered_map>
 #include <unordered_set>
-#if _MSC_VER > 1600
-#define gltf_unordered_map unordered_map
-#define gltf_unordered_set unordered_set
-#else
+#if defined(_MSC_VER) && _MSC_VER <= 1600
 #define gltf_unordered_map tr1::unordered_map
 #define gltf_unordered_set tr1::unordered_set
+#else
+#define gltf_unordered_map unordered_map
+#define gltf_unordered_set unordered_set
 #endif
 #endif
 
@@ -813,6 +813,11 @@ struct Mesh : public Object {
             AccessorList position, normal, tangent;
         };
         std::vector<Target> targets;
+
+        // extension: FB_ngon_encoding
+        bool ngonEncoded;
+
+        Primitive(): ngonEncoded(false) {}
     };
 
     std::vector<Primitive> primitives;
@@ -992,7 +997,9 @@ public:
     virtual void AttachToDocument(Document &doc) = 0;
     virtual void DetachFromDocument() = 0;
 
+#if !defined(ASSIMP_BUILD_NO_EXPORT)
     virtual void WriteObjects(AssetWriter &writer) = 0;
+#endif
 };
 
 template <class T>
@@ -1025,7 +1032,9 @@ class LazyDict : public LazyDictBase {
     void AttachToDocument(Document &doc);
     void DetachFromDocument();
 
+#if !defined(ASSIMP_BUILD_NO_EXPORT)
     void WriteObjects(AssetWriter &writer) { WriteLazyDict<T>(*this, writer); }
+#endif
 
     Ref<T> Add(T *obj);
 
@@ -1108,6 +1117,7 @@ public:
         bool KHR_materials_clearcoat;
         bool KHR_materials_transmission;
         bool KHR_draco_mesh_compression;
+        bool FB_ngon_encoding;
     } extensionsUsed;
 
     //! Keeps info about the required extensions

+ 18 - 0
code/AssetLib/glTF2/glTF2AssetWriter.inl

@@ -507,6 +507,20 @@ namespace glTF2 {
             Mesh::Primitive& p = m.primitives[i];
             Value prim;
             prim.SetObject();
+
+            // Extensions
+            if (p.ngonEncoded)
+            {
+                Value exts;
+                exts.SetObject();
+
+                Value FB_ngon_encoding;
+                FB_ngon_encoding.SetObject();
+
+                exts.AddMember(StringRef("FB_ngon_encoding"), FB_ngon_encoding, w.mAl);
+                prim.AddMember("extensions", exts, w.mAl);
+            }
+
             {
                 prim.AddMember("mode", Value(int(p.mode)).Move(), w.mAl);
 
@@ -874,6 +888,10 @@ namespace glTF2 {
             if (this->mAsset.extensionsUsed.KHR_materials_transmission) {
                 exts.PushBack(StringRef("KHR_materials_transmission"), mAl);
             }
+
+            if (this->mAsset.extensionsUsed.FB_ngon_encoding) {
+                exts.PushBack(StringRef("FB_ngon_encoding"), mAl);
+            }
         }
 
         if (!exts.Empty())

+ 4 - 0
code/AssetLib/glTF2/glTF2Exporter.cpp

@@ -97,6 +97,9 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai
 
     mAsset.reset( new Asset( pIOSystem ) );
 
+    // Always on as our triangulation process is aware of this type of encoding
+    mAsset->extensionsUsed.FB_ngon_encoding = true;
+
     if (isBinary) {
         mAsset->SetAsBinary();
     }
@@ -955,6 +958,7 @@ void glTF2Exporter::ExportMeshes()
         m->name = name;
 
         p.material = mAsset->materials.Get(aim->mMaterialIndex);
+        p.ngonEncoded = (aim->mPrimitiveTypes & aiPrimitiveType_NGONEncodingFlag) != 0;
 
 		/******************* Vertices ********************/
 		Ref<Accessor> v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);

+ 36 - 22
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -44,7 +44,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "AssetLib/glTF2/glTF2Importer.h"
 #include "PostProcessing/MakeVerboseFormat.h"
 #include "AssetLib/glTF2/glTF2Asset.h"
+#if !defined(ASSIMP_BUILD_NO_EXPORT)
 #include "AssetLib/glTF2/glTF2AssetWriter.h"
+#endif
 
 #include <assimp/CreateAnimMesh.h>
 #include <assimp/StringComparison.h>
@@ -527,8 +529,8 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                 std::fill(aim->mAnimMeshes, aim->mAnimMeshes + aim->mNumAnimMeshes, nullptr);
                 for (size_t i = 0; i < targets.size(); i++) {
                     bool needPositions = targets[i].position.size() > 0;
-                    bool needNormals = targets[i].normal.size() > 0;
-                    bool needTangents = targets[i].tangent.size() > 0;
+                    bool needNormals = (targets[i].normal.size() > 0) && aim->HasNormals();
+                    bool needTangents = (targets[i].tangent.size() > 0) && aim->HasTangentsAndBitangents();
                     // GLTF morph does not support colors and texCoords
                     aim->mAnimMeshes[i] = aiCreateAnimMesh(aim,
                             needPositions, needNormals, needTangents, false, false);
@@ -536,35 +538,47 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                     Mesh::Primitive::Target &target = targets[i];
 
                     if (needPositions) {
-                        aiVector3D *positionDiff = nullptr;
-                        target.position[0]->ExtractData(positionDiff);
-                        for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
-                            aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
+                        if (target.position[0]->count != aim->mNumVertices) {
+                            ASSIMP_LOG_WARN_F("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
+                        } else {
+                            aiVector3D *positionDiff = nullptr;
+                            target.position[0]->ExtractData(positionDiff);
+                            for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
+                                aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
+                            }
+                            delete[] positionDiff;
                         }
-                        delete[] positionDiff;
                     }
                     if (needNormals) {
-                        aiVector3D *normalDiff = nullptr;
-                        target.normal[0]->ExtractData(normalDiff);
-                        for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
-                            aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
+                        if (target.normal[0]->count != aim->mNumVertices) {
+                            ASSIMP_LOG_WARN_F("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
+                        } else {
+                            aiVector3D *normalDiff = nullptr;
+                            target.normal[0]->ExtractData(normalDiff);
+                            for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
+                                aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
+                            }
+                            delete[] normalDiff;
                         }
-                        delete[] normalDiff;
                     }
                     if (needTangents) {
-                        Tangent *tangent = nullptr;
-                        attr.tangent[0]->ExtractData(tangent);
+                        if (target.tangent[0]->count != aim->mNumVertices) {
+                            ASSIMP_LOG_WARN_F("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
+                        } else {
+                            Tangent *tangent = nullptr;
+                            attr.tangent[0]->ExtractData(tangent);
 
-                        aiVector3D *tangentDiff = nullptr;
-                        target.tangent[0]->ExtractData(tangentDiff);
+                            aiVector3D *tangentDiff = nullptr;
+                            target.tangent[0]->ExtractData(tangentDiff);
 
-                        for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) {
-                            tangent[vertexId].xyz += tangentDiff[vertexId];
-                            aiAnimMesh.mTangents[vertexId] = tangent[vertexId].xyz;
-                            aiAnimMesh.mBitangents[vertexId] = (aiAnimMesh.mNormals[vertexId] ^ tangent[vertexId].xyz) * tangent[vertexId].w;
+                            for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) {
+                                tangent[vertexId].xyz += tangentDiff[vertexId];
+                                aiAnimMesh.mTangents[vertexId] = tangent[vertexId].xyz;
+                                aiAnimMesh.mBitangents[vertexId] = (aiAnimMesh.mNormals[vertexId] ^ tangent[vertexId].xyz) * tangent[vertexId].w;
+                            }
+                            delete[] tangent;
+                            delete[] tangentDiff;
                         }
-                        delete[] tangent;
-                        delete[] tangentDiff;
                     }
                     if (mesh.weights.size() > i) {
                         aiAnimMesh.mWeight = mesh.weights[i];

+ 0 - 1
code/CMakeLists.txt

@@ -298,7 +298,6 @@ SET(ASSIMP_EXPORTERS_DISABLED "") # disabled exporters list (used to print)
 
 ADD_ASSIMP_IMPORTER( AMF
   AssetLib/AMF/AMFImporter.hpp
-  AssetLib/AMF/AMFImporter_Macro.hpp
   AssetLib/AMF/AMFImporter_Node.hpp
   AssetLib/AMF/AMFImporter.cpp
   AssetLib/AMF/AMFImporter_Geometry.cpp

+ 1 - 1
code/Common/BaseImporter.cpp

@@ -276,7 +276,7 @@ std::string BaseImporter::GetExtension(const std::string &file) {
 
     // thanks to Andy Maloney for the hint
     std::string ret = file.substr(pos + 1);
-    ret = ai_str_tolower(ret);
+    ret = ai_tolower(ret);
 
     return ret;
 }

+ 1 - 1
code/Common/Importer.cpp

@@ -982,7 +982,7 @@ size_t Importer::GetImporterIndex (const char* szExtension) const {
     if (ext.empty()) {
         return static_cast<size_t>(-1);
     }
-    ext = ai_str_tolower(ext);
+    ext = ai_tolower(ext);
     std::set<std::string> str;
     for (std::vector<BaseImporter*>::const_iterator i =  pimpl->mImporter.begin();i != pimpl->mImporter.end();++i)  {
         str.clear();

+ 1 - 1
code/Common/Version.cpp

@@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 static const char *LEGAL_INFORMATION =
         "Open Asset Import Library (Assimp).\n"
         "A free C/C++ library to import various 3D file formats into applications\n\n"
-        "(c) 2006-2020, assimp team\n"
+        "(c) 2006-2021, Assimp team\n"
         "License under the terms and conditions of the 3-clause BSD license\n"
         "https://www.assimp.org\n";
 

+ 1 - 1
code/PostProcessing/EmbedTexturesProcess.cpp

@@ -137,7 +137,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const {
     pTexture->pcData = imageContent;
 
     auto extension = path.substr(path.find_last_of('.') + 1u);
-    extension = ai_str_tolower(extension);
+    extension = ai_tolower(extension);
     if (extension == "jpeg") {
         extension = "jpg";
     }

+ 100 - 3
code/PostProcessing/TriangulateProcess.cpp

@@ -76,6 +76,87 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 using namespace Assimp;
 
+namespace {
+
+    /**
+     * @brief Helper struct used to simplify NGON encoding functions.
+     */
+    struct NGONEncoder {
+        NGONEncoder() : mLastNGONFirstIndex((unsigned int)-1) {}
+
+        /**
+         * @brief Encode the current triangle, and make sure it is recognized as a triangle.
+         * 
+         * This method will rotate indices in tri if needed in order to avoid tri to be considered
+         * part of the previous ngon. This method is to be used whenever you want to emit a real triangle,
+         * and make sure it is seen as a triangle.
+         * 
+         * @param tri Triangle to encode.
+         */
+        void ngonEncodeTriangle(aiFace * tri) {
+            ai_assert(tri->mNumIndices == 3);
+
+            // Rotate indices in new triangle to avoid ngon encoding false ngons
+            // Otherwise, the new triangle would be considered part of the previous NGON.
+            if (isConsideredSameAsLastNgon(tri)) {
+                std::swap(tri->mIndices[0], tri->mIndices[2]);
+                std::swap(tri->mIndices[1], tri->mIndices[2]);
+            }
+
+            mLastNGONFirstIndex = tri->mIndices[0];
+        }
+
+        /**
+         * @brief Encode a quad (2 triangles) in ngon encoding, and make sure they are seen as a single ngon.
+         * 
+         * @param tri1 First quad triangle
+         * @param tri2 Second quad triangle
+         * 
+         * @pre Triangles must be properly fanned from the most appropriate vertex.
+         */
+        void ngonEncodeQuad(aiFace *tri1, aiFace *tri2) {
+            ai_assert(tri1->mNumIndices == 3);
+            ai_assert(tri2->mNumIndices == 3);
+            ai_assert(tri1->mIndices[0] == tri2->mIndices[0]);
+
+            // If the selected fanning vertex is the same as the previously
+            // emitted ngon, we use the opposite vertex which also happens to work
+            // for tri-fanning a concave quad.
+            // ref: https://github.com/assimp/assimp/pull/3695#issuecomment-805999760
+            if (isConsideredSameAsLastNgon(tri1)) {
+                // Right-rotate indices for tri1 (index 2 becomes the new fanning vertex)
+                std::swap(tri1->mIndices[0], tri1->mIndices[2]);
+                std::swap(tri1->mIndices[1], tri1->mIndices[2]);
+
+                // Left-rotate indices for tri2 (index 2 becomes the new fanning vertex)
+                std::swap(tri2->mIndices[1], tri2->mIndices[2]);
+                std::swap(tri2->mIndices[0], tri2->mIndices[2]);
+
+                ai_assert(tri1->mIndices[0] == tri2->mIndices[0]);
+            }
+
+            mLastNGONFirstIndex = tri1->mIndices[0];
+        }
+
+        /**
+         * @brief Check whether this triangle would be considered part of the lastly emitted ngon or not.
+         * 
+         * @param tri Current triangle.
+         * @return true If used as is, this triangle will be part of last ngon.
+         * @return false If used as is, this triangle is not considered part of the last ngon.
+         */
+        bool isConsideredSameAsLastNgon(const aiFace * tri) const {
+            ai_assert(tri->mNumIndices == 3);
+            return tri->mIndices[0] == mLastNGONFirstIndex;
+        }
+
+    private:
+        unsigned int mLastNGONFirstIndex;
+    };
+
+}
+
+
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 TriangulateProcess::TriangulateProcess()
@@ -175,10 +256,15 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
     pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
     pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
 
+    // The mesh becomes NGON encoded now, during the triangulation process.
+    pMesh->mPrimitiveTypes |= aiPrimitiveType_NGONEncodingFlag;
+
     aiFace* out = new aiFace[numOut](), *curOut = out;
     std::vector<aiVector3D> temp_verts3d(max_out+2); /* temporary storage for vertices */
     std::vector<aiVector2D> temp_verts(max_out+2);
 
+    NGONEncoder ngonEncoder;
+
     // Apply vertex colors to represent the face winding?
 #ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
     if (!pMesh->mColors[0])
@@ -220,8 +306,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
             aiFace& nface = *curOut++;
             nface.mNumIndices = face.mNumIndices;
             nface.mIndices    = face.mIndices;
-
             face.mIndices = nullptr;
+
+            // points and lines don't require ngon encoding (and are not supported either!)
+            if (nface.mNumIndices == 3) ngonEncoder.ngonEncodeTriangle(&nface);
+
             continue;
         }
         // optimized code for quadrilaterals
@@ -274,6 +363,9 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 
             // prevent double deletion of the indices field
             face.mIndices = nullptr;
+
+            ngonEncoder.ngonEncodeQuad(&nface, &sface);
+
             continue;
         }
         else
@@ -284,11 +376,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
             // modeling suite to make extensive use of highly concave, monster polygons ...
             // so we need to apply the full 'ear cutting' algorithm to get it right.
 
-            // RERQUIREMENT: polygon is expected to be simple and *nearly* planar.
+            // REQUIREMENT: polygon is expected to be simple and *nearly* planar.
             // We project it onto a plane to get a 2d triangle.
 
             // Collect all vertices of of the polygon.
-           for (tmp = 0; tmp < max; ++tmp) {
+            for (tmp = 0; tmp < max; ++tmp) {
                 temp_verts3d[tmp] = verts[idx[tmp]];
             }
 
@@ -508,6 +600,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
             i[0] = idx[i[0]];
             i[1] = idx[i[1]];
             i[2] = idx[i[2]];
+
+            // IMPROVEMENT: Polygons are not supported yet by this ngon encoding + triangulation step.
+            //              So we encode polygons as regular triangles. No way to reconstruct the original
+            //              polygon in this case.
+            ngonEncoder.ngonEncodeTriangle(f);
             ++f;
         }
 

+ 2 - 2
include/assimp/MathFunctions.h

@@ -87,7 +87,7 @@ inline IntegerType lcm( IntegerType a, IntegerType b ) {
     }
 	return a / t * b;
 }
-/// @brief  Will return the smallest epsilon-value for the requested type. 
+/// @brief  Will return the smallest epsilon-value for the requested type.
 /// @return The numercical limit epsilon depending on its type.
 template<class T>
 inline T getEpsilon() {
@@ -97,7 +97,7 @@ inline T getEpsilon() {
 /// @brief  Will return the constant PI for the requested type.
 /// @return Pi
 template<class T>
-inline T PI() {
+inline T aiPi() {
     return static_cast<T>(3.14159265358979323846);
 }
 

+ 1 - 1
include/assimp/StringUtils.h

@@ -225,7 +225,7 @@ AI_FORCE_INLINE char_t ai_tolower(char_t in) {
 /// @param  in  The incoming string.
 /// @return The string as lowercase.
 // ---------------------------------------------------------------------------------
-AI_FORCE_INLINE std::string ai_str_tolower(const std::string &in) {
+AI_FORCE_INLINE std::string ai_tolower(const std::string &in) {
     std::string out(in);
     ai_trim_left(out);
     ai_trim_right(out);

+ 6 - 1
include/assimp/XmlParser.h

@@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
+/// @brief  Will find a node by its name.
 struct find_node_by_name_predicate {
     std::string mName;
     find_node_by_name_predicate(const std::string &name) :
@@ -88,7 +89,11 @@ public:
     }
 
     void clear() {
-        mData.resize(0);
+        if(mData.empty()) {
+            mDoc = nullptr;
+            return;
+        }
+        mData.clear();
         delete mDoc;
         mDoc = nullptr;
     }

+ 18 - 0
include/assimp/mesh.h

@@ -398,6 +398,24 @@ enum aiPrimitiveType {
      */
     aiPrimitiveType_POLYGON = 0x8,
 
+    /**
+     * A flag to determine whether this triangles only mesh is NGON encoded.
+     * 
+     * NGON encoding is a special encoding that tells whether 2 or more consecutive triangles
+     * should be considered as a triangle fan. This is identified by looking at the first vertex index.
+     * 2 consecutive triangles with the same 1st vertex index are part of the same
+     * NGON.
+     * 
+     * At the moment, only quads (concave or convex) are supported, meaning that polygons are 'seen' as 
+     * triangles, as usual after a triangulation pass.
+     * 
+     * To get an NGON encoded mesh, please use the aiProcess_Triangulate post process.
+     * 
+     * @see aiProcess_Triangulate
+     * @link https://github.com/KhronosGroup/glTF/pull/1620
+     */
+    aiPrimitiveType_NGONEncodingFlag = 0x10,
+
 /** This value is not used. It is just here to force the
      *  compiler to map this enum to a 32 Bit integer.
      */

+ 3 - 3
port/iOS/build.sh

@@ -22,7 +22,7 @@ BUILD_TYPE=Release
 ################################################
 # 		 Minimum iOS deployment target version
 ################################################
-MIN_IOS_VERSION="6.0"
+MIN_IOS_VERSION="10.0"
 
 IOS_SDK_TARGET=$MIN_IOS_VERSION
 XCODE_ROOT_DIR=$(xcode-select  --print-path)
@@ -60,8 +60,8 @@ build_arch()
 
     unset DEVROOT SDKROOT CFLAGS LDFLAGS CPPFLAGS CXXFLAGS CMAKE_CLI_INPUT
            
-	#export CC="$(xcrun -sdk iphoneos -find clang)"
-    #export CPP="$CC -E"
+	export CC="$(xcrun -sdk iphoneos -find clang)"
+    export CPP="$CC -E"
     export DEVROOT=$XCODE_ROOT_DIR/Platforms/$IOS_SDK_DEVICE.platform/Developer
     export SDKROOT=$DEVROOT/SDKs/$IOS_SDK_DEVICE$IOS_SDK_VERSION.sdk
     export CFLAGS="-arch $1 -pipe -no-cpp-precomp -stdlib=$CPP_STD_LIB -isysroot $SDKROOT -I$SDKROOT/usr/include/ -miphoneos-version-min=$IOS_SDK_TARGET"

+ 1 - 1
test/unit/AssimpAPITest_aiMatrix4x4.cpp

@@ -57,7 +57,7 @@ protected:
     aiMatrix4x4 get_predetermined_transformation_matrix_for_decomposition() const {
         aiMatrix4x4 t, r;
         aiMatrix4x4::Translation(aiVector3D(14,-25,-8), t);
-        aiMatrix4x4::Rotation(Math::PI<float>() / 4.0f, aiVector3D(1).Normalize(), r);
+        aiMatrix4x4::Rotation(Math::aiPi<float>() / 4.0f, aiVector3D(1).Normalize(), r);
         return t * r;
     }
 

+ 3 - 3
test/unit/AssimpAPITest_aiQuaternion.cpp

@@ -59,7 +59,7 @@ TEST_F(AssimpAPITest_aiQuaternion, aiCreateQuaternionFromMatrixTest) {
     // to prevent running into division by zero.
     aiMatrix3x3 m, r;
     aiMatrix3x3::Translation(aiVector2D(14,-25), m);
-    aiMatrix3x3::RotationZ(Math::PI<float>() / 4.0f, r);
+    aiMatrix3x3::RotationZ(Math::aiPi<float>() / 4.0f, r);
     m = m * r;
 
     result_cpp = aiQuaternion(m);
@@ -127,8 +127,8 @@ TEST_F(AssimpAPITest_aiQuaternion, aiQuaternionInterpolateTest) {
     // Use predetermined quaternions to prevent division by zero
     // during slerp calculations.
     const float INTERPOLATION(0.5f);
-    const auto q1 = aiQuaternion(aiVector3D(-1,1,1).Normalize(), Math::PI<float>() / 4.0f);
-    const auto q2 = aiQuaternion(aiVector3D(1,2,1).Normalize(), Math::PI<float>() / 2.0f);
+    const auto q1 = aiQuaternion(aiVector3D(-1,1,1).Normalize(), Math::aiPi<float>() / 4.0f);
+    const auto q2 = aiQuaternion(aiVector3D(1,2,1).Normalize(), Math::aiPi<float>() / 2.0f);
     aiQuaternion::Interpolate(result_cpp, q1, q2, INTERPOLATION);
     aiQuaternionInterpolate(&result_c, &q1, &q2, INTERPOLATION);
     EXPECT_EQ(result_cpp, result_c);

+ 1 - 1
test/unit/MathTest.cpp

@@ -51,6 +51,6 @@ const float AssimpMathTest::Epsilon = Math::getEpsilon<float>();
 RandomUniformFloatGenerator AssimpMathTest::RandNonZero(1.0f, 100.0f);
 
 // Initialize with an interval of [-PI,PI] inclusively.
-RandomUniformFloatGenerator AssimpMathTest::RandPI(-Math::PI<float>(), Math::PI<float>());
+RandomUniformFloatGenerator AssimpMathTest::RandPI(-Math::aiPi<float>(), Math::aiPi<float>());
 
 }

+ 1 - 1
test/unit/utVersion.cpp

@@ -48,7 +48,7 @@ TEST_F( utVersion, aiGetLegalStringTest ) {
     EXPECT_NE( lv, nullptr );
     std::string text( lv );
 
-    size_t pos( text.find( std::string( "2020" ) ) );
+    size_t pos( text.find( std::string( "2021" ) ) );
     EXPECT_NE( pos, std::string::npos );
 }
 

+ 1 - 1
tools/assimp_view/LogWindow.cpp

@@ -105,7 +105,7 @@ void CLogWindow::Init() {
 
     // setup the log text
     this->szText = AI_VIEW_RTF_LOG_HEADER;
-    ;
+
     this->szPlainText = "";
 }