فهرست منبع

Merge branch 'master' into windows-crt

Kim Kulling 3 سال پیش
والد
کامیت
6484dd6825

+ 1 - 0
code/AssetLib/Obj/ObjFileMtlImporter.cpp

@@ -238,6 +238,7 @@ void ObjFileMtlImporter::load() {
 
             case 'a': // Anisotropy
             {
+                ++m_DataIt;
                 getFloatValue(m_pModel->m_pCurrentMaterial->anisotropy);
                 m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
             } break;

+ 187 - 186
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -42,23 +42,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
 
 #include "AssetLib/glTF2/glTF2Importer.h"
-#include "PostProcessing/MakeVerboseFormat.h"
 #include "AssetLib/glTF2/glTF2Asset.h"
+#include "PostProcessing/MakeVerboseFormat.h"
 
 #if !defined(ASSIMP_BUILD_NO_EXPORT)
 #include "AssetLib/glTF2/glTF2AssetWriter.h"
 #endif
 
 #include <assimp/CreateAnimMesh.h>
+#include <assimp/DefaultIOSystem.h>
 #include <assimp/StringComparison.h>
 #include <assimp/StringUtils.h>
 #include <assimp/ai_assert.h>
+#include <assimp/commonMetaData.h>
 #include <assimp/importerdesc.h>
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/Importer.hpp>
-#include <assimp/commonMetaData.h>
-#include <assimp/DefaultIOSystem.h>
 
 #include <memory>
 #include <unordered_map>
@@ -111,7 +111,7 @@ const aiImporterDesc *glTF2Importer::GetInfo() const {
     return &desc;
 }
 
-bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig ) const {
+bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
     const std::string extension = GetExtension(filename);
     if (!checkSig && (extension != "gltf") && (extension != "glb")) {
         return false;
@@ -127,16 +127,16 @@ bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, b
 
 static inline aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) {
     switch (gltfWrapMode) {
-        case SamplerWrap::Mirrored_Repeat:
-            return aiTextureMapMode_Mirror;
+    case SamplerWrap::Mirrored_Repeat:
+        return aiTextureMapMode_Mirror;
 
-        case SamplerWrap::Clamp_To_Edge:
-            return aiTextureMapMode_Clamp;
+    case SamplerWrap::Clamp_To_Edge:
+        return aiTextureMapMode_Clamp;
 
-        case SamplerWrap::UNSET:
-        case SamplerWrap::Repeat:
-        default:
-            return aiTextureMapMode_Wrap;
+    case SamplerWrap::UNSET:
+    case SamplerWrap::Repeat:
+    default:
+        return aiTextureMapMode_Wrap;
     }
 }
 
@@ -185,8 +185,9 @@ static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
             // coordinate of the actual meshes during import.
             const ai_real rcos(cos(-transform.mRotation));
             const ai_real rsin(sin(-transform.mRotation));
-            transform.mTranslation.x = (static_cast<ai_real>( 0.5 ) * transform.mScaling.x) * (-rcos + rsin + 1) + prop.TextureTransformExt_t.offset[0];
-            transform.mTranslation.y = ((static_cast<ai_real>( 0.5 ) * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y - prop.TextureTransformExt_t.offset[1];;
+            transform.mTranslation.x = (static_cast<ai_real>(0.5) * transform.mScaling.x) * (-rcos + rsin + 1) + prop.TextureTransformExt_t.offset[0];
+            transform.mTranslation.y = ((static_cast<ai_real>(0.5) * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y - prop.TextureTransformExt_t.offset[1];
+            ;
 
             mat->AddProperty(&transform, 1, _AI_MATKEY_UVTRANSFORM_BASE, texType, texSlot);
         }
@@ -259,7 +260,10 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
         SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE);
         SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR);
 
+        // Keep AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE for backwards compatibility
         SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
+        SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, aiTextureType_METALNESS);
+        SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, aiTextureType_DIFFUSE_ROUGHNESS);
 
         aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR);
         aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR);
@@ -305,7 +309,6 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
 
         aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL);
 
-
         // KHR_materials_sheen
         if (mat.materialSheen.isPresent) {
             MaterialSheen &sheen = mat.materialSheen.value;
@@ -378,7 +381,7 @@ void glTF2Importer::ImportMaterials(Asset &r) {
     }
 }
 
-static inline void SetFaceAndAdvance1(aiFace*& face, unsigned int numVertices, unsigned int a) {
+static inline void SetFaceAndAdvance1(aiFace *&face, unsigned int numVertices, unsigned int a) {
     if (a >= numVertices) {
         return;
     }
@@ -388,7 +391,7 @@ static inline void SetFaceAndAdvance1(aiFace*& face, unsigned int numVertices, u
     ++face;
 }
 
-static inline void SetFaceAndAdvance2(aiFace*& face, unsigned int numVertices,
+static inline void SetFaceAndAdvance2(aiFace *&face, unsigned int numVertices,
         unsigned int a, unsigned int b) {
     if ((a >= numVertices) || (b >= numVertices)) {
         return;
@@ -400,7 +403,7 @@ static inline void SetFaceAndAdvance2(aiFace*& face, unsigned int numVertices,
     ++face;
 }
 
-static inline void SetFaceAndAdvance3(aiFace*& face, unsigned int numVertices, unsigned int a,
+static inline void SetFaceAndAdvance3(aiFace *&face, unsigned int numVertices, unsigned int a,
         unsigned int b, unsigned int c) {
     if ((a >= numVertices) || (b >= numVertices) || (c >= numVertices)) {
         return;
@@ -427,17 +430,16 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign
 }
 #endif // ASSIMP_BUILD_DEBUG
 
-template<typename T>
-aiColor4D* GetVertexColorsForType(Ref<Accessor> input) {
+template <typename T>
+aiColor4D *GetVertexColorsForType(Ref<Accessor> input) {
     constexpr float max = std::numeric_limits<T>::max();
-    aiColor4t<T>* colors;
+    aiColor4t<T> *colors;
     input->ExtractData(colors);
     auto output = new aiColor4D[input->count];
     for (size_t i = 0; i < input->count; i++) {
         output[i] = aiColor4D(
-            colors[i].r / max, colors[i].g / max,
-            colors[i].b / max, colors[i].a / max
-        );
+                colors[i].r / max, colors[i].g / max,
+                colors[i].b / max, colors[i].a / max);
     }
     delete[] colors;
     return output;
@@ -471,21 +473,21 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
             }
 
             switch (prim.mode) {
-                case PrimitiveMode_POINTS:
-                    aim->mPrimitiveTypes |= aiPrimitiveType_POINT;
-                    break;
+            case PrimitiveMode_POINTS:
+                aim->mPrimitiveTypes |= aiPrimitiveType_POINT;
+                break;
 
-                case PrimitiveMode_LINES:
-                case PrimitiveMode_LINE_LOOP:
-                case PrimitiveMode_LINE_STRIP:
-                    aim->mPrimitiveTypes |= aiPrimitiveType_LINE;
-                    break;
+            case PrimitiveMode_LINES:
+            case PrimitiveMode_LINE_LOOP:
+            case PrimitiveMode_LINE_STRIP:
+                aim->mPrimitiveTypes |= aiPrimitiveType_LINE;
+                break;
 
-                case PrimitiveMode_TRIANGLES:
-                case PrimitiveMode_TRIANGLE_STRIP:
-                case PrimitiveMode_TRIANGLE_FAN:
-                    aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
-                    break;
+            case PrimitiveMode_TRIANGLES:
+            case PrimitiveMode_TRIANGLE_STRIP:
+            case PrimitiveMode_TRIANGLE_FAN:
+                aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+                break;
             }
 
             Mesh::Primitive::Attributes &attr = prim.attributes;
@@ -528,7 +530,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
             for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) {
                 if (attr.color[c]->count != aim->mNumVertices) {
                     DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name,
-                                               "\" does not match the vertex count");
+                            "\" does not match the vertex count");
                     continue;
                 }
 
@@ -551,7 +553,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 
                 if (attr.texcoord[tc]->count != aim->mNumVertices) {
                     DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name,
-                                               "\" does not match the vertex count");
+                            "\" does not match the vertex count");
                     continue;
                 }
 
@@ -644,77 +646,77 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                 }
 
                 switch (prim.mode) {
-                    case PrimitiveMode_POINTS: {
-                        nFaces = count;
-                        facePtr = faces = new aiFace[nFaces];
-                        for (unsigned int i = 0; i < count; ++i) {
-                            SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i));
-                        }
-                        break;
+                case PrimitiveMode_POINTS: {
+                    nFaces = count;
+                    facePtr = faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; ++i) {
+                        SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i));
                     }
