Kaynağa Gözat

Merge branch 'master' into msvc-clang-unused-function

Kim Kulling 2 yıl önce
ebeveyn
işleme
f43cfa1fba
60 değiştirilmiş dosya ile 1133 ekleme ve 844 silme
  1. 0 4
      code/AssetLib/3DS/3DSHelper.h
  2. 0 16
      code/AssetLib/ASE/ASELoader.cpp
  3. 4 6
      code/AssetLib/FBX/FBXBinaryTokenizer.cpp
  4. 19 11
      code/AssetLib/FBX/FBXDocument.cpp
  5. 6 2
      code/AssetLib/FBX/FBXDocument.h
  6. 11 9
      code/AssetLib/FBX/FBXImporter.cpp
  7. 30 12
      code/AssetLib/FBX/FBXParser.cpp
  8. 21 15
      code/AssetLib/FBX/FBXParser.h
  9. 10 10
      code/AssetLib/FBX/FBXTokenizer.cpp
  10. 5 3
      code/AssetLib/FBX/FBXTokenizer.h
  11. 11 0
      code/AssetLib/FBX/FBXUtil.h
  12. 2 1
      code/AssetLib/IFC/IFCUtil.cpp
  13. 2 4
      code/AssetLib/LWO/LWOLoader.cpp
  14. 1 1
      code/AssetLib/X/XFileImporter.cpp
  15. 0 2
      code/AssetLib/X3D/X3DExporter.hpp
  16. 24 7
      code/AssetLib/glTF2/glTF2Asset.h
  17. 20 0
      code/AssetLib/glTF2/glTF2Asset.inl
  18. 1 0
      code/AssetLib/glTF2/glTF2AssetWriter.h
  19. 26 2
      code/AssetLib/glTF2/glTF2AssetWriter.inl
  20. 28 5
      code/AssetLib/glTF2/glTF2Exporter.cpp
  21. 2 0
      code/AssetLib/glTF2/glTF2Exporter.h
  22. 12 1
      code/AssetLib/glTF2/glTF2Importer.cpp
  23. 3 1
      code/CMakeLists.txt
  24. 1 1
      code/Common/Exporter.cpp
  25. 3 0
      code/Common/SceneCombiner.cpp
  26. 92 0
      code/Common/StackAllocator.h
  27. 82 0
      code/Common/StackAllocator.inl
  28. 35 11
      code/Geometry/GeometryUtils.cpp
  29. 16 2
      code/Geometry/GeometryUtils.h
  30. 21 6
      code/Pbrt/PbrtExporter.cpp
  31. 3 0
      code/Pbrt/PbrtExporter.h
  32. 12 24
      code/PostProcessing/ArmaturePopulate.cpp
  33. 3 7
      code/PostProcessing/ArmaturePopulate.h
  34. 135 181
      code/PostProcessing/ComputeUVMappingProcess.cpp
  35. 0 9
      code/PostProcessing/ConvertToLHProcess.cpp
  36. 4 8
      code/PostProcessing/DeboneProcess.cpp
  37. 12 13
      code/PostProcessing/DropFaceNormalsProcess.cpp
  38. 40 43
      code/PostProcessing/FindDegenerates.cpp
  39. 151 140
      code/PostProcessing/PretransformVertices.cpp
  40. 9 9
      code/PostProcessing/PretransformVertices.h
  41. 3 4
      code/PostProcessing/ProcessHelper.cpp
  42. 9 24
      code/PostProcessing/RemoveRedundantMaterials.cpp
  43. 0 59
      code/PostProcessing/RemoveVCProcess.cpp
  44. 4 5
      code/PostProcessing/ScaleProcess.cpp
  45. 11 13
      code/PostProcessing/SortByPTypeProcess.cpp
  46. 9 13
      code/PostProcessing/SplitByBoneCountProcess.cpp
  47. 11 2
      code/PostProcessing/SplitByBoneCountProcess.h
  48. 15 19
      code/PostProcessing/SplitLargeMeshes.cpp
  49. 2 4
      code/PostProcessing/TextureTransform.cpp
  50. 4 41
      code/PostProcessing/TriangulateProcess.cpp
  51. 37 50
      code/PostProcessing/ValidateDataStructure.cpp
  52. 8 0
      fuzz/assimp_fuzzer.cc
  53. 1 4
      include/assimp/StreamWriter.h
  54. 12 15
      include/assimp/Vertex.h
  55. 11 0
      include/assimp/config.h.in
  56. 5 1
      include/assimp/defs.h
  57. 12 6
      test/CMakeLists.txt
  58. 68 0
      test/unit/Geometry/utGeometryUtils.cpp
  59. 47 17
      test/unit/utExport.cpp
  60. 7 1
      test/unit/utglTF2ImportExport.cpp

+ 0 - 4
code/AssetLib/3DS/3DSHelper.h

@@ -397,10 +397,6 @@ struct Material {
 
     Material(const Material &other) = default;
 
-    Material(Material &&other) AI_NO_EXCEPT = default;
-
-    Material &operator=(Material &&other) AI_NO_EXCEPT = default;
-
     virtual ~Material() = default;
 
     //! Name of the material

+ 0 - 16
code/AssetLib/ASE/ASELoader.cpp

@@ -44,7 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
-
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
 // internal headers
@@ -322,21 +321,6 @@ void ASEImporter::BuildAnimations(const std::vector<BaseNode *> &nodes) {
                 aiNodeAnim *nd = pcAnim->mChannels[iNum++] = new aiNodeAnim();
                 nd->mNodeName.Set(me->mName + ".Target");
 
-                // If there is no input position channel we will need
-                // to supply the default position from the node's
-                // local transformation matrix.
-                /*TargetAnimationHelper helper;
-                if (me->mAnim.akeyPositions.empty())
-                {
-                    aiMatrix4x4& mat = (*i)->mTransform;
-                    helper.SetFixedMainAnimationChannel(aiVector3D(
-                        mat.a4, mat.b4, mat.c4));
-                }
-                else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions);
-                helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions);
-
-                helper.Process(&me->mTargetAnim.akeyPositions);*/
-
                 // Allocate the key array and fill it
                 nd->mNumPositionKeys = (unsigned int)me->mTargetAnim.akeyPositions.size();
                 nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];

+ 4 - 6
code/AssetLib/FBX/FBXBinaryTokenizer.cpp

@@ -342,8 +342,7 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input,
 
 
 // ------------------------------------------------------------------------------------------------
-bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor, const char* end, bool const is64bits)
-{
+bool ReadScope(TokenList &output_tokens, StackAllocator &token_allocator, const char *input, const char *&cursor, const char *end, bool const is64bits) {
     // the first word contains the offset at which this block ends
 	const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
 
@@ -409,7 +408,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
 
         // XXX this is vulnerable to stack overflowing ..
         while(Offset(input, cursor) < end_offset - sentinel_block_length) {
-			ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits);
+            ReadScope(output_tokens, token_allocator, input, cursor, input + end_offset - sentinel_block_length, is64bits);
         }
         output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor) ));
 
@@ -432,8 +431,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
 
 // ------------------------------------------------------------------------------------------------
 // TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
-void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
-{
+void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, StackAllocator &token_allocator) {
 	ai_assert(input);
 	ASSIMP_LOG_DEBUG("Tokenizing binary FBX file");
 
@@ -466,7 +464,7 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
     try
     {
         while (cursor < end ) {
-		    if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
+            if (!ReadScope(output_tokens, token_allocator, input, cursor, input + length, is64bits)) {
                 break;
             }
         }

+ 19 - 11
code/AssetLib/FBX/FBXDocument.cpp

@@ -243,7 +243,7 @@ FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr<cons
 }
 
 // ------------------------------------------------------------------------------------------------
-Document::Document(const Parser& parser, const ImportSettings& settings) :
+Document::Document(Parser& parser, const ImportSettings& settings) :
      settings(settings), parser(parser) {
 	ASSIMP_LOG_DEBUG("Creating FBX Document");
 
@@ -265,13 +265,17 @@ Document::Document(const Parser& parser, const ImportSettings& settings) :
 }
 
 // ------------------------------------------------------------------------------------------------
-Document::~Document() {
-    for(ObjectMap::value_type& v : objects) {
-        delete v.second;
+Document::~Document()
+{
+	// The document does not own the memory for the following objects, but we need to call their d'tor
+	// so they can properly free memory like string members:
+	
+    for (ObjectMap::value_type &v : objects) {
+        delete_LazyObject(v.second);
     }
 
-    for(ConnectionMap::value_type& v : src_connections) {
-        delete v.second;
+    for (ConnectionMap::value_type &v : src_connections) {
+        delete_Connection(v.second);
     }
     // |dest_connections| contain the same Connection objects as the |src_connections|
 }
@@ -356,9 +360,11 @@ void Document::ReadObjects() {
         DOMError("no Objects dictionary found");
     }
 
+    StackAllocator &allocator = parser.GetAllocator();
+
     // add a dummy entry to represent the Model::RootNode object (id 0),
     // which is only indirectly defined in the input file
-    objects[0] = new LazyObject(0L, *eobjects, *this);
+    objects[0] = new_LazyObject(0L, *eobjects, *this);
 
     const Scope& sobjects = *eobjects->Compound();
     for(const ElementMap::value_type& el : sobjects.Elements()) {
@@ -387,7 +393,7 @@ void Document::ReadObjects() {
             delete foundObject->second;
         }
 
-        objects[id] = new LazyObject(id, *el.second, *this);
+        objects[id] = new_LazyObject(id, *el.second, *this);
 
         // grab all animation stacks upfront since there is no listing of them
         if(!strcmp(el.first.c_str(),"AnimationStack")) {
@@ -454,8 +460,10 @@ void Document::ReadPropertyTemplates() {
 }
 
 // ------------------------------------------------------------------------------------------------
-void Document::ReadConnections() {
-    const Scope& sc = parser.GetRootScope();
+void Document::ReadConnections()
+{
+    StackAllocator &allocator = parser.GetAllocator();
+    const Scope &sc = parser.GetRootScope();
     // read property templates from "Definitions" section
     const Element* const econns = sc["Connections"];
     if(!econns || !econns->Compound()) {
@@ -494,7 +502,7 @@ void Document::ReadConnections() {
         }
 
         // add new connection
-        const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this);
+        const Connection* const c = new_Connection(insertionOrder++,src,dest,prop,*this);
         src_connections.insert(ConnectionMap::value_type(src,c));
         dest_connections.insert(ConnectionMap::value_type(dest,c));
     }

+ 6 - 2
code/AssetLib/FBX/FBXDocument.h

@@ -81,6 +81,10 @@ class BlendShape;
 class Skin;
 class Cluster;
 
+#define new_LazyObject new (allocator.Allocate(sizeof(LazyObject))) LazyObject
+#define new_Connection new (allocator.Allocate(sizeof(Connection))) Connection
+#define delete_LazyObject(_p) (_p)->~LazyObject()
+#define delete_Connection(_p) (_p)->~Connection()
 
 /** Represents a delay-parsed FBX objects. Many objects in the scene
  *  are not needed by assimp, so it makes no sense to parse them
@@ -1073,7 +1077,7 @@ private:
 /** DOM root for a FBX file */
 class Document {
 public:
-    Document(const Parser& parser, const ImportSettings& settings);
+    Document(Parser& parser, const ImportSettings& settings);
 
     ~Document();
 
@@ -1157,7 +1161,7 @@ private:
     const ImportSettings& settings;
 
     ObjectMap objects;
-    const Parser& parser;
+    Parser& parser;
 
     PropertyTemplateMap templates;
     ConnectionMap src_connections;

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

@@ -152,19 +152,19 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 	// broad-phase tokenized pass in which we identify the core
 	// syntax elements of FBX (brackets, commas, key:value mappings)
 	TokenList tokens;
-	try {
-
+    Assimp::StackAllocator tempAllocator;
+    try {
 		bool is_binary = false;
 		if (!strncmp(begin, "Kaydara FBX Binary", 18)) {
 			is_binary = true;
-			TokenizeBinary(tokens, begin, contents.size());
+            TokenizeBinary(tokens, begin, contents.size(), tempAllocator);
 		} else {
-			Tokenize(tokens, begin);
+            Tokenize(tokens, begin, tempAllocator);
 		}
 
 		// use this information to construct a very rudimentary
 		// parse-tree representing the FBX scope structure
-		Parser parser(tokens, is_binary);
+        Parser parser(tokens, tempAllocator, is_binary);
 
 		// take the raw parse-tree and convert it to a FBX DOM
 		Document doc(parser, mSettings);
@@ -183,10 +183,12 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 		// assimp universal format (M)
 		SetFileScale(size_relative_to_cm * 0.01f);
 
-		std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
-	} catch (std::exception &) {
-		std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
-		throw;
+		// This collection does not own the memory for the tokens, but we need to call their d'tor
+        std::for_each(tokens.begin(), tokens.end(), Util::destructor_fun<Token>());
+
+    } catch (std::exception &) {
+        std::for_each(tokens.begin(), tokens.end(), Util::destructor_fun<Token>());
+        throw;
 	}
 }
 

+ 30 - 12
code/AssetLib/FBX/FBXParser.cpp

@@ -116,8 +116,11 @@ namespace Assimp {
 namespace FBX {
 
 // ------------------------------------------------------------------------------------------------
-Element::Element(const Token& key_token, Parser& parser) : key_token(key_token) {
+Element::Element(const Token& key_token, Parser& parser) :
+    key_token(key_token), compound(nullptr)
+{
     TokenPtr n = nullptr;
+    StackAllocator &allocator = parser.GetAllocator();
     do {
         n = parser.AdvanceToNextToken();
         if(!n) {
@@ -146,7 +149,7 @@ Element::Element(const Token& key_token, Parser& parser) : key_token(key_token)
         }
 
         if (n->Type() == TokenType_OPEN_BRACKET) {
-            compound.reset(new Scope(parser));
+            compound = new_Scope(parser);
 
             // current token should be a TOK_CLOSE_BRACKET
             n = parser.CurrentToken();
@@ -164,6 +167,15 @@ Element::Element(const Token& key_token, Parser& parser) : key_token(key_token)
 }
 
 // ------------------------------------------------------------------------------------------------
+Element::~Element()
+{
+    if (compound) {
+        delete_Scope(compound);
+    }
+
+     // no need to delete tokens, they are owned by the parser
+}
+
 Scope::Scope(Parser& parser,bool topLevel)
 {
     if(!topLevel) {
@@ -173,6 +185,7 @@ Scope::Scope(Parser& parser,bool topLevel)
         }
     }
 
+    StackAllocator &allocator = parser.GetAllocator();
     TokenPtr n = parser.AdvanceToNextToken();
     if (n == nullptr) {
         ParseError("unexpected end of file");
@@ -207,22 +220,27 @@ Scope::Scope(Parser& parser,bool topLevel)
 }
 
 // ------------------------------------------------------------------------------------------------
-Scope::~Scope() {
-    for(ElementMap::value_type& v : elements) {
-        delete v.second;
+Scope::~Scope()
+{
+	// This collection does not own the memory for the elements, but we need to call their d'tor:
+
+    for (ElementMap::value_type &v : elements) {
+        delete_Element(v.second);
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-Parser::Parser (const TokenList& tokens, bool is_binary)
-: tokens(tokens)
-, last()
-, current()
-, cursor(tokens.begin())
-, is_binary(is_binary)
+Parser::Parser(const TokenList &tokens, StackAllocator &allocator, bool is_binary) :
+        tokens(tokens), allocator(allocator), last(), current(), cursor(tokens.begin()), is_binary(is_binary)
 {
     ASSIMP_LOG_DEBUG("Parsing FBX tokens");
-    root.reset(new Scope(*this,true));
+    root = new_Scope(*this, true);
+}
+
+// ------------------------------------------------------------------------------------------------
+Parser::~Parser()
+{
+    delete_Scope(root);
 }
 
 // ------------------------------------------------------------------------------------------------

+ 21 - 15
code/AssetLib/FBX/FBXParser.h

@@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/LogAux.h>
 #include <assimp/fast_atof.h>
 
+#include "Common/StackAllocator.h"
 #include "FBXCompileConfig.h"
 #include "FBXTokenizer.h"
 
@@ -63,14 +64,14 @@ class Parser;
 class Element;
 
 // XXX should use C++11's unique_ptr - but assimp's need to keep working with 03
-typedef std::vector< Scope* > ScopeList;
-typedef std::fbx_unordered_multimap< std::string, Element* > ElementMap;
-
-typedef std::pair<ElementMap::const_iterator,ElementMap::const_iterator> ElementCollection;
-
-#   define new_Scope new Scope
-#   define new_Element new Element
+using ScopeList = std::vector<Scope*>;
+using ElementMap = std::fbx_unordered_multimap< std::string, Element*>;
+using ElementCollection = std::pair<ElementMap::const_iterator,ElementMap::const_iterator>;
 
+#define new_Scope new (allocator.Allocate(sizeof(Scope))) Scope
+#define new_Element new (allocator.Allocate(sizeof(Element))) Element
+#define delete_Scope(_p) (_p)->~Scope()
+#define delete_Element(_p) (_p)->~Element()
 
 /** FBX data entity that consists of a key:value tuple.
  *
@@ -82,15 +83,16 @@ typedef std::pair<ElementMap::const_iterator,ElementMap::const_iterator> Element
  *  @endverbatim
  *
  *  As can be seen in this sample, elements can contain nested #Scope
- *  as their trailing member.  **/
+ *  as their trailing member.  
+**/
 class Element
 {
 public:
     Element(const Token& key_token, Parser& parser);
-    ~Element() = default;
+    ~Element();
 
     const Scope* Compound() const {
-        return compound.get();
+        return compound;
     }
 
     const Token& KeyToken() const {
@@ -104,7 +106,7 @@ public:
 private:
     const Token& key_token;
     TokenList tokens;
-    std::unique_ptr<Scope> compound;
+    Scope* compound;
 };
 
 /** FBX data entity that consists of a 'scope', a collection
@@ -159,8 +161,8 @@ class Parser
 public:
     /** Parse given a token list. Does not take ownership of the tokens -
      *  the objects must persist during the entire parser lifetime */
-    Parser (const TokenList& tokens,bool is_binary);
-    ~Parser() = default;
+    Parser(const TokenList &tokens, StackAllocator &allocator, bool is_binary);
+    ~Parser();
 
     const Scope& GetRootScope() const {
         return *root;
@@ -170,6 +172,10 @@ public:
         return is_binary;
     }
 
+    StackAllocator &GetAllocator() {
+        return allocator;
+    }
+
 private:
     friend class Scope;
     friend class Element;
@@ -180,10 +186,10 @@ private:
 
 private:
     const TokenList& tokens;
-
+    StackAllocator &allocator;
     TokenPtr last, current;
     TokenList::const_iterator cursor;
-    std::unique_ptr<Scope> root;
+    Scope *root;
 
     const bool is_binary;
 };

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

@@ -94,7 +94,8 @@ AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line,
 
 // process a potential data token up to 'cur', adding it to 'output_tokens'.
 // ------------------------------------------------------------------------------------------------
-void ProcessDataToken( TokenList& output_tokens, const char*& start, const char*& end,
+void ProcessDataToken(TokenList &output_tokens, StackAllocator &token_allocator,
+                      const char*& start, const char*& end,
                       unsigned int line,
                       unsigned int column,
                       TokenType type = TokenType_DATA,
@@ -131,8 +132,7 @@ void ProcessDataToken( TokenList& output_tokens, const char*& start, const char*
 }
 
 // ------------------------------------------------------------------------------------------------
-void Tokenize(TokenList& output_tokens, const char* input)
-{
+void Tokenize(TokenList &output_tokens, const char *input, StackAllocator &token_allocator) {
 	ai_assert(input);
 	ASSIMP_LOG_DEBUG("Tokenizing ASCII FBX file");
 
@@ -164,7 +164,7 @@ void Tokenize(TokenList& output_tokens, const char* input)
                 in_double_quotes = false;
                 token_end = cur;
 
-                ProcessDataToken(output_tokens,token_begin,token_end,line,column);
+                ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
                 pending_data_token = false;
             }
             continue;
@@ -181,30 +181,30 @@ void Tokenize(TokenList& output_tokens, const char* input)
             continue;
 
         case ';':
-            ProcessDataToken(output_tokens,token_begin,token_end,line,column);
+            ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
             comment = true;
             continue;
 
         case '{':
-            ProcessDataToken(output_tokens,token_begin,token_end, line, column);
+            ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
             output_tokens.push_back(new_Token(cur,cur+1,TokenType_OPEN_BRACKET,line,column));
             continue;
 
         case '}':
-            ProcessDataToken(output_tokens,token_begin,token_end,line,column);
+            ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
             output_tokens.push_back(new_Token(cur,cur+1,TokenType_CLOSE_BRACKET,line,column));
             continue;
 
         case ',':
             if (pending_data_token) {
-                ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_DATA,true);
+                ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column, TokenType_DATA, true);
             }
             output_tokens.push_back(new_Token(cur,cur+1,TokenType_COMMA,line,column));
             continue;
 
         case ':':
             if (pending_data_token) {
-                ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_KEY,true);
+                ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column, TokenType_KEY, true);
             }
             else {
                 TokenizeError("unexpected colon", line, column);
@@ -226,7 +226,7 @@ void Tokenize(TokenList& output_tokens, const char* input)
                     }
                 }
 
-                ProcessDataToken(output_tokens,token_begin,token_end,line,column,type);
+                ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column, type);
             }
 
             pending_data_token = false;

+ 5 - 3
code/AssetLib/FBX/FBXTokenizer.h

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define INCLUDED_AI_FBX_TOKENIZER_H
 
 #include "FBXCompileConfig.h"
+#include "Common/StackAllocator.h"
 #include <assimp/ai_assert.h>
 #include <assimp/defs.h>
 #include <vector>
@@ -157,7 +158,8 @@ private:
 typedef const Token* TokenPtr;
 typedef std::vector< TokenPtr > TokenList;
 
-#define new_Token new Token
+#define new_Token new (token_allocator.Allocate(sizeof(Token))) Token
+#define delete_Token(_p) (_p)->~Token()
 
 
 /** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens.
@@ -167,7 +169,7 @@ typedef std::vector< TokenPtr > TokenList;
  * @param output_tokens Receives a list of all tokens in the input data.
  * @param input_buffer Textual input buffer to be processed, 0-terminated.
  * @throw DeadlyImportError if something goes wrong */
-void Tokenize(TokenList& output_tokens, const char* input);
+void Tokenize(TokenList &output_tokens, const char *input, StackAllocator &tokenAllocator);
 
 
 /** Tokenizer function for binary FBX files.
@@ -178,7 +180,7 @@ void Tokenize(TokenList& output_tokens, const char* input);
  * @param input_buffer Binary input buffer to be processed.
  * @param length Length of input buffer, in bytes. There is no 0-terminal.
  * @throw DeadlyImportError if something goes wrong */
-void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length);
+void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, StackAllocator &tokenAllocator);
 
 
 } // ! FBX

+ 11 - 0
code/AssetLib/FBX/FBXUtil.h

@@ -66,6 +66,17 @@ struct delete_fun
     }
 };
 
+/** helper for std::for_each to call the destructor on all items in a container without freeing their heap*/
+template <typename T>
+struct destructor_fun {
+    void operator()(const volatile T* del) {
+        if (del) {
+            del->~T();
+        }
+    }
+};
+
+
 /** Get a string representation for a #TokenType. */
 const char* TokenTypeString(TokenType t);
 

+ 2 - 1
code/AssetLib/IFC/IFCUtil.cpp

@@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "AssetLib/IFC/IFCUtil.h"
 #include "Common/PolyTools.h"
+#include "Geometry/GeometryUtils.h"
 #include "PostProcessing/ProcessHelper.h"
 
 namespace Assimp {
@@ -235,7 +236,7 @@ IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const {
 struct CompareVector {
     bool operator () (const IfcVector3& a, const IfcVector3& b) const {
         IfcVector3 d = a - b;
-        IfcFloat eps = ai_epsilon;
+        constexpr IfcFloat eps = ai_epsilon;
         return d.x < -eps || (std::abs(d.x) < eps && d.y < -eps) || (std::abs(d.x) < eps && std::abs(d.y) < eps && d.z < -eps);
     }
 };

+ 2 - 4
code/AssetLib/LWO/LWOLoader.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -51,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "AssetLib/LWO/LWOLoader.h"
 #include "PostProcessing/ConvertToLHProcess.h"
 #include "PostProcessing/ProcessHelper.h"
+#include "Geometry/GeometryUtils.h"
 
 #include <assimp/ByteSwapper.h>
 #include <assimp/SGSpatialSort.h>
@@ -528,7 +527,6 @@ void LWOImporter::ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &
                         continue;
                     vNormals += v;
                 }
-                mesh->mNormals[idx] = vNormals.Normalize();
             }
         }
     }
@@ -549,7 +547,6 @@ void LWOImporter::ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &
                     const aiVector3D &v = faceNormals[*a];
                     vNormals += v;
                 }
-                vNormals.Normalize();
                 for (std::vector<unsigned int>::const_iterator a = poResult.begin(); a != poResult.end(); ++a) {
                     mesh->mNormals[*a] = vNormals;
                     vertexDone[*a] = true;
@@ -557,6 +554,7 @@ void LWOImporter::ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &
             }
         }
     }
+    GeometryUtils::normalizeVectorArray(mesh->mNormals, mesh->mNormals, mesh->mNumVertices);
 }
 
 // ------------------------------------------------------------------------------------------------

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

@@ -578,7 +578,7 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector<XFile::Materi
                 aiString name;
                 pScene->mMaterials[b]->Get( AI_MATKEY_NAME, name);
                 if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) {
-                    oldMat.sceneIndex = a;
+                    oldMat.sceneIndex = b;
                     break;
                 }
             }

