Browse Source

Merge pull request #5086 from assimp/kimkulling/refactoring_geoutils

Kimkulling/refactoring geoutils
Kim Kulling 2 years ago
parent
commit
cb22d531a6
31 changed files with 624 additions and 718 deletions
  1. 0 4
      code/AssetLib/3DS/3DSHelper.h
  2. 2 1
      code/AssetLib/IFC/IFCUtil.cpp
  3. 2 4
      code/AssetLib/LWO/LWOLoader.cpp
  4. 0 2
      code/AssetLib/X3D/X3DExporter.hpp
  5. 35 11
      code/Geometry/GeometryUtils.cpp
  6. 16 2
      code/Geometry/GeometryUtils.h
  7. 12 24
      code/PostProcessing/ArmaturePopulate.cpp
  8. 3 7
      code/PostProcessing/ArmaturePopulate.h
  9. 135 181
      code/PostProcessing/ComputeUVMappingProcess.cpp
  10. 0 9
      code/PostProcessing/ConvertToLHProcess.cpp
  11. 4 8
      code/PostProcessing/DeboneProcess.cpp
  12. 12 13
      code/PostProcessing/DropFaceNormalsProcess.cpp
  13. 40 43
      code/PostProcessing/FindDegenerates.cpp
  14. 151 140
      code/PostProcessing/PretransformVertices.cpp
  15. 9 9
      code/PostProcessing/PretransformVertices.h
  16. 3 4
      code/PostProcessing/ProcessHelper.cpp
  17. 9 24
      code/PostProcessing/RemoveRedundantMaterials.cpp
  18. 0 59
      code/PostProcessing/RemoveVCProcess.cpp
  19. 4 5
      code/PostProcessing/ScaleProcess.cpp
  20. 11 13
      code/PostProcessing/SortByPTypeProcess.cpp
  21. 9 13
      code/PostProcessing/SplitByBoneCountProcess.cpp
  22. 11 2
      code/PostProcessing/SplitByBoneCountProcess.h
  23. 15 19
      code/PostProcessing/SplitLargeMeshes.cpp
  24. 2 4
      code/PostProcessing/TextureTransform.cpp
  25. 4 41
      code/PostProcessing/TriangulateProcess.cpp
  26. 37 50
      code/PostProcessing/ValidateDataStructure.cpp
  27. 1 4
      include/assimp/StreamWriter.h
  28. 12 15
      include/assimp/Vertex.h
  29. 5 1
      include/assimp/defs.h
  30. 12 6
      test/CMakeLists.txt
  31. 68 0
      test/unit/Geometry/utGeometryUtils.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

+ 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);
 }
 
 // ------------------------------------------------------------------------------------------------

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

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

+ 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

+ 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

+ 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];
 };
 
 // ------------------------------------------------------------------------------------------------

+ 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;
+}