Browse Source

Upgrade to Assimp 5.0

Marc Chapman 5 years ago
parent
commit
c2018ad3de
100 changed files with 6686 additions and 5687 deletions
  1. 2 2
      Engine/lib/assimp/code/3DS/3DSConverter.cpp
  2. 6 4
      Engine/lib/assimp/code/3DS/3DSExporter.cpp
  3. 0 0
      Engine/lib/assimp/code/3DS/3DSExporter.h
  4. 0 0
      Engine/lib/assimp/code/3DS/3DSHelper.h
  5. 0 2
      Engine/lib/assimp/code/3DS/3DSLoader.cpp
  6. 0 0
      Engine/lib/assimp/code/3DS/3DSLoader.h
  7. 0 0
      Engine/lib/assimp/code/3MF/3MFXmlTags.h
  8. 5 1
      Engine/lib/assimp/code/3MF/D3MFExporter.cpp
  9. 0 0
      Engine/lib/assimp/code/3MF/D3MFExporter.h
  10. 2 2
      Engine/lib/assimp/code/3MF/D3MFImporter.cpp
  11. 0 0
      Engine/lib/assimp/code/3MF/D3MFImporter.h
  12. 207 0
      Engine/lib/assimp/code/3MF/D3MFOpcPackage.cpp
  13. 3 4
      Engine/lib/assimp/code/3MF/D3MFOpcPackage.h
  14. 1 1
      Engine/lib/assimp/code/AC/ACLoader.cpp
  15. 0 0
      Engine/lib/assimp/code/AC/ACLoader.h
  16. 1 1
      Engine/lib/assimp/code/AMF/AMFImporter.cpp
  17. 0 0
      Engine/lib/assimp/code/AMF/AMFImporter.hpp
  18. 0 0
      Engine/lib/assimp/code/AMF/AMFImporter_Geometry.cpp
  19. 0 0
      Engine/lib/assimp/code/AMF/AMFImporter_Macro.hpp
  20. 0 0
      Engine/lib/assimp/code/AMF/AMFImporter_Material.cpp
  21. 0 0
      Engine/lib/assimp/code/AMF/AMFImporter_Node.hpp
  22. 13 13
      Engine/lib/assimp/code/AMF/AMFImporter_Postprocess.cpp
  23. 16 16
      Engine/lib/assimp/code/ASE/ASELoader.cpp
  24. 0 1
      Engine/lib/assimp/code/ASE/ASELoader.h
  25. 2 3
      Engine/lib/assimp/code/ASE/ASEParser.cpp
  26. 6 6
      Engine/lib/assimp/code/ASE/ASEParser.h
  27. 3 2
      Engine/lib/assimp/code/Assbin/AssbinExporter.cpp
  28. 0 0
      Engine/lib/assimp/code/Assbin/AssbinExporter.h
  29. 4 4
      Engine/lib/assimp/code/Assbin/AssbinLoader.cpp
  30. 0 0
      Engine/lib/assimp/code/Assbin/AssbinLoader.h
  31. 109 0
      Engine/lib/assimp/code/Assjson/cencode.c
  32. 31 0
      Engine/lib/assimp/code/Assjson/cencode.h
  33. 809 0
      Engine/lib/assimp/code/Assjson/json_exporter.cpp
  34. 320 0
      Engine/lib/assimp/code/Assjson/mesh_splitter.cpp
  35. 61 0
      Engine/lib/assimp/code/Assjson/mesh_splitter.h
  36. 4 4
      Engine/lib/assimp/code/Assxml/AssxmlExporter.cpp
  37. 0 0
      Engine/lib/assimp/code/Assxml/AssxmlExporter.h
  38. 6 4
      Engine/lib/assimp/code/B3D/B3DImporter.cpp
  39. 0 0
      Engine/lib/assimp/code/B3D/B3DImporter.h
  40. 0 0
      Engine/lib/assimp/code/BVH/BVHLoader.cpp
  41. 0 0
      Engine/lib/assimp/code/BVH/BVHLoader.h
  42. 0 361
      Engine/lib/assimp/code/BaseImporter.h
  43. 0 123
      Engine/lib/assimp/code/Bitmap.h
  44. 0 0
      Engine/lib/assimp/code/Blender/BlenderBMesh.cpp
  45. 0 0
      Engine/lib/assimp/code/Blender/BlenderBMesh.h
  46. 0 0
      Engine/lib/assimp/code/Blender/BlenderCustomData.cpp
  47. 0 0
      Engine/lib/assimp/code/Blender/BlenderCustomData.h
  48. 0 0
      Engine/lib/assimp/code/Blender/BlenderDNA.cpp
  49. 1 1
      Engine/lib/assimp/code/Blender/BlenderDNA.h
  50. 0 0
      Engine/lib/assimp/code/Blender/BlenderDNA.inl
  51. 0 0
      Engine/lib/assimp/code/Blender/BlenderIntermediate.h
  52. 27 0
      Engine/lib/assimp/code/Blender/BlenderLoader.cpp
  53. 0 0
      Engine/lib/assimp/code/Blender/BlenderLoader.h
  54. 0 0
      Engine/lib/assimp/code/Blender/BlenderModifier.cpp
  55. 0 0
      Engine/lib/assimp/code/Blender/BlenderModifier.h
  56. 4 1
      Engine/lib/assimp/code/Blender/BlenderScene.cpp
  57. 4 0
      Engine/lib/assimp/code/Blender/BlenderScene.h
  58. 0 0
      Engine/lib/assimp/code/Blender/BlenderSceneGen.h
  59. 0 0
      Engine/lib/assimp/code/Blender/BlenderTessellator.cpp
  60. 5 1
      Engine/lib/assimp/code/Blender/BlenderTessellator.h
  61. 0 337
      Engine/lib/assimp/code/BlobIOSystem.h
  62. 0 287
      Engine/lib/assimp/code/ByteSwapper.h
  63. 63 92
      Engine/lib/assimp/code/C4D/C4DImporter.cpp
  64. 6 12
      Engine/lib/assimp/code/C4D/C4DImporter.h
  65. 1 1
      Engine/lib/assimp/code/CApi/AssimpCExport.cpp
  66. 0 0
      Engine/lib/assimp/code/CApi/CInterfaceIOWrapper.cpp
  67. 0 0
      Engine/lib/assimp/code/CApi/CInterfaceIOWrapper.h
  68. 644 463
      Engine/lib/assimp/code/CMakeLists.txt
  69. 17 12
      Engine/lib/assimp/code/COB/COBLoader.cpp
  70. 0 0
      Engine/lib/assimp/code/COB/COBLoader.h
  71. 0 0
      Engine/lib/assimp/code/COB/COBScene.h
  72. 0 0
      Engine/lib/assimp/code/CSM/CSMLoader.cpp
  73. 0 0
      Engine/lib/assimp/code/CSM/CSMLoader.h
  74. 169 104
      Engine/lib/assimp/code/Collada/ColladaExporter.cpp
  75. 1 2
      Engine/lib/assimp/code/Collada/ColladaExporter.h
  76. 2 6
      Engine/lib/assimp/code/Collada/ColladaHelper.h
  77. 292 291
      Engine/lib/assimp/code/Collada/ColladaLoader.cpp
  78. 5 8
      Engine/lib/assimp/code/Collada/ColladaLoader.h
  79. 3473 0
      Engine/lib/assimp/code/Collada/ColladaParser.cpp
  80. 26 1
      Engine/lib/assimp/code/Collada/ColladaParser.h
  81. 0 3215
      Engine/lib/assimp/code/ColladaParser.cpp
  82. 1 1
      Engine/lib/assimp/code/Common/Assimp.cpp
  83. 44 4
      Engine/lib/assimp/code/Common/BaseImporter.cpp
  84. 1 1
      Engine/lib/assimp/code/Common/BaseProcess.cpp
  85. 0 0
      Engine/lib/assimp/code/Common/BaseProcess.h
  86. 0 0
      Engine/lib/assimp/code/Common/Bitmap.cpp
  87. 0 4
      Engine/lib/assimp/code/Common/CreateAnimMesh.cpp
  88. 31 2
      Engine/lib/assimp/code/Common/DefaultIOStream.cpp
  89. 60 101
      Engine/lib/assimp/code/Common/DefaultIOSystem.cpp
  90. 0 0
      Engine/lib/assimp/code/Common/DefaultLogger.cpp
  91. 0 0
      Engine/lib/assimp/code/Common/DefaultProgressHandler.h
  92. 88 99
      Engine/lib/assimp/code/Common/Exporter.cpp
  93. 0 0
      Engine/lib/assimp/code/Common/FileLogStream.h
  94. 0 0
      Engine/lib/assimp/code/Common/FileSystemFilter.h
  95. 0 0
      Engine/lib/assimp/code/Common/IFF.h
  96. 11 8
      Engine/lib/assimp/code/Common/Importer.cpp
  97. 0 0
      Engine/lib/assimp/code/Common/Importer.h
  98. 54 48
      Engine/lib/assimp/code/Common/ImporterRegistry.cpp
  99. 0 0
      Engine/lib/assimp/code/Common/PolyTools.h
  100. 45 31
      Engine/lib/assimp/code/Common/PostStepRegistry.cpp

+ 2 - 2
Engine/lib/assimp/code/3DSConverter.cpp → Engine/lib/assimp/code/3DS/3DSConverter.cpp

@@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // internal headers
 #include "3DSLoader.h"
-#include "TargetAnimation.h"
+#include "Common/TargetAnimation.h"
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/StringComparison.h>
@@ -72,7 +72,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial()
     unsigned int idx( NotSet );
     for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
     {
-        std::string &s = mScene->mMaterials[i].mName;
+        std::string s = mScene->mMaterials[i].mName;
         for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) {
             *it = static_cast< char >( ::tolower( *it ) );
         }

+ 6 - 4
Engine/lib/assimp/code/3DSExporter.cpp → Engine/lib/assimp/code/3DS/3DSExporter.cpp

@@ -43,15 +43,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
 
-#include "3DSExporter.h"
-#include "3DSLoader.h"
-#include "3DSHelper.h"
+#include "3DS/3DSExporter.h"
+#include "3DS/3DSLoader.h"
+#include "3DS/3DSHelper.h"
+#include "PostProcessing/SplitLargeMeshes.h"
+
 #include <assimp/SceneCombiner.h>
-#include "SplitLargeMeshes.h"
 #include <assimp/StringComparison.h>
 #include <assimp/IOSystem.hpp>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/Exporter.hpp>
+
 #include <memory>
 
 using namespace Assimp;

+ 0 - 0
Engine/lib/assimp/code/3DSExporter.h → Engine/lib/assimp/code/3DS/3DSExporter.h


+ 0 - 0
Engine/lib/assimp/code/3DSHelper.h → Engine/lib/assimp/code/3DS/3DSHelper.h


+ 0 - 2
Engine/lib/assimp/code/3DSLoader.cpp → Engine/lib/assimp/code/3DS/3DSLoader.cpp

@@ -50,9 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
-// internal headers
 #include "3DSLoader.h"
-#include <assimp/Macros.h>
 #include <assimp/IOSystem.hpp>
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>

+ 0 - 0
Engine/lib/assimp/code/3DSLoader.h → Engine/lib/assimp/code/3DS/3DSLoader.h


+ 0 - 0
Engine/lib/assimp/code/3MFXmlTags.h → Engine/lib/assimp/code/3MF/3MFXmlTags.h


+ 5 - 1
Engine/lib/assimp/code/D3MFExporter.cpp → Engine/lib/assimp/code/3MF/D3MFExporter.cpp

@@ -55,7 +55,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "3MFXmlTags.h"
 #include "D3MFOpcPackage.h"
 
-#include <contrib/zip/src/zip.h>
+#ifdef ASSIMP_USE_HUNTER
+#  include <zip/zip.h>
+#else
+#  include <contrib/zip/src/zip.h>
+#endif
 
 namespace Assimp {
 

+ 0 - 0
Engine/lib/assimp/code/D3MFExporter.h → Engine/lib/assimp/code/3MF/D3MFExporter.h


+ 2 - 2
Engine/lib/assimp/code/D3MFImporter.cpp → Engine/lib/assimp/code/3MF/D3MFImporter.cpp

@@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/importerdesc.h>
 #include <assimp/StringComparison.h>
 #include <assimp/StringUtils.h>
+#include <assimp/ZipArchiveIOSystem.h>
 
 #include <string>
 #include <vector>
@@ -58,7 +59,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <memory>
 
 #include "D3MFOpcPackage.h"
-#include <unzip.h>
 #include <assimp/irrXMLWrapper.h>
 #include "3MFXmlTags.h"
 #include <assimp/fast_atof.h>
@@ -449,7 +449,7 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo
         if ( nullptr == pIOHandler ) {
             return false;
         }
-        if ( !D3MF::D3MFOpcPackage::isZipArchive( pIOHandler, filename ) ) {
+        if ( !ZipArchiveIOSystem::isZipArchive( pIOHandler, filename ) ) {
             return false;
         }
         D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename );

+ 0 - 0
Engine/lib/assimp/code/D3MFImporter.h → Engine/lib/assimp/code/3MF/D3MFImporter.h


+ 207 - 0
Engine/lib/assimp/code/3MF/D3MFOpcPackage.cpp

@@ -0,0 +1,207 @@
+/*
+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.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
+
+#include "D3MFOpcPackage.h"
+#include <assimp/Exceptional.h>
+
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/ai_assert.h>
+#include <assimp/ZipArchiveIOSystem.h>
+
+#include <cstdlib>
+#include <memory>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <cassert>
+#include "3MFXmlTags.h"
+
+namespace Assimp {
+
+namespace D3MF {
+// ------------------------------------------------------------------------------------------------
+
+typedef std::shared_ptr<OpcPackageRelationship> OpcPackageRelationshipPtr;
+
+class OpcPackageRelationshipReader {
+public:
+    OpcPackageRelationshipReader(XmlReader* xmlReader) {        
+        while(xmlReader->read()) {
+            if(xmlReader->getNodeType() == irr::io::EXN_ELEMENT &&
+               xmlReader->getNodeName() == XmlTag::RELS_RELATIONSHIP_CONTAINER)
+            {
+                ParseRootNode(xmlReader);
+            }
+        }
+    }
+
+    void ParseRootNode(XmlReader* xmlReader)
+    {       
+        ParseAttributes(xmlReader);
+
+        while(xmlReader->read())
+        {
+            if(xmlReader->getNodeType() == irr::io::EXN_ELEMENT &&
+               xmlReader->getNodeName() == XmlTag::RELS_RELATIONSHIP_NODE)
+            {
+                ParseChildNode(xmlReader);
+            }
+        }
+    }
+
+    void ParseAttributes(XmlReader*) {
+        // empty
+    }
+
+    bool validateRels( OpcPackageRelationshipPtr &relPtr ) {
+        if ( relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty() ) {
+            return false;
+        }
+        return true;
+    }
+
+    void ParseChildNode(XmlReader* xmlReader) {        
+        OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship());
+
+        relPtr->id = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_ID.c_str());
+        relPtr->type = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TYPE.c_str());
+        relPtr->target = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TARGET.c_str());
+        if ( validateRels( relPtr ) ) {
+            m_relationShips.push_back( relPtr );
+        }
+    }
+
+    std::vector<OpcPackageRelationshipPtr> m_relationShips;
+};
+
+// ------------------------------------------------------------------------------------------------
+D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
+: mRootStream(nullptr)
+, mZipArchive() {    
+    mZipArchive.reset( new ZipArchiveIOSystem( pIOHandler, rFile ) );
+    if(!mZipArchive->isOpen()) {
+        throw DeadlyImportError("Failed to open file " + rFile+ ".");
+    }
+
+    std::vector<std::string> fileList;
+    mZipArchive->getFileList(fileList);
+
+    for (auto& file: fileList) {
+        if(file == D3MF::XmlTag::ROOT_RELATIONSHIPS_ARCHIVE) {
+            //PkgRelationshipReader pkgRelReader(file, archive);
+            ai_assert(mZipArchive->Exists(file.c_str()));
+
+            IOStream *fileStream = mZipArchive->Open(file.c_str());
+
+            ai_assert(fileStream != nullptr);
+
+            std::string rootFile = ReadPackageRootRelationship(fileStream);
+            if ( rootFile.size() > 0 && rootFile[ 0 ] == '/' ) {
+                rootFile = rootFile.substr( 1 );
+                if ( rootFile[ 0 ] == '/' ) {
+                    // deal with zip-bug
+                    rootFile = rootFile.substr( 1 );
+                }
+            }
+
+            ASSIMP_LOG_DEBUG(rootFile);
+
+            mZipArchive->Close(fileStream);
+
+            mRootStream = mZipArchive->Open(rootFile.c_str());
+            ai_assert( mRootStream != nullptr );
+            if ( nullptr == mRootStream ) {
+                throw DeadlyExportError( "Cannot open root-file in archive : " + rootFile );
+            }
+
+        } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
+            ASSIMP_LOG_WARN_F("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES",file);
+        } else {
+            ASSIMP_LOG_WARN_F("Ignored file of unknown type: ",file);
+        }
+
+    }
+}
+
+D3MFOpcPackage::~D3MFOpcPackage() {
+    mZipArchive->Close(mRootStream);
+}
+
+IOStream* D3MFOpcPackage::RootStream() const {
+    return mRootStream;
+}
+
+static const std::string ModelRef = "3D/3dmodel.model";
+
+bool D3MFOpcPackage::validate() {
+    if ( nullptr == mRootStream || nullptr == mZipArchive ) {
+        return false;
+    }
+
+    return mZipArchive->Exists( ModelRef.c_str() );
+}
+
+std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) {
+    std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(stream));
+    std::unique_ptr<XmlReader> xml(irr::io::createIrrXMLReader(xmlStream.get()));
+
+    OpcPackageRelationshipReader reader(xml.get());
+
+    auto itr = std::find_if(reader.m_relationShips.begin(), reader.m_relationShips.end(), [](const OpcPackageRelationshipPtr& rel){
+        return rel->type == XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
+    });
+
+    if ( itr == reader.m_relationShips.end() ) {
+        throw DeadlyImportError( "Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE );
+    }
+
+    return (*itr)->target;
+}
+
+} // Namespace D3MF
+} // Namespace Assimp
+
+#endif //ASSIMP_BUILD_NO_3MF_IMPORTER

+ 3 - 4
Engine/lib/assimp/code/D3MFOpcPackage.h → Engine/lib/assimp/code/3MF/D3MFOpcPackage.h

@@ -49,6 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/irrXMLWrapper.h>
 
 namespace Assimp {
+    class ZipArchiveIOSystem;
+
 namespace D3MF {
 
 using XmlReader = irr::io::IrrXMLReader ;
@@ -60,22 +62,19 @@ struct OpcPackageRelationship {
     std::string target;
 };
 
-class D3MFZipArchive;
-
 class D3MFOpcPackage {
 public:
     D3MFOpcPackage( IOSystem* pIOHandler, const std::string& rFile );
     ~D3MFOpcPackage();
     IOStream* RootStream() const;
     bool validate();
-    static bool isZipArchive( IOSystem* pIOHandler, const std::string& rFile );
 
 protected:
     std::string ReadPackageRootRelationship(IOStream* stream);
 
 private:
     IOStream* mRootStream;
-    std::unique_ptr<D3MFZipArchive> mZipArchive;
+    std::unique_ptr<ZipArchiveIOSystem> mZipArchive;
 };
 
 } // Namespace D3MF

+ 1 - 1
Engine/lib/assimp/code/ACLoader.cpp → Engine/lib/assimp/code/AC/ACLoader.cpp

@@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/ParsingUtils.h>
 #include <assimp/fast_atof.h>
 #include <assimp/Subdivision.h>
-#include "Importer.h"
+#include "Common/Importer.h"
 #include <assimp/BaseImporter.h>
 #include <assimp/Importer.hpp>
 #include <assimp/light.h>

+ 0 - 0
Engine/lib/assimp/code/ACLoader.h → Engine/lib/assimp/code/AC/ACLoader.h


+ 1 - 1
Engine/lib/assimp/code/AMFImporter.cpp → Engine/lib/assimp/code/AMF/AMFImporter.cpp

@@ -83,7 +83,7 @@ void AMFImporter::Clear()
 	mMaterial_Converted.clear();
 	mTexture_Converted.clear();
 	// Delete all elements
-	if(mNodeElement_List.size())
+	if(!mNodeElement_List.empty())
 	{
 		for(CAMFImporter_NodeElement* ne: mNodeElement_List) { delete ne; }
 

+ 0 - 0
Engine/lib/assimp/code/AMFImporter.hpp → Engine/lib/assimp/code/AMF/AMFImporter.hpp


+ 0 - 0
Engine/lib/assimp/code/AMFImporter_Geometry.cpp → Engine/lib/assimp/code/AMF/AMFImporter_Geometry.cpp


+ 0 - 0
Engine/lib/assimp/code/AMFImporter_Macro.hpp → Engine/lib/assimp/code/AMF/AMFImporter_Macro.hpp


+ 0 - 0
Engine/lib/assimp/code/AMFImporter_Material.cpp → Engine/lib/assimp/code/AMF/AMFImporter_Material.cpp


+ 0 - 0
Engine/lib/assimp/code/AMFImporter_Node.hpp → Engine/lib/assimp/code/AMF/AMFImporter_Node.hpp


+ 13 - 13
Engine/lib/assimp/code/AMFImporter_Postprocess.cpp → Engine/lib/assimp/code/AMF/AMFImporter_Postprocess.cpp

@@ -66,7 +66,7 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*
     aiColor4D tcol;
 
 	// Check if stored data are supported.
-	if(Composition.size() != 0)
+	if(!Composition.empty())
 	{
 		throw DeadlyImportError("IME. GetColor for composition");
 	}
@@ -321,7 +321,7 @@ void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace
     };
 
 	pOutputList_Separated.clear();
-	if(pInputList.size() == 0) return;
+	if(pInputList.empty()) return;
 
 	do
 	{
@@ -334,19 +334,19 @@ void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace
 			{
 				auto it_old = it;
 
-				it++;
+				++it;
 				face_list_cur.push_back(*it_old);
 				pInputList.erase(it_old);
 			}
 			else
 			{
-				it++;
+				++it;
 			}
 		}
 
-		if(face_list_cur.size() > 0) pOutputList_Separated.push_back(face_list_cur);
+		if(!face_list_cur.empty()) pOutputList_Separated.push_back(face_list_cur);
 
-	} while(pInputList.size() > 0);
+	} while(!pInputList.empty());
 }
 
 void AMFImporter::Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& metadataList, aiNode& sceneNode) const
@@ -712,7 +712,7 @@ std::list<unsigned int> mesh_idx;
 	}// for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
 
 	// if meshes was created then assign new indices with current aiNode
-	if(mesh_idx.size() > 0)
+	if(!mesh_idx.empty())
 	{
 		std::list<unsigned int>::const_iterator mit = mesh_idx.begin();
 
@@ -787,7 +787,7 @@ std::list<aiNode*> ch_node;
 	}// for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
 
 	// copy found aiNode's as children
-	if(ch_node.size() == 0) throw DeadlyImportError("<constellation> must have at least one <instance>.");
+	if(ch_node.empty()) throw DeadlyImportError("<constellation> must have at least one <instance>.");
 
 	size_t ch_idx = 0;
 
@@ -883,13 +883,13 @@ nl_clean_loop:
 	if(node_list.size() > 1)
 	{
 		// walk through all nodes
-		for(std::list<aiNode*>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++)
+		for(std::list<aiNode*>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); ++nl_it)
 		{
 			// and try to find them in another top nodes.
 			std::list<aiNode*>::const_iterator next_it = nl_it;
 
-			next_it++;
-			for(; next_it != node_list.end(); next_it++)
+			++next_it;
+			for(; next_it != node_list.end(); ++next_it)
 			{
 				if((*next_it)->FindNode((*nl_it)->mName) != nullptr)
 				{
@@ -907,7 +907,7 @@ nl_clean_loop:
 	//
 	//
 	// Nodes
-	if(node_list.size() > 0)
+	if(!node_list.empty())
 	{
 		std::list<aiNode*>::const_iterator nl_it = node_list.begin();
 
@@ -924,7 +924,7 @@ nl_clean_loop:
 
 	//
 	// Meshes
-	if(mesh_list.size() > 0)
+	if(!mesh_list.empty())
 	{
 		std::list<aiMesh*>::const_iterator ml_it = mesh_list.begin();
 

+ 16 - 16
Engine/lib/assimp/code/ASELoader.cpp → Engine/lib/assimp/code/ASE/ASELoader.cpp

@@ -53,7 +53,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "ASELoader.h"
 #include <assimp/StringComparison.h>
 #include <assimp/SkeletonMeshBuilder.h>
-#include "TargetAnimation.h"
+#include "Common/TargetAnimation.h"
+
 #include <assimp/Importer.hpp>
 #include <assimp/IOSystem.hpp>
 #include <assimp/DefaultLogger.hpp>
@@ -88,23 +89,25 @@ ASEImporter::ASEImporter()
 , mBuffer()
 , pcScene()
 , configRecomputeNormals()
-, noSkeletonMesh()
-{}
+, noSkeletonMesh() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
-ASEImporter::~ASEImporter()
-{}
+ASEImporter::~ASEImporter() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
-{
+bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const {
     // check file extension
     const std::string extension = GetExtension(pFile);
 
-    if( extension == "ase" || extension == "ask")
+    if (extension == "ase" || extension == "ask") {
         return true;
+    }
 
     if ((!extension.length() || cs) && pIOHandler) {
         const char* tokens[] = {"*3dsmax_asciiexport"};
@@ -115,15 +118,13 @@ bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool
 
 // ------------------------------------------------------------------------------------------------
 // Loader meta information
-const aiImporterDesc* ASEImporter::GetInfo () const
-{
+const aiImporterDesc* ASEImporter::GetInfo () const {
     return &desc;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Setup configuration options
-void ASEImporter::SetupProperties(const Importer* pImp)
-{
+void ASEImporter::SetupProperties(const Importer* pImp) {
     configRecomputeNormals = (pImp->GetPropertyInteger(
         AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false);
 
@@ -133,12 +134,11 @@ void ASEImporter::SetupProperties(const Importer* pImp)
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 void ASEImporter::InternReadFile( const std::string& pFile,
-    aiScene* pScene, IOSystem* pIOHandler)
-{
+    aiScene* pScene, IOSystem* pIOHandler) {
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
 
     // Check whether we can read from the file
-    if( file.get() == NULL) {
+    if( file.get() == nullptr) {
         throw DeadlyImportError( "Failed to open ASE file " + pFile + ".");
     }
 
@@ -1299,7 +1299,7 @@ void ASEImporter::BuildMaterialIndices()
         }
     }
 
-    // Dekete our temporary array
+    // Delete our temporary array
     delete[] pcIntMaterials;
 }
 

+ 0 - 1
Engine/lib/assimp/code/ASELoader.h → Engine/lib/assimp/code/ASE/ASELoader.h

@@ -4,7 +4,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 - 3
Engine/lib/assimp/code/ASEParser.cpp → Engine/lib/assimp/code/ASE/ASEParser.cpp

@@ -45,20 +45,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Implementation of the ASE parser class
  */
 
-
 #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
 // internal headers
-#include "TextureTransform.h"
+#include "PostProcessing/TextureTransform.h"
 #include "ASELoader.h"
+
 #include <assimp/fast_atof.h>
 #include <assimp/DefaultLogger.hpp>
 
 using namespace Assimp;
 using namespace Assimp::ASE;
 
-
 // ------------------------------------------------------------------------------------------------
 // Begin an ASE parsing function
 

+ 6 - 6
Engine/lib/assimp/code/ASEParser.h → Engine/lib/assimp/code/ASE/ASEParser.h

@@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/qnan.h>
 
 // ASE is quite similar to 3ds. We can reuse some structures
-#include "3DSLoader.h"
+#include "3DS/3DSLoader.h"
 
 namespace Assimp    {
 namespace ASE   {
@@ -188,10 +188,11 @@ struct Animation {
     } mRotationType, mScalingType, mPositionType;
 
     Animation() AI_NO_EXCEPT
-        :   mRotationType   (TRACK)
-        ,   mScalingType    (TRACK)
-        ,   mPositionType   (TRACK)
-    {}
+    :   mRotationType   (TRACK)
+    ,   mScalingType    (TRACK)
+    ,   mPositionType   (TRACK) {
+        // empty
+    }
 
     //! List of track rotation keyframes
     std::vector< aiQuatKey > akeyRotations;
@@ -243,7 +244,6 @@ struct BaseNode {
         mTargetPosition.x = qnan;
     }
 
-
     //! Name of the mesh
     std::string mName;
 

+ 3 - 2
Engine/lib/assimp/code/AssbinExporter.cpp → Engine/lib/assimp/code/Assbin/AssbinExporter.cpp

@@ -46,12 +46,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
 
-#include "assbin_chunks.h"
+#include "Common/assbin_chunks.h"
+#include "PostProcessing/ProcessHelper.h"
+
 #include <assimp/version.h>
 #include <assimp/IOStream.hpp>
 #include <assimp/IOSystem.hpp>
 #include <assimp/Exporter.hpp>
-#include "ProcessHelper.h"
 #include <assimp/Exceptional.h>
 
 #ifdef ASSIMP_BUILD_NO_OWN_ZLIB

+ 0 - 0
Engine/lib/assimp/code/AssbinExporter.h → Engine/lib/assimp/code/Assbin/AssbinExporter.h


+ 4 - 4
Engine/lib/assimp/code/AssbinLoader.cpp → Engine/lib/assimp/code/Assbin/AssbinLoader.cpp

@@ -50,8 +50,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
 
 // internal headers
-#include "AssbinLoader.h"
-#include "assbin_chunks.h"
+#include "Assbin/AssbinLoader.h"
+#include "Common/assbin_chunks.h"
 #include <assimp/MemoryIOWrapper.h>
 #include <assimp/mesh.h>
 #include <assimp/anim.h>
@@ -68,7 +68,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 
 static const aiImporterDesc desc = {
-    ".assbin Importer",
+    "Assimp Binary Importer",
     "Gargaj / Conspiracy",
     "",
     "",
@@ -708,7 +708,7 @@ void AssbinImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
 
         unsigned char * uncompressedData = new unsigned char[ uncompressedSize ];
 
-        int res = uncompress( uncompressedData, &uncompressedSize, compressedData, len );
+        int res = uncompress( uncompressedData, &uncompressedSize, compressedData, (uLong) len );
         if(res != Z_OK)
         {
             delete [] uncompressedData;

+ 0 - 0
Engine/lib/assimp/code/AssbinLoader.h → Engine/lib/assimp/code/Assbin/AssbinLoader.h


+ 109 - 0
Engine/lib/assimp/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
Engine/lib/assimp/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 */

+ 809 - 0
Engine/lib/assimp/code/Assjson/json_exporter.cpp

@@ -0,0 +1,809 @@
+/*
+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*);
+
+// 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
Engine/lib/assimp/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
Engine/lib/assimp/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
+

+ 4 - 4
Engine/lib/assimp/code/AssxmlExporter.cpp → Engine/lib/assimp/code/Assxml/AssxmlExporter.cpp

@@ -46,13 +46,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_EXPORT
 #ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
 
-#include <stdarg.h>
+#include "PostProcessing/ProcessHelper.h"
+
 #include <assimp/version.h>
-#include "ProcessHelper.h"
 #include <assimp/IOStream.hpp>
 #include <assimp/IOSystem.hpp>
 #include <assimp/Exporter.hpp>
 
+#include <stdarg.h>
+
 #ifdef ASSIMP_BUILD_NO_OWN_ZLIB
 #   include <zlib.h>
 #else
@@ -555,8 +557,6 @@ void WriteDump(const aiScene* scene, IOStream* io, bool shortened) {
                             mesh->mNormals[n].z);
                     }
                 }
-                else {
-                }
                 ioprintf(io,"\t\t</Normals>\n");
             }
 

+ 0 - 0
Engine/lib/assimp/code/AssxmlExporter.h → Engine/lib/assimp/code/Assxml/AssxmlExporter.h


+ 6 - 4
Engine/lib/assimp/code/B3DImporter.cpp → Engine/lib/assimp/code/B3D/B3DImporter.cpp

@@ -49,17 +49,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
 
 // internal headers
-#include "B3DImporter.h"
-#include "TextureTransform.h"
-#include "ConvertToLHProcess.h"
+#include "B3D/B3DImporter.h"
+#include "PostProcessing/TextureTransform.h"
+#include "PostProcessing/ConvertToLHProcess.h"
+
 #include <assimp/StringUtils.h>
-#include <memory>
 #include <assimp/IOSystem.hpp>
 #include <assimp/anim.h>
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/importerdesc.h>
 
+#include <memory>
+
 using namespace Assimp;
 using namespace std;
 

+ 0 - 0
Engine/lib/assimp/code/B3DImporter.h → Engine/lib/assimp/code/B3D/B3DImporter.h


+ 0 - 0
Engine/lib/assimp/code/BVHLoader.cpp → Engine/lib/assimp/code/BVH/BVHLoader.cpp


+ 0 - 0
Engine/lib/assimp/code/BVHLoader.h → Engine/lib/assimp/code/BVH/BVHLoader.h


+ 0 - 361
Engine/lib/assimp/code/BaseImporter.h

@@ -1,361 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2017, assimp team
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the
-following conditions are met:
-
-* Redistributions of source code must retain the above
-  copyright notice, this list of conditions and the
-  following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the
-  following disclaimer in the documentation and/or other
-  materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-  contributors may be used to endorse or promote products
-  derived from this software without specific prior
-  written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----------------------------------------------------------------------
-*/
-
-/** @file Definition of the base class for all importer worker classes. */
-#ifndef INCLUDED_AI_BASEIMPORTER_H
-#define INCLUDED_AI_BASEIMPORTER_H
-
-#include "Exceptional.h"
-
-#include <vector>
-#include <set>
-#include <assimp/types.h>
-#include <assimp/ProgressHandler.hpp>
-
-struct aiScene;
-struct aiImporterDesc;
-
-namespace Assimp    {
-
-class Importer;
-class IOSystem;
-class BaseProcess;
-class SharedPostProcessInfo;
-class IOStream;
-
-// utility to do char4 to uint32 in a portable manner
-#define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \
-    (string[1] << 16) + (string[2] << 8) + string[3]))
-
-
-// ---------------------------------------------------------------------------
-/** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface
- *  for all importer worker classes.
- *
- * The interface defines two functions: CanRead() is used to check if the
- * importer can handle the format of the given file. If an implementation of
- * this function returns true, the importer then calls ReadFile() which
- * imports the given file. ReadFile is not overridable, it just calls
- * InternReadFile() and catches any ImportErrorException that might occur.
- */
-class ASSIMP_API BaseImporter
-{
-    friend class Importer;
-
-public:
-
-    /** Constructor to be privately used by #Importer */
-    BaseImporter();
-
-    /** Destructor, private as well */
-    virtual ~BaseImporter();
-
-public:
-    // -------------------------------------------------------------------
-    /** Returns whether the class can handle the format of the given file.
-     *
-     * The implementation should be as quick as possible. A check for
-     * the file extension is enough. If no suitable loader is found with
-     * this strategy, CanRead() is called again, the 'checkSig' parameter
-     * set to true this time. Now the implementation is expected to
-     * perform a full check of the file structure, possibly searching the
-     * first bytes of the file for magic identifiers or keywords.
-     *
-     * @param pFile Path and file name of the file to be examined.
-     * @param pIOHandler The IO handler to use for accessing any file.
-     * @param checkSig Set to true if this method is called a second time.
-     *   This time, the implementation may take more time to examine the
-     *   contents of the file to be loaded for magic bytes, keywords, etc
-     *   to be able to load files with unknown/not existent file extensions.
-     * @return true if the class can read this file, false if not.
-     */
-    virtual bool CanRead(
-        const std::string& pFile,
-        IOSystem* pIOHandler,
-        bool checkSig
-        ) const = 0;
-
-    // -------------------------------------------------------------------
-    /** Imports the given file and returns the imported data.
-     * If the import succeeds, ownership of the data is transferred to
-     * the caller. If the import fails, NULL is returned. The function
-     * takes care that any partially constructed data is destroyed
-     * beforehand.
-     *
-     * @param pImp #Importer object hosting this loader.
-     * @param pFile Path of the file to be imported.
-     * @param pIOHandler IO-Handler used to open this and possible other files.
-     * @return The imported data or NULL if failed. If it failed a
-     * human-readable error description can be retrieved by calling
-     * GetErrorText()
-     *
-     * @note This function is not intended to be overridden. Implement
-     * InternReadFile() to do the import. If an exception is thrown somewhere
-     * in InternReadFile(), this function will catch it and transform it into
-     *  a suitable response to the caller.
-     */
-    aiScene* ReadFile(
-        const Importer* pImp,
-        const std::string& pFile,
-        IOSystem* pIOHandler
-        );
-
-    // -------------------------------------------------------------------
-    /** Returns the error description of the last error that occurred.
-     * @return A description of the last error that occurred. An empty
-     * string if there was no error.
-     */
-    const std::string& GetErrorText() const {
-        return m_ErrorText;
-    }
-
-    // -------------------------------------------------------------------
-    /** Called prior to ReadFile().
-     * The function is a request to the importer to update its configuration
-     * basing on the Importer's configuration property list.
-     * @param pImp Importer instance
-     */
-    virtual void SetupProperties(
-        const Importer* pImp
-        );
-
-    // -------------------------------------------------------------------
-    /** Called by #Importer::GetImporterInfo to get a description of
-     *  some loader features. Importers must provide this information. */
-    virtual const aiImporterDesc* GetInfo() const = 0;
-
-    // -------------------------------------------------------------------
-    /** Called by #Importer::GetExtensionList for each loaded importer.
-     *  Take the extension list contained in the structure returned by
-     *  #GetInfo and insert all file extensions into the given set.
-     *  @param extension set to collect file extensions in*/
-    void GetExtensionList(std::set<std::string>& extensions);
-
-protected:
-
-    // -------------------------------------------------------------------
-    /** Imports the given file into the given scene structure. The
-     * function is expected to throw an ImportErrorException if there is
-     * an error. If it terminates normally, the data in aiScene is
-     * expected to be correct. Override this function to implement the
-     * actual importing.
-     * <br>
-     *  The output scene must meet the following requirements:<br>
-     * <ul>
-     * <li>At least a root node must be there, even if its only purpose
-     *     is to reference one mesh.</li>
-     * <li>aiMesh::mPrimitiveTypes may be 0. The types of primitives
-     *   in the mesh are determined automatically in this case.</li>
-     * <li>the vertex data is stored in a pseudo-indexed "verbose" format.
-     *   In fact this means that every vertex that is referenced by
-     *   a face is unique. Or the other way round: a vertex index may
-     *   not occur twice in a single aiMesh.</li>
-     * <li>aiAnimation::mDuration may be -1. Assimp determines the length
-     *   of the animation automatically in this case as the length of
-     *   the longest animation channel.</li>
-     * <li>aiMesh::mBitangents may be NULL if tangents and normals are
-     *   given. In this case bitangents are computed as the cross product
-     *   between normal and tangent.</li>
-     * <li>There needn't be a material. If none is there a default material
-     *   is generated. However, it is recommended practice for loaders
-     *   to generate a default material for yourself that matches the
-     *   default material setting for the file format better than Assimp's
-     *   generic default material. Note that default materials *should*
-     *   be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded
-     *   or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy)
-     *   texture. </li>
-     * </ul>
-     * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is <b>not</b> set:<ul>
-     * <li> at least one mesh must be there</li>
-     * <li> there may be no meshes with 0 vertices or faces</li>
-     * </ul>
-     * This won't be checked (except by the validation step): Assimp will
-     * crash if one of the conditions is not met!
-     *
-     * @param pFile Path of the file to be imported.
-     * @param pScene The scene object to hold the imported data.
-     * NULL is not a valid parameter.
-     * @param pIOHandler The IO handler to use for any file access.
-     * NULL is not a valid parameter. */
-    virtual void InternReadFile(
-        const std::string& pFile,
-        aiScene* pScene,
-        IOSystem* pIOHandler
-        ) = 0;
-
-public: // static utilities
-
-    // -------------------------------------------------------------------
-    /** A utility for CanRead().
-     *
-     *  The function searches the header of a file for a specific token
-     *  and returns true if this token is found. This works for text
-     *  files only. There is a rudimentary handling of UNICODE files.
-     *  The comparison is case independent.
-     *
-     *  @param pIOSystem IO System to work with
-     *  @param file File name of the file
-     *  @param tokens List of tokens to search for
-     *  @param numTokens Size of the token array
-     *  @param searchBytes Number of bytes to be searched for the tokens.
-     */
-    static bool SearchFileHeaderForToken(
-        IOSystem* pIOSystem,
-        const std::string&  file,
-        const char** tokens,
-        unsigned int numTokens,
-        unsigned int searchBytes = 200,
-        bool tokensSol = false);
-
-    // -------------------------------------------------------------------
-    /** @brief Check whether a file has a specific file extension
-     *  @param pFile Input file
-     *  @param ext0 Extension to check for. Lowercase characters only, no dot!
-     *  @param ext1 Optional second extension
-     *  @param ext2 Optional third extension
-     *  @note Case-insensitive
-     */
-    static bool SimpleExtensionCheck (
-        const std::string& pFile,
-        const char* ext0,
-        const char* ext1 = NULL,
-        const char* ext2 = NULL);
-
-    // -------------------------------------------------------------------
-    /** @brief Extract file extension from a string
-     *  @param pFile Input file
-     *  @return Extension without trailing dot, all lowercase
-     */
-    static std::string GetExtension (
-        const std::string& pFile);
-
-    // -------------------------------------------------------------------
-    /** @brief Check whether a file starts with one or more magic tokens
-     *  @param pFile Input file
-     *  @param pIOHandler IO system to be used
-     *  @param magic n magic tokens
-     *  @params num Size of magic
-     *  @param offset Offset from file start where tokens are located
-     *  @param Size of one token, in bytes. Maximally 16 bytes.
-     *  @return true if one of the given tokens was found
-     *
-     *  @note For convenience, the check is also performed for the
-     *  byte-swapped variant of all tokens (big endian). Only for
-     *  tokens of size 2,4.
-     */
-    static bool CheckMagicToken(
-        IOSystem* pIOHandler,
-        const std::string& pFile,
-        const void* magic,
-        unsigned int num,
-        unsigned int offset = 0,
-        unsigned int size   = 4);
-
-    // -------------------------------------------------------------------
-    /** An utility for all text file loaders. It converts a file to our
-     *   UTF8 character set. Errors are reported, but ignored.
-     *
-     *  @param data File buffer to be converted to UTF8 data. The buffer
-     *  is resized as appropriate. */
-    static void ConvertToUTF8(
-        std::vector<char>& data);
-
-    // -------------------------------------------------------------------
-    /** An utility for all text file loaders. It converts a file from our
-     *   UTF8 character set back to ISO-8859-1. Errors are reported, but ignored.
-     *
-     *  @param data File buffer to be converted from UTF8 to ISO-8859-1. The buffer
-     *  is resized as appropriate. */
-    static void ConvertUTF8toISO8859_1(
-        std::string& data);
-
-    // -------------------------------------------------------------------
-    /// @brief  Enum to define, if empty files are ok or not.
-    enum TextFileMode { 
-        ALLOW_EMPTY,
-        FORBID_EMPTY 
-    };
-
-    // -------------------------------------------------------------------
-    /** Utility for text file loaders which copies the contents of the
-     *  file into a memory buffer and converts it to our UTF8
-     *  representation.
-     *  @param stream Stream to read from.
-     *  @param data Output buffer to be resized and filled with the
-     *   converted text file data. The buffer is terminated with
-     *   a binary 0.
-     *  @param mode Whether it is OK to load empty text files. */
-    static void TextFileToBuffer(
-        IOStream* stream,
-        std::vector<char>& data,
-        TextFileMode mode = FORBID_EMPTY);
-
-    // -------------------------------------------------------------------
-    /** Utility function to move a std::vector into a aiScene array
-    *  @param vec The vector to be moved
-    *  @param out The output pointer to the allocated array.
-    *  @param numOut The output count of elements copied. */
-    template<typename T>
-    AI_FORCE_INLINE
-    static void CopyVector(
-        std::vector<T>& vec,
-        T*& out,
-        unsigned int& outLength)
-    {
-        outLength = unsigned(vec.size());
-        if (outLength) {
-            out = new T[outLength];
-            std::swap_ranges(vec.begin(), vec.end(), out);
-        }
-    }
-
-protected:
-    /// Error description in case there was one.
-    std::string m_ErrorText;
-    /// Currently set progress handler.
-    ProgressHandler* m_progress;
-};
-
-
-
-} // end of namespace Assimp
-
-#endif // AI_BASEIMPORTER_H_INC

+ 0 - 123
Engine/lib/assimp/code/Bitmap.h

@@ -1,123 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2017, assimp team
-
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the following
-conditions are met:
-
-* Redistributions of source code must retain the above
-  copyright notice, this list of conditions and the
-  following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the
-  following disclaimer in the documentation and/or other
-  materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-  contributors may be used to endorse or promote products
-  derived from this software without specific prior
-  written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------
-*/
-
-/** @file Bitmap.h
- *  @brief Defines bitmap format helper for textures
- *
- * Used for file formats which embed their textures into the model file.
- */
-
-#ifndef AI_BITMAP_H_INC
-#define AI_BITMAP_H_INC
-
-#include <stdint.h>
-#include <cstddef>
-
-struct aiTexture;
-
-namespace Assimp {
-
-class IOStream;
-
-class Bitmap {
-protected:
-
-    struct Header {
-        uint16_t type;
-        uint32_t size;
-        uint16_t reserved1;
-        uint16_t reserved2;
-        uint32_t offset;
-
-        // We define the struct size because sizeof(Header) might return a wrong result because of structure padding.
-        // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field).
-        static const std::size_t header_size =
-            sizeof(uint16_t) + // type
-            sizeof(uint32_t) + // size
-            sizeof(uint16_t) + // reserved1
-            sizeof(uint16_t) + // reserved2
-            sizeof(uint32_t);  // offset
-    };
-
-    struct DIB {
-        uint32_t size;
-        int32_t width;
-        int32_t height;
-        uint16_t planes;
-        uint16_t bits_per_pixel;
-        uint32_t compression;
-        uint32_t image_size;
-        int32_t x_resolution;
-        int32_t y_resolution;
-        uint32_t nb_colors;
-        uint32_t nb_important_colors;
-
-        // We define the struct size because sizeof(DIB) might return a wrong result because of structure padding.
-        // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field).
-        static const std::size_t dib_size =
-            sizeof(uint32_t) + // size
-            sizeof(int32_t) +  // width
-            sizeof(int32_t) +  // height
-            sizeof(uint16_t) + // planes
-            sizeof(uint16_t) + // bits_per_pixel
-            sizeof(uint32_t) + // compression
-            sizeof(uint32_t) + // image_size
-            sizeof(int32_t) +  // x_resolution
-            sizeof(int32_t) +  // y_resolution
-            sizeof(uint32_t) + // nb_colors
-            sizeof(uint32_t);  // nb_important_colors
-    };
-
-    static const std::size_t mBytesPerPixel = 4;
-
-public:
-    static void Save(aiTexture* texture, IOStream* file);
-
-protected:
-    static void WriteHeader(Header& header, IOStream* file);
-    static void WriteDIB(DIB& dib, IOStream* file);
-    static void WriteData(aiTexture* texture, IOStream* file);
-};
-
-}
-
-#endif // AI_BITMAP_H_INC

+ 0 - 0
Engine/lib/assimp/code/BlenderBMesh.cpp → Engine/lib/assimp/code/Blender/BlenderBMesh.cpp


+ 0 - 0
Engine/lib/assimp/code/BlenderBMesh.h → Engine/lib/assimp/code/Blender/BlenderBMesh.h


+ 0 - 0
Engine/lib/assimp/code/BlenderCustomData.cpp → Engine/lib/assimp/code/Blender/BlenderCustomData.cpp


+ 0 - 0
Engine/lib/assimp/code/BlenderCustomData.h → Engine/lib/assimp/code/Blender/BlenderCustomData.h


+ 0 - 0
Engine/lib/assimp/code/BlenderDNA.cpp → Engine/lib/assimp/code/Blender/BlenderDNA.cpp


+ 1 - 1
Engine/lib/assimp/code/BlenderDNA.h → Engine/lib/assimp/code/Blender/BlenderDNA.h

@@ -416,7 +416,7 @@ template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
     void operator ()(T& /*out*/,const char* = "") {
         // obviously, it is crucial that _DefaultInitializer is used
         // only from within a catch clause.
-        throw;
+        throw DeadlyImportError("Constructing BlenderDNA Structure encountered an error");
     }
 };
 

+ 0 - 0
Engine/lib/assimp/code/BlenderDNA.inl → Engine/lib/assimp/code/Blender/BlenderDNA.inl


+ 0 - 0
Engine/lib/assimp/code/BlenderIntermediate.h → Engine/lib/assimp/code/Blender/BlenderIntermediate.h


+ 27 - 0
Engine/lib/assimp/code/BlenderLoader.cpp → Engine/lib/assimp/code/Blender/BlenderLoader.cpp

@@ -1225,6 +1225,16 @@ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, c
         case Lamp::Type_Local:
             out->mType = aiLightSource_POINT;
             break;
+        case Lamp::Type_Spot:
+            out->mType = aiLightSource_SPOT;
+
+            // blender orients directional lights as facing toward -z
+            out->mDirection = aiVector3D(0.f, 0.f, -1.f);
+            out->mUp = aiVector3D(0.f, 1.f, 0.f);
+
+            out->mAngleInnerCone = lamp->spotsize * (1.0f - lamp->spotblend);
+            out->mAngleOuterCone = lamp->spotsize;
+            break;
         case Lamp::Type_Sun:
             out->mType = aiLightSource_DIRECTIONAL;
 
@@ -1255,6 +1265,23 @@ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, c
     out->mColorAmbient = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorSpecular = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorDiffuse = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
+
+    // If default values are supplied, compute the coefficients from light's max distance
+    // Read this: https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/
+    //
+    if (lamp->constant_coefficient == 1.0f && lamp->linear_coefficient == 0.0f && lamp->quadratic_coefficient == 0.0f && lamp->dist > 0.0f)
+    {
+        out->mAttenuationConstant = 1.0f;
+        out->mAttenuationLinear = 2.0f / lamp->dist;
+        out->mAttenuationQuadratic = 1.0f / (lamp->dist * lamp->dist);
+    }
+    else
+    {
+        out->mAttenuationConstant = lamp->constant_coefficient;
+        out->mAttenuationLinear = lamp->linear_coefficient;
+        out->mAttenuationQuadratic = lamp->quadratic_coefficient;
+    }
+
     return out.release();
 }
 

+ 0 - 0
Engine/lib/assimp/code/BlenderLoader.h → Engine/lib/assimp/code/Blender/BlenderLoader.h


+ 0 - 0
Engine/lib/assimp/code/BlenderModifier.cpp → Engine/lib/assimp/code/Blender/BlenderModifier.cpp


+ 0 - 0
Engine/lib/assimp/code/BlenderModifier.h → Engine/lib/assimp/code/Blender/BlenderModifier.h


+ 4 - 1
Engine/lib/assimp/code/BlenderScene.cpp → Engine/lib/assimp/code/Blender/BlenderScene.cpp

@@ -211,9 +211,12 @@ template <> void Structure :: Convert<Lamp> (
     ReadField<ErrorPolicy_Warn>(dest.b,"b",db);
     ReadField<ErrorPolicy_Warn>(dest.k,"k",db);
     ReadField<ErrorPolicy_Igno>(dest.energy,"energy",db);
-    ReadField<ErrorPolicy_Igno>(dest.dist,"dist",db);
+    ReadField<ErrorPolicy_Warn>(dest.dist,"dist",db);
     ReadField<ErrorPolicy_Igno>(dest.spotsize,"spotsize",db);
     ReadField<ErrorPolicy_Igno>(dest.spotblend,"spotblend",db);
+    ReadField<ErrorPolicy_Warn>(dest.constant_coefficient, "coeff_const", db);
+    ReadField<ErrorPolicy_Warn>(dest.linear_coefficient, "coeff_lin", db);
+    ReadField<ErrorPolicy_Warn>(dest.quadratic_coefficient, "coeff_quad", db);
     ReadField<ErrorPolicy_Igno>(dest.att1,"att1",db);
     ReadField<ErrorPolicy_Igno>(dest.att2,"att2",db);
     ReadField<ErrorPolicy_Igno>(temp,"falloff_type",db);

+ 4 - 0
Engine/lib/assimp/code/BlenderScene.h → Engine/lib/assimp/code/Blender/BlenderScene.h

@@ -538,6 +538,10 @@ struct Lamp : ElemBase {
       float energy, dist, spotsize, spotblend;
       //float haint;
 
+      float constant_coefficient;
+      float linear_coefficient;
+      float quadratic_coefficient;
+
       float att1, att2;
       //struct CurveMapping *curfalloff;
       FalloffType falloff_type;

+ 0 - 0
Engine/lib/assimp/code/BlenderSceneGen.h → Engine/lib/assimp/code/Blender/BlenderSceneGen.h


+ 0 - 0
Engine/lib/assimp/code/BlenderTessellator.cpp → Engine/lib/assimp/code/Blender/BlenderTessellator.cpp


+ 5 - 1
Engine/lib/assimp/code/BlenderTessellator.h → Engine/lib/assimp/code/Blender/BlenderTessellator.h

@@ -144,7 +144,11 @@ namespace Assimp
 
 #if ASSIMP_BLEND_WITH_POLY_2_TRI
 
-#include "../contrib/poly2tri/poly2tri/poly2tri.h"
+#ifdef ASSIMP_USE_HUNTER
+#  include <poly2tri/poly2tri.h>
+#else
+#  include "../contrib/poly2tri/poly2tri/poly2tri.h"
+#endif
 
 namespace Assimp
 {

+ 0 - 337
Engine/lib/assimp/code/BlobIOSystem.h

@@ -1,337 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2017, assimp team
-
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the following
-conditions are met:
-
-* Redistributions of source code must retain the above
-  copyright notice, this list of conditions and the
-  following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the
-  following disclaimer in the documentation and/or other
-  materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-  contributors may be used to endorse or promote products
-  derived from this software without specific prior
-  written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------
-*/
-
-/** @file Provides cheat implementations for IOSystem and IOStream to
- *  redirect exporter output to a blob chain.*/
-
-#ifndef AI_BLOBIOSYSTEM_H_INCLUDED
-#define AI_BLOBIOSYSTEM_H_INCLUDED
-
-#include <assimp/IOStream.hpp>
-#include <assimp/cexport.h>
-#include <assimp/IOSystem.hpp>
-#include <assimp/DefaultLogger.hpp>
-#include <stdint.h>
-#include <set>
-#include <vector>
-
-namespace Assimp    {
-    class BlobIOSystem;
-
-// --------------------------------------------------------------------------------------------
-/** Redirect IOStream to a blob */
-// --------------------------------------------------------------------------------------------
-class BlobIOStream : public IOStream
-{
-public:
-
-    BlobIOStream(BlobIOSystem* creator, const std::string& file, size_t initial = 4096)
-        : buffer()
-        , cur_size()
-        , file_size()
-        , cursor()
-        , initial(initial)
-        , file(file)
-        , creator(creator)
-    {
-    }
-
-
-    virtual ~BlobIOStream();
-
-public:
-
-    // -------------------------------------------------------------------
-    aiExportDataBlob* GetBlob()
-    {
-        aiExportDataBlob* blob = new aiExportDataBlob();
-        blob->size = file_size;
-        blob->data = buffer;
-
-        buffer = NULL;
-
-        return blob;
-    }
-
-
-public:
-
-
-    // -------------------------------------------------------------------
-    virtual size_t Read( void *,
-        size_t,
-        size_t )
-    {
-        return 0;
-    }
-
-    // -------------------------------------------------------------------
-    virtual size_t Write(const void* pvBuffer,
-        size_t pSize,
-        size_t pCount)
-    {
-        pSize *= pCount;
-        if (cursor + pSize > cur_size) {
-            Grow(cursor + pSize);
-        }
-
-        memcpy(buffer+cursor, pvBuffer, pSize);
-        cursor += pSize;
-
-        file_size = std::max(file_size,cursor);
-        return pCount;
-    }
-
-    // -------------------------------------------------------------------
-    virtual aiReturn Seek(size_t pOffset,
-        aiOrigin pOrigin)
-    {
-        switch(pOrigin)
-        {
-        case aiOrigin_CUR:
-            cursor += pOffset;
-            break;
-
-        case aiOrigin_END:
-            cursor = file_size - pOffset;
-            break;
-
-        case aiOrigin_SET:
-            cursor = pOffset;
-            break;
-
-        default:
-            return AI_FAILURE;
-        }
-
-        if (cursor > file_size) {
-            Grow(cursor);
-        }
-
-        file_size = std::max(cursor,file_size);
-        return AI_SUCCESS;
-    }
-
-    // -------------------------------------------------------------------
-    virtual size_t Tell() const
-    {
-        return cursor;
-    }
-
-    // -------------------------------------------------------------------
-    virtual size_t FileSize() const
-    {
-        return file_size;
-    }
-
-    // -------------------------------------------------------------------
-    virtual void Flush()
-    {
-        // ignore
-    }
-
-
-
-private:
-
-    // -------------------------------------------------------------------
-    void Grow(size_t need = 0)
-    {
-        // 1.5 and phi are very heap-friendly growth factors (the first
-        // allows for frequent re-use of heap blocks, the second
-        // forms a fibonacci sequence with similar characteristics -
-        // since this heavily depends on the heap implementation
-        // and other factors as well, i'll just go with 1.5 since
-        // it is quicker to compute).
-        size_t new_size = std::max(initial, std::max( need, cur_size+(cur_size>>1) ));
-
-        const uint8_t* const old = buffer;
-        buffer = new uint8_t[new_size];
-
-        if (old) {
-            memcpy(buffer,old,cur_size);
-            delete[] old;
-        }
-
-        cur_size = new_size;
-    }
-
-private:
-
-    uint8_t* buffer;
-    size_t cur_size,file_size, cursor, initial;
-
-    const std::string file;
-    BlobIOSystem* const creator;
-};
-
-
-#define AI_BLOBIO_MAGIC "$blobfile"
-
-// --------------------------------------------------------------------------------------------
-/** Redirect IOSystem to a blob */
-// --------------------------------------------------------------------------------------------
-class BlobIOSystem : public IOSystem
-{
-
-    friend class BlobIOStream;
-    typedef std::pair<std::string, aiExportDataBlob*> BlobEntry;
-
-public:
-
-    BlobIOSystem()
-    {
-    }
-
-    virtual ~BlobIOSystem()
-    {
-        for(BlobEntry& blobby : blobs) {
-            delete blobby.second;
-        }
-    }
-
-public:
-
-    // -------------------------------------------------------------------
-    const char* GetMagicFileName() const
-    {
-        return AI_BLOBIO_MAGIC;
-    }
-
-
-    // -------------------------------------------------------------------
-    aiExportDataBlob* GetBlobChain()
-    {
-        // one must be the master
-        aiExportDataBlob* master = NULL, *cur;
-        for(const BlobEntry& blobby : blobs) {
-            if (blobby.first == AI_BLOBIO_MAGIC) {
-                master = blobby.second;
-                break;
-            }
-        }
-        if (!master) {
-            DefaultLogger::get()->error("BlobIOSystem: no data written or master file was not closed properly.");
-            return NULL;
-        }
-
-        master->name.Set("");
-
-        cur = master;
-        for(const BlobEntry& blobby : blobs) {
-            if (blobby.second == master) {
-                continue;
-            }
-
-            cur->next = blobby.second;
-            cur = cur->next;
-
-            // extract the file extension from the file written
-            const std::string::size_type s = blobby.first.find_first_of('.');
-            cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s+1));
-        }
-
-        // give up blob ownership
-        blobs.clear();
-        return master;
-    }
-
-public:
-
-    // -------------------------------------------------------------------
-    virtual bool Exists( const char* pFile) const {
-        return created.find(std::string(pFile)) != created.end();
-    }
-
-
-    // -------------------------------------------------------------------
-    virtual char getOsSeparator() const {
-        return '/';
-    }
-
-
-    // -------------------------------------------------------------------
-    virtual IOStream* Open(const char* pFile,
-        const char* pMode)
-    {
-        if (pMode[0] != 'w') {
-            return NULL;
-        }
-
-        created.insert(std::string(pFile));
-        return new BlobIOStream(this,std::string(pFile));
-    }
-
-    // -------------------------------------------------------------------
-    virtual void Close( IOStream* pFile)
-    {
-        delete pFile;
-    }
-
-private:
-
-    // -------------------------------------------------------------------
-    void OnDestruct(const std::string& filename, BlobIOStream* child)
-    {
-        // we don't know in which the files are closed, so we
-        // can't reliably say that the first must be the master
-        // file ...
-        blobs.push_back( BlobEntry(filename,child->GetBlob()) );
-    }
-
-private:
-    std::set<std::string> created;
-    std::vector< BlobEntry > blobs;
-};
-
-
-// --------------------------------------------------------------------------------------------
-BlobIOStream :: ~BlobIOStream()
-{
-    creator->OnDestruct(file,this);
-    delete[] buffer;
-}
-
-
-} // end Assimp
-
-#endif

+ 0 - 287
Engine/lib/assimp/code/ByteSwapper.h

@@ -1,287 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2017, assimp team
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the
-following conditions are met:
-
-* Redistributions of source code must retain the above
-  copyright notice, this list of conditions and the
-  following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the
-  following disclaimer in the documentation and/or other
-  materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-  contributors may be used to endorse or promote products
-  derived from this software without specific prior
-  written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----------------------------------------------------------------------
-*/
-
-/** @file Helper class tp perform various byte oder swappings
-   (e.g. little to big endian) */
-#ifndef AI_BYTESWAPPER_H_INC
-#define AI_BYTESWAPPER_H_INC
-
-#include <assimp/ai_assert.h>
-#include <assimp/types.h>
-#include <stdint.h>
-
-#if _MSC_VER >= 1400
-#include <stdlib.h>
-#endif
-
-namespace Assimp    {
-// --------------------------------------------------------------------------------------
-/** Defines some useful byte order swap routines.
- *
- * This is required to read big-endian model formats on little-endian machines,
- * and vice versa. Direct use of this class is DEPRECATED. Use #StreamReader instead. */
-// --------------------------------------------------------------------------------------
-class ByteSwap
-{
-    ByteSwap() {}
-
-public:
-
-    // ----------------------------------------------------------------------
-    /** Swap two bytes of data
-     *  @param[inout] _szOut A void* to save the reintcasts for the caller. */
-    static inline void Swap2(void* _szOut)
-    {
-        ai_assert(_szOut);
-
-#if _MSC_VER >= 1400
-        uint16_t* const szOut = reinterpret_cast<uint16_t*>(_szOut);
-        *szOut = _byteswap_ushort(*szOut);
-#else
-        uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut);
-        std::swap(szOut[0],szOut[1]);
-#endif
-    }
-
-    // ----------------------------------------------------------------------
-    /** Swap four bytes of data
-     *  @param[inout] _szOut A void* to save the reintcasts for the caller. */
-    static inline void Swap4(void* _szOut)
-    {
-        ai_assert(_szOut);
-
-#if _MSC_VER >= 1400
-        uint32_t* const szOut = reinterpret_cast<uint32_t*>(_szOut);
-        *szOut = _byteswap_ulong(*szOut);
-#else
-        uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut);
-        std::swap(szOut[0],szOut[3]);
-        std::swap(szOut[1],szOut[2]);
-#endif
-    }
-
-    // ----------------------------------------------------------------------
-    /** Swap eight bytes of data
-     *  @param[inout] _szOut A void* to save the reintcasts for the caller. */
-    static inline void Swap8(void* _szOut)
-    {
-    ai_assert(_szOut);
-
-#if _MSC_VER >= 1400
-        uint64_t* const szOut = reinterpret_cast<uint64_t*>(_szOut);
-        *szOut = _byteswap_uint64(*szOut);
-#else
-        uint8_t* const szOut = reinterpret_cast<uint8_t*>(_szOut);
-        std::swap(szOut[0],szOut[7]);
-        std::swap(szOut[1],szOut[6]);
-        std::swap(szOut[2],szOut[5]);
-        std::swap(szOut[3],szOut[4]);
-#endif
-    }
-
-    // ----------------------------------------------------------------------
-    /** ByteSwap a float. Not a joke.
-     *  @param[inout] fOut ehm. .. */
-    static inline void Swap(float* fOut) {
-        Swap4(fOut);
-    }
-
-    // ----------------------------------------------------------------------
-    /** ByteSwap a double. Not a joke.
-     *  @param[inout] fOut ehm. .. */
-    static inline void Swap(double* fOut) {
-        Swap8(fOut);
-    }
-
-
-    // ----------------------------------------------------------------------
-    /** ByteSwap an int16t. Not a joke.
-     *  @param[inout] fOut ehm. .. */
-    static inline void Swap(int16_t* fOut) {
-        Swap2(fOut);
-    }
-
-    static inline void Swap(uint16_t* fOut) {
-        Swap2(fOut);
-    }
-
-    // ----------------------------------------------------------------------
-    /** ByteSwap an int32t. Not a joke.
-     *  @param[inout] fOut ehm. .. */
-    static inline void Swap(int32_t* fOut){
-        Swap4(fOut);
-    }
-
-    static inline void Swap(uint32_t* fOut){
-        Swap4(fOut);
-    }
-
-    // ----------------------------------------------------------------------
-    /** ByteSwap an int64t. Not a joke.
-     *  @param[inout] fOut ehm. .. */
-    static inline void Swap(int64_t* fOut) {
-        Swap8(fOut);
-    }
-
-    static inline void Swap(uint64_t* fOut) {
-        Swap8(fOut);
-    }
-
-    // ----------------------------------------------------------------------
-    //! Templatized ByteSwap
-    //! \returns param tOut as swapped
-    template<typename Type>
-    static inline Type Swapped(Type tOut)
-    {
-        return _swapper<Type,sizeof(Type)>()(tOut);
-    }
-
-private:
-
-    template <typename T, size_t size> struct _swapper;
-};
-
-template <typename T> struct ByteSwap::_swapper<T,2> {
-    T operator() (T tOut) {
-        Swap2(&tOut);
-        return tOut;
-    }
-};
-
-template <typename T> struct ByteSwap::_swapper<T,4> {
-    T operator() (T tOut) {
-        Swap4(&tOut);
-        return tOut;
-    }
-};
-
-template <typename T> struct ByteSwap::_swapper<T,8> {
-    T operator() (T tOut) {
-        Swap8(&tOut);
-        return tOut;
-    }
-};
-
-
-// --------------------------------------------------------------------------------------
-// ByteSwap macros for BigEndian/LittleEndian support
-// --------------------------------------------------------------------------------------
-#if (defined AI_BUILD_BIG_ENDIAN)
-#   define AI_LE(t) (t)
-#   define AI_BE(t) ByteSwap::Swapped(t)
-#   define AI_LSWAP2(p)
-#   define AI_LSWAP4(p)
-#   define AI_LSWAP8(p)
-#   define AI_LSWAP2P(p)
-#   define AI_LSWAP4P(p)
-#   define AI_LSWAP8P(p)
-#   define LE_NCONST const
-#   define AI_SWAP2(p) ByteSwap::Swap2(&(p))
-#   define AI_SWAP4(p) ByteSwap::Swap4(&(p))
-#   define AI_SWAP8(p) ByteSwap::Swap8(&(p))
-#   define AI_SWAP2P(p) ByteSwap::Swap2((p))
-#   define AI_SWAP4P(p) ByteSwap::Swap4((p))
-#   define AI_SWAP8P(p) ByteSwap::Swap8((p))
-#   define BE_NCONST
-#else
-#   define AI_BE(t) (t)
-#   define AI_LE(t) ByteSwap::Swapped(t)
-#   define AI_SWAP2(p)
-#   define AI_SWAP4(p)
-#   define AI_SWAP8(p)
-#   define AI_SWAP2P(p)
-#   define AI_SWAP4P(p)
-#   define AI_SWAP8P(p)
-#   define BE_NCONST const
-#   define AI_LSWAP2(p)     ByteSwap::Swap2(&(p))
-#   define AI_LSWAP4(p)     ByteSwap::Swap4(&(p))
-#   define AI_LSWAP8(p)     ByteSwap::Swap8(&(p))
-#   define AI_LSWAP2P(p)    ByteSwap::Swap2((p))
-#   define AI_LSWAP4P(p)    ByteSwap::Swap4((p))
-#   define AI_LSWAP8P(p)    ByteSwap::Swap8((p))
-#   define LE_NCONST
-#endif
-
-
-namespace Intern {
-
-// --------------------------------------------------------------------------------------------
-template <typename T, bool doit>
-struct ByteSwapper  {
-    void operator() (T* inout) {
-        ByteSwap::Swap(inout);
-    }
-};
-
-template <typename T>
-struct ByteSwapper<T,false> {
-    void operator() (T*) {
-    }
-};
-
-// --------------------------------------------------------------------------------------------
-template <bool SwapEndianess, typename T, bool RuntimeSwitch>
-struct Getter {
-    void operator() (T* inout, bool le) {
-#ifdef AI_BUILD_BIG_ENDIAN
-        le =  le;
-#else
-        le =  !le;
-#endif
-        if (le) {
-            ByteSwapper<T,(sizeof(T)>1?true:false)> () (inout);
-        }
-        else ByteSwapper<T,false> () (inout);
-    }
-};
-
-template <bool SwapEndianess, typename T>
-struct Getter<SwapEndianess,T,false> {
-
-    void operator() (T* inout, bool /*le*/) {
-        // static branch
-        ByteSwapper<T,(SwapEndianess && sizeof(T)>1)> () (inout);
-    }
-};
-} // end Intern
-} // end Assimp
-
-#endif //!! AI_BYTESWAPPER_H_INC

+ 63 - 92
Engine/lib/assimp/code/C4DImporter.cpp → Engine/lib/assimp/code/C4D/C4DImporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2012, assimp team
+Copyright (c) 2006-2019, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -68,8 +68,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace melange;
 
 // overload this function and fill in your own unique data
-void GetWriterInfo(int &id, String &appname)
-{
+void GetWriterInfo(int &id, String &appname) {
     id = 2424226;
     appname = "Open Asset Import Library";
 }
@@ -78,7 +77,10 @@ using namespace Assimp;
 using namespace Assimp::Formatter;
 
 namespace Assimp {
-    template<> const std::string LogFunctions<C4DImporter>::log_prefix = "C4D: ";
+    template<> const char* LogFunctions<C4DImporter>::Prefix() {
+        static auto prefix = "C4D: ";
+        return prefix;
+    }
 }
 
 static const aiImporterDesc desc = {
@@ -97,47 +99,44 @@ static const aiImporterDesc desc = {
 
 // ------------------------------------------------------------------------------------------------
 C4DImporter::C4DImporter()
-{}
+: BaseImporter() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
-C4DImporter::~C4DImporter()
-{}
+C4DImporter::~C4DImporter() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
-bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
-{
+bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
     const std::string& extension = GetExtension(pFile);
     if (extension == "c4d") {
         return true;
-    }
-
-    else if ((!extension.length() || checkSig) && pIOHandler)   {
+    } else if ((!extension.length() || checkSig) && pIOHandler)   {
         // TODO
     }
+
     return false;
 }
 
 // ------------------------------------------------------------------------------------------------
-const aiImporterDesc* C4DImporter::GetInfo () const
-{
+const aiImporterDesc* C4DImporter::GetInfo () const {
     return &desc;
 }
 
 // ------------------------------------------------------------------------------------------------
-void C4DImporter::SetupProperties(const Importer* /*pImp*/)
-{
+void C4DImporter::SetupProperties(const Importer* /*pImp*/) {
     // nothing to be done for the moment
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
-void C4DImporter::InternReadFile( const std::string& pFile,
-    aiScene* pScene, IOSystem* pIOHandler)
-{
+void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
 
-    if( file.get() == NULL) {
+    if( file.get() == nullptr ) {
         ThrowException("failed to open file " + pFile);
     }
 
@@ -151,7 +150,7 @@ void C4DImporter::InternReadFile( const std::string& pFile,
 
     // open document first
     BaseDocument* doc = LoadDocument(f, SCENEFILTER_OBJECTS | SCENEFILTER_MATERIALS);
-    if(doc == NULL) {
+    if(doc == nullptr ) {
         ThrowException("failed to read document " + pFile);
     }
 
@@ -160,11 +159,10 @@ void C4DImporter::InternReadFile( const std::string& pFile,
     // first convert all materials
     ReadMaterials(doc->GetFirstMaterial());
 
-    // process C4D scenegraph recursively
+    // process C4D scene-graph recursively
     try {
         RecurseHierarchy(doc->GetFirstObject(), pScene->mRootNode);
-    }
-    catch(...) {
+    } catch(...) {
         for(aiMesh* mesh : meshes) {
             delete mesh;
         }
@@ -201,8 +199,7 @@ void C4DImporter::InternReadFile( const std::string& pFile,
 
 
 // ------------------------------------------------------------------------------------------------
-bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
-{
+bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader) {
     // based on Melange sample code (C4DImportExport.cpp)
     while(shader) {
         if(shader->GetType() == Xlayer) {
@@ -220,15 +217,12 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
             // Ignore the actual layer blending - models for real-time rendering should not
             // use them in a non-trivial way. Just try to find textures that we can apply
             // to the model.
-            while (lsl)
-            {
-                if (lsl->GetType() == TypeFolder)
-                {
+            while (lsl) {
+                if (lsl->GetType() == TypeFolder) {
                     BlendFolder* const folder = dynamic_cast<BlendFolder*>(lsl);
                     LayerShaderLayer *subLsl = dynamic_cast<LayerShaderLayer*>(folder->m_Children.GetObject(0));
 
-                    while (subLsl)
-                    {
+                    while (subLsl) {
                         if (subLsl->GetType() == TypeShader) {
                             BlendShader* const shader = dynamic_cast<BlendShader*>(subLsl);
                             if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
@@ -238,8 +232,7 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
 
                         subLsl = subLsl->GetNext();
                     }
-                }
-                else if (lsl->GetType() == TypeShader) {
+                } else if (lsl->GetType() == TypeShader) {
                     BlendShader* const shader = dynamic_cast<BlendShader*>(lsl);
                     if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
                         return true;
@@ -248,33 +241,27 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
 
                 lsl = lsl->GetNext();
             }
-        }
-        else if ( shader->GetType() == Xbitmap )
-        {
+        } else if ( shader->GetType() == Xbitmap ) {
             aiString path;
             shader->GetFileName().GetString().GetCString(path.data, MAXLEN-1);
             path.length = ::strlen(path.data);
             out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0));
             return true;
-        }
-        else {
+        } else {
             LogWarn("ignoring shader type: " + std::string(GetObjectTypeName(shader->GetType())));
         }
         shader = shader->GetNext();
     }
+
     return false;
 }
 
-
 // ------------------------------------------------------------------------------------------------
-void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
-{
+void C4DImporter::ReadMaterials(melange::BaseMaterial* mat) {
     // based on Melange sample code
-    while (mat)
-    {
+    while (mat) {
         const String& name = mat->GetName();
-        if (mat->GetType() == Mmaterial)
-        {
+        if (mat->GetType() == Mmaterial) {
             aiMaterial* out = new aiMaterial();
             material_mapping[mat] = static_cast<unsigned int>(materials.size());
             materials.push_back(out);
@@ -286,8 +273,7 @@ void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
 
             Material& m = dynamic_cast<Material&>(*mat);
 
-            if (m.GetChannelState(CHANNEL_COLOR))
-            {
+            if (m.GetChannelState(CHANNEL_COLOR)) {
                 GeData data;
                 mat->GetParameter(MATERIAL_COLOR_COLOR, data);
                 Vector color = data.GetVector();
@@ -307,9 +293,7 @@ void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
             if(shader) {
                 ReadShader(out, shader);
             }
-        }
-        else
-        {
+        } else {
             LogWarn("ignoring plugin material: " + std::string(GetObjectTypeName(mat->GetType())));
         }
         mat = mat->GetNext();
@@ -317,14 +301,12 @@ void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
 }
 
 // ------------------------------------------------------------------------------------------------
-void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
-{
-    ai_assert(parent != NULL);
+void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent) {
+    ai_assert(parent != nullptr );
     std::vector<aiNode*> nodes;
 
     // based on Melange sample code
-    while (object)
-    {
+    while (object) {
         const String& name = object->GetName();
         const LONG type = object->GetType();
         const Matrix& ml = object->GetMl();
@@ -356,26 +338,20 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
         nodes.push_back(nd);
 
         GeData data;
-        if (type == Ocamera)
-        {
+        if (type == Ocamera) {
             object->GetParameter(CAMERAOBJECT_FOV, data);
             // TODO: read camera
-        }
-        else if (type == Olight)
-        {
+        } else if (type == Olight) {
             // TODO: read light
-        }
-        else if (type == Opolygon)
-        {
+        } else if (type == Opolygon) {
             aiMesh* const mesh = ReadMesh(object);
-            if(mesh != NULL) {
+            if(mesh != nullptr) {
                 nd->mNumMeshes = 1;
                 nd->mMeshes = new unsigned int[1];
                 nd->mMeshes[0] = static_cast<unsigned int>(meshes.size());
                 meshes.push_back(mesh);
             }
-        }
-        else {
+        } else {
             LogWarn("ignoring object: " + std::string(GetObjectTypeName(type)));
         }
 
@@ -389,28 +365,27 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
     std::copy(nodes.begin(), nodes.end(), parent->mChildren);
 }
 
-
 // ------------------------------------------------------------------------------------------------
-aiMesh* C4DImporter::ReadMesh(BaseObject* object)
-{
-    ai_assert(object != NULL && object->GetType() == Opolygon);
+aiMesh* C4DImporter::ReadMesh(BaseObject* object) {
+    ai_assert(object != nullptr);
+    ai_assert( object->GetType() == Opolygon );
 
     // based on Melange sample code
     PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
-    ai_assert(polyObject != NULL);
+    ai_assert(polyObject != nullptr);
 
     const LONG pointCount = polyObject->GetPointCount();
     const LONG polyCount = polyObject->GetPolygonCount();
     if(!polyObject || !pointCount) {
         LogWarn("ignoring mesh with zero vertices or faces");
-        return NULL;
+        return nullptr;
     }
 
     const Vector* points = polyObject->GetPointR();
-    ai_assert(points != NULL);
+    ai_assert(points != nullptr);
 
     const CPolygon* polys = polyObject->GetPolygonR();
-    ai_assert(polys != NULL);
+    ai_assert(polys != nullptr);
 
     std::unique_ptr<aiMesh> mesh(new aiMesh());
     mesh->mNumFaces = static_cast<unsigned int>(polyCount);
@@ -443,14 +418,14 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
 
     // check if there are normals, tangents or UVW coordinates
     BaseTag* tag = object->GetTag(Tnormal);
-    NormalTag* normals_src = NULL;
+    NormalTag* normals_src = nullptr;
     if(tag) {
         normals_src = dynamic_cast<NormalTag*>(tag);
         normals = mesh->mNormals = new aiVector3D[mesh->mNumVertices]();
     }
 
     tag = object->GetTag(Ttangent);
-    TangentTag* tangents_src = NULL;
+    TangentTag* tangents_src = nullptr;
     if(tag) {
         tangents_src = dynamic_cast<TangentTag*>(tag);
         tangents = mesh->mTangents = new aiVector3D[mesh->mNumVertices]();
@@ -458,15 +433,14 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
     }
 
     tag = object->GetTag(Tuvw);
-    UVWTag* uvs_src = NULL;
+    UVWTag* uvs_src = nullptr;
     if(tag) {
         uvs_src = dynamic_cast<UVWTag*>(tag);
         uvs = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]();
     }
 
     // copy vertices and extra channels over and populate faces
-    for (LONG i = 0; i < polyCount; ++i, ++face)
-    {
+    for (LONG i = 0; i < polyCount; ++i, ++face) {
         ai_assert(polys[i].a < pointCount && polys[i].a >= 0);
         const Vector& pointA = points[polys[i].a];
         verts->x = pointA.x;
@@ -489,8 +463,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
         ++verts;
 
         // TODO: do we also need to handle lines or points with similar checks?
-        if (polys[i].c != polys[i].d)
-        {
+        if (polys[i].c != polys[i].d) {
             ai_assert(polys[i].d < pointCount && polys[i].d >= 0);
 
             face->mNumIndices = 4;
@@ -500,8 +473,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
             verts->y = pointD.y;
             verts->z = pointD.z;
             ++verts;
-        }
-        else {
+        } else {
             face->mNumIndices = 3;
         }
         face->mIndices = new unsigned int[face->mNumIndices];
@@ -513,8 +485,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
         if (normals_src) {
             if(i >= normals_src->GetDataCount()) {
                 LogError("unexpected number of normals, ignoring");
-            }
-            else {
+            } else {
                 ConstNormalHandle normal_handle = normals_src->GetDataAddressR();
                 NormalStruct nor;
                 NormalTag::Get(normal_handle, i, nor);
@@ -616,26 +587,25 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
     }
 
     mesh->mMaterialIndex = ResolveMaterial(polyObject);
+
     return mesh.release();
 }
 
-
 // ------------------------------------------------------------------------------------------------
-unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj)
-{
-    ai_assert(obj != NULL);
+unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj) {
+    ai_assert(obj != nullptr);
 
     const unsigned int mat_count = static_cast<unsigned int>(materials.size());
 
     BaseTag* tag = obj->GetTag(Ttexture);
-    if(tag == NULL) {
+    if(tag == nullptr) {
         return mat_count;
     }
 
     TextureTag& ttag = dynamic_cast<TextureTag&>(*tag);
 
     BaseMaterial* const mat = ttag.GetMaterial();
-    ai_assert(mat != NULL);
+    ai_assert(mat != nullptr);
 
     const MaterialMap::const_iterator it = material_mapping.find(mat);
     if(it == material_mapping.end()) {
@@ -643,6 +613,7 @@ unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj)
     }
 
     ai_assert((*it).second < mat_count);
+
     return (*it).second;
 }
 

+ 6 - 12
Engine/lib/assimp/code/C4DImporter.h → Engine/lib/assimp/code/C4D/C4DImporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2012, assimp team
+Copyright (c) 2006-2019, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -48,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/LogAux.h>
 
 #include <map>
+
+// Forward declarations
 struct aiNode;
 struct aiMesh;
 struct aiMaterial;
@@ -61,8 +63,7 @@ namespace melange {
     class BaseShader;
 }
 
-namespace Assimp    {
-
+namespace Assimp  {
     // TinyFormatter.h
     namespace Formatter {
         template <typename T,typename TR, typename A> class basic_formatter;
@@ -75,17 +76,10 @@ namespace Assimp    {
  *
  *  Note that Melange is not free software. */
 // -------------------------------------------------------------------------------------------
-class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter>
-{
+class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter> {
 public:
-
     C4DImporter();
     ~C4DImporter();
-
-
-public:
-
-    // --------------------
     bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
         bool checkSig) const;
 
@@ -119,5 +113,5 @@ private:
 }; // !class C4DImporter
 
 } // end of namespace Assimp
-#endif // INCLUDED_AI_CINEMA_4D_LOADER_H
 
+#endif // INCLUDED_AI_CINEMA_4D_LOADER_H

+ 1 - 1
Engine/lib/assimp/code/AssimpCExport.cpp → Engine/lib/assimp/code/CApi/AssimpCExport.cpp

@@ -49,7 +49,7 @@ Assimp C export interface. See Exporter.cpp for some notes.
 
 #include "CInterfaceIOWrapper.h"
 #include <assimp/SceneCombiner.h>
-#include "ScenePrivate.h"
+#include "Common/ScenePrivate.h"
 #include <assimp/Exporter.hpp>
 
 using namespace Assimp;

+ 0 - 0
Engine/lib/assimp/code/CInterfaceIOWrapper.cpp → Engine/lib/assimp/code/CApi/CInterfaceIOWrapper.cpp


+ 0 - 0
Engine/lib/assimp/code/CInterfaceIOWrapper.h → Engine/lib/assimp/code/CApi/CInterfaceIOWrapper.h


File diff suppressed because it is too large
+ 644 - 463
Engine/lib/assimp/code/CMakeLists.txt


+ 17 - 12
Engine/lib/assimp/code/COBLoader.cpp → Engine/lib/assimp/code/COB/COBLoader.cpp

@@ -45,20 +45,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #ifndef ASSIMP_BUILD_NO_COB_IMPORTER
-#include "COBLoader.h"
-#include "COBScene.h"
-#include "ConvertToLHProcess.h"
+#include "COB/COBLoader.h"
+#include "COB/COBScene.h"
+#include "PostProcessing/ConvertToLHProcess.h"
+
 #include <assimp/StreamReader.h>
 #include <assimp/ParsingUtils.h>
 #include <assimp/fast_atof.h>
 #include <assimp/LineSplitter.h>
 #include <assimp/TinyFormatter.h>
-#include <memory>
 #include <assimp/IOSystem.hpp>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/scene.h>
 #include <assimp/importerdesc.h>
 
+#include <memory>
+
 using namespace Assimp;
 using namespace Assimp::COB;
 using namespace Assimp::Formatter;
@@ -144,7 +146,7 @@ void COBImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
     // check header
     char head[32];
     stream->CopyAndAdvance(head,32);
-    if (strncmp(head,"Caligari ",9)) {
+    if (strncmp(head,"Caligari ",9) != 0) {
         ThrowException("Could not found magic id: `Caligari`");
     }
 
@@ -656,14 +658,14 @@ void COBImporter::ReadLght_Ascii(Scene& out, LineSplitter& splitter, const Chunk
     ReadFloat3Tuple_Ascii(msh.color ,&rgb);
 
     SkipSpaces(&rgb);
-    if (strncmp(rgb,"cone angle",10)) {
+    if (strncmp(rgb,"cone angle",10) != 0) {
         ASSIMP_LOG_WARN_F( "Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id );
     }
     SkipSpaces(rgb+10,&rgb);
     msh.angle = fast_atof(&rgb);
 
     SkipSpaces(&rgb);
-    if (strncmp(rgb,"inner angle",11)) {
+    if (strncmp(rgb,"inner angle",11) != 0) {
         ASSIMP_LOG_WARN_F( "Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id);
     }
     SkipSpaces(rgb+11,&rgb);
@@ -896,6 +898,7 @@ public:
     : nfo(nfo)
     , reader(reader)
     , cur(reader.GetCurrentPos()) {
+        // empty
     }
 
     ~chunk_guard() {
@@ -903,7 +906,7 @@ public:
         if(nfo.size != static_cast<unsigned int>(-1)) {
             try {
                 reader.IncPtr( static_cast< int >( nfo.size ) - reader.GetCurrentPos() + cur );
-            } catch ( DeadlyImportError e ) {
+            } catch (const DeadlyImportError& ) {
                 // out of limit so correct the value
                 reader.IncPtr( reader.GetReadLimit() );
             }
@@ -911,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()
@@ -1214,7 +1219,7 @@ void COBImporter::ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const
 
     const chunk_guard cn(nfo,reader);
 
-    out.nodes.push_back(std::shared_ptr<Group>(new Group()));
+    out.nodes.push_back(std::make_shared<Group>());
     Group& msh = (Group&)(*out.nodes.back().get());
     msh = nfo;
 

+ 0 - 0
Engine/lib/assimp/code/COBLoader.h → Engine/lib/assimp/code/COB/COBLoader.h


+ 0 - 0
Engine/lib/assimp/code/COBScene.h → Engine/lib/assimp/code/COB/COBScene.h


+ 0 - 0
Engine/lib/assimp/code/CSMLoader.cpp → Engine/lib/assimp/code/CSM/CSMLoader.cpp


+ 0 - 0
Engine/lib/assimp/code/CSMLoader.h → Engine/lib/assimp/code/CSM/CSMLoader.h


+ 169 - 104
Engine/lib/assimp/code/ColladaExporter.cpp → Engine/lib/assimp/code/Collada/ColladaExporter.cpp

@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "ColladaExporter.h"
 #include <assimp/Bitmap.h>
+#include <assimp/MathFunctions.h>
 #include <assimp/fast_atof.h>
 #include <assimp/SceneCombiner.h>
 #include <assimp/StringUtils.h>
@@ -64,13 +65,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 using namespace Assimp;
 
-namespace Assimp
-{
+namespace Assimp {
 
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
-void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
-{
+void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) {
     std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
     std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
 
@@ -93,15 +92,45 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p
 
 } // end of namespace Assimp
 
+// ------------------------------------------------------------------------------------------------
+// Encodes a string into a valid XML ID using the xsd:ID schema qualifications.
+static const std::string XMLIDEncode(const std::string& name) {
+    const char XML_ID_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
+    const unsigned int XML_ID_CHARS_COUNT = sizeof(XML_ID_CHARS) / sizeof(char);
+
+    if (name.length() == 0) {
+        return name;
+    }
+
+    std::stringstream idEncoded;
 
+    // xsd:ID must start with letter or underscore
+    if (!((name[0] >= 'A' && name[0] <= 'z') || name[0] == '_')) {
+        idEncoded << '_';
+    }
+
+    for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) {
+        // xsd:ID can only contain letters, digits, underscores, hyphens and periods
+        if (strchr(XML_ID_CHARS, *it) != nullptr) {
+            idEncoded << *it;
+        } else {
+            // Select placeholder character based on invalid character to prevent name collisions 
+            idEncoded << XML_ID_CHARS[(*it) % XML_ID_CHARS_COUNT];
+        }
+    }
+
+    return idEncoded.str();
+}
 
 // ------------------------------------------------------------------------------------------------
 // Constructor for a specific scene to export
-ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) : mIOSystem(pIOSystem), mPath(path), mFile(file)
-{
+ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) 
+: mIOSystem(pIOSystem)
+, mPath(path)
+, mFile(file) {
     // make sure that all formatting happens using the standard, C locale and not the user's current locale
     mOutput.imbue( std::locale("C") );
-    mOutput.precision(16);
+    mOutput.precision(ASSIMP_AI_REAL_TEXT_PRECISION);
 
     mScene = pScene;
     mSceneOwned = false;
@@ -115,17 +144,15 @@ ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, co
 
 // ------------------------------------------------------------------------------------------------
 // Destructor
-ColladaExporter::~ColladaExporter()
-{
-    if(mSceneOwned) {
+ColladaExporter::~ColladaExporter() {
+    if ( mSceneOwned ) {
         delete mScene;
     }
 }
 
 // ------------------------------------------------------------------------------------------------
 // Starts writing the contents
-void ColladaExporter::WriteFile()
-{
+void ColladaExporter::WriteFile() {
     // write the DTD
     mOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>" << endstr;
     // COLLADA element start
@@ -149,7 +176,7 @@ void ColladaExporter::WriteFile()
     // useless Collada fu at the end, just in case we haven't had enough indirections, yet.
     mOutput << startstr << "<scene>" << endstr;
     PushTag();
-    mOutput << startstr << "<instance_visual_scene url=\"#" + XMLEscape(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
+    mOutput << startstr << "<instance_visual_scene url=\"#" + XMLIDEncode(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
     PopTag();
     mOutput << startstr << "</scene>" << endstr;
     PopTag();
@@ -158,9 +185,8 @@ void ColladaExporter::WriteFile()
 
 // ------------------------------------------------------------------------------------------------
 // Writes the asset header
-void ColladaExporter::WriteHeader()
-{
-    static const ai_real epsilon = ai_real( 0.00001 );
+void ColladaExporter::WriteHeader() {
+    static const ai_real epsilon = Math::getEpsilon<ai_real>();
     static const aiQuaternion x_rot(aiMatrix3x3(
         0, -1,  0,
         1,  0,  0,
@@ -238,25 +264,64 @@ void ColladaExporter::WriteHeader()
     mOutput << startstr << "<contributor>" << endstr;
     PushTag();
 
-    aiMetadata* meta = mScene->mRootNode->mMetaData;
+    // If no Scene metadata, use root node metadata
+    aiMetadata* meta = mScene->mMetaData;
+    if (nullptr == meta) {
+        meta = mScene->mRootNode->mMetaData;
+    }
+
     aiString value;
-    if (!meta || !meta->Get("Author", value))
+    if (!meta || !meta->Get("Author", value)) {
         mOutput << startstr << "<author>" << "Assimp" << "</author>" << endstr;
-    else
+    } else {
         mOutput << startstr << "<author>" << XMLEscape(value.C_Str()) << "</author>" << endstr;
+    }
 
-    if (!meta || !meta->Get("AuthoringTool", value))
+    if (nullptr == meta || !meta->Get("AuthoringTool", value)) {
         mOutput << startstr << "<authoring_tool>" << "Assimp Exporter" << "</authoring_tool>" << endstr;
-    else
+    } else {
         mOutput << startstr << "<authoring_tool>" << XMLEscape(value.C_Str()) << "</authoring_tool>" << endstr;
+    }
 
-    //mOutput << startstr << "<author>" << mScene->author.C_Str() << "</author>" << endstr;
-    //mOutput << startstr << "<authoring_tool>" << mScene->authoringTool.C_Str() << "</authoring_tool>" << endstr;
+    if (meta) {
+        if (meta->Get("Comments", value)) {
+            mOutput << startstr << "<comments>" << XMLEscape(value.C_Str()) << "</comments>" << endstr;
+        }
+        if (meta->Get("Copyright", value)) {
+            mOutput << startstr << "<copyright>" << XMLEscape(value.C_Str()) << "</copyright>" << endstr;
+        }
+        if (meta->Get("SourceData", value)) {
+            mOutput << startstr << "<source_data>" << XMLEscape(value.C_Str()) << "</source_data>" << endstr;
+        }
+    }
 
     PopTag();
     mOutput << startstr << "</contributor>" << endstr;
-    mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+
+    if (nullptr == meta || !meta->Get("Created", value)) {
+        mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+    } else {
+        mOutput << startstr << "<created>" << XMLEscape(value.C_Str()) << "</created>" << endstr;
+    }
+
+    // Modified date is always the date saved
     mOutput << startstr << "<modified>" << date_str << "</modified>" << endstr;
+
+    if (meta) {
+        if (meta->Get("Keywords", value)) {
+            mOutput << startstr << "<keywords>" << XMLEscape(value.C_Str()) << "</keywords>" << endstr;
+        }
+        if (meta->Get("Revision", value)) {
+            mOutput << startstr << "<revision>" << XMLEscape(value.C_Str()) << "</revision>" << endstr;
+        }
+        if (meta->Get("Subject", value)) {
+            mOutput << startstr << "<subject>" << XMLEscape(value.C_Str()) << "</subject>" << endstr;
+        }
+        if (meta->Get("Title", value)) {
+            mOutput << startstr << "<title>" << XMLEscape(value.C_Str()) << "</title>" << endstr;
+        }
+    }
+
     mOutput << startstr << "<unit name=\"meter\" meter=\"" << scale << "\" />" << endstr;
     mOutput << startstr << "<up_axis>" << up_axis << "</up_axis>" << endstr;
     PopTag();
@@ -269,18 +334,21 @@ void ColladaExporter::WriteTextures() {
     static const unsigned int buffer_size = 1024;
     char str[buffer_size];
 
-    if(mScene->HasTextures()) {
+    if (mScene->HasTextures()) {
         for(unsigned int i = 0; i < mScene->mNumTextures; i++) {
             // It would be great to be able to create a directory in portable standard C++, but it's not the case,
             // so we just write the textures in the current directory.
 
             aiTexture* texture = mScene->mTextures[i];
+            if ( nullptr == texture ) {
+                continue;
+            }
 
             ASSIMP_itoa10(str, buffer_size, i + 1);
 
             std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char*) texture->achFormatHint);
 
-            std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + name, "wb"));
+            std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb"));
             if(outfile == NULL) {
                 throw DeadlyExportError("could not open output texture file: " + mPath + name);
             }
@@ -318,9 +386,10 @@ void ColladaExporter::WriteCamerasLibrary() {
 void ColladaExporter::WriteCamera(size_t pIndex){
 
     const aiCamera *cam = mScene->mCameras[pIndex];
-    const std::string idstrEscaped = XMLEscape(cam->mName.C_Str());
+    const std::string cameraName = XMLEscape(cam->mName.C_Str());
+    const std::string cameraId = XMLIDEncode(cam->mName.C_Str());
 
-    mOutput << startstr << "<camera id=\"" << idstrEscaped << "-camera\" name=\"" << idstrEscaped << "_name\" >" << endstr;
+    mOutput << startstr << "<camera id=\"" << cameraId << "-camera\" name=\"" << cameraName << "\" >" << endstr;
     PushTag();
     mOutput << startstr << "<optics>" << endstr;
     PushTag();
@@ -374,10 +443,11 @@ void ColladaExporter::WriteLightsLibrary() {
 void ColladaExporter::WriteLight(size_t pIndex){
 
     const aiLight *light = mScene->mLights[pIndex];
-    const std::string idstrEscaped = XMLEscape(light->mName.C_Str());
+    const std::string lightName = XMLEscape(light->mName.C_Str());
+    const std::string lightId = XMLIDEncode(light->mName.C_Str());
 
-    mOutput << startstr << "<light id=\"" << idstrEscaped << "-light\" name=\""
-            << idstrEscaped << "_name\" >" << endstr;
+    mOutput << startstr << "<light id=\"" << lightId << "-light\" name=\""
+            << lightName << "\" >" << endstr;
     PushTag();
     mOutput << startstr << "<technique_common>" << endstr;
     PushTag();
@@ -428,6 +498,7 @@ void ColladaExporter::WritePointLight(const aiLight *const light){
     mOutput << startstr << "</point>" << endstr;
 
 }
+
 void ColladaExporter::WriteDirectionalLight(const aiLight *const light){
     const aiColor3D &color=  light->mColorDiffuse;
     mOutput << startstr << "<directional>" << endstr;
@@ -440,6 +511,7 @@ void ColladaExporter::WriteDirectionalLight(const aiLight *const light){
     mOutput << startstr << "</directional>" << endstr;
 
 }
+
 void ColladaExporter::WriteSpotLight(const aiLight *const light){
 
     const aiColor3D &color=  light->mColorDiffuse;
@@ -496,18 +568,16 @@ void ColladaExporter::WriteAmbienttLight(const aiLight *const light){
 
 // ------------------------------------------------------------------------------------------------
 // Reads a single surface entry from the given material keys
-void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex)
-{
-  if( pSrcMat->GetTextureCount( pTexture) > 0 )
-  {
+void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, 
+                                          aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex) {
+  if( pSrcMat->GetTextureCount( pTexture) > 0 ) {
     aiString texfile;
     unsigned int uvChannel = 0;
     pSrcMat->GetTexture( pTexture, 0, &texfile, NULL, &uvChannel);
 
     std::string index_str(texfile.C_Str());
 
-    if(index_str.size() != 0 && index_str[0] == '*')
-    {
+    if(index_str.size() != 0 && index_str[0] == '*') {
         unsigned int index;
 
         index_str = index_str.substr(1, std::string::npos);
@@ -525,15 +595,13 @@ void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial*
         } else {
             throw DeadlyExportError("could not find embedded texture at index " + index_str);
         }
-    } else
-    {
+    } else {
         poSurface.texture = texfile.C_Str();
     }
 
     poSurface.channel = uvChannel;
     poSurface.exist = true;
-  } else
-  {
+  } else {
     if( pKey )
       poSurface.exist = pSrcMat->Get( pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
   }
@@ -541,18 +609,16 @@ void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial*
 
 // ------------------------------------------------------------------------------------------------
 // Reimplementation of isalnum(,C locale), because AppVeyor does not see standard version.
-static bool isalnum_C(char c)
-{
+static bool isalnum_C(char c) {
   return ( nullptr != strchr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",c) );
 }
 
 // ------------------------------------------------------------------------------------------------
 // Writes an image entry for the given surface
-void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd)
-{
+void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd) {
   if( !pSurface.texture.empty() )
   {
-    mOutput << startstr << "<image id=\"" << XMLEscape(pNameAdd) << "\">" << endstr;
+    mOutput << startstr << "<image id=\"" << XMLIDEncode(pNameAdd) << "\">" << endstr;
     PushTag();
     mOutput << startstr << "<init_from>";
 
@@ -585,7 +651,7 @@ void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std
     }
     else
     {
-      mOutput << startstr << "<texture texture=\"" << XMLEscape(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
+      mOutput << startstr << "<texture texture=\"" << XMLIDEncode(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
     }
     PopTag();
     mOutput << startstr << "</" << pTypeName << ">" << endstr;
@@ -599,21 +665,21 @@ void ColladaExporter::WriteTextureParamEntry( const Surface& pSurface, const std
   // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
   if( !pSurface.texture.empty() )
   {
-    mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
+    mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
     PushTag();
     mOutput << startstr << "<surface type=\"2D\">" << endstr;
     PushTag();
-    mOutput << startstr << "<init_from>" << XMLEscape(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
+    mOutput << startstr << "<init_from>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
     PopTag();
     mOutput << startstr << "</surface>" << endstr;
     PopTag();
     mOutput << startstr << "</newparam>" << endstr;
 
-    mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
+    mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
     PushTag();
     mOutput << startstr << "<sampler2D>" << endstr;
     PushTag();
-    mOutput << startstr << "<source>" << XMLEscape(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
+    mOutput << startstr << "<source>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
     PopTag();
     mOutput << startstr << "</sampler2D>" << endstr;
     PopTag();
@@ -665,11 +731,6 @@ void ColladaExporter::WriteMaterials()
         materials[a].name = std::string(name.C_Str()) + to_string(materialCountWithThisName);
       }
     }
-    for( std::string::iterator it = materials[a].name.begin(); it != materials[a].name.end(); ++it ) {
-      if( !isalnum_C( *it ) ) {
-        *it = '_';
-      }
-    }
 
     aiShadingMode shading = aiShadingMode_Flat;
     materials[a].shading_model = "phong";
@@ -734,7 +795,7 @@ void ColladaExporter::WriteMaterials()
     {
       const Material& mat = *it;
       // this is so ridiculous it must be right
-      mOutput << startstr << "<effect id=\"" << XMLEscape(mat.name) << "-fx\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
+      mOutput << startstr << "<effect id=\"" << XMLIDEncode(mat.name) << "-fx\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
       PushTag();
       mOutput << startstr << "<profile_COMMON>" << endstr;
       PushTag();
@@ -785,9 +846,9 @@ void ColladaExporter::WriteMaterials()
     for( std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it )
     {
       const Material& mat = *it;
-      mOutput << startstr << "<material id=\"" << XMLEscape(mat.name) << "\" name=\"" << mat.name << "\">" << endstr;
+      mOutput << startstr << "<material id=\"" << XMLIDEncode(mat.name) << "\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
       PushTag();
-      mOutput << startstr << "<instance_effect url=\"#" << XMLEscape(mat.name) << "-fx\"/>" << endstr;
+      mOutput << startstr << "<instance_effect url=\"#" << XMLIDEncode(mat.name) << "-fx\"/>" << endstr;
       PopTag();
       mOutput << startstr << "</material>" << endstr;
     }
@@ -803,8 +864,9 @@ void ColladaExporter::WriteControllerLibrary()
     mOutput << startstr << "<library_controllers>" << endstr;
     PushTag();
     
-    for( size_t a = 0; a < mScene->mNumMeshes; ++a)
+    for( size_t a = 0; a < mScene->mNumMeshes; ++a) {
         WriteController( a);
+    }
 
     PopTag();
     mOutput << startstr << "</library_controllers>" << endstr;
@@ -815,8 +877,8 @@ void ColladaExporter::WriteControllerLibrary()
 void ColladaExporter::WriteController( size_t pIndex)
 {
     const aiMesh* mesh = mScene->mMeshes[pIndex];
-    const std::string idstr = GetMeshId( pIndex);
-    const std::string idstrEscaped = XMLEscape(idstr);
+    const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
+    const std::string idstrEscaped = XMLIDEncode(idstr);
 
     if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
         return;
@@ -851,7 +913,7 @@ void ColladaExporter::WriteController( size_t pIndex)
     mOutput << startstr << "<Name_array id=\"" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
 
     for( size_t i = 0; i < mesh->mNumBones; ++i )
-        mOutput << XMLEscape(mesh->mBones[i]->mName.C_Str()) << " ";
+        mOutput << XMLIDEncode(mesh->mBones[i]->mName.C_Str()) << " ";
 
     mOutput << "</Name_array>" << endstr;
 
@@ -986,14 +1048,15 @@ void ColladaExporter::WriteGeometryLibrary()
 void ColladaExporter::WriteGeometry( size_t pIndex)
 {
     const aiMesh* mesh = mScene->mMeshes[pIndex];
-    const std::string idstr = GetMeshId( pIndex);
-    const std::string idstrEscaped = XMLEscape(idstr);
+    const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
+    const std::string geometryName = XMLEscape(idstr);
+    const std::string geometryId = XMLIDEncode(idstr);
 
     if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
         return;
 
     // opening tag
-    mOutput << startstr << "<geometry id=\"" << idstrEscaped << "\" name=\"" << idstrEscaped << "_name\" >" << endstr;
+    mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr;
     PushTag();
 
     mOutput << startstr << "<mesh>" << endstr;
@@ -1024,9 +1087,9 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
 
     // assemble vertex structure
     // Only write input for POSITION since we will write other as shared inputs in polygon definition
-    mOutput << startstr << "<vertices id=\"" << idstrEscaped << "-vertices" << "\">" << endstr;
+    mOutput << startstr << "<vertices id=\"" << geometryId << "-vertices" << "\">" << endstr;
     PushTag();
-    mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << idstrEscaped << "-positions\" />" << endstr;
+    mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << geometryId << "-positions\" />" << endstr;
     PopTag();
     mOutput << startstr << "</vertices>" << endstr;
 
@@ -1044,18 +1107,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
     {
         mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
         PushTag();
-        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstrEscaped << "-vertices\" />" << endstr;
+        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
         if( mesh->HasNormals() )
-            mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << idstrEscaped << "-normals\" />" << endstr;
+            mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
         {
             if( mesh->HasTextureCoords(static_cast<unsigned int>(a)) )
-                mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << idstrEscaped << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
         {
             if( mesh->HasVertexColors(static_cast<unsigned int>(a) ) )
-                mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << idstrEscaped << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
 
         mOutput << startstr << "<p>";
@@ -1078,18 +1141,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
     {
         mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
         PushTag();
-        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstrEscaped << "-vertices\" />" << endstr;
+        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
         if( mesh->HasNormals() )
-            mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << idstrEscaped << "-normals\" />" << endstr;
+            mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
         {
             if( mesh->HasTextureCoords(static_cast<unsigned int>(a)) )
-                mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << idstrEscaped << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
         {
             if( mesh->HasVertexColors(static_cast<unsigned int>(a) ) )
-                mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << idstrEscaped << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
 
         mOutput << startstr << "<vcount>";
@@ -1138,13 +1201,13 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
             return;
     }
 
-    std::string arrayId = pIdString + "-array";
+    std::string arrayId = XMLIDEncode(pIdString) + "-array";
 
-    mOutput << startstr << "<source id=\"" << XMLEscape(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr;
+    mOutput << startstr << "<source id=\"" << XMLIDEncode(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr;
     PushTag();
 
     // source array
-    mOutput << startstr << "<float_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
+    mOutput << startstr << "<float_array id=\"" << arrayId << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
     PushTag();
 
     if( pType == FloatType_TexCoord2 )
@@ -1230,11 +1293,12 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
 // Writes the scene library
 void ColladaExporter::WriteSceneLibrary()
 {
-    const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
+    const std::string sceneName = XMLEscape(mScene->mRootNode->mName.C_Str());
+    const std::string sceneId = XMLIDEncode(mScene->mRootNode->mName.C_Str());
 
     mOutput << startstr << "<library_visual_scenes>" << endstr;
     PushTag();
-    mOutput << startstr << "<visual_scene id=\"" + scene_name_escaped + "\" name=\"" + scene_name_escaped + "\">" << endstr;
+    mOutput << startstr << "<visual_scene id=\"" + sceneId + "\" name=\"" + sceneName + "\">" << endstr;
     PushTag();
 
     // start recursive write at the root node
@@ -1265,7 +1329,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 		idstr = idstr + ending;
 	}
 
-	const std::string idstrEscaped = XMLEscape(idstr);
+	const std::string idstrEscaped = XMLIDEncode(idstr);
 	
 	mOutput << startstr << "<animation id=\"" + idstrEscaped + "\" name=\"" + animation_name_escaped + "\">" << endstr;
 	PushTag();
@@ -1337,13 +1401,13 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 			}
 			
 			const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-interpolation");
-			std::string arrayId = node_idstr + "-array";
+            std::string arrayId = XMLIDEncode(node_idstr) + "-array";
 			
-			mOutput << startstr << "<source id=\"" << XMLEscape(node_idstr) << "\">" << endstr;
+			mOutput << startstr << "<source id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr;
 			PushTag();
 			
 			// source array
-			mOutput << startstr << "<Name_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\"> ";
+			mOutput << startstr << "<Name_array id=\"" << arrayId << "\" count=\"" << names.size() << "\"> ";
 			for( size_t a = 0; a < names.size(); ++a ) {
 				mOutput << names[a] << " ";
             }
@@ -1352,7 +1416,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 			mOutput << startstr << "<technique_common>" << endstr;
 			PushTag();
 
-			mOutput << startstr << "<accessor source=\"#" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
+			mOutput << startstr << "<accessor source=\"#" << arrayId << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
 			PushTag();
 			
 			mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr;
@@ -1374,12 +1438,12 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 		{
 		// samplers
 			const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler");
-			mOutput << startstr << "<sampler id=\"" << XMLEscape(node_idstr) << "\">" << endstr;
+			mOutput << startstr << "<sampler id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr;
 			PushTag();
 			
-			mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr;
-			mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr;
-			mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
 			
 			PopTag();
 			mOutput << startstr << "</sampler>" << endstr;
@@ -1391,7 +1455,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 		
 		{
 		// channels
-			mOutput << startstr << "<channel source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLEscape(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
+			mOutput << startstr << "<channel source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
 		}
 	}
 	
@@ -1402,8 +1466,6 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 // ------------------------------------------------------------------------------------------------
 void ColladaExporter::WriteAnimationsLibrary()
 {
-	const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
-	
 	if ( mScene->mNumAnimations > 0 ) {
 		mOutput << startstr << "<library_animations>" << endstr;
 		PushTag();
@@ -1511,16 +1573,17 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         }
     }
 
-    const std::string node_name_escaped = XMLEscape(pNode->mName.data);
+    const std::string node_id = XMLIDEncode(pNode->mName.data);
+    const std::string node_name = XMLEscape(pNode->mName.data);
 	mOutput << startstr << "<node ";
 	if(is_skeleton_root) {
-		mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"" : "") ; // For now, only support one skeleton in a scene.
-		mFoundSkeletonRootNodeID = node_name_escaped;
+		mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id +"\"" : "") ; // For now, only support one skeleton in a scene.
+		mFoundSkeletonRootNodeID = node_id;
 	} else {
-		mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"": "") ;
+		mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id +"\"": "") ;
 	}
 	
-    mOutput << " name=\"" << node_name_escaped
+    mOutput << " name=\"" << node_name
             << "\" type=\"" << node_type
             << "\">" << endstr;
     PushTag();
@@ -1559,14 +1622,14 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         //check if it is a camera node
         for(size_t i=0; i<mScene->mNumCameras; i++){
             if(mScene->mCameras[i]->mName == pNode->mName){
-                mOutput << startstr <<"<instance_camera url=\"#" << node_name_escaped << "-camera\"/>" << endstr;
+                mOutput << startstr <<"<instance_camera url=\"#" << node_id << "-camera\"/>" << endstr;
                 break;
             }
         }
         //check if it is a light node
         for(size_t i=0; i<mScene->mNumLights; i++){
             if(mScene->mLights[i]->mName == pNode->mName){
-                mOutput << startstr <<"<instance_light url=\"#" << node_name_escaped << "-light\"/>" << endstr;
+                mOutput << startstr <<"<instance_light url=\"#" << node_id << "-light\"/>" << endstr;
                 break;
             }
         }
@@ -1580,15 +1643,17 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
             continue;
 
+        const std::string meshName = mesh->mName.length == 0 ? GetMeshId(pNode->mMeshes[a]) : mesh->mName.C_Str();
+
         if( mesh->mNumBones == 0 )
         {
-            mOutput << startstr << "<instance_geometry url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "\">" << endstr;
+            mOutput << startstr << "<instance_geometry url=\"#" << XMLIDEncode(meshName) << "\">" << endstr;
             PushTag();
         }
         else
         {
             mOutput << startstr
-                    << "<instance_controller url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "-skin\">"
+                    << "<instance_controller url=\"#" << XMLIDEncode(meshName) << "-skin\">"
                     << endstr;
             PushTag();
 
@@ -1596,7 +1661,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
 			// use the first bone to find skeleton root
 			const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh );
 			if ( skeletonRootBoneNode ) {
-				mFoundSkeletonRootNodeID = XMLEscape( skeletonRootBoneNode->mName.C_Str() );
+				mFoundSkeletonRootNodeID = XMLIDEncode( skeletonRootBoneNode->mName.C_Str() );
 			}
             mOutput << startstr << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr;
         }
@@ -1604,7 +1669,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         PushTag();
         mOutput << startstr << "<technique_common>" << endstr;
         PushTag();
-        mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLEscape(materials[mesh->mMaterialIndex].name) << "\">" << endstr;
+        mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLIDEncode(materials[mesh->mMaterialIndex].name) << "\">" << endstr;
         PushTag();
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
         {

+ 1 - 2
Engine/lib/assimp/code/ColladaExporter.h → Engine/lib/assimp/code/Collada/ColladaExporter.h

@@ -150,7 +150,6 @@ public:
     /// Stringstream to write all output into
     std::stringstream mOutput;
 
-protected:
     /// The IOSystem for output
     IOSystem* mIOSystem;
 
@@ -204,7 +203,7 @@ protected:
 
   std::map<unsigned int, std::string> textures;
 
-protected:
+public:
   /// Dammit C++ - y u no compile two-pass? No I have to add all methods below the struct definitions
   /// Reads a single surface entry from the given material keys
   void ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex);

+ 2 - 6
Engine/lib/assimp/code/ColladaHelper.h → Engine/lib/assimp/code/Collada/ColladaHelper.h

@@ -580,15 +580,11 @@ struct Image
 {
     std::string mFileName;
 
-    /** If image file name is zero, embedded image data
-     */
+    /** Embedded image data */
     std::vector<uint8_t> mImageData;
 
-    /** If image file name is zero, file format of
-     *  embedded image data.
-     */
+    /** File format hint of embedded image data */
     std::string mEmbeddedFormat;
-
 };
 
 /** An animation channel. */

File diff suppressed because it is too large
+ 292 - 291
Engine/lib/assimp/code/Collada/ColladaLoader.cpp


+ 5 - 8
Engine/lib/assimp/code/ColladaLoader.h → Engine/lib/assimp/code/Collada/ColladaLoader.h

@@ -94,20 +94,20 @@ public:
 public:
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details. */
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+    bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override;
 
 protected:
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details
      */
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc* GetInfo () const override;
 
-    void SetupProperties(const Importer* pImp);
+    void SetupProperties(const Importer* pImp) override;
 
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details
      */
-    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
 
     /** Recursively constructs a scene node for the given parser node and returns it. */
     aiNode* BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode);
@@ -120,7 +120,7 @@ protected:
     void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode,
         aiNode* pTarget);
 		
-    aiMesh *findMesh(std::string meshid);
+    aiMesh *findMesh(const std::string& meshid);
 
     /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */
     aiMesh* CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
@@ -184,9 +184,6 @@ protected:
     aiString FindFilenameForEffectTexture( const ColladaParser& pParser,
         const Collada::Effect& pEffect, const std::string& pName);
 
-    /** Converts a path read from a collada file to the usual representation */
-    void ConvertPath( aiString& ss);
-
     /** Reads a float value from an accessor and its data array.
      * @param pAccessor The accessor to use for reading
      * @param pData The data array to read from

+ 3473 - 0
Engine/lib/assimp/code/Collada/ColladaParser.cpp

@@ -0,0 +1,3473 @@
+/*
+---------------------------------------------------------------------------
+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.
+---------------------------------------------------------------------------
+*/
+
+/** @file ColladaParser.cpp
+ *  @brief Implementation of the Collada parser helper
+ */
+
+#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
+
+#include <sstream>
+#include <stdarg.h>
+#include "ColladaParser.h"
+#include <assimp/fast_atof.h>
+#include <assimp/ParsingUtils.h>
+#include <assimp/StringUtils.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/IOSystem.hpp>
+#include <assimp/light.h>
+#include <assimp/TinyFormatter.h>
+#include <assimp/ZipArchiveIOSystem.h>
+
+#include <memory>
+
+using namespace Assimp;
+using namespace Assimp::Collada;
+using namespace Assimp::Formatter;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ColladaParser::ColladaParser(IOSystem* pIOHandler, const std::string& pFile)
+    : mFileName(pFile)
+    , mReader(nullptr)
+    , mDataLibrary()
+    , mAccessorLibrary()
+    , mMeshLibrary()
+    , mNodeLibrary()
+    , mImageLibrary()
+    , mEffectLibrary()
+    , mMaterialLibrary()
+    , mLightLibrary()
+    , mCameraLibrary()
+    , mControllerLibrary()
+    , mRootNode(nullptr)
+    , mAnims()
+    , mUnitSize(1.0f)
+    , mUpDirection(UP_Y)
+    , mFormat(FV_1_5_n)    // We assume the newest file format by default
+{
+    // validate io-handler instance
+    if (nullptr == pIOHandler) {
+        throw DeadlyImportError("IOSystem is NULL.");
+    }
+
+    std::unique_ptr<IOStream> daefile;
+    std::unique_ptr<ZipArchiveIOSystem> zip_archive;
+
+    // Determine type
+    std::string extension = BaseImporter::GetExtension(pFile);
+    if (extension != "dae") {
+        zip_archive.reset(new ZipArchiveIOSystem(pIOHandler, pFile));
+    }
+
+    if (zip_archive && zip_archive->isOpen()) {
+        std::string dae_filename = ReadZaeManifest(*zip_archive);
+
+        if (dae_filename.empty()) {
+            ThrowException(std::string("Invalid ZAE"));
+        }
+
+        daefile.reset(zip_archive->Open(dae_filename.c_str()));
+        if (daefile == nullptr) {
+            ThrowException(std::string("Invalid ZAE manifest: '") + std::string(dae_filename) + std::string("' is missing"));
+        }
+    }
+    else {
+        // attempt to open the file directly
+        daefile.reset(pIOHandler->Open(pFile));
+        if (daefile.get() == nullptr) {
+            throw DeadlyImportError("Failed to open file '" + pFile + "'.");
+        }
+    }
+
+    // generate a XML reader for it
+    std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(daefile.get()));
+    mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
+    if (!mReader) {
+        ThrowException("Unable to read file, malformed XML");
+    }
+
+    // start reading
+    ReadContents();
+
+    // read embedded textures
+    if (zip_archive && zip_archive->isOpen()) {
+        ReadEmbeddedTextures(*zip_archive);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ColladaParser::~ColladaParser()
+{
+    delete mReader;
+    for (NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it)
+        delete it->second;
+    for (MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it)
+        delete it->second;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a ZAE manifest and return the filename to attempt to open
+std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) {
+    // Open the manifest
+    std::unique_ptr<IOStream> manifestfile(zip_archive.Open("manifest.xml"));
+    if (manifestfile == nullptr)
+    {
+        // No manifest, hope there is only one .DAE inside
+        std::vector<std::string> file_list;
+        zip_archive.getFileListExtension(file_list, "dae");
+
+        if (file_list.empty())
+            return std::string();
+
+        return file_list.front();
+    }
+
+    std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(manifestfile.get()));
+    std::unique_ptr<irr::io::IrrXMLReader> manifest_reader(irr::io::createIrrXMLReader(mIOWrapper.get()));
+
+    while (manifest_reader->read())
+    {
+        // find the manifest "dae_root" element
+        if (manifest_reader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (::strcmp(manifest_reader->getNodeName(), "dae_root") == 0)
+            {
+                if (!manifest_reader->read())
+                    return std::string();
+                if (manifest_reader->getNodeType() != irr::io::EXN_TEXT && manifest_reader->getNodeType() != irr::io::EXN_CDATA)
+                    return std::string();
+
+                const char* filepath = manifest_reader->getNodeData();
+                if (filepath == nullptr)
+                    return std::string();
+
+                aiString ai_str(filepath);
+                UriDecodePath(ai_str);
+
+                return std::string(ai_str.C_Str());
+            }
+        }
+    }
+    return std::string();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a path read from a collada file to the usual representation
+void ColladaParser::UriDecodePath(aiString& ss)
+{
+    // TODO: collada spec, p 22. Handle URI correctly.
+    // For the moment we're just stripping the file:// away to make it work.
+    // Windows doesn't seem to be able to find stuff like
+    // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
+    if (0 == strncmp(ss.data, "file://", 7))
+    {
+        ss.length -= 7;
+        memmove(ss.data, ss.data + 7, ss.length);
+        ss.data[ss.length] = '\0';
+    }
+
+    // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes...
+    // I need to filter it without destroying linux paths starting with "/somewhere"
+#if defined( _MSC_VER )
+    if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') {
+#else
+    if (ss.data[0] == '/' && isalpha(ss.data[1]) && ss.data[2] == ':') {
+#endif
+        --ss.length;
+        ::memmove(ss.data, ss.data + 1, ss.length);
+        ss.data[ss.length] = 0;
+    }
+
+    // find and convert all %xy special chars
+    char* out = ss.data;
+    for (const char* it = ss.data; it != ss.data + ss.length; /**/)
+    {
+        if (*it == '%' && (it + 3) < ss.data + ss.length)
+        {
+            // separate the number to avoid dragging in chars from behind into the parsing
+            char mychar[3] = { it[1], it[2], 0 };
+            size_t nbr = strtoul16(mychar);
+            it += 3;
+            *out++ = (char)(nbr & 0xFF);
+        }
+        else
+        {
+            *out++ = *it++;
+        }
+    }
+
+    // adjust length and terminator of the shortened string
+    *out = 0;
+    ai_assert(out > ss.data);
+    ss.length = static_cast<ai_uint32>(out - ss.data);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read bool from text contents of current element
+bool ColladaParser::ReadBoolFromTextContent()
+{
+    const char* cur = GetTextContent();
+    return (!ASSIMP_strincmp(cur, "true", 4) || '0' != *cur);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read float from text contents of current element
+ai_real ColladaParser::ReadFloatFromTextContent()
+{
+    const char* cur = GetTextContent();
+    return fast_atof(cur);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the contents of the file
+void ColladaParser::ReadContents()
+{
+    while (mReader->read())
+    {
+        // handle the root element "COLLADA"
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("COLLADA"))
+            {
+                // check for 'version' attribute
+                const int attrib = TestAttribute("version");
+                if (attrib != -1) {
+                    const char* version = mReader->getAttributeValue(attrib);
+
+                    if (!::strncmp(version, "1.5", 3)) {
+                        mFormat = FV_1_5_n;
+                        ASSIMP_LOG_DEBUG("Collada schema version is 1.5.n");
+                    }
+                    else if (!::strncmp(version, "1.4", 3)) {
+                        mFormat = FV_1_4_n;
+                        ASSIMP_LOG_DEBUG("Collada schema version is 1.4.n");
+                    }
+                    else if (!::strncmp(version, "1.3", 3)) {
+                        mFormat = FV_1_3_n;
+                        ASSIMP_LOG_DEBUG("Collada schema version is 1.3.n");
+                    }
+                }
+
+                ReadStructure();
+            }
+            else
+            {
+                ASSIMP_LOG_DEBUG_F("Ignoring global element <", mReader->getNodeName(), ">.");
+                SkipElement();
+            }
+        }
+        else
+        {
+            // skip everything else silently
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the structure of the file
+void ColladaParser::ReadStructure()
+{
+    while (mReader->read())
+    {
+        // beginning of elements
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("asset"))
+                ReadAssetInfo();
+            else if (IsElement("library_animations"))
+                ReadAnimationLibrary();
+            else if (IsElement("library_animation_clips"))
+                ReadAnimationClipLibrary();
+            else if (IsElement("library_controllers"))
+                ReadControllerLibrary();
+            else if (IsElement("library_images"))
+                ReadImageLibrary();
+            else if (IsElement("library_materials"))
+                ReadMaterialLibrary();
+            else if (IsElement("library_effects"))
+                ReadEffectLibrary();
+            else if (IsElement("library_geometries"))
+                ReadGeometryLibrary();
+            else if (IsElement("library_visual_scenes"))
+                ReadSceneLibrary();
+            else if (IsElement("library_lights"))
+                ReadLightLibrary();
+            else if (IsElement("library_cameras"))
+                ReadCameraLibrary();
+            else if (IsElement("library_nodes"))
+                ReadSceneNode(NULL); /* some hacking to reuse this piece of code */
+            else if (IsElement("scene"))
+                ReadScene();
+            else
+                SkipElement();
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            break;
+        }
+    }
+
+    PostProcessRootAnimations();
+    PostProcessControllers();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads asset information such as coordinate system information and legal blah
+void ColladaParser::ReadAssetInfo()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("unit"))
+            {
+                // read unit data from the element's attributes
+                const int attrIndex = TestAttribute("meter");
+                if (attrIndex == -1) {
+                    mUnitSize = 1.f;
+                }
+                else {
+                    mUnitSize = mReader->getAttributeValueAsFloat(attrIndex);
+                }
+
+                // consume the trailing stuff
+                if (!mReader->isEmptyElement())
+                    SkipElement();
+            }
+            else if (IsElement("up_axis"))
+            {
+                // read content, strip whitespace, compare
+                const char* content = GetTextContent();
+                if (strncmp(content, "X_UP", 4) == 0)
+                    mUpDirection = UP_X;
+                else if (strncmp(content, "Z_UP", 4) == 0)
+                    mUpDirection = UP_Z;
+                else
+                    mUpDirection = UP_Y;
+
+                // check element end
+                TestClosing("up_axis");
+            }
+            else if (IsElement("contributor"))
+            {
+                ReadContributorInfo();
+            }
+            else
+            {
+                ReadMetaDataItem(mAssetMetaData);
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "asset") != 0)
+                ThrowException("Expected end of <asset> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the contributor info
+void ColladaParser::ReadContributorInfo()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            ReadMetaDataItem(mAssetMetaData);
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "contributor") != 0)
+                ThrowException("Expected end of <contributor> element.");
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single string metadata item
+void ColladaParser::ReadMetaDataItem(StringMetaData &metadata)
+{
+    // Metadata such as created, keywords, subject etc
+    const char* key_char = mReader->getNodeName();
+    if (key_char != nullptr)
+    {
+        const std::string key_str(key_char);
+        const char* value_char = TestTextContent();
+        if (value_char != nullptr)
+        {
+            std::string camel_key_str = key_str;
+            ToCamelCase(camel_key_str);
+            aiString aistr;
+            aistr.Set(value_char);
+            metadata.emplace(camel_key_str, aistr);
+        }
+        TestClosing(key_str.c_str());
+    }
+    else
+        SkipElement();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert underscore_seperated to CamelCase: "authoring_tool" becomes "AuthoringTool"
+void ColladaParser::ToCamelCase(std::string &text)
+{
+    if (text.empty())
+        return;
+    // Capitalise first character
+    text[0] = ToUpper(text[0]);
+    for (auto it = text.begin(); it != text.end(); /*iterated below*/)
+    {
+        if ((*it) == '_')
+        {
+            it = text.erase(it);
+            if (it != text.end())
+                (*it) = ToUpper(*it);
+        }
+        else
+            ++it;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation clips
+void ColladaParser::ReadAnimationClipLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("animation_clip"))
+            {
+                // optional name given as an attribute
+                std::string animName;
+                int indexName = TestAttribute("name");
+                int indexID = TestAttribute("id");
+                if (indexName >= 0)
+                    animName = mReader->getAttributeValue(indexName);
+                else if (indexID >= 0)
+                    animName = mReader->getAttributeValue(indexID);
+                else
+                    animName = std::string("animation_") + to_string(mAnimationClipLibrary.size());
+
+                std::pair<std::string, std::vector<std::string> > clip;
+
+                clip.first = animName;
+
+                while (mReader->read())
+                {
+                    if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+                    {
+                        if (IsElement("instance_animation"))
+                        {
+                            int indexUrl = TestAttribute("url");
+                            if (indexUrl >= 0)
+                            {
+                                const char* url = mReader->getAttributeValue(indexUrl);
+                                if (url[0] != '#')
+                                    ThrowException("Unknown reference format");
+
+                                url++;
+
+                                clip.second.push_back(url);
+                            }
+                        }
+                        else
+                        {
+                            // ignore the rest
+                            SkipElement();
+                        }
+                    }
+                    else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+                    {
+                        if (strcmp(mReader->getNodeName(), "animation_clip") != 0)
+                            ThrowException("Expected end of <animation_clip> element.");
+
+                        break;
+                    }
+                }
+
+                if (clip.second.size() > 0)
+                {
+                    mAnimationClipLibrary.push_back(clip);
+                }
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_animation_clips") != 0)
+                ThrowException("Expected end of <library_animation_clips> element.");
+
+            break;
+        }
+    }
+}
+
+void ColladaParser::PostProcessControllers()
+{
+    std::string meshId;
+    for (ControllerLibrary::iterator it = mControllerLibrary.begin(); it != mControllerLibrary.end(); ++it) {
+        meshId = it->second.mMeshId;
+        ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId);
+        while (findItr != mControllerLibrary.end()) {
+            meshId = findItr->second.mMeshId;
+            findItr = mControllerLibrary.find(meshId);
+        }
+
+        it->second.mMeshId = meshId;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Re-build animations from animation clip library, if present, otherwise combine single-channel animations
+void ColladaParser::PostProcessRootAnimations()
+{
+    if (mAnimationClipLibrary.size() > 0)
+    {
+        Animation temp;
+
+        for (AnimationClipLibrary::iterator it = mAnimationClipLibrary.begin(); it != mAnimationClipLibrary.end(); ++it)
+        {
+            std::string clipName = it->first;
+
+            Animation *clip = new Animation();
+            clip->mName = clipName;
+
+            temp.mSubAnims.push_back(clip);
+
+            for (std::vector<std::string>::iterator a = it->second.begin(); a != it->second.end(); ++a)
+            {
+                std::string animationID = *a;
+
+                AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID);
+
+                if (animation != mAnimationLibrary.end())
+                {
+                    Animation *pSourceAnimation = animation->second;
+
+                    pSourceAnimation->CollectChannelsRecursively(clip->mChannels);
+                }
+            }
+        }
+
+        mAnims = temp;
+
+        // Ensure no double deletes.
+        temp.mSubAnims.clear();
+    }
+    else
+    {
+        mAnims.CombineSingleChannelAnimations();
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation library
+void ColladaParser::ReadAnimationLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("animation"))
+            {
+                // delegate the reading. Depending on the inner elements it will be a container or a anim channel
+                ReadAnimation(&mAnims);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_animations") != 0)
+                ThrowException("Expected end of <library_animations> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an animation into the given parent structure
+void ColladaParser::ReadAnimation(Collada::Animation* pParent)
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    // an <animation> element may be a container for grouping sub-elements or an animation channel
+    // this is the channel collection by ID, in case it has channels
+    typedef std::map<std::string, AnimationChannel> ChannelMap;
+    ChannelMap channels;
+    // this is the anim container in case we're a container
+    Animation* anim = NULL;
+
+    // optional name given as an attribute
+    std::string animName;
+    std::string animID;
+    int indexName = TestAttribute("name");
+    int indexID = TestAttribute("id");
+
+    if (indexID >= 0)
+        animID = mReader->getAttributeValue(indexID);
+
+    if (indexName >= 0)
+        animName = mReader->getAttributeValue(indexName);
+    else if (indexID >= 0)
+        animName = animID;
+    else
+        animName = "animation";
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            // we have subanimations
+            if (IsElement("animation"))
+            {
+                // create container from our element
+                if (!anim)
+                {
+                    anim = new Animation;
+                    anim->mName = animName;
+                    pParent->mSubAnims.push_back(anim);
+                }
+
+                // recurse into the subelement
+                ReadAnimation(anim);
+            }
+            else if (IsElement("source"))
+            {
+                // possible animation data - we'll never know. Better store it
+                ReadSource();
+            }
+            else if (IsElement("sampler"))
+            {
+                // read the ID to assign the corresponding collada channel afterwards.
+                int indexID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(indexID);
+                ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first;
+
+                // have it read into a channel
+                ReadAnimationSampler(newChannel->second);
+            }
+            else if (IsElement("channel"))
+            {
+                // the binding element whose whole purpose is to provide the target to animate
+                // Thanks, Collada! A directly posted information would have been too simple, I guess.
+                // Better add another indirection to that! Can't have enough of those.
+                int indexTarget = GetAttribute("target");
+                int indexSource = GetAttribute("source");
+                const char* sourceId = mReader->getAttributeValue(indexSource);
+                if (sourceId[0] == '#')
+                    sourceId++;
+                ChannelMap::iterator cit = channels.find(sourceId);
+                if (cit != channels.end())
+                    cit->second.mTarget = mReader->getAttributeValue(indexTarget);
+
+                if (!mReader->isEmptyElement())
+                    SkipElement();
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "animation") != 0)
+                ThrowException("Expected end of <animation> element.");
+
+            break;
+        }
+    }
+
+    // it turned out to have channels - add them
+    if (!channels.empty())
+    {
+        // FIXME: Is this essentially doing the same as "single-anim-node" codepath in
+        //        ColladaLoader::StoreAnimations? For now, this has been deferred to after
+        //        all animations and all clips have been read. Due to handling of
+        //        <library_animation_clips> this cannot be done here, as the channel owner
+        //        is lost, and some exporters make up animations by referring to multiple
+        //        single-channel animations from an <instance_animation>.
+/*
+        // special filtering for stupid exporters packing each channel into a separate animation
+        if( channels.size() == 1)
+        {
+            pParent->mChannels.push_back( channels.begin()->second);
+        } else
+*/
+        {
+            // else create the animation, if not done yet, and store the channels
+            if (!anim)
+            {
+                anim = new Animation;
+                anim->mName = animName;
+                pParent->mSubAnims.push_back(anim);
+            }
+            for (ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it)
+                anim->mChannels.push_back(it->second);
+
+            if (indexID >= 0)
+            {
+                mAnimationLibrary[animID] = anim;
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an animation sampler into the given anim channel
+void ColladaParser::ReadAnimationSampler(Collada::AnimationChannel& pChannel)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("input"))
+            {
+                int indexSemantic = GetAttribute("semantic");
+                const char* semantic = mReader->getAttributeValue(indexSemantic);
+                int indexSource = GetAttribute("source");
+                const char* source = mReader->getAttributeValue(indexSource);
+                if (source[0] != '#')
+                    ThrowException("Unsupported URL format");
+                source++;
+
+                if (strcmp(semantic, "INPUT") == 0)
+                    pChannel.mSourceTimes = source;
+                else if (strcmp(semantic, "OUTPUT") == 0)
+                    pChannel.mSourceValues = source;
+                else if (strcmp(semantic, "IN_TANGENT") == 0)
+                    pChannel.mInTanValues = source;
+                else if (strcmp(semantic, "OUT_TANGENT") == 0)
+                    pChannel.mOutTanValues = source;
+                else if (strcmp(semantic, "INTERPOLATION") == 0)
+                    pChannel.mInterpolationValues = source;
+
+                if (!mReader->isEmptyElement())
+                    SkipElement();
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "sampler") != 0)
+                ThrowException("Expected end of <sampler> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the skeleton controller library
+void ColladaParser::ReadControllerLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("controller"))
+            {
+                // read ID. Ask the spec if it's necessary or optional... you might be surprised.
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                mControllerLibrary[id] = Controller();
+
+                // read on from there
+                ReadController(mControllerLibrary[id]);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_controllers") != 0)
+                ThrowException("Expected end of <library_controllers> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a controller into the given mesh structure
+void ColladaParser::ReadController(Collada::Controller& pController)
+{
+    // initial values
+    pController.mType = Skin;
+    pController.mMethod = Normalized;
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other
+            if (IsElement("morph"))
+            {
+                pController.mType = Morph;
+                int baseIndex = GetAttribute("source");
+                pController.mMeshId = mReader->getAttributeValue(baseIndex) + 1;
+                int methodIndex = GetAttribute("method");
+                if (methodIndex > 0) {
+                    const char *method = mReader->getAttributeValue(methodIndex);
+                    if (strcmp(method, "RELATIVE") == 0)
+                        pController.mMethod = Relative;
+                }
+            }
+            else if (IsElement("skin"))
+            {
+                // read the mesh it refers to. According to the spec this could also be another
+                // controller, but I refuse to implement every single idea they've come up with
+                int sourceIndex = GetAttribute("source");
+                pController.mMeshId = mReader->getAttributeValue(sourceIndex) + 1;
+            }
+            else if (IsElement("bind_shape_matrix"))
+            {
+                // content is 16 floats to define a matrix... it seems to be important for some models
+                const char* content = GetTextContent();
+
+                // read the 16 floats
+                for (unsigned int a = 0; a < 16; a++)
+                {
+                    // read a number
+                    content = fast_atoreal_move<ai_real>(content, pController.mBindShapeMatrix[a]);
+                    // skip whitespace after it
+                    SkipSpacesAndLineEnd(&content);
+                }
+
+                TestClosing("bind_shape_matrix");
+            }
+            else if (IsElement("source"))
+            {
+                // data array - we have specialists to handle this
+                ReadSource();
+            }
+            else if (IsElement("joints"))
+            {
+                ReadControllerJoints(pController);
+            }
+            else if (IsElement("vertex_weights"))
+            {
+                ReadControllerWeights(pController);
+            }
+            else if (IsElement("targets"))
+            {
+                while (mReader->read()) {
+                    if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+                        if (IsElement("input")) {
+                            int semanticsIndex = GetAttribute("semantic");
+                            int sourceIndex = GetAttribute("source");
+
+                            const char *semantics = mReader->getAttributeValue(semanticsIndex);
+                            const char *source = mReader->getAttributeValue(sourceIndex);
+                            if (strcmp(semantics, "MORPH_TARGET") == 0) {
+                                pController.mMorphTarget = source + 1;
+                            }
+                            else if (strcmp(semantics, "MORPH_WEIGHT") == 0)
+                            {
+                                pController.mMorphWeight = source + 1;
+                            }
+                        }
+                    }
+                    else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+                        if (strcmp(mReader->getNodeName(), "targets") == 0)
+                            break;
+                        else
+                            ThrowException("Expected end of <targets> element.");
+                    }
+                }
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "controller") == 0)
+                break;
+            else if (strcmp(mReader->getNodeName(), "skin") != 0 && strcmp(mReader->getNodeName(), "morph") != 0)
+                ThrowException("Expected end of <controller> element.");
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the joint definitions for the given controller
+void ColladaParser::ReadControllerJoints(Collada::Controller& pController)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX"
+            if (IsElement("input"))
+            {
+                int indexSemantic = GetAttribute("semantic");
+                const char* attrSemantic = mReader->getAttributeValue(indexSemantic);
+                int indexSource = GetAttribute("source");
+                const char* attrSource = mReader->getAttributeValue(indexSource);
+
+                // local URLS always start with a '#'. We don't support global URLs
+                if (attrSource[0] != '#')
+                    ThrowException(format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <joints> data <input> element");
+                attrSource++;
+
+                // parse source URL to corresponding source
+                if (strcmp(attrSemantic, "JOINT") == 0)
+                    pController.mJointNameSource = attrSource;
+                else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0)
+                    pController.mJointOffsetMatrixSource = attrSource;
+                else
+                    ThrowException(format() << "Unknown semantic \"" << attrSemantic << "\" in <joints> data <input> element");
+
+                // skip inner data, if present
+                if (!mReader->isEmptyElement())
+                    SkipElement();
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "joints") != 0)
+                ThrowException("Expected end of <joints> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the joint weights for the given controller
+void ColladaParser::ReadControllerWeights(Collada::Controller& pController)
+{
+    // read vertex count from attributes and resize the array accordingly
+    int indexCount = GetAttribute("count");
+    size_t vertexCount = (size_t)mReader->getAttributeValueAsInt(indexCount);
+    pController.mWeightCounts.resize(vertexCount);
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT"
+            if (IsElement("input") && vertexCount > 0)
+            {
+                InputChannel channel;
+
+                int indexSemantic = GetAttribute("semantic");
+                const char* attrSemantic = mReader->getAttributeValue(indexSemantic);
+                int indexSource = GetAttribute("source");
+                const char* attrSource = mReader->getAttributeValue(indexSource);
+                int indexOffset = TestAttribute("offset");
+                if (indexOffset >= 0)
+                    channel.mOffset = mReader->getAttributeValueAsInt(indexOffset);
+
+                // local URLS always start with a '#'. We don't support global URLs
+                if (attrSource[0] != '#')
+                    ThrowException(format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <vertex_weights> data <input> element");
+                channel.mAccessor = attrSource + 1;
+
+                // parse source URL to corresponding source
+                if (strcmp(attrSemantic, "JOINT") == 0)
+                    pController.mWeightInputJoints = channel;
+                else if (strcmp(attrSemantic, "WEIGHT") == 0)
+                    pController.mWeightInputWeights = channel;
+                else
+                    ThrowException(format() << "Unknown semantic \"" << attrSemantic << "\" in <vertex_weights> data <input> element");
+
+                // skip inner data, if present
+                if (!mReader->isEmptyElement())
+                    SkipElement();
+            }
+            else if (IsElement("vcount") && vertexCount > 0)
+            {
+                // read weight count per vertex
+                const char* text = GetTextContent();
+                size_t numWeights = 0;
+                for (std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it)
+                {
+                    if (*text == 0)
+                        ThrowException("Out of data while reading <vcount>");
+
+                    *it = strtoul10(text, &text);
+                    numWeights += *it;
+                    SkipSpacesAndLineEnd(&text);
+                }
+
+                TestClosing("vcount");
+
+                // reserve weight count
+                pController.mWeights.resize(numWeights);
+            }
+            else if (IsElement("v") && vertexCount > 0)
+            {
+                // read JointIndex - WeightIndex pairs
+                const char* text = GetTextContent();
+
+                for (std::vector< std::pair<size_t, size_t> >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it)
+                {
+                    if (*text == 0)
+                        ThrowException("Out of data while reading <vertex_weights>");
+                    it->first = strtoul10(text, &text);
+                    SkipSpacesAndLineEnd(&text);
+                    if (*text == 0)
+                        ThrowException("Out of data while reading <vertex_weights>");
+                    it->second = strtoul10(text, &text);
+                    SkipSpacesAndLineEnd(&text);
+                }
+
+                TestClosing("v");
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "vertex_weights") != 0)
+                ThrowException("Expected end of <vertex_weights> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the image library contents
+void ColladaParser::ReadImageLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("image"))
+            {
+                // read ID. Another entry which is "optional" by design but obligatory in reality
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                mImageLibrary[id] = Image();
+
+                // read on from there
+                ReadImage(mImageLibrary[id]);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "library_images") != 0)
+                ThrowException("Expected end of <library_images> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an image entry into the given image
+void ColladaParser::ReadImage(Collada::Image& pImage)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            // Need to run different code paths here, depending on the Collada XSD version
+            if (IsElement("image")) {
+                SkipElement();
+            }
+            else if (IsElement("init_from"))
+            {
+                if (mFormat == FV_1_4_n)
+                {
+                    // FIX: C4D exporter writes empty <init_from/> tags
+                    if (!mReader->isEmptyElement()) {
+                        // element content is filename - hopefully
+                        const char* sz = TestTextContent();
+                        if (sz)
+                        {
+                            aiString filepath(sz);
+                            UriDecodePath(filepath);
+                            pImage.mFileName = filepath.C_Str();
+                        }
+                        TestClosing("init_from");
+                    }
+                    if (!pImage.mFileName.length()) {
+                        pImage.mFileName = "unknown_texture";
+                    }
+                }
+                else if (mFormat == FV_1_5_n)
+                {
+                    // make sure we skip over mip and array initializations, which
+                    // we don't support, but which could confuse the loader if
+                    // they're not skipped.
+                    int attrib = TestAttribute("array_index");
+                    if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
+                        ASSIMP_LOG_WARN("Collada: Ignoring texture array index");
+                        continue;
+                    }
+
+                    attrib = TestAttribute("mip_index");
+                    if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
+                        ASSIMP_LOG_WARN("Collada: Ignoring MIP map layer");
+                        continue;
+                    }
+
+                    // TODO: correctly jump over cube and volume maps?
+                }
+            }
+            else if (mFormat == FV_1_5_n)
+            {
+                if (IsElement("ref"))
+                {
+                    // element content is filename - hopefully
+                    const char* sz = TestTextContent();
+                    if (sz)
+                    {
+                        aiString filepath(sz);
+                        UriDecodePath(filepath);
+                        pImage.mFileName = filepath.C_Str();
+                    }
+                    TestClosing("ref");
+                }
+                else if (IsElement("hex") && !pImage.mFileName.length())
+                {
+                    // embedded image. get format
+                    const int attrib = TestAttribute("format");
+                    if (-1 == attrib)
+                        ASSIMP_LOG_WARN("Collada: Unknown image file format");
+                    else pImage.mEmbeddedFormat = mReader->getAttributeValue(attrib);
+
+                    const char* data = GetTextContent();
+
+                    // hexadecimal-encoded binary octets. First of all, find the
+                    // required buffer size to reserve enough storage.
+                    const char* cur = data;
+                    while (!IsSpaceOrNewLine(*cur)) cur++;
+
+                    const unsigned int size = (unsigned int)(cur - data) * 2;
+                    pImage.mImageData.resize(size);
+                    for (unsigned int i = 0; i < size; ++i)
+                        pImage.mImageData[i] = HexOctetToDecimal(data + (i << 1));
+
+                    TestClosing("hex");
+                }
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "image") == 0)
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the material library
+void ColladaParser::ReadMaterialLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    std::map<std::string, int> names;
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("material"))
+            {
+                // read ID. By now you probably know my opinion about this "specification"
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                std::string name;
+                int attrName = TestAttribute("name");
+                if (attrName >= 0)
+                    name = mReader->getAttributeValue(attrName);
+
+                // create an entry and store it in the library under its ID
+                mMaterialLibrary[id] = Material();
+
+                if (!name.empty())
+                {
+                    std::map<std::string, int>::iterator it = names.find(name);
+                    if (it != names.end())
+                    {
+                        std::ostringstream strStream;
+                        strStream << ++it->second;
+                        name.append(" " + strStream.str());
+                    }
+                    else
+                    {
+                        names[name] = 0;
+                    }
+
+                    mMaterialLibrary[id].mName = name;
+                }
+
+                ReadMaterial(mMaterialLibrary[id]);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_materials") != 0)
+                ThrowException("Expected end of <library_materials> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the light library
+void ColladaParser::ReadLightLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("light"))
+            {
+                // read ID. By now you probably know my opinion about this "specification"
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                ReadLight(mLightLibrary[id] = Light());
+
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "library_lights") != 0)
+                ThrowException("Expected end of <library_lights> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the camera library
+void ColladaParser::ReadCameraLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("camera"))
+            {
+                // read ID. By now you probably know my opinion about this "specification"
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                Camera& cam = mCameraLibrary[id];
+                attrID = TestAttribute("name");
+                if (attrID != -1)
+                    cam.mName = mReader->getAttributeValue(attrID);
+
+                ReadCamera(cam);
+
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "library_cameras") != 0)
+                ThrowException("Expected end of <library_cameras> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a material entry into the given material
+void ColladaParser::ReadMaterial(Collada::Material& pMaterial)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("material")) {
+                SkipElement();
+            }
+            else if (IsElement("instance_effect"))
+            {
+                // referred effect by URL
+                int attrUrl = GetAttribute("url");
+                const char* url = mReader->getAttributeValue(attrUrl);
+                if (url[0] != '#')
+                    ThrowException("Unknown reference format");
+
+                pMaterial.mEffect = url + 1;
+
+                SkipElement();
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "material") != 0)
+                ThrowException("Expected end of <material> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a light entry into the given light
+void ColladaParser::ReadLight(Collada::Light& pLight)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("light")) {
+                SkipElement();
+            }
+            else if (IsElement("spot")) {
+                pLight.mType = aiLightSource_SPOT;
+            }
+            else if (IsElement("ambient")) {
+                pLight.mType = aiLightSource_AMBIENT;
+            }
+            else if (IsElement("directional")) {
+                pLight.mType = aiLightSource_DIRECTIONAL;
+            }
+            else if (IsElement("point")) {
+                pLight.mType = aiLightSource_POINT;
+            }
+            else if (IsElement("color")) {
+                // text content contains 3 floats
+                const char* content = GetTextContent();
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pLight.mColor.r);
+                SkipSpacesAndLineEnd(&content);
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pLight.mColor.g);
+                SkipSpacesAndLineEnd(&content);
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pLight.mColor.b);
+                SkipSpacesAndLineEnd(&content);
+
+                TestClosing("color");
+            }
+            else if (IsElement("constant_attenuation")) {
+                pLight.mAttConstant = ReadFloatFromTextContent();
+                TestClosing("constant_attenuation");
+            }
+            else if (IsElement("linear_attenuation")) {
+                pLight.mAttLinear = ReadFloatFromTextContent();
+                TestClosing("linear_attenuation");
+            }
+            else if (IsElement("quadratic_attenuation")) {
+                pLight.mAttQuadratic = ReadFloatFromTextContent();
+                TestClosing("quadratic_attenuation");
+            }
+            else if (IsElement("falloff_angle")) {
+                pLight.mFalloffAngle = ReadFloatFromTextContent();
+                TestClosing("falloff_angle");
+            }
+            else if (IsElement("falloff_exponent")) {
+                pLight.mFalloffExponent = ReadFloatFromTextContent();
+                TestClosing("falloff_exponent");
+            }
+            // FCOLLADA extensions
+            // -------------------------------------------------------
+            else if (IsElement("outer_cone")) {
+                pLight.mOuterAngle = ReadFloatFromTextContent();
+                TestClosing("outer_cone");
+            }
+            // ... and this one is even deprecated
+            else if (IsElement("penumbra_angle")) {
+                pLight.mPenumbraAngle = ReadFloatFromTextContent();
+                TestClosing("penumbra_angle");
+            }
+            else if (IsElement("intensity")) {
+                pLight.mIntensity = ReadFloatFromTextContent();
+                TestClosing("intensity");
+            }
+            else if (IsElement("falloff")) {
+                pLight.mOuterAngle = ReadFloatFromTextContent();
+                TestClosing("falloff");
+            }
+            else if (IsElement("hotspot_beam")) {
+                pLight.mFalloffAngle = ReadFloatFromTextContent();
+                TestClosing("hotspot_beam");
+            }
+            // OpenCOLLADA extensions
+            // -------------------------------------------------------
+            else if (IsElement("decay_falloff")) {
+                pLight.mOuterAngle = ReadFloatFromTextContent();
+                TestClosing("decay_falloff");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "light") == 0)
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a camera entry into the given light
+void ColladaParser::ReadCamera(Collada::Camera& pCamera)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("camera")) {
+                SkipElement();
+            }
+            else if (IsElement("orthographic")) {
+                pCamera.mOrtho = true;
+            }
+            else if (IsElement("xfov") || IsElement("xmag")) {
+                pCamera.mHorFov = ReadFloatFromTextContent();
+                TestClosing((pCamera.mOrtho ? "xmag" : "xfov"));
+            }
+            else if (IsElement("yfov") || IsElement("ymag")) {
+                pCamera.mVerFov = ReadFloatFromTextContent();
+                TestClosing((pCamera.mOrtho ? "ymag" : "yfov"));
+            }
+            else if (IsElement("aspect_ratio")) {
+                pCamera.mAspect = ReadFloatFromTextContent();
+                TestClosing("aspect_ratio");
+            }
+            else if (IsElement("znear")) {
+                pCamera.mZNear = ReadFloatFromTextContent();
+                TestClosing("znear");
+            }
+            else if (IsElement("zfar")) {
+                pCamera.mZFar = ReadFloatFromTextContent();
+                TestClosing("zfar");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "camera") == 0)
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the effect library
+void ColladaParser::ReadEffectLibrary()
+{
+    if (mReader->isEmptyElement()) {
+        return;
+    }
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("effect"))
+            {
+                // read ID. Do I have to repeat my ranting about "optional" attributes?
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                mEffectLibrary[id] = Effect();
+                // read on from there
+                ReadEffect(mEffectLibrary[id]);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "library_effects") != 0)
+                ThrowException("Expected end of <library_effects> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry into the given effect
+void ColladaParser::ReadEffect(Collada::Effect& pEffect)
+{
+    // for the moment we don't support any other type of effect.
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("profile_COMMON"))
+                ReadEffectProfileCommon(pEffect);
+            else
+                SkipElement();
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "effect") != 0)
+                ThrowException("Expected end of <effect> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an COMMON effect profile
+void ColladaParser::ReadEffectProfileCommon(Collada::Effect& pEffect)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("newparam")) {
+                // save ID
+                int attrSID = GetAttribute("sid");
+                std::string sid = mReader->getAttributeValue(attrSID);
+                pEffect.mParams[sid] = EffectParam();
+                ReadEffectParam(pEffect.mParams[sid]);
+            }
+            else if (IsElement("technique") || IsElement("extra"))
+            {
+                // just syntactic sugar
+            }
+
+            else if (mFormat == FV_1_4_n && IsElement("image"))
+            {
+                // read ID. Another entry which is "optional" by design but obligatory in reality
+                int attrID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(attrID);
+
+                // create an entry and store it in the library under its ID
+                mImageLibrary[id] = Image();
+
+                // read on from there
+                ReadImage(mImageLibrary[id]);
+            }
+
+            /* Shading modes */
+            else if (IsElement("phong"))
+                pEffect.mShadeType = Shade_Phong;
+            else if (IsElement("constant"))
+                pEffect.mShadeType = Shade_Constant;
+            else if (IsElement("lambert"))
+                pEffect.mShadeType = Shade_Lambert;
+            else if (IsElement("blinn"))
+                pEffect.mShadeType = Shade_Blinn;
+
+            /* Color + texture properties */
+            else if (IsElement("emission"))
+                ReadEffectColor(pEffect.mEmissive, pEffect.mTexEmissive);
+            else if (IsElement("ambient"))
+                ReadEffectColor(pEffect.mAmbient, pEffect.mTexAmbient);
+            else if (IsElement("diffuse"))
+                ReadEffectColor(pEffect.mDiffuse, pEffect.mTexDiffuse);
+            else if (IsElement("specular"))
+                ReadEffectColor(pEffect.mSpecular, pEffect.mTexSpecular);
+            else if (IsElement("reflective")) {
+                ReadEffectColor(pEffect.mReflective, pEffect.mTexReflective);
+            }
+            else if (IsElement("transparent")) {
+                pEffect.mHasTransparency = true;
+
+                const char* opaque = mReader->getAttributeValueSafe("opaque");
+
+                if (::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "RGB_ONE") == 0) {
+                    pEffect.mRGBTransparency = true;
+                }
+
+                // In RGB_ZERO mode, the transparency is interpreted in reverse, go figure...
+                if (::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "A_ZERO") == 0) {
+                    pEffect.mInvertTransparency = true;
+                }
+
+                ReadEffectColor(pEffect.mTransparent, pEffect.mTexTransparent);
+            }
+            else if (IsElement("shininess"))
+                ReadEffectFloat(pEffect.mShininess);
+            else if (IsElement("reflectivity"))
+                ReadEffectFloat(pEffect.mReflectivity);
+
+            /* Single scalar properties */
+            else if (IsElement("transparency"))
+                ReadEffectFloat(pEffect.mTransparency);
+            else if (IsElement("index_of_refraction"))
+                ReadEffectFloat(pEffect.mRefractIndex);
+
+            // GOOGLEEARTH/OKINO extensions
+            // -------------------------------------------------------
+            else if (IsElement("double_sided"))
+                pEffect.mDoubleSided = ReadBoolFromTextContent();
+
+            // FCOLLADA extensions
+            // -------------------------------------------------------
+            else if (IsElement("bump")) {
+                aiColor4D dummy;
+                ReadEffectColor(dummy, pEffect.mTexBump);
+            }
+
+            // MAX3D extensions
+            // -------------------------------------------------------
+            else if (IsElement("wireframe")) {
+                pEffect.mWireframe = ReadBoolFromTextContent();
+                TestClosing("wireframe");
+            }
+            else if (IsElement("faceted")) {
+                pEffect.mFaceted = ReadBoolFromTextContent();
+                TestClosing("faceted");
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "profile_COMMON") == 0)
+            {
+                break;
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read texture wrapping + UV transform settings from a profile==Maya chunk
+void ColladaParser::ReadSamplerProperties(Sampler& out)
+{
+    if (mReader->isEmptyElement()) {
+        return;
+    }
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+
+            // MAYA extensions
+            // -------------------------------------------------------
+            if (IsElement("wrapU")) {
+                out.mWrapU = ReadBoolFromTextContent();
+                TestClosing("wrapU");
+            }
+            else if (IsElement("wrapV")) {
+                out.mWrapV = ReadBoolFromTextContent();
+                TestClosing("wrapV");
+            }
+            else if (IsElement("mirrorU")) {
+                out.mMirrorU = ReadBoolFromTextContent();
+                TestClosing("mirrorU");
+            }
+            else if (IsElement("mirrorV")) {
+                out.mMirrorV = ReadBoolFromTextContent();
+                TestClosing("mirrorV");
+            }
+            else if (IsElement("repeatU")) {
+                out.mTransform.mScaling.x = ReadFloatFromTextContent();
+                TestClosing("repeatU");
+            }
+            else if (IsElement("repeatV")) {
+                out.mTransform.mScaling.y = ReadFloatFromTextContent();
+                TestClosing("repeatV");
+            }
+            else if (IsElement("offsetU")) {
+                out.mTransform.mTranslation.x = ReadFloatFromTextContent();
+                TestClosing("offsetU");
+            }
+            else if (IsElement("offsetV")) {
+                out.mTransform.mTranslation.y = ReadFloatFromTextContent();
+                TestClosing("offsetV");
+            }
+            else if (IsElement("rotateUV")) {
+                out.mTransform.mRotation = ReadFloatFromTextContent();
+                TestClosing("rotateUV");
+            }
+            else if (IsElement("blend_mode")) {
+
+                const char* sz = GetTextContent();
+                // http://www.feelingsoftware.com/content/view/55/72/lang,en/
+                // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE
+                if (0 == ASSIMP_strincmp(sz, "ADD", 3))
+                    out.mOp = aiTextureOp_Add;
+
+                else if (0 == ASSIMP_strincmp(sz, "SUBTRACT", 8))
+                    out.mOp = aiTextureOp_Subtract;
+
+                else if (0 == ASSIMP_strincmp(sz, "MULTIPLY", 8))
+                    out.mOp = aiTextureOp_Multiply;
+
+                else {
+                    ASSIMP_LOG_WARN("Collada: Unsupported MAYA texture blend mode");
+                }
+                TestClosing("blend_mode");
+            }
+            // OKINO extensions
+            // -------------------------------------------------------
+            else if (IsElement("weighting")) {
+                out.mWeighting = ReadFloatFromTextContent();
+                TestClosing("weighting");
+            }
+            else if (IsElement("mix_with_previous_layer")) {
+                out.mMixWithPrevious = ReadFloatFromTextContent();
+                TestClosing("mix_with_previous_layer");
+            }
+            // MAX3D extensions
+            // -------------------------------------------------------
+            else if (IsElement("amount")) {
+                out.mWeighting = ReadFloatFromTextContent();
+                TestClosing("amount");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "technique") == 0)
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry containing a color or a texture defining that color
+void ColladaParser::ReadEffectColor(aiColor4D& pColor, Sampler& pSampler)
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    // Save current element name
+    const std::string curElem = mReader->getNodeName();
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("color"))
+            {
+                // text content contains 4 floats
+                const char* content = GetTextContent();
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pColor.r);
+                SkipSpacesAndLineEnd(&content);
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pColor.g);
+                SkipSpacesAndLineEnd(&content);
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pColor.b);
+                SkipSpacesAndLineEnd(&content);
+
+                content = fast_atoreal_move<ai_real>(content, (ai_real&)pColor.a);
+                SkipSpacesAndLineEnd(&content);
+                TestClosing("color");
+            }
+            else if (IsElement("texture"))
+            {
+                // get name of source texture/sampler
+                int attrTex = GetAttribute("texture");
+                pSampler.mName = mReader->getAttributeValue(attrTex);
+
+                // get name of UV source channel. Specification demands it to be there, but some exporters
+                // don't write it. It will be the default UV channel in case it's missing.
+                attrTex = TestAttribute("texcoord");
+                if (attrTex >= 0)
+                    pSampler.mUVChannel = mReader->getAttributeValue(attrTex);
+                //SkipElement();
+
+                // as we've read texture, the color needs to be 1,1,1,1
+                pColor = aiColor4D(1.f, 1.f, 1.f, 1.f);
+            }
+            else if (IsElement("technique"))
+            {
+                const int _profile = GetAttribute("profile");
+                const char* profile = mReader->getAttributeValue(_profile);
+
+                // Some extensions are quite useful ... ReadSamplerProperties processes
+                // several extensions in MAYA, OKINO and MAX3D profiles.
+                if (!::strcmp(profile, "MAYA") || !::strcmp(profile, "MAX3D") || !::strcmp(profile, "OKINO"))
+                {
+                    // get more information on this sampler
+                    ReadSamplerProperties(pSampler);
+                }
+                else SkipElement();
+            }
+            else if (!IsElement("extra"))
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (mReader->getNodeName() == curElem)
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry containing a float
+void ColladaParser::ReadEffectFloat(ai_real& pFloat)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("float"))
+            {
+                // text content contains a single floats
+                const char* content = GetTextContent();
+                content = fast_atoreal_move<ai_real>(content, pFloat);
+                SkipSpacesAndLineEnd(&content);
+
+                TestClosing("float");
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect parameter specification of any kind
+void ColladaParser::ReadEffectParam(Collada::EffectParam& pParam)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("surface"))
+            {
+                // image ID given inside <init_from> tags
+                TestOpening("init_from");
+                const char* content = GetTextContent();
+                pParam.mType = Param_Surface;
+                pParam.mReference = content;
+                TestClosing("init_from");
+
+                // don't care for remaining stuff
+                SkipElement("surface");
+            }
+            else if (IsElement("sampler2D") && (FV_1_4_n == mFormat || FV_1_3_n == mFormat))
+            {
+                // surface ID is given inside <source> tags
+                TestOpening("source");
+                const char* content = GetTextContent();
+                pParam.mType = Param_Sampler;
+                pParam.mReference = content;
+                TestClosing("source");
+
+                // don't care for remaining stuff
+                SkipElement("sampler2D");
+            }
+            else if (IsElement("sampler2D"))
+            {
+                // surface ID is given inside <instance_image> tags
+                TestOpening("instance_image");
+                int attrURL = GetAttribute("url");
+                const char* url = mReader->getAttributeValue(attrURL);
+                if (url[0] != '#')
+                    ThrowException("Unsupported URL format in instance_image");
+                url++;
+                pParam.mType = Param_Sampler;
+                pParam.mReference = url;
+                SkipElement("sampler2D");
+            }
+            else
+            {
+                // ignore unknown element
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the geometry library contents
+void ColladaParser::ReadGeometryLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("geometry"))
+            {
+                // read ID. Another entry which is "optional" by design but obligatory in reality
+                int indexID = GetAttribute("id");
+                std::string id = mReader->getAttributeValue(indexID);
+
+                // TODO: (thom) support SIDs
+                // ai_assert( TestAttribute( "sid") == -1);
+
+                // create a mesh and store it in the library under its ID
+                Mesh* mesh = new Mesh;
+                mMeshLibrary[id] = mesh;
+
+                // read the mesh name if it exists
+                const int nameIndex = TestAttribute("name");
+                if (nameIndex != -1)
+                {
+                    mesh->mName = mReader->getAttributeValue(nameIndex);
+                }
+
+                // read on from there
+                ReadGeometry(mesh);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_geometries") != 0)
+                ThrowException("Expected end of <library_geometries> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a geometry from the geometry library.
+void ColladaParser::ReadGeometry(Collada::Mesh* pMesh)
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("mesh"))
+            {
+                // read on from there
+                ReadMesh(pMesh);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "geometry") != 0)
+                ThrowException("Expected end of <geometry> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a mesh from the geometry library
+void ColladaParser::ReadMesh(Mesh* pMesh)
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("source"))
+            {
+                // we have professionals dealing with this
+                ReadSource();
+            }
+            else if (IsElement("vertices"))
+            {
+                // read per-vertex mesh data
+                ReadVertexData(pMesh);
+            }
+            else if (IsElement("triangles") || IsElement("lines") || IsElement("linestrips")
+                || IsElement("polygons") || IsElement("polylist") || IsElement("trifans") || IsElement("tristrips"))
+            {
+                // read per-index mesh data and faces setup
+                ReadIndexData(pMesh);
+            }
+            else
+            {
+                // ignore the restf
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "technique_common") == 0)
+            {
+                // end of another meaningless element - read over it
+            }
+            else if (strcmp(mReader->getNodeName(), "mesh") == 0)
+            {
+                // end of <mesh> element - we're done here
+                break;
+            }
+            else
+            {
+                // everything else should be punished
+                ThrowException("Expected end of <mesh> element.");
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a source element
+void ColladaParser::ReadSource()
+{
+    int indexID = GetAttribute("id");
+    std::string sourceID = mReader->getAttributeValue(indexID);
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("float_array") || IsElement("IDREF_array") || IsElement("Name_array"))
+            {
+                ReadDataArray();
+            }
+            else if (IsElement("technique_common"))
+            {
+                // I don't care for your profiles
+            }
+            else if (IsElement("accessor"))
+            {
+                ReadAccessor(sourceID);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "source") == 0)
+            {
+                // end of <source> - we're done
+                break;
+            }
+            else if (strcmp(mReader->getNodeName(), "technique_common") == 0)
+            {
+                // end of another meaningless element - read over it
+            }
+            else
+            {
+                // everything else should be punished
+                ThrowException("Expected end of <source> element.");
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a data array holding a number of floats, and stores it in the global library
+void ColladaParser::ReadDataArray()
+{
+    std::string elmName = mReader->getNodeName();
+    bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array");
+    bool isEmptyElement = mReader->isEmptyElement();
+
+    // read attributes
+    int indexID = GetAttribute("id");
+    std::string id = mReader->getAttributeValue(indexID);
+    int indexCount = GetAttribute("count");
+    unsigned int count = (unsigned int)mReader->getAttributeValueAsInt(indexCount);
+    const char* content = TestTextContent();
+
+    // read values and store inside an array in the data library
+    mDataLibrary[id] = Data();
+    Data& data = mDataLibrary[id];
+    data.mIsStringArray = isStringArray;
+
+    // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them
+    if (content)
+    {
+        if (isStringArray)
+        {
+            data.mStrings.reserve(count);
+            std::string s;
+
+            for (unsigned int a = 0; a < count; a++)
+            {
+                if (*content == 0)
+                    ThrowException("Expected more values while reading IDREF_array contents.");
+
+                s.clear();
+                while (!IsSpaceOrNewLine(*content))
+                    s += *content++;
+                data.mStrings.push_back(s);
+
+                SkipSpacesAndLineEnd(&content);
+            }
+        }
+        else
+        {
+            data.mValues.reserve(count);
+
+            for (unsigned int a = 0; a < count; a++)
+            {
+                if (*content == 0)
+                    ThrowException("Expected more values while reading float_array contents.");
+
+                ai_real value;
+                // read a number
+                content = fast_atoreal_move<ai_real>(content, value);
+                data.mValues.push_back(value);
+                // skip whitespace after it
+                SkipSpacesAndLineEnd(&content);
+            }
+        }
+    }
+
+    // test for closing tag
+    if (!isEmptyElement)
+        TestClosing(elmName.c_str());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an accessor and stores it in the global library
+void ColladaParser::ReadAccessor(const std::string& pID)
+{
+    // read accessor attributes
+    int attrSource = GetAttribute("source");
+    const char* source = mReader->getAttributeValue(attrSource);
+    if (source[0] != '#')
+        ThrowException(format() << "Unknown reference format in url \"" << source << "\" in source attribute of <accessor> element.");
+    int attrCount = GetAttribute("count");
+    unsigned int count = (unsigned int)mReader->getAttributeValueAsInt(attrCount);
+    int attrOffset = TestAttribute("offset");
+    unsigned int offset = 0;
+    if (attrOffset > -1)
+        offset = (unsigned int)mReader->getAttributeValueAsInt(attrOffset);
+    int attrStride = TestAttribute("stride");
+    unsigned int stride = 1;
+    if (attrStride > -1)
+        stride = (unsigned int)mReader->getAttributeValueAsInt(attrStride);
+
+    // store in the library under the given ID
+    mAccessorLibrary[pID] = Accessor();
+    Accessor& acc = mAccessorLibrary[pID];
+    acc.mCount = count;
+    acc.mOffset = offset;
+    acc.mStride = stride;
+    acc.mSource = source + 1; // ignore the leading '#'
+    acc.mSize = 0; // gets incremented with every param
+
+    // and read the components
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("param"))
+            {
+                // read data param
+                int attrName = TestAttribute("name");
+                std::string name;
+                if (attrName > -1)
+                {
+                    name = mReader->getAttributeValue(attrName);
+
+                    // analyse for common type components and store it's sub-offset in the corresponding field
+
+                    /* Cartesian coordinates */
+                    if (name == "X") acc.mSubOffset[0] = acc.mParams.size();
+                    else if (name == "Y") acc.mSubOffset[1] = acc.mParams.size();
+                    else if (name == "Z") acc.mSubOffset[2] = acc.mParams.size();
+
+                    /* RGBA colors */
+                    else if (name == "R") acc.mSubOffset[0] = acc.mParams.size();
+                    else if (name == "G") acc.mSubOffset[1] = acc.mParams.size();
+                    else if (name == "B") acc.mSubOffset[2] = acc.mParams.size();
+                    else if (name == "A") acc.mSubOffset[3] = acc.mParams.size();
+
+                    /* UVWQ (STPQ) texture coordinates */
+                    else if (name == "S") acc.mSubOffset[0] = acc.mParams.size();
+                    else if (name == "T") acc.mSubOffset[1] = acc.mParams.size();
+                    else if (name == "P") acc.mSubOffset[2] = acc.mParams.size();
+                    //  else if( name == "Q") acc.mSubOffset[3] = acc.mParams.size();
+                        /* 4D uv coordinates are not supported in Assimp */
+
+                        /* Generic extra data, interpreted as UV data, too*/
+                    else if (name == "U") acc.mSubOffset[0] = acc.mParams.size();
+                    else if (name == "V") acc.mSubOffset[1] = acc.mParams.size();
+                    //else
+                    //  DefaultLogger::get()->warn( format() << "Unknown accessor parameter \"" << name << "\". Ignoring data channel." );
+                }
+
+                // read data type
+                int attrType = TestAttribute("type");
+                if (attrType > -1)
+                {
+                    // for the moment we only distinguish between a 4x4 matrix and anything else.
+                    // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types
+                    // which should be tested for here.
+                    std::string type = mReader->getAttributeValue(attrType);
+                    if (type == "float4x4")
+                        acc.mSize += 16;
+                    else
+                        acc.mSize += 1;
+                }
+
+                acc.mParams.push_back(name);
+
+                // skip remaining stuff of this element, if any
+                SkipElement();
+            }
+            else
+            {
+                ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <accessor>");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "accessor") != 0)
+                ThrowException("Expected end of <accessor> element.");
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads input declarations of per-vertex mesh data into the given mesh
+void ColladaParser::ReadVertexData(Mesh* pMesh)
+{
+    // extract the ID of the <vertices> element. Not that we care, but to catch strange referencing schemes we should warn about
+    int attrID = GetAttribute("id");
+    pMesh->mVertexID = mReader->getAttributeValue(attrID);
+
+    // a number of <input> elements
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("input"))
+            {
+                ReadInputChannel(pMesh->mPerVertexData);
+            }
+            else
+            {
+                ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <vertices>");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "vertices") != 0)
+                ThrowException("Expected end of <vertices> element.");
+
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads input declarations of per-index mesh data into the given mesh
+void ColladaParser::ReadIndexData(Mesh* pMesh)
+{
+    std::vector<size_t> vcount;
+    std::vector<InputChannel> perIndexData;
+
+    // read primitive count from the attribute
+    int attrCount = GetAttribute("count");
+    size_t numPrimitives = (size_t)mReader->getAttributeValueAsInt(attrCount);
+    // some mesh types (e.g. tristrips) don't specify primitive count upfront,
+    // so we need to sum up the actual number of primitives while we read the <p>-tags
+    size_t actualPrimitives = 0;
+
+    // material subgroup
+    int attrMaterial = TestAttribute("material");
+    SubMesh subgroup;
+    if (attrMaterial > -1)
+        subgroup.mMaterial = mReader->getAttributeValue(attrMaterial);
+
+    // distinguish between polys and triangles
+    std::string elementName = mReader->getNodeName();
+    PrimitiveType primType = Prim_Invalid;
+    if (IsElement("lines"))
+        primType = Prim_Lines;
+    else if (IsElement("linestrips"))
+        primType = Prim_LineStrip;
+    else if (IsElement("polygons"))
+        primType = Prim_Polygon;
+    else if (IsElement("polylist"))
+        primType = Prim_Polylist;
+    else if (IsElement("triangles"))
+        primType = Prim_Triangles;
+    else if (IsElement("trifans"))
+        primType = Prim_TriFans;
+    else if (IsElement("tristrips"))
+        primType = Prim_TriStrips;
+
+    ai_assert(primType != Prim_Invalid);
+
+    // also a number of <input> elements, but in addition a <p> primitive collection and probably index counts for all primitives
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("input"))
+            {
+                ReadInputChannel(perIndexData);
+            }
+            else if (IsElement("vcount"))
+            {
+                if (!mReader->isEmptyElement())
+                {
+                    if (numPrimitives)  // It is possible to define a mesh without any primitives
+                    {
+                        // case <polylist> - specifies the number of indices for each polygon
+                        const char* content = GetTextContent();
+                        vcount.reserve(numPrimitives);
+                        for (unsigned int a = 0; a < numPrimitives; a++)
+                        {
+                            if (*content == 0)
+                                ThrowException("Expected more values while reading <vcount> contents.");
+                            // read a number
+                            vcount.push_back((size_t)strtoul10(content, &content));
+                            // skip whitespace after it
+                            SkipSpacesAndLineEnd(&content);
+                        }
+                    }
+
+                    TestClosing("vcount");
+                }
+            }
+            else if (IsElement("p"))
+            {
+                if (!mReader->isEmptyElement())
+                {
+                    // now here the actual fun starts - these are the indices to construct the mesh data from
+                    actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType);
+                }
+            }
+            else if (IsElement("extra"))
+            {
+                SkipElement("extra");
+            }
+            else if (IsElement("ph")) {
+                SkipElement("ph");
+            }
+            else {
+                ThrowException(format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <" << elementName << ">");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (mReader->getNodeName() != elementName)
+                ThrowException(format() << "Expected end of <" << elementName << "> element.");
+
+            break;
+        }
+    }
+
+#ifdef ASSIMP_BUILD_DEBUG
+    if (primType != Prim_TriFans && primType != Prim_TriStrips && primType != Prim_LineStrip &&
+        primType != Prim_Lines) { // this is ONLY to workaround a bug in SketchUp 15.3.331 where it writes the wrong 'count' when it writes out the 'lines'.
+        ai_assert(actualPrimitives == numPrimitives);
+    }
+#endif
+
+    // only when we're done reading all <p> tags (and thus know the final vertex count) can we commit the submesh
+    subgroup.mNumFaces = actualPrimitives;
+    pMesh->mSubMeshes.push_back(subgroup);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single input channel element and stores it in the given array, if valid
+void ColladaParser::ReadInputChannel(std::vector<InputChannel>& poChannels)
+{
+    InputChannel channel;
+
+    // read semantic
+    int attrSemantic = GetAttribute("semantic");
+    std::string semantic = mReader->getAttributeValue(attrSemantic);
+    channel.mType = GetTypeForSemantic(semantic);
+
+    // read source
+    int attrSource = GetAttribute("source");
+    const char* source = mReader->getAttributeValue(attrSource);
+    if (source[0] != '#')
+        ThrowException(format() << "Unknown reference format in url \"" << source << "\" in source attribute of <input> element.");
+    channel.mAccessor = source + 1; // skipping the leading #, hopefully the remaining text is the accessor ID only
+
+    // read index offset, if per-index <input>
+    int attrOffset = TestAttribute("offset");
+    if (attrOffset > -1)
+        channel.mOffset = mReader->getAttributeValueAsInt(attrOffset);
+
+    // read set if texture coordinates
+    if (channel.mType == IT_Texcoord || channel.mType == IT_Color) {
+        int attrSet = TestAttribute("set");
+        if (attrSet > -1) {
+            attrSet = mReader->getAttributeValueAsInt(attrSet);
+            if (attrSet < 0)
+                ThrowException(format() << "Invalid index \"" << (attrSet) << "\" in set attribute of <input> element");
+
+            channel.mIndex = attrSet;
+        }
+    }
+
+    // store, if valid type
+    if (channel.mType != IT_Invalid)
+        poChannels.push_back(channel);
+
+    // skip remaining stuff of this element, if any
+    SkipElement();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a <p> primitive index list and assembles the mesh data into the given mesh
+size_t ColladaParser::ReadPrimitives(Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels,
+    size_t pNumPrimitives, const std::vector<size_t>& pVCount, PrimitiveType pPrimType)
+{
+    // determine number of indices coming per vertex
+    // find the offset index for all per-vertex channels
+    size_t numOffsets = 1;
+    size_t perVertexOffset = SIZE_MAX; // invalid value
+    for (const InputChannel& channel : pPerIndexChannels)
+    {
+        numOffsets = std::max(numOffsets, channel.mOffset + 1);
+        if (channel.mType == IT_Vertex)
+            perVertexOffset = channel.mOffset;
+    }
+
+    // determine the expected number of indices
+    size_t expectedPointCount = 0;
+    switch (pPrimType)
+    {
+    case Prim_Polylist:
+    {
+        for (size_t i : pVCount)
+            expectedPointCount += i;
+        break;
+    }
+    case Prim_Lines:
+        expectedPointCount = 2 * pNumPrimitives;
+        break;
+    case Prim_Triangles:
+        expectedPointCount = 3 * pNumPrimitives;
+        break;
+    default:
+        // other primitive types don't state the index count upfront... we need to guess
+        break;
+    }
+
+    // and read all indices into a temporary array
+    std::vector<size_t> indices;
+    if (expectedPointCount > 0)
+        indices.reserve(expectedPointCount * numOffsets);
+
+    if (pNumPrimitives > 0) // It is possible to not contain any indices
+    {
+        const char* content = GetTextContent();
+        while (*content != 0)
+        {
+            // read a value.
+            // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
+            int value = std::max(0, strtol10(content, &content));
+            indices.push_back(size_t(value));
+            // skip whitespace after it
+            SkipSpacesAndLineEnd(&content);
+        }
+    }
+
+    // complain if the index count doesn't fit
+    if (expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) {
+        if (pPrimType == Prim_Lines) {
+            // HACK: We just fix this number since SketchUp 15.3.331 writes the wrong 'count' for 'lines'
+            ReportWarning("Expected different index count in <p> element, %zu instead of %zu.", indices.size(), expectedPointCount * numOffsets);
+            pNumPrimitives = (indices.size() / numOffsets) / 2;
+        }
+        else
+            ThrowException("Expected different index count in <p> element.");
+
+    }
+    else if (expectedPointCount == 0 && (indices.size() % numOffsets) != 0)
+        ThrowException("Expected different index count in <p> element.");
+
+    // find the data for all sources
+    for (std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
+    {
+        InputChannel& input = *it;
+        if (input.mResolved)
+            continue;
+
+        // find accessor
+        input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
+        // resolve accessor's data pointer as well, if necessary
+        const Accessor* acc = input.mResolved;
+        if (!acc->mData)
+            acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
+    }
+    // and the same for the per-index channels
+    for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
+    {
+        InputChannel& input = *it;
+        if (input.mResolved)
+            continue;
+
+        // ignore vertex pointer, it doesn't refer to an accessor
+        if (input.mType == IT_Vertex)
+        {
+            // warn if the vertex channel does not refer to the <vertices> element in the same mesh
+            if (input.mAccessor != pMesh->mVertexID)
+                ThrowException("Unsupported vertex referencing scheme.");
+            continue;
+        }
+
+        // find accessor
+        input.mResolved = &ResolveLibraryReference(mAccessorLibrary, input.mAccessor);
+        // resolve accessor's data pointer as well, if necessary
+        const Accessor* acc = input.mResolved;
+        if (!acc->mData)
+            acc->mData = &ResolveLibraryReference(mDataLibrary, acc->mSource);
+    }
+
+    // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
+    size_t numPrimitives = pNumPrimitives;
+    if (pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
+        numPrimitives = 1;
+    // For continued primitives, the given count is actually the number of <p>'s inside the parent tag
+    if (pPrimType == Prim_TriStrips) {
+        size_t numberOfVertices = indices.size() / numOffsets;
+        numPrimitives = numberOfVertices - 2;
+    }
+    if (pPrimType == Prim_LineStrip) {
+        size_t numberOfVertices = indices.size() / numOffsets;
+        numPrimitives = numberOfVertices - 1;
+    }
+
+    pMesh->mFaceSize.reserve(numPrimitives);
+    pMesh->mFacePosIndices.reserve(indices.size() / numOffsets);
+
+    size_t polylistStartVertex = 0;
+    for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++)
+    {
+        // determine number of points for this primitive
+        size_t numPoints = 0;
+        switch (pPrimType)
+        {
+        case Prim_Lines:
+            numPoints = 2;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_LineStrip:
+            numPoints = 2;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_Triangles:
+            numPoints = 3;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_TriStrips:
+            numPoints = 3;
+            ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        case Prim_Polylist:
+            numPoints = pVCount[currentPrimitive];
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices);
+            polylistStartVertex += numPoints;
+            break;
+        case Prim_TriFans:
+        case Prim_Polygon:
+            numPoints = indices.size() / numOffsets;
+            for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+                CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+            break;
+        default:
+            // LineStrip is not supported due to expected index unmangling
+            ThrowException("Unsupported primitive type.");
+            break;
+        }
+
+        // store the face size to later reconstruct the face from
+        pMesh->mFaceSize.push_back(numPoints);
+    }
+
+    // if I ever get my hands on that guy who invented this steaming pile of indirection...
+    TestClosing("p");
+    return numPrimitives;
+}
+
+///@note This function willn't work correctly if both PerIndex and PerVertex channels have same channels.
+///For example if TEXCOORD present in both <vertices> and <polylist> tags this function will create wrong uv coordinates.
+///It's not clear from COLLADA documentation is this allowed or not. For now only exporter fixed to avoid such behavior
+void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices) {
+    // calculate the base offset of the vertex whose attributes we ant to copy
+    size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets;
+
+    // don't overrun the boundaries of the index list
+    ai_assert((baseOffset + numOffsets - 1) < indices.size());
+
+    // extract per-vertex channels using the global per-vertex offset
+    for (std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
+        ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
+    // and extract per-index channels using there specified offset
+    for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
+        ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
+
+    // store the vertex-data index for later assignment of bone vertex weights
+    pMesh->mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
+}
+
+void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices) {
+    if (currentPrimitive % 2 != 0) {
+        //odd tristrip triangles need their indices mangled, to preserve winding direction
+        CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+    }
+    else {//for non tristrips or even tristrip triangles
+        CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+        CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Extracts a single object from an input channel and stores it in the appropriate mesh data array
+void ColladaParser::ExtractDataObjectFromChannel(const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh)
+{
+    // ignore vertex referrer - we handle them that separate
+    if (pInput.mType == IT_Vertex)
+        return;
+
+    const Accessor& acc = *pInput.mResolved;
+    if (pLocalIndex >= acc.mCount)
+        ThrowException(format() << "Invalid data index (" << pLocalIndex << "/" << acc.mCount << ") in primitive specification");
+
+    // get a pointer to the start of the data object referred to by the accessor and the local index
+    const ai_real* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex * acc.mStride;
+
+    // assemble according to the accessors component sub-offset list. We don't care, yet,
+    // what kind of object exactly we're extracting here
+    ai_real obj[4];
+    for (size_t c = 0; c < 4; ++c)
+        obj[c] = dataObject[acc.mSubOffset[c]];
+
+    // now we reinterpret it according to the type we're reading here
+    switch (pInput.mType)
+    {
+    case IT_Position: // ignore all position streams except 0 - there can be only one position
+        if (pInput.mIndex == 0)
+            pMesh->mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        else
+            ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported");
+        break;
+    case IT_Normal:
+        // pad to current vertex count if necessary
+        if (pMesh->mNormals.size() < pMesh->mPositions.size() - 1)
+            pMesh->mNormals.insert(pMesh->mNormals.end(), pMesh->mPositions.size() - pMesh->mNormals.size() - 1, aiVector3D(0, 1, 0));
+
+        // ignore all normal streams except 0 - there can be only one normal
+        if (pInput.mIndex == 0)
+            pMesh->mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        else
+            ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported");
+        break;
+    case IT_Tangent:
+        // pad to current vertex count if necessary
+        if (pMesh->mTangents.size() < pMesh->mPositions.size() - 1)
+            pMesh->mTangents.insert(pMesh->mTangents.end(), pMesh->mPositions.size() - pMesh->mTangents.size() - 1, aiVector3D(1, 0, 0));
+
+        // ignore all tangent streams except 0 - there can be only one tangent
+        if (pInput.mIndex == 0)
+            pMesh->mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        else
+            ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported");
+        break;
+    case IT_Bitangent:
+        // pad to current vertex count if necessary
+        if (pMesh->mBitangents.size() < pMesh->mPositions.size() - 1)
+            pMesh->mBitangents.insert(pMesh->mBitangents.end(), pMesh->mPositions.size() - pMesh->mBitangents.size() - 1, aiVector3D(0, 0, 1));
+
+        // ignore all bitangent streams except 0 - there can be only one bitangent
+        if (pInput.mIndex == 0)
+            pMesh->mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2]));
+        else
+            ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported");
+        break;
+    case IT_Texcoord:
+        // up to 4 texture coord sets are fine, ignore the others
+        if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS)
+        {
+            // pad to current vertex count if necessary
+            if (pMesh->mTexCoords[pInput.mIndex].size() < pMesh->mPositions.size() - 1)
+                pMesh->mTexCoords[pInput.mIndex].insert(pMesh->mTexCoords[pInput.mIndex].end(),
+                    pMesh->mPositions.size() - pMesh->mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0));
+
+            pMesh->mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2]));
+            if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */
+                pMesh->mNumUVComponents[pInput.mIndex] = 3;
+        }
+        else
+        {
+            ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping.");
+        }
+        break;
+    case IT_Color:
+        // up to 4 color sets are fine, ignore the others
+        if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS)
+        {
+            // pad to current vertex count if necessary
+            if (pMesh->mColors[pInput.mIndex].size() < pMesh->mPositions.size() - 1)
+                pMesh->mColors[pInput.mIndex].insert(pMesh->mColors[pInput.mIndex].end(),
+                    pMesh->mPositions.size() - pMesh->mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1));
+
+            aiColor4D result(0, 0, 0, 1);
+            for (size_t i = 0; i < pInput.mResolved->mSize; ++i)
+            {
+                result[static_cast<unsigned int>(i)] = obj[pInput.mResolved->mSubOffset[i]];
+            }
+            pMesh->mColors[pInput.mIndex].push_back(result);
+        }
+        else
+        {
+            ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping.");
+        }
+
+        break;
+    default:
+        // IT_Invalid and IT_Vertex
+        ai_assert(false && "shouldn't ever get here");
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the library of node hierarchies and scene parts
+void ColladaParser::ReadSceneLibrary()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            // a visual scene - generate root node under its ID and let ReadNode() do the recursive work
+            if (IsElement("visual_scene"))
+            {
+                // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then?
+                int indexID = GetAttribute("id");
+                const char* attrID = mReader->getAttributeValue(indexID);
+
+                // read name if given.
+                int indexName = TestAttribute("name");
+                const char* attrName = "unnamed";
+                if (indexName > -1)
+                    attrName = mReader->getAttributeValue(indexName);
+
+                // create a node and store it in the library under its ID
+                Node* node = new Node;
+                node->mID = attrID;
+                node->mName = attrName;
+                mNodeLibrary[node->mID] = node;
+
+                ReadSceneNode(node);
+            }
+            else
+            {
+                // ignore the rest
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "library_visual_scenes") == 0)
+                //ThrowException( "Expected end of \"library_visual_scenes\" element.");
+
+                break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a scene node's contents including children and stores it in the given node
+void ColladaParser::ReadSceneNode(Node* pNode)
+{
+    // quit immediately on <bla/> elements
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            if (IsElement("node"))
+            {
+                Node* child = new Node;
+                int attrID = TestAttribute("id");
+                if (attrID > -1)
+                    child->mID = mReader->getAttributeValue(attrID);
+                int attrSID = TestAttribute("sid");
+                if (attrSID > -1)
+                    child->mSID = mReader->getAttributeValue(attrSID);
+
+                int attrName = TestAttribute("name");
+                if (attrName > -1)
+                    child->mName = mReader->getAttributeValue(attrName);
+
+                // TODO: (thom) support SIDs
+                // ai_assert( TestAttribute( "sid") == -1);
+
+                if (pNode)
+                {
+                    pNode->mChildren.push_back(child);
+                    child->mParent = pNode;
+                }
+                else
+                {
+                    // no parent node given, probably called from <library_nodes> element.
+                    // create new node in node library
+                    mNodeLibrary[child->mID] = child;
+                }
+
+                // read on recursively from there
+                ReadSceneNode(child);
+                continue;
+            }
+            // For any further stuff we need a valid node to work on
+            else if (!pNode)
+                continue;
+
+            if (IsElement("lookat"))
+                ReadNodeTransformation(pNode, TF_LOOKAT);
+            else if (IsElement("matrix"))
+                ReadNodeTransformation(pNode, TF_MATRIX);
+            else if (IsElement("rotate"))
+                ReadNodeTransformation(pNode, TF_ROTATE);
+            else if (IsElement("scale"))
+                ReadNodeTransformation(pNode, TF_SCALE);
+            else if (IsElement("skew"))
+                ReadNodeTransformation(pNode, TF_SKEW);
+            else if (IsElement("translate"))
+                ReadNodeTransformation(pNode, TF_TRANSLATE);
+            else if (IsElement("render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length())
+            {
+                // ... scene evaluation or, in other words, postprocessing pipeline,
+                // or, again in other words, a turing-complete description how to
+                // render a Collada scene. The only thing that is interesting for
+                // us is the primary camera.
+                int attrId = TestAttribute("camera_node");
+                if (-1 != attrId)
+                {
+                    const char* s = mReader->getAttributeValue(attrId);
+                    if (s[0] != '#')
+                        ASSIMP_LOG_ERROR("Collada: Unresolved reference format of camera");
+                    else
+                        pNode->mPrimaryCamera = s + 1;
+                }
+            }
+            else if (IsElement("instance_node"))
+            {
+                // find the node in the library
+                int attrID = TestAttribute("url");
+                if (attrID != -1)
+                {
+                    const char* s = mReader->getAttributeValue(attrID);
+                    if (s[0] != '#')
+                        ASSIMP_LOG_ERROR("Collada: Unresolved reference format of node");
+                    else
+                    {
+                        pNode->mNodeInstances.push_back(NodeInstance());
+                        pNode->mNodeInstances.back().mNode = s + 1;
+                    }
+                }
+            }
+            else if (IsElement("instance_geometry") || IsElement("instance_controller"))
+            {
+                // Reference to a mesh or controller, with possible material associations
+                ReadNodeGeometry(pNode);
+            }
+            else if (IsElement("instance_light"))
+            {
+                // Reference to a light, name given in 'url' attribute
+                int attrID = TestAttribute("url");
+                if (-1 == attrID)
+                    ASSIMP_LOG_WARN("Collada: Expected url attribute in <instance_light> element");
+                else
+                {
+                    const char* url = mReader->getAttributeValue(attrID);
+                    if (url[0] != '#')
+                        ThrowException("Unknown reference format in <instance_light> element");
+
+                    pNode->mLights.push_back(LightInstance());
+                    pNode->mLights.back().mLight = url + 1;
+                }
+            }
+            else if (IsElement("instance_camera"))
+            {
+                // Reference to a camera, name given in 'url' attribute
+                int attrID = TestAttribute("url");
+                if (-1 == attrID)
+                    ASSIMP_LOG_WARN("Collada: Expected url attribute in <instance_camera> element");
+                else
+                {
+                    const char* url = mReader->getAttributeValue(attrID);
+                    if (url[0] != '#')
+                        ThrowException("Unknown reference format in <instance_camera> element");
+
+                    pNode->mCameras.push_back(CameraInstance());
+                    pNode->mCameras.back().mCamera = url + 1;
+                }
+            }
+            else
+            {
+                // skip everything else for the moment
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a node transformation entry of the given type and adds it to the given node's transformation list.
+void ColladaParser::ReadNodeTransformation(Node* pNode, TransformType pType)
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    std::string tagName = mReader->getNodeName();
+
+    Transform tf;
+    tf.mType = pType;
+
+    // read SID
+    int indexSID = TestAttribute("sid");
+    if (indexSID >= 0)
+        tf.mID = mReader->getAttributeValue(indexSID);
+
+    // how many parameters to read per transformation type
+    static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
+    const char* content = GetTextContent();
+
+    // read as many parameters and store in the transformation
+    for (unsigned int a = 0; a < sNumParameters[pType]; a++)
+    {
+        // read a number
+        content = fast_atoreal_move<ai_real>(content, tf.f[a]);
+        // skip whitespace after it
+        SkipSpacesAndLineEnd(&content);
+    }
+
+    // place the transformation at the queue of the node
+    pNode->mTransforms.push_back(tf);
+
+    // and consume the closing tag
+    TestClosing(tagName.c_str());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Processes bind_vertex_input and bind elements
+void ColladaParser::ReadMaterialVertexInputBinding(Collada::SemanticMappingTable& tbl)
+{
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("bind_vertex_input"))
+            {
+                Collada::InputSemanticMapEntry vn;
+
+                // effect semantic
+                int n = GetAttribute("semantic");
+                std::string s = mReader->getAttributeValue(n);
+
+                // input semantic
+                n = GetAttribute("input_semantic");
+                vn.mType = GetTypeForSemantic(mReader->getAttributeValue(n));
+
+                // index of input set
+                n = TestAttribute("input_set");
+                if (-1 != n)
+                    vn.mSet = mReader->getAttributeValueAsInt(n);
+
+                tbl.mMap[s] = vn;
+            }
+            else if (IsElement("bind")) {
+                ASSIMP_LOG_WARN("Collada: Found unsupported <bind> element");
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            if (strcmp(mReader->getNodeName(), "instance_material") == 0)
+                break;
+        }
+    }
+}
+
+void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem& zip_archive)
+{
+    // Attempt to load any undefined Collada::Image in ImageLibrary
+    for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) {
+        Collada::Image &image = (*it).second;
+
+        if (image.mImageData.empty()) {
+            std::unique_ptr<IOStream> image_file(zip_archive.Open(image.mFileName.c_str()));
+            if (image_file) {
+                image.mImageData.resize(image_file->FileSize());
+                image_file->Read(image.mImageData.data(), image_file->FileSize(), 1);
+                image.mEmbeddedFormat = BaseImporter::GetExtension(image.mFileName);
+                if (image.mEmbeddedFormat == "jpeg") {
+                    image.mEmbeddedFormat = "jpg";
+                }
+            }
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a mesh reference in a node and adds it to the node's mesh list
+void ColladaParser::ReadNodeGeometry(Node* pNode)
+{
+    // referred mesh is given as an attribute of the <instance_geometry> element
+    int attrUrl = GetAttribute("url");
+    const char* url = mReader->getAttributeValue(attrUrl);
+    if (url[0] != '#')
+        ThrowException("Unknown reference format");
+
+    Collada::MeshInstance instance;
+    instance.mMeshOrController = url + 1; // skipping the leading #
+
+    if (!mReader->isEmptyElement())
+    {
+        // read material associations. Ignore additional elements in between
+        while (mReader->read())
+        {
+            if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+            {
+                if (IsElement("instance_material"))
+                {
+                    // read ID of the geometry subgroup and the target material
+                    int attrGroup = GetAttribute("symbol");
+                    std::string group = mReader->getAttributeValue(attrGroup);
+                    int attrMaterial = GetAttribute("target");
+                    const char* urlMat = mReader->getAttributeValue(attrMaterial);
+                    Collada::SemanticMappingTable s;
+                    if (urlMat[0] == '#')
+                        urlMat++;
+
+                    s.mMatName = urlMat;
+
+                    // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff
+                    if (!mReader->isEmptyElement())
+                        ReadMaterialVertexInputBinding(s);
+
+                    // store the association
+                    instance.mMaterials[group] = s;
+                }
+            }
+            else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+            {
+                if (strcmp(mReader->getNodeName(), "instance_geometry") == 0
+                    || strcmp(mReader->getNodeName(), "instance_controller") == 0)
+                    break;
+            }
+        }
+    }
+
+    // store it
+    pNode->mMeshes.push_back(instance);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the collada scene
+void ColladaParser::ReadScene()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+            if (IsElement("instance_visual_scene"))
+            {
+                // should be the first and only occurrence
+                if (mRootNode)
+                    ThrowException("Invalid scene containing multiple root nodes in <instance_visual_scene> element");
+
+                // read the url of the scene to instance. Should be of format "#some_name"
+                int urlIndex = GetAttribute("url");
+                const char* url = mReader->getAttributeValue(urlIndex);
+                if (url[0] != '#')
+                    ThrowException("Unknown reference format in <instance_visual_scene> element");
+
+                // find the referred scene, skip the leading #
+                NodeLibrary::const_iterator sit = mNodeLibrary.find(url + 1);
+                if (sit == mNodeLibrary.end())
+                    ThrowException("Unable to resolve visual_scene reference \"" + std::string(url) + "\" in <instance_visual_scene> element.");
+                mRootNode = sit->second;
+            }
+            else {
+                SkipElement();
+            }
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+AI_WONT_RETURN void ColladaParser::ThrowException(const std::string& pError) const
+{
+    throw DeadlyImportError(format() << "Collada: " << mFileName << " - " << pError);
+}
+void ColladaParser::ReportWarning(const char* msg, ...)
+{
+    ai_assert(NULL != msg);
+
+    va_list args;
+    va_start(args, msg);
+
+    char szBuffer[3000];
+    const int iLen = vsprintf(szBuffer, msg, args);
+    ai_assert(iLen > 0);
+
+    va_end(args);
+    ASSIMP_LOG_WARN_F("Validation warning: ", std::string(szBuffer, iLen));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skips all data until the end node of the current element
+void ColladaParser::SkipElement()
+{
+    // nothing to skip if it's an <element />
+    if (mReader->isEmptyElement())
+        return;
+
+    // reroute
+    SkipElement(mReader->getNodeName());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skips all data until the end node of the given element
+void ColladaParser::SkipElement(const char* pElement)
+{
+    // copy the current node's name because it'a pointer to the reader's internal buffer,
+    // which is going to change with the upcoming parsing
+    std::string element = pElement;
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+            if (mReader->getNodeName() == element)
+                break;
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests for an opening element of the given name, throws an exception if not found
+void ColladaParser::TestOpening(const char* pName)
+{
+    // read element start
+    if (!mReader->read())
+        ThrowException(format() << "Unexpected end of file while beginning of <" << pName << "> element.");
+    // whitespace in front is ok, just read again if found
+    if (mReader->getNodeType() == irr::io::EXN_TEXT)
+        if (!mReader->read())
+            ThrowException(format() << "Unexpected end of file while reading beginning of <" << pName << "> element.");
+
+    if (mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp(mReader->getNodeName(), pName) != 0)
+        ThrowException(format() << "Expected start of <" << pName << "> element.");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests for the closing tag of the given element, throws an exception if not found
+void ColladaParser::TestClosing(const char* pName)
+{
+    // check if we're already on the closing tag and return right away
+    if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp(mReader->getNodeName(), pName) == 0)
+        return;
+
+    // if not, read some more
+    if (!mReader->read())
+        ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element.");
+    // whitespace in front is ok, just read again if found
+    if (mReader->getNodeType() == irr::io::EXN_TEXT)
+        if (!mReader->read())
+            ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element.");
+
+    // but this has the be the closing tag, or we're lost
+    if (mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp(mReader->getNodeName(), pName) != 0)
+        ThrowException(format() << "Expected end of <" << pName << "> element.");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes
+int ColladaParser::GetAttribute(const char* pAttr) const
+{
+    int index = TestAttribute(pAttr);
+    if (index != -1)
+        return index;
+
+    // attribute not found -> throw an exception
+    ThrowException(format() << "Expected attribute \"" << pAttr << "\" for element <" << mReader->getNodeName() << ">.");
+    return -1;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests the present element for the presence of one attribute, returns its index or throws an exception if not found
+int ColladaParser::TestAttribute(const char* pAttr) const
+{
+    for (int a = 0; a < mReader->getAttributeCount(); a++)
+        if (strcmp(mReader->getAttributeName(a), pAttr) == 0)
+            return a;
+
+    return -1;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the text contents of an element, throws an exception if not given. Skips leading whitespace.
+const char* ColladaParser::GetTextContent()
+{
+    const char* sz = TestTextContent();
+    if (!sz) {
+        ThrowException("Invalid contents in element \"n\".");
+    }
+    return sz;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the text contents of an element, returns NULL if not given. Skips leading whitespace.
+const char* ColladaParser::TestTextContent()
+{
+    // present node should be the beginning of an element
+    if (mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement())
+        return NULL;
+
+    // read contents of the element
+    if (!mReader->read())
+        return NULL;
+    if (mReader->getNodeType() != irr::io::EXN_TEXT && mReader->getNodeType() != irr::io::EXN_CDATA)
+        return NULL;
+
+    // skip leading whitespace
+    const char* text = mReader->getNodeData();
+    SkipSpacesAndLineEnd(&text);
+
+    return text;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Calculates the resulting transformation fromm all the given transform steps
+aiMatrix4x4 ColladaParser::CalculateResultTransform(const std::vector<Transform>& pTransforms) const
+{
+    aiMatrix4x4 res;
+
+    for (std::vector<Transform>::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it)
+    {
+        const Transform& tf = *it;
+        switch (tf.mType)
+        {
+        case TF_LOOKAT:
+        {
+            aiVector3D pos(tf.f[0], tf.f[1], tf.f[2]);
+            aiVector3D dstPos(tf.f[3], tf.f[4], tf.f[5]);
+            aiVector3D up = aiVector3D(tf.f[6], tf.f[7], tf.f[8]).Normalize();
+            aiVector3D dir = aiVector3D(dstPos - pos).Normalize();
+            aiVector3D right = (dir ^ up).Normalize();
+
+            res *= aiMatrix4x4(
+                right.x, up.x, -dir.x, pos.x,
+                right.y, up.y, -dir.y, pos.y,
+                right.z, up.z, -dir.z, pos.z,
+                0, 0, 0, 1);
+            break;
+        }
+        case TF_ROTATE:
+        {
+            aiMatrix4x4 rot;
+            ai_real angle = tf.f[3] * ai_real(AI_MATH_PI) / ai_real(180.0);
+            aiVector3D axis(tf.f[0], tf.f[1], tf.f[2]);
+            aiMatrix4x4::Rotation(angle, axis, rot);
+            res *= rot;
+            break;
+        }
+        case TF_TRANSLATE:
+        {
+            aiMatrix4x4 trans;
+            aiMatrix4x4::Translation(aiVector3D(tf.f[0], tf.f[1], tf.f[2]), trans);
+            res *= trans;
+            break;
+        }
+        case TF_SCALE:
+        {
+            aiMatrix4x4 scale(tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f,
+                0.0f, 0.0f, 0.0f, 1.0f);
+            res *= scale;
+            break;
+        }
+        case TF_SKEW:
+            // TODO: (thom)
+            ai_assert(false);
+            break;
+        case TF_MATRIX:
+        {
+            aiMatrix4x4 mat(tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7],
+                tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]);
+            res *= mat;
+            break;
+        }
+        default:
+            ai_assert(false);
+            break;
+        }
+    }
+
+    return res;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Determines the input data type for the given semantic string
+Collada::InputType ColladaParser::GetTypeForSemantic(const std::string& semantic)
+{
+    if (semantic.empty()) {
+        ASSIMP_LOG_WARN("Vertex input type is empty.");
+        return IT_Invalid;
+    }
+
+    if (semantic == "POSITION")
+        return IT_Position;
+    else if (semantic == "TEXCOORD")
+        return IT_Texcoord;
+    else if (semantic == "NORMAL")
+        return IT_Normal;
+    else if (semantic == "COLOR")
+        return IT_Color;
+    else if (semantic == "VERTEX")
+        return IT_Vertex;
+    else if (semantic == "BINORMAL" || semantic == "TEXBINORMAL")
+        return IT_Bitangent;
+    else if (semantic == "TANGENT" || semantic == "TEXTANGENT")
+        return IT_Tangent;
+
+    ASSIMP_LOG_WARN_F("Unknown vertex input type \"", semantic, "\". Ignoring.");
+    return IT_Invalid;
+}
+
+#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER

+ 26 - 1
Engine/lib/assimp/code/ColladaParser.h → Engine/lib/assimp/code/Collada/ColladaParser.h

@@ -54,6 +54,7 @@
 
 namespace Assimp
 {
+    class ZipArchiveIOSystem;
 
     // ------------------------------------------------------------------------------------------
     /** Parser helper class for the Collada loader.
@@ -65,13 +66,22 @@ namespace Assimp
     {
         friend class ColladaLoader;
 
+        /** Converts a path read from a collada file to the usual representation */
+        static void UriDecodePath(aiString& ss);
+
     protected:
+        /** Map for generic metadata as aiString */
+        typedef std::map<std::string, aiString> StringMetaData;
+
         /** Constructor from XML file */
-        ColladaParser( IOSystem* pIOHandler, const std::string& pFile);
+        ColladaParser(IOSystem* pIOHandler, const std::string& pFile);
 
         /** Destructor */
         ~ColladaParser();
 
+        /** Attempts to read the ZAE manifest and returns the DAE to open */
+        static std::string ReadZaeManifest(ZipArchiveIOSystem &zip_archive);
+
         /** Reads the contents of the file */
         void ReadContents();
 
@@ -81,6 +91,15 @@ namespace Assimp
         /** Reads asset information such as coordinate system information and legal blah */
         void ReadAssetInfo();
 
+        /** Reads contributor information such as author and legal blah */
+        void ReadContributorInfo();
+
+        /** Reads generic metadata into provided map */
+        void ReadMetaDataItem(StringMetaData &metadata);
+
+        /** Convert underscore_seperated to CamelCase "authoring_tool" becomes "AuthoringTool" */
+        static void ToCamelCase(std::string &text);
+
         /** Reads the animation library */
         void ReadAnimationLibrary();
 
@@ -223,6 +242,9 @@ namespace Assimp
         // Processes bind_vertex_input and bind elements
         void ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl);
 
+        /** Reads embedded textures from a ZAE archive*/
+        void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive);
+
     protected:
         /** Aborts the file reading with an exception */
         AI_WONT_RETURN void ThrowException( const std::string& pError) const AI_WONT_RETURN_SUFFIX;
@@ -343,6 +365,9 @@ namespace Assimp
         /** Which is the up vector */
         enum { UP_X, UP_Y, UP_Z } mUpDirection;
 
+        /** Asset metadata (global for scene) */
+        StringMetaData mAssetMetaData;
+
         /** Collada file format version */
         Collada::FormatVersion mFormat;
     };

+ 0 - 3215
Engine/lib/assimp/code/ColladaParser.cpp

@@ -1,3215 +0,0 @@
-/*
----------------------------------------------------------------------------
-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.
----------------------------------------------------------------------------
-*/
-
-/** @file ColladaParser.cpp
- *  @brief Implementation of the Collada parser helper
- */
-
-#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
-
-#include <sstream>
-#include <stdarg.h>
-#include "ColladaParser.h"
-#include <assimp/fast_atof.h>
-#include <assimp/ParsingUtils.h>
-#include <assimp/StringUtils.h>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/IOSystem.hpp>
-#include <assimp/light.h>
-#include <assimp/TinyFormatter.h>
-
-#include <memory>
-
-using namespace Assimp;
-using namespace Assimp::Collada;
-using namespace Assimp::Formatter;
-
-// ------------------------------------------------------------------------------------------------
-// Constructor to be privately used by Importer
-ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile)
-    : mFileName( pFile )
-    , mReader( nullptr )
-    , mDataLibrary()
-    , mAccessorLibrary()
-    , mMeshLibrary()
-    , mNodeLibrary()
-    , mImageLibrary()
-    , mEffectLibrary()
-    , mMaterialLibrary()
-    , mLightLibrary()
-    , mCameraLibrary()
-    , mControllerLibrary()
-    , mRootNode( nullptr )
-    , mAnims()
-    , mUnitSize( 1.0f )
-    , mUpDirection( UP_Y )
-    , mFormat(FV_1_5_n )    // We assume the newest file format by default
-{
-    // validate io-handler instance
-    if (nullptr == pIOHandler ) {
-        throw DeadlyImportError("IOSystem is NULL." );
-    }
-
-    // open the file
-    std::unique_ptr<IOStream> file( pIOHandler->Open(pFile ) );
-    if (file.get() == nullptr) {
-        throw DeadlyImportError( "Failed to open file " + pFile + "." );
-    }
-
-    // generate a XML reader for it
-    std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
-    mReader = irr::io::createIrrXMLReader( mIOWrapper.get());
-    if (!mReader) {
-        ThrowException("Collada: Unable to open file.");
-    }
-
-    // start reading
-    ReadContents();
-}
-
-// ------------------------------------------------------------------------------------------------
-// Destructor, private as well
-ColladaParser::~ColladaParser()
-{
-    delete mReader;
-    for( NodeLibrary::iterator it = mNodeLibrary.begin(); it != mNodeLibrary.end(); ++it)
-        delete it->second;
-    for( MeshLibrary::iterator it = mMeshLibrary.begin(); it != mMeshLibrary.end(); ++it)
-        delete it->second;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Read bool from text contents of current element
-bool ColladaParser::ReadBoolFromTextContent()
-{
-    const char* cur = GetTextContent();
-    return (!ASSIMP_strincmp(cur,"true",4) || '0' != *cur);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Read float from text contents of current element
-ai_real ColladaParser::ReadFloatFromTextContent()
-{
-    const char* cur = GetTextContent();
-    return fast_atof(cur);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the contents of the file
-void ColladaParser::ReadContents()
-{
-    while( mReader->read())
-    {
-        // handle the root element "COLLADA"
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "COLLADA"))
-            {
-                // check for 'version' attribute
-                const int attrib = TestAttribute("version");
-                if (attrib != -1) {
-                    const char* version = mReader->getAttributeValue(attrib);
-
-                    if (!::strncmp(version,"1.5",3)) {
-                        mFormat =  FV_1_5_n;
-                        ASSIMP_LOG_DEBUG("Collada schema version is 1.5.n");
-                    }
-                    else if (!::strncmp(version,"1.4",3)) {
-                        mFormat =  FV_1_4_n;
-                        ASSIMP_LOG_DEBUG("Collada schema version is 1.4.n");
-                    }
-                    else if (!::strncmp(version,"1.3",3)) {
-                        mFormat =  FV_1_3_n;
-                        ASSIMP_LOG_DEBUG("Collada schema version is 1.3.n");
-                    }
-                }
-
-                ReadStructure();
-            } else
-            {
-                ASSIMP_LOG_DEBUG_F( "Ignoring global element <", mReader->getNodeName(), ">." );
-                SkipElement();
-            }
-        } else
-        {
-            // skip everything else silently
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the structure of the file
-void ColladaParser::ReadStructure()
-{
-    while( mReader->read())
-    {
-        // beginning of elements
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "asset"))
-                ReadAssetInfo();
-            else if( IsElement( "library_animations"))
-                ReadAnimationLibrary();
-			else if (IsElement("library_animation_clips"))
-				ReadAnimationClipLibrary();
-            else if( IsElement( "library_controllers"))
-                ReadControllerLibrary();
-            else if( IsElement( "library_images"))
-                ReadImageLibrary();
-            else if( IsElement( "library_materials"))
-                ReadMaterialLibrary();
-            else if( IsElement( "library_effects"))
-                ReadEffectLibrary();
-            else if( IsElement( "library_geometries"))
-                ReadGeometryLibrary();
-            else if( IsElement( "library_visual_scenes"))
-                ReadSceneLibrary();
-            else if( IsElement( "library_lights"))
-                ReadLightLibrary();
-            else if( IsElement( "library_cameras"))
-                ReadCameraLibrary();
-            else if( IsElement( "library_nodes"))
-                ReadSceneNode(NULL); /* some hacking to reuse this piece of code */
-            else if( IsElement( "scene"))
-                ReadScene();
-            else
-                SkipElement();
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            break;
-        }
-    }
-
-	PostProcessRootAnimations();
-    PostProcessControllers();
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads asset information such as coordinate system information and legal blah
-void ColladaParser::ReadAssetInfo()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "unit"))
-            {
-                // read unit data from the element's attributes
-                const int attrIndex = TestAttribute( "meter");
-                if (attrIndex == -1) {
-                    mUnitSize = 1.f;
-                }
-                else {
-                    mUnitSize = mReader->getAttributeValueAsFloat( attrIndex);
-                }
-
-                // consume the trailing stuff
-                if( !mReader->isEmptyElement())
-                    SkipElement();
-            }
-            else if( IsElement( "up_axis"))
-            {
-                // read content, strip whitespace, compare
-                const char* content = GetTextContent();
-                if( strncmp( content, "X_UP", 4) == 0)
-                    mUpDirection = UP_X;
-                else if( strncmp( content, "Z_UP", 4) == 0)
-                    mUpDirection = UP_Z;
-                else
-                    mUpDirection = UP_Y;
-
-                // check element end
-                TestClosing( "up_axis");
-            } else
-            {
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "asset") != 0)
-                ThrowException( "Expected end of <asset> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the animation clips
-void ColladaParser::ReadAnimationClipLibrary()
-{
-	if (mReader->isEmptyElement())
-		return;
-
-	while (mReader->read())
-	{
-		if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
-		{
-			if (IsElement("animation_clip"))
-			{
-				// optional name given as an attribute
-				std::string animName;
-				int indexName = TestAttribute("name");
-				int indexID = TestAttribute("id");
-				if (indexName >= 0)
-					animName = mReader->getAttributeValue(indexName);
-				else if (indexID >= 0)
-					animName = mReader->getAttributeValue(indexID);
-				else
-					animName = std::string("animation_") + to_string(mAnimationClipLibrary.size());
-
-				std::pair<std::string, std::vector<std::string> > clip;
-
-				clip.first = animName;
-
-				while (mReader->read())
-				{
-					if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
-					{
-						if (IsElement("instance_animation"))
-						{
-							int indexUrl = TestAttribute("url");
-							if (indexUrl >= 0)
-							{
-								const char* url = mReader->getAttributeValue(indexUrl);
-								if (url[0] != '#')
-									ThrowException("Unknown reference format");
-
-								url++;
-
-								clip.second.push_back(url);
-							}
-						}
-						else
-						{
-							// ignore the rest
-							SkipElement();
-						}
-					}
-					else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-					{
-						if (strcmp(mReader->getNodeName(), "animation_clip") != 0)
-							ThrowException("Expected end of <animation_clip> element.");
-
-						break;
-					}
-				}
-
-				if (clip.second.size() > 0)
-				{
-					mAnimationClipLibrary.push_back(clip);
-				}
-			}
-			else
-			{
-				// ignore the rest
-				SkipElement();
-			}
-		}
-		else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-		{
-			if (strcmp(mReader->getNodeName(), "library_animation_clips") != 0)
-				ThrowException("Expected end of <library_animation_clips> element.");
-
-			break;
-		}
-	}
-}
-
-void ColladaParser::PostProcessControllers()
-{
-    std::string meshId;
-    for (ControllerLibrary::iterator it = mControllerLibrary.begin(); it != mControllerLibrary.end(); ++it) {
-        meshId = it->second.mMeshId;
-        ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId);
-        while(findItr != mControllerLibrary.end()) {
-            meshId = findItr->second.mMeshId;
-            findItr = mControllerLibrary.find(meshId);
-        }
-    
-        it->second.mMeshId = meshId;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Re-build animations from animation clip library, if present, otherwise combine single-channel animations
-void ColladaParser::PostProcessRootAnimations()
-{
-	if (mAnimationClipLibrary.size() > 0)
-	{
-		Animation temp;
-
-		for (AnimationClipLibrary::iterator it = mAnimationClipLibrary.begin(); it != mAnimationClipLibrary.end(); ++it)
-		{
-			std::string clipName = it->first;
-
-			Animation *clip = new Animation();
-			clip->mName = clipName;
-
-			temp.mSubAnims.push_back(clip);
-
-			for (std::vector<std::string>::iterator a = it->second.begin(); a != it->second.end(); ++a)
-			{
-				std::string animationID = *a;
-
-				AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID);
-
-				if (animation != mAnimationLibrary.end())
-				{
-					Animation *pSourceAnimation = animation->second;
-
-					pSourceAnimation->CollectChannelsRecursively(clip->mChannels);
-				}
-			}
-		}
-
-		mAnims = temp;
-
-		// Ensure no double deletes.
-		temp.mSubAnims.clear();
-	}
-	else
-	{
-		mAnims.CombineSingleChannelAnimations();
-	}
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the animation library
-void ColladaParser::ReadAnimationLibrary()
-{
-    if (mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "animation"))
-            {
-                // delegate the reading. Depending on the inner elements it will be a container or a anim channel
-                ReadAnimation( &mAnims);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "library_animations") != 0)
-                ThrowException( "Expected end of <library_animations> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an animation into the given parent structure
-void ColladaParser::ReadAnimation( Collada::Animation* pParent)
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    // an <animation> element may be a container for grouping sub-elements or an animation channel
-    // this is the channel collection by ID, in case it has channels
-    typedef std::map<std::string, AnimationChannel> ChannelMap;
-    ChannelMap channels;
-    // this is the anim container in case we're a container
-    Animation* anim = NULL;
-
-    // optional name given as an attribute
-    std::string animName;
-	std::string animID;
-    int indexName = TestAttribute( "name");
-    int indexID = TestAttribute( "id");
-
-	if (indexID >= 0)
-		animID = mReader->getAttributeValue(indexID);
-
-    if( indexName >= 0)
-        animName = mReader->getAttributeValue( indexName);
-    else if( indexID >= 0)
-        animName = animID;
-    else
-        animName = "animation";
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            // we have subanimations
-            if( IsElement( "animation"))
-            {
-                // create container from our element
-                if( !anim)
-                {
-                    anim = new Animation;
-                    anim->mName = animName;
-                    pParent->mSubAnims.push_back( anim);
-                }
-
-                // recurse into the subelement
-                ReadAnimation( anim);
-            }
-            else if( IsElement( "source"))
-            {
-                // possible animation data - we'll never know. Better store it
-                ReadSource();
-            }
-            else if( IsElement( "sampler"))
-            {
-                // read the ID to assign the corresponding collada channel afterwards.
-                int indexID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( indexID);
-                ChannelMap::iterator newChannel = channels.insert( std::make_pair( id, AnimationChannel())).first;
-
-                // have it read into a channel
-                ReadAnimationSampler( newChannel->second);
-            }
-            else if( IsElement( "channel"))
-            {
-                // the binding element whose whole purpose is to provide the target to animate
-                // Thanks, Collada! A directly posted information would have been too simple, I guess.
-                // Better add another indirection to that! Can't have enough of those.
-                int indexTarget = GetAttribute( "target");
-                int indexSource = GetAttribute( "source");
-                const char* sourceId = mReader->getAttributeValue( indexSource);
-                if( sourceId[0] == '#')
-                    sourceId++;
-                ChannelMap::iterator cit = channels.find( sourceId);
-                if( cit != channels.end())
-                    cit->second.mTarget = mReader->getAttributeValue( indexTarget);
-
-                if( !mReader->isEmptyElement())
-                    SkipElement();
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "animation") != 0)
-                ThrowException( "Expected end of <animation> element.");
-
-            break;
-        }
-    }
-
-    // it turned out to have channels - add them
-    if( !channels.empty())
-    {
-		// FIXME: Is this essentially doing the same as "single-anim-node" codepath in
-		//        ColladaLoader::StoreAnimations? For now, this has been deferred to after
-		//        all animations and all clips have been read. Due to handling of
-		//        <library_animation_clips> this cannot be done here, as the channel owner
-		//        is lost, and some exporters make up animations by referring to multiple
-		//        single-channel animations from an <instance_animation>.
-/*
-        // special filtering for stupid exporters packing each channel into a separate animation
-        if( channels.size() == 1)
-        {
-            pParent->mChannels.push_back( channels.begin()->second);
-        } else
-*/
-        {
-            // else create the animation, if not done yet, and store the channels
-            if( !anim)
-            {
-                anim = new Animation;
-                anim->mName = animName;
-                pParent->mSubAnims.push_back( anim);
-            }
-            for( ChannelMap::const_iterator it = channels.begin(); it != channels.end(); ++it)
-                anim->mChannels.push_back( it->second);
-
-			if (indexID >= 0)
-			{
-				mAnimationLibrary[animID] = anim;
-			}
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an animation sampler into the given anim channel
-void ColladaParser::ReadAnimationSampler( Collada::AnimationChannel& pChannel)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "input"))
-            {
-                int indexSemantic = GetAttribute( "semantic");
-                const char* semantic = mReader->getAttributeValue( indexSemantic);
-                int indexSource = GetAttribute( "source");
-                const char* source = mReader->getAttributeValue( indexSource);
-                if( source[0] != '#')
-                    ThrowException( "Unsupported URL format");
-                source++;
-
-                if( strcmp( semantic, "INPUT") == 0)
-                    pChannel.mSourceTimes = source;
-                else if( strcmp( semantic, "OUTPUT") == 0)
-                    pChannel.mSourceValues = source;
-                else if( strcmp( semantic, "IN_TANGENT") == 0)
-                    pChannel.mInTanValues = source;
-                else if( strcmp( semantic, "OUT_TANGENT") == 0)
-                    pChannel.mOutTanValues = source;
-                else if( strcmp( semantic, "INTERPOLATION") == 0)
-                    pChannel.mInterpolationValues = source;
-
-                if( !mReader->isEmptyElement())
-                    SkipElement();
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "sampler") != 0)
-                ThrowException( "Expected end of <sampler> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the skeleton controller library
-void ColladaParser::ReadControllerLibrary()
-{
-    if (mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "controller"))
-            {
-                // read ID. Ask the spec if it's necessary or optional... you might be surprised.
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                mControllerLibrary[id] = Controller();
-
-                // read on from there
-                ReadController( mControllerLibrary[id]);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "library_controllers") != 0)
-                ThrowException( "Expected end of <library_controllers> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a controller into the given mesh structure
-void ColladaParser::ReadController( Collada::Controller& pController)
-{
-    // initial values
-    pController.mType = Skin;
-    pController.mMethod = Normalized;
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            // two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other
-            if( IsElement( "morph"))
-            {
-                pController.mType = Morph;
-                int baseIndex = GetAttribute("source");
-                pController.mMeshId = mReader->getAttributeValue(baseIndex) + 1;
-                int methodIndex = GetAttribute("method");
-                if (methodIndex > 0) {
-                    const char *method = mReader->getAttributeValue(methodIndex);
-                    if (strcmp(method, "RELATIVE") == 0)
-                        pController.mMethod = Relative;
-                }
-            }
-            else if( IsElement( "skin"))
-            {
-                // read the mesh it refers to. According to the spec this could also be another
-                // controller, but I refuse to implement every single idea they've come up with
-                int sourceIndex = GetAttribute( "source");
-                pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1;
-            }
-            else if( IsElement( "bind_shape_matrix"))
-            {
-                // content is 16 floats to define a matrix... it seems to be important for some models
-                const char* content = GetTextContent();
-
-                // read the 16 floats
-                for( unsigned int a = 0; a < 16; a++)
-                {
-                    // read a number
-                    content = fast_atoreal_move<ai_real>( content, pController.mBindShapeMatrix[a]);
-                    // skip whitespace after it
-                    SkipSpacesAndLineEnd( &content);
-                }
-
-                TestClosing( "bind_shape_matrix");
-            }
-            else if( IsElement( "source"))
-            {
-                // data array - we have specialists to handle this
-                ReadSource();
-            }
-            else if( IsElement( "joints"))
-            {
-                ReadControllerJoints( pController);
-            }
-            else if( IsElement( "vertex_weights"))
-            {
-                ReadControllerWeights( pController);
-            }
-            else if ( IsElement( "targets" ))
-            {
-                while (mReader->read()) {
-                    if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-                        if ( IsElement( "input")) {
-                            int semanticsIndex = GetAttribute("semantic");
-                            int sourceIndex = GetAttribute("source");
-
-                            const char *semantics = mReader->getAttributeValue(semanticsIndex);
-                            const char *source = mReader->getAttributeValue(sourceIndex);
-                            if (strcmp(semantics, "MORPH_TARGET") == 0) {
-                                pController.mMorphTarget = source + 1;
-                            }
-                            else if (strcmp(semantics, "MORPH_WEIGHT") == 0)
-                            {
-                                pController.mMorphWeight = source + 1;
-                            }
-                        }
-                    } else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-                        if( strcmp( mReader->getNodeName(), "targets") == 0)
-                            break;
-                        else
-                            ThrowException( "Expected end of <targets> element.");
-                    }
-                }
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "controller") == 0)
-                break;
-            else if( strcmp( mReader->getNodeName(), "skin") != 0 && strcmp( mReader->getNodeName(), "morph") != 0)
-                ThrowException( "Expected end of <controller> element.");
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the joint definitions for the given controller
-void ColladaParser::ReadControllerJoints( Collada::Controller& pController)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            // Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX"
-            if( IsElement( "input"))
-            {
-                int indexSemantic = GetAttribute( "semantic");
-                const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
-                int indexSource = GetAttribute( "source");
-                const char* attrSource = mReader->getAttributeValue( indexSource);
-
-                // local URLS always start with a '#'. We don't support global URLs
-                if( attrSource[0] != '#')
-                    ThrowException( format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <joints> data <input> element" );
-                attrSource++;
-
-                // parse source URL to corresponding source
-                if( strcmp( attrSemantic, "JOINT") == 0)
-                    pController.mJointNameSource = attrSource;
-                else if( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0)
-                    pController.mJointOffsetMatrixSource = attrSource;
-                else
-                    ThrowException( format() << "Unknown semantic \"" << attrSemantic << "\" in <joints> data <input> element" );
-
-                // skip inner data, if present
-                if( !mReader->isEmptyElement())
-                    SkipElement();
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "joints") != 0)
-                ThrowException( "Expected end of <joints> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the joint weights for the given controller
-void ColladaParser::ReadControllerWeights( Collada::Controller& pController)
-{
-    // read vertex count from attributes and resize the array accordingly
-    int indexCount = GetAttribute( "count");
-    size_t vertexCount = (size_t) mReader->getAttributeValueAsInt( indexCount);
-    pController.mWeightCounts.resize( vertexCount);
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            // Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT"
-            if( IsElement( "input") && vertexCount > 0 )
-            {
-                InputChannel channel;
-
-                int indexSemantic = GetAttribute( "semantic");
-                const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
-                int indexSource = GetAttribute( "source");
-                const char* attrSource = mReader->getAttributeValue( indexSource);
-                int indexOffset = TestAttribute( "offset");
-                if( indexOffset >= 0)
-                    channel.mOffset = mReader->getAttributeValueAsInt( indexOffset);
-
-                // local URLS always start with a '#'. We don't support global URLs
-                if( attrSource[0] != '#')
-                    ThrowException( format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <vertex_weights> data <input> element" );
-                channel.mAccessor = attrSource + 1;
-
-                // parse source URL to corresponding source
-                if( strcmp( attrSemantic, "JOINT") == 0)
-                    pController.mWeightInputJoints = channel;
-                else if( strcmp( attrSemantic, "WEIGHT") == 0)
-                    pController.mWeightInputWeights = channel;
-                else
-                    ThrowException( format() << "Unknown semantic \"" << attrSemantic << "\" in <vertex_weights> data <input> element" );
-
-                // skip inner data, if present
-                if( !mReader->isEmptyElement())
-                    SkipElement();
-            }
-            else if( IsElement( "vcount") && vertexCount > 0 )
-            {
-                // read weight count per vertex
-                const char* text = GetTextContent();
-                size_t numWeights = 0;
-                for( std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it)
-                {
-                    if( *text == 0)
-                        ThrowException( "Out of data while reading <vcount>");
-
-                    *it = strtoul10( text, &text);
-                    numWeights += *it;
-                    SkipSpacesAndLineEnd( &text);
-                }
-
-                TestClosing( "vcount");
-
-                // reserve weight count
-                pController.mWeights.resize( numWeights);
-            }
-            else if( IsElement( "v") && vertexCount > 0 )
-            {
-                // read JointIndex - WeightIndex pairs
-                const char* text = GetTextContent();
-
-                for( std::vector< std::pair<size_t, size_t> >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it)
-                {
-                    if( *text == 0)
-                        ThrowException( "Out of data while reading <vertex_weights>");
-                    it->first = strtoul10( text, &text);
-                    SkipSpacesAndLineEnd( &text);
-                    if( *text == 0)
-                        ThrowException( "Out of data while reading <vertex_weights>");
-                    it->second = strtoul10( text, &text);
-                    SkipSpacesAndLineEnd( &text);
-                }
-
-                TestClosing( "v");
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "vertex_weights") != 0)
-                ThrowException( "Expected end of <vertex_weights> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the image library contents
-void ColladaParser::ReadImageLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "image"))
-            {
-                // read ID. Another entry which is "optional" by design but obligatory in reality
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                mImageLibrary[id] = Image();
-
-                // read on from there
-                ReadImage( mImageLibrary[id]);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "library_images") != 0)
-                ThrowException( "Expected end of <library_images> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an image entry into the given image
-void ColladaParser::ReadImage( Collada::Image& pImage)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT){
-            // Need to run different code paths here, depending on the Collada XSD version
-            if (IsElement("image")) {
-                SkipElement();
-            }
-            else if(  IsElement( "init_from"))
-            {
-                if (mFormat == FV_1_4_n)
-                {
-                    // FIX: C4D exporter writes empty <init_from/> tags
-                    if (!mReader->isEmptyElement()) {
-                        // element content is filename - hopefully
-                        const char* sz = TestTextContent();
-                        if (sz)pImage.mFileName = sz;
-                        TestClosing( "init_from");
-                    }
-                    if (!pImage.mFileName.length()) {
-                        pImage.mFileName = "unknown_texture";
-                    }
-                }
-                else if (mFormat == FV_1_5_n)
-                {
-                    // make sure we skip over mip and array initializations, which
-                    // we don't support, but which could confuse the loader if
-                    // they're not skipped.
-                    int attrib = TestAttribute("array_index");
-                    if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
-                        ASSIMP_LOG_WARN("Collada: Ignoring texture array index");
-                        continue;
-                    }
-
-                    attrib = TestAttribute("mip_index");
-                    if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
-                        ASSIMP_LOG_WARN("Collada: Ignoring MIP map layer");
-                        continue;
-                    }
-
-                    // TODO: correctly jump over cube and volume maps?
-                }
-            }
-            else if (mFormat == FV_1_5_n)
-            {
-                if( IsElement( "ref"))
-                {
-                    // element content is filename - hopefully
-                    const char* sz = TestTextContent();
-                    if (sz)pImage.mFileName = sz;
-                    TestClosing( "ref");
-                }
-                else if( IsElement( "hex") && !pImage.mFileName.length())
-                {
-                    // embedded image. get format
-                    const int attrib = TestAttribute("format");
-                    if (-1 == attrib)
-                        ASSIMP_LOG_WARN("Collada: Unknown image file format");
-                    else pImage.mEmbeddedFormat = mReader->getAttributeValue(attrib);
-
-                    const char* data = GetTextContent();
-
-                    // hexadecimal-encoded binary octets. First of all, find the
-                    // required buffer size to reserve enough storage.
-                    const char* cur = data;
-                    while (!IsSpaceOrNewLine(*cur)) cur++;
-
-                    const unsigned int size = (unsigned int)(cur-data) * 2;
-                    pImage.mImageData.resize(size);
-                    for (unsigned int i = 0; i < size;++i)
-                        pImage.mImageData[i] = HexOctetToDecimal(data+(i<<1));
-
-                    TestClosing( "hex");
-                }
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "image") == 0)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the material library
-void ColladaParser::ReadMaterialLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    std::map<std::string, int> names;
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "material"))
-            {
-                // read ID. By now you probably know my opinion about this "specification"
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                std::string name;
-                int attrName = TestAttribute("name");
-                if (attrName >= 0)
-                    name = mReader->getAttributeValue( attrName);
-
-                // create an entry and store it in the library under its ID
-                mMaterialLibrary[id] = Material();
-
-                if( !name.empty())
-                {
-                    std::map<std::string, int>::iterator it = names.find( name);
-                    if( it != names.end())
-                    {
-                        std::ostringstream strStream;
-                        strStream << ++it->second;
-                        name.append( " " + strStream.str());
-                    }
-                    else
-                    {
-                        names[name] = 0;
-                    }
-
-                    mMaterialLibrary[id].mName = name;
-                }
-
-                ReadMaterial( mMaterialLibrary[id]);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "library_materials") != 0)
-                ThrowException( "Expected end of <library_materials> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the light library
-void ColladaParser::ReadLightLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "light"))
-            {
-                // read ID. By now you probably know my opinion about this "specification"
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                ReadLight(mLightLibrary[id] = Light());
-
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)    {
-            if( strcmp( mReader->getNodeName(), "library_lights") != 0)
-                ThrowException( "Expected end of <library_lights> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the camera library
-void ColladaParser::ReadCameraLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "camera"))
-            {
-                // read ID. By now you probably know my opinion about this "specification"
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                Camera& cam = mCameraLibrary[id];
-                attrID = TestAttribute( "name");
-                if (attrID != -1)
-                    cam.mName = mReader->getAttributeValue( attrID);
-
-                ReadCamera(cam);
-
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)    {
-            if( strcmp( mReader->getNodeName(), "library_cameras") != 0)
-                ThrowException( "Expected end of <library_cameras> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a material entry into the given material
-void ColladaParser::ReadMaterial( Collada::Material& pMaterial)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if (IsElement("material")) {
-                SkipElement();
-            }
-            else if( IsElement( "instance_effect"))
-            {
-                // referred effect by URL
-                int attrUrl = GetAttribute( "url");
-                const char* url = mReader->getAttributeValue( attrUrl);
-                if( url[0] != '#')
-                    ThrowException( "Unknown reference format");
-
-                pMaterial.mEffect = url+1;
-
-                SkipElement();
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "material") != 0)
-                ThrowException( "Expected end of <material> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a light entry into the given light
-void ColladaParser::ReadLight( Collada::Light& pLight)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if (IsElement("light")) {
-                SkipElement();
-            }
-            else if (IsElement("spot")) {
-                pLight.mType = aiLightSource_SPOT;
-            }
-            else if (IsElement("ambient")) {
-                pLight.mType = aiLightSource_AMBIENT;
-            }
-            else if (IsElement("directional")) {
-                pLight.mType = aiLightSource_DIRECTIONAL;
-            }
-            else if (IsElement("point")) {
-                pLight.mType = aiLightSource_POINT;
-            }
-            else if (IsElement("color")) {
-                // text content contains 3 floats
-                const char* content = GetTextContent();
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pLight.mColor.r);
-                SkipSpacesAndLineEnd( &content);
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pLight.mColor.g);
-                SkipSpacesAndLineEnd( &content);
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pLight.mColor.b);
-                SkipSpacesAndLineEnd( &content);
-
-                TestClosing( "color");
-            }
-            else if (IsElement("constant_attenuation")) {
-                pLight.mAttConstant = ReadFloatFromTextContent();
-                TestClosing("constant_attenuation");
-            }
-            else if (IsElement("linear_attenuation")) {
-                pLight.mAttLinear = ReadFloatFromTextContent();
-                TestClosing("linear_attenuation");
-            }
-            else if (IsElement("quadratic_attenuation")) {
-                pLight.mAttQuadratic = ReadFloatFromTextContent();
-                TestClosing("quadratic_attenuation");
-            }
-            else if (IsElement("falloff_angle")) {
-                pLight.mFalloffAngle = ReadFloatFromTextContent();
-                TestClosing("falloff_angle");
-            }
-            else if (IsElement("falloff_exponent")) {
-                pLight.mFalloffExponent = ReadFloatFromTextContent();
-                TestClosing("falloff_exponent");
-            }
-            // FCOLLADA extensions
-            // -------------------------------------------------------
-            else if (IsElement("outer_cone")) {
-                pLight.mOuterAngle = ReadFloatFromTextContent();
-                TestClosing("outer_cone");
-            }
-            // ... and this one is even deprecated
-            else if (IsElement("penumbra_angle")) {
-                pLight.mPenumbraAngle = ReadFloatFromTextContent();
-                TestClosing("penumbra_angle");
-            }
-            else if (IsElement("intensity")) {
-                pLight.mIntensity = ReadFloatFromTextContent();
-                TestClosing("intensity");
-            }
-            else if (IsElement("falloff")) {
-                pLight.mOuterAngle = ReadFloatFromTextContent();
-                TestClosing("falloff");
-            }
-            else if (IsElement("hotspot_beam")) {
-                pLight.mFalloffAngle = ReadFloatFromTextContent();
-                TestClosing("hotspot_beam");
-            }
-            // OpenCOLLADA extensions
-            // -------------------------------------------------------
-            else if (IsElement("decay_falloff")) {
-                pLight.mOuterAngle = ReadFloatFromTextContent();
-                TestClosing("decay_falloff");
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "light") == 0)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a camera entry into the given light
-void ColladaParser::ReadCamera( Collada::Camera& pCamera)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if (IsElement("camera")) {
-                SkipElement();
-            }
-            else if (IsElement("orthographic")) {
-                pCamera.mOrtho = true;
-            }
-            else if (IsElement("xfov") || IsElement("xmag")) {
-                pCamera.mHorFov = ReadFloatFromTextContent();
-                TestClosing((pCamera.mOrtho ? "xmag" : "xfov"));
-            }
-            else if (IsElement("yfov") || IsElement("ymag")) {
-                pCamera.mVerFov = ReadFloatFromTextContent();
-                TestClosing((pCamera.mOrtho ? "ymag" : "yfov"));
-            }
-            else if (IsElement("aspect_ratio")) {
-                pCamera.mAspect = ReadFloatFromTextContent();
-                TestClosing("aspect_ratio");
-            }
-            else if (IsElement("znear")) {
-                pCamera.mZNear = ReadFloatFromTextContent();
-                TestClosing("znear");
-            }
-            else if (IsElement("zfar")) {
-                pCamera.mZFar = ReadFloatFromTextContent();
-                TestClosing("zfar");
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "camera") == 0)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the effect library
-void ColladaParser::ReadEffectLibrary()
-{
-    if (mReader->isEmptyElement()) {
-        return;
-    }
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "effect"))
-            {
-                // read ID. Do I have to repeat my ranting about "optional" attributes?
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                mEffectLibrary[id] = Effect();
-                // read on from there
-                ReadEffect( mEffectLibrary[id]);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "library_effects") != 0)
-                ThrowException( "Expected end of <library_effects> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an effect entry into the given effect
-void ColladaParser::ReadEffect( Collada::Effect& pEffect)
-{
-    // for the moment we don't support any other type of effect.
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "profile_COMMON"))
-                ReadEffectProfileCommon( pEffect);
-            else
-                SkipElement();
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "effect") != 0)
-                ThrowException( "Expected end of <effect> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an COMMON effect profile
-void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "newparam")) {
-                // save ID
-                int attrSID = GetAttribute( "sid");
-                std::string sid = mReader->getAttributeValue( attrSID);
-                pEffect.mParams[sid] = EffectParam();
-                ReadEffectParam( pEffect.mParams[sid]);
-            }
-            else if( IsElement( "technique") || IsElement( "extra"))
-            {
-                // just syntactic sugar
-            }
-
-            else if( mFormat == FV_1_4_n && IsElement( "image"))
-            {
-                // read ID. Another entry which is "optional" by design but obligatory in reality
-                int attrID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( attrID);
-
-                // create an entry and store it in the library under its ID
-                mImageLibrary[id] = Image();
-
-                // read on from there
-                ReadImage( mImageLibrary[id]);
-            }
-
-            /* Shading modes */
-            else if( IsElement( "phong"))
-                pEffect.mShadeType = Shade_Phong;
-            else if( IsElement( "constant"))
-                pEffect.mShadeType = Shade_Constant;
-            else if( IsElement( "lambert"))
-                pEffect.mShadeType = Shade_Lambert;
-            else if( IsElement( "blinn"))
-                pEffect.mShadeType = Shade_Blinn;
-
-            /* Color + texture properties */
-            else if( IsElement( "emission"))
-                ReadEffectColor( pEffect.mEmissive, pEffect.mTexEmissive);
-            else if( IsElement( "ambient"))
-                ReadEffectColor( pEffect.mAmbient, pEffect.mTexAmbient);
-            else if( IsElement( "diffuse"))
-                ReadEffectColor( pEffect.mDiffuse, pEffect.mTexDiffuse);
-            else if( IsElement( "specular"))
-                ReadEffectColor( pEffect.mSpecular, pEffect.mTexSpecular);
-            else if( IsElement( "reflective")) {
-                ReadEffectColor( pEffect.mReflective, pEffect.mTexReflective);
-            }
-            else if( IsElement( "transparent")) {
-                pEffect.mHasTransparency = true;
-
-                const char* opaque = mReader->getAttributeValueSafe("opaque");
-
-                if(::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "RGB_ONE") == 0) {
-                    pEffect.mRGBTransparency = true;
-                }
-
-                // In RGB_ZERO mode, the transparency is interpreted in reverse, go figure...
-				if(::strcmp(opaque, "RGB_ZERO") == 0 || ::strcmp(opaque, "A_ZERO") == 0) {
-					pEffect.mInvertTransparency = true;
-				}
-
-                ReadEffectColor( pEffect.mTransparent,pEffect.mTexTransparent);
-            }
-            else if( IsElement( "shininess"))
-                ReadEffectFloat( pEffect.mShininess);
-            else if( IsElement( "reflectivity"))
-                ReadEffectFloat( pEffect.mReflectivity);
-
-            /* Single scalar properties */
-            else if( IsElement( "transparency"))
-                ReadEffectFloat( pEffect.mTransparency);
-            else if( IsElement( "index_of_refraction"))
-                ReadEffectFloat( pEffect.mRefractIndex);
-
-            // GOOGLEEARTH/OKINO extensions
-            // -------------------------------------------------------
-            else if( IsElement( "double_sided"))
-                pEffect.mDoubleSided = ReadBoolFromTextContent();
-
-            // FCOLLADA extensions
-            // -------------------------------------------------------
-            else if( IsElement( "bump")) {
-                aiColor4D dummy;
-                ReadEffectColor( dummy,pEffect.mTexBump);
-            }
-
-            // MAX3D extensions
-            // -------------------------------------------------------
-            else if( IsElement( "wireframe"))   {
-                pEffect.mWireframe = ReadBoolFromTextContent();
-                TestClosing( "wireframe");
-            }
-            else if( IsElement( "faceted")) {
-                pEffect.mFaceted = ReadBoolFromTextContent();
-                TestClosing( "faceted");
-            }
-            else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "profile_COMMON") == 0)
-            {
-                break;
-            }
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Read texture wrapping + UV transform settings from a profile==Maya chunk
-void ColladaParser::ReadSamplerProperties( Sampler& out )
-{
-    if (mReader->isEmptyElement()) {
-        return;
-    }
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-
-            // MAYA extensions
-            // -------------------------------------------------------
-            if( IsElement( "wrapU"))        {
-                out.mWrapU = ReadBoolFromTextContent();
-                TestClosing( "wrapU");
-            }
-            else if( IsElement( "wrapV"))   {
-                out.mWrapV = ReadBoolFromTextContent();
-                TestClosing( "wrapV");
-            }
-            else if( IsElement( "mirrorU"))     {
-                out.mMirrorU = ReadBoolFromTextContent();
-                TestClosing( "mirrorU");
-            }
-            else if( IsElement( "mirrorV")) {
-                out.mMirrorV = ReadBoolFromTextContent();
-                TestClosing( "mirrorV");
-            }
-            else if( IsElement( "repeatU")) {
-                out.mTransform.mScaling.x = ReadFloatFromTextContent();
-                TestClosing( "repeatU");
-            }
-            else if( IsElement( "repeatV")) {
-                out.mTransform.mScaling.y = ReadFloatFromTextContent();
-                TestClosing( "repeatV");
-            }
-            else if( IsElement( "offsetU")) {
-                out.mTransform.mTranslation.x = ReadFloatFromTextContent();
-                TestClosing( "offsetU");
-            }
-            else if( IsElement( "offsetV")) {
-                out.mTransform.mTranslation.y = ReadFloatFromTextContent();
-                TestClosing( "offsetV");
-            }
-            else if( IsElement( "rotateUV"))    {
-                out.mTransform.mRotation = ReadFloatFromTextContent();
-                TestClosing( "rotateUV");
-            }
-            else if( IsElement( "blend_mode"))  {
-
-                const char* sz = GetTextContent();
-                // http://www.feelingsoftware.com/content/view/55/72/lang,en/
-                // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE
-                if (0 == ASSIMP_strincmp(sz,"ADD",3))
-                    out.mOp = aiTextureOp_Add;
-
-                else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8))
-                    out.mOp = aiTextureOp_Subtract;
-
-                else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8))
-                    out.mOp = aiTextureOp_Multiply;
-
-                else  {
-                    ASSIMP_LOG_WARN("Collada: Unsupported MAYA texture blend mode");
-                }
-                TestClosing( "blend_mode");
-            }
-            // OKINO extensions
-            // -------------------------------------------------------
-            else if( IsElement( "weighting"))   {
-                out.mWeighting = ReadFloatFromTextContent();
-                TestClosing( "weighting");
-            }
-            else if( IsElement( "mix_with_previous_layer")) {
-                out.mMixWithPrevious = ReadFloatFromTextContent();
-                TestClosing( "mix_with_previous_layer");
-            }
-            // MAX3D extensions
-            // -------------------------------------------------------
-            else if( IsElement( "amount"))  {
-                out.mWeighting = ReadFloatFromTextContent();
-                TestClosing( "amount");
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            if( strcmp( mReader->getNodeName(), "technique") == 0)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an effect entry containing a color or a texture defining that color
-void ColladaParser::ReadEffectColor( aiColor4D& pColor, Sampler& pSampler)
-{
-    if (mReader->isEmptyElement())
-        return;
-
-    // Save current element name
-    const std::string curElem = mReader->getNodeName();
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "color"))
-            {
-                // text content contains 4 floats
-                const char* content = GetTextContent();
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.r);
-                SkipSpacesAndLineEnd( &content);
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.g);
-                SkipSpacesAndLineEnd( &content);
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.b);
-                SkipSpacesAndLineEnd( &content);
-
-                content = fast_atoreal_move<ai_real>( content, (ai_real&)pColor.a);
-                SkipSpacesAndLineEnd( &content);
-                TestClosing( "color");
-            }
-            else if( IsElement( "texture"))
-            {
-                // get name of source texture/sampler
-                int attrTex = GetAttribute( "texture");
-                pSampler.mName = mReader->getAttributeValue( attrTex);
-
-                // get name of UV source channel. Specification demands it to be there, but some exporters
-                // don't write it. It will be the default UV channel in case it's missing.
-                attrTex = TestAttribute( "texcoord");
-                if( attrTex >= 0 )
-                    pSampler.mUVChannel = mReader->getAttributeValue( attrTex);
-                //SkipElement();
-
-                // as we've read texture, the color needs to be 1,1,1,1
-                pColor = aiColor4D(1.f, 1.f, 1.f, 1.f);
-            }
-            else if( IsElement( "technique"))
-            {
-                const int _profile = GetAttribute( "profile");
-                const char* profile = mReader->getAttributeValue( _profile );
-
-                // Some extensions are quite useful ... ReadSamplerProperties processes
-                // several extensions in MAYA, OKINO and MAX3D profiles.
-                if (!::strcmp(profile,"MAYA") || !::strcmp(profile,"MAX3D") || !::strcmp(profile,"OKINO"))
-                {
-                    // get more information on this sampler
-                    ReadSamplerProperties(pSampler);
-                }
-                else SkipElement();
-            }
-            else if( !IsElement( "extra"))
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
-            if (mReader->getNodeName() == curElem)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an effect entry containing a float
-void ColladaParser::ReadEffectFloat( ai_real& pFloat)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT){
-            if( IsElement( "float"))
-            {
-                // text content contains a single floats
-                const char* content = GetTextContent();
-                content = fast_atoreal_move<ai_real>( content, pFloat);
-                SkipSpacesAndLineEnd( &content);
-
-                TestClosing( "float");
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an effect parameter specification of any kind
-void ColladaParser::ReadEffectParam( Collada::EffectParam& pParam)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "surface"))
-            {
-                // image ID given inside <init_from> tags
-                TestOpening( "init_from");
-                const char* content = GetTextContent();
-                pParam.mType = Param_Surface;
-                pParam.mReference = content;
-                TestClosing( "init_from");
-
-                // don't care for remaining stuff
-                SkipElement( "surface");
-            }
-            else if( IsElement( "sampler2D") && (FV_1_4_n == mFormat || FV_1_3_n == mFormat))
-            {
-                // surface ID is given inside <source> tags
-                TestOpening( "source");
-                const char* content = GetTextContent();
-                pParam.mType = Param_Sampler;
-                pParam.mReference = content;
-                TestClosing( "source");
-
-                // don't care for remaining stuff
-                SkipElement( "sampler2D");
-            }
-            else if( IsElement( "sampler2D"))
-            {
-                // surface ID is given inside <instance_image> tags
-                TestOpening( "instance_image");
-                int attrURL = GetAttribute("url");
-                const char* url = mReader->getAttributeValue( attrURL);
-                if( url[0] != '#')
-                    ThrowException( "Unsupported URL format in instance_image");
-                url++;
-                pParam.mType = Param_Sampler;
-                pParam.mReference = url;
-                SkipElement( "sampler2D");
-            } else
-            {
-                // ignore unknown element
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the geometry library contents
-void ColladaParser::ReadGeometryLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "geometry"))
-            {
-                // read ID. Another entry which is "optional" by design but obligatory in reality
-                int indexID = GetAttribute( "id");
-                std::string id = mReader->getAttributeValue( indexID);
-
-                // TODO: (thom) support SIDs
-                // ai_assert( TestAttribute( "sid") == -1);
-
-                // create a mesh and store it in the library under its ID
-                Mesh* mesh = new Mesh;
-                mMeshLibrary[id] = mesh;
-
-                // read the mesh name if it exists
-                const int nameIndex = TestAttribute("name");
-                if(nameIndex != -1)
-                {
-                    mesh->mName = mReader->getAttributeValue(nameIndex);
-                }
-
-                // read on from there
-                ReadGeometry( mesh);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "library_geometries") != 0)
-                ThrowException( "Expected end of <library_geometries> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a geometry from the geometry library.
-void ColladaParser::ReadGeometry( Collada::Mesh* pMesh)
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "mesh"))
-            {
-                // read on from there
-                ReadMesh( pMesh);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "geometry") != 0)
-                ThrowException( "Expected end of <geometry> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a mesh from the geometry library
-void ColladaParser::ReadMesh( Mesh* pMesh)
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "source"))
-            {
-                // we have professionals dealing with this
-                ReadSource();
-            }
-            else if( IsElement( "vertices"))
-            {
-                // read per-vertex mesh data
-                ReadVertexData( pMesh);
-            }
-            else if( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips")
-                || IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips"))
-            {
-                // read per-index mesh data and faces setup
-                ReadIndexData( pMesh);
-            } else
-            {
-                // ignore the restf
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "technique_common") == 0)
-            {
-                // end of another meaningless element - read over it
-            }
-            else if( strcmp( mReader->getNodeName(), "mesh") == 0)
-            {
-                // end of <mesh> element - we're done here
-                break;
-            } else
-            {
-                // everything else should be punished
-                ThrowException( "Expected end of <mesh> element.");
-            }
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a source element
-void ColladaParser::ReadSource()
-{
-    int indexID = GetAttribute( "id");
-    std::string sourceID = mReader->getAttributeValue( indexID);
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "float_array") || IsElement( "IDREF_array") || IsElement( "Name_array"))
-            {
-                ReadDataArray();
-            }
-            else if( IsElement( "technique_common"))
-            {
-                // I don't care for your profiles
-            }
-            else if( IsElement( "accessor"))
-            {
-                ReadAccessor( sourceID);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "source") == 0)
-            {
-                // end of <source> - we're done
-                break;
-            }
-            else if( strcmp( mReader->getNodeName(), "technique_common") == 0)
-            {
-                // end of another meaningless element - read over it
-            } else
-            {
-                // everything else should be punished
-                ThrowException( "Expected end of <source> element.");
-            }
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a data array holding a number of floats, and stores it in the global library
-void ColladaParser::ReadDataArray()
-{
-    std::string elmName = mReader->getNodeName();
-    bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array");
-  bool isEmptyElement = mReader->isEmptyElement();
-
-    // read attributes
-    int indexID = GetAttribute( "id");
-    std::string id = mReader->getAttributeValue( indexID);
-    int indexCount = GetAttribute( "count");
-    unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount);
-    const char* content = TestTextContent();
-
-  // read values and store inside an array in the data library
-  mDataLibrary[id] = Data();
-  Data& data = mDataLibrary[id];
-  data.mIsStringArray = isStringArray;
-
-  // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them
-  if (content)
-  {
-        if( isStringArray)
-        {
-            data.mStrings.reserve( count);
-            std::string s;
-
-            for( unsigned int a = 0; a < count; a++)
-            {
-                if( *content == 0)
-                    ThrowException( "Expected more values while reading IDREF_array contents.");
-
-                s.clear();
-                while( !IsSpaceOrNewLine( *content))
-                    s += *content++;
-                data.mStrings.push_back( s);
-
-                SkipSpacesAndLineEnd( &content);
-            }
-        } else
-        {
-            data.mValues.reserve( count);
-
-            for( unsigned int a = 0; a < count; a++)
-            {
-                if( *content == 0)
-                    ThrowException( "Expected more values while reading float_array contents.");
-
-                ai_real value;
-                // read a number
-                content = fast_atoreal_move<ai_real>( content, value);
-                data.mValues.push_back( value);
-                // skip whitespace after it
-                SkipSpacesAndLineEnd( &content);
-            }
-        }
-    }
-
-  // test for closing tag
-  if( !isEmptyElement )
-    TestClosing( elmName.c_str());
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads an accessor and stores it in the global library
-void ColladaParser::ReadAccessor( const std::string& pID)
-{
-    // read accessor attributes
-    int attrSource = GetAttribute( "source");
-    const char* source = mReader->getAttributeValue( attrSource);
-    if( source[0] != '#')
-        ThrowException( format() << "Unknown reference format in url \"" << source << "\" in source attribute of <accessor> element." );
-    int attrCount = GetAttribute( "count");
-    unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( attrCount);
-    int attrOffset = TestAttribute( "offset");
-    unsigned int offset = 0;
-    if( attrOffset > -1)
-        offset = (unsigned int) mReader->getAttributeValueAsInt( attrOffset);
-    int attrStride = TestAttribute( "stride");
-    unsigned int stride = 1;
-    if( attrStride > -1)
-        stride = (unsigned int) mReader->getAttributeValueAsInt( attrStride);
-
-    // store in the library under the given ID
-    mAccessorLibrary[pID] = Accessor();
-    Accessor& acc = mAccessorLibrary[pID];
-    acc.mCount = count;
-    acc.mOffset = offset;
-    acc.mStride = stride;
-    acc.mSource = source+1; // ignore the leading '#'
-    acc.mSize = 0; // gets incremented with every param
-
-    // and read the components
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "param"))
-            {
-                // read data param
-                int attrName = TestAttribute( "name");
-                std::string name;
-                if( attrName > -1)
-                {
-                    name = mReader->getAttributeValue( attrName);
-
-                    // analyse for common type components and store it's sub-offset in the corresponding field
-
-                    /* Cartesian coordinates */
-                    if( name == "X") acc.mSubOffset[0] = acc.mParams.size();
-                    else if( name == "Y") acc.mSubOffset[1] = acc.mParams.size();
-                    else if( name == "Z") acc.mSubOffset[2] = acc.mParams.size();
-
-                    /* RGBA colors */
-                    else if( name == "R") acc.mSubOffset[0] = acc.mParams.size();
-                    else if( name == "G") acc.mSubOffset[1] = acc.mParams.size();
-                    else if( name == "B") acc.mSubOffset[2] = acc.mParams.size();
-                    else if( name == "A") acc.mSubOffset[3] = acc.mParams.size();
-
-                    /* UVWQ (STPQ) texture coordinates */
-                    else if( name == "S") acc.mSubOffset[0] = acc.mParams.size();
-                    else if( name == "T") acc.mSubOffset[1] = acc.mParams.size();
-                    else if( name == "P") acc.mSubOffset[2] = acc.mParams.size();
-                //  else if( name == "Q") acc.mSubOffset[3] = acc.mParams.size();
-                    /* 4D uv coordinates are not supported in Assimp */
-
-                    /* Generic extra data, interpreted as UV data, too*/
-                    else if( name == "U") acc.mSubOffset[0] = acc.mParams.size();
-                    else if( name == "V") acc.mSubOffset[1] = acc.mParams.size();
-                    //else
-                    //  DefaultLogger::get()->warn( format() << "Unknown accessor parameter \"" << name << "\". Ignoring data channel." );
-                }
-
-                // read data type
-                int attrType = TestAttribute( "type");
-                if( attrType > -1)
-                {
-                    // for the moment we only distinguish between a 4x4 matrix and anything else.
-                    // TODO: (thom) I don't have a spec here at work. Check if there are other multi-value types
-                    // which should be tested for here.
-                    std::string type = mReader->getAttributeValue( attrType);
-                    if( type == "float4x4")
-                        acc.mSize += 16;
-                    else
-                        acc.mSize += 1;
-                }
-
-                acc.mParams.push_back( name);
-
-                // skip remaining stuff of this element, if any
-                SkipElement();
-            } else
-            {
-                ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <accessor>" );
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "accessor") != 0)
-                ThrowException( "Expected end of <accessor> element.");
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads input declarations of per-vertex mesh data into the given mesh
-void ColladaParser::ReadVertexData( Mesh* pMesh)
-{
-    // extract the ID of the <vertices> element. Not that we care, but to catch strange referencing schemes we should warn about
-    int attrID= GetAttribute( "id");
-    pMesh->mVertexID = mReader->getAttributeValue( attrID);
-
-    // a number of <input> elements
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "input"))
-            {
-                ReadInputChannel( pMesh->mPerVertexData);
-            } else
-            {
-                ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <vertices>" );
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "vertices") != 0)
-                ThrowException( "Expected end of <vertices> element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads input declarations of per-index mesh data into the given mesh
-void ColladaParser::ReadIndexData( Mesh* pMesh)
-{
-    std::vector<size_t> vcount;
-    std::vector<InputChannel> perIndexData;
-
-    // read primitive count from the attribute
-    int attrCount = GetAttribute( "count");
-    size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount);
-    // some mesh types (e.g. tristrips) don't specify primitive count upfront,
-    // so we need to sum up the actual number of primitives while we read the <p>-tags
-    size_t actualPrimitives = 0;
-
-    // material subgroup
-    int attrMaterial = TestAttribute( "material");
-    SubMesh subgroup;
-    if( attrMaterial > -1)
-        subgroup.mMaterial = mReader->getAttributeValue( attrMaterial);
-
-    // distinguish between polys and triangles
-    std::string elementName = mReader->getNodeName();
-    PrimitiveType primType = Prim_Invalid;
-    if( IsElement( "lines"))
-        primType = Prim_Lines;
-    else if( IsElement( "linestrips"))
-        primType = Prim_LineStrip;
-    else if( IsElement( "polygons"))
-        primType = Prim_Polygon;
-    else if( IsElement( "polylist"))
-        primType = Prim_Polylist;
-    else if( IsElement( "triangles"))
-        primType = Prim_Triangles;
-    else if( IsElement( "trifans"))
-        primType = Prim_TriFans;
-    else if( IsElement( "tristrips"))
-        primType = Prim_TriStrips;
-
-    ai_assert( primType != Prim_Invalid);
-
-    // also a number of <input> elements, but in addition a <p> primitive collection and probably index counts for all primitives
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "input"))
-            {
-                ReadInputChannel( perIndexData);
-            }
-            else if( IsElement( "vcount"))
-            {
-                if( !mReader->isEmptyElement())
-                {
-                    if (numPrimitives)  // It is possible to define a mesh without any primitives
-                    {
-                        // case <polylist> - specifies the number of indices for each polygon
-                        const char* content = GetTextContent();
-                        vcount.reserve( numPrimitives);
-                        for( unsigned int a = 0; a < numPrimitives; a++)
-                        {
-                            if( *content == 0)
-                                ThrowException( "Expected more values while reading <vcount> contents.");
-                            // read a number
-                            vcount.push_back( (size_t) strtoul10( content, &content));
-                            // skip whitespace after it
-                            SkipSpacesAndLineEnd( &content);
-                        }
-                    }
-
-                    TestClosing( "vcount");
-                }
-            }
-            else if( IsElement( "p"))
-            {
-                if( !mReader->isEmptyElement())
-                {
-                    // now here the actual fun starts - these are the indices to construct the mesh data from
-                    actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType);
-                }
-            }
-            else if (IsElement("extra"))
-            {
-                SkipElement("extra");
-            } else if ( IsElement("ph")) {                
-                SkipElement("ph");
-            } else {
-                ThrowException( format() << "Unexpected sub element <" << mReader->getNodeName() << "> in tag <" << elementName << ">" );
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( mReader->getNodeName() != elementName)
-                ThrowException( format() << "Expected end of <" << elementName << "> element." );
-
-            break;
-        }
-    }
-
-#ifdef ASSIMP_BUILD_DEBUG
-	if (primType != Prim_TriFans && primType != Prim_TriStrips && primType != Prim_LineStrip &&
-        primType != Prim_Lines) { // this is ONLY to workaround a bug in SketchUp 15.3.331 where it writes the wrong 'count' when it writes out the 'lines'.
-        ai_assert(actualPrimitives == numPrimitives);
-    }
-#endif
-
-    // only when we're done reading all <p> tags (and thus know the final vertex count) can we commit the submesh
-    subgroup.mNumFaces = actualPrimitives;
-    pMesh->mSubMeshes.push_back(subgroup);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a single input channel element and stores it in the given array, if valid
-void ColladaParser::ReadInputChannel( std::vector<InputChannel>& poChannels)
-{
-    InputChannel channel;
-
-    // read semantic
-    int attrSemantic = GetAttribute( "semantic");
-    std::string semantic = mReader->getAttributeValue( attrSemantic);
-    channel.mType = GetTypeForSemantic( semantic);
-
-    // read source
-    int attrSource = GetAttribute( "source");
-    const char* source = mReader->getAttributeValue( attrSource);
-    if( source[0] != '#')
-        ThrowException( format() << "Unknown reference format in url \"" << source << "\" in source attribute of <input> element." );
-    channel.mAccessor = source+1; // skipping the leading #, hopefully the remaining text is the accessor ID only
-
-    // read index offset, if per-index <input>
-    int attrOffset = TestAttribute( "offset");
-    if( attrOffset > -1)
-        channel.mOffset = mReader->getAttributeValueAsInt( attrOffset);
-
-    // read set if texture coordinates
-    if(channel.mType == IT_Texcoord || channel.mType == IT_Color){
-        int attrSet = TestAttribute("set");
-        if(attrSet > -1){
-            attrSet = mReader->getAttributeValueAsInt( attrSet);
-            if(attrSet < 0)
-                ThrowException( format() << "Invalid index \"" << (attrSet) << "\" in set attribute of <input> element" );
-
-            channel.mIndex = attrSet;
-        }
-    }
-
-    // store, if valid type
-    if( channel.mType != IT_Invalid)
-        poChannels.push_back( channel);
-
-    // skip remaining stuff of this element, if any
-    SkipElement();
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a <p> primitive index list and assembles the mesh data into the given mesh
-size_t ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels,
-    size_t pNumPrimitives, const std::vector<size_t>& pVCount, PrimitiveType pPrimType)
-{
-    // determine number of indices coming per vertex
-    // find the offset index for all per-vertex channels
-    size_t numOffsets = 1;
-    size_t perVertexOffset = SIZE_MAX; // invalid value
-    for( const InputChannel& channel : pPerIndexChannels)
-    {
-        numOffsets = std::max( numOffsets, channel.mOffset+1);
-        if( channel.mType == IT_Vertex)
-            perVertexOffset = channel.mOffset;
-    }
-
-    // determine the expected number of indices
-    size_t expectedPointCount = 0;
-    switch( pPrimType)
-    {
-        case Prim_Polylist:
-        {
-            for( size_t i : pVCount)
-                expectedPointCount += i;
-            break;
-        }
-        case Prim_Lines:
-            expectedPointCount = 2 * pNumPrimitives;
-            break;
-        case Prim_Triangles:
-            expectedPointCount = 3 * pNumPrimitives;
-            break;
-        default:
-            // other primitive types don't state the index count upfront... we need to guess
-            break;
-    }
-
-    // and read all indices into a temporary array
-    std::vector<size_t> indices;
-    if( expectedPointCount > 0)
-        indices.reserve( expectedPointCount * numOffsets);
-
-    if (pNumPrimitives > 0) // It is possible to not contain any indices
-    {
-        const char* content = GetTextContent();
-        while( *content != 0)
-        {
-            // read a value.
-            // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
-            int value = std::max( 0, strtol10( content, &content));
-            indices.push_back( size_t( value));
-            // skip whitespace after it
-            SkipSpacesAndLineEnd( &content);
-        }
-    }
-
-	// complain if the index count doesn't fit
-    if( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets) {
-        if (pPrimType == Prim_Lines) {
-            // HACK: We just fix this number since SketchUp 15.3.331 writes the wrong 'count' for 'lines'
-            ReportWarning( "Expected different index count in <p> element, %zu instead of %zu.", indices.size(), expectedPointCount * numOffsets);
-            pNumPrimitives = (indices.size() / numOffsets) / 2;
-        } else
-            ThrowException( "Expected different index count in <p> element.");
-
-    } else if( expectedPointCount == 0 && (indices.size() % numOffsets) != 0)
-		ThrowException( "Expected different index count in <p> element.");
-
-	// find the data for all sources
-    for( std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
-    {
-        InputChannel& input = *it;
-        if( input.mResolved)
-            continue;
-
-        // find accessor
-        input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
-        // resolve accessor's data pointer as well, if necessary
-        const Accessor* acc = input.mResolved;
-        if( !acc->mData)
-            acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
-    }
-    // and the same for the per-index channels
-    for( std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
-    {
-        InputChannel& input = *it;
-        if( input.mResolved)
-            continue;
-
-        // ignore vertex pointer, it doesn't refer to an accessor
-        if( input.mType == IT_Vertex)
-        {
-            // warn if the vertex channel does not refer to the <vertices> element in the same mesh
-            if( input.mAccessor != pMesh->mVertexID)
-                ThrowException( "Unsupported vertex referencing scheme.");
-            continue;
-        }
-
-        // find accessor
-        input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
-        // resolve accessor's data pointer as well, if necessary
-        const Accessor* acc = input.mResolved;
-        if( !acc->mData)
-            acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
-    }
-
-    // For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
-    size_t numPrimitives = pNumPrimitives;
-    if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
-        numPrimitives = 1;
-    // For continued primitives, the given count is actually the number of <p>'s inside the parent tag
-    if ( pPrimType == Prim_TriStrips){
-        size_t numberOfVertices = indices.size() / numOffsets;
-        numPrimitives = numberOfVertices - 2;
-    }
-    if (pPrimType == Prim_LineStrip) {
-        size_t numberOfVertices = indices.size() / numOffsets;
-        numPrimitives = numberOfVertices - 1;
-    }
-
-    pMesh->mFaceSize.reserve( numPrimitives);
-    pMesh->mFacePosIndices.reserve( indices.size() / numOffsets);
-
-    size_t polylistStartVertex = 0;
-    for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++)
-    {
-        // determine number of points for this primitive
-        size_t numPoints = 0;
-        switch( pPrimType)
-        {
-            case Prim_Lines:
-                numPoints = 2;
-                for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
-                    CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-                break;
-            case Prim_LineStrip:
-                numPoints = 2;
-                for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
-                    CopyVertex(currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-                break;
-            case Prim_Triangles:
-                numPoints = 3;
-                for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
-                    CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-                break;
-            case Prim_TriStrips:
-                numPoints = 3;
-                ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-                break;
-            case Prim_Polylist:
-                numPoints = pVCount[currentPrimitive];
-                for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
-                    CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices);
-                polylistStartVertex += numPoints;
-                break;
-            case Prim_TriFans:
-            case Prim_Polygon:
-                numPoints = indices.size() / numOffsets;
-                for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
-                    CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-                break;
-            default:
-                // LineStrip is not supported due to expected index unmangling
-                ThrowException( "Unsupported primitive type.");
-                break;
-        }
-
-        // store the face size to later reconstruct the face from
-        pMesh->mFaceSize.push_back( numPoints);
-    }
-
-    // if I ever get my hands on that guy who invented this steaming pile of indirection...
-    TestClosing( "p");
-    return numPrimitives;
-}
-
-///@note This function willn't work correctly if both PerIndex and PerVertex channels have same channels.
-///For example if TEXCOORD present in both <vertices> and <polylist> tags this function will create wrong uv coordinates.
-///It's not clear from COLLADA documentation is this allowed or not. For now only exporter fixed to avoid such behavior
-void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices){
-    // calculate the base offset of the vertex whose attributes we ant to copy
-    size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets;
-
-    // don't overrun the boundaries of the index list
-    ai_assert((baseOffset + numOffsets - 1) < indices.size());
-
-    // extract per-vertex channels using the global per-vertex offset
-    for (std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
-        ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
-    // and extract per-index channels using there specified offset
-    for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
-        ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
-
-    // store the vertex-data index for later assignment of bone vertex weights
-    pMesh->mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
-}
-
-void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices){
-    if (currentPrimitive % 2 != 0){
-        //odd tristrip triangles need their indices mangled, to preserve winding direction
-        CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-        CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-        CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-    }
-    else {//for non tristrips or even tristrip triangles
-        CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-        CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-        CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Extracts a single object from an input channel and stores it in the appropriate mesh data array
-void ColladaParser::ExtractDataObjectFromChannel( const InputChannel& pInput, size_t pLocalIndex, Mesh* pMesh)
-{
-    // ignore vertex referrer - we handle them that separate
-    if( pInput.mType == IT_Vertex)
-        return;
-
-    const Accessor& acc = *pInput.mResolved;
-    if( pLocalIndex >= acc.mCount)
-        ThrowException( format() << "Invalid data index (" << pLocalIndex << "/" << acc.mCount << ") in primitive specification" );
-
-    // get a pointer to the start of the data object referred to by the accessor and the local index
-    const ai_real* dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex* acc.mStride;
-
-    // assemble according to the accessors component sub-offset list. We don't care, yet,
-    // what kind of object exactly we're extracting here
-    ai_real obj[4];
-    for( size_t c = 0; c < 4; ++c)
-        obj[c] = dataObject[acc.mSubOffset[c]];
-
-    // now we reinterpret it according to the type we're reading here
-    switch( pInput.mType)
-    {
-        case IT_Position: // ignore all position streams except 0 - there can be only one position
-            if( pInput.mIndex == 0)
-                pMesh->mPositions.push_back( aiVector3D( obj[0], obj[1], obj[2]));
-            else
-                ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported");
-            break;
-        case IT_Normal:
-            // pad to current vertex count if necessary
-            if( pMesh->mNormals.size() < pMesh->mPositions.size()-1)
-                pMesh->mNormals.insert( pMesh->mNormals.end(), pMesh->mPositions.size() - pMesh->mNormals.size() - 1, aiVector3D( 0, 1, 0));
-
-            // ignore all normal streams except 0 - there can be only one normal
-            if( pInput.mIndex == 0)
-                pMesh->mNormals.push_back( aiVector3D( obj[0], obj[1], obj[2]));
-            else
-                ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported");
-            break;
-        case IT_Tangent:
-            // pad to current vertex count if necessary
-            if( pMesh->mTangents.size() < pMesh->mPositions.size()-1)
-                pMesh->mTangents.insert( pMesh->mTangents.end(), pMesh->mPositions.size() - pMesh->mTangents.size() - 1, aiVector3D( 1, 0, 0));
-
-            // ignore all tangent streams except 0 - there can be only one tangent
-            if( pInput.mIndex == 0)
-                pMesh->mTangents.push_back( aiVector3D( obj[0], obj[1], obj[2]));
-            else
-                ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported");
-            break;
-        case IT_Bitangent:
-            // pad to current vertex count if necessary
-            if( pMesh->mBitangents.size() < pMesh->mPositions.size()-1)
-                pMesh->mBitangents.insert( pMesh->mBitangents.end(), pMesh->mPositions.size() - pMesh->mBitangents.size() - 1, aiVector3D( 0, 0, 1));
-
-            // ignore all bitangent streams except 0 - there can be only one bitangent
-            if( pInput.mIndex == 0)
-                pMesh->mBitangents.push_back( aiVector3D( obj[0], obj[1], obj[2]));
-            else
-                ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported");
-            break;
-        case IT_Texcoord:
-            // up to 4 texture coord sets are fine, ignore the others
-            if( pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS)
-            {
-                // pad to current vertex count if necessary
-                if( pMesh->mTexCoords[pInput.mIndex].size() < pMesh->mPositions.size()-1)
-                    pMesh->mTexCoords[pInput.mIndex].insert( pMesh->mTexCoords[pInput.mIndex].end(),
-                        pMesh->mPositions.size() - pMesh->mTexCoords[pInput.mIndex].size() - 1, aiVector3D( 0, 0, 0));
-
-                pMesh->mTexCoords[pInput.mIndex].push_back( aiVector3D( obj[0], obj[1], obj[2]));
-                if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) /* hack ... consider cleaner solution */
-                    pMesh->mNumUVComponents[pInput.mIndex]=3;
-            }   else
-            {
-                ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping.");
-            }
-            break;
-        case IT_Color:
-            // up to 4 color sets are fine, ignore the others
-            if( pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS)
-            {
-                // pad to current vertex count if necessary
-                if( pMesh->mColors[pInput.mIndex].size() < pMesh->mPositions.size()-1)
-                    pMesh->mColors[pInput.mIndex].insert( pMesh->mColors[pInput.mIndex].end(),
-                        pMesh->mPositions.size() - pMesh->mColors[pInput.mIndex].size() - 1, aiColor4D( 0, 0, 0, 1));
-
-                aiColor4D result(0, 0, 0, 1);
-                for (size_t i = 0; i < pInput.mResolved->mSize; ++i)
-                {
-                    result[static_cast<unsigned int>(i)] = obj[pInput.mResolved->mSubOffset[i]];
-                }
-                pMesh->mColors[pInput.mIndex].push_back(result);
-            } else
-            {
-                ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping.");
-            }
-
-            break;
-        default:
-            // IT_Invalid and IT_Vertex
-            ai_assert(false && "shouldn't ever get here");
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the library of node hierarchies and scene parts
-void ColladaParser::ReadSceneLibrary()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            // a visual scene - generate root node under its ID and let ReadNode() do the recursive work
-            if( IsElement( "visual_scene"))
-            {
-                // read ID. Is optional according to the spec, but how on earth should a scene_instance refer to it then?
-                int indexID = GetAttribute( "id");
-                const char* attrID = mReader->getAttributeValue( indexID);
-
-                // read name if given.
-                int indexName = TestAttribute( "name");
-                const char* attrName = "unnamed";
-                if( indexName > -1)
-                    attrName = mReader->getAttributeValue( indexName);
-
-                // create a node and store it in the library under its ID
-                Node* node = new Node;
-                node->mID = attrID;
-                node->mName = attrName;
-                mNodeLibrary[node->mID] = node;
-
-                ReadSceneNode( node);
-            } else
-            {
-                // ignore the rest
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-        {
-            if( strcmp( mReader->getNodeName(), "library_visual_scenes") == 0)
-                //ThrowException( "Expected end of \"library_visual_scenes\" element.");
-
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a scene node's contents including children and stores it in the given node
-void ColladaParser::ReadSceneNode( Node* pNode)
-{
-    // quit immediately on <bla/> elements
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-        {
-            if( IsElement( "node"))
-            {
-                Node* child = new Node;
-                int attrID = TestAttribute( "id");
-                if( attrID > -1)
-                    child->mID = mReader->getAttributeValue( attrID);
-                int attrSID = TestAttribute( "sid");
-                if( attrSID > -1)
-                    child->mSID = mReader->getAttributeValue( attrSID);
-
-                int attrName = TestAttribute( "name");
-                if( attrName > -1)
-                    child->mName = mReader->getAttributeValue( attrName);
-
-                // TODO: (thom) support SIDs
-                // ai_assert( TestAttribute( "sid") == -1);
-
-                if (pNode)
-                {
-                    pNode->mChildren.push_back( child);
-                    child->mParent = pNode;
-                }
-                else
-                {
-                    // no parent node given, probably called from <library_nodes> element.
-                    // create new node in node library
-                    mNodeLibrary[child->mID] = child;
-                }
-
-                // read on recursively from there
-                ReadSceneNode( child);
-                continue;
-            }
-            // For any further stuff we need a valid node to work on
-            else if (!pNode)
-                continue;
-
-            if( IsElement( "lookat"))
-                ReadNodeTransformation( pNode, TF_LOOKAT);
-            else if( IsElement( "matrix"))
-                ReadNodeTransformation( pNode, TF_MATRIX);
-            else if( IsElement( "rotate"))
-                ReadNodeTransformation( pNode, TF_ROTATE);
-            else if( IsElement( "scale"))
-                ReadNodeTransformation( pNode, TF_SCALE);
-            else if( IsElement( "skew"))
-                ReadNodeTransformation( pNode, TF_SKEW);
-            else if( IsElement( "translate"))
-                ReadNodeTransformation( pNode, TF_TRANSLATE);
-            else if( IsElement( "render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length())
-            {
-                // ... scene evaluation or, in other words, postprocessing pipeline,
-                // or, again in other words, a turing-complete description how to
-                // render a Collada scene. The only thing that is interesting for
-                // us is the primary camera.
-                int attrId = TestAttribute("camera_node");
-                if (-1 != attrId)
-                {
-                    const char* s = mReader->getAttributeValue(attrId);
-                    if (s[0] != '#')
-                        ASSIMP_LOG_ERROR("Collada: Unresolved reference format of camera");
-                    else
-                        pNode->mPrimaryCamera = s+1;
-                }
-            }
-            else if( IsElement( "instance_node"))
-            {
-                // find the node in the library
-                int attrID = TestAttribute( "url");
-                if( attrID != -1)
-                {
-                    const char* s = mReader->getAttributeValue(attrID);
-                    if (s[0] != '#')
-                        ASSIMP_LOG_ERROR("Collada: Unresolved reference format of node");
-                    else
-                    {
-                        pNode->mNodeInstances.push_back(NodeInstance());
-                        pNode->mNodeInstances.back().mNode = s+1;
-                    }
-                }
-            }
-            else if( IsElement( "instance_geometry") || IsElement( "instance_controller"))
-            {
-                // Reference to a mesh or controller, with possible material associations
-                ReadNodeGeometry( pNode);
-            }
-            else if( IsElement( "instance_light"))
-            {
-                // Reference to a light, name given in 'url' attribute
-                int attrID = TestAttribute("url");
-                if (-1 == attrID)
-                    ASSIMP_LOG_WARN("Collada: Expected url attribute in <instance_light> element");
-                else
-                {
-                    const char* url = mReader->getAttributeValue( attrID);
-                    if( url[0] != '#')
-                        ThrowException( "Unknown reference format in <instance_light> element");
-
-                    pNode->mLights.push_back(LightInstance());
-                    pNode->mLights.back().mLight = url+1;
-                }
-            }
-            else if( IsElement( "instance_camera"))
-            {
-                // Reference to a camera, name given in 'url' attribute
-                int attrID = TestAttribute("url");
-                if (-1 == attrID)
-                    ASSIMP_LOG_WARN("Collada: Expected url attribute in <instance_camera> element");
-                else
-                {
-                    const char* url = mReader->getAttributeValue( attrID);
-                    if( url[0] != '#')
-                        ThrowException( "Unknown reference format in <instance_camera> element");
-
-                    pNode->mCameras.push_back(CameraInstance());
-                    pNode->mCameras.back().mCamera = url+1;
-                }
-            }
-            else
-            {
-                // skip everything else for the moment
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a node transformation entry of the given type and adds it to the given node's transformation list.
-void ColladaParser::ReadNodeTransformation( Node* pNode, TransformType pType)
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    std::string tagName = mReader->getNodeName();
-
-    Transform tf;
-    tf.mType = pType;
-
-    // read SID
-    int indexSID = TestAttribute( "sid");
-    if( indexSID >= 0)
-        tf.mID = mReader->getAttributeValue( indexSID);
-
-    // how many parameters to read per transformation type
-    static const unsigned int sNumParameters[] = { 9, 4, 3, 3, 7, 16 };
-    const char* content = GetTextContent();
-
-    // read as many parameters and store in the transformation
-    for( unsigned int a = 0; a < sNumParameters[pType]; a++)
-    {
-        // read a number
-        content = fast_atoreal_move<ai_real>( content, tf.f[a]);
-        // skip whitespace after it
-        SkipSpacesAndLineEnd( &content);
-    }
-
-    // place the transformation at the queue of the node
-    pNode->mTransforms.push_back( tf);
-
-    // and consume the closing tag
-    TestClosing( tagName.c_str());
-}
-
-// ------------------------------------------------------------------------------------------------
-// Processes bind_vertex_input and bind elements
-void ColladaParser::ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl)
-{
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "bind_vertex_input"))
-            {
-                Collada::InputSemanticMapEntry vn;
-
-                // effect semantic
-                int n = GetAttribute("semantic");
-                std::string s = mReader->getAttributeValue(n);
-
-                // input semantic
-                n = GetAttribute("input_semantic");
-                vn.mType = GetTypeForSemantic( mReader->getAttributeValue(n) );
-
-                // index of input set
-                n = TestAttribute("input_set");
-                if (-1 != n)
-                    vn.mSet = mReader->getAttributeValueAsInt(n);
-
-                tbl.mMap[s] = vn;
-            }
-            else if( IsElement( "bind")) {
-                ASSIMP_LOG_WARN("Collada: Found unsupported <bind> element");
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)    {
-            if( strcmp( mReader->getNodeName(), "instance_material") == 0)
-                break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads a mesh reference in a node and adds it to the node's mesh list
-void ColladaParser::ReadNodeGeometry( Node* pNode)
-{
-    // referred mesh is given as an attribute of the <instance_geometry> element
-    int attrUrl = GetAttribute( "url");
-    const char* url = mReader->getAttributeValue( attrUrl);
-    if( url[0] != '#')
-        ThrowException( "Unknown reference format");
-
-    Collada::MeshInstance instance;
-    instance.mMeshOrController = url+1; // skipping the leading #
-
-    if( !mReader->isEmptyElement())
-    {
-        // read material associations. Ignore additional elements in between
-        while( mReader->read())
-        {
-            if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
-            {
-                if( IsElement( "instance_material"))
-                {
-                    // read ID of the geometry subgroup and the target material
-                    int attrGroup = GetAttribute( "symbol");
-                    std::string group = mReader->getAttributeValue( attrGroup);
-                    int attrMaterial = GetAttribute( "target");
-                    const char* urlMat = mReader->getAttributeValue( attrMaterial);
-                    Collada::SemanticMappingTable s;
-                    if( urlMat[0] == '#')
-                        urlMat++;
-
-                    s.mMatName = urlMat;
-
-                    // resolve further material details + THIS UGLY AND NASTY semantic mapping stuff
-                    if( !mReader->isEmptyElement())
-                        ReadMaterialVertexInputBinding(s);
-
-                    // store the association
-                    instance.mMaterials[group] = s;
-                }
-            }
-            else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-            {
-                if( strcmp( mReader->getNodeName(), "instance_geometry") == 0
-                    || strcmp( mReader->getNodeName(), "instance_controller") == 0)
-                    break;
-            }
-        }
-    }
-
-    // store it
-    pNode->mMeshes.push_back( instance);
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the collada scene
-void ColladaParser::ReadScene()
-{
-    if( mReader->isEmptyElement())
-        return;
-
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
-            if( IsElement( "instance_visual_scene"))
-            {
-                // should be the first and only occurrence
-                if( mRootNode)
-                    ThrowException( "Invalid scene containing multiple root nodes in <instance_visual_scene> element");
-
-                // read the url of the scene to instance. Should be of format "#some_name"
-                int urlIndex = GetAttribute( "url");
-                const char* url = mReader->getAttributeValue( urlIndex);
-                if( url[0] != '#')
-                    ThrowException( "Unknown reference format in <instance_visual_scene> element");
-
-                // find the referred scene, skip the leading #
-                NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1);
-                if( sit == mNodeLibrary.end())
-                    ThrowException( "Unable to resolve visual_scene reference \"" + std::string(url) + "\" in <instance_visual_scene> element.");
-                mRootNode = sit->second;
-            } else  {
-                SkipElement();
-            }
-        }
-        else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
-            break;
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Aborts the file reading with an exception
-AI_WONT_RETURN void ColladaParser::ThrowException( const std::string& pError) const
-{
-    throw DeadlyImportError( format() << "Collada: " << mFileName << " - " << pError );
-}
-void ColladaParser::ReportWarning(const char* msg,...)
-{
-    ai_assert(NULL != msg);
-
-    va_list args;
-    va_start(args,msg);
-
-    char szBuffer[3000];
-    const int iLen = vsprintf(szBuffer,msg,args);
-    ai_assert(iLen > 0);
-
-    va_end(args);
-    ASSIMP_LOG_WARN_F("Validation warning: ", std::string(szBuffer,iLen));
-}
-
-// ------------------------------------------------------------------------------------------------
-// Skips all data until the end node of the current element
-void ColladaParser::SkipElement()
-{
-    // nothing to skip if it's an <element />
-    if( mReader->isEmptyElement())
-        return;
-
-    // reroute
-    SkipElement( mReader->getNodeName());
-}
-
-// ------------------------------------------------------------------------------------------------
-// Skips all data until the end node of the given element
-void ColladaParser::SkipElement( const char* pElement)
-{
-    // copy the current node's name because it'a pointer to the reader's internal buffer,
-    // which is going to change with the upcoming parsing
-    std::string element = pElement;
-    while( mReader->read())
-    {
-        if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
-            if( mReader->getNodeName() == element)
-                break;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-// Tests for an opening element of the given name, throws an exception if not found
-void ColladaParser::TestOpening( const char* pName)
-{
-    // read element start
-    if( !mReader->read())
-        ThrowException( format() << "Unexpected end of file while beginning of <" << pName << "> element." );
-    // whitespace in front is ok, just read again if found
-    if( mReader->getNodeType() == irr::io::EXN_TEXT)
-        if( !mReader->read())
-            ThrowException( format() << "Unexpected end of file while reading beginning of <" << pName << "> element." );
-
-    if( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0)
-        ThrowException( format() << "Expected start of <" << pName << "> element." );
-}
-
-// ------------------------------------------------------------------------------------------------
-// Tests for the closing tag of the given element, throws an exception if not found
-void ColladaParser::TestClosing( const char* pName)
-{
-    // check if we're already on the closing tag and return right away
-    if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp( mReader->getNodeName(), pName) == 0)
-        return;
-
-    // if not, read some more
-    if( !mReader->read())
-        ThrowException( format() << "Unexpected end of file while reading end of <" << pName << "> element." );
-    // whitespace in front is ok, just read again if found
-    if( mReader->getNodeType() == irr::io::EXN_TEXT)
-        if( !mReader->read())
-            ThrowException( format() << "Unexpected end of file while reading end of <" << pName << "> element." );
-
-    // but this has the be the closing tag, or we're lost
-    if( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0)
-        ThrowException( format() << "Expected end of <" << pName << "> element." );
-}
-
-// ------------------------------------------------------------------------------------------------
-// Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes
-int ColladaParser::GetAttribute( const char* pAttr) const
-{
-    int index = TestAttribute( pAttr);
-    if( index != -1)
-        return index;
-
-    // attribute not found -> throw an exception
-    ThrowException( format() << "Expected attribute \"" << pAttr << "\" for element <" << mReader->getNodeName() << ">." );
-    return -1;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Tests the present element for the presence of one attribute, returns its index or throws an exception if not found
-int ColladaParser::TestAttribute( const char* pAttr) const
-{
-    for( int a = 0; a < mReader->getAttributeCount(); a++)
-        if( strcmp( mReader->getAttributeName( a), pAttr) == 0)
-            return a;
-
-    return -1;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the text contents of an element, throws an exception if not given. Skips leading whitespace.
-const char* ColladaParser::GetTextContent()
-{
-    const char* sz = TestTextContent();
-    if(!sz) {
-        ThrowException( "Invalid contents in element \"n\".");
-    }
-    return sz;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Reads the text contents of an element, returns NULL if not given. Skips leading whitespace.
-const char* ColladaParser::TestTextContent()
-{
-    // present node should be the beginning of an element
-    if( mReader->getNodeType() != irr::io::EXN_ELEMENT || mReader->isEmptyElement())
-        return NULL;
-
-    // read contents of the element
-    if( !mReader->read() )
-        return NULL;
-    if( mReader->getNodeType() != irr::io::EXN_TEXT && mReader->getNodeType() != irr::io::EXN_CDATA)
-        return NULL;
-
-    // skip leading whitespace
-    const char* text = mReader->getNodeData();
-    SkipSpacesAndLineEnd( &text);
-
-    return text;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Calculates the resulting transformation fromm all the given transform steps
-aiMatrix4x4 ColladaParser::CalculateResultTransform( const std::vector<Transform>& pTransforms) const
-{
-    aiMatrix4x4 res;
-
-    for( std::vector<Transform>::const_iterator it = pTransforms.begin(); it != pTransforms.end(); ++it)
-    {
-        const Transform& tf = *it;
-        switch( tf.mType)
-        {
-            case TF_LOOKAT:
-      {
-        aiVector3D pos( tf.f[0], tf.f[1], tf.f[2]);
-        aiVector3D dstPos( tf.f[3], tf.f[4], tf.f[5]);
-        aiVector3D up = aiVector3D( tf.f[6], tf.f[7], tf.f[8]).Normalize();
-        aiVector3D dir = aiVector3D( dstPos - pos).Normalize();
-        aiVector3D right = (dir ^ up).Normalize();
-
-        res *= aiMatrix4x4(
-          right.x, up.x, -dir.x, pos.x,
-          right.y, up.y, -dir.y, pos.y,
-          right.z, up.z, -dir.z, pos.z,
-          0, 0, 0, 1);
-                break;
-      }
-            case TF_ROTATE:
-            {
-                aiMatrix4x4 rot;
-                ai_real angle = tf.f[3] * ai_real( AI_MATH_PI) / ai_real( 180.0 );
-                aiVector3D axis( tf.f[0], tf.f[1], tf.f[2]);
-                aiMatrix4x4::Rotation( angle, axis, rot);
-                res *= rot;
-                break;
-            }
-            case TF_TRANSLATE:
-            {
-                aiMatrix4x4 trans;
-                aiMatrix4x4::Translation( aiVector3D( tf.f[0], tf.f[1], tf.f[2]), trans);
-                res *= trans;
-                break;
-            }
-            case TF_SCALE:
-            {
-                aiMatrix4x4 scale( tf.f[0], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[1], 0.0f, 0.0f, 0.0f, 0.0f, tf.f[2], 0.0f,
-                    0.0f, 0.0f, 0.0f, 1.0f);
-                res *= scale;
-                break;
-            }
-            case TF_SKEW:
-                // TODO: (thom)
-                ai_assert( false);
-                break;
-            case TF_MATRIX:
-            {
-                aiMatrix4x4 mat( tf.f[0], tf.f[1], tf.f[2], tf.f[3], tf.f[4], tf.f[5], tf.f[6], tf.f[7],
-                    tf.f[8], tf.f[9], tf.f[10], tf.f[11], tf.f[12], tf.f[13], tf.f[14], tf.f[15]);
-                res *= mat;
-                break;
-            }
-            default:
-                ai_assert( false);
-                break;
-        }
-    }
-
-    return res;
-}
-
-// ------------------------------------------------------------------------------------------------
-// Determines the input data type for the given semantic string
-Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& semantic)
-{
-    if ( semantic.empty() ) {
-        ASSIMP_LOG_WARN("Vertex input type is empty." );
-        return IT_Invalid;
-    }
-
-    if( semantic == "POSITION")
-        return IT_Position;
-    else if( semantic == "TEXCOORD")
-        return IT_Texcoord;
-    else if( semantic == "NORMAL")
-        return IT_Normal;
-    else if( semantic == "COLOR")
-        return IT_Color;
-    else if( semantic == "VERTEX")
-        return IT_Vertex;
-    else if( semantic == "BINORMAL" || semantic ==  "TEXBINORMAL")
-        return IT_Bitangent;
-    else if( semantic == "TANGENT" || semantic == "TEXTANGENT")
-        return IT_Tangent;
-
-    ASSIMP_LOG_WARN_F( "Unknown vertex input type \"", semantic, "\". Ignoring." );
-    return IT_Invalid;
-}
-
-#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER

+ 1 - 1
Engine/lib/assimp/code/Assimp.cpp → Engine/lib/assimp/code/Common/Assimp.cpp

@@ -54,7 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/Exceptional.h>
 #include <assimp/BaseImporter.h>
 
-#include "CInterfaceIOWrapper.h"
+#include "CApi/CInterfaceIOWrapper.h"
 #include "Importer.h"
 #include "ScenePrivate.h"
 

+ 44 - 4
Engine/lib/assimp/code/BaseImporter.cpp → Engine/lib/assimp/code/Common/BaseImporter.cpp

@@ -67,7 +67,20 @@ using namespace Assimp;
 // Constructor to be privately used by Importer
 BaseImporter::BaseImporter() AI_NO_EXCEPT
 : m_progress() {
-    // nothing to do here
+    /**
+    * Assimp Importer
+    * unit conversions available
+    * if you need another measurment unit add it below.
+    * it's currently defined in assimp that we prefer meters.
+    *
+    * NOTE: Initialised here rather than in the header file
+    * to workaround a VS2013 bug with brace initialisers
+    * */
+    importerUnits[ImporterUnits::M] = 1.0;
+    importerUnits[ImporterUnits::CM] = 0.01;
+    importerUnits[ImporterUnits::MM] = 0.001;
+    importerUnits[ImporterUnits::INCHES] = 0.0254;
+    importerUnits[ImporterUnits::FEET] = 0.3048;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -76,9 +89,25 @@ BaseImporter::~BaseImporter() {
     // nothing to do here
 }
 
+void BaseImporter::UpdateImporterScale( Importer* pImp )
+{
+    ai_assert(pImp != nullptr);
+    ai_assert(importerScale != 0.0);
+    ai_assert(fileScale != 0.0);
+
+    double activeScale = importerScale * fileScale;
+
+    // Set active scaling
+    pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, static_cast<float>( activeScale) );
+
+    ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale );
+}
+
 // ------------------------------------------------------------------------------------------------
 // Imports the given file and returns the imported data.
-aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
+aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
+
+
     m_progress = pImp->GetProgressHandler();
     if (nullptr == m_progress) {
         return nullptr;
@@ -100,6 +129,11 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
     {
         InternReadFile( pFile, sc.get(), &filter);
 
+        // Calculate import scale hook - required because pImp not available anywhere else
+        // passes scale into ScaleProcess
+        UpdateImporterScale(pImp);
+
+
     } catch( const std::exception& err )    {
         // extract error description
         m_ErrorText = err.what();
@@ -112,7 +146,7 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
 }
 
 // ------------------------------------------------------------------------------------------------
-void BaseImporter::SetupProperties(const Importer* /*pImp*/)
+void BaseImporter::SetupProperties(const Importer* pImp)
 {
     // the default implementation does nothing
 }
@@ -320,7 +354,11 @@ std::string BaseImporter::GetExtension( const std::string& file ) {
     return false;
 }
 
-#include "../contrib/utf8cpp/source/utf8.h"
+#ifdef ASSIMP_USE_HUNTER
+#  include <utf8/utf8.h>
+#else
+#  include "../contrib/utf8cpp/source/utf8.h"
+#endif
 
 // ------------------------------------------------------------------------------------------------
 // Convert to UTF8 data
@@ -584,6 +622,8 @@ aiScene* BatchLoader::GetImport( unsigned int which )
     return nullptr;
 }
 
+
+
 // ------------------------------------------------------------------------------------------------
 void BatchLoader::LoadAll()
 {

+ 1 - 1
Engine/lib/assimp/code/BaseProcess.cpp → Engine/lib/assimp/code/Common/BaseProcess.cpp

@@ -89,7 +89,7 @@ void BaseProcess::ExecuteOnScene( Importer* pImp)
 
         // and kill the partially imported data
         delete pImp->Pimpl()->mScene;
-        pImp->Pimpl()->mScene = NULL;
+        pImp->Pimpl()->mScene = nullptr;
     }
 }
 

+ 0 - 0
Engine/lib/assimp/code/BaseProcess.h → Engine/lib/assimp/code/Common/BaseProcess.h


+ 0 - 0
Engine/lib/assimp/code/Bitmap.cpp → Engine/lib/assimp/code/Common/Bitmap.cpp


+ 0 - 4
Engine/lib/assimp/code/CreateAnimMesh.cpp → Engine/lib/assimp/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];

+ 31 - 2
Engine/lib/assimp/code/DefaultIOStream.cpp → Engine/lib/assimp/code/Common/DefaultIOStream.cpp

@@ -52,6 +52,35 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 using namespace Assimp;
 
+namespace
+{
+    template<size_t sizeOfPointer>
+    size_t select_ftell(FILE* file)
+    {
+        return ::ftell(file);
+    }
+
+    template<size_t sizeOfPointer>
+    int select_fseek(FILE* file, int64_t offset, int origin)
+    {
+        return ::fseek(file, static_cast<long>(offset), origin);
+    }
+
+#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
+    template<>
+    size_t select_ftell<8>(FILE* file)
+    {
+        return ::_ftelli64(file);
+    }
+
+    template<>
+    int select_fseek<8>(FILE* file, int64_t offset, int origin)
+    {
+        return ::_fseeki64(file, offset, origin);
+    }
+#endif
+}
+
 // ----------------------------------------------------------------------------------
 DefaultIOStream::~DefaultIOStream()
 {
@@ -93,7 +122,7 @@ aiReturn DefaultIOStream::Seek(size_t pOffset,
         aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET");
 
     // do the seek
-    return (0 == ::fseek(mFile, (long)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE);
+    return (0 == select_fseek<sizeof(void*)>(mFile, (int64_t)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE);
 }
 
 // ----------------------------------------------------------------------------------
@@ -102,7 +131,7 @@ size_t DefaultIOStream::Tell() const
     if (!mFile) {
         return 0;
     }
-    return ::ftell(mFile);
+    return select_ftell<sizeof(void*)>(mFile);
 }
 
 // ----------------------------------------------------------------------------------

+ 60 - 101
Engine/lib/assimp/code/DefaultIOSystem.cpp → Engine/lib/assimp/code/Common/DefaultIOSystem.cpp

@@ -61,83 +61,66 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 using namespace Assimp;
 
-// maximum path length
-// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
-#ifdef PATH_MAX
-#   define PATHLIMIT PATH_MAX
-#else
-#   define PATHLIMIT 4096
+#ifdef _WIN32
+static std::wstring Utf8ToWide(const char* in)
+{
+    int size = MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
+    // size includes terminating null; std::wstring adds null automatically
+    std::wstring out(static_cast<size_t>(size) - 1, L'\0');
+    MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size);
+    return out;
+}
+
+static std::string WideToUtf8(const wchar_t* in)
+{
+    int size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
+    // size includes terminating null; std::string adds null automatically
+    std::string out(static_cast<size_t>(size) - 1, '\0');
+    WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr);
+    return out;
+}
 #endif
 
 // ------------------------------------------------------------------------------------------------
 // Tests for the existence of a file at the given path.
-bool DefaultIOSystem::Exists( const char* pFile) const
+bool DefaultIOSystem::Exists(const char* pFile) const
 {
 #ifdef _WIN32
-    wchar_t fileName16[PATHLIMIT];
-
-#ifndef WindowsStore
-    bool isUnicode = IsTextUnicode(pFile, static_cast<int>(strlen(pFile)), NULL) != 0;
-    if (isUnicode) {
-
-        MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT);
-        struct __stat64 filestat;
-        if (0 != _wstat64(fileName16, &filestat)) {
-            return false;
-        }
-    } else {
-#endif
-        FILE* file = ::fopen(pFile, "rb");
-        if (!file)
-            return false;
-
-        ::fclose(file);
-#ifndef WindowsStore
+    struct __stat64 filestat;
+    if (_wstat64(Utf8ToWide(pFile).c_str(), &filestat) != 0) {
+        return false;
     }
-#endif
 #else
-    FILE* file = ::fopen( pFile, "rb");
-    if( !file)
+    FILE* file = ::fopen(pFile, "rb");
+    if (!file)
         return false;
 
-    ::fclose( file);
+    ::fclose(file);
 #endif
     return true;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Open a new file with a given path.
-IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode)
+IOStream* DefaultIOSystem::Open(const char* strFile, const char* strMode)
 {
-    ai_assert(NULL != strFile);
-    ai_assert(NULL != strMode);
+    ai_assert(strFile != nullptr);
+    ai_assert(strMode != nullptr);
     FILE* file;
 #ifdef _WIN32
-    wchar_t fileName16[PATHLIMIT];
-#ifndef WindowsStore
-    bool isUnicode = IsTextUnicode(strFile, static_cast<int>(strlen(strFile)), NULL) != 0;
-    if (isUnicode) {
-        MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT);
-        std::string mode8(strMode);
-        file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str());
-    } else {
-#endif
-        file = ::fopen(strFile, strMode);
-#ifndef WindowsStore
-    }
-#endif
+    file = ::_wfopen(Utf8ToWide(strFile).c_str(), Utf8ToWide(strMode).c_str());
 #else
     file = ::fopen(strFile, strMode);
 #endif
-    if (nullptr == file)
+    if (!file)
         return nullptr;
 
-    return new DefaultIOStream(file, (std::string) strFile);
+    return new DefaultIOStream(file, strFile);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Closes the given file and releases all resources associated with it.
-void DefaultIOSystem::Close( IOStream* pFile)
+void DefaultIOSystem::Close(IOStream* pFile)
 {
     delete pFile;
 }
@@ -155,78 +138,56 @@ char DefaultIOSystem::getOsSeparator() const
 
 // ------------------------------------------------------------------------------------------------
 // IOSystem default implementation (ComparePaths isn't a pure virtual function)
-bool IOSystem::ComparePaths (const char* one, const char* second) const
+bool IOSystem::ComparePaths(const char* one, const char* second) const
 {
-    return !ASSIMP_stricmp(one,second);
+    return !ASSIMP_stricmp(one, second);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Convert a relative path into an absolute path
-inline static void MakeAbsolutePath (const char* in, char* _out)
+inline static std::string MakeAbsolutePath(const char* in)
 {
-    ai_assert(in && _out);
-#if defined( _MSC_VER ) || defined( __MINGW32__ )
-#ifndef WindowsStore
-    bool isUnicode = IsTextUnicode(in, static_cast<int>(strlen(in)), NULL) != 0;
-    if (isUnicode) {
-        wchar_t out16[PATHLIMIT];
-        wchar_t in16[PATHLIMIT];
-        MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT);
-        wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT);
-        if (ret) {
-            WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr);
-        }
-        if (!ret) {
-            // preserve the input path, maybe someone else is able to fix
-            // the path before it is accessed (e.g. our file system filter)
-            ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
-            strcpy(_out, in);
-        }
-
-    } else {
-#endif
-        char* ret = :: _fullpath(_out, in, PATHLIMIT);
-        if (!ret) {
-            // preserve the input path, maybe someone else is able to fix
-            // the path before it is accessed (e.g. our file system filter)
-            ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
-            strcpy(_out, in);
-        }
-#ifndef WindowsStore
+    ai_assert(in);
+    std::string out;
+#ifdef _WIN32
+    wchar_t* ret = ::_wfullpath(nullptr, Utf8ToWide(in).c_str(), 0);
+    if (ret) {
+        out = WideToUtf8(ret);
+        free(ret);
     }
-#endif
 #else
-    // use realpath
-    char* ret = realpath(in, _out);
-    if(!ret) {
+    char* ret = realpath(in, nullptr);
+    if (ret) {
+        out = ret;
+        free(ret);
+    }
+#endif
+    if (!ret) {
         // preserve the input path, maybe someone else is able to fix
         // the path before it is accessed (e.g. our file system filter)
         ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
-        strcpy(_out,in);
+        out = in;
     }
-#endif
+    return out;
 }
 
 // ------------------------------------------------------------------------------------------------
 // DefaultIOSystem's more specialized implementation
-bool DefaultIOSystem::ComparePaths (const char* one, const char* second) const
+bool DefaultIOSystem::ComparePaths(const char* one, const char* second) const
 {
     // chances are quite good both paths are formatted identically,
     // so we can hopefully return here already
-    if( !ASSIMP_stricmp(one,second) )
+    if (!ASSIMP_stricmp(one, second))
         return true;
 
-    char temp1[PATHLIMIT];
-    char temp2[PATHLIMIT];
-
-    MakeAbsolutePath (one, temp1);
-    MakeAbsolutePath (second, temp2);
+    std::string temp1 = MakeAbsolutePath(one);
+    std::string temp2 = MakeAbsolutePath(second);
 
-    return !ASSIMP_stricmp(temp1,temp2);
+    return !ASSIMP_stricmp(temp1, temp2);
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::fileName( const std::string &path )
+std::string DefaultIOSystem::fileName(const std::string& path)
 {
     std::string ret = path;
     std::size_t last = ret.find_last_of("\\/");
@@ -235,16 +196,16 @@ std::string DefaultIOSystem::fileName( const std::string &path )
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::completeBaseName( const std::string &path )
+std::string DefaultIOSystem::completeBaseName(const std::string& path)
 {
     std::string ret = fileName(path);
     std::size_t pos = ret.find_last_of('.');
-    if(pos != ret.npos) ret = ret.substr(0, pos);
+    if (pos != std::string::npos) ret = ret.substr(0, pos);
     return ret;
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::absolutePath( const std::string &path )
+std::string DefaultIOSystem::absolutePath(const std::string& path)
 {
     std::string ret = path;
     std::size_t last = ret.find_last_of("\\/");
@@ -253,5 +214,3 @@ std::string DefaultIOSystem::absolutePath( const std::string &path )
 }
 
 // ------------------------------------------------------------------------------------------------
-
-#undef PATHLIMIT

+ 0 - 0
Engine/lib/assimp/code/DefaultLogger.cpp → Engine/lib/assimp/code/Common/DefaultLogger.cpp


+ 0 - 0
Engine/lib/assimp/code/DefaultProgressHandler.h → Engine/lib/assimp/code/Common/DefaultProgressHandler.h


+ 88 - 99
Engine/lib/assimp/code/Exporter.cpp → Engine/lib/assimp/code/Common/Exporter.cpp

@@ -61,15 +61,16 @@ Here we implement only the C++ interface (Assimp::Exporter).
 #include <assimp/mesh.h>
 #include <assimp/postprocess.h>
 #include <assimp/scene.h>
-
-#include "DefaultProgressHandler.h"
-#include "BaseProcess.h"
-#include "JoinVerticesProcess.h"
-#include "MakeVerboseFormat.h"
-#include "ConvertToLHProcess.h"
-#include "PretransformVertices.h"
 #include <assimp/Exceptional.h>
-#include "ScenePrivate.h"
+
+#include "Common/DefaultProgressHandler.h"
+#include "Common/BaseProcess.h"
+#include "Common/ScenePrivate.h"
+#include "PostProcessing/CalcTangentsProcess.h"
+#include "PostProcessing/MakeVerboseFormat.h"
+#include "PostProcessing/JoinVerticesProcess.h"
+#include "PostProcessing/ConvertToLHProcess.h"
+#include "PostProcessing/PretransformVertices.h"
 
 #include <memory>
 
@@ -101,89 +102,92 @@ 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 ExportSceneM3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
+void ExportSceneA3D(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
-Exporter::ExportFormatEntry gExporters[] =
-{
+
+static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) {
 #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
-    Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada ),
+	exporters.push_back(Exporter::ExportFormatEntry("collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_X_EXPORTER
-    Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile,
-        aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs ),
+	exporters.push_back(Exporter::ExportFormatEntry("x", "X Files", "x", &ExportSceneXFile,
+			aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_STEP_EXPORTER
-    Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0 ),
+	exporters.push_back(Exporter::ExportFormatEntry("stp", "Step Files", "stp", &ExportSceneStep, 0));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
-    Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj,
-        aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ),
-    Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl,
-        aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ),
+	exporters.push_back(Exporter::ExportFormatEntry("obj", "Wavefront OBJ format", "obj", &ExportSceneObj,
+			aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */));
+	exporters.push_back(Exporter::ExportFormatEntry("objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl,
+			aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_STL_EXPORTER
-    Exporter::ExportFormatEntry( "stl", "Stereolithography", "stl" , &ExportSceneSTL,
-        aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
-    ),
-    Exporter::ExportFormatEntry( "stlb", "Stereolithography (binary)", "stl" , &ExportSceneSTLBinary,
-        aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
-    ),
+	exporters.push_back(Exporter::ExportFormatEntry("stl", "Stereolithography", "stl", &ExportSceneSTL,
+			aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices));
+	exporters.push_back(Exporter::ExportFormatEntry("stlb", "Stereolithography (binary)", "stl", &ExportSceneSTLBinary,
+			aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_PLY_EXPORTER
-    Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly,
-        aiProcess_PreTransformVertices
-    ),
-    Exporter::ExportFormatEntry( "plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary,
-        aiProcess_PreTransformVertices
-    ),
+	exporters.push_back(Exporter::ExportFormatEntry("ply", "Stanford Polygon Library", "ply", &ExportScenePly,
+			aiProcess_PreTransformVertices));
+	exporters.push_back(Exporter::ExportFormatEntry("plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary,
+			aiProcess_PreTransformVertices));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
-    Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS,
-        aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices ),
+	exporters.push_back(Exporter::ExportFormatEntry("3ds", "Autodesk 3DS (legacy)", "3ds", &ExportScene3DS,
+			aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
-    Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
-    Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
-    Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
-    Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
-        aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
+	exporters.push_back(Exporter::ExportFormatEntry("gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
+			aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
+	exporters.push_back(Exporter::ExportFormatEntry("glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
+			aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
+	exporters.push_back(Exporter::ExportFormatEntry("gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
+			aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
+	exporters.push_back(Exporter::ExportFormatEntry("glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
+			aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
-    Exporter::ExportFormatEntry( "assbin", "Assimp Binary", "assbin" , &ExportSceneAssbin, 0 ),
+	exporters.push_back(Exporter::ExportFormatEntry("assbin", "Assimp Binary File", "assbin", &ExportSceneAssbin, 0));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
-    Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0 ),
+	exporters.push_back(Exporter::ExportFormatEntry("assxml", "Assimp XML Document", "assxml", &ExportSceneAssxml, 0));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
-    Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ),
+	exporters.push_back(Exporter::ExportFormatEntry("x3d", "Extensible 3D", "x3d", &ExportSceneX3D, 0));
 #endif
 
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
-    Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ),
-    Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ),
+	exporters.push_back(Exporter::ExportFormatEntry("fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0));
+	exporters.push_back(Exporter::ExportFormatEntry("fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0));
 #endif
 
-#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
-    Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 )
+#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
+	exporters.push_back(Exporter::ExportFormatEntry("m3d", "Model 3D (binary)", "m3d", &ExportSceneM3D, 0));
+	exporters.push_back(Exporter::ExportFormatEntry("a3d", "Model 3D (ascii)", "m3d", &ExportSceneA3D, 0));
 #endif
-};
 
-#define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0]))
+#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
+	exporters.push_back(Exporter::ExportFormatEntry("3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0));
+#endif
 
+#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
+	exporters.push_back(Exporter::ExportFormatEntry("assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0));
+#endif
+}
 
 class ExporterPimpl {
 public:
@@ -199,10 +203,7 @@ public:
         GetPostProcessingStepInstanceList(mPostProcessingSteps);
 
         // grab all built-in exporters
-        if ( 0 != ( ASSIMP_NUM_EXPORTERS ) ) {
-            mExporters.resize( ASSIMP_NUM_EXPORTERS );
-            std::copy( gExporters, gExporters + ASSIMP_NUM_EXPORTERS, mExporters.begin() );
-        }
+		setupExporterArray(mExporters);
     }
 
     ~ExporterPimpl() {
@@ -246,24 +247,28 @@ Exporter :: Exporter()
 
 // ------------------------------------------------------------------------------------------------
 Exporter::~Exporter() {
-    FreeBlob();
+	ai_assert(nullptr != pimpl);
+	FreeBlob();
     delete pimpl;
 }
 
 // ------------------------------------------------------------------------------------------------
 void Exporter::SetIOHandler( IOSystem* pIOHandler) {
-    pimpl->mIsDefaultIOHandler = !pIOHandler;
+	ai_assert(nullptr != pimpl);
+	pimpl->mIsDefaultIOHandler = !pIOHandler;
     pimpl->mIOSystem.reset(pIOHandler);
 }
 
 // ------------------------------------------------------------------------------------------------
 IOSystem* Exporter::GetIOHandler() const {
-    return pimpl->mIOSystem.get();
+	ai_assert(nullptr != pimpl);
+	return pimpl->mIOSystem.get();
 }
 
 // ------------------------------------------------------------------------------------------------
 bool Exporter::IsDefaultIOHandler() const {
-    return pimpl->mIsDefaultIOHandler;
+	ai_assert(nullptr != pimpl);
+	return pimpl->mIsDefaultIOHandler;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -288,7 +293,8 @@ void Exporter::SetProgressHandler(ProgressHandler* pHandler) {
 
 // ------------------------------------------------------------------------------------------------
 const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
-                                                unsigned int, const ExportProperties* /*pProperties*/ ) {
+                                                unsigned int pPreprocessing, const ExportProperties* pProperties) {
+	ai_assert(nullptr != pimpl);
     if (pimpl->blob) {
         delete pimpl->blob;
         pimpl->blob = nullptr;
@@ -298,7 +304,7 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha
     BlobIOSystem* blobio = new BlobIOSystem();
     pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio );
 
-    if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName())) {
+    if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) {
         pimpl->mIOSystem = old;
         return nullptr;
     }
@@ -309,44 +315,16 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha
     return pimpl->blob;
 }
 
-// ------------------------------------------------------------------------------------------------
-bool IsVerboseFormat(const aiMesh* mesh) {
-    // avoid slow vector<bool> specialization
-    std::vector<unsigned int> seen(mesh->mNumVertices,0);
-    for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
-        const aiFace& f = mesh->mFaces[i];
-        for(unsigned int j = 0; j < f.mNumIndices; ++j) {
-            if(++seen[f.mIndices[j]] == 2) {
-                // found a duplicate index
-                return false;
-            }
-        }
-    }
-
-    return true;
-}
-
-// ------------------------------------------------------------------------------------------------
-bool IsVerboseFormat(const aiScene* pScene) {
-    for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
-        if(!IsVerboseFormat(pScene->mMeshes[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
 // ------------------------------------------------------------------------------------------------
 aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath,
         unsigned int pPreprocessing, const ExportProperties* pProperties) {
     ASSIMP_BEGIN_EXCEPTION_REGION();
-
+	ai_assert(nullptr != pimpl);
     // when they create scenes from scratch, users will likely create them not in verbose
     // format. They will likely not be aware that there is a flag in the scene to indicate
     // this, however. To avoid surprises and bug reports, we check for duplicates in
     // meshes upfront.
-    const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene);
+    const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || MakeVerboseFormatProcess::IsVerboseFormat(pScene);
 
     pimpl->mProgressHandler->UpdateFileWrite(0, 4);
 
@@ -466,7 +444,10 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
                 }
 
                 ExportProperties emptyProperties;  // Never pass NULL ExportProperties so Exporters don't have to worry.
-                exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties);
+                ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties;
+                                pProp->SetPropertyBool("bJoinIdenticalVertices", must_join_again);
+                                exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
+                exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
 
                 pimpl->mProgressHandler->UpdateFileWrite(4, 4);
             } catch (DeadlyExportError& err) {
@@ -485,11 +466,13 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
 
 // ------------------------------------------------------------------------------------------------
 const char* Exporter::GetErrorString() const {
+	ai_assert(nullptr != pimpl);
     return pimpl->mError.c_str();
 }
 
 // ------------------------------------------------------------------------------------------------
 void Exporter::FreeBlob() {
+	ai_assert(nullptr != pimpl);
     delete pimpl->blob;
     pimpl->blob = nullptr;
 
@@ -498,30 +481,34 @@ void Exporter::FreeBlob() {
 
 // ------------------------------------------------------------------------------------------------
 const aiExportDataBlob* Exporter::GetBlob() const {
-    return pimpl->blob;
+	ai_assert(nullptr != pimpl);
+	return pimpl->blob;
 }
 
 // ------------------------------------------------------------------------------------------------
 const aiExportDataBlob* Exporter::GetOrphanedBlob() const {
-    const aiExportDataBlob* tmp = pimpl->blob;
+	ai_assert(nullptr != pimpl);
+	const aiExportDataBlob *tmp = pimpl->blob;
     pimpl->blob = nullptr;
     return tmp;
 }
 
 // ------------------------------------------------------------------------------------------------
 size_t Exporter::GetExportFormatCount() const {
+	ai_assert(nullptr != pimpl);
     return pimpl->mExporters.size();
 }
 
 // ------------------------------------------------------------------------------------------------
 const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const {
-    if (index >= GetExportFormatCount()) {
+	ai_assert(nullptr != pimpl);
+	if (index >= GetExportFormatCount()) {
         return nullptr;
     }
 
     // Return from static storage if the requested index is built-in.
-    if (index < sizeof(gExporters) / sizeof(gExporters[0])) {
-        return &gExporters[index].mDescription;
+	if (index < pimpl->mExporters.size()) {
+		return &pimpl->mExporters[index].mDescription;
     }
 
     return &pimpl->mExporters[index].mDescription;
@@ -529,7 +516,8 @@ const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) c
 
 // ------------------------------------------------------------------------------------------------
 aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
-    for(const ExportFormatEntry& e : pimpl->mExporters) {
+	ai_assert(nullptr != pimpl);
+	for (const ExportFormatEntry &e : pimpl->mExporters) {
         if (!strcmp(e.mDescription.id,desc.mDescription.id)) {
             return aiReturn_FAILURE;
         }
@@ -541,7 +529,8 @@ aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
 
 // ------------------------------------------------------------------------------------------------
 void Exporter::UnregisterExporter(const char* id) {
-    for(std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
+	ai_assert(nullptr != pimpl);
+	for (std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
             it != pimpl->mExporters.end(); ++it) {
         if (!strcmp((*it).mDescription.id,id)) {
             pimpl->mExporters.erase(it);

+ 0 - 0
Engine/lib/assimp/code/FileLogStream.h → Engine/lib/assimp/code/Common/FileLogStream.h


+ 0 - 0
Engine/lib/assimp/code/FileSystemFilter.h → Engine/lib/assimp/code/Common/FileSystemFilter.h


+ 0 - 0
Engine/lib/assimp/code/IFF.h → Engine/lib/assimp/code/Common/IFF.h


+ 11 - 8
Engine/lib/assimp/code/Importer.cpp → Engine/lib/assimp/code/Common/Importer.cpp

@@ -64,15 +64,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // ------------------------------------------------------------------------------------------------
 // Internal headers
 // ------------------------------------------------------------------------------------------------
-#include "Importer.h"
-#include <assimp/BaseImporter.h>
-#include "BaseProcess.h"
+#include "Common/Importer.h"
+#include "Common/BaseProcess.h"
+#include "Common/DefaultProgressHandler.h"
+#include "PostProcessing/ProcessHelper.h"
+#include "Common/ScenePreprocessor.h"
+#include "Common/ScenePrivate.h"
 
-#include "DefaultProgressHandler.h"
+#include <assimp/BaseImporter.h>
 #include <assimp/GenericProperty.h>
-#include "ProcessHelper.h"
-#include "ScenePreprocessor.h"
-#include "ScenePrivate.h"
 #include <assimp/MemoryIOWrapper.h>
 #include <assimp/Profiler.h>
 #include <assimp/TinyFormatter.h>
@@ -86,7 +86,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/DefaultIOSystem.h>
 
 #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-#   include "ValidateDataStructure.h"
+#   include "PostProcessing/ValidateDataStructure.h"
 #endif
 
 using namespace Assimp::Profiling;
@@ -590,10 +590,12 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
 
         // Find an worker class which can handle the file
         BaseImporter* imp = NULL;
+        SetPropertyInteger("importerIndex", -1);
         for( unsigned int a = 0; a < pimpl->mImporter.size(); a++)  {
 
             if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) {
                 imp = pimpl->mImporter[a];
+                SetPropertyInteger("importerIndex", a);
                 break;
             }
         }
@@ -606,6 +608,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
                 for( unsigned int a = 0; a < pimpl->mImporter.size(); a++)  {
                     if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) {
                         imp = pimpl->mImporter[a];
+                        SetPropertyInteger("importerIndex", a);
                         break;
                     }
                 }

+ 0 - 0
Engine/lib/assimp/code/Importer.h → Engine/lib/assimp/code/Common/Importer.h


+ 54 - 48
Engine/lib/assimp/code/ImporterRegistry.cpp → Engine/lib/assimp/code/Common/ImporterRegistry.cpp

@@ -56,146 +56,149 @@ corresponding preprocessor flag to selectively disable formats.
 // (include_new_importers_here)
 // ------------------------------------------------------------------------------------------------
 #ifndef ASSIMP_BUILD_NO_X_IMPORTER
-#   include "XFileImporter.h"
+#   include "X/XFileImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
-#   include "AMFImporter.hpp"
+#   include "AMF/AMFImporter.hpp"
 #endif
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
-#   include "3DSLoader.h"
+#   include "3DS/3DSLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
-#   include "MD3Loader.h"
+#   include "MD3/MD3Loader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
-#   include "MDLLoader.h"
+#   include "MDL/MDLLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
-#   include "MD2Loader.h"
+#   include "MD2/MD2Loader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
-#   include "PlyLoader.h"
+#   include "Ply/PlyLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
-#   include "ASELoader.h"
+#   include "ASE/ASELoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
-#   include "ObjFileImporter.h"
+#   include "Obj/ObjFileImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
-#   include "HMPLoader.h"
+#   include "HMP/HMPLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
-#   include "SMDLoader.h"
+#   include "SMD/SMDLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
-#   include "MDCLoader.h"
+#   include "MDC/MDCLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
-#   include "MD5Loader.h"
+#   include "MD5/MD5Loader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_STL_IMPORTER
-#   include "STLLoader.h"
+#   include "STL/STLLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
-#   include "LWOLoader.h"
+#   include "LWO/LWOLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
-#   include "DXFLoader.h"
+#   include "DXF/DXFLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
-#   include "NFFLoader.h"
+#   include "NFF/NFFLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
-#   include "RawLoader.h"
+#   include "Raw/RawLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_SIB_IMPORTER
-#   include "SIBImporter.h"
+#   include "SIB/SIBImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
-#   include "OFFLoader.h"
+#   include "OFF/OFFLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_AC_IMPORTER
-#   include "ACLoader.h"
+#   include "AC/ACLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
-#   include "BVHLoader.h"
+#   include "BVH/BVHLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
-#   include "IRRMeshLoader.h"
+#   include "Irr/IRRMeshLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
-#   include "IRRLoader.h"
+#   include "Irr/IRRLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
-#   include "Q3DLoader.h"
+#   include "Q3D/Q3DLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
-#   include "B3DImporter.h"
+#   include "B3D/B3DImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
-#   include "ColladaLoader.h"
+#   include "Collada/ColladaLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
-#   include "TerragenLoader.h"
+#   include "Terragen/TerragenLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
-#   include "CSMLoader.h"
+#   include "CSM/CSMLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_3D_IMPORTER
-#   include "UnrealLoader.h"
+#   include "Unreal/UnrealLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_LWS_IMPORTER
-#   include "LWSLoader.h"
+#   include "LWS/LWSLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
-#   include "OgreImporter.h"
+#   include "Ogre/OgreImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER
-#   include "OpenGEXImporter.h"
+#   include "OpenGEX/OpenGEXImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
-#   include "MS3DLoader.h"
+#   include "MS3D/MS3DLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_COB_IMPORTER
-#   include "COBLoader.h"
+#   include "COB/COBLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
-#   include "BlenderLoader.h"
+#   include "Blender/BlenderLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
-#   include "Q3BSPFileImporter.h"
+#   include "Q3BSP/Q3BSPFileImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_NDO_IMPORTER
-#   include "NDOLoader.h"
+#   include "NDO/NDOLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
 #   include "Importer/IFC/IFCLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_XGL_IMPORTER
-#   include "XGLLoader.h"
+#   include "XGL/XGLLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
-#   include "FBXImporter.h"
+#   include "FBX/FBXImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
-#   include "AssbinLoader.h"
+#   include "Assbin/AssbinLoader.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
-#   include "glTFImporter.h"
-#   include "glTF2Importer.h"
+#   include "glTF/glTFImporter.h"
+#   include "glTF2/glTF2Importer.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
-#   include "C4DImporter.h"
+#   include "C4D/C4DImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
-#   include "D3MFImporter.h"
+#   include "3MF/D3MFImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
-#   include "X3DImporter.hpp"
+#   include "X3D/X3DImporter.hpp"
 #endif
 #ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
-#   include "MMDImporter.h"
+#   include "MMD/MMDImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
+#   include "M3D/M3DImporter.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
 #   include "Importer/StepFile/StepFileImporter.h"
@@ -223,6 +226,9 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
 #if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
     out.push_back( new Discreet3DSImporter());
 #endif
+#if (!defined ASSIMP_BUILD_NO_M3D_IMPORTER)
+    out.push_back( new M3DImporter());
+#endif
 #if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER)
     out.push_back( new MD3Importer());
 #endif
@@ -364,7 +370,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
 }
 

+ 0 - 0
Engine/lib/assimp/code/PolyTools.h → Engine/lib/assimp/code/Common/PolyTools.h


+ 45 - 31
Engine/lib/assimp/code/PostStepRegistry.cpp → Engine/lib/assimp/code/Common/PostStepRegistry.cpp

@@ -48,89 +48,97 @@ directly (unless you are adding new steps), instead use the
 corresponding preprocessor flag to selectively disable steps.
 */
 
-#include "ProcessHelper.h"
+#include "PostProcessing/ProcessHelper.h"
 
 #ifndef ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS
-#   include "CalcTangentsProcess.h"
+#   include "PostProcessing/CalcTangentsProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
-#   include "JoinVerticesProcess.h"
+#   include "PostProcessing/JoinVerticesProcess.h"
 #endif
 #if !(defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS && defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS && defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS)
-#   include "ConvertToLHProcess.h"
+#   include "PostProcessing/ConvertToLHProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
-#   include "TriangulateProcess.h"
+#   include "PostProcessing/TriangulateProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_DROPFACENORMALS_PROCESS
-#   include "DropFaceNormalsProcess.h"
+#   include "PostProcessing/DropFaceNormalsProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS
-#   include "GenFaceNormalsProcess.h"
+#   include "PostProcessing/GenFaceNormalsProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS
-#   include "GenVertexNormalsProcess.h"
+#   include "PostProcessing/GenVertexNormalsProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_REMOVEVC_PROCESS
-#   include "RemoveVCProcess.h"
+#   include "PostProcessing/RemoveVCProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS
-#   include "SplitLargeMeshes.h"
+#   include "PostProcessing/SplitLargeMeshes.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS
-#   include "PretransformVertices.h"
+#   include "PostProcessing/PretransformVertices.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS
-#   include "LimitBoneWeightsProcess.h"
+#   include "PostProcessing/LimitBoneWeightsProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
-#   include "ValidateDataStructure.h"
+#   include "PostProcessing/ValidateDataStructure.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS
-#   include "ImproveCacheLocality.h"
+#   include "PostProcessing/ImproveCacheLocality.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS
-#   include "FixNormalsStep.h"
+#   include "PostProcessing/FixNormalsStep.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS
-#   include "RemoveRedundantMaterials.h"
+#   include "PostProcessing/RemoveRedundantMaterials.h"
 #endif
 #if (!defined ASSIMP_BUILD_NO_EMBEDTEXTURES_PROCESS)
-#   include "EmbedTexturesProcess.h"
+#   include "PostProcessing/EmbedTexturesProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
-#   include "FindInvalidDataProcess.h"
+#   include "PostProcessing/FindInvalidDataProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS
-#   include "FindDegenerates.h"
+#   include "PostProcessing/FindDegenerates.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS
-#   include "SortByPTypeProcess.h"
+#   include "PostProcessing/SortByPTypeProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
-#   include "ComputeUVMappingProcess.h"
+#   include "PostProcessing/ComputeUVMappingProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
-#   include "TextureTransform.h"
+#   include "PostProcessing/TextureTransform.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS
-#   include "FindInstancesProcess.h"
+#   include "PostProcessing/FindInstancesProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
-#   include "OptimizeMeshes.h"
+#   include "PostProcessing/OptimizeMeshes.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
-#   include "OptimizeGraph.h"
+#   include "PostProcessing/OptimizeGraph.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS
-#   include "SplitByBoneCountProcess.h"
+#   include "Common/SplitByBoneCountProcess.h"
 #endif
 #ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS
-#   include "DeboneProcess.h"
+#   include "PostProcessing/DeboneProcess.h"
 #endif
 #if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
-#   include "ScaleProcess.h"
+#   include "PostProcessing/ScaleProcess.h"
 #endif
+#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS)
+#   include "PostProcessing/ArmaturePopulate.h"
+#endif
+#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS)
+#   include "PostProcessing/GenBoundingBoxesProcess.h"
+#endif
+
+
 
 namespace Assimp {
 
@@ -173,6 +181,12 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
 #ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
     out.push_back( new TextureTransformStep());
 #endif
+#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
+    out.push_back( new ScaleProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS)
+    out.push_back( new ArmaturePopulate());
+#endif
 #if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
     out.push_back( new PretransformVertices());
 #endif
@@ -208,9 +222,6 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
 #endif
 #if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS)
     out.push_back( new GenFaceNormalsProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
-    out.push_back( new ScaleProcess());
 #endif
     // .........................................................................
     // DON'T change the order of these five ..
@@ -246,6 +257,9 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
 #if (!defined ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS)
     out.push_back( new ImproveCacheLocalityProcess());
 #endif
+#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS)
+    out.push_back(new GenBoundingBoxesProcess);
+#endif
 }
 
 }

Some files were not shown because too many files changed in this diff