+ 0 - 2
code/AssetLib/X3D/X3DExporter.hpp

@@ -58,8 +58,6 @@ class X3DExporter {
                 Value(value) {
             // empty
         }
-
-        SAttribute(SAttribute &&rhs) AI_NO_EXCEPT = default;
     };
 
     /***********************************************/

+ 24 - 7
code/AssetLib/glTF2/glTF2Asset.h

@@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * glTF Extensions Support:
  *   KHR_materials_pbrSpecularGlossiness full
+ *   KHR_materials_specular full
  *   KHR_materials_unlit full
  *   KHR_lights_punctual full
  *   KHR_materials_sheen full
@@ -710,6 +711,7 @@ const vec4 defaultBaseColor = { 1, 1, 1, 1 };
 const vec3 defaultEmissiveFactor = { 0, 0, 0 };
 const vec4 defaultDiffuseFactor = { 1, 1, 1, 1 };
 const vec3 defaultSpecularFactor = { 1, 1, 1 };
+const vec3 defaultSpecularColorFactor = { 0, 0, 0 };
 const vec3 defaultSheenFactor = { 0, 0, 0 };
 const vec3 defaultAttenuationColor = { 1, 1, 1 };
 
@@ -753,6 +755,16 @@ struct PbrSpecularGlossiness {
     void SetDefaults();
 };
 
+struct MaterialSpecular {
+    float specularFactor;
+    vec3 specularColorFactor;
+    TextureInfo specularTexture;
+    TextureInfo specularColorTexture;
+
+    MaterialSpecular() { SetDefaults(); }
+    void SetDefaults();
+};
+
 struct MaterialSheen {
     vec3 sheenColorFactor;
     float sheenRoughnessFactor;
@@ -817,6 +829,9 @@ struct Material : public Object {
     //extension: KHR_materials_pbrSpecularGlossiness
     Nullable<PbrSpecularGlossiness> pbrSpecularGlossiness;
 
+    //extension: KHR_materials_specular
+    Nullable<MaterialSpecular> materialSpecular;
+
     //extension: KHR_materials_sheen
     Nullable<MaterialSheen> materialSheen;
 
@@ -1099,6 +1114,7 @@ public:
     //! Keeps info about the enabled extensions
     struct Extensions {
         bool KHR_materials_pbrSpecularGlossiness;
+        bool KHR_materials_specular;
         bool KHR_materials_unlit;
         bool KHR_lights_punctual;
         bool KHR_texture_transform;
@@ -1113,13 +1129,14 @@ public:
         bool KHR_texture_basisu;
 
         Extensions() :
-                KHR_materials_pbrSpecularGlossiness(false),
-                KHR_materials_unlit(false),
-                KHR_lights_punctual(false),
-                KHR_texture_transform(false),
-                KHR_materials_sheen(false),
-                KHR_materials_clearcoat(false),
-                KHR_materials_transmission(false),
+                KHR_materials_pbrSpecularGlossiness(false), 
+                KHR_materials_specular(false), 
+                KHR_materials_unlit(false), 
+                KHR_lights_punctual(false), 
+                KHR_texture_transform(false), 
+                KHR_materials_sheen(false), 
+                KHR_materials_clearcoat(false), 
+                KHR_materials_transmission(false), 
                 KHR_materials_volume(false),
                 KHR_materials_ior(false),
                 KHR_materials_emissive_strength(false),

+ 20 - 0
code/AssetLib/glTF2/glTF2Asset.inl

@@ -1263,6 +1263,19 @@ inline void Material::Read(Value &material, Asset &r) {
                 this->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG);
             }
         }
+        
+        if (r.extensionsUsed.KHR_materials_specular) {
+            if (Value *curMatSpecular = FindObject(*extensions, "KHR_materials_specular")) {
+                MaterialSpecular specular;
+
+                ReadMember(*curMatSpecular, "specularFactor", specular.specularFactor);
+                ReadTextureProperty(r, *curMatSpecular, "specularTexture", specular.specularTexture);
+                ReadMember(*curMatSpecular, "specularColorFactor", specular.specularColorFactor);
+                ReadTextureProperty(r, *curMatSpecular, "specularColorTexture", specular.specularColorTexture);
+
+                this->materialSpecular = Nullable<MaterialSpecular>(specular);
+            }
+        }
 
         // Extension KHR_texture_transform is handled in ReadTextureProperty
 
@@ -1361,6 +1374,12 @@ inline void PbrSpecularGlossiness::SetDefaults() {
     glossinessFactor = 1.0f;
 }
 
+inline void MaterialSpecular::SetDefaults() {
+    //KHR_materials_specular properties
+    SetVector(specularColorFactor, defaultSpecularColorFactor);
+    specularFactor = 0.f;
+}
+
 inline void MaterialSheen::SetDefaults() {
     //KHR_materials_sheen properties
     SetVector(sheenColorFactor, defaultSheenFactor);
@@ -2047,6 +2066,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
     }
 
     CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
+    CHECK_EXT(KHR_materials_specular);
     CHECK_EXT(KHR_materials_unlit);
     CHECK_EXT(KHR_lights_punctual);
     CHECK_EXT(KHR_texture_transform);

+ 1 - 0
code/AssetLib/glTF2/glTF2AssetWriter.h

@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * glTF Extensions Support:
  *   KHR_materials_pbrSpecularGlossiness: full
+ *   KHR_materials_specular: full
  *   KHR_materials_unlit: full
  *   KHR_materials_sheen: full
  *   KHR_materials_clearcoat: full

+ 26 - 2
code/AssetLib/glTF2/glTF2AssetWriter.inl

@@ -418,6 +418,26 @@ namespace glTF2 {
           exts.AddMember("KHR_materials_unlit", unlit, w.mAl);
         }
 
+        if (m.materialSpecular.isPresent) {
+            Value materialSpecular(rapidjson::Type::kObjectType);
+            materialSpecular.SetObject();
+
+            MaterialSpecular &specular = m.materialSpecular.value;
+
+            if (specular.specularFactor != 0.0f) {
+                WriteFloat(materialSpecular, specular.specularFactor, "specularFactor", w.mAl);
+                WriteTex(materialSpecular, specular.specularTexture, "specularTexture", w.mAl);
+            }
+            if (specular.specularColorFactor[0] != defaultSpecularColorFactor[0] && specular.specularColorFactor[1] != defaultSpecularColorFactor[1] && specular.specularColorFactor[2] != defaultSpecularColorFactor[2]) {
+                WriteVec(materialSpecular, specular.specularColorFactor, "specularColorFactor", w.mAl);
+                WriteTex(materialSpecular, specular.specularColorTexture, "specularColorTexture", w.mAl);
+            }
+
+            if (!materialSpecular.ObjectEmpty()) {
+                exts.AddMember("KHR_materials_specular", materialSpecular, w.mAl);
+            }
+        }
+
         if (m.materialSheen.isPresent) {
             Value materialSheen(rapidjson::Type::kObjectType);
 
@@ -550,7 +570,7 @@ namespace glTF2 {
 
     inline void Write(Value& obj, Mesh& m, AssetWriter& w)
     {
-		/****************** Primitives *******************/
+        /****************** Primitives *******************/
         Value primitives;
         primitives.SetArray();
         primitives.Reserve(unsigned(m.primitives.size()), w.mAl);
@@ -929,6 +949,10 @@ namespace glTF2 {
               exts.PushBack(StringRef("KHR_materials_unlit"), mAl);
             }
 
+            if (this->mAsset.extensionsUsed.KHR_materials_specular) {
+                exts.PushBack(StringRef("KHR_materials_specular"), mAl);
+            }
+
             if (this->mAsset.extensionsUsed.KHR_materials_sheen) {
                 exts.PushBack(StringRef("KHR_materials_sheen"), mAl);
             }
@@ -980,7 +1004,7 @@ namespace glTF2 {
         if (d.mObjs.empty()) return;
 
         Value* container = &mDoc;
-		const char* context = "Document";
+        const char* context = "Document";
 
         if (d.mExtId) {
             Value* exts = FindObject(mDoc, "extensions");

+ 28 - 5
code/AssetLib/glTF2/glTF2Exporter.cpp

@@ -640,11 +640,10 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial &mat, vec3 &prop, const cha
     return result;
 }
 
+// This extension has been deprecated, only export with the specific flag enabled, defaults to false. Uses KHR_material_specular default.
 bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) {
     bool result = false;
     // If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension
-    // NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular
-
     if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) {
         result = true;
     } else {
@@ -674,6 +673,25 @@ bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlo
     return result;
 }
 
+bool glTF2Exporter::GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecular &specular) {
+    // Specular requires either/or, default factors of zero disables specular, so do not export
+    if (GetMatColor(mat, specular.specularColorFactor, AI_MATKEY_COLOR_SPECULAR) != AI_SUCCESS || mat.Get(AI_MATKEY_SPECULAR_FACTOR, specular.specularFactor) != AI_SUCCESS) {
+        return false;
+    }
+    // The spec states that the default is 1.0 and [1.0, 1.0, 1.0]. We if both are 0, which should disable specular. Otherwise, if one is 0, set to 1.0
+    const bool colorFactorIsZero = specular.specularColorFactor[0] == defaultSpecularColorFactor[0] && specular.specularColorFactor[1] == defaultSpecularColorFactor[1] && specular.specularColorFactor[2] == defaultSpecularColorFactor[2];
+    if (specular.specularFactor == 0.0f && colorFactorIsZero) {
+        return false;
+    } else if (specular.specularFactor == 0.0f) {
+        specular.specularFactor = 1.0f;
+    } else if (colorFactorIsZero) {
+        specular.specularColorFactor[0] = specular.specularColorFactor[1] = specular.specularColorFactor[2] = 1.0f;
+    }
+    GetMatTex(mat, specular.specularColorTexture, aiTextureType_SPECULAR);
+    GetMatTex(mat, specular.specularTexture, aiTextureType_SPECULAR);
+    return true;
+}
+
 bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) {
     // Return true if got any valid Sheen properties or textures
     if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) {
@@ -818,9 +836,9 @@ void glTF2Exporter::ExportMaterials() {
             m->alphaMode = alphaMode.C_Str();
         }
 
-        {
+        // This extension has been deprecated, only export with the specific flag enabled, defaults to false. Uses KHR_material_specular default.
+        if (mProperties->GetPropertyBool(AI_CONFIG_USE_GLTF_PBR_SPECULAR_GLOSSINESS)) {
             // KHR_materials_pbrSpecularGlossiness extension
-            // NOTE: This extension is being considered for deprecation (Dec 2020)
             PbrSpecularGlossiness pbrSG;
             if (GetMatSpecGloss(mat, pbrSG)) {
                 mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true;
@@ -837,7 +855,12 @@ void glTF2Exporter::ExportMaterials() {
         } else {
             // These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness
             if (!m->pbrSpecularGlossiness.isPresent) {
-                // Sheen
+                MaterialSpecular specular;
+                if (GetMatSpecular(mat, specular)) {
+                    mAsset->extensionsUsed.KHR_materials_specular = true;
+                    m->materialSpecular = Nullable<MaterialSpecular>(specular);
+                }
+
                 MaterialSheen sheen;
                 if (GetMatSheen(mat, sheen)) {
                     mAsset->extensionsUsed.KHR_materials_sheen = true;

+ 2 - 0
code/AssetLib/glTF2/glTF2Exporter.h

@@ -76,6 +76,7 @@ struct OcclusionTextureInfo;
 struct Node;
 struct Texture;
 struct PbrSpecularGlossiness;
+struct MaterialSpecular;
 struct MaterialSheen;
 struct MaterialClearcoat;
 struct MaterialTransmission;
@@ -117,6 +118,7 @@ protected:
     aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec4 &prop, const char *propName, int type, int idx) const;
     aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec3 &prop, const char *propName, int type, int idx) const;
     bool GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG);
+    bool GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecular &specular);
     bool GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen);
     bool GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat);
     bool GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission);

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

@@ -278,8 +278,19 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
         aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE);
         aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF);
 