+                    break;
+                }
 
-                    case PrimitiveMode_LINES: {
-                        nFaces = count / 2;
-                        if (nFaces * 2 != count) {
-                            ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
-                            count = nFaces * 2;
-                        }
-                        facePtr = faces = new aiFace[nFaces];
-                        for (unsigned int i = 0; i < count; i += 2) {
-                            SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1));
-                        }
-                        break;
+                case PrimitiveMode_LINES: {
+                    nFaces = count / 2;
+                    if (nFaces * 2 != count) {
+                        ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
+                        count = nFaces * 2;
+                    }
+                    facePtr = faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; i += 2) {
+                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1));
                     }
+                    break;
+                }
 
-                    case PrimitiveMode_LINE_LOOP:
-                    case PrimitiveMode_LINE_STRIP: {
-                        nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
-                        facePtr = faces = new aiFace[nFaces];
-                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1));
-                        for (unsigned int i = 2; i < count; ++i) {
-                            SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i - 1), data.GetUInt(i));
-                        }
-                        if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
-                            SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(static_cast<int>(count) - 1), faces[0].mIndices[0]);
-                        }
-                        break;
+                case PrimitiveMode_LINE_LOOP:
+                case PrimitiveMode_LINE_STRIP: {
+                    nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
+                    facePtr = faces = new aiFace[nFaces];
+                    SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1));
+                    for (unsigned int i = 2; i < count; ++i) {
+                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i - 1), data.GetUInt(i));
                     }
+                    if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
+                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(static_cast<int>(count) - 1), faces[0].mIndices[0]);
+                    }
+                    break;
+                }
 
-                    case PrimitiveMode_TRIANGLES: {
-                        nFaces = count / 3;
-                        if (nFaces * 3 != count) {
-                            ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
-                            count = nFaces * 3;
-                        }
-                        facePtr = faces = new aiFace[nFaces];
-                        for (unsigned int i = 0; i < count; i += 3) {
+                case PrimitiveMode_TRIANGLES: {
+                    nFaces = count / 3;
+                    if (nFaces * 3 != count) {
+                        ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
+                        count = nFaces * 3;
+                    }
+                    facePtr = faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; i += 3) {
+                        SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
+                    }
+                    break;
+                }
+                case PrimitiveMode_TRIANGLE_STRIP: {
+                    nFaces = count - 2;
+                    facePtr = faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < nFaces; ++i) {
+                        //The ordering is to ensure that the triangles are all drawn with the same orientation
+                        if ((i + 1) % 2 == 0) {
+                            //For even n, vertices n + 1, n, and n + 2 define triangle n
+                            SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
+                        } else {
+                            //For odd n, vertices n, n+1, and n+2 define triangle n
                             SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
                         }
-                        break;
                     }
-                    case PrimitiveMode_TRIANGLE_STRIP: {
-                        nFaces = count - 2;
-                        facePtr = faces = new aiFace[nFaces];
-                        for (unsigned int i = 0; i < nFaces; ++i) {
-                            //The ordering is to ensure that the triangles are all drawn with the same orientation
-                            if ((i + 1) % 2 == 0) {
-                                //For even n, vertices n + 1, n, and n + 2 define triangle n
-                                SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
-                            } else {
-                                //For odd n, vertices n, n+1, and n+2 define triangle n
-                                SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
-                            }
-                        }
-                        break;
+                    break;
+                }
+                case PrimitiveMode_TRIANGLE_FAN:
+                    nFaces = count - 2;
+                    facePtr = faces = new aiFace[nFaces];
+                    SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
+                    for (unsigned int i = 1; i < nFaces; ++i) {
+                        SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(i + 1), data.GetUInt(i + 2));
                     }
-                    case PrimitiveMode_TRIANGLE_FAN:
-                        nFaces = count - 2;
-                        facePtr = faces = new aiFace[nFaces];
-                        SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
-                        for (unsigned int i = 1; i < nFaces; ++i) {
-                            SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(i + 1), data.GetUInt(i + 2));
-                        }
-                        break;
+                    break;
                 }
             } else { // no indices provided so directly generate from counts
 
@@ -722,77 +724,77 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                 unsigned int count = aim->mNumVertices;
 
                 switch (prim.mode) {
-                    case PrimitiveMode_POINTS: {
-                        nFaces = count;
-                        facePtr = faces = new aiFace[nFaces];
-                        for (unsigned int i = 0; i < count; ++i) {
-                            SetFaceAndAdvance1(facePtr, aim->mNumVertices, i);
-                        }
-                        break;
+                case PrimitiveMode_POINTS: {
+                    nFaces = count;
+                    facePtr = faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; ++i) {
+                        SetFaceAndAdvance1(facePtr, aim->mNumVertices, i);
                     }
+                    break;
+                }
 
-                    case PrimitiveMode_LINES: {
-                        nFaces = count / 2;
-                        if (nFaces * 2 != count) {
-                            ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
-                            count = (unsigned int)nFaces * 2;
-                        }
-                        facePtr = faces = new aiFace[nFaces];
-                        for (unsigned int i = 0; i < count; i += 2) {
-                            SetFaceAndAdvance2(facePtr, aim->mNumVertices, i, i + 1);
-                        }
-                        break;
+                case PrimitiveMode_LINES: {
+                    nFaces = count / 2;
+                    if (nFaces * 2 != count) {
+                        ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
+                        count = (unsigned int)nFaces * 2;
+                    }
+                    facePtr = faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; i += 2) {
+                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, i, i + 1);
                     }
+                    break;
+                }
 
-                    case PrimitiveMode_LINE_LOOP:
-                    case PrimitiveMode_LINE_STRIP: {
-                        nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
-                        facePtr = faces = new aiFace[nFaces];
-                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, 0, 1);
-                        for (unsigned int i = 2; i < count; ++i) {
-                            SetFaceAndAdvance2(facePtr, aim->mNumVertices, i - 1, i);
-                        }
-                        if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
-                            SetFaceAndAdvance2(facePtr, aim->mNumVertices, count - 1, 0);
-                        }
-                        break;
+                case PrimitiveMode_LINE_LOOP:
+                case PrimitiveMode_LINE_STRIP: {
+                    nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
+                    facePtr = faces = new aiFace[nFaces];
+                    SetFaceAndAdvance2(facePtr, aim->mNumVertices, 0, 1);
+                    for (unsigned int i = 2; i < count; ++i) {
+                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, i - 1, i);
                     }
