Browse Source

Merge pull request #16 from assimp/master

Update to upstream
RichardTea 6 năm trước cách đây
mục cha
commit
9880db8d4d
42 tập tin đã thay đổi với 2059 bổ sung587 xóa
  1. 109 0
      code/Assjson/cencode.c
  2. 31 0
      code/Assjson/cencode.h
  3. 818 0
      code/Assjson/json_exporter.cpp
  4. 320 0
      code/Assjson/mesh_splitter.cpp
  5. 61 0
      code/Assjson/mesh_splitter.h
  6. 8 0
      code/CMakeLists.txt
  7. 7 4
      code/COB/COBLoader.cpp
  8. 102 104
      code/Collada/ColladaLoader.cpp
  9. 0 4
      code/Common/CreateAnimMesh.cpp
  10. 6 1
      code/Common/Exporter.cpp
  11. 1 1
      code/Common/ImporterRegistry.cpp
  12. 18 14
      code/FBX/FBXConverter.cpp
  13. 1 0
      code/FBX/FBXConverter.h
  14. 1 1
      code/FBX/FBXDocument.h
  15. 20 1
      code/FBX/FBXExporter.cpp
  16. 0 4
      code/FBX/FBXParser.cpp
  17. 1 1
      code/Obj/ObjFileParser.cpp
  18. 2 1
      code/Unreal/UnrealLoader.h
  19. 1 3
      include/assimp/camera.h
  20. 6 2
      include/assimp/config.h.in
  21. 6 5
      test/CMakeLists.txt
  22. BIN
      test/models/JT/conrod.jt
  23. 1 1
      test/models/PLY/cube_test.ply
  24. 22 2
      test/unit/AbstractImportExportBase.h
  25. 69 0
      test/unit/ImportExport/utAssjsonImportExport.cpp
  26. 0 2
      test/unit/utAMFImportExport.cpp
  27. 0 2
      test/unit/utASEImportExport.cpp
  28. 3 5
      test/unit/utAssbinImportExport.cpp
  29. 0 2
      test/unit/utB3DImportExport.cpp
  30. 2 9
      test/unit/utBlenderWork.cpp
  31. 25 24
      test/unit/utFBXImporterExporter.cpp
  32. 38 33
      test/unit/utFindDegenerates.cpp
  33. 63 54
      test/unit/utFindInvalidData.cpp
  34. 9 1
      test/unit/utGenBoundingBoxesProcess.cpp
  35. 1 1
      test/unit/utIssues.cpp
  36. 31 31
      test/unit/utJoinVertices.cpp
  37. 36 38
      test/unit/utLimitBoneWeights.cpp
  38. 34 0
      test/unit/utObjImportExport.cpp
  39. 38 35
      test/unit/utPretransformVertices.cpp
  40. 32 35
      test/unit/utScenePreprocessor.cpp
  41. 44 67
      test/unit/utSortByPType.cpp
  42. 92 99
      test/unit/utglTF2ImportExport.cpp

+ 109 - 0
code/Assjson/cencode.c

@@ -0,0 +1,109 @@
+/*
+cencoder.c - c source to a base64 encoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include "cencode.h" // changed from <B64/cencode.h>
+
+const int CHARS_PER_LINE = 72;
+
+void base64_init_encodestate(base64_encodestate* state_in)
+{
+	state_in->step = step_A;
+	state_in->result = 0;
+	state_in->stepcount = 0;
+}
+
+char base64_encode_value(char value_in)
+{
+	static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	if (value_in > 63) return '=';
+	return encoding[(int)value_in];
+}
+
+int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
+{
+	const char* plainchar = plaintext_in;
+	const char* const plaintextend = plaintext_in + length_in;
+	char* codechar = code_out;
+	char result;
+	char fragment;
+	
+	result = state_in->result;
+	
+	switch (state_in->step)
+	{
+		while (1)
+		{
+	case step_A:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_A;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result = (fragment & 0x0fc) >> 2;
+			*codechar++ = base64_encode_value(result);
+			result = (fragment & 0x003) << 4;
+	case step_B:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_B;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result |= (fragment & 0x0f0) >> 4;
+			*codechar++ = base64_encode_value(result);
+			result = (fragment & 0x00f) << 2;
+	case step_C:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_C;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result |= (fragment & 0x0c0) >> 6;
+			*codechar++ = base64_encode_value(result);
+			result  = (fragment & 0x03f) >> 0;
+			*codechar++ = base64_encode_value(result);
+			
+			++(state_in->stepcount);
+			if (state_in->stepcount == CHARS_PER_LINE/4)
+			{
+				*codechar++ = '\n';
+				state_in->stepcount = 0;
+			}
+		}
+	}
+	/* control should not reach here */
+	return codechar - code_out;
+}
+
+int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
+{
+	char* codechar = code_out;
+	
+	switch (state_in->step)
+	{
+	case step_B:
+		*codechar++ = base64_encode_value(state_in->result);
+		*codechar++ = '=';
+		*codechar++ = '=';
+		break;
+	case step_C:
+		*codechar++ = base64_encode_value(state_in->result);
+		*codechar++ = '=';
+		break;
+	case step_A:
+		break;
+	}
+	*codechar++ = '\n';
+	
+	return codechar - code_out;
+}
+

+ 31 - 0
code/Assjson/cencode.h

@@ -0,0 +1,31 @@
+/*
+cencode.h - c header for a base64 encoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CENCODE_H
+#define BASE64_CENCODE_H
+
+typedef enum
+{
+	step_A, step_B, step_C
+} base64_encodestep;
+
+typedef struct
+{
+	base64_encodestep step;
+	char result;
+	int stepcount;
+} base64_encodestate;
+
+void base64_init_encodestate(base64_encodestate* state_in);
+
+char base64_encode_value(char value_in);
+
+int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
+
+int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
+
+#endif /* BASE64_CENCODE_H */

+ 818 - 0
code/Assjson/json_exporter.cpp