+        // KHR_materials_specular
+        if (mat.materialSpecular.isPresent) {
+            MaterialSpecular &specular = mat.materialSpecular.value;
+            // Default values of zero disables Specular
+            if (std::memcmp(specular.specularColorFactor, defaultSpecularColorFactor, sizeof(glTFCommon::vec3)) != 0 || specular.specularFactor != 0.0f) {
+                SetMaterialColorProperty(r, specular.specularColorFactor, aimat, AI_MATKEY_COLOR_SPECULAR);
+                aimat->AddProperty(&specular.specularFactor, 1, AI_MATKEY_SPECULAR_FACTOR);
+                SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularTexture, aimat, aiTextureType_SPECULAR);
+                SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularColorTexture, aimat, aiTextureType_SPECULAR);
+            }
+        }
         // pbrSpecularGlossiness
-        if (mat.pbrSpecularGlossiness.isPresent) {
+        else if (mat.pbrSpecularGlossiness.isPresent) {
             PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
 
             SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);

+ 3 - 1
code/CMakeLists.txt

@@ -194,6 +194,8 @@ SET( Common_SRCS
   Common/ScenePreprocessor.cpp
   Common/ScenePreprocessor.h
   Common/SkeletonMeshBuilder.cpp
+  Common/StackAllocator.h
+  Common/StackAllocator.inl
   Common/StandardShapes.cpp
   Common/TargetAnimation.cpp
   Common/TargetAnimation.h
@@ -1387,7 +1389,7 @@ ENDIF()
 
 # Add RT-extension library for glTF importer with Open3DGC-compression.
 IF (RT_FOUND AND ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC)
-  TARGET_LINK_LIBRARIES(assimp ${RT_LIBRARY})
+  TARGET_LINK_LIBRARIES(assimp rt)
 ENDIF ()
 
 

+ 1 - 1
code/Common/Exporter.cpp

@@ -225,7 +225,7 @@ static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporte
 #endif
 
 #ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
-	exporters.emplace_back("pbrt", "pbrt-v4 scene description file", "pbrt", &ExportScenePbrt, aiProcess_Triangulate | aiProcess_SortByPType);
+	exporters.emplace_back("pbrt", "pbrt-v4 scene description file", "pbrt", &ExportScenePbrt, aiProcess_ConvertToLeftHanded | aiProcess_Triangulate | aiProcess_SortByPType);
 #endif
 
 #ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER

+ 3 - 0
code/Common/SceneCombiner.cpp

@@ -1349,6 +1349,9 @@ void SceneCombiner::Copy(aiMetadata **_dest, const aiMetadata *src) {
         case AI_AIVECTOR3D:
             out.mData = new aiVector3D(*static_cast<aiVector3D *>(in.mData));
             break;
+        case AI_AIMETADATA:
+            out.mData = new aiMetadata(*static_cast<aiMetadata *>(in.mData));
+            break;
         default:
             ai_assert(false);
             break;

+ 92 - 0
code/Common/StackAllocator.h

@@ -0,0 +1,92 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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  StackAllocator.h
+ *  @brief A very bare-bone allocator class that is suitable when
+ *      allocating many small objects, e.g. during parsing.
+ *      Individual objects are not freed, instead only the whole memory
+ *      can be deallocated.
+ */
+#ifndef AI_STACK_ALLOCATOR_H_INC
+#define AI_STACK_ALLOCATOR_H_INC
+
+#include <vector>
+#include <stdint.h>
+#include <stddef.h>
+
+namespace Assimp {
+
+/** @brief A very bare-bone allocator class that is suitable when
+ *      allocating many small objects, e.g. during parsing.
+ *      Individual objects are not freed, instead only the whole memory
+ *      can be deallocated.
+*/
+class StackAllocator {
+public:
+    /// @brief Constructs the allocator
+    inline StackAllocator();
+    /// @brief Destructs the allocator and frees all memory
+    inline ~StackAllocator();
+
+    // non copyable
+    StackAllocator(const StackAllocator &) = delete;
+    StackAllocator &operator=(const StackAllocator &) = delete;
+
+    /// @brief Returns a pointer to byteSize bytes of heap memory that persists
+    ///        for the lifetime of the allocator (or until FreeAll is called).
+    inline void *Allocate(size_t byteSize);
+
+    /// @brief Releases all the memory owned by this allocator.
+    //         Memory provided through function Allocate is not valid anymore after this function has been called.
+    inline void FreeAll();
+
+private:
+    constexpr const static size_t g_maxBytesPerBlock = 64 * 1024 * 1024; // The maximum size (in bytes) of a block
+    constexpr const static size_t g_startBytesPerBlock = 16 * 1024;  // Size of the first block. Next blocks will double in size until maximum size of g_maxBytesPerBlock
+    size_t m_blockAllocationSize = g_startBytesPerBlock; // Block size of the current block
+    size_t m_subIndex = g_maxBytesPerBlock; // The current byte offset in the current block
+    std::vector<uint8_t *> m_storageBlocks;  // A list of blocks
+};
+
+} // namespace Assimp
+
+#include "StackAllocator.inl"
+
+#endif // include guard

+ 82 - 0
code/Common/StackAllocator.inl

@@ -0,0 +1,82 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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.
+
+----------------------------------------------------------------------
+*/
+
+#include "StackAllocator.h"
+#include <assimp/ai_assert.h>
+
+using namespace Assimp;
+
+inline StackAllocator::StackAllocator() {
+}
+
+inline StackAllocator::~StackAllocator() {
+    FreeAll();
+}
+
+inline void *StackAllocator::Allocate(size_t byteSize) {
+    if (m_subIndex + byteSize > m_blockAllocationSize) // start a new block
+    {
+        // double block size every time, up to maximum of g_maxBytesPerBlock.
+        // Block size must be at least as large as byteSize, but we want to use this for small allocations anyway.
+        m_blockAllocationSize = std::max(std::min(m_blockAllocationSize * 2, g_maxBytesPerBlock), byteSize);
+        uint8_t *data = new uint8_t[m_blockAllocationSize];
+        m_storageBlocks.emplace_back(data);
+        m_subIndex = byteSize;
+        return data;
+    }
+
+    uint8_t *data = m_storageBlocks.back();
+    data += m_subIndex;
+    m_subIndex += byteSize;
+
+    return data;
+}
+
+inline void StackAllocator::FreeAll() {
+    for (size_t i = 0; i < m_storageBlocks.size(); i++) {
+        delete [] m_storageBlocks[i];
+    }
+    std::vector<uint8_t *> empty;
+    m_storageBlocks.swap(empty);
+    // start over:
+    m_blockAllocationSize = g_startBytesPerBlock;
+    m_subIndex = g_maxBytesPerBlock;
+}

+ 35 - 11
code/Geometry/GeometryUtils.cpp

@@ -45,35 +45,59 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
+// ------------------------------------------------------------------------------------------------
 ai_real GeometryUtils::heron( ai_real a, ai_real b, ai_real c ) {
-    ai_real s = (a + b + c) / 2;
-    ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 );
+    const ai_real s = (a + b + c) / 2;
+    const ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 );
     return area;
 }
 
-ai_real GeometryUtils::distance3D( const aiVector3D &vA, aiVector3D &vB ) {
+// ------------------------------------------------------------------------------------------------
+ai_real GeometryUtils::distance3D( const aiVector3D &vA, const aiVector3D &vB ) {
     const ai_real lx = ( vB.x - vA.x );
     const ai_real ly = ( vB.y - vA.y );
     const ai_real lz = ( vB.z - vA.z );
-    ai_real a = lx*lx + ly*ly + lz*lz;
-    ai_real d = pow( a, (ai_real)0.5 );
+    const ai_real a = lx*lx + ly*ly + lz*lz;
+    const ai_real d = pow( a, (ai_real)0.5 );
 
     return d;
 }
 
+// ------------------------------------------------------------------------------------------------
 ai_real GeometryUtils::calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) {
     ai_real area = 0;
 
-    aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] );
-    aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] );
-    aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] );
+    const aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] );
+    const aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] );
+    const aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] );
 
-    ai_real a( distance3D( vA, vB ) );
-    ai_real b( distance3D( vB, vC ) );
-    ai_real c( distance3D( vC, vA ) );
+    const ai_real a = distance3D( vA, vB );
+    const ai_real b = distance3D( vB, vC );
+    const ai_real c = distance3D( vC, vA );
     area = heron( a, b, c );
 
     return area;
 }
 
+// ------------------------------------------------------------------------------------------------
+// Check whether a ray intersects a plane and find the intersection point
+bool GeometryUtils::PlaneIntersect(const aiRay& ray, const aiVector3D& planePos, 
+        const aiVector3D& planeNormal, aiVector3D& pos) {
+    const ai_real b = planeNormal * (planePos - ray.pos);
+    ai_real h = ray.dir * planeNormal;
+    if ((h < 10e-5 && h > -10e-5) || (h = b/h) < 0)
+        return false;
+
+    pos = ray.pos + (ray.dir * h);
+    return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+void GeometryUtils::normalizeVectorArray(aiVector3D *vectorArrayIn, aiVector3D *vectorArrayOut, 
+        size_t numVectors) {
+    for (size_t i=0; i<numVectors; ++i) {
+		    vectorArrayOut[i] = vectorArrayIn[i].Normalize();
+	  }
+}
+
 } // namespace Assimp

+ 16 - 2
code/Geometry/GeometryUtils.h

@@ -47,7 +47,7 @@ namespace Assimp {
 // ---------------------------------------------------------------------------
 /// @brief This helper class supports some basic geometry algorithms.
 // ---------------------------------------------------------------------------
-class GeometryUtils {
+class ASSIMP_API GeometryUtils {
 public:
     static ai_real heron( ai_real a, ai_real b, ai_real c );
     
@@ -55,13 +55,27 @@ public:
     /// @param vA  Vector a.
     /// @param vB  Vector b.
     /// @return The distance.
-    static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB );
+    static ai_real distance3D( const aiVector3D &vA, const aiVector3D &vB );
 
     /// @brief Will calculate the area of a triangle described by a aiFace.
     /// @param face   The face
     /// @param mesh   The mesh containing the face
     /// @return The area.
     static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh );
+
+    /// @brief Will calculate the intersection between a ray and a plane
+    /// @param ray          The ray to test for
+    /// @param planePos     A point on the plane
+    /// @param planeNormal  The plane normal to describe its orientation
+    /// @param pos          The position of the intersection.
+    /// @return true is an intersection was detected, false if not.
+    static bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos, const aiVector3D& planeNormal, aiVector3D& pos);
+
+    /// @brief Will normalize an array of vectors.
+    /// @param vectorArrayIn    The incoming arra of vectors.
+    /// @param vectorArrayOut   The normalized vectors.
+    /// @param numVectors       The array size.
+    static void normalizeVectorArray(aiVector3D *vectorArrayIn, aiVector3D *vectorArrayOut, size_t numVectors);
 };
 
 } // namespace Assimp

+ 21 - 6
code/Pbrt/PbrtExporter.cpp