+                    if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
+                        SetFaceAndAdvance2(facePtr, aim->mNumVertices, count - 1, 0);
+                    }
+                    break;
+                }
 
-                    case PrimitiveMode_TRIANGLES: {
-                        nFaces = count / 3;
-                        if (nFaces * 3 != count) {
-                            ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
-                            count = (unsigned int)nFaces * 3;
-                        }
-                        facePtr = faces = new aiFace[nFaces];
-                        for (unsigned int i = 0; i < count; i += 3) {
+                case PrimitiveMode_TRIANGLES: {
+                    nFaces = count / 3;
+                    if (nFaces * 3 != count) {
+                        ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
+                        count = (unsigned int)nFaces * 3;
+                    }
+                    facePtr = faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; i += 3) {
+                        SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2);
+                    }
+                    break;
+                }
+                case PrimitiveMode_TRIANGLE_STRIP: {
+                    nFaces = count - 2;
+                    facePtr = faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < nFaces; ++i) {
+                        //The ordering is to ensure that the triangles are all drawn with the same orientation
+                        if ((i + 1) % 2 == 0) {
+                            //For even n, vertices n + 1, n, and n + 2 define triangle n
+                            SetFaceAndAdvance3(facePtr, aim->mNumVertices, i + 1, i, i + 2);
+                        } else {
+                            //For odd n, vertices n, n+1, and n+2 define triangle n
                             SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2);
                         }
-                        break;
                     }
-                    case PrimitiveMode_TRIANGLE_STRIP: {
-                        nFaces = count - 2;
-                        facePtr = faces = new aiFace[nFaces];
-                        for (unsigned int i = 0; i < nFaces; ++i) {
-                            //The ordering is to ensure that the triangles are all drawn with the same orientation
-                            if ((i + 1) % 2 == 0) {
-                                //For even n, vertices n + 1, n, and n + 2 define triangle n
-                                SetFaceAndAdvance3(facePtr, aim->mNumVertices, i + 1, i, i + 2);
-                            } else {
-                                //For odd n, vertices n, n+1, and n+2 define triangle n
-                                SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2);
-                            }
-                        }
-                        break;
+                    break;
+                }
+                case PrimitiveMode_TRIANGLE_FAN:
+                    nFaces = count - 2;
+                    facePtr = faces = new aiFace[nFaces];
+                    SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, 1, 2);
+                    for (unsigned int i = 1; i < nFaces; ++i) {
+                        SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, i + 1, i + 2);
                     }
-                    case PrimitiveMode_TRIANGLE_FAN:
-                        nFaces = count - 2;
-                        facePtr = faces = new aiFace[nFaces];
-                        SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, 1, 2);
-                        for (unsigned int i = 1; i < nFaces; ++i) {
-                            SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, i + 1, i + 2);
-                        }
-                        break;
+                    break;
                 }
             }
 