@@ -0,0 +1,818 @@
+/*
+Assimp2Json
+Copyright (c) 2011, Alexander C. Gessler
+
+Licensed under a 3-clause BSD license. See the LICENSE file for more information.
+
+*/
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
+
+#include <assimp/Importer.hpp>
+#include <assimp/Exporter.hpp>
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/scene.h>
+
+#include <sstream>
+#include <limits>
+#include <cassert>
+#include <memory>
+
+#define CURRENT_FORMAT_VERSION 100
+
+// grab scoped_ptr from assimp to avoid a dependency on boost. 
+//#include <assimp/../../code/BoostWorkaround/boost/scoped_ptr.hpp>
+
+#include "mesh_splitter.h"
+
+extern "C" {
+    #include "cencode.h"
+}
+namespace Assimp {
+
+void ExportAssimp2Json(const char*, Assimp::IOSystem*, const aiScene*, const Assimp::ExportProperties*);
+
+Exporter::ExportFormatEntry Assimp2Json_desc = Assimp::Exporter::ExportFormatEntry(
+    "json",
+    "Plain JSON representation of the Assimp scene data structure",
+    "json",
+    &ExportAssimp2Json,
+    0u
+);
+
+
+// small utility class to simplify serializing the aiScene to Json
+class JSONWriter {
+public:
+    enum {
+        Flag_DoNotIndent = 0x1,
+        Flag_WriteSpecialFloats = 0x2,
+    };
+
+    JSONWriter(Assimp::IOStream& out, unsigned int flags = 0u)
+    : out(out)
+    , first()
+    , flags(flags) {
+        // make sure that all formatting happens using the standard, C locale and not the user's current locale
+        buff.imbue(std::locale("C"));
+    }
+
+    ~JSONWriter() {
+        Flush();
+    }
+
+    void Flush() {
+        const std::string s = buff.str();
+        out.Write(s.c_str(), s.length(), 1);
+        buff.clear();
+    }
+
+    void PushIndent() {
+        indent += '\t';
+    }
+
+    void PopIndent() {
+        indent.erase(indent.end() - 1);
+    }
+
+    void Key(const std::string& name) {
+        AddIndentation();
+        Delimit();
+        buff << '\"' + name + "\": ";
+    }
+
+    template<typename Literal>
+    void Element(const Literal& name) {
+        AddIndentation();
+        Delimit();
+
+        LiteralToString(buff, name) << '\n';
+    }
+
+    template<typename Literal>
+    void SimpleValue(const Literal& s) {
+        LiteralToString(buff, s) << '\n';
+    }
+
+    void SimpleValue(const void* buffer, size_t len) {
+        base64_encodestate s;
+        base64_init_encodestate(&s);
+
+        char* const out = new char[std::max(len * 2, static_cast<size_t>(16u))];
+        const int n = base64_encode_block(reinterpret_cast<const char*>(buffer), static_cast<int>(len), out, &s);
+        out[n + base64_encode_blockend(out + n, &s)] = '\0';
+
+        // base64 encoding may add newlines, but JSON strings may not contain 'real' newlines
+        // (only escaped ones). Remove any newlines in out.
+        for (char* cur = out; *cur; ++cur) {
+            if (*cur == '\n') {
+                *cur = ' ';
+            }
+        }
+
+        buff << '\"' << out << "\"\n";
+        delete[] out;
+    }
+
+    void StartObj(bool is_element = false) {
+        // if this appears as a plain array element, we need to insert a delimiter and we should also indent it
+        if (is_element) {
+            AddIndentation();
+            if (!first) {
+                buff << ',';
+            }
+        }
+        first = true;
+        buff << "{\n";
+        PushIndent();
+    }
+
+    void EndObj() {
+        PopIndent();
+        AddIndentation();
+        first = false;
+        buff << "}\n";
+    }
+
+    void StartArray(bool is_element = false) {
+        // if this appears as a plain array element, we need to insert a delimiter and we should also indent it
+        if (is_element) {
+            AddIndentation();
+            if (!first) {
+                buff << ',';
+            }
+        }
+        first = true;
+        buff << "[\n";
+        PushIndent();
+    }
+
+    void EndArray() {
+        PopIndent();
+        AddIndentation();
+        buff << "]\n";
+        first = false;
+    }
+
+    void AddIndentation() {
+        if (!(flags & Flag_DoNotIndent)) {
+            buff << indent;
+        }
+    }
+
+    void Delimit() {
+        if (!first) {
+            buff << ',';
+        }
+        else {
+            buff << ' ';
+            first = false;
+        }
+    }
+
+private:
+    template<typename Literal>
+    std::stringstream& LiteralToString(std::stringstream& stream, const Literal& s) {
+        stream << s;
+        return stream;
+    }
+
+    std::stringstream& LiteralToString(std::stringstream& stream, const aiString& s) {
+        std::string t;
+
+        // escape backslashes and single quotes, both would render the JSON invalid if left as is
+        t.reserve(s.length);
+        for (size_t i = 0; i < s.length; ++i) {
+
+            if (s.data[i] == '\\' || s.data[i] == '\'' || s.data[i] == '\"') {
+                t.push_back('\\');
+            }
+
+            t.push_back(s.data[i]);
+        }
+        stream << "\"";
+        stream << t;
+        stream << "\"";
+        return stream;
+    }
+
+    std::stringstream& LiteralToString(std::stringstream& stream, float f) {
+        if (!std::numeric_limits<float>::is_iec559) {
+            // on a non IEEE-754 platform, we make no assumptions about the representation or existence
+            // of special floating-point numbers. 
+            stream << f;
+            return stream;
+        }
+
+        // JSON does not support writing Inf/Nan
+        // [RFC 4672: "Numeric values that cannot be represented as sequences of digits
+        // (such as Infinity and NaN) are not permitted."]
+        // Nevertheless, many parsers will accept the special keywords Infinity, -Infinity and NaN
+        if (std::numeric_limits<float>::infinity() == fabs(f)) {
+            if (flags & Flag_WriteSpecialFloats) {
+                stream << (f < 0 ? "\"-" : "\"") + std::string("Infinity\"");
+                return stream;
+            }
+            //  we should print this warning, but we can't - this is called from within a generic assimp exporter, we cannot use cerr
+            //	std::cerr << "warning: cannot represent infinite number literal, substituting 0 instead (use -i flag to enforce Infinity/NaN)" << std::endl;
+            stream << "0.0";
+            return stream;
+        }
+        // f!=f is the most reliable test for NaNs that I know of
+        else if (f != f) {
+            if (flags & Flag_WriteSpecialFloats) {
+                stream << "\"NaN\"";
+                return stream;
+            }
+            //  we should print this warning, but we can't - this is called from within a generic assimp exporter, we cannot use cerr
+            //	std::cerr << "warning: cannot represent infinite number literal, substituting 0 instead (use -i flag to enforce Infinity/NaN)" << std::endl;
+            stream << "0.0";
+            return stream;
+        }
+
+        stream << f;
+        return stream;
+    }
+
+private:
+    Assimp::IOStream& out;
+    std::string indent, newline;
+    std::stringstream buff;
+    bool first;
+
+    unsigned int flags;
+};
+
+void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) {
+    out.StartArray(is_elem);
+    out.Element(ai.x);
+    out.Element(ai.y);
+    out.Element(ai.z);
+    out.EndArray();
+}
+
+void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) {
+    out.StartArray(is_elem);
+    out.Element(ai.w);
+    out.Element(ai.x);
+    out.Element(ai.y);
+    out.Element(ai.z);
+    out.EndArray();
+}
+
+void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) {
+    out.StartArray(is_elem);
+    out.Element(ai.r);
+    out.Element(ai.g);
+    out.Element(ai.b);
+    out.EndArray();
+}
+
+void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) {
+    out.StartArray(is_elem);
+    for (unsigned int x = 0; x < 4; ++x) {
+        for (unsigned int y = 0; y < 4; ++y) {
+            out.Element(ai[x][y]);
+        }
+    }
+    out.EndArray();
+}
+
+void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("offsetmatrix");
+    Write(out, ai.mOffsetMatrix, false);
+
+    out.Key("weights");
+    out.StartArray();
+    for (unsigned int i = 0; i < ai.mNumWeights; ++i) {
+        out.StartArray(true);
+        out.Element(ai.mWeights[i].mVertexId);
+        out.Element(ai.mWeights[i].mWeight);
+        out.EndArray();
+    }
+    out.EndArray();
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) {
+    out.StartArray(is_elem);
+    for (unsigned int i = 0; i < ai.mNumIndices; ++i) {
+        out.Element(ai.mIndices[i]);
+    }
+    out.EndArray();
+}
+
+void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("materialindex");
+    out.SimpleValue(ai.mMaterialIndex);
+
+    out.Key("primitivetypes");
+    out.SimpleValue(ai.mPrimitiveTypes);
+
+    out.Key("vertices");
+    out.StartArray();
+    for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+        out.Element(ai.mVertices[i].x);
+        out.Element(ai.mVertices[i].y);
+        out.Element(ai.mVertices[i].z);
+    }
+    out.EndArray();
+
+    if (ai.HasNormals()) {
+        out.Key("normals");
+        out.StartArray();
+        for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+            out.Element(ai.mNormals[i].x);
+            out.Element(ai.mNormals[i].y);
+            out.Element(ai.mNormals[i].z);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasTangentsAndBitangents()) {
+        out.Key("tangents");
+        out.StartArray();
+        for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+            out.Element(ai.mTangents[i].x);
+            out.Element(ai.mTangents[i].y);
+            out.Element(ai.mTangents[i].z);
+        }
+        out.EndArray();
+
+        out.Key("bitangents");
+        out.StartArray();
+        for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+            out.Element(ai.mBitangents[i].x);
+            out.Element(ai.mBitangents[i].y);
+            out.Element(ai.mBitangents[i].z);
+        }
+        out.EndArray();
+    }
+
+    if (ai.GetNumUVChannels()) {
+        out.Key("numuvcomponents");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.GetNumUVChannels(); ++n) {
+            out.Element(ai.mNumUVComponents[n]);
+        }
+        out.EndArray();
+
+        out.Key("texturecoords");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.GetNumUVChannels(); ++n) {
+            const unsigned int numc = ai.mNumUVComponents[n] ? ai.mNumUVComponents[n] : 2;
+
+            out.StartArray(true);
+            for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+                for (unsigned int c = 0; c < numc; ++c) {
+                    out.Element(ai.mTextureCoords[n][i][c]);
+                }
+            }
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+
+    if (ai.GetNumColorChannels()) {
+        out.Key("colors");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.GetNumColorChannels(); ++n) {
+            out.StartArray(true);
+            for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
+                out.Element(ai.mColors[n][i].r);
+                out.Element(ai.mColors[n][i].g);
+                out.Element(ai.mColors[n][i].b);
+                out.Element(ai.mColors[n][i].a);
+            }
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+
+    if (ai.mNumBones) {
+        out.Key("bones");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumBones; ++n) {
+            Write(out, *ai.mBones[n]);
+        }
+        out.EndArray();
+    }
+
+    out.Key("faces");
+    out.StartArray();
+    for (unsigned int n = 0; n < ai.mNumFaces; ++n) {
+        Write(out, ai.mFaces[n]);
+    }
+    out.EndArray();
+
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("transformation");
+    Write(out, ai.mTransformation, false);
+
+    if (ai.mNumMeshes) {
+        out.Key("meshes");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumMeshes; ++n) {
+            out.Element(ai.mMeshes[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.mNumChildren) {
+        out.Key("children");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumChildren; ++n) {
+            Write(out, *ai.mChildren[n]);
+        }
+        out.EndArray();
+    }
+
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("properties");
+    out.StartArray();
+    for (unsigned int i = 0; i < ai.mNumProperties; ++i) {
+        const aiMaterialProperty* const prop = ai.mProperties[i];
+        out.StartObj(true);
+        out.Key("key");
+        out.SimpleValue(prop->mKey);
+        out.Key("semantic");
+        out.SimpleValue(prop->mSemantic);
+        out.Key("index");
+        out.SimpleValue(prop->mIndex);
+
+        out.Key("type");
+        out.SimpleValue(prop->mType);
+
+        out.Key("value");
+        switch (prop->mType) {
+            case aiPTI_Float:
+                if (prop->mDataLength / sizeof(float) > 1) {
+                    out.StartArray();
+                    for (unsigned int i = 0; i < prop->mDataLength / sizeof(float); ++i) {
+                        out.Element(reinterpret_cast<float*>(prop->mData)[i]);
+                    }
+                    out.EndArray();
+                }
+                else {
+                    out.SimpleValue(*reinterpret_cast<float*>(prop->mData));
+                }
+                break;
+
+            case aiPTI_Integer:
+                if (prop->mDataLength / sizeof(int) > 1) {
+                    out.StartArray();
+                    for (unsigned int i = 0; i < prop->mDataLength / sizeof(int); ++i) {
+                        out.Element(reinterpret_cast<int*>(prop->mData)[i]);
+                    }
+                    out.EndArray();
+                } else {
+                    out.SimpleValue(*reinterpret_cast<int*>(prop->mData));
+                }
+                break;
+
+            case aiPTI_String:
+                {
+                    aiString s;
+                    aiGetMaterialString(&ai, prop->mKey.data, prop->mSemantic, prop->mIndex, &s);
+                    out.SimpleValue(s);
+                }
+                break;
+            case aiPTI_Buffer:
+                {
+                    // binary data is written as series of hex-encoded octets
+                    out.SimpleValue(prop->mData, prop->mDataLength);
+                }
+                break;
+            default:
+                assert(false);
+        }
+
+        out.EndObj();
+    }
+
+    out.EndArray();
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("width");
+    out.SimpleValue(ai.mWidth);
+
+    out.Key("height");
+    out.SimpleValue(ai.mHeight);
+
+    out.Key("formathint");
+    out.SimpleValue(aiString(ai.achFormatHint));
+
+    out.Key("data");
+    if (!ai.mHeight) {
+        out.SimpleValue(ai.pcData, ai.mWidth);
+    }
+    else {
+        out.StartArray();
+        for (unsigned int y = 0; y < ai.mHeight; ++y) {
+            out.StartArray(true);
+            for (unsigned int x = 0; x < ai.mWidth; ++x) {
+                const aiTexel& tx = ai.pcData[y*ai.mWidth + x];
+                out.StartArray(true);
+                out.Element(static_cast<unsigned int>(tx.r));
+                out.Element(static_cast<unsigned int>(tx.g));
+                out.Element(static_cast<unsigned int>(tx.b));
+                out.Element(static_cast<unsigned int>(tx.a));
+                out.EndArray();
+            }
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("type");
+    out.SimpleValue(ai.mType);
+
+    if (ai.mType == aiLightSource_SPOT || ai.mType == aiLightSource_UNDEFINED) {
+        out.Key("angleinnercone");
+        out.SimpleValue(ai.mAngleInnerCone);
+
+        out.Key("angleoutercone");
+        out.SimpleValue(ai.mAngleOuterCone);
+    }
+
+    out.Key("attenuationconstant");
+    out.SimpleValue(ai.mAttenuationConstant);
+
+    out.Key("attenuationlinear");
+    out.SimpleValue(ai.mAttenuationLinear);
+
+    out.Key("attenuationquadratic");
+    out.SimpleValue(ai.mAttenuationQuadratic);
+
+    out.Key("diffusecolor");
+    Write(out, ai.mColorDiffuse, false);
+
+    out.Key("specularcolor");
+    Write(out, ai.mColorSpecular, false);
+
+    out.Key("ambientcolor");
+    Write(out, ai.mColorAmbient, false);
+
+    if (ai.mType != aiLightSource_POINT) {
+        out.Key("direction");
+        Write(out, ai.mDirection, false);
+
+    }
+
+    if (ai.mType != aiLightSource_DIRECTIONAL) {
+        out.Key("position");
+        Write(out, ai.mPosition, false);
+    }
+
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mNodeName);
+
+    out.Key("prestate");
+    out.SimpleValue(ai.mPreState);
+
+    out.Key("poststate");
+    out.SimpleValue(ai.mPostState);
+
+    if (ai.mNumPositionKeys) {
+        out.Key("positionkeys");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumPositionKeys; ++n) {
+            const aiVectorKey& pos = ai.mPositionKeys[n];
+            out.StartArray(true);
+            out.Element(pos.mTime);
+            Write(out, pos.mValue);
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+
+    if (ai.mNumRotationKeys) {
+        out.Key("rotationkeys");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumRotationKeys; ++n) {
+            const aiQuatKey& rot = ai.mRotationKeys[n];
+            out.StartArray(true);
+            out.Element(rot.mTime);
+            Write(out, rot.mValue);
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+
+    if (ai.mNumScalingKeys) {
+        out.Key("scalingkeys");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumScalingKeys; ++n) {
+            const aiVectorKey& scl = ai.mScalingKeys[n];
+            out.StartArray(true);
+            out.Element(scl.mTime);
+            Write(out, scl.mValue);
+            out.EndArray();
+        }
+        out.EndArray();
+    }
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("tickspersecond");
+    out.SimpleValue(ai.mTicksPerSecond);
+
+    out.Key("duration");
+    out.SimpleValue(ai.mDuration);
+
+    out.Key("channels");
+    out.StartArray();
+    for (unsigned int n = 0; n < ai.mNumChannels; ++n) {
+        Write(out, *ai.mChannels[n]);
+    }
+    out.EndArray();
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) {
+    out.StartObj(is_elem);
+
+    out.Key("name");
+    out.SimpleValue(ai.mName);
+
+    out.Key("aspect");
+    out.SimpleValue(ai.mAspect);
+
+    out.Key("clipplanefar");
+    out.SimpleValue(ai.mClipPlaneFar);
+
+    out.Key("clipplanenear");
+    out.SimpleValue(ai.mClipPlaneNear);
+
+    out.Key("horizontalfov");
+    out.SimpleValue(ai.mHorizontalFOV);
+
+    out.Key("up");
+    Write(out, ai.mUp, false);
+
+    out.Key("lookat");
+    Write(out, ai.mLookAt, false);
+
+    out.EndObj();
+}
+
+void WriteFormatInfo(JSONWriter& out) {
+    out.StartObj();
+    out.Key("format");
+    out.SimpleValue("\"assimp2json\"");
+    out.Key("version");
+    out.SimpleValue(CURRENT_FORMAT_VERSION);
+    out.EndObj();
+}
+
+void Write(JSONWriter& out, const aiScene& ai) {
+    out.StartObj();
+
+    out.Key("__metadata__");
+    WriteFormatInfo(out);
+
+    out.Key("rootnode");
+    Write(out, *ai.mRootNode, false);
+
+    out.Key("flags");
+    out.SimpleValue(ai.mFlags);
+
+    if (ai.HasMeshes()) {
+        out.Key("meshes");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumMeshes; ++n) {
+            Write(out, *ai.mMeshes[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasMaterials()) {
+        out.Key("materials");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumMaterials; ++n) {
+            Write(out, *ai.mMaterials[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasAnimations()) {
+        out.Key("animations");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumAnimations; ++n) {
+            Write(out, *ai.mAnimations[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasLights()) {
+        out.Key("lights");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumLights; ++n) {
+            Write(out, *ai.mLights[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasCameras()) {
+        out.Key("cameras");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumCameras; ++n) {
+            Write(out, *ai.mCameras[n]);
+        }
+        out.EndArray();
+    }
+
+    if (ai.HasTextures()) {
+        out.Key("textures");
+        out.StartArray();
+        for (unsigned int n = 0; n < ai.mNumTextures; ++n) {
+            Write(out, *ai.mTextures[n]);
+        }
+        out.EndArray();
+    }
+    out.EndObj();
+}
+
+
+void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* scene, const Assimp::ExportProperties*) {
+    std::unique_ptr<Assimp::IOStream> str(io->Open(file, "wt"));
+    if (!str) {
+        //throw Assimp::DeadlyExportError("could not open output file");
+    }
+
+    // get a copy of the scene so we can modify it
+    aiScene* scenecopy_tmp;
+    aiCopyScene(scene, &scenecopy_tmp);
+
+    try {
+        // split meshes so they fit into a 16 bit index buffer
+        MeshSplitter splitter;
+        splitter.SetLimit(1 << 16);
+        splitter.Execute(scenecopy_tmp);
+
+        // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters
+        JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats);
+        Write(s, *scenecopy_tmp);
+
+    }
+    catch (...) {
+        aiFreeScene(scenecopy_tmp);
+        throw;
+    }
+    aiFreeScene(scenecopy_tmp);
+}
+
+}
+
+#endif // ASSIMP_BUILD_NO_ASSJSON_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 320 - 0
code/Assjson/mesh_splitter.cpp

@@ -0,0 +1,320 @@
+/*
+Assimp2Json
+Copyright (c) 2011, Alexander C. Gessler
+
+Licensed under a 3-clause BSD license. See the LICENSE file for more information.
+
+*/
+
+#include "mesh_splitter.h"
+
+#include <assimp/scene.h>
+
+// ----------------------------------------------------------------------------
+// Note: this is largely based on assimp's SplitLargeMeshes_Vertex process.
+// it is refactored and the coding style is slightly improved, though.
+// ----------------------------------------------------------------------------
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void MeshSplitter::Execute( aiScene* pScene) {
+	std::vector<std::pair<aiMesh*, unsigned int> > source_mesh_map;
+
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+		SplitMesh(a, pScene->mMeshes[a],source_mesh_map);
+	}
+
+	const unsigned int size = static_cast<unsigned int>(source_mesh_map.size());
+	if (size != pScene->mNumMeshes) {
+		// it seems something has been split. rebuild the mesh list
+		delete[] pScene->mMeshes;
+		pScene->mNumMeshes = size;
+		pScene->mMeshes = new aiMesh*[size]();
+
+		for (unsigned int i = 0; i < size;++i) {
+			pScene->mMeshes[i] = source_mesh_map[i].first;
+		}
+
+		// now we need to update all nodes
+		UpdateNode(pScene->mRootNode,source_mesh_map);
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void MeshSplitter::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map) {
+	// TODO: should better use std::(multi)set for source_mesh_map.
+
+	// for every index in out list build a new entry
+	std::vector<unsigned int> aiEntries;
+	aiEntries.reserve(pcNode->mNumMeshes + 1);
+	for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)	{
+		for (unsigned int a = 0, end = static_cast<unsigned int>(source_mesh_map.size()); a < end;++a)	{
+			if (source_mesh_map[a].second == pcNode->mMeshes[i])	{
+				aiEntries.push_back(a);
+			}
+		}
+	}
+
+	// now build the new list
+	delete pcNode->mMeshes;
+	pcNode->mNumMeshes = static_cast<unsigned int>(aiEntries.size());
+	pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+
+	for (unsigned int b = 0; b < pcNode->mNumMeshes;++b) {
+		pcNode->mMeshes[b] = aiEntries[b];
+	}
+
+	// recursively update children
+	for (unsigned int i = 0, end = pcNode->mNumChildren; i < end;++i)	{
+		UpdateNode ( pcNode->mChildren[i], source_mesh_map );
+	}
+	return;
+}
+
+#define WAS_NOT_COPIED 0xffffffff
+
+typedef std::pair <unsigned int,float> PerVertexWeight;
+typedef std::vector	<PerVertexWeight> VertexWeightTable;
+
+// ------------------------------------------------------------------------------------------------
+VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) {
+	if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) {
+		return nullptr;
+	}
+
+	VertexWeightTable* const avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
+	for (unsigned int i = 0; i < pMesh->mNumBones;++i)	{
+
+		aiBone* bone = pMesh->mBones[i];
+		for (unsigned int a = 0; a < bone->mNumWeights;++a)	{
+			const aiVertexWeight& weight = bone->mWeights[a];
+			avPerVertexWeights[weight.mVertexId].push_back( std::make_pair(i,weight.mWeight) );
+		}
+	}
+	return avPerVertexWeights;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map) {
+	// TODO: should better use std::(multi)set for source_mesh_map.
+
+	if (in_mesh->mNumVertices <= LIMIT)	{
+		source_mesh_map.push_back(std::make_pair(in_mesh,a));
+		return;
+	}
+
+	// build a per-vertex weight list if necessary
+	VertexWeightTable* avPerVertexWeights = ComputeVertexBoneWeightTable(in_mesh);
+
+	// we need to split this mesh into sub meshes. Estimate submesh size
+	const unsigned int sub_meshes = (in_mesh->mNumVertices / LIMIT) + 1;
+
+	// create a std::vector<unsigned int> to remember which vertices have already 
+	// been copied and to which position (i.e. output index)
+	std::vector<unsigned int> was_copied_to;
+	was_copied_to.resize(in_mesh->mNumVertices,WAS_NOT_COPIED);
+
+	// Try to find a good estimate for the number of output faces
+	// per mesh. Add 12.5% as buffer
+	unsigned int size_estimated = in_mesh->mNumFaces / sub_meshes;
+	size_estimated += size_estimated / 8;
+
+	// now generate all submeshes
+	unsigned int base = 0;
+	while (true) {
+		const unsigned int out_vertex_index = LIMIT;
+
+		aiMesh* out_mesh = new aiMesh();			
+		out_mesh->mNumVertices = 0;
+		out_mesh->mMaterialIndex = in_mesh->mMaterialIndex;
+
+		// the name carries the adjacency information between the meshes
+		out_mesh->mName = in_mesh->mName;
+
+		typedef std::vector<aiVertexWeight> BoneWeightList;
+		if (in_mesh->HasBones())	{
+			out_mesh->mBones = new aiBone*[in_mesh->mNumBones]();
+		}
+
+		// clear the temporary helper array
+		if (base)	{
+			std::fill(was_copied_to.begin(), was_copied_to.end(), WAS_NOT_COPIED);
+		}
+
+		std::vector<aiFace> vFaces;
+
+		// reserve enough storage for most cases
+		if (in_mesh->HasPositions()) {
+			out_mesh->mVertices = new aiVector3D[out_vertex_index];
+		}
+
+		if (in_mesh->HasNormals()) {
+			out_mesh->mNormals = new aiVector3D[out_vertex_index];
+		}
+
+		if (in_mesh->HasTangentsAndBitangents())	{
+			out_mesh->mTangents = new aiVector3D[out_vertex_index];
+			out_mesh->mBitangents = new aiVector3D[out_vertex_index];
+		}
+
+		for (unsigned int c = 0; in_mesh->HasVertexColors(c);++c)	{
+			out_mesh->mColors[c] = new aiColor4D[out_vertex_index];
+		}
+
+		for (unsigned int c = 0; in_mesh->HasTextureCoords(c);++c)	{
+			out_mesh->mNumUVComponents[c] = in_mesh->mNumUVComponents[c];
+			out_mesh->mTextureCoords[c] = new aiVector3D[out_vertex_index];
+		}
+		vFaces.reserve(size_estimated);
+
+		// (we will also need to copy the array of indices)
+		while (base < in_mesh->mNumFaces) {
+			const unsigned int iNumIndices = in_mesh->mFaces[base].mNumIndices;
+
+			// doesn't catch degenerates but is quite fast
+			unsigned int iNeed = 0;
+			for (unsigned int v = 0; v < iNumIndices;++v)	{
+				unsigned int index = in_mesh->mFaces[base].mIndices[v];
+
+				// check whether we do already have this vertex
+				if (WAS_NOT_COPIED == was_copied_to[index])	{
+					iNeed++; 
+				}
+			}
+			if (out_mesh->mNumVertices + iNeed > out_vertex_index)	{
+				// don't use this face
+				break;
+			}
+
+			vFaces.push_back(aiFace());
+			aiFace& rFace = vFaces.back();
+
+			// setup face type and number of indices
+			rFace.mNumIndices = iNumIndices;
+			rFace.mIndices = new unsigned int[iNumIndices];
+
+			// need to update the output primitive types
+			switch (rFace.mNumIndices)
+			{
+			case 1:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+				break;
+			case 2:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+				break;
+			case 3:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+				break;
+			default:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+			}
+
+			// and copy the contents of the old array, offset them by current base
+			for (unsigned int v = 0; v < iNumIndices;++v) {
+				const unsigned int index = in_mesh->mFaces[base].mIndices[v];
+
+				// check whether we do already have this vertex
+				if (WAS_NOT_COPIED != was_copied_to[index]) {
+					rFace.mIndices[v] = was_copied_to[index];
+					continue;
+				}
+
+				// copy positions
+				out_mesh->mVertices[out_mesh->mNumVertices] = (in_mesh->mVertices[index]);
+
+				// copy normals
+				if (in_mesh->HasNormals()) {
+					out_mesh->mNormals[out_mesh->mNumVertices] = (in_mesh->mNormals[index]);
+				}
+
+				// copy tangents/bi-tangents
+				if (in_mesh->HasTangentsAndBitangents()) {
+					out_mesh->mTangents[out_mesh->mNumVertices] = (in_mesh->mTangents[index]);
+					out_mesh->mBitangents[out_mesh->mNumVertices] = (in_mesh->mBitangents[index]);
+				}
+
+				// texture coordinates
+				for (unsigned int c = 0;  c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+					if (in_mesh->HasTextureCoords( c)) {
+						out_mesh->mTextureCoords[c][out_mesh->mNumVertices] = in_mesh->mTextureCoords[c][index];
+					}
+				}
+				// vertex colors 
+				for (unsigned int c = 0;  c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) {
+					if (in_mesh->HasVertexColors( c)) {
+						out_mesh->mColors[c][out_mesh->mNumVertices] = in_mesh->mColors[c][index];
+					}
+				}
+				// check whether we have bone weights assigned to this vertex
+				rFace.mIndices[v] = out_mesh->mNumVertices;
+				if (avPerVertexWeights) {
+					VertexWeightTable& table = avPerVertexWeights[ out_mesh->mNumVertices ];
+					for (VertexWeightTable::const_iterator iter = table.begin(), end = table.end(); iter != end;++iter) {
+						// allocate the bone weight array if necessary and store it in the mBones field (HACK!)
+						BoneWeightList* weight_list = reinterpret_cast<BoneWeightList*>(out_mesh->mBones[(*iter).first]);
+						if (!weight_list) {
+							weight_list = new BoneWeightList();
+							out_mesh->mBones[(*iter).first] = reinterpret_cast<aiBone*>(weight_list);
+						}
+						weight_list->push_back(aiVertexWeight(out_mesh->mNumVertices,(*iter).second));
+					}
+				}
+
+				was_copied_to[index] = out_mesh->mNumVertices;
+				out_mesh->mNumVertices++;
+			}
+			base++;
+			if(out_mesh->mNumVertices == out_vertex_index) {
+				// break here. The face is only added if it was complete
+				break;
+			}
+		}
+
+		// check which bones we'll need to create for this submesh
+		if (in_mesh->HasBones()) {
+			aiBone** ppCurrent = out_mesh->mBones;
+			for (unsigned int k = 0; k < in_mesh->mNumBones;++k) {
+				// check whether the bone exists
+				BoneWeightList* const weight_list = reinterpret_cast<BoneWeightList*>(out_mesh->mBones[k]);
+
+				if (weight_list) {
+					const aiBone* const bone_in = in_mesh->mBones[k];
+					aiBone* const bone_out = new aiBone();
+					*ppCurrent++ = bone_out;
+					bone_out->mName = aiString(bone_in->mName);
+					bone_out->mOffsetMatrix =bone_in->mOffsetMatrix;
+					bone_out->mNumWeights = (unsigned int)weight_list->size();
+					bone_out->mWeights = new aiVertexWeight[bone_out->mNumWeights];
+
+					// copy the vertex weights
+					::memcpy(bone_out->mWeights, &(*weight_list)[0],bone_out->mNumWeights * sizeof(aiVertexWeight));
+
+					delete weight_list;
+					out_mesh->mNumBones++;
+				}
+			}
+		}
+
+		// copy the face list to the mesh
+		out_mesh->mFaces = new aiFace[vFaces.size()];
+		out_mesh->mNumFaces = (unsigned int)vFaces.size();
+
+		for (unsigned int p = 0; p < out_mesh->mNumFaces;++p) {
+			out_mesh->mFaces[p] = vFaces[p];
+		}
+
+		// add the newly created mesh to the list
+		source_mesh_map.push_back(std::make_pair(out_mesh,a));
+
+		if (base == in_mesh->mNumFaces) {
+			break;
+		}
+	}
+
+	// delete the per-vertex weight list again
+	delete[] avPerVertexWeights;
+
+	// now delete the old mesh data
+	delete in_mesh;
+}

+ 61 - 0
code/Assjson/mesh_splitter.h

@@ -0,0 +1,61 @@
+/*
+Assimp2Json
+Copyright (c) 2011, Alexander C. Gessler
+
+Licensed under a 3-clause BSD license. See the LICENSE file for more information.
+
+*/
+
+#ifndef INCLUDED_MESH_SPLITTER
+#define INCLUDED_MESH_SPLITTER
+
+// ----------------------------------------------------------------------------
+// Note: this is largely based on assimp's SplitLargeMeshes_Vertex process.
+// it is refactored and the coding style is slightly improved, though.
+// ----------------------------------------------------------------------------
+
+#include <vector>
+
+struct aiScene;
+struct aiMesh;
+struct aiNode;
+
+// ---------------------------------------------------------------------------
+/** Splits meshes of unique vertices into meshes with no more vertices than
+ *  a given, configurable threshold value. 
+ */
+class MeshSplitter 
+{
+
+public:
+	
+	void SetLimit(unsigned int l) {
+		LIMIT = l;
+	}
+
+	unsigned int GetLimit() const {
+		return LIMIT;
+	}
+
+public:
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	 * At the moment a process is not supposed to fail.
+	 * @param pScene The imported data to work at.
+	 */
+	void Execute( aiScene* pScene);
+
+
+private:
+
+	void UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map);
+	void SplitMesh (unsigned int index, aiMesh* mesh, std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map);
+
+public:
+
+	unsigned int LIMIT;
+};
+
+#endif // INCLUDED_MESH_SPLITTER
+

+ 8 - 0
code/CMakeLists.txt

@@ -810,6 +810,14 @@ ADD_ASSIMP_IMPORTER( MMD
   MMD/MMDVmdParser.h
 )
 
+ADD_ASSIMP_EXPORTER( Assjson
+  Assjson/cencode.c
+  Assjson/cencode.h
+  Assjson/json_exporter.cpp
+  Assjson/mesh_splitter.cpp
+  Assjson/mesh_splitter.h
+)
+
 # Workaround for issue #2406 - force problematic large file to be optimized to prevent string table overflow error
 # Used -Os instead of -O2 as previous issues had mentioned, since -Os is roughly speaking -O2, excluding any
 # optimizations that take up extra space. Given that the issue is a string table overflowing, -Os seemed appropriate

+ 7 - 4
code/COB/COBLoader.cpp

@@ -898,6 +898,7 @@ public:
     : nfo(nfo)
     , reader(reader)
     , cur(reader.GetCurrentPos()) {
+        // empty
     }
 
     ~chunk_guard() {
@@ -905,7 +906,7 @@ public:
         if(nfo.size != static_cast<unsigned int>(-1)) {
             try {
                 reader.IncPtr( static_cast< int >( nfo.size ) - reader.GetCurrentPos() + cur );
-            } catch (const DeadlyImportError& e ) {
+            } catch (const DeadlyImportError& ) {
                 // out of limit so correct the value
                 reader.IncPtr( reader.GetReadLimit() );
             }
@@ -913,15 +914,17 @@ public:
     }
 
 private:
-
     const COB::ChunkInfo& nfo;
     StreamReaderLE& reader;
     long cur;
 };
 
 // ------------------------------------------------------------------------------------------------
-void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader)
-{
+void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader) {
+    if (nullptr == reader) {
+        return;
+    }
+
     while(1) {
         std::string type;
          type += reader -> GetI1()

+ 102 - 104
code/Collada/ColladaLoader.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -43,7 +41,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 /** @file Implementation of the Collada loader */
 
-
 #ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
 
 #include "ColladaLoader.h"
@@ -67,7 +64,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <numeric>
 #include <memory>
 
-using namespace Assimp;
+namespace Assimp {
+    
 using namespace Assimp::Formatter;
 
 static const aiImporterDesc desc = {
@@ -112,7 +110,7 @@ ColladaLoader::~ColladaLoader() {
 // Returns whether the class can handle the format of the given file.
 bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
     // check file extension
-    std::string extension = GetExtension(pFile);
+    const std::string extension = GetExtension(pFile);
 
     if (extension == "dae") {
         return true;
@@ -166,12 +164,13 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
     // parse the input file
     ColladaParser parser( pIOHandler, pFile);
 
-    if( !parser.mRootNode)
+    if( !parser.mRootNode) {
         throw DeadlyImportError( "Collada: File came out empty. Something is wrong here.");
+    }
 
     // reserve some storage to avoid unnecessary reallocs
-    newMats.reserve(parser.mMaterialLibrary.size()*2);
-    mMeshes.reserve(parser.mMeshLibrary.size()*2);
+    newMats.reserve(parser.mMaterialLibrary.size()*2u);
+    mMeshes.reserve(parser.mMeshLibrary.size()*2u);
 
     mCameras.reserve(parser.mCameraLibrary.size());
     mLights.reserve(parser.mLightLibrary.size());
@@ -191,19 +190,20 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
                                                         0,  0,  parser.mUnitSize,  0,
                                                         0,  0,  0,  1);
     if( !ignoreUpDirection ) {
-    // Convert to Y_UP, if different orientation
-    if( parser.mUpDirection == ColladaParser::UP_X)
-        pScene->mRootNode->mTransformation *= aiMatrix4x4(
-                0, -1,  0,  0,
-                1,  0,  0,  0,
-                0,  0,  1,  0,
-                0,  0,  0,  1);
-    else if( parser.mUpDirection == ColladaParser::UP_Z)
-        pScene->mRootNode->mTransformation *= aiMatrix4x4(
-                1,  0,  0,  0,
-                0,  0,  1,  0,
-                0, -1,  0,  0,
-                0,  0,  0,  1);
+        // Convert to Y_UP, if different orientation
+        if( parser.mUpDirection == ColladaParser::UP_X) {
+            pScene->mRootNode->mTransformation *= aiMatrix4x4(
+                    0, -1,  0,  0,
+                    1,  0,  0,  0,
+                    0,  0,  1,  0,
+                    0,  0,  0,  1);
+        } else if( parser.mUpDirection == ColladaParser::UP_Z) {
+            pScene->mRootNode->mTransformation *= aiMatrix4x4(
+                    1,  0,  0,  0,
+                    0,  0,  1,  0,
+                    0, -1,  0,  0,
+                    0,  0,  0,  1);
+        }
     }
 
     // Store scene metadata
@@ -211,8 +211,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
         const size_t numMeta(parser.mAssetMetaData.size());
         pScene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
         size_t i = 0;
-        for (auto it = parser.mAssetMetaData.cbegin(); it != parser.mAssetMetaData.cend(); ++it, ++i)
-        {
+        for (auto it = parser.mAssetMetaData.cbegin(); it != parser.mAssetMetaData.cend(); ++it, ++i) {
             pScene->mMetaData->Set(static_cast<unsigned int>(i), (*it).first, (*it).second);
         }
     }
@@ -232,9 +231,8 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
     // store all animations
     StoreAnimations( pScene, parser);
 
-
     // If no meshes have been loaded, it's probably just an animated skeleton.
-    if (!pScene->mNumMeshes) {
+    if ( 0u == pScene->mNumMeshes) {
 
         if (!noSkeletonMesh) {
             SkeletonMeshBuilder hero(pScene);
@@ -302,13 +300,12 @@ void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Co
         // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632
         // need to check for both name and ID to catch all. To avoid breaking valid files,
         // the workaround is only enabled when the first attempt to resolve the node has failed.
-        if (!nd) {
+        if (nullptr == nd) {
             nd = FindNode(pParser.mRootNode, nodeInst.mNode);
         }
-        if (!nd)
+        if (nullptr == nd) {
             ASSIMP_LOG_ERROR_F("Collada: Unable to resolve reference to instanced node ", nodeInst.mNode);
-
-        else {
+        } else {
             //  attach this node to the list of children
             resolved.push_back(nd);
         }
@@ -318,7 +315,7 @@ void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Co
 // ------------------------------------------------------------------------------------------------
 // Resolve UV channels
 void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
-     const Collada::SemanticMappingTable& table) {
+        const Collada::SemanticMappingTable& table) {
     std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel);
     if (it != table.mMap.end()) {
         if (it->second.mType != Collada::IT_Texcoord) {
@@ -431,8 +428,7 @@ void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Col
                 out->mAspect = std::tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) /
                     std::tan(AI_DEG_TO_RAD(srcCamera->mVerFov));
             }
-        }
-        else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f)  {
+        } else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f)  {
             out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(std::atan(srcCamera->mAspect *
                 std::tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f)));
         }
@@ -470,7 +466,7 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll
                 }
             }
 
-            if( !srcMesh) {
+            if( nullptr == srcMesh) {
                 ASSIMP_LOG_WARN_F( "Collada: Unable to find geometry for ID \"", mid.mMeshOrController, "\". Skipping." );
                 continue;
             }
@@ -770,12 +766,13 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::
             IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex];
             size_t pairCount = pSrcController->mWeightCounts[orgIndex];
 
-            for( size_t b = 0; b < pairCount; ++b, ++iit)
-            {
-                size_t jointIndex = iit->first;
-                size_t vertexIndex = iit->second;
-
-                ai_real weight = ReadFloat( weightsAcc, weights, vertexIndex, 0);
+            for( size_t b = 0; b < pairCount; ++b, ++iit) {
+                const size_t jointIndex = iit->first;
+                const size_t vertexIndex = iit->second;                
+                ai_real weight = 1.0f;
+                if (!weights.mValues.empty()) {
+                    weight = ReadFloat(weightsAcc, weights, vertexIndex, 0);
+                }
 
                 // one day I gonna kill that XSI Collada exporter
                 if( weight > 0.0f)
@@ -790,19 +787,21 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::
 
         // count the number of bones which influence vertices of the current submesh
         size_t numRemainingBones = 0;
-        for( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it)
-            if( it->size() > 0)
-                numRemainingBones++;
+        for( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it) {
+            if( it->size() > 0) {
+                ++numRemainingBones;
+            }
+        }
 
         // create bone array and copy bone weights one by one
         dstMesh->mNumBones = static_cast<unsigned int>(numRemainingBones);
         dstMesh->mBones = new aiBone*[numRemainingBones];
         size_t boneCount = 0;
-        for( size_t a = 0; a < numBones; ++a)
-        {
+        for( size_t a = 0; a < numBones; ++a) {
             // omit bones without weights
-            if( dstBones[a].size() == 0)
+            if( dstBones[a].empty() ) {
                 continue;
+            }
 
             // create bone with its weights
             aiBone* bone = new aiBone;
@@ -848,14 +847,16 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::
             // and replace the bone's name by the node's name so that the user can use the standard
             // find-by-name method to associate nodes with bones.
             const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data);
-            if( !bnode)
+            if( !bnode) {
                 bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data);
+            }
 
             // assign the name that we would have assigned for the source node
-            if( bnode)
+            if( bnode) {
                 bone->mName.Set( FindNameForNode( bnode));
-            else
+            } else {
                 ASSIMP_LOG_WARN_F( "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"", bone->mName.data, "\"." );
+            }
 
             // and insert bone
             dstMesh->mBones[boneCount++] = bone;
@@ -867,89 +868,80 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::
 
 // ------------------------------------------------------------------------------------------------
 // Stores all meshes in the given scene
-void ColladaLoader::StoreSceneMeshes( aiScene* pScene)
-{
+void ColladaLoader::StoreSceneMeshes( aiScene* pScene) {
     pScene->mNumMeshes = static_cast<unsigned int>(mMeshes.size());
-    if( mMeshes.size() > 0)
-    {
-        pScene->mMeshes = new aiMesh*[mMeshes.size()];
-        std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes);
-        mMeshes.clear();
+    if( mMeshes.empty() ) {
+        return;
     }
+    pScene->mMeshes = new aiMesh*[mMeshes.size()];
+    std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes);
+    mMeshes.clear();
 }
 
 // ------------------------------------------------------------------------------------------------
 // Stores all cameras in the given scene
-void ColladaLoader::StoreSceneCameras( aiScene* pScene)
-{
+void ColladaLoader::StoreSceneCameras( aiScene* pScene) {
     pScene->mNumCameras = static_cast<unsigned int>(mCameras.size());
-    if( mCameras.size() > 0)
-    {
-        pScene->mCameras = new aiCamera*[mCameras.size()];
-        std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras);
-        mCameras.clear();
+    if( mCameras.empty() ) {
+        return;
     }
+    pScene->mCameras = new aiCamera*[mCameras.size()];
+    std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras);
+    mCameras.clear();
 }
 
 // ------------------------------------------------------------------------------------------------
 // Stores all lights in the given scene
-void ColladaLoader::StoreSceneLights( aiScene* pScene)
-{
+void ColladaLoader::StoreSceneLights( aiScene* pScene) {
     pScene->mNumLights = static_cast<unsigned int>(mLights.size());
-    if( mLights.size() > 0)
-    {
-        pScene->mLights = new aiLight*[mLights.size()];
-        std::copy( mLights.begin(), mLights.end(), pScene->mLights);
-        mLights.clear();
+    if( mLights.empty() ) {
+        return;
     }
+    pScene->mLights = new aiLight*[mLights.size()];
+    std::copy( mLights.begin(), mLights.end(), pScene->mLights);
+    mLights.clear();
 }
 
 // ------------------------------------------------------------------------------------------------
 // Stores all textures in the given scene
-void ColladaLoader::StoreSceneTextures( aiScene* pScene)
-{
+void ColladaLoader::StoreSceneTextures( aiScene* pScene) {
     pScene->mNumTextures = static_cast<unsigned int>(mTextures.size());
-    if( mTextures.size() > 0)
-    {
-        pScene->mTextures = new aiTexture*[mTextures.size()];
-        std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures);
-        mTextures.clear();
+    if( mTextures.empty() ) {
+        return;
     }
+    pScene->mTextures = new aiTexture*[mTextures.size()];
+    std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures);
+    mTextures.clear();
 }
 
 // ------------------------------------------------------------------------------------------------
 // Stores all materials in the given scene
-void ColladaLoader::StoreSceneMaterials( aiScene* pScene)
-{
+void ColladaLoader::StoreSceneMaterials( aiScene* pScene) {
     pScene->mNumMaterials = static_cast<unsigned int>(newMats.size());
-
-    if (newMats.size() > 0) {
-        pScene->mMaterials = new aiMaterial*[newMats.size()];
-        for (unsigned int i = 0; i < newMats.size();++i)
-            pScene->mMaterials[i] = newMats[i].second;
-
-        newMats.clear();
+    if (newMats.empty() ) {
+        return;
+    }
+    pScene->mMaterials = new aiMaterial*[newMats.size()];
+    for (unsigned int i = 0; i < newMats.size();++i) {
+        pScene->mMaterials[i] = newMats[i].second;
     }
+    newMats.clear();
 }
 
 // ------------------------------------------------------------------------------------------------
 // Stores all animations
-void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser)
-{
+void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser) {
     // recursively collect all animations from the collada scene
     StoreAnimations( pScene, pParser, &pParser.mAnims, "");
 
     // catch special case: many animations with the same length, each affecting only a single node.
     // we need to unite all those single-node-anims to a proper combined animation
-    for( size_t a = 0; a < mAnims.size(); ++a)
-    {
+    for( size_t a = 0; a < mAnims.size(); ++a) {
         aiAnimation* templateAnim = mAnims[a];
-        if( templateAnim->mNumChannels == 1)
-        {
+        if( templateAnim->mNumChannels == 1) {
             // search for other single-channel-anims with the same duration
             std::vector<size_t> collectedAnimIndices;
-            for( size_t b = a+1; b < mAnims.size(); ++b)
-            {
+            for( size_t b = a+1; b < mAnims.size(); ++b) {
                 aiAnimation* other = mAnims[b];
                 if( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && 
                         other->mTicksPerSecond == templateAnim->mTicksPerSecond )
@@ -1880,19 +1872,23 @@ const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const
 
 // ------------------------------------------------------------------------------------------------
 // Finds a node in the collada scene by the given SID
-const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const
-{
-  if( pNode->mSID == pSID)
-    return pNode;
+const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const {
+    if (nullptr == pNode) {
+        return nullptr;
+    }
 
-  for( size_t a = 0; a < pNode->mChildren.size(); ++a)
-  {
-    const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID);
-    if( node)
-      return node;
-  }
+    if (pNode->mSID == pSID) {
+        return pNode;
+    }
 
-  return NULL;
+    for( size_t a = 0; a < pNode->mChildren.size(); ++a) {
+        const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID);
+        if (node) {
+            return node;
+        }
+    }
+
+    return nullptr;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -1926,4 +1922,6 @@ std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode)
     }
 }
 
+} // Namespace Assimp
+
 #endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER

+ 0 - 4
code/Common/CreateAnimMesh.cpp

@@ -47,10 +47,6 @@ namespace Assimp    {
 aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh)
 {
     aiAnimMesh *animesh = new aiAnimMesh;
-    animesh->mVertices = NULL;
-    animesh->mNormals = NULL;
-    animesh->mTangents = NULL;
-    animesh->mBitangents = NULL;
     animesh->mNumVertices = mesh->mNumVertices;
     if (mesh->mVertices) {
         animesh->mVertices = new aiVector3D[animesh->mNumVertices];

+ 6 - 1
code/Common/Exporter.cpp

@@ -102,6 +102,7 @@ void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperti
 void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
 void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
+void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
 
 // ------------------------------------------------------------------------------------------------
 // global array of all export formats which Assimp supports in its current build
@@ -179,7 +180,11 @@ Exporter::ExportFormatEntry gExporters[] =
 #endif
 
 #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
-    Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 )
+    Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ),
+#endif
+
+#ifndef ASSIMP_BUILD_NO_Assjson_EXPORTER
+    Exporter::ExportFormatEntry("json", "Plain JSON representation of the Assimp scene data structure", "json", &ExportAssimp2Json, 0)
 #endif
 };
 

+ 1 - 1
code/Common/ImporterRegistry.cpp

@@ -364,7 +364,7 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
 void DeleteImporterInstanceList(std::vector< BaseImporter* >& deleteList){
 	for(size_t i= 0; i<deleteList.size();++i){
 		delete deleteList[i];
-		deleteList[i]=NULL;
+		deleteList[i]=nullptr;
 	}//for
 }
 

+ 18 - 14
code/FBX/FBXConverter.cpp

@@ -420,11 +420,6 @@ namespace Assimp {
 
             out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight();
 
-            //cameras are defined along positive x direction
-            /*out_camera->mPosition = cam.Position();
-            out_camera->mLookAt = (cam.InterestPosition() - out_camera->mPosition).Normalize();
-            out_camera->mUp = cam.UpVector();*/
-
             out_camera->mPosition = aiVector3D(0.0f);
             out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f);
             out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
@@ -667,8 +662,7 @@ namespace Assimp {
                     if ((v - all_ones).SquareLength() > zero_epsilon) {
                         return true;
                     }
-                }
-                else if (ok) {
+                } else if (ok) {
                     if (v.SquareLength() > zero_epsilon) {
                         return true;
                     }
@@ -1253,10 +1247,10 @@ namespace Assimp {
             ai_assert(count_faces);
             ai_assert(count_vertices);
 
-            // mapping from output indices to DOM indexing, needed to resolve weights
+            // mapping from output indices to DOM indexing, needed to resolve weights or blendshapes
             std::vector<unsigned int> reverseMapping;
             std::map<unsigned int, unsigned int> translateIndexMap;
-            if (process_weights) {
+            if (process_weights || mesh.GetBlendShapes().size() > 0) {
                 reverseMapping.resize(count_vertices);
             }
 
@@ -1413,8 +1407,10 @@ namespace Assimp {
                             unsigned int count = 0;
                             const unsigned int* outIndices = mesh.ToOutputVertexIndex(index, count);
                             for (unsigned int k = 0; k < count; k++) {
-                                unsigned int index = translateIndexMap[outIndices[k]];
-
+                                unsigned int outIndex = outIndices[k];
+                                if (translateIndexMap.find(outIndex) == translateIndexMap.end())
+                                    continue;
+                                unsigned int index = translateIndexMap[outIndex];
                                 animMesh->mVertices[index] += vertex;
                                 if (animMesh->mNormals != nullptr) {
                                     animMesh->mNormals[index] += normal;
@@ -1428,6 +1424,15 @@ namespace Assimp {
                 }
             }
 
+            const size_t numAnimMeshes = animMeshes.size();
+            if (numAnimMeshes > 0) {
+                out_mesh->mNumAnimMeshes = static_cast<unsigned int>(numAnimMeshes);
+                out_mesh->mAnimMeshes = new aiAnimMesh*[numAnimMeshes];
+                for (size_t i = 0; i < numAnimMeshes; i++) {
+                    out_mesh->mAnimMeshes[i] = animMeshes.at(i);
+                }
+            }
+
             return static_cast<unsigned int>(meshes.size() - 1);
         }
 
@@ -1733,9 +1738,8 @@ namespace Assimp {
         }
 
         void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures,
-            const std::string& propName,
-            aiTextureType target, const MeshGeometry* const mesh)
-        {
+                const std::string& propName,
+                aiTextureType target, const MeshGeometry* const mesh) {
             TextureMap::const_iterator it = textures.find(propName);
             if (it == textures.end()) {
                 return;

+ 1 - 0
code/FBX/FBXConverter.h

@@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXUtil.h"
 #include "FBXProperties.h"
 #include "FBXImporter.h"
+
 #include <assimp/anim.h>
 #include <assimp/material.h>
 #include <assimp/light.h>

+ 1 - 1
code/FBX/FBXDocument.h

@@ -627,7 +627,7 @@ public:
         return content;
     }
 
-    uint32_t ContentLength() const {
+    uint64_t ContentLength() const {
         return contentLength;
     }
 

+ 20 - 1
code/FBX/FBXExporter.cpp

@@ -1617,6 +1617,17 @@ void FBXExporter::WriteObjects ()
     // at the same time we can build a list of all the skeleton nodes,
     // which will be used later to mark them as type "limbNode".
     std::unordered_set<const aiNode*> limbnodes;
+    
+    //actual bone nodes in fbx, without parenting-up
+    std::unordered_set<std::string> setAllBoneNamesInScene;
+    for(unsigned int m = 0; m < mScene->mNumMeshes; ++ m)
+    {
+        aiMesh* pMesh = mScene->mMeshes[m];
+        for(unsigned int b = 0; b < pMesh->mNumBones; ++ b)
+            setAllBoneNamesInScene.insert(pMesh->mBones[b]->mName.data);
+    }
+    aiMatrix4x4 mxTransIdentity;
+    
     // and a map of nodes by bone name, as finding them is annoying.
     std::map<std::string,aiNode*> node_by_bone;
     for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
@@ -1660,6 +1671,11 @@ void FBXExporter::WriteObjects ()
                 if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) {
                     continue;
                 }
+                //not a bone in scene && no effect in transform
+                if(setAllBoneNamesInScene.find(node_name)==setAllBoneNamesInScene.end()
+                   && parent->mTransformation == mxTransIdentity) {
+                        continue;
+                }
                 // otherwise check if this is the root of the skeleton
                 bool end = false;
                 // is the mesh part of this node?
@@ -1824,7 +1840,10 @@ void FBXExporter::WriteObjects ()
 
             // this should be the same as the bone's mOffsetMatrix.
             // if it's not the same, the skeleton isn't in the bind pose.
-            const float epsilon = 1e-4f; // some error is to be expected
+            float epsilon = 1e-4f; // some error is to be expected
+            float epsilon_custom = mProperties->GetPropertyFloat("BINDPOSE_EPSILON", -1);
+            if(epsilon_custom > 0)
+                epsilon = epsilon_custom;
             bool bone_xform_okay = true;
             if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) {
                 not_in_bind_pose.insert(b);

+ 0 - 4
code/FBX/FBXParser.cpp

@@ -963,7 +963,6 @@ void ParseVectorDataArray(std::vector<float>& out, const Element& el)
     }
 }
 
-
 // ------------------------------------------------------------------------------------------------
 // read an array of uints
 void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el)
@@ -1280,7 +1279,6 @@ float ParseTokenAsFloat(const Token& t)
     return i;
 }
 
-
 // ------------------------------------------------------------------------------------------------
 // wrapper around ParseTokenAsInt() with ParseError handling
 int ParseTokenAsInt(const Token& t)
@@ -1293,8 +1291,6 @@ int ParseTokenAsInt(const Token& t)
     return i;
 }
 
-
-
 // ------------------------------------------------------------------------------------------------
 // wrapper around ParseTokenAsInt64() with ParseError handling
 int64_t ParseTokenAsInt64(const Token& t)

+ 1 - 1
code/Obj/ObjFileParser.cpp

@@ -118,7 +118,7 @@ void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) {
     size_t lastFilePos( 0 );
 
     std::vector<char> buffer;
-    while ( streamBuffer.getNextDataLine( buffer, '\0' ) ) {
+    while ( streamBuffer.getNextDataLine( buffer, '\\' ) ) {
         m_DataIt = buffer.begin();
         m_DataItEnd = buffer.end();
 

+ 2 - 1
code/Unreal/UnrealLoader.h

@@ -127,7 +127,8 @@ inline void CompressVertex(const aiVector3D& v, uint32_t& out)
     n.X = (int32_t)v.x;
     n.Y = (int32_t)v.y;
     n.Z = (int32_t)v.z;
-    out = t;
+    ::memcpy( &out, &t, sizeof(int32_t));
+    //out = t;
 }
 
     // UNREAL vertex decompression

+ 1 - 3
include/assimp/camera.h

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -60,7 +58,7 @@ extern "C" {
  *
  * Cameras have a representation in the node graph and can be animated.
  * An important aspect is that the camera itself is also part of the
- * scenegraph. This means, any values such as the look-at vector are not
+ * scene-graph. This means, any values such as the look-at vector are not
  * *absolute*, they're <b>relative</b> to the coordinate system defined
  * by the node which corresponds to the camera. This allows for camera
  * animations. For static cameras parameters like the 'look-at' or 'up' vectors

+ 6 - 2
include/assimp/config.h.in

@@ -981,8 +981,12 @@ enum aiComponent
 
 #define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT"
 
-/**
- *
+/** @brief Specifies whether the assimp export shall be able to export point clouds
+ * 
+ *  When this flag is not defined the render data has to contain valid faces.
+ *  Point clouds are only a collection of vertices which have nor spatial organization
+ *  by a face and the validation process will remove them. Enabling this feature will
+ *  switch off the flag and enable the functionality to export pure point clouds.
  */
 #define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS"
 

+ 6 - 5
test/CMakeLists.txt

@@ -120,6 +120,7 @@ SET( IMPORTERS
   unit/utB3DImportExport.cpp
   unit/utMDCImportExport.cpp
   unit/utAssbinImportExport.cpp
+  unit/ImportExport/utAssjsonImportExport.cpp
   unit/ImportExport/utCOBImportExport.cpp
   unit/ImportExport/utOgreImportExport.cpp
   unit/ImportExport/utQ3BSPFileImportExport.cpp
@@ -165,11 +166,11 @@ SET( POST_PROCESSES
 )
 
 SOURCE_GROUP( UnitTests\\Compiler     FILES  unit/CCompilerTest.c )
-SOURCE_GROUP( UnitTests\\Common      FILES  ${COMMON} )
-SOURCE_GROUP( UnitTests\\Importers   FILES  ${IMPORTERS} )
-SOURCE_GROUP( UnitTests\\Material    FILES  ${MATERIAL} )
-SOURCE_GROUP( UnitTests\\Math        FILES  ${MATH} )
-SOURCE_GROUP( UnitTests\\PostProcess FILES  ${POST_PROCESSES})
+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})
 
 add_executable( unit
     ../contrib/gtest/src/gtest-all.cc

BIN
test/models/JT/conrod.jt


+ 1 - 1
test/models/PLY/cube_test.ply

@@ -1,6 +1,6 @@
 ply
 format ascii 1.0
-comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.649942190)
+comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.993695325)
 element vertex 8
 property float x
 property float y

+ 22 - 2
test/unit/AbstractImportExportBase.h

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2016, assimp team
+Copyright (c) 2006-2019, assimp team
 
 All rights reserved.
 
@@ -39,17 +39,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 #pragma once
+#ifndef AI_ABSTRACTIMPORTEXPORTBASE_H_INC
+#define AI_ABSTRACTIMPORTEXPORTBASE_H_INC
 
 #include "UnitTestPCH.h"
 
+// ---------------------------------------------------------------------------
+/** Abstract base class to test import and export
+ */
+ // ---------------------------------------------------------------------------
 class AbstractImportExportBase : public ::testing::Test {
 public:
+    /// @brief  The class destructor.
     virtual ~AbstractImportExportBase();
-    virtual bool importerTest() = 0;
+
+    /// @brief  The importer-test, will return true for successful import.
+    /// @return true for success, false for failure.
+    virtual bool importerTest();
+
+    /// @brief  The exporter-test, will return true for successful import.
+    /// @return true for success, false for failure.
     virtual bool exporterTest();
 };
 
+inline
+bool AbstractImportExportBase::importerTest() {
+    return true;
+}
+
 inline 
 bool AbstractImportExportBase::exporterTest() {
     return true;
 }
+
+#endif // AI_ABSTRACTIMPORTEXPORTBASE_H_INC

+ 69 - 0
test/unit/ImportExport/utAssjsonImportExport.cpp

@@ -0,0 +1,69 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 "AbstractImportExportBase.h"
+
+#include <assimp/Importer.hpp>
+#include <assimp/Exporter.hpp>
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+
+using namespace Assimp;
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+
+class utAssjsonImportExport : public AbstractImportExportBase {
+public:
+    bool exporterTest() override {
+        Importer importer;
+        const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure);
+
+        Exporter exporter;
+        aiReturn res = exporter.Export(scene, "json", "./spider_test.json");
+        return aiReturn_SUCCESS == res;
+    }
+};
+
+TEST_F(utAssjsonImportExport, exportTest) {
+    EXPECT_TRUE(exporterTest());
+}
+
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 0 - 2
test/unit/utAMFImportExport.cpp

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

+ 0 - 2
test/unit/utASEImportExport.cpp

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

+ 3 - 5
test/unit/utAssbinImportExport.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -53,8 +51,8 @@ using namespace Assimp;
 
 class utAssbinImportExport : public AbstractImportExportBase {
 public:
-    virtual bool importerTest() {
-        Assimp::Importer importer;
+    bool importerTest() override {
+        Importer importer;
         const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure );
 
         Exporter exporter;
@@ -66,7 +64,7 @@ public:
 };
 
 TEST_F( utAssbinImportExport, exportAssbin3DFromFileTest ) {
-    Assimp::Importer importer;
+    Importer importer;
     const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure );
     EXPECT_NE( nullptr, scene );
 }

+ 0 - 2
test/unit/utB3DImportExport.cpp

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

+ 2 - 9
test/unit/utBlenderWork.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -73,11 +71,6 @@ TEST_F(BlenderWorkTest,work_279) {
     ASSERT_TRUE(pTest->HasMaterials());
     ASSERT_TRUE(pTest->HasMeshes());
     ASSERT_TRUE(pTest->mMeshes[0]->mNumVertices > 0);
-    ASSERT_EQ(44, pTest->mMeshes[0]->mNumFaces);
-    EXPECT_EQ(1, pTest->mNumMaterials);
+    ASSERT_EQ(44u, pTest->mMeshes[0]->mNumFaces);
+    EXPECT_EQ(1u, pTest->mNumMaterials);
 }
-
-
-
-
-

+ 25 - 24
test/unit/utFBXImporterExporter.cpp

@@ -70,10 +70,10 @@ TEST_F( utFBXImporterExporter, importBareBoxWithoutColorsAndTextureCoords ) {
     Assimp::Importer importer;
     const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/box.fbx", aiProcess_ValidateDataStructure );
     EXPECT_NE( nullptr, scene );
-    EXPECT_EQ(scene->mNumMeshes, 1);
+    EXPECT_EQ(scene->mNumMeshes, 1u);
     aiMesh* mesh = scene->mMeshes[0];
-    EXPECT_EQ(mesh->mNumFaces, 12);
-    EXPECT_EQ(mesh->mNumVertices, 36);
+    EXPECT_EQ(mesh->mNumFaces, 12u);
+    EXPECT_EQ(mesh->mNumVertices, 36u);
 }
 
 TEST_F(utFBXImporterExporter, importCubesWithNoNames) {
@@ -85,13 +85,13 @@ TEST_F(utFBXImporterExporter, importCubesWithNoNames) {
     const auto root = scene->mRootNode;
     ASSERT_STREQ(root->mName.C_Str(), "RootNode");
     ASSERT_TRUE(root->mChildren);
-    ASSERT_EQ(root->mNumChildren, 2);
+    ASSERT_EQ(root->mNumChildren, 2u);
 
     const auto child0 = root->mChildren[0];
     ASSERT_TRUE(child0);
     ASSERT_STREQ(child0->mName.C_Str(), "RootNode001");
     ASSERT_TRUE(child0->mChildren);
-    ASSERT_EQ(child0->mNumChildren, 1);
+    ASSERT_EQ(child0->mNumChildren, 1u);
 
     const auto child00 = child0->mChildren[0];
     ASSERT_TRUE(child00);
@@ -101,7 +101,7 @@ TEST_F(utFBXImporterExporter, importCubesWithNoNames) {
     ASSERT_TRUE(child1);
     ASSERT_STREQ(child1->mName.C_Str(), "RootNode002");
     ASSERT_TRUE(child1->mChildren);
-    ASSERT_EQ(child1->mNumChildren, 1);
+    ASSERT_EQ(child1->mNumChildren, 1u);
 
     const auto child10 = child1->mChildren[0];
     ASSERT_TRUE(child10);
@@ -117,13 +117,13 @@ TEST_F(utFBXImporterExporter, importCubesWithUnicodeDuplicatedNames) {
     const auto root = scene->mRootNode;
     ASSERT_STREQ(root->mName.C_Str(), "RootNode");
     ASSERT_TRUE(root->mChildren);
-    ASSERT_EQ(root->mNumChildren, 2);
+    ASSERT_EQ(root->mNumChildren, 2u);
 
     const auto child0 = root->mChildren[0];
     ASSERT_TRUE(child0);
     ASSERT_STREQ(child0->mName.C_Str(), "Cube2");
     ASSERT_TRUE(child0->mChildren);
-    ASSERT_EQ(child0->mNumChildren, 1);
+    ASSERT_EQ(child0->mNumChildren, 1u);
 
     const auto child00 = child0->mChildren[0];
     ASSERT_TRUE(child00);
@@ -133,7 +133,7 @@ TEST_F(utFBXImporterExporter, importCubesWithUnicodeDuplicatedNames) {
     ASSERT_TRUE(child1);
     ASSERT_STREQ(child1->mName.C_Str(), "Cube3");
     ASSERT_TRUE(child1->mChildren);
-    ASSERT_EQ(child1->mNumChildren, 1);
+    ASSERT_EQ(child1->mNumChildren, 1u);
 
     const auto child10 = child1->mChildren[0];
     ASSERT_TRUE(child10);
@@ -149,13 +149,13 @@ TEST_F(utFBXImporterExporter, importCubesComplexTransform) {
     const auto root = scene->mRootNode;
     ASSERT_STREQ(root->mName.C_Str(), "RootNode");
     ASSERT_TRUE(root->mChildren);
-    ASSERT_EQ(root->mNumChildren, 2);
+    ASSERT_EQ(root->mNumChildren, 2u);
 
     const auto child0 = root->mChildren[0];
     ASSERT_TRUE(child0);
     ASSERT_STREQ(child0->mName.C_Str(), "Cube2");
     ASSERT_TRUE(child0->mChildren);
-    ASSERT_EQ(child0->mNumChildren, 1);
+    ASSERT_EQ(child0->mNumChildren, 1u);
 
     const auto child00 = child0->mChildren[0];
     ASSERT_TRUE(child00);
@@ -177,35 +177,36 @@ TEST_F(utFBXImporterExporter, importCubesComplexTransform) {
         "Cube1001_$AssimpFbx$_ScalingPivotInverse",
         "Cube1001"
     };
-    for (size_t i = 0; i < chain_length; ++i)
-    {
+    for (size_t i = 0; i < chain_length; ++i) {
         ASSERT_TRUE(parent->mChildren);
-        ASSERT_EQ(parent->mNumChildren, 1);
+        ASSERT_EQ(parent->mNumChildren, 1u);
         auto node = parent->mChildren[0];
         ASSERT_TRUE(node);
         ASSERT_STREQ(node->mName.C_Str(), chainStr[i]);
         parent = node;
     }
-    ASSERT_EQ(0, parent->mNumChildren) << "Leaf node";
+    ASSERT_EQ(0u, parent->mNumChildren) << "Leaf node";
 }
 
 TEST_F( utFBXImporterExporter, importPhongMaterial ) {
     Assimp::Importer importer;
     const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/phong_cube.fbx", aiProcess_ValidateDataStructure );
     EXPECT_NE( nullptr, scene );
-    EXPECT_EQ( (unsigned int)1, scene->mNumMaterials );
+    EXPECT_EQ( 1u, scene->mNumMaterials );
     const aiMaterial *mat = scene->mMaterials[0];
     EXPECT_NE( nullptr, mat );
-    float f; aiColor3D c;
+    float f;
+    aiColor3D c;
+
     // phong_cube.fbx has all properties defined
     EXPECT_EQ( mat->Get(AI_MATKEY_COLOR_DIFFUSE, c), aiReturn_SUCCESS );
     EXPECT_EQ( c, aiColor3D(0.5, 0.25, 0.25) );
     EXPECT_EQ( mat->Get(AI_MATKEY_COLOR_SPECULAR, c), aiReturn_SUCCESS );
     EXPECT_EQ( c, aiColor3D(0.25, 0.25, 0.5) );
     EXPECT_EQ( mat->Get(AI_MATKEY_SHININESS_STRENGTH, f), aiReturn_SUCCESS );
-    EXPECT_EQ( f, 0.5 );
+    EXPECT_EQ( f, 0.5f );
     EXPECT_EQ( mat->Get(AI_MATKEY_SHININESS, f), aiReturn_SUCCESS );
-    EXPECT_EQ( f, 10.0 );
+    EXPECT_EQ( f, 10.0f );
     EXPECT_EQ( mat->Get(AI_MATKEY_COLOR_AMBIENT, c), aiReturn_SUCCESS );
     EXPECT_EQ( c, aiColor3D(0.125, 0.25, 0.25) );
     EXPECT_EQ( mat->Get(AI_MATKEY_COLOR_EMISSIVE, c), aiReturn_SUCCESS );
@@ -213,7 +214,7 @@ TEST_F( utFBXImporterExporter, importPhongMaterial ) {
     EXPECT_EQ( mat->Get(AI_MATKEY_COLOR_TRANSPARENT, c), aiReturn_SUCCESS );
     EXPECT_EQ( c, aiColor3D(0.75, 0.5, 0.25) );
     EXPECT_EQ( mat->Get(AI_MATKEY_OPACITY, f), aiReturn_SUCCESS );
-    EXPECT_EQ( f, 0.5 );
+    EXPECT_EQ( f, 0.5f );
 }
 
 TEST_F(utFBXImporterExporter, importUnitScaleFactor) {
@@ -234,7 +235,7 @@ TEST_F(utFBXImporterExporter, importEmbeddedAsciiTest) {
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/embedded_ascii/box.FBX", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
 
-    EXPECT_EQ(1, scene->mNumMaterials);
+    EXPECT_EQ(1u, scene->mNumMaterials);
     aiMaterial *mat = scene->mMaterials[0];
     ASSERT_NE(nullptr, mat);
 
@@ -243,7 +244,7 @@ TEST_F(utFBXImporterExporter, importEmbeddedAsciiTest) {
     EXPECT_EQ(aiReturn_SUCCESS, mat->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, nullptr, nullptr, modes));
     ASSERT_STREQ(path.C_Str(), "..\\..\\..\\Desktop\\uv_test.png");
 
-    ASSERT_EQ(1, scene->mNumTextures);
+    ASSERT_EQ(1u, scene->mNumTextures);
     ASSERT_TRUE(scene->mTextures[0]->pcData);
     ASSERT_EQ(439176u, scene->mTextures[0]->mWidth) << "FBX ASCII base64 compression splits data by 512Kb, it should be two parts for this texture";
 }
@@ -254,7 +255,7 @@ TEST_F(utFBXImporterExporter, importEmbeddedFragmentedAsciiTest) {
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/embedded_ascii/box_embedded_texture_fragmented.fbx", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
 
-    EXPECT_EQ(1, scene->mNumMaterials);
+    EXPECT_EQ(1u, scene->mNumMaterials);
     aiMaterial *mat = scene->mMaterials[0];
     ASSERT_NE(nullptr, mat);
 
@@ -263,7 +264,7 @@ TEST_F(utFBXImporterExporter, importEmbeddedFragmentedAsciiTest) {
     ASSERT_EQ(aiReturn_SUCCESS, mat->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, nullptr, nullptr, modes));
     ASSERT_STREQ(path.C_Str(), "paper.png");
 
-    ASSERT_EQ(1, scene->mNumTextures);
+    ASSERT_EQ(1u, scene->mNumTextures);
     ASSERT_TRUE(scene->mTextures[0]->pcData);
     ASSERT_EQ(968029u, scene->mTextures[0]->mWidth) << "FBX ASCII base64 compression splits data by 512Kb, it should be two parts for this texture";
 }

+ 38 - 33
test/unit/utFindDegenerates.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -49,35 +47,42 @@ using namespace Assimp;
 
 class FindDegeneratesProcessTest : public ::testing::Test {
 public:
+    FindDegeneratesProcessTest()
+    : Test()
+    , mMesh( nullptr )
+    , mProcess( nullptr ) {
+        // empty
+    }
+
+protected:
     virtual void SetUp();
     virtual void TearDown();
 
 protected:
-    aiMesh* mesh;
-    FindDegeneratesProcess* process;
+    aiMesh* mMesh;
+    FindDegeneratesProcess* mProcess;
 };
 
-// ------------------------------------------------------------------------------------------------
 void FindDegeneratesProcessTest::SetUp() {
-    mesh = new aiMesh();
-    process = new FindDegeneratesProcess();
+    mMesh = new aiMesh();
+    mProcess = new FindDegeneratesProcess();
 
-    mesh->mNumFaces = 1000;
-    mesh->mFaces = new aiFace[1000];
+    mMesh->mNumFaces = 1000;
+    mMesh->mFaces = new aiFace[1000];
 
-    mesh->mNumVertices = 5000*2;
-    mesh->mVertices = new aiVector3D[5000*2];
+    mMesh->mNumVertices = 5000*2;
+    mMesh->mVertices = new aiVector3D[5000*2];
 
     for (unsigned int i = 0; i < 5000; ++i) {
-        mesh->mVertices[i] = mesh->mVertices[i+5000] = aiVector3D((float)i);
+        mMesh->mVertices[i] = mMesh->mVertices[i+5000] = aiVector3D((float)i);
     }
 
-    mesh->mPrimitiveTypes = aiPrimitiveType_LINE | aiPrimitiveType_POINT |
+    mMesh->mPrimitiveTypes = aiPrimitiveType_LINE | aiPrimitiveType_POINT |
     aiPrimitiveType_POLYGON | aiPrimitiveType_TRIANGLE;
 
     unsigned int numOut = 0, numFaces = 0;
     for (unsigned int i = 0; i < 1000; ++i) {
-        aiFace& f = mesh->mFaces[i];
+        aiFace& f = mMesh->mFaces[i];
     f.mNumIndices = (i % 5)+1; // between 1 and 5
     f.mIndices = new unsigned int[f.mNumIndices];
     bool had = false;
@@ -102,46 +107,46 @@ void FindDegeneratesProcessTest::SetUp() {
         if (!had)
             ++numFaces;
     }
-    mesh->mNumUVComponents[0] = numOut;
-    mesh->mNumUVComponents[1] = numFaces;
+    mMesh->mNumUVComponents[0] = numOut;
+    mMesh->mNumUVComponents[1] = numFaces;
 }
 
 void FindDegeneratesProcessTest::TearDown() {
-    delete mesh;
-    delete process;
+    delete mMesh;
+    delete mProcess;
 }
 
 TEST_F(FindDegeneratesProcessTest, testDegeneratesDetection) {
-    process->EnableInstantRemoval(false);
-    process->ExecuteOnMesh(mesh);
+    mProcess->EnableInstantRemoval(false);
+    mProcess->ExecuteOnMesh(mMesh);
 
     unsigned int out = 0;
     for (unsigned int i = 0; i < 1000; ++i) {
-        aiFace& f = mesh->mFaces[i];
+        aiFace& f = mMesh->mFaces[i];
         out += f.mNumIndices;
     }
 
-    EXPECT_EQ(1000U, mesh->mNumFaces);
-    EXPECT_EQ(10000U, mesh->mNumVertices);
-    EXPECT_EQ(out, mesh->mNumUVComponents[0]);
+    EXPECT_EQ(1000U, mMesh->mNumFaces);
+    EXPECT_EQ(10000U, mMesh->mNumVertices);
+    EXPECT_EQ(out, mMesh->mNumUVComponents[0]);
     EXPECT_EQ(static_cast<unsigned int>(
                   aiPrimitiveType_LINE | aiPrimitiveType_POINT |
                   aiPrimitiveType_POLYGON | aiPrimitiveType_TRIANGLE),
-              mesh->mPrimitiveTypes);
+              mMesh->mPrimitiveTypes);
 }
 
 TEST_F(FindDegeneratesProcessTest, testDegeneratesRemoval) {
-    process->EnableAreaCheck(false);
-    process->EnableInstantRemoval(true);
-    process->ExecuteOnMesh(mesh);
+    mProcess->EnableAreaCheck(false);
+    mProcess->EnableInstantRemoval(true);
+    mProcess->ExecuteOnMesh(mMesh);
 
-    EXPECT_EQ(mesh->mNumUVComponents[1], mesh->mNumFaces);
+    EXPECT_EQ(mMesh->mNumUVComponents[1], mMesh->mNumFaces);
 }
 
 TEST_F(FindDegeneratesProcessTest, testDegeneratesRemovalWithAreaCheck) {
-    process->EnableAreaCheck(true);
-    process->EnableInstantRemoval(true);
-    process->ExecuteOnMesh(mesh);
+    mProcess->EnableAreaCheck(true);
+    mProcess->EnableInstantRemoval(true);
+    mProcess->ExecuteOnMesh(mMesh);
 
-    EXPECT_EQ(mesh->mNumUVComponents[1]-100, mesh->mNumFaces);
+    EXPECT_EQ(mMesh->mNumUVComponents[1]-100, mMesh->mNumFaces);
 }

+ 63 - 54
test/unit/utFindInvalidData.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -48,89 +46,100 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace std;
 using namespace Assimp;
 
-class FindInvalidDataProcessTest : public ::testing::Test
-{
+class utFindInvalidDataProcess : public ::testing::Test {
 public:
+    utFindInvalidDataProcess()
+    : Test()
+    , mMesh(nullptr)
+    , mProcess(nullptr) {
+        // empty
+    }
+
+protected:
     virtual void SetUp();
     virtual void TearDown();
 
 protected:
-    aiMesh* pcMesh;
-    FindInvalidDataProcess* piProcess;
+    aiMesh* mMesh;
+    FindInvalidDataProcess* mProcess;
 };
 
 // ------------------------------------------------------------------------------------------------
-void FindInvalidDataProcessTest::SetUp() {
+void utFindInvalidDataProcess::SetUp() {
     ASSERT_TRUE( AI_MAX_NUMBER_OF_TEXTURECOORDS >= 3);
 
-    piProcess = new FindInvalidDataProcess();
-    pcMesh = new aiMesh();
+    mProcess = new FindInvalidDataProcess();
+    mMesh = new aiMesh();
 
-    pcMesh->mNumVertices = 1000;
-    pcMesh->mVertices = new aiVector3D[1000];
-    for (unsigned int i = 0; i < 1000;++i)
-        pcMesh->mVertices[i] = aiVector3D((float)i);
+    mMesh->mNumVertices = 1000;
+    mMesh->mVertices = new aiVector3D[1000];
+    for (unsigned int i = 0; i < 1000; ++i) {
+        mMesh->mVertices[i] = aiVector3D((float)i);
+    }
 
-    pcMesh->mNormals = new aiVector3D[1000];
-    for (unsigned int i = 0; i < 1000;++i)
-        pcMesh->mNormals[i] = aiVector3D((float)i+1);
+    mMesh->mNormals = new aiVector3D[1000];
+    for (unsigned int i = 0; i < 1000; ++i) {
+        mMesh->mNormals[i] = aiVector3D((float)i + 1);
+    }
 
-    pcMesh->mTangents = new aiVector3D[1000];
-    for (unsigned int i = 0; i < 1000;++i)
-        pcMesh->mTangents[i] = aiVector3D((float)i);
+    mMesh->mTangents = new aiVector3D[1000];
+    for (unsigned int i = 0; i < 1000; ++i) {
+        mMesh->mTangents[i] = aiVector3D((float)i);
+    }
 
-    pcMesh->mBitangents = new aiVector3D[1000];
-    for (unsigned int i = 0; i < 1000;++i)
-        pcMesh->mBitangents[i] = aiVector3D((float)i);
+    mMesh->mBitangents = new aiVector3D[1000];
+    for (unsigned int i = 0; i < 1000; ++i) {
+        mMesh->mBitangents[i] = aiVector3D((float)i);
+    }
 
-    for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a)
-    {
-        pcMesh->mTextureCoords[a] = new aiVector3D[1000];
-        for (unsigned int i = 0; i < 1000;++i)
-            pcMesh->mTextureCoords[a][i] = aiVector3D((float)i);
+    for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) {
+        mMesh->mTextureCoords[a] = new aiVector3D[1000];
+        for (unsigned int i = 0; i < 1000; ++i) {
+            mMesh->mTextureCoords[a][i] = aiVector3D((float)i);
+        }
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void FindInvalidDataProcessTest::TearDown()
-{
-    delete piProcess;
-    delete pcMesh;
+void utFindInvalidDataProcess::TearDown() {
+    delete mProcess;
+    delete mMesh;
 }
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(FindInvalidDataProcessTest, testStepNegativeResult)
-{
-    ::memset(pcMesh->mNormals,0,pcMesh->mNumVertices*sizeof(aiVector3D));
-    ::memset(pcMesh->mBitangents,0,pcMesh->mNumVertices*sizeof(aiVector3D));
+TEST_F(utFindInvalidDataProcess, testStepNegativeResult) {
+    ::memset(mMesh->mNormals, 0, mMesh->mNumVertices*sizeof(aiVector3D) );
+    ::memset(mMesh->mBitangents, 0, mMesh->mNumVertices*sizeof(aiVector3D) );
 
-    pcMesh->mTextureCoords[2][455] = aiVector3D( std::numeric_limits<float>::quiet_NaN() );
+    mMesh->mTextureCoords[2][455] = aiVector3D( std::numeric_limits<float>::quiet_NaN() );
 
-    piProcess->ProcessMesh(pcMesh);
+    mProcess->ProcessMesh(mMesh);
 
-    EXPECT_TRUE(NULL != pcMesh->mVertices);
-    EXPECT_TRUE(NULL == pcMesh->mNormals);
-    EXPECT_TRUE(NULL == pcMesh->mTangents);
-    EXPECT_TRUE(NULL == pcMesh->mBitangents);
+    EXPECT_TRUE(NULL != mMesh->mVertices);
+    EXPECT_EQ(NULL, mMesh->mNormals);
+    EXPECT_EQ(NULL, mMesh->mTangents);
+    EXPECT_EQ(NULL, mMesh->mBitangents);
 
-    for (unsigned int i = 0; i < 2;++i)
-        EXPECT_TRUE(NULL != pcMesh->mTextureCoords[i]);
+    for (unsigned int i = 0; i < 2; ++i) {
+        EXPECT_TRUE(NULL != mMesh->mTextureCoords[i]);
+    }
 
-    for (unsigned int i = 2; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
-        EXPECT_TRUE(NULL == pcMesh->mTextureCoords[i]);
+    for (unsigned int i = 2; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+        EXPECT_EQ(NULL, mMesh->mTextureCoords[i]);
+    }
 }
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(FindInvalidDataProcessTest, testStepPositiveResult)
-{
-    piProcess->ProcessMesh(pcMesh);
+TEST_F(utFindInvalidDataProcess, testStepPositiveResult) {
+    mProcess->ProcessMesh(mMesh);
 
-    EXPECT_TRUE(NULL != pcMesh->mVertices);
+    EXPECT_NE(nullptr, mMesh->mVertices);
 
-    EXPECT_TRUE(NULL != pcMesh->mNormals);
-    EXPECT_TRUE(NULL != pcMesh->mTangents);
-    EXPECT_TRUE(NULL != pcMesh->mBitangents);
+    EXPECT_NE(nullptr, mMesh->mNormals);
+    EXPECT_NE(nullptr, mMesh->mTangents);
+    EXPECT_NE(nullptr, mMesh->mBitangents);
 
-    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
-        EXPECT_TRUE(NULL != pcMesh->mTextureCoords[i]);
+    for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+        EXPECT_NE(nullptr, mMesh->mTextureCoords[i]);
+    }
 }

+ 9 - 1
test/unit/utGenBoundingBoxesProcess.cpp

@@ -48,13 +48,21 @@ using namespace Assimp;
 
 class utGenBoundingBoxesProcess : public ::testing::Test {
 public:
+    utGenBoundingBoxesProcess()
+    : Test()
+    , mProcess(nullptr)
+    , mMesh(nullptr)
+    , mScene(nullptr) {
+        // empty
+    }
+
     void SetUp() override {
         mProcess = new GenBoundingBoxesProcess;
         mMesh = new aiMesh();
         mMesh->mNumVertices = 100;
         mMesh->mVertices = new aiVector3D[100];
         for (unsigned int i = 0; i < 100; ++i) {
-            mMesh->mVertices[i] = aiVector3D(i, i, i);
+            mMesh->mVertices[i] = aiVector3D((ai_real)i, (ai_real)i, (ai_real)i);
         }
         mScene = new aiScene();
         mScene->mNumMeshes = 1;

+ 1 - 1
test/unit/utIssues.cpp

@@ -74,7 +74,7 @@ TEST_F( utIssues, OpacityBugWhenExporting_727 ) {
     if ( newScene->mNumMaterials > 0 ) {
         std::cout << "Desc = " << desc->description << "\n";
         EXPECT_EQ( AI_SUCCESS, newScene->mMaterials[ 0 ]->Get( AI_MATKEY_OPACITY, newOpacity ) );
-        EXPECT_EQ( opacity, newOpacity );
+        EXPECT_FLOAT_EQ( opacity, newOpacity );
     }
     delete scene;
 }

+ 31 - 31
test/unit/utJoinVertices.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -49,8 +47,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace std;
 using namespace Assimp;
 
-class JoinVerticesTest : public ::testing::Test {
+class utJoinVertices : public ::testing::Test {
 public:
+    utJoinVertices()
+    : Test()
+    , piProcess(nullptr)
+    , pcMesh(nullptr) {
+        // empty
+    }
+
+protected:
     virtual void SetUp();
     virtual void TearDown();
 
@@ -60,8 +66,7 @@ protected:
 };
 
 // ------------------------------------------------------------------------------------------------
-void JoinVerticesTest::SetUp()
-{
+void utJoinVertices::SetUp() {
     // construct the process
     piProcess = new JoinVerticesProcess();
 
@@ -71,11 +76,9 @@ void JoinVerticesTest::SetUp()
 
     pcMesh->mNumVertices = 900;
     aiVector3D*& pv = pcMesh->mVertices = new aiVector3D[900];
-    for (unsigned int i = 0; i < 3;++i)
-    {
+    for (unsigned int i = 0; i < 3;++i) {
         const unsigned int base = i*300;
-        for (unsigned int a = 0; a < 300;++a)
-        {
+        for (unsigned int a = 0; a < 300;++a) {
             pv[base+a].x = pv[base+a].y = pv[base+a].z = (float)a;
         }
     }
@@ -83,38 +86,37 @@ void JoinVerticesTest::SetUp()
     // generate faces - each vertex is referenced once
     pcMesh->mNumFaces = 300;
     pcMesh->mFaces = new aiFace[300];
-    for (unsigned int i = 0,p = 0; i < 300;++i)
-    {
+    for (unsigned int i = 0,p = 0; i < 300;++i) {
         aiFace& face = pcMesh->mFaces[i];
         face.mIndices = new unsigned int[ face.mNumIndices = 3 ];
-        for (unsigned int a = 0; a < 3;++a)
+        for (unsigned int a = 0; a < 3; ++a) {
             face.mIndices[a] = p++;
+        }
     }
 
     // generate extra members - set them to zero to make sure they're identical
     pcMesh->mTextureCoords[0] = new aiVector3D[900];
-    for (unsigned int i = 0; i < 900;++i)pcMesh->mTextureCoords[0][i] = aiVector3D( 0.f );
-
+    pcMesh->mBitangents = new aiVector3D[900];
     pcMesh->mNormals = new aiVector3D[900];
-    for (unsigned int i = 0; i < 900;++i)pcMesh->mNormals[i] = aiVector3D( 0.f );
-
     pcMesh->mTangents = new aiVector3D[900];
-    for (unsigned int i = 0; i < 900;++i)pcMesh->mTangents[i] = aiVector3D( 0.f );
-
-    pcMesh->mBitangents = new aiVector3D[900];
-    for (unsigned int i = 0; i < 900;++i)pcMesh->mBitangents[i] = aiVector3D( 0.f );
+    for (unsigned int i = 0; i < 900; ++i) {
+        pcMesh->mTextureCoords[0][i] = aiVector3D(0.f);
+        pcMesh->mNormals[i] = aiVector3D(0.f);
+        pcMesh->mTangents[i] = aiVector3D(0.f);
+        pcMesh->mBitangents[i] = aiVector3D(0.f);
+    }
 }
 
 // ------------------------------------------------------------------------------------------------
-void JoinVerticesTest::TearDown()
-{
+void utJoinVertices::TearDown() {
     delete this->pcMesh;
+    pcMesh = nullptr;
     delete this->piProcess;
+    piProcess = nullptr;
 }
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(JoinVerticesTest, testProcess)
-{
+TEST_F(utJoinVertices, testProcess) {
     // execute the step on the given data
     piProcess->ProcessMesh(pcMesh,0);
 
@@ -122,15 +124,14 @@ TEST_F(JoinVerticesTest, testProcess)
     ASSERT_EQ(300U, pcMesh->mNumFaces);
     ASSERT_EQ(300U, pcMesh->mNumVertices);
 
-    ASSERT_TRUE(NULL != pcMesh->mNormals);
-    ASSERT_TRUE(NULL != pcMesh->mTangents);
-    ASSERT_TRUE(NULL != pcMesh->mBitangents);
-    ASSERT_TRUE(NULL != pcMesh->mTextureCoords[0]);
+    ASSERT_TRUE( nullptr != pcMesh->mNormals);
+    ASSERT_TRUE( nullptr != pcMesh->mTangents);
+    ASSERT_TRUE( nullptr != pcMesh->mBitangents);
+    ASSERT_TRUE( nullptr != pcMesh->mTextureCoords[0]);
 
     // the order doesn't care
     float fSum = 0.f;
-    for (unsigned int i = 0; i < 300;++i)
-    {
+    for (unsigned int i = 0; i < 300; ++i) {
         aiVector3D& v = pcMesh->mVertices[i];
         fSum += v.x + v.y + v.z;
 
@@ -141,4 +142,3 @@ TEST_F(JoinVerticesTest, testProcess)
     }
     EXPECT_EQ(150.f*299.f*3.f, fSum); // gaussian sum equation
 }
-

+ 36 - 38
test/unit/utLimitBoneWeights.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -50,83 +48,83 @@ using namespace Assimp;
 
 class LimitBoneWeightsTest : public ::testing::Test {
 public:
+    LimitBoneWeightsTest()
+    : Test()
+    , mProcess(nullptr)
+    , mMesh(nullptr) {
+        // empty
+    }
+
+protected:
     virtual void SetUp();
     virtual void TearDown();
 
 protected:
-    LimitBoneWeightsProcess* piProcess;
-    aiMesh* pcMesh;
+    LimitBoneWeightsProcess *mProcess;
+    aiMesh *mMesh;
 };
 
 // ------------------------------------------------------------------------------------------------
-void LimitBoneWeightsTest::SetUp()
-{
+void LimitBoneWeightsTest::SetUp() {
     // construct the process
-    this->piProcess = new LimitBoneWeightsProcess();
+    this->mProcess = new LimitBoneWeightsProcess();
 
     // now need to create a nice mesh for testing purposes
-    this->pcMesh = new aiMesh();
+    this->mMesh = new aiMesh();
 
-    pcMesh->mNumVertices = 500;
-    pcMesh->mVertices = new aiVector3D[500]; // uninit.
-    pcMesh->mNumBones = 30;
-    pcMesh->mBones = new aiBone*[30];
+    mMesh->mNumVertices = 500;
+    mMesh->mVertices = new aiVector3D[500]; // uninit.
+    mMesh->mNumBones = 30;
+    mMesh->mBones = new aiBone*[30];
     unsigned int iCur = 0;
-    for (unsigned int i = 0; i < 30;++i)
-    {
-        aiBone* pc = pcMesh->mBones[i] = new aiBone();
+    for (unsigned int i = 0; i < 30;++i) {
+        aiBone* pc = mMesh->mBones[i] = new aiBone();
         pc->mNumWeights = 250;
         pc->mWeights = new aiVertexWeight[pc->mNumWeights];
-        for (unsigned int qq = 0; qq < pc->mNumWeights;++qq)
-        {
+        for (unsigned int qq = 0; qq < pc->mNumWeights;++qq) {
             aiVertexWeight& v = pc->mWeights[qq];
             v.mVertexId = iCur++;
-            if (500 == iCur)iCur = 0;
+            if (500 == iCur) {
+                iCur = 0;
+            }
             v.mWeight = 1.0f / 15; // each vertex should occur once in two bones
         }
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void LimitBoneWeightsTest::TearDown()
-{
-    delete pcMesh;
-    delete piProcess;
+void LimitBoneWeightsTest::TearDown() {
+    delete mMesh;
+    delete mProcess;
 }
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(LimitBoneWeightsTest, testProcess)
-{
+TEST_F(LimitBoneWeightsTest, testProcess) {
     // execute the step on the given data
-    piProcess->ProcessMesh(pcMesh);
+    mProcess->ProcessMesh(mMesh);
 
     // check whether everything is ok ...
     typedef std::vector<LimitBoneWeightsProcess::Weight> VertexWeightList;
-    VertexWeightList* asWeights = new VertexWeightList[pcMesh->mNumVertices];
+    VertexWeightList* asWeights = new VertexWeightList[mMesh->mNumVertices];
 
-    for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
+    for (unsigned int i = 0; i < mMesh->mNumVertices; ++i) {
         asWeights[i].reserve(4);
+    }
 
     // sort back as per-vertex lists
-    for (unsigned int i = 0; i < pcMesh->mNumBones;++i)
-    {
-        aiBone& pcBone = **(pcMesh->mBones+i);
-        for (unsigned int q = 0; q < pcBone.mNumWeights;++q)
-        {
+    for (unsigned int i = 0; i < mMesh->mNumBones;++i) {
+        aiBone& pcBone = **(mMesh->mBones+i);
+        for (unsigned int q = 0; q < pcBone.mNumWeights;++q) {
             aiVertexWeight weight = pcBone.mWeights[q];
             asWeights[weight.mVertexId].push_back(LimitBoneWeightsProcess::Weight (i,weight.mWeight));
         }
     }
 
     // now validate the size of the lists and check whether all weights sum to 1.0f
-    for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
-    {
+    for (unsigned int i = 0; i < mMesh->mNumVertices;++i) {
         EXPECT_LE(asWeights[i].size(), 4U);
         float fSum = 0.0f;
-        for (VertexWeightList::const_iterator
-            iter =  asWeights[i].begin();
-            iter != asWeights[i].end();++iter)
-        {
+        for (VertexWeightList::const_iterator iter =  asWeights[i].begin(); iter != asWeights[i].end();++iter) {
             fSum += (*iter).mWeight;
         }
         EXPECT_GE(fSum, 0.95F);

+ 34 - 0
test/unit/utObjImportExport.cpp

@@ -448,3 +448,37 @@ TEST_F(utObjImportExport, import_without_linend) {
     const aiScene *scene = myImporter.ReadFile(ASSIMP_TEST_MODELS_DIR "/OBJ/box_without_lineending.obj", 0);
     ASSERT_NE(nullptr, scene);
 }
+
+TEST_F(utObjImportExport, import_with_line_continuations) {
+    static const char *ObjModel =
+        "v -0.5 -0.5 0.5\n"
+        "v -0.5 \\\n"
+        "  -0.5 -0.5\n"
+        "v -0.5 \\\n"
+        "   0.5 \\\n"
+        "   -0.5\n"
+        "f 1 2 3\n";
+
+    Assimp::Importer myImporter;
+    const aiScene *scene = myImporter.ReadFileFromMemory(ObjModel, strlen(ObjModel), 0);
+    EXPECT_NE(nullptr, scene);
+
+    EXPECT_EQ(scene->mNumMeshes, 1U);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 3U);
+    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 1U);
+
+    auto vertices = scene->mMeshes[0]->mVertices;
+    const float threshold = 0.0001f;
+
+    EXPECT_NEAR(vertices[0].x, -0.5f, threshold);
+    EXPECT_NEAR(vertices[0].y, -0.5f, threshold);
+    EXPECT_NEAR(vertices[0].z, 0.5f, threshold);
+
+    EXPECT_NEAR(vertices[1].x, -0.5f, threshold);
+    EXPECT_NEAR(vertices[1].y, -0.5f, threshold);
+    EXPECT_NEAR(vertices[1].z, -0.5f, threshold);
+
+    EXPECT_NEAR(vertices[2].x, -0.5f, threshold);
+    EXPECT_NEAR(vertices[2].y, 0.5f, threshold);
+    EXPECT_NEAR(vertices[2].z, -0.5f, threshold);
+}

+ 38 - 35
test/unit/utPretransformVertices.cpp

@@ -51,18 +51,24 @@ using namespace Assimp;
 
 class PretransformVerticesTest : public ::testing::Test {
 public:
+    PretransformVerticesTest()
+    : Test()
+    , mScene(nullptr)
+    , mProcess(nullptr) {
+        // empty
+    }
+
+protected:
     virtual void SetUp();
     virtual void TearDown();
 
 protected:
-
-    aiScene* scene;
-    PretransformVertices* process;
+    aiScene *mScene;
+    PretransformVertices *mProcess;
 };
 
 // ------------------------------------------------------------------------------------------------
-void AddNodes(unsigned int num, aiNode* father, unsigned int depth)
-{
+void AddNodes(unsigned int num, aiNode* father, unsigned int depth) {
     father->mChildren = new aiNode*[father->mNumChildren = 5];
     for (unsigned int i = 0; i < 5; ++i) {
         aiNode* nd = father->mChildren[i] = new aiNode();
@@ -79,26 +85,26 @@ void AddNodes(unsigned int num, aiNode* father, unsigned int depth)
     }
 
     if (depth > 1) {
-        for (unsigned int i = 0; i < 5; ++i)
-            AddNodes(i, father->mChildren[i],depth-1);
+        for (unsigned int i = 0; i < 5; ++i) {
+            AddNodes(i, father->mChildren[i], depth - 1);
+        }
     }
 }
 
 // ------------------------------------------------------------------------------------------------
-void PretransformVerticesTest::SetUp()
-{
-    scene = new aiScene();
+void PretransformVerticesTest::SetUp() {
+    mScene = new aiScene();
 
     // add 5 empty materials
-    scene->mMaterials = new aiMaterial*[scene->mNumMaterials = 5];
+    mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials = 5];
     for (unsigned int i = 0; i < 5;++i) {
-        scene->mMaterials[i] = new aiMaterial();
+        mScene->mMaterials[i] = new aiMaterial();
     }
 
     // add 25 test meshes
-    scene->mMeshes = new aiMesh*[scene->mNumMeshes = 25];
-    for ( unsigned int i = 0; i < 25; ++i) {
-        aiMesh* mesh = scene->mMeshes[ i ] = new aiMesh();
+    mScene->mMeshes = new aiMesh*[mScene->mNumMeshes = 25];
+    for ( unsigned int i = 0; i < 25; ++i) { 
+        aiMesh* mesh = mScene->mMeshes[ i ] = new aiMesh();
 
         mesh->mPrimitiveTypes = aiPrimitiveType_POINT;
         mesh->mFaces = new aiFace[ mesh->mNumFaces = 10+i ];
@@ -124,36 +130,33 @@ void PretransformVerticesTest::SetUp()
     }
 
     // construct some nodes (1+25)
-    scene->mRootNode = new aiNode();
-    scene->mRootNode->mName.Set("Root");
-    AddNodes(0,scene->mRootNode,2);
+    mScene->mRootNode = new aiNode();
+    mScene->mRootNode->mName.Set("Root");
+    AddNodes(0, mScene->mRootNode, 2);
 
-    process = new PretransformVertices();
+    mProcess = new PretransformVertices();
 }
 
 // ------------------------------------------------------------------------------------------------
-void PretransformVerticesTest::TearDown()
-{
-    delete scene;
-    delete process;
+void PretransformVerticesTest::TearDown() {
+    delete mScene;
+    delete mProcess;
 }
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(PretransformVerticesTest, testProcessCollapseHierarchy)
-{
-    process->KeepHierarchy(false);
-    process->Execute(scene);
+TEST_F(PretransformVerticesTest, testProcessCollapseHierarchy) {
+    mProcess->KeepHierarchy(false);
+    mProcess->Execute(mScene);
 
-    EXPECT_EQ(5U, scene->mNumMaterials);
-    EXPECT_EQ(10U, scene->mNumMeshes); // every second mesh has normals
+    EXPECT_EQ(5U, mScene->mNumMaterials);
+    EXPECT_EQ(10U, mScene->mNumMeshes); // every second mesh has normals
 }
 
 // ------------------------------------------------------------------------------------------------
-TEST_F(PretransformVerticesTest, testProcessKeepHierarchy)
-{
-    process->KeepHierarchy(true);
-    process->Execute(scene);
+TEST_F(PretransformVerticesTest, testProcessKeepHierarchy) {
+    mProcess->KeepHierarchy(true);
+    mProcess->Execute(mScene);
 
-    EXPECT_EQ(5U, scene->mNumMaterials);
-    EXPECT_EQ(49U, scene->mNumMeshes); // see note on mesh 12 above
+    EXPECT_EQ(5U, mScene->mNumMaterials);
+    EXPECT_EQ(49U, mScene->mNumMeshes); // see note on mesh 12 above
 }

+ 32 - 35
test/unit/utScenePreprocessor.cpp

@@ -50,67 +50,67 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace std;
 using namespace Assimp;
 
-
-class ScenePreprocessorTest : public ::testing::Test
-{
+class ScenePreprocessorTest : public ::testing::Test {
 public:
+    ScenePreprocessorTest()
+    : Test()
+    , mScenePreprocessor(nullptr)
+    , mScene(nullptr) {
+        // empty
+    }
 
+protected:
     virtual void SetUp();
     virtual void TearDown();
 
 protected:
-
     void CheckIfOnly(aiMesh* p, unsigned int num, unsigned flag);
+    void ProcessAnimation(aiAnimation* anim) { mScenePreprocessor->ProcessAnimation(anim); }
+    void ProcessMesh(aiMesh* mesh) { mScenePreprocessor->ProcessMesh(mesh); }
 
-    void ProcessAnimation(aiAnimation* anim) { pp->ProcessAnimation(anim); }
-    void ProcessMesh(aiMesh* mesh) { pp->ProcessMesh(mesh); }
-
-    ScenePreprocessor* pp;
-    aiScene* scene;
+private:
+    ScenePreprocessor *mScenePreprocessor;
+    aiScene *mScene;
 };
 
 // ------------------------------------------------------------------------------------------------
-void ScenePreprocessorTest::SetUp()
-{
+void ScenePreprocessorTest::SetUp() {
     // setup a dummy scene with a single node
-    scene = new aiScene();
-    scene->mRootNode = new aiNode();
-    scene->mRootNode->mName.Set("<test>");
+    mScene = new aiScene();
+    mScene->mRootNode = new aiNode();
+    mScene->mRootNode->mName.Set("<test>");
 
     // add some translation
-    scene->mRootNode->mTransformation.a4 = 1.f;
-    scene->mRootNode->mTransformation.b4 = 2.f;
-    scene->mRootNode->mTransformation.c4 = 3.f;
+    mScene->mRootNode->mTransformation.a4 = 1.f;
+    mScene->mRootNode->mTransformation.b4 = 2.f;
+    mScene->mRootNode->mTransformation.c4 = 3.f;
 
     // and allocate a ScenePreprocessor to operate on the scene
-    pp = new ScenePreprocessor(scene);
+    mScenePreprocessor = new ScenePreprocessor(mScene);
 }
 
 // ------------------------------------------------------------------------------------------------
-void ScenePreprocessorTest::TearDown()
-{
-    delete pp;
-    delete scene;
+void ScenePreprocessorTest::TearDown() {
+    delete mScenePreprocessor;
+    delete mScene;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Check whether ProcessMesh() returns flag for a mesh that consist of primitives with num indices
-void ScenePreprocessorTest::CheckIfOnly(aiMesh* p, unsigned int num, unsigned int flag)
-{
+void ScenePreprocessorTest::CheckIfOnly(aiMesh* p, unsigned int num, unsigned int flag) {
     // Triangles only
     for (unsigned i = 0; i < p->mNumFaces;++i) {
         p->mFaces[i].mNumIndices = num;
     }
-    pp->ProcessMesh(p);
+    mScenePreprocessor->ProcessMesh(p);
     EXPECT_EQ(flag, p->mPrimitiveTypes);
     p->mPrimitiveTypes = 0;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Check whether a mesh is preprocessed correctly. Case 1: The mesh needs preprocessing
-TEST_F(ScenePreprocessorTest, testMeshPreprocessingPos)
-{
-    aiMesh* p = new aiMesh();
+TEST_F(ScenePreprocessorTest, testMeshPreprocessingPos) {
+    aiMesh* p = new aiMesh;
     p->mNumFaces = 100;
     p->mFaces = new aiFace[p->mNumFaces];
 
@@ -145,9 +145,8 @@ TEST_F(ScenePreprocessorTest, testMeshPreprocessingPos)
 
 // ------------------------------------------------------------------------------------------------
 // Check whether a mesh is preprocessed correctly. Case 1: The mesh doesn't need preprocessing
-TEST_F(ScenePreprocessorTest, testMeshPreprocessingNeg)
-{
-    aiMesh* p = new aiMesh();
+TEST_F(ScenePreprocessorTest, testMeshPreprocessingNeg) {
+    aiMesh* p = new aiMesh;
     p->mPrimitiveTypes = aiPrimitiveType_TRIANGLE|aiPrimitiveType_POLYGON;
     ProcessMesh(p);
 
@@ -160,8 +159,7 @@ TEST_F(ScenePreprocessorTest, testMeshPreprocessingNeg)
 
 // ------------------------------------------------------------------------------------------------
 // Make a dummy animation with a single channel, '<test>'
-aiAnimation* MakeDummyAnimation()
-{
+aiAnimation* MakeDummyAnimation() {
     aiAnimation* p = new aiAnimation();
     p->mNumChannels = 1;
     p->mChannels = new aiNodeAnim*[1];
@@ -172,8 +170,7 @@ aiAnimation* MakeDummyAnimation()
 
 // ------------------------------------------------------------------------------------------------
 // Check whether an anim is preprocessed correctly. Case 1: The anim needs preprocessing
-TEST_F(ScenePreprocessorTest, testAnimationPreprocessingPos)
-{
+TEST_F(ScenePreprocessorTest, testAnimationPreprocessingPos) {
     aiAnimation* p = MakeDummyAnimation();
     aiNodeAnim* anim = p->mChannels[0];
 

+ 44 - 67
test/unit/utSortByPType.cpp

@@ -52,12 +52,20 @@ using namespace Assimp;
 
 class SortByPTypeProcessTest : public ::testing::Test {
 public:
+    SortByPTypeProcessTest()
+    : Test()
+    , mProcess1(nullptr)
+    , mScene(nullptr) {
+        // empty
+    }
+
+protected:
     virtual void SetUp();
     virtual void TearDown();
 
 protected:
-    SortByPTypeProcess* process1;
-    aiScene* scene;
+    SortByPTypeProcess* mProcess1;
+    aiScene* mScene;
 };
 
 // ------------------------------------------------------------------------------------------------
@@ -75,8 +83,7 @@ static unsigned int num[10][4] = {
     };
 
 // ------------------------------------------------------------------------------------------------
-static unsigned int result[10] =
-{
+static unsigned int result[10] = {
     aiPrimitiveType_POLYGON,
     aiPrimitiveType_TRIANGLE,
     aiPrimitiveType_LINE,
@@ -90,19 +97,16 @@ static unsigned int result[10] =
 };
 
 // ------------------------------------------------------------------------------------------------
-void SortByPTypeProcessTest::SetUp()
-{
-//  process0 = new DeterminePTypeHelperProcess();
-    process1 = new SortByPTypeProcess();
-    scene = new aiScene();
+void SortByPTypeProcessTest::SetUp() {
+    mProcess1 = new SortByPTypeProcess();
+    mScene = new aiScene();
 
-    scene->mNumMeshes = 10;
-    scene->mMeshes = new aiMesh*[10];
+    mScene->mNumMeshes = 10;
+    mScene->mMeshes = new aiMesh*[10];
 
     bool five = false;
-    for (unsigned int i = 0; i < 10; ++i)
-    {
-        aiMesh* mesh = scene->mMeshes[i] = new aiMesh();
+    for (unsigned int i = 0; i < 10; ++i) {
+        aiMesh* mesh = mScene->mMeshes[i] = new aiMesh();
         mesh->mNumFaces = 1000;
         aiFace* faces =  mesh->mFaces = new aiFace[1000];
         aiVector3D* pv = mesh->mVertices = new aiVector3D[mesh->mNumFaces*5];
@@ -115,27 +119,24 @@ void SortByPTypeProcessTest::SetUp()
 
         unsigned int remaining[4] = {num[i][0],num[i][1],num[i][2],num[i][3]};
         unsigned int n = 0;
-        for (unsigned int m = 0; m < 1000; ++m)
-        {
+        for (unsigned int m = 0; m < 1000; ++m) {
             unsigned int idx = m % 4;
-            while (true)
-            {
-                if (!remaining[idx])
-                {
-                    if (4 == ++idx)idx = 0;
+            while (true) {
+                if (!remaining[idx]) {
+                    if (4 == ++idx) {
+                        idx = 0;
+                    }
                     continue;
                 }
                 break;
             }
             faces->mNumIndices = idx+1;
-            if (4 == faces->mNumIndices)
-            {
+            if (4 == faces->mNumIndices) {
                 if(five)++faces->mNumIndices;
                 five = !five;
             }
             faces->mIndices = new unsigned int[faces->mNumIndices];
-            for (unsigned int q = 0; q <faces->mNumIndices;++q,++n)
-            {
+            for (unsigned int q = 0; q <faces->mNumIndices;++q,++n) {
                 faces->mIndices[q] = n;
                 float f = (float)remaining[idx];
 
@@ -152,12 +153,11 @@ void SortByPTypeProcessTest::SetUp()
         mesh->mNumVertices = n;
     }
 
-    scene->mRootNode = new aiNode();
-    scene->mRootNode->mNumChildren = 5;
-    scene->mRootNode->mChildren = new aiNode*[5];
-    for (unsigned int i = 0; i< 5;++i )
-    {
-        aiNode* node = scene->mRootNode->mChildren[i] = new aiNode();
+    mScene->mRootNode = new aiNode();
+    mScene->mRootNode->mNumChildren = 5;
+    mScene->mRootNode->mChildren = new aiNode*[5];
+    for (unsigned int i = 0; i< 5;++i ) {
+        aiNode* node = mScene->mRootNode->mChildren[i] = new aiNode();
         node->mNumMeshes = 2;
         node->mMeshes = new unsigned int[2];
         node->mMeshes[0] = (i<<1u);
@@ -166,48 +166,27 @@ void SortByPTypeProcessTest::SetUp()
 }
 
 // ------------------------------------------------------------------------------------------------
-void SortByPTypeProcessTest::TearDown()
-{
-    //delete process0;
-    delete process1;
-    delete scene;
+void SortByPTypeProcessTest::TearDown() {
+    delete mProcess1;
+    delete mScene;
 }
 
 // ------------------------------------------------------------------------------------------------
-//TEST_F(SortByPTypeProcessTest, DeterminePTypeStep()
-//{
-//  process0->Execute(scene);
-//
-//  for (unsigned int i = 0; i < 10; ++i)
-//  {
-//      aiMesh* mesh = scene->mMeshes[i];
-//      EXPECT_TRUE(mesh->mPrimitiveTypes == result[i]);
-//  }
-//}
-
-// ------------------------------------------------------------------------------------------------
-TEST_F(SortByPTypeProcessTest, SortByPTypeStep)
-{
-    // process0->Execute(scene);
-
-    // and another small test for ScenePreprocessor
-    ScenePreprocessor s(scene);
+TEST_F(SortByPTypeProcessTest, SortByPTypeStep) {
+    ScenePreprocessor s(mScene);
     s.ProcessScene();
     for (unsigned int m = 0; m< 10;++m)
-        EXPECT_EQ(result[m], scene->mMeshes[m]->mPrimitiveTypes);
+        EXPECT_EQ(result[m], mScene->mMeshes[m]->mPrimitiveTypes);
 
-    process1->Execute(scene);
+    mProcess1->Execute(mScene);
 
     unsigned int idx = 0;
-    for (unsigned int m = 0,real = 0; m< 10;++m)
-    {
-        for (unsigned int n = 0; n < 4;++n)
-        {
-            if ((idx = num[m][n]))
-            {
-                EXPECT_TRUE(real < scene->mNumMeshes);
+    for (unsigned int m = 0,real = 0; m< 10;++m) {
+        for (unsigned int n = 0; n < 4;++n) {
+            if ((idx = num[m][n])) {
+                EXPECT_TRUE(real < mScene->mNumMeshes);
 
-                aiMesh* mesh = scene->mMeshes[real];
+                aiMesh* mesh = mScene->mMeshes[real];
 
                 EXPECT_TRUE(NULL != mesh);
                 EXPECT_EQ(AI_PRIMITIVE_TYPE_FOR_N_INDICES(n+1), mesh->mPrimitiveTypes);
@@ -218,8 +197,7 @@ TEST_F(SortByPTypeProcessTest, SortByPTypeStep)
                 EXPECT_TRUE(NULL != mesh->mTextureCoords[0]);
 
                 EXPECT_TRUE(mesh->mNumFaces == idx);
-                for (unsigned int f = 0; f < mesh->mNumFaces;++f)
-                {
+                for (unsigned int f = 0; f < mesh->mNumFaces;++f) {
                     aiFace& face = mesh->mFaces[f];
                     EXPECT_TRUE(face.mNumIndices == (n+1) || (3 == n && face.mNumIndices > 3));
                 }
@@ -228,4 +206,3 @@ TEST_F(SortByPTypeProcessTest, SortByPTypeStep)
         }
     }
 }
-

+ 92 - 99
test/unit/utglTF2ImportExport.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -55,17 +53,23 @@ class utglTF2ImportExport : public AbstractImportExportBase {
 public:
     virtual bool importerTest() {
         Assimp::Importer importer;
-        const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", aiProcess_ValidateDataStructure);
+        const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf",
+            aiProcess_ValidateDataStructure);
         EXPECT_NE( scene, nullptr );
-        if ( !scene ) return false;
+        if (!scene) {
+            return false;
+        }
 
         EXPECT_TRUE( scene->HasMaterials() );
-        if ( !scene->HasMaterials() ) return false;
+        if (!scene->HasMaterials()) {
+            return false;
+        }
         const aiMaterial *material = scene->mMaterials[0];
 
         aiString path;
         aiTextureMapMode modes[2];
-        EXPECT_EQ( aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, nullptr, nullptr, modes) );
+        EXPECT_EQ( aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr,
+            nullptr, nullptr, modes) );
         EXPECT_STREQ( path.C_Str(), "CesiumLogoFlat.png" );
         EXPECT_EQ( modes[0], aiTextureMapMode_Mirror );
         EXPECT_EQ( modes[1], aiTextureMapMode_Clamp );
@@ -75,7 +79,8 @@ public:
 
     virtual bool binaryImporterTest() {
         Assimp::Importer importer;
-        const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/2CylinderEngine-glTF-Binary/2CylinderEngine.glb", aiProcess_ValidateDataStructure);
+        const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/2CylinderEngine-glTF-Binary/2CylinderEngine.glb",
+            aiProcess_ValidateDataStructure);
         return nullptr != scene;
     }
 
@@ -83,7 +88,8 @@ public:
     virtual bool exporterTest() {
         Assimp::Importer importer;
         Assimp::Exporter exporter;
-        const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", aiProcess_ValidateDataStructure );
+        const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf",
+            aiProcess_ValidateDataStructure );
         EXPECT_NE( nullptr, scene );
         EXPECT_EQ( aiReturn_SUCCESS, exporter.Export( scene, "gltf2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured_out.gltf" ) );
 
@@ -105,7 +111,8 @@ TEST_F( utglTF2ImportExport, importBinaryglTF2FromFileTest ) {
 TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) {
     Assimp::Importer importer;
     Assimp::Exporter exporter;
-    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", aiProcess_ValidateDataStructure);
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf",
+        aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured_out.obj"));
 }
@@ -113,7 +120,8 @@ TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) {
 TEST_F(utglTF2ImportExport, importglTF2EmbeddedAndExportToOBJ) {
     Assimp::Importer importer;
     Assimp::Exporter exporter;
-    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf", aiProcess_ValidateDataStructure);
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf",
+        aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj"));
 }
@@ -124,10 +132,9 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) {
     //Points without indices
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 1024);
-    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i)
-    {
-        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 1);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 1024u);
+    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
+        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 1u);
         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], i);
     }
 }
@@ -137,12 +144,11 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLinesWithoutIndices) {
     //Lines without indices
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 8);
-    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i)
-    {
-        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 2);
-        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], i*2);
-        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], i*2 + 1);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 8u);
+    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
+        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 2u);
+        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], i*2u);
+        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], i*2u + 1u);
     }
 }
 
@@ -151,15 +157,14 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLinesLoopWithoutIndices) {
     //Lines loop without indices
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
 
-    std::array<int, 5> l1 = {{ 0, 1, 2, 3, 0 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2);
-    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i)
-    {
-        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 2);
+    std::array<unsigned int, 5> l1 = {{ 0u, 1u, 2u, 3u, 0u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u);
+    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
+        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 2u);
         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]);
-        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], l1[i + 1]);
+        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], l1[i + 1u]);
     }
 }
 
@@ -168,14 +173,13 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLinesStripWithoutIndices) {
     //Lines strip without indices
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 5);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 5u);
 
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2);
-    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i)
-    {
-        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 2);
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u);
+    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
+        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 2u);
         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], i);
-        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], i + 1);
+        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], i + 1u);
     }
 }
 
@@ -184,19 +188,17 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesStripWithoutIndices
     //Triangles strip without indices
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4);
-    std::array<int, 3> f1 = {{ 0, 1, 2 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3);
-    for (int i = 0; i < 3; ++i)
-    {
+    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
+    std::array<unsigned int, 3> f1 = {{ 0u, 1u, 2u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u);
+    for (unsigned int i = 0; i < 3; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]);
     }
 
-    std::array<int, 3> f2 = {{ 2, 1, 3 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3);
-    for (int i = 0; i < 3; ++i)
-    {
+    std::array<unsigned int, 3> f2 = {{ 2u, 1u, 3u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u);
+    for (size_t i = 0; i < 3; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]);
     }
 }
@@ -206,19 +208,17 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesFanWithoutIndices)
     //Triangles fan without indices
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4);
-    std::array<int, 3> f1 = {{ 0, 1, 2 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3);
-    for (int i = 0; i < 3; ++i)
-    {
+    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
+    std::array<unsigned int, 3> f1 = {{ 0u, 1u, 2u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u);
+    for (size_t i = 0; i < 3; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]);
     }
 
-    std::array<int, 3> f2 = {{ 0, 2, 3 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3);
-    for (int i = 0; i < 3; ++i)
-    {
+    std::array<unsigned int, 3> f2 = {{ 0u, 2u, 3u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u);
+    for (size_t i = 0; i < 3; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]);
     }
 }
@@ -228,19 +228,17 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesWithoutIndices) {
     //Triangles without indices
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 6);
-    std::array<int, 3> f1 = {{ 0, 1, 2 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3);
-    for (int i = 0; i < 3; ++i)
-    {
+    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 6u);
+    std::array<unsigned int, 3> f1 = {{ 0u, 1u, 2u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u);
+    for (size_t i = 0; i < 3; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]);
     }
 
-    std::array<int, 3> f2 = {{ 3, 4, 5 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3);
-    for (int i = 0; i < 3; ++i)
-    {
+    std::array<unsigned int, 3> f2 = {{ 3u, 4u, 5u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u);
+    for (size_t i = 0; i < 3; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]);
     }
 }
@@ -250,10 +248,9 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePoints) {
     //Line loop
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 1024);
-    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i)
-    {
-        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 1);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 1024u);
+    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
+        EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 1u);
         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], i);
     }
 }
@@ -263,9 +260,9 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLines) {
     //Lines
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4);
-    std::array<int, 5> l1 = {{ 0, 3, 2, 1, 0 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
+    std::array<unsigned int, 5> l1 = {{ 0u, 3u, 2u, 1u, 0u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u);
     for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i)
     {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]);
@@ -278,9 +275,9 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLineLoop) {
     //Line loop
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4);
-    std::array<int, 5> l1 = {{ 0, 3, 2, 1, 0 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
+    std::array<unsigned int, 5> l1 = {{ 0, 3u, 2u, 1u, 0u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u);
     for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i)
     {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]);
@@ -293,11 +290,10 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLineStrip) {
     //Lines Strip
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4);
-    std::array<int, 5> l1 = {{ 0, 3, 2, 1, 0 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2);
-    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i)
-    {
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
+    std::array<unsigned int, 5> l1 = {{ 0u, 3u, 2u, 1u, 0u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u);
+    for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]);
         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], l1[i + 1]);
     }
@@ -308,19 +304,17 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesStrip) {
     //Triangles strip
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4);
-    std::array<int, 3> f1 = {{ 0, 3, 1 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3);
-    for (int i = 0; i < 3; ++i)
-    {
+    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
+    std::array<unsigned int, 3> f1 = {{ 0u, 3u, 1u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u);
+    for (size_t i = 0; i < 3; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]);
     }
 
-    std::array<int, 3> f2 = {{ 1, 3, 2 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3);
-    for (int i = 0; i < 3; ++i)
-    {
+    std::array<unsigned int, 3> f2 = {{ 1u, 3u, 2u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u);
+    for (size_t i = 0; i < 3; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]);
     }
 }
@@ -330,19 +324,17 @@ TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesFan) {
     //Triangles fan
     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.gltf", aiProcess_ValidateDataStructure);
     EXPECT_NE(nullptr, scene);
-    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4);
-    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2);
-    std::array<int, 3> f1 = {{ 0, 3, 2 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3);
-    for (int i = 0; i < 3; ++i)
-    {
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
+    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
+    std::array<unsigned int, 3> f1 = {{ 0u, 3u, 2u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u );
+    for (size_t i = 0; i < 3; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]);
     }
 
-    std::array<int, 3> f2 = {{ 0, 2, 1 }};
-    EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3);
-    for (int i = 0; i < 3; ++i)
-    {
+    std::array<unsigned int, 3> f2 = {{ 0u, 2u, 1u }};
+    EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u );
+    for (size_t i = 0; i < 3; ++i) {
         EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]);
     }
 }
@@ -378,7 +370,8 @@ TEST_F(utglTF2ImportExport, importglTF2FromMemory) {
 
 TEST_F( utglTF2ImportExport, bug_import_simple_skin ) {
     Assimp::Importer importer;
-    const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/simple_skin.gltf", aiProcess_ValidateDataStructure );
+    const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/simple_skin.gltf",
+        aiProcess_ValidateDataStructure );
     EXPECT_NE( nullptr, scene );
 }