@@ -111,7 +111,22 @@ PbrtExporter::PbrtExporter(
         mScene(pScene),
         mIOSystem(pIOSystem),
         mPath(path),
-        mFile(file) {
+        mFile(file),
+        mRootTransform(
+            // rotates the (already left-handed) CRS -90 degrees around the x axis in order to
+            // make +Z 'up' and +Y 'towards viewer', as in default in pbrt
+            1.f,  0.f,  0.f, 0.f, //
+            0.f,  0.f, -1.f, 0.f, //
+            0.f,  1.f,  0.f, 0.f, //
+            0.f,  0.f,  0.f, 1.f  //
+        ) {
+
+    mRootTransform = aiMatrix4x4(
+        -1.f,  0,  0.f, 0.f, //
+        0.0f,  -1.f,  0.f, 0.f, //
+        0.f,  0.f,  1.f, 0.f, //
+        0.f,  0.f,  0.f, 1.f  //
+    ) * mRootTransform;
     // Export embedded textures.
     if (mScene->mNumTextures > 0)
         if (!mIOSystem->CreateDirectory("textures"))
@@ -260,7 +275,7 @@ aiMatrix4x4 PbrtExporter::GetNodeTransform(const aiString &name) const {
             node = node->mParent;
         }
     }
-    return m;
+    return mRootTransform * m;
 }
 
 std::string PbrtExporter::TransformAsString(const aiMatrix4x4 &m) {
@@ -309,7 +324,7 @@ void PbrtExporter::WriteCamera(int i) {
 
     // Get camera fov
     float hfov = AI_RAD_TO_DEG(camera->mHorizontalFOV);
-    float fov = (aspect >= 1.0) ? hfov : (hfov * aspect);
+    float fov = (aspect >= 1.0) ? hfov : (hfov / aspect);
     if (fov < 5) {
         std::cerr << fov << ": suspiciously low field of view specified by camera. Setting to 45 degrees.\n";
         fov = 45;
@@ -327,7 +342,7 @@ void PbrtExporter::WriteCamera(int i) {
 
     if (!cameraActive)
         mOutput << "# ";
-    mOutput << "Scale -1 1 1\n";  // right handed -> left handed
+    mOutput << "Scale 1 1 1\n";
     if (!cameraActive)
         mOutput << "# ";
     mOutput << "LookAt "
@@ -383,8 +398,8 @@ void PbrtExporter::WriteWorldDefinition() {
     }
 
     mOutput << "# Geometry\n\n";
-    aiMatrix4x4 worldFromObject;
-    WriteGeometricObjects(mScene->mRootNode, worldFromObject, meshUses);
+
+    WriteGeometricObjects(mScene->mRootNode, mRootTransform, meshUses);
 }
 
 void PbrtExporter::WriteTextures() {

+ 3 - 0
code/Pbrt/PbrtExporter.h

@@ -100,6 +100,9 @@ private:
     //  A private set to keep track of which textures have been declared
     std::set<std::string> mTextureSet;
 
+    // Transform to apply to the root node and all root objects such as cameras, lights, etc.
+    aiMatrix4x4 mRootTransform;
+
     aiMatrix4x4 GetNodeTransform(const aiString& name) const;
     static std::string TransformAsString(const aiMatrix4x4& m);
 

+ 12 - 24
code/PostProcessing/ArmaturePopulate.cpp

@@ -43,15 +43,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/postprocess.h>
 #include <assimp/scene.h>
-#include <iostream>
 
 namespace Assimp {
 
-/// The default class constructor.
-ArmaturePopulate::ArmaturePopulate() = default;
+static bool IsBoneNode(const aiString &bone_name, std::vector<aiBone *> &bones) {
+    for (aiBone *bone : bones) {
+        if (bone->mName == bone_name) {
+            return true;
+        }
+    }
 
-/// The class destructor.
-ArmaturePopulate::~ArmaturePopulate() = default;
+    return false;
+}
 
 bool ArmaturePopulate::IsActive(unsigned int pFlags) const {
     return (pFlags & aiProcess_PopulateArmatureData) != 0;
@@ -70,7 +73,7 @@ void ArmaturePopulate::Execute(aiScene *out) {
     BuildBoneList(out->mRootNode, out->mRootNode, out, bones);
     BuildNodeList(out->mRootNode, nodes);
 
-    BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes);
+    BuildBoneStack(out->mRootNode, out, bones, bone_stack, nodes);
 
     ASSIMP_LOG_DEBUG("Bone stack size: ", bone_stack.size());
 
@@ -78,9 +81,8 @@ void ArmaturePopulate::Execute(aiScene *out) {
         aiBone *bone = kvp.first;
         aiNode *bone_node = kvp.second;
         ASSIMP_LOG_VERBOSE_DEBUG("active node lookup: ", bone->mName.C_Str());
+        
         // lcl transform grab - done in generate_nodes :)
-
-        // bone->mOffsetMatrix = bone_node->mTransformation;
         aiNode *armature = GetArmatureRoot(bone_node, bones);
 
         ai_assert(armature);
@@ -159,8 +161,7 @@ void ArmaturePopulate::BuildNodeList(const aiNode *current_node,
 // A bone stack allows us to have multiple armatures, with the same bone names
 // A bone stack allows us also to retrieve bones true transform even with
 // duplicate names :)
-void ArmaturePopulate::BuildBoneStack(aiNode *,
-                                      const aiNode *root_node,
+void ArmaturePopulate::BuildBoneStack(const aiNode *root_node,
                                       const aiScene*,
                                       const std::vector<aiBone *> &bones,
                                       std::map<aiBone *, aiNode *> &bone_stack,
@@ -196,8 +197,7 @@ void ArmaturePopulate::BuildBoneStack(aiNode *,
 // This is required to be detected for a bone initially, it will recurse up
 // until it cannot find another bone and return the node No known failure
 // points. (yet)
-aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node,
-                                          std::vector<aiBone *> &bone_list) {
+aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, std::vector<aiBone *> &bone_list) {
     while (nullptr != bone_node) {
         if (!IsBoneNode(bone_node->mName, bone_list)) {
             ASSIMP_LOG_VERBOSE_DEBUG("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str());
@@ -212,18 +212,6 @@ aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node,
     return nullptr;
 }
 
-// Simple IsBoneNode check if this could be a bone
-bool ArmaturePopulate::IsBoneNode(const aiString &bone_name,
-                                  std::vector<aiBone *> &bones) {
-    for (aiBone *bone : bones) {
-        if (bone->mName == bone_name) {
-            return true;
-        }
-    }
-
-    return false;
-}
-
 // Pop this node by name from the stack if found
 // Used in multiple armature situations with duplicate node / bone names
 // Known flaw: cannot have nodes with bone names, will be fixed in later release

+ 3 - 7
code/PostProcessing/ArmaturePopulate.h

@@ -69,10 +69,10 @@ namespace Assimp {
 class ASSIMP_API ArmaturePopulate : public BaseProcess {
 public:
     /// The default class constructor.
-    ArmaturePopulate();
+    ArmaturePopulate() = default;
 
     /// The class destructor.
-    virtual ~ArmaturePopulate();
+    virtual ~ArmaturePopulate() = default;
 
     /// Overwritten, @see BaseProcess
     virtual bool IsActive( unsigned int pFlags ) const;
@@ -86,9 +86,6 @@ public:
     static aiNode *GetArmatureRoot(aiNode *bone_node,
                                       std::vector<aiBone *> &bone_list);
 
-    static bool IsBoneNode(const aiString &bone_name,
-                              std::vector<aiBone *> &bones);
-
     static aiNode *GetNodeFromStack(const aiString &node_name,
                                        std::vector<aiNode *> &nodes);
 
@@ -99,7 +96,7 @@ public:
                                  const aiScene *scene,
                                  std::vector<aiBone *> &bones);
 
-    static void BuildBoneStack(aiNode *current_node, const aiNode *root_node,
+    static void BuildBoneStack(const aiNode *root_node,
                                   const aiScene *scene,
                                   const std::vector<aiBone *> &bones,
                                   std::map<aiBone *, aiNode *> &bone_stack,
@@ -108,5 +105,4 @@ public:
 
 } // Namespace Assimp
 
-
 #endif // SCALE_PROCESS_H_

+ 135 - 181
code/PostProcessing/ComputeUVMappingProcess.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -42,8 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 /** @file GenUVCoords step */
 
-
 #include "ComputeUVMappingProcess.h"
+#include "Geometry/GeometryUtils.h"
 #include "ProcessHelper.h"
 #include <assimp/Exceptional.h>
 
@@ -51,39 +50,25 @@ using namespace Assimp;
 
 namespace {
 
-    const static aiVector3D base_axis_y(0.0,1.0,0.0);
-    const static aiVector3D base_axis_x(1.0,0.0,0.0);
-    const static aiVector3D base_axis_z(0.0,0.0,1.0);
-    const static ai_real angle_epsilon = ai_real( 0.95 );
-}
+const static aiVector3D base_axis_y(0.0, 1.0, 0.0);
+const static aiVector3D base_axis_x(1.0, 0.0, 0.0);
+const static aiVector3D base_axis_z(0.0, 0.0, 1.0);
+const static ai_real angle_epsilon = ai_real(0.95);
+} // namespace
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
-bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const
-{
-    return  (pFlags & aiProcess_GenUVCoords) != 0;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Check whether a ray intersects a plane and find the intersection point
-inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos,
-    const aiVector3D& planeNormal, aiVector3D& pos)
-{
-    const ai_real b = planeNormal * (planePos - ray.pos);
-    ai_real h = ray.dir * planeNormal;
-    if ((h < 10e-5 && h > -10e-5) || (h = b/h) < 0)
-        return false;
-
-    pos = ray.pos + (ray.dir * h);
-    return true;
+bool ComputeUVMappingProcess::IsActive(unsigned int pFlags) const {
+    return (pFlags & aiProcess_GenUVCoords) != 0;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Find the first empty UV channel in a mesh
-inline unsigned int FindEmptyUVChannel (aiMesh* mesh)
-{
-    for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS;++m)
-        if (!mesh->mTextureCoords[m])return m;
+inline unsigned int FindEmptyUVChannel(aiMesh *mesh) {
+    for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++m)
+        if (!mesh->mTextureCoords[m]) {
+            return m;
+        }
 
     ASSIMP_LOG_ERROR("Unable to compute UV coordinates, no free UV slot found");
     return UINT_MAX;
@@ -91,22 +76,22 @@ inline unsigned int FindEmptyUVChannel (aiMesh* mesh)
 
 // ------------------------------------------------------------------------------------------------
 // Try to remove UV seams
-void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
-{
+void RemoveUVSeams(aiMesh *mesh, aiVector3D *out) {
     // TODO: just a very rough algorithm. I think it could be done
     // much easier, but I don't know how and am currently too tired to
     // to think about a better solution.
 
-    const static ai_real LOWER_LIMIT = ai_real( 0.1 );
-    const static ai_real UPPER_LIMIT = ai_real( 0.9 );
+    const static ai_real LOWER_LIMIT = ai_real(0.1);
+    const static ai_real UPPER_LIMIT = ai_real(0.9);
 
-    const static ai_real LOWER_EPSILON = ai_real( 10e-3 );
-    const static ai_real UPPER_EPSILON = ai_real( 1.0-10e-3 );
+    const static ai_real LOWER_EPSILON = ai_real(10e-3);
+    const static ai_real UPPER_EPSILON = ai_real(1.0 - 10e-3);
 
-    for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx)
-    {
-        const aiFace& face = mesh->mFaces[fidx];
-        if (face.mNumIndices < 3) continue; // triangles and polygons only, please
+    for (unsigned int fidx = 0; fidx < mesh->mNumFaces; ++fidx) {
+        const aiFace &face = mesh->mFaces[fidx];
+        if (face.mNumIndices < 3) {
+            continue; // triangles and polygons only, please
+        }
 
         unsigned int smallV = face.mNumIndices, large = smallV;
         bool zero = false, one = false, round_to_zero = false;
@@ -115,20 +100,18 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
         // but the assumption that a face with at least one very small
         // on the one side and one very large U coord on the other side
         // lies on a UV seam should work for most cases.
-        for (unsigned int n = 0; n < face.mNumIndices;++n)
-        {
-            if (out[face.mIndices[n]].x < LOWER_LIMIT)
-            {
+        for (unsigned int n = 0; n < face.mNumIndices; ++n) {
+            if (out[face.mIndices[n]].x < LOWER_LIMIT) {
                 smallV = n;
 
                 // If we have a U value very close to 0 we can't
                 // round the others to 0, too.
                 if (out[face.mIndices[n]].x <= LOWER_EPSILON)
                     zero = true;
-                else round_to_zero = true;
+                else
+                    round_to_zero = true;
             }
-            if (out[face.mIndices[n]].x > UPPER_LIMIT)
-            {
+            if (out[face.mIndices[n]].x > UPPER_LIMIT) {
                 large = n;
 
                 // If we have a U value very close to 1 we can't
@@ -137,10 +120,8 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
                     one = true;
             }
         }
-        if (smallV != face.mNumIndices && large != face.mNumIndices)
-        {
-            for (unsigned int n = 0; n < face.mNumIndices;++n)
-            {
+        if (smallV != face.mNumIndices && large != face.mNumIndices) {
+            for (unsigned int n = 0; n < face.mNumIndices; ++n) {
                 // If the u value is over the upper limit and no other u
                 // value of that face is 0, round it to 0
                 if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero)
@@ -156,9 +137,8 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
                 // Due to numerical inaccuracies one U coord becomes 0, the
                 // other 1. But we do still have a third UV coord to determine
                 // to which side we must round to.
-                else if (one && zero)
-                {
-                    if (round_to_zero && out[face.mIndices[n]].x >=  UPPER_EPSILON)
+                else if (one && zero) {
+                    if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON)
                         out[face.mIndices[n]].x = 0.0;
                     else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON)
                         out[face.mIndices[n]].x = 1.0;
@@ -169,8 +149,7 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
 }
 
 // ------------------------------------------------------------------------------------------------
-void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
-{
+void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh *mesh, const aiVector3D &axis, aiVector3D *out) {
     aiVector3D center, min, max;
     FindMeshCenter(mesh, center, min, max);
 
@@ -178,7 +157,7 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
     // currently the mapping axis will always be one of x,y,z, except if the
     // PretransformVertices step is used (it transforms the meshes into worldspace,
     // thus changing the mapping axis)
-    if (axis * base_axis_x >= angle_epsilon)    {
+    if (axis * base_axis_x >= angle_epsilon) {
 
         // For each point get a normalized projection vector in the sphere,
         // get its longitude and latitude and map them to their respective
@@ -192,58 +171,54 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
         // Thus we can derive:
         // lat  = arcsin (z)
         // lon  = arctan (y/x)
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
-            const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
-            out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
-                (std::asin  (diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D diff = (mesh->mVertices[pnt] - center).Normalize();
+            out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
+                    (std::asin(diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
         }
-    }
-    else if (axis * base_axis_y >= angle_epsilon)   {
+    } else if (axis * base_axis_y >= angle_epsilon) {
         // ... just the same again
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
-            const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
-            out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
-                (std::asin  (diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D diff = (mesh->mVertices[pnt] - center).Normalize();
+            out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
+                    (std::asin(diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
         }
-    }
-    else if (axis * base_axis_z >= angle_epsilon)   {
+    } else if (axis * base_axis_z >= angle_epsilon) {
         // ... just the same again
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
-            const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
-            out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
-                (std::asin  (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D diff = (mesh->mVertices[pnt] - center).Normalize();
+            out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
+                    (std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
         }
     }
     // slower code path in case the mapping axis is not one of the coordinate system axes
-    else    {
+    else {
         aiMatrix4x4 mTrafo;
-        aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
+        aiMatrix4x4::FromToMatrix(axis, base_axis_y, mTrafo);
 
         // again the same, except we're applying a transformation now
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
-            const aiVector3D diff = ((mTrafo*mesh->mVertices[pnt])-center).Normalize();
-            out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
-                (std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D diff = ((mTrafo * mesh->mVertices[pnt]) - center).Normalize();
+            out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
+                    (std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
         }
     }
 
-
     // Now find and remove UV seams. A seam occurs if a face has a tcoord
     // close to zero on the one side, and a tcoord close to one on the
     // other side.
-    RemoveUVSeams(mesh,out);
+    RemoveUVSeams(mesh, out);
 }
 
 // ------------------------------------------------------------------------------------------------
-void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
-{
+void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh *mesh, const aiVector3D &axis, aiVector3D *out) {
     aiVector3D center, min, max;
 
     // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
     // currently the mapping axis will always be one of x,y,z, except if the
     // PretransformVertices step is used (it transforms the meshes into worldspace,
     // thus changing the mapping axis)
-    if (axis * base_axis_x >= angle_epsilon)    {
+    if (axis * base_axis_x >= angle_epsilon) {
         FindMeshCenter(mesh, center, min, max);
         const ai_real diff = max.x - min.x;
 
@@ -251,116 +226,110 @@ void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector
         // directly to the texture V axis. The other axis is derived from
         // the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where
         // 'c' is the center point of the mesh.
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
-            const aiVector3D& pos = mesh->mVertices[pnt];
-            aiVector3D& uv  = out[pnt];
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D &pos = mesh->mVertices[pnt];
+            aiVector3D &uv = out[pnt];
 
             uv.y = (pos.x - min.x) / diff;
-            uv.x = (std::atan2( pos.z - center.z, pos.y - center.y) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
+            uv.x = (std::atan2(pos.z - center.z, pos.y - center.y) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
         }
-    }
-    else if (axis * base_axis_y >= angle_epsilon)   {
+    } else if (axis * base_axis_y >= angle_epsilon) {
         FindMeshCenter(mesh, center, min, max);
         const ai_real diff = max.y - min.y;
 
         // just the same ...
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
-            const aiVector3D& pos = mesh->mVertices[pnt];
-            aiVector3D& uv  = out[pnt];
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D &pos = mesh->mVertices[pnt];
+            aiVector3D &uv = out[pnt];
 
             uv.y = (pos.y - min.y) / diff;
-            uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
+            uv.x = (std::atan2(pos.x - center.x, pos.z - center.z) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
         }
-    }
-    else if (axis * base_axis_z >= angle_epsilon)   {
+    } else if (axis * base_axis_z >= angle_epsilon) {
         FindMeshCenter(mesh, center, min, max);
         const ai_real diff = max.z - min.z;
 
         // just the same ...
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
-            const aiVector3D& pos = mesh->mVertices[pnt];
-            aiVector3D& uv  = out[pnt];
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D &pos = mesh->mVertices[pnt];
+            aiVector3D &uv = out[pnt];
 
             uv.y = (pos.z - min.z) / diff;
-            uv.x = (std::atan2( pos.y - center.y, pos.x - center.x) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
+            uv.x = (std::atan2(pos.y - center.y, pos.x - center.x) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
         }
     }
     // slower code path in case the mapping axis is not one of the coordinate system axes
     else {
         aiMatrix4x4 mTrafo;
-        aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
-        FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
+        aiMatrix4x4::FromToMatrix(axis, base_axis_y, mTrafo);
+        FindMeshCenterTransformed(mesh, center, min, max, mTrafo);
         const ai_real diff = max.y - min.y;
 
         // again the same, except we're applying a transformation now
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt){
-            const aiVector3D pos = mTrafo* mesh->mVertices[pnt];
-            aiVector3D& uv  = out[pnt];
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D pos = mTrafo * mesh->mVertices[pnt];
+            aiVector3D &uv = out[pnt];
 
             uv.y = (pos.y - min.y) / diff;
-            uv.x = (std::atan2( pos.x - center.x, pos.z - center.z) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI;
+            uv.x = (std::atan2(pos.x - center.x, pos.z - center.z) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
         }
     }
 
     // Now find and remove UV seams. A seam occurs if a face has a tcoord
     // close to zero on the one side, and a tcoord close to one on the
     // other side.
-    RemoveUVSeams(mesh,out);
+    RemoveUVSeams(mesh, out);
 }
 
 // ------------------------------------------------------------------------------------------------
-void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
-{
-    ai_real diffu,diffv;
+void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh *mesh, const aiVector3D &axis, aiVector3D *out) {
+    ai_real diffu, diffv;
     aiVector3D center, min, max;
 
     // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
     // currently the mapping axis will always be one of x,y,z, except if the
     // PretransformVertices step is used (it transforms the meshes into worldspace,
     // thus changing the mapping axis)
-    if (axis * base_axis_x >= angle_epsilon)    {
+    if (axis * base_axis_x >= angle_epsilon) {
         FindMeshCenter(mesh, center, min, max);
         diffu = max.z - min.z;
         diffv = max.y - min.y;
 
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
-            const aiVector3D& pos = mesh->mVertices[pnt];
-            out[pnt].Set((pos.z - min.z) / diffu,(pos.y - min.y) / diffv,0.0);
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D &pos = mesh->mVertices[pnt];
+            out[pnt].Set((pos.z - min.z) / diffu, (pos.y - min.y) / diffv, 0.0);
         }
-    }
-    else if (axis * base_axis_y >= angle_epsilon)   {
+    } else if (axis * base_axis_y >= angle_epsilon) {
         FindMeshCenter(mesh, center, min, max);
         diffu = max.x - min.x;
         diffv = max.z - min.z;
 
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
-            const aiVector3D& pos = mesh->mVertices[pnt];
-            out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0);
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D &pos = mesh->mVertices[pnt];
+            out[pnt].Set((pos.x - min.x) / diffu, (pos.z - min.z) / diffv, 0.0);
         }
-    }
-    else if (axis * base_axis_z >= angle_epsilon)   {
+    } else if (axis * base_axis_z >= angle_epsilon) {
         FindMeshCenter(mesh, center, min, max);
         diffu = max.x - min.x;
         diffv = max.y - min.y;
 
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
-            const aiVector3D& pos = mesh->mVertices[pnt];
-            out[pnt].Set((pos.x - min.x) / diffu,(pos.y - min.y) / diffv,0.0);
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
+            const aiVector3D &pos = mesh->mVertices[pnt];
+            out[pnt].Set((pos.x - min.x) / diffu, (pos.y - min.y) / diffv, 0.0);
         }
     }
     // slower code path in case the mapping axis is not one of the coordinate system axes
-    else
-    {
+    else {
         aiMatrix4x4 mTrafo;
-        aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
-        FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
+        aiMatrix4x4::FromToMatrix(axis, base_axis_y, mTrafo);
+        FindMeshCenterTransformed(mesh, center, min, max, mTrafo);
         diffu = max.x - min.x;
         diffv = max.z - min.z;
 
         // again the same, except we're applying a transformation now
-        for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
+        for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
             const aiVector3D pos = mTrafo * mesh->mVertices[pnt];
-            out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv,0.0);
+            out[pnt].Set((pos.x - min.x) / diffu, (pos.z - min.z) / diffv, 0.0);
         }
     }
 
@@ -368,14 +337,12 @@ void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D&
 }
 
 // ------------------------------------------------------------------------------------------------
-void ComputeUVMappingProcess::ComputeBoxMapping( aiMesh*, aiVector3D* )
-{
+void ComputeUVMappingProcess::ComputeBoxMapping(aiMesh *, aiVector3D *) {
     ASSIMP_LOG_ERROR("Mapping type currently not implemented");
 }
 
 // ------------------------------------------------------------------------------------------------
-void ComputeUVMappingProcess::Execute( aiScene* pScene)
-{
+void ComputeUVMappingProcess::Execute(aiScene *pScene) {
     ASSIMP_LOG_DEBUG("GenUVCoordsProcess begin");
     char buffer[1024];
 
@@ -386,23 +353,18 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
 
     /*  Iterate through all materials and search for non-UV mapped textures
      */
-    for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
-    {
+    for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) {
         mappingStack.clear();
-        aiMaterial* mat = pScene->mMaterials[i];
-        for (unsigned int a = 0; a < mat->mNumProperties;++a)
-        {
-            aiMaterialProperty* prop = mat->mProperties[a];
-            if (!::strcmp( prop->mKey.data, "$tex.mapping"))
-            {
-                aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData);
-                if (aiTextureMapping_UV != mapping)
-                {
-                    if (!DefaultLogger::isNullLogger())
-                    {
+        aiMaterial *mat = pScene->mMaterials[i];
+        for (unsigned int a = 0; a < mat->mNumProperties; ++a) {
+            aiMaterialProperty *prop = mat->mProperties[a];
+            if (!::strcmp(prop->mKey.data, "$tex.mapping")) {
+                aiTextureMapping &mapping = *((aiTextureMapping *)prop->mData);
+                if (aiTextureMapping_UV != mapping) {
+                    if (!DefaultLogger::isNullLogger()) {
                         ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s",
-                            aiTextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex,
-                            MappingTypeToString(mapping));
+                                aiTextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex,
+                                MappingTypeToString(mapping));
 
                         ASSIMP_LOG_INFO(buffer);
                     }
@@ -410,70 +372,62 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
                     if (aiTextureMapping_OTHER == mapping)
                         continue;
 
-                    MappingInfo info (mapping);
+                    MappingInfo info(mapping);
 
                     // Get further properties - currently only the major axis
-                    for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2)
-                    {
-                        aiMaterialProperty* prop2 = mat->mProperties[a2];
+                    for (unsigned int a2 = 0; a2 < mat->mNumProperties; ++a2) {
+                        aiMaterialProperty *prop2 = mat->mProperties[a2];
                         if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex)
                             continue;
 
-                        if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis"))  {
-                            info.axis = *((aiVector3D*)prop2->mData);
+                        if (!::strcmp(prop2->mKey.data, "$tex.mapaxis")) {
+                            info.axis = *((aiVector3D *)prop2->mData);
                             break;
                         }
                     }
 
-                    unsigned int idx( 99999999 );
+                    unsigned int idx(99999999);
 
                     // Check whether we have this mapping mode already
-                    std::list<MappingInfo>::iterator it = std::find (mappingStack.begin(),mappingStack.end(), info);
-                    if (mappingStack.end() != it)
-                    {
+                    std::list<MappingInfo>::iterator it = std::find(mappingStack.begin(), mappingStack.end(), info);
+                    if (mappingStack.end() != it) {
                         idx = (*it).uv;
-                    }
-                    else
-                    {
+                    } else {
                         /*  We have found a non-UV mapped texture. Now
-                        *   we need to find all meshes using this material
-                        *   that we can compute UV channels for them.
-                        */
-                        for (unsigned int m = 0; m < pScene->mNumMeshes;++m)
-                        {
-                            aiMesh* mesh = pScene->mMeshes[m];
+                         *   we need to find all meshes using this material
+                         *   that we can compute UV channels for them.
+                         */
+                        for (unsigned int m = 0; m < pScene->mNumMeshes; ++m) {
+                            aiMesh *mesh = pScene->mMeshes[m];
                             unsigned int outIdx = 0;
-                            if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == UINT_MAX ||
-                                !mesh->mNumVertices)
-                            {
+                            if (mesh->mMaterialIndex != i || (outIdx = FindEmptyUVChannel(mesh)) == UINT_MAX ||
+                                    !mesh->mNumVertices) {
                                 continue;
                             }
 
                             // Allocate output storage
-                            aiVector3D* p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices];
+                            aiVector3D *p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices];
 
-                            switch (mapping)
-                            {
+                            switch (mapping) {
                             case aiTextureMapping_SPHERE:
-                                ComputeSphereMapping(mesh,info.axis,p);
+                                ComputeSphereMapping(mesh, info.axis, p);
                                 break;
                             case aiTextureMapping_CYLINDER:
-                                ComputeCylinderMapping(mesh,info.axis,p);
+                                ComputeCylinderMapping(mesh, info.axis, p);
                                 break;
                             case aiTextureMapping_PLANE:
-                                ComputePlaneMapping(mesh,info.axis,p);
+                                ComputePlaneMapping(mesh, info.axis, p);
                                 break;
                             case aiTextureMapping_BOX:
-                                ComputeBoxMapping(mesh,p);
+                                ComputeBoxMapping(mesh, p);
                                 break;
                             default:
                                 ai_assert(false);
                             }
-                            if (m && idx != outIdx)
-                            {
+                            if (m && idx != outIdx) {
                                 ASSIMP_LOG_WARN("UV index mismatch. Not all meshes assigned to "
-                                    "this material have equal numbers of UV channels. The UV index stored in  "
-                                    "the material structure does therefore not apply for all meshes. ");
+                                                "this material have equal numbers of UV channels. The UV index stored in  "
+                                                "the material structure does therefore not apply for all meshes. ");
                             }
                             idx = outIdx;
                         }
@@ -483,7 +437,7 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
 
                     // Update the material property list
                     mapping = aiTextureMapping_UV;
-                    ((aiMaterial*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex));
+                    ((aiMaterial *)mat)->AddProperty(&idx, 1, AI_MATKEY_UVWSRC(prop->mSemantic, prop->mIndex));
                 }
             }
         }

+ 0 - 9
code/PostProcessing/ConvertToLHProcess.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -225,13 +223,6 @@ void MakeLeftHandedProcess::ProcessAnimation(aiNodeAnim *pAnim) {
 
     // rotation keys
     for (unsigned int a = 0; a < pAnim->mNumRotationKeys; a++) {
-        /* That's the safe version, but the float errors add up. So we try the short version instead
-        aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix();
-        rotmat.a3 = -rotmat.a3; rotmat.b3 = -rotmat.b3;
-        rotmat.c1 = -rotmat.c1; rotmat.c2 = -rotmat.c2;
-        aiQuaternion rotquat( rotmat);
-        pAnim->mRotationKeys[a].mValue = rotquat;
-        */
         pAnim->mRotationKeys[a].mValue.x *= -1.0f;
         pAnim->mRotationKeys[a].mValue.y *= -1.0f;
     }

+ 4 - 8
code/PostProcessing/DeboneProcess.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -87,7 +86,7 @@ void DeboneProcess::Execute( aiScene* pScene) {
     if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones))  {
         for(unsigned int a = 0; a < pScene->mNumMeshes; a++)    {
             if(splitList[a]) {
-                numSplits++;
+                ++numSplits;
             }
         }
     }
@@ -119,8 +118,8 @@ void DeboneProcess::Execute( aiScene* pScene) {
                     aiNode *theNode = find ? pScene->mRootNode->FindNode(*find) : nullptr;
                     std::pair<unsigned int,aiNode*> push_pair(static_cast<unsigned int>(meshes.size()),theNode);
 
-                    mSubMeshIndices[a].push_back(push_pair);
-                    meshes.push_back(newMeshes[b].first);
+                    mSubMeshIndices[a].emplace_back(push_pair);
+                    meshes.emplace_back(newMeshes[b].first);
 
                     out+=newMeshes[b].first->mNumBones;
                 }
@@ -360,9 +359,7 @@ void DeboneProcess::UpdateNode(aiNode* pNode) const {
     unsigned int m = static_cast<unsigned int>(pNode->mNumMeshes), n = static_cast<unsigned int>(mSubMeshIndices.size());
 
     // first pass, look for meshes which have not moved
-
     for(unsigned int a=0;a<m;a++)   {
-
         unsigned int srcIndex = pNode->mMeshes[a];
         const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[srcIndex];
         unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size());
@@ -376,8 +373,7 @@ void DeboneProcess::UpdateNode(aiNode* pNode) const {
 
     // second pass, collect deboned meshes
 
-    for(unsigned int a=0;a<n;a++)
-    {
+    for(unsigned int a=0;a<n;a++) {
         const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[a];
         unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size());
 

+ 12 - 13
code/PostProcessing/DropFaceNormalsProcess.cpp

@@ -42,27 +42,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 /** @file Implementation of the post processing step to drop face
-* normals for all imported faces.
-*/
-
+ * normals for all imported faces.
+ */
 
 #include "DropFaceNormalsProcess.h"
+#include <assimp/Exceptional.h>
 #include <assimp/postprocess.h>
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
-#include <assimp/Exceptional.h>
 
 using namespace Assimp;
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
-bool DropFaceNormalsProcess::IsActive( unsigned int pFlags) const {
-    return  (pFlags & aiProcess_DropNormals) != 0;
+bool DropFaceNormalsProcess::IsActive(unsigned int pFlags) const {
+    return (pFlags & aiProcess_DropNormals) != 0;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
-void DropFaceNormalsProcess::Execute( aiScene* pScene) {
+void DropFaceNormalsProcess::Execute(aiScene *pScene) {
     ASSIMP_LOG_DEBUG("DropFaceNormalsProcess begin");
 
     if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
@@ -70,21 +69,21 @@ void DropFaceNormalsProcess::Execute( aiScene* pScene) {
     }
 
     bool bHas = false;
-    for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
-        bHas |= this->DropMeshFaceNormals( pScene->mMeshes[a]);
+    for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+        bHas |= this->DropMeshFaceNormals(pScene->mMeshes[a]);
     }
-    if (bHas)   {
+    if (bHas) {
         ASSIMP_LOG_INFO("DropFaceNormalsProcess finished. "
-            "Face normals have been removed");
+                        "Face normals have been removed");
     } else {
         ASSIMP_LOG_DEBUG("DropFaceNormalsProcess finished. "
-            "No normals were present");
+                         "No normals were present");
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
-bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* mesh) {
+bool DropFaceNormalsProcess::DropMeshFaceNormals(aiMesh *mesh) {
     ai_assert(nullptr != mesh);
 
     if (nullptr == mesh->mNormals) {

+ 40 - 43
code/PostProcessing/FindDegenerates.cpp

@@ -41,11 +41,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 /** @file  FindDegenerates.cpp
  *  @brief Implementation of the FindDegenerates post-process step.
-*/
+ */
 
-#include "ProcessHelper.h"
 #include "FindDegenerates.h"
 #include "Geometry/GeometryUtils.h"
+#include "ProcessHelper.h"
 
 #include <assimp/Exceptional.h>
 
@@ -54,35 +54,35 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 
 // Correct node indices to meshes and remove references to deleted mesh
-static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned int, unsigned int>& meshMap);
+static void updateSceneGraph(aiNode *pNode, const std::unordered_map<unsigned int, unsigned int> &meshMap);
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 FindDegeneratesProcess::FindDegeneratesProcess() :
-        mConfigRemoveDegenerates( false ),
-        mConfigCheckAreaOfTriangle( false ){
+        mConfigRemoveDegenerates(false),
+        mConfigCheckAreaOfTriangle(false) {
     // empty
 }
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
-bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const {
+bool FindDegeneratesProcess::IsActive(unsigned int pFlags) const {
     return 0 != (pFlags & aiProcess_FindDegenerates);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Setup import configuration
-void FindDegeneratesProcess::SetupProperties(const Importer* pImp) {
+void FindDegeneratesProcess::SetupProperties(const Importer *pImp) {
     // Get the current value of AI_CONFIG_PP_FD_REMOVE
-    mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
-    mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) );
+    mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE, 0));
+    mConfigCheckAreaOfTriangle = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA));
 }
 
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
-void FindDegeneratesProcess::Execute( aiScene* pScene) {
+void FindDegeneratesProcess::Execute(aiScene *pScene) {
     ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin");
-    if ( nullptr == pScene) {
+    if (nullptr == pScene) {
         return;
     }
 
@@ -112,7 +112,7 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) {
     ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished");
 }
 
-static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned int, unsigned int>& meshMap) {
+static void updateSceneGraph(aiNode *pNode, const std::unordered_map<unsigned int, unsigned int> &meshMap) {
     unsigned int targetIndex = 0;
     for (unsigned i = 0; i < pNode->mNumMeshes; ++i) {
         const unsigned int sourceMeshIndex = pNode->mMeshes[i];
@@ -123,7 +123,7 @@ static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned in
         }
     }
     pNode->mNumMeshes = targetIndex;
-    //recurse to all children
+    // recurse to all children
     for (unsigned i = 0; i < pNode->mNumChildren; ++i) {
         updateSceneGraph(pNode->mChildren[i], meshMap);
     }
@@ -131,17 +131,17 @@ static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned in
 
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported mesh
-bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
+bool FindDegeneratesProcess::ExecuteOnMesh(aiMesh *mesh) {
     mesh->mPrimitiveTypes = 0;
 
     std::vector<bool> remove_me;
     if (mConfigRemoveDegenerates) {
-        remove_me.resize( mesh->mNumFaces, false );
+        remove_me.resize(mesh->mNumFaces, false);
     }
 
     unsigned int deg = 0, limit;
-    for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) {
-        aiFace& face = mesh->mFaces[a];
+    for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
+        aiFace &face = mesh->mFaces[a];
         bool first = true;
 
         // check whether the face contains degenerated entries
@@ -151,43 +151,43 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
             // double points may not come directly after another.
             limit = face.mNumIndices;
             if (face.mNumIndices > 4) {
-                limit = std::min( limit, i+2 );
+                limit = std::min(limit, i + 2);
             }
 
-            for (unsigned int t = i+1; t < limit; ++t) {
-                if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) {
+            for (unsigned int t = i + 1; t < limit; ++t) {
+                if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]]) {
                     // we have found a matching vertex position
                     // remove the corresponding index from the array
                     --face.mNumIndices;
                     --limit;
                     for (unsigned int m = t; m < face.mNumIndices; ++m) {
-                        face.mIndices[ m ] = face.mIndices[ m+1 ];
+                        face.mIndices[m] = face.mIndices[m + 1];
                     }
                     --t;
 
                     // NOTE: we set the removed vertex index to an unique value
                     // to make sure the developer gets notified when his
                     // application attempts to access this data.
-                    face.mIndices[ face.mNumIndices ] = 0xdeadbeef;
+                    face.mIndices[face.mNumIndices] = 0xdeadbeef;
 
-                    if(first) {
+                    if (first) {
                         ++deg;
                         first = false;
                     }
 
-                    if ( mConfigRemoveDegenerates ) {
-                        remove_me[ a ] = true;
+                    if (mConfigRemoveDegenerates) {
+                        remove_me[a] = true;
                         goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
                     }
                 }
             }
 
-            if ( mConfigCheckAreaOfTriangle ) {
-                if ( face.mNumIndices == 3 ) {
-                    ai_real area = GeometryUtils::calculateAreaOfTriangle( face, mesh );
+            if (mConfigCheckAreaOfTriangle) {
+                if (face.mNumIndices == 3) {
+                    ai_real area = GeometryUtils::calculateAreaOfTriangle(face, mesh);
                     if (area < ai_epsilon) {
-                        if ( mConfigRemoveDegenerates ) {
-                            remove_me[ a ] = true;
+                        if (mConfigRemoveDegenerates) {
+                            remove_me[a] = true;
                             ++deg;
                             goto evil_jump_outside;
                         }
@@ -199,8 +199,7 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
         }
 
         // We need to update the primitive flags array of the mesh.
-        switch (face.mNumIndices)
-        {
+        switch (face.mNumIndices) {
         case 1u:
             mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
             break;
@@ -214,30 +213,28 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
             mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
             break;
         };
-evil_jump_outside:
+    evil_jump_outside:
         continue;
     }
 
     // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
     if (mConfigRemoveDegenerates && deg) {
         unsigned int n = 0;
-        for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
-        {
-            aiFace& face_src = mesh->mFaces[a];
+        for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
+            aiFace &face_src = mesh->mFaces[a];
             if (!remove_me[a]) {
-                aiFace& face_dest = mesh->mFaces[n++];
+                aiFace &face_dest = mesh->mFaces[n++];
 
                 // Do a manual copy, keep the index array
                 face_dest.mNumIndices = face_src.mNumIndices;
-                face_dest.mIndices    = face_src.mIndices;
+                face_dest.mIndices = face_src.mIndices;
 
                 if (&face_src != &face_dest) {
                     // clear source
                     face_src.mNumIndices = 0;
                     face_src.mIndices = nullptr;
                 }
-            }
-            else {
+            } else {
                 // Otherwise delete it if we don't need this face
                 delete[] face_src.mIndices;
                 face_src.mIndices = nullptr;
@@ -247,15 +244,15 @@ evil_jump_outside:
         // Just leave the rest of the array unreferenced, we don't care for now
         mesh->mNumFaces = n;
         if (!mesh->mNumFaces) {
-            //The whole mesh consists of degenerated faces
-            //signal upward, that this mesh should be deleted.
+            // The whole mesh consists of degenerated faces
+            // signal upward, that this mesh should be deleted.
             ASSIMP_LOG_VERBOSE_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives");
             return true;
         }
     }
 
     if (deg && !DefaultLogger::isNullLogger()) {
-        ASSIMP_LOG_WARN( "Found ", deg, " degenerated primitives");
+        ASSIMP_LOG_WARN("Found ", deg, " degenerated primitives");
     }
     return false;
 }

+ 151 - 140
code/PostProcessing/PretransformVertices.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -41,9 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
-/** @file PretransformVertices.cpp
- *  @brief Implementation of the "PretransformVertices" post processing step
-*/
+/// @file  PretransformVertices.cpp
+/// @brief Implementation of the "PretransformVertices" post processing step
 
 #include "PretransformVertices.h"
 #include "ConvertToLHProcess.h"
@@ -57,16 +54,44 @@ using namespace Assimp;
 #define AI_PTVS_VERTEX 0x0
 #define AI_PTVS_FACE 0x1
 
+namespace {
+
+// Get a bitwise combination identifying the vertex format of a mesh
+static unsigned int GetMeshVFormat(aiMesh *pcMesh)  {
+	// the vertex format is stored in aiMesh::mBones for later retrieval.
+	// there isn't a good reason to compute it a few hundred times
+	// from scratch. The pointer is unused as animations are lost
+	// during PretransformVertices.
+	if (pcMesh->mBones)
+		return (unsigned int)(uint64_t)pcMesh->mBones;
+
+	const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
+
+	// store the value for later use
+	pcMesh->mBones = (aiBone **)(uint64_t)iRet;
+	return iRet;
+}
+
+// Get a list of all vertex formats that occur for a given material index
+// The output list contains duplicate elements
+static void GetVFormatList(const aiScene *pcScene, unsigned int iMat, std::list<unsigned int> &aiOut)  {
+	for (unsigned int i = 0; i < pcScene->mNumMeshes; ++i) {
+		aiMesh *pcMesh = pcScene->mMeshes[i];
+		if (iMat == pcMesh->mMaterialIndex) {
+			aiOut.push_back(GetMeshVFormat(pcMesh));
+		}
+	}
+}
+
+}
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 PretransformVertices::PretransformVertices() :
-		configKeepHierarchy(false),
-		configNormalize(false),
-		configTransform(false),
-		configTransformation(),
-		mConfigPointCloud(false) {
-	// empty
-}
+		mConfigKeepHierarchy(false),
+		mConfigNormalize(false),
+		mConfigTransform(false),
+		mConfigTransformation(),
+		mConfigPointCloud(false) {}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
@@ -79,11 +104,11 @@ bool PretransformVertices::IsActive(unsigned int pFlags) const {
 void PretransformVertices::SetupProperties(const Importer *pImp) {
 	// Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE,
 	// AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION
-	configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 0));
-	configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 0));
-	configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 0));
+	mConfigKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 0));
+	mConfigNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 0));
+	mConfigTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 0));
 
-	configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
+	mConfigTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
 
 	mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS);
 }
@@ -99,25 +124,7 @@ unsigned int PretransformVertices::CountNodes(const aiNode *pcNode) const {
 }
 
 // ------------------------------------------------------------------------------------------------
-// Get a bitwise combination identifying the vertex format of a mesh
-unsigned int PretransformVertices::GetMeshVFormat(aiMesh *pcMesh) const {
-	// the vertex format is stored in aiMesh::mBones for later retrieval.
-	// there isn't a good reason to compute it a few hundred times
-	// from scratch. The pointer is unused as animations are lost
-	// during PretransformVertices.
-	if (pcMesh->mBones)
-		return (unsigned int)(uint64_t)pcMesh->mBones;
-
-	const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
-
-	// store the value for later use
-	pcMesh->mBones = (aiBone **)(uint64_t)iRet;
-	return iRet;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Count the number of vertices in the whole scene and a given
-// material index
+// Count the number of vertices in the whole scene and a given material index
 void PretransformVertices::CountVerticesAndFaces(const aiScene *pcScene, const aiNode *pcNode, unsigned int iMat,
 		unsigned int iVFormat, unsigned int *piFaces, unsigned int *piVertices) const {
 	for (unsigned int i = 0; i < pcNode->mNumMeshes; ++i) {
@@ -128,8 +135,7 @@ void PretransformVertices::CountVerticesAndFaces(const aiScene *pcScene, const a
 		}
 	}
 	for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
-		CountVerticesAndFaces(pcScene, pcNode->mChildren[i], iMat,
-				iVFormat, piFaces, piVertices);
+		CountVerticesAndFaces(pcScene, pcNode->mChildren[i], iMat, iVFormat, piFaces, piVertices);
 	}
 }
 
@@ -272,19 +278,6 @@ void PretransformVertices::CollectData(const aiScene *pcScene, const aiNode *pcN
 	}
 }
 
-// ------------------------------------------------------------------------------------------------
-// Get a list of all vertex formats that occur for a given material index
-// The output list contains duplicate elements
-void PretransformVertices::GetVFormatList(const aiScene *pcScene, unsigned int iMat,
-		std::list<unsigned int> &aiOut) const {
-	for (unsigned int i = 0; i < pcScene->mNumMeshes; ++i) {
-		aiMesh *pcMesh = pcScene->mMeshes[i];
-		if (iMat == pcMesh->mMaterialIndex) {
-			aiOut.push_back(GetMeshVFormat(pcMesh));
-		}
-	}
-}
-
 // ------------------------------------------------------------------------------------------------
 // Compute the absolute transformation matrices of each node
 void PretransformVertices::ComputeAbsoluteTransform(aiNode *pcNode) {
@@ -297,39 +290,44 @@ void PretransformVertices::ComputeAbsoluteTransform(aiNode *pcNode) {
 	}
 }
 
+static void normalizeVectorArray(aiVector3D *vectorArrayIn, aiVector3D *vectorArrayOut, size_t numVectors) {
+	for (size_t i=0; i<numVectors; ++i) {
+		vectorArrayOut[i] = vectorArrayIn[i].Normalize();
+	}
+}
+
 // ------------------------------------------------------------------------------------------------
 // Apply the node transformation to a mesh
 void PretransformVertices::ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat) const {
 	// Check whether we need to transform the coordinates at all
-	if (!mat.IsIdentity()) {
+	if (mat.IsIdentity()) {
+		return;
+	}
 
-		// Check for odd negative scale (mirror)
-		if (mesh->HasFaces() && mat.Determinant() < 0) {
-			// Reverse the mesh face winding order
-			FlipWindingOrderProcess::ProcessMesh(mesh);
-		}
+	// Check for odd negative scale (mirror)
+	if (mesh->HasFaces() && mat.Determinant() < 0) {
+		// Reverse the mesh face winding order
+		FlipWindingOrderProcess::ProcessMesh(mesh);
+	}
 
-		// Update positions
-		if (mesh->HasPositions()) {
-			for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-				mesh->mVertices[i] = mat * mesh->mVertices[i];
-			}
+	// Update positions
+	if (mesh->HasPositions()) {
+		for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+			mesh->mVertices[i] = mat * mesh->mVertices[i];
 		}
+	}
 
-		// Update normals and tangents
-		if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
-			const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose();
+	// Update normals and tangents
+	if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
+		const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose();
 
-			if (mesh->HasNormals()) {
-				for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-					mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
-				}
-			}
-			if (mesh->HasTangentsAndBitangents()) {
-				for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-					mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
-					mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
-				}
+		if (mesh->HasNormals()) {
+			normalizeVectorArray(mesh->mNormals, mesh->mNormals, mesh->mNumVertices);
+		}
+		if (mesh->HasTangentsAndBitangents()) {
+			for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+				mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
+				mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
 			}
 		}
 	}
@@ -352,40 +350,41 @@ void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **i
 			// yes, we can.
 			mesh->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
 			mesh->mNumBones = UINT_MAX;
-		} else {
+			continue;
+		} 
 
-			// try to find us in the list of newly created meshes
-			for (unsigned int n = 0; n < out.size(); ++n) {
-				aiMesh *ctz = out[n];
-				if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4 *>(ctz->mBones) == node->mTransformation) {
+		// try to find us in the list of newly created meshes
+		for (unsigned int n = 0; n < out.size(); ++n) {
+			aiMesh *ctz = out[n];
+			if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4 *>(ctz->mBones) == node->mTransformation) {
 
-					// ok, use this one. Update node mesh index
-					node->mMeshes[i] = numIn + n;
-				}
+				// ok, use this one. Update node mesh index
+				node->mMeshes[i] = numIn + n;
 			}
-			if (node->mMeshes[i] < numIn) {
-				// Worst case. Need to operate on a full copy of the mesh
-				ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
-				aiMesh *ntz;
+		}
+		if (node->mMeshes[i] < numIn) {
+			// Worst case. Need to operate on a full copy of the mesh
+			ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
+			aiMesh *ntz;
 
-				const unsigned int tmp = mesh->mNumBones; //
-				mesh->mNumBones = 0;
-				SceneCombiner::Copy(&ntz, mesh);
-				mesh->mNumBones = tmp;
+			const unsigned int cacheNumBones = mesh->mNumBones; //
+			mesh->mNumBones = 0;
+			SceneCombiner::Copy(&ntz, mesh);
+			mesh->mNumBones = cacheNumBones;
 
-				ntz->mNumBones = node->mMeshes[i];
-				ntz->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
+			ntz->mNumBones = node->mMeshes[i];
+			ntz->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
 
-				out.push_back(ntz);
+			out.push_back(ntz);
 
-				node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1);
-			}
+			node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1);
 		}
 	}
 
 	// call children
-	for (unsigned int i = 0; i < node->mNumChildren; ++i)
+	for (unsigned int i = 0; i < node->mNumChildren; ++i) {
 		BuildWCSMeshes(out, in, numIn, node->mChildren[i]);
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -394,8 +393,9 @@ void PretransformVertices::MakeIdentityTransform(aiNode *nd) const {
 	nd->mTransformation = aiMatrix4x4();
 
 	// call children
-	for (unsigned int i = 0; i < nd->mNumChildren; ++i)
+	for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
 		MakeIdentityTransform(nd->mChildren[i]);
+	}
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -405,8 +405,27 @@ void PretransformVertices::BuildMeshRefCountArray(const aiNode *nd, unsigned int
 		refs[nd->mMeshes[i]]++;
 
 	// call children
-	for (unsigned int i = 0; i < nd->mNumChildren; ++i)
+	for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
 		BuildMeshRefCountArray(nd->mChildren[i], refs);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+static void appendNewMeshesToScene(aiScene *pScene, std::vector<aiMesh*> &apcOutMeshes) {
+	ai_assert(pScene != nullptr);
+
+	if (apcOutMeshes.empty()) {
+		return;
+	}
+
+	aiMesh **npp = new aiMesh *[pScene->mNumMeshes + apcOutMeshes.size()];
+
+	::memcpy(npp, pScene->mMeshes, sizeof(aiMesh *) * pScene->mNumMeshes);
+	::memcpy(npp + pScene->mNumMeshes, &apcOutMeshes[0], sizeof(aiMesh *) * apcOutMeshes.size());
+
+	pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size());
+	delete[] pScene->mMeshes;
+	pScene->mMeshes = npp;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -418,12 +437,12 @@ void PretransformVertices::Execute(aiScene *pScene) {
 	if (!pScene->mNumMeshes)
 		return;
 
-	const unsigned int iOldMeshes = pScene->mNumMeshes;
-	const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
-	const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
+	const unsigned int oldMeshes = pScene->mNumMeshes;
+	const unsigned int oldAnimationChannels = pScene->mNumAnimations;
+	const unsigned int oldNodes = CountNodes(pScene->mRootNode);
 
-	if (configTransform) {
-		pScene->mRootNode->mTransformation = configTransformation * pScene->mRootNode->mTransformation;
+	if (mConfigTransform) {
+		pScene->mRootNode->mTransformation = mConfigTransformation * pScene->mRootNode->mTransformation;
 	}
 
 	// first compute absolute transformation matrices for all nodes
@@ -449,22 +468,13 @@ void PretransformVertices::Execute(aiScene *pScene) {
 	// we go on and transform all meshes, if one is referenced by nodes
 	// with different absolute transformations a depth copy of the mesh
 	// is required.
-	if (configKeepHierarchy) {
+	if (mConfigKeepHierarchy) {
 
 		// Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
 		BuildWCSMeshes(apcOutMeshes, pScene->mMeshes, pScene->mNumMeshes, pScene->mRootNode);
 
 		// ... if new meshes have been generated, append them to the end of the scene
-		if (apcOutMeshes.size() > 0) {
-			aiMesh **npp = new aiMesh *[pScene->mNumMeshes + apcOutMeshes.size()];
-
-			memcpy(npp, pScene->mMeshes, sizeof(aiMesh *) * pScene->mNumMeshes);
-			memcpy(npp + pScene->mNumMeshes, &apcOutMeshes[0], sizeof(aiMesh *) * apcOutMeshes.size());
-
-			pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size());
-			delete[] pScene->mMeshes;
-			pScene->mMeshes = npp;
-		}
+		appendNewMeshesToScene(pScene, apcOutMeshes);
 
 		// now iterate through all meshes and transform them to world-space
 		for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
@@ -488,34 +498,35 @@ void PretransformVertices::Execute(aiScene *pScene) {
 			aiVFormats.sort();
 			aiVFormats.unique();
 			for (std::list<unsigned int>::const_iterator j = aiVFormats.begin(); j != aiVFormats.end(); ++j) {
-				unsigned int iVertices = 0;
-				unsigned int iFaces = 0;
-				CountVerticesAndFaces(pScene, pScene->mRootNode, i, *j, &iFaces, &iVertices);
-				if (0 != iFaces && 0 != iVertices) {
+				unsigned int numVertices = 0u;
+				unsigned int numFaces = 0u;
+				CountVerticesAndFaces(pScene, pScene->mRootNode, i, *j, &numFaces, &numVertices);
+				if (0 != numFaces && 0 != numVertices) {
 					apcOutMeshes.push_back(new aiMesh());
 					aiMesh *pcMesh = apcOutMeshes.back();
-					pcMesh->mNumFaces = iFaces;
-					pcMesh->mNumVertices = iVertices;
-					pcMesh->mFaces = new aiFace[iFaces];
-					pcMesh->mVertices = new aiVector3D[iVertices];
+					pcMesh->mNumFaces = numFaces;
+					pcMesh->mNumVertices = numVertices;
+					pcMesh->mFaces = new aiFace[numFaces];
+					pcMesh->mVertices = new aiVector3D[numVertices];
 					pcMesh->mMaterialIndex = i;
-					if ((*j) & 0x2) pcMesh->mNormals = new aiVector3D[iVertices];
+					if ((*j) & 0x2) pcMesh->mNormals = new aiVector3D[numVertices];
 					if ((*j) & 0x4) {
-						pcMesh->mTangents = new aiVector3D[iVertices];
-						pcMesh->mBitangents = new aiVector3D[iVertices];
+						pcMesh->mTangents = new aiVector3D[numVertices];
+						pcMesh->mBitangents = new aiVector3D[numVertices];
 					}
-					iFaces = 0;
-					while ((*j) & (0x100 << iFaces)) {
-						pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
-						if ((*j) & (0x10000 << iFaces))
-							pcMesh->mNumUVComponents[iFaces] = 3;
-						else
-							pcMesh->mNumUVComponents[iFaces] = 2;
-						iFaces++;
+					numFaces = 0;
+					while ((*j) & (0x100 << numFaces)) {
+						pcMesh->mTextureCoords[numFaces] = new aiVector3D[numVertices];
+						if ((*j) & (0x10000 << numFaces)) {
+							pcMesh->mNumUVComponents[numFaces] = 3;
+						} else {
+							pcMesh->mNumUVComponents[numFaces] = 2;
+						}
+						++numFaces;
 					}
-					iFaces = 0;
-					while ((*j) & (0x1000000 << iFaces))
-						pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
+					numFaces = 0;
+					while ((*j) & (0x1000000 << numFaces))
+						pcMesh->mColors[numFaces++] = new aiColor4D[numVertices];
 
 					// fill the mesh ...
 					unsigned int aiTemp[2] = { 0, 0 };
@@ -593,7 +604,7 @@ void PretransformVertices::Execute(aiScene *pScene) {
 		l->mUp = aiMatrix3x3(nd->mTransformation) * l->mUp;
 	}
 
-	if (!configKeepHierarchy) {
+	if (!mConfigKeepHierarchy) {
 
 		// now delete all nodes in the scene and build a new
 		// flat node graph with a root node and some level 1 children
@@ -644,7 +655,7 @@ void PretransformVertices::Execute(aiScene *pScene) {
 		MakeIdentityTransform(pScene->mRootNode);
 	}
 
-	if (configNormalize) {
+	if (mConfigNormalize) {
 		// compute the boundary of all meshes
 		aiVector3D min, max;
 		MinMaxChooser<aiVector3D>()(min, max);
@@ -674,9 +685,9 @@ void PretransformVertices::Execute(aiScene *pScene) {
 	if (!DefaultLogger::isNullLogger()) {
 		ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished");
 
-		ASSIMP_LOG_INFO("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (",
+		ASSIMP_LOG_INFO("Removed ", oldNodes, " nodes and ", oldAnimationChannels, " animation channels (",
 				CountNodes(pScene->mRootNode), " output nodes)");
 		ASSIMP_LOG_INFO("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras.");
-		ASSIMP_LOG_INFO("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")");
+		ASSIMP_LOG_INFO("Moved ", oldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")");
 	}
 }

+ 9 - 9
code/PostProcessing/PretransformVertices.h

@@ -90,7 +90,7 @@ public:
      *  @param keep    true for keep configuration.
      */
 	void KeepHierarchy(bool keep) {
-		configKeepHierarchy = keep;
+		mConfigKeepHierarchy = keep;
 	}
 
 	// -------------------------------------------------------------------
@@ -98,7 +98,7 @@ public:
      *  @return ...
      */
 	bool IsHierarchyKept() const {
-		return configKeepHierarchy;
+		return mConfigKeepHierarchy;
 	}
 
 private:
@@ -108,7 +108,7 @@ private:
 
 	// -------------------------------------------------------------------
 	// Get a bitwise combination identifying the vertex format of a mesh
-	unsigned int GetMeshVFormat(aiMesh *pcMesh) const;
+	//unsigned int GetMeshVFormat(aiMesh *pcMesh) const;
 
 	// -------------------------------------------------------------------
 	// Count the number of vertices in the whole scene and a given
@@ -131,8 +131,8 @@ private:
 	// -------------------------------------------------------------------
 	// Get a list of all vertex formats that occur for a given material
 	// The output list contains duplicate elements
-	void GetVFormatList(const aiScene *pcScene, unsigned int iMat,
-			std::list<unsigned int> &aiOut) const;
+	/*void GetVFormatList(const aiScene *pcScene, unsigned int iMat,
+			std::list<unsigned int> &aiOut) const;*/
 
 	// -------------------------------------------------------------------
 	// Compute the absolute transformation matrices of each node
@@ -156,10 +156,10 @@ private:
 	void BuildMeshRefCountArray(const aiNode *nd, unsigned int *refs) const;
 
 	//! Configuration option: keep scene hierarchy as long as possible
-	bool configKeepHierarchy;
-	bool configNormalize;
-	bool configTransform;
-	aiMatrix4x4 configTransformation;
+	bool mConfigKeepHierarchy;
+	bool mConfigNormalize;
+	bool mConfigTransform;
+	aiMatrix4x4 mConfigTransformation;
 	bool mConfigPointCloud;
 };
 

+ 3 - 4
code/PostProcessing/ProcessHelper.cpp

@@ -175,10 +175,9 @@ unsigned int GetMeshVFormatUnique(const aiMesh *pcMesh) {
     // tangents and bitangents
     if (pcMesh->HasTangentsAndBitangents()) iRet |= 0x4;
 
-#ifdef BOOST_STATIC_ASSERT
-    BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
-    BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
-#endif
+
+    static_assert(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
+    static_assert(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
 
     // texture coordinates
     unsigned int p = 0;

+ 9 - 24
code/PostProcessing/RemoveRedundantMaterials.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -45,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 // internal headers
-
 #include "RemoveRedundantMaterials.h"
 #include <assimp/ParsingUtils.h>
 #include "ProcessHelper.h"
@@ -57,35 +54,28 @@ using namespace Assimp;
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
-RemoveRedundantMatsProcess::RemoveRedundantMatsProcess()
-: mConfigFixedMaterials() {
-    // nothing to do here
-}
+RemoveRedundantMatsProcess::RemoveRedundantMatsProcess() : mConfigFixedMaterials() {}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
-bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const
-{
+bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const {
     return (pFlags & aiProcess_RemoveRedundantMaterials) != 0;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Setup import properties
-void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp)
-{
+void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp) {
     // Get value of AI_CONFIG_PP_RRM_EXCLUDE_LIST
     mConfigFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,"");
 }
 
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
-void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
-{
+void RemoveRedundantMatsProcess::Execute( aiScene* pScene) {
     ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess begin");
 
     unsigned int redundantRemoved = 0, unreferencedRemoved = 0;
-    if (pScene->mNumMaterials)
-    {
+    if (pScene->mNumMaterials) {
         // Find out which materials are referenced by meshes
         std::vector<bool> abReferenced(pScene->mNumMaterials,false);
         for (unsigned int i = 0;i < pScene->mNumMeshes;++i)
@@ -134,8 +124,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
         // we do already have a specific hash. This allows us to
         // determine which materials are identical.
         uint32_t *aiHashes = new uint32_t[ pScene->mNumMaterials ];;
-        for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
-        {
+        for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
             // No mesh is referencing this material, remove it.
             if (!abReferenced[i]) {
                 ++unreferencedRemoved;
@@ -147,8 +136,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
             // Check all previously mapped materials for a matching hash.
             // On a match we can delete this material and just make it ref to the same index.
             uint32_t me = aiHashes[i] = ComputeMaterialHash(pScene->mMaterials[i]);
-            for (unsigned int a = 0; a < i;++a)
-            {
+            for (unsigned int a = 0; a < i;++a) {
                 if (abReferenced[a] && me == aiHashes[a]) {
                     ++redundantRemoved;
                     me = 0;
@@ -205,12 +193,9 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
         delete[] aiHashes;
         delete[] aiMappingTable;
     }
-    if (redundantRemoved == 0 && unreferencedRemoved == 0)
-    {
+    if (redundantRemoved == 0 && unreferencedRemoved == 0) {
         ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess finished ");
-    }
-    else
-    {
+    } else {
         ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ",
             unreferencedRemoved, " unused materials.");
     }

+ 0 - 59
code/PostProcessing/RemoveVCProcess.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -74,63 +72,6 @@ inline void ArrayDelete(T **&in, unsigned int &num) {
     num = 0;
 }
 
-#if 0
-// ------------------------------------------------------------------------------------------------
-// Updates the node graph - removes all nodes which have the "remove" flag set and the
-// "don't remove" flag not set. Nodes with meshes are never deleted.
-bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root)
-{
-    bool b = false;
-
-    std::list<aiNode*> mine;
-    for (unsigned int i = 0; i < node->mNumChildren;++i)
-    {
-        if(UpdateNodeGraph(node->mChildren[i],mine,false))
-            b = true;
-    }
-
-    // somewhat tricky ... mNumMeshes must be originally 0 and MSB2 may not be set,
-    // so we can do a simple comparison against MSB here
-    if (!root && AI_RC_UINT_MSB == node->mNumMeshes )
-    {
-        // this node needs to be removed
-        if(node->mNumChildren)
-        {
-            childsOfParent.insert(childsOfParent.end(),mine.begin(),mine.end());
-
-            // set all children to nullptr to make sure they are not deleted when we delete ourself
-            for (unsigned int i = 0; i < node->mNumChildren;++i)
-                node->mChildren[i] = nullptr;
-        }
-        b = true;
-        delete node;
-    }
-    else
-    {
-        AI_RC_UNMASK(node->mNumMeshes);
-        childsOfParent.push_back(node);
-
-        if (b)
-        {
-            // reallocate the array of our children here
-            node->mNumChildren = (unsigned int)mine.size();
-            aiNode** const children = new aiNode*[mine.size()];
-            aiNode** ptr = children;
-
-            for (std::list<aiNode*>::iterator it = mine.begin(), end = mine.end();
-                 it != end; ++it)
-            {
-                *ptr++ = *it;
-            }
-            delete[] node->mChildren;
-            node->mChildren = children;
-            return false;
-        }
-    }
-    return b;
-}
-#endif
-
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
 void RemoveVCProcess::Execute(aiScene *pScene) {

+ 4 - 5
code/PostProcessing/ScaleProcess.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -86,9 +85,9 @@ void ScaleProcess::Execute( aiScene* pScene ) {
         return; // nothing to scale
     }
 
-    ai_assert( mScale != 0 );
-    ai_assert( nullptr != pScene );
-    ai_assert( nullptr != pScene->mRootNode );
+    ai_assert(mScale != 0 );
+    ai_assert(nullptr != pScene );
+    ai_assert(nullptr != pScene->mRootNode );
 
     if ( nullptr == pScene ) {
         return;
@@ -140,7 +139,7 @@ void ScaleProcess::Execute( aiScene* pScene ) {
             aiMatrix4x4 scaling;
             aiMatrix4x4::Scaling( aiVector3D(scale), scaling );
 
-            aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix());
+            const aiMatrix4x4 RotMatrix = aiMatrix4x4(rotation.GetMatrix());
 
             bone->mOffsetMatrix = translation * RotMatrix * scaling;
         }

+ 11 - 13
code/PostProcessing/SortByPTypeProcess.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -54,10 +52,7 @@ using namespace Assimp;
 
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
-SortByPTypeProcess::SortByPTypeProcess() :
-        mConfigRemoveMeshes(0) {
-    // empty
-}
+SortByPTypeProcess::SortByPTypeProcess() : mConfigRemoveMeshes(0) {}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
@@ -104,8 +99,9 @@ void UpdateNodes(const std::vector<unsigned int> &replaceMeshIndex, aiNode *node
     }
 
     // call all subnodes recursively
-    for (unsigned int m = 0; m < node->mNumChildren; ++m)
+    for (unsigned int m = 0; m < node->mNumChildren; ++m) {
         UpdateNodes(replaceMeshIndex, node->mChildren[m]);
+    }
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -155,7 +151,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
         if (1 == num) {
             if (!(mConfigRemoveMeshes & mesh->mPrimitiveTypes)) {
                 *meshIdx = static_cast<unsigned int>(outMeshes.size());
-                outMeshes.push_back(mesh);
+                outMeshes.emplace_back(mesh);
             } else {
                 delete mesh;
                 pScene->mMeshes[i] = nullptr;
@@ -311,21 +307,23 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
 
                     if (vert) {
                         *vert++ = mesh->mVertices[idx];
-                        //mesh->mVertices[idx].x = get_qnan();
                     }
-                    if (nor) *nor++ = mesh->mNormals[idx];
+                    if (nor) 
+                        *nor++ = mesh->mNormals[idx];
                     if (tan) {
                         *tan++ = mesh->mTangents[idx];
                         *bit++ = mesh->mBitangents[idx];
                     }
 
                     for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp) {
-                        if (!uv[pp]) break;
+                        if (!uv[pp]) 
+                            break;
                         *uv[pp]++ = mesh->mTextureCoords[pp][idx];
                     }
 
                     for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp) {
-                        if (!cols[pp]) break;
+                        if (!cols[pp]) 
+                            break;
                         *cols[pp]++ = mesh->mColors[pp][idx];
                     }
 
@@ -351,7 +349,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
                         }
                     }
                     if (pp == mesh->mNumAnimMeshes)
-                        amIdx++;
+                        ++amIdx;
 
                     in.mIndices[q] = outIdx++;
                 }

+ 9 - 13
code/PostProcessing/SplitByBoneCountProcess.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -58,9 +57,7 @@ using namespace Assimp::Formatter;
 
 // ------------------------------------------------------------------------------------------------
 // Constructor
-SplitByBoneCountProcess::SplitByBoneCountProcess() : mMaxBoneCount(AI_SBBC_DEFAULT_MAX_BONES) {
-    // empty
-}
+SplitByBoneCountProcess::SplitByBoneCountProcess() : mMaxBoneCount(AI_SBBC_DEFAULT_MAX_BONES) {}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag.
@@ -166,7 +163,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
         unsigned int numBones = 0;
         std::vector<bool> isBoneUsed( pMesh->mNumBones, false);
         // indices of the faces which are going to go into this submesh
-        std::vector<unsigned int> subMeshFaces;
+        IndexArray subMeshFaces;
         subMeshFaces.reserve( pMesh->mNumFaces);
         // accumulated vertex count of all the faces in this submesh
         unsigned int numSubMeshVertices = 0;
@@ -202,7 +199,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
             for (std::set<unsigned int>::iterator it = newBonesAtCurrentFace.begin(); it != newBonesAtCurrentFace.end(); ++it) {
                 if (!isBoneUsed[*it]) {
                     isBoneUsed[*it] = true;
-                    numBones++;
+                    ++numBones;
                 }
             }
 
@@ -212,18 +209,17 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
 
             // remember that this face is handled
             isFaceHandled[a] = true;
-            numFacesHandled++;
+            ++numFacesHandled;
         }
 
         // create a new mesh to hold this subset of the source mesh
         aiMesh* newMesh = new aiMesh;
-        if( pMesh->mName.length > 0 )
-        {
+        if( pMesh->mName.length > 0 ) {
             newMesh->mName.Set( format() << pMesh->mName.data << "_sub" << poNewMeshes.size());
         }
         newMesh->mMaterialIndex = pMesh->mMaterialIndex;
         newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
-        poNewMeshes.push_back( newMesh);
+        poNewMeshes.emplace_back( newMesh);
 
         // create all the arrays for this mesh if the old mesh contained them
         newMesh->mNumVertices = numSubMeshVertices;
@@ -251,7 +247,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
         // and copy over the data, generating faces with linear indices along the way
         newMesh->mFaces = new aiFace[subMeshFaces.size()];
         unsigned int nvi = 0; // next vertex index
-        std::vector<unsigned int> previousVertexIndices( numSubMeshVertices, std::numeric_limits<unsigned int>::max()); // per new vertex: its index in the source mesh
+        IndexArray previousVertexIndices( numSubMeshVertices, std::numeric_limits<unsigned int>::max()); // per new vertex: its index in the source mesh
         for( unsigned int a = 0; a < subMeshFaces.size(); ++a ) {
             const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
             aiFace& dstFace = newMesh->mFaces[a];
@@ -399,10 +395,10 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
 void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const {
     // rebuild the node's mesh index list
     if( pNode->mNumMeshes == 0 ) {
-        std::vector<unsigned int> newMeshList;
+        IndexArray newMeshList;
         for( unsigned int a = 0; a < pNode->mNumMeshes; ++a) {
             unsigned int srcIndex = pNode->mMeshes[a];
-            const std::vector<unsigned int>& replaceMeshes = mSubMeshIndices[srcIndex];
+            const IndexArray& replaceMeshes = mSubMeshIndices[srcIndex];
             newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
         }
 

+ 11 - 2
code/PostProcessing/SplitByBoneCountProcess.h

@@ -76,6 +76,10 @@ public:
     /// basing on the Importer's configuration property list.
     virtual void SetupProperties(const Importer* pImp) override;
 
+    /// @brief Will return the maximal number of bones.
+    /// @return The maximal number of bones.
+    size_t getMaxNumberOfBones() const;
+
 protected:
     /// Executes the post processing step on the given imported data.
     /// At the moment a process is not supposed to fail.
@@ -90,14 +94,19 @@ protected:
     /// Recursively updates the node's mesh list to account for the changed mesh list
     void UpdateNode( aiNode* pNode) const;
 
-public:
+private:
     /// Max bone count. Splitting occurs if a mesh has more than that number of bones.
     size_t mMaxBoneCount;
 
     /// Per mesh index: Array of indices of the new submeshes.
-    std::vector< std::vector<unsigned int> > mSubMeshIndices;
+    using IndexArray = std::vector<unsigned int>;
+    std::vector<IndexArray> mSubMeshIndices;
 };
 
+inline size_t SplitByBoneCountProcess::getMaxNumberOfBones() const {
+    return mMaxBoneCount;
+}
+
 } // end of namespace Assimp
 
 #endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC

+ 15 - 19
code/PostProcessing/SplitLargeMeshes.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -40,9 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-/**
- *  @file Implementation of the SplitLargeMeshes postprocessing step
- */
+ ///  @file Implementation of the SplitLargeMeshes postprocessing step
 
 // internal headers of the post-processing framework
 #include "SplitLargeMeshes.h"
@@ -75,22 +72,22 @@ void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene) {
         this->SplitMesh(a, pScene->mMeshes[a],avList);
     }
 
-    if (avList.size() != pScene->mNumMeshes) {
-        // it seems something has been split. rebuild the mesh list
-        delete[] pScene->mMeshes;
-        pScene->mNumMeshes = (unsigned int)avList.size();
-        pScene->mMeshes = new aiMesh*[avList.size()];
+    if (avList.size() == pScene->mNumMeshes) {
+        ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do");
+    }
 
-        for (unsigned int i = 0; i < avList.size();++i) {
-            pScene->mMeshes[i] = avList[i].first;
-        }
+    // it seems something has been split. rebuild the mesh list
+    delete[] pScene->mMeshes;
+    pScene->mNumMeshes = (unsigned int)avList.size();
+    pScene->mMeshes = new aiMesh*[avList.size()];
 
-        // now we need to update all nodes
-        this->UpdateNode(pScene->mRootNode,avList);
-        ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split");
-    } else {
-        ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do");
+    for (unsigned int i = 0; i < avList.size();++i) {
+        pScene->mMeshes[i] = avList[i].first;
     }
+
+    // now we need to update all nodes
+    this->UpdateNode(pScene->mRootNode,avList);
+    ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split");
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -102,8 +99,7 @@ void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) {
 
 // ------------------------------------------------------------------------------------------------
 // Update a node after some meshes have been split
-void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode,
-        const std::vector<std::pair<aiMesh*, unsigned int> >& avList) {
+void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& avList) {
     // for every index in out list build a new entry
     std::vector<unsigned int> aiEntries;
     aiEntries.reserve(pcNode->mNumMeshes + 1);

+ 2 - 4
code/PostProcessing/TextureTransform.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -42,8 +41,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 /** @file A helper class that processes texture transformations */
 
-
-
 #include <assimp/Importer.hpp>
 #include <assimp/postprocess.h>
 #include <assimp/DefaultLogger.hpp>
@@ -494,8 +491,9 @@ void TextureTransformStep::Execute( aiScene* pScene) {
             ai_assert(nullptr != src);
 
             // Copy the data to the destination array
-            if (dest != src)
+            if (dest != src) {
                 ::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices);
+            }
 
             end = dest + mesh->mNumVertices;
 

+ 4 - 41
code/PostProcessing/TriangulateProcess.cpp

@@ -158,15 +158,13 @@ namespace {
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
-bool TriangulateProcess::IsActive( unsigned int pFlags) const
-{
+bool TriangulateProcess::IsActive( unsigned int pFlags) const {
     return (pFlags & aiProcess_Triangulate) != 0;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
-void TriangulateProcess::Execute( aiScene* pScene)
-{
+void TriangulateProcess::Execute( aiScene* pScene) {
     ASSIMP_LOG_DEBUG("TriangulateProcess begin");
 
     bool bHas = false;
@@ -187,8 +185,7 @@ void TriangulateProcess::Execute( aiScene* pScene)
 
 // ------------------------------------------------------------------------------------------------
 // Triangulates the given mesh.
-bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
-{
+bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
     // Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases
     if (!pMesh->mPrimitiveTypes)    {
         bool bNeed = false;
@@ -218,8 +215,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
         if( face.mNumIndices <= 3) {
             numOut++;
 
-        }
-        else {
+        } else {
             numOut += face.mNumIndices-2;
             max_out = std::max(max_out,face.mNumIndices);
         }
@@ -511,22 +507,6 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
 #endif
                     num = 0;
                     break;
-
-                    /*curOut -= (max-num); // undo all previous work
-                    for (tmp = 0; tmp < max-2; ++tmp) {
-                        aiFace& nface = *curOut++;
-
-                        nface.mNumIndices = 3;
-                        if (!nface.mIndices)
-                            nface.mIndices = new unsigned int[3];
-
-                        nface.mIndices[0] = 0;
-                        nface.mIndices[1] = tmp+1;
-                        nface.mIndices[2] = tmp+2;
-
-                    }
-                    num = 0;
-                    break;*/
                 }
 
                 aiFace& nface = *curOut++;
@@ -580,23 +560,6 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
         for(aiFace* f = last_face; f != curOut; ) {
             unsigned int* i = f->mIndices;
 
-            //  drop dumb 0-area triangles - deactivated for now:
-            //FindDegenerates post processing step can do the same thing
-            //if (std::fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) {
-            //    ASSIMP_LOG_VERBOSE_DEBUG("Dropping triangle with area 0");
-            //    --curOut;
-
-            //    delete[] f->mIndices;
-            //    f->mIndices = nullptr;
-
-            //    for(aiFace* ff = f; ff != curOut; ++ff) {
-            //        ff->mNumIndices = (ff+1)->mNumIndices;
-            //        ff->mIndices = (ff+1)->mIndices;
-            //        (ff+1)->mIndices = nullptr;
-            //    }
-            //    continue;
-            //}
-
             i[0] = idx[i[0]];
             i[1] = idx[i[1]];
             i[2] = idx[i[2]];

+ 37 - 50
code/PostProcessing/ValidateDataStructure.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -110,18 +108,21 @@ inline int HasNameMatch(const aiString &in, aiNode *node) {
 template <typename T>
 inline void ValidateDSProcess::DoValidation(T **parray, unsigned int size, const char *firstName, const char *secondName) {
     // validate all entries
-    if (size) {
-        if (!parray) {
-            ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
-                    firstName, secondName, size);
-        }
-        for (unsigned int i = 0; i < size; ++i) {
-            if (!parray[i]) {
-                ReportError("aiScene::%s[%i] is nullptr (aiScene::%s is %i)",
-                        firstName, i, secondName, size);
-            }
-            Validate(parray[i]);
+    if (size == 0) {
+        return;
+    }
+
+    if (!parray) {
+        ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
+                firstName, secondName, size);
+    }
+
+    for (unsigned int i = 0; i < size; ++i) {
+        if (!parray[i]) {
+            ReportError("aiScene::%s[%i] is nullptr (aiScene::%s is %i)",
+                    firstName, i, secondName, size);
         }
+        Validate(parray[i]);
     }
 }
 
@@ -130,25 +131,27 @@ template <typename T>
 inline void ValidateDSProcess::DoValidationEx(T **parray, unsigned int size,
         const char *firstName, const char *secondName) {
     // validate all entries
-    if (size) {
-        if (!parray) {
-            ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
-                    firstName, secondName, size);
-        }
-        for (unsigned int i = 0; i < size; ++i) {
-            if (!parray[i]) {
-                ReportError("aiScene::%s[%u] is nullptr (aiScene::%s is %u)",
-                        firstName, i, secondName, size);
-            }
-            Validate(parray[i]);
-
-            // check whether there are duplicate names
-            for (unsigned int a = i + 1; a < size; ++a) {
-                if (parray[i]->mName == parray[a]->mName) {
-                    ReportError("aiScene::%s[%u] has the same name as "
-                                "aiScene::%s[%u]",
-                            firstName, i, secondName, a);
-                }
+    if (size == 0) {
+        return;
+    }
+        
+    if (!parray) {
+        ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
+                firstName, secondName, size);
+    }
+    for (unsigned int i = 0; i < size; ++i) {
+        if (!parray[i]) {
+            ReportError("aiScene::%s[%u] is nullptr (aiScene::%s is %u)",
+                    firstName, i, secondName, size);
+        }
+        Validate(parray[i]);
+
+        // check whether there are duplicate names
+        for (unsigned int a = i + 1; a < size; ++a) {
+            if (parray[i]->mName == parray[a]->mName) {
+                ReportError("aiScene::%s[%u] has the same name as "
+                            "aiScene::%s[%u]",
+                        firstName, i, secondName, a);
             }
         }
     }
@@ -229,12 +232,6 @@ void ValidateDSProcess::Execute(aiScene *pScene) {
     if (pScene->mNumMaterials) {
         DoValidation(pScene->mMaterials, pScene->mNumMaterials, "mMaterials", "mNumMaterials");
     }
-#if 0
-    // NOTE: ScenePreprocessor generates a default material if none is there
-    else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) {
-        ReportError("aiScene::mNumMaterials is 0. At least one material must be there");
-    }
-#endif
     else if (pScene->mMaterials) {
         ReportError("aiScene::mMaterials is non-null although there are no materials");
     }
@@ -267,8 +264,7 @@ void ValidateDSProcess::Validate(const aiCamera *pCamera) {
     if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear)
         ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear");
 
-    // FIX: there are many 3ds files with invalid FOVs. No reason to
-    // reject them at all ... a warning is appropriate.
+    // There are many 3ds files with invalid FOVs. No reason to reject them at all ... a warning is appropriate.
     if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI)
         ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV", pCamera->mHorizontalFOV);
 }
@@ -361,15 +357,6 @@ void ValidateDSProcess::Validate(const aiMesh *pMesh) {
             if (face.mIndices[a] >= pMesh->mNumVertices) {
                 ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range", i, a);
             }
-            // the MSB flag is temporarily used by the extra verbose
-            // mode to tell us that the JoinVerticesProcess might have
-            // been executed already.
-            /*if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) &&
-				abRefList[face.mIndices[a]])
-            {
-                ReportError("aiMesh::mVertices[%i] is referenced twice - second "
-                    "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a);
-            }*/
             abRefList[face.mIndices[a]] = true;
         }
     }
@@ -465,7 +452,7 @@ void ValidateDSProcess::Validate(const aiMesh *pMesh, const aiBone *pBone, float
     this->Validate(&pBone->mName);
 
     if (!pBone->mNumWeights) {
-        //ReportError("aiBone::mNumWeights is zero");
+        ReportWarning("aiBone::mNumWeights is zero");
     }
 
     // check whether all vertices affected by this bone are valid

+ 8 - 0
fuzz/assimp_fuzzer.cc

@@ -40,6 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 #include <assimp/cimport.h>
 #include <assimp/Importer.hpp>
+#include <assimp/Exporter.hpp>
 #include <assimp/scene.h>
 #include <assimp/postprocess.h>
 
@@ -53,6 +54,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) {
     const aiScene *sc = importer.ReadFileFromMemory(data, dataSize,
         aiProcessPreset_TargetRealtime_Quality, nullptr );
 
+    if (sc == nullptr) {
+        return 0;
+    }
+
+    Exporter exporter;
+    exporter.ExportToBlob(sc, "fbx");
+    
     aiDetachLogStream(&stream);
 
     return 0;

+ 1 - 4
include/assimp/StreamWriter.h

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -68,8 +66,7 @@ namespace Assimp {
  */
 // --------------------------------------------------------------------------------------------
 template <bool SwapEndianess = false, bool RuntimeSwitch = false>
-class StreamWriter
-{
+class StreamWriter {
     enum {
         INITIAL_CAPACITY = 1024
     };

+ 12 - 15
include/assimp/Vertex.h

@@ -97,15 +97,21 @@ namespace Assimp    {
  *  to *all* vertex components equally. This is useful for stuff like interpolation
  *  or subdivision, but won't work if special handling is required for some vertex components. */
 // ------------------------------------------------------------------------------------------------
-class Vertex {
+struct Vertex {
     friend Vertex operator + (const Vertex&,const Vertex&);
     friend Vertex operator - (const Vertex&,const Vertex&);
     friend Vertex operator * (const Vertex&,ai_real);
     friend Vertex operator / (const Vertex&,ai_real);
     friend Vertex operator * (ai_real, const Vertex&);
 
-public:
-    Vertex() {}
+    aiVector3D position;
+    aiVector3D normal;
+    aiVector3D tangent, bitangent;
+
+    aiVector3D texcoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+    aiColor4D colors[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+    Vertex() = default;
 
     // ----------------------------------------------------------------------------
     /** Extract a particular vertex from a mesh and interleave all components */
@@ -178,7 +184,7 @@ public:
     }
 
     // ----------------------------------------------------------------------------
-    /** Convert back to non-interleaved storage */
+    /// Convert back to non-interleaved storage
     void SortBack(aiMesh* out, unsigned int idx) const {
         ai_assert(idx<out->mNumVertices);
         out->mVertices[idx] = position;
@@ -204,7 +210,7 @@ public:
 private:
 
     // ----------------------------------------------------------------------------
-    /** Construct from two operands and a binary operation to combine them */
+    /// Construct from two operands and a binary operation to combine them
     template <template <typename t> class op> static Vertex BinaryOp(const Vertex& v0, const Vertex& v1) {
         // this is a heavy task for the compiler to optimize ... *pray*
 
@@ -224,7 +230,7 @@ private:
     }
 
     // ----------------------------------------------------------------------------
-    /** This time binary arithmetic of v0 with a floating-point number */
+    /// This time binary arithmetic of v0 with a floating-point number
     template <template <typename, typename, typename> class op> static Vertex BinaryOp(const Vertex& v0, ai_real f) {
         // this is a heavy task for the compiler to optimize ... *pray*
 
@@ -262,15 +268,6 @@ private:
         }
         return res;
     }
-
-public:
-
-    aiVector3D position;
-    aiVector3D normal;
-    aiVector3D tangent, bitangent;
-
-    aiVector3D texcoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
-    aiColor4D colors[AI_MAX_NUMBER_OF_COLOR_SETS];
 };
 
 // ------------------------------------------------------------------------------------------------

+ 11 - 0
include/assimp/config.h.in

@@ -1065,6 +1065,17 @@ enum aiComponent
  */
 #define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS"
 
+/** @brief Specifies whether to use the deprecated KHR_materials_pbrSpecularGlossiness extension
+ * 
+ * When this flag is undefined any material with specularity will use the new KHR_materials_specular
+ * extension. Enabling this flag will revert to the deprecated extension. Note that exporting
+ * KHR_materials_pbrSpecularGlossiness with extensions other than KHR_materials_unlit is unsupported,
+ * including the basic pbrMetallicRoughness spec.
+ *
+ * Property type: Bool. Default value: false.
+ */
+#define AI_CONFIG_USE_GLTF_PBR_SPECULAR_GLOSSINESS "USE_GLTF_PBR_SPECULAR_GLOSSINESS"
+
 /**
  * @brief Specifies the blob name, assimp uses for exporting.
  * 

+ 5 - 1
include/assimp/defs.h

@@ -289,7 +289,11 @@ typedef unsigned int ai_uint;
 #define AI_RAD_TO_DEG(x) ((x) * (ai_real) 57.2957795)
 
 /* Numerical limits */
-static const ai_real ai_epsilon = (ai_real) 1e-6;
+#ifdef __cplusplus
+constexpr ai_real ai_epsilon = (ai_real) 1e-6;
+#else
+const ai_real ai_epsilon = (ai_real) 1e-6;
+#endif
 
 /* Support for big-endian builds */
 #if defined(__BYTE_ORDER__)

+ 12 - 6
test/CMakeLists.txt

@@ -102,6 +102,10 @@ SET( COMMON
   unit/Common/utBaseProcess.cpp
 )
 
+SET(Geometry 
+    unit/Geometry/utGeometryUtils.cpp
+)
+
 SET( IMPORTERS
   unit/ImportExport/Assxml/utAssxmlImportExport.cpp
   unit/utLWSImportExport.cpp
@@ -200,12 +204,13 @@ SET( POST_PROCESSES
   unit/utGenBoundingBoxesProcess.cpp
 )
 
-SOURCE_GROUP( UnitTests\\Compiler     FILES  unit/CCompilerTest.c )
-SOURCE_GROUP( UnitTests\\Common       FILES  ${COMMON} )
-SOURCE_GROUP( UnitTests\\ImportExport FILES  ${IMPORTERS} )
-SOURCE_GROUP( UnitTests\\Material     FILES  ${MATERIAL} )
-SOURCE_GROUP( UnitTests\\Math         FILES  ${MATH} )
-SOURCE_GROUP( UnitTests\\PostProcess  FILES  ${POST_PROCESSES})
+SOURCE_GROUP( UnitTests\\Compiler      FILES unit/CCompilerTest.c )
+SOURCE_GROUP( UnitTests\\Common        FILES ${COMMON} )
+SOURCE_GROUP( UnitTests\\GeometryTools FILES ${Geometry} )
+SOURCE_GROUP( UnitTests\\ImportExport  FILES ${IMPORTERS} )
+SOURCE_GROUP( UnitTests\\Material      FILES ${MATERIAL} )
+SOURCE_GROUP( UnitTests\\Math          FILES ${MATH} )
+SOURCE_GROUP( UnitTests\\PostProcess   FILES ${POST_PROCESSES})
 
 add_executable( unit
     unit/CCompilerTest.c
@@ -213,6 +218,7 @@ add_executable( unit
     ../code/Common/Version.cpp
 	../code/Common/Base64.cpp
 	${COMMON}
+  ${Geometry}
 	${IMPORTERS}
 	${MATERIAL}
 	${MATH}

+ 68 - 0
test/unit/Geometry/utGeometryUtils.cpp

@@ -0,0 +1,68 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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.
+---------------------------------------------------------------------------
+*/
+
+#include "UnitTestPCH.h"
+#include "Geometry/GeometryUtils.h"
+
+using namespace Assimp;
+
+class utGeometryUtils : public ::testing::Test {
+protected:
+
+    void SetUp() override {
+    }
+
+    void TearDown() override {
+    }
+};
+static constexpr size_t NumVectors =  100;
+TEST_F(utGeometryUtils, normalizeVectorArrayTest) {
+    aiVector3D *inNormals = new aiVector3D[NumVectors];
+    for (uint32_t i=0; i<NumVectors; ++i){
+        inNormals[i].x = static_cast<ai_real>(i);
+        inNormals[i].y = static_cast<ai_real>(i);
+        inNormals[i].z = static_cast<ai_real>(i);
+    }
+    aiVector3D *outNormals = new aiVector3D[NumVectors];
+    GeometryUtils::normalizeVectorArray(inNormals, outNormals, NumVectors);
+    delete[] outNormals;
+    delete[] inNormals;
+}

+ 47 - 17
test/unit/utExport.cpp

@@ -1,38 +1,71 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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.
+---------------------------------------------------------------------------
+*/
 #include "UnitTestPCH.h"
-
 #include <assimp/cexport.h>
 #include <assimp/Exporter.hpp>
 
-
 #ifndef ASSIMP_BUILD_NO_EXPORT
 
 class ExporterTest : public ::testing::Test {
 public:
-
-    virtual void SetUp()
-    {
+    void SetUp() override {
         ex = new Assimp::Exporter();
         im = new Assimp::Importer();
 
         pTest = im->ReadFile(ASSIMP_TEST_MODELS_DIR "/X/test.x", aiProcess_ValidateDataStructure);
     }
 
-    virtual void TearDown()
-    {
+    void TearDown() override {
         delete ex;
         delete im;
     }
 
 protected:
-
     const aiScene* pTest;
     Assimp::Exporter* ex;
     Assimp::Importer* im;
 };
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(ExporterTest, testExportToFile)
-{
+TEST_F(ExporterTest, testExportToFile) {
     const char* file = "unittest_output.dae";
     EXPECT_EQ(AI_SUCCESS,ex->Export(pTest,"collada",file));
 
@@ -41,8 +74,7 @@ TEST_F(ExporterTest, testExportToFile)
 }
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(ExporterTest, testExportToBlob)
-{
+TEST_F(ExporterTest, testExportToBlob) {
     const aiExportDataBlob* blob = ex->ExportToBlob(pTest,"collada");
     ASSERT_TRUE(blob);
     EXPECT_TRUE(blob->data);
@@ -56,8 +88,7 @@ TEST_F(ExporterTest, testExportToBlob)
 }
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(ExporterTest, testCppExportInterface)
-{
+TEST_F(ExporterTest, testCppExportInterface) {
     EXPECT_TRUE(ex->GetExportFormatCount() > 0);
     for(size_t i = 0; i < ex->GetExportFormatCount(); ++i) {
         const aiExportFormatDesc* const desc = ex->GetExportFormatDescription(i);
@@ -71,14 +102,13 @@ TEST_F(ExporterTest, testCppExportInterface)
 }
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(ExporterTest, testCExportInterface)
-{
+TEST_F(ExporterTest, testCExportInterface) {
     EXPECT_TRUE(aiGetExportFormatCount() > 0);
     for(size_t i = 0; i < aiGetExportFormatCount(); ++i) {
         const aiExportFormatDesc* const desc = aiGetExportFormatDescription(i);
         EXPECT_TRUE(desc);
-        // rest has already been validated by testCppExportInterface
     }
 }
 
 #endif
+

+ 7 - 1
test/unit/utglTF2ImportExport.cpp

@@ -219,8 +219,14 @@ TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossi
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf",
             aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    // Export
+    
+    // Export with specular glossiness disabled
     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb"));
+    
+    // Export with specular glossiness enabled
+    ExportProperties props;
+    props.SetPropertyBool(AI_CONFIG_USE_GLTF_PBR_SPECULAR_GLOSSINESS, true);
+    EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", 0, &props));
 
     // And re-import
     EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true));