@@ -876,15 +878,15 @@ void glTF2Importer::ImportLights(glTF2::Asset &r) {
         aiLight *ail = mScene->mLights[i] = new aiLight();
 
         switch (light.type) {
-            case Light::Directional:
-                ail->mType = aiLightSource_DIRECTIONAL;
-                break;
-            case Light::Point:
-                ail->mType = aiLightSource_POINT;
-                break;
-            case Light::Spot:
-                ail->mType = aiLightSource_SPOT;
-                break;
+        case Light::Directional:
+            ail->mType = aiLightSource_DIRECTIONAL;
+            break;
+        case Light::Point:
+            ail->mType = aiLightSource_POINT;
+            break;
+        case Light::Spot:
+            ail->mType = aiLightSource_SPOT;
+            break;
         }
 
         if (ail->mType != aiLightSource_POINT) {
@@ -926,7 +928,7 @@ static void GetNodeTransform(aiMatrix4x4 &matrix, const glTF2::Node &node) {
     if (node.matrix.isPresent) {
         CopyValue(node.matrix.value, matrix);
         return;
-    } 
+    }
 
     if (node.translation.isPresent) {
         aiVector3D trans;
@@ -1021,7 +1023,7 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
         metadata->Add(extension.name, extension.mBoolValue.value);
     } else if (extension.mValues.isPresent) {
         aiMetadata val;
-        for (auto const & subExtension : extension.mValues.value) {
+        for (auto const &subExtension : extension.mValues.value) {
             ParseExtensions(&val, subExtension);
         }
         metadata->Add(extension.name, val);
@@ -1030,7 +1032,7 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
 
 void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) {
     if (extension.mValues.isPresent) {
-        for (auto const & subExtension : extension.mValues.value) {
+        for (auto const &subExtension : extension.mValues.value) {
             ParseExtensions(metadata, subExtension);
         }
     }
@@ -1068,11 +1070,10 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
 
         if (!node.meshes.empty()) {
             // GLTF files contain at most 1 mesh per node.
-            if (node.meshes.size() > 1)
-            {
+            if (node.meshes.size() > 1) {
                 throw DeadlyImportError("GLTF: Invalid input, found ", node.meshes.size(),
-                    " meshes in ", getContextForErrorMessages(node.id, node.name),
-                    ", but only 1 mesh per node allowed.");
+                        " meshes in ", getContextForErrorMessages(node.id, node.name),
+                        ", but only 1 mesh per node allowed.");
             }
             int mesh_idx = node.meshes[0].GetIndex();
             int count = meshOffsets[mesh_idx + 1] - meshOffsets[mesh_idx];
@@ -1083,7 +1084,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
             if (node.skin) {
                 for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) {
                     aiMesh *mesh = pScene->mMeshes[meshOffsets[mesh_idx] + primitiveNo];
-                    unsigned int numBones =static_cast<unsigned int>(node.skin->jointNames.size());
+                    unsigned int numBones = static_cast<unsigned int>(node.skin->jointNames.size());
 
                     std::vector<std::vector<aiVertexWeight>> weighting(numBones);
                     BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting);
@@ -1222,7 +1223,7 @@ struct AnimationSamplers {
     Animation::Sampler *weight;
 };
 
-aiNodeAnim *CreateNodeAnim(glTF2::Asset&, Node &node, AnimationSamplers &samplers) {
+aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &samplers) {
     aiNodeAnim *anim = new aiNodeAnim();
 
     try {
@@ -1313,7 +1314,7 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset&, Node &node, AnimationSamplers &sampler
     }
 }
 
-aiMeshMorphAnim *CreateMeshMorphAnim(glTF2::Asset&, Node &node, AnimationSamplers &samplers) {
+aiMeshMorphAnim *CreateMeshMorphAnim(glTF2::Asset &, Node &node, AnimationSamplers &samplers) {
     auto *anim = new aiMeshMorphAnim();
 
     try {
@@ -1366,7 +1367,7 @@ std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation &an
             continue;
         }
 
-        auto& animsampler = anim.samplers[channel.sampler];
+        auto &animsampler = anim.samplers[channel.sampler];
 
         if (!animsampler.input) {
             ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping.");
@@ -1555,9 +1556,9 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
             if (ext) {
                 if (strcmp(ext, "jpeg") == 0) {
                     ext = "jpg";
-                } else if(strcmp(ext, "ktx2") == 0) { //basisu: ktx remains
+                } else if (strcmp(ext, "ktx2") == 0) { //basisu: ktx remains
                     ext = "kx2";
-                } else if(strcmp(ext, "basis") == 0) { //basisu
+                } else if (strcmp(ext, "basis") == 0) { //basisu
                     ext = "bu";
                 }
 
@@ -1570,7 +1571,7 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
     }
 }
 
-void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) {
+void glTF2Importer::ImportCommonMetadata(glTF2::Asset &a) {
     ASSIMP_LOG_DEBUG("Importing metadata");
     ai_assert(mScene->mMetaData == nullptr);
     const bool hasVersion = !a.asset.version.empty();
@@ -1604,7 +1605,7 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
     this->mScene = pScene;
 
     // read the asset file
-  	glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider*>(mSchemaDocumentProvider));
+    glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider *>(mSchemaDocumentProvider));
     asset.Load(pFile, GetExtension(pFile) == "glb");
     if (asset.scene) {
         pScene->mName = asset.scene->name;
@@ -1631,7 +1632,7 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
 }
 
 void glTF2Importer::SetupProperties(const Importer *pImp) {
-    mSchemaDocumentProvider = static_cast<rapidjson::IRemoteSchemaDocumentProvider*>(pImp->GetPropertyPointer(AI_CONFIG_IMPORT_SCHEMA_DOCUMENT_PROVIDER));
+    mSchemaDocumentProvider = static_cast<rapidjson::IRemoteSchemaDocumentProvider *>(pImp->GetPropertyPointer(AI_CONFIG_IMPORT_SCHEMA_DOCUMENT_PROVIDER));
 }
 
 #endif // ASSIMP_BUILD_NO_GLTF_IMPORTER

+ 1 - 0
code/CMakeLists.txt

@@ -78,6 +78,7 @@ SET( PUBLIC_HEADERS
   ${HEADER_PATH}/matrix4x4.h
   ${HEADER_PATH}/matrix4x4.inl
   ${HEADER_PATH}/mesh.h
+  ${HEADER_PATH}/ObjMaterial.h
   ${HEADER_PATH}/pbrmaterial.h
   ${HEADER_PATH}/GltfMaterial.h
   ${HEADER_PATH}/postprocess.h

+ 2 - 0
code/PostProcessing/ArmaturePopulate.cpp

@@ -90,12 +90,14 @@ void ArmaturePopulate::Execute(aiScene *out) {
 
         ai_assert(armature);
 
+#ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS
         // set up bone armature id
         bone->mArmature = armature;
 
         // set this bone node to be referenced properly
         ai_assert(bone_node);
         bone->mNode = bone_node;
+#endif
     }
 }
 

+ 2 - 2
doc/Doxyfile.in

@@ -32,13 +32,13 @@ PROJECT_NAME           = Assimp
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 
-PROJECT_NUMBER         = "v5.0.1. (December 2020)"
+PROJECT_NUMBER         = "v5.2.2 (January 2022)"
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description 
 # for a project that appears at the top of each page and should give viewer 
 # a quick idea about the purpose of the project. Keep the description short.
 
-PROJECT_BRIEF          = 
+PROJECT_BRIEF          = The Asset-Importer-Lib API documentation.
 
 # With the PROJECT_LOGO tag one can specify an logo or icon that is 
 # included in the documentation. The maximum height of the logo should not 

+ 395 - 373
tools/assimp_cmd/Info.cpp

@@ -50,12 +50,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <iostream>
 #include <string>
 
-const char* AICMD_MSG_INFO_HELP_E =
-    "assimp info <file> [-r] [-v]\n"
-    "\tPrint basic structure of a 3D model\n"
-    "\t-r,--raw: No postprocessing, do a raw import\n"
-    "\t-v,--verbose: Print verbose info such as node transform data\n"
-    "\t-s, --silent: Print only minimal info\n";
+const char *AICMD_MSG_INFO_HELP_E =
+        "assimp info <file> [-r] [-v]\n"
+        "\tPrint basic structure of a 3D model\n"
+        "\t-r,--raw: No postprocessing, do a raw import\n"
+        "\t-v,--verbose: Print verbose info such as node transform data\n"
+        "\t-s, --silent: Print only minimal info\n";
 
 const char *TREE_BRANCH_ASCII = "|-";
 const char *TREE_BRANCH_UTF8 = "\xe2\x94\x9c\xe2\x95\xb4";
@@ -73,411 +73,433 @@ const char *TREE_STOP = TREE_STOP_UTF8;
 const char *TREE_CONTINUE = TREE_CONTINUE_UTF8;
 
 // -----------------------------------------------------------------------------------
-unsigned int CountNodes(const aiNode* root)
-{
-	unsigned int i = 0;
-	for (unsigned int a = 0; a < root->mNumChildren; ++a ) {
-		i += CountNodes(root->mChildren[a]);
-	}
-	return 1+i;
+unsigned int CountNodes(const aiNode *root) {
+    unsigned int i = 0;
+    for (unsigned int a = 0; a < root->mNumChildren; ++a) {
+        i += CountNodes(root->mChildren[a]);
+    }
+    return 1 + i;
 }
 
 // -----------------------------------------------------------------------------------
-unsigned int GetMaxDepth(const aiNode* root)
-{
-	unsigned int cnt = 0;
-	for (unsigned int i = 0; i < root->mNumChildren; ++i ) {
-		cnt = std::max(cnt,GetMaxDepth(root->mChildren[i]));
-	}
-	return cnt+1;
+unsigned int GetMaxDepth(const aiNode *root) {
+    unsigned int cnt = 0;
+    for (unsigned int i = 0; i < root->mNumChildren; ++i) {
+        cnt = std::max(cnt, GetMaxDepth(root->mChildren[i]));
+    }
+    return cnt + 1;
 }
 
 // -----------------------------------------------------------------------------------
-unsigned int CountVertices(const aiScene* scene)
-{
-	unsigned int cnt = 0;
-	for(unsigned int i = 0; i < scene->mNumMeshes; ++i) {
-		cnt += scene->mMeshes[i]->mNumVertices;
-	}
-	return cnt;
+unsigned int CountVertices(const aiScene *scene) {
+    unsigned int cnt = 0;
+    for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+        cnt += scene->mMeshes[i]->mNumVertices;
+    }
+    return cnt;
 }
 
 // -----------------------------------------------------------------------------------
-unsigned int CountFaces(const aiScene* scene)
-{
-	unsigned int cnt = 0;
-	for(unsigned int i = 0; i < scene->mNumMeshes; ++i) {
-		cnt += scene->mMeshes[i]->mNumFaces;
-	}
-	return cnt;
+unsigned int CountFaces(const aiScene *scene) {
+    unsigned int cnt = 0;
+    for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+        cnt += scene->mMeshes[i]->mNumFaces;
+    }
+    return cnt;
 }
 
 // -----------------------------------------------------------------------------------
-unsigned int CountBones(const aiScene* scene)
-{
-	unsigned int cnt = 0;
-	for(unsigned int i = 0; i < scene->mNumMeshes; ++i) {
-		cnt += scene->mMeshes[i]->mNumBones;
-	}
-	return cnt;
+unsigned int CountBones(const aiScene *scene) {
+    unsigned int cnt = 0;
+    for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+        cnt += scene->mMeshes[i]->mNumBones;
+    }
+    return cnt;
 }
 
 // -----------------------------------------------------------------------------------
-unsigned int CountAnimChannels(const aiScene* scene)
-{
-	unsigned int cnt = 0;
-	for(unsigned int i = 0; i < scene->mNumAnimations; ++i) {
-		cnt += scene->mAnimations[i]->mNumChannels;
-	}
-	return cnt;
+unsigned int CountAnimChannels(const aiScene *scene) {
+    unsigned int cnt = 0;
+    for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
+        cnt += scene->mAnimations[i]->mNumChannels;
+    }
+    return cnt;
 }
 
 // -----------------------------------------------------------------------------------
-unsigned int GetAvgFacePerMesh(const aiScene* scene) {
-	return (scene->mNumMeshes != 0) ? static_cast<unsigned int>(CountFaces(scene)/scene->mNumMeshes) : 0;
+unsigned int GetAvgFacePerMesh(const aiScene *scene) {
+    return (scene->mNumMeshes != 0) ? static_cast<unsigned int>(CountFaces(scene) / scene->mNumMeshes) : 0;
 }
 
 // -----------------------------------------------------------------------------------
-unsigned int GetAvgVertsPerMesh(const aiScene* scene) {
-	return (scene->mNumMeshes != 0) ? static_cast<unsigned int>(CountVertices(scene)/scene->mNumMeshes) : 0;
+unsigned int GetAvgVertsPerMesh(const aiScene *scene) {
+    return (scene->mNumMeshes != 0) ? static_cast<unsigned int>(CountVertices(scene) / scene->mNumMeshes) : 0;
 }
 
 // -----------------------------------------------------------------------------------
-void FindSpecialPoints(const aiScene* scene,const aiNode* root,aiVector3D special_points[3],const aiMatrix4x4& mat=aiMatrix4x4())
-{
-	// XXX that could be greatly simplified by using code from code/ProcessHelper.h
-	// XXX I just don't want to include it here.
-	const aiMatrix4x4 trafo = root->mTransformation*mat;
-	for(unsigned int i = 0; i < root->mNumMeshes; ++i) {
-		const aiMesh* mesh = scene->mMeshes[root->mMeshes[i]];
-
-		for(unsigned int a = 0; a < mesh->mNumVertices; ++a) {
-			aiVector3D v = trafo*mesh->mVertices[a];
-
-			special_points[0].x = std::min(special_points[0].x,v.x);
-			special_points[0].y = std::min(special_points[0].y,v.y);
-			special_points[0].z = std::min(special_points[0].z,v.z);
-
-			special_points[1].x = std::max(special_points[1].x,v.x);
-			special_points[1].y = std::max(special_points[1].y,v.y);
-			special_points[1].z = std::max(special_points[1].z,v.z);
-		}
-	}
-
-	for(unsigned int i = 0; i < root->mNumChildren; ++i) {
-		FindSpecialPoints(scene,root->mChildren[i],special_points,trafo);
-	}
+void FindSpecialPoints(const aiScene *scene, const aiNode *root, aiVector3D special_points[3], const aiMatrix4x4 &mat = aiMatrix4x4()) {
+    // XXX that could be greatly simplified by using code from code/ProcessHelper.h
+    // XXX I just don't want to include it here.
+    const aiMatrix4x4 trafo = root->mTransformation * mat;
+    for (unsigned int i = 0; i < root->mNumMeshes; ++i) {
+        const aiMesh *mesh = scene->mMeshes[root->mMeshes[i]];
+
+        for (unsigned int a = 0; a < mesh->mNumVertices; ++a) {
+            aiVector3D v = trafo * mesh->mVertices[a];
+
+            special_points[0].x = std::min(special_points[0].x, v.x);
+            special_points[0].y = std::min(special_points[0].y, v.y);
+            special_points[0].z = std::min(special_points[0].z, v.z);
+
+            special_points[1].x = std::max(special_points[1].x, v.x);
+            special_points[1].y = std::max(special_points[1].y, v.y);
+            special_points[1].z = std::max(special_points[1].z, v.z);
+        }
+    }
+
+    for (unsigned int i = 0; i < root->mNumChildren; ++i) {
+        FindSpecialPoints(scene, root->mChildren[i], special_points, trafo);
+    }
 }
 
 // -----------------------------------------------------------------------------------
-void FindSpecialPoints(const aiScene* scene,aiVector3D special_points[3])
-{
-	special_points[0] = aiVector3D(1e10,1e10,1e10);
-	special_points[1] = aiVector3D(-1e10,-1e10,-1e10);
+void FindSpecialPoints(const aiScene *scene, aiVector3D special_points[3]) {
+    special_points[0] = aiVector3D(1e10, 1e10, 1e10);
+    special_points[1] = aiVector3D(-1e10, -1e10, -1e10);
 
-	FindSpecialPoints(scene,scene->mRootNode,special_points);
-	special_points[2] = (special_points[0]+special_points[1])*(ai_real)0.5;
+    FindSpecialPoints(scene, scene->mRootNode, special_points);
+    special_points[2] = (special_points[0] + special_points[1]) * (ai_real)0.5;
 }
 
 // -----------------------------------------------------------------------------------
-std::string FindPTypes(const aiScene* scene)
-{
-	bool haveit[4] = {0};
-	for(unsigned int i = 0; i < scene->mNumMeshes; ++i) {
-		const unsigned int pt = scene->mMeshes[i]->mPrimitiveTypes;
-		if (pt & aiPrimitiveType_POINT) {
-			haveit[0]=true;
-		}
-		if (pt & aiPrimitiveType_LINE) {
-			haveit[1]=true;
-		}
-		if (pt & aiPrimitiveType_TRIANGLE) {
-			haveit[2]=true;
-		}
-		if (pt & aiPrimitiveType_POLYGON) {
-			haveit[3]=true;
-		}
-	}
-	return (haveit[0]?std::string("points"):"")+(haveit[1]?"lines":"")+
-		(haveit[2]?"triangles":"")+(haveit[3]?"n-polygons":"");
+std::string FindPTypes(const aiScene *scene) {
+    bool haveit[4] = { 0 };
+    for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+        const unsigned int pt = scene->mMeshes[i]->mPrimitiveTypes;
+        if (pt & aiPrimitiveType_POINT) {
+            haveit[0] = true;
+        }
+        if (pt & aiPrimitiveType_LINE) {
+            haveit[1] = true;
+        }
+        if (pt & aiPrimitiveType_TRIANGLE) {
+            haveit[2] = true;
+        }
+        if (pt & aiPrimitiveType_POLYGON) {
+            haveit[3] = true;
+        }
+    }
+    return (haveit[0] ? std::string("points") : "") + (haveit[1] ? "lines" : "") +
+           (haveit[2] ? "triangles" : "") + (haveit[3] ? "n-polygons" : "");
 }
 
 // -----------------------------------------------------------------------------------
 // Prettily print the node graph to stdout
 void PrintHierarchy(
-	const aiNode* node,
-	const std::string &indent,
-	bool verbose,
-	bool last = false,
-	bool first = true
-){
-	// tree visualization
-	std::string branchchar;
-	if (first) { branchchar = ""; }
-	else if (last) { branchchar = TREE_STOP; } // "'-"
-	else { branchchar = TREE_BRANCH; } // "|-"
-
-	// print the indent and the branch character and the name
-	std::cout << indent << branchchar << node->mName.C_Str();
-
-	// if there are meshes attached, indicate this
-	if (node->mNumMeshes) {
-		std::cout << " (mesh ";
-		bool sep = false;
-		for (size_t i=0; i < node->mNumMeshes; ++i) {
-			unsigned int mesh_index = node->mMeshes[i];
-			if (sep) { std::cout << ", "; }
-			std::cout << mesh_index;
-			sep = true;
-		}
-		std::cout << ")";
-	}
-
-	// finish the line
-	std::cout << std::endl;
-
-	// in verbose mode, print the transform data as well
-	if (verbose) {
-		// indent to use
-		std::string indentadd;
-		if (last) { indentadd += "  "; }
-		else { indentadd += TREE_CONTINUE; } // "| "..
-		if (node->mNumChildren == 0) { indentadd += "  "; }
-		else { indentadd += TREE_CONTINUE; } // .."| "
-		aiVector3D s, r, t;
-		node->mTransformation.Decompose(s, r, t);
-		if (s.x != 1.0 || s.y != 1.0 || s.z != 1.0) {
-			std::cout << indent << indentadd;
-			printf("  S:[%f %f %f]\n", s.x, s.y, s.z);
-		}
-		if (r.x || r.y || r.z) {
-			std::cout << indent << indentadd;
-			printf("  R:[%f %f %f]\n", r.x, r.y, r.z);
-		}
-		if (t.x || t.y || t.z) {
-			std::cout << indent << indentadd;
-			printf("  T:[%f %f %f]\n", t.x, t.y, t.z);
-		}
-	}
-
-	// and recurse
-	std::string nextIndent;
-	if (first) { nextIndent = indent; }
-	else if (last) { nextIndent = indent + "  "; }
-	else { nextIndent = indent + TREE_CONTINUE; } // "| "
-	for (size_t i = 0; i < node->mNumChildren; ++i) {
-		bool lastone = (i == node->mNumChildren - 1);
-		PrintHierarchy(
-			node->mChildren[i],
-			nextIndent,
-			verbose,
-			lastone,
-			false
-		);
-	}
+        const aiNode *node,
+        const std::string &indent,
+        bool verbose,
+        bool last = false,
+        bool first = true) {
+    // tree visualization
+    std::string branchchar;
+    if (first) {
+        branchchar = "";
+    } else if (last) {
+        branchchar = TREE_STOP;
+    } // "'-"
+    else {
+        branchchar = TREE_BRANCH;
+    } // "|-"
+
+    // print the indent and the branch character and the name
+    std::cout << indent << branchchar << node->mName.C_Str();
+
+    // if there are meshes attached, indicate this
+    if (node->mNumMeshes) {
+        std::cout << " (mesh ";
+        bool sep = false;
+        for (size_t i = 0; i < node->mNumMeshes; ++i) {
+            unsigned int mesh_index = node->mMeshes[i];
+            if (sep) {
+                std::cout << ", ";
+            }
+            std::cout << mesh_index;
+            sep = true;
+        }
+        std::cout << ")";
+    }
+
+    // finish the line
+    std::cout << std::endl;
+
+    // in verbose mode, print the transform data as well
+    if (verbose) {
+        // indent to use
+        std::string indentadd;
+        if (last) {
+            indentadd += "  ";
+        } else {
+            indentadd += TREE_CONTINUE;
+        } // "| "..
+        if (node->mNumChildren == 0) {
+            indentadd += "  ";
+        } else {
+            indentadd += TREE_CONTINUE;
+        } // .."| "
+        aiVector3D s, r, t;
+        node->mTransformation.Decompose(s, r, t);
+        if (s.x != 1.0 || s.y != 1.0 || s.z != 1.0) {
+            std::cout << indent << indentadd;
+            printf("  S:[%f %f %f]\n", s.x, s.y, s.z);
+        }
+        if (r.x || r.y || r.z) {
+            std::cout << indent << indentadd;
+            printf("  R:[%f %f %f]\n", r.x, r.y, r.z);
+        }
+        if (t.x || t.y || t.z) {
+            std::cout << indent << indentadd;
+            printf("  T:[%f %f %f]\n", t.x, t.y, t.z);
+        }
+    }
+
+    // and recurse
+    std::string nextIndent;
+    if (first) {
+        nextIndent = indent;
+    } else if (last) {
+        nextIndent = indent + "  ";
+    } else {
+        nextIndent = indent + TREE_CONTINUE;
+    } // "| "
+    for (size_t i = 0; i < node->mNumChildren; ++i) {
+        bool lastone = (i == node->mNumChildren - 1);
+        PrintHierarchy(
+                node->mChildren[i],
+                nextIndent,
+                verbose,
+                lastone,
+                false);
+    }
 }
 
 // -----------------------------------------------------------------------------------
 // Implementation of the assimp info utility to print basic file info
-int Assimp_Info (const char* const* params, unsigned int num) {
-	// --help
-	if (!strcmp( params[0],"-h")||!strcmp( params[0],"--help")||!strcmp( params[0],"-?") ) {
-		printf("%s",AICMD_MSG_INFO_HELP_E);
-		return AssimpCmdError::Success;
-	}
-
-	// asssimp info <file> [-r]
-	if (num < 1) {
-		printf("assimp info: Invalid number of arguments. "
-			"See \'assimp info --help\'\n");
-		return AssimpCmdError::InvalidNumberOfArguments;
-	}
-
-	const std::string in  = std::string(params[0]);
-
-	// get -r and -v arguments
-	bool raw = false;
-	bool verbose = false;
-	bool silent = false;
-	for(unsigned int i = 1; i < num; ++i) {
-		if (!strcmp(params[i],"--raw")||!strcmp(params[i],"-r")) {
-			raw = true;
-		}
-		if (!strcmp(params[i],"--verbose")||!strcmp(params[i],"-v")) {
-			verbose = true;
-		}
-		if (!strcmp(params[i], "--silent") || !strcmp(params[i], "-s")) {
-			silent = true;
-		}
-	}
-
-	// Verbose and silent at the same time are not allowed
-	if ( verbose && silent ) {
-		printf("assimp info: Invalid arguments, verbose and silent at the same time are forbidden. ");
-		return AssimpCmdInfoError::InvalidCombinaisonOfArguments;
-	}
-
-	// Parse post-processing flags unless -r was specified
-	ImportData import;
-	if (!raw) {
-		// get import flags
-		ProcessStandardArguments(import, params + 1, num - 1);
-
-		//No custom post process flags defined, we set all the post process flags active
-		if(import.ppFlags == 0)
-			import.ppFlags |= aiProcessPreset_TargetRealtime_MaxQuality;
-	}
-
-	// import the main model
-	const aiScene* scene = ImportModel(import,in);
-	if (!scene) {
-		printf("assimp info: Unable to load input file %s\n",
-			in.c_str());
-		return AssimpCmdError::FailedToLoadInputFile;
-	}
-
-	aiMemoryInfo mem;
-	globalImporter->GetMemoryRequirements(mem);
-
-
-	static const char* format_string =
-		"Memory consumption: %i B\n"
-		"Nodes:              %i\n"
-		"Maximum depth       %i\n"
-		"Meshes:             %i\n"
-		"Animations:         %i\n"
-		"Textures (embed.):  %i\n"
-		"Materials:          %i\n"
-		"Cameras:            %i\n"
-		"Lights:             %i\n"
-		"Vertices:           %i\n"
-		"Faces:              %i\n"
-		"Bones:              %i\n"
-		"Animation Channels: %i\n"
-		"Primitive Types:    %s\n"
-		"Average faces/mesh  %i\n"
-		"Average verts/mesh  %i\n"
-		"Minimum point      (%f %f %f)\n"
-		"Maximum point      (%f %f %f)\n"
-		"Center point       (%f %f %f)\n"
-
-		;
-
-	aiVector3D special_points[3];
-	FindSpecialPoints(scene,special_points);
-	printf(format_string,
-		mem.total,
-		CountNodes(scene->mRootNode),
-		GetMaxDepth(scene->mRootNode),
-		scene->mNumMeshes,
-		scene->mNumAnimations,
-		scene->mNumTextures,
-		scene->mNumMaterials,
-		scene->mNumCameras,
-		scene->mNumLights,
-		CountVertices(scene),
-		CountFaces(scene),
-		CountBones(scene),
-		CountAnimChannels(scene),
-		FindPTypes(scene).c_str(),
-		GetAvgFacePerMesh(scene),
-		GetAvgVertsPerMesh(scene),
-		special_points[0][0],special_points[0][1],special_points[0][2],
-		special_points[1][0],special_points[1][1],special_points[1][2],
-		special_points[2][0],special_points[2][1],special_points[2][2]
-		)
-	;
-
-	if (silent)
-	{
-		printf("\n");
-		return AssimpCmdError::Success;
-	}
-
-	// meshes
-	if (scene->mNumMeshes) {
-		printf("\nMeshes:  (name) [vertices / bones / faces | primitive_types]\n");
-	}
-	for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
-		const aiMesh* mesh = scene->mMeshes[i];
-		printf("    %d (%s)", i, mesh->mName.C_Str());
-		printf(
-			": [%d / %d / %d |",
-			mesh->mNumVertices,
-			mesh->mNumBones,
-			mesh->mNumFaces
-		);
-		const unsigned int ptypes = mesh->mPrimitiveTypes;
-		if (ptypes & aiPrimitiveType_POINT) { printf(" point"); }
-		if (ptypes & aiPrimitiveType_LINE) { printf(" line"); }
-		if (ptypes & aiPrimitiveType_TRIANGLE) { printf(" triangle"); }
-		if (ptypes & aiPrimitiveType_POLYGON) { printf(" polygon"); }
-		printf("]\n");
-	}
-
-	// materials
-	unsigned int total=0;
-	for(unsigned int i = 0;i < scene->mNumMaterials; ++i) {
-		aiString name;
-		if (AI_SUCCESS==aiGetMaterialString(scene->mMaterials[i],AI_MATKEY_NAME,&name)) {
-			printf("%s\n    \'%s\'",(total++?"":"\nNamed Materials:" ),name.data);
-		}
-	}
-	if(total) {
-		printf("\n");
-	}
-
-	// textures
-	total=0;
-	for(unsigned int i = 0;i < scene->mNumMaterials; ++i) {
-		aiString name;
-		static const aiTextureType types[] = {
-			aiTextureType_NONE,
-			aiTextureType_DIFFUSE,
-			aiTextureType_SPECULAR,
-			aiTextureType_AMBIENT,
-			aiTextureType_EMISSIVE,
-			aiTextureType_HEIGHT,
-			aiTextureType_NORMALS,
-			aiTextureType_SHININESS,
-			aiTextureType_OPACITY,
-			aiTextureType_DISPLACEMENT,
-			aiTextureType_LIGHTMAP,
-			aiTextureType_REFLECTION,
-			aiTextureType_BASE_COLOR,
-			aiTextureType_NORMAL_CAMERA,
-			aiTextureType_EMISSION_COLOR,
-			aiTextureType_METALNESS,
-			aiTextureType_DIFFUSE_ROUGHNESS,
-			aiTextureType_AMBIENT_OCCLUSION,
-			aiTextureType_UNKNOWN
-		};
-		for(unsigned int type = 0; type < sizeof(types)/sizeof(types[0]); ++type) {
-			for(unsigned int idx = 0;AI_SUCCESS==aiGetMaterialString(scene->mMaterials[i],
-				AI_MATKEY_TEXTURE(types[type],idx),&name); ++idx) {
-				printf("%s\n    \'%s\'",(total++?"":"\nTexture Refs:" ),name.data);
-			}
-		}
-	}
-	if(total) {
-		printf("\n");
-	}
-
-	// animations
-	total=0;
-	for(unsigned int i = 0;i < scene->mNumAnimations; ++i) {
-		if (scene->mAnimations[i]->mName.length) {
-			printf("%s\n     \'%s\'",(total++?"":"\nNamed Animations:" ),scene->mAnimations[i]->mName.data);
-		}
-	}
-	if(total) {
-		printf("\n");
-	}
-
-	// node hierarchy
-	printf("\nNode hierarchy:\n");
-	PrintHierarchy(scene->mRootNode,"",verbose);
-
-	printf("\n");
-	return AssimpCmdError::Success;
+int Assimp_Info(const char *const *params, unsigned int num) {
+    // --help
+    if (!strcmp(params[0], "-h") || !strcmp(params[0], "--help") || !strcmp(params[0], "-?")) {
+        printf("%s", AICMD_MSG_INFO_HELP_E);
+        return AssimpCmdError::Success;
+    }
+
+    // asssimp info <file> [-r]
+    if (num < 1) {
+        printf("assimp info: Invalid number of arguments. "
+               "See \'assimp info --help\'\n");
+        return AssimpCmdError::InvalidNumberOfArguments;
+    }
+
+    const std::string in = std::string(params[0]);
+
+    // get -r and -v arguments
+    bool raw = false;
+    bool verbose = false;
+    bool silent = false;
+    for (unsigned int i = 1; i < num; ++i) {
+        if (!strcmp(params[i], "--raw") || !strcmp(params[i], "-r")) {
+            raw = true;
+        }
+        if (!strcmp(params[i], "--verbose") || !strcmp(params[i], "-v")) {
+            verbose = true;
+        }
+        if (!strcmp(params[i], "--silent") || !strcmp(params[i], "-s")) {
+            silent = true;
+        }
+    }
+
+    // Verbose and silent at the same time are not allowed
+    if (verbose && silent) {
+        printf("assimp info: Invalid arguments, verbose and silent at the same time are forbidden. ");
+        return AssimpCmdInfoError::InvalidCombinaisonOfArguments;
+    }
+
+    // Parse post-processing flags unless -r was specified
+    ImportData import;
+    if (!raw) {
+        // get import flags
+        ProcessStandardArguments(import, params + 1, num - 1);
+
+        //No custom post process flags defined, we set all the post process flags active
+        if (import.ppFlags == 0)
+            import.ppFlags |= aiProcessPreset_TargetRealtime_MaxQuality;
+    }
+
+    // import the main model
+    const aiScene *scene = ImportModel(import, in);
+    if (!scene) {
+        printf("assimp info: Unable to load input file %s\n",
+                in.c_str());
+        return AssimpCmdError::FailedToLoadInputFile;
+    }
+
+    aiMemoryInfo mem;
+    globalImporter->GetMemoryRequirements(mem);
+
+    static const char *format_string =
+            "Memory consumption: %i B\n"
+            "Nodes:              %i\n"
+            "Maximum depth       %i\n"
+            "Meshes:             %i\n"
+            "Animations:         %i\n"
+            "Textures (embed.):  %i\n"
+            "Materials:          %i\n"
+            "Cameras:            %i\n"
+            "Lights:             %i\n"
+            "Vertices:           %i\n"
+            "Faces:              %i\n"
+            "Bones:              %i\n"
+            "Animation Channels: %i\n"
+            "Primitive Types:    %s\n"
+            "Average faces/mesh  %i\n"
+            "Average verts/mesh  %i\n"
+            "Minimum point      (%f %f %f)\n"
+            "Maximum point      (%f %f %f)\n"
+            "Center point       (%f %f %f)\n"
+
+            ;
+
+    aiVector3D special_points[3];
+    FindSpecialPoints(scene, special_points);
+    printf(format_string,
+            mem.total,
+            CountNodes(scene->mRootNode),
+            GetMaxDepth(scene->mRootNode),
+            scene->mNumMeshes,
+            scene->mNumAnimations,
+            scene->mNumTextures,
+            scene->mNumMaterials,
+            scene->mNumCameras,
+            scene->mNumLights,
+            CountVertices(scene),
+            CountFaces(scene),
+            CountBones(scene),
+            CountAnimChannels(scene),
+            FindPTypes(scene).c_str(),
+            GetAvgFacePerMesh(scene),
+            GetAvgVertsPerMesh(scene),
+            special_points[0][0], special_points[0][1], special_points[0][2],
+            special_points[1][0], special_points[1][1], special_points[1][2],
+            special_points[2][0], special_points[2][1], special_points[2][2]);
+
+    if (silent) {
+        printf("\n");
+        return AssimpCmdError::Success;
+    }
+
+    // meshes
+    if (scene->mNumMeshes) {
+        printf("\nMeshes:  (name) [vertices / bones / faces | primitive_types]\n");
+    }
+    for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+        const aiMesh *mesh = scene->mMeshes[i];
+        printf("    %d (%s)", i, mesh->mName.C_Str());
+        printf(
+                ": [%d / %d / %d |",
+                mesh->mNumVertices,
+                mesh->mNumBones,
+                mesh->mNumFaces);
+        const unsigned int ptypes = mesh->mPrimitiveTypes;
+        if (ptypes & aiPrimitiveType_POINT) {
+            printf(" point");
+        }
+        if (ptypes & aiPrimitiveType_LINE) {
+            printf(" line");
+        }
+        if (ptypes & aiPrimitiveType_TRIANGLE) {
+            printf(" triangle");
+        }
+        if (ptypes & aiPrimitiveType_POLYGON) {
+            printf(" polygon");
+        }
+        printf("]\n");
+    }
+
+    // materials
+    if (scene->mNumMaterials)
+        printf("\nNamed Materials:");
+    for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
+        const aiMaterial *mat = scene->mMaterials[i];
+        aiString name = mat->GetName();
+        printf("\n    \'%s\'", name.data);
+        if (mat->mNumProperties)
+            printf(" (prop) [index / bytes | texture semantic]");
+        for (unsigned p = 0; p < mat->mNumProperties; p++) {
+            const aiMaterialProperty *prop = mat->mProperties[p];
+            const aiTextureType textype = static_cast<aiTextureType>(prop->mSemantic);
+            printf("\n        %d (%s): [%d / %d | %s]",
+                    p,
+                    prop->mKey.data,
+                    prop->mIndex,
+                    prop->mDataLength,
+                    TextureTypeToString(textype));
+        }
+    }
+    if (scene->mNumMaterials) {
+        printf("\n");
+    }
+
+    // textures
+    unsigned int total = 0;
+    for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
+        aiString name;
+        static const aiTextureType types[] = {
+            aiTextureType_NONE,
+            aiTextureType_DIFFUSE,
+            aiTextureType_SPECULAR,
+            aiTextureType_AMBIENT,
+            aiTextureType_EMISSIVE,
+            aiTextureType_HEIGHT,
+            aiTextureType_NORMALS,
+            aiTextureType_SHININESS,
+            aiTextureType_OPACITY,
+            aiTextureType_DISPLACEMENT,
+            aiTextureType_LIGHTMAP,
+            aiTextureType_REFLECTION,
+            aiTextureType_BASE_COLOR,
+            aiTextureType_NORMAL_CAMERA,
+            aiTextureType_EMISSION_COLOR,
+            aiTextureType_METALNESS,
+            aiTextureType_DIFFUSE_ROUGHNESS,
+            aiTextureType_AMBIENT_OCCLUSION,
+            aiTextureType_UNKNOWN
+        };
+        for (unsigned int type = 0; type < sizeof(types) / sizeof(types[0]); ++type) {
+            for (unsigned int idx = 0; AI_SUCCESS == aiGetMaterialString(scene->mMaterials[i],
+                                                             AI_MATKEY_TEXTURE(types[type], idx), &name);
+                    ++idx) {
+                printf("%s\n    \'%s\'", (total++ ? "" : "\nTexture Refs:"), name.data);
+            }
+        }
+    }
+    if (total) {
+        printf("\n");
+    }
+
+    // animations
+    total = 0;
+    for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
+        if (scene->mAnimations[i]->mName.length) {
+            printf("%s\n     \'%s\'", (total++ ? "" : "\nNamed Animations:"), scene->mAnimations[i]->mName.data);
+        }
+    }
+    if (total) {
+        printf("\n");
+    }
+
+    // node hierarchy
+    printf("\nNode hierarchy:\n");
+    PrintHierarchy(scene->mRootNode, "", verbose);
+
+    printf("\n");
+    return AssimpCmdError::Success;
 }